summaryrefslogtreecommitdiff
path: root/Source/WebCore/Modules
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/Modules')
-rw-r--r--Source/WebCore/Modules/airplay/WebKitPlaybackTargetAvailabilityEvent.cpp57
-rw-r--r--Source/WebCore/Modules/airplay/WebKitPlaybackTargetAvailabilityEvent.h65
-rw-r--r--Source/WebCore/Modules/airplay/WebKitPlaybackTargetAvailabilityEvent.idl36
-rw-r--r--Source/WebCore/Modules/applepay/ApplePayLineItem.h44
-rw-r--r--Source/WebCore/Modules/applepay/ApplePayLineItem.idl39
-rw-r--r--Source/WebCore/Modules/applepay/ApplePayPayment.h49
-rw-r--r--Source/WebCore/Modules/applepay/ApplePayPayment.idl42
-rw-r--r--Source/WebCore/Modules/applepay/ApplePayPaymentAuthorizedEvent.cpp52
-rw-r--r--Source/WebCore/Modules/applepay/ApplePayPaymentAuthorizedEvent.h59
-rw-r--r--Source/WebCore/Modules/applepay/ApplePayPaymentAuthorizedEvent.idl31
-rw-r--r--Source/WebCore/Modules/applepay/ApplePayPaymentContact.h51
-rw-r--r--Source/WebCore/Modules/applepay/ApplePayPaymentContact.idl40
-rw-r--r--Source/WebCore/Modules/applepay/ApplePayPaymentMethod.h48
-rw-r--r--Source/WebCore/Modules/applepay/ApplePayPaymentMethod.idl43
-rw-r--r--Source/WebCore/Modules/applepay/ApplePayPaymentMethodSelectedEvent.cpp52
-rw-r--r--Source/WebCore/Modules/applepay/ApplePayPaymentMethodSelectedEvent.h59
-rw-r--r--Source/WebCore/Modules/applepay/ApplePayPaymentMethodSelectedEvent.idl31
-rw-r--r--Source/WebCore/Modules/applepay/ApplePayPaymentPass.h46
-rw-r--r--Source/WebCore/Modules/applepay/ApplePayPaymentPass.idl45
-rw-r--r--Source/WebCore/Modules/applepay/ApplePayPaymentRequest.h65
-rw-r--r--Source/WebCore/Modules/applepay/ApplePayPaymentRequest.idl74
-rw-r--r--Source/WebCore/Modules/applepay/ApplePaySession.cpp1032
-rw-r--r--Source/WebCore/Modules/applepay/ApplePaySession.h148
-rw-r--r--Source/WebCore/Modules/applepay/ApplePaySession.idl62
-rw-r--r--Source/WebCore/Modules/applepay/ApplePayShippingContactSelectedEvent.cpp52
-rw-r--r--Source/WebCore/Modules/applepay/ApplePayShippingContactSelectedEvent.h58
-rw-r--r--Source/WebCore/Modules/applepay/ApplePayShippingContactSelectedEvent.idl31
-rw-r--r--Source/WebCore/Modules/applepay/ApplePayShippingMethod.h43
-rw-r--r--Source/WebCore/Modules/applepay/ApplePayShippingMethod.idl34
-rw-r--r--Source/WebCore/Modules/applepay/ApplePayShippingMethodSelectedEvent.cpp77
-rw-r--r--Source/WebCore/Modules/applepay/ApplePayShippingMethodSelectedEvent.h58
-rw-r--r--Source/WebCore/Modules/applepay/ApplePayShippingMethodSelectedEvent.idl31
-rw-r--r--Source/WebCore/Modules/applepay/ApplePayValidateMerchantEvent.cpp50
-rw-r--r--Source/WebCore/Modules/applepay/ApplePayValidateMerchantEvent.h57
-rw-r--r--Source/WebCore/Modules/applepay/ApplePayValidateMerchantEvent.idl31
-rw-r--r--Source/WebCore/Modules/applepay/Payment.h63
-rw-r--r--Source/WebCore/Modules/applepay/PaymentAuthorizationStatus.h62
-rw-r--r--Source/WebCore/Modules/applepay/PaymentContact.h58
-rw-r--r--Source/WebCore/Modules/applepay/PaymentCoordinator.cpp190
-rw-r--r--Source/WebCore/Modules/applepay/PaymentCoordinator.h79
-rw-r--r--Source/WebCore/Modules/applepay/PaymentCoordinatorClient.h62
-rw-r--r--Source/WebCore/Modules/applepay/PaymentHeaders.h33
-rw-r--r--Source/WebCore/Modules/applepay/PaymentMerchantSession.h67
-rw-r--r--Source/WebCore/Modules/applepay/PaymentMethod.h59
-rw-r--r--Source/WebCore/Modules/applepay/PaymentRequest.cpp74
-rw-r--r--Source/WebCore/Modules/applepay/PaymentRequest.h152
-rw-r--r--Source/WebCore/Modules/applepay/PaymentRequestValidator.cpp159
-rw-r--r--Source/WebCore/Modules/applepay/PaymentRequestValidator.h43
-rw-r--r--Source/WebCore/Modules/battery/BatteryController.cpp116
-rw-r--r--Source/WebCore/Modules/battery/BatteryController.h65
-rw-r--r--Source/WebCore/Modules/battery/BatteryManager.cpp112
-rw-r--r--Source/WebCore/Modules/battery/BatteryManager.h87
-rw-r--r--Source/WebCore/Modules/battery/BatteryManager.idl45
-rw-r--r--Source/WebCore/Modules/battery/NavigatorBattery.cpp75
-rw-r--r--Source/WebCore/Modules/encryptedmedia/CDM.cpp663
-rw-r--r--Source/WebCore/Modules/encryptedmedia/CDM.h122
-rw-r--r--Source/WebCore/Modules/encryptedmedia/CDMInstance.h92
-rw-r--r--Source/WebCore/Modules/encryptedmedia/CDMPrivate.h63
-rw-r--r--Source/WebCore/Modules/encryptedmedia/InitDataRegistry.cpp165
-rw-r--r--Source/WebCore/Modules/encryptedmedia/InitDataRegistry.h68
-rw-r--r--Source/WebCore/Modules/encryptedmedia/MediaKeyMessageEvent.cpp52
-rw-r--r--Source/WebCore/Modules/encryptedmedia/MediaKeyMessageEvent.h67
-rw-r--r--Source/WebCore/Modules/encryptedmedia/MediaKeyMessageEvent.idl48
-rw-r--r--Source/WebCore/Modules/encryptedmedia/MediaKeyMessageEventInit.h54
-rw-r--r--Source/WebCore/Modules/encryptedmedia/MediaKeyMessageType.h44
-rw-r--r--Source/WebCore/Modules/encryptedmedia/MediaKeySession.cpp709
-rw-r--r--Source/WebCore/Modules/encryptedmedia/MediaKeySession.h119
-rw-r--r--Source/WebCore/Modules/encryptedmedia/MediaKeySession.idl44
-rw-r--r--Source/WebCore/Modules/encryptedmedia/MediaKeySessionType.h43
-rw-r--r--Source/WebCore/Modules/encryptedmedia/MediaKeySessionType.idl35
-rw-r--r--Source/WebCore/Modules/encryptedmedia/MediaKeyStatus.h47
-rw-r--r--Source/WebCore/Modules/encryptedmedia/MediaKeyStatusMap.cpp112
-rw-r--r--Source/WebCore/Modules/encryptedmedia/MediaKeyStatusMap.h81
-rw-r--r--Source/WebCore/Modules/encryptedmedia/MediaKeyStatusMap.idl47
-rw-r--r--Source/WebCore/Modules/encryptedmedia/MediaKeySystemAccess.cpp118
-rw-r--r--Source/WebCore/Modules/encryptedmedia/MediaKeySystemAccess.h65
-rw-r--r--Source/WebCore/Modules/encryptedmedia/MediaKeySystemAccess.idl (renamed from Source/WebCore/Modules/webdatabase/SQLTransactionSyncCallback.idl)34
-rw-r--r--Source/WebCore/Modules/encryptedmedia/MediaKeySystemConfiguration.h55
-rw-r--r--Source/WebCore/Modules/encryptedmedia/MediaKeySystemConfiguration.idl40
-rw-r--r--Source/WebCore/Modules/encryptedmedia/MediaKeySystemMediaCapability.h44
-rw-r--r--Source/WebCore/Modules/encryptedmedia/MediaKeySystemMediaCapability.idl35
-rw-r--r--Source/WebCore/Modules/encryptedmedia/MediaKeys.cpp114
-rw-r--r--Source/WebCore/Modules/encryptedmedia/MediaKeys.h75
-rw-r--r--Source/WebCore/Modules/encryptedmedia/MediaKeys.idl (renamed from Source/WebCore/Modules/webdatabase/SQLTransactionSync.idl)34
-rw-r--r--Source/WebCore/Modules/encryptedmedia/MediaKeysRequirement.h43
-rw-r--r--Source/WebCore/Modules/encryptedmedia/MediaKeysRequirement.idl36
-rw-r--r--Source/WebCore/Modules/encryptedmedia/MediaKeysRestrictions.h42
-rw-r--r--Source/WebCore/Modules/encryptedmedia/NavigatorEME.cpp109
-rw-r--r--Source/WebCore/Modules/encryptedmedia/NavigatorEME.h50
-rw-r--r--Source/WebCore/Modules/encryptedmedia/NavigatorEME.idl34
-rw-r--r--Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDM.cpp151
-rw-r--r--Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDM.h81
-rw-r--r--Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMPrivate.h49
-rw-r--r--Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMPrivateClearKey.cpp68
-rw-r--r--Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMPrivateClearKey.h57
-rw-r--r--Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMPrivateMediaPlayer.cpp68
-rw-r--r--Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMPrivateMediaPlayer.h58
-rw-r--r--Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMSessionClearKey.cpp209
-rw-r--r--Source/WebCore/Modules/encryptedmedia/legacy/LegacyCDMSessionClearKey.h59
-rw-r--r--Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyMessageEvent.cpp61
-rw-r--r--Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyMessageEvent.h70
-rw-r--r--Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyMessageEvent.idl37
-rw-r--r--Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyNeededEvent.cpp (renamed from Source/WebCore/Modules/webdatabase/DatabaseBackendContext.cpp)31
-rw-r--r--Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyNeededEvent.h (renamed from Source/WebCore/Modules/webdatabase/DatabaseBackendSync.h)50
-rw-r--r--Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyNeededEvent.idl35
-rw-r--r--Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeySession.cpp256
-rw-r--r--Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeySession.h106
-rw-r--r--Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeySession.idl41
-rw-r--r--Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeys.cpp165
-rw-r--r--Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeys.h70
-rw-r--r--Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeys.idl34
-rw-r--r--Source/WebCore/Modules/fetch/DOMWindowFetch.cpp51
-rw-r--r--Source/WebCore/Modules/fetch/DOMWindowFetch.h48
-rw-r--r--Source/WebCore/Modules/fetch/DOMWindowFetch.idl35
-rw-r--r--Source/WebCore/Modules/fetch/DOMWindowFetch.js37
-rw-r--r--Source/WebCore/Modules/fetch/FetchBody.cpp277
-rw-r--r--Source/WebCore/Modules/fetch/FetchBody.h125
-rw-r--r--Source/WebCore/Modules/fetch/FetchBody.idl44
-rw-r--r--Source/WebCore/Modules/fetch/FetchBodyConsumer.cpp155
-rw-r--r--Source/WebCore/Modules/fetch/FetchBodyConsumer.h73
-rw-r--r--Source/WebCore/Modules/fetch/FetchBodyOwner.cpp273
-rw-r--r--Source/WebCore/Modules/fetch/FetchBodyOwner.h112
-rw-r--r--Source/WebCore/Modules/fetch/FetchHeaders.cpp148
-rw-r--r--Source/WebCore/Modules/fetch/FetchHeaders.h103
-rw-r--r--Source/WebCore/Modules/fetch/FetchHeaders.idl49
-rw-r--r--Source/WebCore/Modules/fetch/FetchHeaders.js (renamed from Source/WebCore/Modules/mediastream/SourceInfo.idl)31
-rw-r--r--Source/WebCore/Modules/fetch/FetchInternals.js80
-rw-r--r--Source/WebCore/Modules/fetch/FetchLoader.cpp156
-rw-r--r--Source/WebCore/Modules/fetch/FetchLoader.h72
-rw-r--r--Source/WebCore/Modules/fetch/FetchLoaderClient.h53
-rw-r--r--Source/WebCore/Modules/fetch/FetchRequest.cpp241
-rw-r--r--Source/WebCore/Modules/fetch/FetchRequest.h158
-rw-r--r--Source/WebCore/Modules/fetch/FetchRequest.idl84
-rw-r--r--Source/WebCore/Modules/fetch/FetchRequest.js50
-rw-r--r--Source/WebCore/Modules/fetch/FetchResponse.cpp365
-rw-r--r--Source/WebCore/Modules/fetch/FetchResponse.h133
-rw-r--r--Source/WebCore/Modules/fetch/FetchResponse.idl77
-rw-r--r--Source/WebCore/Modules/fetch/FetchResponse.js172
-rw-r--r--Source/WebCore/Modules/fetch/FetchResponseSource.cpp87
-rw-r--r--Source/WebCore/Modules/fetch/FetchResponseSource.h69
-rw-r--r--Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.cpp46
-rw-r--r--Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.h47
-rw-r--r--Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.idl35
-rw-r--r--Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.js37
-rw-r--r--Source/WebCore/Modules/gamepad/Gamepad.cpp67
-rw-r--r--Source/WebCore/Modules/gamepad/Gamepad.h69
-rw-r--r--Source/WebCore/Modules/gamepad/Gamepad.idl38
-rw-r--r--Source/WebCore/Modules/gamepad/GamepadButton.cpp48
-rw-r--r--Source/WebCore/Modules/gamepad/GamepadButton.h54
-rw-r--r--Source/WebCore/Modules/gamepad/GamepadButton.idl34
-rw-r--r--Source/WebCore/Modules/gamepad/GamepadEvent.cpp46
-rw-r--r--Source/WebCore/Modules/gamepad/GamepadEvent.h (renamed from Source/WebCore/Modules/indexeddb/IDBPendingOpenCall.h)56
-rw-r--r--Source/WebCore/Modules/gamepad/GamepadEvent.idl38
-rw-r--r--Source/WebCore/Modules/gamepad/GamepadManager.cpp245
-rw-r--r--Source/WebCore/Modules/gamepad/GamepadManager.h76
-rw-r--r--Source/WebCore/Modules/gamepad/NavigatorGamepad.cpp122
-rw-r--r--Source/WebCore/Modules/gamepad/NavigatorGamepad.h55
-rw-r--r--Source/WebCore/Modules/gamepad/NavigatorGamepad.idl36
-rw-r--r--Source/WebCore/Modules/gamepad/deprecated/Gamepad.cpp61
-rw-r--r--Source/WebCore/Modules/gamepad/deprecated/Gamepad.h72
-rw-r--r--Source/WebCore/Modules/gamepad/deprecated/Gamepad.idl37
-rw-r--r--Source/WebCore/Modules/gamepad/deprecated/GamepadList.cpp (renamed from Source/WebCore/Modules/gamepad/GamepadList.cpp)8
-rw-r--r--Source/WebCore/Modules/gamepad/deprecated/GamepadList.h (renamed from Source/WebCore/Modules/gamepad/GamepadList.h)14
-rw-r--r--Source/WebCore/Modules/gamepad/deprecated/GamepadList.idl (renamed from Source/WebCore/Modules/gamepad/GamepadList.idl)4
-rw-r--r--Source/WebCore/Modules/gamepad/deprecated/NavigatorGamepad.cpp76
-rw-r--r--Source/WebCore/Modules/gamepad/deprecated/NavigatorGamepad.h (renamed from Source/WebCore/Modules/indexeddb/PageGroupIndexedDatabase.h)34
-rw-r--r--Source/WebCore/Modules/gamepad/deprecated/NavigatorGamepad.idl (renamed from Source/WebCore/Modules/mediastream/NavigatorMediaStream.idl)8
-rw-r--r--Source/WebCore/Modules/geolocation/Coordinates.cpp40
-rw-r--r--Source/WebCore/Modules/geolocation/Coordinates.h18
-rw-r--r--Source/WebCore/Modules/geolocation/Coordinates.idl14
-rw-r--r--Source/WebCore/Modules/geolocation/GeoNotifier.cpp132
-rw-r--r--Source/WebCore/Modules/geolocation/GeoNotifier.h80
-rw-r--r--Source/WebCore/Modules/geolocation/Geolocation.cpp325
-rw-r--r--Source/WebCore/Modules/geolocation/Geolocation.h93
-rw-r--r--Source/WebCore/Modules/geolocation/Geolocation.idl16
-rw-r--r--Source/WebCore/Modules/geolocation/GeolocationClient.h7
-rw-r--r--Source/WebCore/Modules/geolocation/GeolocationController.cpp84
-rw-r--r--Source/WebCore/Modules/geolocation/GeolocationController.h30
-rw-r--r--Source/WebCore/Modules/geolocation/GeolocationError.h9
-rw-r--r--Source/WebCore/Modules/geolocation/GeolocationPosition.h18
-rw-r--r--Source/WebCore/Modules/geolocation/Geoposition.h22
-rw-r--r--Source/WebCore/Modules/geolocation/Geoposition.idl4
-rw-r--r--Source/WebCore/Modules/geolocation/NavigatorGeolocation.cpp11
-rw-r--r--Source/WebCore/Modules/geolocation/NavigatorGeolocation.h10
-rw-r--r--Source/WebCore/Modules/geolocation/PositionCallback.h5
-rw-r--r--Source/WebCore/Modules/geolocation/PositionCallback.idl4
-rw-r--r--Source/WebCore/Modules/geolocation/PositionError.h8
-rw-r--r--Source/WebCore/Modules/geolocation/PositionErrorCallback.h5
-rw-r--r--Source/WebCore/Modules/geolocation/PositionErrorCallback.idl4
-rw-r--r--Source/WebCore/Modules/geolocation/PositionOptions.h57
-rw-r--r--Source/WebCore/Modules/geolocation/PositionOptions.idl30
-rw-r--r--Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.cpp40
-rw-r--r--Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.h22
-rw-r--r--Source/WebCore/Modules/indexeddb/DOMWindowIndexedDatabase.idl7
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBActiveDOMObject.h98
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBAny.cpp218
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBAny.h145
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBCallbacks.h83
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBCursor.cpp508
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBCursor.h199
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBCursor.idl18
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBCursorBackend.cpp109
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBCursorBackend.h98
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBCursorBackendOperations.cpp86
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBCursorBackendOperations.h88
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBCursorDirection.h34
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBCursorDirection.idl33
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBCursorWithValue.cpp55
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBCursorWithValue.h60
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBCursorWithValue.idl4
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBDatabase.cpp626
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBDatabase.h197
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBDatabase.idl29
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBDatabaseBackend.cpp623
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBDatabaseBackend.h174
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBDatabaseCallbacks.h57
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBDatabaseCallbacksImpl.cpp82
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBDatabaseCallbacksImpl.h64
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBDatabaseError.h16
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBDatabaseException.cpp12
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBDatabaseException.h11
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBDatabaseIdentifier.cpp84
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBDatabaseIdentifier.h153
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBDatabaseMetadata.h85
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBEventDispatcher.cpp28
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBEventDispatcher.h10
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBFactory.cpp213
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBFactory.h99
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBFactory.idl10
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBFactoryBackendInterface.h74
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBGetAllResult.cpp118
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBGetAllResult.h132
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBGetResult.cpp62
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBGetResult.h119
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBIndex.cpp451
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBIndex.h161
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBIndex.idl38
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBIndexMetadata.h68
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBKey.cpp115
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBKey.h172
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBKeyData.cpp446
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBKeyData.h271
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBKeyPath.cpp183
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBKeyPath.h73
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBKeyRange.cpp140
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBKeyRange.h70
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBKeyRange.idl16
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBKeyRangeData.cpp79
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBKeyRangeData.h69
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBObjectStore.cpp969
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBObjectStore.h220
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBObjectStore.idl55
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBObjectStoreMetadata.h69
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.cpp268
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.h86
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.idl9
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBPendingTransactionMonitor.cpp70
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBPendingTransactionMonitor.h57
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBRecordIdentifier.h18
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBRequest.cpp755
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBRequest.h249
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBRequest.idl27
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBRequestCompletionEvent.cpp41
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBRequestCompletionEvent.h (renamed from Source/WebCore/Modules/indexeddb/IDBPendingDeleteCall.h)28
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBServerConnection.h99
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBTransaction.cpp1492
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBTransaction.h375
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBTransaction.idl21
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBTransactionBackend.cpp359
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBTransactionBackend.h128
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBTransactionBackendOperations.cpp282
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBTransactionBackendOperations.h471
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBTransactionCoordinator.cpp152
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBTransactionCoordinator.h72
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBTransactionMode.h40
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBTransactionMode.idl (renamed from Source/WebCore/Modules/indexeddb/IDBAny.idl)11
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBValue.cpp91
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBValue.h87
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.cpp52
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.h89
-rw-r--r--Source/WebCore/Modules/indexeddb/IDBVersionChangeEvent.idl8
-rw-r--r--Source/WebCore/Modules/indexeddb/IndexedDB.h68
-rw-r--r--Source/WebCore/Modules/indexeddb/PageGroupIndexedDatabase.cpp72
-rw-r--r--Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.cpp44
-rw-r--r--Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.h35
-rw-r--r--Source/WebCore/Modules/indexeddb/WorkerGlobalScopeIndexedDatabase.idl33
-rw-r--r--Source/WebCore/Modules/indexeddb/client/IDBConnectionProxy.cpp562
-rw-r--r--Source/WebCore/Modules/indexeddb/client/IDBConnectionProxy.h177
-rw-r--r--Source/WebCore/Modules/indexeddb/client/IDBConnectionToServer.cpp446
-rw-r--r--Source/WebCore/Modules/indexeddb/client/IDBConnectionToServer.h155
-rw-r--r--Source/WebCore/Modules/indexeddb/client/IDBConnectionToServerDelegate.h98
-rw-r--r--Source/WebCore/Modules/indexeddb/client/TransactionOperation.cpp53
-rw-r--r--Source/WebCore/Modules/indexeddb/client/TransactionOperation.h270
-rw-r--r--Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreCursorLevelDB.cpp202
-rw-r--r--Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreCursorLevelDB.h100
-rw-r--r--Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreLevelDB.cpp1931
-rw-r--r--Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreLevelDB.h143
-rw-r--r--Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreTransactionLevelDB.cpp86
-rw-r--r--Source/WebCore/Modules/indexeddb/leveldb/IDBFactoryBackendLevelDB.cpp197
-rw-r--r--Source/WebCore/Modules/indexeddb/leveldb/IDBFactoryBackendLevelDB.h87
-rw-r--r--Source/WebCore/Modules/indexeddb/leveldb/IDBIndexWriterLevelDB.cpp94
-rw-r--r--Source/WebCore/Modules/indexeddb/leveldb/IDBIndexWriterLevelDB.h66
-rw-r--r--Source/WebCore/Modules/indexeddb/leveldb/IDBLevelDBCoding.cpp1807
-rw-r--r--Source/WebCore/Modules/indexeddb/leveldb/IDBLevelDBCoding.h367
-rw-r--r--Source/WebCore/Modules/indexeddb/leveldb/IDBServerConnectionLevelDB.cpp626
-rw-r--r--Source/WebCore/Modules/indexeddb/leveldb/IDBServerConnectionLevelDB.h100
-rw-r--r--Source/WebCore/Modules/indexeddb/server/IDBBackingStore.h106
-rw-r--r--Source/WebCore/Modules/indexeddb/server/IDBConnectionToClient.cpp192
-rw-r--r--Source/WebCore/Modules/indexeddb/server/IDBConnectionToClient.h92
-rw-r--r--Source/WebCore/Modules/indexeddb/server/IDBConnectionToClientDelegate.h82
-rw-r--r--Source/WebCore/Modules/indexeddb/server/IDBSerialization.cpp420
-rw-r--r--Source/WebCore/Modules/indexeddb/server/IDBSerialization.h45
-rw-r--r--Source/WebCore/Modules/indexeddb/server/IDBServer.cpp682
-rw-r--r--Source/WebCore/Modules/indexeddb/server/IDBServer.h148
-rw-r--r--Source/WebCore/Modules/indexeddb/server/IndexValueEntry.cpp232
-rw-r--r--Source/WebCore/Modules/indexeddb/server/IndexValueEntry.h102
-rw-r--r--Source/WebCore/Modules/indexeddb/server/IndexValueStore.cpp419
-rw-r--r--Source/WebCore/Modules/indexeddb/server/IndexValueStore.h118
-rw-r--r--Source/WebCore/Modules/indexeddb/server/MemoryBackingStoreTransaction.cpp296
-rw-r--r--Source/WebCore/Modules/indexeddb/server/MemoryBackingStoreTransaction.h107
-rw-r--r--Source/WebCore/Modules/indexeddb/server/MemoryCursor.cpp66
-rw-r--r--Source/WebCore/Modules/indexeddb/server/MemoryCursor.h58
-rw-r--r--Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.cpp599
-rw-r--r--Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.h101
-rw-r--r--Source/WebCore/Modules/indexeddb/server/MemoryIndex.cpp277
-rw-r--r--Source/WebCore/Modules/indexeddb/server/MemoryIndex.h112
-rw-r--r--Source/WebCore/Modules/indexeddb/server/MemoryIndexCursor.cpp228
-rw-r--r--Source/WebCore/Modules/indexeddb/server/MemoryIndexCursor.h61
-rw-r--r--Source/WebCore/Modules/indexeddb/server/MemoryObjectStore.cpp522
-rw-r--r--Source/WebCore/Modules/indexeddb/server/MemoryObjectStore.h137
-rw-r--r--Source/WebCore/Modules/indexeddb/server/MemoryObjectStoreCursor.cpp356
-rw-r--r--Source/WebCore/Modules/indexeddb/server/MemoryObjectStoreCursor.h74
-rw-r--r--Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp2601
-rw-r--r--Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h202
-rw-r--r--Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.cpp580
-rw-r--r--Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.h132
-rw-r--r--Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.cpp233
-rw-r--r--Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.h97
-rw-r--r--Source/WebCore/Modules/indexeddb/server/ServerOpenDBRequest.cpp91
-rw-r--r--Source/WebCore/Modules/indexeddb/server/ServerOpenDBRequest.h78
-rw-r--r--Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.cpp1908
-rw-r--r--Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.h281
-rw-r--r--Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseConnection.cpp236
-rw-r--r--Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseConnection.h102
-rw-r--r--Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseTransaction.cpp375
-rw-r--r--Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseTransaction.h105
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBCursorInfo.cpp114
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBCursorInfo.h137
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBCursorRecord.h67
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBDatabaseInfo.cpp178
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBDatabaseInfo.h111
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBError.cpp73
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBError.h87
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBGetAllRecordsData.cpp51
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBGetAllRecordsData.h88
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBGetRecordData.cpp49
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBGetRecordData.h (renamed from Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreTransactionLevelDB.h)65
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBIndexInfo.cpp70
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBIndexInfo.h103
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBIterateCursorData.cpp47
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBIterateCursorData.h78
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBObjectStoreInfo.cpp163
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBObjectStoreInfo.h115
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBRequestData.cpp160
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBRequestData.h175
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBResourceIdentifier.cpp108
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBResourceIdentifier.h150
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBResultData.cpp244
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBResultData.h231
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBTransactionInfo.cpp121
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IDBTransactionInfo.h130
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.cpp451
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.h131
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IndexKey.cpp (renamed from Source/WebCore/Modules/indexeddb/IDBDatabaseMetadata.cpp)72
-rw-r--r--Source/WebCore/Modules/indexeddb/shared/IndexKey.h (renamed from Source/WebCore/Modules/indexeddb/IDBOperation.h)32
-rw-r--r--Source/WebCore/Modules/mediacontrols/MediaControlsHost.cpp294
-rw-r--r--Source/WebCore/Modules/mediacontrols/MediaControlsHost.h101
-rw-r--r--Source/WebCore/Modules/mediacontrols/MediaControlsHost.idl65
-rw-r--r--Source/WebCore/Modules/mediacontrols/assets-apple-iOS.svg52
-rw-r--r--Source/WebCore/Modules/mediacontrols/mediaControlsApple.css1153
-rw-r--r--Source/WebCore/Modules/mediacontrols/mediaControlsApple.js2509
-rw-r--r--Source/WebCore/Modules/mediacontrols/mediaControlsBase.css758
-rw-r--r--Source/WebCore/Modules/mediacontrols/mediaControlsBase.js1336
-rw-r--r--Source/WebCore/Modules/mediacontrols/mediaControlsGtk.js258
-rw-r--r--Source/WebCore/Modules/mediacontrols/mediaControlsiOS.css733
-rw-r--r--Source/WebCore/Modules/mediacontrols/mediaControlsiOS.js628
-rw-r--r--Source/WebCore/Modules/mediasession/HTMLMediaElementMediaSession.cpp59
-rw-r--r--Source/WebCore/Modules/mediasession/HTMLMediaElementMediaSession.h45
-rw-r--r--Source/WebCore/Modules/mediasession/HTMLMediaElementMediaSession.idl (renamed from Source/WebCore/Modules/mediastream/MediaStreamTrackSourcesCallback.idl)16
-rw-r--r--Source/WebCore/Modules/mediasession/MediaRemoteControls.cpp74
-rw-r--r--Source/WebCore/Modules/mediasession/MediaRemoteControls.h76
-rw-r--r--Source/WebCore/Modules/mediasession/MediaRemoteControls.idl (renamed from Source/WebCore/Modules/mediastream/VideoStreamTrack.idl)18
-rw-r--r--Source/WebCore/Modules/mediasession/MediaSession.cpp282
-rw-r--r--Source/WebCore/Modules/mediasession/MediaSession.h111
-rw-r--r--Source/WebCore/Modules/mediasession/MediaSession.idl53
-rw-r--r--Source/WebCore/Modules/mediasession/MediaSessionEvents.h40
-rw-r--r--Source/WebCore/Modules/mediasession/MediaSessionManager.cpp171
-rw-r--r--Source/WebCore/Modules/mediasession/MediaSessionManager.h66
-rw-r--r--Source/WebCore/Modules/mediasession/MediaSessionMetadata.h58
-rw-r--r--Source/WebCore/Modules/mediasession/WebMediaSessionManager.cpp469
-rw-r--r--Source/WebCore/Modules/mediasession/WebMediaSessionManager.h113
-rw-r--r--Source/WebCore/Modules/mediasession/WebMediaSessionManagerClient.h47
-rw-r--r--Source/WebCore/Modules/mediasource/AudioTrackMediaSource.h (renamed from Source/WebCore/Modules/webdatabase/DatabaseError.h)21
-rw-r--r--Source/WebCore/Modules/mediasource/AudioTrackMediaSource.idl4
-rw-r--r--Source/WebCore/Modules/mediasource/DOMURLMediaSource.cpp5
-rw-r--r--Source/WebCore/Modules/mediasource/DOMURLMediaSource.h9
-rw-r--r--Source/WebCore/Modules/mediasource/DOMURLMediaSource.idl2
-rw-r--r--Source/WebCore/Modules/mediasource/MediaSource.cpp786
-rw-r--r--Source/WebCore/Modules/mediasource/MediaSource.h134
-rw-r--r--Source/WebCore/Modules/mediasource/MediaSource.idl27
-rw-r--r--Source/WebCore/Modules/mediasource/MediaSourceRegistry.cpp14
-rw-r--r--Source/WebCore/Modules/mediasource/MediaSourceRegistry.h15
-rw-r--r--Source/WebCore/Modules/mediasource/SampleMap.cpp219
-rw-r--r--Source/WebCore/Modules/mediasource/SampleMap.h120
-rw-r--r--Source/WebCore/Modules/mediasource/SourceBuffer.cpp1651
-rw-r--r--Source/WebCore/Modules/mediasource/SourceBuffer.h198
-rw-r--r--Source/WebCore/Modules/mediasource/SourceBuffer.idl41
-rw-r--r--Source/WebCore/Modules/mediasource/SourceBufferList.cpp31
-rw-r--r--Source/WebCore/Modules/mediasource/SourceBufferList.h30
-rw-r--r--Source/WebCore/Modules/mediasource/SourceBufferList.idl7
-rw-r--r--Source/WebCore/Modules/mediasource/TextTrackMediaSource.h (renamed from Source/WebCore/Modules/webdatabase/AbstractSQLStatement.h)22
-rw-r--r--Source/WebCore/Modules/mediasource/TextTrackMediaSource.idl4
-rw-r--r--Source/WebCore/Modules/mediasource/VideoPlaybackQuality.cpp4
-rw-r--r--Source/WebCore/Modules/mediasource/VideoPlaybackQuality.h9
-rw-r--r--Source/WebCore/Modules/mediasource/VideoPlaybackQuality.idl4
-rw-r--r--Source/WebCore/Modules/mediasource/VideoTrackMediaSource.h (renamed from Source/WebCore/Modules/webdatabase/DatabaseBase.h)23
-rw-r--r--Source/WebCore/Modules/mediasource/VideoTrackMediaSource.idl4
-rw-r--r--Source/WebCore/Modules/mediastream/AllAudioCapabilities.h59
-rw-r--r--Source/WebCore/Modules/mediastream/AllVideoCapabilities.h57
-rw-r--r--Source/WebCore/Modules/mediastream/AllVideoCapabilities.idl38
-rw-r--r--Source/WebCore/Modules/mediastream/AudioStreamTrack.cpp71
-rw-r--r--Source/WebCore/Modules/mediastream/AudioStreamTrack.h59
-rw-r--r--Source/WebCore/Modules/mediastream/AudioStreamTrack.idl31
-rw-r--r--Source/WebCore/Modules/mediastream/CapabilityRange.cpp92
-rw-r--r--Source/WebCore/Modules/mediastream/CapabilityRange.h60
-rw-r--r--Source/WebCore/Modules/mediastream/DOMURLMediaStream.cpp5
-rw-r--r--Source/WebCore/Modules/mediastream/DOMURLMediaStream.h7
-rw-r--r--Source/WebCore/Modules/mediastream/DOMURLMediaStream.idl2
-rw-r--r--Source/WebCore/Modules/mediastream/DoubleRange.h (renamed from Source/WebCore/Modules/mediastream/RTCStatsResponse.idl)23
-rw-r--r--Source/WebCore/Modules/mediastream/DoubleRange.idl (renamed from Source/WebCore/Modules/mediastream/RTCStatsCallback.idl)9
-rw-r--r--Source/WebCore/Modules/mediastream/HTMLMediaElementMediaStream.cpp50
-rw-r--r--Source/WebCore/Modules/mediastream/HTMLMediaElementMediaStream.h48
-rw-r--r--Source/WebCore/Modules/mediastream/LongRange.h (renamed from Source/WebCore/Modules/mediastream/NavigatorUserMediaErrorCallback.idl)20
-rw-r--r--Source/WebCore/Modules/mediastream/LongRange.idl (renamed from Source/WebCore/Modules/mediastream/NavigatorUserMediaSuccessCallback.idl)9
-rw-r--r--Source/WebCore/Modules/mediastream/MediaConstraintsImpl.cpp119
-rw-r--r--Source/WebCore/Modules/mediastream/MediaConstraintsImpl.h54
-rw-r--r--Source/WebCore/Modules/mediastream/MediaDeviceInfo.cpp (renamed from Source/WebCore/Modules/webdatabase/DatabaseBase.cpp)31
-rw-r--r--Source/WebCore/Modules/mediastream/MediaDeviceInfo.h (renamed from Source/WebCore/Modules/webdatabase/DatabaseBackendContext.h)46
-rw-r--r--Source/WebCore/Modules/mediastream/MediaDeviceInfo.idl42
-rw-r--r--Source/WebCore/Modules/mediastream/MediaDevices.cpp108
-rw-r--r--Source/WebCore/Modules/mediastream/MediaDevices.h (renamed from Source/WebCore/Modules/mediastream/RTCSessionDescriptionRequestImpl.h)53
-rw-r--r--Source/WebCore/Modules/mediastream/MediaDevices.idl46
-rw-r--r--Source/WebCore/Modules/mediastream/MediaDevicesEnumerationRequest.cpp113
-rw-r--r--Source/WebCore/Modules/mediastream/MediaDevicesEnumerationRequest.h71
-rw-r--r--Source/WebCore/Modules/mediastream/MediaDevicesRequest.cpp153
-rw-r--r--Source/WebCore/Modules/mediastream/MediaDevicesRequest.h (renamed from Source/WebCore/Modules/mediastream/MediaTrackConstraint.h)49
-rw-r--r--Source/WebCore/Modules/mediastream/MediaEndpointPeerConnection.cpp874
-rw-r--r--Source/WebCore/Modules/mediastream/MediaEndpointPeerConnection.h132
-rw-r--r--Source/WebCore/Modules/mediastream/MediaEndpointSessionDescription.cpp116
-rw-r--r--Source/WebCore/Modules/mediastream/MediaEndpointSessionDescription.h75
-rw-r--r--Source/WebCore/Modules/mediastream/MediaSourceStates.cpp59
-rw-r--r--Source/WebCore/Modules/mediastream/MediaSourceStates.h63
-rw-r--r--Source/WebCore/Modules/mediastream/MediaSourceStates.idl44
-rw-r--r--Source/WebCore/Modules/mediastream/MediaStream.cpp473
-rw-r--r--Source/WebCore/Modules/mediastream/MediaStream.h124
-rw-r--r--Source/WebCore/Modules/mediastream/MediaStream.idl34
-rw-r--r--Source/WebCore/Modules/mediastream/MediaStreamCapabilities.cpp142
-rw-r--r--Source/WebCore/Modules/mediastream/MediaStreamCapabilities.h69
-rw-r--r--Source/WebCore/Modules/mediastream/MediaStreamEvent.cpp35
-rw-r--r--Source/WebCore/Modules/mediastream/MediaStreamEvent.h29
-rw-r--r--Source/WebCore/Modules/mediastream/MediaStreamEvent.idl10
-rw-r--r--Source/WebCore/Modules/mediastream/MediaStreamRegistry.cpp52
-rw-r--r--Source/WebCore/Modules/mediastream/MediaStreamRegistry.h26
-rw-r--r--Source/WebCore/Modules/mediastream/MediaStreamTrack.cpp387
-rw-r--r--Source/WebCore/Modules/mediastream/MediaStreamTrack.h147
-rw-r--r--Source/WebCore/Modules/mediastream/MediaStreamTrack.idl80
-rw-r--r--Source/WebCore/Modules/mediastream/MediaStreamTrackEvent.cpp31
-rw-r--r--Source/WebCore/Modules/mediastream/MediaStreamTrackEvent.h27
-rw-r--r--Source/WebCore/Modules/mediastream/MediaStreamTrackEvent.idl7
-rw-r--r--Source/WebCore/Modules/mediastream/MediaStreamTrackSourcesCallback.h46
-rw-r--r--Source/WebCore/Modules/mediastream/MediaStreamTrackSourcesRequest.cpp72
-rw-r--r--Source/WebCore/Modules/mediastream/MediaStreamTrackSourcesRequest.h65
-rw-r--r--Source/WebCore/Modules/mediastream/MediaTrackConstraint.cpp53
-rw-r--r--Source/WebCore/Modules/mediastream/MediaTrackConstraintSet.cpp53
-rw-r--r--Source/WebCore/Modules/mediastream/MediaTrackConstraintSet.h56
-rw-r--r--Source/WebCore/Modules/mediastream/MediaTrackConstraints.cpp207
-rw-r--r--Source/WebCore/Modules/mediastream/MediaTrackConstraints.h100
-rw-r--r--Source/WebCore/Modules/mediastream/MediaTrackConstraints.idl100
-rw-r--r--Source/WebCore/Modules/mediastream/MediaTrackSupportedConstraints.h53
-rw-r--r--Source/WebCore/Modules/mediastream/MediaTrackSupportedConstraints.idl46
-rw-r--r--Source/WebCore/Modules/mediastream/NavigatorMediaDevices.cpp (renamed from Source/WebCore/Modules/mediastream/RTCVoidRequestImpl.cpp)62
-rw-r--r--Source/WebCore/Modules/mediastream/NavigatorMediaDevices.h (renamed from Source/WebCore/Modules/mediastream/RTCVoidRequestImpl.h)42
-rw-r--r--Source/WebCore/Modules/mediastream/NavigatorMediaDevices.idl (renamed from Source/WebCore/Modules/mediastream/RTCSessionDescriptionCallback.idl)10
-rw-r--r--Source/WebCore/Modules/mediastream/NavigatorMediaStream.cpp71
-rw-r--r--Source/WebCore/Modules/mediastream/NavigatorMediaStream.h50
-rw-r--r--Source/WebCore/Modules/mediastream/NavigatorUserMedia.idl36
-rw-r--r--Source/WebCore/Modules/mediastream/NavigatorUserMedia.js49
-rw-r--r--Source/WebCore/Modules/mediastream/NavigatorUserMediaError.cpp51
-rw-r--r--Source/WebCore/Modules/mediastream/NavigatorUserMediaError.idl33
-rw-r--r--Source/WebCore/Modules/mediastream/NavigatorUserMediaErrorCallback.h45
-rw-r--r--Source/WebCore/Modules/mediastream/NavigatorUserMediaSuccessCallback.h46
-rw-r--r--Source/WebCore/Modules/mediastream/OverconstrainedError.h (renamed from Source/WebCore/Modules/indexeddb/IDBFactoryBackendInterface.cpp)41
-rw-r--r--Source/WebCore/Modules/mediastream/OverconstrainedError.idl (renamed from Source/WebCore/Modules/indexeddb/IDBHistograms.h)29
-rw-r--r--Source/WebCore/Modules/mediastream/OverconstrainedErrorEvent.h77
-rw-r--r--Source/WebCore/Modules/mediastream/OverconstrainedErrorEvent.idl38
-rw-r--r--Source/WebCore/Modules/mediastream/PeerConnectionBackend.cpp312
-rw-r--r--Source/WebCore/Modules/mediastream/PeerConnectionBackend.h146
-rw-r--r--Source/WebCore/Modules/mediastream/RTCConfiguration.h (renamed from Source/WebCore/Modules/mediastream/RTCSessionDescriptionCallback.h)26
-rw-r--r--Source/WebCore/Modules/mediastream/RTCConfiguration.idl36
-rw-r--r--Source/WebCore/Modules/mediastream/RTCDTMFSender.cpp105
-rw-r--r--Source/WebCore/Modules/mediastream/RTCDTMFSender.h69
-rw-r--r--Source/WebCore/Modules/mediastream/RTCDTMFSender.idl20
-rw-r--r--Source/WebCore/Modules/mediastream/RTCDTMFToneChangeEvent.cpp26
-rw-r--r--Source/WebCore/Modules/mediastream/RTCDTMFToneChangeEvent.h26
-rw-r--r--Source/WebCore/Modules/mediastream/RTCDTMFToneChangeEvent.idl12
-rw-r--r--Source/WebCore/Modules/mediastream/RTCDataChannel.cpp190
-rw-r--r--Source/WebCore/Modules/mediastream/RTCDataChannel.h118
-rw-r--r--Source/WebCore/Modules/mediastream/RTCDataChannel.idl32
-rw-r--r--Source/WebCore/Modules/mediastream/RTCDataChannelEvent.cpp31
-rw-r--r--Source/WebCore/Modules/mediastream/RTCDataChannelEvent.h21
-rw-r--r--Source/WebCore/Modules/mediastream/RTCDataChannelEvent.idl2
-rw-r--r--Source/WebCore/Modules/mediastream/RTCIceCandidate.cpp79
-rw-r--r--Source/WebCore/Modules/mediastream/RTCIceCandidate.h41
-rw-r--r--Source/WebCore/Modules/mediastream/RTCIceCandidate.idl21
-rw-r--r--Source/WebCore/Modules/mediastream/RTCIceCandidateEvent.cpp21
-rw-r--r--Source/WebCore/Modules/mediastream/RTCIceCandidateEvent.h15
-rw-r--r--Source/WebCore/Modules/mediastream/RTCIceCandidateEvent.idl2
-rw-r--r--Source/WebCore/Modules/mediastream/RTCIceServer.h (renamed from Source/WebCore/Modules/webdatabase/DatabaseBasicTypes.h)25
-rw-r--r--Source/WebCore/Modules/mediastream/RTCIceServer.idl33
-rw-r--r--Source/WebCore/Modules/mediastream/RTCIceTransport.h69
-rw-r--r--Source/WebCore/Modules/mediastream/RTCOfferAnswerOptions.h48
-rw-r--r--Source/WebCore/Modules/mediastream/RTCPeerConnection.cpp736
-rw-r--r--Source/WebCore/Modules/mediastream/RTCPeerConnection.h175
-rw-r--r--Source/WebCore/Modules/mediastream/RTCPeerConnection.idl150
-rw-r--r--Source/WebCore/Modules/mediastream/RTCPeerConnection.js268
-rw-r--r--Source/WebCore/Modules/mediastream/RTCPeerConnectionInternals.js145
-rw-r--r--Source/WebCore/Modules/mediastream/RTCRtpReceiver.cpp (renamed from Source/WebCore/Modules/mediastream/RTCPeerConnectionErrorCallback.h)33
-rw-r--r--Source/WebCore/Modules/mediastream/RTCRtpReceiver.h57
-rw-r--r--Source/WebCore/Modules/mediastream/RTCRtpReceiver.idl (renamed from Source/WebCore/Modules/mediastream/RTCPeerConnectionErrorCallback.idl)19
-rw-r--r--Source/WebCore/Modules/mediastream/RTCRtpSender.cpp87
-rw-r--r--Source/WebCore/Modules/mediastream/RTCRtpSender.h75
-rw-r--r--Source/WebCore/Modules/mediastream/RTCRtpSender.idl38
-rw-r--r--Source/WebCore/Modules/mediastream/RTCRtpSenderReceiverBase.h62
-rw-r--r--Source/WebCore/Modules/mediastream/RTCRtpTransceiver.cpp110
-rw-r--r--Source/WebCore/Modules/mediastream/RTCRtpTransceiver.h113
-rw-r--r--Source/WebCore/Modules/mediastream/RTCRtpTransceiver.idl46
-rw-r--r--Source/WebCore/Modules/mediastream/RTCSessionDescription.cpp74
-rw-r--r--Source/WebCore/Modules/mediastream/RTCSessionDescription.h40
-rw-r--r--Source/WebCore/Modules/mediastream/RTCSessionDescription.idl21
-rw-r--r--Source/WebCore/Modules/mediastream/RTCSessionDescriptionRequestImpl.cpp95
-rw-r--r--Source/WebCore/Modules/mediastream/RTCStatsCallback.h46
-rw-r--r--Source/WebCore/Modules/mediastream/RTCStatsReport.cpp43
-rw-r--r--Source/WebCore/Modules/mediastream/RTCStatsReport.h35
-rw-r--r--Source/WebCore/Modules/mediastream/RTCStatsReport.idl12
-rw-r--r--Source/WebCore/Modules/mediastream/RTCStatsRequestImpl.cpp92
-rw-r--r--Source/WebCore/Modules/mediastream/RTCStatsRequestImpl.h68
-rw-r--r--Source/WebCore/Modules/mediastream/RTCStatsResponse.cpp69
-rw-r--r--Source/WebCore/Modules/mediastream/RTCStatsResponse.h63
-rw-r--r--Source/WebCore/Modules/mediastream/RTCTrackEvent.cpp72
-rw-r--r--Source/WebCore/Modules/mediastream/RTCTrackEvent.h78
-rw-r--r--Source/WebCore/Modules/mediastream/RTCTrackEvent.idl47
-rw-r--r--Source/WebCore/Modules/mediastream/SDPProcessor.cpp547
-rw-r--r--Source/WebCore/Modules/mediastream/SDPProcessor.h78
-rw-r--r--Source/WebCore/Modules/mediastream/SourceInfo.cpp64
-rw-r--r--Source/WebCore/Modules/mediastream/SourceInfo.h59
-rw-r--r--Source/WebCore/Modules/mediastream/UserMediaClient.h18
-rw-r--r--Source/WebCore/Modules/mediastream/UserMediaController.cpp7
-rw-r--r--Source/WebCore/Modules/mediastream/UserMediaController.h40
-rw-r--r--Source/WebCore/Modules/mediastream/UserMediaRequest.cpp264
-rw-r--r--Source/WebCore/Modules/mediastream/UserMediaRequest.h83
-rw-r--r--Source/WebCore/Modules/mediastream/VideoStreamTrack.cpp71
-rw-r--r--Source/WebCore/Modules/mediastream/VideoStreamTrack.h59
-rw-r--r--Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCDataChannelHandler.cpp114
-rw-r--r--Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCDataChannelHandler.h (renamed from Source/WebCore/Modules/mediastream/NavigatorUserMediaError.h)56
-rw-r--r--Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.cpp527
-rw-r--r--Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.h175
-rw-r--r--Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.cpp231
-rw-r--r--Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.h97
-rw-r--r--Source/WebCore/Modules/mediastream/sdp.js606
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/airplay-button.css29
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/airplay-button.js38
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/airplay-placard.js39
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/background-tint.css48
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/background-tint.js34
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/button.css34
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/button.js83
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/buttons-container.css29
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/buttons-container.js78
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/controls-bar.css35
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/controls-bar.js248
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/forward-button.js38
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/fullscreen-button.js39
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/icon-button.css33
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/icon-button.js134
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/icon-service.js102
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/invalid-placard.js (renamed from Source/WebCore/Modules/mediastream/MediaTrackConstraintSet.idl)24
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/ios-inline-media-controls.css132
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/ios-inline-media-controls.js109
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/layout-item.js53
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/layout-node.js307
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/macos-compact-inline-media-controls.css71
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/macos-fullscreen-media-controls.css119
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/macos-fullscreen-media-controls.js173
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/macos-inline-media-controls.css118
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/macos-inline-media-controls.js202
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/macos-media-controls.css51
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/macos-media-controls.js81
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/media-controls.css77
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/media-controls.js148
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/mute-button.js53
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/pip-button.js38
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/pip-placard.js38
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/placard.css70
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/placard.js48
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/play-pause-button.js53
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/rewind-button.js (renamed from Source/WebCore/Modules/mediastream/HTMLMediaElementMediaStream.idl)25
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/scheduler.js66
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/scrubber.js156
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/seek-button.js73
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/skip-back-button.js38
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/slider.css47
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/slider.js152
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/start-button.css58
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/start-button.js42
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/status-label.css37
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/status-label.js85
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/text-tracks.css82
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/time-control.js93
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/time-label.css33
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/time-label.js80
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/tracks-button.js38
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.css105
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.js305
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/volume-down-button.js38
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/volume-slider.css30
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/volume-slider.js120
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/volume-up-button.js38
-rw-r--r--Source/WebCore/Modules/modern-media-controls/gesture-recognizers/gesture-recognizer.js379
-rw-r--r--Source/WebCore/Modules/modern-media-controls/gesture-recognizers/pinch.js205
-rw-r--r--Source/WebCore/Modules/modern-media-controls/gesture-recognizers/tap.js109
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@1x.pngbin0 -> 2945 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@2x.pngbin0 -> 3210 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@3x.pngbin0 -> 3553 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/airplay@1x.pngbin0 -> 330 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/airplay@2x.pngbin0 -> 362 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@1x.pngbin0 -> 426 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@2x.pngbin0 -> 653 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@3x.pngbin0 -> 958 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@1x.pngbin0 -> 931 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@2x.pngbin0 -> 1705 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@3x.pngbin0 -> 2250 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@1x.pngbin0 -> 1007 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@2x.pngbin0 -> 1831 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@3x.pngbin0 -> 2403 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/pause@1x.pngbin0 -> 1277 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/pause@2x.pngbin0 -> 1375 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/pause@3x.pngbin0 -> 1459 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@1x.pngbin0 -> 351 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@2x.pngbin0 -> 399 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@3x.pngbin0 -> 487 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@1x.pngbin0 -> 2243 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@2x.pngbin0 -> 4106 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@3x.pngbin0 -> 6281 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/play@1x.pngbin0 -> 546 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/play@2x.pngbin0 -> 778 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/play@3x.pngbin0 -> 1013 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/slider-thumb@2x.pngbin0 -> 1475 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/start@1x.pngbin0 -> 592 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/start@2x.pngbin0 -> 1135 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/iOS/start@3x.pngbin0 -> 1545 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-fullscreen@1x.pngbin0 -> 294 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-fullscreen@2x.pngbin0 -> 431 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-placard@1x.pngbin0 -> 2945 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-placard@2x.pngbin0 -> 3210 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/airplay@1x.pngbin0 -> 330 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/airplay@2x.pngbin0 -> 362 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen-compact@1x.pngbin0 -> 263 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen-compact@2x.pngbin0 -> 560 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen@1x.pngbin0 -> 426 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen@2x.pngbin0 -> 653 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/exit-fullscreen@1x.pngbin0 -> 363 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/exit-fullscreen@2x.pngbin0 -> 506 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/forward@1x.pngbin0 -> 569 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/forward@2x.pngbin0 -> 969 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back-compact@1x.pngbin0 -> 535 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back-compact@2x.pngbin0 -> 1304 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back@1x.pngbin0 -> 931 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back@2x.pngbin0 -> 1705 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/invalid-placard@1x.pngbin0 -> 1007 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/invalid-placard@2x.pngbin0 -> 1831 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection-fullscreen@1x.pngbin0 -> 333 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection-fullscreen@2x.pngbin0 -> 484 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection@1x.pngbin0 -> 351 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection@2x.pngbin0 -> 581 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/pause-compact@1x.pngbin0 -> 1233 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/pause-compact@2x.pngbin0 -> 1307 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/pause-fullscreen@1x.pngbin0 -> 1277 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/pause-fullscreen@2x.pngbin0 -> 1375 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/pause@1x.pngbin0 -> 1275 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/pause@2x.pngbin0 -> 1373 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in-fullscreen@1x.pngbin0 -> 233 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in-fullscreen@2x.pngbin0 -> 387 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in@1x.pngbin0 -> 351 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in@2x.pngbin0 -> 399 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/pip-placard@1x.pngbin0 -> 2243 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/pip-placard@2x.pngbin0 -> 4106 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/play-compact@1x.pngbin0 -> 314 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/play-compact@2x.pngbin0 -> 545 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/play-fullscreen@1x.pngbin0 -> 546 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/play-fullscreen@2x.pngbin0 -> 778 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/play@1x.pngbin0 -> 546 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/play@2x.pngbin0 -> 778 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/rewind@1x.pngbin0 -> 595 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/rewind@2x.pngbin0 -> 881 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fill@1x.pngbin0 -> 362 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fill@2x.pngbin0 -> 342 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fit@1x.pngbin0 -> 205 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fit@2x.pngbin0 -> 206 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/start@1x.pngbin0 -> 592 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/start@2x.pngbin0 -> 1135 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/volume-compact@1x.pngbin0 -> 479 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/volume-compact@2x.pngbin0 -> 1093 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/volume-down-fullscreen@1x.pngbin0 -> 318 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/volume-down-fullscreen@2x.pngbin0 -> 623 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/volume-mute@1x.pngbin0 -> 936 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/volume-mute@2x.pngbin0 -> 1545 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/volume-up-fullscreen@1x.pngbin0 -> 577 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/volume-up-fullscreen@2x.pngbin0 -> 1250 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/volume@1x.pngbin0 -> 877 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/images/macOS/volume@2x.pngbin0 -> 2476 bytes
-rw-r--r--Source/WebCore/Modules/modern-media-controls/js-files63
-rw-r--r--Source/WebCore/Modules/modern-media-controls/main.js47
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/airplay-support.js60
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/controls-visibility-support.js77
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/fullscreen-support.js76
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/media-controller-support.js101
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/media-controller.js203
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/mute-support.js52
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/pip-support.js64
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/placard-support.js64
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/playback-support.js51
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/scrubbing-support.js79
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/seek-backward-support.js39
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/seek-forward-support.js39
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/seek-support.js77
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/skip-back-support.js54
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/start-support.js93
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/status-support.js59
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/time-labels-support.js51
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/tracks-support.js150
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/volume-down-support.js41
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/volume-support.js57
-rw-r--r--Source/WebCore/Modules/modern-media-controls/media/volume-up-support.js41
-rw-r--r--Source/WebCore/Modules/navigatorcontentutils/NavigatorContentUtils.cpp153
-rw-r--r--Source/WebCore/Modules/navigatorcontentutils/NavigatorContentUtils.h32
-rw-r--r--Source/WebCore/Modules/navigatorcontentutils/NavigatorContentUtils.idl7
-rw-r--r--Source/WebCore/Modules/navigatorcontentutils/NavigatorContentUtilsClient.h15
-rw-r--r--Source/WebCore/Modules/notifications/DOMWindowNotifications.cpp42
-rw-r--r--Source/WebCore/Modules/notifications/DOMWindowNotifications.h24
-rw-r--r--Source/WebCore/Modules/notifications/DOMWindowNotifications.idl4
-rw-r--r--Source/WebCore/Modules/notifications/Notification.cpp156
-rw-r--r--Source/WebCore/Modules/notifications/Notification.h153
-rw-r--r--Source/WebCore/Modules/notifications/Notification.idl62
-rw-r--r--Source/WebCore/Modules/notifications/NotificationCenter.cpp101
-rw-r--r--Source/WebCore/Modules/notifications/NotificationCenter.h63
-rw-r--r--Source/WebCore/Modules/notifications/NotificationCenter.idl11
-rw-r--r--Source/WebCore/Modules/notifications/NotificationClient.h24
-rw-r--r--Source/WebCore/Modules/notifications/NotificationController.cpp27
-rw-r--r--Source/WebCore/Modules/notifications/NotificationController.h24
-rw-r--r--Source/WebCore/Modules/notifications/NotificationPermissionCallback.h9
-rw-r--r--Source/WebCore/Modules/notifications/NotificationPermissionCallback.idl8
-rw-r--r--Source/WebCore/Modules/notifications/WorkerGlobalScopeNotifications.cpp26
-rw-r--r--Source/WebCore/Modules/notifications/WorkerGlobalScopeNotifications.h21
-rw-r--r--Source/WebCore/Modules/notifications/WorkerGlobalScopeNotifications.idl4
-rw-r--r--Source/WebCore/Modules/plugins/PluginReplacement.h38
-rw-r--r--Source/WebCore/Modules/plugins/QuickTimePluginReplacement.css33
-rw-r--r--Source/WebCore/Modules/plugins/QuickTimePluginReplacement.h70
-rw-r--r--Source/WebCore/Modules/plugins/QuickTimePluginReplacement.idl (renamed from Source/WebCore/Modules/mediastream/AllAudioCapabilities.idl)14
-rw-r--r--Source/WebCore/Modules/plugins/QuickTimePluginReplacement.js350
-rw-r--r--Source/WebCore/Modules/plugins/YouTubePluginReplacement.cpp354
-rw-r--r--Source/WebCore/Modules/plugins/YouTubePluginReplacement.h63
-rw-r--r--Source/WebCore/Modules/proximity/DeviceProximityClient.h4
-rw-r--r--Source/WebCore/Modules/proximity/DeviceProximityController.cpp9
-rw-r--r--Source/WebCore/Modules/proximity/DeviceProximityController.h12
-rw-r--r--Source/WebCore/Modules/proximity/DeviceProximityEvent.cpp10
-rw-r--r--Source/WebCore/Modules/proximity/DeviceProximityEvent.h40
-rw-r--r--Source/WebCore/Modules/proximity/DeviceProximityEvent.idl13
-rw-r--r--Source/WebCore/Modules/quota/DOMWindowQuota.cpp8
-rw-r--r--Source/WebCore/Modules/quota/DOMWindowQuota.h7
-rw-r--r--Source/WebCore/Modules/quota/DOMWindowQuota.idl4
-rw-r--r--Source/WebCore/Modules/quota/NavigatorStorageQuota.cpp5
-rw-r--r--Source/WebCore/Modules/quota/NavigatorStorageQuota.h7
-rw-r--r--Source/WebCore/Modules/quota/StorageErrorCallback.cpp16
-rw-r--r--Source/WebCore/Modules/quota/StorageErrorCallback.h15
-rw-r--r--Source/WebCore/Modules/quota/StorageErrorCallback.idl4
-rw-r--r--Source/WebCore/Modules/quota/StorageInfo.cpp12
-rw-r--r--Source/WebCore/Modules/quota/StorageInfo.h14
-rw-r--r--Source/WebCore/Modules/quota/StorageInfo.idl4
-rw-r--r--Source/WebCore/Modules/quota/StorageQuota.h14
-rw-r--r--Source/WebCore/Modules/quota/StorageQuota.idl4
-rw-r--r--Source/WebCore/Modules/quota/StorageQuotaCallback.h7
-rw-r--r--Source/WebCore/Modules/quota/StorageQuotaCallback.idl4
-rw-r--r--Source/WebCore/Modules/quota/StorageUsageCallback.h7
-rw-r--r--Source/WebCore/Modules/quota/StorageUsageCallback.idl4
-rw-r--r--Source/WebCore/Modules/quota/WorkerNavigatorStorageQuota.cpp5
-rw-r--r--Source/WebCore/Modules/quota/WorkerNavigatorStorageQuota.h7
-rw-r--r--Source/WebCore/Modules/speech/DOMWindowSpeechSynthesis.cpp (renamed from Source/WebCore/Modules/webdatabase/SQLTransactionSync.cpp)50
-rw-r--r--Source/WebCore/Modules/speech/DOMWindowSpeechSynthesis.h55
-rw-r--r--Source/WebCore/Modules/speech/DOMWindowSpeechSynthesis.idl (renamed from Source/WebCore/Modules/mediastream/MediaTrackConstraint.idl)14
-rw-r--r--Source/WebCore/Modules/speech/SpeechSynthesis.cpp242
-rw-r--r--Source/WebCore/Modules/speech/SpeechSynthesis.h100
-rw-r--r--Source/WebCore/Modules/speech/SpeechSynthesis.idl39
-rw-r--r--Source/WebCore/Modules/speech/SpeechSynthesisEvent.cpp (renamed from Source/WebCore/Modules/webdatabase/AbstractSQLStatementBackend.h)37
-rw-r--r--Source/WebCore/Modules/speech/SpeechSynthesisEvent.h (renamed from Source/WebCore/Modules/webdatabase/AbstractSQLTransaction.h)37
-rw-r--r--Source/WebCore/Modules/speech/SpeechSynthesisEvent.idl (renamed from Source/WebCore/Modules/mediastream/MediaStreamCapabilities.idl)17
-rw-r--r--Source/WebCore/Modules/speech/SpeechSynthesisUtterance.cpp (renamed from Source/WebCore/Modules/webdatabase/DatabaseBackendSync.cpp)52
-rw-r--r--Source/WebCore/Modules/speech/SpeechSynthesisUtterance.h84
-rw-r--r--Source/WebCore/Modules/speech/SpeechSynthesisUtterance.idl45
-rw-r--r--Source/WebCore/Modules/speech/SpeechSynthesisVoice.cpp45
-rw-r--r--Source/WebCore/Modules/speech/SpeechSynthesisVoice.h (renamed from Source/WebCore/Modules/webdatabase/AbstractSQLTransactionBackend.h)40
-rw-r--r--Source/WebCore/Modules/speech/SpeechSynthesisVoice.idl (renamed from Source/WebCore/Modules/mediastream/CapabilityRange.idl)18
-rw-r--r--Source/WebCore/Modules/streams/ByteLengthQueuingStrategy.idl37
-rw-r--r--Source/WebCore/Modules/streams/ByteLengthQueuingStrategy.js46
-rw-r--r--Source/WebCore/Modules/streams/CountQueuingStrategy.idl37
-rw-r--r--Source/WebCore/Modules/streams/CountQueuingStrategy.js45
-rw-r--r--Source/WebCore/Modules/streams/ReadableByteStreamController.idl43
-rw-r--r--Source/WebCore/Modules/streams/ReadableByteStreamController.js95
-rw-r--r--Source/WebCore/Modules/streams/ReadableByteStreamInternals.js330
-rw-r--r--Source/WebCore/Modules/streams/ReadableStream.idl45
-rw-r--r--Source/WebCore/Modules/streams/ReadableStream.js229
-rw-r--r--Source/WebCore/Modules/streams/ReadableStreamDefaultController.idl42
-rw-r--r--Source/WebCore/Modules/streams/ReadableStreamDefaultController.js82
-rw-r--r--Source/WebCore/Modules/streams/ReadableStreamDefaultReader.idl42
-rw-r--r--Source/WebCore/Modules/streams/ReadableStreamDefaultReader.js77
-rw-r--r--Source/WebCore/Modules/streams/ReadableStreamInternals.js503
-rw-r--r--Source/WebCore/Modules/streams/ReadableStreamSource.h121
-rw-r--r--Source/WebCore/Modules/streams/ReadableStreamSource.idl40
-rw-r--r--Source/WebCore/Modules/streams/StreamInternals.js129
-rw-r--r--Source/WebCore/Modules/streams/WritableStream.idl42
-rw-r--r--Source/WebCore/Modules/streams/WritableStream.js189
-rw-r--r--Source/WebCore/Modules/streams/WritableStreamInternals.js135
-rw-r--r--Source/WebCore/Modules/vibration/NavigatorVibration.cpp (renamed from Source/WebCore/Modules/battery/BatteryStatus.cpp)38
-rw-r--r--Source/WebCore/Modules/vibration/NavigatorVibration.h (renamed from Source/WebCore/Modules/battery/NavigatorBattery.h)33
-rw-r--r--Source/WebCore/Modules/vibration/NavigatorVibration.idl (renamed from Source/WebCore/Modules/battery/NavigatorBattery.idl)6
-rw-r--r--Source/WebCore/Modules/vibration/Vibration.cpp130
-rw-r--r--Source/WebCore/Modules/vibration/Vibration.h (renamed from Source/WebCore/Modules/battery/BatteryStatus.h)48
-rw-r--r--Source/WebCore/Modules/vibration/VibrationClient.h (renamed from Source/WebCore/Modules/battery/BatteryClient.h)24
-rw-r--r--Source/WebCore/Modules/webaudio/AnalyserNode.cpp35
-rw-r--r--Source/WebCore/Modules/webaudio/AnalyserNode.h40
-rw-r--r--Source/WebCore/Modules/webaudio/AnalyserNode.idl14
-rw-r--r--Source/WebCore/Modules/webaudio/AsyncAudioDecoder.cpp44
-rw-r--r--Source/WebCore/Modules/webaudio/AsyncAudioDecoder.h20
-rw-r--r--Source/WebCore/Modules/webaudio/AudioBasicInspectorNode.cpp28
-rw-r--r--Source/WebCore/Modules/webaudio/AudioBasicInspectorNode.h21
-rw-r--r--Source/WebCore/Modules/webaudio/AudioBasicProcessorNode.cpp4
-rw-r--r--Source/WebCore/Modules/webaudio/AudioBasicProcessorNode.h24
-rw-r--r--Source/WebCore/Modules/webaudio/AudioBuffer.cpp93
-rw-r--r--Source/WebCore/Modules/webaudio/AudioBuffer.h34
-rw-r--r--Source/WebCore/Modules/webaudio/AudioBuffer.idl11
-rw-r--r--Source/WebCore/Modules/webaudio/AudioBufferCallback.h7
-rw-r--r--Source/WebCore/Modules/webaudio/AudioBufferCallback.idl4
-rw-r--r--Source/WebCore/Modules/webaudio/AudioBufferSourceNode.cpp277
-rw-r--r--Source/WebCore/Modules/webaudio/AudioBufferSourceNode.h54
-rw-r--r--Source/WebCore/Modules/webaudio/AudioBufferSourceNode.idl22
-rw-r--r--Source/WebCore/Modules/webaudio/AudioContext.cpp712
-rw-r--r--Source/WebCore/Modules/webaudio/AudioContext.h251
-rw-r--r--Source/WebCore/Modules/webaudio/AudioContext.idl66
-rw-r--r--Source/WebCore/Modules/webaudio/AudioDestinationNode.cpp62
-rw-r--r--Source/WebCore/Modules/webaudio/AudioDestinationNode.h66
-rw-r--r--Source/WebCore/Modules/webaudio/AudioListener.cpp2
-rw-r--r--Source/WebCore/Modules/webaudio/AudioListener.h15
-rw-r--r--Source/WebCore/Modules/webaudio/AudioListener.idl12
-rw-r--r--Source/WebCore/Modules/webaudio/AudioNode.cpp205
-rw-r--r--Source/WebCore/Modules/webaudio/AudioNode.h41
-rw-r--r--Source/WebCore/Modules/webaudio/AudioNode.idl25
-rw-r--r--Source/WebCore/Modules/webaudio/AudioNodeInput.cpp24
-rw-r--r--Source/WebCore/Modules/webaudio/AudioNodeInput.h10
-rw-r--r--Source/WebCore/Modules/webaudio/AudioNodeOutput.cpp45
-rw-r--r--Source/WebCore/Modules/webaudio/AudioNodeOutput.h8
-rw-r--r--Source/WebCore/Modules/webaudio/AudioParam.cpp22
-rw-r--r--Source/WebCore/Modules/webaudio/AudioParam.h20
-rw-r--r--Source/WebCore/Modules/webaudio/AudioParam.idl25
-rw-r--r--Source/WebCore/Modules/webaudio/AudioParamTimeline.cpp28
-rw-r--r--Source/WebCore/Modules/webaudio/AudioParamTimeline.h18
-rw-r--r--Source/WebCore/Modules/webaudio/AudioProcessingEvent.cpp17
-rw-r--r--Source/WebCore/Modules/webaudio/AudioProcessingEvent.h23
-rw-r--r--Source/WebCore/Modules/webaudio/AudioProcessingEvent.idl1
-rw-r--r--Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.cpp113
-rw-r--r--Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.h51
-rw-r--r--Source/WebCore/Modules/webaudio/AudioSummingJunction.cpp19
-rw-r--r--Source/WebCore/Modules/webaudio/AudioSummingJunction.h11
-rw-r--r--Source/WebCore/Modules/webaudio/BiquadDSPKernel.cpp16
-rw-r--r--Source/WebCore/Modules/webaudio/BiquadDSPKernel.h13
-rw-r--r--Source/WebCore/Modules/webaudio/BiquadFilterNode.cpp73
-rw-r--r--Source/WebCore/Modules/webaudio/BiquadFilterNode.h34
-rw-r--r--Source/WebCore/Modules/webaudio/BiquadFilterNode.idl32
-rw-r--r--Source/WebCore/Modules/webaudio/BiquadProcessor.cpp11
-rw-r--r--Source/WebCore/Modules/webaudio/BiquadProcessor.h39
-rw-r--r--Source/WebCore/Modules/webaudio/ChannelMergerNode.cpp10
-rw-r--r--Source/WebCore/Modules/webaudio/ChannelMergerNode.h22
-rw-r--r--Source/WebCore/Modules/webaudio/ChannelMergerNode.idl2
-rw-r--r--Source/WebCore/Modules/webaudio/ChannelSplitterNode.cpp6
-rw-r--r--Source/WebCore/Modules/webaudio/ChannelSplitterNode.h18
-rw-r--r--Source/WebCore/Modules/webaudio/ConvolverNode.cpp43
-rw-r--r--Source/WebCore/Modules/webaudio/ConvolverNode.h41
-rw-r--r--Source/WebCore/Modules/webaudio/ConvolverNode.idl2
-rw-r--r--Source/WebCore/Modules/webaudio/DefaultAudioDestinationNode.cpp58
-rw-r--r--Source/WebCore/Modules/webaudio/DefaultAudioDestinationNode.h41
-rw-r--r--Source/WebCore/Modules/webaudio/DelayDSPKernel.h13
-rw-r--r--Source/WebCore/Modules/webaudio/DelayNode.cpp17
-rw-r--r--Source/WebCore/Modules/webaudio/DelayNode.h19
-rw-r--r--Source/WebCore/Modules/webaudio/DelayProcessor.cpp2
-rw-r--r--Source/WebCore/Modules/webaudio/DelayProcessor.h9
-rw-r--r--Source/WebCore/Modules/webaudio/DynamicsCompressorNode.cpp2
-rw-r--r--Source/WebCore/Modules/webaudio/DynamicsCompressorNode.h23
-rw-r--r--Source/WebCore/Modules/webaudio/GainNode.cpp4
-rw-r--r--Source/WebCore/Modules/webaudio/GainNode.h22
-rw-r--r--Source/WebCore/Modules/webaudio/MediaElementAudioSourceNode.cpp18
-rw-r--r--Source/WebCore/Modules/webaudio/MediaElementAudioSourceNode.h30
-rw-r--r--Source/WebCore/Modules/webaudio/MediaStreamAudioDestinationNode.cpp22
-rw-r--r--Source/WebCore/Modules/webaudio/MediaStreamAudioDestinationNode.h22
-rw-r--r--Source/WebCore/Modules/webaudio/MediaStreamAudioSource.cpp46
-rw-r--r--Source/WebCore/Modules/webaudio/MediaStreamAudioSource.h32
-rw-r--r--Source/WebCore/Modules/webaudio/MediaStreamAudioSourceNode.cpp95
-rw-r--r--Source/WebCore/Modules/webaudio/MediaStreamAudioSourceNode.h42
-rw-r--r--Source/WebCore/Modules/webaudio/OfflineAudioCompletionEvent.cpp18
-rw-r--r--Source/WebCore/Modules/webaudio/OfflineAudioCompletionEvent.h16
-rw-r--r--Source/WebCore/Modules/webaudio/OfflineAudioContext.cpp34
-rw-r--r--Source/WebCore/Modules/webaudio/OfflineAudioContext.h11
-rw-r--r--Source/WebCore/Modules/webaudio/OfflineAudioContext.idl6
-rw-r--r--Source/WebCore/Modules/webaudio/OfflineAudioDestinationNode.cpp38
-rw-r--r--Source/WebCore/Modules/webaudio/OfflineAudioDestinationNode.h23
-rw-r--r--Source/WebCore/Modules/webaudio/OscillatorNode.cpp114
-rw-r--r--Source/WebCore/Modules/webaudio/OscillatorNode.h54
-rw-r--r--Source/WebCore/Modules/webaudio/OscillatorNode.idl31
-rw-r--r--Source/WebCore/Modules/webaudio/PannerNode.cpp135
-rw-r--r--Source/WebCore/Modules/webaudio/PannerNode.h65
-rw-r--r--Source/WebCore/Modules/webaudio/PannerNode.idl49
-rw-r--r--Source/WebCore/Modules/webaudio/PeriodicWave.cpp58
-rw-r--r--Source/WebCore/Modules/webaudio/PeriodicWave.h27
-rw-r--r--Source/WebCore/Modules/webaudio/RealtimeAnalyser.cpp3
-rw-r--r--Source/WebCore/Modules/webaudio/RealtimeAnalyser.h7
-rw-r--r--Source/WebCore/Modules/webaudio/ScriptProcessorNode.cpp75
-rw-r--r--Source/WebCore/Modules/webaudio/ScriptProcessorNode.h30
-rw-r--r--Source/WebCore/Modules/webaudio/ScriptProcessorNode.idl2
-rw-r--r--Source/WebCore/Modules/webaudio/WaveShaperDSPKernel.h15
-rw-r--r--Source/WebCore/Modules/webaudio/WaveShaperNode.cpp56
-rw-r--r--Source/WebCore/Modules/webaudio/WaveShaperNode.h23
-rw-r--r--Source/WebCore/Modules/webaudio/WaveShaperNode.idl2
-rw-r--r--Source/WebCore/Modules/webaudio/WaveShaperProcessor.cpp12
-rw-r--r--Source/WebCore/Modules/webaudio/WaveShaperProcessor.h13
-rw-r--r--Source/WebCore/Modules/webdatabase/AbstractDatabaseServer.h89
-rw-r--r--Source/WebCore/Modules/webdatabase/ChangeVersionData.h9
-rw-r--r--Source/WebCore/Modules/webdatabase/ChangeVersionWrapper.cpp40
-rw-r--r--Source/WebCore/Modules/webdatabase/ChangeVersionWrapper.h22
-rw-r--r--Source/WebCore/Modules/webdatabase/DOMWindowWebDatabase.cpp46
-rw-r--r--Source/WebCore/Modules/webdatabase/DOMWindowWebDatabase.h28
-rw-r--r--Source/WebCore/Modules/webdatabase/DOMWindowWebDatabase.idl11
-rw-r--r--Source/WebCore/Modules/webdatabase/Database.cpp761
-rw-r--r--Source/WebCore/Modules/webdatabase/Database.h161
-rw-r--r--Source/WebCore/Modules/webdatabase/Database.idl12
-rw-r--r--Source/WebCore/Modules/webdatabase/DatabaseAuthorizer.cpp51
-rw-r--r--Source/WebCore/Modules/webdatabase/DatabaseAuthorizer.h13
-rw-r--r--Source/WebCore/Modules/webdatabase/DatabaseBackend.cpp177
-rw-r--r--Source/WebCore/Modules/webdatabase/DatabaseBackend.h87
-rw-r--r--Source/WebCore/Modules/webdatabase/DatabaseBackendBase.cpp626
-rw-r--r--Source/WebCore/Modules/webdatabase/DatabaseBackendBase.h147
-rw-r--r--Source/WebCore/Modules/webdatabase/DatabaseCallback.h13
-rw-r--r--Source/WebCore/Modules/webdatabase/DatabaseCallback.idl9
-rw-r--r--Source/WebCore/Modules/webdatabase/DatabaseContext.cpp130
-rw-r--r--Source/WebCore/Modules/webdatabase/DatabaseContext.h63
-rw-r--r--Source/WebCore/Modules/webdatabase/DatabaseDetails.h11
-rw-r--r--Source/WebCore/Modules/webdatabase/DatabaseManager.cpp452
-rw-r--r--Source/WebCore/Modules/webdatabase/DatabaseManager.h131
-rw-r--r--Source/WebCore/Modules/webdatabase/DatabaseManagerClient.h20
-rw-r--r--Source/WebCore/Modules/webdatabase/DatabaseServer.cpp163
-rw-r--r--Source/WebCore/Modules/webdatabase/DatabaseServer.h78
-rw-r--r--Source/WebCore/Modules/webdatabase/DatabaseSync.cpp195
-rw-r--r--Source/WebCore/Modules/webdatabase/DatabaseSync.h89
-rw-r--r--Source/WebCore/Modules/webdatabase/DatabaseSync.idl42
-rw-r--r--Source/WebCore/Modules/webdatabase/DatabaseTask.cpp104
-rw-r--r--Source/WebCore/Modules/webdatabase/DatabaseTask.h127
-rw-r--r--Source/WebCore/Modules/webdatabase/DatabaseThread.cpp177
-rw-r--r--Source/WebCore/Modules/webdatabase/DatabaseThread.h56
-rw-r--r--Source/WebCore/Modules/webdatabase/DatabaseTracker.cpp918
-rw-r--r--Source/WebCore/Modules/webdatabase/DatabaseTracker.h182
-rw-r--r--Source/WebCore/Modules/webdatabase/OriginLock.cpp5
-rw-r--r--Source/WebCore/Modules/webdatabase/OriginLock.h15
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLCallbackWrapper.h72
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLError.h19
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLError.idl5
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLException.cpp11
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLException.h13
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLException.idl2
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLResultSet.cpp42
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLResultSet.h38
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLResultSet.idl13
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLResultSetRowList.cpp21
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLResultSetRowList.h20
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLResultSetRowList.idl9
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLStatement.cpp202
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLStatement.h55
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLStatementBackend.cpp238
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLStatementBackend.h88
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLStatementCallback.h12
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLStatementCallback.idl8
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLStatementErrorCallback.h13
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLStatementErrorCallback.idl8
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLStatementSync.cpp137
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLStatementSync.h63
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLTransaction.cpp576
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLTransaction.h132
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLTransaction.idl12
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLTransactionBackend.cpp423
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLTransactionBackend.h115
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLTransactionBackendSync.cpp237
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLTransactionBackendSync.h86
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLTransactionCallback.h13
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLTransactionCallback.idl8
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLTransactionClient.cpp62
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLTransactionClient.h57
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.cpp50
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.h21
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLTransactionErrorCallback.h13
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLTransactionErrorCallback.idl8
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLTransactionState.h9
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLTransactionStateMachine.cpp4
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLTransactionStateMachine.h32
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLTransactionSync.h56
-rw-r--r--Source/WebCore/Modules/webdatabase/SQLTransactionSyncCallback.h53
-rw-r--r--Source/WebCore/Modules/webdatabase/WorkerGlobalScopeWebDatabase.cpp76
-rw-r--r--Source/WebCore/Modules/webdatabase/WorkerGlobalScopeWebDatabase.h59
-rw-r--r--Source/WebCore/Modules/webdatabase/WorkerGlobalScopeWebDatabase.idl34
-rw-r--r--Source/WebCore/Modules/webdriver/NavigatorWebDriver.cpp78
-rw-r--r--Source/WebCore/Modules/webdriver/NavigatorWebDriver.h50
-rw-r--r--Source/WebCore/Modules/webdriver/NavigatorWebDriver.idl28
-rw-r--r--Source/WebCore/Modules/websockets/CloseEvent.h47
-rw-r--r--Source/WebCore/Modules/websockets/CloseEvent.idl15
-rw-r--r--Source/WebCore/Modules/websockets/ThreadableWebSocketChannel.cpp22
-rw-r--r--Source/WebCore/Modules/websockets/ThreadableWebSocketChannel.h17
-rw-r--r--Source/WebCore/Modules/websockets/ThreadableWebSocketChannelClientWrapper.cpp153
-rw-r--r--Source/WebCore/Modules/websockets/ThreadableWebSocketChannelClientWrapper.h38
-rw-r--r--Source/WebCore/Modules/websockets/WebSocket.cpp416
-rw-r--r--Source/WebCore/Modules/websockets/WebSocket.h125
-rw-r--r--Source/WebCore/Modules/websockets/WebSocket.idl56
-rw-r--r--Source/WebCore/Modules/websockets/WebSocketChannel.cpp277
-rw-r--r--Source/WebCore/Modules/websockets/WebSocketChannel.h115
-rw-r--r--Source/WebCore/Modules/websockets/WebSocketChannelClient.h41
-rw-r--r--Source/WebCore/Modules/websockets/WebSocketDeflateFramer.cpp55
-rw-r--r--Source/WebCore/Modules/websockets/WebSocketDeflateFramer.h35
-rw-r--r--Source/WebCore/Modules/websockets/WebSocketDeflater.cpp14
-rw-r--r--Source/WebCore/Modules/websockets/WebSocketDeflater.h22
-rw-r--r--Source/WebCore/Modules/websockets/WebSocketExtensionDispatcher.cpp30
-rw-r--r--Source/WebCore/Modules/websockets/WebSocketExtensionDispatcher.h13
-rw-r--r--Source/WebCore/Modules/websockets/WebSocketExtensionParser.h5
-rw-r--r--Source/WebCore/Modules/websockets/WebSocketExtensionProcessor.h7
-rw-r--r--Source/WebCore/Modules/websockets/WebSocketFrame.h7
-rw-r--r--Source/WebCore/Modules/websockets/WebSocketHandshake.cpp249
-rw-r--r--Source/WebCore/Modules/websockets/WebSocketHandshake.h18
-rw-r--r--Source/WebCore/Modules/websockets/WorkerThreadableWebSocketChannel.cpp474
-rw-r--r--Source/WebCore/Modules/websockets/WorkerThreadableWebSocketChannel.h101
1061 files changed, 77745 insertions, 30742 deletions
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 <wtf/NeverDestroyed.h>
+
+#if ENABLE(WIRELESS_PLAYBACK_TARGET)
+
+namespace WebCore {
+
+static const AtomicString& stringForPlaybackTargetAvailability(bool available)
+{
+ static NeverDestroyed<AtomicString> availableString("available", AtomicString::ConstructFromLiteral);
+ static NeverDestroyed<AtomicString> 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<WebKitPlaybackTargetAvailabilityEvent> create(const AtomicString& eventType, bool available)
+ {
+ return adoptRef(*new WebKitPlaybackTargetAvailabilityEvent(eventType, available));
+ }
+
+ struct Init : EventInit {
+ String availability;
+ };
+
+ static Ref<WebKitPlaybackTargetAvailabilityEvent> 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<ApplePayPaymentContact> billingContact;
+ std::optional<ApplePayPaymentContact> 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<ApplePayPaymentAuthorizedEvent> 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 <wtf/Optional.h>
+#include <wtf/Vector.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+struct ApplePayPaymentContact {
+ String phoneNumber;
+ String emailAddress;
+ String givenName;
+ String familyName;
+ std::optional<Vector<String>> 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<DOMString> 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 <wtf/Optional.h>
+
+namespace WebCore {
+
+enum class ApplePayPaymentMethodType { Debit, Credit, Prepaid, Store };
+
+struct ApplePayPaymentMethod {
+ using Type = ApplePayPaymentMethodType;
+
+ String displayName;
+ String network;
+ std::optional<Type> type;
+ std::optional<ApplePayPaymentPass> 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<ApplePayPaymentMethodSelectedEvent> 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 <wtf/text/WTFString.h>
+
+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<MerchantCapability> merchantCapabilities;
+ Vector<String> supportedNetworks;
+ String countryCode;
+ String currencyCode;
+
+ std::optional<Vector<ContactField>> requiredBillingContactFields;
+ std::optional<ApplePayPaymentContact> billingContact;
+
+ std::optional<Vector<ContactField>> requiredShippingContactFields;
+ std::optional<ApplePayPaymentContact> shippingContact;
+
+ ShippingType shippingType { ShippingType::Shipping };
+ std::optional<Vector<ApplePayShippingMethod>> shippingMethods;
+
+ ApplePayLineItem total;
+ std::optional<Vector<ApplePayLineItem>> 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<ApplePayLineItem> lineItems;
+
+ required sequence<ApplePayMerchantCapability> merchantCapabilities;
+ required sequence<DOMString> supportedNetworks; // FIXME: Should this be an sequence of enums?
+ required DOMString countryCode;
+ required DOMString currencyCode;
+
+ sequence<ApplePayContactField> requiredBillingContactFields;
+ ApplePayPaymentContact billingContact;
+
+ sequence<ApplePayContactField> requiredShippingContactFields;
+ ApplePayPaymentContact shippingContact;
+
+ ApplePayShippingType shippingType = "shipping";
+ sequence<ApplePayShippingMethod> 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<int64_t>::max() / 10;
+
+ // Check overflow.
+ if (amount > maxMultiplier || (amount == maxMultiplier && digitValue > (std::numeric_limits<int64_t>::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<int64_t> 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<PaymentRequest::LineItem> 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<PaymentRequest::LineItem> 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<Vector<PaymentRequest::LineItem>> convertAndValidate(std::optional<Vector<ApplePayLineItem>>&& lineItems)
+{
+ Vector<PaymentRequest::LineItem> 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<PaymentRequest::MerchantCapabilities> convertAndValidate(Vector<ApplePayPaymentRequest::MerchantCapability>&& 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<Vector<String>> convertAndValidate(unsigned version, Vector<String>&& 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<PaymentRequest::ContactFields> convertAndValidate(Vector<ApplePayPaymentRequest::ContactField>&& 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<PaymentRequest::ShippingMethod> 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<Vector<PaymentRequest::ShippingMethod>> convertAndValidate(Vector<ApplePayShippingMethod>&& shippingMethods)
+{
+ if (shippingMethods.isEmpty())
+ return Exception { TypeError, "At least one shipping method must be provided." };
+
+ Vector<PaymentRequest::ShippingMethod> 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<PaymentRequest> 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<void> 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<Ref<ApplePaySession>> 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<bool> ApplePaySession::supportsVersion(ScriptExecutionContext& scriptExecutionContext, unsigned version)
+{
+ if (!version)
+ return Exception { INVALID_ACCESS_ERR };
+
+ auto& document = downcast<Document>(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<bool> ApplePaySession::canMakePayments(ScriptExecutionContext& scriptExecutionContext)
+{
+ auto& document = downcast<Document>(scriptExecutionContext);
+
+ auto canCall = canCallApplePaySessionAPIs(document);
+ if (canCall.hasException())
+ return canCall.releaseException();
+
+ return document.frame()->mainFrame().paymentCoordinator().canMakePayments();
+}
+
+ExceptionOr<void> ApplePaySession::canMakePaymentsWithActiveCard(ScriptExecutionContext& scriptExecutionContext, const String& merchantIdentifier, Ref<DeferredPromise>&& passedPromise)
+{
+ auto& document = downcast<Document>(scriptExecutionContext);
+
+ auto canCall = canCallApplePaySessionAPIs(document);
+ if (canCall.hasException())
+ return canCall.releaseException();
+
+ RefPtr<DeferredPromise> promise(WTFMove(passedPromise));
+ if (!shouldDiscloseApplePayCapability(document)) {
+ auto& paymentCoordinator = document.frame()->mainFrame().paymentCoordinator();
+ bool canMakePayments = paymentCoordinator.canMakePayments();
+
+ RunLoop::main().dispatch([promise, canMakePayments]() mutable {
+ promise->resolve<IDLBoolean>(canMakePayments);
+ });
+ return { };
+ }
+
+ auto& paymentCoordinator = document.frame()->mainFrame().paymentCoordinator();
+
+ paymentCoordinator.canMakePaymentsWithActiveCard(merchantIdentifier, document.domain(), [promise](bool canMakePayments) mutable {
+ promise->resolve<IDLBoolean>(canMakePayments);
+ });
+ return { };
+}
+
+ExceptionOr<void> ApplePaySession::openPaymentSetup(ScriptExecutionContext& scriptExecutionContext, const String& merchantIdentifier, Ref<DeferredPromise>&& passedPromise)
+{
+ auto& document = downcast<Document>(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<DeferredPromise> promise(WTFMove(passedPromise));
+ auto& paymentCoordinator = document.frame()->mainFrame().paymentCoordinator();
+
+ paymentCoordinator.openPaymentSetup(merchantIdentifier, document.domain(), [promise](bool result) mutable {
+ promise->resolve<IDLBoolean>(result);
+ });
+
+ return { };
+}
+
+ExceptionOr<void> 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<Document>(scriptExecutionContext());
+
+ Vector<URL> 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<void> ApplePaySession::abort()
+{
+ if (!canAbort())
+ return Exception { INVALID_ACCESS_ERR };
+
+ m_state = State::Aborted;
+ paymentCoordinator().abortPaymentSession();
+
+ didReachFinalState();
+
+ return { };
+}
+
+ExceptionOr<void> ApplePaySession::completeMerchantValidation(JSC::ExecState& state, JSC::JSValue merchantSessionValue)
+{
+ if (!canCompleteMerchantValidation())
+ return Exception { INVALID_ACCESS_ERR };
+
+ if (!merchantSessionValue.isObject())
+ return Exception { TypeError };
+
+ auto& document = *downcast<Document>(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<PaymentAuthorizationStatus> 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<void> ApplePaySession::completeShippingMethodSelection(unsigned short status, ApplePayLineItem&& newTotal, Vector<ApplePayLineItem>&& 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<void> ApplePaySession::completeShippingContactSelection(unsigned short status, Vector<ApplePayShippingMethod>&& newShippingMethods, ApplePayLineItem&& newTotal, Vector<ApplePayLineItem>&& 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<void> ApplePaySession::completePaymentMethodSelection(ApplePayLineItem&& newTotal, Vector<ApplePayLineItem>&& 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<void> 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<Document>(*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 <wtf/Ref.h>
+#include <wtf/RefCounted.h>
+
+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<ApplePaySession>, public ActiveDOMObject, public EventTargetWithInlineData {
+public:
+ static ExceptionOr<Ref<ApplePaySession>> 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<bool> supportsVersion(ScriptExecutionContext&, unsigned version);
+ static ExceptionOr<bool> canMakePayments(ScriptExecutionContext&);
+ static ExceptionOr<void> canMakePaymentsWithActiveCard(ScriptExecutionContext&, const String& merchantIdentifier, Ref<DeferredPromise>&&);
+ static ExceptionOr<void> openPaymentSetup(ScriptExecutionContext&, const String& merchantIdentifier, Ref<DeferredPromise>&&);
+
+ ExceptionOr<void> begin();
+ ExceptionOr<void> abort();
+ ExceptionOr<void> completeMerchantValidation(JSC::ExecState&, JSC::JSValue merchantSession);
+ ExceptionOr<void> completeShippingMethodSelection(unsigned short status, ApplePayLineItem&& newTotal, Vector<ApplePayLineItem>&& newLineItems);
+ ExceptionOr<void> completeShippingContactSelection(unsigned short status, Vector<ApplePayShippingMethod>&& newShippingMethods, ApplePayLineItem&& newTotal, Vector<ApplePayLineItem>&& newLineItems);
+ ExceptionOr<void> completePaymentMethodSelection(ApplePayLineItem&& newTotal, Vector<ApplePayLineItem>&& newLineItems);
+ ExceptionOr<void> 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<ApplePaySession>::ref;
+ using RefCounted<ApplePaySession>::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<boolean> canMakePaymentsWithActiveCard(DOMString merchantIdentifier);
+ [CallWith=ScriptExecutionContext, MayThrowException] static Promise<boolean> 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<ApplePayLineItem> newLineItems);
+ [MayThrowException] void completeShippingContactSelection(unsigned short status, sequence<ApplePayShippingMethod> newShippingMethods, ApplePayLineItem newTotal, sequence<ApplePayLineItem> newLineItems);
+ [MayThrowException] void completePaymentMethodSelection(ApplePayLineItem newTotal, sequence<ApplePayLineItem> 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<ApplePayShippingContactSelectedEvent> 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 <wtf/text/WTFString.h>
+
+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 <wtf/text/StringBuilder.h>
+
+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<ApplePayShippingMethodSelectedEvent> 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<ApplePayValidateMerchantEvent> 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 <wtf/RetainPtr.h>
+
+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<PKPayment> 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 <wtf/Forward.h>
+#include <wtf/RetainPtr.h>
+
+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<PKContact> 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<void (bool)> completionHandler)
+{
+ m_client.canMakePaymentsWithActiveCard(merchantIdentifier, domainName, WTFMove(completionHandler));
+}
+
+void PaymentCoordinator::openPaymentSetup(const String& merchantIdentifier, const String& domainName, std::function<void (bool)> completionHandler)
+{
+ m_client.openPaymentSetup(merchantIdentifier, domainName, WTFMove(completionHandler));
+}
+
+bool PaymentCoordinator::beginPaymentSession(ApplePaySession& paymentSession, const URL& originatingURL, const Vector<URL>& 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<PaymentRequest::TotalAndLineItems> newTotalAndItems)
+{
+ ASSERT(m_activeSession);
+
+ m_client.completeShippingMethodSelection(status, WTFMove(newTotalAndItems));
+}
+
+void PaymentCoordinator::completeShippingContactSelection(PaymentAuthorizationStatus status, const Vector<PaymentRequest::ShippingMethod>& newShippingMethods, std::optional<PaymentRequest::TotalAndLineItems> newTotalAndItems)
+{
+ ASSERT(m_activeSession);
+
+ m_client.completeShippingContactSelection(status, newShippingMethods, WTFMove(newTotalAndItems));
+}
+
+void PaymentCoordinator::completePaymentMethodSelection(std::optional<PaymentRequest::TotalAndLineItems> 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 <functional>
+
+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<void (bool)> completionHandler);
+ void openPaymentSetup(const String& merchantIdentifier, const String& domainName, std::function<void (bool)> completionHandler);
+
+ bool hasActiveSession() const { return m_activeSession; }
+
+ bool beginPaymentSession(ApplePaySession&, const URL& originatingURL, const Vector<URL>& linkIconURLs, const PaymentRequest&);
+ void completeMerchantValidation(const PaymentMerchantSession&);
+ void completeShippingMethodSelection(PaymentAuthorizationStatus, std::optional<PaymentRequest::TotalAndLineItems> newItems);
+ void completeShippingContactSelection(PaymentAuthorizationStatus, const Vector<PaymentRequest::ShippingMethod>& newShippingMethods, std::optional<PaymentRequest::TotalAndLineItems> newItems);
+ void completePaymentMethodSelection(std::optional<PaymentRequest::TotalAndLineItems> 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<ApplePaySession> 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 <functional>
+#include <wtf/Forward.h>
+
+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<void (bool)> completionHandler) = 0;
+ virtual void openPaymentSetup(const String& merchantIdentifier, const String& domainName, std::function<void (bool)> completionHandler) = 0;
+
+ virtual bool showPaymentUI(const URL& originatingURL, const Vector<URL>& linkIconURLs, const PaymentRequest&) = 0;
+ virtual void completeMerchantValidation(const PaymentMerchantSession&) = 0;
+ virtual void completeShippingMethodSelection(PaymentAuthorizationStatus, std::optional<PaymentRequest::TotalAndLineItems> newTotalAndItems) = 0;
+ virtual void completeShippingContactSelection(PaymentAuthorizationStatus, const Vector<PaymentRequest::ShippingMethod>& newShippingMethods, std::optional<PaymentRequest::TotalAndLineItems> newTotalAndItems) = 0;
+ virtual void completePaymentMethodSelection(std::optional<WebCore::PaymentRequest::TotalAndLineItems> 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 <wtf/Forward.h>
+#include <wtf/RetainPtr.h>
+
+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<PaymentMerchantSession> fromJS(JSC::ExecState&, JSC::JSValue, String& errorMessage);
+
+ PKPaymentMerchantSession *pkPaymentMerchantSession() const { return m_pkPaymentMerchantSession.get(); }
+
+private:
+ RetainPtr<PKPaymentMerchantSession> 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 <wtf/RetainPtr.h>
+
+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<PKPaymentMethod> 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(<WebKitAdditions/PaymentRequestAdditions.cpp>)
+#include <WebKitAdditions/PaymentRequestAdditions.cpp>
+#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 <wtf/Optional.h>
+#include <wtf/Vector.h>
+#include <wtf/text/WTFString.h>
+
+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<String>& supportedNetworks() const { return m_supportedNetworks; }
+ void setSupportedNetworks(const Vector<String>& 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<int64_t> 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<ShippingMethod>& shippingMethods() const { return m_shippingMethods; }
+ void setShippingMethods(const Vector<ShippingMethod>& shippingMethods) { m_shippingMethods = shippingMethods; }
+
+ const Vector<LineItem>& lineItems() const { return m_lineItems; }
+ void setLineItems(const Vector<LineItem>& 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<PaymentRequest::LineItem> 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<String> m_supportedNetworks;
+ MerchantCapabilities m_merchantCapabilities;
+
+ ShippingType m_shippingType { ShippingType::Shipping };
+ Vector<ShippingMethod> m_shippingMethods;
+
+ Vector<LineItem> 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 <unicode/ucurr.h>
+#include <unicode/uloc.h>
+
+namespace WebCore {
+
+static ExceptionOr<void> validateCountryCode(const String&);
+static ExceptionOr<void> validateCurrencyCode(const String&);
+static ExceptionOr<void> validateMerchantCapabilities(const PaymentRequest::MerchantCapabilities&);
+static ExceptionOr<void> validateSupportedNetworks(const Vector<String>&);
+static ExceptionOr<void> validateShippingMethods(const Vector<PaymentRequest::ShippingMethod>&);
+static ExceptionOr<void> validateShippingMethod(const PaymentRequest::ShippingMethod&);
+
+
+ExceptionOr<void> 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<void> 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<void> 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<void> validateCurrencyCode(const String& currencyCode)
+{
+ if (!currencyCode)
+ return Exception { TypeError, "Missing currency code." };
+
+ UErrorCode errorCode = U_ZERO_ERROR;
+ auto currencyCodes = std::unique_ptr<UEnumeration, void (*)(UEnumeration*)>(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<void> validateMerchantCapabilities(const PaymentRequest::MerchantCapabilities& merchantCapabilities)
+{
+ if (!merchantCapabilities.supports3DS && !merchantCapabilities.supportsEMV && !merchantCapabilities.supportsCredit && !merchantCapabilities.supportsDebit)
+ return Exception { TypeError, "Missing merchant capabilities." };
+
+ return { };
+}
+
+static ExceptionOr<void> validateSupportedNetworks(const Vector<String>& supportedNetworks)
+{
+ if (supportedNetworks.isEmpty())
+ return Exception { TypeError, "Missing supported networks." };
+
+ return { };
+}
+
+static ExceptionOr<void> 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<void> validateShippingMethods(const Vector<PaymentRequest::ShippingMethod>& 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<void> validate(const PaymentRequest&);
+ static ExceptionOr<void> validateTotal(const PaymentRequest::LineItem&);
+};
+
+}
+
+#endif
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> 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> batteryStatus)
-{
- RefPtr<BatteryStatus> 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> batteryStatus)
-{
- RefPtr<Event> event = Event::create(eventType, false, false);
- RefPtr<BatteryStatus> 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<bool>(BatteryController::from(page));
-}
-
-void provideBatteryTo(Page* page, BatteryClient* client)
-{
- Supplement<Page>::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<Page> {
-public:
- ~BatteryController();
-
- static PassOwnPtr<BatteryController> create(BatteryClient*);
-
- void addListener(BatteryManager*);
- void removeListener(BatteryManager*);
- void updateBatteryStatus(PassRefPtr<BatteryStatus>);
- void didChangeBatteryStatus(const AtomicString& eventType, PassRefPtr<BatteryStatus>);
-
- BatteryClient* client() const { return m_client; }
-
- static const char* supplementName();
- static BatteryController* from(Page* page) { return static_cast<BatteryController*>(Supplement<Page>::from(page, supplementName())); }
- static bool isActive(Page*);
-
-private:
- typedef Vector<BatteryManager*> ListenerVector;
-
- explicit BatteryController(BatteryClient*);
-
- BatteryClient* m_client;
- ListenerVector m_listeners;
-
- RefPtr<BatteryStatus> 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 <limits>
-
-namespace WebCore {
-
-PassRefPtr<BatteryManager> BatteryManager::create(Navigator* navigator)
-{
- RefPtr<BatteryManager> 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<double>::infinity();
-
- return m_batteryStatus->chargingTime();
-}
-
-double BatteryManager::dischargingTime()
-{
- if (!m_batteryStatus || m_batteryStatus->charging())
- return std::numeric_limits<double>::infinity();
-
- return m_batteryStatus->dischargingTime();
-}
-
-double BatteryManager::level()
-{
- return m_batteryStatus ? m_batteryStatus->level() : 1;
-}
-
-void BatteryManager::didChangeBatteryStatus(PassRefPtr<Event> event, PassRefPtr<BatteryStatus> batteryStatus)
-{
- updateBatteryStatus(batteryStatus);
- dispatchEvent(event);
-}
-
-void BatteryManager::updateBatteryStatus(PassRefPtr<BatteryStatus> 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<BatteryManager>, public EventTarget {
-public:
- virtual ~BatteryManager();
- static PassRefPtr<BatteryManager> 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<Event>, PassRefPtr<BatteryStatus>);
- void updateBatteryStatus(PassRefPtr<BatteryStatus>);
- void batteryControllerDestroyed() { m_batteryController = 0; }
-
- using RefCounted<BatteryManager>::ref;
- using RefCounted<BatteryManager>::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<BatteryStatus> 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/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<NavigatorBattery*>(Supplement<Navigator>::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/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 <wtf/NeverDestroyed.h>
+
+namespace WebCore {
+
+static Vector<CDMFactory*>& cdmFactories()
+{
+ static NeverDestroyed<Vector<CDMFactory*>> factories;
+ return factories;
+}
+
+static std::unique_ptr<CDMPrivate> 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> 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<MediaKeySystemConfiguration> 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<String> 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<MediaKeySessionType> 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<Document>(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<Vector<MediaKeySystemMediaCapability>> CDM::getSupportedCapabilitiesForAudioVideoType(CDM::AudioVideoType type, const Vector<MediaKeySystemMediaCapability>& 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<MediaKeySystemMediaCapability> 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<Document>(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<CDMInstance> 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<SharedBuffer> 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<SharedBuffer> CDM::sanitizeResponse(const SharedBuffer& response)
+{
+ if (!m_private)
+ return nullptr;
+ return m_private->sanitizeResponse(response);
+}
+
+std::optional<String> 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 <functional>
+#include <wtf/HashSet.h>
+#include <wtf/Ref.h>
+#include <wtf/RefCounted.h>
+#include <wtf/WeakPtr.h>
+#include <wtf/text/WTFString.h>
+
+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<CDMPrivate> createCDM(CDM&) = 0;
+ virtual bool supportsKeySystem(const String&) = 0;
+};
+
+class CDM : public RefCounted<CDM>, 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<CDM> create(Document&, const String& keySystem);
+ ~CDM();
+
+ using SupportedConfigurationCallback = std::function<void(std::optional<MediaKeySystemConfiguration>)>;
+ void getSupportedConfiguration(MediaKeySystemConfiguration&& candidateConfiguration, SupportedConfigurationCallback&&);
+
+ const String& keySystem() const { return m_keySystem; }
+
+ void loadAndInitialize();
+ RefPtr<CDMInstance> createInstance();
+ bool supportsServerCertificates() const;
+ bool supportsSessions() const;
+ bool supportsInitDataType(const AtomicString&) const;
+
+ RefPtr<SharedBuffer> sanitizeInitData(const AtomicString& initDataType, const SharedBuffer&);
+ bool supportsInitData(const AtomicString& initDataType, const SharedBuffer&);
+
+ RefPtr<SharedBuffer> sanitizeResponse(const SharedBuffer&);
+
+ std::optional<String> 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<MediaKeySystemConfiguration> getSupportedConfiguration(const MediaKeySystemConfiguration& candidateConfiguration, MediaKeysRestrictions&);
+ std::optional<Vector<MediaKeySystemMediaCapability>> getSupportedCapabilitiesForAudioVideoType(AudioVideoType, const Vector<MediaKeySystemMediaCapability>& requestedCapabilities, const MediaKeySystemConfiguration& partialConfiguration, MediaKeysRestrictions&);
+
+ WeakPtr<CDM> createWeakPtr() { return m_weakPtrFactory.createWeakPtr(); }
+
+ using ConsentStatusCallback = std::function<void(ConsentStatus, MediaKeySystemConfiguration&&, MediaKeysRestrictions&&)>;
+ void getConsentStatus(MediaKeySystemConfiguration&& accumulatedConfiguration, MediaKeysRestrictions&&, ConsentStatusCallback&&);
+ String m_keySystem;
+ std::unique_ptr<CDMPrivate> m_private;
+ WeakPtrFactory<CDM> 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 <utility>
+#include <wtf/Forward.h>
+#include <wtf/Optional.h>
+#include <wtf/RefCounted.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+struct MediaKeySystemConfiguration;
+class SharedBuffer;
+
+class CDMInstance : public RefCounted<CDMInstance> {
+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<SharedBuffer>&&) = 0;
+
+ using LicenseCallback = Function<void(Ref<SharedBuffer>&& message, const String& sessionId, bool needsIndividualization, SuccessValue succeeded)>;
+ virtual void requestLicense(LicenseType, const AtomicString& initDataType, Ref<SharedBuffer>&& initData, LicenseCallback) = 0;
+
+ using KeyStatusVector = Vector<std::pair<Ref<SharedBuffer>, KeyStatus>>;
+ using Message = std::pair<MessageType, Ref<SharedBuffer>>;
+ using LicenseUpdateCallback = Function<void(bool sessionWasClosed, std::optional<KeyStatusVector>&& changedKeys, std::optional<double>&& changedExpiration, std::optional<Message>&& 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<void(std::optional<KeyStatusVector>&&, std::optional<double>&&, std::optional<Message>&&, SuccessValue, SessionLoadFailure)>;
+ virtual void loadSession(LicenseType, const String& sessionId, const String& origin, LoadSessionCallback) = 0;
+
+ using CloseSessionCallback = Function<void()>;
+ virtual void closeSession(const String& sessionId, CloseSessionCallback) = 0;
+
+ using RemoveSessionDataCallback = Function<void(KeyStatusVector&&, std::optional<Ref<SharedBuffer>>&&, 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 <wtf/Forward.h>
+
+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<CDMInstance> 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<SharedBuffer> sanitizeResponse(const SharedBuffer&) const = 0;
+ virtual std::optional<String> 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 <wtf/NeverDestroyed.h>
+#include <wtf/text/Base64.h>
+
+using namespace Inspector;
+
+namespace WebCore {
+
+static Vector<Ref<SharedBuffer>> 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<InspectorValue> value;
+ if (!InspectorValue::parseJSON(json, value))
+ return { };
+
+ RefPtr<InspectorObject> object;
+ if (!value->asObject(object))
+ return { };
+
+ RefPtr<InspectorArray> kidsArray;
+ if (!object->getArray("kids", kidsArray))
+ return { };
+
+ Vector<Ref<SharedBuffer>> keyIDs;
+ for (auto& value : *kidsArray) {
+ String keyID;
+ if (!value->asString(keyID))
+ continue;
+
+ Vector<char> keyIDData;
+ if (!WTF::base64URLDecode(keyID, { keyIDData }))
+ continue;
+
+ Ref<SharedBuffer> keyIDBuffer = SharedBuffer::adoptVector(keyIDData);
+ keyIDs.append(WTFMove(keyIDBuffer));
+ }
+
+ return keyIDs;
+}
+
+static RefPtr<SharedBuffer> sanitizeKeyids(const SharedBuffer& buffer)
+{
+ // 1. Format
+ // https://w3c.github.io/encrypted-media/format-registry/initdata/keyids.html#format
+ Vector<Ref<SharedBuffer>> 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<SharedBuffer> 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<Ref<SharedBuffer>> 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<SharedBuffer> sanitizeWebM(const SharedBuffer& buffer)
+{
+ // 1. Format
+ // https://w3c.github.io/encrypted-media/format-registry/initdata/webm.html#format
+ notImplemented();
+ return buffer.copy();
+}
+
+static Vector<Ref<SharedBuffer>> extractKeyIDsWebM(const SharedBuffer&)
+{
+ // 1. Format
+ // https://w3c.github.io/encrypted-media/format-registry/initdata/webm.html#format
+ notImplemented();
+ return { };
+}
+
+InitDataRegistry& InitDataRegistry::shared()
+{
+ static NeverDestroyed<InitDataRegistry> registry;
+ return registry.get();
+}
+
+InitDataRegistry::InitDataRegistry()
+{
+ registerInitDataType("keyids", { &sanitizeKeyids, &extractKeyIDsKeyids });
+ registerInitDataType("cenc", { &sanitizeCenc, &extractKeyIDsCenc });
+ registerInitDataType("webm", { &sanitizeWebM, &extractKeyIDsWebM });
+}
+
+InitDataRegistry::~InitDataRegistry() = default;
+
+RefPtr<SharedBuffer> 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<Ref<SharedBuffer>> 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 <wtf/Function.h>
+#include <wtf/HashMap.h>
+#include <wtf/Ref.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+#include <wtf/text/AtomicString.h>
+#include <wtf/text/AtomicStringHash.h>
+
+namespace WebCore {
+
+class SharedBuffer;
+
+class InitDataRegistry {
+public:
+ WEBCORE_EXPORT static InitDataRegistry& shared();
+ friend class NeverDestroyed<InitDataRegistry>;
+
+ RefPtr<SharedBuffer> sanitizeInitData(const AtomicString& initDataType, const SharedBuffer&);
+ WEBCORE_EXPORT Vector<Ref<SharedBuffer>> extractKeyIDs(const AtomicString& initDataType, const SharedBuffer&);
+
+ struct InitDataTypeCallbacks {
+ using SanitizeInitDataCallback = Function<RefPtr<SharedBuffer>(const SharedBuffer&)>;
+ using ExtractKeyIDsCallback = Function<Vector<Ref<SharedBuffer>>(const SharedBuffer&)>;
+
+ SanitizeInitDataCallback sanitizeInitData;
+ ExtractKeyIDsCallback extractKeyIDs;
+ };
+ void registerInitDataType(const AtomicString& initDataType, InitDataTypeCallbacks&&);
+
+private:
+ InitDataRegistry();
+ ~InitDataRegistry();
+
+ HashMap<AtomicString, InitDataTypeCallbacks> 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 <runtime/ArrayBuffer.h>
+
+namespace WebCore {
+
+class MediaKeyMessageEvent final : public Event {
+public:
+ using Type = MediaKeyMessageType;
+ using Init = MediaKeyMessageEventInit;
+
+ virtual ~MediaKeyMessageEvent();
+
+ static Ref<MediaKeyMessageEvent> 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<JSC::ArrayBuffer> message() const { return m_message; }
+
+private:
+ MediaKeyMessageEvent(const AtomicString&, const MediaKeyMessageEventInit&, IsTrusted);
+
+ // Event
+ EventInterface eventInterface() const override;
+
+ MediaKeyMessageType m_messageType;
+ RefPtr<JSC::ArrayBuffer> 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 <runtime/ArrayBuffer.h>
+
+namespace WebCore {
+
+struct MediaKeyMessageEventInit : EventInit {
+ MediaKeyMessageEventInit() = default;
+
+ MediaKeyMessageEventInit(MediaKeyMessageType messageType, RefPtr<JSC::ArrayBuffer>&& message)
+ : EventInit()
+ , messageType(messageType)
+ , message(WTFMove(message))
+ { }
+
+ MediaKeyMessageType messageType;
+ RefPtr<JSC::ArrayBuffer> 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 <wtf/NeverDestroyed.h>
+
+namespace WebCore {
+
+Ref<MediaKeySession> MediaKeySession::create(ScriptExecutionContext& context, MediaKeySessionType sessionType, bool useDistinctiveIdentifier, Ref<CDM>&& implementation, Ref<CDMInstance>&& 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<CDM>&& implementation, Ref<CDMInstance>&& instance)
+ : ActiveDOMObject(&context)
+ , m_expiration(std::numeric_limits<double>::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<MediaKeyStatusMap> MediaKeySession::keyStatuses() const
+{
+ return m_keyStatuses.copyRef();
+}
+
+void MediaKeySession::generateRequest(const AtomicString& initDataType, const BufferSource& initData, Ref<DeferredPromise>&& 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<SharedBuffer> 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<SharedBuffer>&& 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<DeferredPromise>&& 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<String> 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<Document>(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<CDMInstance::KeyStatusVector>&& knownKeys, std::optional<double>&& expiration, std::optional<CDMInstance::Message>&& 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<IDLBoolean>(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<double>::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<IDLBoolean>(true);
+ });
+ });
+ });
+
+ // 9. Return promise.
+}
+
+void MediaKeySession::update(const BufferSource& response, Ref<DeferredPromise>&& 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<SharedBuffer> 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<CDMInstance::KeyStatusVector>&& changedKeys, std::optional<double>&& changedExpiration, std::optional<CDMInstance::Message>&& 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<DeferredPromise>&& 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<DeferredPromise>&& 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<Ref<SharedBuffer>>&& 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<double>::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<double>::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 <wtf/RefCounted.h>
+#include <wtf/Vector.h>
+#include <wtf/WeakPtr.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class BufferSource;
+class CDM;
+class MediaKeyStatusMap;
+class MediaKeys;
+class SharedBuffer;
+
+class MediaKeySession final : public RefCounted<MediaKeySession>, public EventTargetWithInlineData, public ActiveDOMObject {
+public:
+ static Ref<MediaKeySession> create(ScriptExecutionContext&, MediaKeySessionType, bool useDistinctiveIdentifier, Ref<CDM>&&, Ref<CDMInstance>&&);
+ virtual ~MediaKeySession();
+
+ using RefCounted<MediaKeySession>::ref;
+ using RefCounted<MediaKeySession>::deref;
+
+ const String& sessionId() const;
+ double expiration() const;
+ Ref<MediaKeyStatusMap> keyStatuses() const;
+
+ void generateRequest(const AtomicString&, const BufferSource&, Ref<DeferredPromise>&&);
+ void load(const String&, Ref<DeferredPromise>&&);
+ void update(const BufferSource&, Ref<DeferredPromise>&&);
+ void close(Ref<DeferredPromise>&&);
+ void remove(Ref<DeferredPromise>&&);
+
+ using ClosedPromise = DOMPromise<void>;
+ void registerClosedPromise(ClosedPromise&&);
+
+ const Vector<std::pair<Ref<SharedBuffer>, MediaKeyStatus>>& statuses() const { return m_statuses; }
+
+private:
+ MediaKeySession(ScriptExecutionContext&, MediaKeySessionType, bool useDistinctiveIdentifier, Ref<CDM>&&, Ref<CDMInstance>&&);
+ 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<ClosedPromise> m_closedPromise;
+ Ref<MediaKeyStatusMap> m_keyStatuses;
+ bool m_closed { false };
+ bool m_uninitialized { true };
+ bool m_callable { false };
+ bool m_useDistinctiveIdentifier;
+ MediaKeySessionType m_sessionType;
+ Ref<CDM> m_implementation;
+ Ref<CDMInstance> m_instance;
+ GenericEventQueue m_eventQueue;
+ GenericTaskQueue<Timer> m_taskQueue;
+ Vector<Ref<SharedBuffer>> m_recordOfKeyUsage;
+ double m_firstDecryptTime { 0 };
+ double m_latestDecryptTime { 0 };
+ Vector<std::pair<Ref<SharedBuffer>, MediaKeyStatus>> m_statuses;
+ WeakPtrFactory<MediaKeySession> 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<void> closed;
+ readonly attribute MediaKeyStatusMap keyStatuses;
+ attribute EventHandler onkeystatuseschange;
+ attribute EventHandler onmessage;
+ Promise<void> generateRequest(DOMString initDataType, BufferSource initData);
+ Promise<bool> load(DOMString sessionId);
+ Promise<void> update(BufferSource response);
+ Promise<void> close();
+ Promise<void> 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<WTF::KeyValuePair<BufferSource::VariantType, MediaKeyStatus>> 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<BufferSource::VariantType, MediaKeyStatus> { RefPtr<ArrayBuffer>(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 <runtime/JSCJSValueInlines.h>
+#include <wtf/Optional.h>
+#include <wtf/Ref.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class MediaKeySession;
+class SharedBuffer;
+
+class MediaKeyStatusMap : public RefCounted<MediaKeyStatusMap> {
+public:
+ using Status = MediaKeyStatus;
+
+ static Ref<MediaKeyStatusMap> 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<WTF::KeyValuePair<BufferSource::VariantType, MediaKeyStatus>> next();
+
+ private:
+ Ref<MediaKeyStatusMap> 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<BufferSource, MediaKeyStatus>;
+ 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> MediaKeySystemAccess::create(const String& keySystem, MediaKeySystemConfiguration&& configuration, Ref<CDM>&& implementation)
+{
+ return adoptRef(*new MediaKeySystemAccess(keySystem, WTFMove(configuration), WTFMove(implementation)));
+}
+
+MediaKeySystemAccess::MediaKeySystemAccess(const String& keySystem, MediaKeySystemConfiguration&& configuration, Ref<CDM>&& implementation)
+ : m_keySystem(keySystem)
+ , m_configuration(new MediaKeySystemConfiguration(WTFMove(configuration)))
+ , m_implementation(WTFMove(implementation))
+{
+}
+
+MediaKeySystemAccess::~MediaKeySystemAccess() = default;
+
+void MediaKeySystemAccess::createMediaKeys(Ref<DeferredPromise>&& 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<IDLInterface<MediaKeys>>(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 <wtf/RefCounted.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class CDM;
+class MediaKeys;
+
+struct MediaKeySystemConfiguration;
+
+class MediaKeySystemAccess : public RefCounted<MediaKeySystemAccess> {
+public:
+ static Ref<MediaKeySystemAccess> create(const String& keySystem, MediaKeySystemConfiguration&&, Ref<CDM>&&);
+ ~MediaKeySystemAccess();
+
+ const String& keySystem() const { return m_keySystem; }
+ const MediaKeySystemConfiguration& getConfiguration() const { return *m_configuration; }
+ void createMediaKeys(Ref<DeferredPromise>&&);
+
+private:
+ MediaKeySystemAccess(const String& keySystem, MediaKeySystemConfiguration&&, Ref<CDM>&&);
+
+ String m_keySystem;
+ std::unique_ptr<MediaKeySystemConfiguration> m_configuration;
+ Ref<CDM> m_implementation;
+ GenericTaskQueue<Timer> m_taskQueue;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(ENCRYPTED_MEDIA)
diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionSyncCallback.idl b/Source/WebCore/Modules/encryptedmedia/MediaKeySystemAccess.idl
index c5b5e1ed5..30a825e15 100644
--- a/Source/WebCore/Modules/webdatabase/SQLTransactionSyncCallback.idl
+++ b/Source/WebCore/Modules/encryptedmedia/MediaKeySystemAccess.idl
@@ -1,25 +1,23 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * 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:
+ * modification, are permitted provided that the following conditions
+ * are met:
*
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, 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
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * 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
@@ -29,7 +27,11 @@
*/
[
- Conditional=SQL_DATABASE,
-] callback interface SQLTransactionSyncCallback {
- boolean handleEvent(SQLTransactionSync transaction);
+ Conditional=ENCRYPTED_MEDIA,
+ EnabledAtRuntime=EncryptedMediaAPI,
+ ImplementationLacksVTable,
+] interface MediaKeySystemAccess {
+ readonly attribute DOMString keySystem;
+ MediaKeySystemConfiguration getConfiguration();
+ Promise<MediaKeys> 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 <wtf/Vector.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+struct MediaKeySystemConfiguration {
+ using KeysRequirement = MediaKeysRequirement;
+
+ String label;
+ Vector<String> initDataTypes;
+ Vector<MediaKeySystemMediaCapability> audioCapabilities;
+ Vector<MediaKeySystemMediaCapability> videoCapabilities;
+ MediaKeysRequirement distinctiveIdentifier;
+ MediaKeysRequirement persistentState;
+ Vector<MediaKeySessionType> 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<DOMString> initDataTypes = [];
+ sequence<MediaKeySystemMediaCapability> audioCapabilities = [];
+ sequence<MediaKeySystemMediaCapability> videoCapabilities = [];
+ MediaKeysRequirement distinctiveIdentifier = "optional";
+ MediaKeysRequirement persistentState = "optional";
+ sequence<MediaKeySessionType> 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 <wtf/text/WTFString.h>
+
+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<MediaKeySessionType>& supportedSessionTypes, Ref<CDM>&& implementation, Ref<CDMInstance>&& instance)
+ : m_useDistinctiveIdentifier(useDistinctiveIdentifier)
+ , m_persistentStateAllowed(persistentStateAllowed)
+ , m_supportedSessionTypes(supportedSessionTypes)
+ , m_implementation(WTFMove(implementation))
+ , m_instance(WTFMove(instance))
+{
+}
+
+MediaKeys::~MediaKeys() = default;
+
+ExceptionOr<Ref<MediaKeySession>> 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<DeferredPromise>&& 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<IDLBoolean>(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<IDLBoolean>(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 <wtf/Ref.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class CDM;
+class CDMInstance;
+class BufferSource;
+class MediaKeySession;
+
+class MediaKeys : public RefCounted<MediaKeys> {
+public:
+ using KeySessionType = MediaKeySessionType;
+
+ static Ref<MediaKeys> create(bool useDistinctiveIdentifier, bool persistentStateAllowed, const Vector<MediaKeySessionType>& supportedSessionTypes, Ref<CDM>&& implementation, Ref<CDMInstance>&& instance)
+ {
+ return adoptRef(*new MediaKeys(useDistinctiveIdentifier, persistentStateAllowed, supportedSessionTypes, WTFMove(implementation), WTFMove(instance)));
+ }
+
+ ~MediaKeys();
+
+ ExceptionOr<Ref<MediaKeySession>> createSession(ScriptExecutionContext&, MediaKeySessionType);
+
+ void setServerCertificate(const BufferSource&, Ref<DeferredPromise>&&);
+
+protected:
+ MediaKeys(bool useDistinctiveIdentifier, bool persistentStateAllowed, const Vector<MediaKeySessionType>&, Ref<CDM>&&, Ref<CDMInstance>&&);
+
+ bool m_useDistinctiveIdentifier;
+ bool m_persistentStateAllowed;
+ Vector<MediaKeySessionType> m_supportedSessionTypes;
+ Ref<CDM> m_implementation;
+ Ref<CDMInstance> m_instance;
+ GenericTaskQueue<Timer> m_taskQueue;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(ENCRYPTED_MEDIA)
diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionSync.idl b/Source/WebCore/Modules/encryptedmedia/MediaKeys.idl
index 9b2a4f5a0..781850420 100644
--- a/Source/WebCore/Modules/webdatabase/SQLTransactionSync.idl
+++ b/Source/WebCore/Modules/encryptedmedia/MediaKeys.idl
@@ -1,25 +1,23 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * 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:
+ * modification, are permitted provided that the following conditions
+ * are met:
*
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, 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
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * 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
@@ -29,10 +27,10 @@
*/
[
- NoInterfaceObject,
- Conditional=SQL_DATABASE,
- JSNoStaticTables,
+ Conditional=ENCRYPTED_MEDIA,
+ EnabledAtRuntime=EncryptedMediaAPI,
ImplementationLacksVTable,
-] interface SQLTransactionSync {
- [Custom] SQLResultSet executeSql(DOMString sqlStatement, ObjectArray arguments);
+] interface MediaKeys {
+ [CallWith=ScriptExecutionContext, MayThrowException] MediaKeySession createSession(optional MediaKeySessionType sessionType = "temporary");
+ Promise<bool> 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 <wtf/HashSet.h>
+
+namespace WebCore {
+
+struct MediaKeysRestrictions {
+ bool distinctiveIdentifierDenied { false };
+ bool persistentStateDenied { false };
+ HashSet<String> 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<CDM>&& implementation, Vector<MediaKeySystemConfiguration>&& supportedConfigurations, RefPtr<DeferredPromise>&&);
+
+void NavigatorEME::requestMediaKeySystemAccess(Navigator&, Document& document, const String& keySystem, Vector<MediaKeySystemConfiguration>&& supportedConfigurations, Ref<DeferredPromise>&& 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<CDM> implementation = CDM::create(document, keySystem);
+ tryNextSupportedConfiguration(WTFMove(implementation), WTFMove(supportedConfigurations), WTFMove(promise));
+ });
+}
+
+static void tryNextSupportedConfiguration(RefPtr<CDM>&& implementation, Vector<MediaKeySystemConfiguration>&& supportedConfigurations, RefPtr<DeferredPromise>&& 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<MediaKeySystemConfiguration> 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<IDLInterface<MediaKeySystemAccess>>(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<Page> {
+public:
+ static void requestMediaKeySystemAccess(Navigator&, Document&, const String&, Vector<MediaKeySystemConfiguration>&&, Ref<DeferredPromise>&&);
+};
+
+} // 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<MediaKeySystemAccess> requestMediaKeySystemAccess(DOMString keySystem, sequence<MediaKeySystemConfiguration> 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 <wtf/NeverDestroyed.h>
+#include <wtf/text/WTFString.h>
+
+#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<CDMFactory*>& installedCDMFactories()
+{
+ static NeverDestroyed<Vector<CDMFactory*>> cdms;
+ static bool queriedCDMs = false;
+ if (!queriedCDMs) {
+ queriedCDMs = true;
+
+ cdms.get().append(new CDMFactory([](CDM* cdm) { return std::make_unique<CDMPrivateClearKey>(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<CDMPrivateMediaPlayer>(cdm); },
+ CDMPrivateMediaPlayer::supportsKeySystem, CDMPrivateMediaPlayer::supportsKeySystemAndMimeType));
+
+#if PLATFORM(MAC) && ENABLE(MEDIA_SOURCE)
+ cdms.get().append(new CDMFactory([](CDM* cdm) { return std::make_unique<CDMPrivateMediaSourceAVFObjC>(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> CDM::create(const String& keySystem)
+{
+ if (!supportsKeySystem(keySystem))
+ return nullptr;
+
+ return std::make_unique<CDM>(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<CDMSession> 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 <runtime/Uint8Array.h>
+#include <wtf/Forward.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class CDM;
+class CDMPrivateInterface;
+class MediaPlayer;
+
+typedef std::function<std::unique_ptr<CDMPrivateInterface> (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<CDM> create(const String& keySystem);
+ WEBCORE_EXPORT static void registerCDMFactory(CreateCDM, CDMSupportsKeySystem, CDMSupportsKeySystemAndMimeType);
+ ~CDM();
+
+ bool supportsMIMEType(const String&) const;
+ std::unique_ptr<CDMSession> 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<CDMPrivateInterface> 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 <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class CDMSession;
+class CDMSessionClient;
+
+class CDMPrivateInterface {
+public:
+ CDMPrivateInterface() { }
+ virtual ~CDMPrivateInterface() { }
+
+ virtual bool supportsMIMEType(const String&) = 0;
+
+ virtual std::unique_ptr<CDMSession> 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<CDMSession> CDMPrivateClearKey::createSession(CDMSessionClient* client)
+{
+ return std::make_unique<CDMSessionClearKey>(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<CDMSession> 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<CDMSession> 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<CDMSession> 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 <runtime/JSGlobalObject.h>
+#include <runtime/JSLock.h>
+#include <runtime/JSONObject.h>
+#include <runtime/VM.h>
+#include <wtf/NeverDestroyed.h>
+#include <wtf/text/Base64.h>
+
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
+
+using namespace JSC;
+
+namespace WebCore {
+
+static VM& clearKeyVM()
+{
+ static NeverDestroyed<RefPtr<VM>> vm;
+ if (!vm.get())
+ vm.get() = VM::create();
+
+ return *vm.get();
+}
+
+CDMSessionClearKey::CDMSessionClearKey(CDMSessionClient* client)
+ : m_client(client)
+ , m_sessionId(createCanonicalUUIDString())
+{
+}
+
+CDMSessionClearKey::~CDMSessionClearKey()
+{
+}
+
+RefPtr<Uint8Array> 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<char*>(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<Uint8Array>& 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<uint8_t> 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<ArrayBuffer> CDMSessionClearKey::cachedKeyForKeyID(const String& keyId) const
+{
+ if (!m_cachedKeys.contains(keyId))
+ return nullptr;
+
+ auto keyData = m_cachedKeys.get(keyId);
+ RefPtr<Uint8Array> 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 <wtf/HashMap.h>
+#include <wtf/text/WTFString.h>
+
+#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<Uint8Array> generateKeyRequest(const String& mimeType, Uint8Array*, String&, unsigned short&, uint32_t&) override;
+ void releaseKeys() override;
+ bool update(Uint8Array*, RefPtr<Uint8Array>&, unsigned short&, uint32_t&) override;
+ RefPtr<ArrayBuffer> cachedKeyForKeyID(const String&) const override;
+
+protected:
+ CDMSessionClient* m_client;
+ RefPtr<Uint8Array> m_initData;
+ HashMap<String, Vector<uint8_t>> 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 <runtime/Uint8Array.h>
+
+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<WebKitMediaKeyMessageEvent> create(const AtomicString& type, Uint8Array* message, const String& destinationURL)
+ {
+ return adoptRef(*new WebKitMediaKeyMessageEvent(type, message, destinationURL));
+ }
+
+ struct Init : EventInit {
+ RefPtr<Uint8Array> message;
+ String destinationURL;
+ };
+
+ static Ref<WebKitMediaKeyMessageEvent> 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<Uint8Array> 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/webdatabase/DatabaseBackendContext.cpp b/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyNeededEvent.cpp
index 8a19360fe..e538c1cce 100644
--- a/Source/WebCore/Modules/webdatabase/DatabaseBackendContext.cpp
+++ b/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyNeededEvent.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * 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
@@ -24,32 +24,35 @@
*/
#include "config.h"
-#include "DatabaseBackendContext.h"
+#include "WebKitMediaKeyNeededEvent.h"
-#if ENABLE(SQL_DATABASE)
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
-#include "ScriptExecutionContext.h"
+#include <runtime/Uint8Array.h>
namespace WebCore {
-DatabaseContext* DatabaseBackendContext::frontend()
+WebKitMediaKeyNeededEvent::WebKitMediaKeyNeededEvent(const AtomicString& type, Uint8Array* initData)
+ : Event(type, false, false)
+ , m_initData(initData)
{
- // FIXME: Currently, we're only simulating the frontend by return the
- // backend context as its own the frontend. When we split the 2 apart, this
- // create() function should be changed to return a cached m_frontend.
- return static_cast<DatabaseContext*>(this);
}
-SecurityOrigin* DatabaseBackendContext::securityOrigin() const
+WebKitMediaKeyNeededEvent::WebKitMediaKeyNeededEvent(const AtomicString& type, const Init& initializer, IsTrusted isTrusted)
+ : Event(type, initializer, isTrusted)
+ , m_initData(initializer.initData)
{
- return m_scriptExecutionContext->securityOrigin();
}
-bool DatabaseBackendContext::isContextThread() const
+WebKitMediaKeyNeededEvent::~WebKitMediaKeyNeededEvent()
{
- return m_scriptExecutionContext->isContextThread();
+}
+
+EventInterface WebKitMediaKeyNeededEvent::eventInterface() const
+{
+ return WebKitMediaKeyNeededEventInterfaceType;
}
} // namespace WebCore
-#endif // ENABLE(SQL_DATABASE)
+#endif
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseBackendSync.h b/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyNeededEvent.h
index a976f7f18..de487d5df 100644
--- a/Source/WebCore/Modules/webdatabase/DatabaseBackendSync.h
+++ b/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeyNeededEvent.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * 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
@@ -23,38 +23,44 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DatabaseBackendSync_h
-#define DatabaseBackendSync_h
+#pragma once
-#if ENABLE(SQL_DATABASE)
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
-#include "DatabaseBackendBase.h"
-#include <wtf/text/WTFString.h>
+#include "Event.h"
+#include "WebKitMediaKeyError.h"
namespace WebCore {
-class DatabaseServer;
+class WebKitMediaKeyNeededEvent : public Event {
+public:
+ virtual ~WebKitMediaKeyNeededEvent();
-// FIXME: This implementation of DatabaseBackendSync is only a place holder
-// for the split out of the DatabaseSync backend to be done later. This
-// place holder is needed to allow other code that need to reference the
-// DatabaseBackendSync to do so before the proper backend split is
-// available. This should be replaced with the actual implementation later.
+ static Ref<WebKitMediaKeyNeededEvent> create(const AtomicString& type, Uint8Array* initData)
+ {
+ return adoptRef(*new WebKitMediaKeyNeededEvent(type, initData));
+ }
-class DatabaseBackendSync : public DatabaseBackendBase {
-public:
- virtual ~DatabaseBackendSync();
+ struct Init : EventInit {
+ RefPtr<Uint8Array> initData;
+ };
+
+ static Ref<WebKitMediaKeyNeededEvent> create(const AtomicString& type, const Init& initializer, IsTrusted isTrusted = IsTrusted::No)
+ {
+ return adoptRef(*new WebKitMediaKeyNeededEvent(type, initializer, isTrusted));
+ }
- virtual bool openAndVerifyVersion(bool setVersionInNewDatabase, DatabaseError&, String& errorMessage);
+ EventInterface eventInterface() const override;
-protected:
- DatabaseBackendSync(PassRefPtr<DatabaseBackendContext>, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize);
+ Uint8Array* initData() const { return m_initData.get(); }
- friend class DatabaseServer;
+private:
+ WebKitMediaKeyNeededEvent(const AtomicString& type, Uint8Array* initData);
+ WebKitMediaKeyNeededEvent(const AtomicString& type, const Init&, IsTrusted);
+
+ RefPtr<Uint8Array> m_initData;
};
} // namespace WebCore
-#endif // ENABLE(SQL_DATABASE)
-
-#endif // DatabaseBackendSync_h
+#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> 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<ArrayBuffer> 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<Uint8Array>&& 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<void> WebKitMediaKeySession::update(Ref<Uint8Array>&& key)
+{
+ // From <http://dvcs.w3.org/hg/html-media/raw-file/tip/encrypted-media/encrypted-media.html#dom-addkey>:
+ // 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<Uint8Array> 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<Document>(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 <runtime/Uint8Array.h>
+#include <wtf/Deque.h>
+
+namespace WebCore {
+
+class WebKitMediaKeyError;
+class WebKitMediaKeys;
+
+class WebKitMediaKeySession final : public RefCounted<WebKitMediaKeySession>, public EventTargetWithInlineData, private ActiveDOMObject, private CDMSessionClient {
+public:
+ static Ref<WebKitMediaKeySession> 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<void> update(Ref<Uint8Array>&& key);
+ void close();
+
+ CDMSession* session() { return m_session.get(); }
+
+ void detachKeys() { m_keys = nullptr; }
+
+ void generateKeyRequest(const String& mimeType, Ref<Uint8Array>&& initData);
+ RefPtr<ArrayBuffer> 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<WebKitMediaKeyError> m_error;
+ GenericEventQueue m_asyncEventQueue;
+ std::unique_ptr<CDMSession> m_session;
+
+ struct PendingKeyRequest {
+ String mimeType;
+ Ref<Uint8Array> initData;
+ };
+ Deque<PendingKeyRequest> m_pendingKeyRequests;
+ Timer m_keyRequestTimer;
+
+ Deque<Ref<Uint8Array>> 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<Ref<WebKitMediaKeys>> WebKitMediaKeys::create(const String& keySystem)
+{
+ // From <http://dvcs.w3.org/hg/html-media/raw-file/tip/encrypted-media/encrypted-media.html#dom-media-keys-constructor>:
+ // 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>&& cdm)
+ : m_keySystem(keySystem)
+ , m_cdm(WTFMove(cdm))
+{
+ m_cdm->setClient(this);
+}
+
+WebKitMediaKeys::~WebKitMediaKeys()
+{
+ // From <http://dvcs.w3.org/hg/html-media/raw-file/tip/encrypted-media/encrypted-media.html#dom-media-keys-constructor>:
+ // When destroying a MediaKeys object, follow the steps in close().
+ for (auto& session : m_sessions) {
+ session->close();
+ session->detachKeys();
+ }
+}
+
+ExceptionOr<Ref<WebKitMediaKeySession>> WebKitMediaKeys::createSession(ScriptExecutionContext& context, const String& type, Ref<Uint8Array>&& initData)
+{
+ // From <http://www.w3.org/TR/2014/WD-encrypted-media-20140218/#dom-createsession>:
+ // 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<ArrayBuffer> 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 <runtime/Uint8Array.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class HTMLMediaElement;
+class ScriptExecutionContext;
+class WebKitMediaKeySession;
+
+class WebKitMediaKeys final : public RefCounted<WebKitMediaKeys>, private CDMClient {
+public:
+ static ExceptionOr<Ref<WebKitMediaKeys>> create(const String& keySystem);
+ virtual ~WebKitMediaKeys();
+
+ ExceptionOr<Ref<WebKitMediaKeySession>> createSession(ScriptExecutionContext&, const String& mimeType, Ref<Uint8Array>&& 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<ArrayBuffer> cachedKeyForKeyId(const String& keyId) const;
+
+private:
+ MediaPlayer* cdmMediaPlayer(const CDM*) const final;
+
+ WebKitMediaKeys(const String& keySystem, std::unique_ptr<CDM>&&);
+
+ Vector<Ref<WebKitMediaKeySession>> m_sessions;
+ HTMLMediaElement* m_mediaElement { nullptr };
+ String m_keySystem;
+ std::unique_ptr<CDM> 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<DeferredPromise>&& 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 <wtf/Forward.h>
+
+namespace WebCore {
+
+class DOMWindow;
+class FetchRequest;
+
+class DOMWindowFetch {
+public:
+ static void fetch(DOMWindow&, FetchRequest&, Ref<DeferredPromise>&&);
+};
+
+} // 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<Response> fetch(any input, optional RequestInit init);
+ [PrivateIdentifier, ImplementedAs=fetch] Promise<Response> 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 <runtime/ArrayBufferView.h>
+
+namespace WebCore {
+
+std::optional<FetchBody> 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<Document&>(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<DeferredPromise>&& promise)
+{
+ m_consumer.setType(FetchBodyConsumer::Type::ArrayBuffer);
+ consume(owner, WTFMove(promise));
+}
+
+void FetchBody::blob(FetchBodyOwner& owner, Ref<DeferredPromise>&& 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<DeferredPromise>&& promise)
+{
+ if (isText()) {
+ fulfillPromiseWithJSON(WTFMove(promise), textBody());
+ return;
+ }
+ m_consumer.setType(FetchBodyConsumer::Type::JSON);
+ consume(owner, WTFMove(promise));
+}
+
+void FetchBody::text(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise)
+{
+ if (isText()) {
+ promise->resolve<IDLDOMString>(textBody());
+ return;
+ }
+ m_consumer.setType(FetchBodyConsumer::Type::Text);
+ consume(owner, WTFMove(promise));
+}
+
+void FetchBody::consumeOnceLoadingFinished(FetchBodyConsumer::Type type, Ref<DeferredPromise>&& promise)
+{
+ m_consumer.setType(type);
+ m_consumePromise = WTFMove(promise);
+}
+
+void FetchBody::consume(FetchBodyOwner& owner, Ref<DeferredPromise>&& 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<DeferredPromise>&& promise)
+{
+ m_consumer.resolveWithData(WTFMove(promise), static_cast<const uint8_t*>(arrayBufferBody().data()), arrayBufferBody().byteLength());
+ m_data = nullptr;
+}
+
+void FetchBody::consumeArrayBufferView(Ref<DeferredPromise>&& promise)
+{
+ m_consumer.resolveWithData(WTFMove(promise), static_cast<const uint8_t*>(arrayBufferViewBody().baseAddress()), arrayBufferViewBody().byteLength());
+ m_data = nullptr;
+}
+
+void FetchBody::consumeText(Ref<DeferredPromise>&& promise, const String& text)
+{
+ auto data = UTF8Encoding().encode(text, EntitiesForUnencodables);
+ m_consumer.resolveWithData(WTFMove(promise), reinterpret_cast<const uint8_t*>(data.data()), data.length());
+ m_data = nullptr;
+}
+
+void FetchBody::consumeBlob(FetchBodyOwner& owner, Ref<DeferredPromise>&& 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<FormData> 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<FormData> 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<FormData> body = const_cast<FormData*>(&formDataBody());
+ body->generateFiles(static_cast<Document*>(&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<FormData&>(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 <wtf/Optional.h>
+#include <wtf/Variant.h>
+
+namespace JSC {
+class ExecState;
+class JSValue;
+};
+
+namespace WebCore {
+
+class FetchBodyOwner;
+class FetchResponseSource;
+class ScriptExecutionContext;
+
+class FetchBody {
+public:
+ void arrayBuffer(FetchBodyOwner&, Ref<DeferredPromise>&&);
+ void blob(FetchBodyOwner&, Ref<DeferredPromise>&&, const String&);
+ void json(FetchBodyOwner&, Ref<DeferredPromise>&&);
+ void text(FetchBodyOwner&, Ref<DeferredPromise>&&);
+ void formData(FetchBodyOwner&, Ref<DeferredPromise>&& promise) { promise.get().reject(0); }
+
+#if ENABLE(READABLE_STREAM_API)
+ void consumeAsStream(FetchBodyOwner&, FetchResponseSource&);
+#endif
+
+ bool isBlob() const { return WTF::holds_alternative<Ref<const Blob>>(m_data); }
+ bool isFormData() const { return WTF::holds_alternative<Ref<FormData>>(m_data); }
+ bool isArrayBuffer() const { return WTF::holds_alternative<Ref<const ArrayBuffer>>(m_data); }
+ bool isArrayBufferView() const { return WTF::holds_alternative<Ref<const ArrayBufferView>>(m_data); }
+ bool isURLSearchParams() const { return WTF::holds_alternative<Ref<const URLSearchParams>>(m_data); }
+ bool isText() const { return WTF::holds_alternative<String>(m_data); }
+ bool isReadableStream() const { return m_isReadableStream; }
+
+ static std::optional<FetchBody> extract(ScriptExecutionContext&, JSC::ExecState&, JSC::JSValue, String&);
+ static FetchBody loadingBody() { return { }; }
+
+ void loadingFailed();
+ void loadingSucceeded();
+
+ RefPtr<FormData> bodyForInternalRequest(ScriptExecutionContext&) const;
+
+ FetchBodyConsumer& consumer() { return m_consumer; }
+
+ void consumeOnceLoadingFinished(FetchBodyConsumer::Type, Ref<DeferredPromise>&&);
+ void cleanConsumePromise() { m_consumePromise = nullptr; }
+
+ FetchBody clone() const;
+
+private:
+ explicit FetchBody(Ref<const Blob>&& data) : m_data(WTFMove(data)) { }
+ explicit FetchBody(Ref<const ArrayBuffer>&& data) : m_data(WTFMove(data)) { }
+ explicit FetchBody(Ref<const ArrayBufferView>&& data) : m_data(WTFMove(data)) { }
+ explicit FetchBody(Ref<FormData>&& data) : m_data(WTFMove(data)) { }
+ explicit FetchBody(String&& data) : m_data(WTFMove(data)) { }
+ explicit FetchBody(Ref<const URLSearchParams>&& data) : m_data(WTFMove(data)) { }
+ explicit FetchBody(const FetchBodyConsumer& consumer) : m_consumer(consumer) { }
+ FetchBody() = default;
+
+ void consume(FetchBodyOwner&, Ref<DeferredPromise>&&);
+
+ void consumeArrayBuffer(Ref<DeferredPromise>&&);
+ void consumeArrayBufferView(Ref<DeferredPromise>&&);
+ void consumeText(Ref<DeferredPromise>&&, const String&);
+ void consumeBlob(FetchBodyOwner&, Ref<DeferredPromise>&&);
+
+ const Blob& blobBody() const { return WTF::get<Ref<const Blob>>(m_data).get(); }
+ FormData& formDataBody() { return WTF::get<Ref<FormData>>(m_data).get(); }
+ const FormData& formDataBody() const { return WTF::get<Ref<FormData>>(m_data).get(); }
+ const ArrayBuffer& arrayBufferBody() const { return WTF::get<Ref<const ArrayBuffer>>(m_data).get(); }
+ const ArrayBufferView& arrayBufferViewBody() const { return WTF::get<Ref<const ArrayBufferView>>(m_data).get(); }
+ String& textBody() { return WTF::get<String>(m_data); }
+ const String& textBody() const { return WTF::get<String>(m_data); }
+ const URLSearchParams& urlSearchParamsBody() const { return WTF::get<Ref<const URLSearchParams>>(m_data).get(); }
+
+ Variant<std::nullptr_t, Ref<const Blob>, Ref<FormData>, Ref<const ArrayBuffer>, Ref<const ArrayBufferView>, Ref<const URLSearchParams>, String> m_data { nullptr };
+
+ FetchBodyConsumer m_consumer { FetchBodyConsumer::Type::None };
+ RefPtr<DeferredPromise> 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> arrayBuffer();
+ [NewObject] Promise<Blob> blob();
+ // FIXME: Add support for form data consumption (https://bugs.webkit.org/show_bug.cgi?id=161190).
+ //[NewObject] Promise<DOMFormData> formData();
+ [NewObject] Promise<any> json();
+ [NewObject] Promise<USVString> 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<Blob> blobFromData(const unsigned char* data, unsigned length, const String& contentType)
+{
+ Vector<uint8_t> 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<const char*>(data), length);
+}
+
+void FetchBodyConsumer::resolveWithData(Ref<DeferredPromise>&& promise, const unsigned char* data, unsigned length)
+{
+ switch (m_type) {
+ case Type::ArrayBuffer:
+ fulfillPromiseWithArrayBuffer(WTFMove(promise), data, length);
+ return;
+ case Type::Blob:
+ promise->resolveWithNewlyCreated<IDLInterface<Blob>>(blobFromData(data, length, m_contentType).get());
+ return;
+ case Type::JSON:
+ fulfillPromiseWithJSON(WTFMove(promise), textFromUTF8(data, length));
+ return;
+ case Type::Text:
+ promise->resolve<IDLDOMString>(textFromUTF8(data, length));
+ return;
+ case Type::None:
+ ASSERT_NOT_REACHED();
+ return;
+ }
+}
+
+void FetchBodyConsumer::resolve(Ref<DeferredPromise>&& promise)
+{
+ ASSERT(m_type != Type::None);
+ switch (m_type) {
+ case Type::ArrayBuffer:
+ fulfillPromiseWithArrayBuffer(WTFMove(promise), takeAsArrayBuffer().get());
+ return;
+ case Type::Blob:
+ promise->resolveWithNewlyCreated<IDLInterface<Blob>>(takeAsBlob().get());
+ return;
+ case Type::JSON:
+ fulfillPromiseWithJSON(WTFMove(promise), takeAsText());
+ return;
+ case Type::Text:
+ promise->resolve<IDLDOMString>(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<const char*>(data), length);
+}
+
+RefPtr<SharedBuffer> FetchBodyConsumer::takeData()
+{
+ return WTFMove(m_buffer);
+}
+
+RefPtr<JSC::ArrayBuffer> FetchBodyConsumer::takeAsArrayBuffer()
+{
+ if (!m_buffer)
+ return ArrayBuffer::tryCreate(nullptr, 0);
+
+ auto arrayBuffer = m_buffer->createArrayBuffer();
+ m_buffer = nullptr;
+ return arrayBuffer;
+}
+
+Ref<Blob> FetchBodyConsumer::takeAsBlob()
+{
+ if (!m_buffer)
+ return Blob::create(Vector<uint8_t>(), m_contentType);
+
+ // FIXME: We should try to move m_buffer to Blob without doing extra copy.
+ return blobFromData(reinterpret_cast<const unsigned char*>(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<const unsigned char*>(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<SharedBuffer> takeData();
+ RefPtr<JSC::ArrayBuffer> takeAsArrayBuffer();
+ Ref<Blob> 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<DeferredPromise>&&);
+ void resolveWithData(Ref<DeferredPromise>&&, const unsigned char*, unsigned);
+
+ bool hasData() const { return !!m_buffer; }
+
+private:
+ Type m_type;
+ String m_contentType;
+ RefPtr<SharedBuffer> 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<FetchBody>&& body, Ref<FetchHeaders>&& 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<DeferredPromise>&& 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<DeferredPromise>&& promise)
+{
+ if (isBodyNull()) {
+ promise->resolve<IDLInterface<Blob>>(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<DeferredPromise>&& promise)
+{
+ if (isDisturbedOrLocked()) {
+ promise->reject(TypeError);
+ return;
+ }
+ m_isDisturbed = true;
+ m_body->consumeOnceLoadingFinished(type, WTFMove(promise));
+}
+
+void FetchBodyOwner::formData(Ref<DeferredPromise>&& 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<DeferredPromise>&& 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<DeferredPromise>&& promise)
+{
+ if (isBodyNull()) {
+ promise->resolve<IDLDOMString>({ });
+ 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<FetchLoader>(*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<FetchBodyOwner>, public ActiveDOMObject {
+public:
+ FetchBodyOwner(ScriptExecutionContext&, std::optional<FetchBody>&&, Ref<FetchHeaders>&&);
+
+ // Exposed Body API
+ bool isDisturbed() const { return m_isDisturbed; };
+
+ void arrayBuffer(Ref<DeferredPromise>&&);
+ void blob(Ref<DeferredPromise>&&);
+ void formData(Ref<DeferredPromise>&&);
+ void json(Ref<DeferredPromise>&&);
+ void text(Ref<DeferredPromise>&&);
+
+ 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<DeferredPromise>&&);
+
+ // 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<FetchLoader> loader;
+ };
+
+protected:
+ std::optional<FetchBody> m_body;
+ String m_contentType;
+ bool m_isDisturbed { false };
+#if ENABLE(READABLE_STREAM_API)
+ RefPtr<FetchResponseSource> m_readableStreamSource;
+#endif
+ Ref<FetchHeaders> m_headers;
+
+private:
+ std::optional<BlobLoader> 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<bool> 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<void> 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<void> 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<String> FetchHeaders::get(const String& name) const
+{
+ if (!isValidHTTPToken(name))
+ return Exception { TypeError };
+ return m_headers.get(name);
+}
+
+ExceptionOr<bool> FetchHeaders::has(const String& name) const
+{
+ if (!isValidHTTPToken(name))
+ return Exception { TypeError };
+ return m_headers.contains(name);
+}
+
+ExceptionOr<void> 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<WTF::KeyValuePair<String, String>> 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<String, String> { 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 <wtf/HashTraits.h>
+
+namespace WebCore {
+
+class FetchHeaders : public RefCounted<FetchHeaders> {
+public:
+ enum class Guard {
+ None,
+ Immutable,
+ Request,
+ RequestNoCors,
+ Response
+ };
+
+ static Ref<FetchHeaders> create(Guard guard = Guard::None) { return adoptRef(*new FetchHeaders { guard }); }
+ static Ref<FetchHeaders> create(const FetchHeaders& headers) { return adoptRef(*new FetchHeaders { headers }); }
+
+ ExceptionOr<void> append(const String& name, const String& value);
+ ExceptionOr<void> remove(const String&);
+ ExceptionOr<String> get(const String&) const;
+ ExceptionOr<bool> has(const String&) const;
+ ExceptionOr<void> 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<WTF::KeyValuePair<String, String>> next();
+
+ private:
+ Ref<FetchHeaders> m_headers;
+ size_t m_currentIndex { 0 };
+ Vector<String> 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<DOMString, DOMString>;
+
+ [ImplementedAs=append, MayThrowException, PrivateIdentifier] void appendFromJS(DOMString name, DOMString value);
+ [ImplementedAs=fill, PrivateIdentifier] void fillFromJS(FetchHeaders? headers);
+};
diff --git a/Source/WebCore/Modules/mediastream/SourceInfo.idl b/Source/WebCore/Modules/fetch/FetchHeaders.js
index d238f2d06..e8c2d7598 100644
--- a/Source/WebCore/Modules/mediastream/SourceInfo.idl
+++ b/Source/WebCore/Modules/fetch/FetchHeaders.js
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Google Inc. All rights reserved.
+ * Copyright (C) 2016 Canon Inc.
*
* 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 GOOGLE INC. ``AS IS'' AND ANY
+ * 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 GOOGLE INC. OR
+ * 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
@@ -23,12 +23,19 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-[
- ImplementationLacksVTable,
- NoInterfaceObject,
- Conditional=MEDIA_STREAM,
-] interface SourceInfo {
- readonly attribute DOMString sourceId;
- readonly attribute DOMString kind;
- readonly attribute DOMString label;
-};
+// @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<SharedBuffer> 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<SharedBuffer> 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<ThreadableLoader> 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 <wtf/Forward.h>
+
+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<Exception> 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<Exception> 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<Exception> 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<FetchHeaders&> 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<FetchHeaders&> 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<FetchHeaders&> 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<void> 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<Ref<FetchRequest>> 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 <wtf/Optional.h>
+
+namespace WebCore {
+
+class Blob;
+class ScriptExecutionContext;
+class URLSearchParams;
+
+class FetchRequest final : public FetchBodyOwner {
+public:
+ static Ref<FetchRequest> 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> referrerPolicy;
+ std::optional<Mode> mode;
+ std::optional<Credentials> credentials;
+ std::optional<Cache> cache;
+ std::optional<Redirect> redirect;
+ String integrity;
+ JSC::JSValue window;
+ };
+
+ ExceptionOr<FetchHeaders&> initializeWith(FetchRequest&, const Init&);
+ ExceptionOr<FetchHeaders&> initializeWith(const String&, const Init&);
+ ExceptionOr<void> 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<Ref<FetchRequest>> 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<FetchBody>&&, Ref<FetchHeaders>&&, InternalRequest&&);
+
+ ExceptionOr<FetchHeaders&> 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<FetchBody>&& body, Ref<FetchHeaders>&& 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> FetchResponse::error(ScriptExecutionContext& context)
+{
+ auto response = adoptRef(*new FetchResponse(context, { }, FetchHeaders::create(FetchHeaders::Guard::Immutable), { }));
+ response->m_response.setType(Type::Error);
+ return response;
+}
+
+ExceptionOr<Ref<FetchResponse>> 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<void> 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<FetchBody>&& body, Ref<FetchHeaders>&& headers, ResourceResponse&& response)
+ : FetchBodyOwner(context, WTFMove(body), WTFMove(headers))
+ , m_response(WTFMove(response))
+{
+}
+
+Ref<FetchResponse> 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<FetchResponse> 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<FetchResponse> 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<FetchLoader>(*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<DeferredPromise>&& wrapper)
+{
+ ASSERT(type <= static_cast<unsigned>(FetchBodyConsumer::Type::Text));
+ auto consumerType = static_cast<FetchBodyConsumer::Type>(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<FetchBodyConsumer::Type>(type));
+}
+
+void FetchResponse::consumeChunk(Ref<JSC::Uint8Array>&& chunk)
+{
+ m_consumer.append(chunk->data(), chunk->byteLength());
+}
+
+void FetchResponse::finishConsumingStream(Ref<DeferredPromise>&& 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<SharedBuffer> 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<SharedBuffer> FetchResponse::BodyLoader::startStreaming()
+{
+ ASSERT(m_loader);
+ return m_loader->startStreaming();
+}
+
+void FetchResponse::cancel()
+{
+ m_isDisturbed = true;
+ stop();
+}
+
+#endif
+
+void FetchResponse::stop()
+{
+ RefPtr<FetchResponse> 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 <runtime/TypedArrays.h>
+
+namespace JSC {
+class ExecState;
+class JSValue;
+};
+
+namespace WebCore {
+
+class FetchRequest;
+class ReadableStreamSource;
+
+class FetchResponse final : public FetchBodyOwner {
+public:
+ using Type = ResourceResponse::Type;
+
+ static Ref<FetchResponse> create(ScriptExecutionContext& context) { return adoptRef(*new FetchResponse(context, std::nullopt, FetchHeaders::create(FetchHeaders::Guard::Response), ResourceResponse())); }
+ static Ref<FetchResponse> error(ScriptExecutionContext&);
+ static ExceptionOr<Ref<FetchResponse>> redirect(ScriptExecutionContext&, const String& url, int status);
+
+ using FetchPromise = DOMPromise<IDLInterface<FetchResponse>>;
+ static void fetch(ScriptExecutionContext&, FetchRequest&, FetchPromise&&);
+
+ void consume(unsigned, Ref<DeferredPromise>&&);
+#if ENABLE(READABLE_STREAM_API)
+ void startConsumingStream(unsigned);
+ void consumeChunk(Ref<JSC::Uint8Array>&&);
+ void finishConsumingStream(Ref<DeferredPromise>&&);
+#endif
+
+ ExceptionOr<void> 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<FetchResponse> 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<FetchBody>&&, Ref<FetchHeaders>&&, 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<SharedBuffer> 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<FetchPromise> m_promise;
+ std::unique_ptr<FetchLoader> m_loader;
+ };
+
+ ResourceResponse m_response;
+ std::optional<BodyLoader> 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> arrayBuffer();
+ [JSBuiltin] Promise<Blob> blob();
+ [JSBuiltin] Promise<Blob> formData();
+ [JSBuiltin] Promise<any> json();
+ [JSBuiltin] Promise<USVString> 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<any> finishConsumingStream();
+
+ [PrivateIdentifier] Promise<any> 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<JSC::ArrayBuffer>&& 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<DeferredPromise>&& 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<DeferredPromise>&&);
+};
+
+} // 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<Response> fetch(any input, optional RequestInit init);
+ [PrivateIdentifier, ImplementedAs=fetch] Promise<Response> 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 <wtf/text/WTFString.h>
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<double>& Gamepad::axes() const
{
- m_buttons.resize(count);
- if (count)
- std::copy(data, data + count, m_buttons.begin());
+ return m_axes;
}
-Gamepad::~Gamepad()
+const Vector<Ref<GamepadButton>>& 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<Gamepad> {
public:
- static PassRefPtr<Gamepad> create()
+ static Ref<Gamepad> create(const PlatformGamepad& platformGamepad)
{
- return adoptRef(new Gamepad);
+ return adoptRef(*new Gamepad(platformGamepad));
}
~Gamepad();
- typedef Vector<float> 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<double>& axes() const;
+ const Vector<Ref<GamepadButton>>& 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<double> m_axes;
+ Vector<Ref<GamepadButton>> 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<double> axes;
+ readonly attribute sequence<GamepadButton> 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 <wtf/Ref.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class GamepadButton : public RefCounted<GamepadButton> {
+public:
+ static Ref<GamepadButton> 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/indexeddb/IDBPendingOpenCall.h b/Source/WebCore/Modules/gamepad/GamepadEvent.h
index 193ceeada..d43da58a9 100644
--- a/Source/WebCore/Modules/indexeddb/IDBPendingOpenCall.h
+++ b/Source/WebCore/Modules/gamepad/GamepadEvent.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Apple 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
@@ -23,45 +23,45 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef IDBPendingOpenCall_h
-#define IDBPendingOpenCall_h
+#pragma once
-#include <wtf/PassOwnPtr.h>
-#include <wtf/RefPtr.h>
+#if ENABLE(GAMEPAD)
-#if ENABLE(INDEXED_DATABASE)
+#include "Event.h"
+#include "Gamepad.h"
+#include <wtf/RefPtr.h>
namespace WebCore {
-class IDBCallbacks;
-class IDBDatabaseCallbacks;
-
-class IDBPendingOpenCall {
+class GamepadEvent : public Event {
public:
- static PassOwnPtr<IDBPendingOpenCall> create(IDBCallbacks& callbacks, IDBDatabaseCallbacks& databaseCallbacks, int64_t transactionId, uint64_t version)
+ ~GamepadEvent() { }
+
+ static Ref<GamepadEvent> create(const AtomicString& eventType, Gamepad& gamepad)
{
- return adoptPtr(new IDBPendingOpenCall(callbacks, databaseCallbacks, transactionId, version));
+ return adoptRef(*new GamepadEvent(eventType, gamepad));
}
- 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)
+ struct Init : EventInit {
+ RefPtr<Gamepad> gamepad;
+ };
+
+ static Ref<GamepadEvent> create(const AtomicString& eventType, const Init& initializer, IsTrusted isTrusted = IsTrusted::No)
{
+ return adoptRef(*new GamepadEvent(eventType, initializer, isTrusted));
}
- RefPtr<IDBCallbacks> m_callbacks;
- RefPtr<IDBDatabaseCallbacks> m_databaseCallbacks;
- uint64_t m_version;
- const int64_t m_transactionId;
+
+ 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<Gamepad> m_gamepad;
};
} // namespace WebCore
-#endif // ENABLE(INDEXED_DATABASE)
-#endif // IDBPendingOpenCall_h
+#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/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<GamepadManager> 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<WeakPtr<DOMWindow>> weakWindows;
+ for (auto* domWindow : m_domWindows)
+ weakWindows.append(domWindow->createWeakPtr());
+
+ HashSet<NavigatorGamepad*> 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> 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<NavigatorGamepad*>& navigatorSet, HashSet<DOMWindow*>& domWindowSet)
+{
+ if (navigatorSet.isEmpty() && domWindowSet.isEmpty())
+ return;
+
+ for (auto* navigator : navigatorSet)
+ navigator->gamepadConnected(platformGamepad);
+
+ Vector<WeakPtr<DOMWindow>> 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> 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 <wtf/HashSet.h>
+#include <wtf/NeverDestroyed.h>
+#include <wtf/RefPtr.h>
+#include <wtf/text/AtomicString.h>
+
+namespace WebCore {
+
+class DOMWindow;
+class Gamepad;
+class NavigatorGamepad;
+
+class GamepadManager : public GamepadProviderClient {
+ WTF_MAKE_NONCOPYABLE(GamepadManager);
+ friend class NeverDestroyed<GamepadManager>;
+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<NavigatorGamepad*>&, HashSet<DOMWindow*>&);
+ void dispatchGamepadEvent(const WTF::AtomicString& eventName, PlatformGamepad&);
+
+ void maybeStartMonitoringGamepads();
+ void maybeStopMonitoringGamepads();
+
+ bool m_isMonitoringGamepads;
+
+ HashSet<NavigatorGamepad*> m_navigators;
+ HashSet<NavigatorGamepad*> m_gamepadBlindNavigators;
+ HashSet<DOMWindow*> m_domWindows;
+ HashSet<DOMWindow*> 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 <wtf/PassOwnPtr.h>
+#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<NavigatorGamepad*>(Supplement<Navigator>::from(navigator, supplementName()));
if (!supplement) {
- supplement = new NavigatorGamepad();
- provideTo(navigator, supplementName(), adoptPtr(supplement));
+ auto newSupplement = std::make_unique<NavigatorGamepad>();
+ supplement = newSupplement.get();
+ provideTo(navigator, supplementName(), WTFMove(newSupplement));
}
return supplement;
}
-GamepadList* NavigatorGamepad::webkitGetGamepads(Navigator* navigator)
+Ref<Gamepad> 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<RefPtr<Gamepad>>& NavigatorGamepad::getGamepads(Navigator& navigator)
+{
+ return NavigatorGamepad::from(&navigator)->gamepads();
+}
+
+const Vector<RefPtr<Gamepad>>& NavigatorGamepad::gamepads()
+{
+ if (m_gamepads.isEmpty())
+ return m_gamepads;
+
+ const Vector<PlatformGamepad*>& 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<PlatformGamepad*>& 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 <wtf/Vector.h>
namespace WebCore {
-class GamepadList;
+class Gamepad;
class Navigator;
+class PlatformGamepad;
class NavigatorGamepad : public Supplement<Navigator> {
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<RefPtr<Gamepad>>& getGamepads(Navigator&);
+
+ void gamepadConnected(PlatformGamepad&);
+ void gamepadDisconnected(PlatformGamepad&);
- GamepadList* gamepads();
+ Ref<Gamepad> gamepadFromPlatformGamepad(PlatformGamepad&);
private:
- NavigatorGamepad();
static const char* supplementName();
- RefPtr<GamepadList> m_gamepads;
+ void gamepadsBecameVisible();
+
+ const Vector<RefPtr<Gamepad>>& gamepads();
+
+ Vector<RefPtr<Gamepad>> 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<Gamepad> 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 <wtf/text/WTFString.h>
+
+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 <wtf/RefCounted.h>
+#include <wtf/Vector.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class Gamepad: public RefCounted<Gamepad> {
+public:
+ static Ref<Gamepad> create()
+ {
+ return adoptRef(*new Gamepad);
+ }
+ ~Gamepad();
+
+ typedef Vector<float> 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<unrestricted float> axes;
+ readonly attribute sequence<unrestricted float> buttons;
+};
+
diff --git a/Source/WebCore/Modules/gamepad/GamepadList.cpp b/Source/WebCore/Modules/gamepad/deprecated/GamepadList.cpp
index 265022225..22e028867 100644
--- a/Source/WebCore/Modules/gamepad/GamepadList.cpp
+++ b/Source/WebCore/Modules/gamepad/deprecated/GamepadList.cpp
@@ -28,7 +28,7 @@
#include "Gamepad.h"
-#if ENABLE(GAMEPAD)
+#if ENABLE(GAMEPAD_DEPRECATED)
namespace WebCore {
@@ -36,11 +36,11 @@ GamepadList::~GamepadList()
{
}
-void GamepadList::set(unsigned index, PassRefPtr<Gamepad> gamepad)
+void GamepadList::set(unsigned index, RefPtr<Gamepad>&& gamepad)
{
if (index >= kMaximumGamepads)
return;
- m_items[index] = gamepad;
+ m_items[index] = WTFMove(gamepad);
}
unsigned GamepadList::length() const
@@ -55,4 +55,4 @@ Gamepad* GamepadList::item(unsigned index)
} // namespace WebCore
-#endif // ENABLE(GAMEPAD)
+#endif // ENABLE(GAMEPAD_DEPRECATED)
diff --git a/Source/WebCore/Modules/gamepad/GamepadList.h b/Source/WebCore/Modules/gamepad/deprecated/GamepadList.h
index fa6af61da..ab3626b4a 100644
--- a/Source/WebCore/Modules/gamepad/GamepadList.h
+++ b/Source/WebCore/Modules/gamepad/deprecated/GamepadList.h
@@ -23,13 +23,11 @@
* DAMAGE.
*/
-#ifndef GamepadList_h
-#define GamepadList_h
+#pragma once
-#if ENABLE(GAMEPAD)
+#if ENABLE(GAMEPAD_DEPRECATED)
#include "Gamepad.h"
-#include <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>
#include <wtf/Vector.h>
@@ -39,10 +37,10 @@ typedef Vector<RefPtr<Gamepad> > GamepadVector;
class GamepadList : public RefCounted<GamepadList> {
public:
- static PassRefPtr<GamepadList> create() { return adoptRef(new GamepadList); }
+ static Ref<GamepadList> create() { return adoptRef(*new GamepadList); }
~GamepadList();
- void set(unsigned index, PassRefPtr<Gamepad>);
+ void set(unsigned index, RefPtr<Gamepad>&&);
Gamepad* item(unsigned index);
unsigned length() const;
@@ -54,6 +52,4 @@ private:
} // namespace WebCore
-#endif // ENABLE(GAMEPAD)
-
-#endif // GamepadList_h
+#endif // ENABLE(GAMEPAD_DEPRECATED)
diff --git a/Source/WebCore/Modules/gamepad/GamepadList.idl b/Source/WebCore/Modules/gamepad/deprecated/GamepadList.idl
index 4b256afef..9ab4c57fe 100644
--- a/Source/WebCore/Modules/gamepad/GamepadList.idl
+++ b/Source/WebCore/Modules/gamepad/deprecated/GamepadList.idl
@@ -25,10 +25,10 @@
[
NoInterfaceObject,
- Conditional=GAMEPAD,
+ Conditional=GAMEPAD_DEPRECATED,
ImplementationLacksVTable,
] interface GamepadList {
readonly attribute unsigned long length;
- getter Gamepad item([Default=Undefined] optional unsigned long index);
+ 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<NavigatorGamepad*>(Supplement<Navigator>::from(navigator, supplementName()));
+ if (!supplement) {
+ auto newSupplement = std::make_unique<NavigatorGamepad>();
+ 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/indexeddb/PageGroupIndexedDatabase.h b/Source/WebCore/Modules/gamepad/deprecated/NavigatorGamepad.h
index 92a25582d..6f57882d8 100644
--- a/Source/WebCore/Modules/indexeddb/PageGroupIndexedDatabase.h
+++ b/Source/WebCore/Modules/gamepad/deprecated/NavigatorGamepad.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012, Google Inc. All rights reserved.
+ * 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:
@@ -10,7 +10,7 @@
* notice, 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
+ * 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
@@ -23,36 +23,34 @@
* DAMAGE.
*/
-#ifndef PageGroupIndexedDatabase_h
-#define PageGroupIndexedDatabase_h
+#pragma once
-#if ENABLE(INDEXED_DATABASE)
+#if ENABLE(GAMEPAD_DEPRECATED)
#include "Supplementable.h"
-#include <wtf/text/WTFString.h>
namespace WebCore {
-class IDBFactoryBackendInterface;
-class PageGroup;
+class GamepadList;
+class Navigator;
-class PageGroupIndexedDatabase : public Supplement<PageGroup> {
+class NavigatorGamepad : public Supplement<Navigator> {
public:
- virtual ~PageGroupIndexedDatabase();
- static PageGroupIndexedDatabase* from(PageGroup&);
+ NavigatorGamepad();
+ virtual ~NavigatorGamepad();
- IDBFactoryBackendInterface* factoryBackend();
+ static NavigatorGamepad* from(Navigator*);
+
+ static GamepadList* webkitGetGamepads(Navigator&);
+
+ GamepadList* gamepads();
private:
- explicit PageGroupIndexedDatabase(const String& databaseDirectoryIdentifier);
static const char* supplementName();
- String m_databaseDirectoryIdentifier;
- RefPtr<IDBFactoryBackendInterface> m_factoryBackend;
+ RefPtr<GamepadList> m_gamepads;
};
} // namespace WebCore
-#endif // ENABLE(INDEXED_DATABASE)
-
-#endif // PageGroupIndexedDatabase_h
+#endif // ENABLE(GAMEPAD_DEPRECATED)
diff --git a/Source/WebCore/Modules/mediastream/NavigatorMediaStream.idl b/Source/WebCore/Modules/gamepad/deprecated/NavigatorGamepad.idl
index 0cf230796..d19d843ef 100644
--- a/Source/WebCore/Modules/mediastream/NavigatorMediaStream.idl
+++ b/Source/WebCore/Modules/gamepad/deprecated/NavigatorGamepad.idl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Google Inc. All rights reserved.
+ * 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
@@ -18,10 +18,8 @@
*/
[
- Conditional=MEDIA_STREAM,
+ Conditional=GAMEPAD_DEPRECATED,
] partial interface Navigator {
- [RaisesException] void webkitGetUserMedia(Dictionary options,
- NavigatorUserMediaSuccessCallback successCallback,
- optional NavigatorUserMediaErrorCallback errorCallback);
+ 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<double> 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<double> 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<double> 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<double> 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 <wtf/Optional.h>
#include <wtf/RefCounted.h>
namespace WebCore {
class Coordinates : public RefCounted<Coordinates> {
public:
- static PassRefPtr<Coordinates> 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<Coordinates> 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<Coordinates> isolatedCopy() const
+ Ref<Coordinates> 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<double> 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<double> altitudeAccuracy() const;
+ std::optional<double> heading() const;
+ std::optional<double> 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<PositionCallback>&& successCallback, RefPtr<PositionErrorCallback>&& 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<PositionError>&& 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<GeoNotifier> 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<PositionError> 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 <wtf/Forward.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+class Geoposition;
+class Geolocation;
+class PositionCallback;
+class PositionError;
+class PositionErrorCallback;
+
+class GeoNotifier : public RefCounted<GeoNotifier> {
+public:
+ static Ref<GeoNotifier> create(Geolocation& geolocation, Ref<PositionCallback>&& positionCallback, RefPtr<PositionErrorCallback>&& positionErrorCallback, PositionOptions&& options)
+ {
+ return adoptRef(*new GeoNotifier(geolocation, WTFMove(positionCallback), WTFMove(positionErrorCallback), WTFMove(options)));
+ }
+
+ const PositionOptions& options() const { return m_options; }
+ void setFatalError(RefPtr<PositionError>&&);
+
+ 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<PositionCallback>&&, RefPtr<PositionErrorCallback>&&, PositionOptions&&);
+
+ Ref<Geolocation> m_geolocation;
+ Ref<PositionCallback> m_successCallback;
+ RefPtr<PositionErrorCallback> m_errorCallback;
+ PositionOptions m_options;
+ Timer m_timer;
+ RefPtr<PositionError> 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 <wtf/CurrentTime.h>
-#include <wtf/Ref.h>
-
-#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 <wtf/CurrentTime.h>
+#include <wtf/Ref.h>
+#include <wtf/text/StringBuilder.h>
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<Geoposition> createGeoposition(GeolocationPosition* position)
+static RefPtr<Geoposition> createGeoposition(GeolocationPosition* position)
{
if (!position)
- return 0;
+ return nullptr;
- RefPtr<Coordinates> 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<PositionError> createPositionError(GeolocationError* error)
+static Ref<PositionError> createPositionError(GeolocationError* error)
{
PositionError::ErrorCode code = PositionError::POSITION_UNAVAILABLE;
switch (error->code()) {
@@ -75,117 +79,17 @@ static PassRefPtr<PositionError> createPositionError(GeolocationError* error)
return PositionError::create(code, error->message());
}
-Geolocation::GeoNotifier::GeoNotifier(Geolocation* geolocation, PassRefPtr<PositionCallback> successCallback, PassRefPtr<PositionErrorCallback> errorCallback, PassRefPtr<PositionOptions> 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<PositionError> 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<GeoNotifier>&)
-{
- m_timer.stop();
-
- // Protect this GeoNotifier object, since it
- // could be deleted by a call to clearWatch in a callback.
- Ref<GeoNotifier> 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<PositionError> error = PositionError::create(PositionError::TIMEOUT, ASCIILiteral("Timeout expired"));
- m_errorCallback->handleEvent(error.get());
- }
- m_geolocation->requestTimedOut(this);
-}
-
-bool Geolocation::Watchers::add(int id, PassRefPtr<GeoNotifier> prpNotifier)
+bool Geolocation::Watchers::add(int id, RefPtr<GeoNotifier>&& notifier)
{
ASSERT(id > 0);
- RefPtr<GeoNotifier> 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> Geolocation::create(ScriptExecutionContext* context)
+Ref<Geolocation> Geolocation::create(ScriptExecutionContext* context)
{
- RefPtr<Geolocation> 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<Geolocation>&)
+void Geolocation::resumeTimerFired()
{
m_isSuspended = false;
@@ -308,13 +203,12 @@ void Geolocation::resumeTimerFired(Timer<Geolocation>&)
// 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<PositionCallback> successCallback, PassRefPtr<PositionErrorCallback> errorCallback, PassRefPtr<PositionOptions> options)
+void Geolocation::getCurrentPosition(Ref<PositionCallback>&& successCallback, RefPtr<PositionErrorCallback>&& errorCallback, PositionOptions&& options)
{
if (!frame())
return;
- RefPtr<GeoNotifier> notifier = GeoNotifier::create(this, successCallback, errorCallback, options);
+ RefPtr<GeoNotifier> notifier = GeoNotifier::create(*this, WTFMove(successCallback), WTFMove(errorCallback), WTFMove(options));
startRequest(notifier.get());
m_oneShots.add(notifier);
}
-int Geolocation::watchPosition(PassRefPtr<PositionCallback> successCallback, PassRefPtr<PositionErrorCallback> errorCallback, PassRefPtr<PositionOptions> options)
+int Geolocation::watchPosition(Ref<PositionCallback>&& successCallback, RefPtr<PositionErrorCallback>&& errorCallback, PositionOptions&& options)
{
if (!frame())
return 0;
- RefPtr<GeoNotifier> notifier = GeoNotifier::create(this, successCallback, errorCallback, options);
+ RefPtr<GeoNotifier> 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<Geolocation> protect(*this);
+ Ref<Geolocation> 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<GeoNotifier> 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> 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 <wtf/HashMap.h>
+#include <wtf/HashSet.h>
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<Geolocation>, public ActiveDOMObject
-{
+class Geolocation : public ScriptWrappable, public RefCounted<Geolocation>, public ActiveDOMObject {
+ friend class GeoNotifier;
public:
- static PassRefPtr<Geolocation> 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<PositionCallback>, PassRefPtr<PositionErrorCallback>, PassRefPtr<PositionOptions>);
- int watchPosition(PassRefPtr<PositionCallback>, PassRefPtr<PositionErrorCallback>, PassRefPtr<PositionOptions>);
+ static Ref<Geolocation> create(ScriptExecutionContext*);
+ WEBCORE_EXPORT ~Geolocation();
+
+ WEBCORE_EXPORT void resetAllGeolocationPermission();
+ Document* document() const { return downcast<Document>(scriptExecutionContext()); }
+ Frame* frame() const { return document() ? document()->frame() : nullptr; }
+
+ void getCurrentPosition(Ref<PositionCallback>&&, RefPtr<PositionErrorCallback>&&, PositionOptions&&);
+ int watchPosition(Ref<PositionCallback>&&, RefPtr<PositionErrorCallback>&&, 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<GeoNotifier> {
- public:
- static PassRefPtr<GeoNotifier> create(Geolocation* geolocation, PassRefPtr<PositionCallback> positionCallback, PassRefPtr<PositionErrorCallback> positionErrorCallback, PassRefPtr<PositionOptions> options) { return adoptRef(new GeoNotifier(geolocation, positionCallback, positionErrorCallback, options)); }
-
- PositionOptions* options() const { return m_options.get(); };
- void setFatalError(PassRefPtr<PositionError>);
-
- bool useCachedPosition() const { return m_useCachedPosition; }
- void setUseCachedPosition();
-
- void runSuccessCallback(Geoposition*);
- void runErrorCallback(PositionError*);
-
- void startTimerIfNeeded();
- void stopTimer();
- void timerFired(Timer<GeoNotifier>&);
- bool hasZeroTimeout() const;
-
- private:
- GeoNotifier(Geolocation*, PassRefPtr<PositionCallback>, PassRefPtr<PositionErrorCallback>, PassRefPtr<PositionOptions>);
-
- RefPtr<Geolocation> m_geolocation;
- RefPtr<PositionCallback> m_successCallback;
- RefPtr<PositionErrorCallback> m_errorCallback;
- RefPtr<PositionOptions> m_options;
- Timer<GeoNotifier> m_timer;
- RefPtr<PositionError> m_fatalError;
- bool m_useCachedPosition;
- };
+ SecurityOrigin* securityOrigin() const;
typedef Vector<RefPtr<GeoNotifier>> GeoNotifierVector;
typedef HashSet<RefPtr<GeoNotifier>> GeoNotifierSet;
class Watchers {
public:
- bool add(int id, PassRefPtr<GeoNotifier>);
+ bool add(int id, RefPtr<GeoNotifier>&&);
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<PositionError> m_errorWaitingForResume;
- void resumeTimerFired(Timer<Geolocation>&);
- Timer<Geolocation> 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> 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<RefPtr<Geolocation>> 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<RefPtr<Geolocation>> 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<RefPtr<Geolocation>> 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<Page>::provideTo(page, GeolocationController::supplementName(), GeolocationController::create(client));
+ ASSERT(page);
+ ASSERT(client);
+ Supplement<Page>::provideTo(page, GeolocationController::supplementName(), std::make_unique<GeolocationController>(*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 <wtf/HashSet.h>
@@ -39,45 +39,47 @@ namespace WebCore {
class GeolocationClient;
class GeolocationError;
class GeolocationPosition;
-class Page;
-class GeolocationController : public Supplement<Page> {
+class GeolocationController : public Supplement<Page>, private ActivityStateChangeObserver {
+ WTF_MAKE_FAST_ALLOCATED;
WTF_MAKE_NONCOPYABLE(GeolocationController);
public:
+ GeolocationController(Page&, GeolocationClient&);
~GeolocationController();
- static PassOwnPtr<GeolocationController> 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<GeolocationController*>(Supplement<Page>::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<GeolocationPosition> m_lastPosition;
+
typedef HashSet<RefPtr<Geolocation>> 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<RefPtr<Geolocation>> 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 <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>
-#include <wtf/RefPtr.h>
#include <wtf/text/WTFString.h>
namespace WebCore {
@@ -40,7 +37,7 @@ public:
PositionUnavailable
};
- static PassRefPtr<GeolocationError> create(ErrorCode code, const String& message) { return adoptRef(new GeolocationError(code, message)); }
+ static Ref<GeolocationError> 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 <wtf/PassRefPtr.h>
+#include <wtf/Ref.h>
#include <wtf/RefCounted.h>
-#include <wtf/RefPtr.h>
namespace WebCore {
class GeolocationPosition : public RefCounted<GeolocationPosition> {
public:
- static PassRefPtr<GeolocationPosition> create(double timestamp, double latitude, double longitude, double accuracy) { return adoptRef(new GeolocationPosition(timestamp, latitude, longitude, accuracy)); }
+ static Ref<GeolocationPosition> create(double timestamp, double latitude, double longitude, double accuracy)
+ {
+ return adoptRef(*new GeolocationPosition(timestamp, latitude, longitude, accuracy));
+ }
- static PassRefPtr<GeolocationPosition> 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<GeolocationPosition> 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<Geoposition> {
public:
- static PassRefPtr<Geoposition> create(PassRefPtr<Coordinates> coordinates, DOMTimeStamp timestamp)
+ static Ref<Geoposition> create(Ref<Coordinates>&& coordinates, DOMTimeStamp timestamp)
{
- return adoptRef(new Geoposition(coordinates, timestamp));
+ return adoptRef(*new Geoposition(WTFMove(coordinates), timestamp));
}
- PassRefPtr<Geoposition> isolatedCopy() const
+ Ref<Geoposition> 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> coordinates, DOMTimeStamp timestamp)
- : m_coordinates(coordinates)
+ Geoposition(Ref<Coordinates>&& coordinates, DOMTimeStamp timestamp)
+ : m_coordinates(WTFMove(coordinates))
, m_timestamp(timestamp)
{
- ASSERT(m_coordinates);
}
- RefPtr<Coordinates> m_coordinates;
+ Ref<Coordinates> 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<NavigatorGeolocation*>(Supplement<Navigator>::from(navigator, supplementName()));
if (!supplement) {
- supplement = new NavigatorGeolocation(navigator->frame());
- provideTo(navigator, supplementName(), adoptPtr(supplement));
+ auto newSupplement = std::make_unique<NavigatorGeolocation>(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<Navigator>, 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<Geolocation> 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 <wtf/RefCounted.h>
@@ -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 <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>
#include <wtf/text/WTFString.h>
@@ -40,7 +38,7 @@ public:
TIMEOUT = 3
};
- static PassRefPtr<PositionError> create(ErrorCode code, const String& message) { return adoptRef(new PositionError(code, message)); }
+ static Ref<PositionError> 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 <wtf/RefCounted.h>
@@ -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 <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
+#pragma once
namespace WebCore {
-class PositionOptions : public RefCounted<PositionOptions> {
-public:
- static PassRefPtr<PositionOptions> 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<DOMWindowIndexedDatabase*>(Supplement<DOMWindow>::from(window, supplementName()));
if (!supplement) {
- supplement = new DOMWindowIndexedDatabase(window);
- provideTo(window, supplementName(), adoptPtr(supplement));
+ auto newSupplement = std::make_unique<DOMWindowIndexedDatabase>(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<DOMWindow> {
+ 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 <wtf/CrossThreadTask.h>
+#include <wtf/MainThread.h>
+#include <wtf/Threading.h>
+
+#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> lock(m_scriptExecutionContextLock);
+ ActiveDOMObject::contextDestroyed();
+ }
+
+ template<typename T, typename... Parameters, typename... Arguments>
+ void performCallbackOnOriginThread(T& object, void (T::*method)(Parameters...), Arguments&&... arguments)
+ {
+ ASSERT(originThreadID() == object.originThreadID());
+
+ if (object.originThreadID() == currentThread()) {
+ (object.*method)(arguments...);
+ return;
+ }
+
+ Locker<Lock> lock(m_scriptExecutionContextLock);
+
+ ScriptExecutionContext* context = scriptExecutionContext();
+ if (!context)
+ return;
+
+ context->postCrossThreadTask(object, method, arguments...);
+ }
+
+ void callFunctionOnOriginThread(WTF::Function<void ()>&& function)
+ {
+ if (originThreadID() == currentThread()) {
+ function();
+ return;
+ }
+
+ Locker<Lock> 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> IDBAny::createInvalid()
-{
- return adoptRef(new IDBAny(UndefinedType));
-}
-
-PassRefPtr<IDBAny> IDBAny::createNull()
-{
- return adoptRef(new IDBAny(NullType));
-}
-
-PassRefPtr<IDBAny> 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<DOMStringList> IDBAny::domStringList()
-{
- ASSERT(m_type == DOMStringListType);
- return m_domStringList;
-}
-
-PassRefPtr<IDBCursor> IDBAny::idbCursor()
-{
- ASSERT(m_type == IDBCursorType);
- return m_idbCursor;
-}
-
-PassRefPtr<IDBCursorWithValue> IDBAny::idbCursorWithValue()
-{
- ASSERT(m_type == IDBCursorWithValueType);
- return m_idbCursorWithValue;
-}
-
-PassRefPtr<IDBDatabase> IDBAny::idbDatabase()
-{
- ASSERT(m_type == IDBDatabaseType);
- return m_idbDatabase;
-}
-
-PassRefPtr<IDBFactory> IDBAny::idbFactory()
-{
- ASSERT(m_type == IDBFactoryType);
- return m_idbFactory;
-}
-
-PassRefPtr<IDBIndex> IDBAny::idbIndex()
-{
- ASSERT(m_type == IDBIndexType);
- return m_idbIndex;
-}
-
-PassRefPtr<IDBObjectStore> IDBAny::idbObjectStore()
-{
- ASSERT(m_type == IDBObjectStoreType);
- return m_idbObjectStore;
-}
-
-PassRefPtr<IDBTransaction> 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<DOMStringList> value)
- : m_type(DOMStringListType)
- , m_domStringList(value)
- , m_integer(0)
-{
-}
-
-IDBAny::IDBAny(PassRefPtr<IDBCursorWithValue> value)
- : m_type(IDBCursorWithValueType)
- , m_idbCursorWithValue(value)
- , m_integer(0)
-{
-}
-
-IDBAny::IDBAny(PassRefPtr<IDBCursor> value)
- : m_type(IDBCursorType)
- , m_idbCursor(value)
- , m_integer(0)
-{
-}
-
-IDBAny::IDBAny(PassRefPtr<IDBDatabase> value)
- : m_type(IDBDatabaseType)
- , m_idbDatabase(value)
- , m_integer(0)
-{
-}
-
-IDBAny::IDBAny(PassRefPtr<IDBFactory> value)
- : m_type(IDBFactoryType)
- , m_idbFactory(value)
- , m_integer(0)
-{
-}
-
-IDBAny::IDBAny(PassRefPtr<IDBIndex> value)
- : m_type(IDBIndexType)
- , m_idbIndex(value)
- , m_integer(0)
-{
-}
-
-IDBAny::IDBAny(PassRefPtr<IDBTransaction> value)
- : m_type(IDBTransactionType)
- , m_idbTransaction(value)
- , m_integer(0)
-{
-}
-
-IDBAny::IDBAny(PassRefPtr<IDBObjectStore> 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 <bindings/ScriptValue.h>
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/RefPtr.h>
-#include <wtf/text/WTFString.h>
-
-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<IDBAny> {
-public:
- static PassRefPtr<IDBAny> createInvalid();
- static PassRefPtr<IDBAny> createNull();
- static PassRefPtr<IDBAny> createString(const String&);
- template<typename T>
- static PassRefPtr<IDBAny> create(T* idbObject)
- {
- return adoptRef(new IDBAny(idbObject));
- }
- template<typename T>
- static PassRefPtr<IDBAny> create(const T& idbObject)
- {
- return adoptRef(new IDBAny(idbObject));
- }
- template<typename T>
- static PassRefPtr<IDBAny> create(PassRefPtr<T> idbObject)
- {
- return adoptRef(new IDBAny(idbObject));
- }
- static PassRefPtr<IDBAny> 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> domStringList();
- PassRefPtr<IDBCursor> idbCursor();
- PassRefPtr<IDBCursorWithValue> idbCursorWithValue();
- PassRefPtr<IDBDatabase> idbDatabase();
- PassRefPtr<IDBFactory> idbFactory();
- PassRefPtr<IDBIndex> idbIndex();
- PassRefPtr<IDBObjectStore> idbObjectStore();
- PassRefPtr<IDBTransaction> idbTransaction();
- const Deprecated::ScriptValue& scriptValue();
- int64_t integer();
- const String& string();
- const IDBKeyPath& keyPath() { return m_idbKeyPath; };
-
-private:
- explicit IDBAny(Type);
- explicit IDBAny(PassRefPtr<DOMStringList>);
- explicit IDBAny(PassRefPtr<IDBCursor>);
- explicit IDBAny(PassRefPtr<IDBCursorWithValue>);
- explicit IDBAny(PassRefPtr<IDBDatabase>);
- explicit IDBAny(PassRefPtr<IDBFactory>);
- explicit IDBAny(PassRefPtr<IDBIndex>);
- explicit IDBAny(PassRefPtr<IDBObjectStore>);
- explicit IDBAny(PassRefPtr<IDBTransaction>);
- 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<DOMStringList> m_domStringList;
- const RefPtr<IDBCursor> m_idbCursor;
- const RefPtr<IDBCursorWithValue> m_idbCursorWithValue;
- const RefPtr<IDBDatabase> m_idbDatabase;
- const RefPtr<IDBFactory> m_idbFactory;
- const RefPtr<IDBIndex> m_idbIndex;
- const RefPtr<IDBObjectStore> m_idbObjectStore;
- const RefPtr<IDBTransaction> 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/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 <wtf/RefCounted.h>
-
-#if ENABLE(INDEXED_DATABASE)
-
-namespace WebCore {
-class DOMStringList;
-class IDBCursorBackend;
-class IDBDatabaseBackend;
-
-struct IDBDatabaseMetadata;
-
-class IDBCallbacks : public RefCounted<IDBCallbacks> {
-public:
- virtual ~IDBCallbacks() { }
-
- virtual void onError(PassRefPtr<IDBDatabaseError>) = 0;
- // From IDBFactory.webkitGetDatabaseNames()
- virtual void onSuccess(PassRefPtr<DOMStringList>) = 0;
- // From IDBObjectStore/IDBIndex.openCursor(), IDBIndex.openKeyCursor()
- virtual void onSuccess(PassRefPtr<IDBCursorBackend>, PassRefPtr<IDBKey>, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer>) = 0;
- // From IDBObjectStore.add()/put(), IDBIndex.getKey()
- virtual void onSuccess(PassRefPtr<IDBKey>) = 0;
- // From IDBObjectStore/IDBIndex.get()/count(), and various methods that yield null/undefined.
- virtual void onSuccess(PassRefPtr<SharedBuffer>) = 0;
- // From IDBObjectStore/IDBIndex.get() (with key injection)
- virtual void onSuccess(PassRefPtr<SharedBuffer>, PassRefPtr<IDBKey>, 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<IDBKey>, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer>) = 0;
- // From IDBCursor.advance()/continue()
- virtual void onSuccessWithPrefetch(const Vector<RefPtr<IDBKey>>& keys, const Vector<RefPtr<IDBKey>>& primaryKeys, const Vector<RefPtr<SharedBuffer>>& 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<IDBDatabaseBackend>, const IDBDatabaseMetadata&) { ASSERT_NOT_REACHED(); }
- virtual void onSuccess(PassRefPtr<IDBDatabaseBackend>, 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 <limits>
+#include <heap/HeapInlines.h>
+#include <heap/StrongInlines.h>
+#include <runtime/JSCJSValueInlines.h>
+
+using namespace JSC;
namespace WebCore {
-PassRefPtr<IDBCursor> IDBCursor::create(PassRefPtr<IDBCursorBackend> backend, IndexedDB::CursorDirection direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction)
+Ref<IDBCursor> 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> 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<IDBCursorBackend> 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<IDBObjectStore>& objectStore) { return objectStore->isDeleted(); },
+ [] (const RefPtr<IDBIndex>& 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<IDBObjectStore>& objectStore) -> IDBObjectStore& { return *objectStore; },
+ [] (const RefPtr<IDBIndex>& 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<Ref<IDBRequest>> IDBCursor::update(ExecState& state, JSValue value)
{
- return m_source.get();
-}
+ LOG(IndexedDB, "IDBCursor::update");
+ ASSERT(currentThread() == effectiveObjectStore().transaction().database().originThreadID());
-PassRefPtr<IDBRequest> 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<IDBObjectStore> 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<IDBKey> keyPathKey = createIDBKeyFromScriptValueAndKeyPath(m_request->requestState(), value, keyPath);
- if (!keyPathKey || !keyPathKey->isEqual(m_currentPrimaryKey.get())) {
- ec = IDBDatabaseException::DataError;
- return 0;
- }
+ RefPtr<IDBKey> 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<void> 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<void> 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<RefPtr<IDBIndex>>(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<IDBKey> 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<IDBKey> 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<void> IDBCursor::continueFunction(ExecState& execState, JSValue keyValue)
{
- DOMRequestState requestState(context);
- RefPtr<IDBKey> key = scriptValueToIDBKey(&requestState, keyValue);
- continueFunction(key.release(), ec);
+ RefPtr<IDBKey> key;
+ if (!keyValue.isUndefined())
+ key = scriptValueToIDBKey(execState, keyValue);
+
+ return continueFunction(key.get());
}
-void IDBCursor::continueFunction(PassRefPtr<IDBKey> key, ExceptionCode& ec)
+ExceptionOr<void> 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<IDBRequest> 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<IDBRequest> 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<Ref<WebCore::IDBRequest>> 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<IDBKey> key, PassRefPtr<IDBKey> 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<IDBObjectStore> objectStore = effectiveObjectStore();
- const IDBObjectStoreMetadata metadata = objectStore->metadata();
- if (metadata.autoIncrement && !metadata.keyPath.isNull()) {
-#ifndef NDEBUG
- RefPtr<IDBKey> 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<IDBObjectStore> IDBCursor::effectiveObjectStore()
+const char* IDBCursor::activeDOMObjectName() const
{
- if (m_source->type() == IDBAny::IDBObjectStoreType)
- return m_source->idbObjectStore();
- RefPtr<IDBIndex> 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 <bindings/ScriptValue.h>
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/RefPtr.h>
+#include "ActiveDOMObject.h"
+#include "DOMWrapperWorld.h"
+#include "ExceptionOr.h"
+#include "IDBCursorDirection.h"
+#include "IDBCursorInfo.h"
+#include <heap/Strong.h>
+#include <wtf/Variant.h>
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<IDBCursor> {
+class IDBCursor : public ScriptWrappable, public RefCounted<IDBCursor>, public ActiveDOMObject {
public:
- static const AtomicString& directionNext();
- static const AtomicString& directionNextUnique();
- static const AtomicString& directionPrev();
- static const AtomicString& directionPrevUnique();
+ static Ref<IDBCursor> create(IDBTransaction&, IDBObjectStore&, const IDBCursorInfo&);
+ static Ref<IDBCursor> 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<IDBObjectStore>, RefPtr<IDBIndex>>;
- static PassRefPtr<IDBCursor> create(PassRefPtr<IDBCursorBackend>, 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<Ref<IDBRequest>> update(JSC::ExecState&, JSC::JSValue);
+ ExceptionOr<void> advance(unsigned);
+ ExceptionOr<void> continueFunction(JSC::ExecState&, JSC::JSValue key);
+ ExceptionOr<void> continuePrimaryKey(JSC::ExecState&, JSC::JSValue key, JSC::JSValue primaryKey);
+ ExceptionOr<Ref<IDBRequest>> deleteFunction(JSC::ExecState&);
+
+ ExceptionOr<void> 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<IDBRequest> 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<IDBKey*>(0), ec); }
- void continueFunction(ScriptExecutionContext*, const Deprecated::ScriptValue& key, ExceptionCode&);
- PassRefPtr<IDBRequest> deleteFunction(ScriptExecutionContext*, ExceptionCode&);
-
- void continueFunction(PassRefPtr<IDBKey>, ExceptionCode&);
- void postSuccessHandlerCallback();
- void close();
- void setValueReady(DOMRequestState*, PassRefPtr<IDBKey>, PassRefPtr<IDBKey> primaryKey, Deprecated::ScriptValue&);
- PassRefPtr<IDBKey> 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<IDBCursorBackend>, IndexedDB::CursorDirection, IDBRequest*, IDBAny* source, IDBTransaction*);
- virtual bool isKeyCursor() const { return true; }
+ IDBCursor(IDBTransaction&, IDBObjectStore&, const IDBCursorInfo&);
+ IDBCursor(IDBTransaction&, IDBIndex&, const IDBCursorInfo&);
private:
- PassRefPtr<IDBObjectStore> effectiveObjectStore();
-
- RefPtr<IDBCursorBackend> m_backend;
- RefPtr<IDBRequest> m_request;
- const IndexedDB::CursorDirection m_direction;
- RefPtr<IDBAny> m_source;
- RefPtr<IDBTransaction> 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<IDBKey> m_currentKey;
- RefPtr<IDBKey> 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<JSC::Unknown> m_currentKey;
+ JSC::Strong<JSC::Unknown> m_currentPrimaryKey;
+ JSC::Strong<JSC::Unknown> 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<IDBKey> key, PassRefPtr<IDBCallbacks> prpCallbacks, ExceptionCode&)
-{
- LOG(StorageAPI, "IDBCursorBackend::continue");
- RefPtr<IDBCallbacks> callbacks = prpCallbacks;
- m_transaction->scheduleTask(m_taskType, CursorIterationOperation::create(this, key, callbacks));
-}
-
-void IDBCursorBackend::advance(unsigned long count, PassRefPtr<IDBCallbacks> prpCallbacks, ExceptionCode&)
-{
- LOG(StorageAPI, "IDBCursorBackend::advance");
- RefPtr<IDBCallbacks> callbacks = prpCallbacks;
- m_transaction->scheduleTask(CursorAdvanceOperation::create(this, count, callbacks));
-}
-
-void IDBCursorBackend::deleteFunction(PassRefPtr<IDBCallbacks> prpCallbacks, ExceptionCode&)
-{
- LOG(StorageAPI, "IDBCursorBackend::delete");
- ASSERT(m_transaction->mode() != IndexedDB::TransactionMode::ReadOnly);
- RefPtr<IDBKeyRange> 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 <wtf/OwnPtr.h>
-#include <wtf/PassOwnPtr.h>
-#include <wtf/RefPtr.h>
-
-namespace WebCore {
-
-class IDBKeyRange;
-
-class IDBCursorBackend : public RefCounted<IDBCursorBackend> {
-public:
- static PassRefPtr<IDBCursorBackend> create(int64_t cursorID, IndexedDB::CursorType cursorType, IDBTransactionBackend& transaction, int64_t objectStoreID)
- {
- return adoptRef(new IDBCursorBackend(cursorID, cursorType, IDBDatabaseBackend::NormalTask, transaction, objectStoreID));
- }
- static PassRefPtr<IDBCursorBackend> 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<IDBCallbacks>, ExceptionCode&);
- void continueFunction(PassRefPtr<IDBKey>, PassRefPtr<IDBCallbacks>, ExceptionCode&);
- void deleteFunction(PassRefPtr<IDBCallbacks>, 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<IDBTransactionBackend> m_transaction;
- const int64_t m_objectStoreID;
-
- int64_t m_cursorID;
- int64_t m_savedCursorID;
-
- RefPtr<IDBKey> m_currentKey;
- RefPtr<IDBKey> m_currentPrimaryKey;
- RefPtr<SharedBuffer> 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<void()> completionCallback)
-{
- LOG(StorageAPI, "CursorAdvanceOperation");
-
- RefPtr<CursorAdvanceOperation> operation(this);
- auto callback = [this, operation, completionCallback](PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> value, PassRefPtr<IDBDatabaseError> 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<SharedBuffer*>(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<void()> completionCallback)
-{
- LOG(StorageAPI, "CursorIterationOperation");
-
- RefPtr<CursorIterationOperation> operation(this);
- auto callback = [this, operation, completionCallback](PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> value, PassRefPtr<IDBDatabaseError> 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<SharedBuffer*>(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<IDBOperation> create(PassRefPtr<IDBCursorBackend> cursor, PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks> callbacks)
- {
- return adoptRef(new CursorIterationOperation(cursor, key, callbacks));
- }
- virtual void perform(std::function<void()> completionCallback) override final;
-
- int64_t cursorID() const { return m_cursor->id(); }
- IDBKey* key() const { return m_key.get(); }
-
-private:
- CursorIterationOperation(PassRefPtr<IDBCursorBackend> cursor, PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks> callbacks)
- : m_cursor(cursor)
- , m_key(key)
- , m_callbacks(callbacks)
- {
- }
-
- RefPtr<IDBCursorBackend> m_cursor;
- RefPtr<IDBKey> m_key;
- RefPtr<IDBCallbacks> m_callbacks;
-};
-
-class CursorAdvanceOperation : public IDBOperation {
-public:
- static PassRefPtr<IDBOperation> create(PassRefPtr<IDBCursorBackend> cursor, unsigned long count, PassRefPtr<IDBCallbacks> callbacks)
- {
- return adoptRef(new CursorAdvanceOperation(cursor, count, callbacks));
- }
- virtual void perform(std::function<void()> completionCallback) override final;
-
- int64_t cursorID() const { return m_cursor->id(); }
- unsigned long count() const { return m_count; }
-
-private:
- CursorAdvanceOperation(PassRefPtr<IDBCursorBackend> cursor, unsigned long count, PassRefPtr<IDBCallbacks> callbacks)
- : m_cursor(cursor)
- , m_count(count)
- , m_callbacks(callbacks)
- {
- }
-
- RefPtr<IDBCursorBackend> m_cursor;
- unsigned long m_count;
- RefPtr<IDBCallbacks> 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 <heap/HeapInlines.h>
namespace WebCore {
-PassRefPtr<IDBCursorWithValue> IDBCursorWithValue::create(PassRefPtr<IDBCursorBackend> backend, IndexedDB::CursorDirection direction, IDBRequest* request, IDBAny* source, IDBTransaction* transaction)
+Ref<IDBCursorWithValue> IDBCursorWithValue::create(IDBTransaction& transaction, IDBObjectStore& objectStore, const IDBCursorInfo& info)
+{
+ return adoptRef(*new IDBCursorWithValue(transaction, objectStore, info));
+}
+
+Ref<IDBCursorWithValue> 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> IDBCursorWithValue::fromCursor(PassRefPtr<IDBCursor> prpCursor)
+IDBCursorWithValue::IDBCursorWithValue(IDBTransaction& transaction, IDBObjectStore& objectStore, const IDBCursorInfo& info)
+ : IDBCursor(transaction, objectStore, info)
{
- RefPtr<IDBCursorWithValue> cursorWithValue(static_cast<IDBCursorWithValue*>(prpCursor.get()));
- return cursorWithValue.release();
}
-IDBCursorWithValue::IDBCursorWithValue(PassRefPtr<IDBCursorBackend> 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 <wtf/TypeCasts.h>
namespace WebCore {
-class IDBCursorWithValue : public IDBCursor {
+class IDBCursorWithValue final : public IDBCursor {
public:
- static PassRefPtr<IDBCursorWithValue> create(PassRefPtr<IDBCursorBackend>, IndexedDB::CursorDirection, IDBRequest*, IDBAny* source, IDBTransaction*);
- static PassRefPtr<IDBCursorWithValue> fromCursor(PassRefPtr<IDBCursor>);
- virtual ~IDBCursorWithValue();
+ static Ref<IDBCursorWithValue> create(IDBTransaction&, IDBObjectStore&, const IDBCursorInfo&);
+ static Ref<IDBCursorWithValue> 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<IDBCursorBackend>, 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 <atomic>
-#include <limits>
-#include <wtf/Atomics.h>
+#include <heap/HeapInlines.h>
namespace WebCore {
-PassRefPtr<IDBDatabase> IDBDatabase::create(ScriptExecutionContext* context, PassRefPtr<IDBDatabaseBackend> database, PassRefPtr<IDBDatabaseCallbacks> callbacks)
+Ref<IDBDatabase> IDBDatabase::create(ScriptExecutionContext& context, IDBClient::IDBConnectionProxy& connectionProxy, const IDBResultData& resultData)
{
- RefPtr<IDBDatabase> idbDatabase(adoptRef(new IDBDatabase(context, database, callbacks)));
- idbDatabase->suspendIfNeeded();
- return idbDatabase.release();
+ return adoptRef(*new IDBDatabase(context, connectionProxy, resultData));
}
-IDBDatabase::IDBDatabase(ScriptExecutionContext* context, PassRefPtr<IDBDatabaseBackend> backend, PassRefPtr<IDBDatabaseCallbacks> 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<uint32_t> 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<DOMStringList> 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<DOMStringList> 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<IDBDatabaseError> 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<Ref<IDBObjectStore>> 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<String>(keyPath.value()) && WTF::get<String>(keyPath.value()).isEmpty()) || WTF::holds_alternative<Vector<String>>(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<DOMStringList> IDBDatabase::objectStoreNames() const
+ExceptionOr<Ref<IDBTransaction>> IDBDatabase::transaction(StringOrVectorOfStrings&& storeNames, IDBTransactionMode mode)
{
- RefPtr<DOMStringList> 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<String> objectStores;
+ if (WTF::holds_alternative<Vector<String>>(storeNames))
+ objectStores = WTFMove(WTF::get<Vector<String>>(storeNames));
+ else
+ objectStores.append(WTFMove(WTF::get<String>(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<String> 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<void> 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<IDBObjectStore> IDBDatabase::createObjectStore(const String& name, const Dictionary& options, ExceptionCode& ec)
+void IDBDatabase::close()
{
- IDBKeyPath keyPath;
- bool autoIncrement = false;
- if (!options.isUndefinedOrNull()) {
- String keyPathString;
- Vector<String> 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<IDBObjectStore> 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<IDBObjectStore> 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<IDBTransaction> IDBDatabase::transaction(ScriptExecutionContext* context, const Vector<String>& 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<int64_t> 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<IDBResourceIdentifier> 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<IDBTransaction> IDBDatabase::startVersionChangeTransaction(const IDBTransactionInfo& info, IDBOpenDBRequest& request)
+{
+ LOG(IndexedDB, "IDBDatabase::startVersionChangeTransaction %s", info.identifier().loggingString().utf8().data());
- RefPtr<IDBTransaction> 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<IDBTransaction> transaction = IDBTransaction::create(*this, info, request);
+ m_versionChangeTransaction = &transaction.get();
+
+ m_activeTransactions.set(transaction->info().identifier(), &transaction.get());
+
+ return transaction;
}
-PassRefPtr<IDBTransaction> IDBDatabase::transaction(ScriptExecutionContext* context, const String& storeName, const String& mode, ExceptionCode& ec)
+void IDBDatabase::didStartTransaction(IDBTransaction& transaction)
{
- RefPtr<DOMStringList> 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<IDBDatabase> 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> 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> 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> 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<IDBVersionChangeEvent>(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 <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/RefPtr.h>
+#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<IDBDatabase>, public ScriptWrappable, public EventTargetWithInlineData, public ActiveDOMObject {
+class IDBDatabase : public ThreadSafeRefCounted<IDBDatabase>, public EventTargetWithInlineData, public IDBActiveDOMObject {
public:
- static PassRefPtr<IDBDatabase> create(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackend>, PassRefPtr<IDBDatabaseCallbacks>);
- ~IDBDatabase();
+ static Ref<IDBDatabase> 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<DOMStringList> objectStoreNames() const;
-
- PassRefPtr<IDBObjectStore> createObjectStore(const String& name, const Dictionary&, ExceptionCode&);
- PassRefPtr<IDBObjectStore> createObjectStore(const String& name, const IDBKeyPath&, bool autoIncrement, ExceptionCode&);
- PassRefPtr<IDBTransaction> transaction(ScriptExecutionContext* context, PassRefPtr<DOMStringList> scope, const String& mode, ExceptionCode& ec) { return transaction(context, *scope, mode, ec); }
- PassRefPtr<IDBTransaction> transaction(ScriptExecutionContext*, const Vector<String>&, const String& mode, ExceptionCode&);
- PassRefPtr<IDBTransaction> transaction(ScriptExecutionContext*, const String&, const String& mode, ExceptionCode&);
- void deleteObjectStore(const String& name, ExceptionCode&);
- void close();
+ RefPtr<DOMStringList> objectStoreNames() const;
+
+ struct ObjectStoreParameters {
+ std::optional<IDBKeyPath> keyPath;
+ bool autoIncrement;
+ };
- DEFINE_ATTRIBUTE_EVENT_LISTENER(abort);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(error);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(versionchange);
+ ExceptionOr<Ref<IDBObjectStore>> createObjectStore(const String& name, ObjectStoreParameters&&);
- // IDBDatabaseCallbacks
- virtual void onVersionChange(uint64_t oldVersion, uint64_t newVersion, IndexedDB::VersionNullness newVersionNullness);
- virtual void onAbort(int64_t, PassRefPtr<IDBDatabaseError>);
- virtual void onComplete(int64_t);
+ using StringOrVectorOfStrings = WTF::Variant<String, Vector<String>>;
+ ExceptionOr<Ref<IDBTransaction>> transaction(StringOrVectorOfStrings&& storeNames, IDBTransactionMode);
+ ExceptionOr<void> 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<IDBDatabase>::ref(); }
+ void derefEventTarget() final { ThreadSafeRefCounted<IDBDatabase>::deref(); }
- bool isClosePending() const { return m_closePending; }
- void forceClose();
- const IDBDatabaseMetadata metadata() const { return m_metadata; }
- void enqueueEvent(PassRefPtr<Event>);
+ using ThreadSafeRefCounted<IDBDatabase>::ref;
+ using ThreadSafeRefCounted<IDBDatabase>::deref;
- using EventTarget::dispatchEvent;
- virtual bool dispatchEvent(PassRefPtr<Event>) 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<IDBTransaction> 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<IDBDatabase>::ref;
- using RefCounted<IDBDatabase>::deref;
+ void fireVersionChangeEvent(const IDBResourceIdentifier& requestIdentifier, uint64_t requestedVersion);
+ void didCloseFromServer(const IDBError&);
+ void connectionToServerLost(const IDBError&);
-private:
- IDBDatabase(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackend>, PassRefPtr<IDBDatabaseCallbacks>);
+ 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<IDBDatabaseBackend> m_backend;
- RefPtr<IDBTransaction> m_versionChangeTransaction;
- typedef HashMap<int64_t, IDBTransaction*> 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<RefPtr<Event>> m_enqueuedEvents;
+ void maybeCloseInServer();
- RefPtr<IDBDatabaseCallbacks> m_databaseCallbacks;
+ Ref<IDBClient::IDBConnectionProxy> m_connectionProxy;
+ IDBDatabaseInfo m_info;
+ uint64_t m_databaseConnectionIdentifier { 0 };
+
+ bool m_closePending { false };
+ bool m_closedInServer { false };
+
+ RefPtr<IDBTransaction> m_versionChangeTransaction;
+ HashMap<IDBResourceIdentifier, RefPtr<IDBTransaction>> m_activeTransactions;
+ HashMap<IDBResourceIdentifier, RefPtr<IDBTransaction>> m_committingTransactions;
+ HashMap<IDBResourceIdentifier, RefPtr<IDBTransaction>> 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<DOMString> 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<DOMString>) 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<DOMString>)? 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 <wtf/TemporaryChange.h>
-
-namespace WebCore {
-
-PassRefPtr<IDBDatabaseBackend> IDBDatabaseBackend::create(const String& name, const String& uniqueIdentifier, IDBFactoryBackendInterface* factory, IDBServerConnection& serverConnection)
-{
- RefPtr<IDBDatabaseBackend> 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<IDBDatabaseBackend> 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<IDBDatabaseError> 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<IDBKeyRange> keyRange, bool keyOnly, PassRefPtr<IDBCallbacks> 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<SharedBuffer> value, PassRefPtr<IDBKey> key, PutMode putMode, PassRefPtr<IDBCallbacks> callbacks, const Vector<int64_t>& indexIds, const Vector<IndexKeys>& 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<IDBKey> prpPrimaryKey, const Vector<int64_t>& indexIDs, const Vector<IndexKeys>& indexKeys)
-{
- LOG(StorageAPI, "IDBDatabaseBackend::setIndexKeys");
- ASSERT(prpPrimaryKey);
- ASSERT(m_metadata.objectStores.contains(objectStoreID));
-
- RefPtr<IDBTransactionBackend> transaction = m_transactions.get(transactionID);
- if (!transaction)
- return;
- ASSERT(transaction->mode() == IndexedDB::TransactionMode::VersionChange);
-
- RefPtr<IDBKey> primaryKey = prpPrimaryKey;
- m_serverConnection->setIndexKeys(transactionID, m_metadata.id, objectStoreID, m_metadata.objectStores.get(objectStoreID), *primaryKey, indexIDs, indexKeys, [transaction](PassRefPtr<IDBDatabaseError> error) {
- if (error)
- transaction->abort(error);
- });
-}
-
-void IDBDatabaseBackend::setIndexesReady(int64_t transactionId, int64_t, const Vector<int64_t>& 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<IDBKeyRange> keyRange, IndexedDB::CursorDirection direction, bool keyOnly, TaskType taskType, PassRefPtr<IDBCallbacks> 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<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> 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<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> 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<IDBCallbacks> 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<IDBTransactionBackend> 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<IDBTransactionBackend> 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<IDBTransactionBackend> 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<IDBPendingDeleteCall> 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<OwnPtr<IDBPendingOpenCall>> pendingOpenCalls;
- m_pendingOpenCalls.swap(pendingOpenCalls);
-
- while (!pendingOpenCalls.isEmpty()) {
- OwnPtr<IDBPendingOpenCall> 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<unsigned long long>(pendingOpenCall->version()));
- pendingOpenCall->callbacks()->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, message));
- }
- }
-}
-
-void IDBDatabaseBackend::createTransaction(int64_t transactionID, PassRefPtr<IDBDatabaseCallbacks> callbacks, const Vector<int64_t>& objectStoreIDs, IndexedDB::TransactionMode mode)
-{
- RefPtr<IDBTransactionBackend> transaction = IDBTransactionBackend::create(this, transactionID, callbacks, objectStoreIDs, mode);
-
- ASSERT(!m_transactions.contains(transactionID));
- m_transactions.add(transactionID, transaction.get());
-}
-
-void IDBDatabaseBackend::openConnection(PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> prpDatabaseCallbacks, int64_t transactionId, uint64_t version)
-{
- RefPtr<IDBCallbacks> callbacks = prpCallbacks;
- RefPtr<IDBDatabaseCallbacks> databaseCallbacks = prpDatabaseCallbacks;
-
- m_pendingOpenCalls.append(IDBPendingOpenCall::create(*callbacks, *databaseCallbacks, transactionId, version));
-
- processPendingCalls();
-}
-
-void IDBDatabaseBackend::openConnectionInternal(PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> prpDatabaseCallbacks, int64_t transactionId, uint64_t version)
-{
- ASSERT(m_pendingDeleteCalls.isEmpty());
- ASSERT(!m_runningVersionChangeTransaction);
-
- RefPtr<IDBCallbacks> callbacks = prpCallbacks;
- RefPtr<IDBDatabaseCallbacks> 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<IDBDatabaseCallbacks>(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<unsigned long long>(version), static_cast<unsigned long long>(m_metadata.version))));
- return;
- }
-
- ASSERT(version == m_metadata.version);
- m_databaseCallbacksSet.add(databaseCallbacks);
- callbacks->onSuccess(this, this->metadata());
-}
-
-void IDBDatabaseBackend::runIntVersionChangeTransaction(PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> prpDatabaseCallbacks, int64_t transactionId, int64_t requestedVersion)
-{
- RefPtr<IDBCallbacks> callbacks = prpCallbacks;
- RefPtr<IDBDatabaseCallbacks> 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<int64_t> objectStoreIds;
- createTransaction(transactionId, databaseCallbacks, objectStoreIds, IndexedDB::TransactionMode::VersionChange);
- RefPtr<IDBTransactionBackend> transaction = m_transactions.get(transactionId);
-
- transaction->scheduleVersionChangeOperation(requestedVersion, callbacks, databaseCallbacks, m_metadata);
-
- ASSERT(!m_pendingSecondHalfOpen);
- m_databaseCallbacksSet.add(databaseCallbacks);
-}
-
-void IDBDatabaseBackend::deleteDatabase(PassRefPtr<IDBCallbacks> prpCallbacks)
-{
- RefPtr<IDBCallbacks> 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<IDBCallbacks> callbacks)
-{
- ASSERT(!isDeleteDatabaseBlocked());
-
- RefPtr<IDBDatabaseBackend> 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<IDBDatabaseCallbacks> prpCallbacks)
-{
- RefPtr<IDBDatabaseCallbacks> 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<bool> 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<IDBDatabaseError> 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 <stdint.h>
-#include <wtf/Deque.h>
-#include <wtf/HashMap.h>
-#include <wtf/ListHashSet.h>
-
-#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<RefPtr<IDBKey>> IndexKeys;
-typedef int ExceptionCode;
-
-class IDBDatabaseBackend : public RefCounted<IDBDatabaseBackend> {
-public:
- static PassRefPtr<IDBDatabaseBackend> 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<IDBCallbacks>, PassRefPtr<IDBDatabaseCallbacks>, int64_t transactionId, uint64_t version);
- void deleteDatabase(PassRefPtr<IDBCallbacks>);
-
- // 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<IDBDatabaseCallbacks>, const Vector<int64_t>& objectStoreIds, IndexedDB::TransactionMode);
- void close(PassRefPtr<IDBDatabaseCallbacks>);
-
- void commit(int64_t transactionId);
- void abort(int64_t transactionId);
- void abort(int64_t transactionId, PassRefPtr<IDBDatabaseError>);
-
- 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<IDBKeyRange>, bool keyOnly, PassRefPtr<IDBCallbacks>);
- void put(int64_t transactionId, int64_t objectStoreId, PassRefPtr<SharedBuffer> value, PassRefPtr<IDBKey>, PutMode, PassRefPtr<IDBCallbacks>, const Vector<int64_t>& indexIds, const Vector<IndexKeys>&);
- void setIndexKeys(int64_t transactionId, int64_t objectStoreId, PassRefPtr<IDBKey> prpPrimaryKey, const Vector<int64_t>& indexIds, const Vector<IndexKeys>&);
- void setIndexesReady(int64_t transactionId, int64_t objectStoreId, const Vector<int64_t>& indexIds);
- void openCursor(int64_t transactionId, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange>, IndexedDB::CursorDirection, bool keyOnly, TaskType, PassRefPtr<IDBCallbacks>);
- void count(int64_t transactionId, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange>, PassRefPtr<IDBCallbacks>);
- void deleteRange(int64_t transactionId, int64_t objectStoreId, PassRefPtr<IDBKeyRange>, PassRefPtr<IDBCallbacks>);
- void clearObjectStore(int64_t transactionId, int64_t objectStoreId, PassRefPtr<IDBCallbacks>);
-
- const IDBDatabaseMetadata& metadata() const { return m_metadata; }
- void setCurrentVersion(uint64_t version) { m_metadata.version = version; }
-
- bool hasPendingSecondHalfOpen() { return m_pendingSecondHalfOpen; }
- void setPendingSecondHalfOpen(PassOwnPtr<IDBPendingOpenCall> 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<IDBCallbacks>, PassRefPtr<IDBDatabaseCallbacks>, int64_t transactionId, uint64_t version);
-
- void openInternalAsync();
- void didOpenInternalAsync(const IDBDatabaseMetadata&, bool success);
-
- void runIntVersionChangeTransaction(PassRefPtr<IDBCallbacks>, PassRefPtr<IDBDatabaseCallbacks>, int64_t transactionId, int64_t requestedVersion);
- size_t connectionCount();
- void processPendingCalls();
- void processPendingOpenCalls(bool success);
-
- bool isDeleteDatabaseBlocked();
- void deleteDatabaseAsync(PassRefPtr<IDBCallbacks>);
-
- IDBDatabaseMetadata m_metadata;
-
- String m_identifier;
-
- RefPtr<IDBFactoryBackendInterface> m_factory;
- Ref<IDBServerConnection> m_serverConnection;
-
- OwnPtr<IDBTransactionCoordinator> m_transactionCoordinator;
- RefPtr<IDBTransactionBackend> m_runningVersionChangeTransaction;
-
- typedef HashMap<int64_t, IDBTransactionBackend*> TransactionMap;
- TransactionMap m_transactions;
-
- Deque<OwnPtr<IDBPendingOpenCall>> m_pendingOpenCalls;
- OwnPtr<IDBPendingOpenCall> m_pendingSecondHalfOpen;
-
- Deque<OwnPtr<IDBPendingDeleteCall>> m_pendingDeleteCalls;
- HashSet<RefPtr<IDBCallbacks>> m_deleteCallbacksWaitingCompletion;
-
- typedef ListHashSet<RefPtr<IDBDatabaseCallbacks>> 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 <wtf/RefCounted.h>
-#include <wtf/text/WTFString.h>
-
-namespace WebCore {
-
-class IDBDatabase;
-
-class IDBDatabaseCallbacks : public RefCounted<IDBDatabaseCallbacks> {
-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<IDBDatabaseError>) = 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> 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<IDBDatabaseError> 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 <wtf/RefCounted.h>
-#include <wtf/text/WTFString.h>
-
-#if ENABLE(INDEXED_DATABASE)
-
-namespace WebCore {
-
-class IDBDatabase;
-
-class IDBDatabaseCallbacksImpl final : public IDBDatabaseCallbacks {
-public:
- static PassRefPtr<IDBDatabaseCallbacksImpl> 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<IDBDatabaseError>) 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 <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>
#include <wtf/text/WTFString.h>
@@ -37,18 +35,18 @@ namespace WebCore {
class IDBDatabaseError : public RefCounted<IDBDatabaseError> {
public:
- static PassRefPtr<IDBDatabaseError> create(unsigned short code)
+ static Ref<IDBDatabaseError> create(unsigned short code)
{
ASSERT(code >= IDBDatabaseException::IDBDatabaseExceptionOffset);
ASSERT(code < IDBDatabaseException::IDBDatabaseExceptionMax);
- return adoptRef(new IDBDatabaseError(code));
+ return adoptRef(*new IDBDatabaseError(code));
}
- static PassRefPtr<IDBDatabaseError> create(unsigned short code, const String& message)
+ static Ref<IDBDatabaseError> 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<IDBDatabaseException> create(const ExceptionCodeDescription& description)
+ static Ref<IDBDatabaseException> 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 <wtf/Ref.h>
+#include <wtf/text/WTFString.h>
+
+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 <wtf/Ref.h>
+#include <wtf/text/StringHash.h>
+#include <wtf/text/WTFString.h>
+
+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<sizeof(hashCodes)>(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<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> 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<IDBDatabaseIdentifier> {
+ static const bool hasIsEmptyValueFunction = true;
+ static const bool emptyValueIsZero = false;
+ static bool isEmptyValue(const IDBDatabaseIdentifier& info) { return info.isEmpty(); }
+};
+
+template<class Encoder>
+void IDBDatabaseIdentifier::encode(Encoder& encoder) const
+{
+ encoder << m_databaseName << m_openingOrigin << m_mainFrameOrigin;
+}
+
+template<class Decoder>
+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::IDBDatabaseIdentifier> : WebCore::IDBDatabaseIdentifierHashTraits { };
+template<> struct DefaultHash<WebCore::IDBDatabaseIdentifier> {
+ typedef WebCore::IDBDatabaseIdentifierHash Hash;
+};
+
+} // namespace WTF
+
+#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<int64_t, IDBObjectStoreMetadata> 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<RefPtr<EventTarget>>& eventTargets)
+bool IDBEventDispatcher::dispatch(Event& event, Vector<RefPtr<EventTarget>>& 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<RefPtr<EventTarget>>& 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 <wtf/RefPtr.h>
#include <wtf/Vector.h>
namespace WebCore {
@@ -41,7 +39,7 @@ class EventTarget;
class IDBEventDispatcher {
public:
- static bool dispatch(Event*, Vector<RefPtr<EventTarget>>&); // The target first and then its ancestors in order of how the event bubbles.
+ static bool dispatch(Event&, Vector<RefPtr<EventTarget>>&); // 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<Document>(context) || context.isWorkerGlobalScope());
+ if (is<Document>(context)) {
+ Document& document = downcast<Document>(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> 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<WorkerGlobalScope*>(context);
- const GroupSettings* groupSettings = workerGlobalScope->groupSettings();
- if (groupSettings)
- return groupSettings->indexedDBDatabasePath();
- return String();
-}
}
-PassRefPtr<IDBRequest> IDBFactory::getDatabaseNames(ScriptExecutionContext* context, ExceptionCode& ec)
+ExceptionOr<Ref<IDBOpenDBRequest>> IDBFactory::open(ScriptExecutionContext& context, const String& name, std::optional<uint64_t> 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<IDBRequest> 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<IDBOpenDBRequest> IDBFactory::open(ScriptExecutionContext* context, const String& name, ExceptionCode& ec)
+ExceptionOr<Ref<IDBOpenDBRequest>> 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<IDBOpenDBRequest> 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<IDBOpenDBRequest> 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<IDBDatabaseCallbacks> databaseCallbacks = IDBDatabaseCallbacksImpl::create();
- int64_t transactionId = IDBDatabase::nextTransactionId();
- RefPtr<IDBOpenDBRequest> 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<IDBOpenDBRequest> IDBFactory::deleteDatabase(ScriptExecutionContext* context, const String& name, ExceptionCode& ec)
+ExceptionOr<Ref<IDBOpenDBRequest>> 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<IDBOpenDBRequest> 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<short> IDBFactory::cmp(ExecState& execState, JSValue firstValue, JSValue secondValue)
{
- DOMRequestState requestState(context);
- RefPtr<IDBKey> first = scriptValueToIDBKey(&requestState, firstValue);
- RefPtr<IDBKey> 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<short>(first->compare(second.get()));
+void IDBFactory::getAllDatabaseNames(const SecurityOrigin& mainFrameOrigin, const SecurityOrigin& openingOrigin, std::function<void (const Vector<String>&)> 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 <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/RefPtr.h>
-#include <wtf/text/WTFString.h>
+#pragma once
#if ENABLE(INDEXED_DATABASE)
+#include "ExceptionOr.h"
+#include <functional>
+#include <wtf/Forward.h>
+#include <wtf/Ref.h>
+#include <wtf/ThreadSafeRefCounted.h>
+#include <wtf/Vector.h>
+
+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<IDBFactory> {
+class IDBFactory : public ThreadSafeRefCounted<IDBFactory> {
public:
- static PassRefPtr<IDBFactory> 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<IDBFactory> 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<IDBRequest> getDatabaseNames(ScriptExecutionContext*, ExceptionCode&);
+ ExceptionOr<Ref<IDBOpenDBRequest>> open(ScriptExecutionContext&, const String& name, std::optional<uint64_t> version);
+ ExceptionOr<Ref<IDBOpenDBRequest>> deleteDatabase(ScriptExecutionContext&, const String& name);
- PassRefPtr<IDBOpenDBRequest> open(ScriptExecutionContext*, const String& name, ExceptionCode&);
- PassRefPtr<IDBOpenDBRequest> open(ScriptExecutionContext*, const String& name, unsigned long long version, ExceptionCode&);
- PassRefPtr<IDBOpenDBRequest> deleteDatabase(ScriptExecutionContext*, const String& name, ExceptionCode&);
+ ExceptionOr<short> 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<void (const Vector<String>&)>);
private:
- IDBFactory(IDBFactoryBackendInterface*);
+ explicit IDBFactory(IDBClient::IDBConnectionProxy&);
- PassRefPtr<IDBOpenDBRequest> openInternal(ScriptExecutionContext*, const String& name, uint64_t version, IndexedDB::VersionNullness, ExceptionCode&);
+ ExceptionOr<Ref<IDBOpenDBRequest>> openInternal(ScriptExecutionContext&, const String& name, uint64_t version);
- RefPtr<IDBFactoryBackendInterface> m_backend;
+ Ref<IDBClient::IDBConnectionProxy> 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.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 <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/text/WTFString.h>
-
-#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<IDBFactoryBackendInterface> {
-public:
- static PassRefPtr<IDBFactoryBackendInterface> create(const String& databaseDirectoryIdentifier);
- virtual ~IDBFactoryBackendInterface() { }
-
- virtual void getDatabaseNames(PassRefPtr<IDBCallbacks>, PassRefPtr<SecurityOrigin>, ScriptExecutionContext*, const String& dataDir) = 0;
- virtual void open(const String& name, uint64_t version, int64_t transactionId, PassRefPtr<IDBCallbacks>, PassRefPtr<IDBDatabaseCallbacks>, const SecurityOrigin& openingOrigin, const SecurityOrigin& mainFrameOrigin) = 0;
- virtual void deleteDatabase(const String& name, PassRefPtr<IDBCallbacks>, PassRefPtr<SecurityOrigin>, 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 <wtf/HashSet.h>
+
+namespace WebCore {
+
+template<typename T> void isolatedCopyOfVariant(const WTF::Variant<Vector<IDBKeyData>, Vector<IDBValue>, std::nullptr_t>& source, WTF::Variant<Vector<IDBKeyData>, Vector<IDBValue>, std::nullptr_t>& target)
+{
+ target = Vector<T>();
+ auto& sourceVector = WTF::get<Vector<T>>(source);
+ auto& targetVector = WTF::get<Vector<T>>(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<std::nullptr_t>(source.m_results))
+ return;
+
+ switch (source.m_type) {
+ case IndexedDB::GetAllType::Keys:
+ isolatedCopyOfVariant<IDBKeyData>(source.m_results, destination.m_results);
+ break;
+ case IndexedDB::GetAllType::Values:
+ isolatedCopyOfVariant<IDBValue>(source.m_results, destination.m_results);
+ break;
+ }
+}
+
+void IDBGetAllResult::addKey(IDBKeyData&& key)
+{
+ ASSERT(m_type == IndexedDB::GetAllType::Keys);
+ ASSERT(WTF::holds_alternative<Vector<IDBKeyData>>(m_results));
+ WTF::get<Vector<IDBKeyData>>(m_results).append(WTFMove(key));
+}
+
+void IDBGetAllResult::addValue(IDBValue&& value)
+{
+ ASSERT(m_type == IndexedDB::GetAllType::Values);
+ ASSERT(WTF::holds_alternative<Vector<IDBValue>>(m_results));
+ WTF::get<Vector<IDBValue>>(m_results).append(WTFMove(value));
+}
+
+const Vector<IDBKeyData>& IDBGetAllResult::keys() const
+{
+ ASSERT(m_type == IndexedDB::GetAllType::Keys);
+ ASSERT(WTF::holds_alternative<Vector<IDBKeyData>>(m_results));
+ return WTF::get<Vector<IDBKeyData>>(m_results);
+}
+
+const Vector<IDBValue>& IDBGetAllResult::values() const
+{
+ ASSERT(m_type == IndexedDB::GetAllType::Values);
+ ASSERT(WTF::holds_alternative<Vector<IDBValue>>(m_results));
+ return WTF::get<Vector<IDBValue>>(m_results);
+}
+
+Vector<String> IDBGetAllResult::allBlobFilePaths() const
+{
+ ASSERT(m_type == IndexedDB::GetAllType::Values);
+
+ HashSet<String> pathSet;
+ for (auto& value : WTF::get<Vector<IDBValue>>(m_results)) {
+ for (auto& path : value.blobFilePaths())
+ pathSet.add(path);
+ }
+
+ Vector<String> 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 <wtf/Variant.h>
+
+namespace WebCore {
+
+class IDBGetAllResult {
+public:
+ IDBGetAllResult()
+ {
+ }
+
+ IDBGetAllResult(IndexedDB::GetAllType type)
+ : m_type(type)
+ {
+ switch (m_type) {
+ case IndexedDB::GetAllType::Keys:
+ m_results = Vector<IDBKeyData>();
+ break;
+ case IndexedDB::GetAllType::Values:
+ m_results = Vector<IDBValue>();
+ break;
+ }
+ }
+
+ enum IsolatedCopyTag { IsolatedCopy };
+ IDBGetAllResult(const IDBGetAllResult&, IsolatedCopyTag);
+ IDBGetAllResult isolatedCopy() const;
+
+ IndexedDB::GetAllType type() const { return m_type; }
+ const Vector<IDBKeyData>& keys() const;
+ const Vector<IDBValue>& values() const;
+
+ void addKey(IDBKeyData&&);
+ void addValue(IDBValue&&);
+
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static bool decode(Decoder&, IDBGetAllResult&);
+
+ WEBCORE_EXPORT Vector<String> allBlobFilePaths() const;
+
+private:
+ static void isolatedCopy(const IDBGetAllResult& source, IDBGetAllResult& destination);
+
+ IndexedDB::GetAllType m_type { IndexedDB::GetAllType::Keys };
+ WTF::Variant<Vector<IDBKeyData>, Vector<IDBValue>, std::nullptr_t> m_results { nullptr };
+};
+
+template<class Encoder>
+void IDBGetAllResult::encode(Encoder& encoder) const
+{
+ encoder << m_type << static_cast<uint64_t>(m_results.index());
+
+ switch (m_results.index()) {
+ case 0:
+ encoder << WTF::get<Vector<IDBKeyData>>(m_results);
+ break;
+ case 1:
+ encoder << WTF::get<Vector<IDBValue>>(m_results);
+ break;
+ case 2:
+ break;
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+}
+
+template<class Decoder>
+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<IDBKeyData>();
+ if (!decoder.decode(WTF::get<Vector<IDBKeyData>>(result.m_results)))
+ return false;
+ break;
+ case 1:
+ result.m_results = Vector<IDBValue>();
+ if (!decoder.decode(WTF::get<Vector<IDBValue>>(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<uint8_t> 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<SharedBuffer> buffer)
- : valueBuffer(buffer)
+ IDBGetResult(const IDBValue& value, const IDBKeyData& currentPrimaryKey)
+ : m_value(value)
+ , m_primaryKeyData(currentPrimaryKey)
{
}
- IDBGetResult(PassRefPtr<IDBKey> key)
- : keyData(key.get())
+ IDBGetResult(const ThreadSafeDataBuffer& buffer)
+ : m_value(buffer)
{
}
- IDBGetResult(PassRefPtr<SharedBuffer> buffer, PassRefPtr<IDBKey> 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<SharedBuffer> 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<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> 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<class Encoder>
+void IDBGetResult::encode(Encoder& encoder) const
+{
+ encoder << m_keyData << m_primaryKeyData << m_keyPath << m_isDefined << m_value;
+}
+
+template<class Decoder>
+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/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 <heap/HeapInlines.h>
+
+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<IDBRequest> IDBIndex::openCursor(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> 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<IDBRequest> 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<IDBRequest> IDBIndex::openCursor(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, const String& direction, ExceptionCode& ec)
+bool IDBIndex::hasPendingActivity() const
{
- LOG(StorageAPI, "IDBIndex::openCursor");
- RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(context, key, ec);
- if (ec)
- return 0;
- return openCursor(context, keyRange.release(), direction, ec);
+ return !m_objectStore.transaction().isFinished();
}
-PassRefPtr<IDBRequest> IDBIndex::count(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> 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<IDBRequest> 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<IDBRequest> IDBIndex::count(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec)
+ExceptionOr<void> IDBIndex::setName(const String& name)
{
- LOG(StorageAPI, "IDBIndex::count");
- RefPtr<IDBKeyRange> 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<IDBRequest> IDBIndex::openKeyCursor(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> 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<IDBRequest> 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<IDBRequest> IDBIndex::openKeyCursor(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, const String& direction, ExceptionCode& ec)
+bool IDBIndex::unique() const
{
- LOG(StorageAPI, "IDBIndex::openKeyCursor");
- RefPtr<IDBKeyRange> 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<IDBRequest> IDBIndex::get(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec)
+bool IDBIndex::multiEntry() const
{
- LOG(StorageAPI, "IDBIndex::get");
- RefPtr<IDBKeyRange> 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<IDBRequest> IDBIndex::get(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> 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<IDBRequest> 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<IDBRequest> IDBIndex::getKey(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBIndex::openCursor(ExecState& execState, IDBKeyRange* range, IDBCursorDirection direction)
{
- LOG(StorageAPI, "IDBIndex::getKey");
- RefPtr<IDBKeyRange> 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<IDBRequest> IDBIndex::getKey(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> keyRange, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> 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<Ref<IDBRequest>> 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<IDBRequest> 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<Ref<IDBRequest>> 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<Ref<IDBRequest>> IDBIndex::count(ExecState& execState, IDBKeyRange* range)
+{
+ LOG(IndexedDB, "IDBIndex::count");
+
+ return doCount(execState, range ? IDBKeyRangeData(range) : IDBKeyRangeData::allKeys());
+}
+
+ExceptionOr<Ref<IDBRequest>> 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<Ref<IDBRequest>> 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<Ref<IDBRequest>> IDBIndex::get(ExecState& execState, IDBKeyRange* range)
+{
+ LOG(IndexedDB, "IDBIndex::get");
+
+ return doGet(execState, IDBKeyRangeData(range));
+}
+
+ExceptionOr<Ref<IDBRequest>> 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<Ref<IDBRequest>> 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<Ref<IDBRequest>> IDBIndex::getKey(ExecState& execState, IDBKeyRange* range)
+{
+ LOG(IndexedDB, "IDBIndex::getKey");
+
+ return doGetKey(execState, IDBKeyRangeData(range));
+}
+
+ExceptionOr<Ref<IDBRequest>> 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<Ref<IDBRequest>> 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<Ref<IDBRequest>> IDBIndex::getAll(ExecState& execState, RefPtr<IDBKeyRange> range, std::optional<uint32_t> 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<Ref<IDBRequest>> IDBIndex::getAll(ExecState& execState, JSValue key, std::optional<uint32_t> 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<Ref<IDBRequest>> IDBIndex::getAllKeys(ExecState& execState, RefPtr<IDBKeyRange> range, std::optional<uint32_t> 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<Ref<IDBRequest>> IDBIndex::getAllKeys(ExecState& execState, JSValue key, std::optional<uint32_t> 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 <wtf/Forward.h>
-#include <wtf/text/WTFString.h>
-#if ENABLE(INDEXED_DATABASE)
+namespace JSC {
+class ExecState;
+}
namespace WebCore {
-class IDBObjectStore;
+class IDBKeyRange;
+
+struct IDBKeyRangeData;
-class IDBIndex : public ScriptWrappable, public RefCounted<IDBIndex> {
+class IDBIndex final : private ActiveDOMObject {
public:
- static PassRefPtr<IDBIndex> 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<IDBObjectStore> objectStore() const { return m_objectStore; }
- PassRefPtr<IDBAny> 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<IDBRequest> openCursor(ScriptExecutionContext* context, ExceptionCode& ec) { return openCursor(context, static_cast<IDBKeyRange*>(0), ec); }
- PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> keyRange, ExceptionCode& ec) { return openCursor(context, keyRange, IDBCursor::directionNext(), ec); }
- PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec) { return openCursor(context, key, IDBCursor::directionNext(), ec); }
- PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext*, PassRefPtr<IDBKeyRange>, const String& direction, ExceptionCode&);
- PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext*, const Deprecated::ScriptValue& key, const String& direction, ExceptionCode&);
- PassRefPtr<IDBRequest> count(ScriptExecutionContext* context, ExceptionCode& ec) { return count(context, static_cast<IDBKeyRange*>(0), ec); }
- PassRefPtr<IDBRequest> count(ScriptExecutionContext*, PassRefPtr<IDBKeyRange>, ExceptionCode&);
- PassRefPtr<IDBRequest> count(ScriptExecutionContext*, const Deprecated::ScriptValue& key, ExceptionCode&);
-
- PassRefPtr<IDBRequest> openKeyCursor(ScriptExecutionContext* context, ExceptionCode& ec) { return openKeyCursor(context, static_cast<IDBKeyRange*>(0), ec); }
- PassRefPtr<IDBRequest> openKeyCursor(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> keyRange, ExceptionCode& ec) { return openKeyCursor(context, keyRange, IDBCursor::directionNext(), ec); }
- PassRefPtr<IDBRequest> openKeyCursor(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec) { return openKeyCursor(context, key, IDBCursor::directionNext(), ec); }
- PassRefPtr<IDBRequest> openKeyCursor(ScriptExecutionContext*, PassRefPtr<IDBKeyRange>, const String& direction, ExceptionCode&);
- PassRefPtr<IDBRequest> openKeyCursor(ScriptExecutionContext*, const Deprecated::ScriptValue& key, const String& direction, ExceptionCode&);
-
- PassRefPtr<IDBRequest> get(ScriptExecutionContext*, PassRefPtr<IDBKeyRange>, ExceptionCode&);
- PassRefPtr<IDBRequest> get(ScriptExecutionContext*, const Deprecated::ScriptValue& key, ExceptionCode&);
- PassRefPtr<IDBRequest> getKey(ScriptExecutionContext*, PassRefPtr<IDBKeyRange>, ExceptionCode&);
- PassRefPtr<IDBRequest> 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<void> setName(const String&);
+ IDBObjectStore& objectStore();
+ const IDBKeyPath& keyPath() const;
+ bool unique() const;
+ bool multiEntry() const;
+
+ void rollbackInfoForVersionChangeAbort();
+
+ ExceptionOr<Ref<IDBRequest>> openCursor(JSC::ExecState&, IDBKeyRange*, IDBCursorDirection);
+ ExceptionOr<Ref<IDBRequest>> openCursor(JSC::ExecState&, JSC::JSValue key, IDBCursorDirection);
+ ExceptionOr<Ref<IDBRequest>> openKeyCursor(JSC::ExecState&, IDBKeyRange*, IDBCursorDirection);
+ ExceptionOr<Ref<IDBRequest>> openKeyCursor(JSC::ExecState&, JSC::JSValue key, IDBCursorDirection);
+
+ ExceptionOr<Ref<IDBRequest>> count(JSC::ExecState&, IDBKeyRange*);
+ ExceptionOr<Ref<IDBRequest>> count(JSC::ExecState&, JSC::JSValue key);
+
+ ExceptionOr<Ref<IDBRequest>> get(JSC::ExecState&, IDBKeyRange*);
+ ExceptionOr<Ref<IDBRequest>> get(JSC::ExecState&, JSC::JSValue key);
+ ExceptionOr<Ref<IDBRequest>> getKey(JSC::ExecState&, IDBKeyRange*);
+ ExceptionOr<Ref<IDBRequest>> getKey(JSC::ExecState&, JSC::JSValue key);
+
+ ExceptionOr<Ref<IDBRequest>> getAll(JSC::ExecState&, RefPtr<IDBKeyRange>, std::optional<uint32_t> count);
+ ExceptionOr<Ref<IDBRequest>> getAll(JSC::ExecState&, JSC::JSValue key, std::optional<uint32_t> count);
+ ExceptionOr<Ref<IDBRequest>> getAllKeys(JSC::ExecState&, RefPtr<IDBKeyRange>, std::optional<uint32_t> count);
+ ExceptionOr<Ref<IDBRequest>> getAllKeys(JSC::ExecState&, JSC::JSValue key, std::optional<uint32_t> 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<Ref<IDBRequest>> doCount(JSC::ExecState&, const IDBKeyRangeData&);
+ ExceptionOr<Ref<IDBRequest>> doGet(JSC::ExecState&, const IDBKeyRangeData&);
+ ExceptionOr<Ref<IDBRequest>> 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<IDBObjectStore> m_objectStore;
- RefPtr<IDBTransaction> 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<DOMString>) 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 <wtf/HashMap.h>
-#include <wtf/text/WTFString.h>
-
-#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 <runtime/ArrayBufferView.h>
+#include <runtime/JSArrayBuffer.h>
+#include <runtime/JSArrayBufferView.h>
+#include <runtime/JSCInlines.h>
namespace WebCore {
+using IDBKeyVector = Vector<RefPtr<IDBKey>>;
+
+Ref<IDBKey> IDBKey::createBinary(const ThreadSafeDataBuffer& buffer)
+{
+ return adoptRef(*new IDBKey(buffer));
+}
+
+Ref<IDBKey> IDBKey::createBinary(JSC::JSArrayBuffer& arrayBuffer)
+{
+ auto* buffer = arrayBuffer.impl();
+ return adoptRef(*new IDBKey(ThreadSafeDataBuffer::copyData(buffer->data(), buffer->byteLength())));
+}
+
+Ref<IDBKey> 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<IDBKeyVector>(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<IDBKeyVector>(m_value);
+ auto& otherArray = WTF::get<IDBKeyVector>(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<ThreadSafeDataBuffer>(m_value), WTF::get<ThreadSafeDataBuffer>(other.m_value));
+ case KeyType::String:
+ return -codePointCompare(WTF::get<String>(other.m_value), WTF::get<String>(m_value));
+ case KeyType::Date:
+ case KeyType::Number: {
+ auto number = WTF::get<double>(m_value);
+ auto otherNumber = WTF::get<double>(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 <wtf/Forward.h>
#include <wtf/RefCounted.h>
+#include <wtf/Variant.h>
#include <wtf/Vector.h>
#include <wtf/text/WTFString.h>
+using WebCore::IndexedDB::KeyType;
+
+namespace JSC {
+class JSArrayBuffer;
+class JSArrayBufferView;
+}
+
namespace WebCore {
class IDBKey : public RefCounted<IDBKey> {
public:
- typedef Vector<RefPtr<IDBKey>> KeyArray;
-
- static PassRefPtr<IDBKey> createInvalid()
+ static Ref<IDBKey> createInvalid()
{
- return adoptRef(new IDBKey());
+ return adoptRef(*new IDBKey());
}
- static PassRefPtr<IDBKey> createNumber(double number)
+ static Ref<IDBKey> createNumber(double number)
{
- return adoptRef(new IDBKey(NumberType, number));
+ return adoptRef(*new IDBKey(KeyType::Number, number));
}
- static PassRefPtr<IDBKey> createString(const String& string)
+ static Ref<IDBKey> createString(const String& string)
{
- return adoptRef(new IDBKey(string));
+ return adoptRef(*new IDBKey(string));
}
- static PassRefPtr<IDBKey> createDate(double date)
+ static Ref<IDBKey> createDate(double date)
{
- return adoptRef(new IDBKey(DateType, date));
+ return adoptRef(*new IDBKey(KeyType::Date, date));
}
- static PassRefPtr<IDBKey> createMultiEntryArray(const KeyArray& array)
+ static Ref<IDBKey> createMultiEntryArray(const Vector<RefPtr<IDBKey>>& array)
{
- KeyArray result;
+ Vector<RefPtr<IDBKey>> 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> idbKey = adoptRef(new IDBKey(result, sizeEstimate));
+ Ref<IDBKey> idbKey = adoptRef(*new IDBKey(result, sizeEstimate));
ASSERT(idbKey->isValid());
- return idbKey.release();
+ return idbKey;
}
- static PassRefPtr<IDBKey> createArray(const KeyArray& array)
+ static Ref<IDBKey> createArray(const Vector<RefPtr<IDBKey>>& 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<IDBKey> createBinary(const ThreadSafeDataBuffer&);
+ static Ref<IDBKey> createBinary(JSC::JSArrayBuffer&);
+ static Ref<IDBKey> 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<RefPtr<IDBKey>>& array() const
{
- ASSERT(m_type == ArrayType);
- return m_array;
+ ASSERT(m_type == KeyType::Array);
+ return WTF::get<Vector<RefPtr<IDBKey>>>(m_value);
}
const String& string() const
{
- ASSERT(m_type == StringType);
- return m_string;
+ ASSERT(m_type == KeyType::String);
+ return WTF::get<String>(m_value);
}
double date() const
{
- ASSERT(m_type == DateType);
- return m_number;
+ ASSERT(m_type == KeyType::Date);
+ return WTF::get<double>(m_value);
}
double number() const
{
- ASSERT(m_type == NumberType);
- return m_number;
+ ASSERT(m_type == KeyType::Number);
+ return WTF::get<double>(m_value);
+ }
+
+ const ThreadSafeDataBuffer& binary() const
+ {
+ ASSERT(m_type == KeyType::Binary);
+ return WTF::get<ThreadSafeDataBuffer>(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<IDBKey>::ref;
using RefCounted<IDBKey>::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<RefPtr<IDBKey>>& 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<Vector<RefPtr<IDBKey>>, String, double, ThreadSafeDataBuffer> m_value;
const size_t m_sizeEstimate;
@@ -164,8 +179,43 @@ private:
enum { OverheadSize = 16 };
};
+inline int compareBinaryKeyData(const Vector<uint8_t>& a, const Vector<uint8_t>& 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 <wtf/text/StringBuilder.h>
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<IDBKeyData>();
+ auto& array = WTF::get<Vector<IDBKeyData>>(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<IDBKey> IDBKeyData::maybeCreateIDBKey() const
+RefPtr<IDBKey> 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<RefPtr<IDBKey>> array;
+ for (auto& keyData : WTF::get<Vector<IDBKeyData>>(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<ThreadSafeDataBuffer>(m_value));
+ case KeyType::String:
+ return IDBKey::createString(WTF::get<String>(m_value));
+ case KeyType::Date:
+ return IDBKey::createDate(WTF::get<double>(m_value));
+ case KeyType::Number:
+ return IDBKey::createNumber(WTF::get<double>(m_value));
+ case KeyType::Max:
+ case KeyType::Min:
ASSERT_NOT_REACHED();
return nullptr;
}
@@ -99,74 +105,354 @@ PassRefPtr<IDBKey> 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<IDBKeyData>();
+ auto& destinationArray = WTF::get<Vector<IDBKeyData>>(destination.m_value);
+ for (auto& key : WTF::get<Vector<IDBKeyData>>(source.m_value))
+ destinationArray.append(key.isolatedCopy());
+ return;
+ }
+ case KeyType::Binary:
+ destination.m_value = WTF::get<ThreadSafeDataBuffer>(source.m_value);
+ return;
+ case KeyType::String:
+ destination.m_value = WTF::get<String>(source.m_value).isolatedCopy();
+ return;
+ case KeyType::Date:
+ case KeyType::Number:
+ destination.m_value = WTF::get<double>(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<Vector<IDBKeyData>>(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<ThreadSafeDataBuffer>(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<String>(m_value));
return;
- case IDBKey::DateType:
- case IDBKey::NumberType:
- encoder.encodeDouble("number", numberValue);
+ case KeyType::Date:
+ case KeyType::Number:
+ encoder.encodeDouble("number", WTF::get<double>(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<String>(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<double>(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<uint8_t> 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<IDBKeyData>();
+ return decoder.decodeObjects("array", WTF::get<Vector<IDBKeyData>>(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<Vector<IDBKeyData>>(m_value);
+ auto& otherArray = WTF::get<Vector<IDBKeyData>>(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<ThreadSafeDataBuffer>(m_value), WTF::get<ThreadSafeDataBuffer>(other.m_value));
+ case KeyType::String:
+ return codePointCompare(WTF::get<String>(m_value), WTF::get<String>(other.m_value));
+ case KeyType::Date:
+ case KeyType::Number: {
+ auto number = WTF::get<double>(m_value);
+ auto otherNumber = WTF::get<double>(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 (<rdar://problem/15779644>)
- return false;
+ if (m_isNull)
+ return "<null>";
+
+ String result;
+
+ switch (m_type) {
+ case KeyType::Invalid:
+ return "<invalid>";
+ case KeyType::Array: {
+ StringBuilder builder;
+ builder.appendLiteral("<array> - { ");
+ auto& array = WTF::get<Vector<IDBKeyData>>(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("<binary> - ");
+
+ auto* data = WTF::get<ThreadSafeDataBuffer>(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 = "<string> - " + WTF::get<String>(m_value);
+ break;
+ case KeyType::Date:
+ return String::format("<date> - %f", WTF::get<double>(m_value));
+ case KeyType::Number:
+ return String::format("<number> - %f", WTF::get<double>(m_value));
+ case KeyType::Max:
+ return "<maximum>";
+ case KeyType::Min:
+ return "<minimum>";
+ }
+
+ if (result.length() > 150) {
+ result.truncate(147);
+ result.append(WTF::ASCIILiteral("..."));
+ }
+
+ return result;
}
+#endif
+void IDBKeyData::setArrayValue(const Vector<IDBKeyData>& 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<double>(m_value) == WTF::get<double>(other.m_value);
+ case KeyType::String:
+ return WTF::get<String>(m_value) == WTF::get<String>(other.m_value);
+ case KeyType::Binary:
+ return WTF::get<ThreadSafeDataBuffer>(m_value) == WTF::get<ThreadSafeDataBuffer>(other.m_value);
+ case KeyType::Array:
+ return WTF::get<Vector<IDBKeyData>>(m_value) == WTF::get<Vector<IDBKeyData>>(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 <wtf/Variant.h>
+#include <wtf/text/StringHash.h>
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<IDBKey> 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<IDBKey> 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<IDBKeyData>&);
+ void setBinaryValue(const ThreadSafeDataBuffer&);
+ void setStringValue(const String&);
+ void setDateValue(double);
+ WEBCORE_EXPORT void setNumberValue(double);
+
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> 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<unsigned> hashCodes;
+ hashCodes.append(static_cast<unsigned>(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<sizeof(double)>(&WTF::get<double>(m_value)));
+ break;
+ case KeyType::String:
+ hashCodes.append(StringHash::hash(WTF::get<String>(m_value)));
+ break;
+ case KeyType::Binary: {
+ auto* data = WTF::get<ThreadSafeDataBuffer>(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<Vector<IDBKeyData>>(m_value))
+ hashCodes.append(key.hash());
+ break;
+ }
+
+ return StringHasher::hashMemory(hashCodes.data(), hashCodes.size() * sizeof(unsigned));
+ }
- IDBKey::Type type;
- Vector<IDBKeyData> 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<String>(m_value);
+ }
+
+ double date() const
+ {
+ ASSERT(m_type == KeyType::Date);
+ return WTF::get<double>(m_value);
+ }
+
+ double number() const
+ {
+ ASSERT(m_type == KeyType::Number);
+ return WTF::get<double>(m_value);
+ }
+
+ const ThreadSafeDataBuffer& binary() const
+ {
+ ASSERT(m_type == KeyType::Binary);
+ return WTF::get<ThreadSafeDataBuffer>(m_value);
+ }
+
+ const Vector<IDBKeyData>& array() const
+ {
+ ASSERT(m_type == KeyType::Array);
+ return WTF::get<Vector<IDBKeyData>>(m_value);
+ }
+
+private:
+ static void isolatedCopy(const IDBKeyData& source, IDBKeyData& destination);
+
+ KeyType m_type;
+ Variant<Vector<IDBKeyData>, 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<IDBKeyData> {
+ 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<class Encoder>
+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<Vector<IDBKeyData>>(m_value);
+ break;
+ case KeyType::Binary:
+ encoder << WTF::get<ThreadSafeDataBuffer>(m_value);
+ break;
+ case KeyType::String:
+ encoder << WTF::get<String>(m_value);
+ break;
+ case KeyType::Date:
+ case KeyType::Number:
+ encoder << WTF::get<double>(m_value);
+ break;
+ }
+}
+
+template<class Decoder>
+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<IDBKeyData>();
+ if (!decoder.decode(WTF::get<Vector<IDBKeyData>>(keyData.m_value)))
+ return false;
+ break;
+ case KeyType::Binary:
+ keyData.m_value = ThreadSafeDataBuffer();
+ if (!decoder.decode(WTF::get<ThreadSafeDataBuffer>(keyData.m_value)))
+ return false;
+ break;
+ case KeyType::String:
+ keyData.m_value = String();
+ if (!decoder.decode(WTF::get<String>(keyData.m_value)))
+ return false;
+ break;
+ case KeyType::Date:
+ case KeyType::Number:
+ keyData.m_value = 0.0;
+ if (!decoder.decode(WTF::get<double>(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 <wtf/ASCIICType.h>
#include <wtf/dtoa.h>
-#include <wtf/unicode/Unicode.h>
+#include <wtf/text/StringBuilder.h>
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<String> keyPathElements;
IDBParseKeyPath(keyPath, keyPathElements, error);
- return error == IDBKeyPathParseErrorNone;
+ return error == IDBKeyPathParseError::None;
}
void IDBParseKeyPath(const String& keyPath, Vector<String>& elements, IDBKeyPathParseError& error)
@@ -148,7 +147,7 @@ void IDBParseKeyPath(const String& keyPath, Vector<String>& 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<String>& 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<String>& 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<String>& 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<String>& 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<String>& vector) -> IDBKeyPath {
+ Vector<String> 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<String>& 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 <wtf/Variant.h>
#include <wtf/Vector.h>
#include <wtf/text/WTFString.h>
namespace WebCore {
-class KeyedDecoder;
-class KeyedEncoder;
+using IDBKeyPath = WTF::Variant<String, Vector<String>>;
+bool isIDBKeyPathValid(const IDBKeyPath&);
-enum IDBKeyPathParseError {
- IDBKeyPathParseErrorNone,
- IDBKeyPathParseErrorStart,
- IDBKeyPathParseErrorIdentifier,
- IDBKeyPathParseErrorDot,
+enum class IDBKeyPathParseError {
+ None,
+ Start,
+ Identifier,
+ Dot,
};
void IDBParseKeyPath(const String&, Vector<String>&, IDBKeyPathParseError&);
-
-class IDBKeyPath {
-public:
- IDBKeyPath() : m_type(NullType) { }
- explicit IDBKeyPath(const String&);
- explicit IDBKeyPath(const Vector<String>& array);
-
- enum Type {
- NullType = 0,
- StringType,
- ArrayType
- };
-
- Type type() const { return m_type; }
-
- const Vector<String>& 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<String> m_array;
-};
+IDBKeyPath isolatedCopy(const IDBKeyPath&);
+inline std::optional<IDBKeyPath> isolatedCopy(const std::optional<IDBKeyPath>& 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 <runtime/JSCJSValue.h>
-#if ENABLE(INDEXED_DATABASE)
+using namespace JSC;
namespace WebCore {
-PassRefPtr<IDBKeyRange> IDBKeyRange::create(PassRefPtr<IDBKey> prpKey)
+Ref<IDBKeyRange> IDBKeyRange::create(RefPtr<IDBKey>&& lower, RefPtr<IDBKey>&& upper, bool isLowerOpen, bool isUpperOpen)
{
- RefPtr<IDBKey> key = prpKey;
- return adoptRef(new IDBKeyRange(key, key, LowerBoundClosed, UpperBoundClosed));
+ return adoptRef(*new IDBKeyRange(WTFMove(lower), WTFMove(upper), isLowerOpen, isUpperOpen));
}
-IDBKeyRange::IDBKeyRange(PassRefPtr<IDBKey> lower, PassRefPtr<IDBKey> upper, LowerBoundType lowerType, UpperBoundType upperType)
- : m_lower(lower)
- , m_upper(upper)
- , m_lowerType(lowerType)
- , m_upperType(upperType)
+Ref<IDBKeyRange> IDBKeyRange::create(RefPtr<IDBKey>&& key)
{
+ auto upper = key;
+ return create(WTFMove(key), WTFMove(upper), false, false);
}
-Deprecated::ScriptValue IDBKeyRange::lowerValue(ScriptExecutionContext* context) const
+IDBKeyRange::IDBKeyRange(RefPtr<IDBKey>&& lower, RefPtr<IDBKey>&& 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> IDBKeyRange::only(PassRefPtr<IDBKey> prpKey, ExceptionCode& ec)
+ExceptionOr<Ref<IDBKeyRange>> IDBKeyRange::only(RefPtr<IDBKey>&& key)
{
- RefPtr<IDBKey> 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> IDBKeyRange::only(ScriptExecutionContext* context, const Deprecated::ScriptValue& keyValue, ExceptionCode& ec)
+ExceptionOr<Ref<IDBKeyRange>> IDBKeyRange::only(ExecState& state, JSValue keyValue)
{
- DOMRequestState requestState(context);
- RefPtr<IDBKey> 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> IDBKeyRange::lowerBound(ScriptExecutionContext* context, const Deprecated::ScriptValue& boundValue, bool open, ExceptionCode& ec)
+ExceptionOr<Ref<IDBKeyRange>> IDBKeyRange::lowerBound(ExecState& state, JSValue boundValue, bool open)
{
- DOMRequestState requestState(context);
- RefPtr<IDBKey> 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> IDBKeyRange::upperBound(ScriptExecutionContext* context, const Deprecated::ScriptValue& boundValue, bool open, ExceptionCode& ec)
+ExceptionOr<Ref<IDBKeyRange>> IDBKeyRange::upperBound(ExecState& state, JSValue boundValue, bool open)
{
- DOMRequestState requestState(context);
- RefPtr<IDBKey> 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> IDBKeyRange::bound(ScriptExecutionContext* context, const Deprecated::ScriptValue& lowerValue, const Deprecated::ScriptValue& upperValue, bool lowerOpen, bool upperOpen, ExceptionCode& ec)
+ExceptionOr<Ref<IDBKeyRange>> IDBKeyRange::bound(ExecState& state, JSValue lowerValue, JSValue upperValue, bool lowerOpen, bool upperOpen)
{
- DOMRequestState requestState(context);
- RefPtr<IDBKey> lower = scriptValueToIDBKey(&requestState, lowerValue);
- RefPtr<IDBKey> 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<bool> 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 <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+
+namespace JSC {
+class ExecState;
+class JSValue;
+}
namespace WebCore {
-typedef int ExceptionCode;
+class IDBKey;
+class ScriptExecutionContext;
class IDBKeyRange : public ScriptWrappable, public RefCounted<IDBKeyRange> {
public:
- enum LowerBoundType {
- LowerBoundOpen,
- LowerBoundClosed
- };
- enum UpperBoundType {
- UpperBoundOpen,
- UpperBoundClosed
- };
-
- static PassRefPtr<IDBKeyRange> create(PassRefPtr<IDBKey> lower, PassRefPtr<IDBKey> upper, LowerBoundType lowerType, UpperBoundType upperType)
- {
- return adoptRef(new IDBKeyRange(lower, upper, lowerType, upperType));
- }
- static PassRefPtr<IDBKeyRange> create(PassRefPtr<IDBKey> prpKey);
- ~IDBKeyRange() { }
+ static Ref<IDBKeyRange> create(RefPtr<IDBKey>&& lower, RefPtr<IDBKey>&& upper, bool isLowerOpen, bool isUpperOpen);
+ static Ref<IDBKeyRange> create(RefPtr<IDBKey>&&);
+ ~IDBKeyRange();
- PassRefPtr<IDBKey> lower() const { return m_lower; }
- PassRefPtr<IDBKey> 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<Ref<IDBKeyRange>> only(RefPtr<IDBKey>&& value);
+ static ExceptionOr<Ref<IDBKeyRange>> only(JSC::ExecState&, JSC::JSValue key);
- static PassRefPtr<IDBKeyRange> only(PassRefPtr<IDBKey> value, ExceptionCode&);
- static PassRefPtr<IDBKeyRange> only(ScriptExecutionContext*, const Deprecated::ScriptValue& key, ExceptionCode&);
+ static ExceptionOr<Ref<IDBKeyRange>> lowerBound(JSC::ExecState&, JSC::JSValue bound, bool open);
+ static ExceptionOr<Ref<IDBKeyRange>> upperBound(JSC::ExecState&, JSC::JSValue bound, bool open);
- static PassRefPtr<IDBKeyRange> lowerBound(ScriptExecutionContext* context, const Deprecated::ScriptValue& bound, ExceptionCode& ec) { return lowerBound(context, bound, false, ec); }
- static PassRefPtr<IDBKeyRange> lowerBound(ScriptExecutionContext*, const Deprecated::ScriptValue& bound, bool open, ExceptionCode&);
+ static ExceptionOr<Ref<IDBKeyRange>> bound(JSC::ExecState&, JSC::JSValue lower, JSC::JSValue upper, bool lowerOpen, bool upperOpen);
- static PassRefPtr<IDBKeyRange> upperBound(ScriptExecutionContext* context, const Deprecated::ScriptValue& bound, ExceptionCode& ec) { return upperBound(context, bound, false, ec); }
- static PassRefPtr<IDBKeyRange> upperBound(ScriptExecutionContext*, const Deprecated::ScriptValue& bound, bool open, ExceptionCode&);
+ ExceptionOr<bool> includes(JSC::ExecState&, JSC::JSValue key);
- static PassRefPtr<IDBKeyRange> bound(ScriptExecutionContext* context, const Deprecated::ScriptValue& lower, const Deprecated::ScriptValue& upper, ExceptionCode& ec) { return bound(context, lower, upper, false, false, ec); }
- static PassRefPtr<IDBKeyRange> 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<IDBKeyRange> 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<IDBKey> lower, PassRefPtr<IDBKey> upper, LowerBoundType lowerType, UpperBoundType upperType);
+ IDBKeyRange(RefPtr<IDBKey>&& lower, RefPtr<IDBKey>&& upper, bool isLowerOpen, bool isUpperOpen);
RefPtr<IDBKey> m_lower;
RefPtr<IDBKey> 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<IDBKeyRange> IDBKeyRangeData::maybeCreateIDBKeyRange() const
+RefPtr<IDBKeyRange> 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<IDBKeyRange> maybeCreateIDBKeyRange() const;
+ WEBCORE_EXPORT RefPtr<IDBKeyRange> maybeCreateIDBKeyRange() const;
+
+ WEBCORE_EXPORT bool isExactlyOneKey() const;
+ bool containsKey(const IDBKeyData&) const;
+ bool isValid() const;
+
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> 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<class Encoder>
+void IDBKeyRangeData::encode(Encoder& encoder) const
+{
+ encoder << isNull;
+ if (isNull)
+ return;
+
+ encoder << upperKey << lowerKey << upperOpen << lowerOpen;
+}
+
+template<class Decoder>
+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 <heap/HeapInlines.h>
+#include <wtf/Locker.h>
+
+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<DOMStringList> IDBObjectStore::indexNames() const
+IDBObjectStore::~IDBObjectStore()
{
- LOG(StorageAPI, "IDBObjectStore::indexNames");
- RefPtr<DOMStringList> 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<IDBRequest> IDBObjectStore::get(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> 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<IDBRequest> 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<IDBRequest> IDBObjectStore::get(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec)
+const String& IDBObjectStore::name() const
{
- RefPtr<IDBKeyRange> 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<void> IDBObjectStore::setName(const String& name)
{
- ASSERT(indexKeys);
- RefPtr<IDBKey> 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<IDBKeyPath>& 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<DOMStringList> IDBObjectStore::indexNames() const
+{
+ ASSERT(currentThread() == m_transaction.database().originThreadID());
+
+ RefPtr<DOMStringList> indexNames = DOMStringList::create();
+
+ if (!m_deleted) {
+ for (auto& name : m_info.indexNames())
+ indexNames->append(name);
+ indexNames->sort();
}
+
+ return indexNames;
}
-PassRefPtr<IDBRequest> 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<IDBRequest> 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<IDBKey*>(0), ec);
+ ASSERT(currentThread() == m_transaction.database().originThreadID());
+ return m_info.autoIncrement();
}
-PassRefPtr<IDBRequest> IDBObjectStore::put(JSC::ExecState* state, Deprecated::ScriptValue& value, const Deprecated::ScriptValue& key, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::openCursor(ExecState& execState, RefPtr<IDBKeyRange> 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<IDBRequest> IDBObjectStore::put(JSC::ExecState* state, Deprecated::ScriptValue& value, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::openCursor(ExecState& execState, JSValue key, IDBCursorDirection direction)
{
- LOG(StorageAPI, "IDBObjectStore::put");
- return put(IDBDatabaseBackend::AddOrUpdate, IDBAny::create(this), state, value, static_cast<IDBKey*>(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<IDBRequest> IDBObjectStore::put(IDBDatabaseBackend::PutMode putMode, PassRefPtr<IDBAny> source, JSC::ExecState* state, Deprecated::ScriptValue& value, const Deprecated::ScriptValue& keyValue, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::openKeyCursor(ExecState& execState, RefPtr<IDBKeyRange> range, IDBCursorDirection direction)
{
- ScriptExecutionContext* context = scriptExecutionContextFromExecState(state);
- DOMRequestState requestState(context);
- RefPtr<IDBKey> 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<IDBRequest> IDBObjectStore::put(IDBDatabaseBackend::PutMode putMode, PassRefPtr<IDBAny> source, JSC::ExecState* state, Deprecated::ScriptValue& value, PassRefPtr<IDBKey> prpKey, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::openKeyCursor(ExecState& execState, JSValue key, IDBCursorDirection direction)
{
- RefPtr<IDBKey> 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<SerializedScriptValue> 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<Ref<IDBRequest>> 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<IDBKey> 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<int64_t> indexIds;
- Vector<IndexKeys> 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<Ref<IDBRequest>> 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<IDBRequest> request = IDBRequest::create(context, source, m_transaction.get());
- Vector<uint8_t> valueBytes = serializedValue->toWireBytes();
- // This is a hack to account for disagreements about whether SerializedScriptValue should deal in Vector<uint8_t> or Vector<char>.
- // See https://lists.webkit.org/pipermail/webkit-dev/2013-February/023682.html
- Vector<char>* valueBytesSigned = reinterpret_cast<Vector<char>*>(&valueBytes);
- RefPtr<SharedBuffer> valueBuffer = SharedBuffer::adoptVector(*valueBytesSigned);
- backendDB()->put(m_transaction->id(), id(), valueBuffer, key.release(), static_cast<IDBDatabaseBackend::PutMode>(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<IDBRequest> IDBObjectStore::deleteFunction(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> keyRange, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> 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<IDBRequest> 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<IDBRequest> IDBObjectStore::deleteFunction(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::getKey(ExecState& execState, IDBKeyRange* keyRange)
{
- RefPtr<IDBKeyRange> 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<IDBRequest> IDBObjectStore::clear(ScriptExecutionContext* context, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> 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> idbKey;
+ if (!key.isUndefined())
+ idbKey = scriptValueToIDBKey(execState, key);
+ return putOrAdd(execState, value, idbKey, IndexedDB::ObjectStoreOverwriteMode::NoOverwrite, InlineKeyCheck::Perform);
+}
- RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
- backendDB()->clearObjectStore(m_transaction->id(), id(), request);
- return request.release();
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::put(ExecState& execState, JSValue value, JSValue key)
+{
+ RefPtr<IDBKey> 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<IndexPopulator> create(PassRefPtr<IDBDatabaseBackend> backend, int64_t transactionId, int64_t objectStoreId, const IDBIndexMetadata& indexMetadata)
- {
- return adoptRef(new IndexPopulator(backend, transactionId, objectStoreId, indexMetadata));
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::putForCursorUpdate(ExecState& state, JSValue value, JSValue key)
+{
+ return putOrAdd(state, value, scriptValueToIDBKey(state, key), IndexedDB::ObjectStoreOverwriteMode::OverwriteForCursor, InlineKeyCheck::DoNotPerform);
+}
+
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::putOrAdd(ExecState& state, JSValue value, RefPtr<IDBKey> 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<Document*>(context)->page())
+ privateBrowsingEnabled = page->sessionID().isEphemeral();
}
-private:
- IndexPopulator(PassRefPtr<IDBDatabaseBackend> 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<IDBRequest*>(target);
-
- RefPtr<IDBAny> cursorAny = request->result(ASSERT_NO_EXCEPTION);
- RefPtr<IDBCursorWithValue> cursor;
- if (cursorAny->type() == IDBAny::IDBCursorWithValueType)
- cursor = cursorAny->idbCursorWithValue();
-
- Vector<int64_t, 1> indexIds;
- indexIds.append(m_indexMetadata.id);
- if (cursor) {
- cursor->continueFunction(static_cast<IDBKey*>(0), ASSERT_NO_EXCEPTION);
-
- RefPtr<IDBKey> primaryKey = cursor->idbPrimaryKey();
- Deprecated::ScriptValue value = cursor->value();
-
- IDBObjectStore::IndexKeys indexKeys;
- generateIndexKeysForValue(request->requestState(), m_indexMetadata, value, &indexKeys);
-
- Vector<IDBObjectStore::IndexKeys, 1> 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<IDBKey> 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<IDBDatabaseBackend> 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<IDBIndex> IDBObjectStore::createIndex(ScriptExecutionContext* context, const String& name, const IDBKeyPath& keyPath, const Dictionary& options, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::deleteFunction(ExecState& execState, IDBKeyRange* keyRange)
{
- bool unique = false;
- options.get("unique", unique);
+ return doDelete(execState, keyRange);
+}
+
+ExceptionOr<Ref<IDBRequest>> 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<IDBIndex> IDBObjectStore::createIndex(ScriptExecutionContext* context, const String& name, const IDBKeyPath& keyPath, bool unique, bool multiEntry, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> 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> 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<Ref<IDBRequest>> 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<Ref<IDBIndex>> 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<IDBIndex> 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<IDBRequest> indexRequest = openCursor(context, static_cast<IDBKeyRange*>(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 = IndexPopulator::create(backendDB(), m_transaction->id(), id(), metadata);
- indexRequest->setOnsuccess(indexPopulator);
+ if (parameters.multiEntry && WTF::holds_alternative<Vector<String>>(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<IDBIndex> referencedIndex { *index };
+
+ Locker<Lock> locker(m_referencedIndexLock);
+ m_referencedIndexes.set(name, WTFMove(index));
+
+ return WTFMove(referencedIndex);
}
-PassRefPtr<IDBIndex> IDBObjectStore::index(const String& name, ExceptionCode& ec)
+ExceptionOr<Ref<IDBIndex>> 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<Lock> locker(m_referencedIndexLock);
+ auto iterator = m_referencedIndexes.find(indexName);
+ if (iterator != m_referencedIndexes.end())
+ return Ref<IDBIndex> { *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<IDBIndex>(*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<IDBIndex> referencedIndex { *index };
+
+ m_referencedIndexes.set(indexName, WTFMove(index));
+
+ return WTFMove(referencedIndex);
+}
+
+ExceptionOr<void> 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<Lock> 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<IDBIndex> 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<Ref<IDBRequest>> 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> 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<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::count(ExecState& execState, IDBKeyRange* range)
{
- return openCursor(context, static_cast<IDBKeyRange*>(0), ec);
+ LOG(IndexedDB, "IDBObjectStore::count");
+
+ return doCount(execState, range ? IDBKeyRangeData(range) : IDBKeyRangeData::allKeys());
}
-PassRefPtr<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> keyRange, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> 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<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::getAll(ExecState& execState, RefPtr<IDBKeyRange> range, std::optional<uint32_t> 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<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> range, const String& direction, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::getAll(ExecState& execState, JSValue key, std::optional<uint32_t> 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<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> range, const String& directionString, IDBDatabaseBackend::TaskType taskType, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::getAllKeys(ExecState& execState, RefPtr<IDBKeyRange> range, std::optional<uint32_t> 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<IDBRequest> 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<IDBDatabaseBackend::TaskType>(taskType), request);
- return request.release();
+ return m_transaction.requestGetAllObjectStoreRecords(execState, *this, range.get(), IndexedDB::GetAllType::Keys, count);
}
-PassRefPtr<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, const String& direction, ExceptionCode& ec)
+ExceptionOr<Ref<IDBRequest>> IDBObjectStore::getAllKeys(ExecState& execState, JSValue key, std::optional<uint32_t> count)
{
- RefPtr<IDBKeyRange> 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<IDBRequest> IDBObjectStore::count(ScriptExecutionContext* context, PassRefPtr<IDBKeyRange> 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<uint64_t> 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<Lock> locker(m_referencedIndexLock);
+
+ Vector<uint64_t> 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<IDBRequest> 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<IDBRequest> IDBObjectStore::count(ScriptExecutionContext* context, const Deprecated::ScriptValue& key, ExceptionCode& ec)
+void IDBObjectStore::visitReferencedIndexes(SlotVisitor& visitor) const
{
- RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(context, key, ec);
- if (ec)
- return 0;
- return count(context, keyRange.release(), ec);
+ Locker<Lock> 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 <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/RefPtr.h>
-#include <wtf/text/WTFString.h>
+#pragma once
#if ENABLE(INDEXED_DATABASE)
+#include "ActiveDOMObject.h"
+#include "ExceptionOr.h"
+#include "IDBCursorDirection.h"
+#include "IDBKeyPath.h"
+#include "IDBObjectStoreInfo.h"
+#include <wtf/HashSet.h>
+
+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<IDBObjectStore> {
+namespace IndexedDB {
+enum class ObjectStoreOverwriteMode;
+}
+
+class IDBObjectStore final : public ActiveDOMObject {
public:
- static PassRefPtr<IDBObjectStore> 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<IDBAny> keyPathAny() const { return IDBAny::create(m_metadata.keyPath); }
- const IDBKeyPath keyPath() const { return m_metadata.keyPath; }
- PassRefPtr<DOMStringList> indexNames() const;
- PassRefPtr<IDBTransaction> transaction() const { return m_transaction; }
- bool autoIncrement() const { return m_metadata.autoIncrement; }
-
- PassRefPtr<IDBRequest> add(JSC::ExecState*, Deprecated::ScriptValue&, ExceptionCode&);
- PassRefPtr<IDBRequest> put(JSC::ExecState*, Deprecated::ScriptValue&, ExceptionCode&);
- PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext*, ExceptionCode&);
- PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext*, PassRefPtr<IDBKeyRange>, ExceptionCode&);
- PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext*, const Deprecated::ScriptValue& key, ExceptionCode&);
- PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext*, PassRefPtr<IDBKeyRange>, const String& direction, ExceptionCode&);
- PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext*, PassRefPtr<IDBKeyRange>, const String& direction, IDBDatabaseBackend::TaskType, ExceptionCode&);
- PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext*, const Deprecated::ScriptValue& key, const String& direction, ExceptionCode&);
-
- PassRefPtr<IDBRequest> get(ScriptExecutionContext*, const Deprecated::ScriptValue& key, ExceptionCode&);
- PassRefPtr<IDBRequest> get(ScriptExecutionContext*, PassRefPtr<IDBKeyRange>, ExceptionCode&);
- PassRefPtr<IDBRequest> add(JSC::ExecState*, Deprecated::ScriptValue&, const Deprecated::ScriptValue& key, ExceptionCode&);
- PassRefPtr<IDBRequest> put(JSC::ExecState*, Deprecated::ScriptValue&, const Deprecated::ScriptValue& key, ExceptionCode&);
- PassRefPtr<IDBRequest> deleteFunction(ScriptExecutionContext*, PassRefPtr<IDBKeyRange>, ExceptionCode&);
- PassRefPtr<IDBRequest> deleteFunction(ScriptExecutionContext*, const Deprecated::ScriptValue& key, ExceptionCode&);
- PassRefPtr<IDBRequest> clear(ScriptExecutionContext*, ExceptionCode&);
-
- PassRefPtr<IDBIndex> createIndex(ScriptExecutionContext* context, const String& name, const String& keyPath, const Dictionary& options, ExceptionCode& ec) { return createIndex(context, name, IDBKeyPath(keyPath), options, ec); }
- PassRefPtr<IDBIndex> createIndex(ScriptExecutionContext* context, const String& name, const Vector<String>& keyPath, const Dictionary& options, ExceptionCode& ec) { return createIndex(context, name, IDBKeyPath(keyPath), options, ec); }
- PassRefPtr<IDBIndex> createIndex(ScriptExecutionContext*, const String& name, const IDBKeyPath&, const Dictionary&, ExceptionCode&);
- PassRefPtr<IDBIndex> createIndex(ScriptExecutionContext*, const String& name, const IDBKeyPath&, bool unique, bool multiEntry, ExceptionCode&);
-
- PassRefPtr<IDBIndex> index(const String& name, ExceptionCode&);
- void deleteIndex(const String& name, ExceptionCode&);
-
- PassRefPtr<IDBRequest> count(ScriptExecutionContext* context, ExceptionCode& ec) { return count(context, static_cast<IDBKeyRange*>(0), ec); }
- PassRefPtr<IDBRequest> count(ScriptExecutionContext*, PassRefPtr<IDBKeyRange>, ExceptionCode&);
- PassRefPtr<IDBRequest> count(ScriptExecutionContext*, const Deprecated::ScriptValue& key, ExceptionCode&);
-
- PassRefPtr<IDBRequest> put(IDBDatabaseBackend::PutMode, PassRefPtr<IDBAny> source, JSC::ExecState*, Deprecated::ScriptValue&, const Deprecated::ScriptValue& key, ExceptionCode&);
- PassRefPtr<IDBRequest> put(IDBDatabaseBackend::PutMode, PassRefPtr<IDBAny> source, JSC::ExecState*, Deprecated::ScriptValue&, PassRefPtr<IDBKey>, ExceptionCode&);
- void markDeleted() { m_deleted = true; }
- void transactionFinished();
-
- IDBObjectStoreMetadata metadata() const { return m_metadata; }
- void setMetadata(const IDBObjectStoreMetadata& metadata) { m_metadata = metadata; }
-
- typedef Vector<RefPtr<IDBKey>> IndexKeys;
- typedef HashMap<String, IndexKeys> IndexKeyMap;
-
- IDBDatabaseBackend* backendDB() const;
+ IDBObjectStore(ScriptExecutionContext&, const IDBObjectStoreInfo&, IDBTransaction&);
+ ~IDBObjectStore();
+
+ const String& name() const;
+ ExceptionOr<void> setName(const String&);
+ const std::optional<IDBKeyPath>& keyPath() const;
+ RefPtr<DOMStringList> indexNames() const;
+ IDBTransaction& transaction();
+ bool autoIncrement() const;
+
+ struct IndexParameters {
+ bool unique;
+ bool multiEntry;
+ };
+
+ ExceptionOr<Ref<IDBRequest>> openCursor(JSC::ExecState&, RefPtr<IDBKeyRange>, IDBCursorDirection);
+ ExceptionOr<Ref<IDBRequest>> openCursor(JSC::ExecState&, JSC::JSValue key, IDBCursorDirection);
+ ExceptionOr<Ref<IDBRequest>> openKeyCursor(JSC::ExecState&, RefPtr<IDBKeyRange>, IDBCursorDirection);
+ ExceptionOr<Ref<IDBRequest>> openKeyCursor(JSC::ExecState&, JSC::JSValue key, IDBCursorDirection);
+ ExceptionOr<Ref<IDBRequest>> get(JSC::ExecState&, JSC::JSValue key);
+ ExceptionOr<Ref<IDBRequest>> get(JSC::ExecState&, IDBKeyRange*);
+ ExceptionOr<Ref<IDBRequest>> getKey(JSC::ExecState&, JSC::JSValue key);
+ ExceptionOr<Ref<IDBRequest>> getKey(JSC::ExecState&, IDBKeyRange*);
+ ExceptionOr<Ref<IDBRequest>> add(JSC::ExecState&, JSC::JSValue, JSC::JSValue key);
+ ExceptionOr<Ref<IDBRequest>> put(JSC::ExecState&, JSC::JSValue, JSC::JSValue key);
+ ExceptionOr<Ref<IDBRequest>> deleteFunction(JSC::ExecState&, IDBKeyRange*);
+ ExceptionOr<Ref<IDBRequest>> deleteFunction(JSC::ExecState&, JSC::JSValue key);
+ ExceptionOr<Ref<IDBRequest>> clear(JSC::ExecState&);
+ ExceptionOr<Ref<IDBIndex>> createIndex(JSC::ExecState&, const String& name, IDBKeyPath&&, const IndexParameters&);
+ ExceptionOr<Ref<IDBIndex>> index(const String& name);
+ ExceptionOr<void> deleteIndex(const String& name);
+ ExceptionOr<Ref<IDBRequest>> count(JSC::ExecState&, IDBKeyRange*);
+ ExceptionOr<Ref<IDBRequest>> count(JSC::ExecState&, JSC::JSValue key);
+ ExceptionOr<Ref<IDBRequest>> getAll(JSC::ExecState&, RefPtr<IDBKeyRange>, std::optional<uint32_t> count);
+ ExceptionOr<Ref<IDBRequest>> getAll(JSC::ExecState&, JSC::JSValue key, std::optional<uint32_t> count);
+ ExceptionOr<Ref<IDBRequest>> getAllKeys(JSC::ExecState&, RefPtr<IDBKeyRange>, std::optional<uint32_t> count);
+ ExceptionOr<Ref<IDBRequest>> getAllKeys(JSC::ExecState&, JSC::JSValue key, std::optional<uint32_t> count);
+
+ ExceptionOr<Ref<IDBRequest>> 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<Ref<IDBRequest>> putOrAdd(JSC::ExecState&, JSC::JSValue, RefPtr<IDBKey>, IndexedDB::ObjectStoreOverwriteMode, InlineKeyCheck);
+ ExceptionOr<Ref<IDBRequest>> doCount(JSC::ExecState&, const IDBKeyRangeData&);
+ ExceptionOr<Ref<IDBRequest>> 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<IDBTransaction> 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<String, RefPtr<IDBIndex>> IDBIndexMap;
- IDBIndexMap m_indexMap;
+ bool m_deleted { false };
+
+ mutable Lock m_referencedIndexLock;
+ HashMap<String, std::unique_ptr<IDBIndex>> m_referencedIndexes;
+ HashMap<uint64_t, std::unique_ptr<IDBIndex>> 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<DOMString>) 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<DOMString> 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<DOMString>) 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<int64_t, IDBIndexMetadata> 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> IDBOpenDBRequest::create(ScriptExecutionContext* context, PassRefPtr<IDBDatabaseCallbacks> callbacks, int64_t transactionId, uint64_t version, IndexedDB::VersionNullness versionNullness)
+Ref<IDBOpenDBRequest> IDBOpenDBRequest::createDeleteRequest(ScriptExecutionContext& context, IDBClient::IDBConnectionProxy& connectionProxy, const IDBDatabaseIdentifier& databaseIdentifier)
{
- RefPtr<IDBOpenDBRequest> 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<IDBDatabaseCallbacks> 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> 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<IDBDatabaseBackend> prpDatabaseBackend, const IDBDatabaseMetadata& metadata)
+void IDBOpenDBRequest::fireSuccessAfterVersionChangeCommit()
{
- LOG(StorageAPI, "IDBOpenDBRequest::onUpgradeNeeded()");
- if (m_contextStopped || !scriptExecutionContext()) {
- RefPtr<IDBDatabaseBackend> 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<IDBDatabaseBackend> databaseBackend = prpDatabaseBackend;
+ enqueueEvent(WTFMove(event));
+}
- RefPtr<IDBDatabase> 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<IDBDatabaseBackend> prpBackend, const IDBDatabaseMetadata& metadata)
+void IDBOpenDBRequest::cancelForStop()
{
- LOG(StorageAPI, "IDBOpenDBRequest::onSuccess()");
- if (!shouldEnqueueEvent())
- return;
+ connectionProxy().openDBRequestCancelled({ connectionProxy(), *this });
+}
- RefPtr<IDBDatabaseBackend> backend = prpBackend;
- RefPtr<IDBDatabase> 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> 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<IDBDatabase> database = IDBDatabase::create(*scriptExecutionContext(), connectionProxy(), resultData);
+ Ref<IDBTransaction> 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<IDBOpenDBRequest> create(ScriptExecutionContext*, PassRefPtr<IDBDatabaseCallbacks>, int64_t transactionId, uint64_t version, IndexedDB::VersionNullness);
+ static Ref<IDBOpenDBRequest> createDeleteRequest(ScriptExecutionContext&, IDBClient::IDBConnectionProxy&, const IDBDatabaseIdentifier&);
+ static Ref<IDBOpenDBRequest> 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<IDBDatabaseBackend>, const IDBDatabaseMetadata&) override;
- virtual void onSuccess(PassRefPtr<IDBDatabaseBackend>, const IDBDatabaseMetadata&) override;
+private:
+ IDBOpenDBRequest(ScriptExecutionContext&, IDBClient::IDBConnectionProxy&, const IDBDatabaseIdentifier&, uint64_t version, IndexedDB::RequestType);
- // EventTarget
- virtual EventTargetInterface eventTargetInterface() const;
- virtual bool dispatchEvent(PassRefPtr<Event>) 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<IDBDatabaseCallbacks>, int64_t transactionId, uint64_t version, IndexedDB::VersionNullness);
+ bool isOpenDBRequest() const final { return true; }
- RefPtr<IDBDatabaseCallbacks> 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/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 <mutex>
-#include <wtf/ThreadSpecific.h>
-
-using WTF::ThreadSpecific;
-
-#if ENABLE(INDEXED_DATABASE)
-
-namespace WebCore {
-
-typedef Vector<RefPtr<IDBTransaction>> TransactionList;
-
-static ThreadSpecific<TransactionList>& transactions()
-{
- // FIXME: Move the Vector to ScriptExecutionContext to avoid dealing with
- // thread-local storage.
- static std::once_flag onceFlag;
- static ThreadSpecific<TransactionList>* transactions;
- std::call_once(onceFlag, []{
- transactions = new ThreadSpecific<TransactionList>;
- });
-
- return *transactions;
-}
-
-void IDBPendingTransactionMonitor::addNewTransaction(PassRefPtr<IDBTransaction> transaction)
-{
- transactions()->append(transaction);
-}
-
-void IDBPendingTransactionMonitor::deactivateNewTransactions()
-{
- ThreadSpecific<TransactionList>& list = transactions();
- for (size_t i = 0; i < list->size(); ++i) {
- RefPtr<IDBTransaction> 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 <wtf/Noncopyable.h>
-#include <wtf/Vector.h>
-
-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<IDBTransaction>);
- 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 <wtf/RefCounted.h>
#include <wtf/Vector.h>
@@ -37,14 +37,14 @@ namespace WebCore {
class IDBRecordIdentifier : public RefCounted<IDBRecordIdentifier> {
public:
- static PassRefPtr<IDBRecordIdentifier> create(const Vector<char>& encodedPrimaryKey, int64_t version)
+ static Ref<IDBRecordIdentifier> create(const Vector<char>& encodedPrimaryKey, int64_t version)
{
- return adoptRef(new IDBRecordIdentifier(encodedPrimaryKey, version));
+ return adoptRef(*new IDBRecordIdentifier(encodedPrimaryKey, version));
}
- static PassRefPtr<IDBRecordIdentifier> create()
+ static Ref<IDBRecordIdentifier> create()
{
- return adoptRef(new IDBRecordIdentifier);
+ return adoptRef(*new IDBRecordIdentifier);
}
const Vector<char>& 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 <heap/StrongInlines.h>
+#include <wtf/Variant.h>
+
+using namespace JSC;
namespace WebCore {
-PassRefPtr<IDBRequest> IDBRequest::create(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, IDBTransaction* transaction)
+Ref<IDBRequest> IDBRequest::create(ScriptExecutionContext& context, IDBObjectStore& objectStore, IDBTransaction& transaction)
{
- RefPtr<IDBRequest> 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> IDBRequest::create(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, IDBDatabaseBackend::TaskType taskType, IDBTransaction* transaction)
+Ref<IDBRequest> IDBRequest::create(ScriptExecutionContext& context, IDBCursor& cursor, IDBTransaction& transaction)
{
- RefPtr<IDBRequest> 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<IDBAny> 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> IDBRequest::create(ScriptExecutionContext& context, IDBIndex& index, IDBTransaction& transaction)
{
+ return adoptRef(*new IDBRequest(context, index, transaction));
}
-IDBRequest::~IDBRequest()
+Ref<IDBRequest> 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<IDBAny> IDBRequest::result(ExceptionCode& ec) const
+Ref<IDBRequest> 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<DOMError> 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<IDBAny> 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<IDBTransaction> 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<IDBCursor>& cursor) { cursor->clearRequest(); },
+ [] (const auto&) { }
+ );
+ }
}
-void IDBRequest::abort()
+ExceptionOr<std::optional<IDBRequest::Result>> 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<IDBRequest> self(this);
+ return std::optional<IDBRequest::Result> { 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<DOMError*> 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<ScopeGuard>([this]() {
+ ASSERT(WTF::holds_alternative<RefPtr<IDBCursor>>(m_source.value()));
+ WTF::get<RefPtr<IDBCursor>>(m_source.value())->decrementOutstandingRequestCount();
+ });
}
-void IDBRequest::setPendingCursor(PassRefPtr<IDBCursor> 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<IDBCursor> IDBRequest::getResultCursor()
+RefPtr<WebCore::IDBTransaction> 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<IDBObjectStore>& objectStore) { return objectStore->info().identifier(); },
+ [] (const RefPtr<IDBIndex>& index) { return index->info().objectStoreIdentifier(); },
+ [] (const RefPtr<IDBCursor>&) { return 0; }
+ );
}
-void IDBRequest::setResultCursor(PassRefPtr<IDBCursor> cursor, PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> 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<IDBObjectStore>&) -> uint64_t { return 0; },
+ [] (const RefPtr<IDBIndex>& index) -> uint64_t { return index->info().identifier(); },
+ [] (const RefPtr<IDBCursor>&) -> 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<RefPtr<IDBIndex>>(m_source.value()));
+
+ return m_requestedIndexRecordType;
}
-void IDBRequest::onError(PassRefPtr<IDBDatabaseError> 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<Event> createSuccessEvent()
+const char* IDBRequest::activeDOMObjectName() const
{
- return Event::create(eventNames().successEvent, false, false);
+ ASSERT(currentThread() == originThreadID());
+
+ return "IDBRequest";
}
-void IDBRequest::onSuccess(PassRefPtr<DOMStringList> 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<IDBCursorBackend> backend, PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> 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<IDBCursor> 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> 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<SharedBuffer> valueBuffer)
+void IDBRequest::enqueueEvent(Ref<Event>&& 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<IDBObjectStore> effectiveObjectStore(PassRefPtr<IDBAny> 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<RefPtr<EventTarget>> 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<SharedBuffer> valueBuffer, PassRefPtr<IDBKey> 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<IDBKey> primaryKey = prpPrimaryKey;
-#ifndef NDEBUG
- RefPtr<IDBKey> 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<JSC::Unknown> { vm, toJS<IDLIDBKeyData>(*state, *jsCast<JSDOMGlobalObject*>(state->lexicalGlobalObject()), keyData) } };
}
-void IDBRequest::onSuccessInternal(PassRefPtr<SerializedScriptValue> value)
+void IDBRequest::setResult(const Vector<IDBKeyData>& 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<JSC::Unknown> { vm, toJS<IDLSequence<IDLIDBKeyData>>(*state, *jsCast<JSDOMGlobalObject*>(state->lexicalGlobalObject()), keyDatas) } };
}
-void IDBRequest::onSuccessInternal(const Deprecated::ScriptValue& value)
+void IDBRequest::setResult(const Vector<IDBValue>& 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<JSC::Unknown> { vm, toJS<IDLSequence<IDLIDBValue>>(*state, *jsCast<JSDOMGlobalObject*>(state->lexicalGlobalObject()), values) } };
}
-void IDBRequest::onSuccess(PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer> 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<JSC::Unknown> { context->vm(), toJS<IDLUnrestrictedDouble>(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<JSC::Unknown> { vm, toJS<IDLIDBValue>(*state, *jsCast<JSDOMGlobalObject*>(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<JSC::Unknown> { 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<IDBCursor>& cursor) -> IDBCursor* { return cursor.get(); },
+ [] (const auto&) -> IDBCursor* { return nullptr; }
+ );
}
-bool IDBRequest::dispatchEvent(PassRefPtr<Event> 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<ScopeGuard>([this]() {
+ m_pendingCursor->decrementOutstandingRequestCount();
+ });
+}
- Vector<RefPtr<EventTarget>> 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<IDBCursor> 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> event)
+void IDBRequest::setResult(Ref<IDBDatabase>&& 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<IDBDatabase> { 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 <heap/Strong.h>
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<IDBRequest> {
public:
- static PassRefPtr<IDBRequest> create(ScriptExecutionContext*, PassRefPtr<IDBAny> source, IDBTransaction*);
- static PassRefPtr<IDBRequest> create(ScriptExecutionContext*, PassRefPtr<IDBAny> source, IDBDatabaseBackend::TaskType, IDBTransaction*);
+ static Ref<IDBRequest> create(ScriptExecutionContext&, IDBObjectStore&, IDBTransaction&);
+ static Ref<IDBRequest> create(ScriptExecutionContext&, IDBCursor&, IDBTransaction&);
+ static Ref<IDBRequest> create(ScriptExecutionContext&, IDBIndex&, IDBTransaction&);
+ static Ref<IDBRequest> createObjectStoreGet(ScriptExecutionContext&, IDBObjectStore&, IndexedDB::ObjectStoreRecordType, IDBTransaction&);
+ static Ref<IDBRequest> createIndexGet(ScriptExecutionContext&, IDBIndex&, IndexedDB::IndexRecordType, IDBTransaction&);
+
+ const IDBResourceIdentifier& resourceIdentifier() const { return m_resourceIdentifier; }
+
virtual ~IDBRequest();
- PassRefPtr<IDBAny> result(ExceptionCode&) const;
- unsigned short errorCode(ExceptionCode&) const;
- PassRefPtr<DOMError> error(ExceptionCode&) const;
- PassRefPtr<IDBAny> source() const;
- PassRefPtr<IDBTransaction> transaction() const;
- void preventPropagation() { m_preventPropagation = true; }
+ using Result = Variant<RefPtr<IDBCursor>, RefPtr<IDBDatabase>, JSC::Strong<JSC::Unknown>>;
+ ExceptionOr<std::optional<Result>> result() const;
+
+ using Source = Variant<RefPtr<IDBObjectStore>, RefPtr<IDBIndex>, RefPtr<IDBCursor>>;
+ const std::optional<Source>& source() const { return m_source; }
- // Defined in the IDL
- enum ReadyState {
- PENDING = 1,
- DONE = 2,
- EarlyDeath = 3
- };
+ ExceptionOr<DOMError*> error() const;
- const String& readyState() const;
+ RefPtr<IDBTransaction> 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<IDBCursor>);
- 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<IDBDatabaseError>);
- virtual void onSuccess(PassRefPtr<DOMStringList>);
- virtual void onSuccess(PassRefPtr<IDBCursorBackend>, PassRefPtr<IDBKey>, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer>);
- virtual void onSuccess(PassRefPtr<IDBKey>);
- virtual void onSuccess(PassRefPtr<SharedBuffer>);
- virtual void onSuccess(PassRefPtr<SharedBuffer>, PassRefPtr<IDBKey>, const IDBKeyPath&);
- virtual void onSuccess(int64_t);
- virtual void onSuccess();
- virtual void onSuccess(PassRefPtr<IDBKey>, PassRefPtr<IDBKey> primaryKey, PassRefPtr<SharedBuffer>);
- virtual void onSuccessWithPrefetch(const Vector<RefPtr<IDBKey>>&, const Vector<RefPtr<IDBKey>>&, const Vector<RefPtr<SharedBuffer>>&) { 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<Event>) override;
+ void setResult(const IDBKeyData&);
+ void setResult(const Vector<IDBKeyData>&);
+ void setResult(const Vector<IDBValue>&);
+ void setResult(uint64_t);
+ void setResultToStructuredClone(const IDBValue&);
+ void setResultToUndefined();
- void transactionDidFinishAndDispatch();
+ void willIterateCursor(IDBCursor&);
+ void didOpenOrIterateCursor(const IDBResultData&);
- using RefCounted<IDBCallbacks>::ref;
- using RefCounted<IDBCallbacks>::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<IDBAny> source, IDBDatabaseBackend::TaskType, IDBTransaction*);
- void enqueueEvent(PassRefPtr<Event>);
- virtual bool shouldEnqueueEvent() const;
- void onSuccessInternal(PassRefPtr<SerializedScriptValue>);
- void onSuccessInternal(const Deprecated::ScriptValue&);
-
- RefPtr<IDBAny> m_result;
- unsigned short m_errorCode;
- String m_errorMessage;
- RefPtr<DOMError> m_error;
- bool m_contextStopped;
+ IDBRequest(ScriptExecutionContext&, IDBClient::IDBConnectionProxy&);
+
+ void enqueueEvent(Ref<Event>&&);
+ bool dispatchEvent(Event&) override;
+
+ void setResult(Ref<IDBDatabase>&&);
+
+ 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<IDBTransaction> 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<DOMError> 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<IDBCursor> getResultCursor();
- void setResultCursor(PassRefPtr<IDBCursor>, PassRefPtr<IDBKey>, PassRefPtr<IDBKey> primaryKey, const Deprecated::ScriptValue&);
+ void onError();
+ void onSuccess();
- RefPtr<IDBAny> m_source;
- const IDBDatabaseBackend::TaskType m_taskType;
+ IDBCursor* resultCursor();
- bool m_hasPendingActivity;
- Vector<RefPtr<Event>> m_enqueuedEvents;
+ IDBError m_idbError;
+ IDBResourceIdentifier m_resourceIdentifier;
+
+ std::optional<Result> m_result;
+ std::optional<Source> 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<IDBCursor> m_pendingCursor;
- RefPtr<IDBKey> m_cursorKey;
- RefPtr<IDBKey> m_cursorPrimaryKey;
- Deprecated::ScriptValue m_cursorValue;
- bool m_didFireUpgradeNeededEvent;
- bool m_preventPropagation;
- DOMRequestState m_requestState;
+ std::unique_ptr<ScopeGuard> m_cursorRequestNotifier;
+
+ Ref<IDBClient::IDBConnectionProxy> 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/IDBPendingDeleteCall.h b/Source/WebCore/Modules/indexeddb/IDBRequestCompletionEvent.h
index 22bda5d49..64666435a 100644
--- a/Source/WebCore/Modules/indexeddb/IDBPendingDeleteCall.h
+++ b/Source/WebCore/Modules/indexeddb/IDBRequestCompletionEvent.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * 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
@@ -23,34 +23,28 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef IDBPendingDeleteCall_h
-#define IDBPendingDeleteCall_h
-
-#include "IDBCallbacks.h"
-#include <wtf/PassOwnPtr.h>
-#include <wtf/RefPtr.h>
+#pragma once
#if ENABLE(INDEXED_DATABASE)
+#include "Event.h"
+#include "IDBRequest.h"
+
namespace WebCore {
-class IDBPendingDeleteCall {
+class IDBRequestCompletionEvent : public Event {
public:
- static PassOwnPtr<IDBPendingDeleteCall> create(PassRefPtr<IDBCallbacks> callbacks)
+ static Ref<Event> create(const AtomicString& type, bool canBubble, bool cancelable, IDBRequest& request)
{
- return adoptPtr(new IDBPendingDeleteCall(callbacks));
+ return adoptRef(*new IDBRequestCompletionEvent(type, canBubble, cancelable, request));
}
- IDBCallbacks* callbacks() { return m_callbacks.get(); }
private:
- IDBPendingDeleteCall(PassRefPtr<IDBCallbacks> callbacks)
- : m_callbacks(callbacks)
- {
- }
- RefPtr<IDBCallbacks> m_callbacks;
+ IDBRequestCompletionEvent(const AtomicString& type, bool canBubble, bool cancelable, IDBRequest&);
+
+ Ref<IDBRequest> m_request;
};
} // namespace WebCore
#endif // ENABLE(INDEXED_DATABASE)
-#endif // IDBPendingDeleteCall_h
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 <functional>
-#include <wtf/HashSet.h>
-#include <wtf/RefCounted.h>
-#include <wtf/text/WTFString.h>
-
-#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<IDBServerConnection> {
-public:
- virtual ~IDBServerConnection() { }
-
- virtual bool isClosed() = 0;
-
- typedef std::function<void (bool success)> BoolCallbackFunction;
-
- // Factory-level operations
- virtual void deleteDatabase(const String& name, BoolCallbackFunction successCallback) = 0;
-
- // Database-level operations
- typedef std::function<void (const IDBDatabaseMetadata&, bool success)> GetIDBDatabaseMetadataFunction;
- virtual void getOrEstablishIDBDatabaseMetadata(GetIDBDatabaseMetadataFunction) = 0;
- virtual void close() = 0;
-
- // Transaction-level operations
- virtual void openTransaction(int64_t transactionID, const HashSet<int64_t>& objectStoreIds, IndexedDB::TransactionMode, BoolCallbackFunction successCallback) = 0;
- virtual void beginTransaction(int64_t transactionID, std::function<void()> completionCallback) = 0;
- virtual void commitTransaction(int64_t transactionID, BoolCallbackFunction successCallback) = 0;
- virtual void resetTransaction(int64_t transactionID, std::function<void()> completionCallback) = 0;
- virtual void rollbackTransaction(int64_t transactionID, std::function<void()> completionCallback) = 0;
-
- virtual void setIndexKeys(int64_t transactionID, int64_t databaseID, int64_t objectStoreID, const IDBObjectStoreMetadata&, IDBKey& primaryKey, const Vector<int64_t>& indexIDs, const Vector<Vector<RefPtr<IDBKey>>>& indexKeys, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) = 0;
-
- virtual void createObjectStore(IDBTransactionBackend&, const CreateObjectStoreOperation&, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) = 0;
- virtual void createIndex(IDBTransactionBackend&, const CreateIndexOperation&, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) = 0;
- virtual void deleteIndex(IDBTransactionBackend&, const DeleteIndexOperation&, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) = 0;
- virtual void get(IDBTransactionBackend&, const GetOperation&, std::function<void(const IDBGetResult&, PassRefPtr<IDBDatabaseError>)> completionCallback) = 0;
- virtual void put(IDBTransactionBackend&, const PutOperation&, std::function<void(PassRefPtr<IDBKey>, PassRefPtr<IDBDatabaseError>)> completionCallback) = 0;
- virtual void openCursor(IDBTransactionBackend&, const OpenCursorOperation&, std::function<void(int64_t, PassRefPtr<IDBDatabaseError>)> completionCallback) = 0;
- virtual void count(IDBTransactionBackend&, const CountOperation&, std::function<void(int64_t, PassRefPtr<IDBDatabaseError>)> completionCallback) = 0;
- virtual void deleteRange(IDBTransactionBackend&, const DeleteRangeOperation&, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) = 0;
- virtual void clearObjectStore(IDBTransactionBackend&, const ClearObjectStoreOperation&, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) = 0;
- virtual void deleteObjectStore(IDBTransactionBackend&, const DeleteObjectStoreOperation&, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) = 0;
- virtual void changeDatabaseVersion(IDBTransactionBackend&, const IDBDatabaseBackend::VersionChangeOperation&, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) = 0;
-
- // Cursor-level operations
- virtual void cursorAdvance(IDBCursorBackend&, const CursorAdvanceOperation&, std::function<void(PassRefPtr<IDBKey>, PassRefPtr<IDBKey>, PassRefPtr<SharedBuffer>, PassRefPtr<IDBDatabaseError>)> completionCallback) = 0;
- virtual void cursorIterate(IDBCursorBackend&, const CursorIterationOperation&, std::function<void(PassRefPtr<IDBKey>, PassRefPtr<IDBKey>, PassRefPtr<SharedBuffer>, PassRefPtr<IDBDatabaseError>)> 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 <wtf/NeverDestroyed.h>
+
+using namespace JSC;
namespace WebCore {
-PassRefPtr<IDBTransaction> IDBTransaction::create(ScriptExecutionContext* context, int64_t id, const Vector<String>& objectStoreNames, IndexedDB::TransactionMode mode, IDBDatabase* db)
+Ref<IDBTransaction> IDBTransaction::create(IDBDatabase& database, const IDBTransactionInfo& info)
{
- IDBOpenDBRequest* openDBRequest = 0;
- RefPtr<IDBTransaction> 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> IDBTransaction::create(ScriptExecutionContext* context, int64_t id, IDBDatabase* db, IDBOpenDBRequest* openDBRequest, const IDBDatabaseMetadata& previousMetadata)
+Ref<IDBTransaction> IDBTransaction::create(IDBDatabase& database, const IDBTransactionInfo& info, IDBOpenDBRequest& request)
{
- RefPtr<IDBTransaction> transaction(adoptRef(new IDBTransaction(context, id, Vector<String>(), 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<IDBTransaction> 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<DOMStringList> IDBTransaction::objectStoreNames() const
{
- DEFINE_STATIC_LOCAL(AtomicString, readonly, ("0", AtomicString::ConstructFromLiteral));
- return readonly;
+ ASSERT(currentThread() == m_database->originThreadID());
+
+ const Vector<String> names = isVersionChange() ? m_database->info().objectStoreNames() : m_info.objectStores();
+
+ Ref<DOMStringList> 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<String>& 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<Ref<IDBObjectStore>> 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<Lock> locker(m_referencedObjectStoreLock);
+
+ auto iterator = m_referencedObjectStores.find(objectStoreName);
+ if (iterator != m_referencedObjectStores.end())
+ return Ref<IDBObjectStore> { *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<IDBObjectStore>(*scriptExecutionContext(), *info, *this);
+ auto* rawObjectStore = objectStore.get();
+ m_referencedObjectStores.set(objectStoreName, WTFMove(objectStore));
+
+ return Ref<IDBObjectStore>(*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<void> 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<DOMError> 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<Lock> locker(m_referencedObjectStoreLock);
+
+ auto& info = m_database->info();
+ Vector<uint64_t> 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<IDBObjectStore> 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<RefPtr<IDBClient::TransactionOperation>> 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<RefPtr<IDBClient::TransactionOperation>> 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<IDBObjectStore> 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<IDBObjectStore> prpObjectStore)
+bool IDBTransaction::canSuspendForDocumentSuspension() const
{
- ASSERT(m_state != Finished);
- RefPtr<IDBObjectStore> 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<IDBObjectStore> 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<IDBClient::TransactionOperation>&& 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<IDBRequest> request = *m_requestList.begin();
- m_requestList.remove(request);
- request->abort();
}
- RefPtr<IDBTransaction> selfRef = this;
- backendDB()->abort(m_id);
+ if (!m_transactionOperationMap.isEmpty() || !m_openRequests.isEmpty())
+ return;
+
+ if (!isFinishedOrFinishing())
+ commit();
}
-IDBTransaction::OpenCursorNotifier::OpenCursorNotifier(PassRefPtr<IDBTransaction> 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<IDBCursor*> cursors;
- cursors.swap(m_openCursors);
- for (HashSet<IDBCursor*>::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<IDBDatabaseError> prpError)
+void IDBTransaction::didStart(const IDBError& error)
{
- LOG(StorageAPI, "IDBTransaction::onAbort");
- RefPtr<IDBDatabaseError> 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<IDBRequest> 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>&& 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<RefPtr<EventTarget>> 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> event)
+Ref<IDBObjectStore> 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<Lock> locker(m_referencedObjectStoreLock);
- Vector<RefPtr<EventTarget>> targets;
- targets.append(this);
- targets.append(db());
+ auto objectStore = std::make_unique<IDBObjectStore>(*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<Lock> 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<IDBIndex> 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<IDBIndex>(*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<Lock> 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<IDBRequest> 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<IDBRequest> 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<IDBRequest> IDBTransaction::doRequestOpenCursor(ExecState& state, Ref<IDBCursor>&& 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<IDBRequest> IDBTransaction::requestGetAllObjectStoreRecords(JSC::ExecState& state, IDBObjectStore& objectStore, const IDBKeyRangeData& keyRangeData, IndexedDB::GetAllType getAllType, std::optional<uint32_t> 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<IDBRequest> IDBTransaction::requestGetAllIndexRecords(JSC::ExecState& state, IDBIndex& index, const IDBKeyRangeData& keyRangeData, IndexedDB::GetAllType getAllType, std::optional<uint32_t> 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<IDBRequest> 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<IDBRequest> 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<IDBRequest> 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> event)
+Ref<IDBRequest> 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<IDBRequest> 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<IDBRequest> 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<IDBRequest> 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<IDBRequest> 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<IDBRequest> 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() : "<null key>");
+ scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didPutOrAddOnServer, &IDBTransaction::putOrAddOnServer, key, &value, overwriteMode));
+
+ return request;
+}
+
+void IDBTransaction::putOrAddOnServer(IDBClient::TransactionOperation& operation, RefPtr<IDBKey> key, RefPtr<SerializedScriptValue> 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<IDBClient::TransactionOperation> 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<IDBClient::TransactionOperation>(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<Lock> 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<RefPtr<IDBClient::TransactionOperation>> 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<Lock> 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 <wtf/HashSet.h>
-#include <wtf/RefCounted.h>
+#include "Timer.h"
+#include <wtf/Deque.h>
+#include <wtf/HashMap.h>
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<IDBTransaction>, public EventTargetWithInlineData, public ActiveDOMObject {
-public:
- static PassRefPtr<IDBTransaction> create(ScriptExecutionContext*, int64_t, const Vector<String>& objectStoreNames, IndexedDB::TransactionMode, IDBDatabase*);
- static PassRefPtr<IDBTransaction> 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<DOMError> error() const { return m_error; }
- PassRefPtr<IDBObjectStore> objectStore(const String& name, ExceptionCode&);
- void abort(ExceptionCode&);
-
- class OpenCursorNotifier {
- public:
- OpenCursorNotifier(PassRefPtr<IDBTransaction>, IDBCursor*);
- ~OpenCursorNotifier();
- void cursorFinished();
- private:
- RefPtr<IDBTransaction> m_transaction;
- IDBCursor* m_cursor;
- };
-
- void registerRequest(IDBRequest*);
- void unregisterRequest(IDBRequest*);
- void objectStoreCreated(const String&, PassRefPtr<IDBObjectStore>);
- void objectStoreDeleted(const String&);
- void setActive(bool);
- void setError(PassRefPtr<DOMError>, const String& errorMessage);
-
- DEFINE_ATTRIBUTE_EVENT_LISTENER(abort);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(complete);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(error);
-
- void onAbort(PassRefPtr<IDBDatabaseError>);
- 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<IDBTransaction>, public EventTargetWithInlineData, public IDBActiveDOMObject {
+public:
+ static Ref<IDBTransaction> create(IDBDatabase&, const IDBTransactionInfo&);
+ static Ref<IDBTransaction> create(IDBDatabase&, const IDBTransactionInfo&, IDBOpenDBRequest&);
+
+ ~IDBTransaction() final;
+
+ // IDBTransaction IDL
+ Ref<DOMStringList> objectStoreNames() const;
+ IDBTransactionMode mode() const { return m_info.mode(); }
+ IDBDatabase* db();
+ DOMError* error() const;
+ ExceptionOr<Ref<IDBObjectStore>> objectStore(const String& name);
+ ExceptionOr<void> 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<Event>) override;
+ bool dispatchEvent(Event&) final;
+
+ using ThreadSafeRefCounted<IDBTransaction>::ref;
+ using ThreadSafeRefCounted<IDBTransaction>::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<IDBObjectStore> createObjectStore(const IDBObjectStoreInfo&);
+ void renameObjectStore(IDBObjectStore&, const String& newName);
+ std::unique_ptr<IDBIndex> createIndex(IDBObjectStore&, const IDBIndexInfo&);
+ void renameIndex(IDBIndex&, const String& newName);
+
+ Ref<IDBRequest> requestPutOrAdd(JSC::ExecState&, IDBObjectStore&, IDBKey*, SerializedScriptValue&, IndexedDB::ObjectStoreOverwriteMode);
+ Ref<IDBRequest> requestGetRecord(JSC::ExecState&, IDBObjectStore&, const IDBGetRecordData&);
+ Ref<IDBRequest> requestGetAllObjectStoreRecords(JSC::ExecState&, IDBObjectStore&, const IDBKeyRangeData&, IndexedDB::GetAllType, std::optional<uint32_t> count);
+ Ref<IDBRequest> requestGetAllIndexRecords(JSC::ExecState&, IDBIndex&, const IDBKeyRangeData&, IndexedDB::GetAllType, std::optional<uint32_t> count);
+ Ref<IDBRequest> requestDeleteRecord(JSC::ExecState&, IDBObjectStore&, const IDBKeyRangeData&);
+ Ref<IDBRequest> requestClearObjectStore(JSC::ExecState&, IDBObjectStore&);
+ Ref<IDBRequest> requestCount(JSC::ExecState&, IDBObjectStore&, const IDBKeyRangeData&);
+ Ref<IDBRequest> requestCount(JSC::ExecState&, IDBIndex&, const IDBKeyRangeData&);
+ Ref<IDBRequest> requestGetValue(JSC::ExecState&, IDBIndex&, const IDBKeyRangeData&);
+ Ref<IDBRequest> requestGetKey(JSC::ExecState&, IDBIndex&, const IDBKeyRangeData&);
+ Ref<IDBRequest> requestOpenCursor(JSC::ExecState&, IDBObjectStore&, const IDBCursorInfo&);
+ Ref<IDBRequest> 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<IDBTransaction>::ref;
- using RefCounted<IDBTransaction>::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<String>&, 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<IDBClient::TransactionOperation>&&);
+ void pendingOperationTimerFired();
+ void completedOperationTimerFired();
+
+ void fireOnComplete();
+ void fireOnAbort();
+ void enqueueEvent(Ref<Event>&&);
+
+ Ref<IDBRequest> requestIndexRecord(JSC::ExecState&, IDBIndex&, IndexedDB::IndexRecordType, const IDBKeyRangeData&);
- void enqueueEvent(PassRefPtr<Event>);
- 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<IDBDatabase> m_database;
- const Vector<String> m_objectStoreNames;
- IDBOpenDBRequest* m_openDBRequest;
- const IndexedDB::TransactionMode m_mode;
- State m_state;
- bool m_hasPendingActivity;
- bool m_contextStopped;
- RefPtr<DOMError> m_error;
- String m_errorMessage;
+ void clearObjectStoreOnServer(IDBClient::TransactionOperation&, const uint64_t& objectStoreIdentifier);
+ void didClearObjectStoreOnServer(IDBRequest&, const IDBResultData&);
- ListHashSet<RefPtr<IDBRequest>> m_requestList;
+ void putOrAddOnServer(IDBClient::TransactionOperation&, RefPtr<IDBKey>, RefPtr<SerializedScriptValue>, const IndexedDB::ObjectStoreOverwriteMode&);
+ void didPutOrAddOnServer(IDBRequest&, const IDBResultData&);
- typedef HashMap<String, RefPtr<IDBObjectStore>> IDBObjectStoreMap;
- IDBObjectStoreMap m_objectStoreMap;
+ void getRecordOnServer(IDBClient::TransactionOperation&, const IDBGetRecordData&);
+ void didGetRecordOnServer(IDBRequest&, const IDBResultData&);
- typedef HashSet<RefPtr<IDBObjectStore>> IDBObjectStoreSet;
- IDBObjectStoreSet m_deletedObjectStores;
+ void getAllRecordsOnServer(IDBClient::TransactionOperation&, const IDBGetAllRecordsData&);
+ void didGetAllRecordsOnServer(IDBRequest&, const IDBResultData&);
- typedef HashMap<RefPtr<IDBObjectStore>, IDBObjectStoreMetadata> IDBObjectStoreMetadataMap;
- IDBObjectStoreMetadataMap m_objectStoreCleanupMap;
- IDBDatabaseMetadata m_previousMetadata;
+ void getCountOnServer(IDBClient::TransactionOperation&, const IDBKeyRangeData&);
+ void didGetCountOnServer(IDBRequest&, const IDBResultData&);
- HashSet<IDBCursor*> 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<IDBRequest> doRequestOpenCursor(JSC::ExecState&, Ref<IDBCursor>&&);
+ 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<IDBDatabase> m_database;
+ IDBTransactionInfo m_info;
+
+ IndexedDB::TransactionState m_state { IndexedDB::TransactionState::Inactive };
+ bool m_startedOnServer { false };
+
+ IDBError m_idbError;
+ RefPtr<DOMError> m_domError;
+
+ Timer m_pendingOperationTimer;
+ Timer m_completedOperationTimer;
+ std::unique_ptr<Timer> m_activationTimer;
+
+ RefPtr<IDBOpenDBRequest> m_openDBRequest;
+
+ Deque<RefPtr<IDBClient::TransactionOperation>> m_pendingTransactionOperationQueue;
+ Deque<IDBClient::TransactionOperation*> m_transactionOperationsInProgressQueue;
+ Deque<std::pair<RefPtr<IDBClient::TransactionOperation>, IDBResultData>> m_completedOnServerQueue;
+ Deque<RefPtr<IDBClient::TransactionOperation>> m_abortQueue;
+
+ HashMap<IDBResourceIdentifier, RefPtr<IDBClient::TransactionOperation>> m_transactionOperationMap;
+
+ mutable Lock m_referencedObjectStoreLock;
+ HashMap<String, std::unique_ptr<IDBObjectStore>> m_referencedObjectStores;
+ HashMap<uint64_t, std::unique_ptr<IDBObjectStore>> m_deletedObjectStores;
+
+ HashSet<RefPtr<IDBRequest>> m_openRequests;
+ RefPtr<IDBRequest> 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> IDBTransactionBackend::create(IDBDatabaseBackend* databaseBackend, int64_t id, PassRefPtr<IDBDatabaseCallbacks> callbacks, const Vector<int64_t>& objectStoreIds, IndexedDB::TransactionMode mode)
-{
- HashSet<int64_t> 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<IDBDatabaseCallbacks> callbacks, const HashSet<int64_t>& 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<IDBTransactionBackend> 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<IDBOperation> task, PassRefPtr<IDBSynchronousOperation> 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<IDBDatabaseError> 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<IDBTransactionBackend> 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<IDBSynchronousOperation> 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<long long>(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<long long>(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<IDBTransactionBackend> 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<IDBTransactionBackend>&)
-{
- 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<IDBTransactionBackend> self(this);
-
- TaskQueue* taskQueue = m_pendingPreemptiveEvents ? &m_preemptiveTaskQueue : &m_taskQueue;
- if (!taskQueue->isEmpty() && m_state != Finished) {
- ASSERT(m_state == Running);
- RefPtr<IDBOperation> 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<IDBCursorBackend*>::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<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> 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<IDBKeyRange> keyRange, IndexedDB::CursorType cursorType, PassRefPtr<IDBCallbacks> callbacks)
-{
- scheduleTask(GetOperation::create(this, metadata, objectStoreId, indexId, keyRange, cursorType, callbacks));
-}
-
-void IDBTransactionBackend::schedulePutOperation(const IDBObjectStoreMetadata& objectStoreMetadata, PassRefPtr<SharedBuffer> value, PassRefPtr<IDBKey> key, IDBDatabaseBackend::PutMode putMode, PassRefPtr<IDBCallbacks> callbacks, const Vector<int64_t>& indexIds, const Vector<IndexKeys>& 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<IDBKeyRange> keyRange, IndexedDB::CursorDirection direction, IndexedDB::CursorType cursorType, IDBDatabaseBackend::TaskType taskType, PassRefPtr<IDBCallbacks> callbacks)
-{
- scheduleTask(OpenCursorOperation::create(this, objectStoreId, indexId, keyRange, direction, cursorType, taskType, callbacks));
-}
-
-void IDBTransactionBackend::scheduleCountOperation(int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
-{
- scheduleTask(CountOperation::create(this, objectStoreId, indexId, keyRange, callbacks));
-}
-
-void IDBTransactionBackend::scheduleDeleteRangeOperation(int64_t objectStoreId, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
-{
- scheduleTask(DeleteRangeOperation::create(this, objectStoreId, keyRange, callbacks));
-}
-
-void IDBTransactionBackend::scheduleClearObjectStoreOperation(int64_t objectStoreId, PassRefPtr<IDBCallbacks> 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 <wtf/Deque.h>
-#include <wtf/HashSet.h>
-#include <wtf/RefPtr.h>
-
-namespace WebCore {
-
-class IDBCursorBackend;
-class IDBDatabaseCallbacks;
-
-class IDBTransactionBackend : public RefCounted<IDBTransactionBackend> {
-public:
- static PassRefPtr<IDBTransactionBackend> create(IDBDatabaseBackend*, int64_t transactionId, PassRefPtr<IDBDatabaseCallbacks>, const Vector<int64_t>& objectStoreIds, IndexedDB::TransactionMode);
- ~IDBTransactionBackend();
-
- void commit();
- void abort();
- void abort(PassRefPtr<IDBDatabaseError>);
-
- void run();
- IndexedDB::TransactionMode mode() const { return m_mode; }
- const HashSet<int64_t>& scope() const { return m_objectStoreIds; }
-
- void scheduleTask(PassRefPtr<IDBOperation> task, PassRefPtr<IDBSynchronousOperation> abortTask = nullptr) { scheduleTask(IDBDatabaseBackend::NormalTask, task, abortTask); }
- void scheduleTask(IDBDatabaseBackend::TaskType, PassRefPtr<IDBOperation>, PassRefPtr<IDBSynchronousOperation> 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<IDBCallbacks>, PassRefPtr<IDBDatabaseCallbacks>, 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<IDBKeyRange>, IndexedDB::CursorType, PassRefPtr<IDBCallbacks>);
- void schedulePutOperation(const IDBObjectStoreMetadata&, PassRefPtr<SharedBuffer> value, PassRefPtr<IDBKey>, IDBDatabaseBackend::PutMode, PassRefPtr<IDBCallbacks>, const Vector<int64_t>& indexIds, const Vector<IndexKeys>&);
- void scheduleSetIndexesReadyOperation(size_t indexCount);
- void scheduleOpenCursorOperation(int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange>, IndexedDB::CursorDirection, IndexedDB::CursorType, IDBDatabaseBackend::TaskType, PassRefPtr<IDBCallbacks>);
- void scheduleCountOperation(int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange>, PassRefPtr<IDBCallbacks>);
- void scheduleDeleteRangeOperation(int64_t objectStoreId, PassRefPtr<IDBKeyRange>, PassRefPtr<IDBCallbacks>);
- void scheduleClearObjectStoreOperation(int64_t objectStoreId, PassRefPtr<IDBCallbacks>);
-
- int64_t id() const { return m_id; }
-
-private:
- IDBTransactionBackend(IDBDatabaseBackend*, int64_t id, PassRefPtr<IDBDatabaseCallbacks>, const HashSet<int64_t>& 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<IDBTransactionBackend>&);
- void closeOpenCursors();
-
- const HashSet<int64_t> m_objectStoreIds;
- const IndexedDB::TransactionMode m_mode;
-
- State m_state;
- bool m_commitPending;
- RefPtr<IDBDatabaseCallbacks> m_callbacks;
- RefPtr<IDBDatabaseBackend> m_database;
-
- typedef Deque<RefPtr<IDBOperation>> TaskQueue;
- TaskQueue m_taskQueue;
- TaskQueue m_preemptiveTaskQueue;
- Deque<RefPtr<IDBSynchronousOperation>> m_abortTaskQueue;
-
- // FIXME: delete the timer once we have threads instead.
- Timer<IDBTransactionBackend> m_taskTimer;
- int m_pendingPreemptiveEvents;
-
- HashSet<IDBCursorBackend*> 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 <wtf/text/CString.h>
-
-#if ENABLE(INDEXED_DATABASE)
-
-#define STANDARD_DATABASE_ERROR_CALLBACK std::function<void(PassRefPtr<IDBDatabaseError>)> operationCallback = \
- [operation, completionCallback](PassRefPtr<IDBDatabaseError> error) { \
- if (error) \
- operation->m_transaction->abort(error); \
- completionCallback(); \
- };
-
-namespace WebCore {
-
-void CreateObjectStoreOperation::perform(std::function<void()> completionCallback)
-{
- LOG(StorageAPI, "CreateObjectStoreOperation");
-
- RefPtr<CreateObjectStoreOperation> operation(this);
- STANDARD_DATABASE_ERROR_CALLBACK;
-
- m_transaction->database().serverConnection().createObjectStore(*m_transaction, *this, operationCallback);
-}
-
-void CreateIndexOperation::perform(std::function<void()> completionCallback)
-{
- LOG(StorageAPI, "CreateIndexOperation");
-
- RefPtr<CreateIndexOperation> 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<void()> completionCallback)
-{
- LOG(StorageAPI, "DeleteIndexOperation");
-
- RefPtr<DeleteIndexOperation> 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<void()> completionCallback)
-{
- LOG(StorageAPI, "GetOperation");
-
- RefPtr<GetOperation> operation(this);
- STANDARD_DATABASE_ERROR_CALLBACK;
-
- m_transaction->database().serverConnection().get(*m_transaction, *this, [this, operation, operationCallback](const IDBGetResult& result, PassRefPtr<IDBDatabaseError> prpError) {
- RefPtr<IDBDatabaseError> 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<void()> completionCallback)
-{
- LOG(StorageAPI, "PutOperation");
- ASSERT(m_transaction->mode() != IndexedDB::TransactionMode::ReadOnly);
- ASSERT(m_indexIDs.size() == m_indexKeys.size());
-
- RefPtr<PutOperation> operation(this);
- STANDARD_DATABASE_ERROR_CALLBACK;
-
- m_transaction->database().serverConnection().put(*m_transaction, *this, [this, operation, operationCallback](PassRefPtr<IDBKey> key, PassRefPtr<IDBDatabaseError> prpError) {
- RefPtr<IDBDatabaseError> 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<void()> completionCallback)
-{
- LOG(StorageAPI, "SetIndexesReadyOperation");
-
- for (size_t i = 0; i < m_indexCount; ++i)
- m_transaction->didCompletePreemptiveEvent();
-
- callOnMainThread(completionCallback);
-}
-
-void OpenCursorOperation::perform(std::function<void()> completionCallback)
-{
- LOG(StorageAPI, "OpenCursorOperation");
-
- RefPtr<OpenCursorOperation> operation(this);
- auto callback = [this, operation, completionCallback](int64_t cursorID, PassRefPtr<IDBDatabaseError>) {
- // 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<SharedBuffer*>(0));
- else {
- RefPtr<IDBCursorBackend> 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<void()> completionCallback)
-{
- LOG(StorageAPI, "CountOperation");
-
- RefPtr<CountOperation> operation(this);
- auto callback = [this, operation, completionCallback](int64_t count, PassRefPtr<IDBDatabaseError>) {
- // 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<void()> completionCallback)
-{
- LOG(StorageAPI, "DeleteRangeOperation");
-
- RefPtr<DeleteRangeOperation> operation(this);
- auto callback = [this, operation, completionCallback](PassRefPtr<IDBDatabaseError> 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<void()> completionCallback)
-{
- LOG(StorageAPI, "ClearObjectStoreOperation");
-
- RefPtr<ClearObjectStoreOperation> operation(this);
-
- auto clearCallback = [this, operation, completionCallback](PassRefPtr<IDBDatabaseError> prpError) {
- RefPtr<IDBDatabaseError> 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<void()> completionCallback)
-{
- LOG(StorageAPI, "DeleteObjectStoreOperation");
-
- RefPtr<DeleteObjectStoreOperation> operation(this);
- STANDARD_DATABASE_ERROR_CALLBACK;
-
- m_transaction->database().serverConnection().deleteObjectStore(*m_transaction, *this, operationCallback);
-}
-
-void IDBDatabaseBackend::VersionChangeOperation::perform(std::function<void()> completionCallback)
-{
- LOG(StorageAPI, "VersionChangeOperation");
-
- uint64_t oldVersion = m_transaction->database().metadata().version;
- RefPtr<IDBDatabaseBackend::VersionChangeOperation> operation(this);
- ASSERT(static_cast<uint64_t>(m_version) > oldVersion);
-
- std::function<void(PassRefPtr<IDBDatabaseError>)> operationCallback = [oldVersion, operation, this, completionCallback](PassRefPtr<IDBDatabaseError> prpError) {
- RefPtr<IDBDatabaseError> 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<IDBOperation> create(IDBTransactionBackend* transaction, const IDBObjectStoreMetadata& objectStoreMetadata)
- {
- return adoptRef(new CreateObjectStoreOperation(transaction, objectStoreMetadata));
- }
- virtual void perform(std::function<void()> successCallback) override final;
-
- const IDBObjectStoreMetadata& objectStoreMetadata() const { return m_objectStoreMetadata; }
-
-private:
- CreateObjectStoreOperation(IDBTransactionBackend* transaction, const IDBObjectStoreMetadata& objectStoreMetadata)
- : m_transaction(transaction)
- , m_objectStoreMetadata(objectStoreMetadata)
- {
- }
-
- RefPtr<IDBTransactionBackend> m_transaction;
- const IDBObjectStoreMetadata m_objectStoreMetadata;
-};
-
-class DeleteObjectStoreOperation : public IDBOperation {
-public:
- static PassRefPtr<IDBOperation> create(IDBTransactionBackend* transaction, const IDBObjectStoreMetadata& objectStoreMetadata)
- {
- return adoptRef(new DeleteObjectStoreOperation(transaction, objectStoreMetadata));
- }
- virtual void perform(std::function<void()> 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<IDBTransactionBackend> m_transaction;
- const IDBObjectStoreMetadata m_objectStoreMetadata;
-};
-
-class IDBDatabaseBackend::VersionChangeOperation : public IDBOperation {
-public:
- static PassRefPtr<IDBOperation> create(IDBTransactionBackend* transaction, int64_t version, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks)
- {
- return adoptRef(new VersionChangeOperation(transaction, version, callbacks, databaseCallbacks));
- }
- virtual void perform(std::function<void()> 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<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks)
- : m_transaction(transaction)
- , m_version(version)
- , m_callbacks(callbacks)
- , m_databaseCallbacks(databaseCallbacks)
- {
- }
-
- RefPtr<IDBTransactionBackend> m_transaction;
- int64_t m_version;
- RefPtr<IDBCallbacks> m_callbacks;
- RefPtr<IDBDatabaseCallbacks> m_databaseCallbacks;
-};
-
-class CreateObjectStoreAbortOperation : public IDBSynchronousOperation {
-public:
- static PassRefPtr<IDBSynchronousOperation> 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<IDBTransactionBackend> m_transaction;
- const int64_t m_objectStoreID;
-};
-
-class DeleteObjectStoreAbortOperation : public IDBSynchronousOperation {
-public:
- static PassRefPtr<IDBSynchronousOperation> 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<IDBTransactionBackend> m_transaction;
- IDBObjectStoreMetadata m_objectStoreMetadata;
-};
-
-class IDBDatabaseBackend::VersionChangeAbortOperation : public IDBSynchronousOperation {
-public:
- static PassRefPtr<IDBSynchronousOperation> 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<IDBTransactionBackend> m_transaction;
- String m_previousVersion;
- int64_t m_previousIntVersion;
-};
-
-class CreateIndexOperation : public IDBOperation {
-public:
- static PassRefPtr<IDBOperation> create(IDBTransactionBackend* transaction, int64_t objectStoreId, const IDBIndexMetadata& indexMetadata)
- {
- return adoptRef(new CreateIndexOperation(transaction, objectStoreId, indexMetadata));
- }
- virtual void perform(std::function<void()> 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<IDBTransactionBackend> m_transaction;
- const int64_t m_objectStoreID;
- const IDBIndexMetadata m_indexMetadata;
-};
-
-class CreateIndexAbortOperation : public IDBSynchronousOperation {
-public:
- static PassRefPtr<IDBSynchronousOperation> 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<IDBTransactionBackend> m_transaction;
- const int64_t m_objectStoreID;
- const int64_t m_indexID;
-};
-
-class DeleteIndexOperation : public IDBOperation {
-public:
- static PassRefPtr<IDBOperation> create(IDBTransactionBackend* transaction, int64_t objectStoreId, const IDBIndexMetadata& indexMetadata)
- {
- return adoptRef(new DeleteIndexOperation(transaction, objectStoreId, indexMetadata));
- }
- virtual void perform(std::function<void()> 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<IDBTransactionBackend> m_transaction;
- const int64_t m_objectStoreID;
- const IDBIndexMetadata m_indexMetadata;
-};
-
-class DeleteIndexAbortOperation : public IDBSynchronousOperation {
-public:
- static PassRefPtr<IDBSynchronousOperation> 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<IDBTransactionBackend> m_transaction;
- const int64_t m_objectStoreID;
- const IDBIndexMetadata m_indexMetadata;
-};
-
-class GetOperation : public IDBOperation {
-public:
- static PassRefPtr<IDBOperation> create(IDBTransactionBackend* transaction, const IDBDatabaseMetadata& metadata, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, IndexedDB::CursorType cursorType, PassRefPtr<IDBCallbacks> callbacks)
- {
- return adoptRef(new GetOperation(transaction, metadata, objectStoreId, indexId, keyRange, cursorType, callbacks));
- }
- virtual void perform(std::function<void()> 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<IDBKeyRange> keyRange, IndexedDB::CursorType cursorType, PassRefPtr<IDBCallbacks> 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<IDBTransactionBackend> m_transaction;
- const int64_t m_objectStoreID;
- const int64_t m_indexID;
- const IDBKeyPath m_keyPath;
- const bool m_autoIncrement;
- const RefPtr<IDBKeyRange> m_keyRange;
- const IndexedDB::CursorType m_cursorType;
- const RefPtr<IDBCallbacks> m_callbacks;
-};
-
-class PutOperation : public IDBOperation {
-public:
- static PassRefPtr<IDBOperation> create(IDBTransactionBackend* transaction, const IDBObjectStoreMetadata& objectStore, PassRefPtr<SharedBuffer> value, PassRefPtr<IDBKey> key, IDBDatabaseBackend::PutMode putMode, PassRefPtr<IDBCallbacks> callbacks, const Vector<int64_t>& indexIds, const Vector<IndexKeys>& indexKeys)
- {
- return adoptRef(new PutOperation(transaction, objectStore, value, key, putMode, callbacks, indexIds, indexKeys));
- }
- virtual void perform(std::function<void()> 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<int64_t>& indexIDs() const { return m_indexIDs; }
- const Vector<IndexKeys>& indexKeys() const { return m_indexKeys; }
- SharedBuffer* value() const { return m_value.get(); }
-
-private:
- PutOperation(IDBTransactionBackend* transaction, const IDBObjectStoreMetadata& objectStore, PassRefPtr<SharedBuffer>& value, PassRefPtr<IDBKey> key, IDBDatabaseBackend::PutMode putMode, PassRefPtr<IDBCallbacks> callbacks, const Vector<int64_t>& indexIds, const Vector<IndexKeys>& 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<IDBTransactionBackend> m_transaction;
- const IDBObjectStoreMetadata m_objectStore;
- const RefPtr<SharedBuffer> m_value;
- const RefPtr<IDBKey> m_key;
- const IDBDatabaseBackend::PutMode m_putMode;
- const RefPtr<IDBCallbacks> m_callbacks;
- const Vector<int64_t> m_indexIDs;
- const Vector<IndexKeys> m_indexKeys;
-};
-
-class SetIndexesReadyOperation : public IDBOperation {
-public:
- static PassRefPtr<IDBOperation> create(IDBTransactionBackend* transaction, size_t indexCount)
- {
- return adoptRef(new SetIndexesReadyOperation(transaction, indexCount));
- }
- virtual void perform(std::function<void()> successCallback) override final;
-private:
- SetIndexesReadyOperation(IDBTransactionBackend* transaction, size_t indexCount)
- : m_transaction(transaction)
- , m_indexCount(indexCount)
- {
- }
-
- RefPtr<IDBTransactionBackend> m_transaction;
- const size_t m_indexCount;
-};
-
-class OpenCursorOperation : public IDBOperation {
-public:
- static PassRefPtr<IDBOperation> create(IDBTransactionBackend* transaction, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, IndexedDB::CursorDirection direction, IndexedDB::CursorType cursorType, IDBDatabaseBackend::TaskType taskType, PassRefPtr<IDBCallbacks> callbacks)
- {
- return adoptRef(new OpenCursorOperation(transaction, objectStoreId, indexId, keyRange, direction, cursorType, taskType, callbacks));
- }
- virtual void perform(std::function<void()> 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<IDBKeyRange> keyRange, IndexedDB::CursorDirection direction, IndexedDB::CursorType cursorType, IDBDatabaseBackend::TaskType taskType, PassRefPtr<IDBCallbacks> 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<IDBTransactionBackend> m_transaction;
- const int64_t m_objectStoreID;
- const int64_t m_indexID;
- const PassRefPtr<IDBKeyRange> m_keyRange;
- const IndexedDB::CursorDirection m_direction;
- const IndexedDB::CursorType m_cursorType;
- const IDBDatabaseBackend::TaskType m_taskType;
- const RefPtr<IDBCallbacks> m_callbacks;
-};
-
-class CountOperation : public IDBOperation {
-public:
- static PassRefPtr<IDBOperation> create(IDBTransactionBackend* transaction, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
- {
- return adoptRef(new CountOperation(transaction, objectStoreId, indexId, keyRange, callbacks));
- }
- virtual void perform(std::function<void()> 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<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
- : m_transaction(transaction)
- , m_objectStoreID(objectStoreId)
- , m_indexID(indexId)
- , m_keyRange(keyRange)
- , m_callbacks(callbacks)
- {
- }
-
- RefPtr<IDBTransactionBackend> m_transaction;
- const int64_t m_objectStoreID;
- const int64_t m_indexID;
- const RefPtr<IDBKeyRange> m_keyRange;
- const RefPtr<IDBCallbacks> m_callbacks;
-};
-
-class DeleteRangeOperation : public IDBOperation {
-public:
- static PassRefPtr<IDBOperation> create(IDBTransactionBackend* transaction, int64_t objectStoreId, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
- {
- return adoptRef(new DeleteRangeOperation(transaction, objectStoreId, keyRange, callbacks));
- }
- virtual void perform(std::function<void()> 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<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
- : m_transaction(transaction)
- , m_objectStoreID(objectStoreId)
- , m_keyRange(keyRange)
- , m_callbacks(callbacks)
- {
- }
-
- RefPtr<IDBTransactionBackend> m_transaction;
- const int64_t m_objectStoreID;
- const RefPtr<IDBKeyRange> m_keyRange;
- const RefPtr<IDBCallbacks> m_callbacks;
-};
-
-class ClearObjectStoreOperation : public IDBOperation {
-public:
- static PassRefPtr<IDBOperation> create(IDBTransactionBackend* transaction, int64_t objectStoreId, PassRefPtr<IDBCallbacks> callbacks)
- {
- return adoptRef(new ClearObjectStoreOperation(transaction, objectStoreId, callbacks));
- }
- virtual void perform(std::function<void()> 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<IDBCallbacks> callbacks)
- : m_transaction(transaction)
- , m_objectStoreID(objectStoreId)
- , m_callbacks(callbacks)
- {
- }
-
- RefPtr<IDBTransactionBackend> m_transaction;
- const int64_t m_objectStoreID;
- const RefPtr<IDBCallbacks> 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> 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<IDBTransactionBackend*>::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<int64_t>& scope1, const HashSet<int64_t>& scope2)
-{
- for (HashSet<int64_t>::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<IDBTransactionBackend*>::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<IDBTransactionBackend*>::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 <wtf/HashMap.h>
-#include <wtf/ListHashSet.h>
-#include <wtf/RefPtr.h>
-
-namespace WebCore {
-
-class IDBTransactionBackend;
-
-// Transactions are executed in the order the were created.
-class IDBTransactionCoordinator {
-public:
- static PassOwnPtr<IDBTransactionCoordinator> 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<IDBTransactionBackend*, RefPtr<IDBTransactionBackend> > m_transactions;
- // Transactions in different states are grouped below.
- ListHashSet<IDBTransactionBackend*> m_queuedTransactions;
- HashSet<IDBTransactionBackend*> 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/IDBAny.idl b/Source/WebCore/Modules/indexeddb/IDBTransactionMode.idl
index fb480c92e..7a7eb9306 100644
--- a/Source/WebCore/Modules/indexeddb/IDBAny.idl
+++ b/Source/WebCore/Modules/indexeddb/IDBTransactionMode.idl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * 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
@@ -24,10 +24,9 @@
*/
[
- NoInterfaceObject,
Conditional=INDEXED_DATABASE,
- CustomToJSObject,
- JSNoStaticTables
-] interface IDBAny {
- // This space is intentionally left blank.
+] 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 <wtf/CrossThreadTask.h>
+
+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<String>& blobURLs, const Vector<String>& blobFilePaths)
+ : m_data(ThreadSafeDataBuffer::copyVector(scriptValue.data()))
+ , m_blobURLs(blobURLs)
+ , m_blobFilePaths(blobFilePaths)
+{
+ ASSERT(m_data.data());
+}
+
+IDBValue::IDBValue(const ThreadSafeDataBuffer& value, Vector<String>&& blobURLs, Vector<String>&& blobFilePaths)
+ : m_data(value)
+ , m_blobURLs(WTFMove(blobURLs))
+ , m_blobFilePaths(WTFMove(blobFilePaths))
+{
+}
+
+IDBValue::IDBValue(const ThreadSafeDataBuffer& value, const Vector<String>& blobURLs, const Vector<String>& 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<Vector<String>>::copy(other.m_blobURLs);
+ m_blobFilePaths = CrossThreadCopier<Vector<String>>::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 <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class SerializedScriptValue;
+
+class IDBValue {
+public:
+ WEBCORE_EXPORT IDBValue();
+ IDBValue(const SerializedScriptValue&);
+ IDBValue(const ThreadSafeDataBuffer&);
+ IDBValue(const SerializedScriptValue&, const Vector<String>& blobURLs, const Vector<String>& blobFilePaths);
+ IDBValue(const ThreadSafeDataBuffer&, Vector<String>&& blobURLs, Vector<String>&& blobFilePaths);
+ IDBValue(const ThreadSafeDataBuffer&, const Vector<String>& blobURLs, const Vector<String>& blobFilePaths);
+
+ void setAsIsolatedCopy(const IDBValue&);
+ IDBValue isolatedCopy() const;
+
+ const ThreadSafeDataBuffer& data() const { return m_data; }
+ const Vector<String>& blobURLs() const { return m_blobURLs; }
+ const Vector<String>& blobFilePaths() const { return m_blobFilePaths; }
+
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static bool decode(Decoder&, IDBValue&);
+
+private:
+ ThreadSafeDataBuffer m_data;
+ Vector<String> m_blobURLs;
+ Vector<String> m_blobFilePaths;
+};
+
+
+template<class Encoder>
+void IDBValue::encode(Encoder& encoder) const
+{
+ encoder << m_data;
+ encoder << m_blobURLs;
+ encoder << m_blobFilePaths;
+}
+
+template<class Decoder>
+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 <wtf/PassRefPtr.h>
-#include <wtf/RefPtr.h>
-#include <wtf/text/WTFString.h>
+#include "IDBResourceIdentifier.h"
+#include <wtf/Optional.h>
namespace WebCore {
-class IDBVersionChangeEvent : public Event {
+class IDBVersionChangeEvent final : public Event {
public:
- static PassRefPtr<IDBVersionChangeEvent> create(unsigned long long oldVersion = 0, unsigned long long newVersion = 0, IndexedDB::VersionNullness newVersionNullness = IndexedDB::VersionNullness::Null, const AtomicString& eventType = AtomicString())
+ static Ref<IDBVersionChangeEvent> create(uint64_t oldVersion, uint64_t newVersion, const AtomicString& eventType)
+ {
+ return adoptRef(*new IDBVersionChangeEvent(IDBResourceIdentifier::emptyValue(), oldVersion, newVersion, eventType));
+ }
+
+ static Ref<IDBVersionChangeEvent> 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<uint64_t> 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<IDBVersionChangeEvent> 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<uint64_t> 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<uint64_t> 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<PageGroupIndexedDatabase*>(Supplement<PageGroup>::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/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<WorkerGlobalScopeIndexedDatabase*>(Supplement<ScriptExecutionContext>::from(context, supplementName()));
+ WorkerGlobalScopeIndexedDatabase* supplement = static_cast<WorkerGlobalScopeIndexedDatabase*>(Supplement<WorkerGlobalScope>::from(&scope, supplementName()));
if (!supplement) {
- String databaseDirectoryIdentifier;
- WorkerGlobalScope* workerGlobalScope = static_cast<WorkerGlobalScope*>(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<WorkerGlobalScopeIndexedDatabase>(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 <wtf/text/WTFString.h>
@@ -35,29 +34,29 @@
namespace WebCore {
class IDBFactory;
-class IDBFactoryBackendInterface;
-class ScriptExecutionContext;
+class WorkerGlobalScope;
-class WorkerGlobalScopeIndexedDatabase : public Supplement<ScriptExecutionContext> {
+namespace IDBClient {
+class IDBConnectionProxy;
+}
+
+class WorkerGlobalScopeIndexedDatabase : public Supplement<WorkerGlobalScope> {
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<IDBFactoryBackendInterface> m_factoryBackend;
RefPtr<IDBFactory> m_idbFactory;
+ Ref<IDBClient::IDBConnectionProxy> 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 <wtf/MainThread.h>
+
+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<IDBOpenDBRequest> IDBConnectionProxy::openDatabase(ScriptExecutionContext& context, const IDBDatabaseIdentifier& databaseIdentifier, uint64_t version)
+{
+ RefPtr<IDBOpenDBRequest> request;
+ {
+ Locker<Lock> 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<IDBOpenDBRequest> IDBConnectionProxy::deleteDatabase(ScriptExecutionContext& context, const IDBDatabaseIdentifier& databaseIdentifier)
+{
+ RefPtr<IDBOpenDBRequest> request;
+ {
+ Locker<Lock> 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<IDBOpenDBRequest> request;
+ {
+ Locker<Lock> 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<Lock> locker(m_transactionOperationLock);
+
+ ASSERT(!m_activeOperations.contains(operation.identifier()));
+ m_activeOperations.set(operation.identifier(), &operation);
+}
+
+void IDBConnectionProxy::completeOperation(const IDBResultData& resultData)
+{
+ RefPtr<TransactionOperation> operation;
+ {
+ Locker<Lock> 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<IDBDatabase> database;
+ {
+ Locker<Lock> 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<IDBOpenDBRequest> request;
+ {
+ Locker<Lock> 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<Lock> 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<IDBTransaction> transaction;
+ {
+ Locker<Lock> locker(m_transactionMapLock);
+ transaction = m_pendingTransactions.take(transactionIdentifier);
+ }
+
+ ASSERT(transaction);
+
+ transaction->performCallbackOnOriginThread(*transaction, &IDBTransaction::didStart, error);
+}
+
+void IDBConnectionProxy::commitTransaction(IDBTransaction& transaction)
+{
+ {
+ Locker<Lock> 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<IDBTransaction> transaction;
+ {
+ Locker<Lock> locker(m_transactionMapLock);
+ transaction = m_committingTransactions.take(transactionIdentifier);
+ }
+
+ if (!transaction)
+ return;
+
+ transaction->performCallbackOnOriginThread(*transaction, &IDBTransaction::didCommit, error);
+}
+
+void IDBConnectionProxy::abortTransaction(IDBTransaction& transaction)
+{
+ {
+ Locker<Lock> 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<IDBTransaction> transaction;
+ {
+ Locker<Lock> 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<IDBDatabase> database;
+ {
+ Locker<Lock> 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<uint64_t> databaseConnectionIdentifiers;
+ {
+ Locker<Lock> locker(m_databaseConnectionMapLock);
+ copyKeysToVector(m_databaseConnectionMap, databaseConnectionIdentifiers);
+ }
+
+ for (auto connectionIdentifier : databaseConnectionIdentifiers) {
+ RefPtr<IDBDatabase> database;
+ {
+ Locker<Lock> locker(m_databaseConnectionMapLock);
+ database = m_databaseConnectionMap.get(connectionIdentifier);
+ }
+
+ if (!database)
+ continue;
+
+ database->performCallbackOnOriginThread(*database, &IDBDatabase::connectionToServerLost, error);
+ }
+
+ Vector<IDBResourceIdentifier> openDBRequestIdentifiers;
+ {
+ Locker<Lock> locker(m_openDBRequestMapLock);
+ copyKeysToVector(m_openDBRequestMap, openDBRequestIdentifiers);
+ }
+
+ for (auto& requestIdentifier : openDBRequestIdentifiers) {
+ RefPtr<IDBOpenDBRequest> request;
+ {
+ Locker<Lock> 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<Lock> locker(m_mainThreadTaskLock);
+ if (m_mainThreadProtector)
+ return;
+
+ m_mainThreadProtector = &m_connectionToServer;
+ callOnMainThread([this] {
+ handleMainThreadTasks();
+ });
+}
+
+void IDBConnectionProxy::handleMainThreadTasks()
+{
+ RefPtr<IDBConnectionToServer> protector;
+ {
+ Locker<Lock> 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<void (const Vector<String>&)> 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<Lock> locker(m_databaseConnectionMapLock);
+
+ ASSERT(!m_databaseConnectionMap.contains(database.databaseConnectionIdentifier()));
+ m_databaseConnectionMap.set(database.databaseConnectionIdentifier(), &database);
+}
+
+void IDBConnectionProxy::unregisterDatabaseConnection(IDBDatabase& database)
+{
+ Locker<Lock> 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<RefPtr<TransactionOperation>>& operations)
+{
+ Locker<Lock> locker(m_transactionOperationLock);
+
+ for (auto& operation : operations)
+ m_activeOperations.remove(operation->identifier());
+}
+
+template<typename KeyType, typename ValueType>
+void removeItemsMatchingCurrentThread(HashMap<KeyType, ValueType>& map)
+{
+ auto currentThreadID = currentThread();
+
+ Vector<KeyType> 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> lock(m_databaseConnectionMapLock);
+ removeItemsMatchingCurrentThread(m_databaseConnectionMap);
+ }
+ {
+ Locker<Lock> lock(m_openDBRequestMapLock);
+ removeItemsMatchingCurrentThread(m_openDBRequestMap);
+ }
+ {
+ Locker<Lock> lock(m_transactionMapLock);
+ removeItemsMatchingCurrentThread(m_pendingTransactions);
+ removeItemsMatchingCurrentThread(m_committingTransactions);
+ removeItemsMatchingCurrentThread(m_abortingTransactions);
+ }
+ {
+ Locker<Lock> 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 <functional>
+#include <wtf/CrossThreadQueue.h>
+#include <wtf/CrossThreadTask.h>
+#include <wtf/HashMap.h>
+#include <wtf/MainThread.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+#include <wtf/text/WTFString.h>
+
+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<IDBOpenDBRequest> openDatabase(ScriptExecutionContext&, const IDBDatabaseIdentifier&, uint64_t version);
+ void didOpenDatabase(const IDBResultData&);
+
+ Ref<IDBOpenDBRequest> 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 (const Vector<String>&)>);
+
+ void registerDatabaseConnection(IDBDatabase&);
+ void unregisterDatabaseConnection(IDBDatabase&);
+
+ void forgetActiveOperations(const Vector<RefPtr<TransactionOperation>>&);
+ void forgetActivityForCurrentThread();
+
+private:
+ void completeOpenDBRequest(const IDBResultData&);
+ bool hasRecordOfTransaction(const IDBTransaction&) const;
+
+ void saveOperation(TransactionOperation&);
+
+ template<typename... Parameters, typename... Arguments>
+ void callConnectionOnMainThread(void (IDBConnectionToServer::*method)(Parameters...), Arguments&&... arguments)
+ {
+ if (isMainThread())
+ (m_connectionToServer.*method)(std::forward<Arguments>(arguments)...);
+ else
+ postMainThreadTask(m_connectionToServer, method, arguments...);
+ }
+
+ template<typename... Arguments>
+ 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<uint64_t, IDBDatabase*> m_databaseConnectionMap;
+ Lock m_databaseConnectionMapLock;
+
+ HashMap<IDBResourceIdentifier, RefPtr<IDBOpenDBRequest>> m_openDBRequestMap;
+ Lock m_openDBRequestMapLock;
+
+ HashMap<IDBResourceIdentifier, RefPtr<IDBTransaction>> m_pendingTransactions;
+ HashMap<IDBResourceIdentifier, RefPtr<IDBTransaction>> m_committingTransactions;
+ HashMap<IDBResourceIdentifier, RefPtr<IDBTransaction>> m_abortingTransactions;
+ Lock m_transactionMapLock;
+
+ HashMap<IDBResourceIdentifier, RefPtr<TransactionOperation>> m_activeOperations;
+ Lock m_transactionOperationLock;
+
+ CrossThreadQueue<CrossThreadTask> m_mainThreadQueue;
+ Lock m_mainThreadTaskLock;
+ RefPtr<IDBConnectionToServer> 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 <wtf/MainThread.h>
+
+namespace WebCore {
+namespace IDBClient {
+
+Ref<IDBConnectionToServer> IDBConnectionToServer::create(IDBConnectionToServerDelegate& delegate)
+{
+ return adoptRef(*new IDBConnectionToServer(delegate));
+}
+
+IDBConnectionToServer::IDBConnectionToServer(IDBConnectionToServerDelegate& delegate)
+ : m_delegate(delegate)
+ , m_proxy(std::make_unique<IDBConnectionProxy>(*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<void (const Vector<String>&)> 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<String>& 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 <wtf/HashMap.h>
+#include <wtf/Ref.h>
+#include <wtf/ThreadSafeRefCounted.h>
+
+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<IDBConnectionToServer> {
+public:
+ WEBCORE_EXPORT static Ref<IDBConnectionToServer> 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<void (const Vector<String>&)>);
+ WEBCORE_EXPORT void didGetAllDatabaseNames(uint64_t callbackID, const Vector<String>& databaseNames);
+
+private:
+ IDBConnectionToServer(IDBConnectionToServerDelegate&);
+
+ Ref<IDBConnectionToServerDelegate> m_delegate;
+
+ HashMap<uint64_t, std::function<void (const Vector<String>&)>> m_getAllDatabaseNamesCallbacks;
+
+ std::unique_ptr<IDBConnectionProxy> 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 <wtf/text/WTFString.h>
+
+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 <heap/HeapInlines.h>
+
+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<IDBResourceIdentifier>(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 <wtf/MainThread.h>
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+class IDBResultData;
+
+namespace IndexedDB {
+enum class IndexRecordType;
+}
+
+namespace IDBClient {
+
+class TransactionOperation : public ThreadSafeRefCounted<TransactionOperation> {
+ 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<TransactionOperation>&& 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<void (const IDBResultData&)> 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<IDBTransaction> m_transaction;
+ IDBResourceIdentifier m_identifier;
+ uint64_t m_objectStoreIdentifier { 0 };
+ uint64_t m_indexIdentifier { 0 };
+ std::unique_ptr<IDBResourceIdentifier> m_cursorIdentifier;
+ IndexedDB::IndexRecordType m_indexRecordType;
+ std::function<void ()> m_performFunction;
+ std::function<void (const IDBResultData&)> 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<IDBRequest> m_idbRequest;
+ bool m_nextRequestCanGoToServer { true };
+};
+
+template <typename... Arguments>
+class TransactionOperationImpl final : public TransactionOperation {
+public:
+ TransactionOperationImpl(IDBTransaction& transaction, void (IDBTransaction::*completeMethod)(const IDBResultData&), void (IDBTransaction::*performMethod)(TransactionOperation&, Arguments...), Arguments&&... arguments)
+ : TransactionOperation(transaction)
+ {
+ RefPtr<TransactionOperation> 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<TransactionOperation> protectedThis(this);
+
+ ASSERT(performMethod);
+ m_performFunction = [protectedThis, this, performMethod, arguments...] {
+ (&m_transaction.get()->*performMethod)(*this, arguments...);
+ };
+
+ if (completeMethod) {
+ RefPtr<IDBRequest> refRequest(&request);
+ m_completeFunction = [protectedThis, this, refRequest, completeMethod](const IDBResultData& resultData) {
+ if (completeMethod)
+ (&m_transaction.get()->*completeMethod)(*refRequest, resultData);
+ };
+ }
+ }
+};
+
+inline RefPtr<TransactionOperation> createTransactionOperation(
+ IDBTransaction& transaction,
+ void (IDBTransaction::*complete)(const IDBResultData&),
+ void (IDBTransaction::*perform)(TransactionOperation&))
+{
+ auto operation = new TransactionOperationImpl<>(transaction, complete, perform);
+ return adoptRef(operation);
+}
+
+template<typename MP1, typename P1>
+RefPtr<TransactionOperation> createTransactionOperation(
+ IDBTransaction& transaction,
+ void (IDBTransaction::*complete)(const IDBResultData&),
+ void (IDBTransaction::*perform)(TransactionOperation&, MP1),
+ const P1& parameter1)
+{
+ auto operation = new TransactionOperationImpl<MP1>(transaction, complete, perform, parameter1);
+ return adoptRef(operation);
+}
+
+template<typename MP1, typename P1, typename MP2, typename P2>
+RefPtr<TransactionOperation> createTransactionOperation(
+ IDBTransaction& transaction,
+ void (IDBTransaction::*complete)(const IDBResultData&),
+ void (IDBTransaction::*perform)(TransactionOperation&, MP1, MP2),
+ const P1& parameter1,
+ const P2& parameter2)
+{
+ auto operation = new TransactionOperationImpl<MP1, MP2>(transaction, complete, perform, parameter1, parameter2);
+ return adoptRef(operation);
+}
+
+template<typename MP1, typename P1, typename MP2, typename P2, typename MP3, typename P3>
+RefPtr<TransactionOperation> 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<MP1, MP2, MP3>(transaction, complete, perform, parameter1, parameter2, parameter3);
+ return adoptRef(operation);
+}
+
+template<typename MP1, typename P1>
+RefPtr<TransactionOperation> createTransactionOperation(
+ IDBTransaction& transaction,
+ IDBRequest& request,
+ void (IDBTransaction::*complete)(IDBRequest&, const IDBResultData&),
+ void (IDBTransaction::*perform)(TransactionOperation&, MP1),
+ const P1& parameter1)
+{
+ auto operation = new TransactionOperationImpl<MP1>(transaction, request, complete, perform, parameter1);
+ return adoptRef(operation);
+}
+
+template<typename MP1, typename P1, typename MP2, typename P2>
+RefPtr<TransactionOperation> 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<MP1, MP2>(transaction, request, complete, perform, parameter1, parameter2);
+ return adoptRef(operation);
+}
+
+template<typename MP1, typename MP2, typename MP3, typename P1, typename P2, typename P3>
+RefPtr<TransactionOperation> 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<MP1, MP2, MP3>(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<IDBKey> 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<IDBKey> 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<IDBBackingStoreCursorLevelDB> {
-public:
- enum IteratorState {
- Ready = 0,
- Seek
- };
-
- struct CursorOptions {
- int64_t databaseId;
- int64_t objectStoreId;
- int64_t indexId;
- Vector<char> lowKey;
- bool lowOpen;
- Vector<char> highKey;
- bool highOpen;
- bool forward;
- bool unique;
- };
-
- virtual PassRefPtr<IDBKey> key() const { return m_currentKey; }
- virtual bool continueFunction(const IDBKey* = 0, IteratorState = Seek);
- virtual bool advance(unsigned long);
- bool firstSeek();
-
- virtual PassRefPtr<IDBBackingStoreCursorLevelDB> clone() = 0;
-
- virtual PassRefPtr<IDBKey> primaryKey() const { return m_currentKey; }
- virtual PassRefPtr<SharedBuffer> 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<char> encodeKey(const IDBKey&) = 0;
-
- bool isPastBounds() const;
- bool haveEnteredRange() const;
-
- int64_t m_cursorID;
- LevelDBTransaction* m_transaction;
- const CursorOptions m_cursorOptions;
- OwnPtr<LevelDBIterator> m_iterator;
- RefPtr<IDBKey> m_currentKey;
- RefPtr<IDBRecordIdentifier> 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 <wtf/Assertions.h>
-
-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 <typename DBOrTransaction>
-static bool getInt(DBOrTransaction* db, const LevelDBSlice& key, int64_t& foundInt, bool& found)
-{
- Vector<char> 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 <typename DBOrTransaction>
-WARN_UNUSED_RETURN static bool getVarInt(DBOrTransaction* db, const LevelDBSlice& key, int64_t& foundInt, bool& found)
-{
- Vector<char> 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 <typename DBOrTransaction>
-WARN_UNUSED_RETURN static bool getString(DBOrTransaction* db, const LevelDBSlice& key, String& foundString, bool& found)
-{
- Vector<char> 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<char> schemaVersionKey = SchemaVersionKey::encode();
- const Vector<char> dataVersionKey = DataVersionKey::encode();
-
- RefPtr<LevelDBTransaction> 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<char> startKey = DatabaseNameKey::encodeMinKeyForOrigin(origin);
- const Vector<char> stopKey = DatabaseNameKey::encodeStopKeyForOrigin(origin);
- OwnPtr<LevelDBIterator> 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<char> 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 <typename DBOrTransaction>
-WARN_UNUSED_RETURN static bool getMaxObjectStoreId(DBOrTransaction* db, int64_t databaseId, int64_t& maxObjectStoreId)
-{
- const Vector<char> maxObjectStoreIdKey = DatabaseMetaDataKey::encode(databaseId, DatabaseMetaDataKey::MaxObjectStoreId);
- bool ok = getMaxObjectStoreId(db, maxObjectStoreIdKey, maxObjectStoreId);
- return ok;
-}
-
-template <typename DBOrTransaction>
-WARN_UNUSED_RETURN static bool getMaxObjectStoreId(DBOrTransaction* db, const Vector<char>& 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<LevelDBDatabase> 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<LevelDBDatabase> db, PassOwnPtr<LevelDBComparator> 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> IDBBackingStoreLevelDB::open(const SecurityOrigin& securityOrigin, const String& pathBaseArg, const String& fileIdentifier)
-{
- DefaultLevelDBFactory levelDBFactory;
- return IDBBackingStoreLevelDB::open(securityOrigin, pathBaseArg, fileIdentifier, &levelDBFactory);
-}
-
-PassRefPtr<IDBBackingStoreLevelDB> IDBBackingStoreLevelDB::open(const SecurityOrigin& securityOrigin, const String& pathBaseArg, const String& fileIdentifier, LevelDBFactory* levelDBFactory)
-{
- LOG(StorageAPI, "IDBBackingStoreLevelDB::open");
- ASSERT(!pathBaseArg.isEmpty());
- String pathBase = pathBaseArg;
-
- OwnPtr<LevelDBComparator> comparator = adoptPtr(new Comparator());
- OwnPtr<LevelDBDatabase> 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<IDBBackingStoreLevelDB>();
- }
-
- 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<IDBBackingStoreLevelDB>();
- }
-
- 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<IDBBackingStoreLevelDB>();
- }
- HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenCleanupReopenSuccess, IDBLevelDBBackingStoreOpenMax);
- }
-
- if (!db) {
- ASSERT_NOT_REACHED();
- HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenFailedUnknownErr, IDBLevelDBBackingStoreOpenMax);
- return PassRefPtr<IDBBackingStoreLevelDB>();
- }
-
- return create(fileIdentifier, db.release(), comparator.release());
-}
-
-PassRefPtr<IDBBackingStoreLevelDB> IDBBackingStoreLevelDB::openInMemory(const String& identifier)
-{
- DefaultLevelDBFactory levelDBFactory;
- return IDBBackingStoreLevelDB::openInMemory(identifier, &levelDBFactory);
-}
-
-PassRefPtr<IDBBackingStoreLevelDB> IDBBackingStoreLevelDB::openInMemory(const String& identifier, LevelDBFactory*)
-{
- LOG(StorageAPI, "IDBBackingStoreLevelDB::openInMemory");
-
- OwnPtr<LevelDBComparator> comparator = adoptPtr(new Comparator());
- OwnPtr<LevelDBDatabase> db = LevelDBDatabase::openInMemory(comparator.get());
- if (!db) {
- LOG_ERROR("LevelDBDatabase::openInMemory failed.");
- HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenMemoryFailed, IDBLevelDBBackingStoreOpenMax);
- return PassRefPtr<IDBBackingStoreLevelDB>();
- }
- HistogramSupport::histogramEnumeration("WebCore.IndexedDB.BackingStore.OpenStatus", IDBLevelDBBackingStoreOpenMemorySuccess, IDBLevelDBBackingStoreOpenMax);
-
- return create(identifier, db.release(), comparator.release());
-}
-
-PassRefPtr<IDBBackingStoreLevelDB> IDBBackingStoreLevelDB::create(const String& identifier, PassOwnPtr<LevelDBDatabase> db, PassOwnPtr<LevelDBComparator> comparator)
-{
- // FIXME: Handle comparator name changes.
- RefPtr<IDBBackingStoreLevelDB> backingStore(adoptRef(new IDBBackingStoreLevelDB(identifier, db, comparator)));
-
- if (!setUpMetadata(backingStore->m_db.get(), identifier))
- return PassRefPtr<IDBBackingStoreLevelDB>();
-
- return backingStore.release();
-}
-
-Vector<String> IDBBackingStoreLevelDB::getDatabaseNames()
-{
- Vector<String> foundNames;
- const Vector<char> startKey = DatabaseNameKey::encodeMinKeyForOrigin(m_identifier);
- const Vector<char> stopKey = DatabaseNameKey::encodeStopKeyForOrigin(m_identifier);
-
- ASSERT(foundNames.isEmpty());
-
- OwnPtr<LevelDBIterator> 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<char> 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<void (const IDBDatabaseMetadata&, bool success)> metadataFunction)
-{
- const Vector<char> 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<LevelDBTransaction> 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<LevelDBTransaction> 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<char>& begin, const Vector<char>& end)
-{
- OwnPtr<LevelDBIterator> 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<void (bool success)> boolCallbackFunction)
-{
- LOG(StorageAPI, "IDBBackingStoreLevelDB::deleteDatabase");
- OwnPtr<LevelDBWriteOnlyTransaction> 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<char> startKey = DatabaseMetaDataKey::encode(metadata.id, DatabaseMetaDataKey::OriginName);
- const Vector<char> stopKey = DatabaseMetaDataKey::encode(metadata.id + 1, DatabaseMetaDataKey::OriginName);
- OwnPtr<LevelDBIterator> it = m_db->createIterator();
- for (it->seek(startKey); it->isValid() && compareKeys(it->key(), stopKey) < 0; it->next())
- transaction->remove(it->key());
-
- const Vector<char> 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<char>& 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<char> startKey = ObjectStoreMetaDataKey::encode(databaseId, 1, 0);
- const Vector<char> stopKey = ObjectStoreMetaDataKey::encodeMaxKey(databaseId);
-
- ASSERT(objectStores->isEmpty());
-
- OwnPtr<LevelDBIterator> 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<char> 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<char> nameKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::Name);
- const Vector<char> keyPathKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::KeyPath);
- const Vector<char> autoIncrementKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::AutoIncrement);
- const Vector<char> evictableKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::Evictable);
- const Vector<char> lastVersionKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::LastVersion);
- const Vector<char> maxIndexIdKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::MaxIndexId);
- const Vector<char> hasKeyPathKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::HasKeyPath);
- const Vector<char> keyGeneratorCurrentNumberKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::KeyGeneratorCurrentNumber);
- const Vector<char> 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<char>& record)
-{
- LOG(StorageAPI, "IDBBackingStoreLevelDB::getRecord");
- if (!KeyPrefix::validIds(databaseId, objectStoreId))
- return false;
- LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction);
-
- const Vector<char> leveldbKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, key);
- Vector<char> 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<const char*>(data.end()));
- return true;
-}
-
-WARN_UNUSED_RETURN static bool getNewVersionNumber(LevelDBTransaction* transaction, int64_t databaseId, int64_t objectStoreId, int64_t& newVersionNumber)
-{
- const Vector<char> 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<SharedBuffer> 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<char> objectStoredataKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, key);
-
- Vector<char> v;
- v.appendVector(encodeVarInt(version));
- RefPtr<SharedBuffer> value = prpValue;
- ASSERT(value);
- v.append(value->data(), value->size());
-
- levelDBTransaction->put(objectStoredataKey, v);
-
- const Vector<char> 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<char> startKey = KeyPrefix(databaseId, objectStoreId).encode();
- const Vector<char> 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<char> objectStoreDataKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, recordIdentifier.encodedPrimaryKey());
- levelDBTransaction->remove(objectStoreDataKey);
-
- const Vector<char> 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<char> keyGeneratorCurrentNumberKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::KeyGeneratorCurrentNumber);
-
- keyGeneratorCurrentNumber = -1;
- Vector<char> 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<char> startKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, minIDBKey());
- const Vector<char> stopKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, maxIDBKey());
-
- OwnPtr<LevelDBIterator> 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<int64_t>(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<char> 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<IDBRecordIdentifier>& foundIDBRecordIdentifier)
-{
- LOG(StorageAPI, "IDBBackingStoreLevelDB::keyExistsInObjectStore");
- if (!KeyPrefix::validIds(databaseId, objectStoreId))
- return false;
- bool found = false;
- LevelDBTransaction* levelDBTransaction = IDBBackingStoreTransactionLevelDB::levelDBTransactionFrom(transaction);
- const Vector<char> leveldbKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, key);
- Vector<char> 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<char>& 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<char> startKey = IndexMetaDataKey::encode(databaseId, objectStoreId, 0, 0);
- const Vector<char> stopKey = IndexMetaDataKey::encode(databaseId, objectStoreId + 1, 0, 0);
-
- ASSERT(indexes->isEmpty());
-
- OwnPtr<LevelDBIterator> 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<char> 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<char> nameKey = IndexMetaDataKey::encode(databaseId, objectStoreId, indexId, IndexMetaDataKey::Name);
- const Vector<char> uniqueKey = IndexMetaDataKey::encode(databaseId, objectStoreId, indexId, IndexMetaDataKey::Unique);
- const Vector<char> keyPathKey = IndexMetaDataKey::encode(databaseId, objectStoreId, indexId, IndexMetaDataKey::KeyPath);
- const Vector<char> 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<char> indexMetaDataStart = IndexMetaDataKey::encode(databaseId, objectStoreId, indexId, 0);
- const Vector<char> indexMetaDataEnd = IndexMetaDataKey::encodeMaxKey(databaseId, objectStoreId, indexId);
- deleteRange(levelDBTransaction, indexMetaDataStart, indexMetaDataEnd);
-
- const Vector<char> indexDataStart = IndexDataKey::encodeMinKey(databaseId, objectStoreId, indexId);
- const Vector<char> 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<char> indexDataKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, encodeIDBKey(key), recordIdentifier->encodedPrimaryKey());
-
- Vector<char> data;
- data.appendVector(encodeVarInt(recordIdentifier->version()));
- data.appendVector(recordIdentifier->encodedPrimaryKey());
-
- levelDBTransaction->put(indexDataKey, data);
- return true;
-}
-
-static bool findGreatestKeyLessThanOrEqual(LevelDBTransaction* transaction, const Vector<char>& target, Vector<char>& foundKey)
-{
- OwnPtr<LevelDBIterator> 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<char>& encodedPrimaryKey, bool& exists)
-{
- const Vector<char> key = ExistsEntryKey::encode(databaseId, objectStoreId, encodedPrimaryKey);
- Vector<char> 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<char>& 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<char> leveldbKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, key);
- OwnPtr<LevelDBIterator> 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<IDBKey>& primaryKey)
-{
- LOG(StorageAPI, "IDBBackingStoreLevelDB::getPrimaryKeyViaIndex");
- if (!KeyPrefix::validIds(databaseId, objectStoreId, indexId))
- return false;
-
- bool found = false;
- Vector<char> 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<IDBKey>& foundPrimaryKey, bool& exists)
-{
- LOG(StorageAPI, "IDBBackingStoreLevelDB::keyExistsInIndex");
- if (!KeyPrefix::validIds(databaseId, objectStoreId, indexId))
- return false;
-
- exists = false;
- Vector<char> 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<int64_t>& indexIDs, const Vector<Vector<RefPtr<IDBKey>>>& indexKeys, Vector<RefPtr<IDBIndexWriterLevelDB>>& indexWriters, String* errorMessage, bool& completed)
-{
- ASSERT(indexIDs.size() == indexKeys.size());
- completed = false;
-
- HashMap<int64_t, IndexKeys> 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<IDBIndexWriterLevelDB> 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<IDBKey> 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<int64_t>(floor(key.number())) + 1, checkCurrent);
-}
-
-class ObjectStoreKeyCursorImpl : public IDBBackingStoreCursorLevelDB {
-public:
- static PassRefPtr<ObjectStoreKeyCursorImpl> create(int64_t cursorID, LevelDBTransaction* transaction, const IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions)
- {
- return adoptRef(new ObjectStoreKeyCursorImpl(cursorID, transaction, cursorOptions));
- }
-
- virtual PassRefPtr<IDBBackingStoreCursorLevelDB> clone()
- {
- return adoptRef(new ObjectStoreKeyCursorImpl(this));
- }
-
- // IDBBackingStoreCursorLevelDB
- virtual PassRefPtr<SharedBuffer> value() const override { ASSERT_NOT_REACHED(); return 0; }
- virtual bool loadCurrentRow() override;
-
-protected:
- virtual Vector<char> 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<ObjectStoreCursorImpl> create(int64_t cursorID, LevelDBTransaction* transaction, const IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions)
- {
- return adoptRef(new ObjectStoreCursorImpl(cursorID, transaction, cursorOptions));
- }
-
- virtual PassRefPtr<IDBBackingStoreCursorLevelDB> clone()
- {
- return adoptRef(new ObjectStoreCursorImpl(this));
- }
-
- // IDBBackingStoreCursorLevelDB
- virtual PassRefPtr<SharedBuffer> value() const override { return m_currentValue; }
- virtual bool loadCurrentRow() override;
-
-protected:
- virtual Vector<char> 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<SharedBuffer> 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<char> value;
- value.append(valuePosition, m_iterator->value().end() - valuePosition);
- m_currentValue = SharedBuffer::adoptVector(value);
- return true;
-}
-
-class IndexKeyCursorImpl final : public IDBBackingStoreCursorLevelDB {
-public:
- static PassRefPtr<IndexKeyCursorImpl> create(int64_t cursorID, LevelDBTransaction* transaction, const IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions)
- {
- return adoptRef(new IndexKeyCursorImpl(cursorID, transaction, cursorOptions));
- }
-
- virtual PassRefPtr<IDBBackingStoreCursorLevelDB> clone()
- {
- return adoptRef(new IndexKeyCursorImpl(this));
- }
-
- // IDBBackingStoreCursorLevelDB
- virtual PassRefPtr<SharedBuffer> value() const override { ASSERT_NOT_REACHED(); return 0; }
- virtual PassRefPtr<IDBKey> 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<char> 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<IDBKey> 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<char> primaryLevelDBKey = ObjectStoreDataKey::encode(indexDataKey.databaseId(), indexDataKey.objectStoreId(), *m_primaryKey);
-
- Vector<char> 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<IndexCursorImpl> create(int64_t cursorID, LevelDBTransaction* transaction, const IDBBackingStoreCursorLevelDB::CursorOptions& cursorOptions)
- {
- return adoptRef(new IndexCursorImpl(cursorID, transaction, cursorOptions));
- }
-
- virtual PassRefPtr<IDBBackingStoreCursorLevelDB> clone()
- {
- return adoptRef(new IndexCursorImpl(this));
- }
-
- // IDBBackingStoreCursorLevelDB
- virtual PassRefPtr<SharedBuffer> value() const override { return m_currentValue; }
- virtual PassRefPtr<IDBKey> 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<char> 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<IDBKey> m_primaryKey;
- RefPtr<SharedBuffer> m_currentValue;
- Vector<char> 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<char> 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<char> 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<char> 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<char> 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<IDBBackingStoreCursorLevelDB> 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<ObjectStoreCursorImpl> cursor = ObjectStoreCursorImpl::create(cursorID, levelDBTransaction, cursorOptions);
- if (!cursor->firstSeek())
- return 0;
-
- return cursor.release();
-}
-
-PassRefPtr<IDBBackingStoreCursorLevelDB> 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<ObjectStoreKeyCursorImpl> cursor = ObjectStoreKeyCursorImpl::create(cursorID, levelDBTransaction, cursorOptions);
- if (!cursor->firstSeek())
- return 0;
-
- return cursor.release();
-}
-
-PassRefPtr<IDBBackingStoreCursorLevelDB> 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<IndexKeyCursorImpl> cursor = IndexKeyCursorImpl::create(cursorID, levelDBTransaction, cursorOptions);
- if (!cursor->firstSeek())
- return 0;
-
- return cursor.release();
-}
-
-PassRefPtr<IDBBackingStoreCursorLevelDB> 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<IndexCursorImpl> 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 <functional>
-#include <wtf/OwnPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/WeakPtr.h>
-
-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<LevelDBDatabase> openLevelDB(const String& fileName, const LevelDBComparator*) = 0;
- virtual bool destroyLevelDB(const String& fileName) = 0;
-};
-
-class IDBBackingStoreLevelDB : public RefCounted<IDBBackingStoreLevelDB> {
-public:
- class Transaction;
-
- virtual ~IDBBackingStoreLevelDB();
- static PassRefPtr<IDBBackingStoreLevelDB> open(const SecurityOrigin&, const String& pathBase, const String& fileIdentifier);
- static PassRefPtr<IDBBackingStoreLevelDB> open(const SecurityOrigin&, const String& pathBase, const String& fileIdentifier, LevelDBFactory*);
- static PassRefPtr<IDBBackingStoreLevelDB> openInMemory(const String& identifier);
- static PassRefPtr<IDBBackingStoreLevelDB> openInMemory(const String& identifier, LevelDBFactory*);
- WeakPtr<IDBBackingStoreLevelDB> createWeakPtr() { return m_weakFactory.createWeakPtr(); }
-
- void establishBackingStoreTransaction(int64_t transactionID);
-
- Vector<String> getDatabaseNames();
-
- // New-style asynchronous callbacks
- void getOrEstablishIDBDatabaseMetadata(const String& name, std::function<void (const IDBDatabaseMetadata&, bool success)> callbackFunction);
- void deleteDatabase(const String& name, std::function<void (bool success)> 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<char>& record) WARN_UNUSED_RETURN;
- bool putRecord(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, const IDBKey&, PassRefPtr<SharedBuffer> 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<IDBRecordIdentifier>& 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<IDBKey>& primaryKey) WARN_UNUSED_RETURN;
- bool keyExistsInIndex(IDBBackingStoreTransactionLevelDB&, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& indexKey, RefPtr<IDBKey>& foundPrimaryKey, bool& exists) WARN_UNUSED_RETURN;
-
- virtual PassRefPtr<IDBBackingStoreCursorLevelDB> openObjectStoreKeyCursor(int64_t cursorID, IDBBackingStoreTransactionLevelDB&, int64_t databaseID, int64_t objectStoreId, const IDBKeyRange*, IndexedDB::CursorDirection);
- virtual PassRefPtr<IDBBackingStoreCursorLevelDB> openObjectStoreCursor(int64_t cursorID, IDBBackingStoreTransactionLevelDB&, int64_t databaseID, int64_t objectStoreId, const IDBKeyRange*, IndexedDB::CursorDirection);
- virtual PassRefPtr<IDBBackingStoreCursorLevelDB> openIndexKeyCursor(int64_t cursorID, IDBBackingStoreTransactionLevelDB&, int64_t databaseID, int64_t objectStoreId, int64_t indexId, const IDBKeyRange*, IndexedDB::CursorDirection);
- virtual PassRefPtr<IDBBackingStoreCursorLevelDB> 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<int64_t>& indexIds, const Vector<Vector<RefPtr<IDBKey>>>&, Vector<RefPtr<IDBIndexWriterLevelDB>>& indexWriters, String* errorMessage, bool& completed) WARN_UNUSED_RETURN;
-
- virtual PassRefPtr<IDBKey> 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<LevelDBDatabase>, PassOwnPtr<LevelDBComparator>);
- static PassRefPtr<IDBBackingStoreLevelDB> create(const String& identifier, PassOwnPtr<LevelDBDatabase>, PassOwnPtr<LevelDBComparator>);
-
- // 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<char>& foundEncodedPrimaryKey, bool& found);
- bool getIndexes(int64_t databaseId, int64_t objectStoreId, IDBObjectStoreMetadata::IndexMap*) WARN_UNUSED_RETURN;
-
- String m_identifier;
-
- OwnPtr<LevelDBDatabase> m_db;
- OwnPtr<LevelDBComparator> m_comparator;
- WeakPtrFactory<IDBBackingStoreLevelDB> m_weakFactory;
-
- HashMap<int64_t, RefPtr<IDBBackingStoreTransactionLevelDB>> 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 <wtf/text/CString.h>
-
-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<IDBBackingStoreLevelDB*>(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/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<typename K, typename M>
-static void cleanWeakMap(HashMap<K, WeakPtr<M> >& map)
-{
- HashMap<K, WeakPtr<M> > other;
- other.swap(map);
-
- typename HashMap<K, WeakPtr<M> >::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<IDBCallbacks> callbacks, PassRefPtr<SecurityOrigin> securityOrigin, ScriptExecutionContext*, const String& dataDirectory)
-{
- ASSERT(securityOrigin);
- LOG(StorageAPI, "IDBFactoryBackendLevelDB::getDatabaseNames");
- RefPtr<IDBBackingStoreLevelDB> backingStore = openBackingStore(*securityOrigin, dataDirectory);
- if (!backingStore) {
- callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error opening backing store for indexedDB.webkitGetDatabaseNames."));
- return;
- }
-
- RefPtr<DOMStringList> databaseNames = DOMStringList::create();
-
- Vector<String> foundNames = backingStore->getDatabaseNames();
- for (Vector<String>::const_iterator it = foundNames.begin(); it != foundNames.end(); ++it)
- databaseNames->append(*it);
-
- callbacks->onSuccess(databaseNames.release());
-}
-
-void IDBFactoryBackendLevelDB::deleteDatabase(const String& name, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<SecurityOrigin> 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<IDBBackingStoreLevelDB> backingStore = openBackingStore(*securityOrigin, dataDirectory);
- if (!backingStore) {
- callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error opening backing store for indexedDB.deleteDatabase."));
- return;
- }
-
- RefPtr<IDBServerConnection> serverConnection = IDBServerConnectionLevelDB::create(name, backingStore.get());
- RefPtr<IDBDatabaseBackend> 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<IDBBackingStoreLevelDB> 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<IDBBackingStoreLevelDB> 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<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks, const SecurityOrigin& openingOrigin, const SecurityOrigin&)
-{
- LOG(StorageAPI, "IDBFactoryBackendLevelDB::open");
- const String uniqueIdentifier = computeUniqueIdentifier(name, openingOrigin);
-
- RefPtr<IDBDatabaseBackend> databaseBackend;
- IDBDatabaseBackendMap::iterator it = m_databaseBackendMap.find(uniqueIdentifier);
- if (it == m_databaseBackendMap.end()) {
- RefPtr<IDBBackingStoreLevelDB> backingStore = openBackingStore(openingOrigin, m_databaseDirectory);
- if (!backingStore) {
- callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error opening backing store for indexedDB.open."));
- return;
- }
-
- RefPtr<IDBServerConnection> 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 <wtf/HashMap.h>
-#include <wtf/HashSet.h>
-#include <wtf/RefCounted.h>
-#include <wtf/WeakPtr.h>
-#include <wtf/text/StringHash.h>
-
-namespace WebCore {
-
-class DOMStringList;
-
-class IDBBackingStoreLevelDB;
-class IDBDatabaseBackend;
-
-class IDBFactoryBackendLevelDB : public IDBFactoryBackendInterface {
-public:
- static PassRefPtr<IDBFactoryBackendLevelDB> 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<IDBCallbacks>, PassRefPtr<SecurityOrigin>, ScriptExecutionContext*, const String& dataDir) override final;
- virtual void open(const String& name, uint64_t version, int64_t transactionId, PassRefPtr<IDBCallbacks>, PassRefPtr<IDBDatabaseCallbacks>, const SecurityOrigin& openingOrigin, const SecurityOrigin& mainFrameOrigin) override final;
-
- virtual void deleteDatabase(const String& name, PassRefPtr<IDBCallbacks>, PassRefPtr<SecurityOrigin>, ScriptExecutionContext*, const String& dataDir) override final;
-
-protected:
- virtual PassRefPtr<IDBBackingStoreLevelDB> openBackingStore(const SecurityOrigin&, const String& dataDir);
-
-private:
- explicit IDBFactoryBackendLevelDB(const String& databaseDirectory);
-
- typedef HashMap<String, RefPtr<IDBDatabaseBackend> > IDBDatabaseBackendMap;
- IDBDatabaseBackendMap m_databaseBackendMap;
-
- typedef HashMap<String, WeakPtr<IDBBackingStoreLevelDB> > IDBBackingStoreLevelDBMap;
- IDBBackingStoreLevelDBMap m_backingStoreMap;
-
- HashSet<RefPtr<IDBBackingStoreLevelDB> > 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 <wtf/text/CString.h>
-
-#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<IDBKey> 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 <wtf/RefCounted.h>
-
-#if ENABLE(INDEXED_DATABASE)
-#if USE(LEVELDB)
-
-namespace WebCore {
-
-typedef Vector<RefPtr<IDBKey>> IndexKeys;
-
-class IDBIndexWriterLevelDB : public RefCounted<IDBIndexWriterLevelDB> {
-public:
- static PassRefPtr<IDBIndexWriterLevelDB> 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 <wtf/ByteOrder.h>
-#include <wtf/text/StringBuilder.h>
-
-// LevelDB stores key/value pairs. Keys and values are strings of bytes, normally of type Vector<char>.
-//
-// 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.
-//
-// <database id, 0, 0, 0> => utf16 origin name [DatabaseMetaDataKey]
-// <database id, 0, 0, 1> => utf16 database name [DatabaseMetaDataKey]
-// <database id, 0, 0, 2> => utf16 user version data [DatabaseMetaDataKey]
-// <database id, 0, 0, 3> => maximum object store id ever allocated [DatabaseMetaDataKey]
-// <database id, 0, 0, 4> => 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.
-//
-// <database id, 0, 0, 50, object store id, 0> => utf16 object store name [ObjectStoreMetaDataKey]
-// <database id, 0, 0, 50, object store id, 1> => utf16 key path [ObjectStoreMetaDataKey]
-// <database id, 0, 0, 50, object store id, 2> => has auto increment [ObjectStoreMetaDataKey]
-// <database id, 0, 0, 50, object store id, 3> => is evictable [ObjectStoreMetaDataKey]
-// <database id, 0, 0, 50, object store id, 4> => last "version" number [ObjectStoreMetaDataKey]
-// <database id, 0, 0, 50, object store id, 5> => maximum index id ever allocated [ObjectStoreMetaDataKey]
-// <database id, 0, 0, 50, object store id, 6> => has key path (vs. null) [ObjectStoreMetaDataKey]
-// <database id, 0, 0, 50, object store id, 7> => 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.
-//
-// <database id, 0, 0, 100, object store id, index id, 0> => utf16 index name [IndexMetaDataKey]
-// <database id, 0, 0, 100, object store id, index id, 1> => are index keys unique [IndexMetaDataKey]
-// <database id, 0, 0, 100, object store id, index id, 2> => utf16 key path [IndexMetaDataKey]
-// <database id, 0, 0, 100, object store id, index id, 3> => 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.
-//
-// <database id, 0, 0, 150, object store id> => existence implies the object store id is in the free list [ObjectStoreFreeListKey]
-// <database id, 0, 0, 151, object store id, index id> => existence implies the index id is in the free list [IndexFreeListKey]
-// <database id, 0, 0, 200, utf16 object store name> => object store id [ObjectStoreNamesKey]
-// <database id, 0, 0, 201, object store id, utf16 index name> => index id [IndexNamesKey]
-//
-//
-// Object store data:
-//
-// The prefix is followed by a type byte. The user key is an encoded IDBKey.
-//
-// <database id, object store id, 1, user key> => "version", serialized script value [ObjectStoreDataKey]
-//
-//
-// "Exists" entry:
-//
-// The prefix is followed by a type byte. The user key is an encoded IDBKey.
-//
-// <database id, object store id, 2, user key> => "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.
-//
-// <database id, object store id, index id, index key, sequence number, primary key> => "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<char> encodeByte(unsigned char c)
-{
- Vector<char, DefaultInlineBufferSize> 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<char> maxIDBKey()
-{
- return encodeByte(IDBKeyNullTypeByte);
-}
-
-Vector<char> minIDBKey()
-{
- return encodeByte(IDBKeyMinKeyTypeByte);
-}
-
-Vector<char> encodeBool(bool b)
-{
- Vector<char, DefaultInlineBufferSize> 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<char> encodeInt(int64_t nParam)
-{
- ASSERT(nParam >= 0);
- uint64_t n = static_cast<uint64_t>(nParam);
- Vector<char, DefaultInlineBufferSize> 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<int64_t>(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<char> encodeVarInt(int64_t nParam)
-{
- ASSERT(nParam >= 0);
- uint64_t n = static_cast<uint64_t>(nParam);
- Vector<char, DefaultInlineBufferSize> 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<int64_t>(c & 0x7f) << shift;
- shift += 7;
- } while (*p++ & 0x80);
- return p;
-}
-
-Vector<char> encodeString(const String& s)
-{
- // Backing store is UTF-16BE, convert from host endianness.
- size_t length = s.length();
- Vector<char> ret(length * sizeof(UChar));
-
- const UChar* src = s.characters();
- UChar* dst = reinterpret_cast<UChar*>(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<UChar> buffer(length);
-
- const UChar* src = reinterpret_cast<const UChar*>(start);
- UChar* dst = buffer.data();
- for (unsigned i = 0; i < length; ++i)
- *dst++ = ntohs(*src++);
-
- return String::adopt(buffer);
-}
-
-Vector<char> encodeStringWithLength(const String& s)
-{
- Vector<char> 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<size_t>(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<char> encodeDouble(double x)
-{
- // FIXME: It would be nice if we could be byte order independent.
- const char* p = reinterpret_cast<char*>(&x);
- Vector<char, DefaultInlineBufferSize> 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<char*>(d);
- for (size_t i = 0; i < sizeof(*d); ++i)
- *x++ = *p++;
- return p;
-}
-
-Vector<char> encodeIDBKey(const IDBKey& key)
-{
- Vector<char, DefaultInlineBufferSize> ret;
- encodeIDBKey(key, ret);
- return ret;
-}
-
-void encodeIDBKey(const IDBKey& key, Vector<char, DefaultInlineBufferSize>& 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<IDBKey>& 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<IDBKey> 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<char>* 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<char>& keyA, const Vector<char>& 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<char> 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<char, DefaultInlineBufferSize> ret;
- ret.append(IDBKeyPathTypeCodedByte1);
- ret.append(IDBKeyPathTypeCodedByte2);
- ret.append(static_cast<char>(keyPath.type()));
- switch (keyPath.type()) {
- case IDBKeyPath::NullType:
- break;
- case IDBKeyPath::StringType:
- ret.appendVector(encodeStringWithLength(keyPath.string()));
- break;
- case IDBKeyPath::ArrayType: {
- const Vector<String>& 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<IDBKeyPath::Type>(*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<String> 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<typename KeyType>
-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<ExistsEntryKey>(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<ObjectStoreDataKey>(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<IndexDataKey>(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<DatabaseFreeListKey>(a, b, ignoreDuplicates, ok);
- if (typeByteA == DatabaseNameTypeByte)
- return compare<DatabaseNameKey>(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<ObjectStoreMetaDataKey>(a, b, ignoreDuplicates, ok);
- if (typeByteA == IndexMetaDataTypeByte)
- return compare<IndexMetaDataKey>(a, b, ignoreDuplicates, ok);
- if (typeByteA == ObjectStoreFreeListTypeByte)
- return compare<ObjectStoreFreeListKey>(a, b, ignoreDuplicates, ok);
- if (typeByteA == IndexFreeListTypeByte)
- return compare<IndexFreeListKey>(a, b, ignoreDuplicates, ok);
- if (typeByteA == ObjectStoreNamesTypeByte)
- return compare<ObjectStoreNamesKey>(a, b, ignoreDuplicates, ok);
- if (typeByteA == IndexNamesKeyTypeByte)
- return compare<IndexNamesKey>(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<ObjectStoreDataKey>(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<ExistsEntryKey>(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<IndexDataKey>(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<char> KeyPrefix::encodeEmpty()
-{
- const Vector<char, 4> result(4, 0);
- ASSERT(encodeInternal(0, 0, 0) == Vector<char>(4, 0));
- return result;
-}
-
-Vector<char> KeyPrefix::encode() const
-{
- ASSERT(m_databaseId != InvalidId);
- ASSERT(m_objectStoreId != InvalidId);
- ASSERT(m_indexId != InvalidId);
- return encodeInternal(m_databaseId, m_objectStoreId, m_indexId);
-}
-
-Vector<char> KeyPrefix::encodeInternal(int64_t databaseId, int64_t objectStoreId, int64_t indexId)
-{
- Vector<char> databaseIdString = encodeIntSafely(databaseId, kMaxDatabaseId);
- Vector<char> objectStoreIdString = encodeIntSafely(objectStoreId, kMaxObjectStoreId);
- Vector<char> 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<char, DefaultInlineBufferSize> 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<char> SchemaVersionKey::encode()
-{
- Vector<char> ret = KeyPrefix::encodeEmpty();
- ret.appendVector(encodeByte(SchemaVersionTypeByte));
- return ret;
-}
-
-Vector<char> MaxDatabaseIdKey::encode()
-{
- Vector<char> ret = KeyPrefix::encodeEmpty();
- ret.appendVector(encodeByte(MaxDatabaseIdTypeByte));
- return ret;
-}
-
-Vector<char> DataVersionKey::encode()
-{
- Vector<char> 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<char> DatabaseFreeListKey::encode(int64_t databaseId)
-{
- Vector<char> ret = KeyPrefix::encodeEmpty();
- ret.appendVector(encodeByte(DatabaseFreeListTypeByte));
- ret.appendVector(encodeVarInt(databaseId));
- return ret;
-}
-
-Vector<char> 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<char> DatabaseNameKey::encode(const String& origin, const String& databaseName)
-{
- Vector<char> ret = KeyPrefix::encodeEmpty();
- ret.appendVector(encodeByte(DatabaseNameTypeByte));
- ret.appendVector(encodeStringWithLength(origin));
- ret.appendVector(encodeStringWithLength(databaseName));
- return ret;
-}
-
-Vector<char> DatabaseNameKey::encodeMinKeyForOrigin(const String& origin)
-{
- return encode(origin, "");
-}
-
-Vector<char> 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<char> DatabaseMetaDataKey::encode(int64_t databaseId, MetaDataType metaDataType)
-{
- KeyPrefix prefix(databaseId);
- Vector<char> 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<char> ObjectStoreMetaDataKey::encode(int64_t databaseId, int64_t objectStoreId, unsigned char metaDataType)
-{
- KeyPrefix prefix(databaseId);
- Vector<char> ret = prefix.encode();
- ret.appendVector(encodeByte(ObjectStoreMetaDataTypeByte));
- ret.appendVector(encodeVarInt(objectStoreId));
- ret.appendVector(encodeByte(metaDataType));
- return ret;
-}
-
-Vector<char> ObjectStoreMetaDataKey::encodeMaxKey(int64_t databaseId)
-{
- return encode(databaseId, INT64_MAX, ObjectMetaDataTypeMaximum);
-}
-
-Vector<char> 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<char> IndexMetaDataKey::encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId, unsigned char metaDataType)
-{
- KeyPrefix prefix(databaseId);
- Vector<char> ret = prefix.encode();
- ret.appendVector(encodeByte(IndexMetaDataTypeByte));
- ret.appendVector(encodeVarInt(objectStoreId));
- ret.appendVector(encodeVarInt(indexId));
- ret.appendVector(encodeByte(metaDataType));
- return ret;
-}
-
-Vector<char> IndexMetaDataKey::encodeMaxKey(int64_t databaseId, int64_t objectStoreId)
-{
- return encode(databaseId, objectStoreId, INT64_MAX, IndexMetaDataTypeMaximum);
-}
-
-Vector<char> 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<char> ObjectStoreFreeListKey::encode(int64_t databaseId, int64_t objectStoreId)
-{
- KeyPrefix prefix(databaseId);
- Vector<char> ret = prefix.encode();
- ret.appendVector(encodeByte(ObjectStoreFreeListTypeByte));
- ret.appendVector(encodeVarInt(objectStoreId));
- return ret;
-}
-
-Vector<char> 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<char> IndexFreeListKey::encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId)
-{
- KeyPrefix prefix(databaseId);
- Vector<char> ret = prefix.encode();
- ret.appendVector(encodeByte(IndexFreeListTypeByte));
- ret.appendVector(encodeVarInt(objectStoreId));
- ret.appendVector(encodeVarInt(indexId));
- return ret;
-}
-
-Vector<char> 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<char> ObjectStoreNamesKey::encode(int64_t databaseId, const String& objectStoreName)
-{
- KeyPrefix prefix(databaseId);
- Vector<char> 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<char> IndexNamesKey::encode(int64_t databaseId, int64_t objectStoreId, const String& indexName)
-{
- KeyPrefix prefix(databaseId);
- Vector<char> 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<char> ObjectStoreDataKey::encode(int64_t databaseId, int64_t objectStoreId, const Vector<char> encodedUserKey)
-{
- KeyPrefix prefix(KeyPrefix::createWithSpecialIndex(databaseId, objectStoreId, SpecialIndexNumber));
- Vector<char> ret = prefix.encode();
- ret.appendVector(encodedUserKey);
-
- return ret;
-}
-
-Vector<char> 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<IDBKey> ObjectStoreDataKey::userKey() const
-{
- RefPtr<IDBKey> 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<char> ExistsEntryKey::encode(int64_t databaseId, int64_t objectStoreId, const Vector<char>& encodedKey)
-{
- KeyPrefix prefix(KeyPrefix::createWithSpecialIndex(databaseId, objectStoreId, SpecialIndexNumber));
- Vector<char> ret = prefix.encode();
- ret.appendVector(encodedKey);
- return ret;
-}
-
-Vector<char> 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<IDBKey> ExistsEntryKey::userKey() const
-{
- RefPtr<IDBKey> 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<char> IndexDataKey::encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const Vector<char>& encodedUserKey, const Vector<char>& encodedPrimaryKey, int64_t sequenceNumber)
-{
- KeyPrefix prefix(databaseId, objectStoreId, indexId);
- Vector<char> ret = prefix.encode();
- ret.appendVector(encodedUserKey);
- ret.appendVector(encodeVarInt(sequenceNumber));
- ret.appendVector(encodedPrimaryKey);
- return ret;
-}
-
-Vector<char> IndexDataKey::encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& userKey)
-{
- return encode(databaseId, objectStoreId, indexId, encodeIDBKey(userKey), minIDBKey());
-}
-
-Vector<char> IndexDataKey::encodeMinKey(int64_t databaseId, int64_t objectStoreId, int64_t indexId)
-{
- return encode(databaseId, objectStoreId, indexId, minIDBKey(), minIDBKey());
-}
-
-Vector<char> 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<IDBKey> IndexDataKey::userKey() const
-{
- RefPtr<IDBKey> key;
- decodeIDBKey(m_encodedUserKey.begin(), m_encodedUserKey.end(), key);
- return key;
-}
-
-PassRefPtr<IDBKey> IndexDataKey::primaryKey() const
-{
- RefPtr<IDBKey> 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 <wtf/RefPtr.h>
-#include <wtf/Vector.h>
-#include <wtf/text/WTFString.h>
-
-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<char> encodeByte(unsigned char);
-const char* decodeByte(const char* p, const char* limit, unsigned char& foundChar);
-Vector<char> maxIDBKey();
-Vector<char> minIDBKey();
-Vector<char> encodeBool(bool);
-bool decodeBool(const char* begin, const char* end);
-Vector<char> encodeInt(int64_t);
-inline Vector<char> 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<char> encodeVarInt(int64_t);
-const char* decodeVarInt(const char* p, const char* limit, int64_t& foundInt);
-Vector<char> encodeString(const String&);
-String decodeString(const char* p, const char* end);
-Vector<char> 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<char> encodeDouble(double);
-const char* decodeDouble(const char* p, const char* limit, double*);
-void encodeIDBKey(const IDBKey&, Vector<char, DefaultInlineBufferSize>& into);
-Vector<char> encodeIDBKey(const IDBKey&);
-const char* decodeIDBKey(const char* p, const char* limit, RefPtr<IDBKey>& foundKey);
-const char* extractEncodedIDBKey(const char* start, const char* limit, Vector<char>* result);
-int compareEncodedIDBKeys(const Vector<char>&, const Vector<char>&, bool& ok);
-Vector<char> 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<char> encode() const;
- static Vector<char> 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<char> 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<char> encode();
-};
-
-class MaxDatabaseIdKey {
-public:
- static Vector<char> encode();
-};
-
-class DataVersionKey {
-public:
- static Vector<char> encode();
-};
-
-class DatabaseFreeListKey {
-public:
- DatabaseFreeListKey();
- static const char* decode(const char* start, const char* limit, DatabaseFreeListKey* result);
- static Vector<char> encode(int64_t databaseId);
- static Vector<char> 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<char> encode(const String& origin, const String& databaseName);
- static Vector<char> encodeMinKeyForOrigin(const String& origin);
- static Vector<char> 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<char> 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<char> encode(int64_t databaseId, int64_t objectStoreId, unsigned char metaDataType);
- static Vector<char> encodeMaxKey(int64_t databaseId);
- static Vector<char> 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<char> encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId, unsigned char metaDataType);
- static Vector<char> encodeMaxKey(int64_t databaseId, int64_t objectStoreId);
- static Vector<char> 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<char> encode(int64_t databaseId, int64_t objectStoreId);
- static Vector<char> 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<char> encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId);
- static Vector<char> 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<char> 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<char> 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<char> encode(int64_t databaseId, int64_t objectStoreId, const Vector<char> encodedUserKey);
- static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, const IDBKey& userKey);
- int compare(const ObjectStoreDataKey& other, bool& ok);
- PassRefPtr<IDBKey> userKey() const;
- static const int64_t SpecialIndexNumber;
-
-private:
- Vector<char> m_encodedUserKey;
-};
-
-class ExistsEntryKey {
-public:
- static const char* decode(const char* start, const char* end, ExistsEntryKey* result);
- static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, const Vector<char>& encodedKey);
- static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, const IDBKey& userKey);
- int compare(const ExistsEntryKey& other, bool& ok);
- PassRefPtr<IDBKey> userKey() const;
-
- static const int64_t SpecialIndexNumber;
-
-private:
- Vector<char> m_encodedUserKey;
-};
-
-class IndexDataKey {
-public:
- IndexDataKey();
- static const char* decode(const char* start, const char* limit, IndexDataKey* result);
- static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const Vector<char>& encodedUserKey, const Vector<char>& encodedPrimaryKey, int64_t sequenceNumber = 0);
- static Vector<char> encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& userKey);
- static Vector<char> encodeMinKey(int64_t databaseId, int64_t objectStoreId, int64_t indexId);
- static Vector<char> 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<IDBKey> userKey() const;
- PassRefPtr<IDBKey> primaryKey() const;
-
-private:
- int64_t m_databaseId;
- int64_t m_objectStoreId;
- int64_t m_indexId;
- Vector<char> m_encodedUserKey;
- Vector<char> 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 <wtf/MainThread.h>
-
-#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<IDBServerConnection> 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<IDBServerConnection> 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<int64_t>&, 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<void()> completionCallback)
-{
- RefPtr<IDBBackingStoreTransactionLevelDB> transaction = m_backingStoreTransactions.get(transactionID);
- ASSERT(transaction);
-
- transaction->begin();
- callOnMainThread(completionCallback);
-}
-
-void IDBServerConnectionLevelDB::commitTransaction(int64_t transactionID, BoolCallbackFunction successCallback)
-{
- RefPtr<IDBBackingStoreTransactionLevelDB> transaction = m_backingStoreTransactions.get(transactionID);
- ASSERT(transaction);
-
- bool result = transaction->commit();
- callOnMainThread([successCallback, result]() {
- successCallback(result);
- });
-}
-
-void IDBServerConnectionLevelDB::resetTransaction(int64_t transactionID, std::function<void()> completionCallback)
-{
- RefPtr<IDBBackingStoreTransactionLevelDB> transaction = m_backingStoreTransactions.get(transactionID);
- ASSERT(transaction);
-
- transaction->resetTransaction();
- callOnMainThread(completionCallback);
-}
-
-void IDBServerConnectionLevelDB::rollbackTransaction(int64_t transactionID, std::function<void()> completionCallback)
-{
- RefPtr<IDBBackingStoreTransactionLevelDB> 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<int64_t>& indexIDs, const Vector<Vector<RefPtr<IDBKey>>>& indexKeys, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback)
-{
- RefPtr<IDBBackingStoreTransactionLevelDB> backingStoreTransaction = m_backingStoreTransactions.get(transactionID);
- ASSERT(backingStoreTransaction);
-
- RefPtr<IDBRecordIdentifier> 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<RefPtr<IDBIndexWriterLevelDB>> 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<void(PassRefPtr<IDBDatabaseError>)> 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<void(PassRefPtr<IDBDatabaseError>)> 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<void(PassRefPtr<IDBDatabaseError>)> 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<void(const IDBGetResult&, PassRefPtr<IDBDatabaseError>)> completionCallback)
-{
- IDBBackingStoreTransactionLevelDB* backingStoreTransaction = m_backingStoreTransactions.get(transaction.id());
- ASSERT(backingStoreTransaction);
-
- RefPtr<IDBKey> key;
-
- if (operation.keyRange()->isOnlyKey())
- key = operation.keyRange()->lower();
- else {
- RefPtr<IDBBackingStoreCursorLevelDB> 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<IDBKey> primaryKey;
- bool ok;
- if (operation.indexID() == IDBIndexMetadata::InvalidId) {
- // Object Store Retrieval Operation
- Vector<char> 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<char> 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<void(PassRefPtr<IDBKey>, PassRefPtr<IDBDatabaseError>)> completionCallback)
-{
- IDBBackingStoreTransactionLevelDB* backingStoreTransaction = m_backingStoreTransactions.get(transaction.id());
- ASSERT(backingStoreTransaction);
-
- bool keyWasGenerated = false;
-
- RefPtr<IDBKey> key;
- if (operation.putMode() != IDBDatabaseBackend::CursorUpdate && operation.objectStore().autoIncrement && !operation.key()) {
- RefPtr<IDBKey> 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<IDBRecordIdentifier> 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<RefPtr<IDBIndexWriterLevelDB>> 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<void(int64_t, PassRefPtr<IDBDatabaseError>)> 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<IDBBackingStoreCursorLevelDB> 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<void(int64_t, PassRefPtr<IDBDatabaseError>)> completionCallback)
-{
- IDBBackingStoreTransactionLevelDB* backingStoreTransaction = m_backingStoreTransactions.get(transaction.id());
- ASSERT(backingStoreTransaction);
-
- uint32_t count = 0;
- RefPtr<IDBBackingStoreCursorLevelDB> 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<void(PassRefPtr<IDBDatabaseError>)> completionCallback)
-{
- IDBBackingStoreTransactionLevelDB* backingStoreTransaction = m_backingStoreTransactions.get(transaction.id());
- ASSERT(backingStoreTransaction);
-
- int64_t cursorID = m_nextCursorID++;
-
- RefPtr<IDBBackingStoreCursorLevelDB> 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<void(PassRefPtr<IDBDatabaseError>)> 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<void(PassRefPtr<IDBDatabaseError>)> 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<void(PassRefPtr<IDBDatabaseError>)> 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<IDBDatabaseError> 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<void(PassRefPtr<IDBKey>, PassRefPtr<IDBKey>, PassRefPtr<SharedBuffer>, PassRefPtr<IDBDatabaseError>)> 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<IDBKey> key = backingStoreCursor->key(), primaryKey = backingStoreCursor->primaryKey();
- RefPtr<SharedBuffer> 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<void(PassRefPtr<IDBKey>, PassRefPtr<IDBKey>, PassRefPtr<SharedBuffer>, PassRefPtr<IDBDatabaseError>)> 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<IDBKey> key = backingStoreCursor->key(), primaryKey = backingStoreCursor->primaryKey();
- RefPtr<SharedBuffer> 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<IDBServerConnection> 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<int64_t>& objectStoreIds, IndexedDB::TransactionMode, BoolCallbackFunction successCallback) override;
- virtual void beginTransaction(int64_t transactionID, std::function<void()> completionCallback) override;
- virtual void commitTransaction(int64_t transactionID, BoolCallbackFunction successCallback) override;
- virtual void resetTransaction(int64_t transactionID, std::function<void()> completionCallback) override;
- virtual void rollbackTransaction(int64_t transactionID, std::function<void()> completionCallback) override;
- virtual void setIndexKeys(int64_t transactionID, int64_t databaseID, int64_t objectStoreID, const IDBObjectStoreMetadata&, IDBKey& primaryKey, const Vector<int64_t>& indexIDs, const Vector<Vector<RefPtr<IDBKey>>>& indexKeys, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback);
-
- virtual void createObjectStore(IDBTransactionBackend&, const CreateObjectStoreOperation&, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) override;
- virtual void createIndex(IDBTransactionBackend&, const CreateIndexOperation&, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) override;
- virtual void deleteIndex(IDBTransactionBackend&, const DeleteIndexOperation&, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) override;
- virtual void get(IDBTransactionBackend&, const GetOperation&, std::function<void(const IDBGetResult&, PassRefPtr<IDBDatabaseError>)> completionCallback) override;
- virtual void put(IDBTransactionBackend&, const PutOperation&, std::function<void(PassRefPtr<IDBKey>, PassRefPtr<IDBDatabaseError>)> completionCallback) override;
- virtual void openCursor(IDBTransactionBackend&, const OpenCursorOperation&, std::function<void(int64_t, PassRefPtr<IDBDatabaseError>)> completionCallback) override;
- virtual void count(IDBTransactionBackend&, const CountOperation&, std::function<void(int64_t, PassRefPtr<IDBDatabaseError>)> completionCallback) override;
- virtual void deleteRange(IDBTransactionBackend&, const DeleteRangeOperation&, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) override;
- virtual void clearObjectStore(IDBTransactionBackend&, const ClearObjectStoreOperation&, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) override;
- virtual void deleteObjectStore(IDBTransactionBackend&, const DeleteObjectStoreOperation&, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) override;
- virtual void changeDatabaseVersion(IDBTransactionBackend&, const IDBDatabaseBackend::VersionChangeOperation&, std::function<void(PassRefPtr<IDBDatabaseError>)> completionCallback) override;
-
- // Cursor-level operations
- virtual void cursorAdvance(IDBCursorBackend&, const CursorAdvanceOperation&, std::function<void(PassRefPtr<IDBKey>, PassRefPtr<IDBKey>, PassRefPtr<SharedBuffer>, PassRefPtr<IDBDatabaseError>)> completionCallback) override;
- virtual void cursorIterate(IDBCursorBackend&, const CursorIterationOperation&, std::function<void(PassRefPtr<IDBKey>, PassRefPtr<IDBKey>, PassRefPtr<SharedBuffer>, PassRefPtr<IDBDatabaseError>)> completionCallback) override;
-
-private:
- IDBServerConnectionLevelDB(const String& databaseName, IDBBackingStoreLevelDB*);
-
- RefPtr<IDBBackingStoreLevelDB> m_backingStore;
- HashMap<int64_t, RefPtr<IDBBackingStoreTransactionLevelDB>> m_backingStoreTransactions;
- HashMap<int64_t, RefPtr<IDBBackingStoreCursorLevelDB>> 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> 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<String>& 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 <wtf/HashSet.h>
+#include <wtf/Ref.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class IDBError;
+class IDBResourceIdentifier;
+class IDBResultData;
+
+namespace IDBServer {
+
+class UniqueIDBDatabaseConnection;
+
+class IDBConnectionToClient : public RefCounted<IDBConnectionToClient> {
+public:
+ WEBCORE_EXPORT static Ref<IDBConnectionToClient> 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<String>& databaseNames);
+
+ void registerDatabaseConnection(UniqueIDBDatabaseConnection&);
+ void unregisterDatabaseConnection(UniqueIDBDatabaseConnection&);
+ void connectionToClientClosed();
+
+private:
+ IDBConnectionToClient(IDBConnectionToClientDelegate&);
+
+ Ref<IDBConnectionToClientDelegate> m_delegate;
+ HashSet<UniqueIDBDatabaseConnection*> 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 <wtf/Vector.h>
+#include <wtf/text/WTFString.h>
+
+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<String>& 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 <glib.h>
+#include <wtf/glib/GRefPtr.h>
+#endif
+
+namespace WebCore {
+
+enum class KeyPathType { Null, String, Array };
+
+RefPtr<SharedBuffer> serializeIDBKeyPath(const std::optional<IDBKeyPath>& 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<String>& 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<IDBKeyPath>& 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<String> 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<GBytes> bytes = adoptGRef(g_bytes_new(data, size));
+ GRefPtr<GVariant> 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 <typename T> static void writeLittleEndian(Vector<char>& buffer, T value)
+{
+ for (unsigned i = 0; i < sizeof(T); i++) {
+ buffer.append(value & 0xFF);
+ value >>= 8;
+ }
+}
+
+template <typename T> 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 <typename T> static void writeLittleEndian(Vector<char>& buffer, T value)
+{
+ buffer.append(reinterpret_cast<uint8_t*>(&value), sizeof(value));
+}
+
+template <typename T> static bool readLittleEndian(const uint8_t*& ptr, const uint8_t* end, T& value)
+{
+ if (ptr > end - sizeof(value))
+ return false;
+
+ value = *reinterpret_cast<const T*>(ptr);
+ ptr += sizeof(T);
+
+ return true;
+}
+#endif
+
+static void writeDouble(Vector<char>& data, double d)
+{
+ writeLittleEndian(data, *reinterpret_cast<uint64_t*>(&d));
+}
+
+static bool readDouble(const uint8_t*& data, const uint8_t* end, double& d)
+{
+ return readLittleEndian(data, end, *reinterpret_cast<uint64_t*>(&d));
+}
+
+static void encodeKey(Vector<char>& data, const IDBKeyData& key)
+{
+ SIDBKeyType type = serializedTypeForKeyType(key.type());
+ data.append(static_cast<char>(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<SharedBuffer> serializeIDBKeyData(const IDBKeyData& key)
+{
+ Vector<char> 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<SIDBKeyType>(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<uint64_t>(end - data) < length * 2)
+ return false;
+
+ Vector<UChar> 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<uint64_t>(end - data) < size64)
+ return false;
+
+ if (size64 > std::numeric_limits<size_t>::max())
+ return false;
+
+ size_t size = static_cast<size_t>(size64);
+ Vector<uint8_t> 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<size_t>::max())
+ return false;
+
+ size_t size = static_cast<size_t>(size64);
+ Vector<IDBKeyData> 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<SharedBuffer> serializeIDBKeyPath(const std::optional<IDBKeyPath>&);
+bool deserializeIDBKeyPath(const uint8_t* buffer, size_t bufferSize, std::optional<IDBKeyPath>&);
+
+RefPtr<SharedBuffer> 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 <wtf/CrossThreadCopier.h>
+#include <wtf/Locker.h>
+#include <wtf/MainThread.h>
+
+namespace WebCore {
+namespace IDBServer {
+
+Ref<IDBServer> IDBServer::create(IDBBackingStoreTemporaryFileHandler& fileHandler)
+{
+ return adoptRef(*new IDBServer(fileHandler));
+}
+
+Ref<IDBServer> IDBServer::create(const String& databaseDirectoryPath, IDBBackingStoreTemporaryFileHandler& fileHandler)
+{
+ return adoptRef(*new IDBServer(databaseDirectoryPath, fileHandler));
+}
+
+IDBServer::IDBServer(IDBBackingStoreTemporaryFileHandler& fileHandler)
+ : m_backingStoreTemporaryFileHandler(fileHandler)
+{
+ Locker<Lock> 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<Lock> 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<IDBBackingStore> IDBServer::createBackingStore(const IDBDatabaseIdentifier& identifier)
+{
+ ASSERT(!isMainThread());
+
+ if (m_databaseDirectoryPath.isEmpty())
+ return MemoryIDBBackingStore::create(identifier);
+
+ return std::make_unique<SQLiteIDBBackingStore>(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<String> entries = listDirectory(directory, ASCIILiteral("*"));
+ Vector<String> 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<String>& 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<Lock> locker(m_mainThreadReplyLock);
+ if (m_mainThreadReplyScheduled)
+ return;
+
+ m_mainThreadReplyScheduled = true;
+ callOnMainThread([this] {
+ handleTaskRepliesOnMainThread();
+ });
+}
+
+void IDBServer::databaseThreadEntry(void* threadData)
+{
+ ASSERT(threadData);
+ IDBServer* server = reinterpret_cast<IDBServer*>(threadData);
+ server->databaseRunLoop();
+}
+
+void IDBServer::databaseRunLoop()
+{
+ ASSERT(!isMainThread());
+ {
+ Locker<Lock> locker(m_databaseThreadCreationLock);
+ }
+
+ while (!m_databaseQueue.isKilled())
+ m_databaseQueue.waitForMessage().performTask();
+}
+
+void IDBServer::handleTaskRepliesOnMainThread()
+{
+ {
+ Locker<Lock> 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<void ()> 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<RefPtr<UniqueIDBDatabase>> 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<SecurityOriginData>& origins, std::function<void ()> completionHandler)
+{
+ uint64_t callbackID = generateDeleteCallbackID();
+ auto addResult = m_deleteDatabaseCompletionHandlers.add(callbackID, WTFMove(completionHandler));
+ ASSERT_UNUSED(addResult, addResult.isNewEntry);
+
+ HashSet<RefPtr<UniqueIDBDatabase>> 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<String> 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<String> originPaths = listDirectory(m_databaseDirectoryPath, "*");
+ for (auto& originPath : originPaths)
+ removeAllDatabasesForOriginPath(originPath, modifiedSince);
+ }
+
+ postDatabaseTaskReply(createCrossThreadTask(*this, &IDBServer::didPerformCloseAndDeleteDatabases, callbackID));
+}
+
+void IDBServer::performCloseAndDeleteDatabasesForOrigins(const Vector<SecurityOriginData>& 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 <wtf/CrossThreadQueue.h>
+#include <wtf/CrossThreadTask.h>
+#include <wtf/HashMap.h>
+#include <wtf/Lock.h>
+#include <wtf/Ref.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+class IDBCursorInfo;
+class IDBRequestData;
+class IDBValue;
+
+struct IDBGetRecordData;
+
+namespace IDBServer {
+
+class IDBBackingStoreTemporaryFileHandler;
+
+class IDBServer : public RefCounted<IDBServer> {
+public:
+ static Ref<IDBServer> create(IDBBackingStoreTemporaryFileHandler&);
+ WEBCORE_EXPORT static Ref<IDBServer> 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<IDBBackingStore> createBackingStore(const IDBDatabaseIdentifier&);
+
+ WEBCORE_EXPORT void closeAndDeleteDatabasesModifiedSince(std::chrono::system_clock::time_point, std::function<void ()> completionHandler);
+ WEBCORE_EXPORT void closeAndDeleteDatabasesForOrigins(const Vector<SecurityOriginData>&, std::function<void ()> 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<String>& databaseNames);
+
+ void performCloseAndDeleteDatabasesModifiedSince(std::chrono::system_clock::time_point, uint64_t callbackID);
+ void performCloseAndDeleteDatabasesForOrigins(const Vector<SecurityOriginData>&, uint64_t callbackID);
+ void didPerformCloseAndDeleteDatabases(uint64_t callbackID);
+
+ static void databaseThreadEntry(void*);
+ void databaseRunLoop();
+ void handleTaskRepliesOnMainThread();
+
+ HashMap<uint64_t, RefPtr<IDBConnectionToClient>> m_connectionMap;
+ HashMap<IDBDatabaseIdentifier, RefPtr<UniqueIDBDatabase>> m_uniqueIDBDatabaseMap;
+
+ ThreadIdentifier m_threadID { 0 };
+ Lock m_databaseThreadCreationLock;
+ Lock m_mainThreadReplyLock;
+ bool m_mainThreadReplyScheduled { false };
+
+ CrossThreadQueue<CrossThreadTask> m_databaseQueue;
+ CrossThreadQueue<CrossThreadTask> m_databaseReplyQueue;
+
+ HashMap<uint64_t, UniqueIDBDatabaseConnection*> m_databaseConnections;
+ HashMap<IDBResourceIdentifier, UniqueIDBDatabaseTransaction*> m_transactions;
+
+ HashMap<uint64_t, std::function<void ()>> 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<IDBKeyData>;
+}
+
+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<IDBKeyData>::iterator iterator)
+ : m_entry(&entry)
+ , m_forwardIterator(iterator)
+{
+}
+
+IndexValueEntry::Iterator::Iterator(IndexValueEntry& entry, std::set<IDBKeyData>::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<IDBKeyData>::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 <set>
+
+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<IDBKeyData>::iterator);
+ Iterator(IndexValueEntry&, std::set<IDBKeyData>::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<IDBKeyData>::iterator m_forwardIterator;
+ std::set<IDBKeyData>::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<IDBKeyData>* 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 <wtf/text/StringBuilder.h>
+
+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<IDBKeyData> IndexValueStore::allValuesForKey(const IDBKeyData& key, uint32_t limit) const
+{
+ const auto& entry = m_records.get(key);
+ if (!entry)
+ return { };
+
+ Vector<IDBKeyData> 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<IndexValueEntry>(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<IDBKeyData> 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<IDBKeyData>::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<IDBKeyData>::reverse_iterator IndexValueStore::highestReverseIteratorInRange(const IDBKeyRangeData& range) const
+{
+ auto highestInRange = std::set<IDBKeyData>::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<IDBKeyData>::iterator iterator, IndexValueEntry::Iterator primaryIterator)
+ : m_store(&store)
+ , m_forwardIterator(iterator)
+ , m_primaryKeyIterator(primaryIterator)
+{
+}
+
+IndexValueStore::Iterator::Iterator(IndexValueStore& store, CursorDuplicity duplicity, std::set<IDBKeyData>::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 <set>
+#include <wtf/HashMap.h>
+
+namespace WebCore {
+
+class IDBError;
+
+struct IDBKeyRangeData;
+
+namespace IDBServer {
+
+class MemoryIndex;
+
+typedef HashMap<IDBKeyData, std::unique_ptr<IndexValueEntry>, IDBKeyDataHash, IDBKeyDataHashTraits> IndexKeyValueMap;
+
+class IndexValueStore {
+public:
+ IndexValueStore(bool unique);
+
+ const IDBKeyData* lowestValueForKey(const IDBKeyData&) const;
+ Vector<IDBKeyData> 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<IDBKeyData>::iterator, IndexValueEntry::Iterator);
+ Iterator(IndexValueStore&, CursorDuplicity, std::set<IDBKeyData>::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<IDBKeyData>::iterator m_forwardIterator;
+ std::set<IDBKeyData>::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<IDBKeyData>::iterator lowestIteratorInRange(const IDBKeyRangeData&) const;
+ std::set<IDBKeyData>::reverse_iterator highestReverseIteratorInRange(const IDBKeyRangeData&) const;
+
+ IndexKeyValueMap m_records;
+ std::set<IDBKeyData> 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 <wtf/SetForScope.h>
+
+namespace WebCore {
+namespace IDBServer {
+
+std::unique_ptr<MemoryBackingStoreTransaction> MemoryBackingStoreTransaction::create(MemoryIDBBackingStore& backingStore, const IDBTransactionInfo& info)
+{
+ return std::make_unique<MemoryBackingStoreTransaction>(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<IDBDatabaseInfo>(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<MemoryIndex>&& 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<MemoryObjectStore>&& 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>&& keyValueMap, std::unique_ptr<std::set<IDBKeyData>>&& 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<IndexValueStore>&& 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<KeyValueMap>();
+
+ 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<bool> 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 <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+
+namespace WebCore {
+namespace IDBServer {
+
+class MemoryIDBBackingStore;
+class MemoryIndex;
+class MemoryObjectStore;
+
+typedef HashMap<IDBKeyData, ThreadSafeDataBuffer, IDBKeyDataHash, IDBKeyDataHashTraits> KeyValueMap;
+
+class MemoryBackingStoreTransaction {
+public:
+ static std::unique_ptr<MemoryBackingStoreTransaction> 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<MemoryObjectStore>&&);
+ void objectStoreCleared(MemoryObjectStore&, std::unique_ptr<KeyValueMap>&&, std::unique_ptr<std::set<IDBKeyData>>&&);
+ void objectStoreRenamed(MemoryObjectStore&, const String& oldName);
+ void indexRenamed(MemoryIndex&, const String& oldName);
+ void indexCleared(MemoryIndex&, std::unique_ptr<IndexValueStore>&&);
+
+ void addNewIndex(MemoryIndex&);
+ void addExistingIndex(MemoryIndex&);
+ void indexDeleted(Ref<MemoryIndex>&&);
+
+ void abort();
+ void commit();
+
+private:
+ void finish();
+
+ MemoryIDBBackingStore& m_backingStore;
+ IDBTransactionInfo m_info;
+
+ std::unique_ptr<IDBDatabaseInfo> m_originalDatabaseInfo;
+
+ bool m_inProgress { true };
+ bool m_isAborting { false };
+
+ HashSet<RefPtr<MemoryObjectStore>> m_objectStores;
+ HashSet<RefPtr<MemoryObjectStore>> m_versionChangeAddedObjectStores;
+ HashSet<RefPtr<MemoryIndex>> m_indexes;
+ HashSet<RefPtr<MemoryIndex>> m_versionChangeAddedIndexes;
+
+ HashMap<MemoryObjectStore*, uint64_t> m_originalKeyGenerators;
+ HashMap<String, RefPtr<MemoryObjectStore>> m_deletedObjectStores;
+ HashMap<String, RefPtr<MemoryIndex>> m_deletedIndexes;
+ HashMap<MemoryObjectStore*, std::unique_ptr<KeyValueMap>> m_originalValues;
+ HashMap<MemoryObjectStore*, std::unique_ptr<KeyValueMap>> m_clearedKeyValueMaps;
+ HashMap<MemoryObjectStore*, std::unique_ptr<std::set<IDBKeyData>>> m_clearedOrderedKeys;
+ HashMap<MemoryObjectStore*, String> m_originalObjectStoreNames;
+ HashMap<MemoryIndex*, String> m_originalIndexNames;
+ HashMap<MemoryIndex*, std::unique_ptr<IndexValueStore>> 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 <wtf/HashMap.h>
+#include <wtf/NeverDestroyed.h>
+
+namespace WebCore {
+namespace IDBServer {
+
+static HashMap<IDBResourceIdentifier, MemoryCursor*>& cursorMap()
+{
+ static NeverDestroyed<HashMap<IDBResourceIdentifier, MemoryCursor*>> 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> MemoryIDBBackingStore::create(const IDBDatabaseIdentifier& identifier)
+{
+ return std::make_unique<MemoryIDBBackingStore>(identifier);
+}
+
+MemoryIDBBackingStore::MemoryIDBBackingStore(const IDBDatabaseIdentifier& identifier)
+ : m_identifier(identifier)
+{
+}
+
+MemoryIDBBackingStore::~MemoryIDBBackingStore()
+{
+}
+
+IDBError MemoryIDBBackingStore::getOrEstablishDatabaseInfo(IDBDatabaseInfo& info)
+{
+ if (!m_databaseInfo)
+ m_databaseInfo = std::make_unique<IDBDatabaseInfo>(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<IDBDatabaseInfo>(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<MemoryObjectStore>&& 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<MemoryObjectStore>&& 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<MemoryObjectStore> 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 <wtf/HashMap.h>
+
+namespace WebCore {
+namespace IDBServer {
+
+class MemoryObjectStore;
+
+class MemoryIDBBackingStore : public IDBBackingStore {
+public:
+ static std::unique_ptr<MemoryIDBBackingStore> 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<MemoryObjectStore>&&);
+
+private:
+ RefPtr<MemoryObjectStore> takeObjectStoreByIdentifier(uint64_t identifier);
+
+ IDBDatabaseIdentifier m_identifier;
+ std::unique_ptr<IDBDatabaseInfo> m_databaseInfo;
+
+ HashMap<IDBResourceIdentifier, std::unique_ptr<MemoryBackingStoreTransaction>> m_transactions;
+
+ void registerObjectStore(Ref<MemoryObjectStore>&&);
+ void unregisterObjectStore(MemoryObjectStore&);
+ HashMap<uint64_t, RefPtr<MemoryObjectStore>> m_objectStoresByIdentifier;
+ HashMap<String, MemoryObjectStore*> 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> 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<MemoryIndexCursor*> cursors;
+ copyToVector(m_cleanCursors, cursors);
+ for (auto* cursor : cursors)
+ cursor->indexValueChanged(indexKey, primaryKey);
+}
+
+void MemoryIndex::notifyCursorsOfAllRecordsChanged()
+{
+ Vector<MemoryIndexCursor*> 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<IndexValueStore>&& 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<uint32_t> 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<uint32_t>::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<IndexValueStore>(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<IDBKeyData> 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<IDBKeyData> 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<MemoryIndexCursor>(*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 <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+
+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<MemoryIndex> {
+public:
+ static Ref<MemoryIndex> 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<uint32_t> 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<IndexValueStore>&&);
+
+ 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<IndexValueStore> m_records;
+
+ HashMap<IDBResourceIdentifier, std::unique_ptr<MemoryIndexCursor>> m_cursors;
+ HashSet<MemoryIndexCursor*> 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 <runtime/JSCJSValue.h>
+#include <runtime/JSCJSValueInlines.h>
+#include <runtime/JSLock.h>
+#include <wtf/NeverDestroyed.h>
+
+using namespace JSC;
+
+namespace WebCore {
+namespace IDBServer {
+
+Ref<MemoryObjectStore> 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<MemoryIndex>&& 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<MemoryIndex> 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<uint64_t> 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<KeyValueMap>&& store, std::unique_ptr<std::set<IDBKeyData>>&& 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<KeyValueMap>();
+ m_orderedKeys = std::make_unique<std::set<IDBKeyData>>();
+ }
+
+ 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<IDBKeyData>::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<std::pair<MemoryIndex*, IndexKey>> 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<uint32_t> count, IndexedDB::GetAllType type, IDBGetAllResult& result) const
+{
+ result = { type };
+
+ uint32_t targetCount;
+ if (count && count.value())
+ targetCount = count.value();
+ else
+ targetCount = std::numeric_limits<uint32_t>::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<MemoryIndex>&& 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<MemoryObjectStoreCursor>(*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 <set>
+#include <wtf/HashMap.h>
+#include <wtf/RefCounted.h>
+
+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<IDBKeyData, ThreadSafeDataBuffer, IDBKeyDataHash, IDBKeyDataHashTraits> KeyValueMap;
+
+class MemoryObjectStore : public RefCounted<MemoryObjectStore> {
+public:
+ static Ref<MemoryObjectStore> 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<MemoryIndex>&&);
+
+ 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<KeyValueMap>&&, std::unique_ptr<std::set<IDBKeyData>>&&);
+
+ 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<uint32_t> count, IndexedDB::GetAllType, IDBGetAllResult&) const;
+
+ const IDBObjectStoreInfo& info() const { return m_info; }
+
+ MemoryObjectStoreCursor* maybeOpenCursor(const IDBCursorInfo&);
+
+ std::set<IDBKeyData>* orderedKeys() { return m_orderedKeys.get(); }
+
+ MemoryIndex* indexForIdentifier(uint64_t);
+
+ void maybeRestoreDeletedIndex(Ref<MemoryIndex>&&);
+
+ void rename(const String& newName) { m_info.rename(newName); }
+ void renameIndex(MemoryIndex&, const String& newName);
+
+private:
+ MemoryObjectStore(const IDBObjectStoreInfo&);
+
+ std::set<IDBKeyData>::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<IDBKeyData>::iterator);
+ void updateCursorsForDeleteRecord(const IDBKeyData&);
+
+ RefPtr<MemoryIndex> takeIndexByIdentifier(uint64_t indexIdentifier);
+
+ IDBObjectStoreInfo m_info;
+
+ MemoryBackingStoreTransaction* m_writeTransaction { nullptr };
+ uint64_t m_keyGeneratorValue { 1 };
+
+ std::unique_ptr<KeyValueMap> m_keyValueStore;
+ std::unique_ptr<std::set<IDBKeyData>> m_orderedKeys;
+
+ void unregisterIndex(MemoryIndex&);
+ HashMap<uint64_t, RefPtr<MemoryIndex>> m_indexesByIdentifier;
+ HashMap<String, RefPtr<MemoryIndex>> m_indexesByName;
+ HashMap<IDBResourceIdentifier, std::unique_ptr<MemoryObjectStoreCursor>> 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<IDBKeyData>::iterator iterator)
+{
+ if (m_iterator)
+ return;
+
+ if (*iterator == m_currentPositionKey)
+ m_iterator = iterator;
+}
+
+void MemoryObjectStoreCursor::setFirstInRemainingRange(std::set<IDBKeyData>& 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<IDBKeyData>& 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<IDBKeyData>& 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<IDBKeyData>& 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<IDBKeyData>& 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 <set>
+#include <wtf/Optional.h>
+
+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<IDBKeyData>::iterator);
+
+private:
+ void currentData(IDBGetResult&) final;
+ void iterate(const IDBKeyData&, const IDBKeyData& primaryKey, uint32_t count, IDBGetResult&) final;
+
+ void setFirstInRemainingRange(std::set<IDBKeyData>&);
+ void setForwardIteratorFromRemainingRange(std::set<IDBKeyData>&);
+ void setReverseIteratorFromRemainingRange(std::set<IDBKeyData>&);
+
+ void incrementForwardIterator(std::set<IDBKeyData>&, const IDBKeyData&, uint32_t count);
+ void incrementReverseIterator(std::set<IDBKeyData>&, const IDBKeyData&, uint32_t count);
+
+ bool hasValidPosition() const;
+
+ MemoryObjectStore& m_objectStore;
+
+ IDBKeyRangeData m_remainingRange;
+
+ std::optional<std::set<IDBKeyData>::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 <heap/HeapInlines.h>
+#include <heap/StrongInlines.h>
+#include <runtime/AuxiliaryBarrierInlines.h>
+#include <runtime/JSCJSValueInlines.h>
+#include <runtime/JSGlobalObject.h>
+#include <runtime/StructureInlines.h>
+#include <wtf/NeverDestroyed.h>
+
+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<const uint8_t*>(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<const uint8_t*>(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<WTF::String> v1RecordsTableSchemaString(v1RecordsTableSchema("Records"));
+ return v1RecordsTableSchemaString;
+}
+
+static const String& v1RecordsTableSchemaAlternate()
+{
+ static NeverDestroyed<WTF::String> 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<WTF::String> v2RecordsTableSchemaString(v2RecordsTableSchema("Records"));
+ return v2RecordsTableSchemaString;
+}
+
+static const String& v2RecordsTableSchemaAlternate()
+{
+ static NeverDestroyed<WTF::String> 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<WTF::String> v3RecordsTableSchemaString(v3RecordsTableSchema("Records"));
+ return v3RecordsTableSchemaString;
+}
+
+static const String& v3RecordsTableSchemaAlternate()
+{
+ static NeverDestroyed<WTF::String> 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<WTF::String> v1IndexRecordsTableSchemaString(v1IndexRecordsTableSchema("IndexRecords"));
+ return v1IndexRecordsTableSchemaString;
+}
+
+static const String& v1IndexRecordsTableSchemaAlternate()
+{
+ static NeverDestroyed<WTF::String> 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<WTF::String> v2IndexRecordsTableSchemaString(v2IndexRecordsTableSchema("IndexRecords"));
+ return v2IndexRecordsTableSchemaString;
+}
+
+static const String& v2IndexRecordsTableSchemaAlternate()
+{
+ static NeverDestroyed<WTF::String> 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<WTF::String> indexRecordsTableSchemaString = v3IndexRecordsTableSchema("IndexRecords");
+ return indexRecordsTableSchemaString;
+}
+
+static const String v3IndexRecordsTableSchemaAlternate()
+{
+ static NeverDestroyed<WTF::String> indexRecordsTableSchemaString = v3IndexRecordsTableSchema("\"IndexRecords\"");
+ return indexRecordsTableSchemaString;
+}
+
+static const String& v1IndexRecordsIndexSchema()
+{
+ static NeverDestroyed<WTF::String> 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<String> blobRecordsTableSchemaString(blobRecordsTableSchema("BlobRecords"));
+ return blobRecordsTableSchemaString;
+}
+
+static const String& blobRecordsTableSchemaAlternate()
+{
+ static NeverDestroyed<String> 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<String> blobFilesTableSchemaString(blobFilesTableSchema("BlobFiles"));
+ return blobFilesTableSchemaString;
+}
+
+static const String& blobFilesTableSchemaAlternate()
+{
+ static NeverDestroyed<String> 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<IDBDatabaseInfo> 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<IDBDatabaseInfo>(m_identifier.databaseName(), 0);
+}
+
+std::unique_ptr<IDBDatabaseInfo> 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<IDBDatabaseInfo>(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<char> keyPathBuffer;
+ sql.getColumnBlobAsVector(2, keyPathBuffer);
+
+ std::optional<IDBKeyPath> objectStoreKeyPath;
+ if (!deserializeIDBKeyPath(reinterpret_cast<const uint8_t*>(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<char> keyPathBuffer;
+ sql.getColumnBlobAsVector(3, keyPathBuffer);
+
+ std::optional<IDBKeyPath> indexKeyPath;
+ if (!deserializeIDBKeyPath(reinterpret_cast<const uint8_t*>(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<SQLiteDatabase>();
+ 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<SQLiteIDBTransaction>(*this, info);
+
+ auto error = addResult.iterator->value->begin(*m_sqliteDB);
+ if (error.isNull() && info.mode() == IDBTransactionMode::Versionchange) {
+ m_originalDatabaseInfoBeforeVersionChange = std::make_unique<IDBDatabaseInfo>(*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<SharedBuffer> 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<SharedBuffer> 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<SharedBuffer> 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<IDBKeyData> 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<SharedBuffer> 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<SharedBuffer> 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<SharedBuffer> 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<String> 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<SharedBuffer> 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<IDBKeyData> 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<SharedBuffer> 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<SharedBuffer> 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<String>& blobURLs = value.blobURLs();
+ const Vector<String>& 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<String>& blobURLs, Vector<String>& blobFilePaths)
+{
+ ASSERT(objectStoreRecord);
+
+ HashSet<String> 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<SharedBuffer> 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<SharedBuffer> 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<ASCIILiteral> lowerOpenUpperOpen("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;");
+ static NeverDestroyed<ASCIILiteral> lowerOpenUpperClosed("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;");
+ static NeverDestroyed<ASCIILiteral> lowerClosedUpperOpen("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;");
+ static NeverDestroyed<ASCIILiteral> lowerClosedUpperClosed("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;");
+
+ static NeverDestroyed<ASCIILiteral> lowerOpenUpperOpenKeyOnly("SELECT key FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;");
+ static NeverDestroyed<ASCIILiteral> lowerOpenUpperClosedKeyOnly("SELECT key FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;");
+ static NeverDestroyed<ASCIILiteral> lowerClosedUpperOpenKeyOnly("SELECT key FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;");
+ static NeverDestroyed<ASCIILiteral> 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<uint8_t> 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<String> 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<ASCIILiteral> lowerOpenUpperOpenKey("SELECT key FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;");
+ static NeverDestroyed<ASCIILiteral> lowerOpenUpperClosedKey("SELECT key FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;");
+ static NeverDestroyed<ASCIILiteral> lowerClosedUpperOpenKey("SELECT key FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;");
+ static NeverDestroyed<ASCIILiteral> lowerClosedUpperClosedKey("SELECT key FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;");
+ static NeverDestroyed<ASCIILiteral> lowerOpenUpperOpenValue("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;");
+ static NeverDestroyed<ASCIILiteral> lowerOpenUpperClosedValue("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;");
+ static NeverDestroyed<ASCIILiteral> lowerClosedUpperOpenValue("SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;");
+ static NeverDestroyed<ASCIILiteral> 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<uint32_t>::max();
+
+ int sqlResult = sql->step();
+ uint32_t returnedResults = 0;
+
+ while (sqlResult == SQLITE_ROW && returnedResults < targetResults) {
+ if (getAllRecordsData.getAllType == IndexedDB::GetAllType::Values) {
+ Vector<uint8_t> buffer;
+ sql->getColumnBlobAsVector(0, buffer);
+ ThreadSafeDataBuffer resultBuffer = ThreadSafeDataBuffer::adoptVector(buffer);
+
+ auto recordID = sql->getColumnInt64(1);
+
+ ASSERT(recordID);
+ Vector<String> 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<uint8_t> 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<uint32_t>::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<SharedBuffer> 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<uint8_t> 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<String> 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<String> 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<size_t>(sql)]) {
+ if (m_cachedStatements[static_cast<size_t>(sql)]->reset() == SQLITE_OK)
+ return m_cachedStatements[static_cast<size_t>(sql)].get();
+ m_cachedStatements[static_cast<size_t>(sql)] = nullptr;
+ }
+
+ if (m_sqliteDB) {
+ m_cachedStatements[static_cast<size_t>(sql)] = std::make_unique<SQLiteStatement>(*m_sqliteDB, statement);
+ if (m_cachedStatements[static_cast<size_t>(sql)]->prepare() != SQLITE_OK)
+ m_cachedStatements[static_cast<size_t>(sql)] = nullptr;
+ }
+
+ return m_cachedStatements[static_cast<size_t>(sql)].get();
+}
+
+void SQLiteIDBBackingStore::closeSQLiteDB()
+{
+ for (size_t i = 0; i < static_cast<int>(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 <JavaScriptCore/Strong.h>
+#include <wtf/HashMap.h>
+
+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<String>& blobURLs, Vector<String>& blobFilePaths);
+
+ static String databaseNameFromEncodedFilename(const String&);
+
+private:
+ String filenameForDatabaseName() const;
+ String fullDatabasePath() const;
+
+ bool ensureValidRecordsTable();
+ bool ensureValidIndexRecordsTable();
+ bool ensureValidIndexRecordsIndex();
+ bool ensureValidBlobTables();
+ std::unique_ptr<IDBDatabaseInfo> createAndPopulateInitialDatabaseInfo();
+ std::unique_ptr<IDBDatabaseInfo> 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<SQLiteStatement> m_cachedStatements[static_cast<int>(SQL::Count)];
+
+ JSC::VM& vm();
+ JSC::JSGlobalObject& globalObject();
+ void initializeVM();
+
+ IDBDatabaseIdentifier m_identifier;
+ std::unique_ptr<IDBDatabaseInfo> m_databaseInfo;
+ std::unique_ptr<IDBDatabaseInfo> m_originalDatabaseInfoBeforeVersionChange;
+
+ std::unique_ptr<SQLiteDatabase> m_sqliteDB;
+
+ HashMap<IDBResourceIdentifier, std::unique_ptr<SQLiteIDBTransaction>> m_transactions;
+ HashMap<IDBResourceIdentifier, SQLiteIDBCursor*> m_cursors;
+
+ String m_absoluteDatabaseDirectory;
+
+ RefPtr<JSC::VM> m_vm;
+ JSC::Strong<JSC::JSGlobalObject> 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 <sqlite3.h>
+#include <wtf/text/StringBuilder.h>
+
+namespace WebCore {
+namespace IDBServer {
+
+static const size_t prefetchLimit = 8;
+
+std::unique_ptr<SQLiteIDBCursor> SQLiteIDBCursor::maybeCreate(SQLiteIDBTransaction& transaction, const IDBCursorInfo& info)
+{
+ auto cursor = std::make_unique<SQLiteIDBCursor>(transaction, info);
+
+ if (!cursor->establishStatement())
+ return nullptr;
+
+ if (!cursor->advance(1))
+ return nullptr;
+
+ return cursor;
+}
+
+std::unique_ptr<SQLiteIDBCursor> SQLiteIDBCursor::maybeCreateBackingStoreCursor(SQLiteIDBTransaction& transaction, const uint64_t objectStoreID, const uint64_t indexID, const IDBKeyRangeData& range)
+{
+ auto cursor = std::make_unique<SQLiteIDBCursor>(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<SQLiteStatement>(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<SharedBuffer> 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<uint8_t> 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<String> 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<IDBValue>(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<IDBValue>(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 <wtf/Deque.h>
+#include <wtf/Noncopyable.h>
+
+namespace WebCore {
+
+class IDBCursorInfo;
+class IDBGetResult;
+
+namespace IDBServer {
+
+class SQLiteIDBTransaction;
+
+class SQLiteIDBCursor {
+ WTF_MAKE_NONCOPYABLE(SQLiteIDBCursor);
+public:
+ static std::unique_ptr<SQLiteIDBCursor> maybeCreate(SQLiteIDBTransaction&, const IDBCursorInfo&);
+ static std::unique_ptr<SQLiteIDBCursor> 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<SQLiteCursorRecord> m_fetchedRecords;
+ IDBKeyData m_currentKeyForUniqueness;
+
+ std::unique_ptr<SQLiteStatement> 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<SQLiteTransaction>(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<SQLiteIDBCursor> 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 <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+#include <wtf/Noncopyable.h>
+
+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<SQLiteIDBCursor> 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<SQLiteTransaction> m_sqliteTransaction;
+ HashMap<IDBResourceIdentifier, std::unique_ptr<SQLiteIDBCursor>> m_cursors;
+ HashSet<SQLiteIDBCursor*> m_backingStoreCursors;
+ Vector<std::pair<String, String>> m_blobTemporaryAndStoredFilenames;
+ HashSet<String> 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> 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<uint64_t>&& 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 <wtf/HashSet.h>
+#include <wtf/Ref.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class IDBDatabaseInfo;
+
+namespace IDBServer {
+
+class ServerOpenDBRequest : public RefCounted<ServerOpenDBRequest> {
+public:
+ static Ref<ServerOpenDBRequest> 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<uint64_t>&& 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<uint64_t> 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 <heap/HeapInlines.h>
+#include <heap/StrongInlines.h>
+#include <runtime/AuxiliaryBarrierInlines.h>
+#include <runtime/StructureInlines.h>
+#include <wtf/MainThread.h>
+#include <wtf/NeverDestroyed.h>
+
+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<UniqueIDBDatabaseConnection> 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<IDBDatabaseInfo>(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<UniqueIDBDatabase> 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<uint64_t> 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<UniqueIDBDatabaseConnection>&& 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<IDBDatabaseInfo>(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<Strong<JSGlobalObject>> 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<void ()>([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<IDBDatabaseInfo>(*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<UniqueIDBDatabaseConnection> 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<RefPtr<UniqueIDBDatabaseTransaction>> 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<RefPtr<UniqueIDBDatabaseTransaction>> 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<UniqueIDBDatabaseConnection> 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<UniqueIDBDatabaseTransaction>&& 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<UniqueIDBDatabase> 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<UniqueIDBDatabase> protectedThis(this);
+ RefPtr<UniqueIDBDatabaseTransaction> 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<typename T> bool scopesOverlap(const T& aScopes, const Vector<uint64_t>& bScopes)
+{
+ for (auto scope : bScopes) {
+ if (aScopes.contains(scope))
+ return true;
+ }
+
+ return false;
+}
+
+RefPtr<UniqueIDBDatabaseTransaction> 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<RefPtr<UniqueIDBDatabaseTransaction>> deferredTransactions;
+ RefPtr<UniqueIDBDatabaseTransaction> currentTransaction;
+
+ HashSet<uint64_t> 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<UniqueIDBDatabaseTransaction>&& 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<IDBResourceIdentifier> 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<uint64_t> 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<RefPtr<UniqueIDBDatabaseConnection>> 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 <wtf/CrossThreadQueue.h>
+#include <wtf/CrossThreadTask.h>
+#include <wtf/Deque.h>
+#include <wtf/HashCountedSet.h>
+#include <wtf/HashSet.h>
+#include <wtf/ListHashSet.h>
+#include <wtf/Ref.h>
+#include <wtf/ThreadSafeRefCounted.h>
+
+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<void(const IDBError&)> ErrorCallback;
+typedef std::function<void(const IDBError&, const IDBKeyData&)> KeyDataCallback;
+typedef std::function<void(const IDBError&, const IDBGetResult&)> GetResultCallback;
+typedef std::function<void(const IDBError&, const IDBGetAllResult&)> GetAllResultsCallback;
+typedef std::function<void(const IDBError&, uint64_t)> CountCallback;
+
+class UniqueIDBDatabase : public ThreadSafeRefCounted<UniqueIDBDatabase> {
+public:
+ static Ref<UniqueIDBDatabase> 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<UniqueIDBDatabaseTransaction>&&);
+
+ 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<UniqueIDBDatabaseConnection>&&);
+ bool hasAnyOpenConnections() const;
+ bool allConnectionsAreClosedOrClosing() const;
+
+ void startVersionChangeTransaction();
+ void maybeNotifyConnectionsOfVersionChange();
+ void notifyCurrentRequestConnectionClosedOrFiredVersionChangeEvent(uint64_t connectionIdentifier);
+ bool isVersionChangeInProgress();
+
+ void activateTransactionInBackingStore(UniqueIDBDatabaseTransaction&);
+ void transactionCompleted(RefPtr<UniqueIDBDatabaseTransaction>&&);
+
+ 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<UniqueIDBDatabaseTransaction> 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<RefPtr<ServerOpenDBRequest>> m_pendingOpenDBRequests;
+ RefPtr<ServerOpenDBRequest> m_currentOpenDBRequest;
+
+ ListHashSet<RefPtr<UniqueIDBDatabaseConnection>> m_openDatabaseConnections;
+ HashSet<RefPtr<UniqueIDBDatabaseConnection>> m_clientClosePendingDatabaseConnections;
+ HashSet<RefPtr<UniqueIDBDatabaseConnection>> m_serverClosePendingDatabaseConnections;
+
+ RefPtr<UniqueIDBDatabaseConnection> m_versionChangeDatabaseConnection;
+ RefPtr<UniqueIDBDatabaseTransaction> m_versionChangeTransaction;
+
+ bool m_isOpeningBackingStore { false };
+ IDBError m_backingStoreOpenError;
+ std::unique_ptr<IDBBackingStore> m_backingStore;
+ std::unique_ptr<IDBDatabaseInfo> m_databaseInfo;
+ std::unique_ptr<IDBDatabaseInfo> m_mostRecentDeletedDatabaseInfo;
+
+ bool m_backingStoreSupportsSimultaneousTransactions { false };
+ bool m_backingStoreIsEphemeral { false };
+
+ HashMap<uint64_t, ErrorCallback> m_errorCallbacks;
+ HashMap<uint64_t, KeyDataCallback> m_keyDataCallbacks;
+ HashMap<uint64_t, GetResultCallback> m_getResultCallbacks;
+ HashMap<uint64_t, GetAllResultsCallback> m_getAllResultsCallbacks;
+ HashMap<uint64_t, CountCallback> m_countCallbacks;
+
+ Timer m_operationAndTransactionTimer;
+
+ Deque<RefPtr<UniqueIDBDatabaseTransaction>> m_pendingTransactions;
+ HashMap<IDBResourceIdentifier, RefPtr<UniqueIDBDatabaseTransaction>> m_inProgressTransactions;
+ HashMap<IDBResourceIdentifier, RefPtr<UniqueIDBDatabaseTransaction>> 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<uint64_t> m_objectStoreTransactionCounts;
+ HashSet<uint64_t> m_objectStoreWriteTransactions;
+
+ bool m_deleteBackingStoreInProgress { false };
+
+ CrossThreadQueue<Function<void ()>> m_databaseQueue;
+ CrossThreadQueue<Function<void ()>> m_databaseReplyQueue;
+ std::atomic<uint64_t> m_queuedTaskCount { 0 };
+
+ bool m_hardClosedForUserDelete { false };
+ RefPtr<UniqueIDBDatabase> m_hardCloseProtector;
+
+ HashMap<IDBResourceIdentifier, RefPtr<UniqueIDBDatabase>> 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> 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<UniqueIDBDatabaseConnection> 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<UniqueIDBDatabaseTransaction> 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<UniqueIDBDatabaseTransaction> 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 <wtf/HashMap.h>
+#include <wtf/Ref.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class IDBError;
+class IDBResultData;
+
+namespace IDBServer {
+
+class IDBConnectionToClient;
+class ServerOpenDBRequest;
+class UniqueIDBDatabase;
+class UniqueIDBDatabaseTransaction;
+
+class UniqueIDBDatabaseConnection : public RefCounted<UniqueIDBDatabaseConnection> {
+public:
+ static Ref<UniqueIDBDatabaseConnection> 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<IDBResourceIdentifier, RefPtr<UniqueIDBDatabaseTransaction>> 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> 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<IDBDatabaseInfo>(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<UniqueIDBDatabaseTransaction> 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<UniqueIDBDatabaseTransaction> 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<UniqueIDBDatabaseTransaction> 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<UniqueIDBDatabaseTransaction> 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<UniqueIDBDatabaseTransaction> 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<UniqueIDBDatabaseTransaction> 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<UniqueIDBDatabaseTransaction> 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<UniqueIDBDatabaseTransaction> 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<UniqueIDBDatabaseTransaction> 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<UniqueIDBDatabaseTransaction> 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<UniqueIDBDatabaseTransaction> 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<UniqueIDBDatabaseTransaction> 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<UniqueIDBDatabaseTransaction> 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<UniqueIDBDatabaseTransaction> 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<UniqueIDBDatabaseTransaction> 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<UniqueIDBDatabaseTransaction> 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<uint64_t>& 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 <wtf/Ref.h>
+#include <wtf/RefCounted.h>
+
+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<UniqueIDBDatabaseTransaction> {
+public:
+ static Ref<UniqueIDBDatabaseTransaction> 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<uint64_t>& objectStoreIdentifiers();
+
+private:
+ UniqueIDBDatabaseTransaction(UniqueIDBDatabaseConnection&, const IDBTransactionInfo&);
+
+ Ref<UniqueIDBDatabaseConnection> m_databaseConnection;
+ IDBTransactionInfo m_transactionInfo;
+
+ std::unique_ptr<IDBDatabaseInfo> m_originalDatabaseInfo;
+
+ Vector<uint64_t> 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("<Crsr: %s Idx %" PRIu64 ", OS %" PRIu64 ", tx %s>", m_cursorIdentifier.loggingString().utf8().data(), m_sourceIdentifier, m_objectStoreIdentifier, m_transactionIdentifier.loggingString().utf8().data());
+
+ return String::format("<Crsr: %s OS %" PRIu64 ", tx %s>", 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<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> 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<class Encoder>
+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<class Decoder>
+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<IDBValue> value;
+
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static bool decode(Decoder&, IDBCursorRecord&);
+};
+
+template<class Encoder>
+void IDBCursorRecord::encode(Encoder& encoder) const
+{
+ encoder << key << primaryKey << value;
+}
+
+template<class Decoder>
+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 <wtf/text/StringBuilder.h>
+
+#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<IDBKeyPath>&& 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<IDBDatabaseInfo*>(this)->getInfoForExistingObjectStore(objectStoreIdentifier);
+}
+
+IDBObjectStoreInfo* IDBDatabaseInfo::infoForExistingObjectStore(uint64_t objectStoreIdentifier)
+{
+ return getInfoForExistingObjectStore(objectStoreIdentifier);
+}
+
+const IDBObjectStoreInfo* IDBDatabaseInfo::infoForExistingObjectStore(const String& name) const
+{
+ return const_cast<IDBDatabaseInfo*>(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<String> IDBDatabaseInfo::objectStoreNames() const
+{
+ Vector<String> 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 <wtf/HashMap.h>
+
+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<IDBKeyPath>&&, 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<String> objectStoreNames() const;
+
+ void deleteObjectStore(const String& objectStoreName);
+ void deleteObjectStore(uint64_t objectStoreIdentifier);
+
+ WEBCORE_EXPORT IDBDatabaseInfo();
+
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> 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<uint64_t, IDBObjectStoreInfo> m_objectStoreMap;
+
+};
+
+template<class Encoder>
+void IDBDatabaseInfo::encode(Encoder& encoder) const
+{
+ encoder << m_name << m_version << m_maxObjectStoreID << m_objectStoreMap;
+}
+
+template<class Decoder>
+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<DOMError> 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 <wtf/text/WTFString.h>
+
+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<DOMError> 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<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static bool decode(Decoder&, IDBError&);
+
+private:
+ ExceptionCode m_code { IDBDatabaseException::NoError };
+ String m_message;
+};
+
+template<class Encoder>
+void IDBError::encode(Encoder& encoder) const
+{
+ encoder << m_code << m_message;
+}
+
+template<class Decoder>
+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("<GetAllRecords: Idx %" PRIu64 ", OS %" PRIu64 ", %s, range %s>", indexIdentifier, objectStoreIdentifier, getAllType == IndexedDB::GetAllType::Keys ? "Keys" : "Values", keyRangeData.loggingString().utf8().data());
+ return String::format("<GetAllRecords: OS %" PRIu64 ", %s, range %s>", 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 <wtf/Optional.h>
+
+namespace WebCore {
+
+namespace IndexedDB {
+enum class DataSource;
+enum class GetAllType;
+}
+
+struct IDBGetAllRecordsData {
+ IDBKeyRangeData keyRangeData;
+ IndexedDB::GetAllType getAllType;
+ std::optional<uint32_t> count;
+ uint64_t objectStoreIdentifier;
+ uint64_t indexIdentifier;
+
+ IDBGetAllRecordsData isolatedCopy() const;
+
+#if !LOG_DISABLED
+ String loggingString() const;
+#endif
+
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static bool decode(Decoder&, IDBGetAllRecordsData&);
+};
+
+template<class Encoder>
+void IDBGetAllRecordsData::encode(Encoder& encoder) const
+{
+ encoder << keyRangeData;
+ encoder.encodeEnum(getAllType);
+ encoder << count << objectStoreIdentifier << indexIdentifier;
+}
+
+template<class Decoder>
+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("<GetRecord: %s %s>", type == IDBGetRecordDataType::KeyOnly ? "KeyOnly" : "Key+Value", keyRangeData.loggingString().utf8().data());
+}
+#endif
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreTransactionLevelDB.h b/Source/WebCore/Modules/indexeddb/shared/IDBGetRecordData.h
index c7f7bc494..e82ab54e8 100644
--- a/Source/WebCore/Modules/indexeddb/leveldb/IDBBackingStoreTransactionLevelDB.h
+++ b/Source/WebCore/Modules/indexeddb/shared/IDBGetRecordData.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * 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
@@ -23,49 +23,52 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef IDBBackingStoreTransactionLevelDB_h
-#define IDBBackingStoreTransactionLevelDB_h
-
-#include "LevelDBTransaction.h"
+#pragma once
#if ENABLE(INDEXED_DATABASE)
-#if USE(LEVELDB)
+
+#include "IDBKeyRangeData.h"
namespace WebCore {
-class IDBBackingStoreLevelDB;
+enum class IDBGetRecordDataType {
+ KeyOnly,
+ KeyAndValue,
+};
+
+struct IDBGetRecordData {
+ IDBKeyRangeData keyRangeData;
+ IDBGetRecordDataType type;
-class IDBBackingStoreTransactionLevelDB : public RefCounted<IDBBackingStoreTransactionLevelDB> {
-public:
- static PassRefPtr<IDBBackingStoreTransactionLevelDB> create(int64_t transactionID, IDBBackingStoreLevelDB* backingStore)
- {
- return adoptRef(new IDBBackingStoreTransactionLevelDB(transactionID, backingStore));
- }
+ IDBGetRecordData isolatedCopy() const;
- ~IDBBackingStoreTransactionLevelDB();
+#if !LOG_DISABLED
+ String loggingString() const;
+#endif
- void begin();
- bool commit();
- void rollback();
- void resetTransaction();
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static bool decode(Decoder&, IDBGetRecordData&);
+};
- static LevelDBTransaction* levelDBTransactionFrom(IDBBackingStoreTransactionLevelDB& transaction)
- {
- return static_cast<IDBBackingStoreTransactionLevelDB&>(transaction).m_transaction.get();
- }
+template<class Encoder>
+void IDBGetRecordData::encode(Encoder& encoder) const
+{
+ encoder << keyRangeData;
+ encoder.encodeEnum(type);
+}
- int64_t transactionID() { return m_transactionID; }
+template<class Decoder>
+bool IDBGetRecordData::decode(Decoder& decoder, IDBGetRecordData& getRecordData)
+{
+ if (!decoder.decode(getRecordData.keyRangeData))
+ return false;
-private:
- IDBBackingStoreTransactionLevelDB(int64_t transactionID, IDBBackingStoreLevelDB*);
+ if (!decoder.decodeEnum(getRecordData.type))
+ return false;
- int64_t m_transactionID;
- IDBBackingStoreLevelDB* m_backingStore;
- RefPtr<LevelDBTransaction> m_transaction;
-};
+ return true;
+}
} // namespace WebCore
-#endif // USE(LEVELDB)
#endif // ENABLE(INDEXED_DATABASE)
-#endif // IDBBackingStoreTransactionLevelDB_h
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("<Idx: %s (%" PRIu64 "), OS (%" PRIu64 ")>", 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 <wtf/text/WTFString.h>
+
+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<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> 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<class Encoder>
+void IDBIndexInfo::encode(Encoder& encoder) const
+{
+ encoder << m_identifier << m_objectStoreIdentifier << m_name << m_keyPath << m_unique << m_multiEntry;
+}
+
+template<class Decoder>
+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("<Itr8Crsr: key %s, primaryKey %s, count %u>", 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<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static bool decode(Decoder&, IDBIterateCursorData&);
+};
+
+template<class Encoder>
+void IDBIterateCursorData::encode(Encoder& encoder) const
+{
+ encoder << keyData << primaryKeyData << static_cast<uint64_t>(count);
+}
+
+template<class Decoder>
+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<unsigned>::max())
+ return false;
+
+ iteratorCursorData.count = static_cast<unsigned>(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 <wtf/text/StringBuilder.h>
+
+#if ENABLE(INDEXED_DATABASE)
+
+namespace WebCore {
+
+IDBObjectStoreInfo::IDBObjectStoreInfo()
+{
+}
+
+IDBObjectStoreInfo::IDBObjectStoreInfo(uint64_t identifier, const String& name, std::optional<IDBKeyPath>&& 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<String> IDBObjectStoreInfo::indexNames() const
+{
+ Vector<String> 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("<OS: %s (%" PRIu64 ")>", 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 <wtf/HashMap.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class IDBObjectStoreInfo {
+public:
+ WEBCORE_EXPORT IDBObjectStoreInfo();
+ IDBObjectStoreInfo(uint64_t identifier, const String& name, std::optional<IDBKeyPath>&&, bool autoIncrement);
+
+ uint64_t identifier() const { return m_identifier; }
+ const String& name() const { return m_name; }
+ const std::optional<IDBKeyPath>& 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<String> indexNames() const;
+ const HashMap<uint64_t, IDBIndexInfo>& indexMap() const { return m_indexMap; }
+
+ void deleteIndex(const String& indexName);
+ void deleteIndex(uint64_t indexIdentifier);
+
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> 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<IDBKeyPath> m_keyPath;
+ bool m_autoIncrement { false };
+ uint64_t m_maxIndexID { 0 };
+
+ HashMap<uint64_t, IDBIndexInfo> m_indexMap;
+};
+
+template<class Encoder>
+void IDBObjectStoreInfo::encode(Encoder& encoder) const
+{
+ encoder << m_identifier << m_name << m_keyPath << m_autoIncrement << m_maxIndexID << m_indexMap;
+}
+
+template<class Decoder>
+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<IDBResourceIdentifier>(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<IDBResourceIdentifier>(operation.identifier()))
+ , m_transactionIdentifier(std::make_unique<IDBResourceIdentifier>(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<IDBResourceIdentifier>(*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<IDBResourceIdentifier>(*other.m_requestIdentifier);
+ if (other.m_transactionIdentifier)
+ m_transactionIdentifier = std::make_unique<IDBResourceIdentifier>(*other.m_transactionIdentifier);
+ if (other.m_cursorIdentifier)
+ m_cursorIdentifier = std::make_unique<IDBResourceIdentifier>(*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<IDBResourceIdentifier>(*source.m_requestIdentifier);
+ if (source.m_transactionIdentifier)
+ destination.m_transactionIdentifier = std::make_unique<IDBResourceIdentifier>(*source.m_transactionIdentifier);
+ if (source.m_cursorIdentifier)
+ destination.m_cursorIdentifier = std::make_unique<IDBResourceIdentifier>(*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<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static bool decode(Decoder&, IDBRequestData&);
+
+private:
+ static void isolatedCopy(const IDBRequestData& source, IDBRequestData& destination);
+
+ uint64_t m_serverConnectionIdentifier { 0 };
+ std::unique_ptr<IDBResourceIdentifier> m_requestIdentifier;
+ std::unique_ptr<IDBResourceIdentifier> m_transactionIdentifier;
+ std::unique_ptr<IDBResourceIdentifier> 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<class Encoder>
+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<class Decoder>
+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<IDBResourceIdentifier> object = std::make_unique<IDBResourceIdentifier>();
+ if (!decoder.decode(*object))
+ return false;
+ request.m_requestIdentifier = WTFMove(object);
+ }
+
+ if (!decoder.decode(hasObject))
+ return false;
+ if (hasObject) {
+ std::unique_ptr<IDBResourceIdentifier> object = std::make_unique<IDBResourceIdentifier>();
+ if (!decoder.decode(*object))
+ return false;
+ request.m_transactionIdentifier = WTFMove(object);
+ }
+
+ if (!decoder.decode(hasObject))
+ return false;
+ if (hasObject) {
+ std::unique_ptr<IDBResourceIdentifier> object = std::make_unique<IDBResourceIdentifier>();
+ 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 <wtf/MainThread.h>
+
+namespace WebCore {
+
+static uint64_t nextClientResourceNumber()
+{
+ static std::atomic<uint64_t> 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<uint64_t>::max(), std::numeric_limits<uint64_t>::max());
+}
+
+bool IDBResourceIdentifier::isHashTableDeletedValue() const
+{
+ return m_idbConnectionIdentifier == std::numeric_limits<uint64_t>::max()
+ && m_resourceNumber == std::numeric_limits<uint64_t>::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 <wtf/text/StringHash.h>
+
+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<sizeof(hashCodes)>(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<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> 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<IDBResourceIdentifier> {
+ 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<class Encoder>
+void IDBResourceIdentifier::encode(Encoder& encoder) const
+{
+ encoder << m_idbConnectionIdentifier << m_resourceNumber;
+}
+
+template<class Decoder>
+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::IDBResourceIdentifier> : WebCore::IDBResourceIdentifierHashTraits { };
+template<> struct DefaultHash<WebCore::IDBResourceIdentifier> {
+ 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<IDBDatabaseInfo>(*other.m_databaseInfo);
+ if (other.m_transactionInfo)
+ m_transactionInfo = std::make_unique<IDBTransactionInfo>(*other.m_transactionInfo);
+ if (other.m_resultKey)
+ m_resultKey = std::make_unique<IDBKeyData>(*other.m_resultKey);
+ if (other.m_getResult)
+ m_getResult = std::make_unique<IDBGetResult>(*other.m_getResult);
+ if (other.m_getAllResult)
+ m_getAllResult = std::make_unique<IDBGetAllResult>(*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<IDBDatabaseInfo>(*source.m_databaseInfo, IDBDatabaseInfo::IsolatedCopy);
+ if (source.m_transactionInfo)
+ destination.m_transactionInfo = std::make_unique<IDBTransactionInfo>(*source.m_transactionInfo, IDBTransactionInfo::IsolatedCopy);
+ if (source.m_resultKey)
+ destination.m_resultKey = std::make_unique<IDBKeyData>(*source.m_resultKey, IDBKeyData::IsolatedCopy);
+ if (source.m_getResult)
+ destination.m_getResult = std::make_unique<IDBGetResult>(*source.m_getResult, IDBGetResult::IsolatedCopy);
+ if (source.m_getAllResult)
+ destination.m_getAllResult = std::make_unique<IDBGetAllResult>(*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<IDBDatabaseInfo>(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<IDBDatabaseInfo>(transaction.databaseConnection().database().info());
+ result.m_transactionInfo = std::make_unique<IDBTransactionInfo>(transaction.info());
+ return result;
+}
+
+IDBResultData IDBResultData::deleteDatabaseSuccess(const IDBResourceIdentifier& requestIdentifier, const IDBDatabaseInfo& info)
+{
+ IDBResultData result {IDBResultType::DeleteDatabaseSuccess, requestIdentifier };
+ result.m_databaseInfo = std::make_unique<IDBDatabaseInfo>(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<IDBKeyData>(resultKey);
+ return result;
+}
+
+IDBResultData IDBResultData::getRecordSuccess(const IDBResourceIdentifier& requestIdentifier, const IDBGetResult& getResult)
+{
+ IDBResultData result { IDBResultType::GetRecordSuccess, requestIdentifier };
+ result.m_getResult = std::make_unique<IDBGetResult>(getResult);
+ return result;
+}
+
+IDBResultData IDBResultData::getAllRecordsSuccess(const IDBResourceIdentifier& requestIdentifier, const IDBGetAllResult& getAllResult)
+{
+ IDBResultData result { IDBResultType::GetAllRecordsSuccess, requestIdentifier };
+ result.m_getAllResult = std::make_unique<IDBGetAllResult>(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<IDBGetResult>(getResult);
+ return result;
+}
+
+IDBResultData IDBResultData::iterateCursorSuccess(const IDBResourceIdentifier& requestIdentifier, const IDBGetResult& getResult)
+{
+ IDBResultData result { IDBResultType::IterateCursorSuccess, requestIdentifier };
+ result.m_getResult = std::make_unique<IDBGetResult>(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<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> 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<IDBDatabaseInfo> m_databaseInfo;
+ std::unique_ptr<IDBTransactionInfo> m_transactionInfo;
+ std::unique_ptr<IDBKeyData> m_resultKey;
+ std::unique_ptr<IDBGetResult> m_getResult;
+ std::unique_ptr<IDBGetAllResult> m_getAllResult;
+ uint64_t m_resultInteger { 0 };
+};
+
+template<class Encoder>
+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<class Decoder> 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<IDBDatabaseInfo>();
+ if (!decoder.decode(*object))
+ return false;
+ result.m_databaseInfo = WTFMove(object);
+ }
+
+ if (!decoder.decode(hasObject))
+ return false;
+ if (hasObject) {
+ auto object = std::make_unique<IDBTransactionInfo>();
+ if (!decoder.decode(*object))
+ return false;
+ result.m_transactionInfo = WTFMove(object);
+ }
+
+ if (!decoder.decode(hasObject))
+ return false;
+ if (hasObject) {
+ auto object = std::make_unique<IDBKeyData>();
+ if (!decoder.decode(*object))
+ return false;
+ result.m_resultKey = WTFMove(object);
+ }
+
+ if (!decoder.decode(hasObject))
+ return false;
+ if (hasObject) {
+ auto object = std::make_unique<IDBGetResult>();
+ if (!decoder.decode(*object))
+ return false;
+ result.m_getResult = WTFMove(object);
+ }
+
+ if (!decoder.decode(hasObject))
+ return false;
+ if (hasObject) {
+ auto object = std::make_unique<IDBGetAllResult>();
+ 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<String>& 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<IDBDatabaseInfo>(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<IDBDatabaseInfo>(*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<IDBDatabaseInfo>(*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 <wtf/Vector.h>
+
+namespace WebCore {
+
+namespace IDBClient {
+class IDBConnectionProxy;
+}
+
+namespace IDBServer {
+class IDBConnectionToClient;
+}
+
+class IDBTransactionInfo {
+public:
+ static IDBTransactionInfo clientTransaction(const IDBClient::IDBConnectionProxy&, const Vector<String>& 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<String>& objectStores() const { return m_objectStores; }
+
+ IDBDatabaseInfo* originalDatabaseInfo() const { return m_originalDatabaseInfo.get(); }
+
+ WEBCORE_EXPORT IDBTransactionInfo();
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> 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<String> m_objectStores;
+ std::unique_ptr<IDBDatabaseInfo> m_originalDatabaseInfo;
+};
+
+template<class Encoder>
+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<class Decoder>
+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<IDBDatabaseInfo> object = std::make_unique<IDBDatabaseInfo>();
+ 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 <wtf/RunLoop.h>
+
+namespace WebCore {
+
+Ref<InProcessIDBServer> InProcessIDBServer::create()
+{
+ Ref<InProcessIDBServer> server = adoptRef(*new InProcessIDBServer);
+ server->m_server->registerConnection(server->connectionToClient());
+ return server;
+}
+
+Ref<InProcessIDBServer> InProcessIDBServer::create(const String& databaseDirectoryPath)
+{
+ Ref<InProcessIDBServer> 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<String>& 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 <wtf/HashMap.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+namespace IDBClient {
+class IDBConnectionToServer;
+}
+
+namespace IDBServer {
+class IDBServer;
+}
+
+class InProcessIDBServer final : public IDBClient::IDBConnectionToServerDelegate, public IDBServer::IDBConnectionToClientDelegate, public RefCounted<InProcessIDBServer>, public IDBServer::IDBBackingStoreTemporaryFileHandler {
+public:
+ WEBCORE_EXPORT static Ref<InProcessIDBServer> create();
+ WEBCORE_EXPORT static Ref<InProcessIDBServer> 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<String>& databaseNames) final;
+
+ void ref() override { RefCounted<InProcessIDBServer>::ref(); }
+ void deref() override { RefCounted<InProcessIDBServer>::deref(); }
+
+ void prepareForAccessToTemporaryFile(const String&) override { }
+ void accessToTemporaryFileComplete(const String& path) override;
+
+private:
+ InProcessIDBServer();
+ InProcessIDBServer(const String& databaseDirectoryPath);
+
+ Ref<IDBServer::IDBServer> m_server;
+ RefPtr<IDBClient::IDBConnectionToServer> m_connectionToServer;
+ RefPtr<IDBServer::IDBConnectionToClient> m_connectionToClient;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/IDBDatabaseMetadata.cpp b/Source/WebCore/Modules/indexeddb/shared/IndexKey.cpp
index 0d14288ce..a40f90120 100644
--- a/Source/WebCore/Modules/indexeddb/IDBDatabaseMetadata.cpp
+++ b/Source/WebCore/Modules/indexeddb/shared/IndexKey.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Apple 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
@@ -24,54 +24,64 @@
*/
#include "config.h"
-#include "IDBDatabaseMetadata.h"
+#include "IndexKey.h"
#if ENABLE(INDEXED_DATABASE)
namespace WebCore {
-IDBDatabaseMetadata IDBDatabaseMetadata::isolatedCopy() const
+IndexKey::IndexKey()
{
- IDBDatabaseMetadata result;
- result.id = id;
- result.version = version;
- result.maxObjectStoreId = maxObjectStoreId;
+}
- result.name = name.isolatedCopy();
+IndexKey::IndexKey(Vector<IDBKeyData>&& keys)
+{
+ m_keys.swap(keys);
+}
- for (auto i = objectStores.begin(), end = objectStores.end(); i != end; ++i)
- result.objectStores.set(i->key, i->value.isolatedCopy());
+IndexKey IndexKey::isolatedCopy() const
+{
+ Vector<IDBKeyData> keys;
+ keys.reserveInitialCapacity(m_keys.size());
+ for (auto& key : m_keys)
+ keys.uncheckedAppend(key.isolatedCopy());
- return result;
+ return { WTFMove(keys) };
}
-IDBObjectStoreMetadata IDBObjectStoreMetadata::isolatedCopy() const
+IDBKeyData IndexKey::asOneKey() const
{
- IDBObjectStoreMetadata result;
- result.id = id;
- result.autoIncrement = autoIncrement;
- result.maxIndexId = maxIndexId;
+ if (m_keys.isEmpty())
+ return { };
- 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());
+ if (m_keys.size() == 1)
+ return m_keys[0];
+ IDBKeyData result;
+ result.setArrayValue(m_keys);
return result;
}
-IDBIndexMetadata IDBIndexMetadata::isolatedCopy() const
+Vector<IDBKeyData> IndexKey::multiEntry() const
{
- IDBIndexMetadata result;
- result.id = id;
- result.unique = unique;
- result.multiEntry = multiEntry;
-
- result.name = name.isolatedCopy();
- result.keyPath = keyPath.isolatedCopy();
-
- return result;
+ Vector<IDBKeyData> 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
diff --git a/Source/WebCore/Modules/indexeddb/IDBOperation.h b/Source/WebCore/Modules/indexeddb/shared/IndexKey.h
index d88d2ce21..ad80e86f1 100644
--- a/Source/WebCore/Modules/indexeddb/IDBOperation.h
+++ b/Source/WebCore/Modules/indexeddb/shared/IndexKey.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Apple 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
@@ -23,28 +23,32 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef IDBOperation_h
-#define IDBOperation_h
-
-#include <functional>
+#pragma once
#if ENABLE(INDEXED_DATABASE)
+#include "IDBKeyData.h"
+#include <wtf/HashMap.h>
+#include <wtf/Vector.h>
+
namespace WebCore {
-class IDBOperation : public RefCounted<IDBOperation> {
+class IndexKey {
public:
- virtual ~IDBOperation() { }
- virtual void perform(std::function<void()> completionCallback) = 0;
-};
+ IndexKey();
+ IndexKey(Vector<IDBKeyData>&&);
-class IDBSynchronousOperation : public RefCounted<IDBSynchronousOperation> {
-public:
- virtual ~IDBSynchronousOperation() { }
- virtual void perform() = 0;
+ IndexKey isolatedCopy() const;
+
+ IDBKeyData asOneKey() const;
+ Vector<IDBKeyData> multiEntry() const;
+
+ bool isNull() const { return m_keys.isEmpty(); }
+
+private:
+ Vector<IDBKeyData> m_keys;
};
} // namespace WebCore
#endif // ENABLE(INDEXED_DATABASE)
-#endif // IDBOperation_h
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 <runtime/JSCJSValueInlines.h>
+
+namespace WebCore {
+
+const AtomicString& MediaControlsHost::automaticKeyword()
+{
+ static NeverDestroyed<const AtomicString> automatic("automatic", AtomicString::ConstructFromLiteral);
+ return automatic;
+}
+
+const AtomicString& MediaControlsHost::forcedOnlyKeyword()
+{
+ static NeverDestroyed<const AtomicString> forcedOn("forced-only", AtomicString::ConstructFromLiteral);
+ return forcedOn;
+}
+
+const AtomicString& MediaControlsHost::alwaysOnKeyword()
+{
+ static NeverDestroyed<const AtomicString> alwaysOn("always-on", AtomicString::ConstructFromLiteral);
+ return alwaysOn;
+}
+
+const AtomicString& MediaControlsHost::manualKeyword()
+{
+ static NeverDestroyed<const AtomicString> alwaysOn("manual", AtomicString::ConstructFromLiteral);
+ return alwaysOn;
+}
+
+
+Ref<MediaControlsHost> MediaControlsHost::create(HTMLMediaElement* mediaElement)
+{
+ return adoptRef(*new MediaControlsHost(mediaElement));
+}
+
+MediaControlsHost::MediaControlsHost(HTMLMediaElement* mediaElement)
+ : m_mediaElement(mediaElement)
+{
+ ASSERT(mediaElement);
+}
+
+MediaControlsHost::~MediaControlsHost()
+{
+}
+
+Vector<RefPtr<TextTrack>> MediaControlsHost::sortedTrackListForMenu(TextTrackList& trackList)
+{
+ Page* page = m_mediaElement->document().page();
+ if (!page)
+ return { };
+
+ return page->group().captionPreferences().sortedTrackListForMenu(&trackList);
+}
+
+Vector<RefPtr<AudioTrack>> 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<TextOrAudioTrack>& 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 <bindings/ScriptObject.h>
+#include <wtf/RefCounted.h>
+#include <wtf/Variant.h>
+#include <wtf/Vector.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class AudioTrack;
+class AudioTrackList;
+class Element;
+class HTMLMediaElement;
+class MediaControlTextTrackContainerElement;
+class TextTrack;
+class TextTrackList;
+
+class MediaControlsHost : public RefCounted<MediaControlsHost> {
+public:
+ static Ref<MediaControlsHost> create(HTMLMediaElement*);
+ ~MediaControlsHost();
+
+ static const AtomicString& automaticKeyword();
+ static const AtomicString& forcedOnlyKeyword();
+ static const AtomicString& alwaysOnKeyword();
+ static const AtomicString& manualKeyword();
+
+ Vector<RefPtr<TextTrack>> sortedTrackListForMenu(TextTrackList&);
+ Vector<RefPtr<AudioTrack>> sortedTrackListForMenu(AudioTrackList&);
+
+ using TextOrAudioTrack = WTF::Variant<RefPtr<TextTrack>, RefPtr<AudioTrack>>;
+ String displayNameForTrack(const std::optional<TextOrAudioTrack>&);
+
+ 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<MediaControlTextTrackContainerElement> 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<TextTrack> sortedTrackListForMenu(TextTrackList trackList);
+ sequence<AudioTrack> 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 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 190 44">
+
+ <!--
+ NOTE: THIS FILE IS NOT PART OF THE BUILD PROCESS. EDITS
+ HERE ARE NOT AUTOMATICALLY COPIED TO THE CSS FILES.
+
+ This file holds the raw assets used in mediaControlsApple.css.
+ This is not part of the build process at the moment, but manually
+ copied into the CSS. However, editing from CSS is a burden, so
+ the idea is that you develop here and then paste into CSS.
+ -->
+
+ <!-- PLAY -->
+
+ <g transform="translate(0 0)">
+ <rect width="44" height="44" fill="rgb(255, 255, 200)"/>
+ <svg width="44" height="44">
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 44 44">
+ <path fill="rgba(0, 0, 0, 0.8)" d="M10,11 32,22 10,33z"/>
+ </svg>
+ </svg>
+ </g>
+
+ <!-- PAUSE -->
+
+ <g transform="translate(46 0)">
+ <rect width="44" height="44" fill="rgb(255, 255, 200)"/>
+ <svg width="44" height="44">
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 44 44">
+ <g fill="rgba(0, 0, 0, 0.8)">
+ <rect x="13" y="11" width="6" height="22"/>
+ <rect x="24" y="11" width="6" height="22"/>
+ </g>
+ </svg>
+ </svg>
+ </g>
+
+ <!-- FULLSCREEN -->
+
+ <g transform="translate(92 0)">
+ <rect width="44" height="44" fill="rgb(255, 255, 200)"/>
+ <svg width="44" height="44">
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 44 44">
+ <g stroke="rgba(0, 0, 0, 0.8)" fill="none">
+ <path d="M 14,20 v -6 h 6 M 14,14 l 6,6"/>
+ <path d="M 30,24 v 6 h -6 M 30,30 l -6,-6"/>
+ </g>
+ </svg>
+ </svg>
+ </g>
+
+</svg> \ 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,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 17" fill="rgba(255,255,255,0.45)"><path d="m 7.9131,2 0,-1.548 -2.586,2.155 0,-2.171 -2.582,2.208 2.582,2.175 0,-2.139 2.586,2.155 0,-1.276 c 3.138,0.129 5.491,2.681 5.543,5.838 l -1.031,0 0.016,0.215 1.015,0 c -0.06,3.19 -2.629,5.765 -5.819,5.833 l 0,-1.018 -0.214,0 0,1.021 c -3.21,-0.047 -5.801,-2.631 -5.862,-5.836 l 1.045,0 -0.016,-0.215 -1.045,0 c -0.052,-0.288 -0.318,-0.654 -0.766,-0.654 -0.538,0 -0.755,0.484 -0.755,0.75 0,4.146 3.331,7.506 7.476,7.506 4.146,0 7.506,-3.36 7.506,-7.506 0,-4.059 -3.066,-7.357 -7.093,-7.493"/><path d="m 5.1729,11.0518 c -0.38,0 -0.668,-0.129 -0.945,-0.366 -0.083,-0.071 -0.186,-0.134 -0.338,-0.134 -0.277,0 -0.511,0.238 -0.511,0.521 0,0.154 0.083,0.301 0.179,0.383 0.394,0.346 0.911,0.563 1.601,0.563 1.077,0 1.739,-0.451 1.739,-1.608 l 0,-0.013 c 0,-0.911 -0.641,-1.265 -1.296,-1.376 l 0.945,-0.919 c 0.193,-0.19 0.317,-0.337 0.317,-0.604 0,-0.294 -0.228,-0.477 -0.538,-0.477 l -2.354,0 c -0.248,0 -0.455,0.21 -0.455,0.464 0,0.253 0.207,0.463 0.455,0.463 l 1.485,0 -0.939,0.961 c -0.166,0.169 -0.228,0.295 -0.228,0.444 0,0.25 0.207,0.463 0.455,0.463 l 0.166,0 c 0.594,0 0.945,0.222 0.945,0.624 l 0,0.012 c 0,0.367 -0.282,0.599 -0.453,0.599"/><path d="m 10.354,9.5342 c 0,0.876 -0.379,1.525 -0.979,1.525 -0.599,0 -0.992,-0.655 -0.992,-1.539 l 0,-0.012 c 0,-0.884 0.388,-1.527 0.979,-1.527 0.592,0 0.992,0.663 0.992,1.539 l 0,0.014 z m -0.979,-2.512 c -1.197,0 -2.008,1.097 -2.008,2.498 l 0,0.014 c 0,1.401 0.792,2.484 1.995,2.484 1.205,0 2.01,-1.097 2.01,-2.498 l 0,-0.012 c 0,-1.402 -0.805,-2.486 -1.997,-2.486"/></svg>');
+ 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,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 15" fill="rgba(255,255,255,0.45)"><rect x="0" y="1" width="4" height="13"/><rect x="8" y="1" width="4" height="13"/></svg>');
+ 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,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 15" fill="rgba(255,255,255,0.45)"><path d="M 0,1 12,7.5 0,14 z"/></svg>');
+ 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,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 15" stroke="rgba(255,255,255,0.45)" fill="none" stroke-width="1.25" stroke-linecap="round"><defs><clipPath id="cut-hole"><rect x="3" y="0" width="3.5" height="15"/></clipPath></defs><path stroke="none" fill="rgba(255,255,255,0.45)" shape-rendering="crispEdges" d="M 0,10 0,6 3,6 3,10 z"/><path stroke="none" fill="rgba(255,255,255,0.45)" clip-path="url(#cut-hole)" shape-rendering="crispEdges" d="M 3.5,6 6.5,3 6.5,13 3.5,10 z"/><path d="M 9,5.5 C 10.75,7 10.75,9 9,10.5"/><path d="M 10,3.5 C 13,4.5 13,11.5 10,12.5" /><path d="M 11,1.5 C 15.67,3.5 15.67,12.5 11,14.5"/></svg>');
+}
+
+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,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 15" stroke="rgba(255,255,255,0.45)" fill="none" stroke-width="1.25" stroke-linecap="round"><defs><clipPath id="cut-hole"><rect x="3" y="0" width="3.5" height="15"/></clipPath></defs><path stroke="none" fill="rgba(255,255,255,0.45)" shape-rendering="crispEdges" d="M 0,10 0,6 3,6 3,10 z"/><path stroke="none" fill="rgba(255,255,255,0.45)" clip-path="url(#cut-hole)" shape-rendering="crispEdges" d="M 3.5,6 6.5,3 6.5,13 3.5,10 z"/></svg>');
+}
+
+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,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 15" stroke="rgba(255,255,255,0.45)" fill="none"><defs> <clipPath id="cut-hole"><rect x="0" y="1" width="16" height="10"/><rect x="0" y="11" width="9" height="1"/><rect x="11" y="11" width="5" height="1"/></clipPath></defs> <rect x="0.5" y="1.5" rx="1" ry="1" width="15" height="10" clip-path="url(#cut-hole)"/><path d="M 2,6.5 h 4"/><path d="M 7,6.5 h 7"/><path d="M 3,8.5 h 2"/><path d="M 6,8.5 h 3"/><path d="M 10,8.5 h 3"/><path d="M 8.5,11.5 L 8.5,13.75 L 11,11"/></svg>');
+ 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,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 300"><polygon fill="rgb(163, 163, 163)" points="252.301,4.477 126.667,194.104 43.358,108.3 6.868,161.408 132.515,290.814 297.732,49.926"/></svg>');
+}
+
+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,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 300"><polygon fill="rgba(255,255,255,0.45)" points="252.301,4.477 126.667,194.104 43.358,108.3 6.868,161.408 132.515,290.814 297.732,49.926"/></svg>');
+}
+
+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,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 12" fill="rgba(255,255,255,0.45)"><polygon points="6 8 1 8 1 1 14 1 14 6 15 6 15 0 0 0 0 9 6 9 6 8"/><rect x="7" y="7" width="9" height="5"/><polyline transform="translate(4, 4) rotate(180) translate(-4, -4)" points="5.33 2 5.33 3 3.67 3 5.67 5 5 5.67 3 3.67 3 5.33 2 5.33 2 2"/></svg>');
+ }
+
+video::-webkit-media-controls-panel .picture-in-picture-button.return-from-picture-in-picture {
+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 12" fill="rgba(255,255,255,0.45)"><polygon points="6 8 1 8 1 1 14 1 14 6 15 6 15 0 0 0 0 9 6 9 6 8"/><rect x="7" y="7" width="9" height="5"/><polyline points="5.33 2 5.33 3 3.67 3 5.67 5 5 5.67 3 3.67 3 5.33 2 5.33 2 2"/></svg>');
+ }
+
+video::-webkit-media-controls-fullscreen-button,
+audio::-webkit-media-controls-fullscreen-button {
+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 15" stroke="rgba(255,255,255,0.45)" fill="none"><path d="M 7,1.5 L 12.5,1.5 L 12.5,7"/><path d="M 0.5,8 L 0.5,13.5 L 6,13.5"/><path stroke-linecap="round" d="M 12.5,1.5 L 7.5,6.5"/><path stroke-linecap="round" d="M 0.5,13.5 L 5.5,8.5"/></svg>');
+ 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,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 15" stroke="rgba(255,255,255,0.45)" fill="none"><path d="M 7.5,1 L 7.5,6.5 L 13,6.5"/><path d="M 0,8.5 L 5.5,8.5 L 5.5,14"/><path stroke-linecap="round" d="M 7.5,6.5 L 12.5,1.5"/><path stroke-linecap="round" d="M 5.5,8.5 L 0.5,13.5"/></svg>');
+ 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,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 15" stroke="rgba(255,255,255,0.45)" fill="none" stroke-width="1.25" stroke-linecap="round"><defs><clipPath id="cut-hole"><rect x="3" y="0" width="3.5" height="15"/></clipPath></defs><path stroke="none" fill="rgba(255,255,255,0.45)" shape-rendering="crispEdges" d="M 0,10 0,6 3,6 3,10 z"/><path stroke="none" fill="rgba(255,255,255,0.45)" clip-path="url(#cut-hole)" shape-rendering="crispEdges" d="M 3.5,6 6.5,3 6.5,13 3.5,10 z"/><path d="M 9,5.5 C 10.75,7 10.75,9 9,10.5"/><path d="M 10,3.5 C 13,4.5 13,11.5 10,12.5" /><path d="M 11,1.5 C 15.67,3.5 15.67,12.5 11,14.5"/></svg>');
+}
+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,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 15" stroke="rgba(255,255,255,0.45)" fill="none" stroke-width="1.25" stroke-linecap="round"><defs><clipPath id="cut-hole"><rect x="3" y="0" width="3.5" height="15"/></clipPath></defs><path stroke="none" fill="rgba(255,255,255,0.45)" shape-rendering="crispEdges" d="M 0,10 0,6 3,6 3,10 z"/><path stroke="none" fill="rgba(255,255,255,0.45)" clip-path="url(#cut-hole)" shape-rendering="crispEdges" d="M 3.5,6 6.5,3 6.5,13 3.5,10 z"/><path d="M 9,5.5 C 10.75,7 10.75,9 9,10.5"/></svg>');
+}
+
+video:-webkit-full-screen::-webkit-media-controls-play-button {
+ position: absolute;
+
+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21 23" fill="rgba(255,255,255,0.45)"><path d="M 0,0 0,22 8,22 8,0 z"/><path d="M 13,0 13,22 21,22 21,0 z"/></svg>');
+
+ 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,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21 23" fill="rgba(255,255,255,0.45)"><path d="M 0,0 21,11.5 0,23 z"/></svg>');
+}
+
+video:-webkit-full-screen::-webkit-media-controls-seek-back-button {
+ position: absolute;
+
+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 15" fill="rgba(255,255,255,0.45)"><path d="M 24,0 12,7 24,15 z"/><path d="M 12,0 0,7 12,15 z"/></svg>');
+
+ 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,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 15" fill="rgba(255,255,255,0.45)"><path d="M 0,0 12,7 0,15 z"/><path d="M 12,0 24,7 12,15 z"/></svg>');
+
+ 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,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 15" stroke="white" fill="none" stroke-width="1.25" stroke-linecap="round"><defs><clipPath id="cut-hole"><rect x="3" y="0" width="3.5" height="15"/></clipPath></defs><path stroke="none" fill="white" shape-rendering="crispEdges" d="M 0,10 0,6 3,6 3,10 z"/><path stroke="none" fill="white" clip-path="url(#cut-hole)" shape-rendering="crispEdges" d="M 3.5,6 6.5,3 6.5,13 3.5,10 z"/><path d="M 9,5.5 C 10.75,7 10.75,9 9,10.5"/><path d="M 10,3.5 C 13,4.5 13,11.5 10,12.5" /><path d="M 11,1.5 C 15.67,3.5 15.67,12.5 11,14.5"/></svg>');
+}
+
+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,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 15" stroke="white" fill="none" stroke-width="1.25" stroke-linecap="round"><defs><clipPath id="cut-hole"><rect x="3" y="0" width="3.5" height="15"/></clipPath></defs><path stroke="none" fill="white" shape-rendering="crispEdges" d="M 0,10 0,6 3,6 3,10 z"/><path stroke="none" fill="white" clip-path="url(#cut-hole)" shape-rendering="crispEdges" d="M 3.5,6 6.5,3 6.5,13 3.5,10 z"/></svg>');
+}
+
+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,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 15" stroke="white" fill="none"><defs> <clipPath id="cut-hole"><rect x="0" y="1" width="16" height="10"/><rect x="0" y="11" width="9" height="1"/><rect x="11" y="11" width="5" height="1"/></clipPath></defs> <rect x="0.5" y="1.5" rx="1" ry="1" width="15" height="10" clip-path="url(#cut-hole)"/><path d="M 2,6.5 h 4"/><path d="M 7,6.5 h 7"/><path d="M 3,8.5 h 2"/><path d="M 6,8.5 h 3"/><path d="M 10,8.5 h 3"/><path d="M 8.5,11.5 L 8.5,13.75 L 11,11"/></svg>');
+}
+
+video::-webkit-media-controls-rewind-button:active,
+audio::-webkit-media-controls-rewind-button:active {
+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 17" fill="white"><path d="m 7.9131,2 0,-1.548 -2.586,2.155 0,-2.171 -2.582,2.208 2.582,2.175 0,-2.139 2.586,2.155 0,-1.276 c 3.138,0.129 5.491,2.681 5.543,5.838 l -1.031,0 0.016,0.215 1.015,0 c -0.06,3.19 -2.629,5.765 -5.819,5.833 l 0,-1.018 -0.214,0 0,1.021 c -3.21,-0.047 -5.801,-2.631 -5.862,-5.836 l 1.045,0 -0.016,-0.215 -1.045,0 c -0.052,-0.288 -0.318,-0.654 -0.766,-0.654 -0.538,0 -0.755,0.484 -0.755,0.75 0,4.146 3.331,7.506 7.476,7.506 4.146,0 7.506,-3.36 7.506,-7.506 0,-4.059 -3.066,-7.357 -7.093,-7.493"/><path d="m 5.1729,11.0518 c -0.38,0 -0.668,-0.129 -0.945,-0.366 -0.083,-0.071 -0.186,-0.134 -0.338,-0.134 -0.277,0 -0.511,0.238 -0.511,0.521 0,0.154 0.083,0.301 0.179,0.383 0.394,0.346 0.911,0.563 1.601,0.563 1.077,0 1.739,-0.451 1.739,-1.608 l 0,-0.013 c 0,-0.911 -0.641,-1.265 -1.296,-1.376 l 0.945,-0.919 c 0.193,-0.19 0.317,-0.337 0.317,-0.604 0,-0.294 -0.228,-0.477 -0.538,-0.477 l -2.354,0 c -0.248,0 -0.455,0.21 -0.455,0.464 0,0.253 0.207,0.463 0.455,0.463 l 1.485,0 -0.939,0.961 c -0.166,0.169 -0.228,0.295 -0.228,0.444 0,0.25 0.207,0.463 0.455,0.463 l 0.166,0 c 0.594,0 0.945,0.222 0.945,0.624 l 0,0.012 c 0,0.367 -0.282,0.599 -0.453,0.599"/><path d="m 10.354,9.5342 c 0,0.876 -0.379,1.525 -0.979,1.525 -0.599,0 -0.992,-0.655 -0.992,-1.539 l 0,-0.012 c 0,-0.884 0.388,-1.527 0.979,-1.527 0.592,0 0.992,0.663 0.992,1.539 l 0,0.014 z m -0.979,-2.512 c -1.197,0 -2.008,1.097 -2.008,2.498 l 0,0.014 c 0,1.401 0.792,2.484 1.995,2.484 1.205,0 2.01,-1.097 2.01,-2.498 l 0,-0.012 c 0,-1.402 -0.805,-2.486 -1.997,-2.486"/></svg>');
+}
+
+video::-webkit-media-controls-panel button.paused:active,
+audio::-webkit-media-controls-panel button.paused:active {
+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 15" fill="white"><path d="M 0,1 12,7.5 0,14 z"/></svg>');
+}
+
+video::-webkit-media-controls-play-button:active,
+audio::-webkit-media-controls-play-button:active {
+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 15" fill="white"><rect x="0" y="1" width="4" height="13"/><rect x="8" y="1" width="4" height="13"/></svg>');
+}
+
+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,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 15" stroke="white" fill="none" stroke-width="1.25" stroke-linecap="round"><defs><clipPath id="cut-hole"><rect x="3" y="0" width="3.5" height="15"/></clipPath></defs><path stroke="none" fill="white" shape-rendering="crispEdges" d="M 0,10 0,6 3,6 3,10 z"/><path stroke="none" fill="white" clip-path="url(#cut-hole)" shape-rendering="crispEdges" d="M 3.5,6 6.5,3 6.5,13 3.5,10 z"/><path d="M 9,5.5 C 10.75,7 10.75,9 9,10.5"/><path d="M 10,3.5 C 13,4.5 13,11.5 10,12.5" /><path d="M 11,1.5 C 15.67,3.5 15.67,12.5 11,14.5"/></svg>');
+}
+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,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 15" stroke="white" fill="none" stroke-width="1.25" stroke-linecap="round"><defs><clipPath id="cut-hole"><rect x="3" y="0" width="3.5" height="15"/></clipPath></defs><path stroke="none" fill="white" shape-rendering="crispEdges" d="M 0,10 0,6 3,6 3,10 z"/><path stroke="none" fill="white" clip-path="url(#cut-hole)" shape-rendering="crispEdges" d="M 3.5,6 6.5,3 6.5,13 3.5,10 z"/><path d="M 9,5.5 C 10.75,7 10.75,9 9,10.5"/></svg>');
+}
+
+video:-webkit-full-screen::-webkit-media-controls-play-button:active{
+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21 23" fill="white"><path d="M 0,0 0,22 8,22 8,0 z"/><path d="M 13,0 13,22 21,22 21,0 z"/></svg>');
+}
+
+video:-webkit-full-screen::-webkit-media-controls-panel button.paused:active {
+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21 23" fill="white"><path d="M 0,0 21,11.5 0,23 z"/></svg>');
+}
+
+video:-webkit-full-screen::-webkit-media-controls-seek-back-button:active {
+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 15" fill="white"><path d="M 24,0 12,7 24,15 z"/><path d="M 12,0 0,7 12,15 z"/></svg>');
+}
+
+video:-webkit-full-screen::-webkit-media-controls-seek-forward-button:active {
+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 15" fill="white"><path d="M 0,0 12,7 0,15 z"/><path d="M 12,0 24,7 12,15 z"/></svg>');
+}
+
+video::-webkit-media-controls-fullscreen-button:active,
+audio::-webkit-media-controls-fullscreen-button:active {
+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 15" stroke="white" fill="none"><path d="M 7,1.5 L 12.5,1.5 L 12.5,7"/><path d="M 0.5,8 L 0.5,13.5 L 6,13.5"/><path stroke-linecap="round" d="M 12.5,1.5 L 7.5,6.5"/><path stroke-linecap="round" d="M 0.5,13.5 L 5.5,8.5"/></svg>');
+}
+
+video::-webkit-media-controls-panel button.exit:active,
+audio::-webkit-media-controls-panel button.exit:active {
+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 15" stroke="white" fill="none"><path d="M 7.5,1 L 7.5,6.5 L 13,6.5"/><path d="M 0,8.5 L 5.5,8.5 L 5.5,14"/><path stroke-linecap="round" d="M 7.5,6.5 L 12.5,1.5"/><path stroke-linecap="round" d="M 5.5,8.5 L 0.5,13.5"/></svg>');
+}
+
+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,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 15" stroke="white"><defs> <clipPath fill-rule="evenodd" id="cut-hole"><path d="M 0,0.5 L 16,0.5 L 16,15.5 L 0,15.5 z M 0,14.5 L 16,14.5 L 8,5 z"/></clipPath></defs><rect fill="none" clip-path="url(#cut-hole)" x="0.5" y="2" width="15" height="8"/><path stroke="none" fill="white" d="M 3.5,13.25 L 12.5,13.25 L 8,8 z"/></svg>');
+}
+
+video::-webkit-media-controls-panel .picture-in-picture-button:active {
+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 12" fill="white"><polygon points="6 8 1 8 1 1 14 1 14 6 15 6 15 0 0 0 0 9 6 9 6 8"/><rect x="7" y="7" width="9" height="5"/><path d="M5.5,4.45a0.5,0.5,0,0,0-.5.5v1.3L2.58,3.83a0.5,0.5,0,0,0-.71.71L4.33,7H3A0.5,0.5,0,0,0,3,8H5.5A0.5,0.5,0,0,0,6,7.5V4.95A0.5,0.5,0,0,0,5.5,4.45Z" transform="translate(0 -2)"/></svg>');
+}
+
+video::-webkit-media-controls-panel .picture-in-picture-button.return-from-picture-in-picture:active {
+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 12" fill="white"><polygon points="6 8 1 8 1 1 14 1 14 6 15 6 15 0 0 0 0 9 6 9 6 8"/><rect x="7" y="7" width="9" height="5"/><path d="M2.22,7.23a0.5,0.5,0,0,0,.5-0.5V5.43L5.15,7.85a0.5,0.5,0,0,0,.71-0.71L3.39,4.68H4.77a0.5,0.5,0,0,0,0-1H2.22a0.5,0.5,0,0,0-.5.5V6.73A0.5,0.5,0,0,0,2.22,7.23Z" transform="translate(0 -2)"/></svg>');
+}
+
+/* ==================== 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,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 131 90"><g fill="none" stroke="-apple-system-gray" stroke-width="4"><rect x="2" y="2" width="127" height="76"/><line x1="30" y1="88" x2="101" y2="88"/></g></svg>');
+ 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,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 62 43"><g fill="none" stroke="-apple-system-gray" stroke-width="2"><rect x="1" y="1" width="60" height="36"/><line x1="14" y1="42" x2="48" y2="42"/></g></svg>');
+ 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,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 304 150"><g fill="none" stroke="-apple-system-gray" stroke-width="3"><path d="m 172,106 -81,0 c -3.5,0 -6,-2.5 -6,-6 l 0,-89 c 0,-3.5 2.5,-6 6,-6 l 122,0 c 3.5,0 6,2.5 6,6 l 0,57" /><path d="m 233,119 -53,0 c -3,0 -3,-0 -3,-3 l 0,-40 c 0,-3 0,-3 3,-3 l 53,0 c 3,0 3,0 3,3 l 0,40 c 0,3 0,3 -3,3 z" /></g></svg>');
+}
+
+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 <video> element.
+ event.preventDefault();
+ },
+
+ handlePlaceholderClick: function(event)
+ {
+ // Prevent clicks in the placeholder from playing or pausing the video in a MediaDocument.
+ event.preventDefault();
+ },
+
+ handleRewindButtonClicked: function(event)
+ {
+ var newTime = Math.max(
+ this.video.currentTime - this.RewindAmount,
+ this.video.seekable.start(0));
+ this.video.currentTime = newTime;
+ return true;
+ },
+
+ canPlay: function()
+ {
+ return this.video.paused || this.video.ended || this.video.readyState < HTMLMediaElement.HAVE_METADATA;
+ },
+
+ handlePlayButtonClicked: function(event)
+ {
+ if (this.canPlay()) {
+ this.canToggleShowControlsButton = true;
+ this.video.play();
+ } else
+ this.video.pause();
+ return true;
+ },
+
+ handleTimelineInput: function(event)
+ {
+ if (this.scrubbing)
+ this.video.pause();
+
+ this.video.fastSeek(this.controls.timeline.value);
+ this.updateControlsWhileScrubbing();
+ },
+
+ handleTimelineChange: function(event)
+ {
+ this.video.currentTime = this.controls.timeline.value;
+ this.updateProgress();
+ },
+
+ handleTimelineDown: function(event)
+ {
+ this.controls.thumbnail.classList.add(this.ClassNames.show);
+ },
+
+ handleTimelineUp: function(event)
+ {
+ this.controls.thumbnail.classList.remove(this.ClassNames.show);
+ },
+
+ handleTimelineMouseOver: function(event)
+ {
+ this.controls.thumbnail.classList.add(this.ClassNames.show);
+ },
+
+ handleTimelineMouseOut: function(event)
+ {
+ this.controls.thumbnail.classList.remove(this.ClassNames.show);
+ },
+
+ handleTimelineMouseMove: function(event)
+ {
+ if (this.controls.thumbnail.classList.contains(this.ClassNames.hidden))
+ return;
+
+ this.updateTimelineMetricsIfNeeded();
+ this.controls.thumbnail.classList.add(this.ClassNames.show);
+ var localPoint = webkitConvertPointFromPageToNode(this.controls.timeline, new WebKitPoint(event.clientX, event.clientY));
+ var percent = (localPoint.x - this.timelineLeft) / this.timelineWidth;
+ percent = Math.max(Math.min(1, percent), 0);
+ this.controls.thumbnail.style.left = percent * 100 + '%';
+
+ var thumbnailTime = percent * this.video.duration;
+ for (var i = 0; i < this.video.textTracks.length; ++i) {
+ var track = this.video.textTracks[i];
+ if (!this.trackHasThumbnails(track))
+ continue;
+
+ if (!track.cues)
+ continue;
+
+ for (var j = 0; j < track.cues.length; ++j) {
+ var cue = track.cues[j];
+ if (thumbnailTime >= cue.startTime && thumbnailTime < cue.endTime) {
+ this.controls.thumbnailImage.src = cue.text;
+ return;
+ }
+ }
+ }
+ },
+
+ handleTimelineMouseDown: function(event)
+ {
+ this.scrubbing = true;
+ },
+
+ handleTimelineMouseUp: function(event)
+ {
+ this.scrubbing = false;
+ },
+
+ handleTimelineKeyDown: function(event)
+ {
+ if (event.keyCode == this.KeyCodes.left)
+ this.controls.timeline.value = this.decrementTimelineValue();
+ else if (event.keyCode == this.KeyCodes.right)
+ this.controls.timeline.value = this.incrementTimelineValue();
+ },
+
+ handleMuteButtonClicked: function(event)
+ {
+ this.video.muted = !this.video.muted;
+ if (this.video.muted)
+ this.controls.muteButton.setAttribute('aria-checked', 'true');
+ else
+ this.controls.muteButton.setAttribute('aria-checked', 'false');
+ this.drawVolumeBackground();
+ return true;
+ },
+
+ handleMuteBoxOver: function(event)
+ {
+ this.drawVolumeBackground();
+ },
+
+ handleMinButtonClicked: function(event)
+ {
+ if (this.video.muted) {
+ this.video.muted = false;
+ this.controls.muteButton.setAttribute('aria-checked', 'false');
+ }
+ this.video.volume = 0;
+ return true;
+ },
+
+ handleMaxButtonClicked: function(event)
+ {
+ if (this.video.muted) {
+ this.video.muted = false;
+ this.controls.muteButton.setAttribute('aria-checked', 'false');
+ }
+ this.video.volume = 1;
+ },
+
+ updateVideoVolume: function()
+ {
+ if (this.video.muted) {
+ this.video.muted = false;
+ this.controls.muteButton.setAttribute('aria-checked', 'false');
+ }
+ this.video.volume = this.controls.volume.value;
+ this.controls.volume.setAttribute('aria-valuetext', `${parseInt(this.controls.volume.value * 100)}%`);
+ },
+
+ handleVolumeSliderInput: function(event)
+ {
+ this.updateVideoVolume();
+ this.drawVolumeBackground();
+ },
+
+ handleVolumeSliderChange: function(event)
+ {
+ this.updateVideoVolume();
+ },
+
+ handleVolumeSliderMouseDown: function(event)
+ {
+ this.isVolumeSliderActive = true;
+ this.drawVolumeBackground();
+ },
+
+ handleVolumeSliderMouseUp: function(event)
+ {
+ this.isVolumeSliderActive = false;
+ this.drawVolumeBackground();
+ },
+
+ handleCaptionButtonClicked: function(event)
+ {
+ if (this.captionMenu)
+ this.destroyCaptionMenu();
+ else
+ this.buildCaptionMenu();
+ return true;
+ },
+
+ hasVideo: function()
+ {
+ return this.video.videoTracks && this.video.videoTracks.length;
+ },
+
+ updateFullscreenButtons: function()
+ {
+ var shouldBeHidden = !this.video.webkitSupportsFullscreen || !this.hasVideo();
+ this.controls.fullscreenButton.classList.toggle(this.ClassNames.hidden, shouldBeHidden && !this.isFullScreen());
+ this.updatePictureInPictureButton();
+ this.setNeedsUpdateForDisplayedWidth();
+ this.updateLayoutForDisplayedWidth();
+ },
+
+ handleFullscreenButtonClicked: function(event)
+ {
+ if (this.isFullScreen())
+ this.video.webkitExitFullscreen();
+ else
+ this.video.webkitEnterFullscreen();
+ return true;
+ },
+
+ updateWirelessTargetPickerButton: function() {
+ var wirelessTargetPickerColor;
+ if (this.controls.wirelessTargetPicker.classList.contains('playing'))
+ wirelessTargetPickerColor = "-apple-wireless-playback-target-active";
+ else
+ wirelessTargetPickerColor = "rgba(255,255,255,0.45)";
+ if (window.devicePixelRatio == 2)
+ this.controls.wirelessTargetPicker.style.backgroundImage = "url(\"data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 15' stroke='" + wirelessTargetPickerColor + "'><defs> <clipPath fill-rule='evenodd' id='cut-hole'><path d='M 0,0.5 L 16,0.5 L 16,15.5 L 0,15.5 z M 0,14.5 L 16,14.5 L 8,5 z'/></clipPath></defs><rect fill='none' clip-path='url(#cut-hole)' x='0.5' y='2' width='15' height='8'/><path stroke='none' fill='" + wirelessTargetPickerColor +"' d='M 3.5,13.25 L 12.5,13.25 L 8,8 z'/></svg>\")";
+ else
+ this.controls.wirelessTargetPicker.style.backgroundImage = "url(\"data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 15' stroke='" + wirelessTargetPickerColor + "'><defs> <clipPath fill-rule='evenodd' id='cut-hole'><path d='M 0,1 L 16,1 L 16,16 L 0,16 z M 0,15 L 16,15 L 8,5.5 z'/></clipPath></defs><rect fill='none' clip-path='url(#cut-hole)' x='0.5' y='2.5' width='15' height='8'/><path stroke='none' fill='" + wirelessTargetPickerColor +"' d='M 2.75,14 L 13.25,14 L 8,8.75 z'/></svg>\")";
+ },
+
+ handleControlsChange: function()
+ {
+ try {
+ this.updateBase();
+
+ if (this.shouldHaveControls() && !this.hasControls())
+ this.addControls();
+ else if (!this.shouldHaveControls() && this.hasControls())
+ this.removeControls();
+ } catch(e) {
+ if (window.console)
+ console.error(e);
+ }
+ },
+
+ nextRate: function()
+ {
+ return Math.min(this.MaximumSeekRate, Math.abs(this.video.playbackRate * 2));
+ },
+
+ handleSeekBackMouseDown: function(event)
+ {
+ this.actionAfterSeeking = (this.canPlay() ? Controller.PauseAfterSeeking : Controller.PlayAfterSeeking);
+ this.video.play();
+ this.video.playbackRate = this.nextRate() * -1;
+ this.seekInterval = setInterval(this.seekBackFaster.bind(this), this.SeekDelay);
+ },
+
+ seekBackFaster: function()
+ {
+ this.video.playbackRate = this.nextRate() * -1;
+ },
+
+ handleSeekBackMouseUp: function(event)
+ {
+ this.video.playbackRate = this.video.defaultPlaybackRate;
+ if (this.actionAfterSeeking === Controller.PauseAfterSeeking)
+ this.video.pause();
+ else if (this.actionAfterSeeking === Controller.PlayAfterSeeking)
+ this.video.play();
+ if (this.seekInterval)
+ clearInterval(this.seekInterval);
+ },
+
+ handleSeekForwardMouseDown: function(event)
+ {
+ this.actionAfterSeeking = (this.canPlay() ? Controller.PauseAfterSeeking : Controller.PlayAfterSeeking);
+ this.video.play();
+ this.video.playbackRate = this.nextRate();
+ this.seekInterval = setInterval(this.seekForwardFaster.bind(this), this.SeekDelay);
+ },
+
+ seekForwardFaster: function()
+ {
+ this.video.playbackRate = this.nextRate();
+ },
+
+ handleSeekForwardMouseUp: function(event)
+ {
+ this.video.playbackRate = this.video.defaultPlaybackRate;
+ if (this.actionAfterSeeking === Controller.PauseAfterSeeking)
+ this.video.pause();
+ else if (this.actionAfterSeeking === Controller.PlayAfterSeeking)
+ this.video.play();
+ if (this.seekInterval)
+ clearInterval(this.seekInterval);
+ },
+
+ updateDuration: function()
+ {
+ var duration = this.video.duration;
+ this.controls.timeline.min = 0;
+ this.controls.timeline.max = duration;
+
+ this.setIsLive(duration === Number.POSITIVE_INFINITY);
+
+ var timeControls = [this.controls.currentTime, this.controls.remainingTime, this.currentTimeClone, this.remainingTimeClone];
+
+ function removeTimeClass(className) {
+ for (let element of timeControls)
+ element.classList.remove(className);
+ }
+
+ function addTimeClass(className) {
+ for (let element of timeControls)
+ element.classList.add(className);
+ }
+
+ // Reset existing style.
+ removeTimeClass(this.ClassNames.threeDigitTime);
+ removeTimeClass(this.ClassNames.fourDigitTime);
+ removeTimeClass(this.ClassNames.fiveDigitTime);
+ removeTimeClass(this.ClassNames.sixDigitTime);
+
+ if (duration >= 60*60*10)
+ addTimeClass(this.ClassNames.sixDigitTime);
+ else if (duration >= 60*60)
+ addTimeClass(this.ClassNames.fiveDigitTime);
+ else if (duration >= 60*10)
+ addTimeClass(this.ClassNames.fourDigitTime);
+ else
+ addTimeClass(this.ClassNames.threeDigitTime);
+ },
+
+ progressFillStyle: function(context)
+ {
+ var height = this.timelineHeight;
+ var gradient = context.createLinearGradient(0, 0, 0, height);
+ gradient.addColorStop(0, 'rgb(2, 2, 2)');
+ gradient.addColorStop(1, 'rgb(23, 23, 23)');
+ return gradient;
+ },
+
+ updateProgress: function()
+ {
+ this.updateTimelineMetricsIfNeeded();
+ this.drawTimelineBackground();
+ },
+
+ addRoundedRect: function(ctx, x, y, width, height, radius) {
+ ctx.moveTo(x + radius, y);
+ ctx.arcTo(x + width, y, x + width, y + radius, radius);
+ ctx.lineTo(x + width, y + height - radius);
+ ctx.arcTo(x + width, y + height, x + width - radius, y + height, radius);
+ ctx.lineTo(x + radius, y + height);
+ ctx.arcTo(x, y + height, x, y + height - radius, radius);
+ ctx.lineTo(x, y + radius);
+ ctx.arcTo(x, y, x + radius, y, radius);
+ },
+
+ drawTimelineBackground: function() {
+ var dpr = window.devicePixelRatio;
+ var width = this.timelineWidth * dpr;
+ var height = this.timelineHeight * dpr;
+
+ if (!width || !height)
+ return;
+
+ var played = this.controls.timeline.value / this.controls.timeline.max;
+ var buffered = 0;
+ for (var i = 0, end = this.video.buffered.length; i < end; ++i)
+ buffered = Math.max(this.video.buffered.end(i), buffered);
+
+ buffered /= this.video.duration;
+
+ var ctx = document.getCSSCanvasContext('2d', this.timelineContextName, width, height);
+
+ width /= dpr;
+ height /= dpr;
+
+ ctx.save();
+ ctx.scale(dpr, dpr);
+ ctx.clearRect(0, 0, width, height);
+
+ var timelineHeight = 3;
+ var trackHeight = 1;
+ var scrubberWidth = 3;
+ var scrubberHeight = 15;
+ var borderSize = 2;
+ var scrubberPosition = Math.max(0, Math.min(width - scrubberWidth, Math.round(width * played)));
+
+ // Draw buffered section.
+ ctx.save();
+ if (this.isAudio())
+ ctx.fillStyle = "rgb(71, 71, 71)";
+ else
+ ctx.fillStyle = "rgb(30, 30, 30)";
+ ctx.fillRect(1, 8, Math.round(width * buffered) - borderSize, trackHeight);
+ ctx.restore();
+
+ // Draw timeline border.
+ ctx.save();
+ ctx.beginPath();
+ this.addRoundedRect(ctx, scrubberPosition, 7, width - scrubberPosition, timelineHeight, timelineHeight / 2.0);
+ this.addRoundedRect(ctx, scrubberPosition + 1, 8, width - scrubberPosition - borderSize , trackHeight, trackHeight / 2.0);
+ ctx.closePath();
+ ctx.clip("evenodd");
+ if (this.isAudio())
+ ctx.fillStyle = "rgb(71, 71, 71)";
+ else
+ ctx.fillStyle = "rgb(30, 30, 30)";
+ ctx.fillRect(0, 0, width, height);
+ ctx.restore();
+
+ // Draw played section.
+ ctx.save();
+ ctx.beginPath();
+ this.addRoundedRect(ctx, 0, 7, width, timelineHeight, timelineHeight / 2.0);
+ ctx.closePath();
+ ctx.clip();
+ if (this.isAudio())
+ ctx.fillStyle = "rgb(116, 116, 116)";
+ else
+ ctx.fillStyle = "rgb(75, 75, 75)";
+ ctx.fillRect(0, 0, width * played, height);
+ ctx.restore();
+
+ // Draw the scrubber.
+ ctx.save();
+ ctx.clearRect(scrubberPosition - 1, 0, scrubberWidth + borderSize, height, 0);
+ ctx.beginPath();
+ this.addRoundedRect(ctx, scrubberPosition, 1, scrubberWidth, scrubberHeight, 1);
+ ctx.closePath();
+ ctx.clip();
+ if (this.isAudio())
+ ctx.fillStyle = "rgb(181, 181, 181)";
+ else
+ ctx.fillStyle = "rgb(140, 140, 140)";
+ ctx.fillRect(0, 0, width, height);
+ ctx.restore();
+
+ ctx.restore();
+ },
+
+ drawVolumeBackground: function() {
+ var dpr = window.devicePixelRatio;
+ var width = this.controls.volume.offsetWidth * dpr;
+ var height = this.controls.volume.offsetHeight * dpr;
+
+ if (!width || !height)
+ return;
+
+ var ctx = document.getCSSCanvasContext('2d', this.volumeContextName, width, height);
+
+ width /= dpr;
+ height /= dpr;
+
+ ctx.save();
+ ctx.scale(dpr, dpr);
+ ctx.clearRect(0, 0, width, height);
+
+ var seekerPosition = this.controls.volume.value;
+ var trackHeight = 1;
+ var timelineHeight = 3;
+ var scrubberRadius = 3.5;
+ var scrubberDiameter = 2 * scrubberRadius;
+ var borderSize = 2;
+
+ var scrubberPosition = Math.round(seekerPosition * (width - scrubberDiameter - borderSize));
+
+
+ // Draw portion of volume under slider thumb.
+ ctx.save();
+ ctx.beginPath();
+ this.addRoundedRect(ctx, 0, 3, scrubberPosition + 2, timelineHeight, timelineHeight / 2.0);
+ ctx.closePath();
+ ctx.clip();
+ ctx.fillStyle = "rgb(75, 75, 75)";
+ ctx.fillRect(0, 0, width, height);
+ ctx.restore();
+
+ // Draw portion of volume above slider thumb.
+ ctx.save();
+ ctx.beginPath();
+ this.addRoundedRect(ctx, scrubberPosition, 3, width - scrubberPosition, timelineHeight, timelineHeight / 2.0);
+ ctx.closePath();
+ ctx.clip();
+ ctx.fillStyle = "rgb(30, 30, 30)";
+ ctx.fillRect(0, 0, width, height);
+ ctx.restore();
+
+ // Clear a hole in the slider for the scrubber.
+ ctx.save();
+ ctx.beginPath();
+ this.addRoundedRect(ctx, scrubberPosition, 0, scrubberDiameter + borderSize, height, (scrubberDiameter + borderSize) / 2.0);
+ ctx.closePath();
+ ctx.clip();
+ ctx.clearRect(0, 0, width, height);
+ ctx.restore();
+
+ // Draw scrubber.
+ ctx.save();
+ ctx.beginPath();
+ this.addRoundedRect(ctx, scrubberPosition + 1, 1, scrubberDiameter, scrubberDiameter, scrubberRadius);
+ ctx.closePath();
+ ctx.clip();
+ if (this.isVolumeSliderActive)
+ ctx.fillStyle = "white";
+ else
+ ctx.fillStyle = "rgb(140, 140, 140)";
+ ctx.fillRect(0, 0, width, height);
+ ctx.restore();
+
+ ctx.restore();
+ },
+
+ formatTimeDescription: function(time)
+ {
+ if (isNaN(time))
+ time = 0;
+ var absTime = Math.abs(time);
+ var intSeconds = Math.floor(absTime % 60).toFixed(0);
+ var intMinutes = Math.floor((absTime / 60) % 60).toFixed(0);
+ var intHours = Math.floor(absTime / (60 * 60)).toFixed(0);
+
+ var secondString = intSeconds == 1 ? 'Second' : 'Seconds';
+ var minuteString = intMinutes == 1 ? 'Minute' : 'Minutes';
+ var hourString = intHours == 1 ? 'Hour' : 'Hours';
+ if (intHours > 0)
+ return `${intHours} ${this.UIString(hourString)}, ${intMinutes} ${this.UIString(minuteString)}, ${intSeconds} ${this.UIString(secondString)}`;
+ if (intMinutes > 0)
+ return `${intMinutes} ${this.UIString(minuteString)}, ${intSeconds} ${this.UIString(secondString)}`;
+ return `${intSeconds} ${this.UIString(secondString)}`;
+ },
+
+ formatTime: function(time)
+ {
+ if (isNaN(time))
+ time = 0;
+ var absTime = Math.abs(time);
+ var intSeconds = Math.floor(absTime % 60).toFixed(0);
+ var intMinutes = Math.floor((absTime / 60) % 60).toFixed(0);
+ var intHours = Math.floor(absTime / (60 * 60)).toFixed(0);
+ var sign = time < 0 ? '-' : String();
+
+ if (intHours > 0)
+ return sign + intHours + ':' + String('00' + intMinutes).slice(-2) + ":" + String('00' + intSeconds).slice(-2);
+
+ return sign + String('00' + intMinutes).slice(-2) + ":" + String('00' + intSeconds).slice(-2)
+ },
+
+ updatePlaying: function()
+ {
+ this.setPlaying(!this.canPlay());
+ },
+
+ setPlaying: function(isPlaying)
+ {
+ if (!this.video.controls && !this.isFullScreen())
+ return;
+
+ if (this.isPlaying === isPlaying)
+ return;
+ this.isPlaying = isPlaying;
+
+ if (!isPlaying) {
+ this.controls.panel.classList.add(this.ClassNames.paused);
+ if (this.controls.panelBackground)
+ this.controls.panelBackground.classList.add(this.ClassNames.paused);
+ this.controls.playButton.classList.add(this.ClassNames.paused);
+ this.controls.playButton.setAttribute('aria-label', this.UIString('Play'));
+ this.showControls();
+ } else {
+ this.controls.panel.classList.remove(this.ClassNames.paused);
+ if (this.controls.panelBackground)
+ this.controls.panelBackground.classList.remove(this.ClassNames.paused);
+ this.controls.playButton.classList.remove(this.ClassNames.paused);
+ this.controls.playButton.setAttribute('aria-label', this.UIString('Pause'));
+ this.resetHideControlsTimer();
+ this.canToggleShowControlsButton = true;
+ }
+ },
+
+ updateForShowingControls: function()
+ {
+ this.updateLayoutForDisplayedWidth();
+ this.setNeedsTimelineMetricsUpdate();
+ this.updateTime();
+ this.updateProgress();
+ this.drawVolumeBackground();
+ this.drawTimelineBackground();
+ this.controls.panel.classList.add(this.ClassNames.show);
+ this.controls.panel.classList.remove(this.ClassNames.hidden);
+ if (this.controls.panelBackground) {
+ this.controls.panelBackground.classList.add(this.ClassNames.show);
+ this.controls.panelBackground.classList.remove(this.ClassNames.hidden);
+ }
+ },
+
+ showShowControlsButton: function (shouldShow) {
+ this.showControlsButton.hidden = !shouldShow;
+ if (shouldShow && this.shouldHaveControls())
+ this.showControlsButton.focus();
+ },
+
+ showControls: function(focusControls)
+ {
+ this.updateShouldListenForPlaybackTargetAvailabilityEvent();
+ if (!this.video.controls && !this.isFullScreen())
+ return;
+
+ this.updateForShowingControls();
+ if (this.shouldHaveControls() && !this.controls.panel.parentElement) {
+ this.base.appendChild(this.controls.inlinePlaybackPlaceholder);
+ this.base.appendChild(this.controls.panel);
+ if (focusControls)
+ this.controls.playButton.focus();
+ }
+ this.showShowControlsButton(false);
+ },
+
+ hideControls: function()
+ {
+ if (this.controlsAlwaysVisible())
+ return;
+
+ this.clearHideControlsTimer();
+ this.updateShouldListenForPlaybackTargetAvailabilityEvent();
+ this.controls.panel.classList.remove(this.ClassNames.show);
+ if (this.controls.panelBackground)
+ this.controls.panelBackground.classList.remove(this.ClassNames.show);
+ this.showShowControlsButton(this.isPlayable() && this.isPlaying && this.canToggleShowControlsButton);
+ },
+
+ setNeedsUpdateForDisplayedWidth: function()
+ {
+ this.currentDisplayWidth = 0;
+ },
+
+ scheduleUpdateLayoutForDisplayedWidth: function()
+ {
+ setTimeout(this.updateLayoutForDisplayedWidth.bind(this), 0);
+ },
+
+ isControlVisible: function(control)
+ {
+ if (!control)
+ return false;
+ if (!this.root.contains(control))
+ return false;
+ return !control.classList.contains(this.ClassNames.hidden)
+ },
+
+ updateLayoutForDisplayedWidth: function()
+ {
+ if (!this.controls || !this.controls.panel)
+ return;
+
+ var visibleWidth = this.controls.panel.getBoundingClientRect().width;
+ if (this._pageScaleFactor > 1)
+ visibleWidth *= this._pageScaleFactor;
+
+ if (visibleWidth <= 0 || visibleWidth == this.currentDisplayWidth)
+ return;
+
+ this.currentDisplayWidth = visibleWidth;
+
+ // Filter all the buttons which are not explicitly hidden.
+ var buttons = [this.controls.playButton, this.controls.rewindButton, this.controls.captionButton,
+ this.controls.fullscreenButton, this.controls.pictureInPictureButton,
+ this.controls.wirelessTargetPicker, this.controls.muteBox];
+ var visibleButtons = buttons.filter(this.isControlVisible, this);
+
+ // This tells us how much room we need in order to display every visible button.
+ var visibleButtonWidth = this.ButtonWidth * visibleButtons.length;
+
+ var currentTimeWidth = this.currentTimeClone.getBoundingClientRect().width;
+ var remainingTimeWidth = this.remainingTimeClone.getBoundingClientRect().width;
+
+ // Check if there is enough room for the scrubber.
+ var shouldDropTimeline = (visibleWidth - visibleButtonWidth - currentTimeWidth - remainingTimeWidth) < this.MinimumTimelineWidth;
+ this.controls.timeline.classList.toggle(this.ClassNames.dropped, shouldDropTimeline);
+ this.controls.currentTime.classList.toggle(this.ClassNames.dropped, shouldDropTimeline);
+ this.controls.thumbnailTrack.classList.toggle(this.ClassNames.dropped, shouldDropTimeline);
+ this.controls.remainingTime.classList.toggle(this.ClassNames.dropped, shouldDropTimeline);
+
+ // Then controls in the following order:
+ var removeOrder = [this.controls.wirelessTargetPicker, this.controls.pictureInPictureButton,
+ this.controls.captionButton, this.controls.muteBox, this.controls.rewindButton,
+ this.controls.fullscreenButton];
+ removeOrder.forEach(function(control) {
+ var shouldDropControl = visibleWidth < visibleButtonWidth && this.isControlVisible(control);
+ control.classList.toggle(this.ClassNames.dropped, shouldDropControl);
+ if (shouldDropControl)
+ visibleButtonWidth -= this.ButtonWidth;
+ }, this);
+ },
+
+ controlsAlwaysVisible: function()
+ {
+ if (this.presentationMode() === 'picture-in-picture')
+ return true;
+
+ return this.isAudio() || this.currentPlaybackTargetIsWireless() || this.scrubbing;
+ },
+
+ controlsAreHidden: function()
+ {
+ return !this.controlsAlwaysVisible() && !this.controls.panel.classList.contains(this.ClassNames.show) && !this.controls.panel.parentElement;
+ },
+
+ removeControls: function()
+ {
+ if (this.controls.panel.parentNode)
+ this.controls.panel.parentNode.removeChild(this.controls.panel);
+ this.destroyCaptionMenu();
+ },
+
+ addControls: function()
+ {
+ this.base.appendChild(this.controls.inlinePlaybackPlaceholder);
+ this.base.appendChild(this.controls.panel);
+ this.updateControls();
+ },
+
+ hasControls: function()
+ {
+ return this.controls.panel.parentElement;
+ },
+
+ updateTime: function()
+ {
+ var currentTime = this.video.currentTime;
+ var timeRemaining = currentTime - this.video.duration;
+ this.currentTimeClone.innerText = this.controls.currentTime.innerText = this.formatTime(currentTime);
+ this.controls.currentTime.setAttribute('aria-label', `${this.UIString('Elapsed')} ${this.formatTimeDescription(currentTime)}`);
+ this.controls.timeline.value = this.video.currentTime;
+ this.remainingTimeClone.innerText = this.controls.remainingTime.innerText = this.formatTime(timeRemaining);
+ this.controls.remainingTime.setAttribute('aria-label', `${this.UIString('Remaining')} ${this.formatTimeDescription(timeRemaining)}`);
+
+ // Mark the timeline value in percentage format in accessibility.
+ var timeElapsedPercent = currentTime / this.video.duration;
+ timeElapsedPercent = Math.max(Math.min(1, timeElapsedPercent), 0);
+ this.controls.timeline.setAttribute('aria-valuetext', `${parseInt(timeElapsedPercent * 100)}%`);
+ },
+
+ updateControlsWhileScrubbing: function()
+ {
+ if (!this.scrubbing)
+ return;
+
+ var currentTime = (this.controls.timeline.value / this.controls.timeline.max) * this.video.duration;
+ var timeRemaining = currentTime - this.video.duration;
+ this.currentTimeClone.innerText = this.controls.currentTime.innerText = this.formatTime(currentTime);
+ this.remainingTimeClone.innerText = this.controls.remainingTime.innerText = this.formatTime(timeRemaining);
+ this.drawTimelineBackground();
+ },
+
+ updateReadyState: function()
+ {
+ this.updateStatusDisplay();
+ },
+
+ setStatusHidden: function(hidden)
+ {
+ if (this.statusHidden === hidden)
+ return;
+
+ this.statusHidden = hidden;
+
+ if (hidden) {
+ this.controls.statusDisplay.classList.add(this.ClassNames.hidden);
+ this.controls.currentTime.classList.remove(this.ClassNames.hidden);
+ this.controls.timeline.classList.remove(this.ClassNames.hidden);
+ this.controls.remainingTime.classList.remove(this.ClassNames.hidden);
+ this.setNeedsTimelineMetricsUpdate();
+ this.showControls();
+ } else {
+ this.controls.statusDisplay.classList.remove(this.ClassNames.hidden);
+ this.controls.currentTime.classList.add(this.ClassNames.hidden);
+ this.controls.timeline.classList.add(this.ClassNames.hidden);
+ this.controls.remainingTime.classList.add(this.ClassNames.hidden);
+ this.hideControls();
+ }
+ this.updateWirelessTargetAvailable();
+ },
+
+ trackHasThumbnails: function(track)
+ {
+ return track.kind === 'thumbnails' || (track.kind === 'metadata' && track.label === 'thumbnails');
+ },
+
+ updateThumbnail: function()
+ {
+ for (var i = 0; i < this.video.textTracks.length; ++i) {
+ var track = this.video.textTracks[i];
+ if (this.trackHasThumbnails(track)) {
+ this.controls.thumbnail.classList.remove(this.ClassNames.hidden);
+ return;
+ }
+ }
+
+ this.controls.thumbnail.classList.add(this.ClassNames.hidden);
+ },
+
+ updateCaptionButton: function()
+ {
+ var audioTracks = this.host.sortedTrackListForMenu(this.video.audioTracks);
+ var textTracks = this.host.sortedTrackListForMenu(this.video.textTracks);
+
+ if ((textTracks && textTracks.length) || (audioTracks && audioTracks.length > 1))
+ this.controls.captionButton.classList.remove(this.ClassNames.hidden);
+ else
+ this.controls.captionButton.classList.add(this.ClassNames.hidden);
+ this.setNeedsUpdateForDisplayedWidth();
+ this.updateLayoutForDisplayedWidth();
+ },
+
+ updateCaptionContainer: function()
+ {
+ if (!this.host.textTrackContainer)
+ return;
+
+ var hasClosedCaptions = this.video.webkitHasClosedCaptions;
+ var hasHiddenClass = this.host.textTrackContainer.classList.contains(this.ClassNames.hidden);
+
+ if (hasClosedCaptions && hasHiddenClass)
+ this.host.textTrackContainer.classList.remove(this.ClassNames.hidden);
+ else if (!hasClosedCaptions && !hasHiddenClass)
+ this.host.textTrackContainer.classList.add(this.ClassNames.hidden);
+
+ this.updateBase();
+ this.host.updateTextTrackContainer();
+ },
+
+ buildCaptionMenu: function()
+ {
+ var audioTracks = this.host.sortedTrackListForMenu(this.video.audioTracks);
+ var textTracks = this.host.sortedTrackListForMenu(this.video.textTracks);
+
+ if ((!textTracks || !textTracks.length) && (!audioTracks || !audioTracks.length))
+ return;
+
+ this.captionMenu = document.createElement('div');
+ this.captionMenu.setAttribute('pseudo', '-webkit-media-controls-closed-captions-container');
+ this.captionMenu.setAttribute('id', 'audioAndTextTrackMenu');
+ this.base.appendChild(this.captionMenu);
+ this.captionMenuItems = [];
+
+ var offItem = this.host.captionMenuOffItem;
+ var automaticItem = this.host.captionMenuAutomaticItem;
+ var displayMode = this.host.captionDisplayMode;
+
+ var list = document.createElement('div');
+ this.captionMenu.appendChild(list);
+ list.classList.add(this.ClassNames.list);
+
+ if (audioTracks && audioTracks.length > 1) {
+ var heading = document.createElement('h3');
+ heading.id = 'webkitMediaControlsAudioTrackHeading'; // for AX menu label
+ list.appendChild(heading);
+ heading.innerText = this.UIString('Audio');
+
+ var ul = document.createElement('ul');
+ ul.setAttribute('role', 'menu');
+ ul.setAttribute('aria-labelledby', 'webkitMediaControlsAudioTrackHeading');
+ list.appendChild(ul);
+
+ for (var i = 0; i < audioTracks.length; ++i) {
+ var menuItem = document.createElement('li');
+ menuItem.setAttribute('role', 'menuitemradio');
+ menuItem.setAttribute('tabindex', '-1');
+ this.captionMenuItems.push(menuItem);
+ this.listenFor(menuItem, 'click', this.audioTrackItemSelected);
+ this.listenFor(menuItem, 'keyup', this.handleAudioTrackItemKeyUp);
+ ul.appendChild(menuItem);
+
+ var track = audioTracks[i];
+ menuItem.innerText = this.host.displayNameForTrack(track);
+ menuItem.track = track;
+
+ var itemCheckmark = document.createElement("img");
+ itemCheckmark.classList.add("checkmark-container");
+ menuItem.insertBefore(itemCheckmark, menuItem.firstChild);
+
+ if (track.enabled) {
+ menuItem.classList.add(this.ClassNames.selected);
+ menuItem.setAttribute('tabindex', '0');
+ menuItem.setAttribute('aria-checked', 'true');
+ }
+ }
+ }
+
+ if (textTracks && textTracks.length > 2) {
+ var heading = document.createElement('h3');
+ heading.id = 'webkitMediaControlsClosedCaptionsHeading'; // for AX menu label
+ list.appendChild(heading);
+ heading.innerText = this.UIString('Subtitles');
+
+ var ul = document.createElement('ul');
+ ul.setAttribute('role', 'menu');
+ ul.setAttribute('aria-labelledby', 'webkitMediaControlsClosedCaptionsHeading');
+ list.appendChild(ul);
+
+ for (var i = 0; i < textTracks.length; ++i) {
+ var menuItem = document.createElement('li');
+ menuItem.setAttribute('role', 'menuitemradio');
+ menuItem.setAttribute('tabindex', '-1');
+ this.captionMenuItems.push(menuItem);
+ this.listenFor(menuItem, 'click', this.captionItemSelected);
+ this.listenFor(menuItem, 'keyup', this.handleCaptionItemKeyUp);
+ ul.appendChild(menuItem);
+
+ var track = textTracks[i];
+ menuItem.innerText = this.host.displayNameForTrack(track);
+ menuItem.track = track;
+
+ var itemCheckmark = document.createElement("img");
+ itemCheckmark.classList.add("checkmark-container");
+ menuItem.insertBefore(itemCheckmark, menuItem.firstChild);
+
+ if (track === offItem) {
+ var offMenu = menuItem;
+ continue;
+ }
+
+ if (track === automaticItem) {
+ if (displayMode === 'automatic') {
+ menuItem.classList.add(this.ClassNames.selected);
+ menuItem.setAttribute('tabindex', '0');
+ menuItem.setAttribute('aria-checked', 'true');
+ }
+ continue;
+ }
+
+ if (displayMode != 'automatic' && track.mode === 'showing') {
+ var trackMenuItemSelected = true;
+ menuItem.classList.add(this.ClassNames.selected);
+ menuItem.setAttribute('tabindex', '0');
+ menuItem.setAttribute('aria-checked', 'true');
+ }
+
+ }
+
+ if (offMenu && (displayMode === 'forced-only' || displayMode === 'manual') && !trackMenuItemSelected) {
+ offMenu.classList.add(this.ClassNames.selected);
+ offMenu.setAttribute('tabindex', '0');
+ offMenu.setAttribute('aria-checked', 'true');
+ }
+ }
+
+ // focus first selected menuitem
+ for (var i = 0, c = this.captionMenuItems.length; i < c; i++) {
+ var item = this.captionMenuItems[i];
+ if (item.classList.contains(this.ClassNames.selected)) {
+ item.focus();
+ break;
+ }
+ }
+
+ },
+
+ captionItemSelected: function(event)
+ {
+ this.host.setSelectedTextTrack(event.target.track);
+ this.destroyCaptionMenu();
+ },
+
+ focusSiblingCaptionItem: function(event)
+ {
+ var currentItem = event.target;
+ var pendingItem = false;
+ switch(event.keyCode) {
+ case this.KeyCodes.left:
+ case this.KeyCodes.up:
+ pendingItem = currentItem.previousSibling;
+ break;
+ case this.KeyCodes.right:
+ case this.KeyCodes.down:
+ pendingItem = currentItem.nextSibling;
+ break;
+ }
+ if (pendingItem) {
+ currentItem.setAttribute('tabindex', '-1');
+ pendingItem.setAttribute('tabindex', '0');
+ pendingItem.focus();
+ }
+ },
+
+ handleCaptionItemKeyUp: function(event)
+ {
+ switch (event.keyCode) {
+ case this.KeyCodes.enter:
+ case this.KeyCodes.space:
+ this.captionItemSelected(event);
+ break;
+ case this.KeyCodes.escape:
+ this.destroyCaptionMenu();
+ break;
+ case this.KeyCodes.left:
+ case this.KeyCodes.up:
+ case this.KeyCodes.right:
+ case this.KeyCodes.down:
+ this.focusSiblingCaptionItem(event);
+ break;
+ default:
+ return;
+ }
+ // handled
+ event.stopPropagation();
+ event.preventDefault();
+ },
+
+ audioTrackItemSelected: function(event)
+ {
+ for (var i = 0; i < this.video.audioTracks.length; ++i) {
+ var track = this.video.audioTracks[i];
+ track.enabled = (track == event.target.track);
+ }
+
+ this.destroyCaptionMenu();
+ },
+
+ focusSiblingAudioTrackItem: function(event)
+ {
+ var currentItem = event.target;
+ var pendingItem = false;
+ switch(event.keyCode) {
+ case this.KeyCodes.left:
+ case this.KeyCodes.up:
+ pendingItem = currentItem.previousSibling;
+ break;
+ case this.KeyCodes.right:
+ case this.KeyCodes.down:
+ pendingItem = currentItem.nextSibling;
+ break;
+ }
+ if (pendingItem) {
+ currentItem.setAttribute('tabindex', '-1');
+ pendingItem.setAttribute('tabindex', '0');
+ pendingItem.focus();
+ }
+ },
+
+ handleAudioTrackItemKeyUp: function(event)
+ {
+ switch (event.keyCode) {
+ case this.KeyCodes.enter:
+ case this.KeyCodes.space:
+ this.audioTrackItemSelected(event);
+ break;
+ case this.KeyCodes.escape:
+ this.destroyCaptionMenu();
+ break;
+ case this.KeyCodes.left:
+ case this.KeyCodes.up:
+ case this.KeyCodes.right:
+ case this.KeyCodes.down:
+ this.focusSiblingAudioTrackItem(event);
+ break;
+ default:
+ return;
+ }
+ // handled
+ event.stopPropagation();
+ event.preventDefault();
+ },
+
+ destroyCaptionMenu: function()
+ {
+ if (!this.captionMenu)
+ return;
+
+ this.captionMenuItems.forEach(function(item){
+ this.stopListeningFor(item, 'click', this.captionItemSelected);
+ this.stopListeningFor(item, 'keyup', this.handleCaptionItemKeyUp);
+ }, this);
+
+ // FKA and AX: focus the trigger before destroying the element with focus
+ if (this.controls.captionButton)
+ this.controls.captionButton.focus();
+
+ if (this.captionMenu.parentNode)
+ this.captionMenu.parentNode.removeChild(this.captionMenu);
+ delete this.captionMenu;
+ delete this.captionMenuItems;
+ },
+
+ updateHasAudio: function()
+ {
+ if (this.video.audioTracks.length && !this.currentPlaybackTargetIsWireless())
+ this.controls.muteBox.classList.remove(this.ClassNames.hidden);
+ else
+ this.controls.muteBox.classList.add(this.ClassNames.hidden);
+
+ this.setNeedsUpdateForDisplayedWidth();
+ this.updateLayoutForDisplayedWidth();
+ },
+
+ updateHasVideo: function()
+ {
+ this.controls.panel.classList.toggle(this.ClassNames.noVideo, !this.hasVideo());
+ // The availability of the picture-in-picture button as well as the full-screen
+ // button depends no the value returned by hasVideo(), so make sure we invalidate
+ // the availability of both controls.
+ this.updateFullscreenButtons();
+ },
+
+ updateVolume: function()
+ {
+ if (this.video.muted || !this.video.volume) {
+ this.controls.muteButton.classList.add(this.ClassNames.muted);
+ this.controls.volume.value = 0;
+ } else {
+ this.controls.muteButton.classList.remove(this.ClassNames.muted);
+ this.controls.volume.value = this.video.volume;
+ }
+ this.controls.volume.setAttribute('aria-valuetext', `${parseInt(this.controls.volume.value * 100)}%`);
+ this.drawVolumeBackground();
+ },
+
+ isAudio: function()
+ {
+ return this.video instanceof HTMLAudioElement;
+ },
+
+ clearHideControlsTimer: function()
+ {
+ if (this.hideTimer)
+ clearTimeout(this.hideTimer);
+ this.hideTimer = null;
+ },
+
+ resetHideControlsTimer: function()
+ {
+ if (this.hideTimer) {
+ clearTimeout(this.hideTimer);
+ this.hideTimer = null;
+ }
+
+ if (this.isPlaying)
+ this.hideTimer = setTimeout(this.hideControls.bind(this), this.HideControlsDelay);
+ },
+
+ handlePictureInPictureButtonClicked: function(event) {
+ if (!('webkitSetPresentationMode' in this.video))
+ return;
+
+ if (this.presentationMode() === 'picture-in-picture')
+ this.video.webkitSetPresentationMode('inline');
+ else
+ this.video.webkitSetPresentationMode('picture-in-picture');
+ },
+
+ currentPlaybackTargetIsWireless: function() {
+ if (Controller.gSimulateWirelessPlaybackTarget)
+ return true;
+
+ if (!this.currentTargetIsWireless || this.wirelessPlaybackDisabled)
+ return false;
+
+ return true;
+ },
+
+ updateShouldListenForPlaybackTargetAvailabilityEvent: function() {
+ var shouldListen = true;
+ if (this.video.error)
+ shouldListen = false;
+ if (!this.isAudio() && !this.video.paused && this.controlsAreHidden())
+ shouldListen = false;
+ if (document.hidden)
+ shouldListen = false;
+
+ this.setShouldListenForPlaybackTargetAvailabilityEvent(shouldListen);
+ },
+
+ updateWirelessPlaybackStatus: function() {
+ if (this.currentPlaybackTargetIsWireless()) {
+ var deviceName = "";
+ var deviceType = "";
+ var type = this.host.externalDeviceType;
+ if (type == "airplay") {
+ deviceType = this.UIString('##WIRELESS_PLAYBACK_DEVICE_TYPE##');
+ deviceName = this.UIString('##WIRELESS_PLAYBACK_DEVICE_NAME##', '##DEVICE_NAME##', this.host.externalDeviceDisplayName || "Apple TV");
+ } else if (type == "tvout") {
+ deviceType = this.UIString('##TVOUT_DEVICE_TYPE##');
+ deviceName = this.UIString('##TVOUT_DEVICE_NAME##');
+ }
+
+ this.controls.inlinePlaybackPlaceholderTextTop.innerText = deviceType;
+ this.controls.inlinePlaybackPlaceholderTextBottom.innerText = deviceName;
+ this.controls.inlinePlaybackPlaceholder.setAttribute('aria-label', deviceType + ", " + deviceName);
+ this.controls.inlinePlaybackPlaceholder.classList.add(this.ClassNames.appleTV);
+ this.controls.inlinePlaybackPlaceholder.classList.remove(this.ClassNames.hidden);
+ this.controls.wirelessTargetPicker.classList.add(this.ClassNames.playing);
+ if (!this.isFullScreen() && (this.video.offsetWidth <= 250 || this.video.offsetHeight <= 200)) {
+ this.controls.inlinePlaybackPlaceholder.classList.add(this.ClassNames.small);
+ this.controls.inlinePlaybackPlaceholderTextTop.classList.add(this.ClassNames.small);
+ this.controls.inlinePlaybackPlaceholderTextBottom.classList.add(this.ClassNames.small);
+ } else {
+ this.controls.inlinePlaybackPlaceholder.classList.remove(this.ClassNames.small);
+ this.controls.inlinePlaybackPlaceholderTextTop.classList.remove(this.ClassNames.small);
+ this.controls.inlinePlaybackPlaceholderTextBottom.classList.remove(this.ClassNames.small);
+ }
+ this.controls.volumeBox.classList.add(this.ClassNames.hidden);
+ this.controls.muteBox.classList.add(this.ClassNames.hidden);
+ this.updateBase();
+ this.showControls();
+ } else {
+ this.controls.inlinePlaybackPlaceholder.classList.add(this.ClassNames.hidden);
+ this.controls.inlinePlaybackPlaceholder.classList.remove(this.ClassNames.appleTV);
+ this.controls.wirelessTargetPicker.classList.remove(this.ClassNames.playing);
+ this.controls.volumeBox.classList.remove(this.ClassNames.hidden);
+ this.controls.muteBox.classList.remove(this.ClassNames.hidden);
+ }
+ this.setNeedsUpdateForDisplayedWidth();
+ this.updateLayoutForDisplayedWidth();
+ this.reconnectControls();
+ this.updateWirelessTargetPickerButton();
+ },
+
+ updateWirelessTargetAvailable: function() {
+ this.currentTargetIsWireless = this.video.webkitCurrentPlaybackTargetIsWireless;
+ this.wirelessPlaybackDisabled = this.video.webkitWirelessVideoPlaybackDisabled;
+
+ var wirelessPlaybackTargetsAvailable = Controller.gSimulateWirelessPlaybackTarget || this.hasWirelessPlaybackTargets;
+ if (this.wirelessPlaybackDisabled)
+ wirelessPlaybackTargetsAvailable = false;
+
+ if (wirelessPlaybackTargetsAvailable && this.isPlayable())
+ this.controls.wirelessTargetPicker.classList.remove(this.ClassNames.hidden);
+ else
+ this.controls.wirelessTargetPicker.classList.add(this.ClassNames.hidden);
+ this.setNeedsUpdateForDisplayedWidth();
+ this.updateLayoutForDisplayedWidth();
+ },
+
+ handleWirelessPickerButtonClicked: function(event)
+ {
+ this.video.webkitShowPlaybackTargetPicker();
+ return true;
+ },
+
+ handleWirelessPlaybackChange: function(event) {
+ this.updateWirelessTargetAvailable();
+ this.updateWirelessPlaybackStatus();
+ this.setNeedsTimelineMetricsUpdate();
+ },
+
+ handleWirelessTargetAvailableChange: function(event) {
+ var wirelessPlaybackTargetsAvailable = event.availability == "available";
+ if (this.hasWirelessPlaybackTargets === wirelessPlaybackTargetsAvailable)
+ return;
+
+ this.hasWirelessPlaybackTargets = wirelessPlaybackTargetsAvailable;
+ this.updateWirelessTargetAvailable();
+ this.setNeedsTimelineMetricsUpdate();
+ },
+
+ setShouldListenForPlaybackTargetAvailabilityEvent: function(shouldListen) {
+ if (!window.WebKitPlaybackTargetAvailabilityEvent || this.isListeningForPlaybackTargetAvailabilityEvent == shouldListen)
+ return;
+
+ if (shouldListen && this.video.error)
+ return;
+
+ this.isListeningForPlaybackTargetAvailabilityEvent = shouldListen;
+ if (shouldListen)
+ this.listenFor(this.video, 'webkitplaybacktargetavailabilitychanged', this.handleWirelessTargetAvailableChange);
+ else
+ this.stopListeningFor(this.video, 'webkitplaybacktargetavailabilitychanged', this.handleWirelessTargetAvailableChange);
+ },
+
+ get scrubbing()
+ {
+ return this._scrubbing;
+ },
+
+ set scrubbing(flag)
+ {
+ if (this._scrubbing == flag)
+ return;
+ this._scrubbing = flag;
+
+ if (this._scrubbing)
+ this.wasPlayingWhenScrubbingStarted = !this.video.paused;
+ else if (this.wasPlayingWhenScrubbingStarted && this.video.paused) {
+ this.video.play();
+ this.resetHideControlsTimer();
+ }
+ },
+
+ get pageScaleFactor()
+ {
+ return this._pageScaleFactor;
+ },
+
+ set pageScaleFactor(newScaleFactor)
+ {
+ if (this._pageScaleFactor === newScaleFactor)
+ return;
+
+ this._pageScaleFactor = newScaleFactor;
+ },
+
+ set usesLTRUserInterfaceLayoutDirection(usesLTRUserInterfaceLayoutDirection)
+ {
+ this.controls.volumeBox.classList.toggle(this.ClassNames.usesLTRUserInterfaceLayoutDirection, usesLTRUserInterfaceLayoutDirection);
+ },
+
+ handleRootResize: function(event)
+ {
+ this.updateLayoutForDisplayedWidth();
+ this.setNeedsTimelineMetricsUpdate();
+ this.updateTimelineMetricsIfNeeded();
+ this.drawTimelineBackground();
+ },
+
+ getCurrentControlsStatus: function ()
+ {
+ var result = {
+ idiom: this.idiom,
+ status: "ok"
+ };
+
+ var elements = [
+ {
+ name: "Show Controls",
+ object: this.showControlsButton,
+ extraProperties: ["hidden"],
+ },
+ {
+ name: "Status Display",
+ object: this.controls.statusDisplay,
+ styleValues: ["display"],
+ extraProperties: ["textContent"],
+ },
+ {
+ name: "Play Button",
+ object: this.controls.playButton,
+ extraProperties: ["hidden"],
+ },
+ {
+ name: "Rewind Button",
+ object: this.controls.rewindButton,
+ extraProperties: ["hidden"],
+ },
+ {
+ name: "Timeline Box",
+ object: this.controls.timelineBox,
+ },
+ {
+ name: "Mute Box",
+ object: this.controls.muteBox,
+ extraProperties: ["hidden"],
+ },
+ {
+ name: "Fullscreen Button",
+ object: this.controls.fullscreenButton,
+ extraProperties: ["hidden"],
+ },
+ {
+ name: "AppleTV Device Picker",
+ object: this.controls.wirelessTargetPicker,
+ styleValues: ["display"],
+ extraProperties: ["hidden"],
+ },
+ {
+ name: "Picture-in-picture Button",
+ object: this.controls.pictureInPictureButton,
+ extraProperties: ["parentElement", "hidden"],
+ },
+ {
+ name: "Caption Button",
+ object: this.controls.captionButton,
+ extraProperties: ["hidden"],
+ },
+ {
+ name: "Timeline",
+ object: this.controls.timeline,
+ extraProperties: ["hidden"],
+ },
+ {
+ name: "Current Time",
+ object: this.controls.currentTime,
+ extraProperties: ["hidden"],
+ },
+ {
+ name: "Thumbnail Track",
+ object: this.controls.thumbnailTrack,
+ extraProperties: ["hidden"],
+ },
+ {
+ name: "Time Remaining",
+ object: this.controls.remainingTime,
+ extraProperties: ["hidden"],
+ },
+ {
+ name: "Track Menu",
+ object: this.captionMenu,
+ },
+ {
+ name: "Inline playback placeholder",
+ object: this.controls.inlinePlaybackPlaceholder,
+ },
+ {
+ name: "Media Controls Panel",
+ object: this.controls.panel,
+ extraProperties: ["hidden"],
+ },
+ {
+ name: "Control Base Element",
+ object: this.base || null,
+ },
+ ];
+
+ elements.forEach(function (element) {
+ var obj = element.object;
+ delete element.object;
+
+ element.computedStyle = {};
+ if (obj && element.styleValues) {
+ var computedStyle = window.getComputedStyle(obj);
+ element.styleValues.forEach(function (propertyName) {
+ element.computedStyle[propertyName] = computedStyle[propertyName];
+ });
+ delete element.styleValues;
+ }
+
+ element.bounds = obj ? obj.getBoundingClientRect() : null;
+ element.className = obj ? obj.className : null;
+ element.ariaLabel = obj ? obj.getAttribute('aria-label') : null;
+
+ if (element.extraProperties) {
+ element.extraProperties.forEach(function (property) {
+ element[property] = obj ? obj[property] : null;
+ });
+ delete element.extraProperties;
+ }
+
+ element.element = obj;
+ });
+
+ result.elements = elements;
+
+ return JSON.stringify(result);
+ }
+
+};
diff --git a/Source/WebCore/Modules/mediacontrols/mediaControlsBase.css b/Source/WebCore/Modules/mediacontrols/mediaControlsBase.css
new file mode 100644
index 000000000..61cf47e89
--- /dev/null
+++ b/Source/WebCore/Modules/mediacontrols/mediaControlsBase.css
@@ -0,0 +1,758 @@
+/*
+ * 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,
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (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;
+ -webkit-align-items: stretch;
+ -webkit-justify-content: flex-end;
+ -webkit-flex-direction: column;
+}
+
+video::-webkit-media-text-track-container,
+audio::-webkit-media-text-track-container {
+ position: relative;
+ -webkit-flex: 1 1 auto;
+}
+
+video::-webkit-media-controls-panel-composited-parent {
+ -webkit-transform: translateZ(0);
+ width: 100%;
+}
+
+video::-webkit-media-controls-panel,
+audio::-webkit-media-controls-panel {
+ box-sizing: border-box;
+ position: relative;
+ bottom: 0;
+ width: 100%;
+ padding-top: 1px;
+ min-height: 25px;
+ height: 25px;
+ line-height: 25px;
+ -webkit-user-select: none;
+ background-color: transparent;
+ background-image: -webkit-linear-gradient(top,
+ rgba(0, 0, 0, .92) 0,
+ rgba(0, 0, 0, .92) 1px,
+ rgba(89, 89, 89, .92) 1px,
+ rgba(89, 89, 89, .92) 2px,
+ rgba(60, 60, 60, .92) 2px,
+ rgba(35, 35, 35, .92) 12px,
+ rgba(30, 30, 30, .92) 12px,
+ rgba(30, 30, 30, .92) 13px,
+ rgba(25, 25, 25, .92) 13px,
+ rgba(17, 17, 17, .92) 100%
+ );
+
+ display: -webkit-flex;
+ -webkit-flex-direction: row;
+ -webkit-align-items: center;
+ -webkit-user-select: none;
+
+ direction: ltr;
+
+ transition: opacity 0.25s linear;
+}
+
+video::-webkit-media-controls-panel {
+ cursor: none;
+ opacity: 0;
+}
+
+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 button,
+audio::-webkit-media-controls-panel button {
+ -webkit-appearance: none;
+ display: block;
+ padding: 0;
+ border: 0;
+ height: 16px;
+ width: 16px;
+ background-color: transparent;
+ color: white;
+ background-origin: content-box;
+ background-repeat: no-repeat;
+ background-position: center;
+ -webkit-filter: drop-shadow(black 0 1px 1px);
+}
+
+video::-webkit-media-controls-panel button:active,
+audio::-webkit-media-controls-panel button:active {
+ -webkit-filter: drop-shadow(white 0 0 10px);
+}
+
+video::-webkit-media-controls-rewind-button,
+audio::-webkit-media-controls-rewind-button {
+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 17"><linearGradient id="gradient" x2="0" y2="100%" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="rgb(216, 216, 216)"/><stop offset="0.44444" stop-color="rgb(216, 216, 216)"/><stop offset="0.44444" stop-color="rgb(208, 208, 208)"/><stop offset="0.55555" stop-color="rgb(208, 208, 208)"/><stop offset="0.55555" stop-color="rgb(200, 200, 200)"/><stop offset="1" stop-color="rgb(208, 208, 208)"/></linearGradient><path d="m 7.9131,2 0,-1.548 -2.586,2.155 0,-2.171 -2.582,2.208 2.582,2.175 0,-2.139 2.586,2.155 0,-1.276 c 3.138,0.129 5.491,2.681 5.543,5.838 l -1.031,0 0.016,0.215 1.015,0 c -0.06,3.19 -2.629,5.765 -5.819,5.833 l 0,-1.018 -0.214,0 0,1.021 c -3.21,-0.047 -5.801,-2.631 -5.862,-5.836 l 1.045,0 -0.016,-0.215 -1.045,0 c -0.052,-0.288 -0.318,-0.654 -0.766,-0.654 -0.538,0 -0.755,0.484 -0.755,0.75 0,4.146 3.331,7.506 7.476,7.506 4.146,0 7.506,-3.36 7.506,-7.506 0,-4.059 -3.066,-7.357 -7.093,-7.493" fill="url(#gradient)"/><path d="m 5.1729,11.0518 c -0.38,0 -0.668,-0.129 -0.945,-0.366 -0.083,-0.071 -0.186,-0.134 -0.338,-0.134 -0.277,0 -0.511,0.238 -0.511,0.521 0,0.154 0.083,0.301 0.179,0.383 0.394,0.346 0.911,0.563 1.601,0.563 1.077,0 1.739,-0.681 1.739,-1.608 l 0,-0.013 c 0,-0.911 -0.641,-1.265 -1.296,-1.376 l 0.945,-0.919 c 0.193,-0.19 0.317,-0.337 0.317,-0.604 0,-0.294 -0.228,-0.477 -0.538,-0.477 l -2.354,0 c -0.248,0 -0.455,0.21 -0.455,0.464 0,0.253 0.207,0.463 0.455,0.463 l 1.485,0 -0.939,0.961 c -0.166,0.169 -0.228,0.295 -0.228,0.444 0,0.25 0.207,0.463 0.455,0.463 l 0.166,0 c 0.594,0 0.945,0.222 0.945,0.624 l 0,0.012 c 0,0.367 -0.282,0.599 -0.683,0.599" fill="url(#gradient)"/><path d="m 10.354,9.5342 c 0,0.876 -0.379,1.525 -0.979,1.525 -0.599,0 -0.992,-0.655 -0.992,-1.539 l 0,-0.012 c 0,-0.884 0.388,-1.527 0.979,-1.527 0.592,0 0.992,0.663 0.992,1.539 l 0,0.014 z m -0.979,-2.512 c -1.197,0 -2.008,1.097 -2.008,2.498 l 0,0.014 c 0,1.401 0.792,2.484 1.995,2.484 1.205,0 2.01,-1.097 2.01,-2.498 l 0,-0.012 c 0,-1.402 -0.805,-2.486 -1.997,-2.486" fill="url(#gradient)"/></svg>');
+ width: 16px;
+ height: 18px;
+ margin-bottom: 1px;
+ margin-left: 6px;
+ margin-right: 4px;
+}
+
+video::-webkit-media-controls-play-button,
+audio::-webkit-media-controls-play-button {
+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 15"><linearGradient id="gradient" x2="0" y2="100%" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="rgb(216, 216, 216)"/><stop offset="0.4375" stop-color="rgb(216, 216, 216)"/><stop offset="0.4375" stop-color="rgb(208, 208, 208)"/><stop offset="0.5" stop-color="rgb(208, 208, 208)"/><stop offset="0.5" stop-color="rgb(200, 200, 200)"/><stop offset="1" stop-color="rgb(208, 208, 208)"/></linearGradient><path d="M 0,0 v 16 h 6 v -16 h -6 z" fill="url(#gradient)"/><path d="M 9,0 v 16 h 6 v -16 h -6 z" fill="url(#gradient)"/></svg>');
+ margin-left: 6px;
+ margin-right: 1px;
+}
+
+video::-webkit-media-controls-play-button.paused,
+audio::-webkit-media-controls-play-button.paused {
+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 15"><linearGradient id="gradient" x2="0" y2="100%" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="rgb(216, 216, 216)"/><stop offset="0.4375" stop-color="rgb(216, 216, 216)"/><stop offset="0.4375" stop-color="rgb(208, 208, 208)"/><stop offset="0.5" stop-color="rgb(208, 208, 208)"/><stop offset="0.5" stop-color="rgb(200, 200, 200)"/><stop offset="1" stop-color="rgb(208, 208, 208)"/></linearGradient><path d="M 0,0 15,7 0,15 z" fill="url(#gradient)"/></svg>');
+}
+
+video::-webkit-media-controls-panel .mute-box,
+audio::-webkit-media-controls-panel .mute-box {
+ width: 22px;
+ height: 22px;
+ margin-right: 2px;
+
+ 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 {
+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 15"><linearGradient id="gradient" x2="0" y2="100%" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="rgb(216, 216, 216)"/><stop offset="0.4375" stop-color="rgb(216, 216, 216)"/><stop offset="0.4375" stop-color="rgb(208, 208, 208)"/><stop offset="0.5" stop-color="rgb(208, 208, 208)"/><stop offset="0.5" stop-color="rgb(200, 200, 200)"/><stop offset="1" stop-color="rgb(208, 208, 208)"/></linearGradient><path d="m 0,9 0,-4 3,0 3,-3 0,10 -3,-3 -3,0 z" style="fill:url(#gradient) "/><path d="m 10.449,1.087 c 1.963,1.055 3.322,3.291 3.322,5.881 0,2.642 -1.402,4.913 -3.424,5.945" style="fill:none;stroke:url(#gradient);stroke-width:1.25;stroke-linecap:round;"/><path d="m 9.13,3.134 c 1.289,0.681 2.181,2.142 2.181,3.835 0,1.743001 -0.939,3.24 -2.285,3.897" style="fill:none;stroke:url(#gradient);stroke-width:1.25;stroke-linecap:round;"/><path d="M 7.794,5.175 C 8.403001,5.491 8.827001,6.167 8.827001,6.971 8.827001,7.818 8.356,8.537001 7.688,8.826" style="fill:none;stroke:url(#gradient);stroke-width:1.25;stroke-linecap:round;"/></svg>');
+ width: 14px;
+}
+
+video::-webkit-media-controls-panel .volume-box,
+audio::-webkit-media-controls-panel .volume-box {
+ position: absolute;
+ box-sizing: border-box;
+ height: 22px;
+ bottom: 0;
+ left: 0;
+
+ -webkit-transform: rotate(-90deg);
+ -webkit-transform-origin: 11px 11px;
+
+ background-color: transparent;
+ background-image: -webkit-linear-gradient(
+ left,
+ rgba(17, 17, 17, 0.92),
+ rgba(42, 42, 42, 0.92)
+ );
+ border: 1px solid rgba(0, 0, 0, 0.95);
+ border-radius: 12px;
+
+ display: -webkit-flex;
+ -webkit-flex-direction: row;
+ -webkit-align-items: center;
+ -webkit-justify-content: flex-end;
+
+ opacity: 0;
+ /* make zero width (rather than display:none) for AX and FKA */
+ width: 0; /* will become 114px when shown */
+
+}
+
+/* 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 */
+ width: 114px;
+}
+
+audio::-webkit-media-controls-volume-slider,
+video::-webkit-media-controls-volume-slider {
+ -webkit-appearance: none !important;
+ box-sizing: border-box !important;
+ height: 10px !important;
+ width: 80px !important;
+ padding: 0 !important;
+ margin-right: 6px !important;
+
+ border-radius: 5px !important;
+ background-color: transparent !important;
+ background-image: -webkit-linear-gradient(
+ top,
+ rgba(15, 15, 15, .85) 0,
+ rgba(23, 23, 23, .85) 50%,
+ rgba(15, 15, 15, .85) 100%
+ ) !important;
+ border: 1px solid rgba(0, 0, 0, 0.875) !important;
+}
+
+video::-webkit-media-controls-volume-slider::-webkit-slider-thumb,
+audio::-webkit-media-controls-volume-slider::-webkit-slider-thumb {
+ -webkit-appearance: none !important;
+ width: 8px !important;
+ height: 8px !important;
+ border-radius: 4px !important;
+ background-color: transparent !important;
+
+ /* rotateZ() forces the layer into compositing mode.
+ Slider thumbs are small, so forcing a compositing layer is inexpensive,
+ and it keeps the slider from having to repaint while sliding. */
+ -webkit-transform: rotateZ(0) !important;
+ background-image: -webkit-linear-gradient(
+ left,
+ rgba(99, 99, 99, 1),
+ rgba(144, 144, 144, 1)
+ ) !important;
+ box-shadow: inset -1px 0 0 rgba(255, 255, 255, .5), 0 1px rgba(255, 255, 255, .14) !important;
+}
+video::-webkit-media-controls-volume-slider::-webkit-slider-thumb::-webkit-slider-thumb:active,
+video::-webkit-media-controls-volume-slider::-webkit-slider-thumb:active::-webkit-slider-thumb,
+audio::-webkit-media-controls-volume-slider::-webkit-slider-thumb::-webkit-slider-thumb:active,
+audio::-webkit-media-controls-volume-slider::-webkit-slider-thumb:active::-webkit-slider-thumb {
+ background-image: -webkit-linear-gradient(
+ right top,
+ rgba(160, 160, 160, 1),
+ rgba(221, 221, 221, 1)
+ ) !important;
+}
+
+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,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 15"><linearGradient id="gradient" x2="0" y2="100%" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="rgb(216, 216, 216)"/><stop offset="0.4375" stop-color="rgb(216, 216, 216)"/><stop offset="0.4375" stop-color="rgb(208, 208, 208)"/><stop offset="0.5" stop-color="rgb(208, 208, 208)"/><stop offset="0.5" stop-color="rgb(200, 200, 200)"/><stop offset="1" stop-color="rgb(208, 208, 208)"/></linearGradient><path d="m 0,9 0,-4 3,0 3,-3 0,10 -3,-3 -3,0 z" fill="url(#gradient)"/></svg>');
+}
+
+video::-webkit-media-controls-toggle-closed-captions-button,
+audio::-webkit-media-controls-toggle-closed-captions-button {
+ width: 16px;
+ height: 16px;
+ margin: 0 7px;
+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 102 105"><linearGradient id="gradient" x2="0" y2="100%" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="rgb(216, 216, 216)"/><stop offset="0.46875" stop-color="rgb(216, 216, 216)"/><stop offset="0.46875" stop-color="rgb(208, 208, 208)"/><stop offset="0.53125" stop-color="rgb(208, 208, 208)"/><stop offset="0.53125" stop-color="rgb(200, 200, 200)"/><stop offset="1" stop-color="rgb(208, 208, 208)"/></linearGradient><path d="M98.766,43.224c0-23.163-21.775-41.94-48.637-41.94c-26.859,0-48.635,18.777-48.635,41.94c0,18.266,13.546,33.796,32.444,39.549c1.131,8.356,26.037,24.255,22.864,19.921c-4.462-6.096-5.159-13.183-5.07-17.566C77.85,84.397,98.766,65.923,98.766,43.224z" fill="url(#gradient)"/></svg>');
+ 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.85);
+ border: 3px solid rgba(128, 128, 128, 0.75);
+ border-radius: 10px;
+ 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;
+ font-family: "Helvetica Bold", Helvetica, sans-serif;
+ font-size: 10pt;
+ -webkit-user-select: none;
+}
+
+video::-webkit-media-controls-closed-captions-container h3,
+audio::-webkit-media-controls-closed-captions-container h3 {
+ margin: 0;
+ color: rgb(117, 117, 117);
+ text-shadow: 0 1px 0 black;
+ -webkit-margin-start: 23px;
+ padding-top: 4px;
+ font-weight: bold;
+ font-size: 10pt;
+}
+
+video::-webkit-media-controls-closed-captions-container ul,
+audio::-webkit-media-controls-closed-captions-container ul {
+ list-style-type: none;
+ margin: 0 0 4px 0;
+ padding: 0;
+ font-weight: bold;
+}
+
+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: 37px;
+ padding-right: 35px;
+ 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-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, rgba(255, 255, 255, 0.3)), color-stop(1, rgba(255, 255, 255, 0.2)));
+}
+
+video::-webkit-media-controls-closed-captions-container li:hover,
+audio::-webkit-media-controls-closed-captions-container li:hover {
+ background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, rgb(79, 112, 246)), color-stop(1, rgb(26, 68, 243)));
+ border-top: 1px solid rgb(70, 103, 234);
+ border-bottom: 1px solid rgb(3, 54, 229);
+}
+
+video::-webkit-media-controls-closed-captions-container li.selected::before,
+audio::-webkit-media-controls-closed-captions-container li.selected::before {
+ display: block;
+ content: "";
+ position: absolute;
+ top: 0.25em;
+ width: 1.1em;
+ height: 1.1em;
+ -webkit-margin-start: -20px;
+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 300"><polygon fill="rgb(163, 163, 163)" points="252.301,4.477 126.667,194.104 43.358,108.3 6.868,161.408 132.515,290.814 297.732,49.926"/></svg>');
+ background-repeat: no-repeat;
+}
+
+video::-webkit-media-controls-closed-captions-container li.selected:hover::before,
+audio::-webkit-media-controls-closed-captions-container li.selected:hover::before {
+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 300"><polygon fill="white" points="252.301,4.477 126.667,194.104 43.358,108.3 6.868,161.408 132.515,290.814 297.732,49.926"/></svg>');
+}
+
+video::-webkit-media-controls-fullscreen-button,
+audio::-webkit-media-controls-fullscreen-button {
+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 15" transform="rotate(90,0,0)"><linearGradient id="gradient" x2="0" y2="100%" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="rgb(216, 216, 216)"/><stop offset="0.4375" stop-color="rgb(216, 216, 216)"/><stop offset="0.4375" stop-color="rgb(208, 208, 208)"/><stop offset="0.5" stop-color="rgb(208, 208, 208)"/><stop offset="0.5" stop-color="rgb(200, 200, 200)"/><stop offset="1" stop-color="rgb(208, 208, 208)"/></linearGradient><path d="M 14,1 m 0,6 -2,-2 -2,2 c 0,0 -1,1 -2,0 -1,-1 0,-2 0,-2 l 2,-2 -2,-2 6,0 z" style="fill:url(#gradient) "/><path d="M 1,14 m 0,-6 2,2 2,-2 c 0,0 1,-1 2,0 1,1 0,2 0,2 l -2,2 2,2 -6,0 z" style="fill:url(#gradient) "/></svg>');
+ margin: 0 7px;
+}
+video::-webkit-media-controls-fullscreen-button.exit,
+audio::-webkit-media-controls-fullscreen-button.exit {
+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 15" transform="rotate(90,0,0)"><linearGradient id="gradient" x2="0" y2="100%" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="rgb(216, 216, 216)"/><stop offset="0.4375" stop-color="rgb(216, 216, 216)"/><stop offset="0.4375" stop-color="rgb(208, 208, 208)"/><stop offset="0.5" stop-color="rgb(208, 208, 208)"/><stop offset="0.5" stop-color="rgb(200, 200, 200)"/><stop offset="1" stop-color="rgb(208, 208, 208)"/></linearGradient><path d="M 7,8 m 0,6 -2,-2 -2,2 c 0,0 -1,1 -2,0 -1,-1 0,-2 0,-2 l 2,-2 -2,-2 6,0 z" style="fill:url(#gradient) "/><path d="M 8,7 m 0,-6 2,2 2,-2 c 0,0 1,-1 2,0 1,1 0,2 0,2 l -2,2 2,2 -6,0 z" style="fill:url(#gradient) "/></svg>');
+}
+video::-webkit-media-controls-status-display,
+audio::-webkit-media-controls-status-display {
+ cursor: default;
+ font: -webkit-small-control;
+ font-size: 9px;
+ overflow: hidden;
+ color: white;
+ text-shadow: black 0px 1px 1px;
+
+ 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-timeline,
+audio::-webkit-media-controls-timeline {
+ -webkit-appearance: none !important;
+ -webkit-flex: 1 1 0 !important;
+ height: 9px !important;
+ margin: 0 !important;
+
+ border-radius: 4.5px !important;
+ background-color: rgb(74, 74, 74) !important;
+ box-shadow: inset -1px 0 0 rgba(0, 0, 0, .68), 0 1px rgba(255, 255, 255, .08) !important;
+}
+video::-webkit-media-controls-timeline::-webkit-slider-thumb,
+audio::-webkit-media-controls-timeline::-webkit-slider-thumb {
+ -webkit-appearance: none !important;
+ width:6px !important;
+ height: 6px !important;
+ background-color: white !important;
+
+ /* rotateZ() forces the layer into compositing mode.
+ Slider thumbs are small, so forcing a compositing layer is inexpensive,
+ and it keeps the slider from having to repaint while sliding. */
+ -webkit-transform: translateY(1px) rotateZ(-45deg) !important;
+
+ background-image: -webkit-gradient(
+ linear,
+ left bottom,
+ right top,
+ color-stop(0, rgba(99, 99, 99, 1)),
+ color-stop(1, rgba(144, 144, 144, 1))
+ ) !important;
+}
+video::-webkit-media-controls-timeline::-webkit-slider-thumb:active,
+video::-webkit-media-controls-timeline:active::-webkit-slider-thumb,
+audio::-webkit-media-controls-timeline::-webkit-slider-thumb:active,
+audio::-webkit-media-controls-timeline:active::-webkit-slider-thumb,
+ {
+ background-image: -webkit-gradient(
+ linear,
+ left bottom,
+ right top,
+ color-stop(0, rgba(160, 160, 160, 1)),
+ color-stop(1, rgba(221, 221, 221, 1))
+ ) !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;
+ -webkit-justify-content: center;
+ -webkit-align-items: center;
+ cursor: default;
+ font: -webkit-small-control;
+ font-size: 9px;
+ overflow-y: hidden;
+ overflow-x: hidden;
+ width: 45px;
+ min-width: 45px;
+ color: white;
+ text-shadow: black 0px 1px 1px;
+ letter-spacing: normal;
+ word-spacing: normal;
+ line-height: normal;
+ text-transform: none;
+ text-indent: 0px;
+ text-decoration: none;
+}
+
+video::-webkit-media-controls-timeline-container .hour-long-time,
+audio::-webkit-media-controls-timeline-container .hour-long-time {
+ min-width: 67px;
+}
+
+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;
+ 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;
+ height: 9px;
+ 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;
+}
+
+/* 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;
+
+ width: 440px !important;
+ height: 60px !important;
+ margin: 0 auto 50px auto !important;
+ padding-top: 10px !important;
+
+ background: -webkit-linear-gradient(top,
+ rgba(45, 45, 45, .97) 0,
+ rgba(30, 30, 30, .97) 19px,
+ rgba(25, 25, 25, .97) 19px,
+ rgba(25, 25, 25, .97) 20px,
+ rgba(19, 19, 19, .97) 20px,
+ rgba(12, 12, 12, .97) 100%
+ ) !important;
+
+ box-shadow:
+ inset 0 -1px 1px rgba(0, 0, 0, 0.5),
+ inset 0 1px 0 0px rgba(255, 255, 255, 0.15),
+ inset 0 -1px 0 0px rgba(202, 202, 202, 0.09),
+ 0 0 0 1px rgba(0, 0, 0, 0.5);
+ border-radius: 8px !important;
+
+ transition: opacity 0.3s linear !important;
+}
+
+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;
+ opacity: 1;
+ left: 11px;
+ top: 13px;
+ width: 90px;
+ height: 14px;
+ display: -webkit-flex;
+ -webkit-flex-direction: row;
+ -webkit-align-items: center;
+ background-image: none;
+ border: none;
+}
+
+video:-webkit-full-screen::-webkit-media-controls-volume-slider {
+ height: 6px !important;
+ border-radius: 3px !important;
+ background-image: -webkit-linear-gradient(top,
+ rgba(16, 16, 16, .300) 0,
+ rgba(9, 9, 9, .629) 100%
+ ) !important;
+ box-shadow: inset -1px 0 0 rgba(0, 0, 0, .68), 0 1px rgba(255, 255, 255, .08) !important;
+}
+
+video:-webkit-full-screen::-webkit-media-controls-volume-slider::-webkit-slider-thumb {
+ width: 10px !important;
+ height: 10px !important;
+ border-radius: 5px !important;
+
+ /* rotateZ() forces the layer into compositing mode.
+ Slider thumbs are small, so forcing a compositing layer is inexpensive,
+ and it keeps the slider from having to repaint while sliding. */
+ -webkit-transform: rotateZ(270deg) !important;
+}
+
+video:-webkit-full-screen::-webkit-media-controls-play-button {
+ position: absolute;
+
+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21 22"><linearGradient id="gradient" x2="0" y2="100%" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="rgb(216, 216, 216)"/><stop offset="0.478" stop-color="rgb(216, 216, 216)"/><stop offset="0.478" stop-color="rgb(208, 208, 208)"/><stop offset="0.522" stop-color="rgb(208, 208, 208)"/><stop offset="0.522" stop-color="rgb(200, 200, 200)"/><stop offset="1" stop-color="rgb(208, 208, 208)"/></linearGradient><path d="M 0,0 0,22 8,22 8,0 z" fill="url(#gradient)"/><path d="M 13,0 13,22 21,22 21,0 z" fill="url(#gradient)"/></svg>');
+
+ width: 22px;
+ height: 23px;
+ left: 209px;
+ top: 9px;
+ margin: 0;
+ padding: 0;
+}
+
+video:-webkit-full-screen::-webkit-media-controls-play-button.paused {
+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21 22"><linearGradient id="gradient" x2="0" y2="100%" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="rgb(216, 216, 216)"/><stop offset="0.478" stop-color="rgb(216, 216, 216)"/><stop offset="0.478" stop-color="rgb(208, 208, 208)"/><stop offset="0.522" stop-color="rgb(208, 208, 208)"/><stop offset="0.522" stop-color="rgb(200, 200, 200)"/><stop offset="1" stop-color="rgb(208, 208, 208)"/></linearGradient><path d="M 0,0 21,11 0,22 z" fill="url(#gradient)"/></svg>');
+}
+
+video:-webkit-full-screen::-webkit-media-controls-rewind-button {
+ position: absolute;
+ left: 162px;
+ top: 13px;
+ width: 18px;
+ height: 18px;
+}
+
+video:-webkit-full-screen::-webkit-media-controls-seek-back-button {
+ position: absolute;
+
+
+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 15"><linearGradient id="gradient" x2="0" y2="100%" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="rgb(216, 216, 216)"/><stop offset="0.4375" stop-color="rgb(216, 216, 216)"/><stop offset="0.4375" stop-color="rgb(208, 208, 208)"/><stop offset="0.5" stop-color="rgb(208, 208, 208)"/><stop offset="0.5" stop-color="rgb(200, 200, 200)"/><stop offset="1" stop-color="rgb(208, 208, 208)"/></linearGradient><path d="M 22,0 11,7 22,15 z" fill="url(#gradient)"/><path d="M 11,0 0,7 11,15 z" fill="url(#gradient)"/></svg>');
+
+ width: 23px;
+ height: 16px;
+ left: 156px;
+ top: 13px;
+}
+
+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,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 15"><linearGradient id="gradient" x2="0" y2="100%" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="rgb(216, 216, 216)"/><stop offset="0.4375" stop-color="rgb(216, 216, 216)"/><stop offset="0.4375" stop-color="rgb(208, 208, 208)"/><stop offset="0.5" stop-color="rgb(208, 208, 208)"/><stop offset="0.5" stop-color="rgb(200, 200, 200)"/><stop offset="1" stop-color="rgb(208, 208, 208)"/></linearGradient><path d="M 0,0 11,7 0,15 z" fill="url(#gradient)"/><path d="M 11,0 22,7 11,15 z" fill="url(#gradient)"/></svg>');
+
+ width: 23px;
+ height: 16px;
+ left: 256px;
+ top: 13px;
+}
+
+video:-webkit-full-screen::-webkit-media-controls-timeline-container {
+ height: auto;
+ width: 420px;
+ position: absolute;
+ bottom: 9px;
+ left: 8px;
+ right: 8px;
+}
+
+video:-webkit-full-screen::-webkit-media-controls-status-display {
+ width: 420px;
+ position: absolute;
+ bottom: 7px;
+ left: 8px;
+ right: 8px;
+}
+
+video:-webkit-full-screen::-webkit-media-controls-closed-captions-container {
+ bottom: 114px;
+ 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;
+
+ 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;
+}
diff --git a/Source/WebCore/Modules/mediacontrols/mediaControlsBase.js b/Source/WebCore/Modules/mediacontrols/mediaControlsBase.js
new file mode 100644
index 000000000..f78696d64
--- /dev/null
+++ b/Source/WebCore/Modules/mediacontrols/mediaControlsBase.js
@@ -0,0 +1,1336 @@
+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.hasVisualMedia = false;
+
+ this.addVideoListeners();
+ this.createBase();
+ this.createControls();
+ this.updateBase();
+ this.updateControls();
+ this.updateDuration();
+ this.updateProgress();
+ this.updateTime();
+ this.updateReadyState();
+ this.updatePlaying();
+ this.updateThumbnail();
+ this.updateCaptionButton();
+ this.updateCaptionContainer();
+ this.updateFullscreenButton();
+ this.updateVolume();
+ this.updateHasAudio();
+ this.updateHasVideo();
+};
+
+/* Enums */
+Controller.InlineControls = 0;
+Controller.FullScreenControls = 1;
+
+Controller.PlayAfterSeeking = 0;
+Controller.PauseAfterSeeking = 1;
+
+/* Globals */
+Controller.gLastTimelineId = 0;
+
+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',
+ },
+ HideControlsDelay: 4 * 1000,
+ RewindAmount: 30,
+ MaximumSeekRate: 8,
+ SeekDelay: 1500,
+ ClassNames: {
+ active: 'active',
+ exit: 'exit',
+ failed: 'failed',
+ hidden: 'hidden',
+ hiding: 'hiding',
+ hourLongTime: 'hour-long-time',
+ list: 'list',
+ muteBox: 'mute-box',
+ muted: 'muted',
+ paused: 'paused',
+ playing: 'playing',
+ selected: 'selected',
+ show: 'show',
+ thumbnail: 'thumbnail',
+ thumbnailImage: 'thumbnail-image',
+ thumbnailTrack: 'thumbnail-track',
+ volumeBox: 'volume-box',
+ noVideo: 'no-video',
+ down: 'down',
+ out: 'out',
+ },
+ KeyCodes: {
+ enter: 13,
+ escape: 27,
+ space: 32,
+ pageUp: 33,
+ pageDown: 34,
+ end: 35,
+ home: 36,
+ left: 37,
+ up: 38,
+ right: 39,
+ down: 40
+ },
+
+ extend: function(child)
+ {
+ for (var property in this) {
+ if (!child.hasOwnProperty(property))
+ child[property] = this[property];
+ }
+ },
+
+ 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.updateHasAudio);
+ this.listenFor(this.video.audioTracks, 'addtrack', this.updateHasAudio);
+ this.listenFor(this.video.audioTracks, 'removetrack', this.updateHasAudio);
+
+ /* 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'] });
+ },
+
+ 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.updateHasAudio);
+ this.stopListeningFor(this.video.audioTracks, 'addtrack', this.updateHasAudio);
+ this.stopListeningFor(this.video.audioTracks, 'removetrack', this.updateHasAudio);
+
+ /* 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);
+ },
+
+ 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(base, 'mouseout', this.handleWrapperMouseOut);
+ if (this.host.textTrackContainer)
+ base.appendChild(this.host.textTrackContainer);
+ },
+
+ shouldHaveAnyUI: function()
+ {
+ return this.shouldHaveControls() || (this.video.textTracks && this.video.textTracks.length);
+ },
+
+ shouldHaveControls: function()
+ {
+ return this.video.controls || this.isFullScreen();
+ },
+
+ setNeedsTimelineMetricsUpdate: function()
+ {
+ this.timelineMetricsNeedsUpdate = true;
+ },
+
+ updateTimelineMetricsIfNeeded: function()
+ {
+ if (this.timelineMetricsNeedsUpdate) {
+ 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 panelCompositedParent = this.controls.panelCompositedParent = document.createElement('div');
+ panelCompositedParent.setAttribute('pseudo', '-webkit-media-controls-panel-composited-parent');
+
+ 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);
+
+ 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');
+ this.timelineID = ++Controller.gLastTimelineId;
+ timeline.setAttribute('pseudo', '-webkit-media-controls-timeline');
+ timeline.setAttribute('aria-label', this.UIString('Duration'));
+ timeline.style.backgroundImage = '-webkit-canvas(timeline-' + this.timelineID + ')';
+ timeline.type = 'range';
+ timeline.value = 0;
+ this.listenFor(timeline, 'input', 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);
+ timeline.step = .01;
+
+ 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);
+
+ var muteButton = this.controls.muteButton = document.createElement('button');
+ muteButton.setAttribute('pseudo', '-webkit-media-controls-mute-button');
+ muteButton.setAttribute('aria-label', this.UIString('Mute'));
+ 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 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 = .01;
+ this.listenFor(volume, 'input', this.handleVolumeSliderInput);
+
+ 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', 'audioTrackMenu');
+ 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);
+ },
+
+ setControlsType: function(type)
+ {
+ if (type === this.controlsType)
+ return;
+ this.controlsType = type;
+
+ this.reconnectControls();
+ },
+
+ 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.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()
+ {
+ if (!this.isLive)
+ this.controls.panel.appendChild(this.controls.rewindButton);
+ this.controls.panel.appendChild(this.controls.playButton);
+ 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.volume);
+ this.controls.muteBox.appendChild(this.controls.muteButton);
+ this.controls.panel.appendChild(this.controls.captionButton);
+ if (!this.isAudio())
+ 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.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.captionButton);
+ if (!this.isAudio())
+ 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.setNeedsTimelineMetricsUpdate();
+ },
+
+ updateStatusDisplay: function(event)
+ {
+ 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.video.networkState === HTMLMediaElement.NETWORK_LOADING)
+ this.controls.statusDisplay.innerText = this.UIString('Loading');
+ else
+ this.controls.statusDisplay.innerText = '';
+
+ this.setStatusHidden(!this.isLive && this.video.readyState > HTMLMediaElement.HAVE_NOTHING && !this.video.error);
+ },
+
+ 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.hasVisualMedia = this.video.videoTracks && this.video.videoTracks.length > 0;
+ this.updateReadyState();
+ this.updateDuration();
+ this.updateCaptionButton();
+ this.updateCaptionContainer();
+ this.updateFullscreenButton();
+ this.updateProgress();
+ },
+
+ handleTimeUpdate: function(event)
+ {
+ if (!this.scrubbing)
+ this.updateTime();
+ },
+
+ handleDurationChange: function(event)
+ {
+ this.updateDuration();
+ this.updateTime(true);
+ this.updateProgress(true);
+ },
+
+ 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();
+ },
+
+ isFullScreen: function()
+ {
+ return this.video.webkitDisplayingFullscreen;
+ },
+
+ handleFullscreenChange: function(event)
+ {
+ this.updateBase();
+ this.updateControls();
+
+ 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();
+ }
+ },
+
+ handleWrapperMouseMove: function(event)
+ {
+ 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.panel)
+ 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) > 0)
+ this.controls.panel.classList.remove(this.ClassNames.hidden);
+ else
+ this.controls.panel.classList.add(this.ClassNames.hidden);
+ },
+
+ handlePanelClick: function(event)
+ {
+ // Prevent clicks in the panel from playing or pausing the video in a MediaDocument.
+ event.preventDefault();
+ },
+
+ handleRewindButtonClicked: function(event)
+ {
+ var newTime = Math.max(
+ this.video.currentTime - this.RewindAmount,
+ this.video.seekable.start(0));
+ this.video.currentTime = newTime;
+ return true;
+ },
+
+ canPlay: function()
+ {
+ return this.video.paused || this.video.ended || this.video.readyState < HTMLMediaElement.HAVE_METADATA;
+ },
+
+ handlePlayButtonClicked: function(event)
+ {
+ if (this.canPlay())
+ this.video.play();
+ else
+ this.video.pause();
+ return true;
+ },
+
+ handleTimelineChange: function(event)
+ {
+ this.video.fastSeek(this.controls.timeline.value);
+ },
+
+ handleTimelineDown: function(event)
+ {
+ this.controls.thumbnail.classList.add(this.ClassNames.show);
+ },
+
+ handleTimelineUp: function(event)
+ {
+ this.controls.thumbnail.classList.remove(this.ClassNames.show);
+ },
+
+ handleTimelineMouseOver: function(event)
+ {
+ this.controls.thumbnail.classList.add(this.ClassNames.show);
+ },
+
+ handleTimelineMouseOut: function(event)
+ {
+ this.controls.thumbnail.classList.remove(this.ClassNames.show);
+ },
+
+ handleTimelineMouseMove: function(event)
+ {
+ if (this.controls.thumbnail.classList.contains(this.ClassNames.hidden))
+ return;
+
+ this.updateTimelineMetricsIfNeeded();
+ this.controls.thumbnail.classList.add(this.ClassNames.show);
+ var localPoint = webkitConvertPointFromPageToNode(this.controls.timeline, new WebKitPoint(event.clientX, event.clientY));
+ var percent = (localPoint.x - this.timelineLeft) / this.timelineWidth;
+ percent = Math.max(Math.min(1, percent), 0);
+ this.controls.thumbnail.style.left = percent * 100 + '%';
+
+ var thumbnailTime = percent * this.video.duration;
+ for (var i = 0; i < this.video.textTracks.length; ++i) {
+ var track = this.video.textTracks[i];
+ if (!this.trackHasThumbnails(track))
+ continue;
+
+ if (!track.cues)
+ continue;
+
+ for (var j = 0; j < track.cues.length; ++j) {
+ var cue = track.cues[j];
+ if (thumbnailTime >= cue.startTime && thumbnailTime < cue.endTime) {
+ this.controls.thumbnailImage.src = cue.text;
+ return;
+ }
+ }
+ }
+ },
+
+ handleTimelineMouseDown: function(event)
+ {
+ this.scrubbing = true;
+ },
+
+ handleTimelineMouseUp: function(event)
+ {
+ this.scrubbing = false;
+
+ // Do a precise seek when we lift the mouse:
+ this.video.currentTime = this.controls.timeline.value;
+ },
+
+ handleMuteButtonClicked: function(event)
+ {
+ this.video.muted = !this.video.muted;
+ if (this.video.muted)
+ this.controls.muteButton.setAttribute('aria-label', this.UIString('Unmute'));
+ return true;
+ },
+
+ handleMinButtonClicked: function(event)
+ {
+ if (this.video.muted) {
+ this.video.muted = false;
+ this.controls.muteButton.setAttribute('aria-label', this.UIString('Mute'));
+ }
+ this.video.volume = 0;
+ return true;
+ },
+
+ handleMaxButtonClicked: function(event)
+ {
+ if (this.video.muted) {
+ this.video.muted = false;
+ this.controls.muteButton.setAttribute('aria-label', this.UIString('Mute'));
+ }
+ this.video.volume = 1;
+ },
+
+ handleVolumeSliderInput: function(event)
+ {
+ if (this.video.muted) {
+ this.video.muted = false;
+ this.controls.muteButton.setAttribute('aria-label', this.UIString('Mute'));
+ }
+ this.video.volume = this.controls.volume.value;
+ },
+
+ handleCaptionButtonClicked: function(event)
+ {
+ if (this.captionMenu)
+ this.destroyCaptionMenu();
+ else
+ this.buildCaptionMenu();
+ return true;
+ },
+
+ updateFullscreenButton: function()
+ {
+ this.controls.fullscreenButton.classList.toggle(this.ClassNames.hidden, (!this.video.webkitSupportsFullscreen || !this.hasVisualMedia));
+ },
+
+ handleFullscreenButtonClicked: function(event)
+ {
+ if (this.isFullScreen())
+ this.video.webkitExitFullscreen();
+ else
+ this.video.webkitEnterFullscreen();
+ return true;
+ },
+
+ handleControlsChange: function()
+ {
+ try {
+ this.updateBase();
+
+ if (this.shouldHaveControls())
+ this.addControls();
+ else
+ this.removeControls();
+ } catch(e) {
+ if (window.console)
+ console.error(e);
+ }
+ },
+
+ nextRate: function()
+ {
+ return Math.min(this.MaximumSeekRate, Math.abs(this.video.playbackRate * 2));
+ },
+
+ handleSeekBackMouseDown: function(event)
+ {
+ this.actionAfterSeeking = (this.canPlay() ? Controller.PauseAfterSeeking : Controller.PlayAfterSeeking);
+ this.video.play();
+ this.video.playbackRate = this.nextRate() * -1;
+ this.seekInterval = setInterval(this.seekBackFaster.bind(this), this.SeekDelay);
+ },
+
+ seekBackFaster: function()
+ {
+ this.video.playbackRate = this.nextRate() * -1;
+ },
+
+ handleSeekBackMouseUp: function(event)
+ {
+ this.video.playbackRate = this.video.defaultPlaybackRate;
+ if (this.actionAfterSeeking === Controller.PauseAfterSeeking)
+ this.video.pause();
+ else if (this.actionAfterSeeking === Controller.PlayAfterSeeking)
+ this.video.play();
+ if (this.seekInterval)
+ clearInterval(this.seekInterval);
+ },
+
+ handleSeekForwardMouseDown: function(event)
+ {
+ this.actionAfterSeeking = (this.canPlay() ? Controller.PauseAfterSeeking : Controller.PlayAfterSeeking);
+ this.video.play();
+ this.video.playbackRate = this.nextRate();
+ this.seekInterval = setInterval(this.seekForwardFaster.bind(this), this.SeekDelay);
+ },
+
+ seekForwardFaster: function()
+ {
+ this.video.playbackRate = this.nextRate();
+ },
+
+ handleSeekForwardMouseUp: function(event)
+ {
+ this.video.playbackRate = this.video.defaultPlaybackRate;
+ if (this.actionAfterSeeking === Controller.PauseAfterSeeking)
+ this.video.pause();
+ else if (this.actionAfterSeeking === Controller.PlayAfterSeeking)
+ this.video.play();
+ if (this.seekInterval)
+ clearInterval(this.seekInterval);
+ },
+
+ updateDuration: function()
+ {
+ var duration = this.video.duration;
+ this.controls.timeline.min = 0;
+ this.controls.timeline.max = duration;
+
+ this.setIsLive(duration === Number.POSITIVE_INFINITY);
+
+ this.controls.currentTime.classList.toggle(this.ClassNames.hourLongTime, duration >= 60*60);
+ this.controls.remainingTime.classList.toggle(this.ClassNames.hourLongTime, duration >= 60*60);
+ },
+
+ progressFillStyle: function(context)
+ {
+ var height = this.timelineHeight;
+ var gradient = context.createLinearGradient(0, 0, 0, height);
+ gradient.addColorStop(0, 'rgb(2, 2, 2)');
+ gradient.addColorStop(1, 'rgb(23, 23, 23)');
+ return gradient;
+ },
+
+ updateProgress: function(forceUpdate)
+ {
+ if (!forceUpdate && this.controlsAreHidden())
+ return;
+
+ this.updateTimelineMetricsIfNeeded();
+
+ var width = this.timelineWidth;
+ var height = this.timelineHeight;
+
+ var context = document.getCSSCanvasContext('2d', 'timeline-' + this.timelineID, width, height);
+ context.clearRect(0, 0, width, height);
+
+ context.fillStyle = this.progressFillStyle(context);
+
+ var duration = this.video.duration;
+ var buffered = this.video.buffered;
+ for (var i = 0, end = buffered.length; i < end; ++i) {
+ var startTime = buffered.start(i);
+ var endTime = buffered.end(i);
+
+ var startX = width * startTime / duration;
+ var endX = width * endTime / duration;
+ context.fillRect(startX, 0, endX - startX, height);
+ }
+ },
+
+ formatTime: function(time)
+ {
+ if (isNaN(time))
+ time = 0;
+ var absTime = Math.abs(time);
+ var intSeconds = Math.floor(absTime % 60).toFixed(0);
+ var intMinutes = Math.floor((absTime / 60) % 60).toFixed(0);
+ var intHours = Math.floor(absTime / (60 * 60)).toFixed(0);
+ var sign = time < 0 ? '-' : String();
+
+ if (intHours > 0)
+ return sign + intHours + ':' + String('00' + intMinutes).slice(-2) + ":" + String('00' + intSeconds).slice(-2);
+
+ return sign + String('00' + intMinutes).slice(-2) + ":" + String('00' + intSeconds).slice(-2)
+ },
+
+ updatePlaying: function()
+ {
+ this.setPlaying(!this.canPlay());
+ },
+
+ setPlaying: function(isPlaying)
+ {
+ if (this.isPlaying === isPlaying)
+ return;
+ this.isPlaying = isPlaying;
+
+ if (!isPlaying) {
+ this.controls.panel.classList.add(this.ClassNames.paused);
+ this.controls.playButton.classList.add(this.ClassNames.paused);
+ this.controls.playButton.setAttribute('aria-label', this.UIString('Play'));
+ this.showControls();
+ } else {
+ this.controls.panel.classList.remove(this.ClassNames.paused);
+ this.controls.playButton.classList.remove(this.ClassNames.paused);
+ this.controls.playButton.setAttribute('aria-label', this.UIString('Pause'));
+
+ this.hideControls();
+ this.resetHideControlsTimer();
+ }
+ },
+
+ showControls: function()
+ {
+ this.controls.panel.classList.add(this.ClassNames.show);
+ this.controls.panel.classList.remove(this.ClassNames.hidden);
+
+ this.updateTime();
+ this.setNeedsTimelineMetricsUpdate();
+ },
+
+ hideControls: function()
+ {
+ this.controls.panel.classList.remove(this.ClassNames.show);
+ },
+
+ controlsAreAlwaysVisible: function()
+ {
+ return this.controls.panel.classList.contains(this.ClassNames.noVideo);
+ },
+
+ controlsAreHidden: function()
+ {
+ if (this.controlsAreAlwaysVisible())
+ return false;
+
+ var panel = this.controls.panel;
+ return (!panel.classList.contains(this.ClassNames.show) || panel.classList.contains(this.ClassNames.hidden))
+ && (panel.parentElement.querySelector(':hover') !== panel);
+ },
+
+ removeControls: function()
+ {
+ if (this.controls.panel.parentNode)
+ this.controls.panel.parentNode.removeChild(this.controls.panel);
+ this.destroyCaptionMenu();
+ },
+
+ addControls: function()
+ {
+ this.base.appendChild(this.controls.panelCompositedParent);
+ this.controls.panelCompositedParent.appendChild(this.controls.panel);
+ this.setNeedsTimelineMetricsUpdate();
+ },
+
+ updateTime: function(forceUpdate)
+ {
+ if (!forceUpdate && this.controlsAreHidden())
+ return;
+
+ var currentTime = this.video.currentTime;
+ var timeRemaining = currentTime - this.video.duration;
+ this.controls.currentTime.innerText = this.formatTime(currentTime);
+ this.controls.timeline.value = this.video.currentTime;
+ this.controls.remainingTime.innerText = this.formatTime(timeRemaining);
+ },
+
+ updateReadyState: function()
+ {
+ this.updateStatusDisplay();
+ },
+
+ setStatusHidden: function(hidden)
+ {
+ if (this.statusHidden === hidden)
+ return;
+
+ this.statusHidden = hidden;
+
+ if (hidden) {
+ this.controls.statusDisplay.classList.add(this.ClassNames.hidden);
+ this.controls.currentTime.classList.remove(this.ClassNames.hidden);
+ this.controls.timeline.classList.remove(this.ClassNames.hidden);
+ this.controls.remainingTime.classList.remove(this.ClassNames.hidden);
+ this.setNeedsTimelineMetricsUpdate();
+ } else {
+ this.controls.statusDisplay.classList.remove(this.ClassNames.hidden);
+ this.controls.currentTime.classList.add(this.ClassNames.hidden);
+ this.controls.timeline.classList.add(this.ClassNames.hidden);
+ this.controls.remainingTime.classList.add(this.ClassNames.hidden);
+ }
+ },
+
+ trackHasThumbnails: function(track)
+ {
+ return track.kind === 'thumbnails' || (track.kind === 'metadata' && track.label === 'thumbnails');
+ },
+
+ updateThumbnail: function()
+ {
+ for (var i = 0; i < this.video.textTracks.length; ++i) {
+ var track = this.video.textTracks[i];
+ if (this.trackHasThumbnails(track)) {
+ this.controls.thumbnail.classList.remove(this.ClassNames.hidden);
+ return;
+ }
+ }
+
+ this.controls.thumbnail.classList.add(this.ClassNames.hidden);
+ },
+
+ updateCaptionButton: function()
+ {
+ if (this.video.webkitHasClosedCaptions)
+ this.controls.captionButton.classList.remove(this.ClassNames.hidden);
+ else
+ this.controls.captionButton.classList.add(this.ClassNames.hidden);
+ },
+
+ updateCaptionContainer: function()
+ {
+ if (!this.host.textTrackContainer)
+ return;
+
+ var hasClosedCaptions = this.video.webkitHasClosedCaptions;
+ var hasHiddenClass = this.host.textTrackContainer.classList.contains(this.ClassNames.hidden);
+
+ if (hasClosedCaptions && hasHiddenClass)
+ this.host.textTrackContainer.classList.remove(this.ClassNames.hidden);
+ else if (!hasClosedCaptions && !hasHiddenClass)
+ this.host.textTrackContainer.classList.add(this.ClassNames.hidden);
+
+ this.updateBase();
+ this.host.updateTextTrackContainer();
+ },
+
+ buildCaptionMenu: function()
+ {
+ var tracks = this.host.sortedTrackListForMenu(this.video.textTracks);
+ if (!tracks || !tracks.length)
+ return;
+
+ this.captionMenu = document.createElement('div');
+ this.captionMenu.setAttribute('pseudo', '-webkit-media-controls-closed-captions-container');
+ this.captionMenu.setAttribute('id', 'audioTrackMenu');
+ this.base.appendChild(this.captionMenu);
+ this.captionMenuItems = [];
+
+ var offItem = this.host.captionMenuOffItem;
+ var automaticItem = this.host.captionMenuAutomaticItem;
+ var displayMode = this.host.captionDisplayMode;
+
+ var list = document.createElement('div');
+ this.captionMenu.appendChild(list);
+ list.classList.add(this.ClassNames.list);
+
+ var heading = document.createElement('h3');
+ heading.id = 'webkitMediaControlsClosedCaptionsHeading'; // for AX menu label
+ list.appendChild(heading);
+ heading.innerText = this.UIString('Subtitles');
+
+ var ul = document.createElement('ul');
+ ul.setAttribute('role', 'menu');
+ ul.setAttribute('aria-labelledby', 'webkitMediaControlsClosedCaptionsHeading');
+ list.appendChild(ul);
+
+ for (var i = 0; i < tracks.length; ++i) {
+ var menuItem = document.createElement('li');
+ menuItem.setAttribute('role', 'menuitemradio');
+ menuItem.setAttribute('tabindex', '-1');
+ this.captionMenuItems.push(menuItem);
+ this.listenFor(menuItem, 'click', this.captionItemSelected);
+ this.listenFor(menuItem, 'keyup', this.handleCaptionItemKeyUp);
+ ul.appendChild(menuItem);
+
+ var track = tracks[i];
+ menuItem.innerText = this.host.displayNameForTrack(track);
+ menuItem.track = track;
+
+ if (track === offItem) {
+ var offMenu = menuItem;
+ continue;
+ }
+
+ if (track === automaticItem) {
+ if (displayMode === 'automatic') {
+ menuItem.classList.add(this.ClassNames.selected);
+ menuItem.setAttribute('tabindex', '0');
+ menuItem.setAttribute('aria-checked', 'true');
+ }
+ continue;
+ }
+
+ if (displayMode != 'automatic' && track.mode === 'showing') {
+ var trackMenuItemSelected = true;
+ menuItem.classList.add(this.ClassNames.selected);
+ menuItem.setAttribute('tabindex', '0');
+ menuItem.setAttribute('aria-checked', 'true');
+ }
+
+ }
+
+ if (offMenu && displayMode === 'forced-only' && !trackMenuItemSelected) {
+ offMenu.classList.add(this.ClassNames.selected);
+ menuItem.setAttribute('tabindex', '0');
+ menuItem.setAttribute('aria-checked', 'true');
+ }
+
+ // focus first selected menuitem
+ for (var i = 0, c = this.captionMenuItems.length; i < c; i++) {
+ var item = this.captionMenuItems[i];
+ if (item.classList.contains(this.ClassNames.selected)) {
+ item.focus();
+ break;
+ }
+ }
+
+ },
+
+ captionItemSelected: function(event)
+ {
+ this.host.setSelectedTextTrack(event.target.track);
+ this.destroyCaptionMenu();
+ },
+
+ focusSiblingCaptionItem: function(event)
+ {
+ var currentItem = event.target;
+ var pendingItem = false;
+ switch(event.keyCode) {
+ case this.KeyCodes.left:
+ case this.KeyCodes.up:
+ pendingItem = currentItem.previousSibling;
+ break;
+ case this.KeyCodes.right:
+ case this.KeyCodes.down:
+ pendingItem = currentItem.nextSibling;
+ break;
+ }
+ if (pendingItem) {
+ currentItem.setAttribute('tabindex', '-1');
+ pendingItem.setAttribute('tabindex', '0');
+ pendingItem.focus();
+ }
+ },
+
+ handleCaptionItemKeyUp: function(event)
+ {
+ switch (event.keyCode) {
+ case this.KeyCodes.enter:
+ case this.KeyCodes.space:
+ this.captionItemSelected(event);
+ break;
+ case this.KeyCodes.escape:
+ this.destroyCaptionMenu();
+ break;
+ case this.KeyCodes.left:
+ case this.KeyCodes.up:
+ case this.KeyCodes.right:
+ case this.KeyCodes.down:
+ this.focusSiblingCaptionItem(event);
+ break;
+ default:
+ return;
+ }
+ // handled
+ event.stopPropagation();
+ event.preventDefault();
+ },
+
+ destroyCaptionMenu: function()
+ {
+ if (!this.captionMenu)
+ return;
+
+ this.captionMenuItems.forEach(function(item){
+ this.stopListeningFor(item, 'click', this.captionItemSelected);
+ this.stopListeningFor(item, 'keyup', this.handleCaptionItemKeyUp);
+ }, this);
+
+ // FKA and AX: focus the trigger before destroying the element with focus
+ if (this.controls.captionButton)
+ this.controls.captionButton.focus();
+
+ if (this.captionMenu.parentNode)
+ this.captionMenu.parentNode.removeChild(this.captionMenu);
+ delete this.captionMenu;
+ delete this.captionMenuItems;
+ },
+
+ updateHasAudio: function()
+ {
+ if (this.video.audioTracks.length)
+ this.controls.muteBox.classList.remove(this.ClassNames.hidden);
+ else
+ this.controls.muteBox.classList.add(this.ClassNames.hidden);
+ },
+
+ updateHasVideo: function()
+ {
+ if (this.video.videoTracks.length)
+ this.controls.panel.classList.remove(this.ClassNames.noVideo);
+ else
+ this.controls.panel.classList.add(this.ClassNames.noVideo);
+ },
+
+ updateVolume: function()
+ {
+ if (this.video.muted || !this.video.volume) {
+ this.controls.muteButton.classList.add(this.ClassNames.muted);
+ this.controls.volume.value = 0;
+ } else {
+ this.controls.muteButton.classList.remove(this.ClassNames.muted);
+ this.controls.volume.value = this.video.volume;
+ }
+ },
+
+ isAudio: function()
+ {
+ return this.video instanceof HTMLAudioElement;
+ },
+
+ clearHideControlsTimer: function()
+ {
+ if (this.hideTimer)
+ clearTimeout(this.hideTimer);
+ this.hideTimer = null;
+ },
+
+ resetHideControlsTimer: function()
+ {
+ if (this.hideTimer)
+ clearTimeout(this.hideTimer);
+ this.hideTimer = setTimeout(this.hideControls.bind(this), this.HideControlsDelay);
+ },
+};
diff --git a/Source/WebCore/Modules/mediacontrols/mediaControlsGtk.js b/Source/WebCore/Modules/mediacontrols/mediaControlsGtk.js
new file mode 100644
index 000000000..bca7776c8
--- /dev/null
+++ b/Source/WebCore/Modules/mediacontrols/mediaControlsGtk.js
@@ -0,0 +1,258 @@
+function createControls(root, video, host)
+{
+ return new ControllerGtk(root, video, host);
+};
+
+function ControllerGtk(root, video, host)
+{
+ Controller.call(this, root, video, host);
+};
+
+function contains(list, obj)
+{
+ var i = list.length;
+ while (i--)
+ if (list[i] === obj)
+ return true;
+ return false;
+};
+
+ControllerGtk.prototype = {
+
+ createControls: function()
+ {
+ Controller.prototype.createControls.apply(this);
+
+ this.controls.currentTime.classList.add(this.ClassNames.hidden);
+ this.controls.remainingTime.classList.add(this.ClassNames.hidden);
+
+ this.controls.volumeBox.classList.add(this.ClassNames.hiding);
+
+ this.listenFor(this.controls.muteBox, 'mouseout', this.handleVolumeBoxMouseOut);
+ this.listenFor(this.controls.muteButton, 'mouseover', this.handleMuteButtonMouseOver);
+ this.listenFor(this.controls.volumeBox, 'mouseover', this.handleMuteButtonMouseOver);
+ this.listenFor(this.controls.volume, 'mouseover', this.handleMuteButtonMouseOver);
+ this.listenFor(this.controls.captionButton, 'mouseover', this.handleCaptionButtonMouseOver);
+ this.listenFor(this.controls.captionButton, 'mouseout', this.handleCaptionButtonMouseOut);
+
+ var enclosure = this.controls.enclosure = document.createElement('div');
+ enclosure.setAttribute('pseudo', '-webkit-media-controls-enclosure');
+ },
+
+ configureInlineControls: function()
+ {
+ this.controls.panel.appendChild(this.controls.playButton);
+ this.controls.panel.appendChild(this.controls.timeline);
+ this.controls.panel.appendChild(this.controls.currentTime);
+ this.controls.panel.appendChild(this.controls.remainingTime);
+ this.controls.panel.appendChild(this.controls.captionButton);
+ this.controls.panel.appendChild(this.controls.fullscreenButton);
+ this.controls.panel.appendChild(this.controls.muteBox);
+ this.controls.muteBox.appendChild(this.controls.muteButton);
+ this.controls.muteBox.appendChild(this.controls.volumeBox);
+ this.controls.volumeBox.appendChild(this.controls.volume);
+ this.controls.enclosure.appendChild(this.controls.panel);
+ },
+
+ shouldHaveControls: function()
+ {
+ return this.video.controls || this.isFullScreen();
+ },
+
+ reconnectControls: function()
+ {
+ Controller.prototype.disconnectControls.apply(this, arguments);
+
+ this.configureInlineControls();
+
+ if (this.shouldHaveControls())
+ this.addControls();
+ },
+
+ setStatusHidden: function(hidden)
+ {
+ },
+
+ updateTime: function(forceUpdate)
+ {
+ if (!forceUpdate && this.controlsAreHidden())
+ return;
+
+ var currentTime = this.video.currentTime;
+ var duration = this.video.duration;
+
+ this.controls.currentTime.innerText = this.formatTime(currentTime);
+ this.controls.timeline.value = currentTime;
+ if (duration === Infinity || duration === NaN)
+ this.controls.remainingTime.classList.add(this.ClassNames.hidden);
+ else {
+ this.controls.currentTime.innerText += " / " + this.formatTime(duration);
+ this.controls.remainingTime.innerText = this.formatTime(duration);
+ if (this.controls.currentTime.classList.contains(this.ClassNames.hidden))
+ this.controls.remainingTime.classList.remove(this.ClassNames.hidden);
+ }
+
+ if (currentTime > 0)
+ this.showCurrentTime();
+ },
+
+ showCurrentTime: function()
+ {
+ this.controls.currentTime.classList.remove(this.ClassNames.hidden);
+ this.controls.remainingTime.classList.add(this.ClassNames.hidden);
+ },
+
+ handlePlay: function(event)
+ {
+ Controller.prototype.handlePlay.apply(this, arguments);
+ this.showCurrentTime();
+ if (!this.isLive)
+ this.showCurrentTime();
+ },
+
+ handleTimeUpdate: function(event)
+ {
+ this.updateTime();
+ },
+
+ handleMuteButtonMouseOver: function(event)
+ {
+ if (this.video.offsetTop + this.controls.enclosure.offsetTop < 105) {
+ this.controls.volumeBox.classList.add(this.ClassNames.down);
+ this.controls.panel.classList.add(this.ClassNames.down);
+ } else {
+ this.controls.volumeBox.classList.remove(this.ClassNames.down);
+ this.controls.panel.classList.remove(this.ClassNames.down);
+ }
+ this.controls.volumeBox.classList.remove(this.ClassNames.hiding);
+ return true;
+ },
+
+ handleVolumeBoxMouseOut: function(event)
+ {
+ this.controls.volumeBox.classList.add(this.ClassNames.hiding);
+ return true;
+ },
+
+ removeControls: function()
+ {
+ if (this.controls.enclosure.parentNode)
+ this.controls.enclosure.parentNode.removeChild(this.controls.enclosure);
+ this.destroyCaptionMenu();
+ },
+
+ addControls: function()
+ {
+ this.base.appendChild(this.controls.enclosure);
+ },
+
+ updateReadyState: function()
+ {
+ if (this.host.supportsFullscreen && this.video.videoTracks.length)
+ this.controls.fullscreenButton.classList.remove(this.ClassNames.hidden);
+ else
+ this.controls.fullscreenButton.classList.add(this.ClassNames.hidden);
+ this.updateVolume();
+ },
+
+ updateDuration: function()
+ {
+ Controller.prototype.updateDuration.apply(this, arguments);
+ if (this.isLive)
+ this.controls.timeline.max = 0;
+ },
+
+ setIsLive: function(live)
+ {
+ Controller.prototype.setIsLive.apply(this, arguments);
+ this.controls.timeline.disabled = this.isLive;
+ },
+
+ updatePlaying: function()
+ {
+ Controller.prototype.updatePlaying.apply(this, arguments);
+ if (!this.canPlay())
+ this.showControls();
+ },
+
+ handleCaptionButtonClicked: function(event)
+ {
+ this.handleCaptionButtonShowMenu(event)
+ return true;
+ },
+
+ buildCaptionMenu: function()
+ {
+ Controller.prototype.buildCaptionMenu.apply(this, arguments);
+
+ this.listenFor(this.captionMenu, 'mouseout', this.handleCaptionMouseOut);
+ this.listenFor(this.captionMenu, 'transitionend', this.captionMenuTransitionEnd);
+
+ this.captionMenu.captionMenuTreeElements = this.captionMenu.getElementsByTagName("*");
+
+ // Caption menu has to be centered to the caption button.
+ var captionButtonCenter = this.controls.panel.offsetLeft + this.controls.captionButton.offsetLeft +
+ this.controls.captionButton.offsetWidth / 2;
+ var captionMenuLeft = (captionButtonCenter - this.captionMenu.offsetWidth / 2);
+ if (captionMenuLeft + this.captionMenu.offsetWidth > this.controls.panel.offsetLeft + this.controls.panel.offsetWidth)
+ this.captionMenu.classList.add(this.ClassNames.out);
+ this.captionMenu.style.left = captionMenuLeft + 'px';
+ // As height is not in the css, it needs to be specified to animate it.
+ this.captionMenu.height = this.captionMenu.offsetHeight;
+ this.captionMenu.style.height = 0;
+ },
+
+ destroyCaptionMenu: function()
+ {
+ this.hideCaptionMenu();
+ },
+
+ showCaptionMenu: function()
+ {
+ this.captionMenu.style.height = this.captionMenu.height + 'px';
+ },
+
+ hideCaptionMenu: function()
+ {
+ this.captionMenu.style.height = 0;
+ },
+
+ captionMenuTransitionEnd: function(event)
+ {
+ if (this.captionMenu.offsetHeight === 0)
+ Controller.prototype.destroyCaptionMenu.apply(this, arguments);
+ },
+
+ handleCaptionButtonMouseOver: function(event)
+ {
+ this.handleCaptionButtonShowMenu(event);
+ return true;
+ },
+
+ handleCaptionButtonShowMenu: function(event)
+ {
+ if (!this.captionMenu)
+ this.buildCaptionMenu();
+ if (!contains(this.captionMenu.captionMenuTreeElements, event.relatedTarget))
+ this.showCaptionMenu();
+ return true;
+ },
+
+ handleCaptionButtonMouseOut: function(event)
+ {
+ if (this.captionMenu && !contains(this.captionMenu.captionMenuTreeElements, event.relatedTarget))
+ this.hideCaptionMenu();
+ return true;
+ },
+
+ handleCaptionMouseOut: function(event)
+ {
+ if (event.relatedTarget != this.controls.captionButton &&
+ !contains(this.captionMenu.captionMenuTreeElements, event.relatedTarget))
+ this.hideCaptionMenu();
+ return true;
+ },
+};
+
+Object.create(Controller.prototype).extend(ControllerGtk.prototype);
+Object.defineProperty(ControllerGtk.prototype, 'constructor', { enumerable:false, value:ControllerGtk });
diff --git a/Source/WebCore/Modules/mediacontrols/mediaControlsiOS.css b/Source/WebCore/Modules/mediacontrols/mediaControlsiOS.css
new file mode 100644
index 000000000..28801ef86
--- /dev/null
+++ b/Source/WebCore/Modules/mediacontrols/mediaControlsiOS.css
@@ -0,0 +1,733 @@
+/*
+ * Copyright (C) 2012, 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.
+ */
+
+/* You'll see a lot of !important rules in this file. This is because
+ the inheritance and specificity of Shadow DOM trees is slightly
+ tricky. The page might have accidentally set a style and we have
+ to make sure it is reset. */
+
+audio {
+ min-width: 260px;
+ height: 39px;
+}
+
+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;
+}
+
+::-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-family: -apple-system;
+ overflow: hidden;
+}
+
+video::-webkit-media-controls-panel input[type="button"],
+audio::-webkit-media-controls-panel input[type="button"],
+video::-webkit-media-controls-panel button,
+audio::-webkit-media-controls-panel button {
+ padding: 0;
+ border: none;
+ -webkit-appearance: none;
+}
+
+video::-webkit-media-controls-inline-playback-placeholder,
+audio::-webkit-media-controls-inline-playback-placeholder {
+ display: block;
+ z-index: 0;
+ width: 100%;
+ height: 100%;
+ background-color: black;
+ background-repeat: no-repeat;
+ background-position: 50% 50%;
+ background-size: 100% 50%;
+}
+
+video::-webkit-media-controls-inline-playback-placeholder.hidden,
+audio::-webkit-media-controls-inline-playback-placeholder.hidden {
+ display: none;
+}
+
+video::-webkit-media-text-track-container,
+audio::-webkit-media-text-track-container {
+ position: relative;
+ -webkit-flex: 1 1 auto;
+}
+
+video::-webkit-media-controls-panel-container {
+ -webkit-transform: translateZ(0);
+ width: 100%;
+ direction: ltr;
+ height: 50px;
+ position: absolute;
+ bottom: 0;
+ pointer-events: none;
+}
+
+audio::-webkit-media-controls-panel-container {
+ width: 100%;
+ direction: ltr;
+ height: 39px;
+ position: absolute;
+ bottom: 0;
+}
+
+video::-webkit-media-controls-panel-background {
+ -webkit-transform: translateZ(0);
+ width: 101%; /* Due to some rounding issues we make this a little bit wider than should be necessary. */
+ height: 51px; /* And taller. */
+ -webkit-appearance: media-controls-light-bar-background;
+ transition: opacity 0.25s linear;
+ opacity: 0;
+ pointer-events: none;
+ bottom: 0;
+ position: absolute;
+}
+
+audio::-webkit-media-controls-panel-background {
+ display: none;
+}
+
+video::-webkit-media-controls-panel-background.show,
+video::-webkit-media-controls-panel-background.paused {
+ opacity: 1;
+}
+
+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;
+}
+
+audio::-webkit-media-controls-panel,
+video::-webkit-media-controls-panel {
+ box-sizing: border-box;
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ padding: 0;
+
+ -webkit-user-select: none;
+
+ display: -webkit-flex;
+ -webkit-flex-direction: row;
+ -webkit-flex-wrap: nowrap;
+ -webkit-justify-content: flex-start;
+ -webkit-align-items: center;
+
+ transition: opacity 0.25s linear;
+ -webkit-transform-origin: bottom left;
+}
+
+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;
+}
+
+video::-webkit-media-controls-panel {
+ height: 50px;
+ opacity: 0;
+ pointer-events: none;
+ -webkit-transform: translate3d(0, 0, 0);
+}
+
+audio::-webkit-media-controls-panel {
+ height: 39px;
+ background-color: rgba(212, 212, 212, 1);
+}
+
+video::-webkit-media-controls-panel.show,
+video::-webkit-media-controls-panel.paused {
+ pointer-events: auto;
+ opacity: 1;
+}
+
+video::-webkit-media-controls-panel.picture-in-picture {
+ pointer-events: none;
+}
+
+video::-webkit-media-controls-rewind-button,
+audio::-webkit-media-controls-rewind-button,
+video::-webkit-media-controls-panel .mute-box,
+audio::-webkit-media-controls-panel .mute-box,
+video::-webkit-media-controls-mute-button,
+audio::-webkit-media-controls-mute-button,
+video::-webkit-media-controls-volume-max-button,
+video::-webkit-media-controls-panel .volume-box,
+audio::-webkit-media-controls-panel .volume-box,
+audio::-webkit-media-controls-volume-slider,
+video::-webkit-media-controls-volume-slider {
+ display: none !important;
+}
+
+video::-webkit-media-controls-start-playback-button {
+ -webkit-appearance: none;
+ position: absolute;
+ width: 72px;
+ height: 72px;
+ left: calc(50% - 36px);
+ top: calc(50% - 36px);
+ -webkit-transform: translate3d(0, 0, 0);
+}
+
+video::-webkit-media-controls-start-playback-background {
+ -webkit-appearance: none;
+ position: absolute;
+ width: 72px;
+ height: 72px;
+ -webkit-appearance: media-controls-light-bar-background;
+ -webkit-transform: translateZ(0);
+ -webkit-clip-path: circle(36px);
+}
+
+video::-webkit-media-controls-start-playback-button .webkit-media-controls-start-playback-glyph {
+ -webkit-appearance: none;
+ position: absolute;
+ width: 72px;
+ height: 72px;
+ opacity: 0.6;
+ background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIwAAACMCAYAAACuwEE+AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABKFJREFUeNrsnV9ojWEcx5+jhdUulF0YNS6IpDRJuNDhyi5cjBvkglGKCxtKIlxILkwi+VPsRnbDphTuLEXzJ8u/0UhstJupKRojx+/X85xIztlztrNznve8n099W9lZ3vd9Pr3P8z7P7zxvIpVKGQBfxnEJAGEAYQBhAGEAYQAQBhAGEAYQBhAGEAYAYQBhAGEAYQBhAGEAEAYQBhAGEAYQBgBhAGEAYQBhAGEAYQAQBhAGEAaCpCyKB51IJPTHRMkVyQpJr+Sa5IzkXZwasOAbQul/GLU4GvTw/8kPSZNkUpyEKWSi3CWtynDH3Cl5Kamny2UM8zezs/xuiuSC5JEkSTMjjDLV4zM1ktuSFkk1zR1fYSbo2DeHz6+VvJIcllTQ7PET5vsI/qZcss+Jsy5H4SDG8zDTJJcl9ySLUQBhfFnspGmWVKECwvig3dJGSbfrrspRAmF8qHAD4i5JHZcDYXyZIWl1j+I1XA6E8SVp7KTfWUkllwNhfK/NVslryS7JeC4JwvigC5nHJM8lKxEGfJkluekyF2HAF73LPJGcMDEqo0CY0aFlFDvc+GabiWghGsIUHn2COu3uOEmEAV90TKNzNzqHMxNhwBedJX4hOWpKtIwCYfKPztfsMXZ9quTKRBFm7NAV8HSZ6BKEAV90TequsTU41QgDPmgZhVb5abXfIRPhMgqEKSwqykET4TJRhCkO1eZPmegChAFftEz0oRscVyEM+LZBvXsM32sCL6NAmHDQib4jxk781SEM+KJLC+ky0XkIA74kJZ3GLm5WIgz4oGUTWj6hZRSNJoAyCoSJBlqodVzyTLIGYcCXOcbuunXD2C1NEAa8qJXcl0xHGPBFZ4svIgzkwlKEgVy4ijDgy2PJdoSB4dDtZXWxcpnkM8JANh4YOwO8RfKVx2rIRJ9kk7HlEHeKeSBltEXQDBo7w6tfW/kSwgEhTLi0SXZL3oZ0UAgTHrpCrdvft4d4cIxhwqHfPSYvClUW7jBhoG9gOWnspowDoR8swhSXW8Zuh9YVlQNGmOKg30tqdMJECsYwhWXAPfnMj6Is3GEKxy/JOckBN7iNLAgz9rS7x+TOUjgZhBk73rtxSlspnRTC5B+dwtepfJ3SHyy1k0OY/KFlB5eM3X2qr1RPEmHyQ4frfjpK/UR5rB4deifZYGxtbUccTpg7zMgIruwAYcKl1XU/PXE8ebokf3QeZbmxX1XtietFQJjh0ZlZfW/SQhNw2QFdUvEZkpwyESk7QJjioguD+raSbi4FXVI2tC6l1gVZECYj2uVoIVNkyw7okgrDT8l5Yzdb7kcHhMmGPvE0GPtyLKBLysgbyWpj51SQhTtMRtJlB02SbzR9/IT5JJns8TktO2iW7DclXHZAlzQ8Po2vK8i6krwZWRDmaZbffZCsNzEqO0CY4bn+n38bcmMUfctri+uOII8kUqnoXdNEIpEef6kUOiv70dhia91mvTdODVjo9oukMECXBAgDCAMIwyUAhAGEAYQBhAGEAUAYQBhAGEAYQBhAGACEAYQBhAGEAYQBhAFAGEAYQBhAGEAYAIQBhAGEAYQBhIEY8FuAAQBHs44NYWX3+AAAAABJRU5ErkJggg==');
+ background-repeat: no-repeat;
+ background-position: 50% 50%;
+ background-size: 100% 100%;
+ /* FIXME This is an ad-hoc fix until we fix the real compositing issue tracked by webkit.org/b/158147 */
+ will-change: opacity;
+}
+
+video::-webkit-media-controls-start-playback-button .webkit-media-controls-start-playback-glyph.active {
+ opacity: 1;
+}
+
+video::-webkit-media-controls-start-playback-button .webkit-media-controls-start-playback-glyph.failed {
+ background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIwAAACMCAYAAACuwEE+AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABspJREFUeNrsnVvIVUUYhr8tZv5UZGCShdZFeBElJAQWVD9B5EVQEVIhQRR0URdZQXSAMjIoSqikqLCisIQO1kUHo8woKERMQjqoRUVlB43sfDD7m489izbimjXf3rPWnrV9XnhvF5s9z/rmm5l3rdWZmJgQhGLVARgEMAhgEMAggEEAgxDAIIBBAIMABgEMAhiEAAYBDAIYBDAIYBDAIAQwCGAQwCCAQQCDEMAggEEAgwAGAQwCGIQABgEMAhiUpxSYulyzpjq/6Py78xbnO52PYUTrHd82A7NY/4+9vNt5mfM0UGkvMIc7L3d+y/kh52MT/Rdr9wFM4W+cL3WeBDDtAmbM+eO9BvMv57sTVIGvAsAUfs95HGDaA8wFgcH8dsAq8G8EMIVXOc8GmPyBuTKyCpxu/B8ONMBSWJvjpc4HA0y+wMxx/jtiMLVaPGWsAhN9Wqeyi3RLAWDybHov9ne3pQqM1QhM4Xed5wNMnsvq2b6CxPYdX0RUgR8TQKO/5zHnmQCT5z7MfN+zWKrAvJL/YobzI857EoDzi/NNkZUNYBreuJvkV0fbIwdzjwejrAqc6LwuATTqz5zPA5g8d3p1tXKX35eJrQLXOU8puZ4O9LZE4KzzIAJMhkcDuvO72jCY2wJVQGG60cM1KDRa2R50ng4weZ4lnen8gWFAX3M+ruRaMxP2N9pcXxuobAAzxMPHyc5XOO+IHEw9bLw/UAVS9jdbnRcATJ6n1dM9CLsjB/MHD9rkkust9Ev1FOC8EqhsADPkeIMOzKuGwdQp7aySa435pXOK/kZBvkdaEKPYX/Mw1hXQaimPUaTsb3ZUVDaAGWKAaopvPmMrRBGjOKTkevP8xmCKaUor2zjA5Jm4s1aI7VIeo+j4I4hU/U2osgHMkCOaugJ6xzCYeiRxSgP9jVa2OySTGAXADFYhqmIUWr1Wii2c1U9lA5ghAdNbIW4VW4zilsBh4/yE/Y1WtpMBJi9gCqWMUaTsb/oJiAFMYoXCT6eKLUbxduCwUavQEkP1qqpsS6TBGAXA/K+quzZ1jMJavQYNiAFM6v+i5669OXDXHur3Y2JjFD9VHDam7G9CATGAqQmY2LtW90deEFuM4tzA6myRoXoNUtkApiZgYsPdC8QWowgdNupey9JE/Y3uAd0giWMUAFMNTLEqeTxw1xYxitggeRGjOKyB/iYUEAOYmoCJDXdbYxRVh41a2TZKupjo8QDTLDCx/Y1OOW8YDxvPSLQ6GyQgBjA1AROz35I6RpGyv9Gp8+p+YhQAMxgwMasSbTqvN8YoQoeN2t88nWia+sj5fIBpFpjY/iZljEI1btx9Dvll5yMApllgCn9asSo5ybhRtyGwrE/Z32hfdjTANA9MzMNrqWMUOn3dbth9LvNagBkeMDH9zUHGRva3imnP+hDf3v4DYIYLTOFdFedJWjlW9bGsl8T9zUqAyQOY2F1X60CHpr2iv/ku8lob/cEqwGQEjGWgvzdOezNKrjet4nRd+6MVfnpklZQpMDG7rlUDbX1me1/9zXrn09iHaQcwloF+yTjtnV0x7b3ufEk/gSuAGT4wvQN9TkMxCg4fRwCYmIHWs5+rjDGKeyXhM9sAkx8wMf1N6hgFwLQcmN7+5prAQM8V2/tqNsuAz2wDTN7A9OZlFjQUowCYEQAmpr+ZKrbnuf+UPl59DzDtAqa3v5kWiFE8KvF5YNMz2wDTPmB6G9nLAgNtfd4p6tX3ANNOYPQ44AGpzuYWMYqvjTGKowBmdIDp5yXR1jxwaXoQYNoDzOcy+PNFGqN41hijWAgw7QKmjg9dWGMU+sjMLIDJGxjtJ56Q+j6lo83y5RL/0utN4r9LDjD5AdPkx7osMYq5AJMXMLonskiG8znAOX5jMPT7TgCYPIDJ6YOjZTGKDUxJeQDznOT3SeMDpPsorZ5P/ezPno6so+nt1DmwnU6nbmCa1CZ/Ev2mtEipxxdgqrXTL5NX+JWQAAzA7Ev6ve3lvlfZJS0VwDQDzBrpRiu3SssFMPUC86F0nwhYIyMigKkHGJ1ybnO+z/kfGSEBTFpgFI6HpfvdgZ0yggKYdMDo8nix8/sywgKYwYH5RLofTH9e9gMBTP/A/Crdd9Etk26gWgCmzwu29Ghgp8THDmp7NXsbgOEsqavNklfsAGAyB+bJAChfSkOflwGY9gBzodg/PQww+zEw+vzyM76Z3eIb2lkgUi8wnQYGFo2QAAYBDAIYBDAIYBDAIAQwCGAQwCCAQQCDAAYhgEEAgwAGAQwCGAQwCAEMAhgEMAhgEMAgBDAIYBDAIIBBAIMABiGAQQCDGtN/AgwAK0XUGzgj/EoAAAAASUVORK5CYII=');
+}
+
+/* ================== ALL INLINE BUTTONS ====================== */
+
+video::-webkit-media-controls-play-button,
+audio::-webkit-media-controls-play-button,
+video::-webkit-media-controls-fullscreen-button,
+audio::-webkit-media-controls-fullscreen-button,
+video::-webkit-media-controls-wireless-playback-picker-button,
+audio::-webkit-media-controls-wireless-playback-picker-button,
+video::-webkit-media-controls-picture-in-picture-button,
+audio::-webkit-media-controls-picture-in-picture-button {
+ -webkit-appearance: none;
+ display: block;
+ padding: 0;
+ border: 0;
+ -webkit-user-select: none;
+ background-repeat: no-repeat;
+ background-position: 50% 50%;
+ mix-blend-mode: plus-darker;
+ opacity: 0.55;
+ -webkit-mask-position: 50% 50%;
+ -webkit-mask-repeat: no-repeat;
+ -webkit-mask-origin: border-box;
+ background-color: black;
+ transition: background-color 250ms;
+}
+
+video::-webkit-media-controls-play-button,
+video::-webkit-media-controls-fullscreen-button,
+video::-webkit-media-controls-wireless-playback-picker-button,
+video::-webkit-media-controls-picture-in-picture-button {
+ -webkit-transform: translate3d(0, 0, 0);
+}
+
+video::-webkit-media-controls-play-button:active,
+audio::-webkit-media-controls-play-button:active,
+video::-webkit-media-controls-fullscreen-button:active,
+audio::-webkit-media-controls-fullscreen-button:active,
+audio::-webkit-media-controls-wireless-playback-picker-button:active,
+video::-webkit-media-controls-wireless-playback-picker-button:active,
+video::-webkit-media-controls-picture-in-picture-button:active,
+audio::-webkit-media-controls-picture-in-picture-button:active {
+ mix-blend-mode: normal;
+ opacity: 1;
+ background-color: white;
+ transition: background-color 0ms;
+}
+
+/* ================== PLAY BUTTON ====================== */
+
+audio::-webkit-media-controls-play-button,
+video::-webkit-media-controls-play-button {
+ margin-left: 4px;
+ -webkit-mask-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAsCAYAAAANUxr1AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAFVJREFUeNrs2bENgEAMA0AHsf/EIB5GcIGgOEvpIuf6TJIjXfZyv+qfe67ywJT7Vf+WnwUICAgICAgICAgICAgICAgICAgICAjoyzxvgvPlG1X/EmAASwgGTpkrSW0AAAAASUVORK5CYII=');
+ -webkit-mask-size: 18px 22px;
+ -webkit-order: 1;
+}
+
+audio::-webkit-media-controls-play-button {
+ width: 42px;
+ height: 39px;
+}
+
+video::-webkit-media-controls-play-button {
+ width: 44px;
+ height: 50px;
+}
+
+audio::-webkit-media-controls-play-button.paused,
+video::-webkit-media-controls-play-button.paused {
+ -webkit-mask-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACoAAAAsCAYAAAATmipGAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAUZJREFUeNrU2S8sRWEYgPFzD7PZbDYbQSAoBBKFwgQTmKRpJE3SaBKFRKJJJphiCslNt1AERRBsNpvN/DmeL7ybGfLzne3Xn517d873vqcsimIOtSKDq0Id0zmEhktM5hAazjGWQ2g4xUgOocknjjFkDw0fOMSAPfR78AH67KHhDXvotYeGV+yg2x4aXrCFLntoeMYGOuyh4QnraLeHhkesos0eGh6wglZ7aLjHMlrsoeEOi2i2h4ZbLKC0h4ZrzP8XXMk0MPvbeFRJXf0cjyq5NB5NlIX/qsX/1non6/afvvHXrsESeGN/PMUDv8n8Cl0yv0L1hxL9MU9/cE6jyJp5FNEPd2lc3kSneQGxbV5ApJXOLnqsK5137JuXZLF27Dcvco8waF7knmDYvBo/w6j5Y8MFxs2fb9LEN2UfnmaKDD4xfgkwANpCXZrruKGvAAAAAElFTkSuQmCC');
+ -webkit-mask-size: 21px 22px;
+}
+
+/* ================== FULLSCREEN BUTTON ====================== */
+
+video::-webkit-media-controls-fullscreen-button {
+ width: 44px;
+ height: 50px;
+ -webkit-mask-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAO5JREFUeNrs2F8KwjAMBvAoXsID6GEUPIKPw2sLPiu+CCIYO1nFhf1pWkw+YR9kTyv8KCNpR0TEyjqSUZiZ5vQH+d6dZ6gDDIy5BbyjISVwi4aUQEJDdgGhkH1AGOQQEAI5BnRHpgBdkalAN2QEnpsiNGQEamKKzAFK5CPUCg0YkbdQe8QdjFkifoPmjRr+wDoBJ2Bh1r9uRaW4UzNxKsQ2sxFjsUID1tmVIK0adQ7y/QfDcpJokeZALdIFqEG6AVORrsAUpDtwDAkBHELCAPuQUMAuJBxQIj/AheUVI2eRJfCifP9aP2boV7uXAAMArhj6EAChbh4AAAAASUVORK5CYII=');
+ -webkit-mask-size: 20px 20px;
+ -webkit-order: 5;
+}
+
+/* ================== AIRPLAY BUTTON ====================== */
+
+audio::-webkit-media-controls-wireless-playback-picker-button,
+video::-webkit-media-controls-wireless-playback-picker-button {
+ -webkit-mask-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACoAAAAkCAYAAAD/yagrAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAARlJREFUeNrsmDEOgjAUhtuYMLgwuTG6Mrt7AC/gCVycHLwBN+AEXIADuDO76ubm5OJgYnw+kldDiEb6aElJ3ku+gYSWL4/yl1QrpUCNoLSIehbVgfmBiIqoiIqoiIqoiIpoUKLmAhz+7u2QpUPRj5tL0TXyQm5IGqpo3cVHY64LkoQmmlIXocURiUMRnSHnL5KGAxK5Fu1CiUxooilSdRhTNBIlpk7bPtfq5orkFMmWFmOzRqcSWsNeRE/0mk3ljK5sOqzrn6Kc2jMka57IaqjdwmQlMLkjC9+S7azkckXmviRt15TtmndSnK/UNkV6Fzf3OLnMroh2FvBM3vcArRhA0rDlimYDSrIzdjOw5N+M1QCjOMdVbwEGAJbOn8QOMeI8AAAAAElFTkSuQmCC');
+ -webkit-mask-size: 21px 18px;
+ -webkit-order: 3;
+}
+
+audio::-webkit-media-controls-wireless-playback-picker-button {
+ width: 44px;
+ height: 39px;
+}
+
+video::-webkit-media-controls-wireless-playback-picker-button {
+ width: 44px;
+ height: 50px;
+}
+
+audio::-webkit-media-controls-wireless-playback-picker-button.playing,
+video::-webkit-media-controls-wireless-playback-picker-button.playing {
+ opacity: 1;
+ mix-blend-mode: normal;
+ background-color: -apple-wireless-playback-target-active;
+}
+
+/* ================== PICTURE IN PICTURE BUTTON ====================== */
+
+video::-webkit-media-controls-picture-in-picture-button {
+ width: 35px;
+ height: 44px;
+ -webkit-order: 5;
+ -webkit-mask-size: 23px 18px;
+ -webkit-mask-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC4AAAAkCAYAAAD2IghRAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAANhJREFUeNrslzEOwjAMRR2UA3AQ7sLKwsTEglg6sTIgLgs7qCZBRbIsWpDayHH1v/SHJmn0ZFutHYiIqX4F+cDMtCCncgseh1JiLJ5lxH+Br2qGZ2Gpa/IjeVMZ1/ur0ndgLdaeydsvF9/7LrYEz7oo+J0X8Kyz2GuT917As04K/uAFPKtR5xov4FnHLuIy+i7AqavxVr1jAh7H/HbRZE3QZGndBvaW1k1Z6VqdHtjzIBELZrHoKIfRzbpUxqY4IOJzLpWAiAMc4AAHOMAB/m9T9Bn1veklwACtBYmnlYBaIQAAAABJRU5ErkJggg==');
+}
+
+video::-webkit-media-controls-picture-in-picture-button.return-from-picture-in-picture {
+ -webkit-mask-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC4AAAAkCAYAAAD2IghRAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAOZJREFUeNpiZGBg+M8w+AEjMuf///8MTAxDFAxZh7Pgi5IBBv+HZYgPS4f/JwJ/GA1xCjMnIfAejf9poHPvfyy5GFvS6KdjyYPLXeAKiFiH/0ZiT6GT46ni8CAg/onEn0WH/EEVh4OAL5rjFwIx81BwOAh4APE3JPGlNHQ8VR0OAs5A/BVJbjUQsw0Fh4OAHRB/RpLPG0wOJwSsoTXnfBolF5o5HARUBqL2pYbDB6bGBDqchYYlAk27cqPt8YFuHVIaxYyjIT6ckwrjaIiPOnzU4aMOH3X4qMNHHU5sowjWmxhqACDAAI3lmdvpn4aTAAAAAElFTkSuQmCC');
+}
+
+/* ================== SPACER ====================== */
+
+video::-webkit-media-controls-spacer,
+audio::-webkit-media-controls-spacer {
+ -webkit-appearance: none !important;
+ -webkit-flex: 1 1 0; /* Should be the same as the timeline container. */
+ -webkit-order: 2;
+ height: 8px;
+ margin: 0;
+ background-color: transparent !important;
+}
+
+/* ================== TIMELINE ====================== */
+
+video::-webkit-media-controls-timeline,
+audio::-webkit-media-controls-timeline {
+ -webkit-appearance: none !important;
+ -webkit-flex: 1 1 0;
+ height: 8px;
+ margin: 0;
+ background-color: transparent !important;
+ background-size: 100% 100%;
+ border: none !important;
+ border-radius: 0 !important;
+ box-sizing: content-box !important;
+}
+
+video::-webkit-media-controls-timeline {
+ -webkit-transform: translate3d(0, 0, 0);
+ mix-blend-mode: plus-darker;
+}
+
+audio::-webkit-media-controls-timeline::-webkit-slider-runnable-track,
+video::-webkit-media-controls-timeline::-webkit-slider-runnable-track {
+ -webkit-appearance: none !important;
+ background: none !important;
+ border: none !important;
+}
+
+video::-webkit-media-controls-timeline::-webkit-slider-thumb,
+audio::-webkit-media-controls-timeline::-webkit-slider-thumb {
+ -webkit-appearance: none !important;
+ width: 15px;
+ height: 50px;
+ background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAiCAYAAABIiGl0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAxNpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iUGl4ZWxtYXRvciAyLjIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NTkwQ0UwODdBQTcxMTFFNEE5QTZGQTVGMjFBNkUxN0UiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NTkwQ0UwODhBQTcxMTFFNEE5QTZGQTVGMjFBNkUxN0UiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo1OTBDRTA4NUFBNzExMUU0QTlBNkZBNUYyMUE2RTE3RSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo1OTBDRTA4NkFBNzExMUU0QTlBNkZBNUYyMUE2RTE3RSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pofoz4wAAAJGSURBVHjaxJfNTsJAEMfbLmoNaKIhIE18A+LReOXmi/gQnnwSLz6Fnjh4Id4kHElI5EDwpIkgImWdf/Nfs5ISCrR1kkmg7c5vZz/mw3WSy66oL7ojqqiQkPotOhGdaq1XGnMTvC+KlprN5kW9Xr8sFotnhUKhCsUHs9lsCB2NRu1Op3PfaDRa8vhDdCSqnQ0E3lX7/f6NGH7RCQXfYgzG0sZacthut6/G4/Gj3lAwFjZgKyn0qNfrXYdh+Ka3FNiALdhc6Sk+1CkL4Us997E0aXga5zmX3Y87vdVt9jTJnvPA/blJJZxEnbHwtJds8Mk6V2ZTAQMsAD1EJAQHpdSpk7GAARaYAPuISE5OQpYfeYwwmBeYrMhjZeJuHkKWAtj7B7DnJchQaQsylgvwHGktL6qwXsH0TE7NETw091gjiecFFtYzmNFSo3LICyysBzBxsI5xoWUJWllHL8lSfTnViFyTyGO4PhgMbrP2lgxNpnPAdBXkkBYDsg48lqbRDLrd7t18Pn9P21PYhG3zl8yoXsY+1zCjDEufgIxjMqPCHMm5wpdBBsVeQK2QpUzZs8dKsGY+SrG8DSxvj8j6DdOKHUPZhkO3KOiDBWiZDGW3MC4f7FPVYvKQyuE8YQvzFJMUcJg+qfit3YVKc4clqB8H3zAThWzmJmzstGN1fI511LVJXYzl7hZQ00F+2dA4sH3P7Am4a0zAeDklEODZYueoVgz+DS4rWk5tTdj2cmqt4tr9sccJFqyG3N4CGxrSu3AZ0MiPAAMAZrLkuVVmRJsAAAAASUVORK5CYII=');
+ background-repeat: no-repeat;
+ background-size: 15px 17px;
+ background-position: 0px 18px;
+ background-color: transparent !important;
+ border: none !important;
+ -webkit-transform: rotateZ(0deg);
+}
+
+video::-webkit-media-controls-timeline::-webkit-slider-thumb:active,
+audio::-webkit-media-controls-timeline::-webkit-slider-thumb:active {
+ background-color: transparent !important;
+}
+
+audio::-webkit-media-controls-timeline::-webkit-slider-thumb {
+ height: 39px;
+ background-position: 0px 12px;
+}
+
+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;
+ -webkit-align-items: center;
+ overflow-y: hidden;
+ overflow-x: hidden;
+ letter-spacing: normal;
+ word-spacing: normal;
+ line-height: normal;
+ text-transform: none;
+ text-indent: 0px;
+ text-decoration: none;
+ color: black;
+ mix-blend-mode: plus-darker;
+ opacity: 0.55;
+ height: 50px;
+ font-size: 13px;
+ font-family: -apple-system-monospaced-numbers;
+ -webkit-text-zoom: reset;
+}
+
+audio::-webkit-media-controls-current-time-display,
+video::-webkit-media-controls-current-time-display {
+ min-width: 32px;
+ -webkit-justify-content: flex-end;
+ padding-right: 6px;
+}
+
+audio::-webkit-media-controls-time-remaining-display,
+video::-webkit-media-controls-time-remaining-display {
+ min-width: 38px;
+ -webkit-justify-content: flex-start;
+ padding-left: 6px;
+ margin-right: 6px;
+}
+
+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; /* Any changes here should also be made on the spacer. */
+ position: relative;
+ padding: 0;
+ -webkit-order: 2;
+ -webkit-text-size-adjust: auto;
+}
+
+audio::-webkit-media-controls-timeline-container {
+ padding-right: 10px;
+}
+
+audio::-webkit-media-controls-current-time-display.three-digit-time,
+video::-webkit-media-controls-current-time-display.three-digit-time {
+ min-width: 31px;
+}
+
+audio::-webkit-media-controls-time-remaining-display.three-digit-time,
+video::-webkit-media-controls-time-remaining-display.three-digit-time {
+ min-width: 40px;
+}
+
+audio::-webkit-media-controls-current-time-display.four-digit-time,
+video::-webkit-media-controls-current-time-display.four-digit-time {
+ min-width: 40px;
+}
+
+audio::-webkit-media-controls-time-remaining-display.four-digit-time,
+video::-webkit-media-controls-time-remaining-display.four-digit-time {
+ min-width: 49px;
+}
+
+audio::-webkit-media-controls-current-time-display.five-digit-time,
+video::-webkit-media-controls-current-time-display.five-digit-time {
+ min-width: 51px;
+}
+
+audio::-webkit-media-controls-time-remaining-display.five-digit-time,
+video::-webkit-media-controls-time-remaining-display.five-digit-time {
+ min-width: 60px;
+}
+
+audio::-webkit-media-controls-current-time-display.six-digit-time,
+video::-webkit-media-controls-current-time-display.six-digit-time {
+ min-width: 60px;
+}
+
+audio::-webkit-media-controls-time-remaining-display.six-digit-time,
+video::-webkit-media-controls-time-remaining-display.six-digit-time {
+ min-width: 69px;
+}
+
+/* ================== STATUS DISPLAY ====================== */
+
+video::-webkit-media-controls-status-display,
+audio::-webkit-media-controls-status-display {
+
+ overflow: hidden;
+ font-family: -apple-system;
+ letter-spacing: normal;
+ word-spacing: normal;
+ line-height: normal;
+ text-transform: none;
+ text-indent: 0px;
+ text-decoration: none;
+ color: black;
+ mix-blend-mode: plus-darker;
+ opacity: 0.55;
+ -webkit-order: 2;
+}
+
+/* ================== CAPTIONS ====================== */
+
+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;
+ word-break: break-word;
+
+ -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;
+}
+
+/* ==================== PLAYBACK 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,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 131 90"><g fill="none" stroke="-apple-system-gray" stroke-width="4"><rect x="2" y="2" width="127" height="76"/><line x1="30" y1="88" x2="101" y2="88"/></g></svg>');
+ 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: -apple-system;
+ font-weight: 400;
+ 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: 12pt;
+ font-size: 12pt;
+ text-align: center;
+ margin: 0px;
+ color: -apple-system-gray;
+}
+
+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: 10pt;
+ 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,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 62 43"><g fill="none" stroke="rgb(146,146,146)" stroke-width="2"><rect x="1" y="1" width="60" height="36"/><line x1="14" y1="42" x2="48" y2="42"/></g></svg>');
+ 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,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 304 150"><g fill="none" stroke="-apple-system-gray" stroke-width="3"><path d="m 172,106 -81,0 c -3.5,0 -6,-2.5 -6,-6 l 0,-89 c 0,-3.5 2.5,-6 6,-6 l 122,0 c 3.5,0 6,2.5 6,6 l 0,57" /><path d="m 233,119 -53,0 c -3,0 -3,-0 -3,-3 l 0,-40 c 0,-3 0,-3 3,-3 l 53,0 c 3,0 3,0 3,3 l 0,40 c 0,3 0,3 -3,3 z" /></g></svg>');
+}
+
+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;
+}
+
+/* When PiP is active, we don't want the controls container showing. */
+video::-webkit-media-controls-panel-container.picture-in-picture {
+ opacity: 0;
+ pointer-events: none;
+}
+
+/* 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/mediaControlsiOS.js b/Source/WebCore/Modules/mediacontrols/mediaControlsiOS.js
new file mode 100644
index 000000000..9d6d28abb
--- /dev/null
+++ b/Source/WebCore/Modules/mediacontrols/mediaControlsiOS.js
@@ -0,0 +1,628 @@
+function createControls(root, video, host)
+{
+ return new ControllerIOS(root, video, host);
+};
+
+function ControllerIOS(root, video, host)
+{
+ this.doingSetup = true;
+ this._pageScaleFactor = 1;
+
+ this.timelineContextName = "_webkit-media-controls-timeline-" + host.generateUUID();
+
+ Controller.call(this, root, video, host);
+
+ this.setNeedsTimelineMetricsUpdate();
+
+ this._timelineIsHidden = false;
+ this._currentDisplayWidth = 0;
+ this.scheduleUpdateLayoutForDisplayedWidth();
+
+ host.controlsDependOnPageScaleFactor = true;
+ this.doingSetup = false;
+};
+
+/* Enums */
+ControllerIOS.StartPlaybackControls = 2;
+
+ControllerIOS.prototype = {
+ /* Constants */
+ MinimumTimelineWidth: 150,
+ ButtonWidth: 42,
+
+ get idiom()
+ {
+ return "ios";
+ },
+
+ createBase: function() {
+ Controller.prototype.createBase.call(this);
+
+ var startPlaybackButton = this.controls.startPlaybackButton = document.createElement('div');
+ startPlaybackButton.setAttribute('pseudo', '-webkit-media-controls-start-playback-button');
+ startPlaybackButton.setAttribute('aria-label', this.UIString('Start Playback'));
+ startPlaybackButton.setAttribute('role', 'button');
+
+ var startPlaybackBackground = document.createElement('div');
+ startPlaybackBackground.setAttribute('pseudo', '-webkit-media-controls-start-playback-background');
+ startPlaybackBackground.classList.add('webkit-media-controls-start-playback-background');
+ startPlaybackButton.appendChild(startPlaybackBackground);
+
+ var startPlaybackGlyph = document.createElement('div');
+ startPlaybackGlyph.setAttribute('pseudo', '-webkit-media-controls-start-playback-glyph');
+ startPlaybackGlyph.classList.add('webkit-media-controls-start-playback-glyph');
+ startPlaybackButton.appendChild(startPlaybackGlyph);
+
+ this.listenFor(this.base, 'gesturestart', this.handleBaseGestureStart);
+ this.listenFor(this.base, 'gesturechange', this.handleBaseGestureChange);
+ this.listenFor(this.base, 'gestureend', this.handleBaseGestureEnd);
+ this.listenFor(this.base, 'touchstart', this.handleWrapperTouchStart);
+ this.stopListeningFor(this.base, 'mousemove', this.handleWrapperMouseMove);
+ this.stopListeningFor(this.base, 'mouseout', this.handleWrapperMouseOut);
+
+ this.listenFor(document, 'visibilitychange', this.handleVisibilityChange);
+ },
+
+ shouldHaveStartPlaybackButton: function() {
+ var allowsInline = this.host.allowsInlineMediaPlayback;
+
+ if (this.isPlaying || (this.hasPlayed && allowsInline))
+ return false;
+
+ if (this.isAudio() && allowsInline)
+ return false;
+
+ if (this.doingSetup)
+ return true;
+
+ if (this.isFullScreen())
+ return false;
+
+ if (!this.video.currentSrc && this.video.error)
+ return false;
+
+ if (!this.video.controls && allowsInline)
+ return false;
+
+ if (this.video.currentSrc && this.video.error)
+ return true;
+
+ return true;
+ },
+
+ shouldHaveControls: function() {
+ if (this.shouldHaveStartPlaybackButton())
+ return false;
+
+ return Controller.prototype.shouldHaveControls.call(this);
+ },
+
+ shouldHaveAnyUI: function() {
+ return this.shouldHaveStartPlaybackButton() || Controller.prototype.shouldHaveAnyUI.call(this) || this.currentPlaybackTargetIsWireless();
+ },
+
+ createControls: function() {
+ Controller.prototype.createControls.call(this);
+
+ var panelContainer = this.controls.panelContainer = document.createElement('div');
+ panelContainer.setAttribute('pseudo', '-webkit-media-controls-panel-container');
+
+ var wirelessTargetPicker = this.controls.wirelessTargetPicker;
+ this.listenFor(wirelessTargetPicker, 'touchstart', this.handleWirelessPickerButtonTouchStart);
+ this.listenFor(wirelessTargetPicker, 'touchend', this.handleWirelessPickerButtonTouchEnd);
+ this.listenFor(wirelessTargetPicker, 'touchcancel', this.handleWirelessPickerButtonTouchCancel);
+
+ this.listenFor(this.controls.startPlaybackButton, 'touchstart', this.handleStartPlaybackButtonTouchStart);
+ this.listenFor(this.controls.startPlaybackButton, 'touchend', this.handleStartPlaybackButtonTouchEnd);
+ this.listenFor(this.controls.startPlaybackButton, 'touchcancel', this.handleStartPlaybackButtonTouchCancel);
+
+ this.listenFor(this.controls.panel, 'touchstart', this.handlePanelTouchStart);
+ this.listenFor(this.controls.panel, 'touchend', this.handlePanelTouchEnd);
+ this.listenFor(this.controls.panel, 'touchcancel', this.handlePanelTouchCancel);
+ this.listenFor(this.controls.playButton, 'touchstart', this.handlePlayButtonTouchStart);
+ this.listenFor(this.controls.playButton, 'touchend', this.handlePlayButtonTouchEnd);
+ this.listenFor(this.controls.playButton, 'touchcancel', this.handlePlayButtonTouchCancel);
+ this.listenFor(this.controls.fullscreenButton, 'touchstart', this.handleFullscreenTouchStart);
+ this.listenFor(this.controls.fullscreenButton, 'touchend', this.handleFullscreenTouchEnd);
+ this.listenFor(this.controls.fullscreenButton, 'touchcancel', this.handleFullscreenTouchCancel);
+ this.listenFor(this.controls.pictureInPictureButton, 'touchstart', this.handlePictureInPictureTouchStart);
+ this.listenFor(this.controls.pictureInPictureButton, 'touchend', this.handlePictureInPictureTouchEnd);
+ this.listenFor(this.controls.pictureInPictureButton, 'touchcancel', this.handlePictureInPictureTouchCancel);
+ this.listenFor(this.controls.timeline, 'touchstart', this.handleTimelineTouchStart);
+ this.stopListeningFor(this.controls.playButton, 'click', this.handlePlayButtonClicked);
+
+ this.controls.timeline.style.backgroundImage = '-webkit-canvas(' + this.timelineContextName + ')';
+ },
+
+ setControlsType: function(type) {
+ if (type === this.controlsType)
+ return;
+ Controller.prototype.setControlsType.call(this, type);
+
+ if (type === ControllerIOS.StartPlaybackControls)
+ this.addStartPlaybackControls();
+ else
+ this.removeStartPlaybackControls();
+ },
+
+ addStartPlaybackControls: function() {
+ this.base.appendChild(this.controls.startPlaybackButton);
+ this.showShowControlsButton(false);
+ },
+
+ removeStartPlaybackControls: function() {
+ if (this.controls.startPlaybackButton.parentNode)
+ this.controls.startPlaybackButton.parentNode.removeChild(this.controls.startPlaybackButton);
+ },
+
+ reconnectControls: function()
+ {
+ Controller.prototype.reconnectControls.call(this);
+
+ if (this.controlsType === ControllerIOS.StartPlaybackControls)
+ this.addStartPlaybackControls();
+ },
+
+ 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.playButton);
+ this.controls.panel.appendChild(this.controls.statusDisplay);
+ this.controls.panel.appendChild(this.controls.timelineBox);
+ this.controls.panel.appendChild(this.controls.wirelessTargetPicker);
+ if (!this.isLive) {
+ this.controls.timelineBox.appendChild(this.controls.currentTime);
+ this.controls.timelineBox.appendChild(this.controls.timeline);
+ this.controls.timelineBox.appendChild(this.controls.remainingTime);
+ }
+ if (this.isAudio()) {
+ // Hide the scrubber on audio until the user starts playing.
+ this.controls.timelineBox.classList.add(this.ClassNames.hidden);
+ } else {
+ this.updatePictureInPictureButton();
+ this.controls.panel.appendChild(this.controls.fullscreenButton);
+ }
+ },
+
+ configureFullScreenControls: function() {
+ // Explicitly do nothing to override base-class behavior.
+ },
+
+ controlsAreHidden: function()
+ {
+ // Controls are only ever actually hidden when they are removed from the tree
+ return !this.controls.panelContainer.parentElement;
+ },
+
+ addControls: function() {
+ this.base.appendChild(this.controls.inlinePlaybackPlaceholder);
+ this.base.appendChild(this.controls.panelContainer);
+ this.controls.panelContainer.appendChild(this.controls.panelBackground);
+ this.controls.panelContainer.appendChild(this.controls.panel);
+ this.setNeedsTimelineMetricsUpdate();
+ },
+
+ updateControls: function() {
+ if (this.shouldHaveStartPlaybackButton())
+ this.setControlsType(ControllerIOS.StartPlaybackControls);
+ else if (this.presentationMode() === "fullscreen")
+ this.setControlsType(Controller.FullScreenControls);
+ else
+ this.setControlsType(Controller.InlineControls);
+
+ this.updateLayoutForDisplayedWidth();
+ this.setNeedsTimelineMetricsUpdate();
+ },
+
+ drawTimelineBackground: function() {
+ var width = this.timelineWidth * window.devicePixelRatio;
+ var height = this.timelineHeight * window.devicePixelRatio;
+
+ if (!width || !height)
+ return;
+
+ var played = this.video.currentTime / this.video.duration;
+ var buffered = 0;
+ var bufferedRanges = this.video.buffered;
+ if (bufferedRanges && bufferedRanges.length)
+ buffered = Math.max(bufferedRanges.end(bufferedRanges.length - 1), buffered);
+
+ buffered /= this.video.duration;
+ buffered = Math.max(buffered, played);
+
+ var ctx = document.getCSSCanvasContext('2d', this.timelineContextName, width, height);
+
+ ctx.clearRect(0, 0, width, height);
+
+ var midY = height / 2;
+
+ // 1. Draw the buffered part and played parts, using
+ // solid rectangles that are clipped to the outside of
+ // the lozenge.
+ ctx.save();
+ ctx.beginPath();
+ this.addRoundedRect(ctx, 1, midY - 3, width - 2, 6, 3);
+ ctx.closePath();
+ ctx.clip();
+ ctx.fillStyle = "white";
+ ctx.fillRect(0, 0, Math.round(width * played) + 2, height);
+ ctx.fillStyle = "rgba(0, 0, 0, 0.55)";
+ ctx.fillRect(Math.round(width * played) + 2, 0, Math.round(width * (buffered - played)) + 2, height);
+ ctx.restore();
+
+ // 2. Draw the outline with a clip path that subtracts the
+ // middle of a lozenge. This produces a better result than
+ // stroking.
+ ctx.save();
+ ctx.beginPath();
+ this.addRoundedRect(ctx, 1, midY - 3, width - 2, 6, 3);
+ this.addRoundedRect(ctx, 2, midY - 2, width - 4, 4, 2);
+ ctx.closePath();
+ ctx.clip("evenodd");
+ ctx.fillStyle = "rgba(0, 0, 0, 0.55)";
+ ctx.fillRect(Math.round(width * buffered) + 2, 0, width, height);
+ ctx.restore();
+ },
+
+ formatTime: function(time) {
+ if (isNaN(time))
+ time = 0;
+ var absTime = Math.abs(time);
+ var intSeconds = Math.floor(absTime % 60).toFixed(0);
+ var intMinutes = Math.floor((absTime / 60) % 60).toFixed(0);
+ var intHours = Math.floor(absTime / (60 * 60)).toFixed(0);
+ var sign = time < 0 ? '-' : String();
+
+ if (intHours > 0)
+ return sign + intHours + ':' + String('0' + intMinutes).slice(-2) + ":" + String('0' + intSeconds).slice(-2);
+
+ return sign + String('0' + intMinutes).slice(intMinutes >= 10 ? -2 : -1) + ":" + String('0' + intSeconds).slice(-2);
+ },
+
+ handlePlayButtonTouchStart: function() {
+ this.controls.playButton.classList.add('active');
+ },
+
+ handlePlayButtonTouchEnd: function(event) {
+ this.controls.playButton.classList.remove('active');
+
+ if (this.canPlay()) {
+ this.video.play();
+ this.showControls();
+ } else
+ this.video.pause();
+
+ return true;
+ },
+
+ handlePlayButtonTouchCancel: function(event) {
+ this.controls.playButton.classList.remove('active');
+ return true;
+ },
+
+ handleBaseGestureStart: function(event) {
+ this.gestureStartTime = new Date();
+ // If this gesture started with two fingers inside the video, then
+ // don't treat it as a potential zoom, unless we're still waiting
+ // to play.
+ if (this.mostRecentNumberOfTargettedTouches == 2 && this.controlsType != ControllerIOS.StartPlaybackControls)
+ event.preventDefault();
+ },
+
+ handleBaseGestureChange: function(event) {
+ if (!this.video.controls || this.isAudio() || this.isFullScreen() || this.gestureStartTime === undefined || this.controlsType == ControllerIOS.StartPlaybackControls)
+ return;
+
+ var scaleDetectionThreshold = 0.2;
+ if (event.scale > 1 + scaleDetectionThreshold || event.scale < 1 - scaleDetectionThreshold)
+ delete this.lastDoubleTouchTime;
+
+ if (this.mostRecentNumberOfTargettedTouches == 2 && event.scale >= 1.0)
+ event.preventDefault();
+
+ var currentGestureTime = new Date();
+ var duration = (currentGestureTime - this.gestureStartTime) / 1000;
+ if (!duration)
+ return;
+
+ var velocity = Math.abs(event.scale - 1) / duration;
+
+ var pinchOutVelocityThreshold = 2;
+ var pinchOutGestureScaleThreshold = 1.25;
+ if (velocity < pinchOutVelocityThreshold || event.scale < pinchOutGestureScaleThreshold)
+ return;
+
+ delete this.gestureStartTime;
+ this.video.webkitEnterFullscreen();
+ },
+
+ handleBaseGestureEnd: function(event) {
+ delete this.gestureStartTime;
+ },
+
+ handleWrapperTouchStart: function(event) {
+ if (event.target != this.base && event.target != this.controls.inlinePlaybackPlaceholder)
+ return;
+
+ this.mostRecentNumberOfTargettedTouches = event.targetTouches.length;
+
+ if (this.controlsAreHidden() || !this.controls.panel.classList.contains(this.ClassNames.show)) {
+ this.showControls();
+ this.resetHideControlsTimer();
+ } else if (!this.canPlay())
+ this.hideControls();
+ },
+
+ handlePanelTouchStart: function(event) {
+ this.video.style.webkitUserSelect = 'none';
+ },
+
+ handlePanelTouchEnd: function(event) {
+ this.video.style.removeProperty('-webkit-user-select');
+ },
+
+ handlePanelTouchCancel: function(event) {
+ this.video.style.removeProperty('-webkit-user-select');
+ },
+
+ handleVisibilityChange: function(event) {
+ this.updateShouldListenForPlaybackTargetAvailabilityEvent();
+ },
+
+ handlePanelTransitionEnd: function(event)
+ {
+ var opacity = window.getComputedStyle(this.controls.panel).opacity;
+ if (!parseInt(opacity) && !this.controlsAlwaysVisible()) {
+ this.base.removeChild(this.controls.inlinePlaybackPlaceholder);
+ this.base.removeChild(this.controls.panelContainer);
+ }
+ },
+
+ handleFullscreenButtonClicked: function(event) {
+ if ('webkitSetPresentationMode' in this.video) {
+ if (this.presentationMode() === 'fullscreen')
+ this.video.webkitSetPresentationMode('inline');
+ else
+ this.video.webkitSetPresentationMode('fullscreen');
+
+ return;
+ }
+
+ if (this.isFullScreen())
+ this.video.webkitExitFullscreen();
+ else
+ this.video.webkitEnterFullscreen();
+ },
+
+ handleFullscreenTouchStart: function() {
+ this.controls.fullscreenButton.classList.add('active');
+ },
+
+ handleFullscreenTouchEnd: function(event) {
+ this.controls.fullscreenButton.classList.remove('active');
+
+ this.handleFullscreenButtonClicked();
+
+ return true;
+ },
+
+ handleFullscreenTouchCancel: function(event) {
+ this.controls.fullscreenButton.classList.remove('active');
+ return true;
+ },
+
+ handlePictureInPictureTouchStart: function() {
+ this.controls.pictureInPictureButton.classList.add('active');
+ },
+
+ handlePictureInPictureTouchEnd: function(event) {
+ this.controls.pictureInPictureButton.classList.remove('active');
+
+ this.handlePictureInPictureButtonClicked();
+
+ return true;
+ },
+
+ handlePictureInPictureTouchCancel: function(event) {
+ this.controls.pictureInPictureButton.classList.remove('active');
+ return true;
+ },
+
+ handleStartPlaybackButtonTouchStart: function(event) {
+ this.controls.startPlaybackButton.classList.add('active');
+ this.controls.startPlaybackButton.querySelector('.webkit-media-controls-start-playback-glyph').classList.add('active');
+ },
+
+ handleStartPlaybackButtonTouchEnd: function(event) {
+ this.controls.startPlaybackButton.classList.remove('active');
+ this.controls.startPlaybackButton.querySelector('.webkit-media-controls-start-playback-glyph').classList.remove('active');
+
+ if (this.video.error)
+ return true;
+
+ this.video.play();
+ this.canToggleShowControlsButton = true;
+ this.updateControls();
+
+ return true;
+ },
+
+ handleStartPlaybackButtonTouchCancel: function(event) {
+ this.controls.startPlaybackButton.classList.remove('active');
+ return true;
+ },
+
+ handleTimelineTouchStart: function(event) {
+ this.scrubbing = true;
+ this.listenFor(this.controls.timeline, 'touchend', this.handleTimelineTouchEnd);
+ this.listenFor(this.controls.timeline, 'touchcancel', this.handleTimelineTouchEnd);
+ },
+
+ handleTimelineTouchEnd: function(event) {
+ this.stopListeningFor(this.controls.timeline, 'touchend', this.handleTimelineTouchEnd);
+ this.stopListeningFor(this.controls.timeline, 'touchcancel', this.handleTimelineTouchEnd);
+ this.scrubbing = false;
+ },
+
+ handleWirelessPickerButtonTouchStart: function() {
+ if (!this.video.error)
+ this.controls.wirelessTargetPicker.classList.add('active');
+ },
+
+ handleWirelessPickerButtonTouchEnd: function(event) {
+ this.controls.wirelessTargetPicker.classList.remove('active');
+ return this.handleWirelessPickerButtonClicked();
+ },
+
+ handleWirelessPickerButtonTouchCancel: function(event) {
+ this.controls.wirelessTargetPicker.classList.remove('active');
+ return true;
+ },
+
+ updateShouldListenForPlaybackTargetAvailabilityEvent: function() {
+ if (this.controlsType === ControllerIOS.StartPlaybackControls) {
+ this.setShouldListenForPlaybackTargetAvailabilityEvent(false);
+ return;
+ }
+
+ Controller.prototype.updateShouldListenForPlaybackTargetAvailabilityEvent.call(this);
+ },
+
+ updateWirelessTargetPickerButton: function() {
+ },
+
+ updateStatusDisplay: function(event)
+ {
+ this.controls.startPlaybackButton.classList.toggle(this.ClassNames.failed, this.video.error !== null);
+ this.controls.startPlaybackButton.querySelector(".webkit-media-controls-start-playback-glyph").classList.toggle(this.ClassNames.failed, this.video.error !== null);
+ Controller.prototype.updateStatusDisplay.call(this, event);
+ },
+
+ setPlaying: function(isPlaying)
+ {
+ Controller.prototype.setPlaying.call(this, isPlaying);
+
+ this.updateControls();
+
+ if (isPlaying && this.isAudio())
+ this.controls.timelineBox.classList.remove(this.ClassNames.hidden);
+
+ if (isPlaying)
+ this.hasPlayed = true;
+ else
+ this.showControls();
+ },
+
+ showControls: function()
+ {
+ this.updateShouldListenForPlaybackTargetAvailabilityEvent();
+ if (!this.video.controls)
+ return;
+
+ this.updateForShowingControls();
+ if (this.shouldHaveControls() && !this.controls.panelContainer.parentElement) {
+ this.base.appendChild(this.controls.inlinePlaybackPlaceholder);
+ this.base.appendChild(this.controls.panelContainer);
+ this.showShowControlsButton(false);
+ }
+ },
+
+ setShouldListenForPlaybackTargetAvailabilityEvent: function(shouldListen)
+ {
+ if (shouldListen && (this.shouldHaveStartPlaybackButton() || this.video.error))
+ return;
+
+ Controller.prototype.setShouldListenForPlaybackTargetAvailabilityEvent.call(this, shouldListen);
+ },
+
+ shouldReturnVideoLayerToInline: function()
+ {
+ return this.presentationMode() === 'inline';
+ },
+
+ updatePictureInPicturePlaceholder: function(event)
+ {
+ var presentationMode = this.presentationMode();
+
+ switch (presentationMode) {
+ case 'inline':
+ this.controls.panelContainer.classList.remove(this.ClassNames.pictureInPicture);
+ break;
+ case 'picture-in-picture':
+ this.controls.panelContainer.classList.add(this.ClassNames.pictureInPicture);
+ break;
+ default:
+ this.controls.panelContainer.classList.remove(this.ClassNames.pictureInPicture);
+ break;
+ }
+
+ Controller.prototype.updatePictureInPicturePlaceholder.call(this, event);
+ },
+
+ // Due to the bad way we are faking inheritance here, in particular the extends method
+ // on Controller.prototype, we don't copy getters and setters from the prototype. This
+ // means we have to implement them again, here in the subclass.
+ // FIXME: Use ES6 classes!
+
+ get scrubbing()
+ {
+ return Object.getOwnPropertyDescriptor(Controller.prototype, "scrubbing").get.call(this);
+ },
+
+ set scrubbing(flag)
+ {
+ Object.getOwnPropertyDescriptor(Controller.prototype, "scrubbing").set.call(this, flag);
+ },
+
+ get pageScaleFactor()
+ {
+ return this._pageScaleFactor;
+ },
+
+ set pageScaleFactor(newScaleFactor)
+ {
+ if (!newScaleFactor || this._pageScaleFactor === newScaleFactor)
+ return;
+
+ this._pageScaleFactor = newScaleFactor;
+
+ var scaleValue = 1 / newScaleFactor;
+ var scaleTransform = "scale(" + scaleValue + ")";
+
+ function applyScaleFactorToElement(element) {
+ if (scaleValue > 1) {
+ element.style.zoom = scaleValue;
+ element.style.webkitTransform = "scale(1)";
+ } else {
+ element.style.zoom = 1;
+ element.style.webkitTransform = scaleTransform;
+ }
+ }
+
+ if (this.controls.startPlaybackButton)
+ applyScaleFactorToElement(this.controls.startPlaybackButton);
+ if (this.controls.panel) {
+ applyScaleFactorToElement(this.controls.panel);
+ if (scaleValue > 1) {
+ this.controls.panel.style.width = "100%";
+ this.controls.timelineBox.style.webkitTextSizeAdjust = (100 * scaleValue) + "%";
+ } else {
+ var bottomAligment = -2 * scaleValue;
+ this.controls.panel.style.bottom = bottomAligment + "px";
+ this.controls.panel.style.paddingBottom = -(newScaleFactor * bottomAligment) + "px";
+ this.controls.panel.style.width = Math.round(newScaleFactor * 100) + "%";
+ this.controls.timelineBox.style.webkitTextSizeAdjust = "auto";
+ }
+ this.controls.panelBackground.style.height = (50 * scaleValue) + "px";
+
+ this.setNeedsTimelineMetricsUpdate();
+ this.updateProgress();
+ this.scheduleUpdateLayoutForDisplayedWidth();
+ }
+ },
+
+};
+
+Object.create(Controller.prototype).extend(ControllerIOS.prototype);
+Object.defineProperty(ControllerIOS.prototype, 'constructor', { enumerable: false, value: ControllerIOS });
diff --git a/Source/WebCore/Modules/mediasession/HTMLMediaElementMediaSession.cpp b/Source/WebCore/Modules/mediasession/HTMLMediaElementMediaSession.cpp
new file mode 100644
index 000000000..5469ca1f1
--- /dev/null
+++ b/Source/WebCore/Modules/mediasession/HTMLMediaElementMediaSession.cpp
@@ -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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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 "HTMLMediaElementMediaSession.h"
+
+#if ENABLE(MEDIA_SESSION) && ENABLE(VIDEO)
+
+namespace WebCore {
+
+const String& HTMLMediaElementMediaSession::kind(HTMLMediaElement* element)
+{
+ ASSERT(element);
+ return element->kind();
+}
+
+void HTMLMediaElementMediaSession::setKind(WebCore::HTMLMediaElement* element, const String& kind)
+{
+ ASSERT(element);
+ return element->setKind(kind);
+}
+
+MediaSession* HTMLMediaElementMediaSession::session(HTMLMediaElement* element)
+{
+ ASSERT(element);
+ return element->session();
+}
+
+void HTMLMediaElementMediaSession::setSession(HTMLMediaElement* element, MediaSession* session)
+{
+ ASSERT(element);
+ element->setSession(session);
+}
+
+} // namespace WebCore
+
+#endif /* ENABLE(MEDIA_SESSION) && ENABLE(VIDEO) */
diff --git a/Source/WebCore/Modules/mediasession/HTMLMediaElementMediaSession.h b/Source/WebCore/Modules/mediasession/HTMLMediaElementMediaSession.h
new file mode 100644
index 000000000..ec1718b5e
--- /dev/null
+++ b/Source/WebCore/Modules/mediasession/HTMLMediaElementMediaSession.h
@@ -0,0 +1,45 @@
+/*
+ * 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(MEDIA_SESSION) && ENABLE(VIDEO)
+
+#include "HTMLMediaElement.h"
+
+namespace WebCore {
+
+class HTMLMediaElementMediaSession {
+public:
+ static const String& kind(HTMLMediaElement*);
+ static void setKind(HTMLMediaElement*, const String&);
+
+ static MediaSession* session(HTMLMediaElement*);
+ static void setSession(HTMLMediaElement*, MediaSession*);
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(MEDIA_SESSION) && ENABLE(VIDEO)
diff --git a/Source/WebCore/Modules/mediastream/MediaStreamTrackSourcesCallback.idl b/Source/WebCore/Modules/mediasession/HTMLMediaElementMediaSession.idl
index 4c0593fcf..9357abc0c 100644
--- a/Source/WebCore/Modules/mediastream/MediaStreamTrackSourcesCallback.idl
+++ b/Source/WebCore/Modules/mediasession/HTMLMediaElementMediaSession.idl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 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
@@ -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 GOOGLE 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 GOOGLE 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,9 +24,9 @@
*/
[
- Conditional=MEDIA_STREAM,
- NoInterfaceObject,
-] callback interface MediaStreamTrackSourcesCallback {
- boolean handleEvent(sequence<SourceInfo> sources);
-};
+ Conditional=VIDEO&MEDIA_SESSION,
+] partial interface HTMLMediaElement {
+ attribute DOMString kind;
+ attribute MediaSession? session;
+};
diff --git a/Source/WebCore/Modules/mediasession/MediaRemoteControls.cpp b/Source/WebCore/Modules/mediasession/MediaRemoteControls.cpp
new file mode 100644
index 000000000..4a82775b0
--- /dev/null
+++ b/Source/WebCore/Modules/mediasession/MediaRemoteControls.cpp
@@ -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.
+ */
+
+#include "config.h"
+#include "MediaRemoteControls.h"
+
+#if ENABLE(MEDIA_SESSION)
+
+#include "MediaSession.h"
+
+namespace WebCore {
+
+MediaRemoteControls::MediaRemoteControls(ScriptExecutionContext& context, MediaSession* session)
+ : m_scriptExecutionContext(context)
+ , m_session(session)
+{
+}
+
+void MediaRemoteControls::clearSession()
+{
+ m_session = nullptr;
+}
+
+MediaRemoteControls::~MediaRemoteControls()
+{
+}
+
+void MediaRemoteControls::setPreviousTrackEnabled(bool isEnabled)
+{
+ if (m_previousTrackEnabled == isEnabled)
+ return;
+
+ m_previousTrackEnabled = isEnabled;
+
+ if (m_session)
+ m_session->controlIsEnabledDidChange();
+}
+
+void MediaRemoteControls::setNextTrackEnabled(bool isEnabled)
+{
+ if (m_nextTrackEnabled == isEnabled)
+ return;
+
+ m_nextTrackEnabled = isEnabled;
+
+ if (m_session)
+ m_session->controlIsEnabledDidChange();
+}
+
+}
+
+#endif /* ENABLE(MEDIA_SESSION) */
diff --git a/Source/WebCore/Modules/mediasession/MediaRemoteControls.h b/Source/WebCore/Modules/mediasession/MediaRemoteControls.h
new file mode 100644
index 000000000..cf24dea09
--- /dev/null
+++ b/Source/WebCore/Modules/mediasession/MediaRemoteControls.h
@@ -0,0 +1,76 @@
+/*
+ * 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(MEDIA_SESSION)
+
+#include "EventTarget.h"
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class MediaSession;
+
+class MediaRemoteControls : public RefCounted<MediaRemoteControls>, public EventTargetWithInlineData {
+public:
+ static Ref<MediaRemoteControls> create(ScriptExecutionContext& context, MediaSession* session = nullptr)
+ {
+ return adoptRef(*new MediaRemoteControls(context, session));
+ }
+
+ bool previousTrackEnabled() const { return m_previousTrackEnabled; }
+ void setPreviousTrackEnabled(bool);
+
+ bool nextTrackEnabled() const { return m_nextTrackEnabled; }
+ void setNextTrackEnabled(bool);
+
+ using RefCounted<MediaRemoteControls>::ref;
+ using RefCounted<MediaRemoteControls>::deref;
+
+ void clearSession();
+
+ virtual ~MediaRemoteControls();
+
+ EventTargetInterface eventTargetInterface() const override { return MediaRemoteControlsEventTargetInterfaceType; }
+ ScriptExecutionContext* scriptExecutionContext() const override { return &m_scriptExecutionContext; }
+
+private:
+ MediaRemoteControls(ScriptExecutionContext&, MediaSession*);
+
+ ScriptExecutionContext& m_scriptExecutionContext;
+
+ bool m_previousTrackEnabled { false };
+ bool m_nextTrackEnabled { false };
+
+ MediaSession* m_session { nullptr };
+
+ void refEventTarget() final { ref(); }
+ void derefEventTarget() final { deref(); }
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(MEDIA_SESSION)
diff --git a/Source/WebCore/Modules/mediastream/VideoStreamTrack.idl b/Source/WebCore/Modules/mediasession/MediaRemoteControls.idl
index 65c763d79..0ac900736 100644
--- a/Source/WebCore/Modules/mediastream/VideoStreamTrack.idl
+++ b/Source/WebCore/Modules/mediasession/MediaRemoteControls.idl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Apple 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
@@ -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,9 +24,13 @@
*/
[
- Conditional=MEDIA_STREAM,
+ Conditional=MEDIA_SESSION,
+ Constructor,
ConstructorCallWith=ScriptExecutionContext,
- Constructor(optional Dictionary videoConstraints),
-] interface VideoStreamTrack : MediaStreamTrack {
-};
+] interface MediaRemoteControls : EventTarget {
+ attribute boolean previousTrackEnabled;
+ attribute boolean nextTrackEnabled;
+ attribute EventHandler onprevioustrack;
+ attribute EventHandler onnexttrack;
+};
diff --git a/Source/WebCore/Modules/mediasession/MediaSession.cpp b/Source/WebCore/Modules/mediasession/MediaSession.cpp
new file mode 100644
index 000000000..5d141d3bc
--- /dev/null
+++ b/Source/WebCore/Modules/mediasession/MediaSession.cpp
@@ -0,0 +1,282 @@
+/*
+ * 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 "MediaSession.h"
+
+#if ENABLE(MEDIA_SESSION)
+
+#include "Chrome.h"
+#include "ChromeClient.h"
+#include "Event.h"
+#include "EventNames.h"
+#include "HTMLMediaElement.h"
+#include "MediaSessionManager.h"
+#include "Page.h"
+
+namespace WebCore {
+
+MediaSession::MediaSession(Document& document, Kind kind)
+ : m_document(document)
+ , m_kind(kind)
+{
+ // 4. Media Sessions
+ // 3. If media session's current media session type is "content", then create a new media remote controller for media
+ // session. (Otherwise media session has no media remote controller.)
+ if (m_kind == Kind::Content)
+ m_controls = MediaRemoteControls::create(document, this);
+
+ MediaSessionManager::singleton().addMediaSession(*this);
+}
+
+MediaSession::~MediaSession()
+{
+ MediaSessionManager::singleton().removeMediaSession(*this);
+
+ if (m_controls)
+ m_controls->clearSession();
+}
+
+MediaRemoteControls* MediaSession::controls()
+{
+ return m_controls.get();
+}
+
+void MediaSession::addMediaElement(HTMLMediaElement& element)
+{
+ ASSERT(!m_participatingElements.contains(&element));
+ m_participatingElements.add(&element);
+}
+
+void MediaSession::removeMediaElement(HTMLMediaElement& element)
+{
+ ASSERT(m_participatingElements.contains(&element));
+ m_participatingElements.remove(&element);
+
+ changeActiveMediaElements([&]() {
+ m_activeParticipatingElements.remove(&element);
+ });
+
+ if (m_iteratedActiveParticipatingElements)
+ m_iteratedActiveParticipatingElements->remove(&element);
+}
+
+void MediaSession::changeActiveMediaElements(std::function<void(void)> worker)
+{
+ if (Page *page = m_document.page()) {
+ bool hadActiveMediaElements = MediaSessionManager::singleton().hasActiveMediaElements();
+
+ worker();
+
+ bool hasActiveMediaElements = MediaSessionManager::singleton().hasActiveMediaElements();
+ if (hadActiveMediaElements != hasActiveMediaElements)
+ page->chrome().client().hasMediaSessionWithActiveMediaElementsDidChange(hasActiveMediaElements);
+ } else
+ worker();
+}
+
+void MediaSession::addActiveMediaElement(HTMLMediaElement& element)
+{
+ changeActiveMediaElements([&]() {
+ m_activeParticipatingElements.add(&element);
+ });
+}
+
+bool MediaSession::isMediaElementActive(HTMLMediaElement& element)
+{
+ return m_activeParticipatingElements.contains(&element);
+}
+
+bool MediaSession::hasActiveMediaElements() const
+{
+ return !m_activeParticipatingElements.isEmpty();
+}
+
+void MediaSession::setMetadata(const std::optional<Metadata>& optionalMetadata)
+{
+ if (!optionalMetadata)
+ m_metadata = { };
+ else {
+ auto& metadata = optionalMetadata.value();
+ m_metadata = { metadata.title, metadata.artist, metadata.album, m_document.completeURL(metadata.artwork) };
+ }
+
+ if (auto* page = m_document.page())
+ page->chrome().client().mediaSessionMetadataDidChange(m_metadata);
+}
+
+void MediaSession::deactivate()
+{
+ // 5.1.2. Object members
+ // When the deactivate() method is invoked, the user agent must run the following steps:
+ // 1. Let media session be the current media session.
+ // 2. Indefinitely pause all of media session’s audio-producing participants.
+ // 3. Set media session's resume list to an empty list.
+ // 4. Set media session's audio-producing participants to an empty list.
+ changeActiveMediaElements([&]() {
+ while (!m_activeParticipatingElements.isEmpty())
+ m_activeParticipatingElements.takeAny()->pause();
+ });
+
+ // 5. Run the media session deactivation algorithm for media session.
+ releaseInternal();
+}
+
+void MediaSession::releaseInternal()
+{
+ // 6.5. Releasing a media session
+ // 1. If current media session's current state is idle, then terminate these steps.
+ if (m_currentState == State::Idle)
+ return;
+
+ // 2. If current media session still has one or more active participating media elements, then terminate these steps.
+ if (!m_activeParticipatingElements.isEmpty())
+ return;
+
+ // 3. Optionally, based on platform conventions, the user agent must release any currently held platform media focus
+ // for current media session.
+ // 4. Optionally, based on platform conventions, the user agent must remove any previously established ongoing media
+ // interface in the underlying platform’s notifications area and any ongoing media interface in the underlying
+ // platform's lock screen area for current media session, if any.
+ // 5. Optionally, based on platform conventions, the user agent must prevent any hardware and/or software media keys
+ // from controlling playback of current media session's active participating media elements.
+ // 6. Set current media session's current state to idle.
+ m_currentState = State::Idle;
+}
+
+bool MediaSession::invoke()
+{
+ // 4.4 Activating a media session
+ // 1. If we're already ACTIVE then return success.
+ if (m_currentState == State::Active)
+ return true;
+
+ // 2. Optionally, based on platform conventions, request the most appropriate platform-level media focus for media
+ // session based on its current media session type.
+
+ // 3. Run these substeps...
+
+ // 4. Set our current state to ACTIVE and return success.
+ m_currentState = State::Active;
+ return true;
+}
+
+void MediaSession::handleDuckInterruption()
+{
+ for (auto* element : m_activeParticipatingElements)
+ element->setShouldDuck(true);
+
+ m_currentState = State::Interrupted;
+}
+
+void MediaSession::handleUnduckInterruption()
+{
+ for (auto* element : m_activeParticipatingElements)
+ element->setShouldDuck(false);
+
+ m_currentState = State::Active;
+}
+
+void MediaSession::handleIndefinitePauseInterruption()
+{
+ safelyIterateActiveMediaElements([](HTMLMediaElement* element) {
+ element->pause();
+ });
+
+ m_activeParticipatingElements.clear();
+ m_currentState = State::Idle;
+}
+
+void MediaSession::handlePauseInterruption()
+{
+ m_currentState = State::Interrupted;
+
+ safelyIterateActiveMediaElements([](HTMLMediaElement* element) {
+ element->pause();
+ });
+}
+
+void MediaSession::handleUnpauseInterruption()
+{
+ m_currentState = State::Active;
+
+ safelyIterateActiveMediaElements([](HTMLMediaElement* element) {
+ element->play();
+ });
+}
+
+void MediaSession::togglePlayback()
+{
+ safelyIterateActiveMediaElements([](HTMLMediaElement* element) {
+ if (element->paused())
+ element->play();
+ else
+ element->pause();
+ });
+}
+
+void MediaSession::safelyIterateActiveMediaElements(std::function<void(HTMLMediaElement*)> handler)
+{
+ ASSERT(!m_iteratedActiveParticipatingElements);
+
+ HashSet<HTMLMediaElement*> activeParticipatingElementsCopy = m_activeParticipatingElements;
+ m_iteratedActiveParticipatingElements = &activeParticipatingElementsCopy;
+
+ while (!activeParticipatingElementsCopy.isEmpty())
+ handler(activeParticipatingElementsCopy.takeAny());
+
+ m_iteratedActiveParticipatingElements = nullptr;
+}
+
+void MediaSession::skipToNextTrack()
+{
+ if (m_controls && m_controls->nextTrackEnabled())
+ m_controls->dispatchEvent(Event::create(eventNames().nexttrackEvent, false, false));
+}
+
+void MediaSession::skipToPreviousTrack()
+{
+ if (m_controls && m_controls->previousTrackEnabled())
+ m_controls->dispatchEvent(Event::create(eventNames().previoustrackEvent, false, false));
+}
+
+void MediaSession::controlIsEnabledDidChange()
+{
+ // Media remote controls are only allowed on Content media sessions.
+ ASSERT(m_kind == Kind::Content);
+
+ // Media elements belonging to Content media sessions have mutually-exclusive playback.
+ ASSERT(m_activeParticipatingElements.size() <= 1);
+
+ if (m_activeParticipatingElements.isEmpty())
+ return;
+
+ HTMLMediaElement* element = *m_activeParticipatingElements.begin();
+ m_document.updateIsPlayingMedia(element->elementID());
+}
+
+}
+
+#endif /* ENABLE(MEDIA_SESSION) */
diff --git a/Source/WebCore/Modules/mediasession/MediaSession.h b/Source/WebCore/Modules/mediasession/MediaSession.h
new file mode 100644
index 000000000..fb2b6d1ea
--- /dev/null
+++ b/Source/WebCore/Modules/mediasession/MediaSession.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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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_SESSION)
+
+#include "MediaRemoteControls.h"
+#include "MediaSessionMetadata.h"
+#include <wtf/HashSet.h>
+
+namespace WebCore {
+
+class Document;
+class HTMLMediaElement;
+
+class MediaSession final : public RefCounted<MediaSession> {
+public:
+ enum class Kind { Content, Transient, TransientSolo, Ambient };
+ enum class State { Idle, Active, Interrupted };
+
+ struct Metadata {
+ String title;
+ String artist;
+ String album;
+ String artwork;
+ };
+
+ static Ref<MediaSession> create(Document& document, Kind kind)
+ {
+ return adoptRef(*new MediaSession(document, kind));
+ }
+
+ ~MediaSession();
+
+ Kind kind() const { return m_kind; }
+ MediaRemoteControls* controls();
+
+ WEBCORE_EXPORT State currentState() const { return m_currentState; }
+ bool hasActiveMediaElements() const;
+
+ void setMetadata(const std::optional<Metadata>&);
+
+ void deactivate();
+
+ // Runs the media session invocation algorithm and returns true on success.
+ bool invoke();
+
+ void handleDuckInterruption();
+ void handleIndefinitePauseInterruption();
+ void handlePauseInterruption();
+ void handleUnduckInterruption();
+ void handleUnpauseInterruption();
+
+ void togglePlayback();
+ void skipToNextTrack();
+ void skipToPreviousTrack();
+
+ void controlIsEnabledDidChange();
+
+private:
+ friend class HTMLMediaElement;
+
+ MediaSession(Document&, Kind);
+
+ void addMediaElement(HTMLMediaElement&);
+ void removeMediaElement(HTMLMediaElement&);
+
+ void safelyIterateActiveMediaElements(std::function<void(HTMLMediaElement*)>);
+ void changeActiveMediaElements(std::function<void(void)>);
+ void addActiveMediaElement(HTMLMediaElement&);
+ bool isMediaElementActive(HTMLMediaElement&);
+
+ void releaseInternal();
+
+ State m_currentState { State::Idle };
+ HashSet<HTMLMediaElement*> m_participatingElements;
+ HashSet<HTMLMediaElement*> m_activeParticipatingElements;
+ HashSet<HTMLMediaElement*>* m_iteratedActiveParticipatingElements { nullptr };
+
+ Document& m_document;
+ const Kind m_kind;
+ RefPtr<MediaRemoteControls> m_controls;
+ MediaSessionMetadata m_metadata;
+};
+
+} // namespace WebCore
+
+#endif /* ENABLE(MEDIA_SESSION) */
diff --git a/Source/WebCore/Modules/mediasession/MediaSession.idl b/Source/WebCore/Modules/mediasession/MediaSession.idl
new file mode 100644
index 000000000..174306146
--- /dev/null
+++ b/Source/WebCore/Modules/mediasession/MediaSession.idl
@@ -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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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=MEDIA_SESSION,
+ ConstructorCallWith=ScriptExecutionContext,
+ CustomConstructor(optional MediaSessionKind kind = "content"),
+ ExportMacro=WEBCORE_EXPORT,
+ ImplementationLacksVTable,
+] interface MediaSession {
+ readonly attribute MediaSessionKind kind;
+ readonly attribute MediaRemoteControls? controls;
+
+ void setMetadata(MediaMetadata? metadata);
+
+ void deactivate();
+};
+
+enum MediaSessionKind {
+ "content",
+ "transient",
+ "transient-solo",
+ "ambient"
+};
+
+dictionary MediaMetadata {
+ DOMString title;
+ DOMString artist;
+ DOMString album;
+ USVString artwork;
+};
diff --git a/Source/WebCore/Modules/mediasession/MediaSessionEvents.h b/Source/WebCore/Modules/mediasession/MediaSessionEvents.h
new file mode 100644
index 000000000..a4a50d828
--- /dev/null
+++ b/Source/WebCore/Modules/mediasession/MediaSessionEvents.h
@@ -0,0 +1,40 @@
+/*
+ * 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(MEDIA_SESSION)
+
+namespace WebCore {
+
+enum MediaEventType {
+ PlayPause,
+ TrackNext,
+ TrackPrevious
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(MEDIA_SESSION)
diff --git a/Source/WebCore/Modules/mediasession/MediaSessionManager.cpp b/Source/WebCore/Modules/mediasession/MediaSessionManager.cpp
new file mode 100644
index 000000000..dd901a2ec
--- /dev/null
+++ b/Source/WebCore/Modules/mediasession/MediaSessionManager.cpp
@@ -0,0 +1,171 @@
+/*
+ * 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 "MediaSessionManager.h"
+
+#if ENABLE(MEDIA_SESSION)
+
+#include "MediaSession.h"
+#include "MediaSessionInterruptionProviderMac.h"
+
+namespace WebCore {
+
+static const char* contentSessionKind = "content";
+
+MediaSessionManager& MediaSessionManager::singleton()
+{
+ static NeverDestroyed<MediaSessionManager> manager;
+ return manager;
+}
+
+MediaSessionManager::MediaSessionManager()
+{
+#if PLATFORM(MAC)
+ m_interruptionProvider = adoptRef(new MediaSessionInterruptionProviderMac(*this));
+ m_interruptionProvider->beginListeningForInterruptions();
+#endif
+}
+
+bool MediaSessionManager::hasActiveMediaElements() const
+{
+ for (auto* session : m_sessions) {
+ if (session->hasActiveMediaElements())
+ return true;
+ }
+
+ return false;
+}
+
+void MediaSessionManager::addMediaSession(MediaSession& session)
+{
+ m_sessions.add(&session);
+}
+
+void MediaSessionManager::removeMediaSession(MediaSession& session)
+{
+ m_sessions.remove(&session);
+}
+
+void MediaSessionManager::togglePlayback()
+{
+ for (auto* session : m_sessions) {
+ String sessionKind = session->kind();
+ if (session->currentState() == MediaSession::State::Active && (sessionKind == contentSessionKind || sessionKind == ""))
+ session->togglePlayback();
+ }
+}
+
+void MediaSessionManager::skipToNextTrack()
+{
+ // 5.2.2 When the user presses the MediaTrackNext media key, then for each Content-based media session that is
+ // currently ACTIVE and has a media remote controller with its nextTrackEnabled attribute set to true, queue a task
+ // to fire a simple event named nexttrack at its media remote controller.
+ for (auto* session : m_sessions) {
+ if (session->currentState() == MediaSession::State::Active && session->kind() == contentSessionKind)
+ session->skipToNextTrack();
+ }
+}
+
+void MediaSessionManager::skipToPreviousTrack()
+{
+ // 5.2.2 When the user presses the MediaTrackPrevious media key, then for each Content-based media session that is
+ // currently ACTIVE and has a media remote controller with its previousTrackEnabled attribute set to true, queue a task
+ // to fire a simple event named previoustrack at its media remote controller.
+ for (auto* session : m_sessions) {
+ if (session->currentState() == MediaSession::State::Active && session->kind() == contentSessionKind)
+ session->skipToPreviousTrack();
+ }
+}
+
+void MediaSessionManager::didReceiveStartOfInterruptionNotification(MediaSessionInterruptingCategory interruptingCategory)
+{
+ // 4.5.2 Interrupting a media session
+ // When a start-of-interruption notification event is received from the platform, then the user agent must run the
+ // media session interruption algorithm against all known media sessions, passing in each media session as media session.
+ for (auto* session : m_sessions) {
+ // 1. If media session's current state is not active, then terminate these steps.
+ if (session->currentState() != MediaSession::State::Active)
+ continue;
+
+ // 2. Let interrupting media session category be the media session category that triggered this interruption.
+ // If this interruption has no known media session category, let interrupting media session category be Default.
+
+ // 3. Run these substeps:
+ if (interruptingCategory == MediaSessionInterruptingCategory::Content) {
+ // - If interrupting media session category is Content:
+ // If media session's current media session type is Default or Content then indefinitely pause all of media
+ // session's active audio-producing participants and set media session's current state to idle.
+ if (session->kind() == MediaSessionKind::Default || session->kind() == MediaSessionKind::Content)
+ session->handleIndefinitePauseInterruption();
+ } else if (interruptingCategory == MediaSessionInterruptingCategory::Transient) {
+ // - If interrupting media session category is Transient:
+ // If media session's current media session type is Default or Content then duck all of media session's active
+ // audio-producing participants and set media session's current state to interrupted.
+ if (session->kind() == MediaSessionKind::Default || session->kind() == MediaSessionKind::Content)
+ session->handleDuckInterruption();
+ } else if (interruptingCategory == MediaSessionInterruptingCategory::TransientSolo) {
+ // - If interrupting media session category is Transient Solo:
+ // If media session's current media session type is Default, Content, Transient or Transient Solo then pause
+ // all of media session's active audio-producing participants and set current media session's current state to interrupted.
+ if (session->kind() != MediaSessionKind::Ambient)
+ session->handlePauseInterruption();
+ }
+ }
+}
+
+void MediaSessionManager::didReceiveEndOfInterruptionNotification(MediaSessionInterruptingCategory interruptingCategory)
+{
+ // 4.5.2 Interrupting a media session
+ // When an end-of-interruption notification event is received from the platform, then the user agent must run the
+ // media session continuation algorithm against all known media sessions, passing in each media session as media session.
+ for (auto* session : m_sessions) {
+ // 1. If media session's current state is not interrupted, then terminate these steps.
+ if (session->currentState() != MediaSession::State::Interrupted)
+ continue;
+
+ // 2. Let interrupting media session category be the media session category that initially triggered this interruption.
+ // If this interruption has no known media session category, let interrupting media session category be Default.
+
+ // 3. Run these substeps:
+ if (interruptingCategory == MediaSessionInterruptingCategory::Transient) {
+ // - If interrupting media session category is Transient:
+ // If media session's current media session type is Default or Content, then unduck all of media session's
+ // active audio-producing participants and set media session's current state to active.
+ if (session->kind() == MediaSessionKind::Default || session->kind() == MediaSessionKind::Content)
+ session->handleUnduckInterruption();
+ } else if (interruptingCategory == MediaSessionInterruptingCategory::TransientSolo) {
+ // - If interrupting media session category is Transient Solo:
+ // If media session's current media session type is Default, Content, Transient, or Transient Solo, then
+ // unpause the media session's active audio-producing participants and set media session's current state to active.
+ if (session->kind() != MediaSessionKind::Ambient)
+ session->handleUnpauseInterruption();
+ }
+ }
+}
+
+} // namespace WebCore
+
+#endif /* ENABLE(MEDIA_SESSION) */
diff --git a/Source/WebCore/Modules/mediasession/MediaSessionManager.h b/Source/WebCore/Modules/mediasession/MediaSessionManager.h
new file mode 100644
index 000000000..4cde38abc
--- /dev/null
+++ b/Source/WebCore/Modules/mediasession/MediaSessionManager.h
@@ -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.
+ */
+
+#pragma once
+
+#if ENABLE(MEDIA_SESSION)
+
+#include "MediaSessionInterruptionProvider.h"
+#include <wtf/HashSet.h>
+#include <wtf/NeverDestroyed.h>
+
+namespace WebCore {
+
+class MediaSession;
+
+class MediaSessionManager : public MediaSessionInterruptionProviderClient {
+ friend class NeverDestroyed<MediaSessionManager>;
+public:
+ WEBCORE_EXPORT static MediaSessionManager& singleton();
+
+ WEBCORE_EXPORT void togglePlayback();
+ WEBCORE_EXPORT void skipToNextTrack();
+ WEBCORE_EXPORT void skipToPreviousTrack();
+
+ WEBCORE_EXPORT void didReceiveStartOfInterruptionNotification(MediaSessionInterruptingCategory) override;
+ WEBCORE_EXPORT void didReceiveEndOfInterruptionNotification(MediaSessionInterruptingCategory) override;
+
+private:
+ friend class MediaSession;
+
+ MediaSessionManager();
+
+ bool hasActiveMediaElements() const;
+
+ void addMediaSession(MediaSession&);
+ void removeMediaSession(MediaSession&);
+
+ RefPtr<MediaSessionInterruptionProvider> m_interruptionProvider;
+ HashSet<MediaSession*> m_sessions;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(MEDIA_SESSION)
diff --git a/Source/WebCore/Modules/mediasession/MediaSessionMetadata.h b/Source/WebCore/Modules/mediasession/MediaSessionMetadata.h
new file mode 100644
index 000000000..e3644a875
--- /dev/null
+++ b/Source/WebCore/Modules/mediasession/MediaSessionMetadata.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(MEDIA_SESSION)
+
+#include "URL.h"
+
+namespace WebCore {
+
+class MediaSessionMetadata final {
+public:
+ MediaSessionMetadata() = default;
+
+ MediaSessionMetadata(const String& title, const String& artist, const String& album, const URL& artworkURL)
+ : m_title(title)
+ , m_artist(artist)
+ , m_album(album)
+ , m_artworkURL(artworkURL) { }
+
+ String title() const { return m_title; }
+ String artist() const { return m_artist; }
+ String album() const { return m_album; }
+ URL artworkURL() const { return m_artworkURL; }
+
+private:
+ String m_title;
+ String m_artist;
+ String m_album;
+ URL m_artworkURL;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(MEDIA_SESSION)
diff --git a/Source/WebCore/Modules/mediasession/WebMediaSessionManager.cpp b/Source/WebCore/Modules/mediasession/WebMediaSessionManager.cpp
new file mode 100644
index 000000000..7a3369f10
--- /dev/null
+++ b/Source/WebCore/Modules/mediasession/WebMediaSessionManager.cpp
@@ -0,0 +1,469 @@
+/*
+ * 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 "WebMediaSessionManager.h"
+
+#if ENABLE(WIRELESS_PLAYBACK_TARGET) && !PLATFORM(IOS)
+
+#include "FloatRect.h"
+#include "Logging.h"
+#include "MediaPlaybackTargetPickerMock.h"
+#include "WebMediaSessionManagerClient.h"
+#include <wtf/text/StringBuilder.h>
+
+namespace WebCore {
+
+static const double taskDelayInterval = 1.0 / 10.0;
+
+struct ClientState {
+ explicit ClientState(WebMediaSessionManagerClient& client, uint64_t contextId)
+ : client(client)
+ , contextId(contextId)
+ {
+ }
+
+ bool operator == (ClientState const& other) const
+ {
+ return contextId == other.contextId && &client == &other.client;
+ }
+
+ WebMediaSessionManagerClient& client;
+ uint64_t contextId { 0 };
+ WebCore::MediaProducer::MediaStateFlags flags { WebCore::MediaProducer::IsNotPlaying };
+ bool requestedPicker { false };
+ bool previouslyRequestedPicker { false };
+ bool configurationRequired { true };
+ bool playedToEnd { false };
+};
+
+static bool flagsAreSet(MediaProducer::MediaStateFlags value, unsigned flags)
+{
+ return value & flags;
+}
+
+#if !LOG_DISABLED
+static String mediaProducerStateString(MediaProducer::MediaStateFlags flags)
+{
+ StringBuilder string;
+ if (flags & MediaProducer::IsPlayingAudio)
+ string.append("IsPlayingAudio + ");
+ if (flags & MediaProducer::IsPlayingVideo)
+ string.append("IsPlayingVideo + ");
+ if (flags & MediaProducer::IsPlayingToExternalDevice)
+ string.append("IsPlayingToExternalDevice + ");
+ if (flags & MediaProducer::HasPlaybackTargetAvailabilityListener)
+ string.append("HasPlaybackTargetAvailabilityListener + ");
+ if (flags & MediaProducer::RequiresPlaybackTargetMonitoring)
+ string.append("RequiresPlaybackTargetMonitoring + ");
+ if (flags & MediaProducer::ExternalDeviceAutoPlayCandidate)
+ string.append("ExternalDeviceAutoPlayCandidate + ");
+ if (flags & MediaProducer::DidPlayToEnd)
+ string.append("DidPlayToEnd + ");
+ if (flags & MediaProducer::HasAudioOrVideo)
+ string.append("HasAudioOrVideo + ");
+ if (string.isEmpty())
+ string.append("IsNotPlaying");
+ else
+ string.resize(string.length() - 2);
+
+ return string.toString();
+}
+#endif
+
+void WebMediaSessionManager::setMockMediaPlaybackTargetPickerEnabled(bool enabled)
+{
+ LOG(Media, "WebMediaSessionManager::setMockMediaPlaybackTargetPickerEnabled - enabled = %i", (int)enabled);
+
+ if (m_mockPickerEnabled == enabled)
+ return;
+
+ m_mockPickerEnabled = enabled;
+}
+
+void WebMediaSessionManager::setMockMediaPlaybackTargetPickerState(const String& name, MediaPlaybackTargetContext::State state)
+{
+ LOG(Media, "WebMediaSessionManager::setMockMediaPlaybackTargetPickerState - name = %s, state = %i", name.utf8().data(), (int)state);
+
+ mockPicker().setState(name, state);
+}
+
+MediaPlaybackTargetPickerMock& WebMediaSessionManager::mockPicker()
+{
+ if (!m_pickerOverride)
+ m_pickerOverride = std::make_unique<MediaPlaybackTargetPickerMock>(*this);
+
+ return *m_pickerOverride.get();
+}
+
+WebCore::MediaPlaybackTargetPicker& WebMediaSessionManager::targetPicker()
+{
+ if (m_mockPickerEnabled)
+ return mockPicker();
+
+ return platformPicker();
+}
+
+WebMediaSessionManager::WebMediaSessionManager()
+ : m_taskTimer(RunLoop::current(), this, &WebMediaSessionManager::taskTimerFired)
+ , m_watchdogTimer(RunLoop::current(), this, &WebMediaSessionManager::watchdogTimerFired)
+{
+}
+
+WebMediaSessionManager::~WebMediaSessionManager()
+{
+}
+
+uint64_t WebMediaSessionManager::addPlaybackTargetPickerClient(WebMediaSessionManagerClient& client, uint64_t contextId)
+{
+ size_t index = find(&client, contextId);
+ ASSERT(index == notFound);
+ if (index != notFound)
+ return 0;
+
+ LOG(Media, "WebMediaSessionManager::addPlaybackTargetPickerClient(%p + %llu)", &client, contextId);
+
+ m_clientState.append(std::make_unique<ClientState>(client, contextId));
+
+ if (m_externalOutputDeviceAvailable || m_playbackTarget)
+ scheduleDelayedTask(InitialConfigurationTask | TargetClientsConfigurationTask);
+
+ return contextId;
+}
+
+void WebMediaSessionManager::removePlaybackTargetPickerClient(WebMediaSessionManagerClient& client, uint64_t contextId)
+{
+ size_t index = find(&client, contextId);
+ ASSERT(index != notFound);
+ if (index == notFound)
+ return;
+
+ LOG(Media, "WebMediaSessionManager::removePlaybackTargetPickerClient(%p + %llu)", &client, contextId);
+
+ m_clientState.remove(index);
+ scheduleDelayedTask(TargetMonitoringConfigurationTask | TargetClientsConfigurationTask);
+}
+
+void WebMediaSessionManager::removeAllPlaybackTargetPickerClients(WebMediaSessionManagerClient& client)
+{
+ if (m_clientState.isEmpty())
+ return;
+
+ LOG(Media, "WebMediaSessionManager::removeAllPlaybackTargetPickerClients(%p)", &client);
+
+ for (size_t i = m_clientState.size(); i > 0; --i) {
+ if (&m_clientState[i - 1]->client == &client)
+ m_clientState.remove(i - 1);
+ }
+ scheduleDelayedTask(TargetMonitoringConfigurationTask | TargetClientsConfigurationTask);
+}
+
+void WebMediaSessionManager::showPlaybackTargetPicker(WebMediaSessionManagerClient& client, uint64_t contextId, const IntRect& rect, bool)
+{
+ size_t index = find(&client, contextId);
+ ASSERT(index != notFound);
+ if (index == notFound)
+ return;
+
+ auto& clientRequestingPicker = m_clientState[index];
+ for (auto& state : m_clientState) {
+ state->requestedPicker = state == clientRequestingPicker;
+ state->previouslyRequestedPicker = state == clientRequestingPicker;
+ }
+
+ bool hasActiveRoute = flagsAreSet(m_clientState[index]->flags, MediaProducer::IsPlayingToExternalDevice);
+ LOG(Media, "WebMediaSessionManager::showPlaybackTargetPicker(%p + %llu) - hasActiveRoute = %i", &client, contextId, (int)hasActiveRoute);
+ targetPicker().showPlaybackTargetPicker(FloatRect(rect), hasActiveRoute);
+}
+
+void WebMediaSessionManager::clientStateDidChange(WebMediaSessionManagerClient& client, uint64_t contextId, MediaProducer::MediaStateFlags newFlags)
+{
+ size_t index = find(&client, contextId);
+ ASSERT(index != notFound);
+ if (index == notFound)
+ return;
+
+ auto& changedClientState = m_clientState[index];
+ MediaProducer::MediaStateFlags oldFlags = changedClientState->flags;
+ if (newFlags == oldFlags)
+ return;
+
+ LOG(Media, "WebMediaSessionManager::clientStateDidChange(%p + %llu) - new flags = %s, old flags = %s", &client, contextId, mediaProducerStateString(newFlags).utf8().data(), mediaProducerStateString(oldFlags).utf8().data());
+
+ changedClientState->flags = newFlags;
+
+ MediaProducer::MediaStateFlags updateConfigurationFlags = MediaProducer::RequiresPlaybackTargetMonitoring | MediaProducer::HasPlaybackTargetAvailabilityListener | MediaProducer::HasAudioOrVideo;
+ if ((oldFlags & updateConfigurationFlags) != (newFlags & updateConfigurationFlags))
+ scheduleDelayedTask(TargetMonitoringConfigurationTask);
+
+ MediaProducer::MediaStateFlags playingToTargetFlags = MediaProducer::IsPlayingToExternalDevice | MediaProducer::IsPlayingVideo;
+ if ((oldFlags & playingToTargetFlags) != (newFlags & playingToTargetFlags)) {
+ if (flagsAreSet(oldFlags, MediaProducer::IsPlayingVideo) && !flagsAreSet(newFlags, MediaProducer::IsPlayingVideo) && flagsAreSet(newFlags, MediaProducer::DidPlayToEnd))
+ changedClientState->playedToEnd = true;
+ scheduleDelayedTask(WatchdogTimerConfigurationTask);
+ }
+
+ if (!m_playbackTarget || !m_playbackTarget->hasActiveRoute() || !flagsAreSet(newFlags, MediaProducer::ExternalDeviceAutoPlayCandidate))
+ return;
+
+ // Do not interrupt another element already playing to a device.
+ for (auto& state : m_clientState) {
+ if (state == changedClientState)
+ continue;
+
+ if (flagsAreSet(state->flags, MediaProducer::IsPlayingToExternalDevice) && flagsAreSet(state->flags, MediaProducer::IsPlayingVideo))
+ return;
+ }
+
+ // Do not begin playing to the device unless playback has just started.
+ if (!flagsAreSet(newFlags, MediaProducer::IsPlayingVideo) || flagsAreSet(oldFlags, MediaProducer::IsPlayingVideo))
+ return;
+
+ for (auto& state : m_clientState) {
+ if (state == changedClientState)
+ continue;
+ state->client.setShouldPlayToPlaybackTarget(state->contextId, false);
+ }
+
+ changedClientState->client.setShouldPlayToPlaybackTarget(changedClientState->contextId, true);
+
+ if (index && m_clientState.size() > 1)
+ std::swap(m_clientState.at(index), m_clientState.at(0));
+}
+
+void WebMediaSessionManager::setPlaybackTarget(Ref<MediaPlaybackTarget>&& target)
+{
+ m_playbackTarget = WTFMove(target);
+ m_targetChanged = true;
+ scheduleDelayedTask(TargetClientsConfigurationTask);
+}
+
+void WebMediaSessionManager::externalOutputDeviceAvailableDidChange(bool available)
+{
+ LOG(Media, "WebMediaSessionManager::externalOutputDeviceAvailableDidChange - clients = %zu, available = %i", m_clientState.size(), (int)available);
+
+ m_externalOutputDeviceAvailable = available;
+ for (auto& state : m_clientState)
+ state->client.externalOutputDeviceAvailableDidChange(state->contextId, available);
+}
+
+void WebMediaSessionManager::configureNewClients()
+{
+ for (auto& state : m_clientState) {
+ if (!state->configurationRequired)
+ continue;
+
+ state->configurationRequired = false;
+ if (m_externalOutputDeviceAvailable)
+ state->client.externalOutputDeviceAvailableDidChange(state->contextId, true);
+
+ if (m_playbackTarget)
+ state->client.setPlaybackTarget(state->contextId, *m_playbackTarget.copyRef());
+ }
+}
+
+void WebMediaSessionManager::configurePlaybackTargetClients()
+{
+ if (m_clientState.isEmpty())
+ return;
+
+ size_t indexOfClientThatRequestedPicker = notFound;
+ size_t indexOfLastClientToRequestPicker = notFound;
+ size_t indexOfClientWillPlayToTarget = notFound;
+ bool haveActiveRoute = m_playbackTarget && m_playbackTarget->hasActiveRoute();
+
+ for (size_t i = 0; i < m_clientState.size(); ++i) {
+ auto& state = m_clientState[i];
+
+ LOG(Media, "WebMediaSessionManager::configurePlaybackTargetClients %zu - client (%p + %llu) requestedPicker = %i, flags = %s", i, &state->client, state->contextId, state->requestedPicker, mediaProducerStateString(state->flags).utf8().data());
+
+ if (m_targetChanged && state->requestedPicker)
+ indexOfClientThatRequestedPicker = i;
+
+ if (indexOfClientWillPlayToTarget == notFound && flagsAreSet(state->flags, MediaProducer::IsPlayingToExternalDevice))
+ indexOfClientWillPlayToTarget = i;
+
+ if (indexOfClientWillPlayToTarget == notFound && haveActiveRoute && state->previouslyRequestedPicker)
+ indexOfLastClientToRequestPicker = i;
+ }
+
+ if (indexOfClientThatRequestedPicker != notFound)
+ indexOfClientWillPlayToTarget = indexOfClientThatRequestedPicker;
+ if (indexOfClientWillPlayToTarget == notFound && indexOfLastClientToRequestPicker != notFound)
+ indexOfClientWillPlayToTarget = indexOfLastClientToRequestPicker;
+ if (indexOfClientWillPlayToTarget == notFound && haveActiveRoute && flagsAreSet(m_clientState[0]->flags, MediaProducer::ExternalDeviceAutoPlayCandidate) && !flagsAreSet(m_clientState[0]->flags, MediaProducer::IsPlayingVideo))
+ indexOfClientWillPlayToTarget = 0;
+
+ LOG(Media, "WebMediaSessionManager::configurePlaybackTargetClients - indexOfClientWillPlayToTarget = %zu", indexOfClientWillPlayToTarget);
+
+ for (size_t i = 0; i < m_clientState.size(); ++i) {
+ auto& state = m_clientState[i];
+
+ if (m_playbackTarget)
+ state->client.setPlaybackTarget(state->contextId, *m_playbackTarget.copyRef());
+
+ if (i != indexOfClientWillPlayToTarget || !haveActiveRoute)
+ state->client.setShouldPlayToPlaybackTarget(state->contextId, false);
+
+ state->configurationRequired = false;
+ if (m_targetChanged)
+ state->requestedPicker = false;
+ }
+
+ if (haveActiveRoute && indexOfClientWillPlayToTarget != notFound) {
+ auto& state = m_clientState[indexOfClientWillPlayToTarget];
+ if (!flagsAreSet(state->flags, MediaProducer::IsPlayingToExternalDevice))
+ state->client.setShouldPlayToPlaybackTarget(state->contextId, true);
+ }
+
+ m_targetChanged = false;
+ configureWatchdogTimer();
+}
+
+void WebMediaSessionManager::configurePlaybackTargetMonitoring()
+{
+ bool monitoringRequired = false;
+ bool hasAvailabilityListener = false;
+ bool haveClientWithMedia = false;
+ for (auto& state : m_clientState) {
+ if (state->flags & MediaProducer::RequiresPlaybackTargetMonitoring) {
+ monitoringRequired = true;
+ break;
+ }
+ if (state->flags & MediaProducer::HasPlaybackTargetAvailabilityListener)
+ hasAvailabilityListener = true;
+ if (state->flags & MediaProducer::HasAudioOrVideo)
+ haveClientWithMedia = true;
+ }
+
+ LOG(Media, "WebMediaSessionManager::configurePlaybackTargetMonitoring - monitoringRequired = %i", static_cast<int>(monitoringRequired || (hasAvailabilityListener && haveClientWithMedia)));
+
+ if (monitoringRequired || (hasAvailabilityListener && haveClientWithMedia))
+ targetPicker().startingMonitoringPlaybackTargets();
+ else
+ targetPicker().stopMonitoringPlaybackTargets();
+}
+
+#if !LOG_DISABLED
+String WebMediaSessionManager::toString(ConfigurationTasks tasks)
+{
+ StringBuilder string;
+ if (tasks & InitialConfigurationTask)
+ string.append("InitialConfigurationTask + ");
+ if (tasks & TargetClientsConfigurationTask)
+ string.append("TargetClientsConfigurationTask + ");
+ if (tasks & TargetMonitoringConfigurationTask)
+ string.append("TargetMonitoringConfigurationTask + ");
+ if (tasks & WatchdogTimerConfigurationTask)
+ string.append("WatchdogTimerConfigurationTask + ");
+ if (string.isEmpty())
+ string.append("NoTask");
+ else
+ string.resize(string.length() - 2);
+
+ return string.toString();
+}
+#endif
+
+void WebMediaSessionManager::scheduleDelayedTask(ConfigurationTasks tasks)
+{
+ LOG(Media, "WebMediaSessionManager::scheduleDelayedTask - %s", toString(tasks).utf8().data());
+
+ m_taskFlags |= tasks;
+ m_taskTimer.startOneShot(taskDelayInterval);
+}
+
+void WebMediaSessionManager::taskTimerFired()
+{
+ LOG(Media, "WebMediaSessionManager::taskTimerFired - tasks = %s", toString(m_taskFlags).utf8().data());
+
+ if (m_taskFlags & InitialConfigurationTask)
+ configureNewClients();
+ if (m_taskFlags & TargetClientsConfigurationTask)
+ configurePlaybackTargetClients();
+ if (m_taskFlags & TargetMonitoringConfigurationTask)
+ configurePlaybackTargetMonitoring();
+ if (m_taskFlags & WatchdogTimerConfigurationTask)
+ configureWatchdogTimer();
+
+ m_taskFlags = NoTask;
+}
+
+size_t WebMediaSessionManager::find(WebMediaSessionManagerClient* client, uint64_t contextId)
+{
+ for (size_t i = 0; i < m_clientState.size(); ++i) {
+ if (m_clientState[i]->contextId == contextId && &m_clientState[i]->client == client)
+ return i;
+ }
+
+ return notFound;
+}
+
+void WebMediaSessionManager::configureWatchdogTimer()
+{
+ static const double watchdogTimerIntervalAfterPausing = 60 * 60;
+ static const double watchdogTimerIntervalAfterPlayingToEnd = 8 * 60;
+
+ if (!m_playbackTarget || !m_playbackTarget->hasActiveRoute()) {
+ m_watchdogTimer.stop();
+ return;
+ }
+
+ bool stopTimer = false;
+ bool didPlayToEnd = false;
+ for (auto& state : m_clientState) {
+ if (flagsAreSet(state->flags, MediaProducer::IsPlayingToExternalDevice) && flagsAreSet(state->flags, MediaProducer::IsPlayingVideo))
+ stopTimer = true;
+ if (state->playedToEnd)
+ didPlayToEnd = true;
+ state->playedToEnd = false;
+ }
+
+ if (stopTimer) {
+ m_currentWatchdogInterval = 0;
+ m_watchdogTimer.stop();
+ LOG(Media, "WebMediaSessionManager::configureWatchdogTimer - timer stopped");
+ } else {
+ double interval = didPlayToEnd ? watchdogTimerIntervalAfterPlayingToEnd : watchdogTimerIntervalAfterPausing;
+ if (interval != m_currentWatchdogInterval || !m_watchdogTimer.isActive()) {
+ m_watchdogTimer.startOneShot(interval);
+ LOG(Media, "WebMediaSessionManager::configureWatchdogTimer - timer scheduled for %.0f", interval);
+ }
+ m_currentWatchdogInterval = interval;
+ }
+}
+
+void WebMediaSessionManager::watchdogTimerFired()
+{
+ LOG(Media, "WebMediaSessionManager::watchdogTimerFired");
+ if (!m_playbackTarget)
+ return;
+
+ targetPicker().invalidatePlaybackTargets();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WIRELESS_PLAYBACK_TARGET) && !PLATFORM(IOS)
diff --git a/Source/WebCore/Modules/mediasession/WebMediaSessionManager.h b/Source/WebCore/Modules/mediasession/WebMediaSessionManager.h
new file mode 100644
index 000000000..57d58ef91
--- /dev/null
+++ b/Source/WebCore/Modules/mediasession/WebMediaSessionManager.h
@@ -0,0 +1,113 @@
+/*
+ * 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(WIRELESS_PLAYBACK_TARGET) && !PLATFORM(IOS)
+
+#include "MediaPlaybackTargetContext.h"
+#include "MediaPlaybackTargetPicker.h"
+#include "MediaPlaybackTargetPickerMock.h"
+#include "MediaProducer.h"
+#include "Timer.h"
+#include <wtf/Ref.h>
+#include <wtf/RefPtr.h>
+#include <wtf/RunLoop.h>
+
+namespace WebCore {
+
+struct ClientState;
+class IntRect;
+class WebMediaSessionManagerClient;
+
+class WebMediaSessionManager : public MediaPlaybackTargetPicker::Client {
+ WTF_MAKE_NONCOPYABLE(WebMediaSessionManager);
+public:
+
+ WEBCORE_EXPORT static WebMediaSessionManager& shared();
+
+ WEBCORE_EXPORT void setMockMediaPlaybackTargetPickerEnabled(bool);
+ WEBCORE_EXPORT void setMockMediaPlaybackTargetPickerState(const String&, MediaPlaybackTargetContext::State);
+
+ WEBCORE_EXPORT uint64_t addPlaybackTargetPickerClient(WebMediaSessionManagerClient&, uint64_t);
+ WEBCORE_EXPORT void removePlaybackTargetPickerClient(WebMediaSessionManagerClient&, uint64_t);
+ WEBCORE_EXPORT void removeAllPlaybackTargetPickerClients(WebMediaSessionManagerClient&);
+ WEBCORE_EXPORT void showPlaybackTargetPicker(WebMediaSessionManagerClient&, uint64_t, const IntRect&, bool);
+ WEBCORE_EXPORT void clientStateDidChange(WebMediaSessionManagerClient&, uint64_t, WebCore::MediaProducer::MediaStateFlags);
+
+protected:
+ WebMediaSessionManager();
+ virtual ~WebMediaSessionManager();
+
+ virtual WebCore::MediaPlaybackTargetPicker& platformPicker() = 0;
+ static WebMediaSessionManager& platformManager();
+
+private:
+
+ WebCore::MediaPlaybackTargetPicker& targetPicker();
+ WebCore::MediaPlaybackTargetPickerMock& mockPicker();
+
+ // MediaPlaybackTargetPicker::Client
+ void setPlaybackTarget(Ref<WebCore::MediaPlaybackTarget>&&) override;
+ void externalOutputDeviceAvailableDidChange(bool) override;
+
+ size_t find(WebMediaSessionManagerClient*, uint64_t);
+
+ void configurePlaybackTargetClients();
+ void configureNewClients();
+ void configurePlaybackTargetMonitoring();
+ void configureWatchdogTimer();
+
+ enum ConfigurationTaskFlags {
+ NoTask = 0,
+ InitialConfigurationTask = 1 << 0,
+ TargetClientsConfigurationTask = 1 << 1,
+ TargetMonitoringConfigurationTask = 1 << 2,
+ WatchdogTimerConfigurationTask = 1 << 3,
+ };
+ typedef unsigned ConfigurationTasks;
+ String toString(ConfigurationTasks);
+
+ void scheduleDelayedTask(ConfigurationTasks);
+ void taskTimerFired();
+
+ void watchdogTimerFired();
+
+ RunLoop::Timer<WebMediaSessionManager> m_taskTimer;
+ RunLoop::Timer<WebMediaSessionManager> m_watchdogTimer;
+
+ Vector<std::unique_ptr<ClientState>> m_clientState;
+ RefPtr<MediaPlaybackTarget> m_playbackTarget;
+ std::unique_ptr<WebCore::MediaPlaybackTargetPickerMock> m_pickerOverride;
+ ConfigurationTasks m_taskFlags { NoTask };
+ double m_currentWatchdogInterval { 0 };
+ bool m_externalOutputDeviceAvailable { false };
+ bool m_targetChanged { false };
+ bool m_mockPickerEnabled { false };
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WIRELESS_PLAYBACK_TARGET) && !PLATFORM(IOS)
diff --git a/Source/WebCore/Modules/mediasession/WebMediaSessionManagerClient.h b/Source/WebCore/Modules/mediasession/WebMediaSessionManagerClient.h
new file mode 100644
index 000000000..4dad21153
--- /dev/null
+++ b/Source/WebCore/Modules/mediasession/WebMediaSessionManagerClient.h
@@ -0,0 +1,47 @@
+/*
+ * 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(WIRELESS_PLAYBACK_TARGET)
+
+#include "MediaPlaybackTarget.h"
+#include "MediaProducer.h"
+#include <wtf/Ref.h>
+
+namespace WebCore {
+
+class WebMediaSessionManagerClient {
+public:
+ virtual ~WebMediaSessionManagerClient() { }
+
+ virtual void setPlaybackTarget(uint64_t, Ref<MediaPlaybackTarget>&&) = 0;
+ virtual void externalOutputDeviceAvailableDidChange(uint64_t, bool) = 0;
+ virtual void setShouldPlayToPlaybackTarget(uint64_t, bool) = 0;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WIRELESS_PLAYBACK_TARGET)
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseError.h b/Source/WebCore/Modules/mediasource/AudioTrackMediaSource.h
index 3611c9470..786c71722 100644
--- a/Source/WebCore/Modules/webdatabase/DatabaseError.h
+++ b/Source/WebCore/Modules/mediasource/AudioTrackMediaSource.h
@@ -23,24 +23,19 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DatabaseError_h
-#define DatabaseError_h
+#pragma once
-#if ENABLE(SQL_DATABASE)
+#if ENABLE(MEDIA_SOURCE) && ENABLE(VIDEO_TRACK)
+
+#include "AudioTrack.h"
namespace WebCore {
-enum class DatabaseError {
- None = 0,
- DatabaseIsBeingDeleted,
- DatabaseSizeExceededQuota,
- DatabaseSizeOverflowed,
- GenericSecurityError,
- InvalidDatabaseState
+class AudioTrackMediaSource {
+public:
+ static SourceBuffer* sourceBuffer(AudioTrack& track) { return track.sourceBuffer(); }
};
} // namespace WebCore
-#endif // ENABLE(SQL_DATABASE)
-
-#endif // DatabaseError_h
+#endif // ENABLE(MEDIA_SOURCE) && ENABLE(VIDEO_TRACK)
diff --git a/Source/WebCore/Modules/mediasource/AudioTrackMediaSource.idl b/Source/WebCore/Modules/mediasource/AudioTrackMediaSource.idl
index f91cb4a04..714241391 100644
--- a/Source/WebCore/Modules/mediasource/AudioTrackMediaSource.idl
+++ b/Source/WebCore/Modules/mediasource/AudioTrackMediaSource.idl
@@ -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
diff --git a/Source/WebCore/Modules/mediasource/DOMURLMediaSource.cpp b/Source/WebCore/Modules/mediasource/DOMURLMediaSource.cpp
index 205127484..513f9fb9a 100644
--- a/Source/WebCore/Modules/mediasource/DOMURLMediaSource.cpp
+++ b/Source/WebCore/Modules/mediasource/DOMURLMediaSource.cpp
@@ -39,13 +39,10 @@
namespace WebCore {
-String DOMURLMediaSource::createObjectURL(ScriptExecutionContext* scriptExecutionContext, MediaSource* source)
+String DOMURLMediaSource::createObjectURL(ScriptExecutionContext& scriptExecutionContext, MediaSource& source)
{
// Since WebWorkers cannot obtain MediaSource objects, we should be on the main thread.
ASSERT(isMainThread());
-
- if (!scriptExecutionContext || !source)
- return String();
return DOMURL::createPublicURL(scriptExecutionContext, source);
}
diff --git a/Source/WebCore/Modules/mediasource/DOMURLMediaSource.h b/Source/WebCore/Modules/mediasource/DOMURLMediaSource.h
index 1eccd4b75..4a3e8c4d3 100644
--- a/Source/WebCore/Modules/mediasource/DOMURLMediaSource.h
+++ b/Source/WebCore/Modules/mediasource/DOMURLMediaSource.h
@@ -28,8 +28,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DOMURLMediaSource_h
-#define DOMURLMediaSource_h
+#pragma once
#if ENABLE(MEDIA_SOURCE)
@@ -42,11 +41,9 @@ class ScriptExecutionContext;
class DOMURLMediaSource {
public:
- static String createObjectURL(ScriptExecutionContext*, MediaSource*);
+ static String createObjectURL(ScriptExecutionContext&, MediaSource&);
};
} // namespace WebCore
-#endif
-
-#endif
+#endif // ENABLE(MEDIA_SOURCE)
diff --git a/Source/WebCore/Modules/mediasource/DOMURLMediaSource.idl b/Source/WebCore/Modules/mediasource/DOMURLMediaSource.idl
index d154397eb..a8e78decd 100644
--- a/Source/WebCore/Modules/mediasource/DOMURLMediaSource.idl
+++ b/Source/WebCore/Modules/mediasource/DOMURLMediaSource.idl
@@ -31,5 +31,5 @@
Conditional=MEDIA_SOURCE
]
partial interface DOMURL {
- [CallWith=ScriptExecutionContext,TreatReturnedNullStringAs=Null] static DOMString createObjectURL(MediaSource? source);
+ [CallWith=ScriptExecutionContext] static DOMString createObjectURL(MediaSource source);
};
diff --git a/Source/WebCore/Modules/mediasource/MediaSource.cpp b/Source/WebCore/Modules/mediasource/MediaSource.cpp
index 7a3294676..4f188b852 100644
--- a/Source/WebCore/Modules/mediasource/MediaSource.cpp
+++ b/Source/WebCore/Modules/mediasource/MediaSource.cpp
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2013 Google Inc. All rights reserved.
+ * Copyright (C) 2013-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
@@ -33,79 +34,103 @@
#if ENABLE(MEDIA_SOURCE)
-#include "AudioTrack.h"
#include "AudioTrackList.h"
#include "ContentType.h"
#include "Event.h"
+#include "EventNames.h"
#include "ExceptionCode.h"
-#include "ExceptionCodePlaceholder.h"
-#include "GenericEventQueue.h"
#include "HTMLMediaElement.h"
#include "Logging.h"
-#include "MIMETypeRegistry.h"
-#include "MediaError.h"
-#include "MediaPlayer.h"
+#include "MediaSourcePrivate.h"
#include "MediaSourceRegistry.h"
+#include "SourceBuffer.h"
+#include "SourceBufferList.h"
#include "SourceBufferPrivate.h"
-#include "TextTrack.h"
#include "TextTrackList.h"
#include "TimeRanges.h"
-#include "VideoTrack.h"
#include "VideoTrackList.h"
-#include <runtime/Uint8Array.h>
-#include <wtf/text/CString.h>
-#include <wtf/text/WTFString.h>
namespace WebCore {
-PassRefPtr<MediaSource> MediaSource::create(ScriptExecutionContext& context)
+URLRegistry* MediaSource::s_registry;
+
+void MediaSource::setRegistry(URLRegistry* registry)
+{
+ ASSERT(!s_registry);
+ s_registry = registry;
+}
+
+Ref<MediaSource> MediaSource::create(ScriptExecutionContext& context)
{
- RefPtr<MediaSource> mediaSource(adoptRef(new MediaSource(context)));
+ auto mediaSource = adoptRef(*new MediaSource(context));
mediaSource->suspendIfNeeded();
- return mediaSource.release();
+ return mediaSource;
}
MediaSource::MediaSource(ScriptExecutionContext& context)
: ActiveDOMObject(&context)
- , m_mediaElement(0)
+ , m_duration(MediaTime::invalidTime())
+ , m_pendingSeekTime(MediaTime::invalidTime())
, m_readyState(closedKeyword())
, m_asyncEventQueue(*this)
{
- LOG(Media, "MediaSource::MediaSource %p", this);
+ LOG(MediaSource, "MediaSource::MediaSource %p", this);
m_sourceBuffers = SourceBufferList::create(scriptExecutionContext());
m_activeSourceBuffers = SourceBufferList::create(scriptExecutionContext());
}
MediaSource::~MediaSource()
{
- LOG(Media, "MediaSource::~MediaSource %p", this);
+ LOG(MediaSource, "MediaSource::~MediaSource %p", this);
ASSERT(isClosed());
}
const AtomicString& MediaSource::openKeyword()
{
- DEFINE_STATIC_LOCAL(const AtomicString, open, ("open", AtomicString::ConstructFromLiteral));
+ static NeverDestroyed<const AtomicString> open("open", AtomicString::ConstructFromLiteral);
return open;
}
const AtomicString& MediaSource::closedKeyword()
{
- DEFINE_STATIC_LOCAL(const AtomicString, closed, ("closed", AtomicString::ConstructFromLiteral));
+ static NeverDestroyed<const AtomicString> closed("closed", AtomicString::ConstructFromLiteral);
return closed;
}
const AtomicString& MediaSource::endedKeyword()
{
- DEFINE_STATIC_LOCAL(const AtomicString, ended, ("ended", AtomicString::ConstructFromLiteral));
+ static NeverDestroyed<const AtomicString> ended("ended", AtomicString::ConstructFromLiteral);
return ended;
}
-void MediaSource::setPrivateAndOpen(PassRef<MediaSourcePrivate> mediaSourcePrivate)
+void MediaSource::setPrivateAndOpen(Ref<MediaSourcePrivate>&& mediaSourcePrivate)
{
ASSERT(!m_private);
ASSERT(m_mediaElement);
- m_private = std::move(mediaSourcePrivate);
+ m_private = WTFMove(mediaSourcePrivate);
+
+ // 2.4.1 Attaching to a media element
+ // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#mediasource-attach
+
+ // ↳ If readyState is NOT set to "closed"
+ // Run the "If the media data cannot be fetched at all, due to network errors, causing the user agent to give up trying
+ // to fetch the resource" steps of the resource fetch algorithm's media data processing steps list.
+ if (!isClosed()) {
+ m_mediaElement->mediaLoadingFailedFatally(MediaPlayer::NetworkError);
+ return;
+ }
+
+ // ↳ Otherwise
+ // 1. Set the media element's delaying-the-load-event-flag to false.
+ m_mediaElement->setShouldDelayLoadEvent(false);
+
+ // 2. Set the readyState attribute to "open".
+ // 3. Queue a task to fire a simple event named sourceopen at the MediaSource.
setReadyState(openKeyword());
+
+ // 4. Continue the resource fetch algorithm by running the remaining "Otherwise (mode is local)" steps,
+ // with these clarifications:
+ // NOTE: This is handled in HTMLMediaElement.
}
void MediaSource::addedToRegistry()
@@ -118,110 +143,268 @@ void MediaSource::removedFromRegistry()
unsetPendingActivity(this);
}
-double MediaSource::duration() const
+MediaTime MediaSource::duration() const
{
- return isClosed() ? std::numeric_limits<float>::quiet_NaN() : m_private->duration();
+ return m_duration;
}
-PassRefPtr<TimeRanges> MediaSource::buffered() const
+void MediaSource::durationChanged(const MediaTime& duration)
{
+ m_duration = duration;
+}
+
+MediaTime MediaSource::currentTime() const
+{
+ return m_mediaElement ? m_mediaElement->currentMediaTime() : MediaTime::zeroTime();
+}
+
+std::unique_ptr<PlatformTimeRanges> MediaSource::buffered() const
+{
+ if (m_buffered && m_activeSourceBuffers->length() && std::all_of(m_activeSourceBuffers->begin(), m_activeSourceBuffers->end(), [](auto& buffer) { return !buffer->isBufferedDirty(); }))
+ return std::make_unique<PlatformTimeRanges>(*m_buffered);
+
+ m_buffered = std::make_unique<PlatformTimeRanges>();
+ for (auto& sourceBuffer : *m_activeSourceBuffers)
+ sourceBuffer->setBufferedDirty(false);
+
// Implements MediaSource algorithm for HTMLMediaElement.buffered.
// https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#htmlmediaelement-extensions
- Vector<RefPtr<TimeRanges>> ranges = activeRanges();
+ Vector<PlatformTimeRanges> activeRanges = this->activeRanges();
// 1. If activeSourceBuffers.length equals 0 then return an empty TimeRanges object and abort these steps.
- if (ranges.isEmpty())
- return TimeRanges::create();
+ if (activeRanges.isEmpty())
+ return std::make_unique<PlatformTimeRanges>(*m_buffered);
// 2. Let active ranges be the ranges returned by buffered for each SourceBuffer object in activeSourceBuffers.
// 3. Let highest end time be the largest range end time in the active ranges.
- double highestEndTime = -1;
- for (size_t i = 0; i < ranges.size(); ++i) {
- unsigned length = ranges[i]->length();
+ MediaTime highestEndTime = MediaTime::zeroTime();
+ for (auto& ranges : activeRanges) {
+ unsigned length = ranges.length();
if (length)
- highestEndTime = std::max(highestEndTime, ranges[i]->end(length - 1, ASSERT_NO_EXCEPTION));
+ highestEndTime = std::max(highestEndTime, ranges.end(length - 1));
}
// Return an empty range if all ranges are empty.
- if (highestEndTime < 0)
- return TimeRanges::create();
+ if (!highestEndTime)
+ return std::make_unique<PlatformTimeRanges>(*m_buffered);
// 4. Let intersection ranges equal a TimeRange object containing a single range from 0 to highest end time.
- RefPtr<TimeRanges> intersectionRanges = TimeRanges::create(0, highestEndTime);
+ m_buffered->add(MediaTime::zeroTime(), highestEndTime);
// 5. For each SourceBuffer object in activeSourceBuffers run the following steps:
bool ended = readyState() == endedKeyword();
- for (size_t i = 0; i < ranges.size(); ++i) {
+ for (auto& sourceRanges : activeRanges) {
// 5.1 Let source ranges equal the ranges returned by the buffered attribute on the current SourceBuffer.
- TimeRanges* sourceRanges = ranges[i].get();
-
// 5.2 If readyState is "ended", then set the end time on the last range in source ranges to highest end time.
- if (ended && sourceRanges->length())
- sourceRanges->add(sourceRanges->start(sourceRanges->length() - 1, ASSERT_NO_EXCEPTION), highestEndTime);
+ if (ended && sourceRanges.length())
+ sourceRanges.add(sourceRanges.start(sourceRanges.length() - 1), highestEndTime);
// 5.3 Let new intersection ranges equal the the intersection between the intersection ranges and the source ranges.
// 5.4 Replace the ranges in intersection ranges with the new intersection ranges.
- intersectionRanges->intersectWith(sourceRanges);
+ m_buffered->intersectWith(sourceRanges);
}
- return intersectionRanges.release();
+ return std::make_unique<PlatformTimeRanges>(*m_buffered);
}
-class SourceBufferBufferedDoesNotContainTime {
-public:
- SourceBufferBufferedDoesNotContainTime(double time) : m_time(time) { }
- bool operator()(RefPtr<SourceBuffer> sourceBuffer)
- {
- return !sourceBuffer->buffered()->contain(m_time);
- }
+void MediaSource::seekToTime(const MediaTime& time)
+{
+ if (isClosed())
+ return;
- double m_time;
-};
+ // 2.4.3 Seeking
+ // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#mediasource-seeking
-class SourceBufferBufferedHasEnough {
-public:
- SourceBufferBufferedHasEnough(double time, double duration) : m_time(time), m_duration(duration) { }
- bool operator()(RefPtr<SourceBuffer> sourceBuffer)
- {
- size_t rangePos = sourceBuffer->buffered()->find(m_time);
- if (rangePos == notFound)
- return false;
+ m_pendingSeekTime = time;
- double endTime = sourceBuffer->buffered()->end(rangePos, IGNORE_EXCEPTION);
- return m_duration - endTime < 1;
+ // Run the following steps as part of the "Wait until the user agent has established whether or not the
+ // media data for the new playback position is available, and, if it is, until it has decoded enough data
+ // to play back that position" step of the seek algorithm:
+ // ↳ If new playback position is not in any TimeRange of HTMLMediaElement.buffered
+ if (!hasBufferedTime(time)) {
+ // 1. If the HTMLMediaElement.readyState attribute is greater than HAVE_METADATA,
+ // then set the HTMLMediaElement.readyState attribute to HAVE_METADATA.
+ m_private->setReadyState(MediaPlayer::HaveMetadata);
+
+ // 2. The media element waits until an appendBuffer() or an appendStream() call causes the coded
+ // frame processing algorithm to set the HTMLMediaElement.readyState attribute to a value greater
+ // than HAVE_METADATA.
+ LOG(MediaSource, "MediaSource::seekToTime(%p) - waitForSeekCompleted()", this);
+ m_private->waitForSeekCompleted();
+ return;
}
+ // ↳ Otherwise
+ // Continue
+
+ completeSeek();
+}
+
+void MediaSource::completeSeek()
+{
+ if (isClosed())
+ return;
+
+ // 2.4.3 Seeking, ctd.
+ // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#mediasource-seeking
+
+ ASSERT(m_pendingSeekTime.isValid());
+
+ // 2. The media element resets all decoders and initializes each one with data from the appropriate
+ // initialization segment.
+ // 3. The media element feeds coded frames from the active track buffers into the decoders starting
+ // with the closest random access point before the new playback position.
+ MediaTime pendingSeekTime = m_pendingSeekTime;
+ m_pendingSeekTime = MediaTime::invalidTime();
+ for (auto& sourceBuffer : *m_activeSourceBuffers)
+ sourceBuffer->seekToTime(pendingSeekTime);
+
+ // 4. Resume the seek algorithm at the "Await a stable state" step.
+ m_private->seekCompleted();
+
+ monitorSourceBuffers();
+}
+
+Ref<TimeRanges> MediaSource::seekable()
+{
+ // 6. HTMLMediaElement Extensions, seekable
+ // W3C Editor's Draft 16 September 2016
+ // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#htmlmediaelement-extensions
+
+ // ↳ If duration equals NaN:
+ // Return an empty TimeRanges object.
+ if (m_duration.isInvalid())
+ return TimeRanges::create();
- double m_time;
- double m_duration;
-};
-
-class SourceBufferBufferedHasFuture {
-public:
- SourceBufferBufferedHasFuture(double time) : m_time(time) { }
- bool operator()(RefPtr<SourceBuffer> sourceBuffer)
- {
- size_t rangePos = sourceBuffer->buffered()->find(m_time);
- if (rangePos == notFound)
- return false;
-
- double endTime = sourceBuffer->buffered()->end(rangePos, IGNORE_EXCEPTION);
- return endTime - m_time > 1;
+ // ↳ If duration equals positive Infinity:
+ if (m_duration.isPositiveInfinite()) {
+ auto buffered = this->buffered();
+ // If live seekable range is not empty:
+ if (m_liveSeekable && m_liveSeekable->length()) {
+ // Let union ranges be the union of live seekable range and the HTMLMediaElement.buffered attribute.
+ buffered->unionWith(*m_liveSeekable);
+ // Return a single range with a start time equal to the earliest start time in union ranges
+ // and an end time equal to the highest end time in union ranges and abort these steps.
+ buffered->add(buffered->start(0), buffered->maximumBufferedTime());
+ return TimeRanges::create(*buffered);
+ }
+
+ // If the HTMLMediaElement.buffered attribute returns an empty TimeRanges object, then return
+ // an empty TimeRanges object and abort these steps.
+ if (!buffered->length())
+ return TimeRanges::create();
+
+ // Return a single range with a start time of 0 and an end time equal to the highest end time
+ // reported by the HTMLMediaElement.buffered attribute.
+ return TimeRanges::create({MediaTime::zeroTime(), buffered->maximumBufferedTime()});
}
- double m_time;
-};
+ // ↳ Otherwise:
+ // Return a single range with a start time of 0 and an end time equal to duration.
+ return TimeRanges::create({MediaTime::zeroTime(), m_duration});
+}
+
+ExceptionOr<void> MediaSource::setLiveSeekableRange(double start, double end)
+{
+ // W3C Editor's Draft 16 September 2016
+ // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#dom-mediasource-setliveseekablerange
+
+ // If the readyState attribute is not "open" then throw an InvalidStateError exception and abort these steps.
+ if (!isOpen())
+ return Exception { INVALID_STATE_ERR };
+
+ // If start is negative or greater than end, then throw a TypeError exception and abort these steps.
+ if (start < 0 || start > end)
+ return Exception { TypeError };
+
+ // Set live seekable range to be a new normalized TimeRanges object containing a single range
+ // whose start position is start and end position is end.
+ m_liveSeekable = std::make_unique<PlatformTimeRanges>(MediaTime::createWithDouble(start), MediaTime::createWithDouble(end));
+
+ return { };
+}
+
+ExceptionOr<void> MediaSource::clearLiveSeekableRange()
+{
+ // W3C Editor's Draft 16 September 2016
+ // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#dom-mediasource-clearliveseekablerange
+
+ // If the readyState attribute is not "open" then throw an InvalidStateError exception and abort these steps.
+ if (!isOpen())
+ return Exception { INVALID_STATE_ERR };
+ m_liveSeekable = nullptr;
+ return { };
+}
+
+const MediaTime& MediaSource::currentTimeFudgeFactor()
+{
+ // Allow hasCurrentTime() to be off by as much as the length of two 24fps video frames
+ static NeverDestroyed<MediaTime> fudgeFactor(2002, 24000);
+ return fudgeFactor;
+}
+
+bool MediaSource::hasBufferedTime(const MediaTime& time)
+{
+ if (time > duration())
+ return false;
+
+ auto ranges = buffered();
+ if (!ranges->length())
+ return false;
+
+ return abs(ranges->nearest(time) - time) <= currentTimeFudgeFactor();
+}
+
+bool MediaSource::hasCurrentTime()
+{
+ return hasBufferedTime(currentTime());
+}
+
+bool MediaSource::hasFutureTime()
+{
+ MediaTime currentTime = this->currentTime();
+ MediaTime duration = this->duration();
+
+ if (currentTime >= duration)
+ return true;
+
+ auto ranges = buffered();
+ MediaTime nearest = ranges->nearest(currentTime);
+ if (abs(nearest - currentTime) > currentTimeFudgeFactor())
+ return false;
+
+ size_t found = ranges->find(nearest);
+ if (found == notFound)
+ return false;
+
+ MediaTime localEnd = ranges->end(found);
+ if (localEnd == duration)
+ return true;
+
+ return localEnd - currentTime > currentTimeFudgeFactor();
+}
void MediaSource::monitorSourceBuffers()
{
- double currentTime = mediaElement()->currentTime();
+ if (isClosed())
+ return;
// 2.4.4 SourceBuffer Monitoring
- // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#buffer-monitoring
- // ↳ If buffered for all objects in activeSourceBuffers do not contain TimeRanges for the current
- // playback position:
- auto begin = m_activeSourceBuffers->begin();
- auto end = m_activeSourceBuffers->end();
- if (std::all_of(begin, end, SourceBufferBufferedDoesNotContainTime(currentTime))) {
+ // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#buffer-monitoring
+
+ // Note, the behavior if activeSourceBuffers is empty is undefined.
+ if (!m_activeSourceBuffers) {
+ m_private->setReadyState(MediaPlayer::HaveNothing);
+ return;
+ }
+
+ // ↳ If the the HTMLMediaElement.readyState attribute equals HAVE_NOTHING:
+ if (mediaElement()->readyState() == HTMLMediaElement::HAVE_NOTHING) {
+ // 1. Abort these steps.
+ return;
+ }
+
+ // ↳ If HTMLMediaElement.buffered does not contain a TimeRange for the current playback position:
+ if (!hasCurrentTime()) {
// 1. Set the HTMLMediaElement.readyState attribute to HAVE_METADATA.
// 2. If this is the first transition to HAVE_METADATA, then queue a task to fire a simple event
// named loadedmetadata at the media element.
@@ -231,33 +414,40 @@ void MediaSource::monitorSourceBuffers()
return;
}
- // ↳ If buffered for all objects in activeSourceBuffers contain TimeRanges that include the current
- // playback position and enough data to ensure uninterrupted playback:
- if (std::all_of(begin, end, SourceBufferBufferedHasEnough(currentTime, mediaElement()->duration()))) {
+ // ↳ If HTMLMediaElement.buffered contains a TimeRange that includes the current
+ // playback position and enough data to ensure uninterrupted playback:
+ auto ranges = buffered();
+ if (std::all_of(m_activeSourceBuffers->begin(), m_activeSourceBuffers->end(), [&](auto& sourceBuffer) {
+ return sourceBuffer->canPlayThroughRange(*ranges);
+ })) {
// 1. Set the HTMLMediaElement.readyState attribute to HAVE_ENOUGH_DATA.
// 2. Queue a task to fire a simple event named canplaythrough at the media element.
// 3. Playback may resume at this point if it was previously suspended by a transition to HAVE_CURRENT_DATA.
m_private->setReadyState(MediaPlayer::HaveEnoughData);
+ if (m_pendingSeekTime.isValid())
+ completeSeek();
+
// 4. Abort these steps.
return;
}
- // ↳ If buffered for at least one object in activeSourceBuffers contains a TimeRange that includes
- // the current playback position but not enough data to ensure uninterrupted playback:
- if (std::any_of(begin, end, SourceBufferBufferedHasFuture(currentTime))) {
+ // ↳ If HTMLMediaElement.buffered contains a TimeRange that includes the current playback
+ // position and some time beyond the current playback position, then run the following steps:
+ if (hasFutureTime()) {
// 1. Set the HTMLMediaElement.readyState attribute to HAVE_FUTURE_DATA.
// 2. If the previous value of HTMLMediaElement.readyState was less than HAVE_FUTURE_DATA, then queue a task to fire a simple event named canplay at the media element.
// 3. Playback may resume at this point if it was previously suspended by a transition to HAVE_CURRENT_DATA.
m_private->setReadyState(MediaPlayer::HaveFutureData);
+ if (m_pendingSeekTime.isValid())
+ completeSeek();
+
// 4. Abort these steps.
return;
}
- // ↳ If buffered for at least one object in activeSourceBuffers contains a TimeRange that ends
- // at the current playback position and does not have a range covering the time immediately
- // after the current position:
+ // ↳ If HTMLMediaElement.buffered contains a TimeRange that ends at the current playback position and does not have a range covering the time immediately after the current position:
// NOTE: Logically, !(all objects do not contain currentTime) == (some objects contain current time)
// 1. Set the HTMLMediaElement.readyState attribute to HAVE_CURRENT_DATA.
@@ -266,35 +456,84 @@ void MediaSource::monitorSourceBuffers()
// 3. Playback is suspended at this point since the media element doesn't have enough data to
// advance the media timeline.
m_private->setReadyState(MediaPlayer::HaveCurrentData);
-
+
+ if (m_pendingSeekTime.isValid())
+ completeSeek();
+
// 4. Abort these steps.
}
-void MediaSource::setDuration(double duration, ExceptionCode& ec)
+ExceptionOr<void> MediaSource::setDuration(double duration)
{
- if (duration < 0.0 || std::isnan(duration)) {
- ec = INVALID_ACCESS_ERR;
- return;
- }
- if (!isOpen()) {
- ec = INVALID_STATE_ERR;
- return;
+ // 2.1 Attributes - Duration
+ // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#attributes
+
+ // On setting, run the following steps:
+ // 1. If the value being set is negative or NaN then throw an INVALID_ACCESS_ERR exception and abort these steps.
+ if (duration < 0.0 || std::isnan(duration))
+ return Exception { INVALID_ACCESS_ERR };
+
+ // 2. If the readyState attribute is not "open" then throw an INVALID_STATE_ERR exception and abort these steps.
+ if (!isOpen())
+ return Exception { INVALID_STATE_ERR };
+
+ // 3. If the updating attribute equals true on any SourceBuffer in sourceBuffers, then throw an INVALID_STATE_ERR
+ // exception and abort these steps.
+ for (auto& sourceBuffer : *m_sourceBuffers) {
+ if (sourceBuffer->updating())
+ return Exception { INVALID_STATE_ERR };
}
- m_private->setDuration(duration);
+
+ // 4. Run the duration change algorithm with new duration set to the value being assigned to this attribute.
+ return setDurationInternal(MediaTime::createWithDouble(duration));
}
+ExceptionOr<void> MediaSource::setDurationInternal(const MediaTime& duration)
+{
+ // 2.4.6 Duration Change
+ // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#duration-change-algorithm
+
+ MediaTime newDuration = duration;
+
+ // 1. If the current value of duration is equal to new duration, then return.
+ if (newDuration == m_duration)
+ return { };
+
+ // 2. If new duration is less than the highest presentation timestamp of any buffered coded frames
+ // for all SourceBuffer objects in sourceBuffers, then throw an InvalidStateError exception and
+ // abort these steps.
+ // 3. Let highest end time be the largest track buffer ranges end time across all the track buffers
+ // across all SourceBuffer objects in sourceBuffers.
+ MediaTime highestPresentationTimestamp;
+ MediaTime highestEndTime;
+ for (auto& sourceBuffer : *m_sourceBuffers) {
+ highestPresentationTimestamp = std::max(highestPresentationTimestamp, sourceBuffer->highestPresentationTimestamp());
+ highestEndTime = std::max(highestEndTime, sourceBuffer->bufferedInternal().ranges().maximumBufferedTime());
+ }
+ if (highestPresentationTimestamp.isValid() && newDuration < highestPresentationTimestamp)
+ return Exception { INVALID_STATE_ERR };
+
+ // 4. If new duration is less than highest end time, then
+ // 4.1. Update new duration to equal highest end time.
+ if (highestEndTime.isValid() && newDuration < highestEndTime)
+ newDuration = highestEndTime;
+
+ // 5. Update duration to new duration.
+ m_duration = newDuration;
+
+ // 6. Update the media duration to new duration and run the HTMLMediaElement duration change algorithm.
+ LOG(MediaSource, "MediaSource::setDurationInternal(%p) - duration(%g)", this, duration.toDouble());
+ m_private->durationChanged();
+
+ return { };
+}
void MediaSource::setReadyState(const AtomicString& state)
{
ASSERT(state == openKeyword() || state == closedKeyword() || state == endedKeyword());
AtomicString oldState = readyState();
- LOG(Media, "MediaSource::setReadyState() %p : %s -> %s", this, oldState.string().ascii().data(), state.string().ascii().data());
-
- if (state == closedKeyword()) {
- m_private.clear();
- m_mediaElement = 0;
- }
+ LOG(MediaSource, "MediaSource::setReadyState(%p) : %s -> %s", this, oldState.string().ascii().data(), state.string().ascii().data());
if (oldState == state)
return;
@@ -304,55 +543,52 @@ void MediaSource::setReadyState(const AtomicString& state)
onReadyStateChange(oldState, state);
}
-static bool SourceBufferIsUpdating(RefPtr<SourceBuffer>& sourceBuffer)
-{
- return sourceBuffer->updating();
-}
-
-void MediaSource::endOfStream(const AtomicString& error, ExceptionCode& ec)
+ExceptionOr<void> MediaSource::endOfStream(std::optional<EndOfStreamError> error)
{
// 2.2 https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#widl-MediaSource-endOfStream-void-EndOfStreamError-error
// 1. If the readyState attribute is not in the "open" state then throw an
// INVALID_STATE_ERR exception and abort these steps.
- if (!isOpen()) {
- ec = INVALID_STATE_ERR;
- return;
- }
+ if (!isOpen())
+ return Exception { INVALID_STATE_ERR };
// 2. If the updating attribute equals true on any SourceBuffer in sourceBuffers, then throw an
// INVALID_STATE_ERR exception and abort these steps.
- if (std::any_of(m_sourceBuffers->begin(), m_sourceBuffers->end(), SourceBufferIsUpdating)) {
- ec = INVALID_STATE_ERR;
- return;
- }
+ if (std::any_of(m_sourceBuffers->begin(), m_sourceBuffers->end(), [](auto& sourceBuffer) { return sourceBuffer->updating(); }))
+ return Exception { INVALID_STATE_ERR };
// 3. Run the end of stream algorithm with the error parameter set to error.
- streamEndedWithError(error, ec);
+ streamEndedWithError(error);
+
+ return { };
}
-void MediaSource::streamEndedWithError(const AtomicString& error, ExceptionCode& ec)
+void MediaSource::streamEndedWithError(std::optional<EndOfStreamError> error)
{
- DEFINE_STATIC_LOCAL(const AtomicString, network, ("network", AtomicString::ConstructFromLiteral));
- DEFINE_STATIC_LOCAL(const AtomicString, decode, ("decode", AtomicString::ConstructFromLiteral));
+ LOG(MediaSource, "MediaSource::streamEndedWithError(%p)", this);
+ if (isClosed())
+ return;
// 2.4.7 https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#end-of-stream-algorithm
+
// 1. Change the readyState attribute value to "ended".
// 2. Queue a task to fire a simple event named sourceended at the MediaSource.
setReadyState(endedKeyword());
// 3.
- if (error.isEmpty()) {
+ if (!error) {
// ↳ If error is not set, is null, or is an empty string
- // 1. Run the duration change algorithm with new duration set to the highest end timestamp
- // across all SourceBuffer objects in sourceBuffers.
- MediaTime maxEndTimestamp;
- for (auto it = m_sourceBuffers->begin(), end = m_sourceBuffers->end(); it != end; ++it)
- maxEndTimestamp = std::max((*it)->highestPresentationEndTimestamp(), maxEndTimestamp);
- m_private->setDuration(maxEndTimestamp.toDouble());
+ // 1. Run the duration change algorithm with new duration set to the highest end time reported by
+ // the buffered attribute across all SourceBuffer objects in sourceBuffers.
+ MediaTime maxEndTime;
+ for (auto& sourceBuffer : *m_sourceBuffers) {
+ if (auto length = sourceBuffer->bufferedInternal().length())
+ maxEndTime = std::max(sourceBuffer->bufferedInternal().ranges().end(length - 1), maxEndTime);
+ }
+ setDurationInternal(maxEndTime);
// 2. Notify the media element that it now has all of the media data.
m_private->markEndOfStream(MediaSourcePrivate::EosNoError);
- } else if (error == network) {
+ } else if (error == EndOfStreamError::Network) {
// ↳ If error is set to "network"
ASSERT(m_mediaElement);
if (m_mediaElement->readyState() == HTMLMediaElement::HAVE_NOTHING) {
@@ -368,8 +604,9 @@ void MediaSource::streamEndedWithError(const AtomicString& error, ExceptionCode&
// NOTE: This step is handled by HTMLMediaElement::mediaLoadingFailedFatally().
m_mediaElement->mediaLoadingFailedFatally(MediaPlayer::NetworkError);
}
- } else if (error == decode) {
+ } else {
// ↳ If error is set to "decode"
+ ASSERT(error == EndOfStreamError::Decode);
ASSERT(m_mediaElement);
if (m_mediaElement->readyState() == HTMLMediaElement::HAVE_NOTHING) {
// ↳ If the HTMLMediaElement.readyState attribute equals HAVE_NOTHING
@@ -383,101 +620,98 @@ void MediaSource::streamEndedWithError(const AtomicString& error, ExceptionCode&
// NOTE: This step is handled by HTMLMediaElement::mediaLoadingFailedFatally().
m_mediaElement->mediaLoadingFailedFatally(MediaPlayer::DecodeError);
}
- } else {
- // ↳ Otherwise
- // Throw an INVALID_ACCESS_ERR exception.
- ec = INVALID_ACCESS_ERR;
}
}
-SourceBuffer* MediaSource::addSourceBuffer(const String& type, ExceptionCode& ec)
+ExceptionOr<SourceBuffer&> MediaSource::addSourceBuffer(const String& type)
{
- LOG(Media, "MediaSource::addSourceBuffer(%s) %p", type.ascii().data(), this);
+ LOG(MediaSource, "MediaSource::addSourceBuffer(%s) %p", type.ascii().data(), this);
- // 2.2 https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-MediaSource-addSourceBuffer-SourceBuffer-DOMString-type
- // 1. If type is null or an empty then throw an INVALID_ACCESS_ERR exception and
- // abort these steps.
- if (type.isNull() || type.isEmpty()) {
- ec = INVALID_ACCESS_ERR;
- return nullptr;
- }
+ // 2.2 http://www.w3.org/TR/media-source/#widl-MediaSource-addSourceBuffer-SourceBuffer-DOMString-type
+ // When this method is invoked, the user agent must run the following steps:
+
+ // 1. If type is an empty string then throw a TypeError exception and abort these steps.
+ if (type.isEmpty())
+ return Exception { TypeError };
// 2. If type contains a MIME type that is not supported ..., then throw a
// NOT_SUPPORTED_ERR exception and abort these steps.
- if (!isTypeSupported(type)) {
- ec = NOT_SUPPORTED_ERR;
- return nullptr;
- }
+ if (!isTypeSupported(type))
+ return Exception { NOT_SUPPORTED_ERR };
// 4. If the readyState attribute is not in the "open" state then throw an
// INVALID_STATE_ERR exception and abort these steps.
- if (!isOpen()) {
- ec = INVALID_STATE_ERR;
- return nullptr;
- }
+ if (!isOpen())
+ return Exception { INVALID_STATE_ERR };
// 5. Create a new SourceBuffer object and associated resources.
ContentType contentType(type);
- RefPtr<SourceBufferPrivate> sourceBufferPrivate = createSourceBufferPrivate(contentType, ec);
+ auto sourceBufferPrivate = createSourceBufferPrivate(contentType);
- if (!sourceBufferPrivate) {
- ASSERT(ec == NOT_SUPPORTED_ERR || ec == QUOTA_EXCEEDED_ERR);
+ if (sourceBufferPrivate.hasException()) {
// 2. If type contains a MIME type that is not supported ..., then throw a NOT_SUPPORTED_ERR exception and abort these steps.
// 3. If the user agent can't handle any more SourceBuffer objects then throw a QUOTA_EXCEEDED_ERR exception and abort these steps
- return nullptr;
+ return sourceBufferPrivate.releaseException();
}
- RefPtr<SourceBuffer> buffer = SourceBuffer::create(sourceBufferPrivate.releaseNonNull(), this);
- // 6. Add the new object to sourceBuffers and fire a addsourcebuffer on that object.
- m_sourceBuffers->add(buffer);
- m_activeSourceBuffers->add(buffer);
- // 7. Return the new object to the caller.
- return buffer.get();
+ auto buffer = SourceBuffer::create(sourceBufferPrivate.releaseReturnValue(), this);
+
+ // 6. Set the generate timestamps flag on the new object to the value in the "Generate Timestamps Flag"
+ // column of the byte stream format registry [MSE-REGISTRY] entry that is associated with type.
+ // NOTE: In the current byte stream format registry <http://www.w3.org/2013/12/byte-stream-format-registry/>
+ // only the "MPEG Audio Byte Stream Format" has the "Generate Timestamps Flag" value set.
+ bool shouldGenerateTimestamps = contentType.type() == "audio/aac" || contentType.type() == "audio/mpeg";
+ buffer->setShouldGenerateTimestamps(shouldGenerateTimestamps);
+
+ // 7. If the generate timestamps flag equals true:
+ // ↳ Set the mode attribute on the new object to "sequence".
+ // Otherwise:
+ // ↳ Set the mode attribute on the new object to "segments".
+ buffer->setMode(shouldGenerateTimestamps ? SourceBuffer::AppendMode::Sequence : SourceBuffer::AppendMode::Segments);
+
+ auto& result = buffer.get();
+
+ // 8. Add the new object to sourceBuffers and fire a addsourcebuffer on that object.
+ m_sourceBuffers->add(WTFMove(buffer));
+ regenerateActiveSourceBuffers();
+
+ // 9. Return the new object to the caller.
+ return result;
}
-void MediaSource::removeSourceBuffer(SourceBuffer* buffer, ExceptionCode& ec)
+ExceptionOr<void> MediaSource::removeSourceBuffer(SourceBuffer& buffer)
{
- LOG(Media, "MediaSource::removeSourceBuffer() %p", this);
- RefPtr<SourceBuffer> protect(buffer);
-
- // 2.2 https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-MediaSource-removeSourceBuffer-void-SourceBuffer-sourceBuffer
- // 1. If sourceBuffer is null then throw an INVALID_ACCESS_ERR exception and
- // abort these steps.
- if (!buffer) {
- ec = INVALID_ACCESS_ERR;
- return;
- }
+ LOG(MediaSource, "MediaSource::removeSourceBuffer() %p", this);
+ Ref<SourceBuffer> protect(buffer);
// 2. If sourceBuffer specifies an object that is not in sourceBuffers then
// throw a NOT_FOUND_ERR exception and abort these steps.
- if (!m_sourceBuffers->length() || !m_sourceBuffers->contains(buffer)) {
- ec = NOT_FOUND_ERR;
- return;
- }
+ if (!m_sourceBuffers->length() || !m_sourceBuffers->contains(buffer))
+ return Exception { NOT_FOUND_ERR };
// 3. If the sourceBuffer.updating attribute equals true, then run the following steps: ...
- buffer->abortIfUpdating();
+ buffer.abortIfUpdating();
// 4. Let SourceBuffer audioTracks list equal the AudioTrackList object returned by sourceBuffer.audioTracks.
- RefPtr<AudioTrackList> audioTracks = buffer->audioTracks();
+ auto& audioTracks = buffer.audioTracks();
// 5. If the SourceBuffer audioTracks list is not empty, then run the following steps:
- if (audioTracks->length()) {
+ if (audioTracks.length()) {
// 5.1 Let HTMLMediaElement audioTracks list equal the AudioTrackList object returned by the audioTracks
// attribute on the HTMLMediaElement.
// 5.2 Let the removed enabled audio track flag equal false.
bool removedEnabledAudioTrack = false;
// 5.3 For each AudioTrack object in the SourceBuffer audioTracks list, run the following steps:
- while (audioTracks->length()) {
- AudioTrack* track = audioTracks->lastItem();
+ while (audioTracks.length()) {
+ auto& track = *audioTracks.lastItem();
// 5.3.1 Set the sourceBuffer attribute on the AudioTrack object to null.
- track->setSourceBuffer(0);
+ track.setSourceBuffer(nullptr);
// 5.3.2 If the enabled attribute on the AudioTrack object is true, then set the removed enabled
// audio track flag to true.
- if (track->enabled())
+ if (track.enabled())
removedEnabledAudioTrack = true;
// 5.3.3 Remove the AudioTrack object from the HTMLMediaElement audioTracks list.
@@ -489,35 +723,35 @@ void MediaSource::removeSourceBuffer(SourceBuffer* buffer, ExceptionCode& ec)
// 5.3.5 Remove the AudioTrack object from the SourceBuffer audioTracks list.
// 5.3.6 Queue a task to fire a trusted event named removetrack, that does not bubble and is not
// cancelable, and that uses the TrackEvent interface, at the SourceBuffer audioTracks list.
- audioTracks->remove(track);
+ audioTracks.remove(track);
}
// 5.4 If the removed enabled audio track flag equals true, then queue a task to fire a simple event
// named change at the HTMLMediaElement audioTracks list.
if (removedEnabledAudioTrack)
- mediaElement()->audioTracks()->scheduleChangeEvent();
+ mediaElement()->audioTracks().scheduleChangeEvent();
}
// 6. Let SourceBuffer videoTracks list equal the VideoTrackList object returned by sourceBuffer.videoTracks.
- RefPtr<VideoTrackList> videoTracks = buffer->videoTracks();
+ auto& videoTracks = buffer.videoTracks();
// 7. If the SourceBuffer videoTracks list is not empty, then run the following steps:
- if (videoTracks->length()) {
+ if (videoTracks.length()) {
// 7.1 Let HTMLMediaElement videoTracks list equal the VideoTrackList object returned by the videoTracks
// attribute on the HTMLMediaElement.
// 7.2 Let the removed selected video track flag equal false.
bool removedSelectedVideoTrack = false;
// 7.3 For each VideoTrack object in the SourceBuffer videoTracks list, run the following steps:
- while (videoTracks->length()) {
- VideoTrack* track = videoTracks->lastItem();
+ while (videoTracks.length()) {
+ auto& track = *videoTracks.lastItem();
// 7.3.1 Set the sourceBuffer attribute on the VideoTrack object to null.
- track->setSourceBuffer(0);
+ track.setSourceBuffer(nullptr);
// 7.3.2 If the selected attribute on the VideoTrack object is true, then set the removed selected
// video track flag to true.
- if (track->selected())
+ if (track.selected())
removedSelectedVideoTrack = true;
// 7.3.3 Remove the VideoTrack object from the HTMLMediaElement videoTracks list.
@@ -529,35 +763,35 @@ void MediaSource::removeSourceBuffer(SourceBuffer* buffer, ExceptionCode& ec)
// 7.3.5 Remove the VideoTrack object from the SourceBuffer videoTracks list.
// 7.3.6 Queue a task to fire a trusted event named removetrack, that does not bubble and is not
// cancelable, and that uses the TrackEvent interface, at the SourceBuffer videoTracks list.
- videoTracks->remove(track);
+ videoTracks.remove(track);
}
// 7.4 If the removed selected video track flag equals true, then queue a task to fire a simple event
// named change at the HTMLMediaElement videoTracks list.
if (removedSelectedVideoTrack)
- mediaElement()->videoTracks()->scheduleChangeEvent();
+ mediaElement()->videoTracks().scheduleChangeEvent();
}
// 8. Let SourceBuffer textTracks list equal the TextTrackList object returned by sourceBuffer.textTracks.
- RefPtr<TextTrackList> textTracks = buffer->textTracks();
+ auto& textTracks = buffer.textTracks();
// 9. If the SourceBuffer textTracks list is not empty, then run the following steps:
- if (textTracks->length()) {
+ if (textTracks.length()) {
// 9.1 Let HTMLMediaElement textTracks list equal the TextTrackList object returned by the textTracks
// attribute on the HTMLMediaElement.
// 9.2 Let the removed enabled text track flag equal false.
bool removedEnabledTextTrack = false;
// 9.3 For each TextTrack object in the SourceBuffer textTracks list, run the following steps:
- while (textTracks->length()) {
- TextTrack* track = textTracks->lastItem();
+ while (textTracks.length()) {
+ auto& track = *textTracks.lastItem();
// 9.3.1 Set the sourceBuffer attribute on the TextTrack object to null.
- track->setSourceBuffer(0);
+ track.setSourceBuffer(nullptr);
// 9.3.2 If the mode attribute on the TextTrack object is set to "showing" or "hidden", then
// set the removed enabled text track flag to true.
- if (track->mode() == TextTrack::showingKeyword() || track->mode() == TextTrack::hiddenKeyword())
+ if (track.mode() == TextTrack::Mode::Showing || track.mode() == TextTrack::Mode::Hidden)
removedEnabledTextTrack = true;
// 9.3.3 Remove the TextTrack object from the HTMLMediaElement textTracks list.
@@ -569,30 +803,31 @@ void MediaSource::removeSourceBuffer(SourceBuffer* buffer, ExceptionCode& ec)
// 9.3.5 Remove the TextTrack object from the SourceBuffer textTracks list.
// 9.3.6 Queue a task to fire a trusted event named removetrack, that does not bubble and is not
// cancelable, and that uses the TrackEvent interface, at the SourceBuffer textTracks list.
- textTracks->remove(track);
+ textTracks.remove(track);
}
-
+
// 9.4 If the removed enabled text track flag equals true, then queue a task to fire a simple event
// named change at the HTMLMediaElement textTracks list.
if (removedEnabledTextTrack)
- mediaElement()->textTracks()->scheduleChangeEvent();
+ mediaElement()->textTracks().scheduleChangeEvent();
}
-
-
+
// 10. If sourceBuffer is in activeSourceBuffers, then remove sourceBuffer from activeSourceBuffers ...
m_activeSourceBuffers->remove(buffer);
-
+
// 11. Remove sourceBuffer from sourceBuffers and fire a removesourcebuffer event
// on that object.
m_sourceBuffers->remove(buffer);
-
+
// 12. Destroy all resources for sourceBuffer.
- buffer->removedFromMediaSource();
+ buffer.removedFromMediaSource();
+
+ return { };
}
bool MediaSource::isTypeSupported(const String& type)
{
- LOG(Media, "MediaSource::isTypeSupported(%s)", type.ascii().data());
+ LOG(MediaSource, "MediaSource::isTypeSupported(%s)", type.ascii().data());
// Section 2.2 isTypeSupported() method steps.
// https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#widl-MediaSource-isTypeSupported-boolean-DOMString-type
@@ -600,11 +835,12 @@ bool MediaSource::isTypeSupported(const String& type)
if (type.isNull() || type.isEmpty())
return false;
- ContentType contentType(type);
+ // FIXME: Why do we convert to lowercase here, but not in MediaSource::addSourceBuffer?
+ ContentType contentType(type.convertToASCIILowercase());
String codecs = contentType.parameter("codecs");
// 2. If type does not contain a valid MIME type string, then return false.
- if (contentType.type().isEmpty() || codecs.isEmpty())
+ if (contentType.type().isEmpty())
return false;
// 3. If type contains a media type or media subtype that the MediaSource does not support, then return false.
@@ -615,7 +851,12 @@ bool MediaSource::isTypeSupported(const String& type)
parameters.type = contentType.type();
parameters.codecs = codecs;
parameters.isMediaSource = true;
- return MediaPlayer::supportsType(parameters, 0) != MediaPlayer::IsNotSupported;
+ MediaPlayer::SupportsType supported = MediaPlayer::supportsType(parameters, 0);
+
+ if (codecs.isEmpty())
+ return supported != MediaPlayer::IsNotSupported;
+
+ return supported == MediaPlayer::IsSupported;
}
bool MediaSource::isOpen() const
@@ -628,27 +869,52 @@ bool MediaSource::isClosed() const
return readyState() == closedKeyword();
}
-void MediaSource::close()
+bool MediaSource::isEnded() const
{
+ return readyState() == endedKeyword();
+}
+
+void MediaSource::detachFromElement(HTMLMediaElement& element)
+{
+ ASSERT_UNUSED(element, m_mediaElement == &element);
+
+ // 2.4.2 Detaching from a media element
+ // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#mediasource-detach
+
+ // 1. Set the readyState attribute to "closed".
+ // 7. Queue a task to fire a simple event named sourceclose at the MediaSource.
setReadyState(closedKeyword());
+
+ // 2. Update duration to NaN.
+ m_duration = MediaTime::invalidTime();
+
+ // 3. Remove all the SourceBuffer objects from activeSourceBuffers.
+ // 4. Queue a task to fire a simple event named removesourcebuffer at activeSourceBuffers.
+ while (m_activeSourceBuffers->length())
+ removeSourceBuffer(*m_activeSourceBuffers->item(0));
+
+ // 5. Remove all the SourceBuffer objects from sourceBuffers.
+ // 6. Queue a task to fire a simple event named removesourcebuffer at sourceBuffers.
+ while (m_sourceBuffers->length())
+ removeSourceBuffer(*m_sourceBuffers->item(0));
+
+ m_private = nullptr;
+ m_mediaElement = nullptr;
}
-void MediaSource::sourceBufferDidChangeAcitveState(SourceBuffer* sourceBuffer, bool active)
+void MediaSource::sourceBufferDidChangeActiveState(SourceBuffer&, bool)
{
- if (active && !m_activeSourceBuffers->contains(sourceBuffer))
- m_activeSourceBuffers->add(sourceBuffer);
- else if (!active && m_activeSourceBuffers->contains(sourceBuffer))
- m_activeSourceBuffers->remove(sourceBuffer);
+ regenerateActiveSourceBuffers();
}
-bool MediaSource::attachToElement(HTMLMediaElement* element)
+bool MediaSource::attachToElement(HTMLMediaElement& element)
{
if (m_mediaElement)
return false;
ASSERT(isClosed());
- m_mediaElement = element;
+ m_mediaElement = &element;
return true;
}
@@ -670,13 +936,27 @@ bool MediaSource::hasPendingActivity() const
void MediaSource::stop()
{
m_asyncEventQueue.close();
- if (!isClosed())
- setReadyState(closedKeyword());
- m_private.clear();
+ if (m_mediaElement)
+ m_mediaElement->detachMediaSource();
+ m_readyState = closedKeyword();
+ m_private = nullptr;
+}
+
+bool MediaSource::canSuspendForDocumentSuspension() const
+{
+ return isClosed() && !m_asyncEventQueue.hasPendingEvents();
+}
+
+const char* MediaSource::activeDOMObjectName() const
+{
+ return "MediaSource";
}
void MediaSource::onReadyStateChange(const AtomicString& oldState, const AtomicString& newState)
{
+ for (auto& buffer : *m_sourceBuffers)
+ buffer->readyStateChanged();
+
if (isOpen()) {
scheduleEvent(eventNames().sourceopenEvent);
return;
@@ -688,58 +968,46 @@ void MediaSource::onReadyStateChange(const AtomicString& oldState, const AtomicS
}
ASSERT(isClosed());
-
- m_activeSourceBuffers->clear();
-
- // Clear SourceBuffer references to this object.
- for (unsigned long i = 0, length = m_sourceBuffers->length(); i < length; ++i)
- m_sourceBuffers->item(i)->removedFromMediaSource();
- m_sourceBuffers->clear();
-
scheduleEvent(eventNames().sourcecloseEvent);
}
-Vector<RefPtr<TimeRanges>> MediaSource::activeRanges() const
+Vector<PlatformTimeRanges> MediaSource::activeRanges() const
{
- Vector<RefPtr<TimeRanges>> activeRanges(m_activeSourceBuffers->length());
- for (size_t i = 0, length = m_activeSourceBuffers->length(); i < length; ++i)
- activeRanges[i] = m_activeSourceBuffers->item(i)->buffered(ASSERT_NO_EXCEPTION);
-
+ Vector<PlatformTimeRanges> activeRanges;
+ for (auto& sourceBuffer : *m_activeSourceBuffers)
+ activeRanges.append(sourceBuffer->bufferedInternal().ranges());
return activeRanges;
}
-RefPtr<SourceBufferPrivate> MediaSource::createSourceBufferPrivate(const ContentType& type, ExceptionCode& ec)
+ExceptionOr<Ref<SourceBufferPrivate>> MediaSource::createSourceBufferPrivate(const ContentType& type)
{
RefPtr<SourceBufferPrivate> sourceBufferPrivate;
switch (m_private->addSourceBuffer(type, sourceBufferPrivate)) {
- case MediaSourcePrivate::Ok: {
- return sourceBufferPrivate;
- }
+ case MediaSourcePrivate::Ok:
+ return sourceBufferPrivate.releaseNonNull();
case MediaSourcePrivate::NotSupported:
// 2.2 https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-MediaSource-addSourceBuffer-SourceBuffer-DOMString-type
// Step 2: If type contains a MIME type ... that is not supported with the types
// specified for the other SourceBuffer objects in sourceBuffers, then throw
// a NOT_SUPPORTED_ERR exception and abort these steps.
- ec = NOT_SUPPORTED_ERR;
- return nullptr;
+ return Exception { NOT_SUPPORTED_ERR };
case MediaSourcePrivate::ReachedIdLimit:
// 2.2 https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-MediaSource-addSourceBuffer-SourceBuffer-DOMString-type
// Step 3: If the user agent can't handle any more SourceBuffer objects then throw
// a QUOTA_EXCEEDED_ERR exception and abort these steps.
- ec = QUOTA_EXCEEDED_ERR;
- return nullptr;
+ return Exception { QUOTA_EXCEEDED_ERR };
}
ASSERT_NOT_REACHED();
- return nullptr;
+ return Exception { QUOTA_EXCEEDED_ERR };
}
void MediaSource::scheduleEvent(const AtomicString& eventName)
{
- RefPtr<Event> event = Event::create(eventName, false, false);
+ auto event = Event::create(eventName, false, false);
event->setTarget(this);
- m_asyncEventQueue.enqueueEvent(event.release());
+ m_asyncEventQueue.enqueueEvent(WTFMove(event));
}
ScriptExecutionContext* MediaSource::scriptExecutionContext() const
@@ -757,6 +1025,18 @@ URLRegistry& MediaSource::registry() const
return MediaSourceRegistry::registry();
}
+void MediaSource::regenerateActiveSourceBuffers()
+{
+ Vector<RefPtr<SourceBuffer>> newList;
+ for (auto& sourceBuffer : *m_sourceBuffers) {
+ if (sourceBuffer->active())
+ newList.append(sourceBuffer);
+ }
+ m_activeSourceBuffers->swap(newList);
+ for (auto& sourceBuffer : *m_activeSourceBuffers)
+ sourceBuffer->setBufferedDirty(true);
+}
+
}
#endif
diff --git a/Source/WebCore/Modules/mediasource/MediaSource.h b/Source/WebCore/Modules/mediasource/MediaSource.h
index 131b549d0..549319f12 100644
--- a/Source/WebCore/Modules/mediasource/MediaSource.h
+++ b/Source/WebCore/Modules/mediasource/MediaSource.h
@@ -28,99 +28,125 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef MediaSource_h
-#define MediaSource_h
+#pragma once
#if ENABLE(MEDIA_SOURCE)
#include "ActiveDOMObject.h"
#include "EventTarget.h"
+#include "ExceptionOr.h"
#include "GenericEventQueue.h"
-#include "HTMLMediaSource.h"
-#include "MediaSourcePrivate.h"
-#include "ScriptWrappable.h"
-#include "SourceBuffer.h"
-#include "SourceBufferList.h"
+#include "MediaSourcePrivateClient.h"
#include "URLRegistry.h"
-#include <wtf/PassOwnPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/Vector.h>
namespace WebCore {
-class GenericEventQueue;
+class ContentType;
+class HTMLMediaElement;
+class SourceBuffer;
+class SourceBufferList;
+class SourceBufferPrivate;
+class TimeRanges;
-class MediaSource : public RefCounted<MediaSource>, public HTMLMediaSource, public ActiveDOMObject, public EventTargetWithInlineData, public ScriptWrappable {
+class MediaSource final : public MediaSourcePrivateClient, public ActiveDOMObject, public EventTargetWithInlineData, public URLRegistrable {
public:
- static const AtomicString& openKeyword();
- static const AtomicString& closedKeyword();
- static const AtomicString& endedKeyword();
+ static void setRegistry(URLRegistry*);
+ static MediaSource* lookup(const String& url) { return s_registry ? static_cast<MediaSource*>(s_registry->lookup(url)) : nullptr; }
- static PassRefPtr<MediaSource> create(ScriptExecutionContext&);
+ static Ref<MediaSource> create(ScriptExecutionContext&);
virtual ~MediaSource();
void addedToRegistry();
void removedFromRegistry();
void openIfInEndedState();
bool isOpen() const;
- void sourceBufferDidChangeAcitveState(SourceBuffer*, bool);
- void streamEndedWithError(const AtomicString& error, ExceptionCode&);
-
- // HTMLMediaSource
- virtual bool attachToElement(HTMLMediaElement*) override;
- virtual void setPrivateAndOpen(PassRef<MediaSourcePrivate>) override;
- virtual void close() override;
- virtual bool isClosed() const override;
- virtual double duration() const override;
- virtual PassRefPtr<TimeRanges> buffered() const override;
- virtual void refHTMLMediaSource() override { ref(); }
- virtual void derefHTMLMediaSource() override { deref(); }
- virtual void monitorSourceBuffers() override;
-
- void setDuration(double, ExceptionCode&);
+ bool isClosed() const;
+ bool isEnded() const;
+ void sourceBufferDidChangeActiveState(SourceBuffer&, bool);
+
+ enum class EndOfStreamError { Network, Decode };
+ void streamEndedWithError(std::optional<EndOfStreamError>);
+
+ MediaTime duration() const final;
+ void durationChanged(const MediaTime&) final;
+ std::unique_ptr<PlatformTimeRanges> buffered() const final;
+
+ bool attachToElement(HTMLMediaElement&);
+ void detachFromElement(HTMLMediaElement&);
+ void monitorSourceBuffers() override;
+ bool isSeeking() const { return m_pendingSeekTime.isValid(); }
+ Ref<TimeRanges> seekable();
+ ExceptionOr<void> setLiveSeekableRange(double start, double end);
+ ExceptionOr<void> clearLiveSeekableRange();
+
+ ExceptionOr<void> setDuration(double);
+ ExceptionOr<void> setDurationInternal(const MediaTime&);
+ MediaTime currentTime() const;
const AtomicString& readyState() const { return m_readyState; }
- void setReadyState(const AtomicString&);
- void endOfStream(const AtomicString& error, ExceptionCode&);
+ ExceptionOr<void> endOfStream(std::optional<EndOfStreamError>);
HTMLMediaElement* mediaElement() const { return m_mediaElement; }
- // MediaSource.idl methods
SourceBufferList* sourceBuffers() { return m_sourceBuffers.get(); }
SourceBufferList* activeSourceBuffers() { return m_activeSourceBuffers.get(); }
- SourceBuffer* addSourceBuffer(const String& type, ExceptionCode&);
- void removeSourceBuffer(SourceBuffer*, ExceptionCode&);
+ ExceptionOr<SourceBuffer&> addSourceBuffer(const String& type);
+ ExceptionOr<void> removeSourceBuffer(SourceBuffer&);
static bool isTypeSupported(const String& type);
- // ActiveDOMObject interface
- virtual bool hasPendingActivity() const override;
- virtual void stop() override;
+ ScriptExecutionContext* scriptExecutionContext() const final;
- // EventTarget interface
- virtual ScriptExecutionContext* scriptExecutionContext() const override final;
- virtual void refEventTarget() override final { ref(); }
- virtual void derefEventTarget() override final { deref(); }
- virtual EventTargetInterface eventTargetInterface() const override;
+ using RefCounted::ref;
+ using RefCounted::deref;
- // URLRegistrable interface
- virtual URLRegistry& registry() const override;
+ bool hasPendingActivity() const final;
- using RefCounted<MediaSource>::ref;
- using RefCounted<MediaSource>::deref;
+ static const MediaTime& currentTimeFudgeFactor();
-protected:
+private:
explicit MediaSource(ScriptExecutionContext&);
+ void stop() final;
+ bool canSuspendForDocumentSuspension() const final;
+ const char* activeDOMObjectName() const final;
+
+ void setPrivateAndOpen(Ref<MediaSourcePrivate>&&) final;
+ void seekToTime(const MediaTime&) final;
+
+ void refEventTarget() final { ref(); }
+ void derefEventTarget() final { deref(); }
+ EventTargetInterface eventTargetInterface() const final;
+
+ URLRegistry& registry() const final;
+
+ static const AtomicString& openKeyword();
+ static const AtomicString& closedKeyword();
+ static const AtomicString& endedKeyword();
+ void setReadyState(const AtomicString&);
void onReadyStateChange(const AtomicString& oldState, const AtomicString& newState);
- Vector<RefPtr<TimeRanges>> activeRanges() const;
- RefPtr<SourceBufferPrivate> createSourceBufferPrivate(const ContentType&, ExceptionCode&);
+ Vector<PlatformTimeRanges> activeRanges() const;
+
+ ExceptionOr<Ref<SourceBufferPrivate>> createSourceBufferPrivate(const ContentType&);
void scheduleEvent(const AtomicString& eventName);
- GenericEventQueue& asyncEventQueue() { return m_asyncEventQueue; }
+
+ bool hasBufferedTime(const MediaTime&);
+ bool hasCurrentTime();
+ bool hasFutureTime();
+
+ void regenerateActiveSourceBuffers();
+
+ void completeSeek();
+
+ static URLRegistry* s_registry;
RefPtr<MediaSourcePrivate> m_private;
RefPtr<SourceBufferList> m_sourceBuffers;
RefPtr<SourceBufferList> m_activeSourceBuffers;
- HTMLMediaElement* m_mediaElement;
+ mutable std::unique_ptr<PlatformTimeRanges> m_buffered;
+ std::unique_ptr<PlatformTimeRanges> m_liveSeekable;
+ HTMLMediaElement* m_mediaElement { nullptr };
+ MediaTime m_duration;
+ MediaTime m_pendingSeekTime;
AtomicString m_readyState;
GenericEventQueue m_asyncEventQueue;
};
@@ -128,5 +154,3 @@ protected:
}
#endif
-
-#endif
diff --git a/Source/WebCore/Modules/mediasource/MediaSource.idl b/Source/WebCore/Modules/mediasource/MediaSource.idl
index 3cb8ebd87..5a9fef118 100644
--- a/Source/WebCore/Modules/mediasource/MediaSource.idl
+++ b/Source/WebCore/Modules/mediasource/MediaSource.idl
@@ -28,15 +28,17 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+enum EndOfStreamError {
+ "network",
+ "decode"
+};
+
[
- Conditional=MEDIA_SOURCE,
ActiveDOMObject,
- EventTarget,
- EnabledBySetting=MediaSource,
- JSGenerateToJSObject,
- JSGenerateToNativeObject,
+ Conditional=MEDIA_SOURCE,
Constructor,
ConstructorCallWith=ScriptExecutionContext,
+ EnabledBySetting=MediaSource,
] interface MediaSource : EventTarget {
// All the source buffers created by this object.
readonly attribute SourceBufferList sourceBuffers;
@@ -44,14 +46,21 @@
// Subset of sourceBuffers that provide data for the selected/enabled tracks.
readonly attribute SourceBufferList activeSourceBuffers;
- [SetterRaisesException] attribute double duration;
+ [SetterMayThrowException] attribute unrestricted double duration;
- [RaisesException] SourceBuffer addSourceBuffer(DOMString type);
- [RaisesException] void removeSourceBuffer(SourceBuffer buffer);
+ [MayThrowException] SourceBuffer addSourceBuffer(DOMString type);
+ [MayThrowException] void removeSourceBuffer(SourceBuffer buffer);
readonly attribute DOMString readyState;
- [RaisesException] void endOfStream([Default=NullString] optional DOMString error);
+ [MayThrowException] void endOfStream(optional EndOfStreamError error);
static boolean isTypeSupported (DOMString type);
+
+ [MayThrowException] void setLiveSeekableRange(double start, double end);
+ [MayThrowException] void clearLiveSeekableRange();
+
+ attribute EventHandler onsourceopen;
+ attribute EventHandler onsourceended;
+ attribute EventHandler onsourceclose;
};
diff --git a/Source/WebCore/Modules/mediasource/MediaSourceRegistry.cpp b/Source/WebCore/Modules/mediasource/MediaSourceRegistry.cpp
index a65308860..b6974c2ca 100644
--- a/Source/WebCore/Modules/mediasource/MediaSourceRegistry.cpp
+++ b/Source/WebCore/Modules/mediasource/MediaSourceRegistry.cpp
@@ -42,18 +42,18 @@ namespace WebCore {
MediaSourceRegistry& MediaSourceRegistry::registry()
{
ASSERT(isMainThread());
- DEFINE_STATIC_LOCAL(MediaSourceRegistry, instance, ());
+ static NeverDestroyed<MediaSourceRegistry> instance;
return instance;
}
-void MediaSourceRegistry::registerURL(SecurityOrigin*, const URL& url, URLRegistrable* registrable)
+void MediaSourceRegistry::registerURL(SecurityOrigin*, const URL& url, URLRegistrable& registrable)
{
- ASSERT(&registrable->registry() == this);
+ ASSERT(&registrable.registry() == this);
ASSERT(isMainThread());
- MediaSource* source = static_cast<MediaSource*>(registrable);
- source->addedToRegistry();
- m_mediaSources.set(url.string(), source);
+ MediaSource& source = static_cast<MediaSource&>(registrable);
+ source.addedToRegistry();
+ m_mediaSources.set(url.string(), &source);
}
void MediaSourceRegistry::unregisterURL(const URL& url)
@@ -76,7 +76,7 @@ URLRegistrable* MediaSourceRegistry::lookup(const String& url) const
MediaSourceRegistry::MediaSourceRegistry()
{
- HTMLMediaSource::setRegistry(this);
+ MediaSource::setRegistry(this);
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/mediasource/MediaSourceRegistry.h b/Source/WebCore/Modules/mediasource/MediaSourceRegistry.h
index 9974ea4af..eb87b40dc 100644
--- a/Source/WebCore/Modules/mediasource/MediaSourceRegistry.h
+++ b/Source/WebCore/Modules/mediasource/MediaSourceRegistry.h
@@ -28,14 +28,13 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef MediaSourceRegistry_h
-#define MediaSourceRegistry_h
+#pragma once
#if ENABLE(MEDIA_SOURCE)
#include "URLRegistry.h"
#include <wtf/HashMap.h>
-#include <wtf/PassRefPtr.h>
+#include <wtf/NeverDestroyed.h>
#include <wtf/text/StringHash.h>
namespace WebCore {
@@ -44,14 +43,15 @@ class URL;
class MediaSource;
class MediaSourceRegistry final : public URLRegistry {
+ friend class NeverDestroyed<MediaSourceRegistry>;
public:
// Returns a single instance of MediaSourceRegistry.
static MediaSourceRegistry& registry();
// Registers a blob URL referring to the specified media source.
- virtual void registerURL(SecurityOrigin*, const URL&, URLRegistrable*) override;
- virtual void unregisterURL(const URL&) override;
- virtual URLRegistrable* lookup(const String&) const override;
+ void registerURL(SecurityOrigin*, const URL&, URLRegistrable&) override;
+ void unregisterURL(const URL&) override;
+ URLRegistrable* lookup(const String&) const override;
private:
MediaSourceRegistry();
@@ -60,5 +60,4 @@ private:
} // namespace WebCore
-#endif
-#endif
+#endif // ENABLE(MEDIA_SOURCE)
diff --git a/Source/WebCore/Modules/mediasource/SampleMap.cpp b/Source/WebCore/Modules/mediasource/SampleMap.cpp
index 6cdf37e77..42157c5fc 100644
--- a/Source/WebCore/Modules/mediasource/SampleMap.cpp
+++ b/Source/WebCore/Modules/mediasource/SampleMap.cpp
@@ -32,28 +32,32 @@
namespace WebCore {
+template <typename M>
class SampleIsLessThanMediaTimeComparator {
public:
- bool operator()(std::pair<MediaTime, RefPtr<MediaSample>> value, MediaTime time)
+ typedef typename M::value_type value_type;
+ bool operator()(const value_type& value, const MediaTime& time)
{
MediaTime presentationEndTime = value.second->presentationTime() + value.second->duration();
return presentationEndTime <= time;
}
- bool operator()(MediaTime time, std::pair<MediaTime, RefPtr<MediaSample>> value)
+ bool operator()(const MediaTime& time, const value_type& value)
{
MediaTime presentationStartTime = value.second->presentationTime();
return time < presentationStartTime;
}
};
+template <typename M>
class SampleIsGreaterThanMediaTimeComparator {
public:
- bool operator()(std::pair<MediaTime, RefPtr<MediaSample>> value, MediaTime time)
+ typedef typename M::value_type value_type;
+ bool operator()(const value_type& value, const MediaTime& time)
{
MediaTime presentationStartTime = value.second->presentationTime();
return presentationStartTime > time;
}
- bool operator()(MediaTime time, std::pair<MediaTime, RefPtr<MediaSample>> value)
+ bool operator()(const MediaTime& time, const value_type& value)
{
MediaTime presentationEndTime = value.second->presentationTime() + value.second->duration();
return time >= presentationEndTime;
@@ -62,125 +66,228 @@ public:
class SampleIsRandomAccess {
public:
- bool operator()(std::pair<MediaTime, RefPtr<MediaSample>> value)
+ bool operator()(DecodeOrderSampleMap::MapType::value_type& value)
{
return value.second->flags() == MediaSample::IsSync;
}
};
-class SamplePresentationTimeIsWithinRangeComparator {
-public:
- bool operator()(std::pair<MediaTime, MediaTime> range, std::pair<MediaTime, RefPtr<MediaSample>> value)
+// SamplePresentationTimeIsInsideRangeComparator matches (range.first, range.second]
+struct SamplePresentationTimeIsInsideRangeComparator {
+ bool operator()(std::pair<MediaTime, MediaTime> range, const std::pair<MediaTime, RefPtr<MediaSample>>& value)
{
return range.second < value.first;
}
- bool operator()(std::pair<MediaTime, RefPtr<MediaSample>> value, std::pair<MediaTime, MediaTime> range)
+ bool operator()(const std::pair<MediaTime, RefPtr<MediaSample>>& value, std::pair<MediaTime, MediaTime> range)
+ {
+ return value.first <= range.first;
+ }
+};
+
+// SamplePresentationTimeIsWithinRangeComparator matches [range.first, range.second)
+struct SamplePresentationTimeIsWithinRangeComparator {
+ bool operator()(std::pair<MediaTime, MediaTime> range, const std::pair<MediaTime, RefPtr<MediaSample>>& value)
+ {
+ return range.second <= value.first;
+ }
+ bool operator()(const std::pair<MediaTime, RefPtr<MediaSample>>& value, std::pair<MediaTime, MediaTime> range)
{
return value.first < range.first;
}
};
-void SampleMap::addSample(PassRefPtr<MediaSample> prpSample)
+bool SampleMap::empty() const
{
- RefPtr<MediaSample> sample = prpSample;
- ASSERT(sample);
- m_presentationSamples.insert(MapType::value_type(sample->presentationTime(), sample));
- m_decodeSamples.insert(MapType::value_type(sample->decodeTime(), sample));
+ return presentationOrder().m_samples.empty();
+}
+
+void SampleMap::clear()
+{
+ presentationOrder().m_samples.clear();
+ decodeOrder().m_samples.clear();
+ m_totalSize = 0;
+}
+
+void SampleMap::addSample(MediaSample& sample)
+{
+ MediaTime presentationTime = sample.presentationTime();
+
+ presentationOrder().m_samples.insert(PresentationOrderSampleMap::MapType::value_type(presentationTime, &sample));
+
+ auto decodeKey = DecodeOrderSampleMap::KeyType(sample.decodeTime(), presentationTime);
+ decodeOrder().m_samples.insert(DecodeOrderSampleMap::MapType::value_type(decodeKey, &sample));
+
+ m_totalSize += sample.sizeInBytes();
}
void SampleMap::removeSample(MediaSample* sample)
{
ASSERT(sample);
- m_presentationSamples.erase(sample->presentationTime());
- m_decodeSamples.erase(sample->decodeTime());
+ MediaTime presentationTime = sample->presentationTime();
+
+ m_totalSize -= sample->sizeInBytes();
+
+ auto decodeKey = DecodeOrderSampleMap::KeyType(sample->decodeTime(), presentationTime);
+ presentationOrder().m_samples.erase(presentationTime);
+ decodeOrder().m_samples.erase(decodeKey);
}
-SampleMap::iterator SampleMap::findSampleContainingPresentationTime(const MediaTime& time)
+PresentationOrderSampleMap::iterator PresentationOrderSampleMap::findSampleWithPresentationTime(const MediaTime& time)
{
- return std::equal_range(presentationBegin(), presentationEnd(), time, SampleIsLessThanMediaTimeComparator()).first;
+ auto range = m_samples.equal_range(time);
+ if (range.first == range.second)
+ return end();
+ return range.first;
+}
+
+PresentationOrderSampleMap::iterator PresentationOrderSampleMap::findSampleContainingPresentationTime(const MediaTime& time)
+{
+ // upper_bound will return the first sample whose presentation start time is greater than the search time.
+ // If this is the first sample, that means no sample in the map contains the requested time.
+ auto iter = m_samples.upper_bound(time);
+ if (iter == begin())
+ return end();
+
+ // Look at the previous sample; does it contain the requested time?
+ --iter;
+ MediaSample& sample = *iter->second;
+ if (sample.presentationTime() + sample.duration() > time)
+ return iter;
+ return end();
}
-SampleMap::iterator SampleMap::findSampleAfterPresentationTime(const MediaTime& time)
+PresentationOrderSampleMap::iterator PresentationOrderSampleMap::findSampleStartingOnOrAfterPresentationTime(const MediaTime& time)
{
- return std::lower_bound(presentationBegin(), presentationEnd(), time, SampleIsLessThanMediaTimeComparator());
+ return m_samples.lower_bound(time);
}
-SampleMap::iterator SampleMap::findSampleWithDecodeTime(const MediaTime& time)
+DecodeOrderSampleMap::iterator DecodeOrderSampleMap::findSampleWithDecodeKey(const KeyType& key)
{
- return m_decodeSamples.find(time);
+ return m_samples.find(key);
}
-SampleMap::reverse_iterator SampleMap::reverseFindSampleContainingPresentationTime(const MediaTime& time)
+PresentationOrderSampleMap::reverse_iterator PresentationOrderSampleMap::reverseFindSampleContainingPresentationTime(const MediaTime& time)
{
- return std::equal_range(reversePresentationBegin(), reversePresentationEnd(), time, SampleIsGreaterThanMediaTimeComparator()).first;
+ auto range = std::equal_range(rbegin(), rend(), time, SampleIsGreaterThanMediaTimeComparator<MapType>());
+ if (range.first == range.second)
+ return rend();
+ return range.first;
}
-SampleMap::reverse_iterator SampleMap::reverseFindSampleBeforePresentationTime(const MediaTime& time)
+PresentationOrderSampleMap::reverse_iterator PresentationOrderSampleMap::reverseFindSampleBeforePresentationTime(const MediaTime& time)
{
- return std::lower_bound(reversePresentationBegin(), reversePresentationEnd(), time, SampleIsGreaterThanMediaTimeComparator());
+ if (m_samples.empty())
+ return rend();
+
+ // upper_bound will return the first sample whose presentation start time is greater than the search time.
+ auto found = m_samples.upper_bound(time);
+
+ // If no sample was found with a time greater than the search time, return the last sample.
+ if (found == end())
+ return rbegin();
+
+ // If the first sample has a time grater than the search time, no samples will have a presentation time before the search time.
+ if (found == begin())
+ return rend();
+
+ // Otherwise, return the sample immediately previous to the one found.
+ return --reverse_iterator(--found);
}
-SampleMap::reverse_iterator SampleMap::reverseFindSampleWithDecodeTime(const MediaTime& time)
+DecodeOrderSampleMap::reverse_iterator DecodeOrderSampleMap::reverseFindSampleWithDecodeKey(const KeyType& key)
{
- SampleMap::iterator found = findSampleWithDecodeTime(time);
- if (found == decodeEnd())
- return reverseDecodeEnd();
+ DecodeOrderSampleMap::iterator found = findSampleWithDecodeKey(key);
+ if (found == end())
+ return rend();
return --reverse_iterator(found);
}
-SampleMap::reverse_iterator SampleMap::findSyncSamplePriorToPresentationTime(const MediaTime& time, const MediaTime& threshold)
+DecodeOrderSampleMap::reverse_iterator DecodeOrderSampleMap::findSyncSamplePriorToPresentationTime(const MediaTime& time, const MediaTime& threshold)
{
- reverse_iterator reverseCurrentSamplePTS = reverseFindSampleBeforePresentationTime(time);
- if (reverseCurrentSamplePTS == reversePresentationEnd())
- return reverseDecodeEnd();
+ PresentationOrderSampleMap::reverse_iterator reverseCurrentSamplePTS = m_presentationOrder.reverseFindSampleBeforePresentationTime(time);
+ if (reverseCurrentSamplePTS == m_presentationOrder.rend())
+ return rend();
- reverse_iterator reverseCurrentSampleDTS = reverseFindSampleWithDecodeTime(reverseCurrentSamplePTS->second->decodeTime());
+ const RefPtr<MediaSample>& sample = reverseCurrentSamplePTS->second;
+ reverse_iterator reverseCurrentSampleDTS = reverseFindSampleWithDecodeKey(KeyType(sample->decodeTime(), sample->presentationTime()));
reverse_iterator foundSample = findSyncSamplePriorToDecodeIterator(reverseCurrentSampleDTS);
- if (foundSample == reverseDecodeEnd())
- return reverseDecodeEnd();
+ if (foundSample == rend())
+ return rend();
if (foundSample->second->presentationTime() < time - threshold)
- return reverseDecodeEnd();
+ return rend();
return foundSample;
}
-SampleMap::reverse_iterator SampleMap::findSyncSamplePriorToDecodeIterator(reverse_iterator iterator)
+DecodeOrderSampleMap::reverse_iterator DecodeOrderSampleMap::findSyncSamplePriorToDecodeIterator(reverse_iterator iterator)
{
- return std::find_if(iterator, reverseDecodeEnd(), SampleIsRandomAccess());
+ return std::find_if(iterator, rend(), SampleIsRandomAccess());
}
-SampleMap::iterator SampleMap::findSyncSampleAfterPresentationTime(const MediaTime& time, const MediaTime& threshold)
+DecodeOrderSampleMap::iterator DecodeOrderSampleMap::findSyncSampleAfterPresentationTime(const MediaTime& time, const MediaTime& threshold)
{
- iterator currentSamplePTS = findSampleAfterPresentationTime(time);
- if (currentSamplePTS == presentationEnd())
- return decodeEnd();
+ PresentationOrderSampleMap::iterator currentSamplePTS = m_presentationOrder.findSampleStartingOnOrAfterPresentationTime(time);
+ if (currentSamplePTS == m_presentationOrder.end())
+ return end();
- iterator currentSampleDTS = findSampleWithDecodeTime(currentSamplePTS->second->decodeTime());
+ const RefPtr<MediaSample>& sample = currentSamplePTS->second;
+ iterator currentSampleDTS = findSampleWithDecodeKey(KeyType(sample->decodeTime(), sample->presentationTime()));
MediaTime upperBound = time + threshold;
- iterator foundSample = std::find_if(currentSampleDTS, decodeEnd(), SampleIsRandomAccess());
- if (foundSample == decodeEnd())
- return decodeEnd();
+ iterator foundSample = std::find_if(currentSampleDTS, end(), SampleIsRandomAccess());
+ if (foundSample == end())
+ return end();
if (foundSample->second->presentationTime() > upperBound)
- return decodeEnd();
+ return end();
return foundSample;
}
-SampleMap::iterator SampleMap::findSyncSampleAfterDecodeIterator(iterator currentSampleDTS)
+DecodeOrderSampleMap::iterator DecodeOrderSampleMap::findSyncSampleAfterDecodeIterator(iterator currentSampleDTS)
{
- return std::find_if(currentSampleDTS, decodeEnd(), SampleIsRandomAccess());
+ if (currentSampleDTS == end())
+ return end();
+ return std::find_if(++currentSampleDTS, end(), SampleIsRandomAccess());
}
-SampleMap::iterator_range SampleMap::findSamplesBetweenPresentationTimes(const MediaTime& begin, const MediaTime& end)
+PresentationOrderSampleMap::iterator_range PresentationOrderSampleMap::findSamplesBetweenPresentationTimes(const MediaTime& beginTime, const MediaTime& endTime)
{
- std::pair<MediaTime, MediaTime> range(begin, end);
- return std::equal_range(presentationBegin(), presentationEnd(), range, SamplePresentationTimeIsWithinRangeComparator());
+ // startTime is inclusive, so use lower_bound to include samples wich start exactly at startTime.
+ // endTime is not inclusive, so use lower_bound to exclude samples which start exactly at endTime.
+ auto lower_bound = m_samples.lower_bound(beginTime);
+ auto upper_bound = m_samples.lower_bound(endTime);
+ if (lower_bound == upper_bound)
+ return { end(), end() };
+ return { lower_bound, upper_bound };
+}
+
+PresentationOrderSampleMap::iterator_range PresentationOrderSampleMap::findSamplesWithinPresentationRange(const MediaTime& beginTime, const MediaTime& endTime)
+{
+ // startTime is not inclusive, so use upper_bound to exclude samples which start exactly at startTime.
+ // endTime is inclusive, so use upper_bound to include samples which start exactly at endTime.
+ auto lower_bound = m_samples.upper_bound(beginTime);
+ auto upper_bound = m_samples.upper_bound(endTime);
+ if (lower_bound == upper_bound)
+ return { end(), end() };
+ return { lower_bound, upper_bound };
+}
+
+PresentationOrderSampleMap::iterator_range PresentationOrderSampleMap::findSamplesWithinPresentationRangeFromEnd(const MediaTime& beginTime, const MediaTime& endTime)
+{
+ reverse_iterator rangeEnd = std::find_if(rbegin(), rend(), [&beginTime](auto& value) {
+ return value.second->presentationTime() <= beginTime;
+ });
+
+ reverse_iterator rangeStart = std::find_if(rbegin(), rangeEnd, [&endTime](auto& value) {
+ return value.second->presentationTime() <= endTime;
+ });
+
+ return iterator_range(rangeEnd.base(), rangeStart.base());
}
-SampleMap::reverse_iterator_range SampleMap::findDependentSamples(MediaSample* sample)
+DecodeOrderSampleMap::reverse_iterator_range DecodeOrderSampleMap::findDependentSamples(MediaSample* sample)
{
ASSERT(sample);
- reverse_iterator currentDecodeIter = reverseFindSampleWithDecodeTime(sample->decodeTime());
+ reverse_iterator currentDecodeIter = reverseFindSampleWithDecodeKey(KeyType(sample->decodeTime(), sample->presentationTime()));
reverse_iterator nextSyncSample = findSyncSamplePriorToDecodeIterator(currentDecodeIter);
return reverse_iterator_range(currentDecodeIter, nextSyncSample);
}
diff --git a/Source/WebCore/Modules/mediasource/SampleMap.h b/Source/WebCore/Modules/mediasource/SampleMap.h
index deb42c453..639797a2b 100644
--- a/Source/WebCore/Modules/mediasource/SampleMap.h
+++ b/Source/WebCore/Modules/mediasource/SampleMap.h
@@ -23,8 +23,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef SampleMap_h
-#define SampleMap_h
+#pragma once
#if ENABLE(MEDIA_SOURCE)
@@ -35,48 +34,103 @@
namespace WebCore {
class MediaSample;
+class SampleMap;
-class SampleMap {
+class PresentationOrderSampleMap {
+ friend class SampleMap;
public:
typedef std::map<MediaTime, RefPtr<MediaSample>> MapType;
typedef MapType::iterator iterator;
+ typedef MapType::const_iterator const_iterator;
typedef MapType::reverse_iterator reverse_iterator;
+ typedef MapType::const_reverse_iterator const_reverse_iterator;
typedef std::pair<iterator, iterator> iterator_range;
+
+ iterator begin() { return m_samples.begin(); }
+ const_iterator begin() const { return m_samples.begin(); }
+ iterator end() { return m_samples.end(); }
+ const_iterator end() const { return m_samples.end(); }
+ reverse_iterator rbegin() { return m_samples.rbegin(); }
+ const_reverse_iterator rbegin() const { return m_samples.rbegin(); }
+ reverse_iterator rend() { return m_samples.rend(); }
+ const_reverse_iterator rend() const { return m_samples.rend(); }
+
+ WEBCORE_EXPORT iterator findSampleWithPresentationTime(const MediaTime&);
+ WEBCORE_EXPORT iterator findSampleContainingPresentationTime(const MediaTime&);
+ WEBCORE_EXPORT iterator findSampleStartingOnOrAfterPresentationTime(const MediaTime&);
+ WEBCORE_EXPORT reverse_iterator reverseFindSampleContainingPresentationTime(const MediaTime&);
+ WEBCORE_EXPORT reverse_iterator reverseFindSampleBeforePresentationTime(const MediaTime&);
+ WEBCORE_EXPORT iterator_range findSamplesBetweenPresentationTimes(const MediaTime&, const MediaTime&);
+ WEBCORE_EXPORT iterator_range findSamplesWithinPresentationRange(const MediaTime&, const MediaTime&);
+ WEBCORE_EXPORT iterator_range findSamplesWithinPresentationRangeFromEnd(const MediaTime&, const MediaTime&);
+
+private:
+ MapType m_samples;
+};
+
+class DecodeOrderSampleMap {
+ friend class SampleMap;
+public:
+ typedef std::pair<MediaTime, MediaTime> KeyType;
+ typedef std::map<KeyType, RefPtr<MediaSample>> MapType;
+ typedef MapType::iterator iterator;
+ typedef MapType::const_iterator const_iterator;
+ typedef MapType::reverse_iterator reverse_iterator;
+ typedef MapType::const_reverse_iterator const_reverse_iterator;
typedef std::pair<reverse_iterator, reverse_iterator> reverse_iterator_range;
- void addSample(PassRefPtr<MediaSample>);
- void removeSample(MediaSample*);
-
- iterator presentationBegin() { return m_presentationSamples.begin(); }
- iterator presentationEnd() { return m_presentationSamples.end(); }
- iterator decodeBegin() { return m_decodeSamples.begin(); }
- iterator decodeEnd() { return m_decodeSamples.end(); }
- reverse_iterator reversePresentationBegin() { return m_presentationSamples.rbegin(); }
- reverse_iterator reversePresentationEnd() { return m_presentationSamples.rend(); }
- reverse_iterator reverseDecodeBegin() { return m_decodeSamples.rbegin(); }
- reverse_iterator reverseDecodeEnd() { return m_decodeSamples.rend(); }
-
- iterator findSampleContainingPresentationTime(const MediaTime&);
- iterator findSampleAfterPresentationTime(const MediaTime&);
- iterator findSampleWithDecodeTime(const MediaTime&);
- reverse_iterator reverseFindSampleContainingPresentationTime(const MediaTime&);
- reverse_iterator reverseFindSampleBeforePresentationTime(const MediaTime&);
- reverse_iterator reverseFindSampleWithDecodeTime(const MediaTime&);
- reverse_iterator findSyncSamplePriorToPresentationTime(const MediaTime&, const MediaTime& threshold = MediaTime::positiveInfiniteTime());
- reverse_iterator findSyncSamplePriorToDecodeIterator(reverse_iterator);
- iterator findSyncSampleAfterPresentationTime(const MediaTime&, const MediaTime& threshold = MediaTime::positiveInfiniteTime());
- iterator findSyncSampleAfterDecodeIterator(iterator);
-
- iterator_range findSamplesBetweenPresentationTimes(const MediaTime&, const MediaTime&);
- reverse_iterator_range findDependentSamples(MediaSample*);
-
+ iterator begin() { return m_samples.begin(); }
+ const_iterator begin() const { return m_samples.begin(); }
+ iterator end() { return m_samples.end(); }
+ const_iterator end() const { return m_samples.end(); }
+ reverse_iterator rbegin() { return m_samples.rbegin(); }
+ const_reverse_iterator rbegin() const { return m_samples.rbegin(); }
+ reverse_iterator rend() { return m_samples.rend(); }
+ const_reverse_iterator rend() const { return m_samples.rend(); }
+
+ WEBCORE_EXPORT iterator findSampleWithDecodeKey(const KeyType&);
+ WEBCORE_EXPORT reverse_iterator reverseFindSampleWithDecodeKey(const KeyType&);
+ WEBCORE_EXPORT reverse_iterator findSyncSamplePriorToPresentationTime(const MediaTime&, const MediaTime& threshold = MediaTime::positiveInfiniteTime());
+ WEBCORE_EXPORT reverse_iterator findSyncSamplePriorToDecodeIterator(reverse_iterator);
+ WEBCORE_EXPORT iterator findSyncSampleAfterPresentationTime(const MediaTime&, const MediaTime& threshold = MediaTime::positiveInfiniteTime());
+ WEBCORE_EXPORT iterator findSyncSampleAfterDecodeIterator(iterator);
+ WEBCORE_EXPORT reverse_iterator_range findDependentSamples(MediaSample*);
+
+private:
+ MapType m_samples;
+ PresentationOrderSampleMap m_presentationOrder;
+};
+
+class SampleMap {
+public:
+ SampleMap() = default;
+
+ WEBCORE_EXPORT bool empty() const;
+ WEBCORE_EXPORT void clear();
+ WEBCORE_EXPORT void addSample(MediaSample&);
+ WEBCORE_EXPORT void removeSample(MediaSample*);
+ size_t sizeInBytes() const { return m_totalSize; }
+
+ template<typename I>
+ void addRange(I begin, I end);
+
+ DecodeOrderSampleMap& decodeOrder() { return m_decodeOrder; }
+ const DecodeOrderSampleMap& decodeOrder() const { return m_decodeOrder; }
+ PresentationOrderSampleMap& presentationOrder() { return m_decodeOrder.m_presentationOrder; }
+ const PresentationOrderSampleMap& presentationOrder() const { return m_decodeOrder.m_presentationOrder; }
+
private:
- MapType m_presentationSamples;
- MapType m_decodeSamples;
+ DecodeOrderSampleMap m_decodeOrder;
+ size_t m_totalSize { 0 };
};
+template<typename I>
+inline void SampleMap::addRange(I begin, I end)
+{
+ for (I iter = begin; iter != end; ++iter)
+ addSample(*iter->second);
}
-#endif
+} // namespace WebCore
-#endif // SampleMap_h
+#endif // ENABLE(MEDIA_SOURCE)
diff --git a/Source/WebCore/Modules/mediasource/SourceBuffer.cpp b/Source/WebCore/Modules/mediasource/SourceBuffer.cpp
index e642179f3..2b4ac5854 100644
--- a/Source/WebCore/Modules/mediasource/SourceBuffer.cpp
+++ b/Source/WebCore/Modules/mediasource/SourceBuffer.cpp
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2013 Google Inc. All rights reserved.
+ * Copyright (C) 2013-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
@@ -34,8 +35,10 @@
#if ENABLE(MEDIA_SOURCE)
#include "AudioTrackList.h"
+#include "BufferSource.h"
#include "Event.h"
-#include "ExceptionCodePlaceholder.h"
+#include "EventNames.h"
+#include "ExceptionCode.h"
#include "GenericEventQueue.h"
#include "HTMLMediaElement.h"
#include "InbandTextTrack.h"
@@ -44,58 +47,71 @@
#include "MediaSample.h"
#include "MediaSource.h"
#include "SampleMap.h"
+#include "SourceBufferList.h"
#include "SourceBufferPrivate.h"
#include "TextTrackList.h"
#include "TimeRanges.h"
#include "VideoTrackList.h"
+#include <limits>
#include <map>
+#include <runtime/JSCInlines.h>
+#include <runtime/JSLock.h>
+#include <runtime/VM.h>
+#include <wtf/CurrentTime.h>
#include <wtf/NeverDestroyed.h>
namespace WebCore {
+static const double ExponentialMovingAverageCoefficient = 0.1;
+
struct SourceBuffer::TrackBuffer {
MediaTime lastDecodeTimestamp;
MediaTime lastFrameDuration;
MediaTime highestPresentationTimestamp;
MediaTime lastEnqueuedPresentationTime;
- bool needRandomAccessFlag;
- bool enabled;
+ MediaTime lastEnqueuedDecodeEndTime;
+ bool needRandomAccessFlag { true };
+ bool enabled { false };
+ bool needsReenqueueing { false };
SampleMap samples;
- SampleMap::MapType decodeQueue;
+ DecodeOrderSampleMap::MapType decodeQueue;
RefPtr<MediaDescription> description;
+ PlatformTimeRanges buffered;
TrackBuffer()
: lastDecodeTimestamp(MediaTime::invalidTime())
, lastFrameDuration(MediaTime::invalidTime())
, highestPresentationTimestamp(MediaTime::invalidTime())
, lastEnqueuedPresentationTime(MediaTime::invalidTime())
- , needRandomAccessFlag(true)
- , enabled(false)
+ , lastEnqueuedDecodeEndTime(MediaTime::invalidTime())
{
}
};
-PassRef<SourceBuffer> SourceBuffer::create(PassRef<SourceBufferPrivate> sourceBufferPrivate, MediaSource* source)
+Ref<SourceBuffer> SourceBuffer::create(Ref<SourceBufferPrivate>&& sourceBufferPrivate, MediaSource* source)
{
- RefPtr<SourceBuffer> sourceBuffer(adoptRef(new SourceBuffer(std::move(sourceBufferPrivate), source)));
+ auto sourceBuffer = adoptRef(*new SourceBuffer(WTFMove(sourceBufferPrivate), source));
sourceBuffer->suspendIfNeeded();
- return sourceBuffer.releaseNonNull();
+ return sourceBuffer;
}
-SourceBuffer::SourceBuffer(PassRef<SourceBufferPrivate> sourceBufferPrivate, MediaSource* source)
+SourceBuffer::SourceBuffer(Ref<SourceBufferPrivate>&& sourceBufferPrivate, MediaSource* source)
: ActiveDOMObject(source->scriptExecutionContext())
- , m_private(std::move(sourceBufferPrivate))
+ , m_private(WTFMove(sourceBufferPrivate))
, m_source(source)
, m_asyncEventQueue(*this)
- , m_updating(false)
- , m_appendBufferTimer(this, &SourceBuffer::appendBufferTimerFired)
- , m_highestPresentationEndTimestamp(MediaTime::invalidTime())
- , m_receivedFirstInitializationSegment(false)
+ , m_appendBufferTimer(*this, &SourceBuffer::appendBufferTimerFired)
+ , m_appendWindowStart(MediaTime::zeroTime())
+ , m_appendWindowEnd(MediaTime::positiveInfiniteTime())
+ , m_groupStartTimestamp(MediaTime::invalidTime())
+ , m_groupEndTimestamp(MediaTime::zeroTime())
, m_buffered(TimeRanges::create())
- , m_active(false)
, m_appendState(WaitingForSegment)
+ , m_timeOfBufferingMonitor(monotonicallyIncreasingTime())
+ , m_pendingRemoveStart(MediaTime::invalidTime())
+ , m_pendingRemoveEnd(MediaTime::invalidTime())
+ , m_removeTimer(*this, &SourceBuffer::removeTimerFired)
{
- ASSERT(m_private);
ASSERT(m_source);
m_private->setClient(this);
@@ -105,50 +121,37 @@ SourceBuffer::~SourceBuffer()
{
ASSERT(isRemoved());
- m_private->setClient(0);
+ m_private->setClient(nullptr);
}
-PassRefPtr<TimeRanges> SourceBuffer::buffered(ExceptionCode& ec) const
+ExceptionOr<Ref<TimeRanges>> SourceBuffer::buffered() const
{
// Section 3.1 buffered attribute steps.
// https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#attributes-1
// 1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an
// INVALID_STATE_ERR exception and abort these steps.
- if (isRemoved()) {
- ec = INVALID_STATE_ERR;
- return nullptr;
- }
+ if (isRemoved())
+ return Exception { INVALID_STATE_ERR };
// 2. Return a new static normalized TimeRanges object for the media segments buffered.
return m_buffered->copy();
}
-const RefPtr<TimeRanges>& SourceBuffer::buffered() const
-{
- return m_buffered;
-}
-
double SourceBuffer::timestampOffset() const
{
return m_timestampOffset.toDouble();
}
-void SourceBuffer::setTimestampOffset(double offset, ExceptionCode& ec)
+ExceptionOr<void> SourceBuffer::setTimestampOffset(double offset)
{
// Section 3.1 timestampOffset attribute setter steps.
// https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#attributes-1
- // 1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an
+ // 1. Let new timestamp offset equal the new value being assigned to this attribute.
+ // 2. If this object has been removed from the sourceBuffers attribute of the parent media source, then throw an
// INVALID_STATE_ERR exception and abort these steps.
- if (isRemoved()) {
- ec = INVALID_STATE_ERR;
- return;
- }
-
// 3. If the updating attribute equals true, then throw an INVALID_STATE_ERR exception and abort these steps.
- if (m_updating) {
- ec = INVALID_STATE_ERR;
- return;
- }
+ if (isRemoved() || m_updating)
+ return Exception { INVALID_STATE_ERR };
// 4. If the readyState attribute of the parent media source is in the "ended" state then run the following steps:
// 4.1 Set the readyState attribute of the parent media source to "open"
@@ -156,155 +159,277 @@ void SourceBuffer::setTimestampOffset(double offset, ExceptionCode& ec)
m_source->openIfInEndedState();
// 5. If the append state equals PARSING_MEDIA_SEGMENT, then throw an INVALID_STATE_ERR and abort these steps.
- if (m_appendState == ParsingMediaSegment) {
- ec = INVALID_STATE_ERR;
- return;
- }
+ if (m_appendState == ParsingMediaSegment)
+ return Exception { INVALID_STATE_ERR };
+
+ MediaTime newTimestampOffset = MediaTime::createWithDouble(offset);
+
+ // 6. If the mode attribute equals "sequence", then set the group start timestamp to new timestamp offset.
+ if (m_mode == AppendMode::Sequence)
+ m_groupStartTimestamp = newTimestampOffset;
- // 6. Update the attribute to the new value.
- m_timestampOffset = MediaTime::createWithDouble(offset);
+ // 7. Update the attribute to the new value.
+ m_timestampOffset = newTimestampOffset;
+
+ return { };
}
-void SourceBuffer::appendBuffer(PassRefPtr<ArrayBuffer> data, ExceptionCode& ec)
+double SourceBuffer::appendWindowStart() const
{
- // Section 3.2 appendBuffer()
- // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
- // 1. If data is null then throw an INVALID_ACCESS_ERR exception and abort these steps.
- if (!data) {
- ec = INVALID_ACCESS_ERR;
- return;
- }
+ return m_appendWindowStart.toDouble();
+}
- appendBufferInternal(static_cast<unsigned char*>(data->data()), data->byteLength(), ec);
+ExceptionOr<void> SourceBuffer::setAppendWindowStart(double newValue)
+{
+ // Section 3.1 appendWindowStart attribute setter steps.
+ // W3C Editor's Draft 16 September 2016
+ // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#dom-sourcebuffer-appendwindowstart
+ // 1. If this object has been removed from the sourceBuffers attribute of the parent media source,
+ // then throw an InvalidStateError exception and abort these steps.
+ // 2. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
+ if (isRemoved() || m_updating)
+ return Exception { INVALID_STATE_ERR };
+
+ // 3. If the new value is less than 0 or greater than or equal to appendWindowEnd then
+ // throw an TypeError exception and abort these steps.
+ if (newValue < 0 || newValue >= m_appendWindowEnd.toDouble())
+ return Exception { TypeError };
+
+ // 4. Update the attribute to the new value.
+ m_appendWindowStart = MediaTime::createWithDouble(newValue);
+
+ return { };
}
-void SourceBuffer::appendBuffer(PassRefPtr<ArrayBufferView> data, ExceptionCode& ec)
+double SourceBuffer::appendWindowEnd() const
{
- // Section 3.2 appendBuffer()
- // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
- // 1. If data is null then throw an INVALID_ACCESS_ERR exception and abort these steps.
- if (!data) {
- ec = INVALID_ACCESS_ERR;
- return;
+ return m_appendWindowEnd.toDouble();
+}
+
+ExceptionOr<void> SourceBuffer::setAppendWindowEnd(double newValue)
+{
+ // Section 3.1 appendWindowEnd attribute setter steps.
+ // W3C Editor's Draft 16 September 2016
+ // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#dom-sourcebuffer-appendwindowend
+ // 1. If this object has been removed from the sourceBuffers attribute of the parent media source,
+ // then throw an InvalidStateError exception and abort these steps.
+ // 2. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
+ if (isRemoved() || m_updating)
+ return Exception { INVALID_STATE_ERR };
+
+ // 3. If the new value equals NaN, then throw an TypeError and abort these steps.
+ // 4. If the new value is less than or equal to appendWindowStart then throw an TypeError exception
+ // and abort these steps.
+ if (std::isnan(newValue) || newValue <= m_appendWindowStart.toDouble())
+ return Exception { TypeError };
+
+ // 5.. Update the attribute to the new value.
+ m_appendWindowEnd = MediaTime::createWithDouble(newValue);
+
+ return { };
+}
+
+ExceptionOr<void> SourceBuffer::appendBuffer(const BufferSource& data)
+{
+ return appendBufferInternal(static_cast<const unsigned char*>(data.data()), data.length());
+}
+
+void SourceBuffer::resetParserState()
+{
+ // Section 3.5.2 Reset Parser State algorithm steps.
+ // http://www.w3.org/TR/2014/CR-media-source-20140717/#sourcebuffer-reset-parser-state
+ // 1. If the append state equals PARSING_MEDIA_SEGMENT and the input buffer contains some complete coded frames,
+ // then run the coded frame processing algorithm until all of these complete coded frames have been processed.
+ // FIXME: If any implementation will work in pulling mode (instead of async push to SourceBufferPrivate, and forget)
+ // this should be handled somehow either here, or in m_private->abort();
+
+ // 2. Unset the last decode timestamp on all track buffers.
+ // 3. Unset the last frame duration on all track buffers.
+ // 4. Unset the highest presentation timestamp on all track buffers.
+ // 5. Set the need random access point flag on all track buffers to true.
+ for (auto& trackBufferPair : m_trackBufferMap.values()) {
+ trackBufferPair.lastDecodeTimestamp = MediaTime::invalidTime();
+ trackBufferPair.lastFrameDuration = MediaTime::invalidTime();
+ trackBufferPair.highestPresentationTimestamp = MediaTime::invalidTime();
+ trackBufferPair.needRandomAccessFlag = true;
}
+ // 6. Remove all bytes from the input buffer.
+ // Note: this is handled by abortIfUpdating()
+ // 7. Set append state to WAITING_FOR_SEGMENT.
+ m_appendState = WaitingForSegment;
- appendBufferInternal(static_cast<unsigned char*>(data->baseAddress()), data->byteLength(), ec);
+ m_private->resetParserState();
}
-void SourceBuffer::abort(ExceptionCode& ec)
+ExceptionOr<void> SourceBuffer::abort()
{
// Section 3.2 abort() method steps.
- // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-abort-void
+ // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#dom-sourcebuffer-abort
// 1. If this object has been removed from the sourceBuffers attribute of the parent media source
// then throw an INVALID_STATE_ERR exception and abort these steps.
// 2. If the readyState attribute of the parent media source is not in the "open" state
// then throw an INVALID_STATE_ERR exception and abort these steps.
- if (isRemoved() || !m_source->isOpen()) {
- ec = INVALID_STATE_ERR;
- return;
- }
+ if (isRemoved() || !m_source->isOpen())
+ return Exception { INVALID_STATE_ERR };
- // 3. If the sourceBuffer.updating attribute equals true, then run the following steps: ...
+ // 3. If the range removal algorithm is running, then throw an InvalidStateError exception and abort these steps.
+ if (m_removeTimer.isActive())
+ return Exception { INVALID_STATE_ERR };
+
+ // 4. If the sourceBuffer.updating attribute equals true, then run the following steps: ...
abortIfUpdating();
- // 4. Run the reset parser state algorithm.
- m_private->abort();
+ // 5. Run the reset parser state algorithm.
+ resetParserState();
+
+ // 6. Set appendWindowStart to the presentation start time.
+ m_appendWindowStart = MediaTime::zeroTime();
+
+ // 7. Set appendWindowEnd to positive Infinity.
+ m_appendWindowEnd = MediaTime::positiveInfiniteTime();
+
+ return { };
+}
+
+ExceptionOr<void> SourceBuffer::remove(double start, double end)
+{
+ return remove(MediaTime::createWithDouble(start), MediaTime::createWithDouble(end));
+}
+
+ExceptionOr<void> SourceBuffer::remove(const MediaTime& start, const MediaTime& end)
+{
+ LOG(MediaSource, "SourceBuffer::remove(%p) - start(%lf), end(%lf)", this, start.toDouble(), end.toDouble());
+
+ // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#dom-sourcebuffer-remove
+ // Section 3.2 remove() method steps.
+ // 1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw
+ // an InvalidStateError exception and abort these steps.
+ // 2. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
+ if (isRemoved() || m_updating)
+ return Exception { INVALID_STATE_ERR };
+
+ // 3. If duration equals NaN, then throw a TypeError exception and abort these steps.
+ // 4. If start is negative or greater than duration, then throw a TypeError exception and abort these steps.
+ // 5. If end is less than or equal to start or end equals NaN, then throw a TypeError exception and abort these steps.
+ if (m_source->duration().isInvalid()
+ || end.isInvalid()
+ || start.isInvalid()
+ || start < MediaTime::zeroTime()
+ || start > m_source->duration()
+ || end <= start) {
+ return Exception { TypeError };
+ }
- // FIXME(229408) Add steps 5-6 update appendWindowStart & appendWindowEnd.
+ // 6. If the readyState attribute of the parent media source is in the "ended" state then run the following steps:
+ // 6.1. Set the readyState attribute of the parent media source to "open"
+ // 6.2. Queue a task to fire a simple event named sourceopen at the parent media source .
+ m_source->openIfInEndedState();
+
+ // 7. Run the range removal algorithm with start and end as the start and end of the removal range.
+ rangeRemoval(start, end);
+
+ return { };
}
+void SourceBuffer::rangeRemoval(const MediaTime& start, const MediaTime& end)
+{
+ // 3.5.7 Range Removal
+ // https://rawgit.com/w3c/media-source/7bbe4aa33c61ec025bc7acbd80354110f6a000f9/media-source.html#sourcebuffer-range-removal
+ // 1. Let start equal the starting presentation timestamp for the removal range.
+ // 2. Let end equal the end presentation timestamp for the removal range.
+ // 3. Set the updating attribute to true.
+ m_updating = true;
+
+ // 4. Queue a task to fire a simple event named updatestart at this SourceBuffer object.
+ scheduleEvent(eventNames().updatestartEvent);
+
+ // 5. Return control to the caller and run the rest of the steps asynchronously.
+ m_pendingRemoveStart = start;
+ m_pendingRemoveEnd = end;
+ m_removeTimer.startOneShot(0);
+}
void SourceBuffer::abortIfUpdating()
{
- // Section 3.2 abort() method step 3 substeps.
- // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-abort-void
+ // Section 3.2 abort() method step 4 substeps.
+ // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#dom-sourcebuffer-abort
if (!m_updating)
return;
- // 3.1. Abort the buffer append and stream append loop algorithms if they are running.
+ // 4.1. Abort the buffer append algorithm if it is running.
m_appendBufferTimer.stop();
m_pendingAppendData.clear();
+ m_private->abort();
- // 3.2. Set the updating attribute to false.
+ // 4.2. Set the updating attribute to false.
m_updating = false;
- // 3.3. Queue a task to fire a simple event named abort at this SourceBuffer object.
+ // 4.3. Queue a task to fire a simple event named abort at this SourceBuffer object.
scheduleEvent(eventNames().abortEvent);
- // 3.4. Queue a task to fire a simple event named updateend at this SourceBuffer object.
+ // 4.4. Queue a task to fire a simple event named updateend at this SourceBuffer object.
scheduleEvent(eventNames().updateendEvent);
}
-void SourceBuffer::removedFromMediaSource()
+MediaTime SourceBuffer::highestPresentationTimestamp() const
{
- if (isRemoved())
- return;
-
- m_private->removedFromMediaSource();
- m_source = 0;
- m_asyncEventQueue.close();
+ MediaTime highestTime;
+ for (auto& trackBuffer : m_trackBufferMap.values()) {
+ auto lastSampleIter = trackBuffer.samples.presentationOrder().rbegin();
+ if (lastSampleIter == trackBuffer.samples.presentationOrder().rend())
+ continue;
+ highestTime = std::max(highestTime, lastSampleIter->first);
+ }
+ return highestTime;
}
-void SourceBuffer::sourceBufferPrivateSeekToTime(SourceBufferPrivate*, const MediaTime& time)
+void SourceBuffer::readyStateChanged()
{
- LOG(Media, "SourceBuffer::sourceBufferPrivateSeekToTime(%p)", this);
-
- for (auto trackBufferIterator = m_trackBufferMap.begin(); trackBufferIterator != m_trackBufferMap.end(); ++trackBufferIterator) {
- TrackBuffer& trackBuffer = trackBufferIterator->value;
- AtomicString trackID = trackBufferIterator->key;
-
- // Find the sample which contains the current presentation time.
- auto currentSamplePTSIterator = trackBuffer.samples.findSampleContainingPresentationTime(time);
+ updateBufferedFromTrackBuffers();
+}
- if (currentSamplePTSIterator == trackBuffer.samples.presentationEnd()) {
- trackBuffer.decodeQueue.clear();
- m_private->flushAndEnqueueNonDisplayingSamples(Vector<RefPtr<MediaSample>>(), trackID);
- continue;
- }
+void SourceBuffer::removedFromMediaSource()
+{
+ if (isRemoved())
+ return;
- // Seach backward for the previous sync sample.
- MediaTime currentSampleDecodeTime = currentSamplePTSIterator->second->decodeTime();
- auto currentSampleDTSIterator = trackBuffer.samples.findSampleWithDecodeTime(currentSampleDecodeTime);
- ASSERT(currentSampleDTSIterator != trackBuffer.samples.decodeEnd());
+ abortIfUpdating();
- auto reverseCurrentSampleIter = --SampleMap::reverse_iterator(currentSampleDTSIterator);
- auto reverseLastSyncSampleIter = trackBuffer.samples.findSyncSamplePriorToDecodeIterator(reverseCurrentSampleIter);
- if (reverseLastSyncSampleIter == trackBuffer.samples.reverseDecodeEnd()) {
- trackBuffer.decodeQueue.clear();
- m_private->flushAndEnqueueNonDisplayingSamples(Vector<RefPtr<MediaSample>>(), trackID);
- continue;
- }
+ for (auto& trackBufferPair : m_trackBufferMap.values()) {
+ trackBufferPair.samples.clear();
+ trackBufferPair.decodeQueue.clear();
+ }
- Vector<RefPtr<MediaSample>> nonDisplayingSamples;
- for (auto iter = reverseLastSyncSampleIter; iter != reverseCurrentSampleIter; --iter)
- nonDisplayingSamples.append(iter->second);
+ m_private->removedFromMediaSource();
+ m_source = nullptr;
+}
- m_private->flushAndEnqueueNonDisplayingSamples(nonDisplayingSamples, trackID);
+void SourceBuffer::seekToTime(const MediaTime& time)
+{
+ LOG(MediaSource, "SourceBuffer::seekToTime(%p) - time(%s)", this, toString(time).utf8().data());
- // Fill the decode queue with the remaining samples.
- trackBuffer.decodeQueue.clear();
- for (auto iter = currentSampleDTSIterator; iter != trackBuffer.samples.decodeEnd(); ++iter)
- trackBuffer.decodeQueue.insert(*iter);
+ for (auto& trackBufferPair : m_trackBufferMap) {
+ TrackBuffer& trackBuffer = trackBufferPair.value;
+ const AtomicString& trackID = trackBufferPair.key;
- provideMediaData(trackBuffer, trackID);
+ trackBuffer.needsReenqueueing = true;
+ reenqueueMediaForTime(trackBuffer, trackID, time);
}
}
-MediaTime SourceBuffer::sourceBufferPrivateFastSeekTimeForMediaTime(SourceBufferPrivate*, const MediaTime& targetTime, const MediaTime& negativeThreshold, const MediaTime& positiveThreshold)
+MediaTime SourceBuffer::sourceBufferPrivateFastSeekTimeForMediaTime(const MediaTime& targetTime, const MediaTime& negativeThreshold, const MediaTime& positiveThreshold)
{
MediaTime seekTime = targetTime;
MediaTime lowerBoundTime = targetTime - negativeThreshold;
MediaTime upperBoundTime = targetTime + positiveThreshold;
- for (auto trackBufferIterator = m_trackBufferMap.begin(); trackBufferIterator != m_trackBufferMap.end(); ++trackBufferIterator) {
- TrackBuffer& trackBuffer = trackBufferIterator->value;
-
+ for (auto& trackBuffer : m_trackBufferMap.values()) {
// Find the sample which contains the target time time.
- auto futureSyncSampleIterator = trackBuffer.samples.findSyncSampleAfterPresentationTime(targetTime, positiveThreshold);
- auto pastSyncSampleIterator = trackBuffer.samples.findSyncSamplePriorToPresentationTime(targetTime, negativeThreshold);
- auto upperBound = trackBuffer.samples.decodeEnd();
- auto lowerBound = trackBuffer.samples.reverseDecodeEnd();
+ auto futureSyncSampleIterator = trackBuffer.samples.decodeOrder().findSyncSampleAfterPresentationTime(targetTime, positiveThreshold);
+ auto pastSyncSampleIterator = trackBuffer.samples.decodeOrder().findSyncSamplePriorToPresentationTime(targetTime, negativeThreshold);
+ auto upperBound = trackBuffer.samples.decodeOrder().end();
+ auto lowerBound = trackBuffer.samples.decodeOrder().rend();
if (futureSyncSampleIterator == upperBound && pastSyncSampleIterator == lowerBound)
continue;
@@ -331,12 +456,23 @@ MediaTime SourceBuffer::sourceBufferPrivateFastSeekTimeForMediaTime(SourceBuffer
bool SourceBuffer::hasPendingActivity() const
{
- return m_source;
+ return m_source || m_asyncEventQueue.hasPendingEvents();
}
void SourceBuffer::stop()
{
m_appendBufferTimer.stop();
+ m_removeTimer.stop();
+}
+
+bool SourceBuffer::canSuspendForDocumentSuspension() const
+{
+ return !hasPendingActivity();
+}
+
+const char* SourceBuffer::activeDOMObjectName() const
+{
+ return "SourceBuffer";
}
bool SourceBuffer::isRemoved() const
@@ -346,13 +482,13 @@ bool SourceBuffer::isRemoved() const
void SourceBuffer::scheduleEvent(const AtomicString& eventName)
{
- RefPtr<Event> event = Event::create(eventName, false, false);
+ auto event = Event::create(eventName, false, false);
event->setTarget(this);
- m_asyncEventQueue.enqueueEvent(event.release());
+ m_asyncEventQueue.enqueueEvent(WTFMove(event));
}
-void SourceBuffer::appendBufferInternal(unsigned char* data, unsigned size, ExceptionCode& ec)
+ExceptionOr<void> SourceBuffer::appendBufferInternal(const unsigned char* data, unsigned size)
{
// Section 3.2 appendBuffer()
// https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
@@ -364,10 +500,8 @@ void SourceBuffer::appendBufferInternal(unsigned char* data, unsigned size, Exce
// 1. If the SourceBuffer has been removed from the sourceBuffers attribute of the parent media source
// then throw an INVALID_STATE_ERR exception and abort these steps.
// 2. If the updating attribute equals true, then throw an INVALID_STATE_ERR exception and abort these steps.
- if (isRemoved() || m_updating) {
- ec = INVALID_STATE_ERR;
- return;
- }
+ if (isRemoved() || m_updating)
+ return Exception { INVALID_STATE_ERR };
// 3. If the readyState attribute of the parent media source is in the "ended" state then run the following steps:
// 3.1. Set the readyState attribute of the parent media source to "open"
@@ -375,13 +509,16 @@ void SourceBuffer::appendBufferInternal(unsigned char* data, unsigned size, Exce
m_source->openIfInEndedState();
// 4. Run the coded frame eviction algorithm.
- m_private->evictCodedFrames();
+ evictCodedFrames(size);
+ // FIXME: enable this code when MSE libraries have been updated to support it.
+#if USE(GSTREAMER)
// 5. If the buffer full flag equals true, then throw a QUOTA_EXCEEDED_ERR exception and abort these step.
- if (m_private->isFull()) {
- ec = QUOTA_EXCEEDED_ERR;
- return;
+ if (m_bufferFull) {
+ LOG(MediaSource, "SourceBuffer::appendBufferInternal(%p) - buffer full, failing with QUOTA_EXCEEDED_ERR error", this);
+ return Exception { QUOTA_EXCEEDED_ERR };
}
+#endif
// NOTE: Return to 3.2 appendBuffer()
// 3. Add data to the end of the input buffer.
@@ -395,10 +532,17 @@ void SourceBuffer::appendBufferInternal(unsigned char* data, unsigned size, Exce
// 6. Asynchronously run the buffer append algorithm.
m_appendBufferTimer.startOneShot(0);
+
+ reportExtraMemoryAllocated();
+
+ return { };
}
-void SourceBuffer::appendBufferTimerFired(Timer<SourceBuffer>&)
+void SourceBuffer::appendBufferTimerFired()
{
+ if (isRemoved())
+ return;
+
ASSERT(m_updating);
// Section 3.5.5 Buffer Append Algorithm
@@ -417,31 +561,44 @@ void SourceBuffer::appendBufferTimerFired(Timer<SourceBuffer>&)
// https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#sourcebuffer-segment-parser-loop
// When the segment parser loop algorithm is invoked, run the following steps:
- SourceBufferPrivate::AppendResult result = SourceBufferPrivate::AppendSucceeded;
- do {
- // 1. Loop Top: If the input buffer is empty, then jump to the need more data step below.
- if (!m_pendingAppendData.size())
- break;
+ // 1. Loop Top: If the input buffer is empty, then jump to the need more data step below.
+ if (!m_pendingAppendData.size()) {
+ sourceBufferPrivateAppendComplete(AppendSucceeded);
+ return;
+ }
- result = m_private->append(m_pendingAppendData.data(), appendSize);
- m_pendingAppendData.clear();
+ m_private->append(m_pendingAppendData.data(), appendSize);
+ m_pendingAppendData.clear();
+}
- // 2. If the input buffer contains bytes that violate the SourceBuffer byte stream format specification,
- // then run the end of stream algorithm with the error parameter set to "decode" and abort this algorithm.
- if (result == SourceBufferPrivate::ParsingFailed) {
- m_source->streamEndedWithError(decodeError(), IgnorableExceptionCode());
- break;
- }
+void SourceBuffer::sourceBufferPrivateAppendComplete(AppendResult result)
+{
+ if (isRemoved())
+ return;
- // NOTE: Steps 3 - 6 enforced by sourceBufferPrivateDidReceiveInitializationSegment() and
- // sourceBufferPrivateDidReceiveSample below.
+ // Resolve the changes it TrackBuffers' buffered ranges
+ // into the SourceBuffer's buffered ranges
+ updateBufferedFromTrackBuffers();
- // 7. Need more data: Return control to the calling algorithm.
- } while (0);
+ // Section 3.5.5 Buffer Append Algorithm, ctd.
+ // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-buffer-append
+
+ // 2. If the input buffer contains bytes that violate the SourceBuffer byte stream format specification,
+ // then run the append error algorithm with the decode error parameter set to true and abort this algorithm.
+ if (result == ParsingFailed) {
+ LOG(MediaSource, "SourceBuffer::sourceBufferPrivateAppendComplete(%p) - result = ParsingFailed", this);
+ appendError(true);
+ return;
+ }
+
+ // NOTE: Steps 3 - 6 enforced by sourceBufferPrivateDidReceiveInitializationSegment() and
+ // sourceBufferPrivateDidReceiveSample below.
+
+ // 7. Need more data: Return control to the calling algorithm.
// NOTE: return to Section 3.5.5
// 2.If the segment parser loop algorithm in the previous step was aborted, then abort this algorithm.
- if (result != SourceBufferPrivate::AppendSucceeded)
+ if (result != AppendSucceeded)
return;
// 3. Set the updating attribute to false.
@@ -453,54 +610,364 @@ void SourceBuffer::appendBufferTimerFired(Timer<SourceBuffer>&)
// 5. Queue a task to fire a simple event named updateend at this SourceBuffer object.
scheduleEvent(eventNames().updateendEvent);
- m_source->monitorSourceBuffers();
- for (auto iter = m_trackBufferMap.begin(), end = m_trackBufferMap.end(); iter != end; ++iter)
- provideMediaData(iter->value, iter->key);
+ if (m_source)
+ m_source->monitorSourceBuffers();
+
+ MediaTime currentMediaTime = m_source->currentTime();
+ for (auto& trackBufferPair : m_trackBufferMap) {
+ TrackBuffer& trackBuffer = trackBufferPair.value;
+ const AtomicString& trackID = trackBufferPair.key;
+
+ if (trackBuffer.needsReenqueueing) {
+ LOG(MediaSource, "SourceBuffer::sourceBufferPrivateAppendComplete(%p) - reenqueuing at time (%s)", this, toString(currentMediaTime).utf8().data());
+ reenqueueMediaForTime(trackBuffer, trackID, currentMediaTime);
+ } else
+ provideMediaData(trackBuffer, trackID);
+ }
+
+ reportExtraMemoryAllocated();
+ if (extraMemoryCost() > this->maximumBufferSize())
+ m_bufferFull = true;
+
+ LOG(Media, "SourceBuffer::sourceBufferPrivateAppendComplete(%p) - buffered = %s", this, toString(m_buffered->ranges()).utf8().data());
}
-const AtomicString& SourceBuffer::decodeError()
+void SourceBuffer::sourceBufferPrivateDidReceiveRenderingError(int error)
{
- static NeverDestroyed<AtomicString> decode("decode", AtomicString::ConstructFromLiteral);
- return decode;
+#if LOG_DISABLED
+ UNUSED_PARAM(error);
+#endif
+
+ LOG(MediaSource, "SourceBuffer::sourceBufferPrivateDidReceiveRenderingError(%p) - result = %i", this, error);
+
+ if (!isRemoved())
+ m_source->streamEndedWithError(MediaSource::EndOfStreamError::Decode);
}
-const AtomicString& SourceBuffer::networkError()
+static bool decodeTimeComparator(const PresentationOrderSampleMap::MapType::value_type& a, const PresentationOrderSampleMap::MapType::value_type& b)
{
- static NeverDestroyed<AtomicString> network("network", AtomicString::ConstructFromLiteral);
- return network;
+ return a.second->decodeTime() < b.second->decodeTime();
}
-VideoTrackList* SourceBuffer::videoTracks()
+static PlatformTimeRanges removeSamplesFromTrackBuffer(const DecodeOrderSampleMap::MapType& samples, SourceBuffer::TrackBuffer& trackBuffer, const SourceBuffer* buffer, const char* logPrefix)
{
- if (!m_source->mediaElement())
- return nullptr;
+#if !LOG_DISABLED
+ MediaTime earliestSample = MediaTime::positiveInfiniteTime();
+ MediaTime latestSample = MediaTime::zeroTime();
+ size_t bytesRemoved = 0;
+#else
+ UNUSED_PARAM(logPrefix);
+ UNUSED_PARAM(buffer);
+#endif
- if (!m_videoTracks)
- m_videoTracks = VideoTrackList::create(m_source->mediaElement(), ActiveDOMObject::scriptExecutionContext());
+ PlatformTimeRanges erasedRanges;
+ for (auto sampleIt : samples) {
+ const DecodeOrderSampleMap::KeyType& decodeKey = sampleIt.first;
+#if !LOG_DISABLED
+ size_t startBufferSize = trackBuffer.samples.sizeInBytes();
+#endif
+
+ RefPtr<MediaSample>& sample = sampleIt.second;
+ LOG(MediaSource, "SourceBuffer::%s(%p) - removing sample(%s)", logPrefix, buffer, toString(*sampleIt.second).utf8().data());
+
+ // Remove the erased samples from the TrackBuffer sample map.
+ trackBuffer.samples.removeSample(sample.get());
+
+ // Also remove the erased samples from the TrackBuffer decodeQueue.
+ trackBuffer.decodeQueue.erase(decodeKey);
+
+ auto startTime = sample->presentationTime();
+ auto endTime = startTime + sample->duration();
+ erasedRanges.add(startTime, endTime);
+
+#if !LOG_DISABLED
+ bytesRemoved += startBufferSize - trackBuffer.samples.sizeInBytes();
+ if (startTime < earliestSample)
+ earliestSample = startTime;
+ if (endTime > latestSample)
+ latestSample = endTime;
+#endif
+ }
+
+ // Because we may have added artificial padding in the buffered ranges when adding samples, we may
+ // need to remove that padding when removing those same samples. Walk over the erased ranges looking
+ // for unbuffered areas and expand erasedRanges to encompass those areas.
+ PlatformTimeRanges additionalErasedRanges;
+ for (unsigned i = 0; i < erasedRanges.length(); ++i) {
+ auto erasedStart = erasedRanges.start(i);
+ auto erasedEnd = erasedRanges.end(i);
+ auto startIterator = trackBuffer.samples.presentationOrder().reverseFindSampleBeforePresentationTime(erasedStart);
+ if (startIterator == trackBuffer.samples.presentationOrder().rend())
+ additionalErasedRanges.add(MediaTime::zeroTime(), erasedStart);
+ else {
+ auto& previousSample = *startIterator->second;
+ if (previousSample.presentationTime() + previousSample.duration() < erasedStart)
+ additionalErasedRanges.add(previousSample.presentationTime() + previousSample.duration(), erasedStart);
+ }
+
+ auto endIterator = trackBuffer.samples.presentationOrder().findSampleStartingOnOrAfterPresentationTime(erasedEnd);
+ if (endIterator == trackBuffer.samples.presentationOrder().end())
+ additionalErasedRanges.add(erasedEnd, MediaTime::positiveInfiniteTime());
+ else {
+ auto& nextSample = *endIterator->second;
+ if (nextSample.presentationTime() > erasedEnd)
+ additionalErasedRanges.add(erasedEnd, nextSample.presentationTime());
+ }
+ }
+ if (additionalErasedRanges.length())
+ erasedRanges.unionWith(additionalErasedRanges);
+
+#if !LOG_DISABLED
+ if (bytesRemoved)
+ LOG(MediaSource, "SourceBuffer::%s(%p) removed %zu bytes, start(%lf), end(%lf)", logPrefix, buffer, bytesRemoved, earliestSample.toDouble(), latestSample.toDouble());
+#endif
+
+ return erasedRanges;
+}
+
+void SourceBuffer::removeCodedFrames(const MediaTime& start, const MediaTime& end)
+{
+ LOG(MediaSource, "SourceBuffer::removeCodedFrames(%p) - start(%s), end(%s)", this, toString(start).utf8().data(), toString(end).utf8().data());
+
+ // 3.5.9 Coded Frame Removal Algorithm
+ // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#sourcebuffer-coded-frame-removal
+
+ // 1. Let start be the starting presentation timestamp for the removal range.
+ MediaTime durationMediaTime = m_source->duration();
+ MediaTime currentMediaTime = m_source->currentTime();
+
+ // 2. Let end be the end presentation timestamp for the removal range.
+ // 3. For each track buffer in this source buffer, run the following steps:
+ for (auto& trackBuffer : m_trackBufferMap.values()) {
+ // 3.1. Let remove end timestamp be the current value of duration
+ // 3.2 If this track buffer has a random access point timestamp that is greater than or equal to end, then update
+ // remove end timestamp to that random access point timestamp.
+
+ // NOTE: To handle MediaSamples which may be an amalgamation of multiple shorter samples, find samples whose presentation
+ // interval straddles the start and end times, and divide them if possible:
+ auto divideSampleIfPossibleAtPresentationTime = [&] (const MediaTime& time) {
+ auto sampleIterator = trackBuffer.samples.presentationOrder().findSampleContainingPresentationTime(time);
+ if (sampleIterator == trackBuffer.samples.presentationOrder().end())
+ return;
+ RefPtr<MediaSample> sample = sampleIterator->second;
+ if (!sample->isDivisable())
+ return;
+ std::pair<RefPtr<MediaSample>, RefPtr<MediaSample>> replacementSamples = sample->divide(time);
+ if (!replacementSamples.first || !replacementSamples.second)
+ return;
+ LOG(MediaSource, "SourceBuffer::removeCodedFrames(%p) - splitting sample (%s) into\n\t(%s)\n\t(%s)", this,
+ toString(sample).utf8().data(),
+ toString(replacementSamples.first).utf8().data(),
+ toString(replacementSamples.second).utf8().data());
+ trackBuffer.samples.removeSample(sample.get());
+ trackBuffer.samples.addSample(*replacementSamples.first);
+ trackBuffer.samples.addSample(*replacementSamples.second);
+ };
+ divideSampleIfPossibleAtPresentationTime(start);
+ divideSampleIfPossibleAtPresentationTime(end);
+
+ // NOTE: findSyncSampleAfterPresentationTime will return the next sync sample on or after the presentation time
+ // or decodeOrder().end() if no sync sample exists after that presentation time.
+ DecodeOrderSampleMap::iterator removeDecodeEnd = trackBuffer.samples.decodeOrder().findSyncSampleAfterPresentationTime(end);
+ PresentationOrderSampleMap::iterator removePresentationEnd;
+ if (removeDecodeEnd == trackBuffer.samples.decodeOrder().end())
+ removePresentationEnd = trackBuffer.samples.presentationOrder().end();
+ else
+ removePresentationEnd = trackBuffer.samples.presentationOrder().findSampleWithPresentationTime(removeDecodeEnd->second->presentationTime());
+
+ PresentationOrderSampleMap::iterator removePresentationStart = trackBuffer.samples.presentationOrder().findSampleStartingOnOrAfterPresentationTime(start);
+ if (removePresentationStart == removePresentationEnd)
+ continue;
+
+ // 3.3 Remove all media data, from this track buffer, that contain starting timestamps greater than or equal to
+ // start and less than the remove end timestamp.
+ // NOTE: frames must be removed in decode order, so that all dependant frames between the frame to be removed
+ // and the next sync sample frame are removed. But we must start from the first sample in decode order, not
+ // presentation order.
+ PresentationOrderSampleMap::iterator minDecodeTimeIter = std::min_element(removePresentationStart, removePresentationEnd, decodeTimeComparator);
+ DecodeOrderSampleMap::KeyType decodeKey(minDecodeTimeIter->second->decodeTime(), minDecodeTimeIter->second->presentationTime());
+ DecodeOrderSampleMap::iterator removeDecodeStart = trackBuffer.samples.decodeOrder().findSampleWithDecodeKey(decodeKey);
+
+ DecodeOrderSampleMap::MapType erasedSamples(removeDecodeStart, removeDecodeEnd);
+ PlatformTimeRanges erasedRanges = removeSamplesFromTrackBuffer(erasedSamples, trackBuffer, this, "removeCodedFrames");
+
+ // Only force the TrackBuffer to re-enqueue if the removed ranges overlap with enqueued and possibly
+ // not yet displayed samples.
+ if (trackBuffer.lastEnqueuedPresentationTime.isValid() && currentMediaTime < trackBuffer.lastEnqueuedPresentationTime) {
+ PlatformTimeRanges possiblyEnqueuedRanges(currentMediaTime, trackBuffer.lastEnqueuedPresentationTime);
+ possiblyEnqueuedRanges.intersectWith(erasedRanges);
+ if (possiblyEnqueuedRanges.length())
+ trackBuffer.needsReenqueueing = true;
+ }
+
+ erasedRanges.invert();
+ trackBuffer.buffered.intersectWith(erasedRanges);
+ setBufferedDirty(true);
+
+ // 3.4 If this object is in activeSourceBuffers, the current playback position is greater than or equal to start
+ // and less than the remove end timestamp, and HTMLMediaElement.readyState is greater than HAVE_METADATA, then set
+ // the HTMLMediaElement.readyState attribute to HAVE_METADATA and stall playback.
+ if (m_active && currentMediaTime >= start && currentMediaTime < end && m_private->readyState() > MediaPlayer::HaveMetadata)
+ m_private->setReadyState(MediaPlayer::HaveMetadata);
+ }
+
+ updateBufferedFromTrackBuffers();
+
+ // 4. If buffer full flag equals true and this object is ready to accept more bytes, then set the buffer full flag to false.
+ // No-op
- return m_videoTracks.get();
+ LOG(Media, "SourceBuffer::removeCodedFrames(%p) - buffered = %s", this, toString(m_buffered->ranges()).utf8().data());
}
-AudioTrackList* SourceBuffer::audioTracks()
+void SourceBuffer::removeTimerFired()
{
- if (!m_source->mediaElement())
- return nullptr;
+ if (isRemoved())
+ return;
- if (!m_audioTracks)
- m_audioTracks = AudioTrackList::create(m_source->mediaElement(), ActiveDOMObject::scriptExecutionContext());
+ ASSERT(m_updating);
+ ASSERT(m_pendingRemoveStart.isValid());
+ ASSERT(m_pendingRemoveStart < m_pendingRemoveEnd);
+
+ // Section 3.5.7 Range Removal
+ // http://w3c.github.io/media-source/#sourcebuffer-range-removal
+
+ // 6. Run the coded frame removal algorithm with start and end as the start and end of the removal range.
+ removeCodedFrames(m_pendingRemoveStart, m_pendingRemoveEnd);
- return m_audioTracks.get();
+ // 7. Set the updating attribute to false.
+ m_updating = false;
+ m_pendingRemoveStart = MediaTime::invalidTime();
+ m_pendingRemoveEnd = MediaTime::invalidTime();
+
+ // 8. Queue a task to fire a simple event named update at this SourceBuffer object.
+ scheduleEvent(eventNames().updateEvent);
+
+ // 9. Queue a task to fire a simple event named updateend at this SourceBuffer object.
+ scheduleEvent(eventNames().updateendEvent);
}
-TextTrackList* SourceBuffer::textTracks()
+void SourceBuffer::evictCodedFrames(size_t newDataSize)
{
- if (!m_source->mediaElement())
- return nullptr;
+ // 3.5.13 Coded Frame Eviction Algorithm
+ // http://www.w3.org/TR/media-source/#sourcebuffer-coded-frame-eviction
- if (!m_textTracks)
- m_textTracks = TextTrackList::create(m_source->mediaElement(), ActiveDOMObject::scriptExecutionContext());
+ if (isRemoved())
+ return;
+
+ // This algorithm is run to free up space in this source buffer when new data is appended.
+ // 1. Let new data equal the data that is about to be appended to this SourceBuffer.
+ // 2. If the buffer full flag equals false, then abort these steps.
+ if (!m_bufferFull)
+ return;
+
+ size_t maximumBufferSize = this->maximumBufferSize();
+
+ // 3. Let removal ranges equal a list of presentation time ranges that can be evicted from
+ // the presentation to make room for the new data.
+
+ // NOTE: begin by removing data from the beginning of the buffered ranges, 30 seconds at
+ // a time, up to 30 seconds before currentTime.
+ MediaTime thirtySeconds = MediaTime(30, 1);
+ MediaTime currentTime = m_source->currentTime();
+ MediaTime maximumRangeEnd = currentTime - thirtySeconds;
+
+#if !LOG_DISABLED
+ LOG(MediaSource, "SourceBuffer::evictCodedFrames(%p) - currentTime = %lf, require %zu bytes, maximum buffer size is %zu", this, m_source->currentTime().toDouble(), extraMemoryCost() + newDataSize, maximumBufferSize);
+ size_t initialBufferedSize = extraMemoryCost();
+#endif
+
+ MediaTime rangeStart = MediaTime::zeroTime();
+ MediaTime rangeEnd = rangeStart + thirtySeconds;
+ while (rangeStart < maximumRangeEnd) {
+ // 4. For each range in removal ranges, run the coded frame removal algorithm with start and
+ // end equal to the removal range start and end timestamp respectively.
+ removeCodedFrames(rangeStart, std::min(rangeEnd, maximumRangeEnd));
+ if (extraMemoryCost() + newDataSize < maximumBufferSize) {
+ m_bufferFull = false;
+ break;
+ }
+
+ rangeStart += thirtySeconds;
+ rangeEnd += thirtySeconds;
+ }
+
+ if (!m_bufferFull) {
+ LOG(MediaSource, "SourceBuffer::evictCodedFrames(%p) - evicted %zu bytes", this, initialBufferedSize - extraMemoryCost());
+ return;
+ }
+
+ // If there still isn't enough free space and there buffers in time ranges after the current range (ie. there is a gap after
+ // the current buffered range), delete 30 seconds at a time from duration back to the current time range or 30 seconds after
+ // currenTime whichever we hit first.
+ auto buffered = m_buffered->ranges();
+ size_t currentTimeRange = buffered.find(currentTime);
+ if (currentTimeRange == notFound || currentTimeRange == buffered.length() - 1) {
+ LOG(MediaSource, "SourceBuffer::evictCodedFrames(%p) - evicted %zu bytes but FAILED to free enough", this, initialBufferedSize - extraMemoryCost());
+ return;
+ }
+
+ MediaTime minimumRangeStart = currentTime + thirtySeconds;
+
+ rangeEnd = m_source->duration();
+ rangeStart = rangeEnd - thirtySeconds;
+ while (rangeStart > minimumRangeStart) {
+
+ // Do not evict data from the time range that contains currentTime.
+ size_t startTimeRange = buffered.find(rangeStart);
+ if (startTimeRange == currentTimeRange) {
+ size_t endTimeRange = buffered.find(rangeEnd);
+ if (endTimeRange == currentTimeRange)
+ break;
- return m_textTracks.get();
+ rangeEnd = buffered.start(endTimeRange);
+ }
+
+ // 4. For each range in removal ranges, run the coded frame removal algorithm with start and
+ // end equal to the removal range start and end timestamp respectively.
+ removeCodedFrames(std::max(minimumRangeStart, rangeStart), rangeEnd);
+ if (extraMemoryCost() + newDataSize < maximumBufferSize) {
+ m_bufferFull = false;
+ break;
+ }
+
+ rangeStart -= thirtySeconds;
+ rangeEnd -= thirtySeconds;
+ }
+
+ LOG(MediaSource, "SourceBuffer::evictCodedFrames(%p) - evicted %zu bytes%s", this, initialBufferedSize - extraMemoryCost(), m_bufferFull ? "" : " but FAILED to free enough");
+}
+
+size_t SourceBuffer::maximumBufferSize() const
+{
+ if (isRemoved())
+ return 0;
+
+ auto* element = m_source->mediaElement();
+ if (!element)
+ return 0;
+
+ return element->maximumSourceBufferSize(*this);
+}
+
+VideoTrackList& SourceBuffer::videoTracks()
+{
+ if (!m_videoTracks)
+ m_videoTracks = VideoTrackList::create(m_source->mediaElement(), scriptExecutionContext());
+ return *m_videoTracks;
+}
+
+AudioTrackList& SourceBuffer::audioTracks()
+{
+ if (!m_audioTracks)
+ m_audioTracks = AudioTrackList::create(m_source->mediaElement(), scriptExecutionContext());
+ return *m_audioTracks;
+}
+
+TextTrackList& SourceBuffer::textTracks()
+{
+ if (!m_textTracks)
+ m_textTracks = TextTrackList::create(m_source->mediaElement(), scriptExecutionContext());
+ return *m_textTracks;
}
void SourceBuffer::setActive(bool active)
@@ -510,42 +977,86 @@ void SourceBuffer::setActive(bool active)
m_active = active;
m_private->setActive(active);
- m_source->sourceBufferDidChangeAcitveState(this, active);
+ if (!isRemoved())
+ m_source->sourceBufferDidChangeActiveState(*this, active);
}
-void SourceBuffer::sourceBufferPrivateDidEndStream(SourceBufferPrivate*, const WTF::AtomicString& error)
+void SourceBuffer::sourceBufferPrivateDidReceiveInitializationSegment(const InitializationSegment& segment)
{
- m_source->endOfStream(error, IgnorableExceptionCode());
-}
+ if (isRemoved())
+ return;
+
+ LOG(MediaSource, "SourceBuffer::sourceBufferPrivateDidReceiveInitializationSegment(%p)", this);
+
+ // 3.5.8 Initialization Segment Received (ctd)
+ // https://rawgit.com/w3c/media-source/c3ad59c7a370d04430969ba73d18dc9bcde57a33/index.html#sourcebuffer-init-segment-received [Editor's Draft 09 January 2015]
-void SourceBuffer::sourceBufferPrivateDidReceiveInitializationSegment(SourceBufferPrivate*, const InitializationSegment& segment)
-{
- // 3.5.7 Initialization Segment Received
- // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-init-segment-received
// 1. Update the duration attribute if it currently equals NaN:
- if (std::isnan(m_source->duration())) {
+ if (m_source->duration().isInvalid()) {
// ↳ If the initialization segment contains a duration:
// Run the duration change algorithm with new duration set to the duration in the initialization segment.
// ↳ Otherwise:
// Run the duration change algorithm with new duration set to positive Infinity.
MediaTime newDuration = segment.duration.isValid() ? segment.duration : MediaTime::positiveInfiniteTime();
- m_source->setDuration(newDuration.toDouble(), IGNORE_EXCEPTION);
+ m_source->setDurationInternal(newDuration);
}
- // 2. If the initialization segment has no audio, video, or text tracks, then run the end of stream
- // algorithm with the error parameter set to "decode" and abort these steps.
- if (!segment.audioTracks.size() && !segment.videoTracks.size() && !segment.textTracks.size())
- m_source->endOfStream(decodeError(), IgnorableExceptionCode());
-
+ // 2. If the initialization segment has no audio, video, or text tracks, then run the append error algorithm
+ // with the decode error parameter set to true and abort these steps.
+ if (segment.audioTracks.isEmpty() && segment.videoTracks.isEmpty() && segment.textTracks.isEmpty()) {
+ appendError(true);
+ return;
+ }
// 3. If the first initialization segment flag is true, then run the following steps:
if (m_receivedFirstInitializationSegment) {
+
+ // 3.1. Verify the following properties. If any of the checks fail then run the append error algorithm
+ // with the decode error parameter set to true and abort these steps.
if (!validateInitializationSegment(segment)) {
- m_source->endOfStream(decodeError(), IgnorableExceptionCode());
+ appendError(true);
return;
}
// 3.2 Add the appropriate track descriptions from this initialization segment to each of the track buffers.
- // NOTE: No changes to make
+ ASSERT(segment.audioTracks.size() == audioTracks().length());
+ for (auto& audioTrackInfo : segment.audioTracks) {
+ if (audioTracks().length() == 1) {
+ audioTracks().item(0)->setPrivate(*audioTrackInfo.track);
+ break;
+ }
+
+ auto audioTrack = audioTracks().getTrackById(audioTrackInfo.track->id());
+ ASSERT(audioTrack);
+ audioTrack->setPrivate(*audioTrackInfo.track);
+ }
+
+ ASSERT(segment.videoTracks.size() == videoTracks().length());
+ for (auto& videoTrackInfo : segment.videoTracks) {
+ if (videoTracks().length() == 1) {
+ videoTracks().item(0)->setPrivate(*videoTrackInfo.track);
+ break;
+ }
+
+ auto videoTrack = videoTracks().getTrackById(videoTrackInfo.track->id());
+ ASSERT(videoTrack);
+ videoTrack->setPrivate(*videoTrackInfo.track);
+ }
+
+ ASSERT(segment.textTracks.size() == textTracks().length());
+ for (auto& textTrackInfo : segment.textTracks) {
+ if (textTracks().length() == 1) {
+ downcast<InbandTextTrack>(*textTracks().item(0)).setPrivate(*textTrackInfo.track);
+ break;
+ }
+
+ auto textTrack = textTracks().getTrackById(textTrackInfo.track->id());
+ ASSERT(textTrack);
+ downcast<InbandTextTrack>(*textTrack).setPrivate(*textTrackInfo.track);
+ }
+
+ // 3.3 Set the need random access point flag on all track buffers to true.
+ for (auto& trackBuffer : m_trackBufferMap.values())
+ trackBuffer.needRandomAccessFlag = true;
}
// 4. Let active track flag equal false.
@@ -554,20 +1065,19 @@ void SourceBuffer::sourceBufferPrivateDidReceiveInitializationSegment(SourceBuff
// 5. If the first initialization segment flag is false, then run the following steps:
if (!m_receivedFirstInitializationSegment) {
// 5.1 If the initialization segment contains tracks with codecs the user agent does not support,
- // then run the end of stream algorithm with the error parameter set to "decode" and abort these steps.
+ // then run the append error algorithm with the decode error parameter set to true and abort these steps.
// NOTE: This check is the responsibility of the SourceBufferPrivate.
// 5.2 For each audio track in the initialization segment, run following steps:
- for (auto it = segment.audioTracks.begin(); it != segment.audioTracks.end(); ++it) {
- AudioTrackPrivate* audioTrackPrivate = it->track.get();
-
+ for (auto& audioTrackInfo : segment.audioTracks) {
+ // FIXME: Implement steps 5.2.1-5.2.8.1 as per Editor's Draft 09 January 2015, and reorder this
// 5.2.1 Let new audio track be a new AudioTrack object.
// 5.2.2 Generate a unique ID and assign it to the id property on new video track.
- RefPtr<AudioTrack> newAudioTrack = AudioTrack::create(this, audioTrackPrivate);
+ auto newAudioTrack = AudioTrack::create(*this, *audioTrackInfo.track);
newAudioTrack->setSourceBuffer(this);
// 5.2.3 If audioTracks.length equals 0, then run the following steps:
- if (!audioTracks()->length()) {
+ if (!audioTracks().length()) {
// 5.2.3.1 Set the enabled property on new audio track to true.
newAudioTrack->setEnabled(true);
@@ -579,33 +1089,34 @@ void SourceBuffer::sourceBufferPrivateDidReceiveInitializationSegment(SourceBuff
// 5.2.5 Queue a task to fire a trusted event named addtrack, that does not bubble and is
// not cancelable, and that uses the TrackEvent interface, at the AudioTrackList object
// referenced by the audioTracks attribute on this SourceBuffer object.
- audioTracks()->append(newAudioTrack);
+ audioTracks().append(newAudioTrack.copyRef());
// 5.2.6 Add new audio track to the audioTracks attribute on the HTMLMediaElement.
// 5.2.7 Queue a task to fire a trusted event named addtrack, that does not bubble and is
// not cancelable, and that uses the TrackEvent interface, at the AudioTrackList object
// referenced by the audioTracks attribute on the HTMLMediaElement.
- m_source->mediaElement()->audioTracks()->append(newAudioTrack);
+ m_source->mediaElement()->audioTracks().append(newAudioTrack.copyRef());
// 5.2.8 Create a new track buffer to store coded frames for this track.
ASSERT(!m_trackBufferMap.contains(newAudioTrack->id()));
- TrackBuffer& trackBuffer = m_trackBufferMap.add(newAudioTrack->id(), TrackBuffer()).iterator->value;
+ auto& trackBuffer = m_trackBufferMap.add(newAudioTrack->id(), TrackBuffer()).iterator->value;
// 5.2.9 Add the track description for this track to the track buffer.
- trackBuffer.description = it->description;
+ trackBuffer.description = audioTrackInfo.description;
+
+ m_audioCodecs.append(trackBuffer.description->codec());
}
// 5.3 For each video track in the initialization segment, run following steps:
- for (auto it = segment.videoTracks.begin(); it != segment.videoTracks.end(); ++it) {
- VideoTrackPrivate* videoTrackPrivate = it->track.get();
-
+ for (auto& videoTrackInfo : segment.videoTracks) {
+ // FIXME: Implement steps 5.3.1-5.3.8.1 as per Editor's Draft 09 January 2015, and reorder this
// 5.3.1 Let new video track be a new VideoTrack object.
// 5.3.2 Generate a unique ID and assign it to the id property on new video track.
- RefPtr<VideoTrack> newVideoTrack = VideoTrack::create(this, videoTrackPrivate);
+ auto newVideoTrack = VideoTrack::create(*this, *videoTrackInfo.track);
newVideoTrack->setSourceBuffer(this);
// 5.3.3 If videoTracks.length equals 0, then run the following steps:
- if (!videoTracks()->length()) {
+ if (!videoTracks().length()) {
// 5.3.3.1 Set the selected property on new video track to true.
newVideoTrack->setSelected(true);
@@ -617,58 +1128,64 @@ void SourceBuffer::sourceBufferPrivateDidReceiveInitializationSegment(SourceBuff
// 5.3.5 Queue a task to fire a trusted event named addtrack, that does not bubble and is
// not cancelable, and that uses the TrackEvent interface, at the VideoTrackList object
// referenced by the videoTracks attribute on this SourceBuffer object.
- videoTracks()->append(newVideoTrack);
+ videoTracks().append(newVideoTrack.copyRef());
// 5.3.6 Add new video track to the videoTracks attribute on the HTMLMediaElement.
// 5.3.7 Queue a task to fire a trusted event named addtrack, that does not bubble and is
// not cancelable, and that uses the TrackEvent interface, at the VideoTrackList object
// referenced by the videoTracks attribute on the HTMLMediaElement.
- m_source->mediaElement()->videoTracks()->append(newVideoTrack);
+ m_source->mediaElement()->videoTracks().append(newVideoTrack.copyRef());
// 5.3.8 Create a new track buffer to store coded frames for this track.
ASSERT(!m_trackBufferMap.contains(newVideoTrack->id()));
- TrackBuffer& trackBuffer = m_trackBufferMap.add(newVideoTrack->id(), TrackBuffer()).iterator->value;
+ auto& trackBuffer = m_trackBufferMap.add(newVideoTrack->id(), TrackBuffer()).iterator->value;
// 5.3.9 Add the track description for this track to the track buffer.
- trackBuffer.description = it->description;
+ trackBuffer.description = videoTrackInfo.description;
+
+ m_videoCodecs.append(trackBuffer.description->codec());
}
// 5.4 For each text track in the initialization segment, run following steps:
- for (auto it = segment.textTracks.begin(); it != segment.textTracks.end(); ++it) {
- InbandTextTrackPrivate* textTrackPrivate = it->track.get();
+ for (auto& textTrackInfo : segment.textTracks) {
+ auto& textTrackPrivate = *textTrackInfo.track;
+ // FIXME: Implement steps 5.4.1-5.4.8.1 as per Editor's Draft 09 January 2015, and reorder this
// 5.4.1 Let new text track be a new TextTrack object with its properties populated with the
// appropriate information from the initialization segment.
- RefPtr<InbandTextTrack> newTextTrack = InbandTextTrack::create(scriptExecutionContext(), this, textTrackPrivate);
+ auto newTextTrack = InbandTextTrack::create(*scriptExecutionContext(), *this, textTrackPrivate);
// 5.4.2 If the mode property on new text track equals "showing" or "hidden", then set active
// track flag to true.
- if (textTrackPrivate->mode() != InbandTextTrackPrivate::Disabled)
+ if (textTrackPrivate.mode() != InbandTextTrackPrivate::Disabled)
activeTrackFlag = true;
// 5.4.3 Add new text track to the textTracks attribute on this SourceBuffer object.
// 5.4.4 Queue a task to fire a trusted event named addtrack, that does not bubble and is
// not cancelable, and that uses the TrackEvent interface, at textTracks attribute on this
// SourceBuffer object.
- textTracks()->append(newTextTrack);
+ textTracks().append(newTextTrack.get());
// 5.4.5 Add new text track to the textTracks attribute on the HTMLMediaElement.
// 5.4.6 Queue a task to fire a trusted event named addtrack, that does not bubble and is
// not cancelable, and that uses the TrackEvent interface, at the TextTrackList object
// referenced by the textTracks attribute on the HTMLMediaElement.
- m_source->mediaElement()->textTracks()->append(newTextTrack);
+ m_source->mediaElement()->textTracks().append(WTFMove(newTextTrack));
// 5.4.7 Create a new track buffer to store coded frames for this track.
- ASSERT(!m_trackBufferMap.contains(textTrackPrivate->id()));
- TrackBuffer& trackBuffer = m_trackBufferMap.add(textTrackPrivate->id(), TrackBuffer()).iterator->value;
+ ASSERT(!m_trackBufferMap.contains(textTrackPrivate.id()));
+ auto& trackBuffer = m_trackBufferMap.add(textTrackPrivate.id(), TrackBuffer()).iterator->value;
// 5.4.8 Add the track description for this track to the track buffer.
- trackBuffer.description = it->description;
+ trackBuffer.description = textTrackInfo.description;
+
+ m_textCodecs.append(trackBuffer.description->codec());
}
// 5.5 If active track flag equals true, then run the following steps:
if (activeTrackFlag) {
// 5.5.1 Add this SourceBuffer to activeSourceBuffers.
+ // 5.5.2 Queue a task to fire a simple event named addsourcebuffer at activeSourceBuffers
setActive(true);
}
@@ -679,8 +1196,8 @@ void SourceBuffer::sourceBufferPrivateDidReceiveInitializationSegment(SourceBuff
// 6. If the HTMLMediaElement.readyState attribute is HAVE_NOTHING, then run the following steps:
if (m_private->readyState() == MediaPlayer::HaveNothing) {
// 6.1 If one or more objects in sourceBuffers have first initialization segment flag set to false, then abort these steps.
- for (unsigned long i = 0; i < m_source->sourceBuffers()->length(); ++i) {
- if (!m_source->sourceBuffers()->item(i)->m_receivedFirstInitializationSegment)
+ for (auto& sourceBuffer : *m_source->sourceBuffers()) {
+ if (!sourceBuffer->m_receivedFirstInitializationSegment)
return;
}
@@ -698,52 +1215,52 @@ void SourceBuffer::sourceBufferPrivateDidReceiveInitializationSegment(SourceBuff
bool SourceBuffer::validateInitializationSegment(const InitializationSegment& segment)
{
- // 3.5.7 Initialization Segment Received (ctd)
- // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-init-segment-received
+ // FIXME: ordering of all 3.5.X (X>=7) functions needs to be updated to post-[24 July 2014 Editor's Draft] version
+ // 3.5.8 Initialization Segment Received (ctd)
+ // https://rawgit.com/w3c/media-source/c3ad59c7a370d04430969ba73d18dc9bcde57a33/index.html#sourcebuffer-init-segment-received [Editor's Draft 09 January 2015]
- // 3.1. Verify the following properties. If any of the checks fail then run the end of stream
- // algorithm with the error parameter set to "decode" and abort these steps.
+ // Note: those are checks from step 3.1
// * The number of audio, video, and text tracks match what was in the first initialization segment.
- if (segment.audioTracks.size() != audioTracks()->length()
- || segment.videoTracks.size() != videoTracks()->length()
- || segment.textTracks.size() != textTracks()->length())
+ if (segment.audioTracks.size() != audioTracks().length()
+ || segment.videoTracks.size() != videoTracks().length()
+ || segment.textTracks.size() != textTracks().length())
return false;
// * The codecs for each track, match what was specified in the first initialization segment.
- for (auto it = segment.audioTracks.begin(); it != segment.audioTracks.end(); ++it) {
- if (!m_videoCodecs.contains(it->description->codec()))
+ for (auto& audioTrackInfo : segment.audioTracks) {
+ if (!m_audioCodecs.contains(audioTrackInfo.description->codec()))
return false;
}
- for (auto it = segment.videoTracks.begin(); it != segment.videoTracks.end(); ++it) {
- if (!m_audioCodecs.contains(it->description->codec()))
+ for (auto& videoTrackInfo : segment.videoTracks) {
+ if (!m_videoCodecs.contains(videoTrackInfo.description->codec()))
return false;
}
- for (auto it = segment.textTracks.begin(); it != segment.textTracks.end(); ++it) {
- if (!m_textCodecs.contains(it->description->codec()))
+ for (auto& textTrackInfo : segment.textTracks) {
+ if (!m_textCodecs.contains(textTrackInfo.description->codec()))
return false;
}
// * If more than one track for a single type are present (ie 2 audio tracks), then the Track
// IDs match the ones in the first initialization segment.
if (segment.audioTracks.size() >= 2) {
- for (auto it = segment.audioTracks.begin(); it != segment.audioTracks.end(); ++it) {
- if (!m_trackBufferMap.contains(it->track->id()))
+ for (auto& audioTrackInfo : segment.audioTracks) {
+ if (!m_trackBufferMap.contains(audioTrackInfo.track->id()))
return false;
}
}
if (segment.videoTracks.size() >= 2) {
- for (auto it = segment.videoTracks.begin(); it != segment.videoTracks.end(); ++it) {
- if (!m_trackBufferMap.contains(it->track->id()))
+ for (auto& videoTrackInfo : segment.videoTracks) {
+ if (!m_trackBufferMap.contains(videoTrackInfo.track->id()))
return false;
}
}
if (segment.textTracks.size() >= 2) {
- for (auto it = segment.videoTracks.begin(); it != segment.videoTracks.end(); ++it) {
- if (!m_trackBufferMap.contains(it->track->id()))
+ for (auto& textTrackInfo : segment.videoTracks) {
+ if (!m_trackBufferMap.contains(textTrackInfo.track->id()))
return false;
}
}
@@ -769,102 +1286,188 @@ public:
}
};
-void SourceBuffer::sourceBufferPrivateDidReceiveSample(SourceBufferPrivate*, PassRefPtr<MediaSample> prpSample)
+void SourceBuffer::appendError(bool decodeErrorParam)
{
- RefPtr<MediaSample> sample = prpSample;
+ // 3.5.3 Append Error Algorithm
+ // https://rawgit.com/w3c/media-source/c3ad59c7a370d04430969ba73d18dc9bcde57a33/index.html#sourcebuffer-append-error [Editor's Draft 09 January 2015]
+
+ ASSERT(m_updating);
+ // 1. Run the reset parser state algorithm.
+ resetParserState();
+
+ // 2. Set the updating attribute to false.
+ m_updating = false;
+
+ // 3. Queue a task to fire a simple event named error at this SourceBuffer object.
+ scheduleEvent(eventNames().errorEvent);
+
+ // 4. Queue a task to fire a simple event named updateend at this SourceBuffer object.
+ scheduleEvent(eventNames().updateendEvent);
+
+ // 5. If decode error is true, then run the end of stream algorithm with the error parameter set to "decode".
+ if (decodeErrorParam)
+ m_source->streamEndedWithError(MediaSource::EndOfStreamError::Decode);
+}
+
+void SourceBuffer::sourceBufferPrivateDidReceiveSample(MediaSample& sample)
+{
+ if (isRemoved())
+ return;
+
+ // 3.5.1 Segment Parser Loop
+ // 6.1 If the first initialization segment received flag is false, then run the append error algorithm
+ // with the decode error parameter set to true and abort this algorithm.
+ // Note: current design makes SourceBuffer somehow ignorant of append state - it's more a thing
+ // of SourceBufferPrivate. That's why this check can't really be done in appendInternal.
+ // unless we force some kind of design with state machine switching.
+ if (!m_receivedFirstInitializationSegment) {
+ appendError(true);
+ return;
+ }
// 3.5.8 Coded Frame Processing
+ // http://www.w3.org/TR/media-source/#sourcebuffer-coded-frame-processing
+
// When complete coded frames have been parsed by the segment parser loop then the following steps
// are run:
// 1. For each coded frame in the media segment run the following steps:
// 1.1. Loop Top
do {
- // 1.1 (ctd) Let presentation timestamp be a double precision floating point representation of
- // the coded frame's presentation timestamp in seconds.
- MediaTime presentationTimestamp = sample->presentationTime();
-
- // 1.2 Let decode timestamp be a double precision floating point representation of the coded frame's
- // decode timestamp in seconds.
- MediaTime decodeTimestamp = sample->decodeTime();
+ MediaTime presentationTimestamp;
+ MediaTime decodeTimestamp;
+
+ if (m_shouldGenerateTimestamps) {
+ // ↳ If generate timestamps flag equals true:
+ // 1. Let presentation timestamp equal 0.
+ presentationTimestamp = MediaTime::zeroTime();
+
+ // 2. Let decode timestamp equal 0.
+ decodeTimestamp = MediaTime::zeroTime();
+ } else {
+ // ↳ Otherwise:
+ // 1. Let presentation timestamp be a double precision floating point representation of
+ // the coded frame's presentation timestamp in seconds.
+ presentationTimestamp = sample.presentationTime();
+
+ // 2. Let decode timestamp be a double precision floating point representation of the coded frame's
+ // decode timestamp in seconds.
+ decodeTimestamp = sample.decodeTime();
+ }
- // 1.3 Let frame duration be a double precision floating point representation of the coded frame's
+ // 1.2 Let frame duration be a double precision floating point representation of the coded frame's
// duration in seconds.
- MediaTime frameDuration = sample->duration();
+ MediaTime frameDuration = sample.duration();
+
+ // 1.3 If mode equals "sequence" and group start timestamp is set, then run the following steps:
+ if (m_mode == AppendMode::Sequence && m_groupStartTimestamp.isValid()) {
+ // 1.3.1 Set timestampOffset equal to group start timestamp - presentation timestamp.
+ m_timestampOffset = m_groupStartTimestamp;
- // 1.4 If mode equals "sequence" and group start timestamp is set, then run the following steps:
- // FIXME: add support for "sequence" mode
+ // 1.3.2 Set group end timestamp equal to group start timestamp.
+ m_groupEndTimestamp = m_groupStartTimestamp;
- // 1.5 If timestampOffset is not 0, then run the following steps:
- if (m_timestampOffset != MediaTime::zeroTime()) {
- // 1.5.1 Add timestampOffset to the presentation timestamp.
+ // 1.3.3 Set the need random access point flag on all track buffers to true.
+ for (auto& trackBuffer : m_trackBufferMap.values())
+ trackBuffer.needRandomAccessFlag = true;
+
+ // 1.3.4 Unset group start timestamp.
+ m_groupStartTimestamp = MediaTime::invalidTime();
+ }
+
+ // 1.4 If timestampOffset is not 0, then run the following steps:
+ if (m_timestampOffset) {
+ // 1.4.1 Add timestampOffset to the presentation timestamp.
presentationTimestamp += m_timestampOffset;
- // 1.5.2 Add timestampOffset to the decode timestamp.
+ // 1.4.2 Add timestampOffset to the decode timestamp.
decodeTimestamp += m_timestampOffset;
-
- // 1.5.3 If the presentation timestamp or decode timestamp is less than the presentation start
- // time, then run the end of stream algorithm with the error parameter set to "decode", and
- // abort these steps.
- MediaTime presentationStartTime = MediaTime::zeroTime();
- if (presentationTimestamp < presentationStartTime || decodeTimestamp < presentationStartTime) {
- m_source->streamEndedWithError(decodeError(), IgnorableExceptionCode());
- return;
- }
}
- // 1.6 Let track buffer equal the track buffer that the coded frame will be added to.
- AtomicString trackID = sample->trackID();
+ // 1.5 Let track buffer equal the track buffer that the coded frame will be added to.
+ AtomicString trackID = sample.trackID();
auto it = m_trackBufferMap.find(trackID);
- if (it == m_trackBufferMap.end())
- it = m_trackBufferMap.add(trackID, TrackBuffer()).iterator;
+ if (it == m_trackBufferMap.end()) {
+ // The client managed to append a sample with a trackID not present in the initialization
+ // segment. This would be a good place to post an message to the developer console.
+ didDropSample();
+ return;
+ }
TrackBuffer& trackBuffer = it->value;
- // 1.7 If last decode timestamp for track buffer is set and decode timestamp is less than last
+ // 1.6 ↳ If last decode timestamp for track buffer is set and decode timestamp is less than last
// decode timestamp:
// OR
- // If last decode timestamp for track buffer is set and the difference between decode timestamp and
+ // ↳ If last decode timestamp for track buffer is set and the difference between decode timestamp and
// last decode timestamp is greater than 2 times last frame duration:
if (trackBuffer.lastDecodeTimestamp.isValid() && (decodeTimestamp < trackBuffer.lastDecodeTimestamp
|| abs(decodeTimestamp - trackBuffer.lastDecodeTimestamp) > (trackBuffer.lastFrameDuration * 2))) {
- // 1.7.1 If mode equals "segments":
- // Set highest presentation end timestamp to presentation timestamp.
- m_highestPresentationEndTimestamp = presentationTimestamp;
-
- // If mode equals "sequence":
- // Set group start timestamp equal to the highest presentation end timestamp.
- // FIXME: Add support for "sequence" mode.
-
- for (auto i = m_trackBufferMap.values().begin(); i != m_trackBufferMap.values().end(); ++i) {
- // 1.7.2 Unset the last decode timestamp on all track buffers.
- i->lastDecodeTimestamp = MediaTime::invalidTime();
- // 1.7.3 Unset the last frame duration on all track buffers.
- i->lastFrameDuration = MediaTime::invalidTime();
- // 1.7.4 Unset the highest presentation timestamp on all track buffers.
- i->highestPresentationTimestamp = MediaTime::invalidTime();
- // 1.7.5 Set the need random access point flag on all track buffers to true.
- i->needRandomAccessFlag = true;
+
+ // 1.6.1:
+ if (m_mode == AppendMode::Segments) {
+ // ↳ If mode equals "segments":
+ // Set group end timestamp to presentation timestamp.
+ m_groupEndTimestamp = presentationTimestamp;
+ } else {
+ // ↳ If mode equals "sequence":
+ // Set group start timestamp equal to the group end timestamp.
+ m_groupStartTimestamp = m_groupEndTimestamp;
}
- // 1.7.6 Jump to the Loop Top step above to restart processing of the current coded frame.
+ for (auto& trackBuffer : m_trackBufferMap.values()) {
+ // 1.6.2 Unset the last decode timestamp on all track buffers.
+ trackBuffer.lastDecodeTimestamp = MediaTime::invalidTime();
+ // 1.6.3 Unset the last frame duration on all track buffers.
+ trackBuffer.lastFrameDuration = MediaTime::invalidTime();
+ // 1.6.4 Unset the highest presentation timestamp on all track buffers.
+ trackBuffer.highestPresentationTimestamp = MediaTime::invalidTime();
+ // 1.6.5 Set the need random access point flag on all track buffers to true.
+ trackBuffer.needRandomAccessFlag = true;
+ }
+
+ // 1.6.6 Jump to the Loop Top step above to restart processing of the current coded frame.
continue;
}
- // 1.8 Let frame end timestamp equal the sum of presentation timestamp and frame duration.
+ if (m_mode == AppendMode::Sequence) {
+ // Use the generated timestamps instead of the sample's timestamps.
+ sample.setTimestamps(presentationTimestamp, decodeTimestamp);
+ } else if (m_timestampOffset) {
+ // Reflect the timestamp offset into the sample.
+ sample.offsetTimestampsBy(m_timestampOffset);
+ }
+
+ // 1.7 Let frame end timestamp equal the sum of presentation timestamp and frame duration.
MediaTime frameEndTimestamp = presentationTimestamp + frameDuration;
- // 1.9 If presentation timestamp is less than appendWindowStart, then set the need random access
+ // 1.8 If presentation timestamp is less than appendWindowStart, then set the need random access
// point flag to true, drop the coded frame, and jump to the top of the loop to start processing
// the next coded frame.
- // 1.10 If frame end timestamp is greater than appendWindowEnd, then set the need random access
+ // 1.9 If frame end timestamp is greater than appendWindowEnd, then set the need random access
// point flag to true, drop the coded frame, and jump to the top of the loop to start processing
// the next coded frame.
- // FIXME: implement append windows
+ if (presentationTimestamp < m_appendWindowStart || frameEndTimestamp > m_appendWindowEnd) {
+ trackBuffer.needRandomAccessFlag = true;
+ didDropSample();
+ return;
+ }
+
+
+ // 1.10 If the decode timestamp is less than the presentation start time, then run the end of stream
+ // algorithm with the error parameter set to "decode", and abort these steps.
+ // NOTE: Until <https://www.w3.org/Bugs/Public/show_bug.cgi?id=27487> is resolved, we will only check
+ // the presentation timestamp.
+ MediaTime presentationStartTime = MediaTime::zeroTime();
+ if (presentationTimestamp < presentationStartTime) {
+ LOG(MediaSource, "SourceBuffer::sourceBufferPrivateDidReceiveSample(%p) - failing because presentationTimestamp < presentationStartTime", this);
+ m_source->streamEndedWithError(MediaSource::EndOfStreamError::Decode);
+ return;
+ }
// 1.11 If the need random access point flag on track buffer equals true, then run the following steps:
if (trackBuffer.needRandomAccessFlag) {
// 1.11.1 If the coded frame is not a random access point, then drop the coded frame and jump
// to the top of the loop to start processing the next coded frame.
- if (!sample->isSync()) {
+ if (!sample.isSync()) {
didDropSample();
return;
}
@@ -877,16 +1480,15 @@ void SourceBuffer::sourceBufferPrivateDidReceiveSample(SourceBufferPrivate*, Pas
// 1.13 Let spliced timed text frame be an unset variable for holding timed text splice information
// FIXME: Add support for sample splicing.
- SampleMap::MapType erasedSamples;
+ SampleMap erasedSamples;
MediaTime microsecond(1, 1000000);
- // 1.14 If last decode timestamp for track buffer is unset and there is a coded frame in
- // track buffer with a presentation timestamp less than or equal to presentation timestamp
- // and presentation timestamp is less than this coded frame's presentation timestamp plus
- // its frame duration, then run the following steps:
+ // 1.14 If last decode timestamp for track buffer is unset and presentation timestamp falls
+ // falls within the presentation interval of a coded frame in track buffer, then run the
+ // following steps:
if (trackBuffer.lastDecodeTimestamp.isInvalid()) {
- auto iter = trackBuffer.samples.findSampleContainingPresentationTime(presentationTimestamp);
- if (iter != trackBuffer.samples.presentationEnd()) {
+ auto iter = trackBuffer.samples.presentationOrder().findSampleContainingPresentationTime(presentationTimestamp);
+ if (iter != trackBuffer.samples.presentationOrder().end()) {
// 1.14.1 Let overlapped frame be the coded frame in track buffer that matches the condition above.
RefPtr<MediaSample> overlappedFrame = iter->second;
@@ -908,7 +1510,7 @@ void SourceBuffer::sourceBufferPrivateDidReceiveSample(SourceBufferPrivate*, Pas
// 1.14.2.3 If the presentation timestamp is less than the remove window timestamp,
// then remove overlapped frame and any coded frames that depend on it from track buffer.
if (presentationTimestamp < removeWindowTimestamp)
- erasedSamples.insert(*iter);
+ erasedSamples.addSample(*iter->second);
}
// If track buffer contains timed text coded frames:
@@ -922,52 +1524,64 @@ void SourceBuffer::sourceBufferPrivateDidReceiveSample(SourceBufferPrivate*, Pas
if (trackBuffer.highestPresentationTimestamp.isInvalid()) {
// Remove all coded frames from track buffer that have a presentation timestamp greater than or
// equal to presentation timestamp and less than frame end timestamp.
- auto iter_pair = trackBuffer.samples.findSamplesBetweenPresentationTimes(presentationTimestamp, frameEndTimestamp);
- if (iter_pair.first != trackBuffer.samples.presentationEnd())
- erasedSamples.insert(iter_pair.first, iter_pair.second);
+ auto iter_pair = trackBuffer.samples.presentationOrder().findSamplesBetweenPresentationTimes(presentationTimestamp, frameEndTimestamp);
+ if (iter_pair.first != trackBuffer.samples.presentationOrder().end())
+ erasedSamples.addRange(iter_pair.first, iter_pair.second);
}
- // If highest presentation timestamp for track buffer is set and less than presentation timestamp
- if (trackBuffer.highestPresentationTimestamp.isValid() && trackBuffer.highestPresentationTimestamp < presentationTimestamp) {
+ // If highest presentation timestamp for track buffer is set and less than or equal to presentation timestamp
+ if (trackBuffer.highestPresentationTimestamp.isValid() && trackBuffer.highestPresentationTimestamp <= presentationTimestamp) {
// Remove all coded frames from track buffer that have a presentation timestamp greater than highest
// presentation timestamp and less than or equal to frame end timestamp.
- auto iter_pair = trackBuffer.samples.findSamplesBetweenPresentationTimes(trackBuffer.highestPresentationTimestamp, frameEndTimestamp);
- if (iter_pair.first != trackBuffer.samples.presentationEnd())
- erasedSamples.insert(iter_pair.first, iter_pair.second);
+ do {
+ // NOTE: Searching from the end of the trackBuffer will be vastly more efficient if the search range is
+ // near the end of the buffered range. Use a linear-backwards search if the search range is within one
+ // frame duration of the end:
+ unsigned bufferedLength = trackBuffer.buffered.length();
+ if (!bufferedLength)
+ break;
+
+ MediaTime highestBufferedTime = trackBuffer.buffered.maximumBufferedTime();
+
+ PresentationOrderSampleMap::iterator_range range;
+ if (highestBufferedTime - trackBuffer.highestPresentationTimestamp < trackBuffer.lastFrameDuration)
+ range = trackBuffer.samples.presentationOrder().findSamplesWithinPresentationRangeFromEnd(trackBuffer.highestPresentationTimestamp, frameEndTimestamp);
+ else
+ range = trackBuffer.samples.presentationOrder().findSamplesWithinPresentationRange(trackBuffer.highestPresentationTimestamp, frameEndTimestamp);
+
+ if (range.first != trackBuffer.samples.presentationOrder().end())
+ erasedSamples.addRange(range.first, range.second);
+ } while(false);
}
// 1.16 Remove decoding dependencies of the coded frames removed in the previous step:
- SampleMap::MapType dependentSamples;
+ DecodeOrderSampleMap::MapType dependentSamples;
if (!erasedSamples.empty()) {
// If detailed information about decoding dependencies is available:
// FIXME: Add support for detailed dependency information
// Otherwise: Remove all coded frames between the coded frames removed in the previous step
// and the next random access point after those removed frames.
- for (auto erasedIt = erasedSamples.begin(), end = erasedSamples.end(); erasedIt != end; ++erasedIt) {
- auto currentDecodeIter = trackBuffer.samples.findSampleWithDecodeTime(erasedIt->second->decodeTime());
- auto nextSyncIter = trackBuffer.samples.findSyncSampleAfterDecodeIterator(currentDecodeIter);
- dependentSamples.insert(currentDecodeIter, nextSyncIter);
- }
-
-
- RefPtr<TimeRanges> erasedRanges = TimeRanges::create();
- for (auto erasedIt = erasedSamples.begin(), end = erasedSamples.end(); erasedIt != end; ++erasedIt) {
- double startTime = erasedIt->first.toDouble();
- double endTime = ((erasedIt->first + erasedIt->second->duration()) + microsecond).toDouble();
- erasedRanges->add(startTime, endTime);
- trackBuffer.samples.removeSample(erasedIt->second.get());
- }
-
- for (auto dependentIt = dependentSamples.begin(), end = dependentSamples.end(); dependentIt != end; ++dependentIt) {
- double startTime = dependentIt->first.toDouble();
- double endTime = ((dependentIt->first + dependentIt->second->duration()) + microsecond).toDouble();
- erasedRanges->add(startTime, endTime);
- trackBuffer.samples.removeSample(dependentIt->second.get());
+ auto firstDecodeIter = trackBuffer.samples.decodeOrder().findSampleWithDecodeKey(erasedSamples.decodeOrder().begin()->first);
+ auto lastDecodeIter = trackBuffer.samples.decodeOrder().findSampleWithDecodeKey(erasedSamples.decodeOrder().rbegin()->first);
+ auto nextSyncIter = trackBuffer.samples.decodeOrder().findSyncSampleAfterDecodeIterator(lastDecodeIter);
+ dependentSamples.insert(firstDecodeIter, nextSyncIter);
+
+ PlatformTimeRanges erasedRanges = removeSamplesFromTrackBuffer(dependentSamples, trackBuffer, this, "sourceBufferPrivateDidReceiveSample");
+
+ // Only force the TrackBuffer to re-enqueue if the removed ranges overlap with enqueued and possibly
+ // not yet displayed samples.
+ MediaTime currentMediaTime = m_source->currentTime();
+ if (currentMediaTime < trackBuffer.lastEnqueuedPresentationTime) {
+ PlatformTimeRanges possiblyEnqueuedRanges(currentMediaTime, trackBuffer.lastEnqueuedPresentationTime);
+ possiblyEnqueuedRanges.intersectWith(erasedRanges);
+ if (possiblyEnqueuedRanges.length())
+ trackBuffer.needsReenqueueing = true;
}
- erasedRanges->invert();
- m_buffered->intersectWith(erasedRanges.get());
+ erasedRanges.invert();
+ trackBuffer.buffered.intersectWith(erasedRanges);
+ setBufferedDirty(true);
}
// 1.17 If spliced audio frame is set:
@@ -979,7 +1593,11 @@ void SourceBuffer::sourceBufferPrivateDidReceiveSample(SourceBufferPrivate*, Pas
// Otherwise:
// Add the coded frame with the presentation timestamp, decode timestamp, and frame duration to the track buffer.
trackBuffer.samples.addSample(sample);
- trackBuffer.decodeQueue.insert(SampleMap::MapType::value_type(decodeTimestamp, sample));
+
+ if (trackBuffer.lastEnqueuedDecodeEndTime.isInvalid() || decodeTimestamp >= trackBuffer.lastEnqueuedDecodeEndTime) {
+ DecodeOrderSampleMap::KeyType decodeKey(decodeTimestamp, presentationTimestamp);
+ trackBuffer.decodeQueue.insert(DecodeOrderSampleMap::MapType::value_type(decodeKey, &sample));
+ }
// 1.18 Set last decode timestamp for track buffer to decode timestamp.
trackBuffer.lastDecodeTimestamp = decodeTimestamp;
@@ -993,12 +1611,29 @@ void SourceBuffer::sourceBufferPrivateDidReceiveSample(SourceBufferPrivate*, Pas
if (trackBuffer.highestPresentationTimestamp.isInvalid() || frameEndTimestamp > trackBuffer.highestPresentationTimestamp)
trackBuffer.highestPresentationTimestamp = frameEndTimestamp;
- // 1.21 If highest presentation end timestamp is unset or frame end timestamp is greater than highest
- // presentation end timestamp, then set highest presentation end timestamp equal to frame end timestamp.
- if (m_highestPresentationEndTimestamp.isInvalid() || frameEndTimestamp > m_highestPresentationEndTimestamp)
- m_highestPresentationEndTimestamp = frameEndTimestamp;
+ // 1.21 If frame end timestamp is greater than group end timestamp, then set group end timestamp equal
+ // to frame end timestamp.
+ if (m_groupEndTimestamp.isInvalid() || frameEndTimestamp > m_groupEndTimestamp)
+ m_groupEndTimestamp = frameEndTimestamp;
+
+ // 1.22 If generate timestamps flag equals true, then set timestampOffset equal to frame end timestamp.
+ if (m_shouldGenerateTimestamps)
+ m_timestampOffset = frameEndTimestamp;
- m_buffered->add(presentationTimestamp.toDouble(), (presentationTimestamp + frameDuration + microsecond).toDouble());
+ // Eliminate small gaps between buffered ranges by coalescing
+ // disjoint ranges separated by less than a "fudge factor".
+ auto presentationEndTime = presentationTimestamp + frameDuration;
+ auto nearestToPresentationStartTime = trackBuffer.buffered.nearest(presentationTimestamp);
+ if (nearestToPresentationStartTime.isValid() && (presentationTimestamp - nearestToPresentationStartTime).isBetween(MediaTime::zeroTime(), MediaSource::currentTimeFudgeFactor()))
+ presentationTimestamp = nearestToPresentationStartTime;
+
+ auto nearestToPresentationEndTime = trackBuffer.buffered.nearest(presentationEndTime);
+ if (nearestToPresentationEndTime.isValid() && (nearestToPresentationEndTime - presentationEndTime).isBetween(MediaTime::zeroTime(), MediaSource::currentTimeFudgeFactor()))
+ presentationEndTime = nearestToPresentationEndTime;
+
+ trackBuffer.buffered.add(presentationTimestamp, presentationEndTime);
+ m_bufferedSinceLastMonitor += frameDuration.toDouble();
+ setBufferedDirty(true);
break;
} while (1);
@@ -1006,35 +1641,45 @@ void SourceBuffer::sourceBufferPrivateDidReceiveSample(SourceBufferPrivate*, Pas
// Steps 2-4 will be handled by MediaSource::monitorSourceBuffers()
// 5. If the media segment contains data beyond the current duration, then run the duration change algorithm with new
- // duration set to the maximum of the current duration and the highest end timestamp reported by HTMLMediaElement.buffered.
- if (highestPresentationEndTimestamp().toDouble() > m_source->duration())
- m_source->setDuration(highestPresentationEndTimestamp().toDouble(), IgnorableExceptionCode());
+ // duration set to the maximum of the current duration and the group end timestamp.
+ if (m_groupEndTimestamp > m_source->duration())
+ m_source->setDurationInternal(m_groupEndTimestamp);
}
-bool SourceBuffer::sourceBufferPrivateHasAudio(const SourceBufferPrivate*) const
+bool SourceBuffer::hasAudio() const
{
return m_audioTracks && m_audioTracks->length();
}
-bool SourceBuffer::sourceBufferPrivateHasVideo(const SourceBufferPrivate*) const
+bool SourceBuffer::hasVideo() const
{
return m_videoTracks && m_videoTracks->length();
}
-void SourceBuffer::videoTrackSelectedChanged(VideoTrack* track)
+bool SourceBuffer::sourceBufferPrivateHasAudio() const
+{
+ return hasAudio();
+}
+
+bool SourceBuffer::sourceBufferPrivateHasVideo() const
+{
+ return hasVideo();
+}
+
+void SourceBuffer::videoTrackSelectedChanged(VideoTrack& track)
{
// 2.4.5 Changes to selected/enabled track state
// If the selected video track changes, then run the following steps:
// 1. If the SourceBuffer associated with the previously selected video track is not associated with
// any other enabled tracks, run the following steps:
- if (track->selected()
+ if (!track.selected()
&& (!m_videoTracks || !m_videoTracks->isAnyTrackEnabled())
&& (!m_audioTracks || !m_audioTracks->isAnyTrackEnabled())
&& (!m_textTracks || !m_textTracks->isAnyTrackEnabled())) {
// 1.1 Remove the SourceBuffer from activeSourceBuffers.
// 1.2 Queue a task to fire a simple event named removesourcebuffer at activeSourceBuffers
setActive(false);
- } else if (!track->selected()) {
+ } else if (track.selected()) {
// 2. If the SourceBuffer associated with the newly selected video track is not already in activeSourceBuffers,
// run the following steps:
// 2.1 Add the SourceBuffer to activeSourceBuffers.
@@ -1042,23 +1687,26 @@ void SourceBuffer::videoTrackSelectedChanged(VideoTrack* track)
setActive(true);
}
+ if (m_videoTracks && m_videoTracks->contains(track))
+ m_videoTracks->scheduleChangeEvent();
+
if (!isRemoved())
m_source->mediaElement()->videoTrackSelectedChanged(track);
}
-void SourceBuffer::audioTrackEnabledChanged(AudioTrack* track)
+void SourceBuffer::audioTrackEnabledChanged(AudioTrack& track)
{
// 2.4.5 Changes to selected/enabled track state
// If an audio track becomes disabled and the SourceBuffer associated with this track is not
// associated with any other enabled or selected track, then run the following steps:
- if (track->enabled()
+ if (!track.enabled()
&& (!m_videoTracks || !m_videoTracks->isAnyTrackEnabled())
&& (!m_audioTracks || !m_audioTracks->isAnyTrackEnabled())
&& (!m_textTracks || !m_textTracks->isAnyTrackEnabled())) {
// 1. Remove the SourceBuffer associated with the audio track from activeSourceBuffers
// 2. Queue a task to fire a simple event named removesourcebuffer at activeSourceBuffers
setActive(false);
- } else if (!track->enabled()) {
+ } else if (track.enabled()) {
// If an audio track becomes enabled and the SourceBuffer associated with this track is
// not already in activeSourceBuffers, then run the following steps:
// 1. Add the SourceBuffer associated with the audio track to activeSourceBuffers
@@ -1066,16 +1714,19 @@ void SourceBuffer::audioTrackEnabledChanged(AudioTrack* track)
setActive(true);
}
+ if (m_audioTracks && m_audioTracks->contains(track))
+ m_audioTracks->scheduleChangeEvent();
+
if (!isRemoved())
m_source->mediaElement()->audioTrackEnabledChanged(track);
}
-void SourceBuffer::textTrackModeChanged(TextTrack* track)
+void SourceBuffer::textTrackModeChanged(TextTrack& track)
{
// 2.4.5 Changes to selected/enabled track state
// If a text track mode becomes "disabled" and the SourceBuffer associated with this track is not
// associated with any other enabled or selected track, then run the following steps:
- if (track->mode() == TextTrack::disabledKeyword()
+ if (track.mode() == TextTrack::Mode::Disabled
&& (!m_videoTracks || !m_videoTracks->isAnyTrackEnabled())
&& (!m_audioTracks || !m_audioTracks->isAnyTrackEnabled())
&& (!m_textTracks || !m_textTracks->isAnyTrackEnabled())) {
@@ -1090,82 +1741,324 @@ void SourceBuffer::textTrackModeChanged(TextTrack* track)
setActive(true);
}
+ if (m_textTracks && m_textTracks->contains(track))
+ m_textTracks->scheduleChangeEvent();
+
if (!isRemoved())
m_source->mediaElement()->textTrackModeChanged(track);
}
-void SourceBuffer::textTrackAddCue(TextTrack* track, WTF::PassRefPtr<TextTrackCue> cue)
+void SourceBuffer::textTrackAddCue(TextTrack& track, TextTrackCue& cue)
{
if (!isRemoved())
m_source->mediaElement()->textTrackAddCue(track, cue);
}
-void SourceBuffer::textTrackAddCues(TextTrack* track, TextTrackCueList const* cueList)
+void SourceBuffer::textTrackAddCues(TextTrack& track, const TextTrackCueList& cueList)
{
if (!isRemoved())
m_source->mediaElement()->textTrackAddCues(track, cueList);
}
-void SourceBuffer::textTrackRemoveCue(TextTrack* track, WTF::PassRefPtr<TextTrackCue> cue)
+void SourceBuffer::textTrackRemoveCue(TextTrack& track, TextTrackCue& cue)
{
if (!isRemoved())
m_source->mediaElement()->textTrackRemoveCue(track, cue);
}
-void SourceBuffer::textTrackRemoveCues(TextTrack* track, TextTrackCueList const* cueList)
+void SourceBuffer::textTrackRemoveCues(TextTrack& track, const TextTrackCueList& cueList)
{
if (!isRemoved())
m_source->mediaElement()->textTrackRemoveCues(track, cueList);
}
-void SourceBuffer::textTrackKindChanged(TextTrack* track)
+void SourceBuffer::textTrackKindChanged(TextTrack& track)
{
if (!isRemoved())
m_source->mediaElement()->textTrackKindChanged(track);
}
-void SourceBuffer::sourceBufferPrivateDidBecomeReadyForMoreSamples(SourceBufferPrivate*, AtomicString trackID)
+void SourceBuffer::sourceBufferPrivateDidBecomeReadyForMoreSamples(const AtomicString& trackID)
{
- LOG(Media, "SourceBuffer::sourceBufferPrivateDidBecomeReadyForMoreSamples(%p)", this);
+ LOG(MediaSource, "SourceBuffer::sourceBufferPrivateDidBecomeReadyForMoreSamples(%p)", this);
auto it = m_trackBufferMap.find(trackID);
if (it == m_trackBufferMap.end())
return;
- provideMediaData(it->value, trackID);
+ auto& trackBuffer = it->value;
+ if (!trackBuffer.needsReenqueueing && !m_source->isSeeking())
+ provideMediaData(trackBuffer, trackID);
}
-void SourceBuffer::provideMediaData(TrackBuffer& trackBuffer, AtomicString trackID)
+void SourceBuffer::provideMediaData(TrackBuffer& trackBuffer, const AtomicString& trackID)
{
+ if (m_source->isSeeking())
+ return;
+
#if !LOG_DISABLED
unsigned enqueuedSamples = 0;
#endif
- auto sampleIt = trackBuffer.decodeQueue.begin();
- for (auto sampleEnd = trackBuffer.decodeQueue.end(); sampleIt != sampleEnd; ++sampleIt) {
+ while (!trackBuffer.decodeQueue.empty()) {
if (!m_private->isReadyForMoreSamples(trackID)) {
m_private->notifyClientWhenReadyForMoreSamples(trackID);
break;
}
- RefPtr<MediaSample> sample = sampleIt->second;
+ // FIXME(rdar://problem/20635969): Remove this re-entrancy protection when the aforementioned radar is resolved; protecting
+ // against re-entrancy introduces a small inefficency when removing appended samples from the decode queue one at a time
+ // rather than when all samples have been enqueued.
+ auto sample = trackBuffer.decodeQueue.begin()->second;
+ trackBuffer.decodeQueue.erase(trackBuffer.decodeQueue.begin());
+
+ // Do not enqueue samples spanning a significant unbuffered gap.
+ // NOTE: one second is somewhat arbitrary. MediaSource::monitorSourceBuffers() is run
+ // on the playbackTimer, which is effectively every 350ms. Allowing > 350ms gap between
+ // enqueued samples allows for situations where we overrun the end of a buffered range
+ // but don't notice for 350s of playback time, and the client can enqueue data for the
+ // new current time without triggering this early return.
+ // FIXME(135867): Make this gap detection logic less arbitrary.
+ MediaTime oneSecond(1, 1);
+ if (trackBuffer.lastEnqueuedDecodeEndTime.isValid() && sample->decodeTime() - trackBuffer.lastEnqueuedDecodeEndTime > oneSecond)
+ break;
+
trackBuffer.lastEnqueuedPresentationTime = sample->presentationTime();
- m_private->enqueueSample(sample.release(), trackID);
+ trackBuffer.lastEnqueuedDecodeEndTime = sample->decodeTime() + sample->duration();
+ m_private->enqueueSample(sample.releaseNonNull(), trackID);
#if !LOG_DISABLED
++enqueuedSamples;
#endif
+ }
+
+ LOG(MediaSource, "SourceBuffer::provideMediaData(%p) - Enqueued %u samples", this, enqueuedSamples);
+}
+
+void SourceBuffer::reenqueueMediaForTime(TrackBuffer& trackBuffer, const AtomicString& trackID, const MediaTime& time)
+{
+ m_private->flush(trackID);
+ trackBuffer.decodeQueue.clear();
+ // Find the sample which contains the current presentation time.
+ auto currentSamplePTSIterator = trackBuffer.samples.presentationOrder().findSampleContainingPresentationTime(time);
+
+ if (currentSamplePTSIterator == trackBuffer.samples.presentationOrder().end())
+ currentSamplePTSIterator = trackBuffer.samples.presentationOrder().findSampleStartingOnOrAfterPresentationTime(time);
+
+ if (currentSamplePTSIterator == trackBuffer.samples.presentationOrder().end()
+ || (currentSamplePTSIterator->first - time) > MediaSource::currentTimeFudgeFactor())
+ return;
+
+ // Seach backward for the previous sync sample.
+ DecodeOrderSampleMap::KeyType decodeKey(currentSamplePTSIterator->second->decodeTime(), currentSamplePTSIterator->second->presentationTime());
+ auto currentSampleDTSIterator = trackBuffer.samples.decodeOrder().findSampleWithDecodeKey(decodeKey);
+ ASSERT(currentSampleDTSIterator != trackBuffer.samples.decodeOrder().end());
+
+ auto reverseCurrentSampleIter = --DecodeOrderSampleMap::reverse_iterator(currentSampleDTSIterator);
+ auto reverseLastSyncSampleIter = trackBuffer.samples.decodeOrder().findSyncSamplePriorToDecodeIterator(reverseCurrentSampleIter);
+ if (reverseLastSyncSampleIter == trackBuffer.samples.decodeOrder().rend())
+ return;
+
+ // Fill the decode queue with the non-displaying samples.
+ for (auto iter = reverseLastSyncSampleIter; iter != reverseCurrentSampleIter; --iter) {
+ auto copy = iter->second->createNonDisplayingCopy();
+ DecodeOrderSampleMap::KeyType decodeKey(copy->decodeTime(), copy->presentationTime());
+ trackBuffer.decodeQueue.insert(DecodeOrderSampleMap::MapType::value_type(decodeKey, WTFMove(copy)));
+ }
+
+ if (!trackBuffer.decodeQueue.empty()) {
+ auto& lastSample = trackBuffer.decodeQueue.rbegin()->second;
+ trackBuffer.lastEnqueuedPresentationTime = lastSample->presentationTime();
+ trackBuffer.lastEnqueuedDecodeEndTime = lastSample->decodeTime();
+ } else {
+ trackBuffer.lastEnqueuedPresentationTime = MediaTime::invalidTime();
+ trackBuffer.lastEnqueuedDecodeEndTime = MediaTime::invalidTime();
}
- trackBuffer.decodeQueue.erase(trackBuffer.decodeQueue.begin(), sampleIt);
- LOG(Media, "SourceBuffer::provideMediaData(%p) - Enqueued %u samples", this, enqueuedSamples);
+ // Fill the decode queue with the remaining samples.
+ for (auto iter = currentSampleDTSIterator; iter != trackBuffer.samples.decodeOrder().end(); ++iter)
+ trackBuffer.decodeQueue.insert(*iter);
+ provideMediaData(trackBuffer, trackID);
+
+ trackBuffer.needsReenqueueing = false;
}
+
void SourceBuffer::didDropSample()
{
if (!isRemoved())
m_source->mediaElement()->incrementDroppedFrameCount();
}
+void SourceBuffer::monitorBufferingRate()
+{
+ double now = monotonicallyIncreasingTime();
+ double interval = now - m_timeOfBufferingMonitor;
+ double rateSinceLastMonitor = m_bufferedSinceLastMonitor / interval;
+
+ m_timeOfBufferingMonitor = now;
+ m_bufferedSinceLastMonitor = 0;
+
+ m_averageBufferRate += (interval * ExponentialMovingAverageCoefficient) * (rateSinceLastMonitor - m_averageBufferRate);
+
+ LOG(MediaSource, "SourceBuffer::monitorBufferingRate(%p) - m_avegareBufferRate: %lf", this, m_averageBufferRate);
+}
+
+void SourceBuffer::updateBufferedFromTrackBuffers()
+{
+ // 3.1 Attributes, buffered
+ // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#dom-sourcebuffer-buffered
+
+ // 2. Let highest end time be the largest track buffer ranges end time across all the track buffers managed by this SourceBuffer object.
+ MediaTime highestEndTime = MediaTime::negativeInfiniteTime();
+ for (auto& trackBuffer : m_trackBufferMap.values()) {
+ if (!trackBuffer.buffered.length())
+ continue;
+ highestEndTime = std::max(highestEndTime, trackBuffer.buffered.maximumBufferedTime());
+ }
+
+ // NOTE: Short circuit the following if none of the TrackBuffers have buffered ranges to avoid generating
+ // a single range of {0, 0}.
+ if (highestEndTime.isNegativeInfinite()) {
+ m_buffered->ranges() = PlatformTimeRanges();
+ return;
+ }
+
+ // 3. Let intersection ranges equal a TimeRange object containing a single range from 0 to highest end time.
+ PlatformTimeRanges intersectionRanges { MediaTime::zeroTime(), highestEndTime };
+
+ // 4. For each audio and video track buffer managed by this SourceBuffer, run the following steps:
+ for (auto& trackBuffer : m_trackBufferMap.values()) {
+ // 4.1 Let track ranges equal the track buffer ranges for the current track buffer.
+ PlatformTimeRanges trackRanges = trackBuffer.buffered;
+ // 4.2 If readyState is "ended", then set the end time on the last range in track ranges to highest end time.
+ if (m_source->isEnded())
+ trackRanges.add(trackRanges.maximumBufferedTime(), highestEndTime);
+
+ // 4.3 Let new intersection ranges equal the intersection between the intersection ranges and the track ranges.
+ // 4.4 Replace the ranges in intersection ranges with the new intersection ranges.
+ intersectionRanges.intersectWith(trackRanges);
+ }
+
+ // 5. If intersection ranges does not contain the exact same range information as the current value of this attribute,
+ // then update the current value of this attribute to intersection ranges.
+ m_buffered->ranges() = intersectionRanges;
+ setBufferedDirty(true);
+}
+
+bool SourceBuffer::canPlayThroughRange(PlatformTimeRanges& ranges)
+{
+ if (isRemoved())
+ return false;
+
+ monitorBufferingRate();
+
+ // Assuming no fluctuations in the buffering rate, loading 1 second per second or greater
+ // means indefinite playback. This could be improved by taking jitter into account.
+ if (m_averageBufferRate > 1)
+ return true;
+
+ // Add up all the time yet to be buffered.
+ MediaTime currentTime = m_source->currentTime();
+ MediaTime duration = m_source->duration();
+
+ PlatformTimeRanges unbufferedRanges = ranges;
+ unbufferedRanges.invert();
+ unbufferedRanges.intersectWith(PlatformTimeRanges(currentTime, std::max(currentTime, duration)));
+ MediaTime unbufferedTime = unbufferedRanges.totalDuration();
+ if (!unbufferedTime.isValid())
+ return true;
+
+ MediaTime timeRemaining = duration - currentTime;
+ return unbufferedTime.toDouble() / m_averageBufferRate < timeRemaining.toDouble();
+}
+
+size_t SourceBuffer::extraMemoryCost() const
+{
+ size_t extraMemoryCost = m_pendingAppendData.capacity();
+ for (auto& trackBuffer : m_trackBufferMap.values())
+ extraMemoryCost += trackBuffer.samples.sizeInBytes();
+
+ return extraMemoryCost;
+}
+
+void SourceBuffer::reportExtraMemoryAllocated()
+{
+ size_t extraMemoryCost = this->extraMemoryCost();
+ if (extraMemoryCost <= m_reportedExtraMemoryCost)
+ return;
+
+ size_t extraMemoryCostDelta = extraMemoryCost - m_reportedExtraMemoryCost;
+ m_reportedExtraMemoryCost = extraMemoryCost;
+
+ JSC::JSLockHolder lock(scriptExecutionContext()->vm());
+ // FIXME: Adopt reportExtraMemoryVisited, and switch to reportExtraMemoryAllocated.
+ // https://bugs.webkit.org/show_bug.cgi?id=142595
+ scriptExecutionContext()->vm().heap.deprecatedReportExtraMemory(extraMemoryCostDelta);
+}
+
+Vector<String> SourceBuffer::bufferedSamplesForTrackID(const AtomicString& trackID)
+{
+ auto it = m_trackBufferMap.find(trackID);
+ if (it == m_trackBufferMap.end())
+ return Vector<String>();
+
+ TrackBuffer& trackBuffer = it->value;
+ Vector<String> sampleDescriptions;
+ for (auto& pair : trackBuffer.samples.decodeOrder())
+ sampleDescriptions.append(toString(*pair.second));
+
+ return sampleDescriptions;
+}
+
+Vector<String> SourceBuffer::enqueuedSamplesForTrackID(const AtomicString& trackID)
+{
+ return m_private->enqueuedSamplesForTrackID(trackID);
+}
+
+Document& SourceBuffer::document() const
+{
+ ASSERT(scriptExecutionContext());
+ return downcast<Document>(*scriptExecutionContext());
+}
+
+ExceptionOr<void> SourceBuffer::setMode(AppendMode newMode)
+{
+ // 3.1 Attributes - mode
+ // http://www.w3.org/TR/media-source/#widl-SourceBuffer-mode
+
+ // On setting, run the following steps:
+
+ // 1. Let new mode equal the new value being assigned to this attribute.
+ // 2. If generate timestamps flag equals true and new mode equals "segments", then throw an INVALID_ACCESS_ERR exception and abort these steps.
+ if (m_shouldGenerateTimestamps && newMode == AppendMode::Segments)
+ return Exception { INVALID_ACCESS_ERR };
+
+ // 3. If this object has been removed from the sourceBuffers attribute of the parent media source, then throw an INVALID_STATE_ERR exception and abort these steps.
+ // 4. If the updating attribute equals true, then throw an INVALID_STATE_ERR exception and abort these steps.
+ if (isRemoved() || m_updating)
+ return Exception { INVALID_STATE_ERR };
+
+ // 5. If the readyState attribute of the parent media source is in the "ended" state then run the following steps:
+ if (m_source->isEnded()) {
+ // 5.1. Set the readyState attribute of the parent media source to "open"
+ // 5.2. Queue a task to fire a simple event named sourceopen at the parent media source.
+ m_source->openIfInEndedState();
+ }
+
+ // 6. If the append state equals PARSING_MEDIA_SEGMENT, then throw an INVALID_STATE_ERR and abort these steps.
+ if (m_appendState == ParsingMediaSegment)
+ return Exception { INVALID_STATE_ERR };
+
+ // 7. If the new mode equals "sequence", then set the group start timestamp to the group end timestamp.
+ if (newMode == AppendMode::Sequence)
+ m_groupStartTimestamp = m_groupEndTimestamp;
+
+ // 8. Update the attribute to new mode.
+ m_mode = newMode;
+
+ return { };
+}
+
} // namespace WebCore
#endif
diff --git a/Source/WebCore/Modules/mediasource/SourceBuffer.h b/Source/WebCore/Modules/mediasource/SourceBuffer.h
index 163405045..5a142d4ca 100644
--- a/Source/WebCore/Modules/mediasource/SourceBuffer.h
+++ b/Source/WebCore/Modules/mediasource/SourceBuffer.h
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2013 Google Inc. All rights reserved.
+ * Copyright (C) 2013-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
@@ -28,130 +29,167 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef SourceBuffer_h
-#define SourceBuffer_h
+#pragma once
#if ENABLE(MEDIA_SOURCE)
#include "ActiveDOMObject.h"
#include "AudioTrack.h"
#include "EventTarget.h"
-#include "ExceptionCode.h"
+#include "ExceptionOr.h"
#include "GenericEventQueue.h"
-#include "ScriptWrappable.h"
#include "SourceBufferPrivateClient.h"
#include "TextTrack.h"
#include "Timer.h"
#include "VideoTrack.h"
-#include <runtime/ArrayBufferView.h>
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/text/WTFString.h>
namespace WebCore {
class AudioTrackList;
+class BufferSource;
class MediaSource;
+class PlatformTimeRanges;
class SourceBufferPrivate;
class TextTrackList;
class TimeRanges;
class VideoTrackList;
-class SourceBuffer final : public RefCounted<SourceBuffer>, public ActiveDOMObject, public EventTargetWithInlineData, public ScriptWrappable, public SourceBufferPrivateClient, public AudioTrackClient, public VideoTrackClient, public TextTrackClient {
+class SourceBuffer final : public RefCounted<SourceBuffer>, public ActiveDOMObject, public EventTargetWithInlineData, private SourceBufferPrivateClient, private AudioTrackClient, private VideoTrackClient, private TextTrackClient {
public:
- static PassRef<SourceBuffer> create(PassRef<SourceBufferPrivate>, MediaSource*);
-
+ static Ref<SourceBuffer> create(Ref<SourceBufferPrivate>&&, MediaSource*);
virtual ~SourceBuffer();
- // SourceBuffer.idl methods
bool updating() const { return m_updating; }
- PassRefPtr<TimeRanges> buffered(ExceptionCode&) const;
- const RefPtr<TimeRanges>& buffered() const;
+ ExceptionOr<Ref<TimeRanges>> buffered() const;
double timestampOffset() const;
- void setTimestampOffset(double, ExceptionCode&);
- void appendBuffer(PassRefPtr<ArrayBuffer> data, ExceptionCode&);
- void appendBuffer(PassRefPtr<ArrayBufferView> data, ExceptionCode&);
- void abort(ExceptionCode&);
+ ExceptionOr<void> setTimestampOffset(double);
+
+#if ENABLE(VIDEO_TRACK)
+ VideoTrackList& videoTracks();
+ AudioTrackList& audioTracks();
+ TextTrackList& textTracks();
+#endif
+
+ double appendWindowStart() const;
+ ExceptionOr<void> setAppendWindowStart(double);
+ double appendWindowEnd() const;
+ ExceptionOr<void> setAppendWindowEnd(double);
+
+ ExceptionOr<void> appendBuffer(const BufferSource&);
+ ExceptionOr<void> abort();
+ ExceptionOr<void> remove(double start, double end);
+ ExceptionOr<void> remove(const MediaTime&, const MediaTime&);
+
+ const TimeRanges& bufferedInternal() const { ASSERT(m_buffered); return *m_buffered; }
void abortIfUpdating();
void removedFromMediaSource();
- const MediaTime& highestPresentationEndTimestamp() const { return m_highestPresentationEndTimestamp; }
+ void seekToTime(const MediaTime&);
-#if ENABLE(VIDEO_TRACK)
- VideoTrackList* videoTracks();
- AudioTrackList* audioTracks();
- TextTrackList* textTracks();
-#endif
+ bool canPlayThroughRange(PlatformTimeRanges&);
+
+ bool hasVideo() const;
+
+ bool active() const { return m_active; }
+
+ ScriptExecutionContext* scriptExecutionContext() const final { return ActiveDOMObject::scriptExecutionContext(); }
+
+ using RefCounted::ref;
+ using RefCounted::deref;
+
+ struct TrackBuffer;
+
+ Document& document() const;
+
+ enum class AppendMode { Segments, Sequence };
+ AppendMode mode() const { return m_mode; }
+ ExceptionOr<void> setMode(AppendMode);
- // ActiveDOMObject interface
- virtual bool hasPendingActivity() const override;
- virtual void stop() override;
+ void setShouldGenerateTimestamps(bool flag) { m_shouldGenerateTimestamps = flag; }
- // EventTarget interface
- virtual ScriptExecutionContext* scriptExecutionContext() const override { return ActiveDOMObject::scriptExecutionContext(); }
- virtual EventTargetInterface eventTargetInterface() const override { return SourceBufferEventTargetInterfaceType; }
+ bool isBufferedDirty() const { return m_bufferedDirty; }
+ void setBufferedDirty(bool flag) { m_bufferedDirty = flag; }
- using RefCounted<SourceBuffer>::ref;
- using RefCounted<SourceBuffer>::deref;
+ MediaTime highestPresentationTimestamp() const;
+ void readyStateChanged();
-protected:
- // EventTarget interface
- virtual void refEventTarget() override { ref(); }
- virtual void derefEventTarget() override { deref(); }
+ bool hasPendingActivity() const final;
private:
- SourceBuffer(PassRef<SourceBufferPrivate>, MediaSource*);
+ SourceBuffer(Ref<SourceBufferPrivate>&&, MediaSource*);
- // SourceBufferPrivateClient
- virtual void sourceBufferPrivateDidEndStream(SourceBufferPrivate*, const WTF::AtomicString&) override;
- virtual void sourceBufferPrivateDidReceiveInitializationSegment(SourceBufferPrivate*, const InitializationSegment&) override;
- virtual void sourceBufferPrivateDidReceiveSample(SourceBufferPrivate*, PassRefPtr<MediaSample>) override;
- virtual bool sourceBufferPrivateHasAudio(const SourceBufferPrivate*) const override;
- virtual bool sourceBufferPrivateHasVideo(const SourceBufferPrivate*) const override;
- virtual void sourceBufferPrivateDidBecomeReadyForMoreSamples(SourceBufferPrivate*, AtomicString trackID) override;
- virtual void sourceBufferPrivateSeekToTime(SourceBufferPrivate*, const MediaTime&);
- virtual MediaTime sourceBufferPrivateFastSeekTimeForMediaTime(SourceBufferPrivate*, const MediaTime&, const MediaTime& negativeThreshold, const MediaTime& positiveThreshold);
+ void refEventTarget() final { ref(); }
+ void derefEventTarget() final { deref(); }
+ void stop() final;
+ const char* activeDOMObjectName() const final;
+ bool canSuspendForDocumentSuspension() const final;
- // AudioTrackClient
- virtual void audioTrackEnabledChanged(AudioTrack*) override;
+ void sourceBufferPrivateDidReceiveInitializationSegment(const InitializationSegment&) final;
+ void sourceBufferPrivateDidReceiveSample(MediaSample&) final;
+ bool sourceBufferPrivateHasAudio() const final;
+ bool sourceBufferPrivateHasVideo() const final;
+ void sourceBufferPrivateDidBecomeReadyForMoreSamples(const AtomicString& trackID) final;
+ MediaTime sourceBufferPrivateFastSeekTimeForMediaTime(const MediaTime&, const MediaTime& negativeThreshold, const MediaTime& positiveThreshold) final;
+ void sourceBufferPrivateAppendComplete(AppendResult) final;
+ void sourceBufferPrivateDidReceiveRenderingError(int errorCode) final;
- // VideoTrackClient
- virtual void videoTrackSelectedChanged(VideoTrack*) override;
+ void audioTrackEnabledChanged(AudioTrack&) final;
+ void videoTrackSelectedChanged(VideoTrack&) final;
- // TextTrackClient
- virtual void textTrackKindChanged(TextTrack*) override;
- virtual void textTrackModeChanged(TextTrack*) override;
- virtual void textTrackAddCues(TextTrack*, const TextTrackCueList*) override;
- virtual void textTrackRemoveCues(TextTrack*, const TextTrackCueList*) override;
- virtual void textTrackAddCue(TextTrack*, PassRefPtr<TextTrackCue>) override;
- virtual void textTrackRemoveCue(TextTrack*, PassRefPtr<TextTrackCue>) override;
+ void textTrackKindChanged(TextTrack&) final;
+ void textTrackModeChanged(TextTrack&) final;
+ void textTrackAddCues(TextTrack&, const TextTrackCueList&) final;
+ void textTrackRemoveCues(TextTrack&, const TextTrackCueList&) final;
+ void textTrackAddCue(TextTrack&, TextTrackCue&) final;
+ void textTrackRemoveCue(TextTrack&, TextTrackCue&) final;
- static const WTF::AtomicString& decodeError();
- static const WTF::AtomicString& networkError();
+ EventTargetInterface eventTargetInterface() const final { return SourceBufferEventTargetInterfaceType; }
bool isRemoved() const;
void scheduleEvent(const AtomicString& eventName);
- void appendBufferInternal(unsigned char*, unsigned, ExceptionCode&);
- void appendBufferTimerFired(Timer<SourceBuffer>&);
+ ExceptionOr<void> appendBufferInternal(const unsigned char*, unsigned);
+ void appendBufferTimerFired();
+ void resetParserState();
void setActive(bool);
bool validateInitializationSegment(const InitializationSegment&);
- struct TrackBuffer;
- void provideMediaData(TrackBuffer&, AtomicString trackID);
+ void reenqueueMediaForTime(TrackBuffer&, const AtomicString& trackID, const MediaTime&);
+ void provideMediaData(TrackBuffer&, const AtomicString& trackID);
void didDropSample();
+ void evictCodedFrames(size_t newDataSize);
+ size_t maximumBufferSize() const;
+
+ void monitorBufferingRate();
+
+ void removeTimerFired();
+ void removeCodedFrames(const MediaTime& start, const MediaTime& end);
+
+ size_t extraMemoryCost() const;
+ void reportExtraMemoryAllocated();
+
+ void updateBufferedFromTrackBuffers();
+
+ void appendError(bool);
+
+ bool hasAudio() const;
+
+ void rangeRemoval(const MediaTime&, const MediaTime&);
- RefPtr<SourceBufferPrivate> m_private;
+ friend class Internals;
+ WEBCORE_EXPORT Vector<String> bufferedSamplesForTrackID(const AtomicString&);
+ WEBCORE_EXPORT Vector<String> enqueuedSamplesForTrackID(const AtomicString&);
+
+ Ref<SourceBufferPrivate> m_private;
MediaSource* m_source;
GenericEventQueue m_asyncEventQueue;
-
- bool m_updating;
+ AppendMode m_mode { AppendMode::Segments };
Vector<unsigned char> m_pendingAppendData;
- Timer<SourceBuffer> m_appendBufferTimer;
+ Timer m_appendBufferTimer;
RefPtr<VideoTrackList> m_videoTracks;
RefPtr<AudioTrackList> m_audioTracks;
@@ -162,20 +200,36 @@ private:
Vector<AtomicString> m_textCodecs;
MediaTime m_timestampOffset;
- MediaTime m_highestPresentationEndTimestamp;
+ MediaTime m_appendWindowStart;
+ MediaTime m_appendWindowEnd;
+
+ MediaTime m_groupStartTimestamp;
+ MediaTime m_groupEndTimestamp;
HashMap<AtomicString, TrackBuffer> m_trackBufferMap;
- bool m_receivedFirstInitializationSegment;
RefPtr<TimeRanges> m_buffered;
- bool m_active;
+ bool m_bufferedDirty { true };
enum AppendStateType { WaitingForSegment, ParsingInitSegment, ParsingMediaSegment };
AppendStateType m_appendState;
+ double m_timeOfBufferingMonitor;
+ double m_bufferedSinceLastMonitor { 0 };
+ double m_averageBufferRate { 0 };
+
+ size_t m_reportedExtraMemoryCost { 0 };
+
+ MediaTime m_pendingRemoveStart;
+ MediaTime m_pendingRemoveEnd;
+ Timer m_removeTimer;
+
+ bool m_updating { false };
+ bool m_receivedFirstInitializationSegment { false };
+ bool m_active { false };
+ bool m_bufferFull { false };
+ bool m_shouldGenerateTimestamps { false };
};
} // namespace WebCore
#endif
-
-#endif
diff --git a/Source/WebCore/Modules/mediasource/SourceBuffer.idl b/Source/WebCore/Modules/mediasource/SourceBuffer.idl
index f13c6fcd3..9c847c72a 100644
--- a/Source/WebCore/Modules/mediasource/SourceBuffer.idl
+++ b/Source/WebCore/Modules/mediasource/SourceBuffer.idl
@@ -28,33 +28,44 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+[Conditional=MEDIA_SOURCE] enum AppendMode {
+ "segments",
+ "sequence"
+};
+
[
- Conditional=MEDIA_SOURCE,
- NoInterfaceObject,
ActiveDOMObject,
- EventTarget,
- JSGenerateToJSObject,
- JSGenerateToNativeObject,
+ Conditional=MEDIA_SOURCE,
+ ExportMacro=WEBCORE_EXPORT,
] interface SourceBuffer : EventTarget {
+ [SetterMayThrowException] attribute AppendMode mode;
readonly attribute boolean updating;
// Returns the time ranges buffered.
- [GetterRaisesException] readonly attribute TimeRanges buffered;
+ [GetterMayThrowException] readonly attribute TimeRanges buffered;
// Applies an offset to media segment timestamps.
- [SetterRaisesException] attribute double timestampOffset;
+ [SetterMayThrowException] attribute double timestampOffset;
- // Append segment data.
- [RaisesException] void appendBuffer(ArrayBuffer data);
- [RaisesException] void appendBuffer(ArrayBufferView data);
-
- // Abort the current segment append sequence.
- [RaisesException] void abort();
-
// Track support
[Conditional=VIDEO_TRACK] readonly attribute AudioTrackList audioTracks;
[Conditional=VIDEO_TRACK] readonly attribute VideoTrackList videoTracks;
[Conditional=VIDEO_TRACK] readonly attribute TextTrackList textTracks;
-};
+ [SetterMayThrowException] attribute double appendWindowStart;
+ [SetterMayThrowException] attribute unrestricted double appendWindowEnd;
+
+ // Append segment data.
+ [MayThrowException] void appendBuffer(BufferSource data);
+
+ // Abort the current segment append sequence.
+ [MayThrowException] void abort();
+ [MayThrowException] void remove(unrestricted double start, unrestricted double end);
+
+ attribute EventHandler onupdatestart;
+ attribute EventHandler onupdate;
+ attribute EventHandler onupdateend;
+ attribute EventHandler onerror;
+ attribute EventHandler onabort;
+};
diff --git a/Source/WebCore/Modules/mediasource/SourceBufferList.cpp b/Source/WebCore/Modules/mediasource/SourceBufferList.cpp
index 2dd7b81f0..005973709 100644
--- a/Source/WebCore/Modules/mediasource/SourceBufferList.cpp
+++ b/Source/WebCore/Modules/mediasource/SourceBufferList.cpp
@@ -34,6 +34,7 @@
#if ENABLE(MEDIA_SOURCE)
#include "Event.h"
+#include "EventNames.h"
#include "SourceBuffer.h"
namespace WebCore {
@@ -49,15 +50,15 @@ SourceBufferList::~SourceBufferList()
ASSERT(m_list.isEmpty());
}
-void SourceBufferList::add(PassRefPtr<SourceBuffer> buffer)
+void SourceBufferList::add(Ref<SourceBuffer>&& buffer)
{
- m_list.append(buffer);
+ m_list.append(WTFMove(buffer));
scheduleEvent(eventNames().addsourcebufferEvent);
}
-void SourceBufferList::remove(SourceBuffer* buffer)
+void SourceBufferList::remove(SourceBuffer& buffer)
{
- size_t index = m_list.find(buffer);
+ size_t index = m_list.find(&buffer);
if (index == notFound)
return;
m_list.remove(index);
@@ -70,12 +71,30 @@ void SourceBufferList::clear()
scheduleEvent(eventNames().removesourcebufferEvent);
}
+void SourceBufferList::swap(Vector<RefPtr<SourceBuffer>>& other)
+{
+ int changeInSize = other.size() - m_list.size();
+ int addedEntries = 0;
+ for (auto& sourceBuffer : other) {
+ if (!m_list.contains(sourceBuffer))
+ ++addedEntries;
+ }
+ int removedEntries = addedEntries - changeInSize;
+
+ m_list.swap(other);
+
+ if (addedEntries)
+ scheduleEvent(eventNames().addsourcebufferEvent);
+ if (removedEntries)
+ scheduleEvent(eventNames().removesourcebufferEvent);
+}
+
void SourceBufferList::scheduleEvent(const AtomicString& eventName)
{
- RefPtr<Event> event = Event::create(eventName, false, false);
+ auto event = Event::create(eventName, false, false);
event->setTarget(this);
- m_asyncEventQueue.enqueueEvent(event.release());
+ m_asyncEventQueue.enqueueEvent(WTFMove(event));
}
diff --git a/Source/WebCore/Modules/mediasource/SourceBufferList.h b/Source/WebCore/Modules/mediasource/SourceBufferList.h
index fa237b385..fc47f05fe 100644
--- a/Source/WebCore/Modules/mediasource/SourceBufferList.h
+++ b/Source/WebCore/Modules/mediasource/SourceBufferList.h
@@ -28,8 +28,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef SourceBufferList_h
-#define SourceBufferList_h
+#pragma once
#if ENABLE(MEDIA_SOURCE)
@@ -43,28 +42,29 @@ namespace WebCore {
class SourceBuffer;
-class SourceBufferList final : public RefCounted<SourceBufferList>, public ScriptWrappable, public EventTargetWithInlineData {
+class SourceBufferList final : public RefCounted<SourceBufferList>, public EventTargetWithInlineData {
public:
- static PassRefPtr<SourceBufferList> create(ScriptExecutionContext* context)
+ static Ref<SourceBufferList> create(ScriptExecutionContext* context)
{
- return adoptRef(new SourceBufferList(context));
+ return adoptRef(*new SourceBufferList(context));
}
virtual ~SourceBufferList();
unsigned long length() const { return m_list.size(); }
- SourceBuffer* item(unsigned long index) const { return (index < m_list.size()) ? m_list[index].get() : 0; }
+ SourceBuffer* item(unsigned long index) const { return (index < m_list.size()) ? m_list[index].get() : nullptr; }
- void add(PassRefPtr<SourceBuffer>);
- void remove(SourceBuffer*);
- bool contains(SourceBuffer* buffer) { return m_list.find(buffer) != notFound; }
+ void add(Ref<SourceBuffer>&&);
+ void remove(SourceBuffer&);
+ bool contains(SourceBuffer& buffer) { return m_list.find(&buffer) != notFound; }
void clear();
+ void swap(Vector<RefPtr<SourceBuffer>>&);
Vector<RefPtr<SourceBuffer>>::iterator begin() { return m_list.begin(); }
Vector<RefPtr<SourceBuffer>>::iterator end() { return m_list.end(); }
// EventTarget interface
- virtual EventTargetInterface eventTargetInterface() const override { return SourceBufferListEventTargetInterfaceType; }
- virtual ScriptExecutionContext* scriptExecutionContext() const override { return m_scriptExecutionContext; }
+ EventTargetInterface eventTargetInterface() const override { return SourceBufferListEventTargetInterfaceType; }
+ ScriptExecutionContext* scriptExecutionContext() const override { return m_scriptExecutionContext; }
using RefCounted<SourceBufferList>::ref;
using RefCounted<SourceBufferList>::deref;
@@ -74,8 +74,8 @@ private:
void scheduleEvent(const AtomicString&);
- virtual void refEventTarget() override { ref(); }
- virtual void derefEventTarget() override { deref(); }
+ void refEventTarget() override { ref(); }
+ void derefEventTarget() override { deref(); }
ScriptExecutionContext* m_scriptExecutionContext;
GenericEventQueue m_asyncEventQueue;
@@ -85,6 +85,4 @@ private:
} // namespace WebCore
-#endif
-
-#endif
+#endif // ENABLE(MEDIA_SOURCE)
diff --git a/Source/WebCore/Modules/mediasource/SourceBufferList.idl b/Source/WebCore/Modules/mediasource/SourceBufferList.idl
index d3b36df25..f82ab6ca2 100644
--- a/Source/WebCore/Modules/mediasource/SourceBufferList.idl
+++ b/Source/WebCore/Modules/mediasource/SourceBufferList.idl
@@ -30,14 +30,13 @@
[
Conditional=MEDIA_SOURCE,
- NoInterfaceObject,
- EventTarget,
- JSGenerateToJSObject,
- JSGenerateToNativeObject,
GenerateIsReachable=Impl,
CallWith=ScriptExecutionContext,
] interface SourceBufferList : EventTarget {
readonly attribute unsigned long length;
getter SourceBuffer item(unsigned long index);
+
+ attribute EventHandler onaddsourcebuffer;
+ attribute EventHandler onremovesourcebuffer;
};
diff --git a/Source/WebCore/Modules/webdatabase/AbstractSQLStatement.h b/Source/WebCore/Modules/mediasource/TextTrackMediaSource.h
index 2196a9fc8..ad21acff0 100644
--- a/Source/WebCore/Modules/webdatabase/AbstractSQLStatement.h
+++ b/Source/WebCore/Modules/mediasource/TextTrackMediaSource.h
@@ -23,29 +23,21 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AbstractSQLStatement_h
-#define AbstractSQLStatement_h
+#pragma once
-#if ENABLE(SQL_DATABASE)
+#if ENABLE(MEDIA_SOURCE) && ENABLE(VIDEO_TRACK)
-#include <wtf/ThreadSafeRefCounted.h>
+#include "TextTrack.h"
namespace WebCore {
-class AbstractSQLStatementBackend;
+class SourceBuffer;
-class AbstractSQLStatement {
+class TextTrackMediaSource {
public:
- virtual ~AbstractSQLStatement() { }
-
- virtual void setBackend(AbstractSQLStatementBackend*) = 0;
-
- virtual bool hasCallback() = 0;
- virtual bool hasErrorCallback() = 0;
+ static SourceBuffer* sourceBuffer(TextTrack& track) { return track.sourceBuffer(); }
};
} // namespace WebCore
-#endif // ENABLE(SQL_DATABASE)
-
-#endif // AbstractSQLStatement_h
+#endif // ENABLE(MEDIA_SOURCE) && ENABLE(VIDEO_TRACK)
diff --git a/Source/WebCore/Modules/mediasource/TextTrackMediaSource.idl b/Source/WebCore/Modules/mediasource/TextTrackMediaSource.idl
index 567dd3506..88ed22f31 100644
--- a/Source/WebCore/Modules/mediasource/TextTrackMediaSource.idl
+++ b/Source/WebCore/Modules/mediasource/TextTrackMediaSource.idl
@@ -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
diff --git a/Source/WebCore/Modules/mediasource/VideoPlaybackQuality.cpp b/Source/WebCore/Modules/mediasource/VideoPlaybackQuality.cpp
index f7a5cf83c..f19561f30 100644
--- a/Source/WebCore/Modules/mediasource/VideoPlaybackQuality.cpp
+++ b/Source/WebCore/Modules/mediasource/VideoPlaybackQuality.cpp
@@ -28,9 +28,9 @@
namespace WebCore {
-RefPtr<VideoPlaybackQuality> VideoPlaybackQuality::create(double creationTime, unsigned long totalVideoFrames, unsigned long droppedVideoFrames, unsigned long corruptedVideoFrames, double totalFrameDelay)
+Ref<VideoPlaybackQuality> VideoPlaybackQuality::create(double creationTime, unsigned long totalVideoFrames, unsigned long droppedVideoFrames, unsigned long corruptedVideoFrames, double totalFrameDelay)
{
- return adoptRef(new VideoPlaybackQuality(creationTime, totalVideoFrames, droppedVideoFrames, corruptedVideoFrames, totalFrameDelay));
+ return adoptRef(*new VideoPlaybackQuality(creationTime, totalVideoFrames, droppedVideoFrames, corruptedVideoFrames, totalFrameDelay));
}
VideoPlaybackQuality::VideoPlaybackQuality(double creationTime, unsigned long totalVideoFrames, unsigned long droppedVideoFrames, unsigned long corruptedVideoFrames, double totalFrameDelay)
diff --git a/Source/WebCore/Modules/mediasource/VideoPlaybackQuality.h b/Source/WebCore/Modules/mediasource/VideoPlaybackQuality.h
index 5b89377b7..2fff6d35f 100644
--- a/Source/WebCore/Modules/mediasource/VideoPlaybackQuality.h
+++ b/Source/WebCore/Modules/mediasource/VideoPlaybackQuality.h
@@ -23,8 +23,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef VideoPlaybackQuality_h
-#define VideoPlaybackQuality_h
+#pragma once
#include <wtf/RefCounted.h>
#include <wtf/RefPtr.h>
@@ -34,7 +33,7 @@ namespace WebCore {
class VideoPlaybackQuality : public RefCounted<VideoPlaybackQuality> {
WTF_MAKE_NONCOPYABLE(VideoPlaybackQuality)
public:
- static RefPtr<VideoPlaybackQuality> create(double creationTime, unsigned long totalVideoFrames, unsigned long droppedVideoFrames, unsigned long corruptedVideoFrames, double totalFrameDelay);
+ static Ref<VideoPlaybackQuality> create(double creationTime, unsigned long totalVideoFrames, unsigned long droppedVideoFrames, unsigned long corruptedVideoFrames, double totalFrameDelay);
double creationTime() const { return m_creationTime; }
unsigned long totalVideoFrames() const { return m_totalVideoFrames; }
@@ -52,6 +51,4 @@ protected:
double m_totalFrameDelay;
};
-}
-
-#endif // VideoPlaybackQuality_h
+} // namespace WebCore
diff --git a/Source/WebCore/Modules/mediasource/VideoPlaybackQuality.idl b/Source/WebCore/Modules/mediasource/VideoPlaybackQuality.idl
index 65c619301..e78d0eb96 100644
--- a/Source/WebCore/Modules/mediasource/VideoPlaybackQuality.idl
+++ b/Source/WebCore/Modules/mediasource/VideoPlaybackQuality.idl
@@ -28,11 +28,11 @@
NoInterfaceObject,
ImplementationLacksVTable,
] interface VideoPlaybackQuality {
- readonly attribute double creationTime;
+ readonly attribute unrestricted double creationTime;
readonly attribute unsigned long totalVideoFrames;
readonly attribute unsigned long droppedVideoFrames;
readonly attribute unsigned long corruptedVideoFrames;
- readonly attribute double totalFrameDelay;
+ readonly attribute unrestricted double totalFrameDelay;
};
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseBase.h b/Source/WebCore/Modules/mediasource/VideoTrackMediaSource.h
index 573bf14d6..8e0f5f43e 100644
--- a/Source/WebCore/Modules/webdatabase/DatabaseBase.h
+++ b/Source/WebCore/Modules/mediasource/VideoTrackMediaSource.h
@@ -23,30 +23,19 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DatabaseBase_h
-#define DatabaseBase_h
+#pragma once
-#if ENABLE(SQL_DATABASE)
+#if ENABLE(MEDIA_SOURCE) && ENABLE(VIDEO_TRACK)
-#include <wtf/text/WTFString.h>
+#include "VideoTrack.h"
namespace WebCore {
-class ScriptExecutionContext;
-
-class DatabaseBase {
+class VideoTrackMediaSource {
public:
- ScriptExecutionContext* scriptExecutionContext() const;
- void logErrorMessage(const String& message);
-
-protected:
- DatabaseBase(ScriptExecutionContext*);
-
- RefPtr<ScriptExecutionContext> m_scriptExecutionContext;
+ static SourceBuffer* sourceBuffer(VideoTrack& track) { return track.sourceBuffer(); }
};
} // namespace WebCore
-#endif // ENABLE(SQL_DATABASE)
-
-#endif // DatabaseBase_h
+#endif // ENABLE(MEDIA_SOURCE) && ENABLE(VIDEO_TRACK)
diff --git a/Source/WebCore/Modules/mediasource/VideoTrackMediaSource.idl b/Source/WebCore/Modules/mediasource/VideoTrackMediaSource.idl
index 4866a28b0..0f7c65b25 100644
--- a/Source/WebCore/Modules/mediasource/VideoTrackMediaSource.idl
+++ b/Source/WebCore/Modules/mediasource/VideoTrackMediaSource.idl
@@ -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
diff --git a/Source/WebCore/Modules/mediastream/AllAudioCapabilities.h b/Source/WebCore/Modules/mediastream/AllAudioCapabilities.h
deleted file mode 100644
index 532956479..000000000
--- a/Source/WebCore/Modules/mediastream/AllAudioCapabilities.h
+++ /dev/null
@@ -1,59 +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 COMPUTER, 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
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (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 AllAudioCapabilities_h
-#define AllAudioCapabilities_h
-
-#if ENABLE(MEDIA_STREAM)
-
-#include "MediaStreamCapabilities.h"
-#include "MediaStreamSourceCapabilities.h"
-#include <wtf/text/WTFString.h>
-
-namespace WebCore {
-
-class CapabilityRange;
-class MediaStreamSourceCapabilities;
-
-class AllAudioCapabilities : public MediaStreamCapabilities {
-public:
- static RefPtr<AllAudioCapabilities> create(PassRefPtr<MediaStreamSourceCapabilities> capabilities)
- {
- return adoptRef(new AllAudioCapabilities(capabilities));
- }
- virtual ~AllAudioCapabilities() { }
-
-private:
- explicit AllAudioCapabilities(PassRefPtr<MediaStreamSourceCapabilities> capabilities)
- : MediaStreamCapabilities(capabilities)
- {
- }
-};
-
-} // namespace WebCore
-
-#endif // AllAudioCapabilities_h
-
-#endif
diff --git a/Source/WebCore/Modules/mediastream/AllVideoCapabilities.h b/Source/WebCore/Modules/mediastream/AllVideoCapabilities.h
deleted file mode 100644
index 667f214f3..000000000
--- a/Source/WebCore/Modules/mediastream/AllVideoCapabilities.h
+++ /dev/null
@@ -1,57 +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 COMPUTER, 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
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (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 AllVideoCapabilities_h
-#define AllVideoCapabilities_h
-
-#if ENABLE(MEDIA_STREAM)
-
-#include "MediaStreamCapabilities.h"
-
-namespace WebCore {
-
-class CapabilityRange;
-class MediaStreamSourceCapabilities;
-
-class AllVideoCapabilities : public MediaStreamCapabilities {
-public:
- static RefPtr<AllVideoCapabilities> create(PassRefPtr<MediaStreamSourceCapabilities> capabilities)
- {
- return adoptRef(new AllVideoCapabilities(capabilities));
- }
- virtual ~AllVideoCapabilities() { }
-
-private:
- explicit AllVideoCapabilities(PassRefPtr<MediaStreamSourceCapabilities> capabilities)
- : MediaStreamCapabilities(capabilities)
- {
- }
-};
-
-} // namespace WebCore
-
-#endif // AllVideoCapabilities_h
-
-#endif
diff --git a/Source/WebCore/Modules/mediastream/AllVideoCapabilities.idl b/Source/WebCore/Modules/mediastream/AllVideoCapabilities.idl
deleted file mode 100644
index 515137cff..000000000
--- a/Source/WebCore/Modules/mediastream/AllVideoCapabilities.idl
+++ /dev/null
@@ -1,38 +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 COMPUTER, 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
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (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=MEDIA_STREAM,
- JSGenerateToJSObject,
-] interface AllVideoCapabilities : MediaStreamCapabilities {
- readonly attribute DOMString[] sourceType;
- readonly attribute DOMString[] sourceId;
- readonly attribute CapabilityRange width;
- readonly attribute CapabilityRange height;
- readonly attribute CapabilityRange frameRate;
- readonly attribute CapabilityRange aspectRatio;
- readonly attribute DOMString[] facingMode;
-};
diff --git a/Source/WebCore/Modules/mediastream/AudioStreamTrack.cpp b/Source/WebCore/Modules/mediastream/AudioStreamTrack.cpp
deleted file mode 100644
index 101b09cb1..000000000
--- a/Source/WebCore/Modules/mediastream/AudioStreamTrack.cpp
+++ /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 COMPUTER, 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
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (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_STREAM)
-
-#include "AudioStreamTrack.h"
-
-#include "Dictionary.h"
-#include "ScriptExecutionContext.h"
-#include <wtf/NeverDestroyed.h>
-
-namespace WebCore {
-
-RefPtr<AudioStreamTrack> AudioStreamTrack::create(ScriptExecutionContext& context, const Dictionary& audioConstraints)
-{
- return adoptRef(new AudioStreamTrack(context, *MediaStreamTrackPrivate::create(0), &audioConstraints));
-}
-
-RefPtr<AudioStreamTrack> AudioStreamTrack::create(ScriptExecutionContext& context, MediaStreamTrackPrivate& privateTrack)
-{
- return adoptRef(new AudioStreamTrack(context, privateTrack, 0));
-}
-
-RefPtr<AudioStreamTrack> AudioStreamTrack::create(MediaStreamTrack& track)
-{
- return adoptRef(new AudioStreamTrack(track));
-}
-
-AudioStreamTrack::AudioStreamTrack(ScriptExecutionContext& context, MediaStreamTrackPrivate& privateTrack, const Dictionary* audioConstraints)
- : MediaStreamTrack(context, privateTrack, audioConstraints)
-{
-}
-
-AudioStreamTrack::AudioStreamTrack(MediaStreamTrack& track)
- : MediaStreamTrack(track)
-{
-}
-
-const AtomicString& AudioStreamTrack::kind() const
-{
- static NeverDestroyed<AtomicString> audioKind("audio", AtomicString::ConstructFromLiteral);
- return audioKind;
-}
-
-} // namespace WebCore
-
-#endif // ENABLE(MEDIA_STREAM)
diff --git a/Source/WebCore/Modules/mediastream/AudioStreamTrack.h b/Source/WebCore/Modules/mediastream/AudioStreamTrack.h
deleted file mode 100644
index 79346c38a..000000000
--- a/Source/WebCore/Modules/mediastream/AudioStreamTrack.h
+++ /dev/null
@@ -1,59 +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 COMPUTER, 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
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (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 AudioStreamTrack_h
-#define AudioStreamTrack_h
-
-#if ENABLE(MEDIA_STREAM)
-
-#include "MediaStreamTrack.h"
-#include <wtf/RefCounted.h>
-#include <wtf/Vector.h>
-
-namespace WebCore {
-
-class MediaStreamSource;
-class ScriptExecutionContext;
-
-class AudioStreamTrack final : public MediaStreamTrack {
-public:
- static RefPtr<AudioStreamTrack> create(ScriptExecutionContext&, const Dictionary&);
- static RefPtr<AudioStreamTrack> create(ScriptExecutionContext&, MediaStreamTrackPrivate&);
- static RefPtr<AudioStreamTrack> create(MediaStreamTrack&);
-
- virtual ~AudioStreamTrack() { }
-
- virtual const AtomicString& kind() const override;
-
-private:
- AudioStreamTrack(ScriptExecutionContext&, MediaStreamTrackPrivate&, const Dictionary*);
- explicit AudioStreamTrack(MediaStreamTrack&);
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(MEDIA_STREAM)
-
-#endif // AudioStreamTrack_h
diff --git a/Source/WebCore/Modules/mediastream/AudioStreamTrack.idl b/Source/WebCore/Modules/mediastream/AudioStreamTrack.idl
deleted file mode 100644
index 2f6e5411a..000000000
--- a/Source/WebCore/Modules/mediastream/AudioStreamTrack.idl
+++ /dev/null
@@ -1,31 +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 COMPUTER, 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
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (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=MEDIA_STREAM,
- Constructor(optional Dictionary audioConstraints),
- ConstructorCallWith=ScriptExecutionContext,
-] interface AudioStreamTrack : MediaStreamTrack {
-};
diff --git a/Source/WebCore/Modules/mediastream/CapabilityRange.cpp b/Source/WebCore/Modules/mediastream/CapabilityRange.cpp
deleted file mode 100644
index 6c125a47d..000000000
--- a/Source/WebCore/Modules/mediastream/CapabilityRange.cpp
+++ /dev/null
@@ -1,92 +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. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED 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
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (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_STREAM)
-
-#include "CapabilityRange.h"
-
-#include "JSDOMBinding.h"
-#include "MediaSourceStates.h"
-#include <bindings/ScriptValue.h>
-#include <interpreter/CallFrame.h>
-#include <runtime/JSCJSValue.h>
-
-using namespace JSC;
-
-namespace WebCore {
-
-RefPtr<CapabilityRange> CapabilityRange::create(const MediaStreamSourceCapabilityRange& rangeInfo)
-{
- return adoptRef(new CapabilityRange(rangeInfo));
-}
-
-CapabilityRange::CapabilityRange(const MediaStreamSourceCapabilityRange& rangeInfo)
- : m_rangeInfo(rangeInfo)
-{
-}
-
-static Deprecated::ScriptValue scriptValue(ExecState* exec, const MediaStreamSourceCapabilityRange::ValueUnion& value, MediaStreamSourceCapabilityRange::Type type)
-{
- // NOTE: the spec says:
- // ... an implementation should make a reasonable attempt to translate and scale the hardware's setting
- // onto the mapping provided by this specification. If this is not possible due to the user agent's
- // inability to retrieve a given capability from a source, then for CapabilityRange-typed capabilities,
- // the min and max fields will not be present on the returned dictionary, and the supported field will be false.
- // We don't do this currently because I don't know of an instance where it isn't possible to retrieve a source
- // capability, but when we have to deal with this we will need to mark CapabilityRange.min and CapabilityRange.max as
- // "Custom" and return jsUndefined() from the custom getter to support it.
-
- switch (type) {
- case MediaStreamSourceCapabilityRange::Float:
- return Deprecated::ScriptValue(exec->vm(), JSValue(value.asFloat));
- break;
- case MediaStreamSourceCapabilityRange::ULong:
- return Deprecated::ScriptValue(exec->vm(), JSValue(value.asULong));
- break;
- case MediaStreamSourceCapabilityRange::Undefined:
- return Deprecated::ScriptValue(exec->vm(), jsUndefined());
- break;
- }
-
- ASSERT_NOT_REACHED();
- return Deprecated::ScriptValue(exec->vm(), jsUndefined());
-}
-
-Deprecated::ScriptValue CapabilityRange::min(ExecState* exec) const
-{
- return scriptValue(exec, m_rangeInfo.min(), m_rangeInfo.type());
-}
-
-Deprecated::ScriptValue CapabilityRange::max(ExecState* exec) const
-{
- return scriptValue(exec, m_rangeInfo.max(), m_rangeInfo.type());
-}
-
-} // namespace WebCore
-
-#endif
diff --git a/Source/WebCore/Modules/mediastream/CapabilityRange.h b/Source/WebCore/Modules/mediastream/CapabilityRange.h
deleted file mode 100644
index 905ae3f02..000000000
--- a/Source/WebCore/Modules/mediastream/CapabilityRange.h
+++ /dev/null
@@ -1,60 +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. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED 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
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (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 CapabilityRange_h
-#define CapabilityRange_h
-
-#if ENABLE(MEDIA_STREAM)
-
-#include "MediaStreamSourceCapabilities.h"
-#include "ScriptWrappable.h"
-#include <bindings/ScriptValue.h>
-#include <interpreter/CallFrame.h>
-#include <wtf/RefCounted.h>
-
-namespace WebCore {
-
-class CapabilityRange : public RefCounted<CapabilityRange>, public ScriptWrappable {
-public:
- virtual ~CapabilityRange() { }
-
- static RefPtr<CapabilityRange> create(const MediaStreamSourceCapabilityRange&);
-
- Deprecated::ScriptValue min(JSC::ExecState*) const;
- Deprecated::ScriptValue max(JSC::ExecState*) const;
- bool supported() const { return m_rangeInfo.supported(); }
-
-private:
- CapabilityRange(const MediaStreamSourceCapabilityRange&);
-
- MediaStreamSourceCapabilityRange m_rangeInfo;
-};
-
-} // namespace WebCore
-
-#endif // CapabilityRange_h
-
-#endif
diff --git a/Source/WebCore/Modules/mediastream/DOMURLMediaStream.cpp b/Source/WebCore/Modules/mediastream/DOMURLMediaStream.cpp
index 0eeed9c9f..69dc73042 100644
--- a/Source/WebCore/Modules/mediastream/DOMURLMediaStream.cpp
+++ b/Source/WebCore/Modules/mediastream/DOMURLMediaStream.cpp
@@ -40,13 +40,10 @@
namespace WebCore {
-String DOMURLMediaStream::createObjectURL(ScriptExecutionContext* scriptExecutionContext, MediaStream* stream)
+String DOMURLMediaStream::createObjectURL(ScriptExecutionContext& scriptExecutionContext, MediaStream& stream)
{
// Since WebWorkers cannot obtain Stream objects, we should be on the main thread.
ASSERT(isMainThread());
-
- if (!scriptExecutionContext || !stream)
- return String();
return DOMURL::createPublicURL(scriptExecutionContext, stream);
}
diff --git a/Source/WebCore/Modules/mediastream/DOMURLMediaStream.h b/Source/WebCore/Modules/mediastream/DOMURLMediaStream.h
index 3e2d8c905..560f621e5 100644
--- a/Source/WebCore/Modules/mediastream/DOMURLMediaStream.h
+++ b/Source/WebCore/Modules/mediastream/DOMURLMediaStream.h
@@ -28,8 +28,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DOMURLMediaStream_h
-#define DOMURLMediaStream_h
+#pragma once
#if ENABLE(MEDIA_STREAM)
@@ -42,11 +41,9 @@ class ScriptExecutionContext;
class DOMURLMediaStream {
public:
- static String createObjectURL(ScriptExecutionContext*, MediaStream*);
+ static String createObjectURL(ScriptExecutionContext&, MediaStream&);
};
} // namespace WebCore
#endif // ENABLE(MEDIA_STREAM)
-
-#endif // URLMediaStream_h
diff --git a/Source/WebCore/Modules/mediastream/DOMURLMediaStream.idl b/Source/WebCore/Modules/mediastream/DOMURLMediaStream.idl
index 77b689761..d40ce6749 100644
--- a/Source/WebCore/Modules/mediastream/DOMURLMediaStream.idl
+++ b/Source/WebCore/Modules/mediastream/DOMURLMediaStream.idl
@@ -31,5 +31,5 @@
Conditional=MEDIA_STREAM
]
partial interface DOMURL {
- [CallWith=ScriptExecutionContext,TreatReturnedNullStringAs=Null] static DOMString createObjectURL(MediaStream? stream);
+ [CallWith=ScriptExecutionContext] static DOMString createObjectURL(MediaStream stream);
};
diff --git a/Source/WebCore/Modules/mediastream/RTCStatsResponse.idl b/Source/WebCore/Modules/mediastream/DoubleRange.h
index 16634d741..84a46bd60 100644
--- a/Source/WebCore/Modules/mediastream/RTCStatsResponse.idl
+++ b/Source/WebCore/Modules/mediastream/DoubleRange.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Google Inc. All rights reserved.
+ * 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
@@ -22,10 +22,19 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-[
- NoInterfaceObject,
- Conditional=MEDIA_STREAM,
-] interface RTCStatsResponse {
- sequence<RTCStatsReport> result();
- getter RTCStatsReport namedItem([Default=Undefined] optional DOMString name);
+#pragma once
+
+#if ENABLE(MEDIA_STREAM)
+
+#include <wtf/Optional.h>
+
+namespace WebCore {
+
+struct DoubleRange {
+ std::optional<double> max;
+ std::optional<double> min;
};
+
+}
+
+#endif
diff --git a/Source/WebCore/Modules/mediastream/RTCStatsCallback.idl b/Source/WebCore/Modules/mediastream/DoubleRange.idl
index b32f43d2f..1ca3724e0 100644
--- a/Source/WebCore/Modules/mediastream/RTCStatsCallback.idl
+++ b/Source/WebCore/Modules/mediastream/DoubleRange.idl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Google Inc. All rights reserved.
+ * 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
@@ -24,7 +24,8 @@
[
Conditional=MEDIA_STREAM,
-] callback interface RTCStatsCallback {
- boolean handleEvent(RTCStatsResponse response);
+ JSGenerateToJSObject,
+] dictionary DoubleRange {
+ double max;
+ double min;
};
-
diff --git a/Source/WebCore/Modules/mediastream/HTMLMediaElementMediaStream.cpp b/Source/WebCore/Modules/mediastream/HTMLMediaElementMediaStream.cpp
deleted file mode 100644
index 1dc905a57..000000000
--- a/Source/WebCore/Modules/mediastream/HTMLMediaElementMediaStream.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, 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 ``AS IS'' AND ANY
- * EXPRESS OR 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 HOLDERS 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 "HTMLMediaElementMediaStream.h"
-
-#if ENABLE(MEDIA_STREAM) && ENABLE(VIDEO)
-
-#include "HTMLMediaElement.h"
-#include "MediaStream.h"
-
-namespace WebCore {
-
-MediaStream* HTMLMediaElementMediaStream::srcObject(HTMLMediaElement* mediaElement, bool& isNull)
-{
- ASSERT(mediaElement);
- return mediaElement->srcObject();
-}
-
-void HTMLMediaElementMediaStream::setSrcObject(HTMLMediaElement* mediaElement, MediaStream* mediaStream)
-{
- ASSERT(mediaElement);
- mediaElement->setSrcObject(mediaStream);
-}
-
-} // namespace WebCore
-
-#endif // ENABLE(MEDIA_STREAM) && ENABLE(VIDEO)
diff --git a/Source/WebCore/Modules/mediastream/HTMLMediaElementMediaStream.h b/Source/WebCore/Modules/mediastream/HTMLMediaElementMediaStream.h
deleted file mode 100644
index 41bf48418..000000000
--- a/Source/WebCore/Modules/mediastream/HTMLMediaElementMediaStream.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, 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 ``AS IS'' AND ANY
- * EXPRESS OR 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 HOLDERS 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.
- */
-
-#ifndef HTMLMediaElementMediaStream_h
-#define HTMLMediaElementMediaStream_h
-
-#if ENABLE(MEDIA_STREAM) && ENABLE(VIDEO)
-
-#include <wtf/PassRefPtr.h>
-
-namespace WebCore {
-
-class HTMLMediaElement;
-class MediaStream;
-
-class HTMLMediaElementMediaStream {
-public:
- static MediaStream* srcObject(HTMLMediaElement*, bool& isNull);
- static void setSrcObject(HTMLMediaElement*, MediaStream*);
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(MEDIA_STREAM) && ENABLE(VIDEO)
-
-#endif // HTMLMediaElementMediaStream_h
diff --git a/Source/WebCore/Modules/mediastream/NavigatorUserMediaErrorCallback.idl b/Source/WebCore/Modules/mediastream/LongRange.h
index e7754f18a..160ef86b6 100644
--- a/Source/WebCore/Modules/mediastream/NavigatorUserMediaErrorCallback.idl
+++ b/Source/WebCore/Modules/mediastream/LongRange.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 Google Inc. All rights reserved.
+ * 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
@@ -22,9 +22,19 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-[
- Conditional=MEDIA_STREAM,
-] callback interface NavigatorUserMediaErrorCallback {
- boolean handleEvent(NavigatorUserMediaError error);
+#pragma once
+
+#if ENABLE(MEDIA_STREAM)
+
+#include <wtf/Optional.h>
+
+namespace WebCore {
+
+struct LongRange {
+ std::optional<int> max;
+ std::optional<int> min;
};
+}
+
+#endif
diff --git a/Source/WebCore/Modules/mediastream/NavigatorUserMediaSuccessCallback.idl b/Source/WebCore/Modules/mediastream/LongRange.idl
index 3912797d3..69ba22410 100644
--- a/Source/WebCore/Modules/mediastream/NavigatorUserMediaSuccessCallback.idl
+++ b/Source/WebCore/Modules/mediastream/LongRange.idl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 Google Inc. All rights reserved.
+ * 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
@@ -24,7 +24,8 @@
[
Conditional=MEDIA_STREAM,
-] callback interface NavigatorUserMediaSuccessCallback {
- boolean handleEvent(MediaStream stream);
+ JSGenerateToJSObject,
+] dictionary LongRange {
+ long max;
+ long min;
};
-
diff --git a/Source/WebCore/Modules/mediastream/MediaConstraintsImpl.cpp b/Source/WebCore/Modules/mediastream/MediaConstraintsImpl.cpp
index 10f812bda..f50f758be 100644
--- a/Source/WebCore/Modules/mediastream/MediaConstraintsImpl.cpp
+++ b/Source/WebCore/Modules/mediastream/MediaConstraintsImpl.cpp
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2012 Google Inc. All rights reserved.
+ * 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
@@ -34,126 +35,16 @@
#include "MediaConstraintsImpl.h"
-#include "ArrayValue.h"
-#include "Dictionary.h"
-#include "ExceptionCode.h"
-#include <wtf/HashMap.h>
-
namespace WebCore {
-PassRefPtr<MediaConstraintsImpl> MediaConstraintsImpl::create(const Dictionary& constraints, ExceptionCode& ec)
-{
- RefPtr<MediaConstraintsImpl> object = adoptRef(new MediaConstraintsImpl());
- if (!object->initialize(constraints)) {
- ec = TYPE_MISMATCH_ERR;
- return 0;
- }
- return object.release();
-}
-
-PassRefPtr<MediaConstraintsImpl> MediaConstraintsImpl::create()
-{
- return adoptRef(new MediaConstraintsImpl());
-}
-
-bool MediaConstraintsImpl::initialize(const Dictionary& constraints)
-{
- if (constraints.isUndefinedOrNull())
- return true;
-
- Vector<String> names;
- constraints.getOwnPropertyNames(names);
-
- String mandatory = ASCIILiteral("mandatory");
- String optional = ASCIILiteral("optional");
-
- for (Vector<String>::iterator it = names.begin(); it != names.end(); ++it) {
- if (*it != mandatory && *it != optional)
- return false;
- }
-
- if (names.contains(mandatory)) {
- Dictionary mandatoryConstraints;
- bool ok = constraints.get(mandatory, mandatoryConstraints);
- if (!ok || mandatoryConstraints.isUndefinedOrNull())
- return false;
-
- ok = mandatoryConstraints.getOwnPropertiesAsStringHashMap(m_mandatoryConstraints);
- if (!ok)
- return false;
- }
-
- if (names.contains(optional)) {
- ArrayValue optionalConstraints;
- bool ok = constraints.get(optional, optionalConstraints);
- if (!ok || optionalConstraints.isUndefinedOrNull())
- return false;
-
- size_t numberOfConstraints;
- ok = optionalConstraints.length(numberOfConstraints);
- if (!ok)
- return false;
-
- for (size_t i = 0; i < numberOfConstraints; ++i) {
- Dictionary constraint;
- ok = optionalConstraints.get(i, constraint);
- if (!ok || constraint.isUndefinedOrNull())
- return false;
- Vector<String> localNames;
- constraint.getOwnPropertyNames(localNames);
- if (localNames.size() != 1)
- return false;
- String key = localNames[0];
- String value;
- ok = constraint.get(key, value);
- if (!ok)
- return false;
- m_optionalConstraints.append(MediaConstraint(key, value));
- }
- }
-
- return true;
-}
-
-MediaConstraintsImpl::~MediaConstraintsImpl()
+Ref<MediaConstraintsImpl> MediaConstraintsImpl::create(MediaTrackConstraintSetMap&& mandatoryConstraints, Vector<MediaTrackConstraintSetMap>&& advancedConstraints, bool isValid)
{
+ return adoptRef(*new MediaConstraintsImpl(WTFMove(mandatoryConstraints), WTFMove(advancedConstraints), isValid));
}
-void MediaConstraintsImpl::getMandatoryConstraints(Vector<MediaConstraint>& constraints) const
+Ref<MediaConstraintsImpl> MediaConstraintsImpl::create(const MediaConstraintsData& data)
{
- constraints.clear();
- HashMap<String, String>::const_iterator i = m_mandatoryConstraints.begin();
- for (; i != m_mandatoryConstraints.end(); ++i)
- constraints.append(MediaConstraint(i->key, i->value));
-}
-
-void MediaConstraintsImpl::getOptionalConstraints(Vector<MediaConstraint>& constraints) const
-{
- constraints.clear();
- constraints.appendRange(m_optionalConstraints.begin(), m_optionalConstraints.end());
-}
-
-bool MediaConstraintsImpl::getMandatoryConstraintValue(const String& name, String& value) const
-{
- HashMap<String, String>::const_iterator i = m_mandatoryConstraints.find(name);
- if (i == m_mandatoryConstraints.end())
- return false;
-
- value = i->value;
- return true;
-}
-
-bool MediaConstraintsImpl::getOptionalConstraintValue(const String& name, String& value) const
-{
- Vector<MediaConstraint>::const_iterator i = m_optionalConstraints.begin();
- for (; i != m_optionalConstraints.end(); ++i) {
- if (i->m_name == name) {
- value = i->m_value;
- return true;
- }
- }
-
- return false;
+ return adoptRef(*new MediaConstraintsImpl(data));
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/mediastream/MediaConstraintsImpl.h b/Source/WebCore/Modules/mediastream/MediaConstraintsImpl.h
index eeb631ca3..fbee397cd 100644
--- a/Source/WebCore/Modules/mediastream/MediaConstraintsImpl.h
+++ b/Source/WebCore/Modules/mediastream/MediaConstraintsImpl.h
@@ -28,44 +28,56 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef MediaConstraintsImpl_h
-#define MediaConstraintsImpl_h
+#pragma once
#if ENABLE(MEDIA_STREAM)
#include "ExceptionBase.h"
#include "MediaConstraints.h"
-#include <wtf/HashMap.h>
#include <wtf/Vector.h>
namespace WebCore {
-class Dictionary;
-class MediaConstraintsImpl : public MediaConstraints {
-public:
- static PassRefPtr<MediaConstraintsImpl> create();
- static PassRefPtr<MediaConstraintsImpl> create(const Dictionary&, ExceptionCode&);
+struct MediaConstraintsData {
+ MediaConstraintsData() = default;
+ MediaConstraintsData(MediaTrackConstraintSetMap&& mandatoryConstraints, Vector<MediaTrackConstraintSetMap>&& advancedConstraints, bool isValid)
+ : mandatoryConstraints(WTFMove(mandatoryConstraints))
+ , advancedConstraints(WTFMove(advancedConstraints))
+ , isValid(isValid)
+ {
+ }
+
+ MediaTrackConstraintSetMap mandatoryConstraints;
+ Vector<MediaTrackConstraintSetMap> advancedConstraints;
+ bool isValid { false };
+};
- virtual ~MediaConstraintsImpl();
- bool initialize(const Dictionary&);
+class MediaConstraintsImpl final : public MediaConstraints {
+public:
+ static Ref<MediaConstraintsImpl> create(MediaTrackConstraintSetMap&& mandatoryConstraints, Vector<MediaTrackConstraintSetMap>&& advancedConstraints, bool isValid);
+ WEBCORE_EXPORT static Ref<MediaConstraintsImpl> create(const MediaConstraintsData&);
- virtual void getMandatoryConstraints(Vector<MediaConstraint>&) const override;
- virtual void getOptionalConstraints(Vector<MediaConstraint>&) const override;
+ MediaConstraintsImpl() = default;
+ virtual ~MediaConstraintsImpl() = default;
- virtual bool getMandatoryConstraintValue(const String& name, String& value) const override;
- virtual bool getOptionalConstraintValue(const String& name, String& value) const override;
+ const MediaTrackConstraintSetMap& mandatoryConstraints() const final { return m_data.mandatoryConstraints; }
+ const Vector<MediaTrackConstraintSetMap>& advancedConstraints() const final { return m_data.advancedConstraints; }
+ bool isValid() const final { return m_data.isValid; }
+ const MediaConstraintsData& data() const { return m_data; }
private:
- MediaConstraintsImpl() { }
+ MediaConstraintsImpl(MediaTrackConstraintSetMap&& mandatoryConstraints, Vector<MediaTrackConstraintSetMap>&& advancedConstraints, bool isValid)
+ : m_data({ WTFMove(mandatoryConstraints), WTFMove(advancedConstraints), isValid })
+ {
+ }
+ explicit MediaConstraintsImpl(const MediaConstraintsData& data)
+ : m_data(data)
+ {
+ }
- HashMap<String, String> m_mandatoryConstraints;
- Vector<MediaConstraint> m_optionalConstraints;
+ MediaConstraintsData m_data;
};
} // namespace WebCore
#endif // ENABLE(MEDIA_STREAM)
-
-#endif // MediaConstraintsImpl_h
-
-
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseBase.cpp b/Source/WebCore/Modules/mediastream/MediaDeviceInfo.cpp
index af2377a73..fe760d917 100644
--- a/Source/WebCore/Modules/webdatabase/DatabaseBase.cpp
+++ b/Source/WebCore/Modules/mediastream/MediaDeviceInfo.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Apple 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
@@ -20,35 +20,30 @@
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
-#include "DatabaseBase.h"
+#include "MediaDeviceInfo.h"
-#if ENABLE(SQL_DATABASE)
-
-#include "ScriptExecutionContext.h"
-#include <wtf/Assertions.h>
+#if ENABLE(MEDIA_STREAM)
namespace WebCore {
-DatabaseBase::DatabaseBase(ScriptExecutionContext* scriptExecutionContext)
- : m_scriptExecutionContext(scriptExecutionContext)
-{
- ASSERT(m_scriptExecutionContext->isContextThread());
-}
-
-ScriptExecutionContext* DatabaseBase::scriptExecutionContext() const
+inline MediaDeviceInfo::MediaDeviceInfo(ScriptExecutionContext* context, const String& label, const String& deviceId, const String& groupId, Kind kind)
+ : ContextDestructionObserver(context)
+ , m_label(label)
+ , m_deviceId(deviceId)
+ , m_groupId(groupId)
+ , m_kind(kind)
{
- return m_scriptExecutionContext.get();
}
-void DatabaseBase::logErrorMessage(const String& message)
+Ref<MediaDeviceInfo> MediaDeviceInfo::create(ScriptExecutionContext* context, const String& label, const String& deviceId, const String& groupId, Kind kind)
{
- m_scriptExecutionContext->addConsoleMessage(StorageMessageSource, ErrorMessageLevel, message);
+ return adoptRef(*new MediaDeviceInfo(context, label, deviceId, groupId, kind));
}
} // namespace WebCore
-#endif // ENABLE(SQL_DATABASE)
+#endif
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseBackendContext.h b/Source/WebCore/Modules/mediastream/MediaDeviceInfo.h
index b3abef7b5..fc6890433 100644
--- a/Source/WebCore/Modules/webdatabase/DatabaseBackendContext.h
+++ b/Source/WebCore/Modules/mediastream/MediaDeviceInfo.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Apple 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
@@ -20,39 +20,41 @@
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DatabaseBackendContext_h
-#define DatabaseBackendContext_h
+#pragma once
-#if ENABLE(SQL_DATABASE)
+#if ENABLE(MEDIA_STREAM)
-#include "DatabaseContext.h"
+#include "ContextDestructionObserver.h"
+#include "ScriptWrappable.h"
+#include <wtf/text/WTFString.h>
namespace WebCore {
-class ScriptExecutionContext;
-class SecurityOrigin;
+class MediaDeviceInfo : public RefCounted<MediaDeviceInfo>, public ScriptWrappable, private ContextDestructionObserver {
+public:
+ enum class Kind { Audioinput, Audiooutput, Videoinput };
-// FIXME: This implementation of DatabaseBackendContext is only a place holder
-// for the split out of the DatabaseContext backend to be done later. This
-// place holder is needed to allow other code that need to reference the
-// DatabaseBackendContext to do so before the proper backend split is
-// available. This should be replaced with the actual implementation later.
+ static Ref<MediaDeviceInfo> create(ScriptExecutionContext*, const String&, const String&, const String&, Kind);
-class DatabaseBackendContext : public DatabaseContext {
-public:
- DatabaseContext* frontend();
+ const String& label() const { return m_label; }
+ const String& deviceId() const { return m_deviceId; }
+ const String& groupId() const { return m_groupId; }
+ Kind kind() const { return m_kind; }
- ScriptExecutionContext* scriptExecutionContext() const { return m_scriptExecutionContext; }
- SecurityOrigin* securityOrigin() const;
+private:
+ MediaDeviceInfo(ScriptExecutionContext*, const String&, const String&, const String&, Kind);
- bool isContextThread() const;
+ const String m_label;
+ const String m_deviceId;
+ const String m_groupId;
+ const Kind m_kind;
};
-} // namespace WebCore
+typedef Vector<RefPtr<MediaDeviceInfo>> MediaDeviceInfoVector;
-#endif // ENABLE(SQL_DATABASE)
+}
-#endif // DatabaseBackendContext_h
+#endif
diff --git a/Source/WebCore/Modules/mediastream/MediaDeviceInfo.idl b/Source/WebCore/Modules/mediastream/MediaDeviceInfo.idl
new file mode 100644
index 000000000..927cd7263
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/MediaDeviceInfo.idl
@@ -0,0 +1,42 @@
+/*
+* 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 GOOGLE 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=MEDIA_STREAM,
+ Constructor(DOMString deviceId, DOMString label, DOMString groupId, MediaDeviceKind kind),
+ JSGenerateToJSObject,
+ NoInterfaceObject,
+] interface MediaDeviceInfo {
+ readonly attribute DOMString deviceId;
+ readonly attribute DOMString groupId;
+ readonly attribute MediaDeviceKind kind;
+ readonly attribute DOMString label;
+};
+
+enum MediaDeviceKind {
+ "audioinput",
+ "audiooutput",
+ "videoinput"
+};
diff --git a/Source/WebCore/Modules/mediastream/MediaDevices.cpp b/Source/WebCore/Modules/mediastream/MediaDevices.cpp
new file mode 100644
index 000000000..db1ac33e0
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/MediaDevices.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2015 Ericsson AB. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Ericsson 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 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
+ * OWNER 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 "MediaDevices.h"
+
+#if ENABLE(MEDIA_STREAM)
+
+#include "Document.h"
+#include "MediaConstraintsImpl.h"
+#include "MediaDevicesRequest.h"
+#include "MediaTrackSupportedConstraints.h"
+#include "RealtimeMediaSourceCenter.h"
+#include "UserMediaRequest.h"
+
+namespace WebCore {
+
+inline MediaDevices::MediaDevices(Document& document)
+ : ContextDestructionObserver(&document)
+{
+}
+
+Ref<MediaDevices> MediaDevices::create(Document& document)
+{
+ return adoptRef(*new MediaDevices(document));
+}
+
+Document* MediaDevices::document() const
+{
+ return downcast<Document>(scriptExecutionContext());
+}
+
+static Ref<MediaConstraintsImpl> createMediaConstraintsImpl(const Variant<bool, MediaTrackConstraints>& constraints)
+{
+ return WTF::switchOn(constraints,
+ [&] (bool constraints) {
+ return MediaConstraintsImpl::create({ }, { }, constraints);
+ },
+ [&] (const MediaTrackConstraints& constraints) {
+ return createMediaConstraintsImpl(constraints);
+ }
+ );
+}
+
+ExceptionOr<void> MediaDevices::getUserMedia(const StreamConstraints& constraints, Promise&& promise) const
+{
+ auto* document = this->document();
+ if (!document)
+ return Exception { INVALID_STATE_ERR };
+ return UserMediaRequest::start(*document, createMediaConstraintsImpl(constraints.audio), createMediaConstraintsImpl(constraints.video), WTFMove(promise));
+}
+
+void MediaDevices::enumerateDevices(EnumerateDevicesPromise&& promise) const
+{
+ auto* document = this->document();
+ if (!document)
+ return;
+ MediaDevicesRequest::create(*document, WTFMove(promise))->start();
+}
+
+MediaTrackSupportedConstraints MediaDevices::getSupportedConstraints()
+{
+ auto& supported = RealtimeMediaSourceCenter::singleton().supportedConstraints();
+ MediaTrackSupportedConstraints result;
+ result.width = supported.supportsWidth();
+ result.height = supported.supportsHeight();
+ result.aspectRatio = supported.supportsAspectRatio();
+ result.frameRate = supported.supportsFrameRate();
+ result.facingMode = supported.supportsFacingMode();
+ result.volume = supported.supportsVolume();
+ result.sampleRate = supported.supportsSampleRate();
+ result.sampleSize = supported.supportsSampleSize();
+ result.echoCancellation = supported.supportsEchoCancellation();
+ result.deviceId = supported.supportsDeviceId();
+ result.groupId = supported.supportsGroupId();
+ return result;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(MEDIA_STREAM)
diff --git a/Source/WebCore/Modules/mediastream/RTCSessionDescriptionRequestImpl.h b/Source/WebCore/Modules/mediastream/MediaDevices.h
index c5624b1c5..0d90d259d 100644
--- a/Source/WebCore/Modules/mediastream/RTCSessionDescriptionRequestImpl.h
+++ b/Source/WebCore/Modules/mediastream/MediaDevices.h
@@ -1,6 +1,6 @@
/*
- * Copyright (C) 2012 Google Inc. All rights reserved.
- * Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
+ * Copyright (C) 2015 Ericsson AB. All rights reserved.
+ * 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
@@ -12,7 +12,7 @@
* 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 Google Inc. nor the names of its contributors
+ * 3. Neither the name of Ericsson nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
@@ -29,44 +29,43 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef RTCSessionDescriptionRequestImpl_h
-#define RTCSessionDescriptionRequestImpl_h
+#pragma once
#if ENABLE(MEDIA_STREAM)
-#include "ActiveDOMObject.h"
-#include "RTCSessionDescriptionRequest.h"
+#include "ExceptionOr.h"
+#include "JSDOMPromise.h"
+#include "MediaTrackConstraints.h"
namespace WebCore {
-class RTCPeerConnectionErrorCallback;
-class RTCPeerConnection;
-class RTCSessionDescriptionCallback;
+class Document;
+class MediaDeviceInfo;
+class MediaStream;
-class RTCSessionDescriptionRequestImpl : public RTCSessionDescriptionRequest, public ActiveDOMObject {
-public:
- static PassRefPtr<RTCSessionDescriptionRequestImpl> create(ScriptExecutionContext*, PassRefPtr<RTCSessionDescriptionCallback>, PassRefPtr<RTCPeerConnectionErrorCallback>);
- virtual ~RTCSessionDescriptionRequestImpl();
+struct MediaTrackSupportedConstraints;
- virtual void requestSucceeded(PassRefPtr<RTCSessionDescriptionDescriptor>) override;
- virtual void requestFailed(const String& error) override;
+class MediaDevices : public ScriptWrappable, public RefCounted<MediaDevices>, public ContextDestructionObserver {
+public:
+ static Ref<MediaDevices> create(Document&);
- // ActiveDOMObject
- virtual void stop() override;
+ Document* document() const;
-private:
- RTCSessionDescriptionRequestImpl(ScriptExecutionContext*, PassRefPtr<RTCSessionDescriptionCallback>, PassRefPtr<RTCPeerConnectionErrorCallback>);
+ using Promise = DOMPromise<IDLInterface<MediaStream>>;
+ using EnumerateDevicesPromise = DOMPromise<IDLSequence<IDLInterface<MediaDeviceInfo>>>;
- void clear();
+ struct StreamConstraints {
+ Variant<bool, MediaTrackConstraints> video;
+ Variant<bool, MediaTrackConstraints> audio;
+ };
+ ExceptionOr<void> getUserMedia(const StreamConstraints&, Promise&&) const;
+ void enumerateDevices(EnumerateDevicesPromise&&) const;
+ MediaTrackSupportedConstraints getSupportedConstraints();
- RefPtr<RTCSessionDescriptionCallback> m_successCallback;
- RefPtr<RTCPeerConnectionErrorCallback> m_errorCallback;
+private:
+ explicit MediaDevices(Document&);
};
} // namespace WebCore
#endif // ENABLE(MEDIA_STREAM)
-
-#endif // RTCSessionDescriptionRequestImpl_h
-
-
diff --git a/Source/WebCore/Modules/mediastream/MediaDevices.idl b/Source/WebCore/Modules/mediastream/MediaDevices.idl
new file mode 100644
index 000000000..0c0866295
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/MediaDevices.idl
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2015 Ericsson AB. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Ericsson 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 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
+ * OWNER 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=MEDIA_STREAM,
+ NoInterfaceObject,
+] interface MediaDevices {
+ MediaTrackSupportedConstraints getSupportedConstraints();
+
+ [PrivateIdentifier, PublicIdentifier] Promise<MediaStream> getUserMedia(optional MediaStreamConstraints constraints);
+ Promise<sequence<MediaDeviceInfo>> enumerateDevices();
+};
+
+[
+ Conditional=MEDIA_STREAM,
+] dictionary MediaStreamConstraints {
+ (boolean or MediaTrackConstraints) video = false;
+ (boolean or MediaTrackConstraints) audio = false;
+};
diff --git a/Source/WebCore/Modules/mediastream/MediaDevicesEnumerationRequest.cpp b/Source/WebCore/Modules/mediastream/MediaDevicesEnumerationRequest.cpp
new file mode 100644
index 000000000..c89d43d79
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/MediaDevicesEnumerationRequest.cpp
@@ -0,0 +1,113 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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 "MediaDevicesEnumerationRequest.h"
+
+#if ENABLE(MEDIA_STREAM)
+
+#include "CaptureDevice.h"
+#include "Document.h"
+#include "MainFrame.h"
+#include "SecurityOrigin.h"
+#include "UserMediaController.h"
+
+namespace WebCore {
+
+Ref<MediaDevicesEnumerationRequest> MediaDevicesEnumerationRequest::create(Document& document, CompletionHandler&& completionHandler)
+{
+ return adoptRef(*new MediaDevicesEnumerationRequest(document, WTFMove(completionHandler)));
+}
+
+MediaDevicesEnumerationRequest::MediaDevicesEnumerationRequest(ScriptExecutionContext& context, CompletionHandler&& completionHandler)
+ : ContextDestructionObserver(&context)
+ , m_completionHandler(WTFMove(completionHandler))
+{
+}
+
+MediaDevicesEnumerationRequest::~MediaDevicesEnumerationRequest()
+{
+}
+
+SecurityOrigin* MediaDevicesEnumerationRequest::userMediaDocumentOrigin() const
+{
+ if (!scriptExecutionContext())
+ return nullptr;
+
+ return scriptExecutionContext()->securityOrigin();
+}
+
+SecurityOrigin* MediaDevicesEnumerationRequest::topLevelDocumentOrigin() const
+{
+ if (!scriptExecutionContext())
+ return nullptr;
+
+ if (Frame* frame = downcast<Document>(*scriptExecutionContext()).frame()) {
+ if (frame->isMainFrame())
+ return nullptr;
+ }
+
+ return &scriptExecutionContext()->topOrigin();
+}
+
+void MediaDevicesEnumerationRequest::contextDestroyed()
+{
+ cancel();
+ ContextDestructionObserver::contextDestroyed();
+}
+
+void MediaDevicesEnumerationRequest::start()
+{
+ ASSERT(scriptExecutionContext());
+
+ auto& document = downcast<Document>(*scriptExecutionContext());
+ auto* controller = UserMediaController::from(document.page());
+ if (!controller)
+ return;
+
+ Ref<MediaDevicesEnumerationRequest> protectedThis(*this);
+ controller->enumerateMediaDevices(*this);
+}
+
+void MediaDevicesEnumerationRequest::cancel()
+{
+ m_completionHandler = nullptr;
+}
+
+void MediaDevicesEnumerationRequest::setDeviceInfo(const Vector<CaptureDevice>& deviceList, const String& deviceIdentifierHashSalt, bool originHasPersistentAccess)
+{
+ m_deviceList = deviceList;
+ m_deviceIdentifierHashSalt = deviceIdentifierHashSalt;
+ m_originHasPersistentAccess = originHasPersistentAccess;
+
+ if (m_completionHandler)
+ m_completionHandler(m_deviceList, m_deviceIdentifierHashSalt, m_originHasPersistentAccess);
+ m_completionHandler = nullptr;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(MEDIA_STREAM)
diff --git a/Source/WebCore/Modules/mediastream/MediaDevicesEnumerationRequest.h b/Source/WebCore/Modules/mediastream/MediaDevicesEnumerationRequest.h
new file mode 100644
index 000000000..ec4df0054
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/MediaDevicesEnumerationRequest.h
@@ -0,0 +1,71 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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_STREAM)
+
+#include "ActiveDOMObject.h"
+#include <functional>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class CaptureDevice;
+class Document;
+class SecurityOrigin;
+
+class MediaDevicesEnumerationRequest final : public ContextDestructionObserver, public RefCounted<MediaDevicesEnumerationRequest> {
+public:
+ using CompletionHandler = std::function<void(const Vector<CaptureDevice>&, const String& deviceIdentifierHashSalt, bool originHasPersistentAccess)>;
+
+ static Ref<MediaDevicesEnumerationRequest> create(Document&, CompletionHandler&&);
+
+ virtual ~MediaDevicesEnumerationRequest();
+
+ void start();
+ void cancel();
+
+ WEBCORE_EXPORT void setDeviceInfo(const Vector<CaptureDevice>&, const String& deviceIdentifierHashSalt, bool originHasPersistentAccess);
+
+ WEBCORE_EXPORT SecurityOrigin* userMediaDocumentOrigin() const;
+ WEBCORE_EXPORT SecurityOrigin* topLevelDocumentOrigin() const;
+
+private:
+ MediaDevicesEnumerationRequest(ScriptExecutionContext&, CompletionHandler&&);
+
+ // ContextDestructionObserver
+ void contextDestroyed() final;
+
+ CompletionHandler m_completionHandler;
+ Vector<CaptureDevice> m_deviceList;
+ String m_deviceIdentifierHashSalt;
+ bool m_originHasPersistentAccess { false };
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(MEDIA_STREAM)
diff --git a/Source/WebCore/Modules/mediastream/MediaDevicesRequest.cpp b/Source/WebCore/Modules/mediastream/MediaDevicesRequest.cpp
new file mode 100644
index 000000000..4a0b3e19e
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/MediaDevicesRequest.cpp
@@ -0,0 +1,153 @@
+/*
+ * 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 "MediaDevicesRequest.h"
+
+#if ENABLE(MEDIA_STREAM)
+
+#include "CaptureDevice.h"
+#include "Document.h"
+#include "Frame.h"
+#include "JSMediaDeviceInfo.h"
+#include "MediaDevicesEnumerationRequest.h"
+#include "SecurityOrigin.h"
+#include "UserMediaController.h"
+#include <wtf/MainThread.h>
+#include <wtf/SHA1.h>
+
+namespace WebCore {
+
+inline MediaDevicesRequest::MediaDevicesRequest(Document& document, MediaDevices::EnumerateDevicesPromise&& promise)
+ : ContextDestructionObserver(&document)
+ , m_promise(WTFMove(promise))
+{
+}
+
+Ref<MediaDevicesRequest> MediaDevicesRequest::create(Document& document, MediaDevices::EnumerateDevicesPromise&& promise)
+{
+ return adoptRef(*new MediaDevicesRequest(document, WTFMove(promise)));
+}
+
+MediaDevicesRequest::~MediaDevicesRequest()
+{
+ if (m_enumerationRequest)
+ m_enumerationRequest->cancel();
+}
+
+SecurityOrigin* MediaDevicesRequest::securityOrigin() const
+{
+ if (scriptExecutionContext())
+ return scriptExecutionContext()->securityOrigin();
+
+ return nullptr;
+}
+
+void MediaDevicesRequest::contextDestroyed()
+{
+ if (m_enumerationRequest) {
+ m_enumerationRequest->cancel();
+ m_enumerationRequest = nullptr;
+ }
+ ContextDestructionObserver::contextDestroyed();
+}
+
+void MediaDevicesRequest::start()
+{
+ RefPtr<MediaDevicesRequest> protectedThis = this;
+ auto completion = [this, protectedThis = WTFMove(protectedThis)] (const Vector<CaptureDevice>& captureDevices, const String& deviceIdentifierHashSalt, bool originHasPersistentAccess) mutable {
+
+ m_enumerationRequest = nullptr;
+
+ if (!scriptExecutionContext())
+ return;
+
+ Document& document = downcast<Document>(*scriptExecutionContext());
+ UserMediaController* controller = UserMediaController::from(document.page());
+ if (!controller)
+ return;
+
+ m_idHashSalt = deviceIdentifierHashSalt;
+
+ Vector<RefPtr<MediaDeviceInfo>> devices;
+ for (auto& deviceInfo : captureDevices) {
+ auto label = emptyString();
+ if (originHasPersistentAccess || document.hasHadActiveMediaStreamTrack())
+ label = deviceInfo.label();
+
+ auto id = hashID(deviceInfo.persistentId());
+ if (id.isEmpty())
+ continue;
+
+ auto groupId = hashID(deviceInfo.groupId());
+ auto deviceType = deviceInfo.type() == CaptureDevice::DeviceType::Audio ? MediaDeviceInfo::Kind::Audioinput : MediaDeviceInfo::Kind::Videoinput;
+ devices.append(MediaDeviceInfo::create(scriptExecutionContext(), label, id, groupId, deviceType));
+ }
+
+ callOnMainThread([protectedThis = makeRef(*this), devices = WTFMove(devices)]() mutable {
+ protectedThis->m_promise.resolve(devices);
+ });
+ };
+
+ m_enumerationRequest = MediaDevicesEnumerationRequest::create(*downcast<Document>(scriptExecutionContext()), WTFMove(completion));
+ m_enumerationRequest->start();
+}
+
+static void hashString(SHA1& sha1, const String& string)
+{
+ if (string.isEmpty())
+ return;
+
+ if (string.is8Bit() && string.containsOnlyASCII()) {
+ const uint8_t nullByte = 0;
+ sha1.addBytes(string.characters8(), string.length());
+ sha1.addBytes(&nullByte, 1);
+ return;
+ }
+
+ auto utf8 = string.utf8();
+ sha1.addBytes(reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length() + 1); // Include terminating null byte.
+}
+
+String MediaDevicesRequest::hashID(const String& id)
+{
+ if (id.isEmpty() || m_idHashSalt.isEmpty())
+ return emptyString();
+
+ SHA1 sha1;
+
+ hashString(sha1, id);
+ hashString(sha1, m_idHashSalt);
+
+ SHA1::Digest digest;
+ sha1.computeHash(digest);
+
+ return SHA1::hexDigest(digest).data();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(MEDIA_STREAM)
diff --git a/Source/WebCore/Modules/mediastream/MediaTrackConstraint.h b/Source/WebCore/Modules/mediastream/MediaDevicesRequest.h
index 2a93fc763..d47002fd9 100644
--- a/Source/WebCore/Modules/mediastream/MediaTrackConstraint.h
+++ b/Source/WebCore/Modules/mediastream/MediaDevicesRequest.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Apple 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
@@ -13,48 +13,53 @@
* 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
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
-#ifndef MediaTrackConstraint_h
-#define MediaTrackConstraint_h
+#pragma once
#if ENABLE(MEDIA_STREAM)
-#include "Dictionary.h"
-#include "ScriptWrappable.h"
-#include <wtf/RefCounted.h>
-
-namespace Deprecated {
-class ScriptValue;
-}
+#include "MediaDevices.h"
namespace WebCore {
-class MediaTrackConstraint : public RefCounted<MediaTrackConstraint>, public ScriptWrappable {
+class Document;
+class MediaDevicesEnumerationRequest;
+class SecurityOrigin;
+
+class MediaDevicesRequest : public RefCounted<MediaDevicesRequest>, private ContextDestructionObserver {
public:
- static RefPtr<MediaTrackConstraint> create(const Dictionary&);
+ static Ref<MediaDevicesRequest> create(Document&, MediaDevices::EnumerateDevicesPromise&&);
- virtual ~MediaTrackConstraint();
+ virtual ~MediaDevicesRequest();
- const Dictionary& constraint() const { return m_constraint; }
+ void start();
+
+ SecurityOrigin* securityOrigin() const;
private:
- explicit MediaTrackConstraint(const Dictionary&);
-
- const Dictionary& m_constraint;
+ MediaDevicesRequest(Document&, MediaDevices::EnumerateDevicesPromise&&);
+
+ void contextDestroyed() final;
+
+ String hashID(const String&);
+
+ MediaDevices::EnumerateDevicesPromise m_promise;
+ RefPtr<MediaDevicesRequest> m_protector;
+ RefPtr<MediaDevicesEnumerationRequest> m_enumerationRequest;
+
+ String m_idHashSalt;
};
} // namespace WebCore
-#endif // MediaTrackConstraint_h
-
-#endif
+#endif // ENABLE(MEDIA_STREAM)
diff --git a/Source/WebCore/Modules/mediastream/MediaEndpointPeerConnection.cpp b/Source/WebCore/Modules/mediastream/MediaEndpointPeerConnection.cpp
new file mode 100644
index 000000000..14ee0f5d3
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/MediaEndpointPeerConnection.cpp
@@ -0,0 +1,874 @@
+/*
+ * Copyright (C) 2015 Ericsson AB. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Ericsson 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 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
+ * OWNER 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(WEB_RTC)
+#include "MediaEndpointPeerConnection.h"
+
+#include "EventNames.h"
+#include "JSRTCSessionDescription.h"
+#include "MediaEndpointSessionConfiguration.h"
+#include "MediaEndpointSessionDescription.h"
+#include "MediaStream.h"
+#include "MediaStreamEvent.h"
+#include "MediaStreamTrack.h"
+#include "NotImplemented.h"
+#include "PeerMediaDescription.h"
+#include "RTCConfiguration.h"
+#include "RTCIceCandidate.h"
+#include "RTCIceCandidateEvent.h"
+#include "RTCOfferAnswerOptions.h"
+#include "RTCPeerConnection.h"
+#include "RTCRtpTransceiver.h"
+#include "RTCTrackEvent.h"
+#include "SDPProcessor.h"
+#include <wtf/MainThread.h>
+#include <wtf/text/Base64.h>
+
+namespace WebCore {
+
+using namespace PeerConnection;
+using namespace PeerConnectionStates;
+
+using MediaDescriptionVector = Vector<PeerMediaDescription>;
+using RtpTransceiverVector = Vector<RefPtr<RTCRtpTransceiver>>;
+
+// We use base64 to generate the random strings so we need a size that avoids padding to get ice-chars.
+static const size_t cnameSize = 18;
+// Size range from 4 to 256 ice-chars defined in RFC 5245.
+static const size_t iceUfragSize = 6;
+// Size range from 22 to 256 ice-chars defined in RFC 5245.
+static const size_t icePasswordSize = 24;
+
+#if !USE(LIBWEBRTC)
+static std::unique_ptr<PeerConnectionBackend> createMediaEndpointPeerConnection(RTCPeerConnection& peerConnection)
+{
+ return std::unique_ptr<PeerConnectionBackend>(new MediaEndpointPeerConnection(peerConnection));
+}
+
+CreatePeerConnectionBackend PeerConnectionBackend::create = createMediaEndpointPeerConnection;
+#endif
+
+static String randomString(size_t size)
+{
+ unsigned char randomValues[size];
+ cryptographicallyRandomValues(randomValues, size);
+ return base64Encode(randomValues, size);
+}
+
+MediaEndpointPeerConnection::MediaEndpointPeerConnection(RTCPeerConnection& peerConnection)
+ : PeerConnectionBackend(peerConnection)
+ , m_mediaEndpoint(MediaEndpoint::create(*this))
+ , m_sdpProcessor(std::make_unique<SDPProcessor>(m_peerConnection.scriptExecutionContext()))
+ , m_cname(randomString(cnameSize))
+ , m_iceUfrag(randomString(iceUfragSize))
+ , m_icePassword(randomString(icePasswordSize))
+{
+ ASSERT(m_mediaEndpoint);
+
+ m_defaultAudioPayloads = m_mediaEndpoint->getDefaultAudioPayloads();
+ m_defaultVideoPayloads = m_mediaEndpoint->getDefaultVideoPayloads();
+
+ // Tasks (see runTask()) will be deferred until we get the DTLS fingerprint.
+ m_mediaEndpoint->generateDtlsInfo();
+}
+
+static RTCRtpTransceiver* matchTransceiver(const RtpTransceiverVector& transceivers, const std::function<bool(RTCRtpTransceiver&)>& matchFunction)
+{
+ for (auto& transceiver : transceivers) {
+ if (matchFunction(*transceiver))
+ return transceiver.get();
+ }
+ return nullptr;
+}
+
+static RTCRtpTransceiver* matchTransceiverByMid(const RtpTransceiverVector& transceivers, const String& mid)
+{
+ return matchTransceiver(transceivers, [&mid] (RTCRtpTransceiver& current) {
+ return current.mid() == mid;
+ });
+}
+
+static bool hasUnassociatedTransceivers(const RtpTransceiverVector& transceivers)
+{
+ return matchTransceiver(transceivers, [] (RTCRtpTransceiver& current) {
+ return current.mid().isNull() && !current.stopped();
+ });
+}
+
+void MediaEndpointPeerConnection::runTask(Function<void ()>&& task)
+{
+ if (m_dtlsFingerprint.isNull()) {
+ // Only one task needs to be deferred since it will hold off any others until completed.
+ ASSERT(!m_initialDeferredTask);
+ m_initialDeferredTask = WTFMove(task);
+ } else
+ callOnMainThread(WTFMove(task));
+}
+
+void MediaEndpointPeerConnection::startRunningTasks()
+{
+ if (!m_initialDeferredTask)
+ return;
+
+ m_initialDeferredTask();
+ m_initialDeferredTask = nullptr;
+}
+
+void MediaEndpointPeerConnection::doCreateOffer(RTCOfferOptions&& options)
+{
+ runTask([this, protectedOptions = WTFMove(options)]() mutable {
+ createOfferTask(protectedOptions);
+ });
+}
+
+void MediaEndpointPeerConnection::createOfferTask(const RTCOfferOptions&)
+{
+ ASSERT(!m_dtlsFingerprint.isEmpty());
+
+ MediaEndpointSessionDescription* localDescription = internalLocalDescription();
+ RefPtr<MediaEndpointSessionConfiguration> configurationSnapshot = localDescription ?
+ localDescription->configuration()->clone() : MediaEndpointSessionConfiguration::create();
+
+ configurationSnapshot->setSessionVersion(m_sdpOfferSessionVersion++);
+
+ auto transceivers = RtpTransceiverVector(m_peerConnection.getTransceivers());
+
+ // Remove any transceiver objects from transceivers that can be matched to an existing media description.
+ for (auto& mediaDescription : configurationSnapshot->mediaDescriptions()) {
+ if (!mediaDescription.port) {
+ // This media description should be recycled.
+ continue;
+ }
+
+ RTCRtpTransceiver* transceiver = matchTransceiverByMid(transceivers, mediaDescription.mid);
+ if (!transceiver)
+ continue;
+
+ mediaDescription.mode = transceiver->directionString();
+ if (transceiver->hasSendingDirection()) {
+ auto& sender = transceiver->sender();
+
+ mediaDescription.mediaStreamId = sender.mediaStreamIds()[0];
+ mediaDescription.mediaStreamTrackId = sender.trackId();
+ }
+
+ transceivers.removeFirst(transceiver);
+ }
+
+ // Add media descriptions for remaining transceivers.
+ for (auto& transceiver : transceivers) {
+ PeerMediaDescription mediaDescription;
+ auto& sender = transceiver->sender();
+
+ mediaDescription.mode = transceiver->directionString();
+ mediaDescription.mid = transceiver->provisionalMid();
+ mediaDescription.mediaStreamId = sender.mediaStreamIds()[0];
+ mediaDescription.type = sender.trackKind();
+ mediaDescription.payloads = sender.trackKind() == "audio" ? m_defaultAudioPayloads : m_defaultVideoPayloads;
+ mediaDescription.dtlsFingerprintHashFunction = m_dtlsFingerprintFunction;
+ mediaDescription.dtlsFingerprint = m_dtlsFingerprint;
+ mediaDescription.cname = m_cname;
+ mediaDescription.addSsrc(cryptographicallyRandomNumber());
+ mediaDescription.iceUfrag = m_iceUfrag;
+ mediaDescription.icePassword = m_icePassword;
+
+ if (sender.track())
+ mediaDescription.mediaStreamTrackId = sender.trackId();
+
+ configurationSnapshot->addMediaDescription(WTFMove(mediaDescription));
+ }
+
+ String sdp;
+ SDPProcessor::Result result = m_sdpProcessor->generate(*configurationSnapshot, sdp);
+ if (result != SDPProcessor::Result::Success) {
+ createOfferFailed(Exception { OperationError, "SDPProcessor internal error" });
+ return;
+ }
+ createOfferSucceeded(WTFMove(sdp));
+}
+
+void MediaEndpointPeerConnection::doCreateAnswer(RTCAnswerOptions&& options)
+{
+ runTask([this, protectedOptions = WTFMove(options)]() mutable {
+ createAnswerTask(protectedOptions);
+ });
+}
+
+void MediaEndpointPeerConnection::createAnswerTask(const RTCAnswerOptions&)
+{
+ ASSERT(!m_dtlsFingerprint.isEmpty());
+
+ if (!internalRemoteDescription()) {
+ createAnswerFailed(Exception { INVALID_STATE_ERR, "No remote description set" });
+ return;
+ }
+
+ MediaEndpointSessionDescription* localDescription = internalLocalDescription();
+ RefPtr<MediaEndpointSessionConfiguration> configurationSnapshot = localDescription ?
+ localDescription->configuration()->clone() : MediaEndpointSessionConfiguration::create();
+
+ configurationSnapshot->setSessionVersion(m_sdpAnswerSessionVersion++);
+
+ auto transceivers = RtpTransceiverVector(m_peerConnection.getTransceivers());
+ auto& remoteMediaDescriptions = internalRemoteDescription()->configuration()->mediaDescriptions();
+
+ for (unsigned i = 0; i < remoteMediaDescriptions.size(); ++i) {
+ auto& remoteMediaDescription = remoteMediaDescriptions[i];
+
+ auto* transceiver = matchTransceiverByMid(transceivers, remoteMediaDescription.mid);
+ if (!transceiver) {
+ LOG_ERROR("Could not find a matching transceiver for remote description while creating answer");
+ continue;
+ }
+
+ if (i >= configurationSnapshot->mediaDescriptions().size()) {
+ PeerMediaDescription newMediaDescription;
+
+ auto& sender = transceiver->sender();
+ if (sender.track()) {
+ if (sender.mediaStreamIds().size())
+ newMediaDescription.mediaStreamId = sender.mediaStreamIds()[0];
+ newMediaDescription.mediaStreamTrackId = sender.trackId();
+ newMediaDescription.addSsrc(cryptographicallyRandomNumber());
+ }
+
+ newMediaDescription.mode = transceiver->directionString();
+ newMediaDescription.type = remoteMediaDescription.type;
+ newMediaDescription.mid = remoteMediaDescription.mid;
+ newMediaDescription.dtlsSetup = remoteMediaDescription.dtlsSetup == "active" ? "passive" : "active";
+ newMediaDescription.dtlsFingerprintHashFunction = m_dtlsFingerprintFunction;
+ newMediaDescription.dtlsFingerprint = m_dtlsFingerprint;
+ newMediaDescription.cname = m_cname;
+ newMediaDescription.iceUfrag = m_iceUfrag;
+ newMediaDescription.icePassword = m_icePassword;
+
+ configurationSnapshot->addMediaDescription(WTFMove(newMediaDescription));
+ }
+
+ PeerMediaDescription& localMediaDescription = configurationSnapshot->mediaDescriptions()[i];
+
+ localMediaDescription.payloads = remoteMediaDescription.payloads;
+ localMediaDescription.rtcpMux = remoteMediaDescription.rtcpMux;
+
+ if (!localMediaDescription.ssrcs.size())
+ localMediaDescription.addSsrc(cryptographicallyRandomNumber());
+
+ if (localMediaDescription.dtlsSetup == "actpass")
+ localMediaDescription.dtlsSetup = "passive";
+
+ transceivers.removeFirst(transceiver);
+ }
+
+ // Unassociated (non-stopped) transceivers need to be negotiated in a follow-up offer.
+ if (hasUnassociatedTransceivers(transceivers))
+ markAsNeedingNegotiation();
+
+ String sdp;
+ SDPProcessor::Result result = m_sdpProcessor->generate(*configurationSnapshot, sdp);
+ if (result != SDPProcessor::Result::Success) {
+ createAnswerFailed(Exception { OperationError, "SDPProcessor internal error" });
+ return;
+ }
+ createAnswerSucceeded(WTFMove(sdp));
+}
+
+static RealtimeMediaSourceMap createSourceMap(const MediaDescriptionVector& remoteMediaDescriptions, unsigned localMediaDescriptionCount, const RtpTransceiverVector& transceivers)
+{
+ RealtimeMediaSourceMap sourceMap;
+
+ for (unsigned i = 0; i < remoteMediaDescriptions.size() && i < localMediaDescriptionCount; ++i) {
+ auto& remoteMediaDescription = remoteMediaDescriptions[i];
+ if (remoteMediaDescription.type != "audio" && remoteMediaDescription.type != "video")
+ continue;
+
+ RTCRtpTransceiver* transceiver = matchTransceiverByMid(transceivers, remoteMediaDescription.mid);
+ if (transceiver) {
+ if (transceiver->hasSendingDirection() && transceiver->sender().track())
+ sourceMap.set(transceiver->mid(), &transceiver->sender().track()->source());
+ }
+ }
+
+ return sourceMap;
+}
+
+void MediaEndpointPeerConnection::doSetLocalDescription(RTCSessionDescription& description)
+{
+ runTask([this, protectedDescription = RefPtr<RTCSessionDescription>(&description)]() mutable {
+ setLocalDescriptionTask(WTFMove(protectedDescription));
+ });
+}
+
+void MediaEndpointPeerConnection::setLocalDescriptionTask(RefPtr<RTCSessionDescription>&& description)
+{
+ if (m_peerConnection.internalSignalingState() == SignalingState::Closed)
+ return;
+
+ auto result = MediaEndpointSessionDescription::create(WTFMove(description), *m_sdpProcessor);
+ if (result.hasException()) {
+ setLocalDescriptionFailed(result.releaseException());
+ return;
+ }
+ auto newDescription = result.releaseReturnValue();
+
+ const RtpTransceiverVector& transceivers = m_peerConnection.getTransceivers();
+ const MediaDescriptionVector& mediaDescriptions = newDescription->configuration()->mediaDescriptions();
+ MediaEndpointSessionDescription* localDescription = internalLocalDescription();
+ unsigned previousNumberOfMediaDescriptions = localDescription ? localDescription->configuration()->mediaDescriptions().size() : 0;
+ bool hasNewMediaDescriptions = mediaDescriptions.size() > previousNumberOfMediaDescriptions;
+ bool isInitiator = newDescription->type() == RTCSessionDescription::SdpType::Offer;
+
+ if (hasNewMediaDescriptions) {
+ MediaEndpoint::UpdateResult result = m_mediaEndpoint->updateReceiveConfiguration(newDescription->configuration(), isInitiator);
+
+ if (result == MediaEndpoint::UpdateResult::SuccessWithIceRestart) {
+ if (m_peerConnection.internalIceGatheringState() != IceGatheringState::Gathering)
+ m_peerConnection.updateIceGatheringState(IceGatheringState::Gathering);
+
+ if (m_peerConnection.internalIceConnectionState() != IceConnectionState::Completed)
+ m_peerConnection.updateIceConnectionState(IceConnectionState::Connected);
+
+ LOG_ERROR("ICE restart is not implemented");
+ notImplemented();
+
+ } else if (result == MediaEndpoint::UpdateResult::Failed) {
+ setLocalDescriptionFailed(Exception { OperationError, "Unable to apply session description" });
+ return;
+ }
+
+ // Associate media descriptions with transceivers (set provisional mid to 'final' mid).
+ for (auto& mediaDescription : mediaDescriptions) {
+ RTCRtpTransceiver* transceiver = matchTransceiver(transceivers, [&mediaDescription] (RTCRtpTransceiver& current) {
+ return current.provisionalMid() == mediaDescription.mid;
+ });
+ if (transceiver)
+ transceiver->setMid(transceiver->provisionalMid());
+ }
+ }
+
+ if (internalRemoteDescription()) {
+ MediaEndpointSessionConfiguration* remoteConfiguration = internalRemoteDescription()->configuration();
+ RealtimeMediaSourceMap sendSourceMap = createSourceMap(remoteConfiguration->mediaDescriptions(), mediaDescriptions.size(), transceivers);
+
+ if (m_mediaEndpoint->updateSendConfiguration(remoteConfiguration, sendSourceMap, isInitiator) == MediaEndpoint::UpdateResult::Failed) {
+ setLocalDescriptionFailed(Exception { OperationError, "Unable to apply session description" });
+ return;
+ }
+ }
+
+ if (!hasUnassociatedTransceivers(transceivers))
+ clearNegotiationNeededState();
+
+ SignalingState newSignalingState;
+
+ // Update state and local descriptions according to setLocal/RemoteDescription processing model
+ switch (newDescription->type()) {
+ case RTCSessionDescription::SdpType::Offer:
+ m_pendingLocalDescription = WTFMove(newDescription);
+ newSignalingState = SignalingState::HaveLocalOffer;
+ break;
+
+ case RTCSessionDescription::SdpType::Answer:
+ m_currentLocalDescription = WTFMove(newDescription);
+ m_currentRemoteDescription = m_pendingRemoteDescription;
+ m_pendingLocalDescription = nullptr;
+ m_pendingRemoteDescription = nullptr;
+ newSignalingState = SignalingState::Stable;
+ break;
+
+ case RTCSessionDescription::SdpType::Rollback:
+ m_pendingLocalDescription = nullptr;
+ newSignalingState = SignalingState::Stable;
+ break;
+
+ case RTCSessionDescription::SdpType::Pranswer:
+ m_pendingLocalDescription = WTFMove(newDescription);
+ newSignalingState = SignalingState::HaveLocalPrAnswer;
+ break;
+ }
+
+ updateSignalingState(newSignalingState);
+
+ if (m_peerConnection.internalIceGatheringState() == IceGatheringState::New && mediaDescriptions.size())
+ m_peerConnection.updateIceGatheringState(IceGatheringState::Gathering);
+
+ markAsNeedingNegotiation();
+ setLocalDescriptionSucceeded();
+}
+
+RefPtr<RTCSessionDescription> MediaEndpointPeerConnection::localDescription() const
+{
+ return createRTCSessionDescription(internalLocalDescription());
+}
+
+RefPtr<RTCSessionDescription> MediaEndpointPeerConnection::currentLocalDescription() const
+{
+ return createRTCSessionDescription(m_currentLocalDescription.get());
+}
+
+RefPtr<RTCSessionDescription> MediaEndpointPeerConnection::pendingLocalDescription() const
+{
+ return createRTCSessionDescription(m_pendingLocalDescription.get());
+}
+
+void MediaEndpointPeerConnection::doSetRemoteDescription(RTCSessionDescription& description)
+{
+ runTask([this, protectedDescription = RefPtr<RTCSessionDescription>(&description)]() mutable {
+ setRemoteDescriptionTask(WTFMove(protectedDescription));
+ });
+}
+
+void MediaEndpointPeerConnection::setRemoteDescriptionTask(RefPtr<RTCSessionDescription>&& description)
+{
+ auto result = MediaEndpointSessionDescription::create(WTFMove(description), *m_sdpProcessor);
+ if (result.hasException()) {
+ setRemoteDescriptionFailed(result.releaseException());
+ return;
+ }
+ auto newDescription = result.releaseReturnValue();
+
+ auto& mediaDescriptions = newDescription->configuration()->mediaDescriptions();
+ for (auto& mediaDescription : mediaDescriptions) {
+ if (mediaDescription.type != "audio" && mediaDescription.type != "video")
+ continue;
+
+ mediaDescription.payloads = m_mediaEndpoint->filterPayloads(mediaDescription.payloads, mediaDescription.type == "audio" ? m_defaultAudioPayloads : m_defaultVideoPayloads);
+ }
+
+ bool isInitiator = newDescription->type() == RTCSessionDescription::SdpType::Answer;
+ const RtpTransceiverVector& transceivers = m_peerConnection.getTransceivers();
+
+ RealtimeMediaSourceMap sendSourceMap;
+ if (internalLocalDescription())
+ sendSourceMap = createSourceMap(mediaDescriptions, internalLocalDescription()->configuration()->mediaDescriptions().size(), transceivers);
+
+ if (m_mediaEndpoint->updateSendConfiguration(newDescription->configuration(), sendSourceMap, isInitiator) == MediaEndpoint::UpdateResult::Failed) {
+ setRemoteDescriptionFailed(Exception { OperationError, "Unable to apply session description" });
+ return;
+ }
+
+ // One legacy MediaStreamEvent will be fired for every new MediaStream created as this remote description is set.
+ Vector<RefPtr<MediaStreamEvent>> legacyMediaStreamEvents;
+
+ for (auto& mediaDescription : mediaDescriptions) {
+ RTCRtpTransceiver* transceiver = matchTransceiverByMid(transceivers, mediaDescription.mid);
+ if (!transceiver) {
+ bool receiveOnlyFlag = false;
+
+ if (mediaDescription.mode == "sendrecv" || mediaDescription.mode == "recvonly") {
+ // Try to match an existing transceiver.
+ transceiver = matchTransceiver(transceivers, [&mediaDescription] (RTCRtpTransceiver& current) {
+ return !current.stopped() && current.mid().isNull() && current.sender().trackKind() == mediaDescription.type;
+ });
+
+ if (transceiver) {
+ // This transceiver was created locally with a provisional mid. Its real mid will now be set by the remote
+ // description so we need to update the mid of the transceiver's muted source to preserve the association.
+ transceiver->setMid(mediaDescription.mid);
+ m_mediaEndpoint->replaceMutedRemoteSourceMid(transceiver->provisionalMid(), mediaDescription.mid);
+ } else
+ receiveOnlyFlag = true;
+ }
+
+ if (!transceiver) {
+ auto sender = RTCRtpSender::create(mediaDescription.type, Vector<String>(), m_peerConnection.senderClient());
+ auto receiver = createReceiver(mediaDescription.mid, mediaDescription.type, mediaDescription.mediaStreamTrackId);
+
+ auto newTransceiver = RTCRtpTransceiver::create(WTFMove(sender), WTFMove(receiver));
+ newTransceiver->setMid(mediaDescription.mid);
+ if (receiveOnlyFlag)
+ newTransceiver->disableSendingDirection();
+
+ transceiver = newTransceiver.ptr();
+ m_peerConnection.addTransceiver(WTFMove(newTransceiver));
+ }
+ }
+
+ if (mediaDescription.mode == "sendrecv" || mediaDescription.mode == "sendonly") {
+ auto& receiver = transceiver->receiver();
+ if (receiver.isDispatched())
+ continue;
+ receiver.setDispatched(true);
+
+ Vector<String> mediaStreamIds;
+ if (!mediaDescription.mediaStreamId.isEmpty())
+ mediaStreamIds.append(mediaDescription.mediaStreamId);
+
+ // A remote track can be associated with 0..* MediaStreams. We create a new stream for
+ // a track in case of an unrecognized stream id, or just add the track if the stream
+ // already exists.
+ HashMap<String, RefPtr<MediaStream>> trackEventMediaStreams;
+ for (auto& id : mediaStreamIds) {
+ if (m_remoteStreamMap.contains(id)) {
+ RefPtr<MediaStream> stream = m_remoteStreamMap.get(id);
+ stream->addTrack(*receiver.track());
+ trackEventMediaStreams.add(id, WTFMove(stream));
+ } else {
+ auto newStream = MediaStream::create(*m_peerConnection.scriptExecutionContext(), MediaStreamTrackVector({ receiver.track() }));
+ m_remoteStreamMap.add(id, newStream.copyRef());
+ legacyMediaStreamEvents.append(MediaStreamEvent::create(eventNames().addstreamEvent, false, false, newStream.copyRef()));
+ trackEventMediaStreams.add(id, WTFMove(newStream));
+ }
+ }
+
+ Vector<RefPtr<MediaStream>> streams;
+ copyValuesToVector(trackEventMediaStreams, streams);
+
+ m_peerConnection.fireEvent(RTCTrackEvent::create(eventNames().trackEvent, false, false,
+ &receiver, receiver.track(), WTFMove(streams), transceiver));
+ }
+ }
+
+ // Fire legacy addstream events.
+ for (auto& event : legacyMediaStreamEvents)
+ m_peerConnection.fireEvent(*event);
+
+ SignalingState newSignalingState;
+
+ // Update state and local descriptions according to setLocal/RemoteDescription processing model
+ switch (newDescription->type()) {
+ case RTCSessionDescription::SdpType::Offer:
+ m_pendingRemoteDescription = WTFMove(newDescription);
+ newSignalingState = SignalingState::HaveRemoteOffer;
+ break;
+
+ case RTCSessionDescription::SdpType::Answer:
+ m_currentRemoteDescription = WTFMove(newDescription);
+ m_currentLocalDescription = m_pendingLocalDescription;
+ m_pendingRemoteDescription = nullptr;
+ m_pendingLocalDescription = nullptr;
+ newSignalingState = SignalingState::Stable;
+ break;
+
+ case RTCSessionDescription::SdpType::Rollback:
+ m_pendingRemoteDescription = nullptr;
+ newSignalingState = SignalingState::Stable;
+ break;
+
+ case RTCSessionDescription::SdpType::Pranswer:
+ m_pendingRemoteDescription = WTFMove(newDescription);
+ newSignalingState = SignalingState::HaveRemotePrAnswer;
+ break;
+ }
+
+ updateSignalingState(newSignalingState);
+ setRemoteDescriptionSucceeded();
+}
+
+RefPtr<RTCSessionDescription> MediaEndpointPeerConnection::remoteDescription() const
+{
+ return createRTCSessionDescription(internalRemoteDescription());
+}
+
+RefPtr<RTCSessionDescription> MediaEndpointPeerConnection::currentRemoteDescription() const
+{
+ return createRTCSessionDescription(m_currentRemoteDescription.get());
+}
+
+RefPtr<RTCSessionDescription> MediaEndpointPeerConnection::pendingRemoteDescription() const
+{
+ return createRTCSessionDescription(m_pendingRemoteDescription.get());
+}
+
+void MediaEndpointPeerConnection::setConfiguration(MediaEndpointConfiguration&& configuration)
+{
+ m_mediaEndpoint->setConfiguration(WTFMove(configuration));
+}
+
+void MediaEndpointPeerConnection::doAddIceCandidate(RTCIceCandidate& rtcCandidate)
+{
+ runTask([this, protectedCandidate = RefPtr<RTCIceCandidate>(&rtcCandidate)]() mutable {
+ addIceCandidateTask(*protectedCandidate);
+ });
+}
+
+void MediaEndpointPeerConnection::addIceCandidateTask(RTCIceCandidate& rtcCandidate)
+{
+ if (!internalRemoteDescription()) {
+ addIceCandidateFailed(Exception { INVALID_STATE_ERR, "No remote description set" });
+ return;
+ }
+
+ auto& remoteMediaDescriptions = internalRemoteDescription()->configuration()->mediaDescriptions();
+ PeerMediaDescription* targetMediaDescription = nullptr;
+
+ // When identifying the target media description, sdpMid takes precedence over sdpMLineIndex
+ // if both are present.
+ if (!rtcCandidate.sdpMid().isNull()) {
+ const String& mid = rtcCandidate.sdpMid();
+ for (auto& description : remoteMediaDescriptions) {
+ if (description.mid == mid) {
+ targetMediaDescription = &description;
+ break;
+ }
+ }
+
+ if (!targetMediaDescription) {
+ addIceCandidateFailed(Exception { OperationError, "sdpMid did not match any media description" });
+ return;
+ }
+ } else if (rtcCandidate.sdpMLineIndex()) {
+ unsigned short sdpMLineIndex = rtcCandidate.sdpMLineIndex().value();
+ if (sdpMLineIndex >= remoteMediaDescriptions.size()) {
+ addIceCandidateFailed(Exception { OperationError, "sdpMLineIndex is out of range" });
+ return;
+ }
+ targetMediaDescription = &remoteMediaDescriptions[sdpMLineIndex];
+ } else {
+ ASSERT_NOT_REACHED();
+ return;
+ }
+
+ auto result = m_sdpProcessor->parseCandidateLine(rtcCandidate.candidate());
+ if (result.parsingStatus() != SDPProcessor::Result::Success) {
+ if (result.parsingStatus() == SDPProcessor::Result::ParseError)
+ addIceCandidateFailed(Exception { OperationError, "Invalid candidate content" });
+ else
+ LOG_ERROR("SDPProcessor internal error");
+ return;
+ }
+
+ ASSERT(targetMediaDescription);
+ m_mediaEndpoint->addRemoteCandidate(result.candidate(), targetMediaDescription->mid, targetMediaDescription->iceUfrag, targetMediaDescription->icePassword);
+
+ targetMediaDescription->addIceCandidate(WTFMove(result.candidate()));
+
+ addIceCandidateSucceeded();
+}
+
+void MediaEndpointPeerConnection::getStats(MediaStreamTrack* track, Ref<DeferredPromise>&& promise)
+{
+ m_mediaEndpoint->getStats(track, WTFMove(promise));
+}
+
+Vector<RefPtr<MediaStream>> MediaEndpointPeerConnection::getRemoteStreams() const
+{
+ Vector<RefPtr<MediaStream>> remoteStreams;
+ copyValuesToVector(m_remoteStreamMap, remoteStreams);
+ return remoteStreams;
+}
+
+Ref<RTCRtpReceiver> MediaEndpointPeerConnection::createReceiver(const String& transceiverMid, const String& trackKind, const String& trackId)
+{
+ RealtimeMediaSource::Type sourceType = trackKind == "audio" ? RealtimeMediaSource::Type::Audio : RealtimeMediaSource::Type::Video;
+
+ // Create a muted remote source that will be unmuted once media starts arriving.
+ auto remoteSource = m_mediaEndpoint->createMutedRemoteSource(transceiverMid, sourceType);
+ auto remoteTrackPrivate = MediaStreamTrackPrivate::create(WTFMove(remoteSource), String(trackId));
+ auto remoteTrack = MediaStreamTrack::create(*m_peerConnection.scriptExecutionContext(), WTFMove(remoteTrackPrivate));
+
+ return RTCRtpReceiver::create(WTFMove(remoteTrack));
+}
+
+std::unique_ptr<RTCDataChannelHandler> MediaEndpointPeerConnection::createDataChannelHandler(const String& label, const RTCDataChannelInit& options)
+{
+ return m_mediaEndpoint->createDataChannelHandler(label, options);
+}
+
+void MediaEndpointPeerConnection::replaceTrack(RTCRtpSender& sender, RefPtr<MediaStreamTrack>&& withTrack, DOMPromise<void>&& promise)
+{
+ RTCRtpTransceiver* transceiver = matchTransceiver(m_peerConnection.getTransceivers(), [&sender] (RTCRtpTransceiver& current) {
+ return &current.sender() == &sender;
+ });
+ ASSERT(transceiver);
+
+ const String& mid = transceiver->mid();
+ if (mid.isNull()) {
+ // Transceiver is not associated with a media description yet.
+ sender.setTrack(WTFMove(withTrack));
+ promise.resolve();
+ return;
+ }
+
+ runTask([this, protectedSender = RefPtr<RTCRtpSender>(&sender), mid, protectedTrack = WTFMove(withTrack), protectedPromise = WTFMove(promise)]() mutable {
+ replaceTrackTask(*protectedSender, mid, WTFMove(protectedTrack), protectedPromise);
+ });
+}
+
+void MediaEndpointPeerConnection::replaceTrackTask(RTCRtpSender& sender, const String& mid, RefPtr<MediaStreamTrack>&& withTrack, DOMPromise<void>& promise)
+{
+ if (m_peerConnection.internalSignalingState() == SignalingState::Closed)
+ return;
+
+ m_mediaEndpoint->replaceSendSource(withTrack->source(), mid);
+
+ sender.setTrack(WTFMove(withTrack));
+ promise.resolve();
+}
+
+void MediaEndpointPeerConnection::doStop()
+{
+ m_mediaEndpoint->stop();
+}
+
+void MediaEndpointPeerConnection::emulatePlatformEvent(const String& action)
+{
+ m_mediaEndpoint->emulatePlatformEvent(action);
+}
+
+MediaEndpointSessionDescription* MediaEndpointPeerConnection::internalLocalDescription() const
+{
+ return m_pendingLocalDescription ? m_pendingLocalDescription.get() : m_currentLocalDescription.get();
+}
+
+MediaEndpointSessionDescription* MediaEndpointPeerConnection::internalRemoteDescription() const
+{
+ return m_pendingRemoteDescription ? m_pendingRemoteDescription.get() : m_currentRemoteDescription.get();
+}
+
+RefPtr<RTCSessionDescription> MediaEndpointPeerConnection::createRTCSessionDescription(MediaEndpointSessionDescription* description) const
+{
+ return description ? description->toRTCSessionDescription(*m_sdpProcessor) : nullptr;
+}
+
+void MediaEndpointPeerConnection::gotDtlsFingerprint(const String& fingerprint, const String& fingerprintFunction)
+{
+ ASSERT(isMainThread());
+
+ m_dtlsFingerprint = fingerprint;
+ m_dtlsFingerprintFunction = fingerprintFunction;
+
+ startRunningTasks();
+}
+
+void MediaEndpointPeerConnection::gotIceCandidate(const String& mid, IceCandidate&& candidate)
+{
+ ASSERT(isMainThread());
+
+ auto& mediaDescriptions = internalLocalDescription()->configuration()->mediaDescriptions();
+ size_t mediaDescriptionIndex = notFound;
+
+ for (size_t i = 0; i < mediaDescriptions.size(); ++i) {
+ if (mediaDescriptions[i].mid == mid) {
+ mediaDescriptionIndex = i;
+ break;
+ }
+ }
+ ASSERT(mediaDescriptionIndex != notFound);
+
+ String candidateLine;
+ auto result = m_sdpProcessor->generateCandidateLine(candidate, candidateLine);
+ if (result != SDPProcessor::Result::Success) {
+ LOG_ERROR("SDPProcessor internal error");
+ return;
+ }
+
+ mediaDescriptions[mediaDescriptionIndex].addIceCandidate(WTFMove(candidate));
+
+ fireICECandidateEvent(RTCIceCandidate::create(candidateLine, mid, mediaDescriptionIndex));
+}
+
+void MediaEndpointPeerConnection::doneGatheringCandidates(const String& mid)
+{
+ ASSERT(isMainThread());
+
+ RtpTransceiverVector transceivers = RtpTransceiverVector(m_peerConnection.getTransceivers());
+ RTCRtpTransceiver* notifyingTransceiver = matchTransceiverByMid(transceivers, mid);
+ ASSERT(notifyingTransceiver);
+
+ notifyingTransceiver->iceTransport().setGatheringState(RTCIceTransport::GatheringState::Complete);
+
+ // Don't notify the script if there are transceivers still gathering.
+ RTCRtpTransceiver* stillGatheringTransceiver = matchTransceiver(transceivers, [] (RTCRtpTransceiver& current) {
+ return !current.stopped() && !current.mid().isNull()
+ && current.iceTransport().gatheringState() != RTCIceTransport::GatheringState::Complete;
+ });
+ if (!stillGatheringTransceiver)
+ PeerConnectionBackend::doneGatheringCandidates();
+}
+
+static RTCIceTransport::TransportState deriveAggregatedIceConnectionState(const Vector<RTCIceTransport::TransportState>& states)
+{
+ unsigned newCount = 0;
+ unsigned checkingCount = 0;
+ unsigned connectedCount = 0;
+ unsigned completedCount = 0;
+ unsigned failedCount = 0;
+ unsigned disconnectedCount = 0;
+ unsigned closedCount = 0;
+
+ for (auto& state : states) {
+ switch (state) {
+ case RTCIceTransport::TransportState::New: ++newCount; break;
+ case RTCIceTransport::TransportState::Checking: ++checkingCount; break;
+ case RTCIceTransport::TransportState::Connected: ++connectedCount; break;
+ case RTCIceTransport::TransportState::Completed: ++completedCount; break;
+ case RTCIceTransport::TransportState::Failed: ++failedCount; break;
+ case RTCIceTransport::TransportState::Disconnected: ++disconnectedCount; break;
+ case RTCIceTransport::TransportState::Closed: ++closedCount; break;
+ }
+ }
+
+ // The aggregated RTCIceConnectionState is derived from the RTCIceTransportState of all RTCIceTransports.
+ if ((newCount > 0 && !checkingCount && !failedCount && !disconnectedCount) || (closedCount == states.size()))
+ return RTCIceTransport::TransportState::New;
+
+ if (checkingCount > 0 && !failedCount && !disconnectedCount)
+ return RTCIceTransport::TransportState::Checking;
+
+ if ((connectedCount + completedCount + closedCount) == states.size() && connectedCount > 0)
+ return RTCIceTransport::TransportState::Connected;
+
+ if ((completedCount + closedCount) == states.size() && completedCount > 0)
+ return RTCIceTransport::TransportState::Completed;
+
+ if (failedCount > 0)
+ return RTCIceTransport::TransportState::Failed;
+
+ if (disconnectedCount > 0) // Any failed caught above.
+ return RTCIceTransport::TransportState::Disconnected;
+
+ ASSERT_NOT_REACHED();
+ return RTCIceTransport::TransportState::New;
+}
+
+void MediaEndpointPeerConnection::iceTransportStateChanged(const String& mid, MediaEndpoint::IceTransportState mediaEndpointIceTransportState)
+{
+ ASSERT(isMainThread());
+
+ RTCRtpTransceiver* transceiver = matchTransceiverByMid(m_peerConnection.getTransceivers(), mid);
+ ASSERT(transceiver);
+
+ RTCIceTransport::TransportState transportState = static_cast<RTCIceTransport::TransportState>(mediaEndpointIceTransportState);
+ transceiver->iceTransport().setTransportState(transportState);
+
+ // Determine if the script needs to be notified.
+ Vector<RTCIceTransport::TransportState> transportStates;
+ for (auto& transceiver : m_peerConnection.getTransceivers())
+ transportStates.append(transceiver->iceTransport().transportState());
+
+ RTCIceTransport::TransportState derivedState = deriveAggregatedIceConnectionState(transportStates);
+ m_peerConnection.updateIceConnectionState(static_cast<IceConnectionState>(derivedState));
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/MediaEndpointPeerConnection.h b/Source/WebCore/Modules/mediastream/MediaEndpointPeerConnection.h
new file mode 100644
index 000000000..01b37843d
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/MediaEndpointPeerConnection.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2015, 2016 Ericsson AB. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Ericsson 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 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
+ * OWNER 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(WEB_RTC)
+
+#include "MediaEndpoint.h"
+#include "PeerConnectionBackend.h"
+#include "RTCSessionDescription.h"
+#include <wtf/Function.h>
+
+namespace WebCore {
+
+class MediaEndpointSessionDescription;
+class SDPProcessor;
+
+class MediaEndpointPeerConnection final : public PeerConnectionBackend, public MediaEndpointClient {
+public:
+ WEBCORE_EXPORT explicit MediaEndpointPeerConnection(RTCPeerConnection&);
+
+private:
+ RefPtr<RTCSessionDescription> localDescription() const final;
+ RefPtr<RTCSessionDescription> currentLocalDescription() const final;
+ RefPtr<RTCSessionDescription> pendingLocalDescription() const final;
+
+ RefPtr<RTCSessionDescription> remoteDescription() const final;
+ RefPtr<RTCSessionDescription> currentRemoteDescription() const final;
+ RefPtr<RTCSessionDescription> pendingRemoteDescription() const final;
+
+ void setConfiguration(MediaEndpointConfiguration&&) final;
+
+ void getStats(MediaStreamTrack*, Ref<DeferredPromise>&&) final;
+
+ Vector<RefPtr<MediaStream>> getRemoteStreams() const final;
+
+ Ref<RTCRtpReceiver> createReceiver(const String& transceiverMid, const String& trackKind, const String& trackId) final;
+ void replaceTrack(RTCRtpSender&, RefPtr<MediaStreamTrack>&&, DOMPromise<void>&&) final;
+
+ void emulatePlatformEvent(const String& action) final;
+
+ void runTask(Function<void ()>&&);
+ void startRunningTasks();
+
+ void doCreateOffer(RTCOfferOptions&&) final;
+ void doCreateAnswer(RTCAnswerOptions&&) final;
+ void doSetLocalDescription(RTCSessionDescription&) final;
+ void doSetRemoteDescription(RTCSessionDescription&) final;
+ void doAddIceCandidate(RTCIceCandidate&) final;
+ void doStop() final;
+
+ void createOfferTask(const RTCOfferOptions&);
+ void createAnswerTask(const RTCAnswerOptions&);
+
+ void setLocalDescriptionTask(RefPtr<RTCSessionDescription>&&);
+ void setRemoteDescriptionTask(RefPtr<RTCSessionDescription>&&);
+
+ void addIceCandidateTask(RTCIceCandidate&);
+
+ void replaceTrackTask(RTCRtpSender&, const String& mid, RefPtr<MediaStreamTrack>&&, DOMPromise<void>&);
+
+ bool localDescriptionTypeValidForState(RTCSessionDescription::SdpType) const;
+ bool remoteDescriptionTypeValidForState(RTCSessionDescription::SdpType) const;
+
+ MediaEndpointSessionDescription* internalLocalDescription() const;
+ MediaEndpointSessionDescription* internalRemoteDescription() const;
+ RefPtr<RTCSessionDescription> createRTCSessionDescription(MediaEndpointSessionDescription*) const;
+
+ // MediaEndpointClient
+ void gotDtlsFingerprint(const String& fingerprint, const String& fingerprintFunction) final;
+ void gotIceCandidate(const String& mid, IceCandidate&&) final;
+ void doneGatheringCandidates(const String& mid) final;
+ void iceTransportStateChanged(const String& mid, MediaEndpoint::IceTransportState) final;
+
+ std::unique_ptr<RTCDataChannelHandler> createDataChannelHandler(const String&, const RTCDataChannelInit&) final;
+
+ std::unique_ptr<MediaEndpoint> m_mediaEndpoint;
+
+ Function<void ()> m_initialDeferredTask;
+
+ std::unique_ptr<SDPProcessor> m_sdpProcessor;
+
+ Vector<MediaPayload> m_defaultAudioPayloads;
+ Vector<MediaPayload> m_defaultVideoPayloads;
+
+ String m_cname;
+ String m_iceUfrag;
+ String m_icePassword;
+ String m_dtlsFingerprint;
+ String m_dtlsFingerprintFunction;
+ unsigned m_sdpOfferSessionVersion { 0 };
+ unsigned m_sdpAnswerSessionVersion { 0 };
+
+ RefPtr<MediaEndpointSessionDescription> m_currentLocalDescription;
+ RefPtr<MediaEndpointSessionDescription> m_pendingLocalDescription;
+
+ RefPtr<MediaEndpointSessionDescription> m_currentRemoteDescription;
+ RefPtr<MediaEndpointSessionDescription> m_pendingRemoteDescription;
+
+ HashMap<String, RefPtr<MediaStream>> m_remoteStreamMap;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/MediaEndpointSessionDescription.cpp b/Source/WebCore/Modules/mediastream/MediaEndpointSessionDescription.cpp
new file mode 100644
index 000000000..ac5b2f0d8
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/MediaEndpointSessionDescription.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2015, 2016 Ericsson AB. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Ericsson 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 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
+ * OWNER 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 "MediaEndpointSessionDescription.h"
+
+#if ENABLE(WEB_RTC)
+
+#include "ExceptionCode.h"
+#include "SDPProcessor.h"
+#include <wtf/NeverDestroyed.h>
+
+namespace WebCore {
+
+#define STRING_FUNCTION(name) \
+ static const String& name##String() \
+ { \
+ static NeverDestroyed<const String> name { ASCIILiteral(#name) }; \
+ return name; \
+ }
+
+STRING_FUNCTION(offer)
+STRING_FUNCTION(pranswer)
+STRING_FUNCTION(answer)
+STRING_FUNCTION(rollback)
+
+Ref<MediaEndpointSessionDescription> MediaEndpointSessionDescription::create(RTCSessionDescription::SdpType type, RefPtr<MediaEndpointSessionConfiguration>&& configuration)
+{
+ return adoptRef(*new MediaEndpointSessionDescription(type, WTFMove(configuration), nullptr));
+}
+
+ExceptionOr<Ref<MediaEndpointSessionDescription>> MediaEndpointSessionDescription::create(RefPtr<RTCSessionDescription>&& rtcDescription, const SDPProcessor& sdpProcessor)
+{
+ RefPtr<MediaEndpointSessionConfiguration> configuration;
+ auto result = sdpProcessor.parse(rtcDescription->sdp(), configuration);
+ if (result != SDPProcessor::Result::Success) {
+ if (result == SDPProcessor::Result::ParseError)
+ return Exception { INVALID_ACCESS_ERR, ASCIILiteral("SDP content is invalid") };
+ LOG_ERROR("SDPProcessor internal error");
+ return Exception { ABORT_ERR, ASCIILiteral("Internal error") };
+ }
+ return adoptRef(*new MediaEndpointSessionDescription(rtcDescription->type(), WTFMove(configuration), WTFMove(rtcDescription)));
+}
+
+RefPtr<RTCSessionDescription> MediaEndpointSessionDescription::toRTCSessionDescription(const SDPProcessor& sdpProcessor) const
+{
+ String sdpString;
+ SDPProcessor::Result result = sdpProcessor.generate(*m_configuration, sdpString);
+ if (result != SDPProcessor::Result::Success) {
+ LOG_ERROR("SDPProcessor internal error");
+ return nullptr;
+ }
+
+ // If this object was created from an RTCSessionDescription, toRTCSessionDescription will return
+ // that same instance but with an updated sdp. It is used for RTCPeerConnection's description
+ // atributes (e.g. localDescription and pendingLocalDescription).
+ if (m_rtcDescription) {
+ m_rtcDescription->setSdp(sdpString);
+ return m_rtcDescription;
+ }
+
+ return RTCSessionDescription::create(m_type, sdpString);
+}
+
+const String& MediaEndpointSessionDescription::typeString() const
+{
+ switch (m_type) {
+ case RTCSessionDescription::SdpType::Offer:
+ return offerString();
+ case RTCSessionDescription::SdpType::Pranswer:
+ return pranswerString();
+ case RTCSessionDescription::SdpType::Answer:
+ return answerString();
+ case RTCSessionDescription::SdpType::Rollback:
+ return rollbackString();
+ }
+
+ ASSERT_NOT_REACHED();
+ return emptyString();
+}
+
+bool MediaEndpointSessionDescription::isLaterThan(MediaEndpointSessionDescription* other) const
+{
+ return !other || configuration()->sessionVersion() > other->configuration()->sessionVersion();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/MediaEndpointSessionDescription.h b/Source/WebCore/Modules/mediastream/MediaEndpointSessionDescription.h
new file mode 100644
index 000000000..60316ce58
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/MediaEndpointSessionDescription.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2015, 2016 Ericsson AB. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Ericsson 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 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
+ * OWNER 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(WEB_RTC)
+
+#include "ExceptionOr.h"
+#include "RTCSessionDescription.h"
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+class MediaEndpointSessionConfiguration;
+class SDPProcessor;
+class DOMError;
+
+class MediaEndpointSessionDescription : public RefCounted<MediaEndpointSessionDescription> {
+public:
+ static Ref<MediaEndpointSessionDescription> create(RTCSessionDescription::SdpType, RefPtr<MediaEndpointSessionConfiguration>&&);
+ static ExceptionOr<Ref<MediaEndpointSessionDescription>> create(RefPtr<RTCSessionDescription>&&, const SDPProcessor&);
+ virtual ~MediaEndpointSessionDescription() { } // FIXME: Why is this virtual? There are no other virtual functions in this class.
+
+ RefPtr<RTCSessionDescription> toRTCSessionDescription(const SDPProcessor&) const;
+
+ RTCSessionDescription::SdpType type() const { return m_type; }
+ const String& typeString() const;
+ MediaEndpointSessionConfiguration* configuration() const { return m_configuration.get(); }
+
+ bool isLaterThan(MediaEndpointSessionDescription* other) const;
+
+private:
+ MediaEndpointSessionDescription(RTCSessionDescription::SdpType type, RefPtr<MediaEndpointSessionConfiguration>&& configuration, RefPtr<RTCSessionDescription>&& rtcDescription)
+ : m_type(type)
+ , m_configuration(configuration)
+ , m_rtcDescription(WTFMove(rtcDescription))
+ { }
+
+ RTCSessionDescription::SdpType m_type;
+ RefPtr<MediaEndpointSessionConfiguration> m_configuration;
+
+ RefPtr<RTCSessionDescription> m_rtcDescription;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/MediaSourceStates.cpp b/Source/WebCore/Modules/mediastream/MediaSourceStates.cpp
deleted file mode 100644
index 6f7eff6e4..000000000
--- a/Source/WebCore/Modules/mediastream/MediaSourceStates.cpp
+++ /dev/null
@@ -1,59 +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 COMPUTER, 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
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (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_STREAM)
-
-#include "MediaSourceStates.h"
-
-#include <wtf/NeverDestroyed.h>
-
-namespace WebCore {
-
-RefPtr<MediaSourceStates> MediaSourceStates::create(const MediaStreamSourceStates& states)
-{
- return adoptRef(new MediaSourceStates(states));
-}
-
-MediaSourceStates::MediaSourceStates(const MediaStreamSourceStates& states)
- : m_sourceStates(states)
-{
-}
-
-const AtomicString& MediaSourceStates::sourceType() const
-{
- return MediaStreamSourceStates::sourceType(m_sourceStates.sourceType());
-}
-
-const AtomicString& MediaSourceStates::facingMode() const
-{
- return MediaStreamSourceStates::facingMode(m_sourceStates.facingMode());
-}
-
-
-} // namespace WebCore
-
-#endif
diff --git a/Source/WebCore/Modules/mediastream/MediaSourceStates.h b/Source/WebCore/Modules/mediastream/MediaSourceStates.h
deleted file mode 100644
index b0a4554e2..000000000
--- a/Source/WebCore/Modules/mediastream/MediaSourceStates.h
+++ /dev/null
@@ -1,63 +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 COMPUTER, 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
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (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 MediaSourceStates_h
-#define MediaSourceStates_h
-
-#if ENABLE(MEDIA_STREAM)
-
-#include "MediaStreamSourceCapabilities.h"
-#include "ScriptWrappable.h"
-#include <wtf/Forward.h>
-#include <wtf/RefCounted.h>
-
-namespace WebCore {
-
-class MediaSourceStates : public RefCounted<MediaSourceStates>, public ScriptWrappable {
-public:
- static RefPtr<MediaSourceStates> create(const MediaStreamSourceStates&);
-
- const AtomicString& sourceType() const;
- const AtomicString& sourceId() const { return m_sourceStates.sourceId(); }
- unsigned long width() const { return m_sourceStates.width(); }
- unsigned long height() const { return m_sourceStates.height(); }
- float frameRate() const { return m_sourceStates.frameRate(); }
- float aspectRatio() const { return m_sourceStates.aspectRatio(); }
- const AtomicString& facingMode() const;
- unsigned long volume() const { return m_sourceStates.volume(); }
-
- bool hasVideoSource() const { return m_sourceStates.sourceType() == MediaStreamSourceStates::Camera; }
-
-private:
- explicit MediaSourceStates(const MediaStreamSourceStates&);
-
- MediaStreamSourceStates m_sourceStates;
-};
-
-} // namespace WebCore
-
-#endif // MediaSourceStates_h
-
-#endif
diff --git a/Source/WebCore/Modules/mediastream/MediaSourceStates.idl b/Source/WebCore/Modules/mediastream/MediaSourceStates.idl
deleted file mode 100644
index 6ce9f7ef1..000000000
--- a/Source/WebCore/Modules/mediastream/MediaSourceStates.idl
+++ /dev/null
@@ -1,44 +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 COMPUTER, 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
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (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 SourceTypeEnum { "none", "camera", "microphone" };
-enum VideoFacingModeEnum { "user", "environment", "left", "right" };
-
-[
- Conditional=MEDIA_STREAM,
- ImplementationLacksVTable,
- NoInterfaceObject,
-] interface MediaSourceStates {
- readonly attribute SourceTypeEnum sourceType;
- readonly attribute DOMString sourceId;
-
- [CustomGetter] readonly attribute unsigned long? width;
- [CustomGetter] readonly attribute unsigned long? height;
- [CustomGetter] readonly attribute float? frameRate;
- [CustomGetter] readonly attribute float? aspectRatio;
- [CustomGetter] readonly attribute VideoFacingModeEnum? facingMode;
- [CustomGetter] readonly attribute unsigned long? volume;
-};
-
diff --git a/Source/WebCore/Modules/mediastream/MediaStream.cpp b/Source/WebCore/Modules/mediastream/MediaStream.cpp
index 22ec3d4cc..dcacf8f82 100644
--- a/Source/WebCore/Modules/mediastream/MediaStream.cpp
+++ b/Source/WebCore/Modules/mediastream/MediaStream.cpp
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2011 Google Inc. All rights reserved.
- * Copyright (C) 2011, 2012 Ericsson AB. All rights reserved.
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2012, 2015 Ericsson AB. All rights reserved.
+ * Copyright (C) 2013-2016 Apple Inc. All rights reserved.
* Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
*
* Redistribution and use in source and binary forms, with or without
@@ -30,356 +30,366 @@
#if ENABLE(MEDIA_STREAM)
-#include "AudioStreamTrack.h"
+#include "Document.h"
#include "Event.h"
-#include "ExceptionCode.h"
-#include "MediaStreamCenter.h"
+#include "EventNames.h"
+#include "Logging.h"
#include "MediaStreamRegistry.h"
-#include "MediaStreamSource.h"
#include "MediaStreamTrackEvent.h"
-#include "VideoStreamTrack.h"
-#include <wtf/NeverDestroyed.h>
+#include "Page.h"
+#include "RealtimeMediaSource.h"
+#include "URL.h"
namespace WebCore {
-PassRefPtr<MediaStream> MediaStream::create(ScriptExecutionContext& context)
+Ref<MediaStream> MediaStream::create(ScriptExecutionContext& context)
{
- return MediaStream::create(context, MediaStreamPrivate::create(Vector<RefPtr<MediaStreamSource>>(), Vector<RefPtr<MediaStreamSource>>()));
+ return MediaStream::create(context, MediaStreamPrivate::create(Vector<RefPtr<MediaStreamTrackPrivate>>()));
}
-PassRefPtr<MediaStream> MediaStream::create(ScriptExecutionContext& context, PassRefPtr<MediaStream> stream)
+Ref<MediaStream> MediaStream::create(ScriptExecutionContext& context, MediaStream& stream)
{
- ASSERT(stream);
-
- Vector<RefPtr<MediaStreamTrackPrivate>> audioTracks;
- Vector<RefPtr<MediaStreamTrackPrivate>> videoTracks;
-
- for (size_t i = 0; i < stream->m_audioTracks.size(); ++i)
- audioTracks.append(&stream->m_audioTracks[i]->privateTrack());
-
- for (size_t i = 0; i < stream->m_videoTracks.size(); ++i)
- videoTracks.append(&stream->m_videoTracks[i]->privateTrack());
-
- return MediaStream::create(context, MediaStreamPrivate::create(audioTracks, videoTracks));
+ return adoptRef(*new MediaStream(context, stream.getTracks()));
}
-PassRefPtr<MediaStream> MediaStream::create(ScriptExecutionContext& context, const Vector<RefPtr<MediaStreamTrack>>& tracks)
+Ref<MediaStream> MediaStream::create(ScriptExecutionContext& context, const MediaStreamTrackVector& tracks)
{
- Vector<RefPtr<MediaStreamTrackPrivate>> audioTracks;
- Vector<RefPtr<MediaStreamTrackPrivate>> videoTracks;
-
- for (size_t i = 0; i < tracks.size(); ++i) {
- if (tracks[i]->kind() == "audio")
- audioTracks.append(&tracks[i]->privateTrack());
- else
- videoTracks.append(&tracks[i]->privateTrack());
- }
+ return adoptRef(*new MediaStream(context, tracks));
+}
- return MediaStream::create(context, MediaStreamPrivate::create(audioTracks, videoTracks));
+Ref<MediaStream> MediaStream::create(ScriptExecutionContext& context, RefPtr<MediaStreamPrivate>&& streamPrivate)
+{
+ return adoptRef(*new MediaStream(context, WTFMove(streamPrivate)));
}
-PassRefPtr<MediaStream> MediaStream::create(ScriptExecutionContext& context, PassRefPtr<MediaStreamPrivate> privateStream)
+MediaStream::MediaStream(ScriptExecutionContext& context, const MediaStreamTrackVector& tracks)
+ : ContextDestructionObserver(&context)
+ , m_activityEventTimer(*this, &MediaStream::activityEventTimerFired)
{
- return adoptRef(new MediaStream(context, privateStream));
+ // This constructor preserves MediaStreamTrack instances and must be used by calls originating
+ // from the JavaScript MediaStream constructor.
+ MediaStreamTrackPrivateVector trackPrivates;
+ trackPrivates.reserveCapacity(tracks.size());
+
+ for (auto& track : tracks) {
+ track->addObserver(*this);
+ m_trackSet.add(track->id(), track);
+ trackPrivates.append(&track->privateTrack());
+ }
+
+ m_private = MediaStreamPrivate::create(trackPrivates);
+ setIsActive(m_private->active());
+ m_private->addObserver(*this);
+ MediaStreamRegistry::shared().registerStream(*this);
+ document()->addAudioProducer(this);
}
-MediaStream::MediaStream(ScriptExecutionContext& context, PassRefPtr<MediaStreamPrivate> privateStream)
+MediaStream::MediaStream(ScriptExecutionContext& context, RefPtr<MediaStreamPrivate>&& streamPrivate)
: ContextDestructionObserver(&context)
- , m_private(privateStream)
- , m_scheduledEventTimer(this, &MediaStream::scheduledEventTimerFired)
+ , m_private(streamPrivate)
+ , m_activityEventTimer(*this, &MediaStream::activityEventTimerFired)
{
ASSERT(m_private);
- m_private->setClient(this);
-
- RefPtr<MediaStreamTrack> track;
- size_t numberOfAudioTracks = m_private->numberOfAudioTracks();
- m_audioTracks.reserveCapacity(numberOfAudioTracks);
- for (size_t i = 0; i < numberOfAudioTracks; i++) {
- track = AudioStreamTrack::create(context, *m_private->audioTracks(i));
- track->addObserver(this);
- m_audioTracks.append(track.release());
+ setIsActive(m_private->active());
+ m_private->addObserver(*this);
+ MediaStreamRegistry::shared().registerStream(*this);
+
+ for (auto& trackPrivate : m_private->tracks()) {
+ auto track = MediaStreamTrack::create(context, *trackPrivate);
+ track->addObserver(*this);
+ m_trackSet.add(track->id(), WTFMove(track));
}
+ document()->addAudioProducer(this);
+}
- size_t numberOfVideoTracks = m_private->numberOfVideoTracks();
- m_videoTracks.reserveCapacity(numberOfVideoTracks);
- for (size_t i = 0; i < numberOfVideoTracks; i++) {
- track = VideoStreamTrack::create(context, *m_private->videoTracks(i));
- track->addObserver(this);
- m_videoTracks.append(track.release());
+MediaStream::~MediaStream()
+{
+ // Set isActive to false immediately so an callbacks triggered by shutting down, e.g.
+ // mediaState(), are short circuited.
+ m_isActive = false;
+ MediaStreamRegistry::shared().unregisterStream(*this);
+ m_private->removeObserver(*this);
+ for (auto& track : m_trackSet.values())
+ track->removeObserver(*this);
+ if (Document* document = this->document()) {
+ document->removeAudioProducer(this);
+ if (m_isWaitingUntilMediaCanStart)
+ document->removeMediaCanStartListener(this);
}
}
-MediaStream::~MediaStream()
+RefPtr<MediaStream> MediaStream::clone()
{
- m_private->setClient(0);
+ MediaStreamTrackVector clonedTracks;
+ clonedTracks.reserveCapacity(m_trackSet.size());
+
+ for (auto& track : m_trackSet.values())
+ clonedTracks.append(track->clone());
+
+ return MediaStream::create(*scriptExecutionContext(), clonedTracks);
}
-bool MediaStream::ended() const
+void MediaStream::addTrack(MediaStreamTrack& track)
{
- return m_private->ended();
+ if (!internalAddTrack(track, StreamModifier::DomAPI))
+ return;
+
+ for (auto& observer : m_observers)
+ observer->didAddOrRemoveTrack();
}
-void MediaStream::setEnded()
+void MediaStream::removeTrack(MediaStreamTrack& track)
{
- if (ended())
+ if (!internalRemoveTrack(track.id(), StreamModifier::DomAPI))
return;
- m_private->setEnded();
+
+ for (auto& observer : m_observers)
+ observer->didAddOrRemoveTrack();
}
-PassRefPtr<MediaStream> MediaStream::clone()
+MediaStreamTrack* MediaStream::getTrackById(String id)
{
- Vector<RefPtr<MediaStreamTrack>> trackSet;
+ auto it = m_trackSet.find(id);
+ if (it != m_trackSet.end())
+ return it->value.get();
- cloneMediaStreamTrackVector(trackSet, getAudioTracks());
- cloneMediaStreamTrackVector(trackSet, getVideoTracks());
- return MediaStream::create(*scriptExecutionContext(), trackSet);
+ return nullptr;
}
-void MediaStream::cloneMediaStreamTrackVector(Vector<RefPtr<MediaStreamTrack>>& destination, const Vector<RefPtr<MediaStreamTrack>>& source)
+MediaStreamTrackVector MediaStream::getAudioTracks() const
{
- for (auto it = source.begin(), end = source.end(); it != end; ++it)
- destination.append((*it)->clone());
+ return trackVectorForType(RealtimeMediaSource::Audio);
}
-void MediaStream::addTrack(PassRefPtr<MediaStreamTrack> prpTrack, ExceptionCode& ec)
+MediaStreamTrackVector MediaStream::getVideoTracks() const
{
- if (ended()) {
- ec = INVALID_STATE_ERR;
- return;
- }
+ return trackVectorForType(RealtimeMediaSource::Video);
+}
- if (!prpTrack) {
- ec = TYPE_MISMATCH_ERR;
- return;
- }
+MediaStreamTrackVector MediaStream::getTracks() const
+{
+ MediaStreamTrackVector tracks;
+ tracks.reserveCapacity(m_trackSet.size());
+ copyValuesToVector(m_trackSet, tracks);
- if (addTrack(prpTrack)) {
- for (auto observer = m_observers.begin(), end = m_observers.end(); observer != end; ++observer)
- (*observer)->didAddOrRemoveTrack();
- }
+ return tracks;
}
-bool MediaStream::addTrack(PassRefPtr<MediaStreamTrack> prpTrack)
+void MediaStream::contextDestroyed()
{
- // This is a common part used by addTrack called by JavaScript
- // and addRemoteTrack and only addRemoteTrack must fire addtrack event
- RefPtr<MediaStreamTrack> track = prpTrack;
- if (getTrackById(track->id()))
- return false;
-
- Vector<RefPtr<MediaStreamTrack>>* tracks = trackVectorForType(track->source()->type());
+ ContextDestructionObserver::contextDestroyed();
+}
- tracks->append(track);
- track->addObserver(this);
- m_private->addTrack(&track->privateTrack());
- return true;
+void MediaStream::trackDidEnd()
+{
+ m_private->updateActiveState(MediaStreamPrivate::NotifyClientOption::Notify);
}
-void MediaStream::removeTrack(PassRefPtr<MediaStreamTrack> prpTrack, ExceptionCode& ec)
+void MediaStream::activeStatusChanged()
{
- if (ended()) {
- ec = INVALID_STATE_ERR;
- return;
- }
+ // Schedule the active state change and event dispatch since this callback may be called
+ // synchronously from the DOM API (e.g. as a result of addTrack()).
+ scheduleActiveStateChange();
+}
- if (!prpTrack) {
- ec = TYPE_MISMATCH_ERR;
+void MediaStream::didAddTrack(MediaStreamTrackPrivate& trackPrivate)
+{
+ ScriptExecutionContext* context = scriptExecutionContext();
+ if (!context)
return;
- }
- if (removeTrack(prpTrack)) {
- for (auto observer = m_observers.begin(), end = m_observers.end(); observer != end; ++observer)
- (*observer)->didAddOrRemoveTrack();
- }
+ if (!getTrackById(trackPrivate.id()))
+ internalAddTrack(MediaStreamTrack::create(*context, trackPrivate), StreamModifier::Platform);
}
-bool MediaStream::removeTrack(PassRefPtr<MediaStreamTrack> prpTrack)
+void MediaStream::didRemoveTrack(MediaStreamTrackPrivate& trackPrivate)
{
- // This is a common part used by removeTrack called by JavaScript
- // and removeRemoteTrack and only removeRemoteTrack must fire removetrack event
- RefPtr<MediaStreamTrack> track = prpTrack;
- Vector<RefPtr<MediaStreamTrack>>* tracks = trackVectorForType(track->source()->type());
+ internalRemoveTrack(trackPrivate.id(), StreamModifier::Platform);
+}
- size_t pos = tracks->find(track);
- if (pos == notFound)
+bool MediaStream::internalAddTrack(Ref<MediaStreamTrack>&& trackToAdd, StreamModifier streamModifier)
+{
+ auto result = m_trackSet.add(trackToAdd->id(), WTFMove(trackToAdd));
+ if (!result.isNewEntry)
return false;
- tracks->remove(pos);
- m_private->removeTrack(&track->privateTrack());
- // There can be other tracks using the same source in the same MediaStream,
- // like when MediaStreamTrack::clone() is called, for instance.
- // Spec says that a source can be shared, so we must assure that there is no
- // other track using it.
- if (!haveTrackWithSource(track->source()))
- m_private->removeSource(track->source());
+ ASSERT(result.iterator->value);
+ auto& track = *result.iterator->value;
+ track.addObserver(*this);
- track->removeObserver(this);
- if (!m_audioTracks.size() && !m_videoTracks.size())
- setEnded();
+ if (streamModifier == StreamModifier::DomAPI)
+ m_private->addTrack(&track.privateTrack(), MediaStreamPrivate::NotifyClientOption::DontNotify);
+ else
+ dispatchEvent(MediaStreamTrackEvent::create(eventNames().addtrackEvent, false, false, &track));
return true;
}
-bool MediaStream::haveTrackWithSource(PassRefPtr<MediaStreamSource> source)
+bool MediaStream::internalRemoveTrack(const String& trackId, StreamModifier streamModifier)
{
- if (source->type() == MediaStreamSource::Audio) {
- for (auto it = m_audioTracks.begin(), end = m_audioTracks.end(); it != end; ++it) {
- if ((*it)->source() == source.get())
- return true;
- }
+ auto track = m_trackSet.take(trackId);
+ if (!track)
return false;
- }
- for (auto it = m_videoTracks.begin(), end = m_videoTracks.end(); it != end; ++it) {
- if ((*it)->source() == source.get())
- return true;
- }
+ track->removeObserver(*this);
+
+ if (streamModifier == StreamModifier::DomAPI)
+ m_private->removeTrack(track->privateTrack(), MediaStreamPrivate::NotifyClientOption::DontNotify);
+ else
+ dispatchEvent(MediaStreamTrackEvent::create(eventNames().removetrackEvent, false, false, WTFMove(track)));
- return false;
+ return true;
}
-MediaStreamTrack* MediaStream::getTrackById(String id)
+void MediaStream::setIsActive(bool active)
{
- for (auto it = m_audioTracks.begin(), end = m_audioTracks.end(); it != end; ++it) {
- if ((*it)->id() == id)
- return (*it).get();
- }
-
- for (auto it = m_videoTracks.begin(), end = m_videoTracks.end(); it != end; ++it) {
- if ((*it)->id() == id)
- return (*it).get();
- }
+ if (m_isActive == active)
+ return;
- return nullptr;
+ m_isActive = active;
+ statusDidChange();
}
-void MediaStream::trackDidEnd()
+void MediaStream::mediaCanStart(Document& document)
{
- for (auto it = m_audioTracks.begin(), end = m_audioTracks.end(); it != end; ++it) {
- if (!(*it)->ended())
- return;
- }
- for (auto it = m_videoTracks.begin(), end = m_videoTracks.end(); it != end; ++it) {
- if (!(*it)->ended())
- return;
+ ASSERT_UNUSED(document, &document == this->document());
+ ASSERT(m_isWaitingUntilMediaCanStart);
+ if (m_isWaitingUntilMediaCanStart) {
+ m_isWaitingUntilMediaCanStart = false;
+ startProducingData();
}
-
- setEnded();
}
-void MediaStream::streamDidEnd()
+void MediaStream::startProducingData()
{
- if (ended())
+ Document* document = this->document();
+ if (!document || !document->page())
return;
- scheduleDispatchEvent(Event::create(eventNames().endedEvent, false, false));
-}
+ // If we can't start a load right away, start it later.
+ if (!document->page()->canStartMedia()) {
+ LOG(Media, "MediaStream::startProducingData(%p) - not allowed to start in background, waiting", this);
+ if (m_isWaitingUntilMediaCanStart)
+ return;
-void MediaStream::contextDestroyed()
-{
- ContextDestructionObserver::contextDestroyed();
+ m_isWaitingUntilMediaCanStart = true;
+ document->addMediaCanStartListener(this);
+ return;
+ }
+
+ m_private->startProducingData();
}
-void MediaStream::addRemoteSource(MediaStreamSource* source)
+void MediaStream::stopProducingData()
{
- ASSERT(source);
- addRemoteTrack(MediaStreamTrackPrivate::create(source).get());
+ m_private->stopProducingData();
}
-void MediaStream::removeRemoteSource(MediaStreamSource* source)
+void MediaStream::pageMutedStateDidChange()
{
- ASSERT(source);
- if (ended())
+ if (!m_isActive)
return;
- Vector<RefPtr<MediaStreamTrack>>* tracks = trackVectorForType(source->type());
-
- for (int i = tracks->size() - 1; i >= 0; --i) {
- if ((*tracks)[i]->source() != source)
- continue;
+ Document* document = this->document();
+ if (!document)
+ return;
- RefPtr<MediaStreamTrack> track = (*tracks)[i];
- track->removeObserver(this);
- tracks->remove(i);
- m_private->removeTrack(&track->privateTrack());
- scheduleDispatchEvent(MediaStreamTrackEvent::create(eventNames().removetrackEvent, false, false, track.release()));
- }
+ bool pageMuted = document->page()->isMediaCaptureMuted();
+ if (m_externallyMuted == pageMuted)
+ return;
- m_private->removeSource(source);
+ m_externallyMuted = pageMuted;
+ if (pageMuted)
+ stopProducingData();
+ else
+ startProducingData();
}
-void MediaStream::addRemoteTrack(MediaStreamTrackPrivate* privateTrack)
+MediaProducer::MediaStateFlags MediaStream::mediaState() const
{
- ASSERT(privateTrack);
- if (ended())
- return;
+ MediaStateFlags state = IsNotPlaying;
- RefPtr<MediaStreamTrack> track;
- switch (privateTrack->type()) {
- case MediaStreamSource::Audio:
- track = AudioStreamTrack::create(*scriptExecutionContext(), *privateTrack);
- break;
- case MediaStreamSource::Video:
- track = VideoStreamTrack::create(*scriptExecutionContext(), *privateTrack);
- break;
- case MediaStreamSource::None:
- ASSERT_NOT_REACHED();
- break;
+ if (!m_isActive)
+ return state;
+
+ if (m_private->hasAudio()) {
+ state |= HasAudioOrVideo;
+ if (m_private->hasLocalAudioSource() && m_private->isProducingData())
+ state |= HasActiveAudioCaptureDevice;
}
- if (!track)
- return;
+ if (m_private->hasVideo()) {
+ state |= HasAudioOrVideo;
+ if (m_private->hasLocalVideoSource() && m_private->isProducingData())
+ state |= HasActiveVideoCaptureDevice;
+ }
- if (addTrack(track))
- scheduleDispatchEvent(MediaStreamTrackEvent::create(eventNames().addtrackEvent, false, false, track));
+ return state;
}
-void MediaStream::removeRemoteTrack(MediaStreamTrackPrivate* privateTrack)
+void MediaStream::statusDidChange()
{
- ASSERT(privateTrack);
- if (ended())
- return;
+ if (Document* document = this->document()) {
+ if (m_isActive)
+ document->setHasActiveMediaStreamTrack();
+ document->updateIsPlayingMedia();
+ }
+}
- RefPtr<MediaStreamTrack> track = getTrackById(privateTrack->id());
- if (removeTrack(track))
- scheduleDispatchEvent(MediaStreamTrackEvent::create(eventNames().removetrackEvent, false, false, track.release()));
+void MediaStream::characteristicsChanged()
+{
+ bool muted = m_private->muted();
+ if (m_isMuted != muted) {
+ m_isMuted = muted;
+ statusDidChange();
+ }
}
-void MediaStream::scheduleDispatchEvent(PassRefPtr<Event> event)
+void MediaStream::scheduleActiveStateChange()
{
- m_scheduledEvents.append(event);
+ bool active = false;
+ for (auto& track : m_trackSet.values()) {
+ if (!track->ended()) {
+ active = true;
+ break;
+ }
+ }
+
+ if (m_isActive == active)
+ return;
+
+ setIsActive(active);
+
+ const AtomicString& eventName = m_isActive ? eventNames().inactiveEvent : eventNames().activeEvent;
+ m_scheduledActivityEvents.append(Event::create(eventName, false, false));
- if (!m_scheduledEventTimer.isActive())
- m_scheduledEventTimer.startOneShot(0);
+ if (!m_activityEventTimer.isActive())
+ m_activityEventTimer.startOneShot(0);
}
-void MediaStream::scheduledEventTimerFired(Timer<MediaStream>*)
+void MediaStream::activityEventTimerFired()
{
- Vector<RefPtr<Event>> events;
- events.swap(m_scheduledEvents);
+ Vector<Ref<Event>> events;
+ events.swap(m_scheduledActivityEvents);
- for (auto it = events.begin(), end = events.end(); it != end; ++it)
- dispatchEvent((*it).release());
-
- events.clear();
+ for (auto& event : events)
+ dispatchEvent(event);
}
URLRegistry& MediaStream::registry() const
{
- return MediaStreamRegistry::registry();
+ return MediaStreamRegistry::shared();
}
-Vector<RefPtr<MediaStreamTrack>>* MediaStream::trackVectorForType(MediaStreamSource::Type type)
+MediaStreamTrackVector MediaStream::trackVectorForType(RealtimeMediaSource::Type filterType) const
{
- switch (type) {
- case MediaStreamSource::Audio:
- return &m_audioTracks;
- case MediaStreamSource::Video:
- return &m_videoTracks;
- case MediaStreamSource::None:
- ASSERT_NOT_REACHED();
+ MediaStreamTrackVector tracks;
+ for (auto& track : m_trackSet.values()) {
+ if (track->source().type() == filterType)
+ tracks.append(track);
}
- return nullptr;
+
+ return tracks;
}
void MediaStream::addObserver(MediaStream::Observer* observer)
@@ -395,6 +405,11 @@ void MediaStream::removeObserver(MediaStream::Observer* observer)
m_observers.remove(pos);
}
+Document* MediaStream::document() const
+{
+ return downcast<Document>(scriptExecutionContext());
+}
+
} // namespace WebCore
#endif // ENABLE(MEDIA_STREAM)
diff --git a/Source/WebCore/Modules/mediastream/MediaStream.h b/Source/WebCore/Modules/mediastream/MediaStream.h
index 1791c2490..816d706a3 100644
--- a/Source/WebCore/Modules/mediastream/MediaStream.h
+++ b/Source/WebCore/Modules/mediastream/MediaStream.h
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2011 Google Inc. All rights reserved.
- * Copyright (C) 2011 Ericsson AB. All rights reserved.
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2015 Ericsson AB. All rights reserved.
+ * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
* Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
*
* Redistribution and use in source and binary forms, with or without
@@ -25,116 +25,140 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef MediaStream_h
-#define MediaStream_h
+#pragma once
#if ENABLE(MEDIA_STREAM)
#include "ContextDestructionObserver.h"
#include "EventTarget.h"
#include "ExceptionBase.h"
+#include "MediaCanStartListener.h"
+#include "MediaProducer.h"
#include "MediaStreamPrivate.h"
#include "MediaStreamTrack.h"
#include "ScriptWrappable.h"
#include "Timer.h"
#include "URLRegistry.h"
+#include <wtf/HashMap.h>
#include <wtf/RefCounted.h>
#include <wtf/RefPtr.h>
namespace WebCore {
-class MediaStreamCenter;
-
-class MediaStream final : public RefCounted<MediaStream>, public URLRegistrable, public ScriptWrappable, public MediaStreamPrivateClient, public EventTargetWithInlineData, public ContextDestructionObserver {
+class Document;
+
+class MediaStream final
+ : public URLRegistrable
+ , public EventTargetWithInlineData
+ , public ContextDestructionObserver
+ , public MediaStreamTrack::Observer
+ , public MediaStreamPrivate::Observer
+ , private MediaProducer
+ , private MediaCanStartListener
+ , public RefCounted<MediaStream> {
public:
class Observer {
public:
+ virtual ~Observer() { }
virtual void didAddOrRemoveTrack() = 0;
};
- static PassRefPtr<MediaStream> create(ScriptExecutionContext&);
- static PassRefPtr<MediaStream> create(ScriptExecutionContext&, PassRefPtr<MediaStream>);
- static PassRefPtr<MediaStream> create(ScriptExecutionContext&, const Vector<RefPtr<MediaStreamTrack>>&);
- static PassRefPtr<MediaStream> create(ScriptExecutionContext&, PassRefPtr<MediaStreamPrivate>);
+ static Ref<MediaStream> create(ScriptExecutionContext&);
+ static Ref<MediaStream> create(ScriptExecutionContext&, MediaStream&);
+ static Ref<MediaStream> create(ScriptExecutionContext&, const MediaStreamTrackVector&);
+ static Ref<MediaStream> create(ScriptExecutionContext&, RefPtr<MediaStreamPrivate>&&);
virtual ~MediaStream();
String id() const { return m_private->id(); }
- void addTrack(PassRefPtr<MediaStreamTrack>, ExceptionCode&);
- void removeTrack(PassRefPtr<MediaStreamTrack>, ExceptionCode&);
+ void addTrack(MediaStreamTrack&);
+ void removeTrack(MediaStreamTrack&);
MediaStreamTrack* getTrackById(String);
- Vector<RefPtr<MediaStreamTrack>> getAudioTracks() const { return m_audioTracks; }
- Vector<RefPtr<MediaStreamTrack>> getVideoTracks() const { return m_videoTracks; }
+ MediaStreamTrackVector getAudioTracks() const;
+ MediaStreamTrackVector getVideoTracks() const;
+ MediaStreamTrackVector getTracks() const;
- bool ended() const;
- void setEnded();
- PassRefPtr<MediaStream> clone();
+ RefPtr<MediaStream> clone();
- DEFINE_ATTRIBUTE_EVENT_LISTENER(ended);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(addtrack);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(removetrack);
+ bool active() const { return m_isActive; }
+ bool muted() const { return m_isMuted; }
MediaStreamPrivate* privateStream() const { return m_private.get(); }
+ void startProducingData();
+ void stopProducingData();
+
// EventTarget
- virtual EventTargetInterface eventTargetInterface() const final { return MediaStreamEventTargetInterfaceType; }
- virtual ScriptExecutionContext* scriptExecutionContext() const final { return ContextDestructionObserver::scriptExecutionContext(); }
+ EventTargetInterface eventTargetInterface() const final { return MediaStreamEventTargetInterfaceType; }
+ ScriptExecutionContext* scriptExecutionContext() const final { return ContextDestructionObserver::scriptExecutionContext(); }
using RefCounted<MediaStream>::ref;
using RefCounted<MediaStream>::deref;
// URLRegistrable
- virtual URLRegistry& registry() const override;
+ URLRegistry& registry() const override;
void addObserver(Observer*);
void removeObserver(Observer*);
protected:
- MediaStream(ScriptExecutionContext&, PassRefPtr<MediaStreamPrivate>);
+ MediaStream(ScriptExecutionContext&, const MediaStreamTrackVector&);
+ MediaStream(ScriptExecutionContext&, RefPtr<MediaStreamPrivate>&&);
// ContextDestructionObserver
- virtual void contextDestroyed() override final;
+ void contextDestroyed() final;
private:
+ enum class StreamModifier { DomAPI, Platform };
+
// EventTarget
- virtual void refEventTarget() override final { ref(); }
- virtual void derefEventTarget() override final { deref(); }
+ void refEventTarget() final { ref(); }
+ void derefEventTarget() final { deref(); }
+
+ // MediaStreamTrack::Observer
+ void trackDidEnd() final;
+
+ // MediaStreamPrivate::Observer
+ void activeStatusChanged() final;
+ void didAddTrack(MediaStreamTrackPrivate&) final;
+ void didRemoveTrack(MediaStreamTrackPrivate&) final;
+ void characteristicsChanged() final;
- // MediaStreamPrivateClient
- virtual void trackDidEnd() override final;
- virtual void streamDidEnd() override final;
- virtual void addRemoteSource(MediaStreamSource*) override final;
- virtual void removeRemoteSource(MediaStreamSource*) override final;
- virtual void addRemoteTrack(MediaStreamTrackPrivate*) override final;
- virtual void removeRemoteTrack(MediaStreamTrackPrivate*) override final;
+ // MediaProducer
+ void pageMutedStateDidChange() final;
+ MediaProducer::MediaStateFlags mediaState() const final;
- bool removeTrack(PassRefPtr<MediaStreamTrack>);
- bool addTrack(PassRefPtr<MediaStreamTrack>);
+ // MediaCanStartListener
+ void mediaCanStart(Document&) final;
- bool haveTrackWithSource(PassRefPtr<MediaStreamSource>);
+ bool internalAddTrack(Ref<MediaStreamTrack>&&, StreamModifier);
+ bool internalRemoveTrack(const String&, StreamModifier);
- void scheduleDispatchEvent(PassRefPtr<Event>);
- void scheduledEventTimerFired(Timer<MediaStream>*);
+ void scheduleActiveStateChange();
+ void activityEventTimerFired();
+ void setIsActive(bool);
+ void statusDidChange();
- void cloneMediaStreamTrackVector(Vector<RefPtr<MediaStreamTrack>>&, const Vector<RefPtr<MediaStreamTrack>>&);
+ Document* document() const;
- Vector<RefPtr<MediaStreamTrack>>* trackVectorForType(MediaStreamSource::Type);
+ MediaStreamTrackVector trackVectorForType(RealtimeMediaSource::Type) const;
RefPtr<MediaStreamPrivate> m_private;
- Vector<RefPtr<MediaStreamTrack>> m_audioTracks;
- Vector<RefPtr<MediaStreamTrack>> m_videoTracks;
- Timer<MediaStream> m_scheduledEventTimer;
- Vector<RefPtr<Event>> m_scheduledEvents;
+ HashMap<String, RefPtr<MediaStreamTrack>> m_trackSet;
+
+ Timer m_activityEventTimer;
+ Vector<Ref<Event>> m_scheduledActivityEvents;
Vector<Observer*> m_observers;
-};
-typedef Vector<RefPtr<MediaStream>> MediaStreamVector;
+ bool m_isActive { false };
+ bool m_isMuted { true };
+ bool m_externallyMuted { false };
+ bool m_isWaitingUntilMediaCanStart { false };
+};
} // namespace WebCore
#endif // ENABLE(MEDIA_STREAM)
-
-#endif // MediaStream_h
diff --git a/Source/WebCore/Modules/mediastream/MediaStream.idl b/Source/WebCore/Modules/mediastream/MediaStream.idl
index 8dd4c9419..7fa377b89 100644
--- a/Source/WebCore/Modules/mediastream/MediaStream.idl
+++ b/Source/WebCore/Modules/mediastream/MediaStream.idl
@@ -24,37 +24,31 @@
[
Conditional=MEDIA_STREAM,
- EventTarget,
Constructor,
Constructor(MediaStream stream),
- Constructor(MediaStreamTrack[] tracks),
+ Constructor(sequence<MediaStreamTrack> tracks),
ConstructorCallWith=ScriptExecutionContext,
- InterfaceName=webkitMediaStream,
-] interface MediaStream {
+ PrivateIdentifier,
+ PublicIdentifier
+] interface MediaStream : EventTarget {
readonly attribute DOMString id;
sequence<MediaStreamTrack> getAudioTracks();
sequence<MediaStreamTrack> getVideoTracks();
-
- [RaisesException] void addTrack(MediaStreamTrack track);
- [RaisesException] void removeTrack(MediaStreamTrack track);
+ [PrivateIdentifier, PublicIdentifier] sequence<MediaStreamTrack> getTracks();
MediaStreamTrack getTrackById(DOMString trackId);
- MediaStream clone();
- readonly attribute boolean ended;
+ void addTrack(MediaStreamTrack track);
+ void removeTrack(MediaStreamTrack track);
+
+ MediaStream clone();
- attribute EventListener onended;
- attribute EventListener onaddtrack;
- attribute EventListener onremovetrack;
+ readonly attribute boolean active;
- // 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);
+ attribute EventHandler onactive;
+ attribute EventHandler oninactive;
+ attribute EventHandler onaddtrack;
+ attribute EventHandler onremovetrack;
};
diff --git a/Source/WebCore/Modules/mediastream/MediaStreamCapabilities.cpp b/Source/WebCore/Modules/mediastream/MediaStreamCapabilities.cpp
deleted file mode 100644
index ad7cead3f..000000000
--- a/Source/WebCore/Modules/mediastream/MediaStreamCapabilities.cpp
+++ /dev/null
@@ -1,142 +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 COMPUTER, 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
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (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_STREAM)
-
-#include "MediaStreamCapabilities.h"
-
-#include "AllAudioCapabilities.h"
-#include "AllVideoCapabilities.h"
-#include "CapabilityRange.h"
-#include "MediaSourceStates.h"
-#include "MediaStreamSourceCapabilities.h"
-
-namespace WebCore {
-
-RefPtr<MediaStreamCapabilities> MediaStreamCapabilities::create(PassRefPtr<MediaStreamSourceCapabilities> capabilities)
-{
- if (capabilities->hasVideoSource())
- return AllVideoCapabilities::create(capabilities);
-
- return AllAudioCapabilities::create(capabilities);
-}
-
-MediaStreamCapabilities::MediaStreamCapabilities(PassRefPtr<MediaStreamSourceCapabilities> capabilities)
- : m_SourceCapabilities(capabilities)
-{
-}
-
-Vector<String> MediaStreamCapabilities::sourceType() const
-{
- ASSERT(m_SourceCapabilities->hasVideoSource());
-
- size_t count = m_SourceCapabilities->sourceTypes().size();
- if (!count)
- return Vector<String>();
-
- const Vector<MediaStreamSourceStates::SourceType>& sourceTypes = m_SourceCapabilities->sourceTypes();
- Vector<String> capabilities;
- capabilities.reserveCapacity(count);
-
- for (size_t i = 0; i < count; ++i)
- capabilities.append(MediaStreamSourceStates::sourceType(sourceTypes[i]));
-
- return capabilities;
-}
-
-Vector<String> MediaStreamCapabilities::sourceId() const
-{
- size_t count = m_SourceCapabilities->sourceId().size();
- if (!count)
- return Vector<String>();
-
- const Vector<AtomicString>& sourceIds = m_SourceCapabilities->sourceId();
- Vector<String> capabilities;
- capabilities.reserveCapacity(count);
-
- for (size_t i = 0; i < count; ++i)
- capabilities.append(sourceIds[i]);
-
- return capabilities;
-}
-
-Vector<String> MediaStreamCapabilities::facingMode() const
-{
- ASSERT(m_SourceCapabilities->hasVideoSource());
-
- size_t count = m_SourceCapabilities->facingModes().size();
- if (!count)
- return Vector<String>();
-
- const Vector<MediaStreamSourceStates::VideoFacingMode>& facingModes = m_SourceCapabilities->facingModes();
- Vector<String> capabilities;
- capabilities.reserveCapacity(count);
-
- for (size_t i = 0; i < count; ++i)
- capabilities.append(MediaStreamSourceStates::facingMode(facingModes[i]));
-
- return capabilities;
-}
-
-RefPtr<CapabilityRange> MediaStreamCapabilities::width() const
-{
- ASSERT(m_SourceCapabilities->hasVideoSource());
-
- return CapabilityRange::create(m_SourceCapabilities->width());
-}
-
-RefPtr<CapabilityRange> MediaStreamCapabilities::height() const
-{
- ASSERT(m_SourceCapabilities->hasVideoSource());
-
- return CapabilityRange::create(m_SourceCapabilities->height());
-}
-
-RefPtr<CapabilityRange> MediaStreamCapabilities::frameRate() const
-{
- ASSERT(m_SourceCapabilities->hasVideoSource());
-
- return CapabilityRange::create(m_SourceCapabilities->frameRate());
-}
-
-RefPtr<CapabilityRange> MediaStreamCapabilities::aspectRatio() const
-{
- ASSERT(m_SourceCapabilities->hasVideoSource());
-
- return CapabilityRange::create(m_SourceCapabilities->aspectRatio());
-}
-
-RefPtr<CapabilityRange> MediaStreamCapabilities::volume() const
-{
- ASSERT(!m_SourceCapabilities->hasVideoSource());
-
- return CapabilityRange::create(m_SourceCapabilities->volume());
-}
-
-} // namespace WebCore
-
-#endif
diff --git a/Source/WebCore/Modules/mediastream/MediaStreamCapabilities.h b/Source/WebCore/Modules/mediastream/MediaStreamCapabilities.h
deleted file mode 100644
index 5ad3412ea..000000000
--- a/Source/WebCore/Modules/mediastream/MediaStreamCapabilities.h
+++ /dev/null
@@ -1,69 +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 COMPUTER, 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
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (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 MediaStreamCapabilities_h
-#define MediaStreamCapabilities_h
-
-#if ENABLE(MEDIA_STREAM)
-
-#include "MediaStreamCapabilities.h"
-#include "MediaStreamSourceCapabilities.h"
-#include "ScriptWrappable.h"
-#include <wtf/RefCounted.h>
-#include <wtf/Vector.h>
-#include <wtf/text/WTFString.h>
-
-namespace WebCore {
-
-class CapabilityRange;
-class MediaStreamSourceCapabilities;
-
-class MediaStreamCapabilities : public RefCounted<MediaStreamCapabilities>, public ScriptWrappable {
-public:
- static RefPtr<MediaStreamCapabilities> create(PassRefPtr<MediaStreamSourceCapabilities>);
- virtual ~MediaStreamCapabilities() { }
-
- virtual Vector<String> sourceType() const;
- virtual Vector<String> sourceId() const;
- virtual RefPtr<CapabilityRange> width() const;
- virtual RefPtr<CapabilityRange> height() const;
- virtual RefPtr<CapabilityRange> frameRate() const;
- virtual RefPtr<CapabilityRange> aspectRatio() const;
- virtual Vector<String> facingMode() const;
- virtual RefPtr<CapabilityRange> volume() const;
-
- bool hasVideoSource() { return m_SourceCapabilities->hasVideoSource(); }
-
-protected:
- explicit MediaStreamCapabilities(PassRefPtr<MediaStreamSourceCapabilities>);
-
- RefPtr<MediaStreamSourceCapabilities> m_SourceCapabilities;
-};
-
-} // namespace WebCore
-
-#endif // MediaStreamCapabilities_h
-
-#endif
diff --git a/Source/WebCore/Modules/mediastream/MediaStreamEvent.cpp b/Source/WebCore/Modules/mediastream/MediaStreamEvent.cpp
index 92f6bb14f..09584b2ba 100644
--- a/Source/WebCore/Modules/mediastream/MediaStreamEvent.cpp
+++ b/Source/WebCore/Modules/mediastream/MediaStreamEvent.cpp
@@ -25,45 +25,30 @@
#include "config.h"
#include "MediaStreamEvent.h"
-#if ENABLE(MEDIA_STREAM)
+#if ENABLE(WEB_RTC)
-#include "EventNames.h"
#include "MediaStream.h"
namespace WebCore {
-MediaStreamEventInit::MediaStreamEventInit()
- : stream(0)
+Ref<MediaStreamEvent> MediaStreamEvent::create(const AtomicString& type, bool canBubble, bool cancelable, RefPtr<MediaStream>&& stream)
{
+ return adoptRef(*new MediaStreamEvent(type, canBubble, cancelable, WTFMove(stream)));
}
-PassRefPtr<MediaStreamEvent> MediaStreamEvent::create()
+Ref<MediaStreamEvent> MediaStreamEvent::create(const AtomicString& type, const Init& initializer, IsTrusted isTrusted)
{
- return adoptRef(new MediaStreamEvent);
+ return adoptRef(*new MediaStreamEvent(type, initializer, isTrusted));
}
-PassRefPtr<MediaStreamEvent> MediaStreamEvent::create(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<MediaStream> stream)
-{
- return adoptRef(new MediaStreamEvent(type, canBubble, cancelable, stream));
-}
-
-PassRefPtr<MediaStreamEvent> MediaStreamEvent::create(const AtomicString& type, const MediaStreamEventInit& initializer)
-{
- return adoptRef(new MediaStreamEvent(type, initializer));
-}
-
-MediaStreamEvent::MediaStreamEvent()
-{
-}
-
-MediaStreamEvent::MediaStreamEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<MediaStream> stream)
+MediaStreamEvent::MediaStreamEvent(const AtomicString& type, bool canBubble, bool cancelable, RefPtr<MediaStream>&& stream)
: Event(type, canBubble, cancelable)
- , m_stream(stream)
+ , m_stream(WTFMove(stream))
{
}
-MediaStreamEvent::MediaStreamEvent(const AtomicString& type, const MediaStreamEventInit& initializer)
- : Event(type, initializer)
+MediaStreamEvent::MediaStreamEvent(const AtomicString& type, const Init& initializer, IsTrusted isTrusted)
+ : Event(type, initializer, isTrusted)
, m_stream(initializer.stream)
{
}
@@ -84,5 +69,5 @@ EventInterface MediaStreamEvent::eventInterface() const
} // namespace WebCore
-#endif // ENABLE(MEDIA_STREAM)
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/MediaStreamEvent.h b/Source/WebCore/Modules/mediastream/MediaStreamEvent.h
index bad394e0a..f67c52952 100644
--- a/Source/WebCore/Modules/mediastream/MediaStreamEvent.h
+++ b/Source/WebCore/Modules/mediastream/MediaStreamEvent.h
@@ -22,10 +22,9 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef MediaStreamEvent_h
-#define MediaStreamEvent_h
+#pragma once
-#if ENABLE(MEDIA_STREAM)
+#if ENABLE(WEB_RTC)
#include "Event.h"
#include "MediaStream.h"
@@ -33,34 +32,28 @@
namespace WebCore {
-struct MediaStreamEventInit : public EventInit {
- MediaStreamEventInit();
-
- RefPtr<MediaStream> stream;
-};
-
class MediaStreamEvent : public Event {
public:
virtual ~MediaStreamEvent();
- static PassRefPtr<MediaStreamEvent> create();
- static PassRefPtr<MediaStreamEvent> create(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<MediaStream>);
- static PassRefPtr<MediaStreamEvent> create(const AtomicString& type, const MediaStreamEventInit& initializer);
+ static Ref<MediaStreamEvent> create(const AtomicString& type, bool canBubble, bool cancelable, RefPtr<MediaStream>&&);
+
+ struct Init : EventInit {
+ RefPtr<MediaStream> stream;
+ };
+ static Ref<MediaStreamEvent> create(const AtomicString& type, const Init& initializer, IsTrusted = IsTrusted::No);
MediaStream* stream() const;
virtual EventInterface eventInterface() const;
private:
- MediaStreamEvent();
- MediaStreamEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<MediaStream>);
- MediaStreamEvent(const AtomicString& type, const MediaStreamEventInit&);
+ MediaStreamEvent(const AtomicString& type, bool canBubble, bool cancelable, RefPtr<MediaStream>&&);
+ MediaStreamEvent(const AtomicString& type, const Init&, IsTrusted);
RefPtr<MediaStream> m_stream;
};
} // namespace WebCore
-#endif // ENABLE(MEDIA_STREAM)
-
-#endif // MediaStreamEvent_h
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/MediaStreamEvent.idl b/Source/WebCore/Modules/mediastream/MediaStreamEvent.idl
index b44729ca3..e4ffceb15 100644
--- a/Source/WebCore/Modules/mediastream/MediaStreamEvent.idl
+++ b/Source/WebCore/Modules/mediastream/MediaStreamEvent.idl
@@ -23,9 +23,13 @@
*/
[
- Conditional=MEDIA_STREAM,
- ConstructorTemplate=Event
+ Conditional=WEB_RTC,
+ Constructor(DOMString type, optional MediaStreamEventInit eventInitDict),
+ EnabledAtRuntime=MediaStream,
] interface MediaStreamEvent : Event {
- [InitializedByEventConstructor] readonly attribute MediaStream stream;
+ readonly attribute MediaStream? stream;
};
+dictionary MediaStreamEventInit : EventInit {
+ MediaStream? stream = null;
+};
diff --git a/Source/WebCore/Modules/mediastream/MediaStreamRegistry.cpp b/Source/WebCore/Modules/mediastream/MediaStreamRegistry.cpp
index 9a2eeb18e..843498657 100644
--- a/Source/WebCore/Modules/mediastream/MediaStreamRegistry.cpp
+++ b/Source/WebCore/Modules/mediastream/MediaStreamRegistry.cpp
@@ -28,25 +28,26 @@
#if ENABLE(MEDIA_STREAM)
-#include "URL.h"
#include "MediaStream.h"
+#include "URL.h"
#include <wtf/MainThread.h>
+#include <wtf/NeverDestroyed.h>
namespace WebCore {
-MediaStreamRegistry& MediaStreamRegistry::registry()
+MediaStreamRegistry& MediaStreamRegistry::shared()
{
// Since WebWorkers cannot obtain MediaSource objects, we should be on the main thread.
ASSERT(isMainThread());
- DEFINE_STATIC_LOCAL(MediaStreamRegistry, instance, ());
+ static NeverDestroyed<MediaStreamRegistry> instance;
return instance;
}
-void MediaStreamRegistry::registerURL(SecurityOrigin*, const URL& url, URLRegistrable* stream)
+void MediaStreamRegistry::registerURL(SecurityOrigin*, const URL& url, URLRegistrable& stream)
{
- ASSERT(&stream->registry() == this);
+ ASSERT(&stream.registry() == this);
ASSERT(isMainThread());
- m_mediaStreams.set(url.string(), static_cast<MediaStream*>(stream));
+ m_mediaStreams.set(url.string(), static_cast<MediaStream*>(&stream));
}
void MediaStreamRegistry::unregisterURL(const URL& url)
@@ -61,6 +62,45 @@ URLRegistrable* MediaStreamRegistry::lookup(const String& url) const
return m_mediaStreams.get(url);
}
+MediaStreamRegistry::MediaStreamRegistry()
+{
+}
+
+MediaStream* MediaStreamRegistry::lookUp(const URL& url) const
+{
+ return static_cast<MediaStream*>(lookup(url.string()));
+}
+
+static Vector<MediaStream*>& mediaStreams()
+{
+ static NeverDestroyed<Vector<MediaStream*>> streams;
+ return streams;
+}
+
+void MediaStreamRegistry::registerStream(MediaStream& stream)
+{
+ mediaStreams().append(&stream);
+}
+
+void MediaStreamRegistry::unregisterStream(MediaStream& stream)
+{
+ Vector<MediaStream*>& allStreams = mediaStreams();
+ size_t pos = allStreams.find(&stream);
+ if (pos != notFound)
+ allStreams.remove(pos);
+}
+
+MediaStream* MediaStreamRegistry::lookUp(const MediaStreamPrivate& privateStream) const
+{
+ Vector<MediaStream*>& allStreams = mediaStreams();
+ for (auto& stream : allStreams) {
+ if (stream->privateStream() == &privateStream)
+ return stream;
+ }
+
+ return nullptr;
+}
+
} // namespace WebCore
#endif // ENABLE(MEDIA_STREAM)
diff --git a/Source/WebCore/Modules/mediastream/MediaStreamRegistry.h b/Source/WebCore/Modules/mediastream/MediaStreamRegistry.h
index cfea9f59e..7e23a1e2e 100644
--- a/Source/WebCore/Modules/mediastream/MediaStreamRegistry.h
+++ b/Source/WebCore/Modules/mediastream/MediaStreamRegistry.h
@@ -23,38 +23,44 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef MediaStreamRegistry_h
-#define MediaStreamRegistry_h
+#pragma once
#if ENABLE(MEDIA_STREAM)
#include "URLRegistry.h"
#include <wtf/HashMap.h>
-#include <wtf/PassRefPtr.h>
#include <wtf/text/StringHash.h>
namespace WebCore {
-class URL;
class MediaStream;
+class MediaStreamPrivate;
+class URL;
class MediaStreamRegistry final : public URLRegistry {
public:
+ friend class NeverDestroyed<MediaStreamRegistry>;
+
// Returns a single instance of MediaStreamRegistry.
- static MediaStreamRegistry& registry();
+ static MediaStreamRegistry& shared();
// Registers a blob URL referring to the specified stream data.
- virtual void registerURL(SecurityOrigin*, const URL&, URLRegistrable*) override;
- virtual void unregisterURL(const URL&) override;
+ void registerURL(SecurityOrigin*, const URL&, URLRegistrable&) override;
+ void unregisterURL(const URL&) override;
- virtual URLRegistrable* lookup(const String&) const override;
+ URLRegistrable* lookup(const String&) const override;
+
+ void registerStream(MediaStream&);
+ void unregisterStream(MediaStream&);
+
+ MediaStream* lookUp(const URL&) const;
+ MediaStream* lookUp(const MediaStreamPrivate&) const;
private:
+ MediaStreamRegistry();
HashMap<String, RefPtr<MediaStream>> m_mediaStreams;
};
} // namespace WebCore
#endif // ENABLE(MEDIA_STREAM)
-
-#endif // MediaStreamRegistry_h
diff --git a/Source/WebCore/Modules/mediastream/MediaStreamTrack.cpp b/Source/WebCore/Modules/mediastream/MediaStreamTrack.cpp
index 476dec103..96cc6298b 100644
--- a/Source/WebCore/Modules/mediastream/MediaStreamTrack.cpp
+++ b/Source/WebCore/Modules/mediastream/MediaStreamTrack.cpp
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2011 Google Inc. All rights reserved.
- * Copyright (C) 2011 Ericsson AB. All rights reserved.
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2015 Ericsson AB. All rights reserved.
+ * Copyright (C) 2013-2016 Apple Inc. All rights reserved.
* Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
*
* Redistribution and use in source and binary forms, with or without
@@ -30,292 +30,345 @@
#if ENABLE(MEDIA_STREAM)
-#include "AllAudioCapabilities.h"
-#include "AllVideoCapabilities.h"
-#include "AudioStreamTrack.h"
-#include "Dictionary.h"
#include "Event.h"
-#include "ExceptionCode.h"
-#include "ExceptionCodePlaceholder.h"
+#include "EventNames.h"
+#include "JSOverconstrainedError.h"
#include "MediaConstraintsImpl.h"
-#include "MediaSourceStates.h"
#include "MediaStream.h"
-#include "MediaStreamCenter.h"
#include "MediaStreamPrivate.h"
-#include "MediaStreamTrackSourcesCallback.h"
-#include "MediaStreamTrackSourcesRequest.h"
-#include "MediaTrackConstraints.h"
#include "NotImplemented.h"
-#include "VideoStreamTrack.h"
-#include <wtf/Functional.h>
+#include "OverconstrainedError.h"
+#include "ScriptExecutionContext.h"
#include <wtf/NeverDestroyed.h>
namespace WebCore {
-MediaStreamTrack::MediaStreamTrack(ScriptExecutionContext& context, MediaStreamTrackPrivate& privateTrack, const Dictionary* constraints)
- : RefCounted()
- , ActiveDOMObject(&context)
- , m_privateTrack(privateTrack)
- , m_eventDispatchScheduled(false)
- , m_stoppingTrack(false)
+Ref<MediaStreamTrack> MediaStreamTrack::create(ScriptExecutionContext& context, Ref<MediaStreamTrackPrivate>&& privateTrack)
{
- suspendIfNeeded();
-
- m_privateTrack->setClient(this);
-
- if (constraints)
- applyConstraints(*constraints);
+ return adoptRef(*new MediaStreamTrack(context, WTFMove(privateTrack)));
}
-MediaStreamTrack::MediaStreamTrack(MediaStreamTrack& other)
- : RefCounted()
- , ActiveDOMObject(other.scriptExecutionContext())
- , m_privateTrack(*other.privateTrack().clone())
- , m_eventDispatchScheduled(false)
- , m_stoppingTrack(false)
+MediaStreamTrack::MediaStreamTrack(ScriptExecutionContext& context, Ref<MediaStreamTrackPrivate>&& privateTrack)
+ : ActiveDOMObject(&context)
+ , m_private(WTFMove(privateTrack))
+ , m_weakPtrFactory(this)
{
suspendIfNeeded();
- m_privateTrack->setClient(this);
+ m_private->addObserver(*this);
}
MediaStreamTrack::~MediaStreamTrack()
{
- m_privateTrack->setClient(nullptr);
+ m_private->removeObserver(*this);
}
-void MediaStreamTrack::setSource(PassRefPtr<MediaStreamSource> newSource)
+const AtomicString& MediaStreamTrack::kind() const
{
- m_privateTrack->setSource(newSource);
+ static NeverDestroyed<AtomicString> audioKind("audio", AtomicString::ConstructFromLiteral);
+ static NeverDestroyed<AtomicString> videoKind("video", AtomicString::ConstructFromLiteral);
+
+ if (m_private->type() == RealtimeMediaSource::Audio)
+ return audioKind;
+ return videoKind;
}
const String& MediaStreamTrack::id() const
{
- return m_privateTrack->id();
+ return m_private->id();
}
const String& MediaStreamTrack::label() const
{
- return m_privateTrack->label();
+ return m_private->label();
}
bool MediaStreamTrack::enabled() const
{
- return m_privateTrack->enabled();
+ return m_private->enabled();
}
void MediaStreamTrack::setEnabled(bool enabled)
{
- m_privateTrack->setEnabled(enabled);
-}
-
-bool MediaStreamTrack::stopped() const
-{
- return m_privateTrack->stopped();
+ m_private->setEnabled(enabled);
}
bool MediaStreamTrack::muted() const
{
- return m_privateTrack->muted();
+ return m_private->muted();
}
bool MediaStreamTrack::readonly() const
{
- return m_privateTrack->readonly();
+ return m_private->readonly();
}
bool MediaStreamTrack::remote() const
{
- return m_privateTrack->remote();
+ return m_private->remote();
}
-const AtomicString& MediaStreamTrack::readyState() const
+auto MediaStreamTrack::readyState() const -> State
{
- static NeverDestroyed<AtomicString> ended("ended", AtomicString::ConstructFromLiteral);
- static NeverDestroyed<AtomicString> live("live", AtomicString::ConstructFromLiteral);
- static NeverDestroyed<AtomicString> newState("new", AtomicString::ConstructFromLiteral);
-
- switch (m_privateTrack->readyState()) {
- case MediaStreamSource::Live:
- return live;
- case MediaStreamSource::New:
- return newState;
- case MediaStreamSource::Ended:
- return ended;
- }
-
- ASSERT_NOT_REACHED();
- return emptyAtom;
+ return ended() ? State::Ended : State::Live;
}
-void MediaStreamTrack::getSources(ScriptExecutionContext* context, PassRefPtr<MediaStreamTrackSourcesCallback> callback, ExceptionCode& ec)
+bool MediaStreamTrack::ended() const
{
- RefPtr<MediaStreamTrackSourcesRequest> request = MediaStreamTrackSourcesRequest::create(context, callback);
- if (!MediaStreamCenter::shared().getMediaStreamTrackSources(request.release()))
- ec = NOT_SUPPORTED_ERR;
+ return m_ended || m_private->ended();
}
-RefPtr<MediaTrackConstraints> MediaStreamTrack::constraints() const
+Ref<MediaStreamTrack> MediaStreamTrack::clone()
{
- // FIXME: https://bugs.webkit.org/show_bug.cgi?id=122428
- notImplemented();
- return 0;
+ return MediaStreamTrack::create(*scriptExecutionContext(), m_private->clone());
}
-RefPtr<MediaSourceStates> MediaStreamTrack::states() const
+void MediaStreamTrack::stopProducingData()
{
- return MediaSourceStates::create(m_privateTrack->states());
-}
+ // NOTE: this method is called when the "stop" method is called from JS, using
+ // the "ImplementedAs" IDL attribute. This is done because ActiveDOMObject requires
+ // a "stop" method.
-RefPtr<MediaStreamCapabilities> MediaStreamTrack::capabilities() const
-{
- // The source may be shared by multiple tracks, so its states is not necessarily
- // in sync with the track state. A track that is new or has ended always has a source
- // type of "none".
- RefPtr<MediaStreamSourceCapabilities> sourceCapabilities = m_privateTrack->capabilities();
- MediaStreamSource::ReadyState readyState = m_privateTrack->readyState();
- if (readyState == MediaStreamSource::New || readyState == MediaStreamSource::Ended)
- sourceCapabilities->setSourceType(MediaStreamSourceStates::None);
-
- return MediaStreamCapabilities::create(sourceCapabilities.release());
+ // http://w3c.github.io/mediacapture-main/#widl-MediaStreamTrack-stop-void
+ // 4.3.3.2 Methods
+ // When a MediaStreamTrack object's stop() method is invoked, the User Agent must run following steps:
+ // 1. Let track be the current MediaStreamTrack object.
+ // 2. If track is sourced by a non-local source, then abort these steps.
+ if (remote() || ended())
+ return;
+
+ // 3. Notify track's source that track is ended so that the source may be stopped, unless other
+ // MediaStreamTrack objects depend on it.
+ // 4. Set track's readyState attribute to ended.
+
+ // Set m_ended to true before telling the private to stop so we do not fire an 'ended' event.
+ m_ended = true;
+
+ m_private->endTrack();
+}
+
+MediaStreamTrack::TrackSettings MediaStreamTrack::getSettings() const
+{
+ auto& settings = m_private->settings();
+ TrackSettings result;
+ if (settings.supportsWidth())
+ result.width = settings.width();
+ if (settings.supportsHeight())
+ result.height = settings.height();
+ if (settings.supportsAspectRatio() && settings.aspectRatio()) // FIXME: Why the check for zero here?
+ result.aspectRatio = settings.aspectRatio();
+ if (settings.supportsFrameRate())
+ result.frameRate = settings.frameRate();
+ if (settings.supportsFacingMode())
+ result.facingMode = RealtimeMediaSourceSettings::facingMode(settings.facingMode());
+ if (settings.supportsVolume())
+ result.volume = settings.volume();
+ if (settings.supportsSampleRate())
+ result.sampleRate = settings.sampleRate();
+ if (settings.supportsSampleSize())
+ result.sampleSize = settings.sampleSize();
+ if (settings.supportsEchoCancellation())
+ result.echoCancellation = settings.echoCancellation();
+ if (settings.supportsDeviceId())
+ result.deviceId = settings.deviceId();
+ if (settings.supportsGroupId())
+ result.groupId = settings.groupId();
+ return result;
+}
+
+static DoubleRange capabilityDoubleRange(const CapabilityValueOrRange& value)
+{
+ DoubleRange range;
+ switch (value.type()) {
+ case CapabilityValueOrRange::Double:
+ range.min = value.value().asDouble;
+ range.max = range.min;
+ break;
+ case CapabilityValueOrRange::DoubleRange:
+ range.min = value.rangeMin().asDouble;
+ range.max = value.rangeMax().asDouble;
+ break;
+ case CapabilityValueOrRange::Undefined:
+ case CapabilityValueOrRange::ULong:
+ case CapabilityValueOrRange::ULongRange:
+ ASSERT_NOT_REACHED();
+ }
+ return range;
+}
+
+static LongRange capabilityIntRange(const CapabilityValueOrRange& value)
+{
+ LongRange range;
+ switch (value.type()) {
+ case CapabilityValueOrRange::ULong:
+ range.min = value.value().asInt;
+ range.max = range.min;
+ break;
+ case CapabilityValueOrRange::ULongRange:
+ range.min = value.rangeMin().asInt;
+ range.max = value.rangeMax().asInt;
+ break;
+ case CapabilityValueOrRange::Undefined:
+ case CapabilityValueOrRange::Double:
+ case CapabilityValueOrRange::DoubleRange:
+ ASSERT_NOT_REACHED();
+ }
+ return range;
}
-void MediaStreamTrack::applyConstraints(const Dictionary& constraints)
+static Vector<String> capabilityStringVector(const Vector<RealtimeMediaSourceSettings::VideoFacingMode>& modes)
{
- m_constraints->initialize(constraints);
- m_privateTrack->applyConstraints(m_constraints);
+ Vector<String> result;
+ result.reserveCapacity(modes.size());
+ for (auto& mode : modes)
+ result.uncheckedAppend(RealtimeMediaSourceSettings::facingMode(mode));
+ return result;
}
-void MediaStreamTrack::applyConstraints(PassRefPtr<MediaConstraints>)
+static Vector<bool> capabilityBooleanVector(RealtimeMediaSourceCapabilities::EchoCancellation cancellation)
{
- // FIXME: apply the new constraints to the track
- // https://bugs.webkit.org/show_bug.cgi?id=122428
+ Vector<bool> result;
+ result.reserveCapacity(2);
+ result.uncheckedAppend(true);
+ result.uncheckedAppend(cancellation == RealtimeMediaSourceCapabilities::EchoCancellation::ReadWrite);
+ return result;
}
-RefPtr<MediaStreamTrack> MediaStreamTrack::clone()
+MediaStreamTrack::TrackCapabilities MediaStreamTrack::getCapabilities() const
{
- if (m_privateTrack->type() == MediaStreamSource::Audio)
- return AudioStreamTrack::create(*this);
-
- return VideoStreamTrack::create(*this);
+ auto capabilities = m_private->capabilities();
+ TrackCapabilities result;
+ if (capabilities->supportsWidth())
+ result.width = capabilityIntRange(capabilities->width());
+ if (capabilities->supportsHeight())
+ result.height = capabilityIntRange(capabilities->height());
+ if (capabilities->supportsAspectRatio())
+ result.aspectRatio = capabilityDoubleRange(capabilities->aspectRatio());
+ if (capabilities->supportsFrameRate())
+ result.frameRate = capabilityDoubleRange(capabilities->frameRate());
+ if (capabilities->supportsFacingMode())
+ result.facingMode = capabilityStringVector(capabilities->facingMode());
+ if (capabilities->supportsVolume())
+ result.volume = capabilityDoubleRange(capabilities->volume());
+ if (capabilities->supportsSampleRate())
+ result.sampleRate = capabilityIntRange(capabilities->sampleRate());
+ if (capabilities->supportsSampleSize())
+ result.sampleSize = capabilityIntRange(capabilities->sampleSize());
+ if (capabilities->supportsEchoCancellation())
+ result.echoCancellation = capabilityBooleanVector(capabilities->echoCancellation());
+ if (capabilities->supportsDeviceId())
+ result.deviceId = capabilities->deviceId();
+ if (capabilities->supportsGroupId())
+ result.groupId = capabilities->groupId();
+ return result;
}
-void MediaStreamTrack::stopProducingData()
+static Ref<MediaConstraintsImpl> createMediaConstraintsImpl(const std::optional<MediaTrackConstraints>& constraints)
{
- // NOTE: this method is called when the "stop" method is called from JS, using
- // the "ImplementedAs" IDL attribute. This is done because ActiveDOMObject requires
- // a "stop" method.
-
- // The stop method should "Permanently stop the generation of data for track's source", but it
- // should not post an 'ended' event.
- m_stoppingTrack = true;
- m_privateTrack->stop(MediaStreamTrackPrivate::StopTrackAndStopSource);
- m_stoppingTrack = false;
+ if (!constraints)
+ return MediaConstraintsImpl::create({ }, { }, true);
+ return createMediaConstraintsImpl(constraints.value());
}
-bool MediaStreamTrack::ended() const
+void MediaStreamTrack::applyConstraints(const std::optional<MediaTrackConstraints>& constraints, DOMPromise<void>&& promise)
{
- return m_privateTrack->ended();
+ m_promise = WTFMove(promise);
+
+ auto weakThis = createWeakPtr();
+ auto failureHandler = [weakThis] (const String& failedConstraint, const String& message) {
+ if (!weakThis || !weakThis->m_promise)
+ return;
+ weakThis->m_promise->rejectType<IDLInterface<OverconstrainedError>>(OverconstrainedError::create(failedConstraint, message).get());
+ };
+ auto successHandler = [weakThis, constraints] () {
+ if (!weakThis || !weakThis->m_promise)
+ return;
+ weakThis->m_promise->resolve();
+ weakThis->m_constraints = constraints.value_or(MediaTrackConstraints { });
+ };
+ m_private->applyConstraints(createMediaConstraintsImpl(constraints), successHandler, failureHandler);
}
-void MediaStreamTrack::addObserver(MediaStreamTrack::Observer* observer)
+void MediaStreamTrack::addObserver(Observer& observer)
{
- m_observers.append(observer);
+ m_observers.append(&observer);
}
-void MediaStreamTrack::removeObserver(MediaStreamTrack::Observer* observer)
+void MediaStreamTrack::removeObserver(Observer& observer)
{
- size_t pos = m_observers.find(observer);
- if (pos != notFound)
- m_observers.remove(pos);
+ m_observers.removeFirst(&observer);
}
-void MediaStreamTrack::trackReadyStateChanged()
+void MediaStreamTrack::trackEnded(MediaStreamTrackPrivate&)
{
- if (stopped())
+ // http://w3c.github.io/mediacapture-main/#life-cycle
+ // When a MediaStreamTrack track ends for any reason other than the stop() method being invoked, the User Agent must queue a task that runs the following steps:
+ // 1. If the track's readyState attribute has the value ended already, then abort these steps.
+ if (m_ended)
return;
- MediaStreamSource::ReadyState readyState = m_privateTrack->readyState();
- if (readyState == MediaStreamSource::Live)
- scheduleEventDispatch(Event::create(eventNames().startedEvent, false, false));
- else if (readyState == MediaStreamSource::Ended && !m_stoppingTrack)
- scheduleEventDispatch(Event::create(eventNames().endedEvent, false, false));
+ // 2. Set track's readyState attribute to ended.
+ m_ended = true;
+
+ if (scriptExecutionContext()->activeDOMObjectsAreSuspended() || scriptExecutionContext()->activeDOMObjectsAreStopped())
+ return;
+
+ // 3. Notify track's source that track is ended so that the source may be stopped, unless other MediaStreamTrack objects depend on it.
+ // 4. Fire a simple event named ended at the object.
+ dispatchEvent(Event::create(eventNames().endedEvent, false, false));
+
+ for (auto& observer : m_observers)
+ observer->trackDidEnd();
configureTrackRendering();
}
-void MediaStreamTrack::trackMutedChanged()
+void MediaStreamTrack::trackMutedChanged(MediaStreamTrackPrivate&)
{
- if (stopped())
+ if (scriptExecutionContext()->activeDOMObjectsAreSuspended() || scriptExecutionContext()->activeDOMObjectsAreStopped())
return;
- if (muted())
- scheduleEventDispatch(Event::create(eventNames().muteEvent, false, false));
- else
- scheduleEventDispatch(Event::create(eventNames().unmuteEvent, false, false));
+ AtomicString eventType = muted() ? eventNames().muteEvent : eventNames().unmuteEvent;
+ dispatchEvent(Event::create(eventType, false, false));
configureTrackRendering();
}
-void MediaStreamTrack::trackEnabledChanged()
+void MediaStreamTrack::trackSettingsChanged(MediaStreamTrackPrivate&)
{
- if (stopped())
- return;
+ configureTrackRendering();
+}
- setEnabled(m_privateTrack->enabled());
+void MediaStreamTrack::trackEnabledChanged(MediaStreamTrackPrivate&)
+{
configureTrackRendering();
}
void MediaStreamTrack::configureTrackRendering()
{
- if (stopped())
- return;
-
// 4.3.1
// ... media from the source only flows when a MediaStreamTrack object is both unmuted and enabled
}
-void MediaStreamTrack::trackDidEnd()
+void MediaStreamTrack::stop()
{
- m_privateTrack->setReadyState(MediaStreamSource::Ended);
-
- for (Vector<Observer*>::iterator i = m_observers.begin(); i != m_observers.end(); ++i)
- (*i)->trackDidEnd();
+ stopProducingData();
}
-void MediaStreamTrack::stop()
+const char* MediaStreamTrack::activeDOMObjectName() const
{
- m_privateTrack->stop(MediaStreamTrackPrivate::StopTrackOnly);
+ return "MediaStreamTrack";
}
-void MediaStreamTrack::scheduleEventDispatch(PassRefPtr<Event> event)
+bool MediaStreamTrack::canSuspendForDocumentSuspension() const
{
- {
- MutexLocker locker(m_mutex);
- m_scheduledEvents.append(event);
- if (m_eventDispatchScheduled)
- return;
- m_eventDispatchScheduled = true;
- }
-
- callOnMainThread(bind(&MediaStreamTrack::dispatchQueuedEvents, this));
+ // FIXME: We should try and do better here.
+ return false;
}
-void MediaStreamTrack::dispatchQueuedEvents()
+AudioSourceProvider* MediaStreamTrack::audioSourceProvider()
{
- Vector<RefPtr<Event>> events;
- {
- MutexLocker locker(m_mutex);
- m_eventDispatchScheduled = false;
- events.swap(m_scheduledEvents);
- }
- if (!scriptExecutionContext())
- return;
-
- for (auto it = events.begin(); it != events.end(); ++it)
- dispatchEvent((*it).release());
-
- events.clear();
+ return m_private->audioSourceProvider();
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/mediastream/MediaStreamTrack.h b/Source/WebCore/Modules/mediastream/MediaStreamTrack.h
index 64ea32d3f..118728758 100644
--- a/Source/WebCore/Modules/mediastream/MediaStreamTrack.h
+++ b/Source/WebCore/Modules/mediastream/MediaStreamTrack.h
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2011 Google Inc. All rights reserved.
- * Copyright (C) 2011 Ericsson AB. All rights reserved.
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2015 Ericsson AB. All rights reserved.
+ * Copyright (C) 2013-2016 Apple Inc. All rights reserved.
* Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
*
* Redistribution and use in source and binary forms, with or without
@@ -25,31 +25,25 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef MediaStreamTrack_h
-#define MediaStreamTrack_h
+#pragma once
#if ENABLE(MEDIA_STREAM)
#include "ActiveDOMObject.h"
+#include "DoubleRange.h"
#include "EventTarget.h"
-#include "MediaStreamSource.h"
+#include "JSDOMPromise.h"
+#include "LongRange.h"
#include "MediaStreamTrackPrivate.h"
-#include "ScriptWrappable.h"
-#include <wtf/RefCounted.h>
-#include <wtf/RefPtr.h>
-#include <wtf/Vector.h>
-#include <wtf/text/WTFString.h>
+#include "MediaTrackConstraints.h"
namespace WebCore {
-class Dictionary;
-class MediaConstraintsImpl;
-class MediaSourceStates;
-class MediaStreamTrackSourcesCallback;
-class MediaStreamCapabilities;
-class MediaTrackConstraints;
+class AudioSourceProvider;
-class MediaStreamTrack : public RefCounted<MediaStreamTrack>, public ScriptWrappable, public ActiveDOMObject, public EventTargetWithInlineData, public MediaStreamTrackPrivateClient {
+struct MediaTrackConstraints;
+
+class MediaStreamTrack final : public RefCounted<MediaStreamTrack>, public ActiveDOMObject, public EventTargetWithInlineData, private MediaStreamTrackPrivate::Observer {
public:
class Observer {
public:
@@ -57,9 +51,10 @@ public:
virtual void trackDidEnd() = 0;
};
+ static Ref<MediaStreamTrack> create(ScriptExecutionContext&, Ref<MediaStreamTrackPrivate>&&);
virtual ~MediaStreamTrack();
- virtual const AtomicString& kind() const = 0;
+ const AtomicString& kind() const;
const String& id() const;
const String& label() const;
@@ -69,82 +64,96 @@ public:
bool muted() const;
bool readonly() const;
bool remote() const;
- bool stopped() const;
-
- const AtomicString& readyState() const;
- static void getSources(ScriptExecutionContext*, PassRefPtr<MediaStreamTrackSourcesCallback>, ExceptionCode&);
+ enum class State { New, Live, Ended };
+ State readyState() const;
- RefPtr<MediaTrackConstraints> constraints() const;
- RefPtr<MediaSourceStates> states() const;
- RefPtr<MediaStreamCapabilities> capabilities() const;
- void applyConstraints(const Dictionary&);
- void applyConstraints(PassRefPtr<MediaConstraints>);
+ bool ended() const;
- RefPtr<MediaStreamTrack> clone();
+ Ref<MediaStreamTrack> clone();
void stopProducingData();
- DEFINE_ATTRIBUTE_EVENT_LISTENER(mute);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(unmute);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(started);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(ended);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(overconstrained);
-
- MediaStreamSource* source() const { return m_privateTrack->source(); }
- MediaStreamTrackPrivate& privateTrack() { return m_privateTrack.get(); }
-
- bool ended() const;
+ struct TrackSettings {
+ std::optional<int> width;
+ std::optional<int> height;
+ std::optional<double> aspectRatio;
+ std::optional<double> frameRate;
+ String facingMode;
+ std::optional<double> volume;
+ std::optional<int> sampleRate;
+ std::optional<int> sampleSize;
+ std::optional<bool> echoCancellation;
+ String deviceId;
+ String groupId;
+ };
+ TrackSettings getSettings() const;
+
+ struct TrackCapabilities {
+ std::optional<LongRange> width;
+ std::optional<LongRange> height;
+ std::optional<DoubleRange> aspectRatio;
+ std::optional<DoubleRange> frameRate;
+ std::optional<Vector<String>> facingMode;
+ std::optional<DoubleRange> volume;
+ std::optional<LongRange> sampleRate;
+ std::optional<LongRange> sampleSize;
+ std::optional<Vector<bool>> echoCancellation;
+ String deviceId;
+ String groupId;
+ };
+ TrackCapabilities getCapabilities() const;
- void addObserver(Observer*);
- void removeObserver(Observer*);
+ const MediaTrackConstraints& getConstraints() const { return m_constraints; }
+ void applyConstraints(const std::optional<MediaTrackConstraints>&, DOMPromise<void>&&);
- // EventTarget
- virtual EventTargetInterface eventTargetInterface() const override final { return MediaStreamTrackEventTargetInterfaceType; }
- virtual ScriptExecutionContext* scriptExecutionContext() const override final { return ActiveDOMObject::scriptExecutionContext(); }
+ RealtimeMediaSource& source() { return m_private->source(); }
+ MediaStreamTrackPrivate& privateTrack() { return m_private.get(); }
- using RefCounted<MediaStreamTrack>::ref;
- using RefCounted<MediaStreamTrack>::deref;
+ AudioSourceProvider* audioSourceProvider();
-protected:
- explicit MediaStreamTrack(MediaStreamTrack&);
- MediaStreamTrack(ScriptExecutionContext&, MediaStreamTrackPrivate&, const Dictionary*);
+ void addObserver(Observer&);
+ void removeObserver(Observer&);
- void setSource(PassRefPtr<MediaStreamSource>);
+ using RefCounted::ref;
+ using RefCounted::deref;
private:
+ MediaStreamTrack(ScriptExecutionContext&, Ref<MediaStreamTrackPrivate>&&);
+ explicit MediaStreamTrack(MediaStreamTrack&);
void configureTrackRendering();
- void trackDidEnd();
- void scheduleEventDispatch(PassRefPtr<Event>);
- void dispatchQueuedEvents();
- // ActiveDOMObject
- virtual void stop() override final;
+ // ActiveDOMObject API.
+ void stop() final;
+ const char* activeDOMObjectName() const final;
+ bool canSuspendForDocumentSuspension() const final;
// EventTarget
- virtual void refEventTarget() override final { ref(); }
- virtual void derefEventTarget() override final { deref(); }
+ void refEventTarget() final { ref(); }
+ void derefEventTarget() final { deref(); }
+ EventTargetInterface eventTargetInterface() const final { return MediaStreamTrackEventTargetInterfaceType; }
+ ScriptExecutionContext* scriptExecutionContext() const final { return ActiveDOMObject::scriptExecutionContext(); }
- // MediaStreamTrackPrivateClient
- void trackReadyStateChanged();
- void trackMutedChanged();
- void trackEnabledChanged();
+ // MediaStreamTrackPrivate::Observer
+ void trackEnded(MediaStreamTrackPrivate&) override;
+ void trackMutedChanged(MediaStreamTrackPrivate&) override;
+ void trackSettingsChanged(MediaStreamTrackPrivate&) override;
+ void trackEnabledChanged(MediaStreamTrackPrivate&) override;
- Vector<RefPtr<Event>> m_scheduledEvents;
-
- RefPtr<MediaConstraintsImpl> m_constraints;
- Mutex m_mutex;
+ WeakPtr<MediaStreamTrack> createWeakPtr() { return m_weakPtrFactory.createWeakPtr(); }
Vector<Observer*> m_observers;
+ Ref<MediaStreamTrackPrivate> m_private;
- Ref<MediaStreamTrackPrivate> m_privateTrack;
- bool m_eventDispatchScheduled;
+ MediaTrackConstraints m_constraints;
+ std::optional<DOMPromise<void>> m_promise;
+ WeakPtrFactory<MediaStreamTrack> m_weakPtrFactory;
- bool m_stoppingTrack;
+ bool m_ended { false };
};
+typedef Vector<RefPtr<MediaStreamTrack>> MediaStreamTrackVector;
+
} // namespace WebCore
#endif // ENABLE(MEDIA_STREAM)
-
-#endif // MediaStreamTrack_h
diff --git a/Source/WebCore/Modules/mediastream/MediaStreamTrack.idl b/Source/WebCore/Modules/mediastream/MediaStreamTrack.idl
index 68d5fb91c..ac033019a 100644
--- a/Source/WebCore/Modules/mediastream/MediaStreamTrack.idl
+++ b/Source/WebCore/Modules/mediastream/MediaStreamTrack.idl
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2011 Google Inc. All rights reserved.
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * 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
@@ -26,45 +26,69 @@
enum MediaStreamTrackState { "new", "live", "ended" };
[
- Conditional=MEDIA_STREAM,
- EventTarget,
ActiveDOMObject,
+ Conditional=MEDIA_STREAM,
+ PrivateIdentifier,
+ PublicIdentifier,
SkipVTableValidation,
-] interface MediaStreamTrack {
+] interface MediaStreamTrack : EventTarget {
readonly attribute DOMString kind;
readonly attribute DOMString id;
readonly attribute DOMString label;
- attribute boolean enabled;
+ attribute boolean enabled;
readonly attribute boolean muted;
- attribute EventListener onmute;
- attribute EventListener onunmute;
+ attribute EventHandler onmute;
+ attribute EventHandler onunmute;
readonly attribute boolean _readonly;
readonly attribute boolean remote;
readonly attribute MediaStreamTrackState readyState;
- attribute EventListener onstarted;
- attribute EventListener onended;
-
- [CallWith=ScriptExecutionContext, RaisesException] static void getSources(MediaStreamTrackSourcesCallback callback);
-
- MediaTrackConstraints constraints();
+ attribute EventHandler onended;
- MediaSourceStates states();
-
- MediaStreamCapabilities capabilities(); // returns either AllVideoCapabilities or AllAudioCapabilities
-
- void applyConstraints(Dictionary constraints);
-
- attribute EventListener onoverconstrained;
MediaStreamTrack clone();
[ImplementedAs=stopProducingData] void stop();
- // 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);
+ MediaTrackConstraints getConstraints();
+ MediaTrackSettings getSettings();
+ MediaTrackCapabilities getCapabilities();
+ Promise<void> applyConstraints(optional MediaTrackConstraints constraints);
+
+ attribute EventHandler onoverconstrained;
};
+[
+ Conditional=MEDIA_STREAM,
+ JSGenerateToJSObject,
+] dictionary MediaTrackCapabilities {
+ LongRange width;
+ LongRange height;
+ DoubleRange aspectRatio;
+ DoubleRange frameRate;
+ sequence<DOMString> facingMode;
+ DoubleRange volume;
+ LongRange sampleRate;
+ LongRange sampleSize;
+ sequence<boolean> echoCancellation;
+ // FIXME: add latency
+ // FIXME: add channelCount
+ DOMString deviceId;
+ DOMString groupId;
+};
+
+[
+ Conditional=MEDIA_STREAM,
+ JSGenerateToJSObject,
+] dictionary MediaTrackSettings {
+ long width;
+ long height;
+ double aspectRatio;
+ double frameRate;
+ DOMString facingMode;
+ double volume;
+ long sampleRate;
+ long sampleSize;
+ boolean echoCancellation;
+ // FIXME: add latency
+ // FIXME: add channelCount
+ DOMString deviceId;
+ DOMString groupId;
+};
diff --git a/Source/WebCore/Modules/mediastream/MediaStreamTrackEvent.cpp b/Source/WebCore/Modules/mediastream/MediaStreamTrackEvent.cpp
index 6d884fc5a..de46e0bc2 100644
--- a/Source/WebCore/Modules/mediastream/MediaStreamTrackEvent.cpp
+++ b/Source/WebCore/Modules/mediastream/MediaStreamTrackEvent.cpp
@@ -27,43 +27,28 @@
#include "MediaStreamTrackEvent.h"
-#include "EventNames.h"
#include "MediaStreamTrack.h"
namespace WebCore {
-MediaStreamTrackEventInit::MediaStreamTrackEventInit()
- : track(0)
+Ref<MediaStreamTrackEvent> MediaStreamTrackEvent::create(const AtomicString& type, bool canBubble, bool cancelable, RefPtr<MediaStreamTrack>&& track)
{
+ return adoptRef(*new MediaStreamTrackEvent(type, canBubble, cancelable, WTFMove(track)));
}
-PassRefPtr<MediaStreamTrackEvent> MediaStreamTrackEvent::create()
+Ref<MediaStreamTrackEvent> MediaStreamTrackEvent::create(const AtomicString& type, const Init& initializer, IsTrusted isTrusted)
{
- return adoptRef(new MediaStreamTrackEvent);
+ return adoptRef(*new MediaStreamTrackEvent(type, initializer, isTrusted));
}
-PassRefPtr<MediaStreamTrackEvent> MediaStreamTrackEvent::create(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<MediaStreamTrack> track)
-{
- return adoptRef(new MediaStreamTrackEvent(type, canBubble, cancelable, track));
-}
-
-PassRefPtr<MediaStreamTrackEvent> MediaStreamTrackEvent::create(const AtomicString& type, const MediaStreamTrackEventInit& initializer)
-{
- return adoptRef(new MediaStreamTrackEvent(type, initializer));
-}
-
-MediaStreamTrackEvent::MediaStreamTrackEvent()
-{
-}
-
-MediaStreamTrackEvent::MediaStreamTrackEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<MediaStreamTrack> track)
+MediaStreamTrackEvent::MediaStreamTrackEvent(const AtomicString& type, bool canBubble, bool cancelable, RefPtr<MediaStreamTrack>&& track)
: Event(type, canBubble, cancelable)
- , m_track(track)
+ , m_track(WTFMove(track))
{
}
-MediaStreamTrackEvent::MediaStreamTrackEvent(const AtomicString& type, const MediaStreamTrackEventInit& initializer)
- : Event(type, initializer)
+MediaStreamTrackEvent::MediaStreamTrackEvent(const AtomicString& type, const Init& initializer, IsTrusted isTrusted)
+ : Event(type, initializer, isTrusted)
, m_track(initializer.track)
{
}
diff --git a/Source/WebCore/Modules/mediastream/MediaStreamTrackEvent.h b/Source/WebCore/Modules/mediastream/MediaStreamTrackEvent.h
index b0818abb4..4101f49f8 100644
--- a/Source/WebCore/Modules/mediastream/MediaStreamTrackEvent.h
+++ b/Source/WebCore/Modules/mediastream/MediaStreamTrackEvent.h
@@ -22,8 +22,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef MediaStreamTrackEvent_h
-#define MediaStreamTrackEvent_h
+#pragma once
#if ENABLE(MEDIA_STREAM)
@@ -34,29 +33,25 @@ namespace WebCore {
class MediaStreamTrack;
-struct MediaStreamTrackEventInit : public EventInit {
- MediaStreamTrackEventInit();
-
- RefPtr<MediaStreamTrack> track;
-};
-
class MediaStreamTrackEvent : public Event {
public:
virtual ~MediaStreamTrackEvent();
- static PassRefPtr<MediaStreamTrackEvent> create();
- static PassRefPtr<MediaStreamTrackEvent> create(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<MediaStreamTrack>);
- static PassRefPtr<MediaStreamTrackEvent> create(const AtomicString& type, const MediaStreamTrackEventInit& initializer);
+ static Ref<MediaStreamTrackEvent> create(const AtomicString& type, bool canBubble, bool cancelable, RefPtr<MediaStreamTrack>&&);
+
+ struct Init : EventInit {
+ RefPtr<MediaStreamTrack> track;
+ };
+ static Ref<MediaStreamTrackEvent> create(const AtomicString& type, const Init&, IsTrusted = IsTrusted::No);
MediaStreamTrack* track() const;
// Event
- virtual EventInterface eventInterface() const override;
+ EventInterface eventInterface() const override;
private:
- MediaStreamTrackEvent();
- MediaStreamTrackEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<MediaStreamTrack>);
- MediaStreamTrackEvent(const AtomicString& type, const MediaStreamTrackEventInit&);
+ MediaStreamTrackEvent(const AtomicString& type, bool canBubble, bool cancelable, RefPtr<MediaStreamTrack>&&);
+ MediaStreamTrackEvent(const AtomicString& type, const Init&, IsTrusted);
RefPtr<MediaStreamTrack> m_track;
};
@@ -64,5 +59,3 @@ private:
} // namespace WebCore
#endif // ENABLE(MEDIA_STREAM)
-
-#endif // MediaStreamTrackEvent_h
diff --git a/Source/WebCore/Modules/mediastream/MediaStreamTrackEvent.idl b/Source/WebCore/Modules/mediastream/MediaStreamTrackEvent.idl
index 10d5f333f..26d9cca68 100644
--- a/Source/WebCore/Modules/mediastream/MediaStreamTrackEvent.idl
+++ b/Source/WebCore/Modules/mediastream/MediaStreamTrackEvent.idl
@@ -24,8 +24,11 @@
[
Conditional=MEDIA_STREAM,
- ConstructorTemplate=Event
+ Constructor(DOMString type, MediaStreamTrackEventInit eventInitDict),
] interface MediaStreamTrackEvent : Event {
- [InitializedByEventConstructor] readonly attribute MediaStreamTrack track;
+ readonly attribute MediaStreamTrack track;
};
+dictionary MediaStreamTrackEventInit : EventInit {
+ required MediaStreamTrack track;
+};
diff --git a/Source/WebCore/Modules/mediastream/MediaStreamTrackSourcesCallback.h b/Source/WebCore/Modules/mediastream/MediaStreamTrackSourcesCallback.h
deleted file mode 100644
index fcee4df8a..000000000
--- a/Source/WebCore/Modules/mediastream/MediaStreamTrackSourcesCallback.h
+++ /dev/null
@@ -1,46 +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.
- *
- * THIS SOFTWARE IS PROVIDED BY GOOGLE 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 GOOGLE 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.
- */
-
-#ifndef MediaStreamTrackSourcesCallback_h
-#define MediaStreamTrackSourcesCallback_h
-
-#if ENABLE(MEDIA_STREAM)
-
-#include "SourceInfo.h"
-#include <wtf/RefCounted.h>
-
-namespace WebCore {
-
-class MediaStreamTrackSourcesCallback : public RefCounted<MediaStreamTrackSourcesCallback> {
-public:
- virtual ~MediaStreamTrackSourcesCallback() { }
- virtual bool handleEvent(Vector<RefPtr<SourceInfo>>) = 0;
-};
-
-} // namespace WebCore
-
-#endif
-
-#endif // MediaStreamTrackSourcesCallback_h
diff --git a/Source/WebCore/Modules/mediastream/MediaStreamTrackSourcesRequest.cpp b/Source/WebCore/Modules/mediastream/MediaStreamTrackSourcesRequest.cpp
deleted file mode 100644
index 18b929a9e..000000000
--- a/Source/WebCore/Modules/mediastream/MediaStreamTrackSourcesRequest.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2013 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 GOOGLE 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 GOOGLE 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 "MediaStreamTrackSourcesRequest.h"
-
-#if ENABLE(MEDIA_STREAM)
-
-#include "MediaStreamTrackSourcesCallback.h"
-#include "ScriptExecutionContext.h"
-#include "SecurityOrigin.h"
-#include "SourceInfo.h"
-#include <wtf/Functional.h>
-#include <wtf/MainThread.h>
-
-namespace WebCore {
-
-PassRefPtr<MediaStreamTrackSourcesRequest> MediaStreamTrackSourcesRequest::create(ScriptExecutionContext* context, PassRefPtr<MediaStreamTrackSourcesCallback> callback)
-{
- return adoptRef(new MediaStreamTrackSourcesRequest(context, callback));
-}
-
-MediaStreamTrackSourcesRequest::MediaStreamTrackSourcesRequest(ScriptExecutionContext* context, PassRefPtr<MediaStreamTrackSourcesCallback> callback)
- : m_callback(callback)
-{
- m_origin = context->securityOrigin()->toString();
-}
-
-void MediaStreamTrackSourcesRequest::didCompleteRequest(const Vector<RefPtr<TrackSourceInfo>>& requestSourceInfos)
-{
- ASSERT(m_callback);
-
- for (size_t i = 0; i < requestSourceInfos.size(); ++i)
- m_sourceInfos.append(SourceInfo::create(requestSourceInfos[i]));
-
- callOnMainThread(bind(&MediaStreamTrackSourcesRequest::callCompletionHandler, this));
-}
-
-void MediaStreamTrackSourcesRequest::callCompletionHandler()
-{
- ASSERT(m_callback);
-
- m_callback->handleEvent(m_sourceInfos);
- m_callback = nullptr;
-}
-
-} // namespace WebCore
-
-#endif
diff --git a/Source/WebCore/Modules/mediastream/MediaStreamTrackSourcesRequest.h b/Source/WebCore/Modules/mediastream/MediaStreamTrackSourcesRequest.h
deleted file mode 100644
index 96599b436..000000000
--- a/Source/WebCore/Modules/mediastream/MediaStreamTrackSourcesRequest.h
+++ /dev/null
@@ -1,65 +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.
- *
- * THIS SOFTWARE IS PROVIDED BY GOOGLE 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 GOOGLE 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.
- */
-
-#ifndef MediaStreamTrackSourcesRequest_h
-#define MediaStreamTrackSourcesRequest_h
-
-#if ENABLE(MEDIA_STREAM)
-
-#include "MediaStreamTrackSourcesRequestClient.h"
-#include "SourceInfo.h"
-#include "Timer.h"
-#include <wtf/RefPtr.h>
-#include <wtf/text/WTFString.h>
-
-namespace WebCore {
-
-class MediaStreamTrackSourcesCallback;
-class ScriptExecutionContext;
-
-class MediaStreamTrackSourcesRequest : public MediaStreamTrackSourcesRequestClient {
-public:
- static PassRefPtr<MediaStreamTrackSourcesRequest> create(ScriptExecutionContext*, PassRefPtr<MediaStreamTrackSourcesCallback>);
- virtual ~MediaStreamTrackSourcesRequest() { }
-
-private:
- MediaStreamTrackSourcesRequest(ScriptExecutionContext*, PassRefPtr<MediaStreamTrackSourcesCallback>);
-
- // MediaStreamTrackSourcesRequestClient
- virtual const String& requestOrigin() const override { return m_origin; }
- virtual void didCompleteRequest(const Vector<RefPtr<TrackSourceInfo>>&) override;
-
- void callCompletionHandler();
-
- String m_origin;
- RefPtr<MediaStreamTrackSourcesCallback> m_callback;
- Vector<RefPtr<SourceInfo>> m_sourceInfos;
-};
-
-} // namespace WebCore
-
-#endif // MediaStreamTrackSourcesRequest_h
-
-#endif
diff --git a/Source/WebCore/Modules/mediastream/MediaTrackConstraint.cpp b/Source/WebCore/Modules/mediastream/MediaTrackConstraint.cpp
deleted file mode 100644
index 453fcebc1..000000000
--- a/Source/WebCore/Modules/mediastream/MediaTrackConstraint.cpp
+++ /dev/null
@@ -1,53 +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. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED 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
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (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_STREAM)
-
-#include "MediaTrackConstraint.h"
-
-using namespace JSC;
-
-namespace WebCore {
-
-RefPtr<MediaTrackConstraint> MediaTrackConstraint::create(const Dictionary& constraint)
-{
- return adoptRef(new MediaTrackConstraint(constraint));
-}
-
-MediaTrackConstraint::MediaTrackConstraint(const Dictionary& constraint)
- : m_constraint(constraint)
-{
-}
-
-MediaTrackConstraint::~MediaTrackConstraint()
-{
-}
-
-} // namespace WebCore
-
-#endif
diff --git a/Source/WebCore/Modules/mediastream/MediaTrackConstraintSet.cpp b/Source/WebCore/Modules/mediastream/MediaTrackConstraintSet.cpp
deleted file mode 100644
index a5aec90a5..000000000
--- a/Source/WebCore/Modules/mediastream/MediaTrackConstraintSet.cpp
+++ /dev/null
@@ -1,53 +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. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED 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
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (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_STREAM)
-
-#include "MediaTrackConstraintSet.h"
-
-using namespace JSC;
-
-namespace WebCore {
-
-RefPtr<MediaTrackConstraintSet> MediaTrackConstraintSet::create(const Dictionary& constraints)
-{
- return adoptRef(new MediaTrackConstraintSet(constraints));
-}
-
-MediaTrackConstraintSet::MediaTrackConstraintSet(const Dictionary& constraints)
- : m_constraints(constraints)
-{
-}
-
-MediaTrackConstraintSet::~MediaTrackConstraintSet()
-{
-}
-
-} // namespace WebCore
-
-#endif
diff --git a/Source/WebCore/Modules/mediastream/MediaTrackConstraintSet.h b/Source/WebCore/Modules/mediastream/MediaTrackConstraintSet.h
deleted file mode 100644
index b6978ae30..000000000
--- a/Source/WebCore/Modules/mediastream/MediaTrackConstraintSet.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. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED 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
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (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 MediaTrackConstraintSet_h
-#define MediaTrackConstraintSet_h
-
-#if ENABLE(MEDIA_STREAM)
-
-#include "Dictionary.h"
-#include "ScriptWrappable.h"
-#include <wtf/RefCounted.h>
-
-namespace WebCore {
-
-class MediaTrackConstraintSet : public RefCounted<MediaTrackConstraintSet>, public ScriptWrappable {
-public:
- static RefPtr<MediaTrackConstraintSet> create(const Dictionary&);
-
- virtual ~MediaTrackConstraintSet();
-
- const Dictionary& constraints() const { return m_constraints; }
-
-private:
- explicit MediaTrackConstraintSet(const Dictionary&);
-
- const Dictionary& m_constraints;
-};
-
-} // namespace WebCore
-
-#endif // MediaTrackConstraintSet_h
-
-#endif
diff --git a/Source/WebCore/Modules/mediastream/MediaTrackConstraints.cpp b/Source/WebCore/Modules/mediastream/MediaTrackConstraints.cpp
index 77b105448..c4c4cf122 100644
--- a/Source/WebCore/Modules/mediastream/MediaTrackConstraints.cpp
+++ b/Source/WebCore/Modules/mediastream/MediaTrackConstraints.cpp
@@ -1,67 +1,200 @@
/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * 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. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED 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
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this 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 "MediaTrackConstraints.h"
#if ENABLE(MEDIA_STREAM)
-#include "MediaTrackConstraints.h"
+#include "MediaConstraintsImpl.h"
-#include "MediaTrackConstraint.h"
-#include "MediaTrackConstraintSet.h"
-#include "NotImplemented.h"
+namespace WebCore {
-using namespace JSC;
+enum class ConstraintSetType { Mandatory, Advanced };
-namespace WebCore {
+static void set(MediaTrackConstraintSetMap& map, ConstraintSetType setType, const char* typeAsString, MediaConstraintType type, const ConstrainLong& value)
+{
+ IntConstraint constraint(typeAsString, type);
+ WTF::switchOn(value,
+ [&] (int integer) {
+ if (setType == ConstraintSetType::Mandatory)
+ constraint.setIdeal(integer);
+ else
+ constraint.setExact(integer);
+ },
+ [&] (const ConstrainLongRange& range) {
+ if (range.min)
+ constraint.setMin(range.min.value());
+ if (range.max)
+ constraint.setMax(range.max.value());
+ if (range.exact)
+ constraint.setExact(range.exact.value());
+ if (range.ideal)
+ constraint.setIdeal(range.ideal.value());
+ }
+ );
+ map.set(type, WTFMove(constraint));
+}
+
+static void set(MediaTrackConstraintSetMap& map, ConstraintSetType setType, const char* typeAsString, MediaConstraintType type, const ConstrainDouble& value)
+{
+ DoubleConstraint constraint(typeAsString, type);
+ WTF::switchOn(value,
+ [&] (double number) {
+ if (setType == ConstraintSetType::Mandatory)
+ constraint.setIdeal(number);
+ else
+ constraint.setExact(number);
+ },
+ [&] (const ConstrainDoubleRange& range) {
+ if (range.min)
+ constraint.setMin(range.min.value());
+ if (range.max)
+ constraint.setMax(range.max.value());
+ if (range.exact)
+ constraint.setExact(range.exact.value());
+ if (range.ideal)
+ constraint.setIdeal(range.ideal.value());
+ }
+ );
+ map.set(type, WTFMove(constraint));
+}
-RefPtr<MediaTrackConstraints> MediaTrackConstraints::create(PassRefPtr<MediaConstraintsImpl> constraints)
+static void set(MediaTrackConstraintSetMap& map, ConstraintSetType setType, const char* typeAsString, MediaConstraintType type, const ConstrainBoolean& value)
{
- return adoptRef(new MediaTrackConstraints(constraints));
+ BooleanConstraint constraint(typeAsString, type);
+ WTF::switchOn(value,
+ [&] (bool boolean) {
+ if (setType == ConstraintSetType::Mandatory)
+ constraint.setIdeal(boolean);
+ else
+ constraint.setExact(boolean);
+ },
+ [&] (const ConstrainBooleanParameters& parameters) {
+ if (parameters.exact)
+ constraint.setExact(parameters.exact.value());
+ if (parameters.ideal)
+ constraint.setIdeal(parameters.ideal.value());
+ }
+ );
+ map.set(type, WTFMove(constraint));
}
-MediaTrackConstraints::MediaTrackConstraints(PassRefPtr<MediaConstraintsImpl> constraints)
- : m_constraints(constraints)
+static void set(MediaTrackConstraintSetMap& map, ConstraintSetType setType, const char* typeAsString, MediaConstraintType type, const ConstrainDOMString& value)
{
+ StringConstraint constraint(typeAsString, type);
+ WTF::switchOn(value,
+ [&] (const String& string) {
+ if (setType == ConstraintSetType::Mandatory)
+ constraint.appendIdeal(string);
+ else
+ constraint.appendExact(string);
+ },
+ [&] (const Vector<String>& vector) {
+ if (setType == ConstraintSetType::Mandatory) {
+ for (auto& string : vector)
+ constraint.appendIdeal(string);
+ } else {
+ for (auto& string : vector)
+ constraint.appendExact(string);
+ }
+ },
+ [&] (const ConstrainDOMStringParameters& parameters) {
+ if (parameters.exact) {
+ WTF::switchOn(parameters.exact.value(),
+ [&] (const String& string) {
+ constraint.appendExact(string);
+ },
+ [&] (const Vector<String>& vector) {
+ for (auto& string : vector)
+ constraint.appendExact(string);
+ }
+ );
+ }
+ if (parameters.ideal) {
+ WTF::switchOn(parameters.ideal.value(),
+ [&] (const String& string) {
+ constraint.appendIdeal(string);
+ },
+ [&] (const Vector<String>& vector) {
+ for (auto& string : vector)
+ constraint.appendIdeal(string);
+ }
+ );
+ }
+ }
+ );
+ map.set(type, WTFMove(constraint));
}
-Vector<PassRefPtr<MediaTrackConstraint>> MediaTrackConstraints::optional(bool) const
+template<typename T> static inline void set(MediaTrackConstraintSetMap& map, ConstraintSetType setType, const char* typeAsString, MediaConstraintType type, const std::optional<T>& value)
{
- // https://bugs.webkit.org/show_bug.cgi?id=121954
- notImplemented();
- return Vector<PassRefPtr<MediaTrackConstraint>>();
+ if (!value)
+ return;
+ set(map, setType, typeAsString, type, value.value());
}
-RefPtr<MediaTrackConstraintSet> MediaTrackConstraints::mandatory(bool) const
+static MediaTrackConstraintSetMap convertToInternalForm(ConstraintSetType setType, const MediaTrackConstraintSet& constraintSet)
{
- // https://bugs.webkit.org/show_bug.cgi?id=121954
- notImplemented();
- return nullptr;
+ MediaTrackConstraintSetMap result;
+ set(result, setType, "width", MediaConstraintType::Width, constraintSet.width);
+ set(result, setType, "height", MediaConstraintType::Height, constraintSet.height);
+ set(result, setType, "aspectRatio", MediaConstraintType::AspectRatio, constraintSet.aspectRatio);
+ set(result, setType, "frameRate", MediaConstraintType::FrameRate, constraintSet.frameRate);
+ set(result, setType, "facingMode", MediaConstraintType::FacingMode, constraintSet.facingMode);
+ set(result, setType, "volume", MediaConstraintType::Volume, constraintSet.volume);
+ set(result, setType, "sampleRate", MediaConstraintType::SampleRate, constraintSet.sampleRate);
+ set(result, setType, "sampleSize", MediaConstraintType::SampleSize, constraintSet.sampleSize);
+ set(result, setType, "echoCancellation", MediaConstraintType::EchoCancellation, constraintSet.echoCancellation);
+ // FIXME: add latency
+ // FIXME: add channelCount
+ set(result, setType, "deviceId", MediaConstraintType::DeviceId, constraintSet.deviceId);
+ set(result, setType, "groupId", MediaConstraintType::GroupId, constraintSet.groupId);
+ return result;
}
-} // namespace WebCore
+static Vector<MediaTrackConstraintSetMap> convertAdvancedToInternalForm(const Vector<MediaTrackConstraintSet>& vector)
+{
+ Vector<MediaTrackConstraintSetMap> result;
+ result.reserveInitialCapacity(vector.size());
+ for (auto& set : vector)
+ result.uncheckedAppend(convertToInternalForm(ConstraintSetType::Advanced, set));
+ return result;
+}
+
+static Vector<MediaTrackConstraintSetMap> convertAdvancedToInternalForm(const std::optional<Vector<MediaTrackConstraintSet>>& optionalVector)
+{
+ if (!optionalVector)
+ return { };
+ return convertAdvancedToInternalForm(optionalVector.value());
+}
+
+Ref<MediaConstraintsImpl> createMediaConstraintsImpl(const MediaTrackConstraints& constraints)
+{
+ return MediaConstraintsImpl::create(convertToInternalForm(ConstraintSetType::Mandatory, constraints), convertAdvancedToInternalForm(constraints.advanced), true);
+}
+
+}
#endif
diff --git a/Source/WebCore/Modules/mediastream/MediaTrackConstraints.h b/Source/WebCore/Modules/mediastream/MediaTrackConstraints.h
index 95a8ccaff..3bccdaea0 100644
--- a/Source/WebCore/Modules/mediastream/MediaTrackConstraints.h
+++ b/Source/WebCore/Modules/mediastream/MediaTrackConstraints.h
@@ -1,60 +1,86 @@
/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * 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. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED 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
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this 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 MediaTrackConstraints_h
-#define MediaTrackConstraints_h
+#pragma once
#if ENABLE(MEDIA_STREAM)
-#include "MediaConstraintsImpl.h"
-#include "ScriptWrappable.h"
-#include <wtf/RefCounted.h>
+#include "DoubleRange.h"
+#include "LongRange.h"
+#include <wtf/Variant.h>
+#include <wtf/Vector.h>
+#include <wtf/text/WTFString.h>
namespace WebCore {
-class MediaTrackConstraint;
-class MediaTrackConstraintSet;
+class MediaConstraintsImpl;
-class MediaTrackConstraints : public RefCounted<MediaTrackConstraints>, public ScriptWrappable {
-public:
- virtual ~MediaTrackConstraints() { }
+struct ConstrainBooleanParameters {
+ std::optional<bool> exact;
+ std::optional<bool> ideal;
+};
- static RefPtr<MediaTrackConstraints> create(PassRefPtr<MediaConstraintsImpl>);
+struct ConstrainDOMStringParameters {
+ std::optional<Variant<String, Vector<String>>> exact;
+ std::optional<Variant<String, Vector<String>>> ideal;
+};
+
+struct ConstrainDoubleRange : DoubleRange {
+ std::optional<double> exact;
+ std::optional<double> ideal;
+};
- Vector<PassRefPtr<MediaTrackConstraint>> optional(bool) const;
- RefPtr<MediaTrackConstraintSet> mandatory(bool) const;
+struct ConstrainLongRange : LongRange {
+ std::optional<int> exact;
+ std::optional<int> ideal;
+};
+
+using ConstrainBoolean = Variant<bool, ConstrainBooleanParameters>;
+using ConstrainDOMString = Variant<String, Vector<String>, ConstrainDOMStringParameters>;
+using ConstrainDouble = Variant<double, ConstrainDoubleRange>;
+using ConstrainLong = Variant<int, ConstrainLongRange>;
+
+struct MediaTrackConstraintSet {
+ std::optional<ConstrainLong> width;
+ std::optional<ConstrainLong> height;
+ std::optional<ConstrainDouble> aspectRatio;
+ std::optional<ConstrainDouble> frameRate;
+ std::optional<ConstrainDOMString> facingMode;
+ std::optional<ConstrainDouble> volume;
+ std::optional<ConstrainLong> sampleRate;
+ std::optional<ConstrainLong> sampleSize;
+ std::optional<ConstrainBoolean> echoCancellation;
+ std::optional<ConstrainDOMString> deviceId;
+ std::optional<ConstrainDOMString> groupId;
+};
-private:
- explicit MediaTrackConstraints(PassRefPtr<MediaConstraintsImpl>);
-
- RefPtr<MediaConstraintsImpl> m_constraints;
+struct MediaTrackConstraints : MediaTrackConstraintSet {
+ std::optional<Vector<MediaTrackConstraintSet>> advanced;
};
-} // namespace WebCore
+Ref<MediaConstraintsImpl> createMediaConstraintsImpl(const MediaTrackConstraints&);
-#endif // MediaTrackConstraints_h
+}
#endif
diff --git a/Source/WebCore/Modules/mediastream/MediaTrackConstraints.idl b/Source/WebCore/Modules/mediastream/MediaTrackConstraints.idl
index aab80d0aa..59b96bd37 100644
--- a/Source/WebCore/Modules/mediastream/MediaTrackConstraints.idl
+++ b/Source/WebCore/Modules/mediastream/MediaTrackConstraints.idl
@@ -1,33 +1,91 @@
/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * 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.
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this 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
- * EXPRESS OR IMPLIED 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
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (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.
*/
[
Conditional=MEDIA_STREAM,
- NoInterfaceObject,
-] interface MediaTrackConstraints {
- readonly attribute MediaTrackConstraintSet? mandatory;
- readonly attribute MediaTrackConstraint[]? optional;
+ JSGenerateToJSObject,
+] dictionary MediaTrackConstraints : MediaTrackConstraintSet {
+ sequence<MediaTrackConstraintSet> advanced;
};
+[
+ Conditional=MEDIA_STREAM,
+ JSGenerateToJSObject,
+ ImplementedAs=MediaTrackConstraintSet
+] dictionary MediaTrackConstraintSet {
+ ConstrainLong width;
+ ConstrainLong height;
+ ConstrainDouble aspectRatio;
+ ConstrainDouble frameRate;
+ ConstrainDOMString facingMode;
+ ConstrainDouble volume;
+ ConstrainLong sampleRate;
+ ConstrainLong sampleSize;
+ ConstrainBoolean echoCancellation;
+ // FIXME: add latency
+ // FIXME: add channelCount
+ ConstrainDOMString deviceId;
+ ConstrainDOMString groupId;
+};
+
+typedef (double or ConstrainDoubleRange) ConstrainDouble;
+typedef (long or ConstrainLongRange) ConstrainLong;
+typedef (boolean or ConstrainBooleanParameters) ConstrainBoolean;
+typedef (DOMString or sequence<DOMString> or ConstrainDOMStringParameters) ConstrainDOMString;
+
+[
+ Conditional=MEDIA_STREAM,
+ JSGenerateToJSObject,
+ ImplementedAs=ConstrainBooleanParameters
+] dictionary ConstrainBooleanParameters {
+ boolean exact;
+ boolean ideal;
+};
+
+[
+ Conditional=MEDIA_STREAM,
+ JSGenerateToJSObject,
+ ImplementedAs=ConstrainDOMStringParameters
+] dictionary ConstrainDOMStringParameters {
+ (DOMString or sequence<DOMString>) exact;
+ (DOMString or sequence<DOMString>) ideal;
+};
+
+[
+ Conditional=MEDIA_STREAM,
+ JSGenerateToJSObject,
+ ImplementedAs=ConstrainDoubleRange
+] dictionary ConstrainDoubleRange : DoubleRange {
+ double exact;
+ double ideal;
+};
+
+[
+ Conditional=MEDIA_STREAM,
+ JSGenerateToJSObject,
+ ImplementedAs=ConstrainLongRange
+] dictionary ConstrainLongRange : LongRange {
+ long exact;
+ long ideal;
+};
diff --git a/Source/WebCore/Modules/mediastream/MediaTrackSupportedConstraints.h b/Source/WebCore/Modules/mediastream/MediaTrackSupportedConstraints.h
new file mode 100644
index 000000000..f8626b899
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/MediaTrackSupportedConstraints.h
@@ -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.
+ * 3. Neither the name of Ericsson 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 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
+ * OWNER 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_STREAM)
+
+namespace WebCore {
+
+struct MediaTrackSupportedConstraints {
+ bool width;
+ bool height;
+ bool aspectRatio;
+ bool frameRate;
+ bool facingMode;
+ bool volume;
+ bool sampleRate;
+ bool sampleSize;
+ bool echoCancellation;
+ bool deviceId;
+ bool groupId;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(MEDIA_STREAM)
diff --git a/Source/WebCore/Modules/mediastream/MediaTrackSupportedConstraints.idl b/Source/WebCore/Modules/mediastream/MediaTrackSupportedConstraints.idl
new file mode 100644
index 000000000..586ec6990
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/MediaTrackSupportedConstraints.idl
@@ -0,0 +1,46 @@
+/*
+* 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.
+* 3. Neither the name of Ericsson 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 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
+* OWNER 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=MEDIA_STREAM,
+ JSGenerateToJSObject,
+] dictionary MediaTrackSupportedConstraints {
+ boolean width = true;
+ boolean height = true;
+ boolean aspectRatio = true;
+ boolean frameRate = true;
+ boolean facingMode = true;
+ boolean volume = true;
+ boolean sampleRate = true;
+ boolean sampleSize = true;
+ boolean echoCancellation = true;
+ boolean deviceId = true;
+ boolean groupId = true;
+};
diff --git a/Source/WebCore/Modules/mediastream/RTCVoidRequestImpl.cpp b/Source/WebCore/Modules/mediastream/NavigatorMediaDevices.cpp
index 116115818..8a0f16ba6 100644
--- a/Source/WebCore/Modules/mediastream/RTCVoidRequestImpl.cpp
+++ b/Source/WebCore/Modules/mediastream/NavigatorMediaDevices.cpp
@@ -1,6 +1,5 @@
/*
- * Copyright (C) 2012 Google Inc. All rights reserved.
- * Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
+ * Copyright (C) 2015 Ericsson AB. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -12,7 +11,7 @@
* 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 Google Inc. nor the names of its contributors
+ * 3. Neither the name of Ericsson nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
@@ -30,61 +29,52 @@
*/
#include "config.h"
+#include "NavigatorMediaDevices.h"
#if ENABLE(MEDIA_STREAM)
-#include "RTCVoidRequestImpl.h"
-
-#include "DOMError.h"
-#include "RTCPeerConnection.h"
-#include "RTCPeerConnectionErrorCallback.h"
-#include "VoidCallback.h"
+#include "Document.h"
+#include "Frame.h"
+#include "MediaDevices.h"
+#include "Navigator.h"
namespace WebCore {
-PassRefPtr<RTCVoidRequestImpl> RTCVoidRequestImpl::create(ScriptExecutionContext* context, PassRefPtr<VoidCallback> successCallback, PassRefPtr<RTCPeerConnectionErrorCallback> errorCallback)
-{
- RefPtr<RTCVoidRequestImpl> request = adoptRef(new RTCVoidRequestImpl(context, successCallback, errorCallback));
- request->suspendIfNeeded();
- return request.release();
-}
-
-RTCVoidRequestImpl::RTCVoidRequestImpl(ScriptExecutionContext* context, PassRefPtr<VoidCallback> successCallback, PassRefPtr<RTCPeerConnectionErrorCallback> errorCallback)
- : ActiveDOMObject(context)
- , m_successCallback(successCallback)
- , m_errorCallback(errorCallback)
+NavigatorMediaDevices::NavigatorMediaDevices(Frame* frame)
+ : DOMWindowProperty(frame)
{
}
-RTCVoidRequestImpl::~RTCVoidRequestImpl()
+NavigatorMediaDevices::~NavigatorMediaDevices()
{
}
-void RTCVoidRequestImpl::requestSucceeded()
+NavigatorMediaDevices* NavigatorMediaDevices::from(Navigator* navigator)
{
- if (m_successCallback)
- m_successCallback->handleEvent();
-
- clear();
+ NavigatorMediaDevices* supplement = static_cast<NavigatorMediaDevices*>(Supplement<Navigator>::from(navigator, supplementName()));
+ if (!supplement) {
+ auto newSupplement = std::make_unique<NavigatorMediaDevices>(navigator->frame());
+ supplement = newSupplement.get();
+ provideTo(navigator, supplementName(), WTFMove(newSupplement));
+ }
+ return supplement;
}
-void RTCVoidRequestImpl::requestFailed(const String& error)
+MediaDevices* NavigatorMediaDevices::mediaDevices(Navigator& navigator)
{
- if (m_errorCallback.get())
- m_errorCallback->handleEvent(DOMError::create(error).get());
-
- clear();
+ return NavigatorMediaDevices::from(&navigator)->mediaDevices();
}
-void RTCVoidRequestImpl::stop()
+MediaDevices* NavigatorMediaDevices::mediaDevices() const
{
- clear();
+ if (!m_mediaDevices && frame())
+ m_mediaDevices = MediaDevices::create(*frame()->document());
+ return m_mediaDevices.get();
}
-void RTCVoidRequestImpl::clear()
+const char* NavigatorMediaDevices::supplementName()
{
- m_successCallback.clear();
- m_errorCallback.clear();
+ return "NavigatorMediaDevices";
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/mediastream/RTCVoidRequestImpl.h b/Source/WebCore/Modules/mediastream/NavigatorMediaDevices.h
index 3c3f34d48..677ddbbdf 100644
--- a/Source/WebCore/Modules/mediastream/RTCVoidRequestImpl.h
+++ b/Source/WebCore/Modules/mediastream/NavigatorMediaDevices.h
@@ -1,6 +1,5 @@
/*
- * Copyright (C) 2012 Google Inc. All rights reserved.
- * Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
+ * Copyright (C) 2015 Ericsson AB. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -12,7 +11,7 @@
* 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 Google Inc. nor the names of its contributors
+ * 3. Neither the name of Ericsson nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
@@ -29,43 +28,34 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef RTCVoidRequestImpl_h
-#define RTCVoidRequestImpl_h
+#pragma once
#if ENABLE(MEDIA_STREAM)
-#include "ActiveDOMObject.h"
-#include "RTCVoidRequest.h"
+#include "DOMWindowProperty.h"
+#include "Supplementable.h"
namespace WebCore {
-class RTCPeerConnectionErrorCallback;
-class VoidCallback;
+class Frame;
+class MediaDevices;
+class Navigator;
-class RTCVoidRequestImpl : public RTCVoidRequest, public ActiveDOMObject {
+class NavigatorMediaDevices : public Supplement<Navigator>, public DOMWindowProperty {
public:
- static PassRefPtr<RTCVoidRequestImpl> create(ScriptExecutionContext*, PassRefPtr<VoidCallback>, PassRefPtr<RTCPeerConnectionErrorCallback>);
- virtual ~RTCVoidRequestImpl();
+ explicit NavigatorMediaDevices(Frame*);
+ virtual ~NavigatorMediaDevices();
+ static NavigatorMediaDevices* from(Navigator*);
- virtual void requestSucceeded();
- virtual void requestFailed(const String& error);
-
- // ActiveDOMObject
- virtual void stop() override;
+ static MediaDevices* mediaDevices(Navigator&);
+ MediaDevices* mediaDevices() const;
private:
- RTCVoidRequestImpl(ScriptExecutionContext*, PassRefPtr<VoidCallback>, PassRefPtr<RTCPeerConnectionErrorCallback>);
-
- void clear();
+ static const char* supplementName();
- RefPtr<VoidCallback> m_successCallback;
- RefPtr<RTCPeerConnectionErrorCallback> m_errorCallback;
+ mutable RefPtr<MediaDevices> m_mediaDevices;
};
} // namespace WebCore
#endif // ENABLE(MEDIA_STREAM)
-
-#endif // RTCVoidRequestImpl_h
-
-
diff --git a/Source/WebCore/Modules/mediastream/RTCSessionDescriptionCallback.idl b/Source/WebCore/Modules/mediastream/NavigatorMediaDevices.idl
index 30ce08568..9c8e7849c 100644
--- a/Source/WebCore/Modules/mediastream/RTCSessionDescriptionCallback.idl
+++ b/Source/WebCore/Modules/mediastream/NavigatorMediaDevices.idl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Google Inc. All rights reserved.
+ * Copyright (C) 2015 Ericsson AB. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -11,7 +11,7 @@
* 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 Google Inc. nor the names of its contributors
+ * 3. Neither the name of Ericsson nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
@@ -30,7 +30,7 @@
[
Conditional=MEDIA_STREAM,
-] callback interface RTCSessionDescriptionCallback {
- boolean handleEvent(RTCSessionDescription sdp);
+ EnabledAtRuntime=MediaStream,
+] partial interface Navigator {
+ readonly attribute MediaDevices mediaDevices;
};
-
diff --git a/Source/WebCore/Modules/mediastream/NavigatorMediaStream.cpp b/Source/WebCore/Modules/mediastream/NavigatorMediaStream.cpp
deleted file mode 100644
index c14bc10e1..000000000
--- a/Source/WebCore/Modules/mediastream/NavigatorMediaStream.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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) 2008 Nokia Corporation and/or its subsidiary(-ies)
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "config.h"
-#include "NavigatorMediaStream.h"
-
-#if ENABLE(MEDIA_STREAM)
-
-#include "Dictionary.h"
-#include "Document.h"
-#include "ExceptionCode.h"
-#include "Frame.h"
-#include "Navigator.h"
-#include "NavigatorUserMediaErrorCallback.h"
-#include "NavigatorUserMediaSuccessCallback.h"
-#include "Page.h"
-#include "UserMediaController.h"
-#include "UserMediaRequest.h"
-
-namespace WebCore {
-
-NavigatorMediaStream::NavigatorMediaStream()
-{
-}
-
-NavigatorMediaStream::~NavigatorMediaStream()
-{
-}
-
-void NavigatorMediaStream::webkitGetUserMedia(Navigator* navigator, const Dictionary& options, PassRefPtr<NavigatorUserMediaSuccessCallback> successCallback, PassRefPtr<NavigatorUserMediaErrorCallback> errorCallback, ExceptionCode& ec)
-{
- if (!successCallback)
- return;
-
- UserMediaController* userMedia = UserMediaController::from(navigator->frame() ? navigator->frame()->page() : 0);
- if (!userMedia) {
- ec = NOT_SUPPORTED_ERR;
- return;
- }
-
- RefPtr<UserMediaRequest> request = UserMediaRequest::create(navigator->frame()->document(), userMedia, options, successCallback, errorCallback, ec);
- if (!request) {
- ec = NOT_SUPPORTED_ERR;
- return;
- }
-
- request->start();
-}
-
-} // namespace WebCore
-
-#endif // ENABLE(MEDIA_STREAM)
diff --git a/Source/WebCore/Modules/mediastream/NavigatorMediaStream.h b/Source/WebCore/Modules/mediastream/NavigatorMediaStream.h
deleted file mode 100644
index 1bdf4de32..000000000
--- a/Source/WebCore/Modules/mediastream/NavigatorMediaStream.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
-
- 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 NavigatorMediaStream_h
-#define NavigatorMediaStream_h
-
-#if ENABLE(MEDIA_STREAM)
-
-#include <wtf/PassRefPtr.h>
-#include <wtf/text/WTFString.h>
-
-namespace WebCore {
-
-class Dictionary;
-class Navigator;
-class NavigatorUserMediaErrorCallback;
-class NavigatorUserMediaSuccessCallback;
-
-typedef int ExceptionCode;
-
-class NavigatorMediaStream {
-public:
- static void webkitGetUserMedia(Navigator*, const Dictionary&, PassRefPtr<NavigatorUserMediaSuccessCallback>, PassRefPtr<NavigatorUserMediaErrorCallback>, ExceptionCode&);
-
-private:
- NavigatorMediaStream();
- ~NavigatorMediaStream();
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(MEDIA_STREAM)
-
-#endif // NavigatorMediaStream_h
diff --git a/Source/WebCore/Modules/mediastream/NavigatorUserMedia.idl b/Source/WebCore/Modules/mediastream/NavigatorUserMedia.idl
new file mode 100644
index 000000000..85e32d318
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/NavigatorUserMedia.idl
@@ -0,0 +1,36 @@
+/*
+* 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.
+* 3. Neither the name of Ericsson 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 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
+* OWNER 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=MEDIA_STREAM,
+ EnabledAtRuntime=MediaStream,
+] partial interface Navigator {
+ [JSBuiltin] void getUserMedia(MediaStreamConstraints constraints, NavigatorUserMediaSuccessCallback successCallback, NavigatorUserMediaErrorCallback errorCallback);
+};
diff --git a/Source/WebCore/Modules/mediastream/NavigatorUserMedia.js b/Source/WebCore/Modules/mediastream/NavigatorUserMedia.js
new file mode 100644
index 000000000..d1417d78b
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/NavigatorUserMedia.js
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2015 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 met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this 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(MEDIA_STREAM)
+
+function getUserMedia(options, successCallback, errorCallback)
+{
+ "use strict";
+
+ // FIXME: We should raise a DOM unsupported exception if there is no navigator and properly detect whether method is not called on a Navigator object.
+ if (!(this.mediaDevices && this.mediaDevices.@getUserMedia))
+ throw @makeThisTypeError("Navigator", "getUserMedia");
+
+ if (arguments.length < 3)
+ @throwTypeError("Not enough arguments");
+
+ if (options !== @Object(options))
+ @throwTypeError("Argument 1 (options) to Navigator.getUserMedia must be an object");
+
+ if (typeof successCallback !== "function")
+ @throwTypeError("Argument 2 ('successCallback') to Navigator.getUserMedia must be a function");
+ if (typeof errorCallback !== "function")
+ @throwTypeError("Argument 3 ('errorCallback') to Navigator.getUserMedia must be a function");
+
+ this.mediaDevices.@getUserMedia(options).@then(successCallback, errorCallback);
+}
diff --git a/Source/WebCore/Modules/mediastream/NavigatorUserMediaError.cpp b/Source/WebCore/Modules/mediastream/NavigatorUserMediaError.cpp
deleted file mode 100644
index 5ff494582..000000000
--- a/Source/WebCore/Modules/mediastream/NavigatorUserMediaError.cpp
+++ /dev/null
@@ -1,51 +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 COMPUTER, 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
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (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_STREAM)
-
-#include "NavigatorUserMediaError.h"
-
-#include <wtf/NeverDestroyed.h>
-
-namespace WebCore {
-
-const AtomicString& NavigatorUserMediaError::permissionDeniedErrorName()
-{
- static NeverDestroyed<AtomicString> permissionDenied("PermissionDeniedError", AtomicString::ConstructFromLiteral);
- return permissionDenied;
-}
-
-const AtomicString& NavigatorUserMediaError::constraintNotSatisfiedErrorName()
-{
- static NeverDestroyed<AtomicString> constraintNotSatisfied("ConstraintNotSatisfiedError", AtomicString::ConstructFromLiteral);
- return constraintNotSatisfied;
-}
-
-} // namespace WebCore
-
-#endif // ENABLE(MEDIA_STREAM)
-
diff --git a/Source/WebCore/Modules/mediastream/NavigatorUserMediaError.idl b/Source/WebCore/Modules/mediastream/NavigatorUserMediaError.idl
deleted file mode 100644
index de1030417..000000000
--- a/Source/WebCore/Modules/mediastream/NavigatorUserMediaError.idl
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2011 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. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED 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=MEDIA_STREAM,
- JSGenerateToJSObject,
-] interface NavigatorUserMediaError : DOMError {
- readonly attribute DOMString constraintName;
-};
-
diff --git a/Source/WebCore/Modules/mediastream/NavigatorUserMediaErrorCallback.h b/Source/WebCore/Modules/mediastream/NavigatorUserMediaErrorCallback.h
deleted file mode 100644
index dd3c8b32b..000000000
--- a/Source/WebCore/Modules/mediastream/NavigatorUserMediaErrorCallback.h
+++ /dev/null
@@ -1,45 +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 NavigatorUserMediaErrorCallback_h
-#define NavigatorUserMediaErrorCallback_h
-
-#if ENABLE(MEDIA_STREAM)
-
-#include "NavigatorUserMediaError.h"
-#include <wtf/RefCounted.h>
-
-namespace WebCore {
-
-class NavigatorUserMediaErrorCallback : public RefCounted<NavigatorUserMediaErrorCallback> {
-public:
- virtual ~NavigatorUserMediaErrorCallback() { }
- virtual bool handleEvent(NavigatorUserMediaError*) = 0;
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(MEDIA_STREAM)
-
-#endif // NavigatorUserMediaErrorCallback_h
diff --git a/Source/WebCore/Modules/mediastream/NavigatorUserMediaSuccessCallback.h b/Source/WebCore/Modules/mediastream/NavigatorUserMediaSuccessCallback.h
deleted file mode 100644
index 9f456eadc..000000000
--- a/Source/WebCore/Modules/mediastream/NavigatorUserMediaSuccessCallback.h
+++ /dev/null
@@ -1,46 +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 NavigatorUserMediaSuccessCallback_h
-#define NavigatorUserMediaSuccessCallback_h
-
-#if ENABLE(MEDIA_STREAM)
-
-#include <wtf/RefCounted.h>
-
-namespace WebCore {
-
-class MediaStream;
-
-class NavigatorUserMediaSuccessCallback : public RefCounted<NavigatorUserMediaSuccessCallback> {
-public:
- virtual ~NavigatorUserMediaSuccessCallback() { }
- virtual bool handleEvent(MediaStream*) = 0;
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(MEDIA_STREAM)
-
-#endif // NavigatorUserMediaSuccessCallback_h
diff --git a/Source/WebCore/Modules/indexeddb/IDBFactoryBackendInterface.cpp b/Source/WebCore/Modules/mediastream/OverconstrainedError.h
index d1c9fbde5..126ccb617 100644
--- a/Source/WebCore/Modules/indexeddb/IDBFactoryBackendInterface.cpp
+++ b/Source/WebCore/Modules/mediastream/OverconstrainedError.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * 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
@@ -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,21 +25,38 @@
* (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)
+#pragma once
-#include "DatabaseStrategy.h"
-#include "PlatformStrategies.h"
+#if ENABLE(MEDIA_STREAM)
+
+#include <wtf/RefCounted.h>
+#include <wtf/text/WTFString.h>
namespace WebCore {
-PassRefPtr<IDBFactoryBackendInterface> IDBFactoryBackendInterface::create(const String& databaseDirectoryIdentifier)
-{
- return platformStrategies()->databaseStrategy()->createIDBFactoryBackend(databaseDirectoryIdentifier);
-}
+class OverconstrainedError : public RefCounted<OverconstrainedError> {
+public:
+ static Ref<OverconstrainedError> create(const String& constraint, const String& message)
+ {
+ return adoptRef(*new OverconstrainedError(constraint, message));
+ }
+
+ String constraint() const { return m_constraint; }
+ String message() const { return m_message; }
+
+protected:
+ explicit OverconstrainedError(const String& constraint, const String& message)
+ : m_constraint(constraint)
+ , m_message(message)
+ {
+ }
+
+private:
+ String m_constraint;
+ String m_message;
+};
} // namespace WebCore
-#endif // ENABLE(INDEXED_DATABASE)
+#endif
diff --git a/Source/WebCore/Modules/indexeddb/IDBHistograms.h b/Source/WebCore/Modules/mediastream/OverconstrainedError.idl
index d58f088bd..8c505c6a5 100644
--- a/Source/WebCore/Modules/indexeddb/IDBHistograms.h
+++ b/Source/WebCore/Modules/mediastream/OverconstrainedError.idl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Google Inc. All rights reserved.
+ * 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
@@ -10,11 +10,11 @@
* 2. Redistributions in binary form must 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.
*
- * THIS SOFTWARE IS PROVIDED BY GOOGLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * 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
@@ -26,20 +26,11 @@
* 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,
+[
+ Constructor(optional DOMString constraint = "", optional DOMString message = ""),
+ ImplementationLacksVTable,
+ Conditional=MEDIA_STREAM,
+] exception OverconstrainedError {
+ readonly attribute DOMString message;
+ readonly attribute DOMString constraint;
};
-
-}
-
-#endif
diff --git a/Source/WebCore/Modules/mediastream/OverconstrainedErrorEvent.h b/Source/WebCore/Modules/mediastream/OverconstrainedErrorEvent.h
new file mode 100644
index 000000000..9c4f39640
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/OverconstrainedErrorEvent.h
@@ -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.
+ * 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.
+ *
+ * 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.
+ */
+
+#pragma once
+
+#if ENABLE(MEDIA_STREAM)
+
+#include "Event.h"
+#include "OverconstrainedError.h"
+#include <wtf/text/AtomicString.h>
+
+namespace WebCore {
+
+class OverconstrainedErrorEvent : public Event {
+public:
+ virtual ~OverconstrainedErrorEvent() { }
+
+ static Ref<OverconstrainedErrorEvent> create(const AtomicString& type, bool canBubble, bool cancelable, OverconstrainedError* error)
+ {
+ return adoptRef(*new OverconstrainedErrorEvent(type, canBubble, cancelable, error));
+ }
+
+ struct Init : EventInit {
+ RefPtr<OverconstrainedError> error;
+ };
+
+ static Ref<OverconstrainedErrorEvent> create(const AtomicString& type, const Init& initializer, IsTrusted isTrusted = IsTrusted::No)
+ {
+ return adoptRef(*new OverconstrainedErrorEvent(type, initializer, isTrusted));
+ }
+
+ OverconstrainedError* error() const { return m_error.get(); }
+ EventInterface eventInterface() const override { return OverconstrainedErrorEventInterfaceType; }
+
+private:
+ explicit OverconstrainedErrorEvent(const AtomicString& type, bool canBubble, bool cancelable, OverconstrainedError* error)
+ : Event(type, canBubble, cancelable)
+ , m_error(error)
+ {
+ }
+ OverconstrainedErrorEvent(const AtomicString& type, const Init& initializer, IsTrusted isTrusted)
+ : Event(type, initializer, isTrusted)
+ , m_error(initializer.error)
+ {
+ }
+
+ RefPtr<OverconstrainedError> m_error;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(MEDIA_STREAM)
diff --git a/Source/WebCore/Modules/mediastream/OverconstrainedErrorEvent.idl b/Source/WebCore/Modules/mediastream/OverconstrainedErrorEvent.idl
new file mode 100644
index 000000000..ceddfa7f2
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/OverconstrainedErrorEvent.idl
@@ -0,0 +1,38 @@
+/*
+* 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.
+* 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.
+*
+* 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=MEDIA_STREAM,
+ Constructor(DOMString type, optional OverconstrainedErrorEventInit eventInitDict),
+] interface OverconstrainedErrorEvent : Event {
+ readonly attribute OverconstrainedError? error;
+};
+
+dictionary OverconstrainedErrorEventInit : EventInit {
+ OverconstrainedError? error = null;
+};
diff --git a/Source/WebCore/Modules/mediastream/PeerConnectionBackend.cpp b/Source/WebCore/Modules/mediastream/PeerConnectionBackend.cpp
new file mode 100644
index 000000000..ba2d43fd2
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/PeerConnectionBackend.cpp
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2015 Ericsson AB. All rights reserved.
+ * 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.
+ * 3. Neither the name of Ericsson 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 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
+ * OWNER 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 "PeerConnectionBackend.h"
+
+#if ENABLE(WEB_RTC)
+
+#include "EventNames.h"
+#include "JSRTCSessionDescription.h"
+#include "RTCIceCandidate.h"
+#include "RTCIceCandidateEvent.h"
+#include "RTCPeerConnection.h"
+
+namespace WebCore {
+
+void PeerConnectionBackend::createOffer(RTCOfferOptions&& options, PeerConnection::SessionDescriptionPromise&& promise)
+{
+ ASSERT(!m_offerAnswerPromise);
+ ASSERT(m_peerConnection.internalSignalingState() != PeerConnectionStates::SignalingState::Closed);
+
+ m_offerAnswerPromise = WTFMove(promise);
+ doCreateOffer(WTFMove(options));
+}
+
+void PeerConnectionBackend::createOfferSucceeded(String&& sdp)
+{
+ ASSERT(isMainThread());
+
+ if (m_peerConnection.internalSignalingState() == PeerConnectionStates::SignalingState::Closed)
+ return;
+
+ ASSERT(m_offerAnswerPromise);
+ m_offerAnswerPromise->resolve(RTCSessionDescription::create(RTCSessionDescription::SdpType::Offer, WTFMove(sdp)));
+ m_offerAnswerPromise = std::nullopt;
+}
+
+void PeerConnectionBackend::createOfferFailed(Exception&& exception)
+{
+ ASSERT(isMainThread());
+
+ if (m_peerConnection.internalSignalingState() == PeerConnectionStates::SignalingState::Closed)
+ return;
+
+ ASSERT(m_offerAnswerPromise);
+ m_offerAnswerPromise->reject(WTFMove(exception));
+ m_offerAnswerPromise = std::nullopt;
+}
+
+void PeerConnectionBackend::createAnswer(RTCAnswerOptions&& options, PeerConnection::SessionDescriptionPromise&& promise)
+{
+ ASSERT(!m_offerAnswerPromise);
+ ASSERT(m_peerConnection.internalSignalingState() != PeerConnectionStates::SignalingState::Closed);
+
+ m_offerAnswerPromise = WTFMove(promise);
+ doCreateAnswer(WTFMove(options));
+}
+
+void PeerConnectionBackend::createAnswerSucceeded(String&& sdp)
+{
+ ASSERT(isMainThread());
+
+ if (m_peerConnection.internalSignalingState() == PeerConnectionStates::SignalingState::Closed)
+ return;
+
+ ASSERT(m_offerAnswerPromise);
+ m_offerAnswerPromise->resolve(RTCSessionDescription::create(RTCSessionDescription::SdpType::Answer, WTFMove(sdp)));
+ m_offerAnswerPromise = std::nullopt;
+}
+
+void PeerConnectionBackend::createAnswerFailed(Exception&& exception)
+{
+ ASSERT(isMainThread());
+
+ if (m_peerConnection.internalSignalingState() == PeerConnectionStates::SignalingState::Closed)
+ return;
+
+ ASSERT(m_offerAnswerPromise);
+ m_offerAnswerPromise->reject(WTFMove(exception));
+ m_offerAnswerPromise = std::nullopt;
+}
+
+static inline bool isLocalDescriptionTypeValidForState(RTCSessionDescription::SdpType type, PeerConnectionStates::SignalingState state)
+{
+ switch (state) {
+ case PeerConnectionStates::SignalingState::Stable:
+ return type == RTCSessionDescription::SdpType::Offer;
+ case PeerConnectionStates::SignalingState::HaveLocalOffer:
+ return type == RTCSessionDescription::SdpType::Offer;
+ case PeerConnectionStates::SignalingState::HaveRemoteOffer:
+ return type == RTCSessionDescription::SdpType::Answer || type == RTCSessionDescription::SdpType::Pranswer;
+ case PeerConnectionStates::SignalingState::HaveLocalPrAnswer:
+ return type == RTCSessionDescription::SdpType::Answer || type == RTCSessionDescription::SdpType::Pranswer;
+ default:
+ return false;
+ };
+
+ ASSERT_NOT_REACHED();
+ return false;
+}
+
+void PeerConnectionBackend::setLocalDescription(RTCSessionDescription& sessionDescription, DOMPromise<void>&& promise)
+{
+ ASSERT(m_peerConnection.internalSignalingState() != PeerConnectionStates::SignalingState::Closed);
+
+ if (!isLocalDescriptionTypeValidForState(sessionDescription.type(), m_peerConnection.internalSignalingState())) {
+ promise.reject(INVALID_STATE_ERR, "Description type incompatible with current signaling state");
+ return;
+ }
+
+ m_setDescriptionPromise = WTFMove(promise);
+ doSetLocalDescription(sessionDescription);
+}
+
+void PeerConnectionBackend::setLocalDescriptionSucceeded()
+{
+ ASSERT(isMainThread());
+
+ if (m_peerConnection.internalSignalingState() == PeerConnectionStates::SignalingState::Closed)
+ return;
+
+ ASSERT(m_setDescriptionPromise);
+
+ m_setDescriptionPromise->resolve();
+ m_setDescriptionPromise = std::nullopt;
+}
+
+void PeerConnectionBackend::setLocalDescriptionFailed(Exception&& exception)
+{
+ ASSERT(isMainThread());
+
+ if (m_peerConnection.internalSignalingState() == PeerConnectionStates::SignalingState::Closed)
+ return;
+
+ ASSERT(m_setDescriptionPromise);
+
+ m_setDescriptionPromise->reject(WTFMove(exception));
+ m_setDescriptionPromise = std::nullopt;
+}
+
+static inline bool isRemoteDescriptionTypeValidForState(RTCSessionDescription::SdpType type, PeerConnectionStates::SignalingState state)
+{
+ switch (state) {
+ case PeerConnectionStates::SignalingState::Stable:
+ return type == RTCSessionDescription::SdpType::Offer;
+ case PeerConnectionStates::SignalingState::HaveLocalOffer:
+ return type == RTCSessionDescription::SdpType::Answer || type == RTCSessionDescription::SdpType::Pranswer;
+ case PeerConnectionStates::SignalingState::HaveRemoteOffer:
+ return type == RTCSessionDescription::SdpType::Offer;
+ case PeerConnectionStates::SignalingState::HaveRemotePrAnswer:
+ return type == RTCSessionDescription::SdpType::Answer || type == RTCSessionDescription::SdpType::Pranswer;
+ default:
+ return false;
+ };
+
+ ASSERT_NOT_REACHED();
+ return false;
+}
+
+void PeerConnectionBackend::setRemoteDescription(RTCSessionDescription& sessionDescription, DOMPromise<void>&& promise)
+{
+ ASSERT(m_peerConnection.internalSignalingState() != PeerConnectionStates::SignalingState::Closed);
+
+ if (!isRemoteDescriptionTypeValidForState(sessionDescription.type(), m_peerConnection.internalSignalingState())) {
+ promise.reject(INVALID_STATE_ERR, "Description type incompatible with current signaling state");
+ return;
+ }
+
+ m_setDescriptionPromise = WTFMove(promise);
+ doSetRemoteDescription(sessionDescription);
+}
+
+void PeerConnectionBackend::setRemoteDescriptionSucceeded()
+{
+ ASSERT(isMainThread());
+
+ if (m_peerConnection.internalSignalingState() == PeerConnectionStates::SignalingState::Closed)
+ return;
+
+ ASSERT(m_setDescriptionPromise);
+
+ m_setDescriptionPromise->resolve();
+ m_setDescriptionPromise = std::nullopt;
+}
+
+void PeerConnectionBackend::setRemoteDescriptionFailed(Exception&& exception)
+{
+ ASSERT(isMainThread());
+
+ if (m_peerConnection.internalSignalingState() == PeerConnectionStates::SignalingState::Closed)
+ return;
+
+ ASSERT(m_setDescriptionPromise);
+
+ m_setDescriptionPromise->reject(WTFMove(exception));
+ m_setDescriptionPromise = std::nullopt;
+}
+
+void PeerConnectionBackend::addIceCandidate(RTCIceCandidate& iceCandidate, DOMPromise<void>&& promise)
+{
+ ASSERT(m_peerConnection.internalSignalingState() != PeerConnectionStates::SignalingState::Closed);
+
+ if (iceCandidate.sdpMid().isNull() && !iceCandidate.sdpMLineIndex()) {
+ promise.reject(Exception { TypeError, ASCIILiteral("Trying to add a candidate that is missing both sdpMid and sdpMLineIndex") });
+ return;
+ }
+ m_addIceCandidatePromise = WTFMove(promise);
+ doAddIceCandidate(iceCandidate);
+}
+
+void PeerConnectionBackend::addIceCandidateSucceeded()
+{
+ ASSERT(isMainThread());
+
+ if (m_peerConnection.internalSignalingState() == PeerConnectionStates::SignalingState::Closed)
+ return;
+
+ // FIXME: Update remote description and set ICE connection state to checking if not already done so.
+ ASSERT(m_addIceCandidatePromise);
+
+ m_addIceCandidatePromise->resolve();
+ m_addIceCandidatePromise = std::nullopt;
+}
+
+void PeerConnectionBackend::addIceCandidateFailed(Exception&& exception)
+{
+ ASSERT(isMainThread());
+ if (m_peerConnection.internalSignalingState() == PeerConnectionStates::SignalingState::Closed)
+ return;
+
+ ASSERT(m_addIceCandidatePromise);
+
+ m_addIceCandidatePromise->reject(WTFMove(exception));
+ m_addIceCandidatePromise = std::nullopt;
+}
+
+void PeerConnectionBackend::fireICECandidateEvent(RefPtr<RTCIceCandidate>&& candidate)
+{
+ ASSERT(isMainThread());
+
+ m_peerConnection.fireEvent(RTCIceCandidateEvent::create(false, false, WTFMove(candidate)));
+}
+
+void PeerConnectionBackend::doneGatheringCandidates()
+{
+ ASSERT(isMainThread());
+
+ m_peerConnection.fireEvent(RTCIceCandidateEvent::create(false, false, nullptr));
+ m_peerConnection.updateIceGatheringState(PeerConnectionStates::IceGatheringState::Complete);
+}
+
+void PeerConnectionBackend::updateSignalingState(PeerConnectionStates::SignalingState newSignalingState)
+{
+ ASSERT(isMainThread());
+
+ if (newSignalingState != m_peerConnection.internalSignalingState()) {
+ m_peerConnection.setSignalingState(newSignalingState);
+ m_peerConnection.fireEvent(Event::create(eventNames().signalingstatechangeEvent, false, false));
+ }
+}
+
+void PeerConnectionBackend::stop()
+{
+ m_offerAnswerPromise = std::nullopt;
+ m_setDescriptionPromise = std::nullopt;
+ m_addIceCandidatePromise = std::nullopt;
+
+ doStop();
+}
+
+void PeerConnectionBackend::markAsNeedingNegotiation()
+{
+ if (m_negotiationNeeded)
+ return;
+
+ m_negotiationNeeded = true;
+
+ if (m_peerConnection.internalSignalingState() == PeerConnectionStates::SignalingState::Stable)
+ m_peerConnection.scheduleNegotiationNeededEvent();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/PeerConnectionBackend.h b/Source/WebCore/Modules/mediastream/PeerConnectionBackend.h
new file mode 100644
index 000000000..5df7db454
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/PeerConnectionBackend.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2015 Ericsson AB. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Ericsson 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 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
+ * OWNER 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(WEB_RTC)
+
+#include "JSDOMPromise.h"
+#include "PeerConnectionStates.h"
+
+namespace WebCore {
+
+class MediaStream;
+class MediaStreamTrack;
+class PeerConnectionBackend;
+class RTCDataChannelHandler;
+class RTCIceCandidate;
+class RTCPeerConnection;
+class RTCRtpReceiver;
+class RTCRtpSender;
+class RTCSessionDescription;
+class RTCStatsReport;
+
+struct MediaEndpointConfiguration;
+struct RTCAnswerOptions;
+struct RTCDataChannelInit;
+struct RTCOfferOptions;
+
+namespace PeerConnection {
+using SessionDescriptionPromise = DOMPromise<IDLInterface<RTCSessionDescription>>;
+using StatsPromise = DOMPromise<IDLInterface<RTCStatsReport>>;
+}
+
+using CreatePeerConnectionBackend = std::unique_ptr<PeerConnectionBackend> (*)(RTCPeerConnection&);
+
+// FIXME: What is the value of this abstract class? There is only one concrete class derived from it.
+class PeerConnectionBackend {
+public:
+ WEBCORE_EXPORT static CreatePeerConnectionBackend create;
+
+ PeerConnectionBackend(RTCPeerConnection& peerConnection) : m_peerConnection(peerConnection) { }
+ virtual ~PeerConnectionBackend() { }
+
+ void createOffer(RTCOfferOptions&&, PeerConnection::SessionDescriptionPromise&&);
+ void createAnswer(RTCAnswerOptions&&, PeerConnection::SessionDescriptionPromise&&);
+ void setLocalDescription(RTCSessionDescription&, DOMPromise<void>&&);
+ void setRemoteDescription(RTCSessionDescription&, DOMPromise<void>&&);
+ void addIceCandidate(RTCIceCandidate&, DOMPromise<void>&&);
+
+ virtual std::unique_ptr<RTCDataChannelHandler> createDataChannelHandler(const String&, const RTCDataChannelInit&) = 0;
+
+ void stop();
+
+ virtual RefPtr<RTCSessionDescription> localDescription() const = 0;
+ virtual RefPtr<RTCSessionDescription> currentLocalDescription() const = 0;
+ virtual RefPtr<RTCSessionDescription> pendingLocalDescription() const = 0;
+
+ virtual RefPtr<RTCSessionDescription> remoteDescription() const = 0;
+ virtual RefPtr<RTCSessionDescription> currentRemoteDescription() const = 0;
+ virtual RefPtr<RTCSessionDescription> pendingRemoteDescription() const = 0;
+
+ virtual void setConfiguration(MediaEndpointConfiguration&&) = 0;
+
+ virtual void getStats(MediaStreamTrack*, Ref<DeferredPromise>&&) = 0;
+
+ virtual Vector<RefPtr<MediaStream>> getRemoteStreams() const = 0;
+
+ virtual Ref<RTCRtpReceiver> createReceiver(const String& transceiverMid, const String& trackKind, const String& trackId) = 0;
+ virtual void replaceTrack(RTCRtpSender&, RefPtr<MediaStreamTrack>&&, DOMPromise<void>&&) = 0;
+
+ void markAsNeedingNegotiation();
+ bool isNegotiationNeeded() const { return m_negotiationNeeded; };
+ void clearNegotiationNeededState() { m_negotiationNeeded = false; };
+
+ virtual void emulatePlatformEvent(const String& action) = 0;
+
+protected:
+ void fireICECandidateEvent(RefPtr<RTCIceCandidate>&&);
+ void doneGatheringCandidates();
+
+ void updateSignalingState(PeerConnectionStates::SignalingState);
+
+ void createOfferSucceeded(String&&);
+ void createOfferFailed(Exception&&);
+
+ void createAnswerSucceeded(String&&);
+ void createAnswerFailed(Exception&&);
+
+ void setLocalDescriptionSucceeded();
+ void setLocalDescriptionFailed(Exception&&);
+
+ void setRemoteDescriptionSucceeded();
+ void setRemoteDescriptionFailed(Exception&&);
+
+ void addIceCandidateSucceeded();
+ void addIceCandidateFailed(Exception&&);
+
+private:
+ virtual void doCreateOffer(RTCOfferOptions&&) = 0;
+ virtual void doCreateAnswer(RTCAnswerOptions&&) = 0;
+ virtual void doSetLocalDescription(RTCSessionDescription&) = 0;
+ virtual void doSetRemoteDescription(RTCSessionDescription&) = 0;
+ virtual void doAddIceCandidate(RTCIceCandidate&) = 0;
+ virtual void doStop() = 0;
+
+protected:
+ RTCPeerConnection& m_peerConnection;
+
+private:
+ std::optional<PeerConnection::SessionDescriptionPromise> m_offerAnswerPromise;
+ std::optional<DOMPromise<void>> m_setDescriptionPromise;
+ std::optional<DOMPromise<void>> m_addIceCandidatePromise;
+
+ bool m_negotiationNeeded { false };
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCSessionDescriptionCallback.h b/Source/WebCore/Modules/mediastream/RTCConfiguration.h
index 06ee071c4..2f06ea25a 100644
--- a/Source/WebCore/Modules/mediastream/RTCSessionDescriptionCallback.h
+++ b/Source/WebCore/Modules/mediastream/RTCConfiguration.h
@@ -28,25 +28,27 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef RTCSessionDescriptionCallback_h
-#define RTCSessionDescriptionCallback_h
+#pragma once
-#if ENABLE(MEDIA_STREAM)
+#if ENABLE(WEB_RTC)
-#include <wtf/RefCounted.h>
+#include "PeerConnectionStates.h"
+#include "RTCIceServer.h"
namespace WebCore {
-class RTCSessionDescription;
+using RTCIceTransportPolicy = PeerConnectionStates::IceTransportPolicy;
+using RTCBundlePolicy = PeerConnectionStates::BundlePolicy;
-class RTCSessionDescriptionCallback : public RefCounted<RTCSessionDescriptionCallback> {
-public:
- virtual ~RTCSessionDescriptionCallback() { }
- virtual bool handleEvent(RTCSessionDescription*) = 0;
+struct RTCConfiguration {
+ using IceTransportPolicy = RTCIceTransportPolicy;
+ using BundlePolicy = RTCBundlePolicy;
+
+ std::optional<Vector<RTCIceServer>> iceServers;
+ IceTransportPolicy iceTransportPolicy;
+ BundlePolicy bundlePolicy;
};
} // namespace WebCore
-#endif // ENABLE(MEDIA_STREAM)
-
-#endif // RTCSessionDescriptionCallback_h
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCConfiguration.idl b/Source/WebCore/Modules/mediastream/RTCConfiguration.idl
new file mode 100644
index 000000000..3d2664dbc
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/RTCConfiguration.idl
@@ -0,0 +1,36 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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 RTCIceTransportPolicy { "relay", "all" };
+enum RTCBundlePolicy { "balanced", "max-compat", "max-bundle" };
+
+[
+ Conditional=WEB_RTC,
+ JSGenerateToJSObject,
+] dictionary RTCConfiguration {
+ sequence<RTCIceServer> iceServers;
+ RTCIceTransportPolicy iceTransportPolicy = "all";
+ RTCBundlePolicy bundlePolicy = "balanced";
+};
diff --git a/Source/WebCore/Modules/mediastream/RTCDTMFSender.cpp b/Source/WebCore/Modules/mediastream/RTCDTMFSender.cpp
index 3167d9275..08eae7b70 100644
--- a/Source/WebCore/Modules/mediastream/RTCDTMFSender.cpp
+++ b/Source/WebCore/Modules/mediastream/RTCDTMFSender.cpp
@@ -24,50 +24,31 @@
*/
#include "config.h"
-
-#if ENABLE(MEDIA_STREAM)
-
#include "RTCDTMFSender.h"
-#include "ExceptionCode.h"
+#if ENABLE(WEB_RTC)
+
#include "MediaStreamTrack.h"
#include "RTCDTMFSenderHandler.h"
#include "RTCDTMFToneChangeEvent.h"
-#include "RTCPeerConnectionHandler.h"
#include "ScriptExecutionContext.h"
namespace WebCore {
-static const long minToneDurationMs = 70;
+static const long minToneDurationMs = 40;
static const long defaultToneDurationMs = 100;
static const long maxToneDurationMs = 6000;
-static const long minInterToneGapMs = 50;
-static const long defaultInterToneGapMs = 50;
-
-PassRefPtr<RTCDTMFSender> RTCDTMFSender::create(ScriptExecutionContext* context, RTCPeerConnectionHandler* peerConnectionHandler, PassRefPtr<MediaStreamTrack> prpTrack, ExceptionCode& ec)
-{
- RefPtr<MediaStreamTrack> track = prpTrack;
- std::unique_ptr<RTCDTMFSenderHandler> handler = peerConnectionHandler->createDTMFSender(track->source());
- if (!handler) {
- ec = NOT_SUPPORTED_ERR;
- return nullptr;
- }
-
- RefPtr<RTCDTMFSender> dtmfSender = adoptRef(new RTCDTMFSender(context, track, std::move(handler)));
- dtmfSender->suspendIfNeeded();
- return dtmfSender.release();
-}
+static const long minInterToneGapMs = 30;
+static const long defaultInterToneGapMs = 70;
-RTCDTMFSender::RTCDTMFSender(ScriptExecutionContext* context, PassRefPtr<MediaStreamTrack> track, std::unique_ptr<RTCDTMFSenderHandler> handler)
- : ActiveDOMObject(context)
- , m_track(track)
+RTCDTMFSender::RTCDTMFSender(ScriptExecutionContext& context, RefPtr<MediaStreamTrack>&& track)
+ : ActiveDOMObject(&context)
+ , m_track(WTFMove(track))
, m_duration(defaultToneDurationMs)
, m_interToneGap(defaultInterToneGapMs)
- , m_handler(std::move(handler))
, m_stopped(false)
- , m_scheduledEventTimer(this, &RTCDTMFSender::scheduledEventTimerFired)
+ , m_scheduledEventTimer(*this, &RTCDTMFSender::scheduledEventTimerFired)
{
- m_handler->setClient(this);
}
RTCDTMFSender::~RTCDTMFSender()
@@ -76,7 +57,7 @@ RTCDTMFSender::~RTCDTMFSender()
bool RTCDTMFSender::canInsertDTMF() const
{
- return m_handler->canInsertDTMF();
+ return false;
}
MediaStreamTrack* RTCDTMFSender::track() const
@@ -86,41 +67,24 @@ MediaStreamTrack* RTCDTMFSender::track() const
String RTCDTMFSender::toneBuffer() const
{
- return m_handler->currentToneBuffer();
+ return { };
}
-void RTCDTMFSender::insertDTMF(const String& tones, ExceptionCode& ec)
+ExceptionOr<void> RTCDTMFSender::insertDTMF(const String&, std::optional<int> duration, std::optional<int> interToneGap)
{
- insertDTMF(tones, defaultToneDurationMs, defaultInterToneGapMs, ec);
-}
+ if (!canInsertDTMF())
+ return Exception { NOT_SUPPORTED_ERR };
-void RTCDTMFSender::insertDTMF(const String& tones, long duration, ExceptionCode& ec)
-{
- insertDTMF(tones, duration, defaultInterToneGapMs, ec);
-}
+ if (duration && (duration.value() > maxToneDurationMs || duration.value() < minToneDurationMs))
+ return Exception { SYNTAX_ERR };
-void RTCDTMFSender::insertDTMF(const String& tones, long duration, long interToneGap, ExceptionCode& ec)
-{
- if (!canInsertDTMF()) {
- ec = NOT_SUPPORTED_ERR;
- return;
- }
+ if (interToneGap && interToneGap.value() < minInterToneGapMs)
+ return Exception { SYNTAX_ERR };
- if (duration > maxToneDurationMs || duration < minToneDurationMs) {
- ec = SYNTAX_ERR;
- return;
- }
-
- if (interToneGap < minInterToneGapMs) {
- ec = SYNTAX_ERR;
- return;
- }
+ m_duration = duration.value_or(defaultToneDurationMs);
+ m_interToneGap = interToneGap.value_or(defaultInterToneGapMs);
- m_duration = duration;
- m_interToneGap = interToneGap;
-
- if (!m_handler->insertDTMF(tones, m_duration, m_interToneGap))
- ec = SYNTAX_ERR;
+ return Exception { SYNTAX_ERR };
}
void RTCDTMFSender::didPlayTone(const String& tone)
@@ -131,30 +95,39 @@ void RTCDTMFSender::didPlayTone(const String& tone)
void RTCDTMFSender::stop()
{
m_stopped = true;
- m_handler->setClient(0);
}
-void RTCDTMFSender::scheduleDispatchEvent(PassRefPtr<Event> event)
+const char* RTCDTMFSender::activeDOMObjectName() const
+{
+ return "RTCDTMFSender";
+}
+
+bool RTCDTMFSender::canSuspendForDocumentSuspension() const
+{
+ // FIXME: We should try and do better here.
+ return false;
+}
+
+void RTCDTMFSender::scheduleDispatchEvent(Ref<Event>&& event)
{
- m_scheduledEvents.append(event);
+ m_scheduledEvents.append(WTFMove(event));
if (!m_scheduledEventTimer.isActive())
m_scheduledEventTimer.startOneShot(0);
}
-void RTCDTMFSender::scheduledEventTimerFired(Timer<RTCDTMFSender>*)
+void RTCDTMFSender::scheduledEventTimerFired()
{
if (m_stopped)
return;
- Vector<RefPtr<Event>> events;
+ Vector<Ref<Event>> events;
events.swap(m_scheduledEvents);
- Vector<RefPtr<Event>>::iterator it = events.begin();
- for (; it != events.end(); ++it)
- dispatchEvent((*it).release());
+ for (auto& event : events)
+ dispatchEvent(event);
}
} // namespace WebCore
-#endif // ENABLE(MEDIA_STREAM)
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCDTMFSender.h b/Source/WebCore/Modules/mediastream/RTCDTMFSender.h
index 0e6b49d32..c67b3a164 100644
--- a/Source/WebCore/Modules/mediastream/RTCDTMFSender.h
+++ b/Source/WebCore/Modules/mediastream/RTCDTMFSender.h
@@ -23,78 +23,63 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef RTCDTMFSender_h
-#define RTCDTMFSender_h
+#pragma once
-#if ENABLE(MEDIA_STREAM)
+#if ENABLE(WEB_RTC)
#include "ActiveDOMObject.h"
#include "EventTarget.h"
-#include "RTCDTMFSenderHandlerClient.h"
+#include "ExceptionOr.h"
#include "ScriptWrappable.h"
#include "Timer.h"
-#include <wtf/RefCounted.h>
namespace WebCore {
class MediaStreamTrack;
-class RTCPeerConnectionHandler;
-class RTCDTMFSenderHandler;
-class RTCDTMFSender final : public RefCounted<RTCDTMFSender>, public ScriptWrappable, public EventTargetWithInlineData, public RTCDTMFSenderHandlerClient, public ActiveDOMObject {
+class RTCDTMFSender final : public RefCounted<RTCDTMFSender>, public EventTargetWithInlineData, public ActiveDOMObject {
public:
- static PassRefPtr<RTCDTMFSender> create(ScriptExecutionContext*, RTCPeerConnectionHandler*, PassRefPtr<MediaStreamTrack>, ExceptionCode&);
- ~RTCDTMFSender();
+ virtual ~RTCDTMFSender();
bool canInsertDTMF() const;
MediaStreamTrack* track() const;
String toneBuffer() const;
- long duration() const { return m_duration; }
- long interToneGap() const { return m_interToneGap; }
+ int duration() const { return m_duration; }
+ int interToneGap() const { return m_interToneGap; }
- void insertDTMF(const String& tones, ExceptionCode&);
- void insertDTMF(const String& tones, long duration, ExceptionCode&);
- void insertDTMF(const String& tones, long duration, long interToneGap, ExceptionCode&);
+ ExceptionOr<void> insertDTMF(const String& tones, std::optional<int> duration, std::optional<int> interToneGap);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(tonechange);
+ using RefCounted::ref;
+ using RefCounted::deref;
- // EventTarget
- virtual EventTargetInterface eventTargetInterface() const override { return RTCDTMFSenderEventTargetInterfaceType; }
- virtual ScriptExecutionContext* scriptExecutionContext() const override { return ActiveDOMObject::scriptExecutionContext(); }
-
- // ActiveDOMObject
- virtual void stop() override;
+private:
+ RTCDTMFSender(ScriptExecutionContext&, RefPtr<MediaStreamTrack>&&);
- using RefCounted<RTCDTMFSender>::ref;
- using RefCounted<RTCDTMFSender>::deref;
+ void stop() final;
+ const char* activeDOMObjectName() const final;
+ bool canSuspendForDocumentSuspension() const final;
-private:
- RTCDTMFSender(ScriptExecutionContext*, PassRefPtr<MediaStreamTrack>, std::unique_ptr<RTCDTMFSenderHandler>);
+ EventTargetInterface eventTargetInterface() const final { return RTCDTMFSenderEventTargetInterfaceType; }
+ ScriptExecutionContext* scriptExecutionContext() const final { return ActiveDOMObject::scriptExecutionContext(); }
- void scheduleDispatchEvent(PassRefPtr<Event>);
- void scheduledEventTimerFired(Timer<RTCDTMFSender>*);
+ void refEventTarget() final { ref(); }
+ void derefEventTarget() final { deref(); }
- // EventTarget
- virtual void refEventTarget() override { ref(); }
- virtual void derefEventTarget() override { deref(); }
+ void didPlayTone(const String&);
- // RTCDTMFSenderHandlerClient
- virtual void didPlayTone(const String&) override;
+ void scheduleDispatchEvent(Ref<Event>&&);
+ void scheduledEventTimerFired();
RefPtr<MediaStreamTrack> m_track;
- long m_duration;
- long m_interToneGap;
-
- std::unique_ptr<RTCDTMFSenderHandler> m_handler;
+ int m_duration;
+ int m_interToneGap;
bool m_stopped;
- Timer<RTCDTMFSender> m_scheduledEventTimer;
- Vector<RefPtr<Event>> m_scheduledEvents;
+ Timer m_scheduledEventTimer;
+ Vector<Ref<Event>> m_scheduledEvents;
};
} // namespace WebCore
-#endif // ENABLE(MEDIA_STREAM)
-
-#endif // RTCDTMFSender_h
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCDTMFSender.idl b/Source/WebCore/Modules/mediastream/RTCDTMFSender.idl
index f4fb77158..fea23bda9 100644
--- a/Source/WebCore/Modules/mediastream/RTCDTMFSender.idl
+++ b/Source/WebCore/Modules/mediastream/RTCDTMFSender.idl
@@ -24,27 +24,17 @@
*/
[
- NoInterfaceObject,
- Conditional=MEDIA_STREAM,
ActiveDOMObject,
- EventTarget,
-] interface RTCDTMFSender {
+ Conditional=WEB_RTC,
+ NoInterfaceObject,
+] interface RTCDTMFSender : EventTarget {
readonly attribute boolean canInsertDTMF;
readonly attribute MediaStreamTrack track;
readonly attribute DOMString toneBuffer;
readonly attribute long duration;
readonly attribute long interToneGap;
- [RaisesException] void insertDTMF(DOMString tones, optional long duration, optional long interToneGap);
-
- attribute EventListener ontonechange;
+ [MayThrowException] void insertDTMF(DOMString tones, optional long duration, optional long interToneGap);
- // 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);
+ attribute EventHandler ontonechange;
};
diff --git a/Source/WebCore/Modules/mediastream/RTCDTMFToneChangeEvent.cpp b/Source/WebCore/Modules/mediastream/RTCDTMFToneChangeEvent.cpp
index 526ef2c6b..d6ff9fda8 100644
--- a/Source/WebCore/Modules/mediastream/RTCDTMFToneChangeEvent.cpp
+++ b/Source/WebCore/Modules/mediastream/RTCDTMFToneChangeEvent.cpp
@@ -26,30 +26,20 @@
#include "config.h"
#include "RTCDTMFToneChangeEvent.h"
-#if ENABLE(MEDIA_STREAM)
+#if ENABLE(WEB_RTC)
#include "EventNames.h"
namespace WebCore {
-PassRefPtr<RTCDTMFToneChangeEvent> RTCDTMFToneChangeEvent::create()
+Ref<RTCDTMFToneChangeEvent> RTCDTMFToneChangeEvent::create(const String& tone)
{
- return adoptRef(new RTCDTMFToneChangeEvent);
+ return adoptRef(*new RTCDTMFToneChangeEvent(tone));
}
-PassRefPtr<RTCDTMFToneChangeEvent> RTCDTMFToneChangeEvent::create(const String& tone)
-{
- return adoptRef(new RTCDTMFToneChangeEvent(tone));
-}
-
-PassRefPtr<RTCDTMFToneChangeEvent> RTCDTMFToneChangeEvent::create(const AtomicString& type, const RTCDTMFToneChangeEventInit& initializer)
-{
- ASSERT_UNUSED(type, type == eventNames().tonechangeEvent);
- return adoptRef(new RTCDTMFToneChangeEvent(initializer));
-}
-
-RTCDTMFToneChangeEvent::RTCDTMFToneChangeEvent()
+Ref<RTCDTMFToneChangeEvent> RTCDTMFToneChangeEvent::create(const AtomicString& type, const Init& initializer, IsTrusted isTrusted)
{
+ return adoptRef(*new RTCDTMFToneChangeEvent(type, initializer, isTrusted));
}
RTCDTMFToneChangeEvent::RTCDTMFToneChangeEvent(const String& tone)
@@ -58,8 +48,8 @@ RTCDTMFToneChangeEvent::RTCDTMFToneChangeEvent(const String& tone)
{
}
-RTCDTMFToneChangeEvent::RTCDTMFToneChangeEvent(const RTCDTMFToneChangeEventInit& initializer)
- : Event(eventNames().tonechangeEvent, initializer)
+RTCDTMFToneChangeEvent::RTCDTMFToneChangeEvent(const AtomicString& type, const Init& initializer, IsTrusted isTrusted)
+ : Event(type, initializer, isTrusted)
, m_tone(initializer.tone)
{
}
@@ -80,5 +70,5 @@ EventInterface RTCDTMFToneChangeEvent::eventInterface() const
} // namespace WebCore
-#endif // ENABLE(MEDIA_STREAM)
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCDTMFToneChangeEvent.h b/Source/WebCore/Modules/mediastream/RTCDTMFToneChangeEvent.h
index 0693db694..112d529a5 100644
--- a/Source/WebCore/Modules/mediastream/RTCDTMFToneChangeEvent.h
+++ b/Source/WebCore/Modules/mediastream/RTCDTMFToneChangeEvent.h
@@ -23,42 +23,38 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef RTCDTMFToneChangeEvent_h
-#define RTCDTMFToneChangeEvent_h
+#pragma once
-#if ENABLE(MEDIA_STREAM)
+#if ENABLE(WEB_RTC)
#include "Event.h"
#include <wtf/text/AtomicString.h>
namespace WebCore {
-struct RTCDTMFToneChangeEventInit : public EventInit {
- String tone;
-};
-
class RTCDTMFToneChangeEvent : public Event {
public:
virtual ~RTCDTMFToneChangeEvent();
- static PassRefPtr<RTCDTMFToneChangeEvent> create();
- static PassRefPtr<RTCDTMFToneChangeEvent> create(const String& tone);
- static PassRefPtr<RTCDTMFToneChangeEvent> create(const AtomicString& type, const RTCDTMFToneChangeEventInit& initializer);
+ static Ref<RTCDTMFToneChangeEvent> create(const String& tone);
+
+ struct Init : EventInit {
+ String tone;
+ };
+
+ static Ref<RTCDTMFToneChangeEvent> create(const AtomicString& type, const Init&, IsTrusted = IsTrusted::No);
const String& tone() const;
virtual EventInterface eventInterface() const;
private:
- RTCDTMFToneChangeEvent();
explicit RTCDTMFToneChangeEvent(const String& tone);
- explicit RTCDTMFToneChangeEvent(const RTCDTMFToneChangeEventInit&);
+ RTCDTMFToneChangeEvent(const AtomicString& type, const Init&, IsTrusted);
String m_tone;
};
} // namespace WebCore
-#endif // ENABLE(MEDIA_STREAM)
-
-#endif // RTCDTMFToneChangeEvent_h
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCDTMFToneChangeEvent.idl b/Source/WebCore/Modules/mediastream/RTCDTMFToneChangeEvent.idl
index 1edebd33e..722df3cf7 100644
--- a/Source/WebCore/Modules/mediastream/RTCDTMFToneChangeEvent.idl
+++ b/Source/WebCore/Modules/mediastream/RTCDTMFToneChangeEvent.idl
@@ -24,9 +24,13 @@
*/
[
- NoInterfaceObject,
- Conditional=MEDIA_STREAM,
- ConstructorTemplate=Event
+ Conditional=WEB_RTC,
+ Constructor(DOMString type, optional RTCDTMFToneChangeEventInit eventInitDict),
+ EnabledAtRuntime=PeerConnection,
] interface RTCDTMFToneChangeEvent : Event {
- [InitializedByEventConstructor] readonly attribute DOMString tone;
+ readonly attribute DOMString tone;
+};
+
+dictionary RTCDTMFToneChangeEventInit : EventInit {
+ DOMString tone = "";
};
diff --git a/Source/WebCore/Modules/mediastream/RTCDataChannel.cpp b/Source/WebCore/Modules/mediastream/RTCDataChannel.cpp
index 4e876bb7e..76723e4ee 100644
--- a/Source/WebCore/Modules/mediastream/RTCDataChannel.cpp
+++ b/Source/WebCore/Modules/mediastream/RTCDataChannel.cpp
@@ -23,18 +23,16 @@
*/
#include "config.h"
-
-#if ENABLE(MEDIA_STREAM)
-
#include "RTCDataChannel.h"
+#if ENABLE(WEB_RTC)
+
#include "Blob.h"
-#include "Dictionary.h"
#include "Event.h"
+#include "EventNames.h"
#include "ExceptionCode.h"
#include "MessageEvent.h"
#include "RTCDataChannelHandler.h"
-#include "RTCPeerConnectionHandler.h"
#include "ScriptExecutionContext.h"
#include <runtime/ArrayBuffer.h>
#include <runtime/ArrayBufferView.h>
@@ -54,87 +52,30 @@ static const AtomicString& arraybufferKeyword()
return arraybuffer;
}
-PassRefPtr<RTCDataChannel> RTCDataChannel::create(ScriptExecutionContext* context, RTCPeerConnectionHandler* peerConnectionHandler, const String& label, const Dictionary& options, ExceptionCode& ec)
-{
- RTCDataChannelInit initData;
- options.get("ordered", initData.ordered);
- options.get("negotiated", initData.negotiated);
- options.get("id", initData.id);
- options.get("maxRetransmits", initData.maxRetransmits);
- options.get("maxRetransmitTime", initData.maxRetransmitTime);
- options.get("protocol", initData.protocol);
-
- std::unique_ptr<RTCDataChannelHandler> handler = peerConnectionHandler->createDataChannel(label, initData);
- if (!handler) {
- ec = NOT_SUPPORTED_ERR;
- return nullptr;
- }
- return adoptRef(new RTCDataChannel(context, std::move(handler)));
-}
-
-PassRefPtr<RTCDataChannel> RTCDataChannel::create(ScriptExecutionContext* context, std::unique_ptr<RTCDataChannelHandler> handler)
+Ref<RTCDataChannel> RTCDataChannel::create(ScriptExecutionContext& context, std::unique_ptr<RTCDataChannelHandler>&& handler, String&& label, RTCDataChannelInit&& options)
{
ASSERT(handler);
- return adoptRef(new RTCDataChannel(context, std::move(handler)));
-}
-
-RTCDataChannel::RTCDataChannel(ScriptExecutionContext* context, std::unique_ptr<RTCDataChannelHandler> handler)
- : m_scriptExecutionContext(context)
- , m_handler(std::move(handler))
- , m_stopped(false)
- , m_readyState(ReadyStateConnecting)
- , m_binaryType(BinaryTypeArrayBuffer)
- , m_scheduledEventTimer(this, &RTCDataChannel::scheduledEventTimerFired)
-{
- m_handler->setClient(this);
-}
-
-RTCDataChannel::~RTCDataChannel()
-{
-}
-
-String RTCDataChannel::label() const
-{
- return m_handler->label();
-}
-
-bool RTCDataChannel::ordered() const
-{
- return m_handler->ordered();
-}
-
-unsigned short RTCDataChannel::maxRetransmitTime() const
-{
- return m_handler->maxRetransmitTime();
-}
-
-unsigned short RTCDataChannel::maxRetransmits() const
-{
-return m_handler->maxRetransmits();
-}
-
-String RTCDataChannel::protocol() const
-{
- return m_handler->protocol();
-}
-
-bool RTCDataChannel::negotiated() const
-{
- return m_handler->negotiated();
+ auto channel = adoptRef(*new RTCDataChannel(context, WTFMove(handler), WTFMove(label), WTFMove(options)));
+ channel->m_handler->setClient(&channel.get());
+ return channel;
}
-unsigned short RTCDataChannel::id() const
+RTCDataChannel::RTCDataChannel(ScriptExecutionContext& context, std::unique_ptr<RTCDataChannelHandler>&& handler, String&& label, RTCDataChannelInit&& options)
+ : m_scriptExecutionContext(&context)
+ , m_handler(WTFMove(handler))
+ , m_scheduledEventTimer(*this, &RTCDataChannel::scheduledEventTimerFired)
+ , m_label(WTFMove(label))
+ , m_options(WTFMove(options))
{
- return m_handler->id();
}
-AtomicString RTCDataChannel::readyState() const
+const AtomicString& RTCDataChannel::readyState() const
{
static NeverDestroyed<AtomicString> connectingState("connecting", AtomicString::ConstructFromLiteral);
static NeverDestroyed<AtomicString> openState("open", AtomicString::ConstructFromLiteral);
static NeverDestroyed<AtomicString> closingState("closing", AtomicString::ConstructFromLiteral);
static NeverDestroyed<AtomicString> closedState("closed", AtomicString::ConstructFromLiteral);
-
+
switch (m_readyState) {
case ReadyStateConnecting:
return connectingState;
@@ -150,17 +91,17 @@ AtomicString RTCDataChannel::readyState() const
return emptyAtom;
}
-unsigned long RTCDataChannel::bufferedAmount() const
+size_t RTCDataChannel::bufferedAmount() const
{
return m_handler->bufferedAmount();
}
-AtomicString RTCDataChannel::binaryType() const
+const AtomicString& RTCDataChannel::binaryType() const
{
switch (m_binaryType) {
- case BinaryTypeBlob:
+ case BinaryType::Blob:
return blobKeyword();
- case BinaryTypeArrayBuffer:
+ case BinaryType::ArrayBuffer:
return arraybufferKeyword();
}
@@ -168,60 +109,58 @@ AtomicString RTCDataChannel::binaryType() const
return emptyAtom;
}
-void RTCDataChannel::setBinaryType(const AtomicString& binaryType, ExceptionCode& ec)
+ExceptionOr<void> RTCDataChannel::setBinaryType(const AtomicString& binaryType)
{
if (binaryType == blobKeyword())
- ec = NOT_SUPPORTED_ERR;
- else if (binaryType == arraybufferKeyword())
- m_binaryType = BinaryTypeArrayBuffer;
- else
- ec = TYPE_MISMATCH_ERR;
+ return Exception { NOT_SUPPORTED_ERR };
+ if (binaryType == arraybufferKeyword()) {
+ m_binaryType = BinaryType::ArrayBuffer;
+ return { };
+ }
+ return Exception { TYPE_MISMATCH_ERR };
}
-void RTCDataChannel::send(const String& data, ExceptionCode& ec)
+ExceptionOr<void> RTCDataChannel::send(const String& data)
{
- if (m_readyState != ReadyStateOpen) {
- ec = INVALID_STATE_ERR;
- return;
- }
+ if (m_readyState != ReadyStateOpen)
+ return Exception { INVALID_STATE_ERR };
if (!m_handler->sendStringData(data)) {
// FIXME: Decide what the right exception here is.
- ec = SYNTAX_ERR;
+ return Exception { SYNTAX_ERR };
}
+
+ return { };
}
-void RTCDataChannel::send(PassRefPtr<ArrayBuffer> prpData, ExceptionCode& ec)
+ExceptionOr<void> RTCDataChannel::send(ArrayBuffer& data)
{
- if (m_readyState != ReadyStateOpen) {
- ec = INVALID_STATE_ERR;
- return;
- }
-
- RefPtr<ArrayBuffer> data = prpData;
+ if (m_readyState != ReadyStateOpen)
+ return Exception { INVALID_STATE_ERR };
- size_t dataLength = data->byteLength();
+ size_t dataLength = data.byteLength();
if (!dataLength)
- return;
+ return { };
- const char* dataPointer = static_cast<const char*>(data->data());
+ const char* dataPointer = static_cast<const char*>(data.data());
if (!m_handler->sendRawData(dataPointer, dataLength)) {
// FIXME: Decide what the right exception here is.
- ec = SYNTAX_ERR;
+ return Exception { SYNTAX_ERR };
}
+
+ return { };
}
-void RTCDataChannel::send(PassRefPtr<ArrayBufferView> data, ExceptionCode& ec)
+ExceptionOr<void> RTCDataChannel::send(ArrayBufferView& data)
{
- RefPtr<ArrayBuffer> arrayBuffer(data->buffer());
- send(arrayBuffer.release(), ec);
+ return send(*data.unsharedBuffer());
}
-void RTCDataChannel::send(PassRefPtr<Blob>, ExceptionCode& ec)
+ExceptionOr<void> RTCDataChannel::send(Blob&)
{
- // FIXME: implement
- ec = NOT_SUPPORTED_ERR;
+ // FIXME: Implement.
+ return Exception { NOT_SUPPORTED_ERR };
}
void RTCDataChannel::close()
@@ -264,14 +203,13 @@ void RTCDataChannel::didReceiveRawData(const char* data, size_t dataLength)
if (m_stopped)
return;
- if (m_binaryType == BinaryTypeBlob) {
+ if (m_binaryType == BinaryType::Blob) {
// FIXME: Implement.
return;
}
- if (m_binaryType == BinaryTypeArrayBuffer) {
- RefPtr<ArrayBuffer> buffer = ArrayBuffer::create(data, dataLength);
- scheduleDispatchEvent(MessageEvent::create(buffer.release()));
+ if (m_binaryType == BinaryType::ArrayBuffer) {
+ scheduleDispatchEvent(MessageEvent::create(ArrayBuffer::create(data, dataLength)));
return;
}
ASSERT_NOT_REACHED();
@@ -285,37 +223,43 @@ void RTCDataChannel::didDetectError()
scheduleDispatchEvent(Event::create(eventNames().errorEvent, false, false));
}
+void RTCDataChannel::bufferedAmountIsDecreasing()
+{
+ if (m_stopped)
+ return;
+
+ if (bufferedAmount() <= m_bufferedAmountLowThreshold)
+ scheduleDispatchEvent(Event::create(eventNames().bufferedAmountLowThresholdEvent, false, false));
+}
+
void RTCDataChannel::stop()
{
m_stopped = true;
m_readyState = ReadyStateClosed;
- m_handler->setClient(0);
- m_scriptExecutionContext = 0;
+ m_handler->setClient(nullptr);
+ m_scriptExecutionContext = nullptr;
}
-void RTCDataChannel::scheduleDispatchEvent(PassRefPtr<Event> event)
+void RTCDataChannel::scheduleDispatchEvent(Ref<Event>&& event)
{
- m_scheduledEvents.append(event);
+ m_scheduledEvents.append(WTFMove(event));
if (!m_scheduledEventTimer.isActive())
m_scheduledEventTimer.startOneShot(0);
}
-void RTCDataChannel::scheduledEventTimerFired(Timer<RTCDataChannel>*)
+void RTCDataChannel::scheduledEventTimerFired()
{
if (m_stopped)
return;
- Vector<RefPtr<Event>> events;
+ Vector<Ref<Event>> events;
events.swap(m_scheduledEvents);
- Vector<RefPtr<Event>>::iterator it = events.begin();
- for (; it != events.end(); ++it)
- dispatchEvent((*it).release());
-
- events.clear();
+ for (auto& event : events)
+ dispatchEvent(event);
}
} // namespace WebCore
-#endif // ENABLE(MEDIA_STREAM)
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCDataChannel.h b/Source/WebCore/Modules/mediastream/RTCDataChannel.h
index 54a21d441..cd860f184 100644
--- a/Source/WebCore/Modules/mediastream/RTCDataChannel.h
+++ b/Source/WebCore/Modules/mediastream/RTCDataChannel.h
@@ -22,16 +22,17 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef RTCDataChannel_h
-#define RTCDataChannel_h
+#pragma once
-#if ENABLE(MEDIA_STREAM)
+#if ENABLE(WEB_RTC)
+#include "Event.h"
#include "EventTarget.h"
+#include "ExceptionOr.h"
+#include "RTCDataChannelHandler.h"
#include "RTCDataChannelHandlerClient.h"
#include "ScriptWrappable.h"
#include "Timer.h"
-#include <wtf/RefCounted.h>
namespace JSC {
class ArrayBuffer;
@@ -41,85 +42,78 @@ namespace JSC {
namespace WebCore {
class Blob;
-class Dictionary;
-class RTCDataChannelHandler;
class RTCPeerConnectionHandler;
-class RTCDataChannel final : public RefCounted<RTCDataChannel>, public ScriptWrappable, public EventTargetWithInlineData, public RTCDataChannelHandlerClient {
+class RTCDataChannel final : public RTCDataChannelHandlerClient, public EventTargetWithInlineData {
public:
- static PassRefPtr<RTCDataChannel> create(ScriptExecutionContext*, std::unique_ptr<RTCDataChannelHandler>);
- static PassRefPtr<RTCDataChannel> create(ScriptExecutionContext*, RTCPeerConnectionHandler*, const String& , const Dictionary&, ExceptionCode&);
- ~RTCDataChannel();
-
- String label() const;
- bool ordered() const;
- unsigned short maxRetransmitTime() const;
- unsigned short maxRetransmits() const;
- String protocol() const;
- bool negotiated() const;
- unsigned short id() const;
- AtomicString readyState() const;
- unsigned long bufferedAmount() const;
-
- AtomicString binaryType() const;
- void setBinaryType(const AtomicString&, ExceptionCode&);
-
- void send(const String&, ExceptionCode&);
- void send(PassRefPtr<JSC::ArrayBuffer>, ExceptionCode&);
- void send(PassRefPtr<JSC::ArrayBufferView>, ExceptionCode&);
- void send(PassRefPtr<Blob>, ExceptionCode&);
+ static Ref<RTCDataChannel> create(ScriptExecutionContext&, std::unique_ptr<RTCDataChannelHandler>&&, String&&, RTCDataChannelInit&&);
- void close();
+ bool ordered() const { return m_options.ordered; }
+ unsigned short maxRetransmitTime() const { return m_options.maxRetransmitTime; }
+ unsigned short maxRetransmits() const { return m_options.maxRetransmits; }
+ String protocol() const { return m_options.protocol; }
+ bool negotiated() const { return m_options.negotiated; };
+ unsigned short id() const { return m_options.id; };
- DEFINE_ATTRIBUTE_EVENT_LISTENER(open);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(error);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(close);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(message);
+ String label() const { return m_label; }
+ const AtomicString& readyState() const;
+ size_t bufferedAmount() const;
+ size_t bufferedAmountLowThreshold() const { return m_bufferedAmountLowThreshold; }
+ void setBufferedAmountLowThreshold(size_t value) { m_bufferedAmountLowThreshold = value; }
- void stop();
+ const AtomicString& binaryType() const;
+ ExceptionOr<void> setBinaryType(const AtomicString&);
+
+ ExceptionOr<void> send(const String&);
+ ExceptionOr<void> send(JSC::ArrayBuffer&);
+ ExceptionOr<void> send(JSC::ArrayBufferView&);
+ ExceptionOr<void> send(Blob&);
+
+ void close();
- // EventTarget
- virtual EventTargetInterface eventTargetInterface() const override { return RTCDataChannelEventTargetInterfaceType; }
- virtual ScriptExecutionContext* scriptExecutionContext() const override { return m_scriptExecutionContext; }
+ void stop();
- using RefCounted<RTCDataChannel>::ref;
- using RefCounted<RTCDataChannel>::deref;
+ using RTCDataChannelHandlerClient::ref;
+ using RTCDataChannelHandlerClient::deref;
private:
- RTCDataChannel(ScriptExecutionContext*, std::unique_ptr<RTCDataChannelHandler>);
+ RTCDataChannel(ScriptExecutionContext&, std::unique_ptr<RTCDataChannelHandler>&&, String&&, RTCDataChannelInit&&);
+
+ void scheduleDispatchEvent(Ref<Event>&&);
+ void scheduledEventTimerFired();
- void scheduleDispatchEvent(PassRefPtr<Event>);
- void scheduledEventTimerFired(Timer<RTCDataChannel>*);
+ EventTargetInterface eventTargetInterface() const final { return RTCDataChannelEventTargetInterfaceType; }
+ ScriptExecutionContext* scriptExecutionContext() const final { return m_scriptExecutionContext; }
- // EventTarget
- virtual void refEventTarget() override { ref(); }
- virtual void derefEventTarget() override { deref(); }
+ void refEventTarget() final { ref(); }
+ void derefEventTarget() final { deref(); }
ScriptExecutionContext* m_scriptExecutionContext;
- // RTCDataChannelHandlerClient
- virtual void didChangeReadyState(ReadyState) override;
- virtual void didReceiveStringData(const String&) override;
- virtual void didReceiveRawData(const char*, size_t) override;
- virtual void didDetectError() override;
+ // RTCDataChannelHandlerClient API
+ void didChangeReadyState(ReadyState) final;
+ void didReceiveStringData(const String&) final;
+ void didReceiveRawData(const char*, size_t) final;
+ void didDetectError() final;
+ void bufferedAmountIsDecreasing() final;
std::unique_ptr<RTCDataChannelHandler> m_handler;
- bool m_stopped;
+ bool m_stopped { false };
+
+ ReadyState m_readyState { ReadyStateConnecting };
- ReadyState m_readyState;
- enum BinaryType {
- BinaryTypeBlob,
- BinaryTypeArrayBuffer
- };
- BinaryType m_binaryType;
+ enum class BinaryType { Blob, ArrayBuffer };
+ BinaryType m_binaryType { BinaryType::ArrayBuffer };
- Timer<RTCDataChannel> m_scheduledEventTimer;
- Vector<RefPtr<Event>> m_scheduledEvents;
+ Timer m_scheduledEventTimer;
+ Vector<Ref<Event>> m_scheduledEvents;
+
+ String m_label;
+ RTCDataChannelInit m_options;
+ size_t m_bufferedAmountLowThreshold { 0 };
};
} // namespace WebCore
-#endif // ENABLE(MEDIA_STREAM)
-
-#endif // RTCDataChannel_h
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCDataChannel.idl b/Source/WebCore/Modules/mediastream/RTCDataChannel.idl
index 19c24fa1b..e778c0f96 100644
--- a/Source/WebCore/Modules/mediastream/RTCDataChannel.idl
+++ b/Source/WebCore/Modules/mediastream/RTCDataChannel.idl
@@ -23,10 +23,9 @@
*/
[
+ Conditional=WEB_RTC,
NoInterfaceObject,
- Conditional=MEDIA_STREAM,
- EventTarget,
-] interface RTCDataChannel {
+] interface RTCDataChannel : EventTarget {
readonly attribute DOMString label;
readonly attribute boolean ordered;
readonly attribute unsigned short maxRetransmitTime;
@@ -37,26 +36,17 @@
readonly attribute DOMString readyState;
readonly attribute unsigned long bufferedAmount;
- [SetterRaisesException] attribute DOMString binaryType;
+ [SetterMayThrowException] attribute DOMString binaryType;
- [RaisesException] void send(ArrayBuffer data);
- [RaisesException] void send(ArrayBufferView data);
- [RaisesException] void send(Blob data);
- [RaisesException] void send(DOMString data);
+ [MayThrowException] void send(ArrayBuffer data);
+ [MayThrowException] void send(ArrayBufferView data);
+ [MayThrowException] void send(Blob data);
+ [MayThrowException] void send(DOMString data);
void close();
- attribute EventListener onopen;
- attribute EventListener onerror;
- attribute EventListener onclose;
- attribute EventListener onmessage;
-
- // 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);
+ attribute EventHandler onopen;
+ attribute EventHandler onerror;
+ attribute EventHandler onclose;
+ attribute EventHandler onmessage;
};
diff --git a/Source/WebCore/Modules/mediastream/RTCDataChannelEvent.cpp b/Source/WebCore/Modules/mediastream/RTCDataChannelEvent.cpp
index 136581384..17f81aa34 100644
--- a/Source/WebCore/Modules/mediastream/RTCDataChannelEvent.cpp
+++ b/Source/WebCore/Modules/mediastream/RTCDataChannelEvent.cpp
@@ -25,41 +25,26 @@
#include "config.h"
#include "RTCDataChannelEvent.h"
-#if ENABLE(MEDIA_STREAM)
+#if ENABLE(WEB_RTC)
-#include "EventNames.h"
#include "RTCDataChannel.h"
namespace WebCore {
-PassRefPtr<RTCDataChannelEvent> RTCDataChannelEvent::create()
+Ref<RTCDataChannelEvent> RTCDataChannelEvent::create(const AtomicString& type, bool canBubble, bool cancelable, Ref<RTCDataChannel>&& channel)
{
- return adoptRef(new RTCDataChannelEvent);
+ return adoptRef(*new RTCDataChannelEvent(type, canBubble, cancelable, WTFMove(channel)));
}
-PassRefPtr<RTCDataChannelEvent> RTCDataChannelEvent::create(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<RTCDataChannel> channel)
-{
- return adoptRef(new RTCDataChannelEvent(type, canBubble, cancelable, channel));
-}
-
-
-RTCDataChannelEvent::RTCDataChannelEvent()
-{
-}
-
-RTCDataChannelEvent::RTCDataChannelEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<RTCDataChannel> channel)
+RTCDataChannelEvent::RTCDataChannelEvent(const AtomicString& type, bool canBubble, bool cancelable, Ref<RTCDataChannel>&& channel)
: Event(type, canBubble, cancelable)
- , m_channel(channel)
-{
-}
-
-RTCDataChannelEvent::~RTCDataChannelEvent()
+ , m_channel(WTFMove(channel))
{
}
-RTCDataChannel* RTCDataChannelEvent::channel() const
+RTCDataChannel* RTCDataChannelEvent::channel()
{
- return m_channel.get();
+ return m_channel.ptr();
}
EventInterface RTCDataChannelEvent::eventInterface() const
@@ -69,5 +54,5 @@ EventInterface RTCDataChannelEvent::eventInterface() const
} // namespace WebCore
-#endif // ENABLE(MEDIA_STREAM)
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCDataChannelEvent.h b/Source/WebCore/Modules/mediastream/RTCDataChannelEvent.h
index 41b003610..5241e2790 100644
--- a/Source/WebCore/Modules/mediastream/RTCDataChannelEvent.h
+++ b/Source/WebCore/Modules/mediastream/RTCDataChannelEvent.h
@@ -22,10 +22,9 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef RTCDataChannelEvent_h
-#define RTCDataChannelEvent_h
+#pragma once
-#if ENABLE(MEDIA_STREAM)
+#if ENABLE(WEB_RTC)
#include "Event.h"
#include "RTCDataChannel.h"
@@ -35,24 +34,18 @@ namespace WebCore {
class RTCDataChannelEvent : public Event {
public:
- virtual ~RTCDataChannelEvent();
+ static Ref<RTCDataChannelEvent> create(const AtomicString& type, bool canBubble, bool cancelable, Ref<RTCDataChannel>&&);
- static PassRefPtr<RTCDataChannelEvent> create();
- static PassRefPtr<RTCDataChannelEvent> create(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<RTCDataChannel>);
-
- RTCDataChannel* channel() const;
+ RTCDataChannel* channel();
virtual EventInterface eventInterface() const;
private:
- RTCDataChannelEvent();
- RTCDataChannelEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<RTCDataChannel>);
+ RTCDataChannelEvent(const AtomicString& type, bool canBubble, bool cancelable, Ref<RTCDataChannel>&&);
- RefPtr<RTCDataChannel> m_channel;
+ Ref<RTCDataChannel> m_channel;
};
} // namespace WebCore
-#endif // ENABLE(MEDIA_STREAM)
-
-#endif // RTCDataChannelEvent_h
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCDataChannelEvent.idl b/Source/WebCore/Modules/mediastream/RTCDataChannelEvent.idl
index 946337594..1dc11f986 100644
--- a/Source/WebCore/Modules/mediastream/RTCDataChannelEvent.idl
+++ b/Source/WebCore/Modules/mediastream/RTCDataChannelEvent.idl
@@ -24,7 +24,7 @@
[
NoInterfaceObject,
- Conditional=MEDIA_STREAM,
+ Conditional=WEB_RTC,
] interface RTCDataChannelEvent : Event {
readonly attribute RTCDataChannel channel;
};
diff --git a/Source/WebCore/Modules/mediastream/RTCIceCandidate.cpp b/Source/WebCore/Modules/mediastream/RTCIceCandidate.cpp
index 353e309dd..2c7e0d364 100644
--- a/Source/WebCore/Modules/mediastream/RTCIceCandidate.cpp
+++ b/Source/WebCore/Modules/mediastream/RTCIceCandidate.cpp
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2012 Google Inc. All rights reserved.
* Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
+ * Copyright (C) 2015, 2016 Ericsson AB. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -30,84 +31,34 @@
*/
#include "config.h"
-
-#if ENABLE(MEDIA_STREAM)
-
#include "RTCIceCandidate.h"
-#include "Dictionary.h"
+#if ENABLE(WEB_RTC)
+
#include "ExceptionCode.h"
-#include "RTCIceCandidateDescriptor.h"
namespace WebCore {
-PassRefPtr<RTCIceCandidate> RTCIceCandidate::create(const Dictionary& dictionary, ExceptionCode& ec)
-{
- String candidate;
- bool ok = dictionary.get("candidate", candidate);
- if (ok && candidate.isEmpty()) {
- ec = TYPE_MISMATCH_ERR;
- return nullptr;
- }
-
- String sdpMid;
- ok = dictionary.get("sdpMid", sdpMid);
- if (ok && sdpMid.isEmpty()) {
- ec = TYPE_MISMATCH_ERR;
- return nullptr;
- }
-
- String tempLineIndex;
- unsigned short sdpMLineIndex = 0;
- // First we check if the property exists in the Dictionary.
- ok = dictionary.get("sdpMLineIndex", tempLineIndex);
- // Then we try to convert it to a number and check if it was successful.
- if (ok) {
- bool intConversionOk;
- sdpMLineIndex = tempLineIndex.toUIntStrict(&intConversionOk);
- if (!intConversionOk) {
- ec = TYPE_MISMATCH_ERR;
- return nullptr;
- }
- }
-
- return adoptRef(new RTCIceCandidate(RTCIceCandidateDescriptor::create(candidate, sdpMid, sdpMLineIndex)));
-}
-
-PassRefPtr<RTCIceCandidate> RTCIceCandidate::create(PassRefPtr<RTCIceCandidateDescriptor> descriptor)
-{
- return adoptRef(new RTCIceCandidate(descriptor));
-}
-
-RTCIceCandidate::RTCIceCandidate(PassRefPtr<RTCIceCandidateDescriptor> descriptor)
- : m_descriptor(descriptor)
-{
-}
-
-RTCIceCandidate::~RTCIceCandidate()
-{
-}
-
-const String& RTCIceCandidate::candidate() const
-{
- return m_descriptor->candidate();
-}
-
-const String& RTCIceCandidate::sdpMid() const
+inline RTCIceCandidate::RTCIceCandidate(const String& candidate, const String& sdpMid, std::optional<unsigned short> sdpMLineIndex)
+ : m_candidate(candidate)
+ , m_sdpMid(sdpMid)
+ , m_sdpMLineIndex(sdpMLineIndex)
{
- return m_descriptor->sdpMid();
+ ASSERT(!sdpMid.isNull() || sdpMLineIndex);
}
-unsigned short RTCIceCandidate::sdpMLineIndex() const
+ExceptionOr<Ref<RTCIceCandidate>> RTCIceCandidate::create(const Init& dictionary)
{
- return m_descriptor->sdpMLineIndex();
+ if (dictionary.sdpMid.isNull() && !dictionary.sdpMLineIndex)
+ return Exception { TypeError };
+ return create(dictionary.candidate, dictionary.sdpMid, dictionary.sdpMLineIndex);
}
-RTCIceCandidateDescriptor* RTCIceCandidate::descriptor()
+Ref<RTCIceCandidate> RTCIceCandidate::create(const String& candidate, const String& sdpMid, std::optional<unsigned short> sdpMLineIndex)
{
- return m_descriptor.get();
+ return adoptRef(*new RTCIceCandidate(candidate, sdpMid, sdpMLineIndex));
}
} // namespace WebCore
-#endif // ENABLE(MEDIA_STREAM)
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCIceCandidate.h b/Source/WebCore/Modules/mediastream/RTCIceCandidate.h
index 0dad25c27..2c665491d 100644
--- a/Source/WebCore/Modules/mediastream/RTCIceCandidate.h
+++ b/Source/WebCore/Modules/mediastream/RTCIceCandidate.h
@@ -28,43 +28,38 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef RTCIceCandidate_h
-#define RTCIceCandidate_h
+#pragma once
-#if ENABLE(MEDIA_STREAM)
+#if ENABLE(WEB_RTC)
-#include "ExceptionBase.h"
+#include "ExceptionOr.h"
#include "ScriptWrappable.h"
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/RefPtr.h>
-#include <wtf/text/WTFString.h>
namespace WebCore {
-class Dictionary;
-class RTCIceCandidateDescriptor;
-
class RTCIceCandidate : public RefCounted<RTCIceCandidate>, public ScriptWrappable {
public:
- static PassRefPtr<RTCIceCandidate> create(const Dictionary&, ExceptionCode&);
- static PassRefPtr<RTCIceCandidate> create(PassRefPtr<RTCIceCandidateDescriptor>);
- virtual ~RTCIceCandidate();
+ struct Init {
+ String candidate;
+ String sdpMid;
+ std::optional<unsigned short> sdpMLineIndex;
+ };
- const String& candidate() const;
- const String& sdpMid() const;
- unsigned short sdpMLineIndex() const;
+ static ExceptionOr<Ref<RTCIceCandidate>> create(const Init&);
+ static Ref<RTCIceCandidate> create(const String& candidate, const String& sdpMid, std::optional<unsigned short> sdpMLineIndex);
- RTCIceCandidateDescriptor* descriptor();
+ const String& candidate() const { return m_candidate; }
+ const String& sdpMid() const { return m_sdpMid; }
+ std::optional<unsigned short> sdpMLineIndex() const { return m_sdpMLineIndex; }
private:
- explicit RTCIceCandidate(PassRefPtr<RTCIceCandidateDescriptor>);
+ RTCIceCandidate(const String& candidate, const String& sdpMid, std::optional<unsigned short> sdpMLineIndex);
- RefPtr<RTCIceCandidateDescriptor> m_descriptor;
+ String m_candidate;
+ String m_sdpMid;
+ std::optional<unsigned short> m_sdpMLineIndex;
};
} // namespace WebCore
-#endif // ENABLE(MEDIA_STREAM)
-
-#endif // RTCIceCandidate_h
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCIceCandidate.idl b/Source/WebCore/Modules/mediastream/RTCIceCandidate.idl
index 4fb49586d..49c74e15c 100644
--- a/Source/WebCore/Modules/mediastream/RTCIceCandidate.idl
+++ b/Source/WebCore/Modules/mediastream/RTCIceCandidate.idl
@@ -30,12 +30,23 @@
*/
[
- Conditional=MEDIA_STREAM,
- CustomConstructor(optional Dictionary dictionary),
- ConstructorRaisesException
+ Conditional=WEB_RTC,
+ Constructor(RTCIceCandidateInit candidateInitDict),
+ ConstructorMayThrowException,
+ EnabledAtRuntime=PeerConnection,
+ ImplementationLacksVTable,
+ PrivateIdentifier,
+ PublicIdentifier
] interface RTCIceCandidate {
readonly attribute DOMString candidate;
- readonly attribute DOMString sdpMid;
- readonly attribute unsigned short sdpMLineIndex;
+ readonly attribute DOMString? sdpMid;
+ readonly attribute unsigned short? sdpMLineIndex;
+
+ serializer = {candidate, sdpMid, sdpMLineIndex};
};
+dictionary RTCIceCandidateInit {
+ required DOMString candidate;
+ DOMString? sdpMid = null;
+ unsigned short? sdpMLineIndex = null;
+};
diff --git a/Source/WebCore/Modules/mediastream/RTCIceCandidateEvent.cpp b/Source/WebCore/Modules/mediastream/RTCIceCandidateEvent.cpp
index 1c28e58e2..ba62fbbe1 100644
--- a/Source/WebCore/Modules/mediastream/RTCIceCandidateEvent.cpp
+++ b/Source/WebCore/Modules/mediastream/RTCIceCandidateEvent.cpp
@@ -24,7 +24,7 @@
#include "config.h"
-#if ENABLE(MEDIA_STREAM)
+#if ENABLE(WEB_RTC)
#include "RTCIceCandidateEvent.h"
@@ -33,23 +33,14 @@
namespace WebCore {
-PassRefPtr<RTCIceCandidateEvent> RTCIceCandidateEvent::create()
+Ref<RTCIceCandidateEvent> RTCIceCandidateEvent::create(bool canBubble, bool cancelable, RefPtr<RTCIceCandidate>&& candidate)
{
- return adoptRef(new RTCIceCandidateEvent);
+ return adoptRef(*new RTCIceCandidateEvent(canBubble, cancelable, WTFMove(candidate)));
}
-PassRefPtr<RTCIceCandidateEvent> RTCIceCandidateEvent::create(bool canBubble, bool cancelable, PassRefPtr<RTCIceCandidate> candidate)
-{
- return adoptRef(new RTCIceCandidateEvent(canBubble, cancelable, candidate));
-}
-
-RTCIceCandidateEvent::RTCIceCandidateEvent()
-{
-}
-
-RTCIceCandidateEvent::RTCIceCandidateEvent(bool canBubble, bool cancelable, PassRefPtr<RTCIceCandidate> candidate)
+RTCIceCandidateEvent::RTCIceCandidateEvent(bool canBubble, bool cancelable, RefPtr<RTCIceCandidate>&& candidate)
: Event(eventNames().icecandidateEvent, canBubble, cancelable)
- , m_candidate(candidate)
+ , m_candidate(WTFMove(candidate))
{
}
@@ -69,5 +60,5 @@ EventInterface RTCIceCandidateEvent::eventInterface() const
} // namespace WebCore
-#endif // ENABLE(MEDIA_STREAM)
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCIceCandidateEvent.h b/Source/WebCore/Modules/mediastream/RTCIceCandidateEvent.h
index 466636831..8d371d4ab 100644
--- a/Source/WebCore/Modules/mediastream/RTCIceCandidateEvent.h
+++ b/Source/WebCore/Modules/mediastream/RTCIceCandidateEvent.h
@@ -22,10 +22,9 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef RTCIceCandidateEvent_h
-#define RTCIceCandidateEvent_h
+#pragma once
-#if ENABLE(MEDIA_STREAM)
+#if ENABLE(WEB_RTC)
#include "Event.h"
#include <wtf/text/AtomicString.h>
@@ -37,22 +36,18 @@ class RTCIceCandidateEvent : public Event {
public:
virtual ~RTCIceCandidateEvent();
- static PassRefPtr<RTCIceCandidateEvent> create();
- static PassRefPtr<RTCIceCandidateEvent> create(bool canBubble, bool cancelable, PassRefPtr<RTCIceCandidate>);
+ static Ref<RTCIceCandidateEvent> create(bool canBubble, bool cancelable, RefPtr<RTCIceCandidate>&&);
RTCIceCandidate* candidate() const;
virtual EventInterface eventInterface() const;
private:
- RTCIceCandidateEvent();
- RTCIceCandidateEvent(bool canBubble, bool cancelable, PassRefPtr<RTCIceCandidate>);
+ RTCIceCandidateEvent(bool canBubble, bool cancelable, RefPtr<RTCIceCandidate>&&);
RefPtr<RTCIceCandidate> m_candidate;
};
} // namespace WebCore
-#endif // ENABLE(MEDIA_STREAM)
-
-#endif // RTCIceCandidateEvent_h
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCIceCandidateEvent.idl b/Source/WebCore/Modules/mediastream/RTCIceCandidateEvent.idl
index 3fa06379d..ff00d5bd2 100644
--- a/Source/WebCore/Modules/mediastream/RTCIceCandidateEvent.idl
+++ b/Source/WebCore/Modules/mediastream/RTCIceCandidateEvent.idl
@@ -24,7 +24,7 @@
[
NoInterfaceObject,
- Conditional=MEDIA_STREAM,
+ Conditional=WEB_RTC,
] interface RTCIceCandidateEvent : Event {
readonly attribute RTCIceCandidate candidate;
};
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseBasicTypes.h b/Source/WebCore/Modules/mediastream/RTCIceServer.h
index 8739f7085..b0b1798ea 100644
--- a/Source/WebCore/Modules/webdatabase/DatabaseBasicTypes.h
+++ b/Source/WebCore/Modules/mediastream/RTCIceServer.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Apple 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
@@ -23,23 +23,22 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DatabaseBasicTypes_h
-#define DatabaseBasicTypes_h
+#pragma once
-#if ENABLE(SQL_DATABASE)
+#if ENABLE(WEB_RTC)
-namespace WebCore {
+#include <wtf/Variant.h>
+#include <wtf/Vector.h>
+#include <wtf/text/WTFString.h>
-typedef int DatabaseGuid;
-typedef int ExceptionCode;
+namespace WebCore {
-enum class DatabaseType {
- Async,
- Sync
+struct RTCIceServer {
+ Variant<String, Vector<String>> urls;
+ String credential;
+ String username;
};
} // namespace WebCore
-#endif // ENABLE(SQL_DATABASE)
-
-#endif // DatabaseBasicTypes_h
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCIceServer.idl b/Source/WebCore/Modules/mediastream/RTCIceServer.idl
new file mode 100644
index 000000000..b2529c664
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/RTCIceServer.idl
@@ -0,0 +1,33 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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=WEB_RTC,
+ JSGenerateToJSObject,
+] dictionary RTCIceServer {
+ required (DOMString or sequence<DOMString>) urls;
+ DOMString username;
+ DOMString credential;
+};
diff --git a/Source/WebCore/Modules/mediastream/RTCIceTransport.h b/Source/WebCore/Modules/mediastream/RTCIceTransport.h
new file mode 100644
index 000000000..45cf7ed84
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/RTCIceTransport.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016 Ericsson AB. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Ericsson 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 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
+ * OWNER 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(WEB_RTC)
+
+#include "PeerConnectionStates.h"
+#include "ScriptWrappable.h"
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+class RTCIceTransport : public RefCounted<RTCIceTransport>, public ScriptWrappable {
+public:
+
+ using TransportState = PeerConnectionStates::IceTransportState;
+ using GatheringState = PeerConnectionStates::IceGatheringState;
+
+ static Ref<RTCIceTransport> create()
+ {
+ return adoptRef(*new RTCIceTransport());
+ }
+ virtual ~RTCIceTransport() { }
+
+ TransportState transportState() const { return m_transportState; }
+ void setTransportState(TransportState state) { m_transportState = state; }
+
+ GatheringState gatheringState() const { return m_gatheringState; }
+ void setGatheringState(GatheringState state) { m_gatheringState = state; }
+
+private:
+ RTCIceTransport() { };
+
+ TransportState m_transportState { TransportState::New };
+ GatheringState m_gatheringState { GatheringState::New };
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCOfferAnswerOptions.h b/Source/WebCore/Modules/mediastream/RTCOfferAnswerOptions.h
new file mode 100644
index 000000000..f42af80c3
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/RTCOfferAnswerOptions.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2015 Ericsson AB. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this 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(WEB_RTC)
+
+namespace WebCore {
+
+struct RTCOfferAnswerOptions {
+ bool voiceActivityDetection { true };
+};
+
+struct RTCOfferOptions : RTCOfferAnswerOptions {
+ int64_t offerToReceiveVideo { 0 };
+ int64_t offerToReceiveAudio { 0 };
+ bool iceRestart { false };
+};
+
+struct RTCAnswerOptions : RTCOfferAnswerOptions {
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCPeerConnection.cpp b/Source/WebCore/Modules/mediastream/RTCPeerConnection.cpp
index bb895c92f..8e4dcb113 100644
--- a/Source/WebCore/Modules/mediastream/RTCPeerConnection.cpp
+++ b/Source/WebCore/Modules/mediastream/RTCPeerConnection.cpp
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2012 Google Inc. All rights reserved.
* Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
+ * Copyright (C) 2015, 2016 Ericsson AB. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -30,335 +31,278 @@
*/
#include "config.h"
-
-#if ENABLE(MEDIA_STREAM)
-
#include "RTCPeerConnection.h"
-#include "ArrayValue.h"
+#if ENABLE(WEB_RTC)
+
#include "Document.h"
#include "Event.h"
-#include "ExceptionCode.h"
+#include "EventNames.h"
#include "Frame.h"
-#include "FrameLoader.h"
-#include "FrameLoaderClient.h"
-#include "MediaConstraintsImpl.h"
-#include "MediaStreamEvent.h"
+#include "MediaEndpointConfiguration.h"
+#include "MediaStream.h"
+#include "MediaStreamTrack.h"
#include "RTCConfiguration.h"
-#include "RTCDTMFSender.h"
#include "RTCDataChannel.h"
-#include "RTCDataChannelEvent.h"
-#include "RTCDataChannelHandler.h"
#include "RTCIceCandidate.h"
-#include "RTCIceCandidateDescriptor.h"
#include "RTCIceCandidateEvent.h"
-#include "RTCPeerConnectionErrorCallback.h"
+#include "RTCOfferAnswerOptions.h"
#include "RTCSessionDescription.h"
-#include "RTCSessionDescriptionCallback.h"
-#include "RTCSessionDescriptionDescriptor.h"
-#include "RTCSessionDescriptionRequestImpl.h"
-#include "RTCStatsCallback.h"
-#include "RTCStatsRequestImpl.h"
-#include "RTCVoidRequestImpl.h"
-#include "ScriptExecutionContext.h"
-#include "VoidCallback.h"
-#include <wtf/Functional.h>
+#include "RTCTrackEvent.h"
+#include "UUID.h"
#include <wtf/MainThread.h>
+#include <wtf/text/Base64.h>
namespace WebCore {
-PassRefPtr<RTCConfiguration> RTCPeerConnection::parseConfiguration(const Dictionary& configuration, ExceptionCode& ec)
-{
- if (configuration.isUndefinedOrNull())
- return nullptr;
-
- ArrayValue iceServers;
- bool ok = configuration.get("iceServers", iceServers);
- if (!ok || iceServers.isUndefinedOrNull()) {
- ec = TYPE_MISMATCH_ERR;
- return nullptr;
- }
+using namespace PeerConnection;
+using namespace PeerConnectionStates;
- size_t numberOfServers;
- ok = iceServers.length(numberOfServers);
- if (!ok) {
- ec = TYPE_MISMATCH_ERR;
- return nullptr;
- }
+Ref<RTCPeerConnection> RTCPeerConnection::create(ScriptExecutionContext& context)
+{
+ Ref<RTCPeerConnection> peerConnection = adoptRef(*new RTCPeerConnection(context));
+ peerConnection->suspendIfNeeded();
- RefPtr<RTCConfiguration> rtcConfiguration = RTCConfiguration::create();
+ return peerConnection;
+}
- for (size_t i = 0; i < numberOfServers; ++i) {
- Dictionary iceServer;
- ok = iceServers.get(i, iceServer);
- if (!ok) {
- ec = TYPE_MISMATCH_ERR;
- return nullptr;
- }
+RTCPeerConnection::RTCPeerConnection(ScriptExecutionContext& context)
+ : ActiveDOMObject(&context)
+ , m_backend(PeerConnectionBackend::create(*this))
+{
+}
- String urlString, credential, username;
- ok = iceServer.get("url", urlString);
- if (!ok) {
- ec = TYPE_MISMATCH_ERR;
- return nullptr;
- }
- URL url(URL(), urlString);
- if (!url.isValid() || !(url.protocolIs("turn") || url.protocolIs("stun"))) {
- ec = TYPE_MISMATCH_ERR;
- return nullptr;
- }
+RTCPeerConnection::~RTCPeerConnection()
+{
+ stop();
+}
- iceServer.get("credential", credential);
- iceServer.get("username", username);
+ExceptionOr<void> RTCPeerConnection::initializeWith(Document& document, RTCConfiguration&& configuration)
+{
+ if (!document.frame())
+ return Exception { NOT_SUPPORTED_ERR };
- rtcConfiguration->appendServer(RTCIceServer::create(url, credential, username));
- }
+ if (!m_backend)
+ return Exception { NOT_SUPPORTED_ERR };
- return rtcConfiguration.release();
+ return setConfiguration(WTFMove(configuration));
}
-PassRefPtr<RTCPeerConnection> RTCPeerConnection::create(ScriptExecutionContext& context, const Dictionary& rtcConfiguration, const Dictionary& mediaConstraints, ExceptionCode& ec)
+ExceptionOr<Ref<RTCRtpSender>> RTCPeerConnection::addTrack(Ref<MediaStreamTrack>&& track, const Vector<std::reference_wrapper<MediaStream>>& streams)
{
- RefPtr<RTCConfiguration> configuration = parseConfiguration(rtcConfiguration, ec);
- if (ec)
- return nullptr;
+ if (m_signalingState == SignalingState::Closed)
+ return Exception { INVALID_STATE_ERR };
- RefPtr<MediaConstraints> constraints = MediaConstraintsImpl::create(mediaConstraints, ec);
- if (ec)
- return nullptr;
+ // Require at least one stream until https://github.com/w3c/webrtc-pc/issues/288 is resolved
+ if (!streams.size())
+ return Exception { NOT_SUPPORTED_ERR };
- RefPtr<RTCPeerConnection> peerConnection = adoptRef(new RTCPeerConnection(context, configuration.release(), constraints.release(), ec));
- peerConnection->suspendIfNeeded();
- if (ec)
- return nullptr;
+ for (RTCRtpSender& sender : m_transceiverSet->senders()) {
+ if (sender.trackId() == track->id())
+ return Exception { INVALID_ACCESS_ERR };
+ }
- return peerConnection.release();
-}
+ Vector<String> mediaStreamIds;
+ for (auto stream : streams)
+ mediaStreamIds.append(stream.get().id());
-RTCPeerConnection::RTCPeerConnection(ScriptExecutionContext& context, PassRefPtr<RTCConfiguration> configuration, PassRefPtr<MediaConstraints> constraints, ExceptionCode& ec)
- : ActiveDOMObject(&context)
- , m_signalingState(SignalingStateStable)
- , m_iceGatheringState(IceGatheringStateNew)
- , m_iceConnectionState(IceConnectionStateNew)
- , m_scheduledEventTimer(this, &RTCPeerConnection::scheduledEventTimerFired)
- , m_stopped(false)
-{
- Document& document = toDocument(context);
+ RTCRtpSender* sender = nullptr;
- if (!document.frame()) {
- ec = NOT_SUPPORTED_ERR;
- return;
+ // Reuse an existing sender with the same track kind if it has never been used to send before.
+ for (auto& transceiver : m_transceiverSet->list()) {
+ auto& existingSender = transceiver->sender();
+ if (existingSender.trackKind() == track->kind() && existingSender.trackId().isNull() && !transceiver->hasSendingDirection()) {
+ existingSender.setTrack(WTFMove(track));
+ existingSender.setMediaStreamIds(WTFMove(mediaStreamIds));
+ transceiver->enableSendingDirection();
+ sender = &existingSender;
+ break;
+ }
}
- m_peerHandler = RTCPeerConnectionHandler::create(this);
- if (!m_peerHandler) {
- ec = NOT_SUPPORTED_ERR;
- return;
- }
+ if (!sender) {
+ String transceiverMid = RTCRtpTransceiver::getNextMid();
+ const String& trackKind = track->kind();
+ String trackId = createCanonicalUUIDString();
- document.frame()->loader().client().dispatchWillStartUsingPeerConnectionHandler(m_peerHandler.get());
+ auto newSender = RTCRtpSender::create(WTFMove(track), WTFMove(mediaStreamIds), *this);
+ auto receiver = m_backend->createReceiver(transceiverMid, trackKind, trackId);
+ auto transceiver = RTCRtpTransceiver::create(WTFMove(newSender), WTFMove(receiver));
- if (!m_peerHandler->initialize(configuration, constraints)) {
- ec = NOT_SUPPORTED_ERR;
- return;
+ // This transceiver is not yet associated with an m-line (null mid), but we need a
+ // provisional mid if the transceiver is used to create an offer.
+ transceiver->setProvisionalMid(transceiverMid);
+
+ sender = &transceiver->sender();
+ m_transceiverSet->append(WTFMove(transceiver));
}
-}
-RTCPeerConnection::~RTCPeerConnection()
-{
- stop();
+ m_backend->markAsNeedingNegotiation();
- for (auto stream = m_localStreams.begin(), end = m_localStreams.end(); stream != end; ++stream)
- (*stream)->removeObserver(this);
+ return Ref<RTCRtpSender> { *sender };
}
-void RTCPeerConnection::createOffer(PassRefPtr<RTCSessionDescriptionCallback> successCallback, PassRefPtr<RTCPeerConnectionErrorCallback> errorCallback, const Dictionary& mediaConstraints, ExceptionCode& ec)
+ExceptionOr<void> RTCPeerConnection::removeTrack(RTCRtpSender& sender)
{
- if (m_signalingState == SignalingStateClosed) {
- ec = INVALID_STATE_ERR;
- return;
- }
+ if (m_signalingState == SignalingState::Closed)
+ return Exception { INVALID_STATE_ERR };
- if (!successCallback) {
- ec = TYPE_MISMATCH_ERR;
- return;
+ bool shouldAbort = true;
+ for (RTCRtpSender& senderInSet : m_transceiverSet->senders()) {
+ if (&senderInSet == &sender) {
+ shouldAbort = sender.isStopped();
+ break;
+ }
}
+ if (shouldAbort)
+ return { };
- RefPtr<MediaConstraints> constraints = MediaConstraintsImpl::create(mediaConstraints, ec);
- if (ec)
- return;
+ sender.stop();
- RefPtr<RTCSessionDescriptionRequestImpl> request = RTCSessionDescriptionRequestImpl::create(scriptExecutionContext(), successCallback, errorCallback);
- m_peerHandler->createOffer(request.release(), constraints);
+ m_backend->markAsNeedingNegotiation();
+ return { };
}
-void RTCPeerConnection::createAnswer(PassRefPtr<RTCSessionDescriptionCallback> successCallback, PassRefPtr<RTCPeerConnectionErrorCallback> errorCallback, const Dictionary& mediaConstraints, ExceptionCode& ec)
+ExceptionOr<Ref<RTCRtpTransceiver>> RTCPeerConnection::addTransceiver(Ref<MediaStreamTrack>&& track, const RtpTransceiverInit& init)
{
- if (m_signalingState == SignalingStateClosed) {
- ec = INVALID_STATE_ERR;
- return;
- }
+ if (m_signalingState == SignalingState::Closed)
+ return Exception { INVALID_STATE_ERR };
- if (!successCallback) {
- ec = TYPE_MISMATCH_ERR;
- return;
- }
+ String transceiverMid = RTCRtpTransceiver::getNextMid();
+ const String& trackKind = track->kind();
+ const String& trackId = track->id();
- RefPtr<MediaConstraints> constraints = MediaConstraintsImpl::create(mediaConstraints, ec);
- if (ec)
- return;
+ auto sender = RTCRtpSender::create(WTFMove(track), Vector<String>(), *this);
+ auto receiver = m_backend->createReceiver(transceiverMid, trackKind, trackId);
+ auto transceiver = RTCRtpTransceiver::create(WTFMove(sender), WTFMove(receiver));
+ transceiver->setProvisionalMid(transceiverMid);
- RefPtr<RTCSessionDescriptionRequestImpl> request = RTCSessionDescriptionRequestImpl::create(scriptExecutionContext(), successCallback, errorCallback);
- m_peerHandler->createAnswer(request.release(), constraints.release());
+ completeAddTransceiver(transceiver, init);
+ return WTFMove(transceiver);
}
-bool RTCPeerConnection::checkStateForLocalDescription(RTCSessionDescription* localDescription)
+ExceptionOr<Ref<RTCRtpTransceiver>> RTCPeerConnection::addTransceiver(const String& kind, const RtpTransceiverInit& init)
{
- if (localDescription->type() == "offer")
- return m_signalingState == SignalingStateStable || m_signalingState == SignalingStateHaveLocalOffer;
- if (localDescription->type() == "answer")
- return m_signalingState == SignalingStateHaveRemoteOffer || m_signalingState == SignalingStateHaveLocalPrAnswer;
- if (localDescription->type() == "pranswer")
- return m_signalingState == SignalingStateHaveLocalPrAnswer || m_signalingState == SignalingStateHaveRemoteOffer;
+ if (m_signalingState == SignalingState::Closed)
+ return Exception { INVALID_STATE_ERR };
- return false;
-}
+ if (kind != "audio" && kind != "video")
+ return Exception { TypeError };
-bool RTCPeerConnection::checkStateForRemoteDescription(RTCSessionDescription* remoteDescription)
-{
- if (remoteDescription->type() == "offer")
- return m_signalingState == SignalingStateStable || m_signalingState == SignalingStateHaveRemoteOffer;
- if (remoteDescription->type() == "answer")
- return m_signalingState == SignalingStateHaveLocalOffer || m_signalingState == SignalingStateHaveRemotePrAnswer;
- if (remoteDescription->type() == "pranswer")
- return m_signalingState == SignalingStateHaveRemotePrAnswer || m_signalingState == SignalingStateHaveLocalOffer;
+ String transceiverMid = RTCRtpTransceiver::getNextMid();
+ String trackId = createCanonicalUUIDString();
- return false;
+ auto sender = RTCRtpSender::create(kind, Vector<String>(), *this);
+ auto receiver = m_backend->createReceiver(transceiverMid, kind, trackId);
+ auto transceiver = RTCRtpTransceiver::create(WTFMove(sender), WTFMove(receiver));
+ transceiver->setProvisionalMid(transceiverMid);
+
+ completeAddTransceiver(transceiver, init);
+ return WTFMove(transceiver);
}
-void RTCPeerConnection::setLocalDescription(PassRefPtr<RTCSessionDescription> prpSessionDescription, PassRefPtr<VoidCallback> successCallback, PassRefPtr<RTCPeerConnectionErrorCallback> errorCallback, ExceptionCode& ec)
+void RTCPeerConnection::completeAddTransceiver(RTCRtpTransceiver& transceiver, const RtpTransceiverInit& init)
{
- if (m_signalingState == SignalingStateClosed) {
- ec = INVALID_STATE_ERR;
- return;
- }
+ transceiver.setDirection(static_cast<RTCRtpTransceiver::Direction>(init.direction));
- RefPtr<RTCSessionDescription> sessionDescription = prpSessionDescription;
- if (!sessionDescription) {
- ec = TYPE_MISMATCH_ERR;
- return;
- }
+ m_transceiverSet->append(transceiver);
+ m_backend->markAsNeedingNegotiation();
+}
- if (!checkStateForLocalDescription(sessionDescription.get())) {
- RefPtr<DOMError> error = DOMError::create(RTCPeerConnectionHandler::invalidSessionDescriptionErrorName());
- callOnMainThread(bind(&RTCPeerConnectionErrorCallback::handleEvent, errorCallback.get(), error.release()));
+void RTCPeerConnection::queuedCreateOffer(RTCOfferOptions&& options, SessionDescriptionPromise&& promise)
+{
+ if (m_signalingState == SignalingState::Closed) {
+ promise.reject(INVALID_STATE_ERR);
return;
}
- RefPtr<RTCVoidRequestImpl> request = RTCVoidRequestImpl::create(scriptExecutionContext(), successCallback, errorCallback);
- m_peerHandler->setLocalDescription(request.release(), sessionDescription->descriptor());
+ m_backend->createOffer(WTFMove(options), WTFMove(promise));
}
-PassRefPtr<RTCSessionDescription> RTCPeerConnection::localDescription(ExceptionCode& ec)
+void RTCPeerConnection::queuedCreateAnswer(RTCAnswerOptions&& options, SessionDescriptionPromise&& promise)
{
- RefPtr<RTCSessionDescriptionDescriptor> descriptor = m_peerHandler->localDescription();
- if (!descriptor) {
- ec = INVALID_STATE_ERR;
- return nullptr;
+ if (m_signalingState == SignalingState::Closed) {
+ promise.reject(INVALID_STATE_ERR);
+ return;
}
- RefPtr<RTCSessionDescription> sessionDescription = RTCSessionDescription::create(descriptor.release());
- return sessionDescription.release();
+ m_backend->createAnswer(WTFMove(options), WTFMove(promise));
}
-void RTCPeerConnection::setRemoteDescription(PassRefPtr<RTCSessionDescription> prpSessionDescription, PassRefPtr<VoidCallback> successCallback, PassRefPtr<RTCPeerConnectionErrorCallback> errorCallback, ExceptionCode& ec)
+void RTCPeerConnection::queuedSetLocalDescription(RTCSessionDescription& description, DOMPromise<void>&& promise)
{
- if (m_signalingState == SignalingStateClosed) {
- ec = INVALID_STATE_ERR;
+ if (m_signalingState == SignalingState::Closed) {
+ promise.reject(INVALID_STATE_ERR);
return;
}
- RefPtr<RTCSessionDescription> sessionDescription = prpSessionDescription;
- if (!sessionDescription) {
- ec = TYPE_MISMATCH_ERR;
- return;
- }
-
- if (!checkStateForRemoteDescription(sessionDescription.get())) {
- RefPtr<DOMError> error = DOMError::create(RTCPeerConnectionHandler::invalidSessionDescriptionErrorName());
- callOnMainThread(bind(&RTCPeerConnectionErrorCallback::handleEvent, errorCallback.get(), error.release()));
- return;
- }
+ m_backend->setLocalDescription(description, WTFMove(promise));
+}
- RefPtr<RTCVoidRequestImpl> request = RTCVoidRequestImpl::create(scriptExecutionContext(), successCallback, errorCallback);
- m_peerHandler->setRemoteDescription(request.release(), sessionDescription->descriptor());
+RefPtr<RTCSessionDescription> RTCPeerConnection::localDescription() const
+{
+ return m_backend->localDescription();
}
-PassRefPtr<RTCSessionDescription> RTCPeerConnection::remoteDescription(ExceptionCode& ec)
+RefPtr<RTCSessionDescription> RTCPeerConnection::currentLocalDescription() const
{
- RefPtr<RTCSessionDescriptionDescriptor> descriptor = m_peerHandler->remoteDescription();
- if (!descriptor) {
- ec = INVALID_STATE_ERR;
- return nullptr;
- }
+ return m_backend->currentLocalDescription();
+}
- RefPtr<RTCSessionDescription> desc = RTCSessionDescription::create(descriptor.release());
- return desc.release();
+RefPtr<RTCSessionDescription> RTCPeerConnection::pendingLocalDescription() const
+{
+ return m_backend->pendingLocalDescription();
}
-void RTCPeerConnection::updateIce(const Dictionary& rtcConfiguration, const Dictionary& mediaConstraints, ExceptionCode& ec)
+void RTCPeerConnection::queuedSetRemoteDescription(RTCSessionDescription& description, DOMPromise<void>&& promise)
{
- if (m_signalingState == SignalingStateClosed) {
- ec = INVALID_STATE_ERR;
+ if (m_signalingState == SignalingState::Closed) {
+ promise.reject(INVALID_STATE_ERR);
return;
}
- RefPtr<RTCConfiguration> configuration = parseConfiguration(rtcConfiguration, ec);
- if (ec)
- return;
+ m_backend->setRemoteDescription(description, WTFMove(promise));
+}
- RefPtr<MediaConstraints> constraints = MediaConstraintsImpl::create(mediaConstraints, ec);
- if (ec)
- return;
+RefPtr<RTCSessionDescription> RTCPeerConnection::remoteDescription() const
+{
+ return m_backend->remoteDescription();
+}
- bool valid = m_peerHandler->updateIce(configuration, constraints);
- if (!valid)
- ec = SYNTAX_ERR;
+RefPtr<RTCSessionDescription> RTCPeerConnection::currentRemoteDescription() const
+{
+ return m_backend->currentRemoteDescription();
}
-void RTCPeerConnection::addIceCandidate(RTCIceCandidate* iceCandidate, PassRefPtr<VoidCallback> successCallback, PassRefPtr<RTCPeerConnectionErrorCallback> errorCallback, ExceptionCode& ec)
+RefPtr<RTCSessionDescription> RTCPeerConnection::pendingRemoteDescription() const
{
- if (m_signalingState == SignalingStateClosed) {
- ec = INVALID_STATE_ERR;
- return;
- }
+ return m_backend->pendingRemoteDescription();
+}
- if (!iceCandidate || !successCallback || !errorCallback) {
- ec = TYPE_MISMATCH_ERR;
+void RTCPeerConnection::queuedAddIceCandidate(RTCIceCandidate& rtcCandidate, DOMPromise<void>&& promise)
+{
+ if (m_signalingState == SignalingState::Closed) {
+ promise.reject(INVALID_STATE_ERR);
return;
}
- RefPtr<RTCVoidRequestImpl> request = RTCVoidRequestImpl::create(scriptExecutionContext(), successCallback, errorCallback);
-
- bool implemented = m_peerHandler->addIceCandidate(request.release(), iceCandidate->descriptor());
- if (!implemented)
- ec = SYNTAX_ERR;
+ m_backend->addIceCandidate(rtcCandidate, WTFMove(promise));
}
String RTCPeerConnection::signalingState() const
{
switch (m_signalingState) {
- case SignalingStateStable:
+ case SignalingState::Stable:
return ASCIILiteral("stable");
- case SignalingStateHaveLocalOffer:
+ case SignalingState::HaveLocalOffer:
return ASCIILiteral("have-local-offer");
- case SignalingStateHaveRemoteOffer:
+ case SignalingState::HaveRemoteOffer:
return ASCIILiteral("have-remote-offer");
- case SignalingStateHaveLocalPrAnswer:
+ case SignalingState::HaveLocalPrAnswer:
return ASCIILiteral("have-local-pranswer");
- case SignalingStateHaveRemotePrAnswer:
+ case SignalingState::HaveRemotePrAnswer:
return ASCIILiteral("have-remote-pranswer");
- case SignalingStateClosed:
+ case SignalingState::Closed:
return ASCIILiteral("closed");
}
@@ -369,11 +313,11 @@ String RTCPeerConnection::signalingState() const
String RTCPeerConnection::iceGatheringState() const
{
switch (m_iceGatheringState) {
- case IceGatheringStateNew:
+ case IceGatheringState::New:
return ASCIILiteral("new");
- case IceGatheringStateGathering:
+ case IceGatheringState::Gathering:
return ASCIILiteral("gathering");
- case IceGatheringStateComplete:
+ case IceGatheringState::Complete:
return ASCIILiteral("complete");
}
@@ -384,19 +328,19 @@ String RTCPeerConnection::iceGatheringState() const
String RTCPeerConnection::iceConnectionState() const
{
switch (m_iceConnectionState) {
- case IceConnectionStateNew:
+ case IceConnectionState::New:
return ASCIILiteral("new");
- case IceConnectionStateChecking:
+ case IceConnectionState::Checking:
return ASCIILiteral("checking");
- case IceConnectionStateConnected:
+ case IceConnectionState::Connected:
return ASCIILiteral("connected");
- case IceConnectionStateCompleted:
+ case IceConnectionState::Completed:
return ASCIILiteral("completed");
- case IceConnectionStateFailed:
+ case IceConnectionState::Failed:
return ASCIILiteral("failed");
- case IceConnectionStateDisconnected:
+ case IceConnectionState::Disconnected:
return ASCIILiteral("disconnected");
- case IceConnectionStateClosed:
+ case IceConnectionState::Closed:
return ASCIILiteral("closed");
}
@@ -404,295 +348,145 @@ String RTCPeerConnection::iceConnectionState() const
return String();
}
-void RTCPeerConnection::addStream(PassRefPtr<MediaStream> prpStream, const Dictionary& mediaConstraints, ExceptionCode& ec)
-{
- if (m_signalingState == SignalingStateClosed) {
- ec = INVALID_STATE_ERR;
- return;
- }
-
- RefPtr<MediaStream> stream = prpStream;
- if (!stream) {
- ec = TYPE_MISMATCH_ERR;
- return;
- }
-
- if (m_localStreams.contains(stream))
- return;
-
- RefPtr<MediaConstraints> constraints = MediaConstraintsImpl::create(mediaConstraints, ec);
- if (ec)
- return;
-
- bool valid = m_peerHandler->addStream(stream->privateStream(), constraints);
- if (!valid)
- ec = SYNTAX_ERR;
- else {
- m_localStreams.append(stream);
- stream->addObserver(this);
- }
-}
-
-void RTCPeerConnection::removeStream(PassRefPtr<MediaStream> prpStream, ExceptionCode& ec)
-{
- if (m_signalingState == SignalingStateClosed) {
- ec = INVALID_STATE_ERR;
- return;
- }
-
- if (!prpStream) {
- ec = TYPE_MISMATCH_ERR;
- return;
- }
-
- RefPtr<MediaStream> stream = prpStream;
-
- size_t pos = m_localStreams.find(stream);
- if (pos == notFound)
- return;
-
- m_localStreams.remove(pos);
- stream->removeObserver(this);
- m_peerHandler->removeStream(stream->privateStream());
-}
-
-MediaStreamVector RTCPeerConnection::getLocalStreams() const
-{
- return m_localStreams;
-}
-
-MediaStreamVector RTCPeerConnection::getRemoteStreams() const
-{
- return m_remoteStreams;
-}
-
-MediaStream* RTCPeerConnection::getStreamById(const String& streamId)
-{
- for (MediaStreamVector::iterator iter = m_localStreams.begin(); iter != m_localStreams.end(); ++iter) {
- if ((*iter)->id() == streamId)
- return iter->get();
- }
-
- for (MediaStreamVector::iterator iter = m_remoteStreams.begin(); iter != m_remoteStreams.end(); ++iter) {
- if ((*iter)->id() == streamId)
- return iter->get();
+ExceptionOr<void> RTCPeerConnection::setConfiguration(RTCConfiguration&& configuration)
+{
+ if (m_signalingState == SignalingState::Closed)
+ return Exception { INVALID_STATE_ERR };
+
+ Vector<MediaEndpointConfiguration::IceServerInfo> servers;
+ if (configuration.iceServers) {
+ servers.reserveInitialCapacity(configuration.iceServers->size());
+ for (auto& server : configuration.iceServers.value()) {
+ Vector<URL> serverURLs;
+ WTF::switchOn(server.urls,
+ [&serverURLs] (const String& string) {
+ serverURLs.reserveInitialCapacity(1);
+ serverURLs.uncheckedAppend(URL { URL { }, string });
+ },
+ [&serverURLs] (const Vector<String>& vector) {
+ serverURLs.reserveInitialCapacity(vector.size());
+ for (auto& string : vector)
+ serverURLs.uncheckedAppend(URL { URL { }, string });
+ }
+ );
+ for (auto& serverURL : serverURLs) {
+ if (!(serverURL.protocolIs("turn") || serverURL.protocolIs("turns") || serverURL.protocolIs("stun")))
+ return Exception { INVALID_ACCESS_ERR };
+ }
+ servers.uncheckedAppend({ WTFMove(serverURLs), server.credential, server.username });
+ }
}
- return nullptr;
+ m_backend->setConfiguration({ WTFMove(servers), configuration.iceTransportPolicy, configuration.bundlePolicy });
+ m_configuration = WTFMove(configuration);
+ return { };
}
-void RTCPeerConnection::getStats(PassRefPtr<RTCStatsCallback> successCallback, PassRefPtr<MediaStreamTrack> selector)
+void RTCPeerConnection::getStats(MediaStreamTrack* selector, Ref<DeferredPromise>&& promise)
{
- RefPtr<RTCStatsRequestImpl> statsRequest = RTCStatsRequestImpl::create(scriptExecutionContext(), successCallback, selector);
- // FIXME: Add passing selector as part of the statsRequest.
- m_peerHandler->getStats(statsRequest.release());
+ m_backend->getStats(selector, WTFMove(promise));
}
-PassRefPtr<RTCDataChannel> RTCPeerConnection::createDataChannel(String label, const Dictionary& options, ExceptionCode& ec)
+ExceptionOr<Ref<RTCDataChannel>> RTCPeerConnection::createDataChannel(ScriptExecutionContext& context, String&& label, RTCDataChannelInit&& options)
{
- if (m_signalingState == SignalingStateClosed) {
- ec = INVALID_STATE_ERR;
- return nullptr;
- }
+ if (m_signalingState == SignalingState::Closed)
+ return Exception { INVALID_STATE_ERR };
- RefPtr<RTCDataChannel> channel = RTCDataChannel::create(scriptExecutionContext(), m_peerHandler.get(), label, options, ec);
- if (ec)
- return nullptr;
+ // FIXME: Check options
+ auto channelHandler = m_backend->createDataChannelHandler(label, options);
+ if (!channelHandler)
+ return Exception { NOT_SUPPORTED_ERR };
- m_dataChannels.append(channel);
- return channel.release();
+ return RTCDataChannel::create(context, WTFMove(channelHandler), WTFMove(label), WTFMove(options));
}
-bool RTCPeerConnection::hasLocalStreamWithTrackId(const String& trackId)
+void RTCPeerConnection::close()
{
- for (MediaStreamVector::iterator iter = m_localStreams.begin(); iter != m_localStreams.end(); ++iter) {
- if ((*iter)->getTrackById(trackId))
- return true;
- }
- return false;
-}
-
-PassRefPtr<RTCDTMFSender> RTCPeerConnection::createDTMFSender(PassRefPtr<MediaStreamTrack> prpTrack, ExceptionCode& ec)
-{
- if (m_signalingState == SignalingStateClosed) {
- ec = INVALID_STATE_ERR;
- return nullptr;
- }
-
- if (!prpTrack) {
- ec = TypeError;
- return nullptr;
- }
-
- RefPtr<MediaStreamTrack> track = prpTrack;
-
- if (!hasLocalStreamWithTrackId(track->id())) {
- ec = SYNTAX_ERR;
- return nullptr;
- }
-
- RefPtr<RTCDTMFSender> dtmfSender = RTCDTMFSender::create(scriptExecutionContext(), m_peerHandler.get(), track.release(), ec);
- if (ec)
- return nullptr;
- return dtmfSender.release();
-}
-
-void RTCPeerConnection::close(ExceptionCode& ec)
-{
- if (m_signalingState == SignalingStateClosed) {
- ec = INVALID_STATE_ERR;
+ if (m_signalingState == SignalingState::Closed)
return;
- }
- m_peerHandler->stop();
+ m_backend->stop();
- changeIceConnectionState(IceConnectionStateClosed);
- changeIceGatheringState(IceGatheringStateComplete);
- changeSignalingState(SignalingStateClosed);
-}
+ m_iceConnectionState = IceConnectionState::Closed;
+ m_signalingState = SignalingState::Closed;
-void RTCPeerConnection::negotiationNeeded()
-{
- scheduleDispatchEvent(Event::create(eventNames().negotiationneededEvent, false, false));
-}
-
-void RTCPeerConnection::didGenerateIceCandidate(PassRefPtr<RTCIceCandidateDescriptor> iceCandidateDescriptor)
-{
- ASSERT(scriptExecutionContext()->isContextThread());
- if (!iceCandidateDescriptor)
- scheduleDispatchEvent(RTCIceCandidateEvent::create(false, false, 0));
- else {
- RefPtr<RTCIceCandidate> iceCandidate = RTCIceCandidate::create(iceCandidateDescriptor);
- scheduleDispatchEvent(RTCIceCandidateEvent::create(false, false, iceCandidate.release()));
- }
+ for (RTCRtpSender& sender : m_transceiverSet->senders())
+ sender.stop();
}
-void RTCPeerConnection::didChangeSignalingState(SignalingState newState)
+void RTCPeerConnection::emulatePlatformEvent(const String& action)
{
- ASSERT(scriptExecutionContext()->isContextThread());
- changeSignalingState(newState);
+ m_backend->emulatePlatformEvent(action);
}
-void RTCPeerConnection::didChangeIceGatheringState(IceGatheringState newState)
+void RTCPeerConnection::stop()
{
- ASSERT(scriptExecutionContext()->isContextThread());
- changeIceGatheringState(newState);
+ close();
}
-void RTCPeerConnection::didChangeIceConnectionState(IceConnectionState newState)
+const char* RTCPeerConnection::activeDOMObjectName() const
{
- ASSERT(scriptExecutionContext()->isContextThread());
- changeIceConnectionState(newState);
+ return "RTCPeerConnection";
}
-void RTCPeerConnection::didAddRemoteStream(PassRefPtr<MediaStreamPrivate> privateStream)
+bool RTCPeerConnection::canSuspendForDocumentSuspension() const
{
- ASSERT(scriptExecutionContext()->isContextThread());
-
- if (m_signalingState == SignalingStateClosed)
- return;
-
- RefPtr<MediaStream> stream = MediaStream::create(*scriptExecutionContext(), privateStream);
- m_remoteStreams.append(stream);
-
- scheduleDispatchEvent(MediaStreamEvent::create(eventNames().addstreamEvent, false, false, stream.release()));
+ // FIXME: We should try and do better here.
+ return false;
}
-void RTCPeerConnection::didRemoveRemoteStream(MediaStreamPrivate* privateStream)
+void RTCPeerConnection::addTransceiver(Ref<RTCRtpTransceiver>&& transceiver)
{
- ASSERT(scriptExecutionContext()->isContextThread());
- ASSERT(privateStream->client());
-
- // FIXME: this class shouldn't know that the private stream client is a MediaStream!
- RefPtr<MediaStream> stream = static_cast<MediaStream*>(privateStream->client());
- stream->setEnded();
-
- if (m_signalingState == SignalingStateClosed)
- return;
-
- size_t pos = m_remoteStreams.find(stream);
- ASSERT(pos != notFound);
- m_remoteStreams.remove(pos);
-
- scheduleDispatchEvent(MediaStreamEvent::create(eventNames().removestreamEvent, false, false, stream.release()));
+ m_transceiverSet->append(WTFMove(transceiver));
}
-void RTCPeerConnection::didAddRemoteDataChannel(std::unique_ptr<RTCDataChannelHandler> handler)
+void RTCPeerConnection::setSignalingState(SignalingState newState)
{
- ASSERT(scriptExecutionContext()->isContextThread());
-
- if (m_signalingState == SignalingStateClosed)
- return;
-
- RefPtr<RTCDataChannel> channel = RTCDataChannel::create(scriptExecutionContext(), std::move(handler));
- m_dataChannels.append(channel);
-
- scheduleDispatchEvent(RTCDataChannelEvent::create(eventNames().datachannelEvent, false, false, channel.release()));
+ m_signalingState = newState;
}
-void RTCPeerConnection::stop()
+void RTCPeerConnection::updateIceGatheringState(IceGatheringState newState)
{
- if (m_stopped)
- return;
-
- m_stopped = true;
- m_iceConnectionState = IceConnectionStateClosed;
- m_signalingState = SignalingStateClosed;
+ scriptExecutionContext()->postTask([=](ScriptExecutionContext&) {
+ if (m_signalingState == SignalingState::Closed || m_iceGatheringState == newState)
+ return;
- Vector<RefPtr<RTCDataChannel>>::iterator i = m_dataChannels.begin();
- for (; i != m_dataChannels.end(); ++i)
- (*i)->stop();
+ m_iceGatheringState = newState;
+ dispatchEvent(Event::create(eventNames().icegatheringstatechangeEvent, false, false));
+ });
}
-void RTCPeerConnection::didAddOrRemoveTrack()
+void RTCPeerConnection::updateIceConnectionState(IceConnectionState newState)
{
- negotiationNeeded();
-}
+ scriptExecutionContext()->postTask([=](ScriptExecutionContext&) {
+ if (m_signalingState == SignalingState::Closed || m_iceConnectionState == newState)
+ return;
-void RTCPeerConnection::changeSignalingState(SignalingState signalingState)
-{
- if (m_signalingState != SignalingStateClosed && m_signalingState != signalingState) {
- m_signalingState = signalingState;
- scheduleDispatchEvent(Event::create(eventNames().signalingstatechangeEvent, false, false));
- }
+ m_iceConnectionState = newState;
+ dispatchEvent(Event::create(eventNames().iceconnectionstatechangeEvent, false, false));
+ });
}
-void RTCPeerConnection::changeIceGatheringState(IceGatheringState iceGatheringState)
+void RTCPeerConnection::scheduleNegotiationNeededEvent()
{
- m_iceGatheringState = iceGatheringState;
-}
-
-void RTCPeerConnection::changeIceConnectionState(IceConnectionState iceConnectionState)
-{
- if (m_iceConnectionState != IceConnectionStateClosed && m_iceConnectionState != iceConnectionState) {
- m_iceConnectionState = iceConnectionState;
- scheduleDispatchEvent(Event::create(eventNames().iceconnectionstatechangeEvent, false, false));
- }
+ scriptExecutionContext()->postTask([=](ScriptExecutionContext&) {
+ if (m_backend->isNegotiationNeeded()) {
+ m_backend->clearNegotiationNeededState();
+ dispatchEvent(Event::create(eventNames().negotiationneededEvent, false, false));
+ }
+ });
}
-void RTCPeerConnection::scheduleDispatchEvent(PassRefPtr<Event> event)
+void RTCPeerConnection::fireEvent(Event& event)
{
- m_scheduledEvents.append(event);
-
- if (!m_scheduledEventTimer.isActive())
- m_scheduledEventTimer.startOneShot(0);
+ dispatchEvent(event);
}
-void RTCPeerConnection::scheduledEventTimerFired(Timer<RTCPeerConnection>*)
+void RTCPeerConnection::replaceTrack(RTCRtpSender& sender, RefPtr<MediaStreamTrack>&& withTrack, DOMPromise<void>&& promise)
{
- if (m_stopped)
- return;
-
- Vector<RefPtr<Event>> events;
- events.swap(m_scheduledEvents);
-
- Vector<RefPtr<Event>>::iterator it = events.begin();
- for (; it != events.end(); ++it)
- dispatchEvent((*it).release());
-
- events.clear();
+ m_backend->replaceTrack(sender, WTFMove(withTrack), WTFMove(promise));
}
} // namespace WebCore
-#endif // ENABLE(MEDIA_STREAM)
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCPeerConnection.h b/Source/WebCore/Modules/mediastream/RTCPeerConnection.h
index 0d375be4c..a10aaeb4c 100644
--- a/Source/WebCore/Modules/mediastream/RTCPeerConnection.h
+++ b/Source/WebCore/Modules/mediastream/RTCPeerConnection.h
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2012 Google Inc. All rights reserved.
* Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
+ * Copyright (C) 2015 Ericsson AB. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -29,148 +30,142 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef RTCPeerConnection_h
-#define RTCPeerConnection_h
+#pragma once
-#if ENABLE(MEDIA_STREAM)
+#if ENABLE(WEB_RTC)
#include "ActiveDOMObject.h"
-#include "Dictionary.h"
#include "EventTarget.h"
-#include "ExceptionBase.h"
#include "MediaStream.h"
-#include "RTCIceCandidate.h"
-#include "RTCPeerConnectionHandler.h"
-#include "RTCPeerConnectionHandlerClient.h"
-#include "ScriptWrappable.h"
-#include "Timer.h"
-#include <wtf/RefCounted.h>
+#include "RTCConfiguration.h"
+#include "RTCDataChannel.h"
+#include "RTCOfferAnswerOptions.h"
+#include "RTCRtpTransceiver.h"
namespace WebCore {
-class MediaConstraints;
class MediaStreamTrack;
-class RTCConfiguration;
-class RTCDTMFSender;
-class RTCDataChannel;
+class PeerConnectionBackend;
+class RTCIceCandidate;
class RTCPeerConnectionErrorCallback;
class RTCSessionDescription;
-class RTCSessionDescriptionCallback;
class RTCStatsCallback;
-class VoidCallback;
-class RTCPeerConnection final : public RefCounted<RTCPeerConnection>, public ScriptWrappable, public RTCPeerConnectionHandlerClient, public EventTargetWithInlineData, public ActiveDOMObject, public MediaStream::Observer {
+class RTCPeerConnection final : public RefCounted<RTCPeerConnection>, public RTCRtpSenderClient, public EventTargetWithInlineData, public ActiveDOMObject {
public:
- static PassRefPtr<RTCPeerConnection> create(ScriptExecutionContext&, const Dictionary& rtcConfiguration, const Dictionary& mediaConstraints, ExceptionCode&);
- ~RTCPeerConnection();
+ static Ref<RTCPeerConnection> create(ScriptExecutionContext&);
+ virtual ~RTCPeerConnection();
- void createOffer(PassRefPtr<RTCSessionDescriptionCallback>, PassRefPtr<RTCPeerConnectionErrorCallback>, const Dictionary& mediaConstraints, ExceptionCode&);
+ using AnswerOptions = RTCAnswerOptions;
+ using DataChannelInit = RTCDataChannelInit;
+ using OfferAnswerOptions = RTCOfferAnswerOptions;
+ using OfferOptions = RTCOfferOptions;
- void createAnswer(PassRefPtr<RTCSessionDescriptionCallback>, PassRefPtr<RTCPeerConnectionErrorCallback>, const Dictionary& mediaConstraints, ExceptionCode&);
+ ExceptionOr<void> initializeWith(Document&, RTCConfiguration&&);
- void setLocalDescription(PassRefPtr<RTCSessionDescription>, PassRefPtr<VoidCallback>, PassRefPtr<RTCPeerConnectionErrorCallback>, ExceptionCode&);
- PassRefPtr<RTCSessionDescription> localDescription(ExceptionCode&);
+ const Vector<std::reference_wrapper<RTCRtpSender>>& getSenders() const { return m_transceiverSet->senders(); }
+ const Vector<std::reference_wrapper<RTCRtpReceiver>>& getReceivers() const { return m_transceiverSet->receivers(); }
+ const Vector<RefPtr<RTCRtpTransceiver>>& getTransceivers() const { return m_transceiverSet->list(); }
- void setRemoteDescription(PassRefPtr<RTCSessionDescription>, PassRefPtr<VoidCallback>, PassRefPtr<RTCPeerConnectionErrorCallback>, ExceptionCode&);
- PassRefPtr<RTCSessionDescription> remoteDescription(ExceptionCode&);
+ // Part of legacy MediaStream-based API (mostly implemented as JS built-ins)
+ Vector<RefPtr<MediaStream>> getRemoteStreams() const { return m_backend->getRemoteStreams(); }
- String signalingState() const;
+ ExceptionOr<Ref<RTCRtpSender>> addTrack(Ref<MediaStreamTrack>&&, const Vector<std::reference_wrapper<MediaStream>>&);
+ ExceptionOr<void> removeTrack(RTCRtpSender&);
- void updateIce(const Dictionary& rtcConfiguration, const Dictionary& mediaConstraints, ExceptionCode&);
+ // This enum is mirrored in RTCRtpTransceiver.h
+ enum class RtpTransceiverDirection { Sendrecv, Sendonly, Recvonly, Inactive };
- void addIceCandidate(RTCIceCandidate*, PassRefPtr<VoidCallback>, PassRefPtr<RTCPeerConnectionErrorCallback>, ExceptionCode&);
+ struct RtpTransceiverInit {
+ RtpTransceiverDirection direction;
+ };
- String iceGatheringState() const;
+ ExceptionOr<Ref<RTCRtpTransceiver>> addTransceiver(Ref<MediaStreamTrack>&&, const RtpTransceiverInit&);
+ ExceptionOr<Ref<RTCRtpTransceiver>> addTransceiver(const String& kind, const RtpTransceiverInit&);
- String iceConnectionState() const;
+ void queuedCreateOffer(RTCOfferOptions&&, PeerConnection::SessionDescriptionPromise&&);
+ void queuedCreateAnswer(RTCAnswerOptions&&, PeerConnection::SessionDescriptionPromise&&);
- MediaStreamVector getLocalStreams() const;
+ void queuedSetLocalDescription(RTCSessionDescription&, DOMPromise<void>&&);
+ RefPtr<RTCSessionDescription> localDescription() const;
+ RefPtr<RTCSessionDescription> currentLocalDescription() const;
+ RefPtr<RTCSessionDescription> pendingLocalDescription() const;
- MediaStreamVector getRemoteStreams() const;
+ void queuedSetRemoteDescription(RTCSessionDescription&, DOMPromise<void>&&);
+ RefPtr<RTCSessionDescription> remoteDescription() const;
+ RefPtr<RTCSessionDescription> currentRemoteDescription() const;
+ RefPtr<RTCSessionDescription> pendingRemoteDescription() const;
- MediaStream* getStreamById(const String& streamId);
+ String signalingState() const;
- void addStream(PassRefPtr<MediaStream>, const Dictionary& mediaConstraints, ExceptionCode&);
+ void queuedAddIceCandidate(RTCIceCandidate&, DOMPromise<void>&&);
- void removeStream(PassRefPtr<MediaStream>, ExceptionCode&);
+ String iceGatheringState() const;
+ String iceConnectionState() const;
- void getStats(PassRefPtr<RTCStatsCallback> successCallback, PassRefPtr<MediaStreamTrack> selector);
+ const RTCConfiguration& getConfiguration() const { return m_configuration; }
+ ExceptionOr<void> setConfiguration(RTCConfiguration&&);
- PassRefPtr<RTCDataChannel> createDataChannel(String label, const Dictionary& dataChannelDict, ExceptionCode&);
+ void getStats(MediaStreamTrack*, Ref<DeferredPromise>&&);
- PassRefPtr<RTCDTMFSender> createDTMFSender(PassRefPtr<MediaStreamTrack>, ExceptionCode&);
+ ExceptionOr<Ref<RTCDataChannel>> createDataChannel(ScriptExecutionContext&, String&&, RTCDataChannelInit&&);
- void close(ExceptionCode&);
+ void close();
- DEFINE_ATTRIBUTE_EVENT_LISTENER(negotiationneeded);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(icecandidate);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(signalingstatechange);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(addstream);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(removestream);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(iceconnectionstatechange);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(datachannel);
+ // EventTarget
+ EventTargetInterface eventTargetInterface() const final { return RTCPeerConnectionEventTargetInterfaceType; }
+ ScriptExecutionContext* scriptExecutionContext() const final { return ActiveDOMObject::scriptExecutionContext(); }
- // RTCPeerConnectionHandlerClient
- virtual void negotiationNeeded() override;
- virtual void didGenerateIceCandidate(PassRefPtr<RTCIceCandidateDescriptor>) override;
- virtual void didChangeSignalingState(SignalingState) override;
- virtual void didChangeIceGatheringState(IceGatheringState) override;
- virtual void didChangeIceConnectionState(IceConnectionState) override;
- virtual void didAddRemoteStream(PassRefPtr<MediaStreamPrivate>) override;
- virtual void didRemoveRemoteStream(MediaStreamPrivate*) override;
- virtual void didAddRemoteDataChannel(std::unique_ptr<RTCDataChannelHandler>) override;
+ using RefCounted::ref;
+ using RefCounted::deref;
- // EventTarget
- virtual EventTargetInterface eventTargetInterface() const override { return RTCPeerConnectionEventTargetInterfaceType; }
- virtual ScriptExecutionContext* scriptExecutionContext() const override { return ActiveDOMObject::scriptExecutionContext(); }
+ // Used for testing with a mock
+ WEBCORE_EXPORT void emulatePlatformEvent(const String& action);
- // ActiveDOMObject
- virtual void stop() override;
+ // API used by PeerConnectionBackend and relatives
+ void addTransceiver(Ref<RTCRtpTransceiver>&&);
+ void setSignalingState(PeerConnectionStates::SignalingState);
+ void updateIceGatheringState(PeerConnectionStates::IceGatheringState);
+ void updateIceConnectionState(PeerConnectionStates::IceConnectionState);
- // MediaStream::Observer
- virtual void didAddOrRemoveTrack() override;
+ void scheduleNegotiationNeededEvent();
- using RefCounted<RTCPeerConnection>::ref;
- using RefCounted<RTCPeerConnection>::deref;
+ RTCRtpSenderClient& senderClient() { return *this; }
+ void fireEvent(Event&);
+ PeerConnectionStates::SignalingState internalSignalingState() const { return m_signalingState; }
+ PeerConnectionStates::IceGatheringState internalIceGatheringState() const { return m_iceGatheringState; }
+ PeerConnectionStates::IceConnectionState internalIceConnectionState() const { return m_iceConnectionState; }
private:
- RTCPeerConnection(ScriptExecutionContext&, PassRefPtr<RTCConfiguration>, PassRefPtr<MediaConstraints>, ExceptionCode&);
+ RTCPeerConnection(ScriptExecutionContext&);
- static PassRefPtr<RTCConfiguration> parseConfiguration(const Dictionary& configuration, ExceptionCode&);
- void scheduleDispatchEvent(PassRefPtr<Event>);
- void scheduledEventTimerFired(Timer<RTCPeerConnection>*);
- bool hasLocalStreamWithTrackId(const String& trackId);
+ void completeAddTransceiver(RTCRtpTransceiver&, const RtpTransceiverInit&);
// EventTarget implementation.
- virtual void refEventTarget() override { ref(); }
- virtual void derefEventTarget() override { deref(); }
+ void refEventTarget() final { ref(); }
+ void derefEventTarget() final { deref(); }
- void changeSignalingState(SignalingState);
- void changeIceGatheringState(IceGatheringState);
- void changeIceConnectionState(IceConnectionState);
+ // ActiveDOMObject
+ void stop() final;
+ const char* activeDOMObjectName() const final;
+ bool canSuspendForDocumentSuspension() const final;
- bool checkStateForLocalDescription(RTCSessionDescription*);
- bool checkStateForRemoteDescription(RTCSessionDescription*);
+ // RTCRtpSenderClient
+ void replaceTrack(RTCRtpSender&, RefPtr<MediaStreamTrack>&&, DOMPromise<void>&&) final;
- SignalingState m_signalingState;
- IceGatheringState m_iceGatheringState;
- IceConnectionState m_iceConnectionState;
+ PeerConnectionStates::SignalingState m_signalingState { PeerConnectionStates::SignalingState::Stable };
+ PeerConnectionStates::IceGatheringState m_iceGatheringState { PeerConnectionStates::IceGatheringState::New };
+ PeerConnectionStates::IceConnectionState m_iceConnectionState { PeerConnectionStates::IceConnectionState::New };
- MediaStreamVector m_localStreams;
- MediaStreamVector m_remoteStreams;
+ std::unique_ptr<RtpTransceiverSet> m_transceiverSet { std::unique_ptr<RtpTransceiverSet>(new RtpTransceiverSet()) };
Vector<RefPtr<RTCDataChannel>> m_dataChannels;
- std::unique_ptr<RTCPeerConnectionHandler> m_peerHandler;
-
- Timer<RTCPeerConnection> m_scheduledEventTimer;
- Vector<RefPtr<Event>> m_scheduledEvents;
+ std::unique_ptr<PeerConnectionBackend> m_backend;
- bool m_stopped;
+ RTCConfiguration m_configuration;
};
} // namespace WebCore
-#endif // ENABLE(MEDIA_STREAM)
-
-#endif // RTCPeerConnection_h
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCPeerConnection.idl b/Source/WebCore/Modules/mediastream/RTCPeerConnection.idl
index 1d6017f65..9ad581351 100644
--- a/Source/WebCore/Modules/mediastream/RTCPeerConnection.idl
+++ b/Source/WebCore/Modules/mediastream/RTCPeerConnection.idl
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2012 Google Inc. All rights reserved.
* Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
+ * Copyright (C) 2015, 2016 Ericsson AB. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -29,65 +30,126 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-[
- Conditional=MEDIA_STREAM,
- ActiveDOMObject,
- CustomConstructor(Dictionary rtcIceServers, optional Dictionary mediaConstraints),
- ConstructorCallWith=ScriptExecutionContext,
- ConstructorRaisesException,
- ConstructorCallWith=ScriptExecutionContext,
- EventTarget,
- InterfaceName=webkitRTCPeerConnection,
-] interface RTCPeerConnection {
- [RaisesException] void createOffer(RTCSessionDescriptionCallback successCallback, [Default=Undefined] optional RTCPeerConnectionErrorCallback failureCallback, optional Dictionary mediaConstraints);
+dictionary RTCOfferAnswerOptions {
+ boolean voiceActivityDetection = true;
+};
+
+dictionary RTCOfferOptions : RTCOfferAnswerOptions {
+ boolean iceRestart = false;
+};
- [RaisesException] void createAnswer(RTCSessionDescriptionCallback successCallback, [Default=Undefined] optional RTCPeerConnectionErrorCallback failureCallback, optional Dictionary mediaConstraints);
+dictionary RTCAnswerOptions : RTCOfferAnswerOptions {
+};
- [RaisesException] void setLocalDescription(RTCSessionDescription description, [Default=Undefined] optional VoidCallback successCallback, [Default=Undefined] optional RTCPeerConnectionErrorCallback failureCallback);
- [GetterRaisesException] readonly attribute RTCSessionDescription localDescription;
+dictionary RTCDataChannelInit {
+ boolean ordered = true;
+ unsigned short maxRetransmitTime;
+ unsigned short maxRetransmits;
+ USVString protocol = "";
+ boolean negotiated = false;
+ unsigned short id;
+};
- [RaisesException] void setRemoteDescription(RTCSessionDescription description, [Default=Undefined] optional VoidCallback successCallback, [Default=Undefined] optional RTCPeerConnectionErrorCallback failureCallback);
- [GetterRaisesException] readonly attribute RTCSessionDescription remoteDescription;
+[
+ ActiveDOMObject,
+ Conditional=WEB_RTC,
+ ConstructorCallWith=ScriptExecutionContext,
+ EnabledAtRuntime=PeerConnection,
+ ExportMacro=WEBCORE_EXPORT,
+ JSBuiltinConstructor,
+] interface RTCPeerConnection : EventTarget {
+ // Private initializer
+ [PrivateIdentifier, CallWith=Document, MayThrowException] void initializeWith(RTCConfiguration configuration);
+
+ // RTP Media API extensions
+ [PrivateIdentifier, PublicIdentifier] sequence<RTCRtpSender> getSenders();
+ sequence<RTCRtpReceiver> getReceivers();
+ sequence<RTCRtpTransceiver> getTransceivers();
+
+ [PrivateIdentifier, PublicIdentifier, MayThrowException] RTCRtpSender addTrack(MediaStreamTrack track, MediaStream... streams);
+ [PrivateIdentifier, PublicIdentifier, MayThrowException] void removeTrack(RTCRtpSender sender);
+
+ [MayThrowException] RTCRtpTransceiver addTransceiver(MediaStreamTrack track, optional RTCRtpTransceiverInit init);
+ [MayThrowException] RTCRtpTransceiver addTransceiver(DOMString kind, optional RTCRtpTransceiverInit init);
+
+ // Legacy MediaSream-based API (implemented on top of the RTP Media API)
+ [JSBuiltin] sequence<MediaStream> getLocalStreams();
+ [PrivateIdentifier, PublicIdentifier] sequence<MediaStream> getRemoteStreams();
+ [JSBuiltin] MediaStream getStreamById(DOMString streamId);
+
+ [JSBuiltin] void addStream(MediaStream stream);
+ [JSBuiltin] void removeStream(MediaStream stream);
+
+ [JSBuiltin] Promise<RTCSessionDescriptionInit> createOffer(optional RTCOfferOptions offerOptions);
+ // Legacy signature: Promise<void> createOffer(RTCSessionDescriptionCallback successCallback
+ // RTCPeerConnectionErrorCallback errorCallback,
+ // optional Dictionary offerOptions);
+
+ [JSBuiltin] Promise<RTCSessionDescriptionInit> createAnswer(optional RTCAnswerOptions answerOptions);
+ // Legacy signature: Promise<void> createAnswer(RTCSessionDescriptionCallback successCallback
+ // RTCPeerConnectionErrorCallback errorCallback,
+ // optional Dictionary answerOptions);
+
+ [JSBuiltin] Promise<void> setLocalDescription(RTCSessionDescription description);
+ // Legacy signature: Promise<void> setLocalDescription(RTCSessionDescription description
+ // VoidCallback successCallback,
+ // RTCPeerConnectionErrorCallback errorCallback);
+
+ readonly attribute RTCSessionDescription localDescription;
+ readonly attribute RTCSessionDescription currentLocalDescription;
+ readonly attribute RTCSessionDescription pendingLocalDescription;
+
+ [JSBuiltin] Promise<void> setRemoteDescription(RTCSessionDescription description);
+ // Legacy signature: Promise<void> setRemoteDescription(RTCSessionDescription description
+ // VoidCallback successCallback,
+ // RTCPeerConnectionErrorCallback errorCallback);
+
+ readonly attribute RTCSessionDescription remoteDescription;
+ readonly attribute RTCSessionDescription currentRemoteDescription;
+ readonly attribute RTCSessionDescription pendingRemoteDescription;
readonly attribute DOMString signalingState;
- [RaisesException] void updateIce(optional Dictionary configuration, optional Dictionary mediaConstraints);
-
- [RaisesException] void addIceCandidate(RTCIceCandidate candidate, VoidCallback successCallback, RTCPeerConnectionErrorCallback failureCallback);
+ [JSBuiltin] Promise<void> addIceCandidate(RTCIceCandidate candidate);
+ // Legacy signature: Promise<void> addIceCandidate(RTCIceCandidate candidate
+ // VoidCallback successCallback,
+ // RTCPeerConnectionErrorCallback errorCallback);
readonly attribute DOMString iceGatheringState;
readonly attribute DOMString iceConnectionState;
- sequence<MediaStream> getLocalStreams();
- sequence<MediaStream> getRemoteStreams();
- MediaStream getStreamById(DOMString streamId);
-
- [StrictTypeChecking, RaisesException] void addStream(MediaStream stream, optional Dictionary mediaConstraints);
- [StrictTypeChecking, RaisesException] void removeStream(MediaStream stream);
+ RTCConfiguration getConfiguration();
+ [MayThrowException] void setConfiguration(RTCConfiguration configuration);
- void getStats(RTCStatsCallback successCallback, [Default=Undefined] optional MediaStreamTrack selector);
+ Promise<RTCStatsReport> getStats(optional MediaStreamTrack? selector = null);
- [RaisesException] RTCDataChannel createDataChannel([TreatNullAs=NullString, TreatUndefinedAs=NullString] DOMString label, optional Dictionary options);
+ // Private API used to implement the overloaded operations above. Queued functions are called by
+ // runQueuedOperation() (defined in RTCPeerConnectionInternals.js).
+ [PrivateIdentifier] Promise<RTCSessionDescriptionInit> queuedCreateOffer(optional RTCOfferOptions offerOptions);
+ [PrivateIdentifier] Promise<RTCSessionDescriptionInit> queuedCreateAnswer(optional RTCAnswerOptions answerOptions);
+ [PrivateIdentifier] Promise<void> queuedSetLocalDescription(RTCSessionDescription description);
+ [PrivateIdentifier] Promise<void> queuedSetRemoteDescription(RTCSessionDescription description);
+ [PrivateIdentifier] Promise<void> queuedAddIceCandidate(RTCIceCandidate candidate);
- [RaisesException] RTCDTMFSender createDTMFSender(MediaStreamTrack track);
+ [CallWith=ScriptExecutionContext, MayThrowException] RTCDataChannel createDataChannel([TreatNullAs=EmptyString] DOMString label, optional RTCDataChannelInit options);
- [RaisesException] void close();
+ void close();
- attribute EventListener onnegotiationneeded;
- attribute EventListener onicecandidate;
- attribute EventListener onsignalingstatechange;
- attribute EventListener onaddstream;
- attribute EventListener onremovestream;
- attribute EventListener oniceconnectionstatechange;
- attribute EventListener ondatachannel;
+ attribute EventHandler onnegotiationneeded;
+ attribute EventHandler onicecandidate;
+ attribute EventHandler onsignalingstatechange;
+ attribute EventHandler ontrack;
+ attribute EventHandler oniceconnectionstatechange;
+ attribute EventHandler onicegatheringstatechange;
+ attribute EventHandler ondatachannel;
- // 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);
+ // Legacy event handler (MediaStream-based API)
+ attribute EventHandler onaddstream;
};
+// This enum is mirrored in RTCRtpTransceiver.idl
+enum RTCRtpTransceiverDirection { "sendrecv", "sendonly", "recvonly", "inactive" };
+
+dictionary RTCRtpTransceiverInit {
+ RTCRtpTransceiverDirection direction = "sendrecv";
+};
diff --git a/Source/WebCore/Modules/mediastream/RTCPeerConnection.js b/Source/WebCore/Modules/mediastream/RTCPeerConnection.js
new file mode 100644
index 000000000..2cb6a4036
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/RTCPeerConnection.js
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2015, 2016 Ericsson AB. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Ericsson 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 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
+ * OWNER 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(WEB_RTC)
+
+function initializeRTCPeerConnection(configuration)
+{
+ "use strict";
+
+ if (configuration == null)
+ configuration = {};
+ else if (!@isObject(configuration))
+ @throwTypeError("RTCPeerConnection argument must be a valid dictionary");
+
+ // FIXME: Handle errors in a better way than catching and re-throwing (http://webkit.org/b/158936)
+ try {
+ this.@initializeWith(configuration);
+ } catch (e) {
+ const message = e.name === "TypeMismatchError" ? "Invalid RTCPeerConnection constructor arguments"
+ : "Error creating RTCPeerConnection";
+ @throwTypeError(message);
+ }
+ this.@operations = [];
+ this.@localStreams = [];
+
+ return this;
+}
+
+function getLocalStreams()
+{
+ "use strict";
+
+ if (!@isRTCPeerConnection(this))
+ throw @makeThisTypeError("RTCPeerConnection", "getLocalStreams");
+
+ return this.@localStreams.slice();
+}
+
+function getStreamById(streamIdArg)
+{
+ "use strict";
+
+ if (!@isRTCPeerConnection(this))
+ throw @makeThisTypeError("RTCPeerConnection", "getStreamById");
+
+ if (arguments.length < 1)
+ @throwTypeError("Not enough arguments");
+
+ const streamId = @String(streamIdArg);
+
+ return this.@localStreams.find(stream => stream.id === streamId)
+ || this.@getRemoteStreams().find(stream => stream.id === streamId)
+ || null;
+}
+
+function addStream(stream)
+{
+ "use strict";
+
+ if (!@isRTCPeerConnection(this))
+ throw @makeThisTypeError("RTCPeerConnection", "addStream");
+
+ if (arguments.length < 1)
+ @throwTypeError("Not enough arguments");
+
+ if (!(stream instanceof @MediaStream))
+ @throwTypeError("Argument 1 ('stream') to RTCPeerConnection.addStream must be an instance of MediaStream");
+
+ if (this.@localStreams.find(localStream => localStream.id === stream.id))
+ return;
+
+ this.@localStreams.@push(stream);
+ stream.@getTracks().forEach(track => this.@addTrack(track, stream));
+}
+
+function removeStream(stream)
+{
+ "use strict";
+
+ if (!@isRTCPeerConnection(this))
+ throw @makeThisTypeError("RTCPeerConnection", "removeStream");
+
+ if (arguments.length < 1)
+ @throwTypeError("Not enough arguments");
+
+ if (!(stream instanceof @MediaStream))
+ @throwTypeError("Argument 1 ('stream') to RTCPeerConnection.removeStream must be an instance of MediaStream");
+
+ const indexOfStreamToRemove = this.@localStreams.findIndex(localStream => localStream.id === stream.id);
+ if (indexOfStreamToRemove === -1)
+ return;
+
+ const senders = this.@getSenders();
+ this.@localStreams[indexOfStreamToRemove].@getTracks().forEach(track => {
+ const senderForTrack = senders.find(sender => sender.track && sender.track.id === track.id);
+ if (senderForTrack)
+ this.@removeTrack(senderForTrack);
+ });
+
+ this.@localStreams.splice(indexOfStreamToRemove, 1);
+}
+
+function createOffer()
+{
+ "use strict";
+
+ if (!@isRTCPeerConnection(this))
+ return @Promise.@reject(@makeThisTypeError("RTCPeerConnection", "createOffer"));
+
+ const peerConnection = this;
+
+ return @callbacksAndDictionaryOverload(arguments, "createOffer", function (options) {
+ // Promise mode
+ return @enqueueOperation(peerConnection, function () {
+ return peerConnection.@queuedCreateOffer(options);
+ });
+ }, function (successCallback, errorCallback, options) {
+ // Legacy callbacks mode
+ @enqueueOperation(peerConnection, function () {
+ return peerConnection.@queuedCreateOffer(options).@then(successCallback, errorCallback);
+ });
+
+ return @Promise.@resolve(@undefined);
+ });
+}
+
+function createAnswer()
+{
+ "use strict";
+
+ if (!@isRTCPeerConnection(this))
+ return @Promise.@reject(@makeThisTypeError("RTCPeerConnection", "createAnswer"));
+
+ const peerConnection = this;
+
+ return @callbacksAndDictionaryOverload(arguments, "createAnswer", function (options) {
+ // Promise mode
+ return @enqueueOperation(peerConnection, function () {
+ return peerConnection.@queuedCreateAnswer(options);
+ });
+ }, function (successCallback, errorCallback, options) {
+ // Legacy callbacks mode
+ @enqueueOperation(peerConnection, function () {
+ return peerConnection.@queuedCreateAnswer(options).@then(successCallback, errorCallback);
+ });
+
+ return @Promise.@resolve(@undefined);
+ });
+}
+
+function setLocalDescription()
+{
+ "use strict";
+
+ if (!@isRTCPeerConnection(this))
+ return @Promise.@reject(@makeThisTypeError("RTCPeerConnection", "setLocalDescription"));
+
+ const peerConnection = this;
+
+ // FIXME: According the spec, we should throw when receiving a RTCSessionDescription.
+ const objectInfo = {
+ "constructor": @RTCSessionDescription,
+ "argName": "description",
+ "argType": "RTCSessionDescription",
+ "maybeDictionary": "true"
+ };
+ return @objectAndCallbacksOverload(arguments, "setLocalDescription", objectInfo, function (description) {
+ // Promise mode
+ return @enqueueOperation(peerConnection, function () {
+ return peerConnection.@queuedSetLocalDescription(description);
+ });
+ }, function (description, successCallback, errorCallback) {
+ // Legacy callbacks mode
+ @enqueueOperation(peerConnection, function () {
+ return peerConnection.@queuedSetLocalDescription(description).@then(successCallback, errorCallback);
+ });
+
+ return @Promise.@resolve(@undefined);
+ });
+}
+
+function setRemoteDescription()
+{
+ "use strict";
+
+ if (!@isRTCPeerConnection(this))
+ return @Promise.@reject(@makeThisTypeError("RTCPeerConnection", "setRemoteDescription"));
+
+ const peerConnection = this;
+
+ // FIXME: According the spec, we should throw when receiving a RTCSessionDescription.
+ const objectInfo = {
+ "constructor": @RTCSessionDescription,
+ "argName": "description",
+ "argType": "RTCSessionDescription",
+ "maybeDictionary": "true"
+ };
+ return @objectAndCallbacksOverload(arguments, "setRemoteDescription", objectInfo, function (description) {
+ // Promise mode
+ return @enqueueOperation(peerConnection, function () {
+ return peerConnection.@queuedSetRemoteDescription(description);
+ });
+ }, function (description, successCallback, errorCallback) {
+ // Legacy callbacks mode
+ @enqueueOperation(peerConnection, function () {
+ return peerConnection.@queuedSetRemoteDescription(description).@then(successCallback, errorCallback);
+ });
+
+ return @Promise.@resolve(@undefined);
+ });
+}
+
+function addIceCandidate()
+{
+ "use strict";
+
+ if (!@isRTCPeerConnection(this))
+ return @Promise.@reject(@makeThisTypeError("RTCPeerConnection", "addIceCandidate"));
+
+ const peerConnection = this;
+
+ const objectInfo = {
+ "constructor": @RTCIceCandidate,
+ "argName": "candidate",
+ "argType": "RTCIceCandidate",
+ "maybeDictionary": "true"
+ };
+ return @objectAndCallbacksOverload(arguments, "addIceCandidate", objectInfo, function (candidate) {
+ // Promise mode
+ return @enqueueOperation(peerConnection, function () {
+ return peerConnection.@queuedAddIceCandidate(candidate);
+ });
+ }, function (candidate, successCallback, errorCallback) {
+ // Legacy callbacks mode
+ @enqueueOperation(peerConnection, function () {
+ return peerConnection.@queuedAddIceCandidate(candidate).@then(successCallback, errorCallback);
+ });
+
+ return @Promise.@resolve(@undefined);
+ });
+}
diff --git a/Source/WebCore/Modules/mediastream/RTCPeerConnectionInternals.js b/Source/WebCore/Modules/mediastream/RTCPeerConnectionInternals.js
new file mode 100644
index 000000000..498d4f886
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/RTCPeerConnectionInternals.js
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2015, 2016 Ericsson AB. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Ericsson 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 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
+ * OWNER 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(WEB_RTC)
+// @internal
+
+// Operation queue as specified in section 4.3.1 (WebRTC 1.0)
+function enqueueOperation(peerConnection, operation)
+{
+ "use strict";
+
+ const operations = peerConnection.@operations;
+
+ function runNext() {
+ operations.@shift();
+ if (operations.length)
+ operations[0]();
+ };
+
+ return new @Promise(function (resolve, reject) {
+ operations.@push(function() {
+ operation().@then(resolve, reject).@then(runNext, runNext);
+ });
+
+ if (operations.length == 1)
+ operations[0]();
+ });
+}
+
+function objectAndCallbacksOverload(args, functionName, objectInfo, promiseMode, legacyMode)
+{
+ "use strict";
+
+ let argsCount = args.length;
+ let objectArg = args[0];
+ let objectArgOk = false;
+
+ if (!argsCount) {
+ if (!objectInfo.defaultsToNull)
+ return @Promise.@reject(new @TypeError("Not enough arguments"));
+
+ objectArg = null;
+ objectArgOk = true;
+ argsCount = 1;
+ } else {
+ const hasMatchingType = objectArg instanceof objectInfo.constructor;
+ if (hasMatchingType)
+ objectArgOk = true;
+ else if (objectInfo.defaultsToNull)
+ objectArgOk = objectArg === null || typeof objectArg === "undefined";
+ else if (objectInfo.maybeDictionary) {
+ try {
+ objectArg = new objectInfo.constructor(objectArg);
+ objectArgOk = true;
+ } catch (e) {
+ objectArgOk = false;
+ }
+ }
+ }
+
+ if (!objectArgOk)
+ return @Promise.@reject(new @TypeError(`Argument 1 ('${objectInfo.argName}') to RTCPeerConnection.${functionName} must be an instance of ${objectInfo.argType}`));
+
+ if (argsCount === 1)
+ return promiseMode(objectArg);
+
+ // More than one argument: Legacy mode
+ if (argsCount < 3)
+ return @Promise.@reject(new @TypeError("Not enough arguments"));
+
+ const successCallback = args[1];
+ const errorCallback = args[2];
+
+ if (typeof successCallback !== "function")
+ return @Promise.@reject(new @TypeError(`Argument 2 ('successCallback') to RTCPeerConnection.${functionName} must be a function`));
+
+ if (typeof errorCallback !== "function")
+ return @Promise.@reject(new @TypeError(`Argument 3 ('errorCallback') to RTCPeerConnection.${functionName} must be a function`));
+
+ return legacyMode(objectArg, successCallback, errorCallback);
+}
+
+function callbacksAndDictionaryOverload(args, functionName, promiseMode, legacyMode)
+{
+ "use strict";
+
+ if (args.length <= 1) {
+ // Zero or one arguments: Promise mode
+ const options = args[0];
+ if (args.length && !@isDictionary(options))
+ return @Promise.@reject(new @TypeError(`Argument 1 ('options') to RTCPeerConnection.${functionName} must be a dictionary`));
+
+ return promiseMode(options);
+ }
+
+ // More than one argument: Legacy mode
+ const successCallback = args[0];
+ const errorCallback = args[1];
+ const options = args[2];
+
+ if (typeof successCallback !== "function")
+ return @Promise.@reject(new @TypeError(`Argument 1 ('successCallback') to RTCPeerConnection.${functionName} must be a function`));
+
+ if (typeof errorCallback !== "function")
+ return @Promise.@reject(new @TypeError(`Argument 2 ('errorCallback') to RTCPeerConnection.${functionName} must be a function`));
+
+ if (args.length > 2 && !@isDictionary(options))
+ return @Promise.@reject(new @TypeError(`Argument 3 ('options') to RTCPeerConnection.${functionName} must be a dictionary`));
+
+ return legacyMode(successCallback, errorCallback, args[2]);
+}
+
+function isRTCPeerConnection(connection)
+{
+ "use strict";
+
+ return @isObject(connection) && !!connection.@operations;
+}
diff --git a/Source/WebCore/Modules/mediastream/RTCPeerConnectionErrorCallback.h b/Source/WebCore/Modules/mediastream/RTCRtpReceiver.cpp
index c2aecba0a..152e60270 100644
--- a/Source/WebCore/Modules/mediastream/RTCPeerConnectionErrorCallback.h
+++ b/Source/WebCore/Modules/mediastream/RTCRtpReceiver.cpp
@@ -1,14 +1,19 @@
/*
- * Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
+ * Copyright (C) 2015 Ericsson AB. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
+ *
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
+ * 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 Ericsson 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
@@ -23,24 +28,18 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef RTCPeerConnectionErrorCallback_h
-#define RTCPeerConnectionErrorCallback_h
-
-#if ENABLE(MEDIA_STREAM)
+#include "config.h"
+#include "RTCRtpReceiver.h"
-#include "DOMError.h"
-#include <wtf/RefCounted.h>
+#if ENABLE(WEB_RTC)
namespace WebCore {
-class RTCPeerConnectionErrorCallback : public RefCounted<RTCPeerConnectionErrorCallback> {
-public:
- virtual ~RTCPeerConnectionErrorCallback() { }
- virtual bool handleEvent(DOMError*) = 0;
-};
+RTCRtpReceiver::RTCRtpReceiver(Ref<MediaStreamTrack>&& track)
+ : RTCRtpSenderReceiverBase(WTFMove(track))
+{
+}
} // namespace WebCore
-#endif // ENABLE(MEDIA_STREAM)
-
-#endif // RTCPeerConnectionErrorCallback_h
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCRtpReceiver.h b/Source/WebCore/Modules/mediastream/RTCRtpReceiver.h
new file mode 100644
index 000000000..ba787a63e
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/RTCRtpReceiver.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 Ericsson AB. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Ericsson 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 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
+ * OWNER 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(WEB_RTC)
+
+#include "RTCRtpSenderReceiverBase.h"
+
+namespace WebCore {
+
+class RTCRtpReceiver : public RTCRtpSenderReceiverBase {
+public:
+ static Ref<RTCRtpReceiver> create(Ref<MediaStreamTrack>&& track)
+ {
+ return adoptRef(*new RTCRtpReceiver(WTFMove(track)));
+ }
+
+ bool isDispatched() const { return m_isDispatched; }
+ void setDispatched(bool isDispatched) { m_isDispatched = isDispatched; }
+
+private:
+ explicit RTCRtpReceiver(Ref<MediaStreamTrack>&&);
+
+ bool m_isDispatched { false };
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCPeerConnectionErrorCallback.idl b/Source/WebCore/Modules/mediastream/RTCRtpReceiver.idl
index 4b87ad7ff..645ab3cbc 100644
--- a/Source/WebCore/Modules/mediastream/RTCPeerConnectionErrorCallback.idl
+++ b/Source/WebCore/Modules/mediastream/RTCRtpReceiver.idl
@@ -1,14 +1,19 @@
/*
- * Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
+ * Copyright (C) 2015 Ericsson AB. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
+ *
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
+ * 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 Ericsson 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
@@ -24,8 +29,8 @@
*/
[
- Conditional=MEDIA_STREAM,
-] callback interface RTCPeerConnectionErrorCallback {
- boolean handleEvent(DOMError error);
+ Conditional=WEB_RTC,
+ EnabledAtRuntime=PeerConnection,
+] interface RTCRtpReceiver {
+ readonly attribute MediaStreamTrack track;
};
-
diff --git a/Source/WebCore/Modules/mediastream/RTCRtpSender.cpp b/Source/WebCore/Modules/mediastream/RTCRtpSender.cpp
new file mode 100644
index 000000000..53b6f2cd4
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/RTCRtpSender.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2015 Ericsson AB. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Ericsson 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 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
+ * OWNER 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 "RTCRtpSender.h"
+
+#if ENABLE(WEB_RTC)
+
+#include "ExceptionCode.h"
+
+namespace WebCore {
+
+Ref<RTCRtpSender> RTCRtpSender::create(Ref<MediaStreamTrack>&& track, Vector<String>&& mediaStreamIds, RTCRtpSenderClient& client)
+{
+ const String& trackKind = track->kind();
+ return adoptRef(*new RTCRtpSender(WTFMove(track), trackKind, WTFMove(mediaStreamIds), client));
+}
+
+Ref<RTCRtpSender> RTCRtpSender::create(const String& trackKind, Vector<String>&& mediaStreamIds, RTCRtpSenderClient& client)
+{
+ return adoptRef(*new RTCRtpSender(nullptr, trackKind, WTFMove(mediaStreamIds), client));
+}
+
+RTCRtpSender::RTCRtpSender(RefPtr<MediaStreamTrack>&& track, const String& trackKind, Vector<String>&& mediaStreamIds, RTCRtpSenderClient& client)
+ : RTCRtpSenderReceiverBase()
+ , m_trackKind(trackKind)
+ , m_mediaStreamIds(WTFMove(mediaStreamIds))
+ , m_client(&client)
+{
+ setTrack(WTFMove(track));
+}
+
+void RTCRtpSender::setTrack(RefPtr<MediaStreamTrack>&& track)
+{
+ // Save the id from the first non-null track set. That id will be used to negotiate the sender
+ // even if the track is replaced.
+ if (!m_track && track)
+ m_trackId = track->id();
+
+ m_track = WTFMove(track);
+}
+
+ExceptionOr<void> RTCRtpSender::replaceTrack(Ref<MediaStreamTrack>&& withTrack, DOMPromise<void>&& promise)
+{
+ if (isStopped()) {
+ promise.reject(INVALID_STATE_ERR);
+ return { };
+ }
+
+ if (m_trackKind != withTrack->kind())
+ return Exception { TypeError };
+
+ m_client->replaceTrack(*this, WTFMove(withTrack), WTFMove(promise));
+
+ return { };
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCRtpSender.h b/Source/WebCore/Modules/mediastream/RTCRtpSender.h
new file mode 100644
index 000000000..95bea8b7b
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/RTCRtpSender.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2015 Ericsson AB. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Ericsson 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 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
+ * OWNER 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(WEB_RTC)
+
+#include "PeerConnectionBackend.h"
+#include "RTCRtpSenderReceiverBase.h"
+
+namespace WebCore {
+
+class RTCRtpSenderClient {
+public:
+ virtual void replaceTrack(RTCRtpSender&, RefPtr<MediaStreamTrack>&&, DOMPromise<void>&&) = 0;
+
+ virtual ~RTCRtpSenderClient() { }
+};
+
+class RTCRtpSender : public RTCRtpSenderReceiverBase {
+public:
+ static Ref<RTCRtpSender> create(Ref<MediaStreamTrack>&&, Vector<String>&& mediaStreamIds, RTCRtpSenderClient&);
+ static Ref<RTCRtpSender> create(const String& trackKind, Vector<String>&& mediaStreamIds, RTCRtpSenderClient&);
+
+ const String& trackId() const { return m_trackId; }
+ const String& trackKind() const { return m_trackKind; }
+
+ const Vector<String>& mediaStreamIds() const { return m_mediaStreamIds; }
+ void setMediaStreamIds(Vector<String>&& mediaStreamIds) { m_mediaStreamIds = WTFMove(mediaStreamIds); }
+
+ bool isStopped() const { return !m_client; }
+ void stop() { m_client = nullptr; }
+ void setTrack(RefPtr<MediaStreamTrack>&&);
+
+ ExceptionOr<void> replaceTrack(Ref<MediaStreamTrack>&&, DOMPromise<void>&&);
+
+private:
+ RTCRtpSender(RefPtr<MediaStreamTrack>&&, const String& trackKind, Vector<String>&& mediaStreamIds, RTCRtpSenderClient&);
+
+ String m_trackId;
+ String m_trackKind;
+ Vector<String> m_mediaStreamIds;
+ RTCRtpSenderClient* m_client;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCRtpSender.idl b/Source/WebCore/Modules/mediastream/RTCRtpSender.idl
new file mode 100644
index 000000000..21f91fd8e
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/RTCRtpSender.idl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 Ericsson AB. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Ericsson 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 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
+ * OWNER 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=WEB_RTC,
+ EnabledAtRuntime=PeerConnection,
+] interface RTCRtpSender {
+ readonly attribute MediaStreamTrack? track;
+
+ [MayThrowException] Promise<void> replaceTrack(MediaStreamTrack withTrack);
+};
diff --git a/Source/WebCore/Modules/mediastream/RTCRtpSenderReceiverBase.h b/Source/WebCore/Modules/mediastream/RTCRtpSenderReceiverBase.h
new file mode 100644
index 000000000..3dd219a26
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/RTCRtpSenderReceiverBase.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 Ericsson AB. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Ericsson 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 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
+ * OWNER 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(WEB_RTC)
+
+#include "MediaStreamTrack.h"
+#include "ScriptWrappable.h"
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+class MediaStreamTrack;
+
+class RTCRtpSenderReceiverBase : public RefCounted<RTCRtpSenderReceiverBase>, public ScriptWrappable {
+public:
+ virtual ~RTCRtpSenderReceiverBase() = default;
+
+ MediaStreamTrack* track() { return m_track.get(); }
+
+protected:
+ RTCRtpSenderReceiverBase() = default;
+
+ RTCRtpSenderReceiverBase(Ref<MediaStreamTrack>&& track)
+ : m_track(WTFMove(track))
+ { }
+
+ RefPtr<MediaStreamTrack> m_track;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCRtpTransceiver.cpp b/Source/WebCore/Modules/mediastream/RTCRtpTransceiver.cpp
new file mode 100644
index 000000000..733db7be0
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/RTCRtpTransceiver.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2016 Ericsson AB. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Ericsson 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 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
+ * OWNER 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 "RTCRtpTransceiver.h"
+
+#if ENABLE(WEB_RTC)
+
+#include <wtf/NeverDestroyed.h>
+
+namespace WebCore {
+
+#define STRING_FUNCTION(name) \
+ static const String& name##String() \
+ { \
+ static NeverDestroyed<const String> name { ASCIILiteral(#name) }; \
+ return name; \
+ }
+
+STRING_FUNCTION(sendrecv)
+STRING_FUNCTION(sendonly)
+STRING_FUNCTION(recvonly)
+STRING_FUNCTION(inactive)
+
+String RTCRtpTransceiver::getNextMid()
+{
+ static unsigned mid = 0;
+ return String::number(++mid);
+}
+
+RTCRtpTransceiver::RTCRtpTransceiver(Ref<RTCRtpSender>&& sender, Ref<RTCRtpReceiver>&& receiver)
+ : m_direction(Direction::Sendrecv)
+ , m_sender(WTFMove(sender))
+ , m_receiver(WTFMove(receiver))
+ , m_iceTransport(RTCIceTransport::create())
+{
+}
+
+const String& RTCRtpTransceiver::directionString() const
+{
+ switch (m_direction) {
+ case Direction::Sendrecv: return sendrecvString();
+ case Direction::Sendonly: return sendonlyString();
+ case Direction::Recvonly: return recvonlyString();
+ case Direction::Inactive: return inactiveString();
+ }
+
+ ASSERT_NOT_REACHED();
+ return inactiveString();
+}
+
+bool RTCRtpTransceiver::hasSendingDirection() const
+{
+ return m_direction == Direction::Sendrecv || m_direction == Direction::Sendonly;
+}
+
+void RTCRtpTransceiver::enableSendingDirection()
+{
+ if (m_direction == Direction::Recvonly)
+ m_direction = Direction::Sendrecv;
+ else if (m_direction == Direction::Inactive)
+ m_direction = Direction::Sendonly;
+}
+
+void RTCRtpTransceiver::disableSendingDirection()
+{
+ if (m_direction == Direction::Sendrecv)
+ m_direction = Direction::Recvonly;
+ else if (m_direction == Direction::Sendonly)
+ m_direction = Direction::Inactive;
+}
+
+void RtpTransceiverSet::append(Ref<RTCRtpTransceiver>&& transceiver)
+{
+ m_senders.append(transceiver->sender());
+ m_receivers.append(transceiver->receiver());
+
+ m_transceivers.append(WTFMove(transceiver));
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCRtpTransceiver.h b/Source/WebCore/Modules/mediastream/RTCRtpTransceiver.h
new file mode 100644
index 000000000..e000cb2c1
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/RTCRtpTransceiver.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2016 Ericsson AB. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Ericsson 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 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
+ * OWNER 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(WEB_RTC)
+
+#include "RTCIceTransport.h"
+#include "RTCRtpReceiver.h"
+#include "RTCRtpSender.h"
+#include "ScriptWrappable.h"
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class RTCRtpTransceiver : public RefCounted<RTCRtpTransceiver>, public ScriptWrappable {
+public:
+ // This enum is mirrored in RTCPeerConnection.h
+ enum class Direction { Sendrecv, Sendonly, Recvonly, Inactive };
+
+ static Ref<RTCRtpTransceiver> create(Ref<RTCRtpSender>&& sender, Ref<RTCRtpReceiver>&& receiver) { return adoptRef(*new RTCRtpTransceiver(WTFMove(sender), WTFMove(receiver))); }
+ virtual ~RTCRtpTransceiver() { }
+
+ bool hasSendingDirection() const;
+ void enableSendingDirection();
+ void disableSendingDirection();
+
+ const String& directionString() const;
+ Direction direction() const { return m_direction; }
+ void setDirection(Direction direction) { m_direction = direction; }
+
+ const String& provisionalMid() const { return m_provisionalMid; }
+ void setProvisionalMid(const String& provisionalMid) { m_provisionalMid = provisionalMid; }
+
+ const String& mid() const { return m_mid; }
+ void setMid(const String& mid) { m_mid = mid; }
+
+ RTCRtpSender& sender() { return m_sender.get(); }
+ RTCRtpReceiver& receiver() { return m_receiver.get(); }
+
+ bool stopped() const { return m_stopped; }
+ void stop() { m_stopped = true; }
+
+ // FIXME: Temporary solution to keep track of ICE states for this transceiver. Later, each
+ // sender and receiver will have up to two DTLS transports, which in turn will have an ICE
+ // transport each.
+ RTCIceTransport& iceTransport() { return m_iceTransport.get(); }
+
+ static String getNextMid();
+
+private:
+ RTCRtpTransceiver(Ref<RTCRtpSender>&&, Ref<RTCRtpReceiver>&&);
+
+ String m_provisionalMid;
+ String m_mid;
+
+ Direction m_direction;
+
+ Ref<RTCRtpSender> m_sender;
+ Ref<RTCRtpReceiver> m_receiver;
+
+ bool m_stopped { false };
+
+ Ref<RTCIceTransport> m_iceTransport;
+};
+
+class RtpTransceiverSet {
+public:
+ const Vector<RefPtr<RTCRtpTransceiver>>& list() const { return m_transceivers; }
+ void append(Ref<RTCRtpTransceiver>&&);
+
+ const Vector<std::reference_wrapper<RTCRtpSender>>& senders() const { return m_senders; }
+ const Vector<std::reference_wrapper<RTCRtpReceiver>>& receivers() const { return m_receivers; }
+
+private:
+ Vector<RefPtr<RTCRtpTransceiver>> m_transceivers;
+
+ Vector<std::reference_wrapper<RTCRtpSender>> m_senders;
+ Vector<std::reference_wrapper<RTCRtpReceiver>> m_receivers;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCRtpTransceiver.idl b/Source/WebCore/Modules/mediastream/RTCRtpTransceiver.idl
new file mode 100644
index 000000000..68cf1ea48
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/RTCRtpTransceiver.idl
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 Ericsson AB. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Ericsson 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 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
+ * OWNER 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=WEB_RTC,
+ EnabledAtRuntime=PeerConnection,
+] interface RTCRtpTransceiver {
+ readonly attribute DOMString? mid;
+ readonly attribute RTCRtpSender sender;
+ readonly attribute RTCRtpReceiver receiver;
+ readonly attribute boolean stopped;
+ readonly attribute RTCRtpTransceiverDirection direction;
+
+ void setDirection(RTCRtpTransceiverDirection direction);
+ void stop();
+};
+
+// This enum is mirrored in RTCPeerConnection.idl
+enum RTCRtpTransceiverDirection { "sendrecv", "sendonly", "recvonly", "inactive" };
diff --git a/Source/WebCore/Modules/mediastream/RTCSessionDescription.cpp b/Source/WebCore/Modules/mediastream/RTCSessionDescription.cpp
index 85bfb5c31..4dab0632b 100644
--- a/Source/WebCore/Modules/mediastream/RTCSessionDescription.cpp
+++ b/Source/WebCore/Modules/mediastream/RTCSessionDescription.cpp
@@ -30,84 +30,28 @@
*/
#include "config.h"
-
-#if ENABLE(MEDIA_STREAM)
-
#include "RTCSessionDescription.h"
-#include "Dictionary.h"
-#include "ExceptionCode.h"
-#include "RTCSessionDescriptionDescriptor.h"
+#if ENABLE(WEB_RTC)
namespace WebCore {
-static bool verifyType(const String& type)
-{
- return type == "offer" || type == "pranswer" || type == "answer";
-}
-
-PassRefPtr<RTCSessionDescription> RTCSessionDescription::create(const Dictionary& dictionary, ExceptionCode& ec)
-{
- String type;
- bool ok = dictionary.get("type", type);
- if (ok && !verifyType(type)) {
- ec = TYPE_MISMATCH_ERR;
- return nullptr;
- }
-
- String sdp;
- ok = dictionary.get("sdp", sdp);
- if (ok && sdp.isEmpty()) {
- ec = TYPE_MISMATCH_ERR;
- return nullptr;
- }
-
- return adoptRef(new RTCSessionDescription(RTCSessionDescriptionDescriptor::create(type, sdp)));
-}
-
-PassRefPtr<RTCSessionDescription> RTCSessionDescription::create(PassRefPtr<RTCSessionDescriptionDescriptor> descriptor)
-{
- ASSERT(descriptor);
- return adoptRef(new RTCSessionDescription(descriptor));
-}
-
-RTCSessionDescription::RTCSessionDescription(PassRefPtr<RTCSessionDescriptionDescriptor> descriptor)
- : m_descriptor(descriptor)
-{
-}
-
-RTCSessionDescription::~RTCSessionDescription()
-{
-}
-
-const String& RTCSessionDescription::type() const
-{
- return m_descriptor->type();
-}
-
-void RTCSessionDescription::setType(const String& type, ExceptionCode& ec)
-{
- if (verifyType(type))
- m_descriptor->setType(type);
- else
- ec = TYPE_MISMATCH_ERR;
-}
-
-const String& RTCSessionDescription::sdp() const
+inline RTCSessionDescription::RTCSessionDescription(SdpType type, const String& sdp)
+ : m_type(type)
+ , m_sdp(sdp)
{
- return m_descriptor->sdp();
}
-void RTCSessionDescription::setSdp(const String& sdp)
+Ref<RTCSessionDescription> RTCSessionDescription::create(const Init& dictionary)
{
- m_descriptor->setSdp(sdp);
+ return create(dictionary.type, dictionary.sdp);
}
-RTCSessionDescriptionDescriptor* RTCSessionDescription::descriptor()
+Ref<RTCSessionDescription> RTCSessionDescription::create(SdpType type, const String& sdp)
{
- return m_descriptor.get();
+ return adoptRef(*new RTCSessionDescription(type, sdp));
}
} // namespace WebCore
-#endif // ENABLE(MEDIA_STREAM)
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCSessionDescription.h b/Source/WebCore/Modules/mediastream/RTCSessionDescription.h
index fafe2b65a..df3fc6b7a 100644
--- a/Source/WebCore/Modules/mediastream/RTCSessionDescription.h
+++ b/Source/WebCore/Modules/mediastream/RTCSessionDescription.h
@@ -28,44 +28,38 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef RTCSessionDescription_h
-#define RTCSessionDescription_h
+#pragma once
-#if ENABLE(MEDIA_STREAM)
+#if ENABLE(WEB_RTC)
-#include "ExceptionBase.h"
+#include "ExceptionOr.h"
#include "ScriptWrappable.h"
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/text/WTFString.h>
namespace WebCore {
-class Dictionary;
-class RTCSessionDescriptionDescriptor;
-
class RTCSessionDescription : public RefCounted<RTCSessionDescription>, public ScriptWrappable {
public:
- static PassRefPtr<RTCSessionDescription> create(const Dictionary&, ExceptionCode&);
- static PassRefPtr<RTCSessionDescription> create(PassRefPtr<RTCSessionDescriptionDescriptor>);
- virtual ~RTCSessionDescription();
+ enum class SdpType { Offer, Pranswer, Answer, Rollback };
- const String& type() const;
- void setType(const String&, ExceptionCode&);
+ struct Init {
+ SdpType type;
+ String sdp;
+ };
+ static Ref<RTCSessionDescription> create(const Init&);
+ static Ref<RTCSessionDescription> create(SdpType, const String& sdp);
- const String& sdp() const;
- void setSdp(const String&);
+ SdpType type() const { return m_type; }
- RTCSessionDescriptionDescriptor* descriptor();
+ const String& sdp() const { return m_sdp; }
+ void setSdp(const String& sdp) { m_sdp = sdp; }
private:
- explicit RTCSessionDescription(PassRefPtr<RTCSessionDescriptionDescriptor>);
+ explicit RTCSessionDescription(SdpType, const String& sdp);
- RefPtr<RTCSessionDescriptionDescriptor> m_descriptor;
+ SdpType m_type;
+ String m_sdp;
};
} // namespace WebCore
-#endif // ENABLE(MEDIA_STREAM)
-
-#endif // RTCSessionDescription_h
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCSessionDescription.idl b/Source/WebCore/Modules/mediastream/RTCSessionDescription.idl
index e62998b16..27c6571ea 100644
--- a/Source/WebCore/Modules/mediastream/RTCSessionDescription.idl
+++ b/Source/WebCore/Modules/mediastream/RTCSessionDescription.idl
@@ -30,11 +30,22 @@
*/
[
- Conditional=MEDIA_STREAM,
- CustomConstructor(optional Dictionary dictionary),
- ConstructorRaisesException
+ Conditional=WEB_RTC,
+ Constructor(RTCSessionDescriptionInit descriptionInitDict),
+ EnabledAtRuntime=PeerConnection,
+ ImplementationLacksVTable,
+ PrivateIdentifier,
+ PublicIdentifier,
] interface RTCSessionDescription {
- [SetterRaisesException] attribute DOMString type;
- attribute DOMString sdp;
+ [SetterMayThrowException] readonly attribute RTCSdpType type;
+ readonly attribute DOMString sdp;
+
+ serializer = {type, sdp};
};
+enum RTCSdpType { "offer", "pranswer", "answer", "rollback" };
+
+dictionary RTCSessionDescriptionInit {
+ required RTCSdpType type;
+ DOMString sdp = "";
+};
diff --git a/Source/WebCore/Modules/mediastream/RTCSessionDescriptionRequestImpl.cpp b/Source/WebCore/Modules/mediastream/RTCSessionDescriptionRequestImpl.cpp
deleted file mode 100644
index 872a54e18..000000000
--- a/Source/WebCore/Modules/mediastream/RTCSessionDescriptionRequestImpl.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2012 Google Inc. All rights reserved.
- * Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must 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 Google 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 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
- * OWNER 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_STREAM)
-
-#include "RTCSessionDescriptionRequestImpl.h"
-
-#include "RTCPeerConnection.h"
-#include "RTCPeerConnectionErrorCallback.h"
-#include "RTCSessionDescription.h"
-#include "RTCSessionDescriptionCallback.h"
-#include "RTCSessionDescriptionDescriptor.h"
-
-namespace WebCore {
-
-PassRefPtr<RTCSessionDescriptionRequestImpl> RTCSessionDescriptionRequestImpl::create(ScriptExecutionContext* context, PassRefPtr<RTCSessionDescriptionCallback> successCallback, PassRefPtr<RTCPeerConnectionErrorCallback> errorCallback)
-{
- RefPtr<RTCSessionDescriptionRequestImpl> request = adoptRef(new RTCSessionDescriptionRequestImpl(context, successCallback, errorCallback));
- request->suspendIfNeeded();
- return request.release();
-}
-
-RTCSessionDescriptionRequestImpl::RTCSessionDescriptionRequestImpl(ScriptExecutionContext* context, PassRefPtr<RTCSessionDescriptionCallback> successCallback, PassRefPtr<RTCPeerConnectionErrorCallback> errorCallback)
- : ActiveDOMObject(context)
- , m_successCallback(successCallback)
- , m_errorCallback(errorCallback)
-{
-}
-
-RTCSessionDescriptionRequestImpl::~RTCSessionDescriptionRequestImpl()
-{
-}
-
-void RTCSessionDescriptionRequestImpl::requestSucceeded(PassRefPtr<RTCSessionDescriptionDescriptor> descriptor)
-{
- if (m_successCallback) {
- RefPtr<RTCSessionDescription> sessionDescription = RTCSessionDescription::create(descriptor);
- m_successCallback->handleEvent(sessionDescription.get());
- }
-
- clear();
-}
-
-void RTCSessionDescriptionRequestImpl::requestFailed(const String& error)
-{
- if (m_errorCallback)
- m_errorCallback->handleEvent(DOMError::create(error).get());
-
- clear();
-}
-
-void RTCSessionDescriptionRequestImpl::stop()
-{
- clear();
-}
-
-void RTCSessionDescriptionRequestImpl::clear()
-{
- m_successCallback.clear();
- m_errorCallback.clear();
-}
-
-} // namespace WebCore
-
-#endif // ENABLE(MEDIA_STREAM)
diff --git a/Source/WebCore/Modules/mediastream/RTCStatsCallback.h b/Source/WebCore/Modules/mediastream/RTCStatsCallback.h
deleted file mode 100644
index 419b82025..000000000
--- a/Source/WebCore/Modules/mediastream/RTCStatsCallback.h
+++ /dev/null
@@ -1,46 +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 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 RTCStatsCallback_h
-#define RTCStatsCallback_h
-
-#if ENABLE(MEDIA_STREAM)
-
-#include <wtf/RefCounted.h>
-
-namespace WebCore {
-
-class RTCStatsResponse;
-
-class RTCStatsCallback : public RefCounted<RTCStatsCallback> {
-public:
- virtual ~RTCStatsCallback() { }
- virtual bool handleEvent(RTCStatsResponse*) = 0;
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(MEDIA_STREAM)
-
-#endif // RTCStatsCallback_h
diff --git a/Source/WebCore/Modules/mediastream/RTCStatsReport.cpp b/Source/WebCore/Modules/mediastream/RTCStatsReport.cpp
index 27937eba3..146415b89 100644
--- a/Source/WebCore/Modules/mediastream/RTCStatsReport.cpp
+++ b/Source/WebCore/Modules/mediastream/RTCStatsReport.cpp
@@ -24,51 +24,12 @@
*/
#include "config.h"
-
-#if ENABLE(MEDIA_STREAM)
-
#include "RTCStatsReport.h"
-#include <wtf/text/StringHash.h>
+#if ENABLE(WEB_RTC)
namespace WebCore {
-PassRefPtr<RTCStatsReport> RTCStatsReport::create(const String& id, const String& type, double timestamp)
-{
- return adoptRef(new RTCStatsReport(id, type, timestamp));
-}
-
-RTCStatsReport::RTCStatsReport(const String& id, const String& type, double timestamp)
- : m_id(id)
- , m_type(type)
- , m_timestamp(timestamp)
-{
-}
-
-Vector<String> RTCStatsReport::names() const
-{
- Vector<String> result;
- for (HashMap<String, String>::const_iterator it = m_stats.begin(); it != m_stats.end(); ++it) {
- result.append(it->key);
- }
- return result;
-}
-
-const PassRefPtr<RTCStatsReport> RTCStatsReport::local()
-{
- return this;
-}
-
-const PassRefPtr<RTCStatsReport> RTCStatsReport::remote()
-{
- return this;
-}
-
-void RTCStatsReport::addStatistic(const String& name, const String& value)
-{
- m_stats.add(name, value);
-}
-
} // namespace WebCore
-#endif // ENABLE(MEDIA_STREAM)
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCStatsReport.h b/Source/WebCore/Modules/mediastream/RTCStatsReport.h
index f7b8afe29..3f4918020 100644
--- a/Source/WebCore/Modules/mediastream/RTCStatsReport.h
+++ b/Source/WebCore/Modules/mediastream/RTCStatsReport.h
@@ -22,44 +22,19 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef RTCStatsReport_h
-#define RTCStatsReport_h
+#pragma once
-#include "ScriptWrappable.h"
-#include <wtf/HashMap.h>
-#include <wtf/PassRefPtr.h>
+#include <wtf/Ref.h>
#include <wtf/RefCounted.h>
-#include <wtf/Vector.h>
-#include <wtf/text/WTFString.h>
namespace WebCore {
-class RTCStatsReport : public RefCounted<RTCStatsReport>, public ScriptWrappable {
+class RTCStatsReport : public RefCounted<RTCStatsReport> {
public:
- static PassRefPtr<RTCStatsReport> create(const String& id, const String& type, double timestamp);
-
- double timestamp() const { return m_timestamp; }
- String id() { return m_id; }
- String type() { return m_type; }
- String stat(const String& name) { return m_stats.get(name); }
- Vector<String> names() const;
-
- // DEPRECATED
- const PassRefPtr<RTCStatsReport> local();
- // DEPRECATED
- const PassRefPtr<RTCStatsReport> remote();
-
- void addStatistic(const String& name, const String& value);
+ static Ref<RTCStatsReport> create() { return adoptRef(*new RTCStatsReport); }
private:
- RTCStatsReport(const String& id, const String& type, double timestamp);
-
- String m_id;
- String m_type;
- double m_timestamp;
- HashMap<String, String> m_stats;
+ RTCStatsReport() = default;
};
} // namespace WebCore
-
-#endif // RTCStatsReport_h
diff --git a/Source/WebCore/Modules/mediastream/RTCStatsReport.idl b/Source/WebCore/Modules/mediastream/RTCStatsReport.idl
index aa04b7358..8452534b3 100644
--- a/Source/WebCore/Modules/mediastream/RTCStatsReport.idl
+++ b/Source/WebCore/Modules/mediastream/RTCStatsReport.idl
@@ -24,15 +24,9 @@
[
NoInterfaceObject,
- Conditional=MEDIA_STREAM,
+ Conditional=WEB_RTC,
ImplementationLacksVTable,
] interface RTCStatsReport {
- readonly attribute Date timestamp;
- readonly attribute DOMString id;
- readonly attribute DOMString type;
- DOMString stat(DOMString name);
- sequence<DOMString> names();
- // DEPRECATED - fake for old RTCStatsElement object.
- readonly attribute RTCStatsReport local;
- readonly attribute RTCStatsReport remote;
+ // FIXME: Make it setlike
+ //readonly maplike<DOMString, object>;
};
diff --git a/Source/WebCore/Modules/mediastream/RTCStatsRequestImpl.cpp b/Source/WebCore/Modules/mediastream/RTCStatsRequestImpl.cpp
deleted file mode 100644
index 26456087c..000000000
--- a/Source/WebCore/Modules/mediastream/RTCStatsRequestImpl.cpp
+++ /dev/null
@@ -1,92 +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 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(MEDIA_STREAM)
-
-#include "RTCStatsRequestImpl.h"
-
-#include "MediaStreamTrack.h"
-#include "RTCStatsCallback.h"
-#include "RTCStatsRequest.h"
-#include "RTCStatsResponse.h"
-
-namespace WebCore {
-
-PassRefPtr<RTCStatsRequestImpl> RTCStatsRequestImpl::create(ScriptExecutionContext* context, PassRefPtr<RTCStatsCallback> callback, PassRefPtr<MediaStreamTrack> selector)
-{
- RefPtr<RTCStatsRequestImpl> request = adoptRef(new RTCStatsRequestImpl(context, callback, selector));
- request->suspendIfNeeded();
- return request.release();
-}
-
-RTCStatsRequestImpl::RTCStatsRequestImpl(ScriptExecutionContext* context, PassRefPtr<RTCStatsCallback> callback, PassRefPtr<MediaStreamTrack> selector)
- : ActiveDOMObject(context)
- , m_successCallback(callback)
- , m_track(selector)
-{
-}
-
-RTCStatsRequestImpl::~RTCStatsRequestImpl()
-{
-}
-
-PassRefPtr<RTCStatsResponseBase> RTCStatsRequestImpl::createResponse()
-{
- return RTCStatsResponse::create();
-}
-
-bool RTCStatsRequestImpl::hasSelector()
-{
- return m_track;
-}
-
-MediaStreamTrack* RTCStatsRequestImpl::track()
-{
- return m_track.get();
-}
-
-void RTCStatsRequestImpl::requestSucceeded(PassRefPtr<RTCStatsResponseBase> response)
-{
- if (!m_successCallback)
- return;
- m_successCallback->handleEvent(static_cast<RTCStatsResponse*>(response.get()));
- clear();
-}
-
-void RTCStatsRequestImpl::stop()
-{
- clear();
-}
-
-void RTCStatsRequestImpl::clear()
-{
- m_successCallback.clear();
-}
-
-
-} // namespace WebCore
-
-#endif // ENABLE(MEDIA_STREAM)
diff --git a/Source/WebCore/Modules/mediastream/RTCStatsRequestImpl.h b/Source/WebCore/Modules/mediastream/RTCStatsRequestImpl.h
deleted file mode 100644
index b95f4503c..000000000
--- a/Source/WebCore/Modules/mediastream/RTCStatsRequestImpl.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.
- *
- * 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 RTCStatsRequestImpl_h
-#define RTCStatsRequestImpl_h
-
-#if ENABLE(MEDIA_STREAM)
-
-#include "ActiveDOMObject.h"
-#include "RTCStatsRequest.h"
-#include "RTCStatsResponse.h"
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/text/WTFString.h>
-
-namespace WebCore {
-
-class RTCStatsCallback;
-
-class RTCStatsRequestImpl : public RTCStatsRequest, public ActiveDOMObject {
-public:
- static PassRefPtr<RTCStatsRequestImpl> create(ScriptExecutionContext*, PassRefPtr<RTCStatsCallback>, PassRefPtr<MediaStreamTrack>);
- virtual ~RTCStatsRequestImpl();
-
- virtual PassRefPtr<RTCStatsResponseBase> createResponse() override;
- virtual bool hasSelector() override;
- virtual MediaStreamTrack* track() override;
-
- virtual void requestSucceeded(PassRefPtr<RTCStatsResponseBase>) override;
-
- // ActiveDOMObject
- virtual void stop() override;
-
-private:
- RTCStatsRequestImpl(ScriptExecutionContext*, PassRefPtr<RTCStatsCallback>, PassRefPtr<MediaStreamTrack>);
-
- void clear();
-
- RefPtr<RTCStatsCallback> m_successCallback;
- RefPtr<MediaStreamTrack> m_track;
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(MEDIA_STREAM)
-
-#endif // RTCStatsRequestImpl_h
diff --git a/Source/WebCore/Modules/mediastream/RTCStatsResponse.cpp b/Source/WebCore/Modules/mediastream/RTCStatsResponse.cpp
deleted file mode 100644
index c3b41fe19..000000000
--- a/Source/WebCore/Modules/mediastream/RTCStatsResponse.cpp
+++ /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.
- *
- * 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(MEDIA_STREAM)
-
-#include "RTCStatsResponse.h"
-
-namespace WebCore {
-
-PassRefPtr<RTCStatsResponse> RTCStatsResponse::create()
-{
- return adoptRef(new RTCStatsResponse());
-}
-
-RTCStatsResponse::RTCStatsResponse()
-{
-}
-
-PassRefPtr<RTCStatsReport> RTCStatsResponse::namedItem(const AtomicString& name)
-{
- if (m_idmap.find(name) != m_idmap.end())
- return m_result[m_idmap.get(name)];
- return nullptr;
-}
-
-bool RTCStatsResponse::canGetItemsForName(const AtomicString& name)
-{
- return m_idmap.contains(name);
-}
-
-size_t RTCStatsResponse::addReport(String id, String type, double timestamp)
-{
- m_result.append(RTCStatsReport::create(id, type, timestamp));
- m_idmap.add(id, m_result.size() - 1);
- return m_result.size() - 1;
-}
-
-void RTCStatsResponse::addStatistic(size_t report, String name, String value)
-{
- ASSERT_WITH_SECURITY_IMPLICATION(report < m_result.size());
- m_result[report]->addStatistic(name, value);
-}
-
-} // namespace WebCore
-
-#endif // ENABLE(MEDIA_STREAM)
diff --git a/Source/WebCore/Modules/mediastream/RTCStatsResponse.h b/Source/WebCore/Modules/mediastream/RTCStatsResponse.h
deleted file mode 100644
index 30e89e4a1..000000000
--- a/Source/WebCore/Modules/mediastream/RTCStatsResponse.h
+++ /dev/null
@@ -1,63 +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 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 RTCStatsResponse_h
-#define RTCStatsResponse_h
-
-#include "ActiveDOMObject.h"
-#include "DOMError.h"
-#include "DOMStringList.h"
-#include "Event.h"
-#include "EventListener.h"
-#include "EventNames.h"
-#include "EventTarget.h"
-#include "MediaStreamTrack.h"
-#include "RTCStatsReport.h"
-#include "RTCStatsResponseBase.h"
-#include "ScriptWrappable.h"
-#include <wtf/HashMap.h>
-
-namespace WebCore {
-
-class RTCStatsResponse : public RTCStatsResponseBase, public ScriptWrappable {
-public:
- static PassRefPtr<RTCStatsResponse> create();
-
- const Vector<RefPtr<RTCStatsReport>>& result() const { return m_result; };
-
- PassRefPtr<RTCStatsReport> namedItem(const AtomicString&);
- bool canGetItemsForName(const AtomicString&);
-
- virtual size_t addReport(String id, String type, double timestamp) override;
- virtual void addStatistic(size_t report, String name, String value) override;
-
-private:
- RTCStatsResponse();
- Vector<RefPtr<RTCStatsReport>> m_result;
- HashMap<String, int> m_idmap;
-};
-
-} // namespace WebCore
-
-#endif // RTCStatsResponse_h
diff --git a/Source/WebCore/Modules/mediastream/RTCTrackEvent.cpp b/Source/WebCore/Modules/mediastream/RTCTrackEvent.cpp
new file mode 100644
index 000000000..c090157f3
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/RTCTrackEvent.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2015 Ericsson AB. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Ericsson 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 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
+ * OWNER 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 "RTCTrackEvent.h"
+
+#if ENABLE(WEB_RTC)
+
+#include "MediaStream.h"
+#include "MediaStreamTrack.h"
+#include "RTCRtpTransceiver.h"
+
+namespace WebCore {
+
+Ref<RTCTrackEvent> RTCTrackEvent::create(const AtomicString& type, bool canBubble, bool cancelable, RefPtr<RTCRtpReceiver>&& receiver, RefPtr<MediaStreamTrack>&& track, Vector<RefPtr<MediaStream>>&& streams, RefPtr<RTCRtpTransceiver>&& transceiver)
+{
+ return adoptRef(*new RTCTrackEvent(type, canBubble, cancelable, WTFMove(receiver), WTFMove(track), WTFMove(streams), WTFMove(transceiver)));
+}
+
+Ref<RTCTrackEvent> RTCTrackEvent::create(const AtomicString& type, const Init& initializer, IsTrusted isTrusted)
+{
+ return adoptRef(*new RTCTrackEvent(type, initializer, isTrusted));
+}
+
+RTCTrackEvent::RTCTrackEvent(const AtomicString& type, bool canBubble, bool cancelable, RefPtr<RTCRtpReceiver>&& receiver, RefPtr<MediaStreamTrack>&& track, Vector<RefPtr<MediaStream>>&& streams, RefPtr<RTCRtpTransceiver>&& transceiver)
+ : Event(type, canBubble, cancelable)
+ , m_receiver(WTFMove(receiver))
+ , m_track(WTFMove(track))
+ , m_streams(WTFMove(streams))
+ , m_transceiver(WTFMove(transceiver))
+{
+}
+
+RTCTrackEvent::RTCTrackEvent(const AtomicString& type, const Init& initializer, IsTrusted isTrusted)
+ : Event(type, initializer, isTrusted)
+ , m_receiver(initializer.receiver)
+ , m_track(initializer.track)
+ , m_streams(initializer.streams)
+ , m_transceiver(initializer.transceiver)
+{
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCTrackEvent.h b/Source/WebCore/Modules/mediastream/RTCTrackEvent.h
new file mode 100644
index 000000000..3617cbd36
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/RTCTrackEvent.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 Ericsson AB. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Ericsson 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 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
+ * OWNER 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(WEB_RTC)
+
+#include "Event.h"
+#include <wtf/text/AtomicString.h>
+
+namespace WebCore {
+
+class MediaStream;
+class MediaStreamTrack;
+class RTCRtpReceiver;
+class RTCRtpTransceiver;
+
+typedef Vector<RefPtr<MediaStream>> MediaStreamArray;
+
+class RTCTrackEvent : public Event {
+public:
+ static Ref<RTCTrackEvent> create(const AtomicString& type, bool canBubble, bool cancelable, RefPtr<RTCRtpReceiver>&&, RefPtr<MediaStreamTrack>&&, MediaStreamArray&&, RefPtr<RTCRtpTransceiver>&&);
+
+ struct Init : EventInit {
+ RefPtr<RTCRtpReceiver> receiver;
+ RefPtr<MediaStreamTrack> track;
+ MediaStreamArray streams;
+ RefPtr<RTCRtpTransceiver> transceiver;
+ };
+ static Ref<RTCTrackEvent> create(const AtomicString& type, const Init&, IsTrusted = IsTrusted::No);
+
+ RTCRtpReceiver* receiver() const { return m_receiver.get(); }
+ MediaStreamTrack* track() const { return m_track.get(); }
+ const MediaStreamArray& streams() const { return m_streams; }
+ RTCRtpTransceiver* transceiver() const { return m_transceiver.get(); }
+
+ virtual EventInterface eventInterface() const { return RTCTrackEventInterfaceType; }
+
+private:
+ RTCTrackEvent(const AtomicString& type, bool canBubble, bool cancelable, RefPtr<RTCRtpReceiver>&&, RefPtr<MediaStreamTrack>&&, MediaStreamArray&&, RefPtr<RTCRtpTransceiver>&&);
+ RTCTrackEvent(const AtomicString& type, const Init&, IsTrusted);
+
+ RefPtr<RTCRtpReceiver> m_receiver;
+ RefPtr<MediaStreamTrack> m_track;
+ MediaStreamArray m_streams;
+ RefPtr<RTCRtpTransceiver> m_transceiver;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/RTCTrackEvent.idl b/Source/WebCore/Modules/mediastream/RTCTrackEvent.idl
new file mode 100644
index 000000000..04fe5558a
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/RTCTrackEvent.idl
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 Ericsson AB. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Ericsson 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 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
+ * OWNER 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=WEB_RTC,
+ Constructor(DOMString type, RTCTrackEventInit eventInitDict),
+ EnabledAtRuntime=PeerConnection,
+] interface RTCTrackEvent : Event {
+ readonly attribute RTCRtpReceiver receiver;
+ readonly attribute MediaStreamTrack track;
+ readonly attribute FrozenArray<MediaStream> streams;
+ readonly attribute RTCRtpTransceiver transceiver;
+};
+
+dictionary RTCTrackEventInit : EventInit {
+ required RTCRtpReceiver receiver;
+ required MediaStreamTrack track;
+ sequence<MediaStream> streams = [];
+ required RTCRtpTransceiver transceiver;
+};
diff --git a/Source/WebCore/Modules/mediastream/SDPProcessor.cpp b/Source/WebCore/Modules/mediastream/SDPProcessor.cpp
new file mode 100644
index 000000000..9254fdc07
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/SDPProcessor.cpp
@@ -0,0 +1,547 @@
+/*
+ * Copyright (C) 2015, 2016 Ericsson AB. All rights reserved.
+ * 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.
+ * 3. Neither the name of Ericsson 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 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
+ * OWNER 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(WEB_RTC)
+#include "SDPProcessor.h"
+
+#include "CommonVM.h"
+#include "Document.h"
+#include "Frame.h"
+#include "SDPProcessorScriptResource.h"
+#include "ScriptController.h"
+#include "ScriptSourceCode.h"
+#include "inspector/InspectorValues.h"
+#include <bindings/ScriptObject.h>
+#include <wtf/NeverDestroyed.h>
+
+using namespace Inspector;
+
+namespace WebCore {
+
+#define STRING_FUNCTION(name) \
+ static const String& name##String() \
+ { \
+ static NeverDestroyed<const String> name { ASCIILiteral(#name) }; \
+ return name; \
+ }
+
+STRING_FUNCTION(address)
+STRING_FUNCTION(apt)
+STRING_FUNCTION(candidates)
+STRING_FUNCTION(ccmfir)
+STRING_FUNCTION(channels)
+STRING_FUNCTION(clockRate)
+STRING_FUNCTION(cname)
+STRING_FUNCTION(componentId)
+STRING_FUNCTION(dtls)
+STRING_FUNCTION(encodingName)
+STRING_FUNCTION(fingerprint)
+STRING_FUNCTION(fingerprintHashFunction)
+STRING_FUNCTION(foundation)
+STRING_FUNCTION(ice)
+STRING_FUNCTION(mediaDescriptions)
+STRING_FUNCTION(mediaStreamId)
+STRING_FUNCTION(mediaStreamTrackId)
+STRING_FUNCTION(mid)
+STRING_FUNCTION(mode)
+STRING_FUNCTION(mux)
+STRING_FUNCTION(nack)
+STRING_FUNCTION(nackpli)
+STRING_FUNCTION(originator)
+STRING_FUNCTION(packetizationMode)
+STRING_FUNCTION(parameters)
+STRING_FUNCTION(password)
+STRING_FUNCTION(payloads)
+STRING_FUNCTION(port)
+STRING_FUNCTION(priority)
+STRING_FUNCTION(relatedAddress)
+STRING_FUNCTION(relatedPort)
+STRING_FUNCTION(rtcp)
+STRING_FUNCTION(rtcpAddress)
+STRING_FUNCTION(rtcpPort)
+STRING_FUNCTION(rtxTime)
+STRING_FUNCTION(sessionId)
+STRING_FUNCTION(sessionVersion)
+STRING_FUNCTION(setup)
+STRING_FUNCTION(ssrcs)
+STRING_FUNCTION(tcpType)
+STRING_FUNCTION(transport)
+STRING_FUNCTION(type)
+STRING_FUNCTION(ufrag)
+
+SDPProcessor::SDPProcessor(ScriptExecutionContext* context)
+ : ContextDestructionObserver(context)
+{
+}
+
+// Note that MediaEndpointSessionConfiguration is a "flatter" structure that the JSON representation. For
+// example, the JSON representation has an "ice" object which collects a set of properties under a
+// namespace. MediaEndpointSessionConfiguration has "ice"-prefixes on the corresponding properties.
+
+static RefPtr<InspectorObject> createCandidateObject(const IceCandidate& candidate)
+{
+ RefPtr<InspectorObject> candidateObject = InspectorObject::create();
+
+ candidateObject->setString(typeString(), candidate.type);
+ candidateObject->setString(foundationString(), candidate.foundation);
+ candidateObject->setInteger(componentIdString(), candidate.componentId);
+ candidateObject->setString(transportString(), candidate.transport);
+ candidateObject->setInteger(priorityString(), candidate.priority);
+ candidateObject->setString(addressString(), candidate.address);
+ candidateObject->setInteger(portString(), candidate.port);
+ if (!candidate.tcpType.isEmpty())
+ candidateObject->setString(tcpTypeString(), candidate.tcpType);
+ if (candidate.type.convertToASCIIUppercase() != "HOST") {
+ candidateObject->setString(relatedAddressString(), candidate.relatedAddress);
+ candidateObject->setInteger(relatedPortString(), candidate.relatedPort);
+ }
+
+ return candidateObject;
+}
+
+static IceCandidate createCandidate(const InspectorObject& candidateObject)
+{
+ IceCandidate candidate;
+ String stringValue;
+ unsigned intValue;
+
+ if (candidateObject.getString(typeString(), stringValue))
+ candidate.type = stringValue;
+
+ if (candidateObject.getString(foundationString(), stringValue))
+ candidate.foundation = stringValue;
+
+ if (candidateObject.getInteger(componentIdString(), intValue))
+ candidate.componentId = intValue;
+
+ if (candidateObject.getString(transportString(), stringValue))
+ candidate.transport = stringValue;
+
+ if (candidateObject.getInteger(priorityString(), intValue))
+ candidate.priority = intValue;
+
+ if (candidateObject.getString(addressString(), stringValue))
+ candidate.address = stringValue;
+
+ if (candidateObject.getInteger(portString(), intValue))
+ candidate.port = intValue;
+
+ if (candidateObject.getString(tcpTypeString(), stringValue))
+ candidate.tcpType = stringValue;
+
+ if (candidateObject.getString(relatedAddressString(), stringValue))
+ candidate.relatedAddress = stringValue;
+
+ if (candidateObject.getInteger(relatedPortString(), intValue))
+ candidate.relatedPort = intValue;
+
+ return candidate;
+}
+
+static RefPtr<MediaEndpointSessionConfiguration> configurationFromJSON(const String& json)
+{
+ RefPtr<InspectorValue> value;
+ if (!InspectorValue::parseJSON(json, value))
+ return nullptr;
+
+ RefPtr<InspectorObject> object;
+ if (!value->asObject(object))
+ return nullptr;
+
+ RefPtr<MediaEndpointSessionConfiguration> configuration = MediaEndpointSessionConfiguration::create();
+
+ String stringValue;
+ unsigned intValue;
+ unsigned long longValue;
+ bool boolValue;
+
+ RefPtr<InspectorObject> originatorObject = InspectorObject::create();
+ if (object->getObject(originatorString(), originatorObject)) {
+ if (originatorObject->getInteger(sessionIdString(), longValue))
+ configuration->setSessionId(longValue);
+ if (originatorObject->getInteger(sessionVersionString(), intValue))
+ configuration->setSessionVersion(intValue);
+ }
+
+ RefPtr<InspectorArray> mediaDescriptionsArray = InspectorArray::create();
+ object->getArray(mediaDescriptionsString(), mediaDescriptionsArray);
+
+ for (unsigned i = 0; i < mediaDescriptionsArray->length(); ++i) {
+ RefPtr<InspectorObject> mediaDescriptionObject = InspectorObject::create();
+ mediaDescriptionsArray->get(i)->asObject(mediaDescriptionObject);
+
+ PeerMediaDescription mediaDescription;
+
+ if (mediaDescriptionObject->getString(typeString(), stringValue))
+ mediaDescription.type = stringValue;
+
+ if (mediaDescriptionObject->getInteger(portString(), intValue))
+ mediaDescription.port = intValue;
+
+ if (mediaDescriptionObject->getString(addressString(), stringValue))
+ mediaDescription.address = stringValue;
+
+ if (mediaDescriptionObject->getString(modeString(), stringValue))
+ mediaDescription.mode = stringValue;
+
+ if (mediaDescriptionObject->getString(midString(), stringValue))
+ mediaDescription.mid = stringValue;
+
+ RefPtr<InspectorArray> payloadsArray = InspectorArray::create();
+ mediaDescriptionObject->getArray(payloadsString(), payloadsArray);
+
+ for (unsigned j = 0; j < payloadsArray->length(); ++j) {
+ RefPtr<InspectorObject> payloadsObject = InspectorObject::create();
+ payloadsArray->get(j)->asObject(payloadsObject);
+
+ MediaPayload payload;
+
+ if (payloadsObject->getInteger(typeString(), intValue))
+ payload.type = intValue;
+
+ if (payloadsObject->getString(encodingNameString(), stringValue))
+ payload.encodingName = stringValue;
+
+ if (payloadsObject->getInteger(clockRateString(), intValue))
+ payload.clockRate = intValue;
+
+ if (payloadsObject->getInteger(channelsString(), intValue))
+ payload.channels = intValue;
+
+ if (payloadsObject->getBoolean(ccmfirString(), boolValue))
+ payload.ccmfir = boolValue;
+
+ if (payloadsObject->getBoolean(nackpliString(), boolValue))
+ payload.nackpli = boolValue;
+
+ if (payloadsObject->getBoolean(nackString(), boolValue))
+ payload.nack = boolValue;
+
+ RefPtr<InspectorObject> parametersObject = InspectorObject::create();
+ if (payloadsObject->getObject(parametersString(), parametersObject)) {
+ if (parametersObject->getInteger(packetizationModeString(), intValue))
+ payload.addParameter("packetizationMode", intValue);
+
+ if (parametersObject->getInteger(aptString(), intValue))
+ payload.addParameter("apt", intValue);
+
+ if (parametersObject->getInteger(rtxTimeString(), intValue))
+ payload.addParameter("rtxTime", intValue);
+ }
+
+ mediaDescription.addPayload(WTFMove(payload));
+ }
+
+ RefPtr<InspectorObject> rtcpObject = InspectorObject::create();
+ if (mediaDescriptionObject->getObject(rtcpString(), rtcpObject)) {
+ if (rtcpObject->getBoolean(muxString(), boolValue))
+ mediaDescription.rtcpMux = boolValue;
+
+ if (rtcpObject->getString(rtcpAddressString(), stringValue))
+ mediaDescription.rtcpAddress = stringValue;
+
+ if (rtcpObject->getInteger(rtcpPortString(), intValue))
+ mediaDescription.rtcpPort = intValue;
+ }
+
+ if (mediaDescriptionObject->getString(mediaStreamIdString(), stringValue))
+ mediaDescription.mediaStreamId = stringValue;
+
+ if (mediaDescriptionObject->getString(mediaStreamTrackIdString(), stringValue))
+ mediaDescription.mediaStreamTrackId = stringValue;
+
+ RefPtr<InspectorObject> dtlsObject = InspectorObject::create();
+ if (mediaDescriptionObject->getObject(dtlsString(), dtlsObject)) {
+ if (dtlsObject->getString(setupString(), stringValue))
+ mediaDescription.dtlsSetup = stringValue;
+
+ if (dtlsObject->getString(fingerprintHashFunctionString(), stringValue))
+ mediaDescription.dtlsFingerprintHashFunction = stringValue;
+
+ if (dtlsObject->getString(fingerprintString(), stringValue))
+ mediaDescription.dtlsFingerprint = stringValue;
+ }
+
+ RefPtr<InspectorArray> ssrcsArray = InspectorArray::create();
+ mediaDescriptionObject->getArray(ssrcsString(), ssrcsArray);
+
+ for (unsigned j = 0; j < ssrcsArray->length(); ++j) {
+ ssrcsArray->get(j)->asInteger(intValue);
+ mediaDescription.addSsrc(intValue);
+ }
+
+ if (mediaDescriptionObject->getString(cnameString(), stringValue))
+ mediaDescription.cname = stringValue;
+
+ RefPtr<InspectorObject> iceObject = InspectorObject::create();
+ if (mediaDescriptionObject->getObject(iceString(), iceObject)) {
+ if (iceObject->getString(ufragString(), stringValue))
+ mediaDescription.iceUfrag = stringValue;
+
+ if (iceObject->getString(passwordString(), stringValue))
+ mediaDescription.icePassword = stringValue;
+
+ RefPtr<InspectorArray> candidatesArray = InspectorArray::create();
+ iceObject->getArray(candidatesString(), candidatesArray);
+
+ for (unsigned j = 0; j < candidatesArray->length(); ++j) {
+ RefPtr<InspectorObject> candidateObject = InspectorObject::create();
+ candidatesArray->get(j)->asObject(candidateObject);
+
+ mediaDescription.addIceCandidate(createCandidate(*candidateObject));
+ }
+ }
+
+ configuration->addMediaDescription(WTFMove(mediaDescription));
+ }
+
+ return configuration;
+}
+
+static std::optional<IceCandidate> iceCandidateFromJSON(const String& json)
+{
+ RefPtr<InspectorValue> value;
+ if (!InspectorValue::parseJSON(json, value))
+ return std::nullopt;
+
+ RefPtr<InspectorObject> candidateObject;
+ if (!value->asObject(candidateObject))
+ return std::nullopt;
+
+ return createCandidate(*candidateObject);
+}
+
+static String configurationToJSON(const MediaEndpointSessionConfiguration& configuration)
+{
+ RefPtr<InspectorObject> object = InspectorObject::create();
+
+ RefPtr<InspectorObject> originatorObject = InspectorObject::create();
+ originatorObject->setDouble(sessionIdString(), configuration.sessionId());
+ originatorObject->setInteger(sessionVersionString(), configuration.sessionVersion());
+ object->setObject(originatorString(), originatorObject);
+
+ RefPtr<InspectorArray> mediaDescriptionsArray = InspectorArray::create();
+
+ for (const auto& mediaDescription : configuration.mediaDescriptions()) {
+ RefPtr<InspectorObject> mediaDescriptionObject = InspectorObject::create();
+
+ mediaDescriptionObject->setString(typeString(), mediaDescription.type);
+ mediaDescriptionObject->setInteger(portString(), mediaDescription.port);
+ mediaDescriptionObject->setString(addressString(), mediaDescription.address);
+ mediaDescriptionObject->setString(modeString(), mediaDescription.mode);
+ mediaDescriptionObject->setString(midString(), mediaDescription.mid);
+
+ RefPtr<InspectorArray> payloadsArray = InspectorArray::create();
+
+ for (auto& payload : mediaDescription.payloads) {
+ RefPtr<InspectorObject> payloadObject = InspectorObject::create();
+
+ payloadObject->setInteger(typeString(), payload.type);
+ payloadObject->setString(encodingNameString(), payload.encodingName);
+ payloadObject->setInteger(clockRateString(), payload.clockRate);
+ payloadObject->setInteger(channelsString(), payload.channels);
+ payloadObject->setBoolean(ccmfirString(), payload.ccmfir);
+ payloadObject->setBoolean(nackpliString(), payload.nackpli);
+ payloadObject->setBoolean(nackString(), payload.nack);
+
+ if (!payload.parameters.isEmpty()) {
+ RefPtr<InspectorObject> parametersObject = InspectorObject::create();
+
+ for (auto& name : payload.parameters.keys())
+ parametersObject->setInteger(name, payload.parameters.get(name));
+
+ payloadObject->setObject(parametersString(), parametersObject);
+ }
+
+ payloadsArray->pushObject(payloadObject);
+ }
+ mediaDescriptionObject->setArray(payloadsString(), payloadsArray);
+
+ RefPtr<InspectorObject> rtcpObject = InspectorObject::create();
+ rtcpObject->setBoolean(muxString(), mediaDescription.rtcpMux);
+ rtcpObject->setString(addressString(), mediaDescription.rtcpAddress);
+ rtcpObject->setInteger(portString(), mediaDescription.rtcpPort);
+ mediaDescriptionObject->setObject(rtcpString(), rtcpObject);
+
+ mediaDescriptionObject->setString(mediaStreamIdString(), mediaDescription.mediaStreamId);
+ mediaDescriptionObject->setString(mediaStreamTrackIdString(), mediaDescription.mediaStreamTrackId);
+
+ RefPtr<InspectorObject> dtlsObject = InspectorObject::create();
+ dtlsObject->setString(setupString(), mediaDescription.dtlsSetup);
+ dtlsObject->setString(fingerprintHashFunctionString(), mediaDescription.dtlsFingerprintHashFunction);
+ dtlsObject->setString(fingerprintString(), mediaDescription.dtlsFingerprint);
+ mediaDescriptionObject->setObject(dtlsString(), dtlsObject);
+
+ RefPtr<InspectorArray> ssrcsArray = InspectorArray::create();
+
+ for (auto ssrc : mediaDescription.ssrcs)
+ ssrcsArray->pushDouble(ssrc);
+ mediaDescriptionObject->setArray(ssrcsString(), ssrcsArray);
+
+ mediaDescriptionObject->setString(cnameString(), mediaDescription.cname);
+
+ RefPtr<InspectorObject> iceObject = InspectorObject::create();
+ iceObject->setString(ufragString(), mediaDescription.iceUfrag);
+ iceObject->setString(passwordString(), mediaDescription.icePassword);
+
+ RefPtr<InspectorArray> candidatesArray = InspectorArray::create();
+
+ for (auto& candidate : mediaDescription.iceCandidates)
+ candidatesArray->pushObject(createCandidateObject(candidate));
+
+ iceObject->setArray(candidatesString(), candidatesArray);
+ mediaDescriptionObject->setObject(iceString(), iceObject);
+
+ mediaDescriptionsArray->pushObject(mediaDescriptionObject);
+ }
+ object->setArray(mediaDescriptionsString(), mediaDescriptionsArray);
+
+ return object->toJSONString();
+}
+
+static String iceCandidateToJSON(const IceCandidate& candidate)
+{
+ return createCandidateObject(candidate)->toJSONString();
+}
+
+SDPProcessor::Result SDPProcessor::generate(const MediaEndpointSessionConfiguration& configuration, String& outSdpString) const
+{
+ String sdpString;
+ if (!callScript("generate", configurationToJSON(configuration), sdpString))
+ return Result::InternalError;
+
+ outSdpString = sdpString;
+ return Result::Success;
+}
+
+SDPProcessor::Result SDPProcessor::parse(const String& sdp, RefPtr<MediaEndpointSessionConfiguration>& outConfiguration) const
+{
+ String scriptOutput;
+ if (!callScript("parse", sdp, scriptOutput))
+ return Result::InternalError;
+
+ if (scriptOutput == "ParseError")
+ return Result::ParseError;
+
+ RefPtr<MediaEndpointSessionConfiguration> configuration = configurationFromJSON(scriptOutput);
+ if (!configuration)
+ return Result::InternalError;
+
+ outConfiguration = configuration;
+ return Result::Success;
+}
+
+SDPProcessor::Result SDPProcessor::generateCandidateLine(const IceCandidate& candidate, String& outCandidateLine) const
+{
+ String candidateLine;
+ if (!callScript("generateCandidateLine", iceCandidateToJSON(candidate), candidateLine))
+ return Result::InternalError;
+
+ outCandidateLine = candidateLine;
+ return Result::Success;
+}
+
+SDPProcessor::ParsingResult SDPProcessor::parseCandidateLine(const String& candidateLine) const
+{
+ String scriptOutput;
+ if (!callScript("parseCandidateLine", candidateLine, scriptOutput))
+ return { Result::InternalError };
+
+ if (scriptOutput == "ParseError")
+ return { Result::ParseError };
+
+ auto candidate = iceCandidateFromJSON(scriptOutput);
+ if (!candidate)
+ return { Result::InternalError };
+ return { WTFMove(candidate.value()) };
+}
+
+bool SDPProcessor::callScript(const String& functionName, const String& argument, String& outResult) const
+{
+ if (!scriptExecutionContext())
+ return false;
+
+ Document* document = downcast<Document>(scriptExecutionContext());
+ if (!document->frame())
+ return false;
+
+ if (!m_isolatedWorld)
+ m_isolatedWorld = DOMWrapperWorld::create(commonVM());
+
+ ScriptController& scriptController = document->frame()->script();
+ JSDOMGlobalObject* globalObject = JSC::jsCast<JSDOMGlobalObject*>(scriptController.globalObject(*m_isolatedWorld));
+ JSC::VM& vm = globalObject->vm();
+ JSC::JSLockHolder lock(vm);
+ auto scope = DECLARE_CATCH_SCOPE(vm);
+ JSC::ExecState* exec = globalObject->globalExec();
+
+ JSC::JSValue probeFunctionValue = globalObject->get(exec, JSC::Identifier::fromString(exec, "generate"));
+ if (!probeFunctionValue.isFunction()) {
+ URL scriptURL;
+ scriptController.evaluateInWorld(ScriptSourceCode(SDPProcessorScriptResource::scriptString(), scriptURL), *m_isolatedWorld);
+ if (UNLIKELY(scope.exception())) {
+ scope.clearException();
+ return false;
+ }
+ }
+
+ JSC::JSValue functionValue = globalObject->get(exec, JSC::Identifier::fromString(exec, functionName));
+ if (!functionValue.isFunction())
+ return false;
+
+ JSC::JSObject* function = functionValue.toObject(exec);
+ JSC::CallData callData;
+ JSC::CallType callType = function->methodTable()->getCallData(function, callData);
+ if (callType == JSC::CallType::None)
+ return false;
+
+ JSC::MarkedArgumentBuffer argList;
+ argList.append(JSC::jsString(exec, argument));
+
+ JSC::JSValue result = JSC::call(exec, function, callType, callData, globalObject, argList);
+ if (UNLIKELY(scope.exception())) {
+ LOG_ERROR("SDPProcessor script threw in function %s", functionName.ascii().data());
+ scope.clearException();
+ return false;
+ }
+
+ if (!result.isString())
+ return false;
+
+ outResult = asString(result)->value(exec);
+ return true;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/SDPProcessor.h b/Source/WebCore/Modules/mediastream/SDPProcessor.h
new file mode 100644
index 000000000..ca61c9a92
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/SDPProcessor.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 Ericsson AB. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Ericsson 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 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
+ * OWNER 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(WEB_RTC)
+
+#include "ContextDestructionObserver.h"
+#include "IceCandidate.h"
+#include "MediaEndpointSessionConfiguration.h"
+#include <wtf/RefPtr.h>
+#include <wtf/Variant.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class DOMWrapperWorld;
+class ScriptExecutionContext;
+
+class SDPProcessor : public ContextDestructionObserver {
+public:
+ enum class Result {
+ Success = 1,
+ InternalError = 2,
+ ParseError = 3
+ };
+
+ SDPProcessor(ScriptExecutionContext*);
+
+ Result generate(const MediaEndpointSessionConfiguration&, String& outSdpString) const;
+ Result parse(const String& sdp, RefPtr<MediaEndpointSessionConfiguration>&) const;
+
+ Result generateCandidateLine(const IceCandidate&, String& outCandidateLine) const;
+
+ struct ParsingResult {
+ Variant<IceCandidate, Result> result;
+
+ Result parsingStatus() const { return WTF::holds_alternative<IceCandidate>(result) ? Result::Success : WTF::get<SDPProcessor::Result>(result); }
+ IceCandidate& candidate() { return WTF::get<IceCandidate>(result); }
+ };
+ ParsingResult parseCandidateLine(const String& candidateLine) const;
+
+private:
+ bool callScript(const String& functionName, const String& argument, String& outResult) const;
+
+ mutable RefPtr<DOMWrapperWorld> m_isolatedWorld;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_RTC)
diff --git a/Source/WebCore/Modules/mediastream/SourceInfo.cpp b/Source/WebCore/Modules/mediastream/SourceInfo.cpp
deleted file mode 100644
index b116f2fd1..000000000
--- a/Source/WebCore/Modules/mediastream/SourceInfo.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2013 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 GOOGLE 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 GOOGLE 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 "SourceInfo.h"
-
-#if ENABLE(MEDIA_STREAM)
-
-#include <wtf/NeverDestroyed.h>
-
-namespace WebCore {
-
-PassRefPtr<SourceInfo> SourceInfo::create(PassRefPtr<TrackSourceInfo> trackSourceInfo)
-{
- return adoptRef(new SourceInfo(trackSourceInfo));
-}
-
-SourceInfo::SourceInfo(PassRefPtr<TrackSourceInfo> trackSourceInfo)
- : m_trackSourceInfo(trackSourceInfo)
-{
-}
-
-const AtomicString& SourceInfo::kind() const
-{
- static NeverDestroyed<AtomicString> audioKind("audio", AtomicString::ConstructFromLiteral);
- static NeverDestroyed<AtomicString> videoKind("video", AtomicString::ConstructFromLiteral);
-
- switch (m_trackSourceInfo->kind()) {
- case TrackSourceInfo::Audio:
- return audioKind;
- case TrackSourceInfo::Video:
- return videoKind;
- }
-
- ASSERT_NOT_REACHED();
- return emptyAtom;
-}
-
-} // namespace WebCore
-
-#endif
diff --git a/Source/WebCore/Modules/mediastream/SourceInfo.h b/Source/WebCore/Modules/mediastream/SourceInfo.h
deleted file mode 100644
index 23f162e37..000000000
--- a/Source/WebCore/Modules/mediastream/SourceInfo.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2013 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 GOOGLE 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 GOOGLE 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.
- */
-
-#ifndef SourceInfo_h
-#define SourceInfo_h
-
-#if ENABLE(MEDIA_STREAM)
-
-#include "MediaStreamTrackSourcesRequestClient.h"
-#include "ScriptWrappable.h"
-#include <wtf/Forward.h>
-#include <wtf/RefCounted.h>
-#include <wtf/Vector.h>
-#include <wtf/text/WTFString.h>
-
-namespace WebCore {
-
-class SourceInfo : public RefCounted<SourceInfo>, public ScriptWrappable {
-public:
- static PassRefPtr<SourceInfo> create(PassRefPtr<TrackSourceInfo>);
-
- const AtomicString& sourceId() const { return m_trackSourceInfo->id(); }
- const AtomicString& label() const { return m_trackSourceInfo->label(); }
- const AtomicString& kind() const;
-
-private:
- SourceInfo(PassRefPtr<TrackSourceInfo>);
-
- RefPtr<TrackSourceInfo> m_trackSourceInfo;
-};
-
-} // namespace WebCore
-
-#endif // SourceInfo_h
-
-#endif
diff --git a/Source/WebCore/Modules/mediastream/UserMediaClient.h b/Source/WebCore/Modules/mediastream/UserMediaClient.h
index b847c7867..f8627acec 100644
--- a/Source/WebCore/Modules/mediastream/UserMediaClient.h
+++ b/Source/WebCore/Modules/mediastream/UserMediaClient.h
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2011 Ericsson AB. All rights reserved.
+ * 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
@@ -28,15 +29,13 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef UserMediaClient_h
-#define UserMediaClient_h
+#pragma once
#if ENABLE(MEDIA_STREAM)
-#include <wtf/PassRefPtr.h>
-
namespace WebCore {
+class MediaDevicesEnumerationRequest;
class Page;
class UserMediaRequest;
@@ -44,17 +43,18 @@ class UserMediaClient {
public:
virtual void pageDestroyed() = 0;
- virtual void requestPermission(PassRefPtr<UserMediaRequest>) = 0;
- virtual void cancelRequest(UserMediaRequest*) = 0;
+ virtual void requestUserMediaAccess(UserMediaRequest&) = 0;
+ virtual void cancelUserMediaAccessRequest(UserMediaRequest&) = 0;
+
+ virtual void enumerateMediaDevices(MediaDevicesEnumerationRequest&) = 0;
+ virtual void cancelMediaDevicesEnumerationRequest(MediaDevicesEnumerationRequest&) = 0;
protected:
virtual ~UserMediaClient() { }
};
-void provideUserMediaTo(Page*, UserMediaClient*);
+WEBCORE_EXPORT void provideUserMediaTo(Page*, UserMediaClient*);
} // namespace WebCore
#endif // ENABLE(MEDIA_STREAM)
-
-#endif // UserMediaClient_h
diff --git a/Source/WebCore/Modules/mediastream/UserMediaController.cpp b/Source/WebCore/Modules/mediastream/UserMediaController.cpp
index d46d4bc39..1991e1fb2 100644
--- a/Source/WebCore/Modules/mediastream/UserMediaController.cpp
+++ b/Source/WebCore/Modules/mediastream/UserMediaController.cpp
@@ -44,14 +44,9 @@ UserMediaController::~UserMediaController()
m_client->pageDestroyed();
}
-PassOwnPtr<UserMediaController> UserMediaController::create(UserMediaClient* client)
-{
- return adoptPtr(new UserMediaController(client));
-}
-
void provideUserMediaTo(Page* page, UserMediaClient* client)
{
- UserMediaController::provideTo(page, UserMediaController::supplementName(), UserMediaController::create(client));
+ UserMediaController::provideTo(page, UserMediaController::supplementName(), std::make_unique<UserMediaController>(client));
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/mediastream/UserMediaController.h b/Source/WebCore/Modules/mediastream/UserMediaController.h
index 4d15c981e..707fd8e40 100644
--- a/Source/WebCore/Modules/mediastream/UserMediaController.h
+++ b/Source/WebCore/Modules/mediastream/UserMediaController.h
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2012 Google Inc. All rights reserved.
+ * 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
@@ -22,49 +23,56 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef UserMediaController_h
-#define UserMediaController_h
+#pragma once
#if ENABLE(MEDIA_STREAM)
#include "Page.h"
#include "UserMediaClient.h"
#include "UserMediaRequest.h"
-#include <wtf/PassOwnPtr.h>
namespace WebCore {
class UserMediaController : public Supplement<Page> {
public:
+ explicit UserMediaController(UserMediaClient*);
~UserMediaController();
UserMediaClient* client() const { return m_client; }
- void requestPermission(PassRefPtr<UserMediaRequest>);
- void cancelRequest(UserMediaRequest*);
- static PassOwnPtr<UserMediaController> create(UserMediaClient*);
- static const char* supplementName();
- static UserMediaController* from(Page* page) { return static_cast<UserMediaController*>(Supplement<Page>::from(page, supplementName())); }
+ void requestUserMediaAccess(UserMediaRequest&);
+ void cancelUserMediaAccessRequest(UserMediaRequest&);
-protected:
- explicit UserMediaController(UserMediaClient*);
+ void enumerateMediaDevices(MediaDevicesEnumerationRequest&);
+ void cancelMediaDevicesEnumerationRequest(MediaDevicesEnumerationRequest&);
+
+ WEBCORE_EXPORT static const char* supplementName();
+ static UserMediaController* from(Page* page) { return static_cast<UserMediaController*>(Supplement<Page>::from(page, supplementName())); }
private:
UserMediaClient* m_client;
};
-inline void UserMediaController::requestPermission(PassRefPtr<UserMediaRequest> request)
+inline void UserMediaController::requestUserMediaAccess(UserMediaRequest& request)
+{
+ m_client->requestUserMediaAccess(request);
+}
+
+inline void UserMediaController::cancelUserMediaAccessRequest(UserMediaRequest& request)
+{
+ m_client->cancelUserMediaAccessRequest(request);
+}
+
+inline void UserMediaController::enumerateMediaDevices(MediaDevicesEnumerationRequest& request)
{
- m_client->requestPermission(request);
+ m_client->enumerateMediaDevices(request);
}
-inline void UserMediaController::cancelRequest(UserMediaRequest* request)
+inline void UserMediaController::cancelMediaDevicesEnumerationRequest(MediaDevicesEnumerationRequest& request)
{
- m_client->cancelRequest(request);
+ m_client->cancelMediaDevicesEnumerationRequest(request);
}
} // namespace WebCore
#endif // ENABLE(MEDIA_STREAM)
-
-#endif // UserMediaController_h
diff --git a/Source/WebCore/Modules/mediastream/UserMediaRequest.cpp b/Source/WebCore/Modules/mediastream/UserMediaRequest.cpp
index e438d35a2..5df3e9896 100644
--- a/Source/WebCore/Modules/mediastream/UserMediaRequest.cpp
+++ b/Source/WebCore/Modules/mediastream/UserMediaRequest.cpp
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2011 Ericsson AB. All rights reserved.
* Copyright (C) 2012 Google Inc. All rights reserved.
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2016 Apple Inc. All rights reserved.
* Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
*
* Redistribution and use in source and binary forms, with or without
@@ -32,68 +32,44 @@
*/
#include "config.h"
+#include "UserMediaRequest.h"
#if ENABLE(MEDIA_STREAM)
-#include "UserMediaRequest.h"
-
-#include "Dictionary.h"
#include "Document.h"
+#include "DocumentLoader.h"
#include "ExceptionCode.h"
+#include "JSMediaStream.h"
+#include "JSOverconstrainedError.h"
+#include "MainFrame.h"
#include "MediaConstraintsImpl.h"
-#include "MediaStream.h"
-#include "MediaStreamCenter.h"
-#include "MediaStreamPrivate.h"
-#include "SecurityOrigin.h"
+#include "RealtimeMediaSourceCenter.h"
+#include "Settings.h"
#include "UserMediaController.h"
-#include <wtf/Functional.h>
-#include <wtf/MainThread.h>
namespace WebCore {
-static PassRefPtr<MediaConstraints> parseOptions(const Dictionary& options, const String& mediaType, ExceptionCode& ec)
-{
- RefPtr<MediaConstraints> constraints;
-
- Dictionary constraintsDictionary;
- bool ok = options.get(mediaType, constraintsDictionary);
- if (ok && !constraintsDictionary.isUndefinedOrNull())
- constraints = MediaConstraintsImpl::create(constraintsDictionary, ec);
- else {
- bool mediaRequested = false;
- options.get(mediaType, mediaRequested);
- if (mediaRequested)
- constraints = MediaConstraintsImpl::create();
- }
-
- return constraints.release();
-}
-
-PassRefPtr<UserMediaRequest> UserMediaRequest::create(ScriptExecutionContext* context, UserMediaController* controller, const Dictionary& options, PassRefPtr<NavigatorUserMediaSuccessCallback> successCallback, PassRefPtr<NavigatorUserMediaErrorCallback> errorCallback, ExceptionCode& ec)
+ExceptionOr<void> UserMediaRequest::start(Document& document, Ref<MediaConstraintsImpl>&& audioConstraints, Ref<MediaConstraintsImpl>&& videoConstraints, DOMPromise<IDLInterface<MediaStream>>&& promise)
{
- ASSERT(successCallback);
-
- RefPtr<MediaConstraints> audioConstraints = parseOptions(options, AtomicString("audio", AtomicString::ConstructFromLiteral), ec);
- if (ec)
- return nullptr;
+ auto* userMedia = UserMediaController::from(document.page());
+ if (!userMedia)
+ return Exception { NOT_SUPPORTED_ERR }; // FIXME: Why is it better to return an exception here instead of rejecting the promise as we do just below?
- RefPtr<MediaConstraints> videoConstraints = parseOptions(options, AtomicString("video", AtomicString::ConstructFromLiteral), ec);
- if (ec)
- return nullptr;
-
- if (!audioConstraints && !videoConstraints)
- return nullptr;
+ if (!audioConstraints->isValid() && !videoConstraints->isValid()) {
+ promise.reject(TypeError);
+ return { };
+ }
- return adoptRef(new UserMediaRequest(context, controller, audioConstraints.release(), videoConstraints.release(), successCallback, errorCallback));
+ adoptRef(*new UserMediaRequest(document, *userMedia, WTFMove(audioConstraints), WTFMove(videoConstraints), WTFMove(promise)))->start();
+ return { };
}
-UserMediaRequest::UserMediaRequest(ScriptExecutionContext* context, UserMediaController* controller, PassRefPtr<MediaConstraints> audioConstraints, PassRefPtr<MediaConstraints> videoConstraints, PassRefPtr<NavigatorUserMediaSuccessCallback> successCallback, PassRefPtr<NavigatorUserMediaErrorCallback> errorCallback)
- : ContextDestructionObserver(context)
- , m_audioConstraints(audioConstraints)
- , m_videoConstraints(videoConstraints)
- , m_controller(controller)
- , m_successCallback(successCallback)
- , m_errorCallback(errorCallback)
+UserMediaRequest::UserMediaRequest(Document& document, UserMediaController& controller, Ref<MediaConstraintsImpl>&& audioConstraints, Ref<MediaConstraintsImpl>&& videoConstraints, DOMPromise<IDLInterface<MediaStream>>&& promise)
+ : ContextDestructionObserver(&document)
+ , m_audioConstraints(WTFMove(audioConstraints))
+ , m_videoConstraints(WTFMove(videoConstraints))
+ , m_controller(&controller)
+ , m_promise(WTFMove(promise))
{
}
@@ -101,126 +77,158 @@ UserMediaRequest::~UserMediaRequest()
{
}
-SecurityOrigin* UserMediaRequest::securityOrigin() const
+SecurityOrigin* UserMediaRequest::userMediaDocumentOrigin() const
{
- if (m_scriptExecutionContext)
- return m_scriptExecutionContext->securityOrigin();
-
- return nullptr;
-}
-
-void UserMediaRequest::start()
-{
- // 1 - make sure the system is capable of supporting the audio and video constraints. We don't want to ask for
- // user permission if the constraints can not be suported.
- MediaStreamCenter::shared().validateRequestConstraints(this, m_audioConstraints, m_videoConstraints);
+ if (!m_scriptExecutionContext)
+ return nullptr;
+ return m_scriptExecutionContext->securityOrigin();
}
-
-void UserMediaRequest::constraintsValidated()
+SecurityOrigin* UserMediaRequest::topLevelDocumentOrigin() const
{
- if (m_controller)
- callOnMainThread(bind(&UserMediaRequest::requestPermission, this));
+ if (!m_scriptExecutionContext)
+ return nullptr;
+ return &m_scriptExecutionContext->topOrigin();
}
-void UserMediaRequest::requestPermission()
+static bool isSecure(DocumentLoader& documentLoader)
{
- // 2 - The constraints are valid, ask the user for access to media.
- if (m_controller)
- m_controller->requestPermission(this);
+ auto& response = documentLoader.response();
+ return response.url().protocolIs("https")
+ && response.certificateInfo()
+ && !response.certificateInfo()->containsNonRootSHA1SignedCertificate();
}
-void UserMediaRequest::userMediaAccessGranted()
+static bool canCallGetUserMedia(Document& document, String& errorMessage)
{
- callOnMainThread(bind(&UserMediaRequest::createMediaStream, this));
-}
+ bool requiresSecureConnection = document.settings().mediaCaptureRequiresSecureConnection();
+ if (requiresSecureConnection && !isSecure(*document.loader())) {
+ errorMessage = "Trying to call getUserMedia from an insecure document.";
+ return false;
+ }
-void UserMediaRequest::createMediaStream()
-{
- // 3 - the user granted access, ask platform to create the media stream descriptors.
- MediaStreamCenter::shared().createMediaStream(this, m_audioConstraints, m_videoConstraints);
+ auto& topDocument = document.topDocument();
+ if (&document != &topDocument) {
+ auto& topOrigin = topDocument.topOrigin();
+
+ if (!document.securityOrigin().isSameSchemeHostPort(topOrigin)) {
+ errorMessage = "Trying to call getUserMedia from a document with a different security origin than its top-level frame.";
+ return false;
+ }
+
+ for (auto* ancestorDocument = document.parentDocument(); ancestorDocument != &topDocument; ancestorDocument = ancestorDocument->parentDocument()) {
+ if (requiresSecureConnection && !isSecure(*ancestorDocument->loader())) {
+ errorMessage = "Trying to call getUserMedia from a document with an insecure parent frame.";
+ return false;
+ }
+
+ if (!ancestorDocument->securityOrigin().isSameSchemeHostPort(topOrigin)) {
+ errorMessage = "Trying to call getUserMedia from a document with a different security origin than its top-level frame.";
+ return false;
+ }
+ }
+ }
+
+ return true;
}
-void UserMediaRequest::userMediaAccessDenied()
+void UserMediaRequest::start()
{
- failedToCreateStreamWithPermissionError();
-}
+ if (!m_scriptExecutionContext || !m_controller) {
+ deny(MediaAccessDenialReason::OtherFailure, emptyString());
+ return;
+ }
-void UserMediaRequest::constraintsInvalid(const String& constraintName)
-{
- failedToCreateStreamWithConstraintsError(constraintName);
-}
+ Document& document = downcast<Document>(*m_scriptExecutionContext);
-void UserMediaRequest::didCreateStream(PassRefPtr<MediaStreamPrivate> privateStream)
-{
- if (!m_scriptExecutionContext || !m_successCallback)
+ // 10.2 - 6.3 Optionally, e.g., based on a previously-established user preference, for security reasons,
+ // or due to platform limitations, jump to the step labeled Permission Failure below.
+ String errorMessage;
+ if (!canCallGetUserMedia(document, errorMessage)) {
+ deny(MediaAccessDenialReason::PermissionDenied, emptyString());
+ document.domWindow()->printErrorMessage(errorMessage);
return;
+ }
- callOnMainThread(bind(&UserMediaRequest::callSuccessHandler, this, privateStream));
+ m_controller->requestUserMediaAccess(*this);
}
-void UserMediaRequest::callSuccessHandler(PassRefPtr<MediaStreamPrivate> privateStream)
+void UserMediaRequest::allow(const String& audioDeviceUID, const String& videoDeviceUID)
{
- // 4 - Create the MediaStream and pass it to the success callback.
- ASSERT(m_successCallback);
+ m_allowedAudioDeviceUID = audioDeviceUID;
+ m_allowedVideoDeviceUID = videoDeviceUID;
- RefPtr<MediaStream> stream = MediaStream::create(*m_scriptExecutionContext, privateStream);
+ RefPtr<UserMediaRequest> protectedThis = this;
+ RealtimeMediaSourceCenter::NewMediaStreamHandler callback = [this, protectedThis = WTFMove(protectedThis)](RefPtr<MediaStreamPrivate>&& privateStream) mutable {
+ if (!m_scriptExecutionContext)
+ return;
- Vector<RefPtr<MediaStreamTrack>> tracks = stream->getAudioTracks();
- for (auto iter = tracks.begin(); iter != tracks.end(); ++iter)
- (*iter)->applyConstraints(m_audioConstraints);
+ if (!privateStream) {
+ deny(MediaAccessDenialReason::HardwareError, emptyString());
+ return;
+ }
- tracks = stream->getVideoTracks();
- for (auto iter = tracks.begin(); iter != tracks.end(); ++iter)
- (*iter)->applyConstraints(m_videoConstraints);
-
- m_successCallback->handleEvent(stream.get());
-}
+ auto stream = MediaStream::create(*m_scriptExecutionContext, WTFMove(privateStream));
+ if (stream->getTracks().isEmpty()) {
+ deny(MediaAccessDenialReason::HardwareError, emptyString());
+ return;
+ }
-void UserMediaRequest::failedToCreateStreamWithConstraintsError(const String& constraintName)
-{
- ASSERT(!constraintName.isEmpty());
- if (!m_scriptExecutionContext)
- return;
+ for (auto& track : stream->getAudioTracks())
+ track->source().startProducingData();
- if (!m_errorCallback)
- return;
+ for (auto& track : stream->getVideoTracks())
+ track->source().startProducingData();
+
+ m_promise.resolve(stream);
+ };
- RefPtr<NavigatorUserMediaError> error = NavigatorUserMediaError::create(NavigatorUserMediaError::constraintNotSatisfiedErrorName(), constraintName);
- callOnMainThread(bind(&UserMediaRequest::callErrorHandler, this, error.release()));
+ RealtimeMediaSourceCenter::singleton().createMediaStream(WTFMove(callback), m_allowedAudioDeviceUID, m_allowedVideoDeviceUID, &m_audioConstraints.get(), &m_videoConstraints.get());
}
-void UserMediaRequest::failedToCreateStreamWithPermissionError()
+void UserMediaRequest::deny(MediaAccessDenialReason reason, const String& invalidConstraint)
{
if (!m_scriptExecutionContext)
return;
- if (!m_errorCallback)
- return;
-
- RefPtr<NavigatorUserMediaError> error = NavigatorUserMediaError::create(NavigatorUserMediaError::permissionDeniedErrorName(), emptyString());
- callOnMainThread(bind(&UserMediaRequest::callErrorHandler, this, error.release()));
-}
-
-void UserMediaRequest::callErrorHandler(PassRefPtr<NavigatorUserMediaError> prpError)
-{
- RefPtr<NavigatorUserMediaError> error = prpError;
-
- ASSERT(error);
-
- m_errorCallback->handleEvent(error.get());
+ switch (reason) {
+ case MediaAccessDenialReason::NoConstraints:
+ m_promise.reject(TypeError);
+ break;
+ case MediaAccessDenialReason::UserMediaDisabled:
+ m_promise.reject(SECURITY_ERR);
+ break;
+ case MediaAccessDenialReason::NoCaptureDevices:
+ m_promise.reject(NOT_FOUND_ERR);
+ break;
+ case MediaAccessDenialReason::InvalidConstraint:
+ m_promise.rejectType<IDLInterface<OverconstrainedError>>(OverconstrainedError::create(invalidConstraint, ASCIILiteral("Invalid constraint")).get());
+ break;
+ case MediaAccessDenialReason::HardwareError:
+ m_promise.reject(NotReadableError);
+ break;
+ case MediaAccessDenialReason::OtherFailure:
+ m_promise.reject(ABORT_ERR);
+ break;
+ case MediaAccessDenialReason::PermissionDenied:
+ m_promise.reject(NotAllowedError);
+ break;
+ }
}
void UserMediaRequest::contextDestroyed()
{
- Ref<UserMediaRequest> protect(*this);
-
+ ContextDestructionObserver::contextDestroyed();
+ Ref<UserMediaRequest> protectedThis(*this);
if (m_controller) {
- m_controller->cancelRequest(this);
- m_controller = 0;
+ m_controller->cancelUserMediaAccessRequest(*this);
+ m_controller = nullptr;
}
+}
- ContextDestructionObserver::contextDestroyed();
+Document* UserMediaRequest::document() const
+{
+ return downcast<Document>(m_scriptExecutionContext);
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/mediastream/UserMediaRequest.h b/Source/WebCore/Modules/mediastream/UserMediaRequest.h
index 6ad20fe06..316fee154 100644
--- a/Source/WebCore/Modules/mediastream/UserMediaRequest.h
+++ b/Source/WebCore/Modules/mediastream/UserMediaRequest.h
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2011 Ericsson AB. All rights reserved.
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2016 Apple Inc. All rights reserved.
* Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
*
* Redistribution and use in source and binary forms, with or without
@@ -30,71 +30,66 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef UserMediaRequest_h
-#define UserMediaRequest_h
+#pragma once
#if ENABLE(MEDIA_STREAM)
#include "ActiveDOMObject.h"
-#include "MediaStreamCreationClient.h"
-#include "MediaStreamSource.h"
-#include "NavigatorUserMediaErrorCallback.h"
-#include "NavigatorUserMediaSuccessCallback.h"
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/text/WTFString.h>
+#include "JSDOMPromise.h"
namespace WebCore {
-class Dictionary;
-class Document;
-class MediaConstraints;
-class MediaStreamPrivate;
-class UserMediaController;
+class MediaConstraintsImpl;
+class MediaStream;
class SecurityOrigin;
+class UserMediaController;
-typedef int ExceptionCode;
-
-class UserMediaRequest : public MediaStreamCreationClient, public ContextDestructionObserver {
+class UserMediaRequest : public RefCounted<UserMediaRequest>, private ContextDestructionObserver {
public:
- static PassRefPtr<UserMediaRequest> create(ScriptExecutionContext*, UserMediaController*, const Dictionary& options, PassRefPtr<NavigatorUserMediaSuccessCallback>, PassRefPtr<NavigatorUserMediaErrorCallback>, ExceptionCode&);
- ~UserMediaRequest();
+ static ExceptionOr<void> start(Document&, Ref<MediaConstraintsImpl>&& audioConstraints, Ref<MediaConstraintsImpl>&& videoConstraints, DOMPromise<IDLInterface<MediaStream>>&&);
- SecurityOrigin* securityOrigin() const;
+ virtual ~UserMediaRequest();
void start();
- void userMediaAccessGranted();
- void userMediaAccessDenied();
-private:
- UserMediaRequest(ScriptExecutionContext*, UserMediaController*, PassRefPtr<MediaConstraints> audioConstraints, PassRefPtr<MediaConstraints> videoConstraints, PassRefPtr<NavigatorUserMediaSuccessCallback>, PassRefPtr<NavigatorUserMediaErrorCallback>);
+ WEBCORE_EXPORT void setAllowedMediaDeviceUIDs(const String& audioDeviceUID, const String& videoDeviceUID);
+ WEBCORE_EXPORT void allow(const String& audioDeviceUID, const String& videoDeviceUID);
+
+ enum MediaAccessDenialReason { NoConstraints, UserMediaDisabled, NoCaptureDevices, InvalidConstraint, HardwareError, PermissionDenied, OtherFailure };
+ WEBCORE_EXPORT void deny(MediaAccessDenialReason, const String& invalidConstraint);
+
+ const Vector<String>& audioDeviceUIDs() const { return m_audioDeviceUIDs; }
+ const Vector<String>& videoDeviceUIDs() const { return m_videoDeviceUIDs; }
+
+ const MediaConstraintsImpl& audioConstraints() const { return m_audioConstraints; }
+ const MediaConstraintsImpl& videoConstraints() const { return m_videoConstraints; }
- // MediaStreamCreationClient
- virtual void constraintsValidated() override final;
- virtual void constraintsInvalid(const String& constraintName) override final;
- virtual void didCreateStream(PassRefPtr<MediaStreamPrivate>) override final;
- virtual void failedToCreateStreamWithConstraintsError(const String& constraintName) override final;
- virtual void failedToCreateStreamWithPermissionError() override final;
+ const String& allowedAudioDeviceUID() const { return m_allowedAudioDeviceUID; }
+ const String& allowedVideoDeviceUID() const { return m_allowedVideoDeviceUID; }
- // ContextDestructionObserver
- virtual void contextDestroyed() override final;
+ WEBCORE_EXPORT SecurityOrigin* userMediaDocumentOrigin() const;
+ WEBCORE_EXPORT SecurityOrigin* topLevelDocumentOrigin() const;
+ WEBCORE_EXPORT Document* document() const;
+
+private:
+ UserMediaRequest(Document&, UserMediaController&, Ref<MediaConstraintsImpl>&& audioConstraints, Ref<MediaConstraintsImpl>&& videoConstraints, DOMPromise<IDLInterface<MediaStream>>&&);
+
+ void contextDestroyed() final;
- void callSuccessHandler(PassRefPtr<MediaStreamPrivate>);
- void callErrorHandler(PassRefPtr<NavigatorUserMediaError>);
- void requestPermission();
- void createMediaStream();
+ Ref<MediaConstraintsImpl> m_audioConstraints;
+ Ref<MediaConstraintsImpl> m_videoConstraints;
- RefPtr<MediaConstraints> m_audioConstraints;
- RefPtr<MediaConstraints> m_videoConstraints;
+ Vector<String> m_videoDeviceUIDs;
+ Vector<String> m_audioDeviceUIDs;
- UserMediaController* m_controller;
+ String m_allowedVideoDeviceUID;
+ String m_allowedAudioDeviceUID;
- RefPtr<NavigatorUserMediaSuccessCallback> m_successCallback;
- RefPtr<NavigatorUserMediaErrorCallback> m_errorCallback;
+ UserMediaController* m_controller;
+ DOMPromise<IDLInterface<MediaStream>> m_promise;
+ RefPtr<UserMediaRequest> m_protector;
};
} // namespace WebCore
#endif // ENABLE(MEDIA_STREAM)
-
-#endif // UserMediaRequest_h
diff --git a/Source/WebCore/Modules/mediastream/VideoStreamTrack.cpp b/Source/WebCore/Modules/mediastream/VideoStreamTrack.cpp
deleted file mode 100644
index 2bd432d0e..000000000
--- a/Source/WebCore/Modules/mediastream/VideoStreamTrack.cpp
+++ /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 COMPUTER, 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
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (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_STREAM)
-
-#include "VideoStreamTrack.h"
-
-#include "Dictionary.h"
-#include "ScriptExecutionContext.h"
-#include <wtf/NeverDestroyed.h>
-
-namespace WebCore {
-
-RefPtr<VideoStreamTrack> VideoStreamTrack::create(ScriptExecutionContext& context, const Dictionary& videoConstraints)
-{
- return adoptRef(new VideoStreamTrack(context, *MediaStreamTrackPrivate::create(0), &videoConstraints));
-}
-
-RefPtr<VideoStreamTrack> VideoStreamTrack::create(ScriptExecutionContext& context, MediaStreamTrackPrivate& privateTrack)
-{
- return adoptRef(new VideoStreamTrack(context, privateTrack, 0));
-}
-
-RefPtr<VideoStreamTrack> VideoStreamTrack::create(MediaStreamTrack& track)
-{
- return adoptRef(new VideoStreamTrack(track));
-}
-
-VideoStreamTrack::VideoStreamTrack(ScriptExecutionContext& context, MediaStreamTrackPrivate& privateTrack, const Dictionary* videoConstraints)
- : MediaStreamTrack(context, privateTrack, videoConstraints)
-{
-}
-
-VideoStreamTrack::VideoStreamTrack(MediaStreamTrack& track)
- : MediaStreamTrack(track)
-{
-}
-
-const AtomicString& VideoStreamTrack::kind() const
-{
- static NeverDestroyed<AtomicString> videoKind("video", AtomicString::ConstructFromLiteral);
- return videoKind;
-}
-
-} // namespace WebCore
-
-#endif // ENABLE(MEDIA_STREAM)
diff --git a/Source/WebCore/Modules/mediastream/VideoStreamTrack.h b/Source/WebCore/Modules/mediastream/VideoStreamTrack.h
deleted file mode 100644
index 94d0aea2c..000000000
--- a/Source/WebCore/Modules/mediastream/VideoStreamTrack.h
+++ /dev/null
@@ -1,59 +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 COMPUTER, 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
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (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 VideoStreamTrack_h
-#define VideoStreamTrack_h
-
-#if ENABLE(MEDIA_STREAM)
-
-#include "MediaStreamTrack.h"
-#include <wtf/RefCounted.h>
-#include <wtf/Vector.h>
-
-namespace WebCore {
-
-class MediaStreamSource;
-class ScriptExecutionContext;
-
-class VideoStreamTrack final : public MediaStreamTrack {
-public:
- static RefPtr<VideoStreamTrack> create(ScriptExecutionContext&, const Dictionary&);
- static RefPtr<VideoStreamTrack> create(ScriptExecutionContext&, MediaStreamTrackPrivate&);
- static RefPtr<VideoStreamTrack> create(MediaStreamTrack&);
-
- virtual ~VideoStreamTrack() { }
-
- virtual const AtomicString& kind() const override;
-
-private:
- VideoStreamTrack(ScriptExecutionContext&, MediaStreamTrackPrivate&, const Dictionary*);
- explicit VideoStreamTrack(MediaStreamTrack&);
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(MEDIA_STREAM)
-
-#endif // VideoStreamTrack_h
diff --git a/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCDataChannelHandler.cpp b/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCDataChannelHandler.cpp
new file mode 100644
index 000000000..564b6d1c8
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCDataChannelHandler.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2017 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. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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 "LibWebRTCDataChannelHandler.h"
+
+#if USE(LIBWEBRTC)
+
+#include "RTCDataChannel.h"
+#include <wtf/MainThread.h>
+
+namespace WebCore {
+
+LibWebRTCDataChannelHandler::~LibWebRTCDataChannelHandler()
+{
+ if (m_client)
+ m_channel->UnregisterObserver();
+}
+
+void LibWebRTCDataChannelHandler::setClient(RTCDataChannelHandlerClient* client)
+{
+ m_client = client;
+ if (m_client)
+ m_channel->RegisterObserver(this);
+ else
+ m_channel->UnregisterObserver();
+}
+
+bool LibWebRTCDataChannelHandler::sendStringData(const String& text)
+{
+ return m_channel->Send({rtc::CopyOnWriteBuffer(text.utf8().data(), text.length()), false});
+}
+
+bool LibWebRTCDataChannelHandler::sendRawData(const char* data, size_t length)
+{
+ return m_channel->Send({rtc::CopyOnWriteBuffer(data, length), true});
+}
+
+void LibWebRTCDataChannelHandler::close()
+{
+ m_channel->Close();
+}
+
+void LibWebRTCDataChannelHandler::OnStateChange()
+{
+ RTCDataChannel::ReadyState state;
+ switch (m_channel->state()) {
+ case webrtc::DataChannelInterface::kConnecting:
+ state = RTCDataChannel::ReadyStateConnecting;
+ break;
+ case webrtc::DataChannelInterface::kOpen:
+ state = RTCDataChannel::ReadyStateOpen;
+ break;
+ case webrtc::DataChannelInterface::kClosing:
+ state = RTCDataChannel::ReadyStateClosing;
+ break;
+ case webrtc::DataChannelInterface::kClosed:
+ state = RTCDataChannel::ReadyStateClosed;
+ break;
+ }
+ ASSERT(m_client);
+ callOnMainThread([protectedClient = makeRef(*m_client), state] {
+ protectedClient->didChangeReadyState(state);
+ });
+}
+
+void LibWebRTCDataChannelHandler::OnMessage(const webrtc::DataBuffer& buffer)
+{
+ ASSERT(m_client);
+ std::unique_ptr<webrtc::DataBuffer> protectedBuffer(new webrtc::DataBuffer(buffer));
+ callOnMainThread([protectedClient = makeRef(*m_client), buffer = WTFMove(protectedBuffer)] {
+ // FIXME: Ensure this is correct by adding some tests with non-ASCII characters.
+ const char* data = reinterpret_cast<const char*>(buffer->data.data());
+ if (buffer->binary)
+ protectedClient->didReceiveRawData(data, buffer->size());
+ else
+ protectedClient->didReceiveStringData(String(data, buffer->size()));
+ });
+}
+
+void LibWebRTCDataChannelHandler::OnBufferedAmountChange(uint64_t previousAmount)
+{
+ if (previousAmount <= m_channel->buffered_amount())
+ return;
+ ASSERT(m_client);
+ callOnMainThread([protectedClient = makeRef(*m_client)] {
+ protectedClient->bufferedAmountIsDecreasing();
+ });
+}
+
+} // namespace WebCore
+
+#endif // USE(LIBWEBRTC)
diff --git a/Source/WebCore/Modules/mediastream/NavigatorUserMediaError.h b/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCDataChannelHandler.h
index ea37871da..7cc3502b7 100644
--- a/Source/WebCore/Modules/mediastream/NavigatorUserMediaError.h
+++ b/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCDataChannelHandler.h
@@ -1,6 +1,5 @@
/*
- * Copyright (C) 2011 Google Inc. All rights reserved.
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2017 Apple Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -23,43 +22,40 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef NavigatorUserMediaError_h
-#define NavigatorUserMediaError_h
+#pragma once
-#include "DOMError.h"
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
+#if USE(LIBWEBRTC)
-#if ENABLE(MEDIA_STREAM)
+#include "LibWebRTCMacros.h"
+#include "RTCDataChannelHandler.h"
+#include <webrtc/api/datachannelinterface.h>
namespace WebCore {
-class NavigatorUserMediaError : public DOMError {
-public:
- static PassRefPtr<NavigatorUserMediaError> create(const String& name, const String& constraintName)
- {
- return adoptRef(new NavigatorUserMediaError(name, constraintName));
- }
-
- virtual ~NavigatorUserMediaError() { }
-
- const String& constraintName() const { return m_constraintName; }
+class RTCDataChannelHandlerClient;
- static const AtomicString& permissionDeniedErrorName();
- static const AtomicString& constraintNotSatisfiedErrorName();
+class LibWebRTCDataChannelHandler final : public RTCDataChannelHandler, private webrtc::DataChannelObserver {
+public:
+ explicit LibWebRTCDataChannelHandler(rtc::scoped_refptr<webrtc::DataChannelInterface>&& channel) : m_channel(WTFMove(channel)) { ASSERT(m_channel); }
+ ~LibWebRTCDataChannelHandler();
private:
- NavigatorUserMediaError(const String& name, const String& constraintName)
- : DOMError(name)
- , m_constraintName(constraintName)
- {
- }
-
- String m_constraintName;
+ // RTCDataChannelHandler API
+ void setClient(RTCDataChannelHandlerClient*) final;
+ bool sendStringData(const String&) final;
+ bool sendRawData(const char*, size_t) final;
+ void close() final;
+ size_t bufferedAmount() const final { return static_cast<size_t>(m_channel->buffered_amount()); }
+
+ // webrtc::DataChannelObserver API
+ void OnStateChange();
+ void OnMessage(const webrtc::DataBuffer&);
+ void OnBufferedAmountChange(uint64_t);
+
+ rtc::scoped_refptr<webrtc::DataChannelInterface> m_channel;
+ RTCDataChannelHandlerClient* m_client { nullptr };
};
} // namespace WebCore
-#endif // ENABLE(MEDIA_STREAM)
-
-#endif // NavigatorUserMediaError_h
+#endif // USE(LIBWEBRTC)
diff --git a/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.cpp b/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.cpp
new file mode 100644
index 000000000..e46c31c4e
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.cpp
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2017 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. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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 "LibWebRTCMediaEndpoint.h"
+
+#if USE(LIBWEBRTC)
+
+#include "EventNames.h"
+#include "LibWebRTCDataChannelHandler.h"
+#include "LibWebRTCPeerConnectionBackend.h"
+#include "LibWebRTCProvider.h"
+#include "MediaStreamEvent.h"
+#include "NotImplemented.h"
+#include "PlatformStrategies.h"
+#include "RTCDataChannel.h"
+#include "RTCDataChannelEvent.h"
+#include "RTCIceCandidate.h"
+#include "RTCPeerConnection.h"
+#include "RTCSessionDescription.h"
+#include "RTCTrackEvent.h"
+#include "RealtimeIncomingAudioSource.h"
+#include "RealtimeIncomingVideoSource.h"
+#include <webrtc/api/peerconnectionfactory.h>
+#include <webrtc/base/physicalsocketserver.h>
+#include <webrtc/p2p/base/basicpacketsocketfactory.h>
+#include <webrtc/p2p/client/basicportallocator.h>
+#include <wtf/MainThread.h>
+
+#include "CoreMediaSoftLink.h"
+
+namespace WebCore {
+
+LibWebRTCMediaEndpoint::LibWebRTCMediaEndpoint(LibWebRTCPeerConnectionBackend& peerConnection, LibWebRTCProvider& client)
+ : m_peerConnectionBackend(peerConnection)
+ , m_backend(client.createPeerConnection(*this))
+ , m_createSessionDescriptionObserver(*this)
+ , m_setLocalSessionDescriptionObserver(*this)
+ , m_setRemoteSessionDescriptionObserver(*this)
+{
+ ASSERT(m_backend);
+}
+
+static inline const char* sessionDescriptionType(RTCSessionDescription::SdpType sdpType)
+{
+ switch (sdpType) {
+ case RTCSessionDescription::SdpType::Offer:
+ return "offer";
+ case RTCSessionDescription::SdpType::Pranswer:
+ return "pranswer";
+ case RTCSessionDescription::SdpType::Answer:
+ return "answer";
+ case RTCSessionDescription::SdpType::Rollback:
+ return "rollback";
+ }
+}
+
+static inline RTCSessionDescription::SdpType fromSessionDescriptionType(const webrtc::SessionDescriptionInterface& description)
+{
+ auto type = description.type();
+ if (type == webrtc::SessionDescriptionInterface::kOffer)
+ return RTCSessionDescription::SdpType::Offer;
+ if (type == webrtc::SessionDescriptionInterface::kAnswer)
+ return RTCSessionDescription::SdpType::Answer;
+ ASSERT(type == webrtc::SessionDescriptionInterface::kPrAnswer);
+ return RTCSessionDescription::SdpType::Pranswer;
+}
+
+static inline RefPtr<RTCSessionDescription> fromSessionDescription(const webrtc::SessionDescriptionInterface* description)
+{
+ if (!description)
+ return nullptr;
+
+ std::string sdp;
+ description->ToString(&sdp);
+ String sdpString(sdp.data(), sdp.size());
+
+ return RTCSessionDescription::create(fromSessionDescriptionType(*description), WTFMove(sdpString));
+}
+
+RefPtr<RTCSessionDescription> LibWebRTCMediaEndpoint::localDescription() const
+{
+ // FIXME: We might want to create a new object only if the session actually changed.
+ return fromSessionDescription(m_backend->local_description());
+}
+
+RefPtr<RTCSessionDescription> LibWebRTCMediaEndpoint::remoteDescription() const
+{
+ // FIXME: We might want to create a new object only if the session actually changed.
+ return fromSessionDescription(m_backend->remote_description());
+}
+
+void LibWebRTCMediaEndpoint::doSetLocalDescription(RTCSessionDescription& description)
+{
+ webrtc::SdpParseError error;
+ std::unique_ptr<webrtc::SessionDescriptionInterface> sessionDescription(webrtc::CreateSessionDescription(sessionDescriptionType(description.type()), description.sdp().utf8().data(), &error));
+
+ if (!sessionDescription) {
+ String errorMessage(error.description.data(), error.description.size());
+ m_peerConnectionBackend.setLocalDescriptionFailed(Exception { OperationError, WTFMove(errorMessage) });
+ return;
+ }
+ m_backend->SetLocalDescription(&m_setLocalSessionDescriptionObserver, sessionDescription.release());
+}
+
+void LibWebRTCMediaEndpoint::doSetRemoteDescription(RTCSessionDescription& description)
+{
+ webrtc::SdpParseError error;
+ std::unique_ptr<webrtc::SessionDescriptionInterface> sessionDescription(webrtc::CreateSessionDescription(sessionDescriptionType(description.type()), description.sdp().utf8().data(), &error));
+ if (!sessionDescription) {
+ String errorMessage(error.description.data(), error.description.size());
+ m_peerConnectionBackend.setRemoteDescriptionFailed(Exception { OperationError, WTFMove(errorMessage) });
+ return;
+ }
+ m_backend->SetRemoteDescription(&m_setRemoteSessionDescriptionObserver, sessionDescription.release());
+}
+
+static inline std::string streamId(RTCPeerConnection& connection)
+{
+ auto& senders = connection.getSenders();
+ if (senders.size()) {
+ for (RTCRtpSender& sender : senders) {
+ auto* track = sender.track();
+ if (track) {
+ ASSERT(sender.mediaStreamIds().size() == 1);
+ return std::string(sender.mediaStreamIds().first().utf8().data());
+ }
+ }
+ }
+ return "av_label";
+}
+
+void LibWebRTCMediaEndpoint::doCreateOffer()
+{
+ m_isInitiator = true;
+ auto& senders = m_peerConnectionBackend.connection().getSenders();
+ if (senders.size()) {
+ // FIXME: We only support one stream for the moment.
+ auto stream = LibWebRTCProvider::factory().CreateLocalMediaStream(streamId(m_peerConnectionBackend.connection()));
+ for (RTCRtpSender& sender : senders) {
+ auto* track = sender.track();
+ if (track) {
+ ASSERT(sender.mediaStreamIds().size() == 1);
+ auto& source = track->source();
+ if (source.type() == RealtimeMediaSource::Audio) {
+ auto trackSource = RealtimeOutgoingAudioSource::create(source);
+ auto rtcTrack = LibWebRTCProvider::factory().CreateAudioTrack(track->id().utf8().data(), trackSource.ptr());
+ trackSource->setTrack(rtc::scoped_refptr<webrtc::AudioTrackInterface>(rtcTrack));
+ m_peerConnectionBackend.addAudioSource(WTFMove(trackSource));
+ stream->AddTrack(WTFMove(rtcTrack));
+ } else {
+ auto videoSource = RealtimeOutgoingVideoSource::create(source);
+ auto videoTrack = LibWebRTCProvider::factory().CreateVideoTrack(track->id().utf8().data(), videoSource.ptr());
+ m_peerConnectionBackend.addVideoSource(WTFMove(videoSource));
+ stream->AddTrack(WTFMove(videoTrack));
+ }
+ }
+ }
+ m_backend->AddStream(stream);
+ }
+ m_backend->CreateOffer(&m_createSessionDescriptionObserver, nullptr);
+}
+
+void LibWebRTCMediaEndpoint::doCreateAnswer()
+{
+ m_isInitiator = false;
+
+ auto& senders = m_peerConnectionBackend.connection().getSenders();
+ if (senders.size()) {
+ // FIXME: We only support one stream for the moment.
+ auto stream = LibWebRTCProvider::factory().CreateLocalMediaStream(streamId(m_peerConnectionBackend.connection()));
+ for (RTCRtpSender& sender : senders) {
+ auto* track = sender.track();
+ if (track) {
+ ASSERT(sender.mediaStreamIds().size() == 1);
+ auto& source = track->source();
+ if (source.type() == RealtimeMediaSource::Audio) {
+ auto trackSource = RealtimeOutgoingAudioSource::create(source);
+ auto rtcTrack = LibWebRTCProvider::factory().CreateAudioTrack(track->id().utf8().data(), trackSource.ptr());
+ trackSource->setTrack(rtc::scoped_refptr<webrtc::AudioTrackInterface>(rtcTrack));
+ m_peerConnectionBackend.addAudioSource(WTFMove(trackSource));
+ stream->AddTrack(WTFMove(rtcTrack));
+ } else {
+ auto videoSource = RealtimeOutgoingVideoSource::create(source);
+ auto videoTrack = LibWebRTCProvider::factory().CreateVideoTrack(track->id().utf8().data(), videoSource.ptr());
+ m_peerConnectionBackend.addVideoSource(WTFMove(videoSource));
+ stream->AddTrack(WTFMove(videoTrack));
+ }
+ }
+ }
+ m_backend->AddStream(stream);
+ }
+ m_backend->CreateAnswer(&m_createSessionDescriptionObserver, nullptr);
+}
+
+void LibWebRTCMediaEndpoint::getStats(MediaStreamTrack* track, const DeferredPromise& promise)
+{
+ m_backend->GetStats(StatsCollector::create(*this, promise, track).get());
+}
+
+LibWebRTCMediaEndpoint::StatsCollector::StatsCollector(LibWebRTCMediaEndpoint& endpoint, const DeferredPromise& promise, MediaStreamTrack* track)
+ : m_endpoint(endpoint)
+ , m_promise(promise)
+{
+ if (track)
+ m_id = track->id();
+}
+
+void LibWebRTCMediaEndpoint::StatsCollector::OnStatsDelivered(const rtc::scoped_refptr<const webrtc::RTCStatsReport>& report)
+{
+ callOnMainThread([protectedThis = rtc::scoped_refptr<LibWebRTCMediaEndpoint::StatsCollector>(this), report] {
+ if (protectedThis->m_endpoint.isStopped())
+ return;
+
+ // FIXME: Fulfill promise with the report
+ UNUSED_PARAM(report);
+
+ protectedThis->m_endpoint.m_peerConnectionBackend.getStatsFailed(protectedThis->m_promise, Exception { TypeError, ASCIILiteral("Stats API is not yet implemented") });
+ });
+}
+
+static PeerConnectionStates::SignalingState signalingState(webrtc::PeerConnectionInterface::SignalingState state)
+{
+ switch (state) {
+ case webrtc::PeerConnectionInterface::kStable:
+ return PeerConnectionStates::SignalingState::Stable;
+ case webrtc::PeerConnectionInterface::kHaveLocalOffer:
+ return PeerConnectionStates::SignalingState::HaveLocalOffer;
+ case webrtc::PeerConnectionInterface::kHaveLocalPrAnswer:
+ return PeerConnectionStates::SignalingState::HaveLocalPrAnswer;
+ case webrtc::PeerConnectionInterface::kHaveRemoteOffer:
+ return PeerConnectionStates::SignalingState::HaveRemoteOffer;
+ case webrtc::PeerConnectionInterface::kHaveRemotePrAnswer:
+ return PeerConnectionStates::SignalingState::HaveRemotePrAnswer;
+ case webrtc::PeerConnectionInterface::kClosed:
+ return PeerConnectionStates::SignalingState::Closed;
+ }
+}
+
+void LibWebRTCMediaEndpoint::OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState rtcState)
+{
+ auto state = signalingState(rtcState);
+ callOnMainThread([protectedThis = makeRef(*this), state] {
+ if (protectedThis->isStopped())
+ return;
+ protectedThis->m_peerConnectionBackend.updateSignalingState(state);
+ });
+}
+
+static inline String trackId(webrtc::MediaStreamTrackInterface& videoTrack)
+{
+ return String(videoTrack.id().data(), videoTrack.id().size());
+}
+
+static inline Ref<MediaStreamTrack> createMediaStreamTrack(ScriptExecutionContext& context, Ref<RealtimeMediaSource>&& remoteSource)
+{
+ String trackId = remoteSource->id();
+ return MediaStreamTrack::create(context, MediaStreamTrackPrivate::create(WTFMove(remoteSource), WTFMove(trackId)));
+}
+
+void LibWebRTCMediaEndpoint::addStream(webrtc::MediaStreamInterface& stream)
+{
+ MediaStreamTrackVector tracks;
+ for (auto& videoTrack : stream.GetVideoTracks()) {
+ ASSERT(videoTrack);
+ String id = trackId(*videoTrack);
+ auto remoteSource = RealtimeIncomingVideoSource::create(WTFMove(videoTrack), WTFMove(id));
+ tracks.append(createMediaStreamTrack(*m_peerConnectionBackend.connection().scriptExecutionContext(), WTFMove(remoteSource)));
+ }
+ for (auto& audioTrack : stream.GetAudioTracks()) {
+ ASSERT(audioTrack);
+ String id = trackId(*audioTrack);
+ auto remoteSource = RealtimeIncomingAudioSource::create(WTFMove(audioTrack), WTFMove(id));
+ tracks.append(createMediaStreamTrack(*m_peerConnectionBackend.connection().scriptExecutionContext(), WTFMove(remoteSource)));
+ }
+
+ auto newStream = MediaStream::create(*m_peerConnectionBackend.connection().scriptExecutionContext(), WTFMove(tracks));
+ m_peerConnectionBackend.connection().fireEvent(MediaStreamEvent::create(eventNames().addstreamEvent, false, false, newStream.copyRef()));
+
+ Vector<RefPtr<MediaStream>> streams;
+ streams.append(newStream.copyRef());
+ for (auto& track : newStream->getTracks())
+ m_peerConnectionBackend.connection().fireEvent(RTCTrackEvent::create(eventNames().trackEvent, false, false, nullptr, track.get(), Vector<RefPtr<MediaStream>>(streams), nullptr));
+}
+
+void LibWebRTCMediaEndpoint::OnAddStream(rtc::scoped_refptr<webrtc::MediaStreamInterface> stream)
+{
+ callOnMainThread([protectedThis = makeRef(*this), stream = WTFMove(stream)] {
+ if (protectedThis->isStopped())
+ return;
+ ASSERT(stream);
+ protectedThis->addStream(*stream.get());
+ });
+}
+
+void LibWebRTCMediaEndpoint::OnRemoveStream(rtc::scoped_refptr<webrtc::MediaStreamInterface>)
+{
+ notImplemented();
+}
+
+std::unique_ptr<RTCDataChannelHandler> LibWebRTCMediaEndpoint::createDataChannel(const String& label, const RTCDataChannelInit& options)
+{
+ webrtc::DataChannelInit init;
+ init.ordered = options.ordered;
+ init.maxRetransmitTime = options.maxRetransmitTime;
+ init.maxRetransmits = options.maxRetransmits;
+ init.protocol = options.protocol.utf8().data();
+ init.negotiated = options.negotiated;
+ init.id = options.id;
+
+ return std::make_unique<LibWebRTCDataChannelHandler>(m_backend->CreateDataChannel(label.utf8().data(), &init));
+}
+
+void LibWebRTCMediaEndpoint::addDataChannel(rtc::scoped_refptr<webrtc::DataChannelInterface>&& dataChannel)
+{
+ auto protocol = dataChannel->protocol();
+ auto label = dataChannel->label();
+
+ RTCDataChannelInit init;
+ init.ordered = dataChannel->ordered();
+ init.maxRetransmitTime = dataChannel->maxRetransmitTime();
+ init.maxRetransmits = dataChannel->maxRetransmits();
+ init.protocol = String(protocol.data(), protocol.size());
+ init.negotiated = dataChannel->negotiated();
+ init.id = dataChannel->id();
+
+ bool isOpened = dataChannel->state() == webrtc::DataChannelInterface::kOpen;
+
+ auto handler = std::make_unique<LibWebRTCDataChannelHandler>(WTFMove(dataChannel));
+ ASSERT(m_peerConnectionBackend.connection().scriptExecutionContext());
+ auto channel = RTCDataChannel::create(*m_peerConnectionBackend.connection().scriptExecutionContext(), WTFMove(handler), String(label.data(), label.size()), WTFMove(init));
+
+ if (isOpened) {
+ callOnMainThread([channel = channel.copyRef()] {
+ // FIXME: We should be able to write channel->didChangeReadyState(...)
+ RTCDataChannelHandlerClient& client = channel.get();
+ client.didChangeReadyState(RTCDataChannel::ReadyStateOpen);
+ });
+ }
+
+ m_peerConnectionBackend.connection().fireEvent(RTCDataChannelEvent::create(eventNames().datachannelEvent, false, false, WTFMove(channel)));
+}
+
+void LibWebRTCMediaEndpoint::OnDataChannel(rtc::scoped_refptr<webrtc::DataChannelInterface> dataChannel)
+{
+ callOnMainThread([protectedThis = makeRef(*this), dataChannel = WTFMove(dataChannel)] {
+ if (protectedThis->isStopped())
+ return;
+ protectedThis->addDataChannel(rtc::scoped_refptr<webrtc::DataChannelInterface>(dataChannel));
+ });
+}
+
+void LibWebRTCMediaEndpoint::stop()
+{
+ ASSERT(m_backend);
+ m_backend->Close();
+ m_backend = nullptr;
+}
+
+void LibWebRTCMediaEndpoint::OnRenegotiationNeeded()
+{
+ callOnMainThread([protectedThis = makeRef(*this)] {
+ if (protectedThis->isStopped())
+ return;
+ protectedThis->m_peerConnectionBackend.markAsNeedingNegotiation();
+ });
+}
+
+static inline PeerConnectionStates::IceConnectionState iceConnectionState(webrtc::PeerConnectionInterface::IceConnectionState state)
+{
+ switch (state) {
+ case webrtc::PeerConnectionInterface::kIceConnectionNew:
+ return PeerConnectionStates::IceConnectionState::New;
+ case webrtc::PeerConnectionInterface::kIceConnectionChecking:
+ return PeerConnectionStates::IceConnectionState::Checking;
+ case webrtc::PeerConnectionInterface::kIceConnectionConnected:
+ return PeerConnectionStates::IceConnectionState::Connected;
+ case webrtc::PeerConnectionInterface::kIceConnectionCompleted:
+ return PeerConnectionStates::IceConnectionState::Completed;
+ case webrtc::PeerConnectionInterface::kIceConnectionFailed:
+ return PeerConnectionStates::IceConnectionState::Failed;
+ case webrtc::PeerConnectionInterface::kIceConnectionDisconnected:
+ return PeerConnectionStates::IceConnectionState::Disconnected;
+ case webrtc::PeerConnectionInterface::kIceConnectionClosed:
+ return PeerConnectionStates::IceConnectionState::Closed;
+ case webrtc::PeerConnectionInterface::kIceConnectionMax:
+ ASSERT_NOT_REACHED();
+ return PeerConnectionStates::IceConnectionState::New;
+ }
+}
+
+void LibWebRTCMediaEndpoint::OnIceConnectionChange(webrtc::PeerConnectionInterface::IceConnectionState state)
+{
+ auto connectionState = iceConnectionState(state);
+ callOnMainThread([protectedThis = makeRef(*this), connectionState] {
+ if (protectedThis->isStopped())
+ return;
+ if (protectedThis->m_peerConnectionBackend.connection().internalIceConnectionState() != connectionState)
+ protectedThis->m_peerConnectionBackend.connection().updateIceConnectionState(connectionState);
+ });
+}
+
+void LibWebRTCMediaEndpoint::OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState state)
+{
+ if (state == webrtc::PeerConnectionInterface::kIceGatheringComplete) {
+ callOnMainThread([protectedThis = makeRef(*this)] {
+ if (protectedThis->isStopped())
+ return;
+ protectedThis->m_peerConnectionBackend.doneGatheringCandidates();
+ });
+ }
+}
+
+void LibWebRTCMediaEndpoint::OnIceCandidate(const webrtc::IceCandidateInterface *rtcCandidate)
+{
+ ASSERT(rtcCandidate);
+
+ std::string sdp;
+ rtcCandidate->ToString(&sdp);
+ String candidateSDP(sdp.data(), sdp.size());
+
+ auto mid = rtcCandidate->sdp_mid();
+ String candidateMid(mid.data(), mid.size());
+
+ callOnMainThread([protectedThis = makeRef(*this), mid = WTFMove(candidateMid), sdp = WTFMove(candidateSDP)] {
+ if (protectedThis->isStopped())
+ return;
+ protectedThis->m_peerConnectionBackend.fireICECandidateEvent(RTCIceCandidate::create(String(sdp), String(mid), 0));
+ });
+}
+
+void LibWebRTCMediaEndpoint::OnIceCandidatesRemoved(const std::vector<cricket::Candidate>&)
+{
+ ASSERT_NOT_REACHED();
+}
+
+void LibWebRTCMediaEndpoint::createSessionDescriptionSucceeded(webrtc::SessionDescriptionInterface* description)
+{
+ std::string sdp;
+ description->ToString(&sdp);
+ String sdpString(sdp.data(), sdp.size());
+
+ callOnMainThread([protectedThis = makeRef(*this), sdp = WTFMove(sdpString)] {
+ if (protectedThis->isStopped())
+ return;
+ if (protectedThis->m_isInitiator)
+ protectedThis->m_peerConnectionBackend.createOfferSucceeded(String(sdp));
+ else
+ protectedThis->m_peerConnectionBackend.createAnswerSucceeded(String(sdp));
+ });
+}
+
+void LibWebRTCMediaEndpoint::createSessionDescriptionFailed(const std::string& errorMessage)
+{
+ String error(errorMessage.data(), errorMessage.size());
+ callOnMainThread([protectedThis = makeRef(*this), error = WTFMove(error)] {
+ if (protectedThis->isStopped())
+ return;
+ if (protectedThis->m_isInitiator)
+ protectedThis->m_peerConnectionBackend.createOfferFailed(Exception { OperationError, String(error) });
+ else
+ protectedThis->m_peerConnectionBackend.createAnswerFailed(Exception { OperationError, String(error) });
+ });
+}
+
+void LibWebRTCMediaEndpoint::setLocalSessionDescriptionSucceeded()
+{
+ callOnMainThread([protectedThis = makeRef(*this)] {
+ if (protectedThis->isStopped())
+ return;
+ protectedThis->m_peerConnectionBackend.setLocalDescriptionSucceeded();
+ });
+}
+
+void LibWebRTCMediaEndpoint::setLocalSessionDescriptionFailed(const std::string& errorMessage)
+{
+ String error(errorMessage.data(), errorMessage.size());
+ callOnMainThread([protectedThis = makeRef(*this), error = WTFMove(error)] {
+ if (protectedThis->isStopped())
+ return;
+ protectedThis->m_peerConnectionBackend.setLocalDescriptionFailed(Exception { OperationError, String(error) });
+ });
+}
+
+void LibWebRTCMediaEndpoint::setRemoteSessionDescriptionSucceeded()
+{
+ callOnMainThread([protectedThis = makeRef(*this)] {
+ if (protectedThis->isStopped())
+ return;
+ protectedThis->m_peerConnectionBackend.setRemoteDescriptionSucceeded();
+ });
+}
+
+void LibWebRTCMediaEndpoint::setRemoteSessionDescriptionFailed(const std::string& errorMessage)
+{
+ String error(errorMessage.data(), errorMessage.size());
+ callOnMainThread([protectedThis = makeRef(*this), error = WTFMove(error)] {
+ if (protectedThis->isStopped())
+ return;
+ protectedThis->m_peerConnectionBackend.setRemoteDescriptionFailed(Exception { OperationError, String(error) });
+ });
+}
+
+} // namespace WebCore
+
+#endif // USE(LIBWEBRTC)
diff --git a/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.h b/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.h
new file mode 100644
index 000000000..7b308e0b1
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCMediaEndpoint.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2017 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. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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 USE(LIBWEBRTC)
+
+#include "LibWebRTCProvider.h"
+#include "PeerConnectionBackend.h"
+#include "RealtimeOutgoingAudioSource.h"
+#include "RealtimeOutgoingVideoSource.h"
+
+#include <webrtc/api/jsep.h>
+#include <webrtc/api/peerconnectionfactory.h>
+#include <webrtc/api/peerconnectioninterface.h>
+#include <webrtc/api/rtcstatscollector.h>
+
+#include <wtf/ThreadSafeRefCounted.h>
+
+namespace webrtc {
+class CreateSessionDescriptionObserver;
+class DataChannelInterface;
+class IceCandidateInterface;
+class MediaStreamInterface;
+class PeerConnectionObserver;
+class SessionDescriptionInterface;
+class SetSessionDescriptionObserver;
+}
+
+namespace WebCore {
+
+class LibWebRTCProvider;
+class LibWebRTCPeerConnectionBackend;
+class MediaStreamTrack;
+class RTCSessionDescription;
+
+class LibWebRTCMediaEndpoint : public ThreadSafeRefCounted<LibWebRTCMediaEndpoint>, private webrtc::PeerConnectionObserver {
+public:
+ static Ref<LibWebRTCMediaEndpoint> create(LibWebRTCPeerConnectionBackend& peerConnection, LibWebRTCProvider& client) { return adoptRef(*new LibWebRTCMediaEndpoint(peerConnection, client)); }
+ virtual ~LibWebRTCMediaEndpoint() { }
+
+ webrtc::PeerConnectionInterface& backend() const { ASSERT(m_backend); return *m_backend.get(); }
+ void doSetLocalDescription(RTCSessionDescription&);
+ void doSetRemoteDescription(RTCSessionDescription&);
+ void doCreateOffer();
+ void doCreateAnswer();
+ void getStats(MediaStreamTrack*, const DeferredPromise&);
+ std::unique_ptr<RTCDataChannelHandler> createDataChannel(const String&, const RTCDataChannelInit&);
+ bool addIceCandidate(webrtc::IceCandidateInterface& candidate) { return m_backend->AddIceCandidate(&candidate); }
+
+ void stop();
+ bool isStopped() const { return !m_backend; }
+
+ RefPtr<RTCSessionDescription> localDescription() const;
+ RefPtr<RTCSessionDescription> remoteDescription() const;
+
+private:
+ LibWebRTCMediaEndpoint(LibWebRTCPeerConnectionBackend&, LibWebRTCProvider&);
+
+ // webrtc::PeerConnectionObserver API
+ void OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState) final;
+ void OnAddStream(rtc::scoped_refptr<webrtc::MediaStreamInterface>) final;
+ void OnRemoveStream(rtc::scoped_refptr<webrtc::MediaStreamInterface>) final;
+ void OnDataChannel(rtc::scoped_refptr<webrtc::DataChannelInterface>) final;
+ void OnRenegotiationNeeded() final;
+ void OnIceConnectionChange(webrtc::PeerConnectionInterface::IceConnectionState) final;
+ void OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState) final;
+ void OnIceCandidate(const webrtc::IceCandidateInterface*) final;
+ void OnIceCandidatesRemoved(const std::vector<cricket::Candidate>&) final;
+
+ void createSessionDescriptionSucceeded(webrtc::SessionDescriptionInterface*);
+ void createSessionDescriptionFailed(const std::string&);
+ void setLocalSessionDescriptionSucceeded();
+ void setLocalSessionDescriptionFailed(const std::string&);
+ void setRemoteSessionDescriptionSucceeded();
+ void setRemoteSessionDescriptionFailed(const std::string&);
+ void addStream(webrtc::MediaStreamInterface&);
+ void addDataChannel(rtc::scoped_refptr<webrtc::DataChannelInterface>&&);
+
+ int AddRef() const { ref(); return static_cast<int>(refCount()); }
+ int Release() const { deref(); return static_cast<int>(refCount()); }
+
+ class CreateSessionDescriptionObserver final : public webrtc::CreateSessionDescriptionObserver {
+ public:
+ explicit CreateSessionDescriptionObserver(LibWebRTCMediaEndpoint &endpoint) : m_endpoint(endpoint) { }
+
+ void OnSuccess(webrtc::SessionDescriptionInterface* sessionDescription) final { m_endpoint.createSessionDescriptionSucceeded(sessionDescription); }
+ void OnFailure(const std::string& error) final { m_endpoint.createSessionDescriptionFailed(error); }
+
+ int AddRef() const { return m_endpoint.AddRef(); }
+ int Release() const { return m_endpoint.Release(); }
+
+ private:
+ LibWebRTCMediaEndpoint& m_endpoint;
+ };
+
+ class SetLocalSessionDescriptionObserver final : public webrtc::SetSessionDescriptionObserver {
+ public:
+ explicit SetLocalSessionDescriptionObserver(LibWebRTCMediaEndpoint &endpoint) : m_endpoint(endpoint) { }
+
+ void OnSuccess() final { m_endpoint.setLocalSessionDescriptionSucceeded(); }
+ void OnFailure(const std::string& error) final { m_endpoint.setLocalSessionDescriptionFailed(error); }
+
+ int AddRef() const { return m_endpoint.AddRef(); }
+ int Release() const { return m_endpoint.Release(); }
+
+ private:
+ LibWebRTCMediaEndpoint& m_endpoint;
+ };
+
+ class SetRemoteSessionDescriptionObserver final : public webrtc::SetSessionDescriptionObserver {
+ public:
+ explicit SetRemoteSessionDescriptionObserver(LibWebRTCMediaEndpoint &endpoint) : m_endpoint(endpoint) { }
+
+ void OnSuccess() final { m_endpoint.setRemoteSessionDescriptionSucceeded(); }
+ void OnFailure(const std::string& error) final { m_endpoint.setRemoteSessionDescriptionFailed(error); }
+
+ int AddRef() const { return m_endpoint.AddRef(); }
+ int Release() const { return m_endpoint.Release(); }
+
+ private:
+ LibWebRTCMediaEndpoint& m_endpoint;
+ };
+
+ class StatsCollector final : public webrtc::RTCStatsCollectorCallback {
+ public:
+ static rtc::scoped_refptr<StatsCollector> create(LibWebRTCMediaEndpoint& endpoint, const DeferredPromise& promise, MediaStreamTrack* track) { return new StatsCollector(endpoint, promise, track); }
+
+ int AddRef() const { return m_endpoint.AddRef(); }
+ int Release() const { return m_endpoint.Release(); }
+
+ private:
+ StatsCollector(LibWebRTCMediaEndpoint&, const DeferredPromise&, MediaStreamTrack*);
+
+ void OnStatsDelivered(const rtc::scoped_refptr<const webrtc::RTCStatsReport>&) final;
+
+ LibWebRTCMediaEndpoint& m_endpoint;
+ const DeferredPromise& m_promise;
+ String m_id;
+ };
+
+ LibWebRTCPeerConnectionBackend& m_peerConnectionBackend;
+ rtc::scoped_refptr<webrtc::PeerConnectionInterface> m_backend;
+
+ CreateSessionDescriptionObserver m_createSessionDescriptionObserver;
+ SetLocalSessionDescriptionObserver m_setLocalSessionDescriptionObserver;
+ SetRemoteSessionDescriptionObserver m_setRemoteSessionDescriptionObserver;
+
+ bool m_isInitiator { false };
+};
+
+} // namespace WebCore
+
+#endif // USE(LIBWEBRTC)
diff --git a/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.cpp b/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.cpp
new file mode 100644
index 000000000..c8a382c5b
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.cpp
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2017 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. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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 "LibWebRTCPeerConnectionBackend.h"
+
+#if USE(LIBWEBRTC)
+
+#include "Document.h"
+#include "IceCandidate.h"
+#include "JSRTCStatsReport.h"
+#include "LibWebRTCDataChannelHandler.h"
+#include "LibWebRTCMediaEndpoint.h"
+#include "MediaEndpointConfiguration.h"
+#include "Page.h"
+#include "RTCIceCandidate.h"
+#include "RTCPeerConnection.h"
+#include "RTCRtpReceiver.h"
+#include "RTCSessionDescription.h"
+#include "RealtimeIncomingAudioSource.h"
+#include "RealtimeIncomingVideoSource.h"
+
+namespace WebCore {
+
+static std::unique_ptr<PeerConnectionBackend> createLibWebRTCPeerConnectionBackend(RTCPeerConnection& peerConnection)
+{
+ return std::make_unique<LibWebRTCPeerConnectionBackend>(peerConnection);
+}
+
+CreatePeerConnectionBackend PeerConnectionBackend::create = createLibWebRTCPeerConnectionBackend;
+
+static inline LibWebRTCProvider& libWebRTCProvider(RTCPeerConnection& peerConnection)
+{
+ ASSERT(peerConnection.scriptExecutionContext()->isDocument());
+ auto* page = static_cast<Document*>(peerConnection.scriptExecutionContext())->page();
+ return page->libWebRTCProvider();
+}
+
+LibWebRTCPeerConnectionBackend::LibWebRTCPeerConnectionBackend(RTCPeerConnection& peerConnection)
+ : PeerConnectionBackend(peerConnection)
+ , m_endpoint(LibWebRTCMediaEndpoint::create(*this, libWebRTCProvider(peerConnection)))
+{
+}
+
+LibWebRTCPeerConnectionBackend::~LibWebRTCPeerConnectionBackend()
+{
+}
+
+static webrtc::PeerConnectionInterface::RTCConfiguration configurationFromMediaEndpointConfiguration(MediaEndpointConfiguration&& configuration)
+{
+ webrtc::PeerConnectionInterface::RTCConfiguration rtcConfiguration(webrtc::PeerConnectionInterface::RTCConfigurationType::kAggressive);
+
+ if (configuration.iceTransportPolicy == PeerConnectionStates::IceTransportPolicy::Relay)
+ rtcConfiguration.type = webrtc::PeerConnectionInterface::kRelay;
+
+ if (configuration.bundlePolicy == PeerConnectionStates::BundlePolicy::MaxBundle)
+ rtcConfiguration.bundle_policy = webrtc::PeerConnectionInterface::kBundlePolicyMaxBundle;
+ else if (configuration.bundlePolicy == PeerConnectionStates::BundlePolicy::MaxCompat)
+ rtcConfiguration.bundle_policy = webrtc::PeerConnectionInterface::kBundlePolicyMaxCompat;
+
+ for (auto& server : configuration.iceServers) {
+ webrtc::PeerConnectionInterface::IceServer iceServer;
+ iceServer.username = server.username.utf8().data();
+ iceServer.password = server.credential.utf8().data();
+ for (auto& url : server.urls)
+ iceServer.urls.push_back({ url.string().utf8().data() });
+ rtcConfiguration.servers.push_back(WTFMove(iceServer));
+ }
+
+ return rtcConfiguration;
+}
+
+void LibWebRTCPeerConnectionBackend::setConfiguration(MediaEndpointConfiguration&& configuration)
+{
+ m_endpoint->backend().SetConfiguration(configurationFromMediaEndpointConfiguration(WTFMove(configuration)));
+}
+
+void LibWebRTCPeerConnectionBackend::getStats(MediaStreamTrack* track, Ref<DeferredPromise>&& promise)
+{
+ if (m_endpoint->isStopped())
+ return;
+
+ auto& statsPromise = promise.get();
+ m_statsPromises.add(&statsPromise, WTFMove(promise));
+ m_endpoint->getStats(track, statsPromise);
+}
+
+void LibWebRTCPeerConnectionBackend::getStatsSucceeded(const DeferredPromise& promise, Ref<RTCStatsReport>&& report)
+{
+ auto statsPromise = m_statsPromises.take(&promise);
+ ASSERT(statsPromise);
+ statsPromise.value()->resolve<IDLInterface<RTCStatsReport>>(WTFMove(report));
+}
+
+void LibWebRTCPeerConnectionBackend::getStatsFailed(const DeferredPromise& promise, Exception&& exception)
+{
+ auto statsPromise = m_statsPromises.take(&promise);
+ ASSERT(statsPromise);
+ statsPromise.value()->reject(WTFMove(exception));
+}
+
+void LibWebRTCPeerConnectionBackend::doSetLocalDescription(RTCSessionDescription& description)
+{
+ m_endpoint->doSetLocalDescription(description);
+ if (!m_isLocalDescriptionSet) {
+ if (m_isRemoteDescriptionSet) {
+ while (m_pendingCandidates.size())
+ m_endpoint->addIceCandidate(*m_pendingCandidates.takeLast().release());
+ }
+ m_isLocalDescriptionSet = true;
+ }
+}
+
+void LibWebRTCPeerConnectionBackend::doSetRemoteDescription(RTCSessionDescription& description)
+{
+ m_endpoint->doSetRemoteDescription(description);
+ if (!m_isRemoteDescriptionSet) {
+ if (m_isLocalDescriptionSet) {
+ while (m_pendingCandidates.size())
+ m_endpoint->addIceCandidate(*m_pendingCandidates.takeLast().release());
+ }
+ m_isRemoteDescriptionSet = true;
+ }
+}
+
+void LibWebRTCPeerConnectionBackend::doCreateOffer(RTCOfferOptions&&)
+{
+ m_endpoint->doCreateOffer();
+}
+
+void LibWebRTCPeerConnectionBackend::doCreateAnswer(RTCAnswerOptions&&)
+{
+ if (!m_isRemoteDescriptionSet) {
+ createAnswerFailed(Exception { INVALID_STATE_ERR, "No remote description set" });
+ return;
+ }
+ m_endpoint->doCreateAnswer();
+}
+
+void LibWebRTCPeerConnectionBackend::doStop()
+{
+ m_endpoint->stop();
+}
+
+void LibWebRTCPeerConnectionBackend::doAddIceCandidate(RTCIceCandidate& candidate)
+{
+ if (!m_isRemoteDescriptionSet) {
+ addIceCandidateFailed(Exception { INVALID_STATE_ERR, "No remote description set" });
+ return;
+ }
+
+ webrtc::SdpParseError error;
+ int sdpMLineIndex = candidate.sdpMLineIndex() ? candidate.sdpMLineIndex().value() : 0;
+ std::unique_ptr<webrtc::IceCandidateInterface> rtcCandidate(webrtc::CreateIceCandidate(candidate.sdpMid().utf8().data(), sdpMLineIndex, candidate.candidate().utf8().data(), &error));
+
+ if (!rtcCandidate) {
+ String message(error.description.data(), error.description.size());
+ addIceCandidateFailed(Exception { OperationError, WTFMove(message) });
+ return;
+ }
+
+ // libwebrtc does not like that ice candidates are set before the description.
+ if (!m_isLocalDescriptionSet || !m_isRemoteDescriptionSet)
+ m_pendingCandidates.append(WTFMove(rtcCandidate));
+ else if (!m_endpoint->addIceCandidate(*rtcCandidate.get())) {
+ ASSERT_NOT_REACHED();
+ addIceCandidateFailed(Exception { OperationError, ASCIILiteral("Failed to apply the received candidate") });
+ return;
+ }
+ addIceCandidateSucceeded();
+}
+
+void LibWebRTCPeerConnectionBackend::addAudioSource(Ref<RealtimeOutgoingAudioSource>&& source)
+{
+ m_audioSources.append(WTFMove(source));
+}
+
+void LibWebRTCPeerConnectionBackend::addVideoSource(Ref<RealtimeOutgoingVideoSource>&& source)
+{
+ m_videoSources.append(WTFMove(source));
+}
+
+Ref<RTCRtpReceiver> LibWebRTCPeerConnectionBackend::createReceiver(const String&, const String& trackKind, const String& trackId)
+{
+ // FIXME: We need to create a source that will get fueled once we will receive OnAddStream.
+ // For the moment, we create an empty one.
+ auto remoteTrackPrivate = (trackKind == "audio") ? MediaStreamTrackPrivate::create(RealtimeIncomingAudioSource::create(nullptr, String(trackId))) : MediaStreamTrackPrivate::create(RealtimeIncomingVideoSource::create(nullptr, String(trackId)));
+ auto remoteTrack = MediaStreamTrack::create(*m_peerConnection.scriptExecutionContext(), WTFMove(remoteTrackPrivate));
+
+ return RTCRtpReceiver::create(WTFMove(remoteTrack));
+}
+
+std::unique_ptr<RTCDataChannelHandler> LibWebRTCPeerConnectionBackend::createDataChannelHandler(const String& label, const RTCDataChannelInit& options)
+{
+ return m_endpoint->createDataChannel(label, options);
+}
+
+RefPtr<RTCSessionDescription> LibWebRTCPeerConnectionBackend::localDescription() const
+{
+ return m_endpoint->localDescription();
+}
+
+RefPtr<RTCSessionDescription> LibWebRTCPeerConnectionBackend::remoteDescription() const
+{
+ return m_endpoint->remoteDescription();
+}
+
+} // namespace WebCore
+
+#endif // USE(LIBWEBRTC)
diff --git a/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.h b/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.h
new file mode 100644
index 000000000..f5b4c565f
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/libwebrtc/LibWebRTCPeerConnectionBackend.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2017 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. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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 USE(LIBWEBRTC)
+
+#include "PeerConnectionBackend.h"
+#include <wtf/HashMap.h>
+
+namespace webrtc {
+class IceCandidateInterface;
+}
+
+namespace WebCore {
+
+class LibWebRTCMediaEndpoint;
+class RTCRtpReceiver;
+class RTCSessionDescription;
+class RTCstatsReport;
+class RealtimeOutgoingAudioSource;
+class RealtimeOutgoingVideoSource;
+
+class LibWebRTCPeerConnectionBackend final : public PeerConnectionBackend {
+public:
+ explicit LibWebRTCPeerConnectionBackend(RTCPeerConnection&);
+ ~LibWebRTCPeerConnectionBackend();
+
+private:
+ void doCreateOffer(RTCOfferOptions&&) final;
+ void doCreateAnswer(RTCAnswerOptions&&) final;
+ void doSetLocalDescription(RTCSessionDescription&) final;
+ void doSetRemoteDescription(RTCSessionDescription&) final;
+ void doAddIceCandidate(RTCIceCandidate&) final;
+ void doStop() final;
+ std::unique_ptr<RTCDataChannelHandler> createDataChannelHandler(const String&, const RTCDataChannelInit&) final;
+ void setConfiguration(MediaEndpointConfiguration&&) final;
+ void getStats(MediaStreamTrack*, Ref<DeferredPromise>&&) final;
+ Ref<RTCRtpReceiver> createReceiver(const String& transceiverMid, const String& trackKind, const String& trackId) final;
+
+ RefPtr<RTCSessionDescription> localDescription() const final;
+ RefPtr<RTCSessionDescription> currentLocalDescription() const final { return localDescription(); }
+ RefPtr<RTCSessionDescription> pendingLocalDescription() const final { return localDescription(); }
+
+ RefPtr<RTCSessionDescription> remoteDescription() const final;
+ RefPtr<RTCSessionDescription> currentRemoteDescription() const final { return remoteDescription(); }
+ RefPtr<RTCSessionDescription> pendingRemoteDescription() const final { return remoteDescription(); }
+
+ // FIXME: API to implement for real
+ Vector<RefPtr<MediaStream>> getRemoteStreams() const final { return { }; }
+ void replaceTrack(RTCRtpSender&, RefPtr<MediaStreamTrack>&&, DOMPromise<void>&&) final { }
+
+ void emulatePlatformEvent(const String&) final { }
+
+ friend LibWebRTCMediaEndpoint;
+ RTCPeerConnection& connection() { return m_peerConnection; }
+ void addAudioSource(Ref<RealtimeOutgoingAudioSource>&&);
+ void addVideoSource(Ref<RealtimeOutgoingVideoSource>&&);
+
+ void getStatsSucceeded(const DeferredPromise&, Ref<RTCStatsReport>&&);
+ void getStatsFailed(const DeferredPromise&, Exception&&);
+
+private:
+ Ref<LibWebRTCMediaEndpoint> m_endpoint;
+ bool m_isLocalDescriptionSet { false };
+ bool m_isRemoteDescriptionSet { false };
+
+ Vector<std::unique_ptr<webrtc::IceCandidateInterface>> m_pendingCandidates;
+ Vector<Ref<RealtimeOutgoingAudioSource>> m_audioSources;
+ Vector<Ref<RealtimeOutgoingVideoSource>> m_videoSources;
+ HashMap<const DeferredPromise*, Ref<DeferredPromise>> m_statsPromises;
+};
+
+} // namespace WebCore
+
+#endif // USE(LIBWEBRTC)
diff --git a/Source/WebCore/Modules/mediastream/sdp.js b/Source/WebCore/Modules/mediastream/sdp.js
new file mode 100644
index 000000000..ecd256070
--- /dev/null
+++ b/Source/WebCore/Modules/mediastream/sdp.js
@@ -0,0 +1,606 @@
+/*
+ * Copyright (C) 2014-2015 Ericsson AB. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, 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
+ * OWNER 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.
+ */
+
+"use strict";
+
+if (typeof(SDP) == "undefined")
+ var SDP = {};
+
+(function () {
+ var regexps = {
+ "vline": "^v=([\\d]+).*$",
+ "oline": "^o=([\\w\\-@\\.]+) ([\\d]+) ([\\d]+) IN (IP[46]) ([\\d\\.a-f\\:]+).*$",
+ "sline": "^s=(.*)$",
+ "tline": "^t=([\\d]+) ([\\d]+).*$",
+ "cline": "^c=IN (IP[46]) ([\\d\\.a-f\\:]+).*$",
+ "msidsemantic": "^a=msid-semantic: *WMS .*$",
+ "mblock": "^m=(audio|video|application) ([\\d]+) ([A-Z/]+)([\\d ]*)$\\r?\\n",
+ "mode": "^a=(sendrecv|sendonly|recvonly|inactive).*$",
+ "mid": "^a=mid:([!#$%&'*+-.\\w]*).*$",
+ "rtpmap": "^a=rtpmap:${type} ([\\w\\-]+)/([\\d]+)/?([\\d]+)?.*$",
+ "fmtp": "^a=fmtp:${type} ([\\w\\-=; ]+).*$",
+ "param": "([\\w\\-]+)=([\\w\\-]+);?",
+ "nack": "^a=rtcp-fb:${type} nack$",
+ "nackpli": "^a=rtcp-fb:${type} nack pli$",
+ "ccmfir": "^a=rtcp-fb:${type} ccm fir$",
+ "ericscream": "^a=rtcp-fb:${type} ericscream$",
+ "rtcp": "^a=rtcp:([\\d]+)( IN (IP[46]) ([\\d\\.a-f\\:]+))?.*$",
+ "rtcpmux": "^a=rtcp-mux.*$",
+ "cname": "^a=ssrc:(\\d+) cname:([\\w+/\\-@\\.\\{\\}]+).*$",
+ "msid": "^a=(ssrc:\\d+ )?msid:([\\w+/\\-=]+) +([\\w+/\\-=]+).*$",
+ "ufrag": "^a=ice-ufrag:([\\w+/]*).*$",
+ "pwd": "^a=ice-pwd:([\\w+/]*).*$",
+ "candidate": "^a=candidate:(\\d+) (\\d) (UDP|TCP) ([\\d\\.]*) ([\\d\\.a-f\\:]*) (\\d*)" +
+ " typ ([a-z]*)( raddr ([\\d\\.a-f\\:]*) rport (\\d*))?" +
+ "( tcptype (active|passive|so))?.*$",
+ "fingerprint": "^a=fingerprint:(sha-1|sha-256) ([A-Fa-f\\d\:]+).*$",
+ "setup": "^a=setup:(actpass|active|passive).*$",
+ "sctpmap": "^a=sctpmap:${port} ([\\w\\-]+)( [\\d]+)?.*$"
+ };
+
+ var templates = {
+ "sdp":
+ "v=${version}\r\n" +
+ "o=${username} ${sessionId} ${sessionVersion} ${netType} ${addressType} ${address}\r\n" +
+ "s=${sessionName}\r\n" +
+ "t=${startTime} ${stopTime}\r\n" +
+ "${msidsemanticLine}",
+
+ "msidsemantic": "a=msid-semantic:WMS ${mediaStreamIds}\r\n",
+
+ "mblock":
+ "m=${type} ${port} ${protocol} ${fmt}\r\n" +
+ "c=${netType} ${addressType} ${address}\r\n" +
+ "${rtcpLine}" +
+ "${rtcpMuxLine}" +
+ "a=${mode}\r\n" +
+ "${midLine}" +
+ "${rtpMapLines}" +
+ "${fmtpLines}" +
+ "${nackLines}" +
+ "${nackpliLines}" +
+ "${ccmfirLines}" +
+ "${ericScreamLines}" +
+ "${cnameLines}" +
+ "${msidLines}" +
+ "${iceCredentialLines}" +
+ "${candidateLines}" +
+ "${dtlsFingerprintLine}" +
+ "${dtlsSetupLine}" +
+ "${sctpmapLine}",
+
+ "rtcp": "a=rtcp:${port}${[ ]netType}${[ ]addressType}${[ ]address}\r\n",
+ "rtcpMux": "a=rtcp-mux\r\n",
+ "mid": "a=mid:${mid}\r\n",
+
+ "rtpMap": "a=rtpmap:${type} ${encodingName}/${clockRate}${[/]channels}\r\n",
+ "fmtp": "a=fmtp:${type} ${parameters}\r\n",
+ "nack": "a=rtcp-fb:${type} nack\r\n",
+ "nackpli": "a=rtcp-fb:${type} nack pli\r\n",
+ "ccmfir": "a=rtcp-fb:${type} ccm fir\r\n",
+ "ericscream": "a=rtcp-fb:${type} ericscream\r\n",
+
+ "cname": "a=ssrc:${ssrc} cname:${cname}\r\n",
+ "msid": "a=msid:${mediaStreamId} ${mediaStreamTrackId}\r\n",
+
+ "iceCredentials":
+ "a=ice-ufrag:${ufrag}\r\n" +
+ "a=ice-pwd:${password}\r\n",
+
+ "candidate":
+ "a=candidate:${foundation} ${componentId} ${transport} ${priority} ${address} ${port}" +
+ " typ ${type}${[ raddr ]relatedAddress}${[ rport ]relatedPort}${[ tcptype ]tcpType}\r\n",
+
+ "dtlsFingerprint": "a=fingerprint:${fingerprintHashFunction} ${fingerprint}\r\n",
+ "dtlsSetup": "a=setup:${setup}\r\n",
+
+ "sctpmap": "a=sctpmap:${port} ${app}${[ ]streams}\r\n"
+ };
+
+ function match(data, pattern, flags, alt) {
+ var r = new RegExp(pattern, flags);
+ return data.match(r) || alt && alt.match(r) || null;
+ }
+
+ function addDefaults(obj, defaults) {
+ for (var p in defaults) {
+ if (!defaults.hasOwnProperty(p))
+ continue;
+ if (typeof(obj[p]) == "undefined")
+ obj[p] = defaults[p];
+ }
+ }
+
+ function fillTemplate(template, info) {
+ var text = template;
+ for (var p in info) {
+ if (!info.hasOwnProperty(p))
+ continue;
+ var r = new RegExp("\\${(\\[[^\\]]+\\])?" + p + "(\\[[^\\]]+\\])?}");
+ text = text.replace(r, function (_, prefix, suffix) {
+ if (!info[p] && info[p] != 0)
+ return "";
+ prefix = prefix ? prefix.substr(1, prefix.length - 2) : "";
+ suffix = suffix ? suffix.substr(1, suffix.length - 2) : "";
+ return prefix + info[p] + suffix;
+ });
+ }
+ return text;
+ }
+
+ SDP.parse = function (sdpText) {
+ sdpText = new String(sdpText);
+ var sdpObj = {};
+ var parts = sdpText.split(new RegExp(regexps.mblock, "m")) || [sdpText];
+ var sblock = parts.shift();
+ var version = parseInt((match(sblock, regexps.vline, "m") || [])[1]);
+ if (!isNaN(version))
+ sdpObj.version = version;
+ var originator = match(sblock, regexps.oline, "m");;
+ if (originator) {
+ sdpObj.originator = {
+ "username": originator[1],
+ "sessionId": originator[2],
+ "sessionVersion": parseInt(originator[3]),
+ "netType": "IN",
+ "addressType": originator[4],
+ "address": originator[5]
+ };
+ }
+ var sessionName = match(sblock, regexps.sline, "m");
+ if (sessionName)
+ sdpObj.sessionName = sessionName[1];
+ var sessionTime = match(sblock, regexps.tline, "m");
+ if (sessionTime) {
+ sdpObj.startTime = parseInt(sessionTime[1]);
+ sdpObj.stopTime = parseInt(sessionTime[2]);
+ }
+ var hasMediaStreamId = !!match(sblock, regexps.msidsemantic, "m");
+ sdpObj.mediaDescriptions = [];
+
+ for (var i = 0; i < parts.length; i += 5) {
+ var mediaDescription = {
+ "type": parts[i],
+ "port": parseInt(parts[i + 1]),
+ "protocol": parts[i + 2],
+ };
+ var fmt = parts[i + 3].replace(/^[\s\uFEFF\xA0]+/, '')
+ .split(/ +/)
+ .map(function (x) {
+ return parseInt(x);
+ });
+ var mblock = parts[i + 4];
+
+ var connection = match(mblock, regexps.cline, "m", sblock);
+ if (connection) {
+ mediaDescription.netType = "IN";
+ mediaDescription.addressType = connection[1];
+ mediaDescription.address = connection[2];
+ }
+ var mode = match(mblock, regexps.mode, "m", sblock);
+ if (mode)
+ mediaDescription.mode = mode[1];
+
+ var mid = match(mblock, regexps.mid, "m", sblock);
+ if (mid)
+ mediaDescription.mid = mid[1];
+
+ var payloadTypes = [];
+ if (match(mediaDescription.protocol, "(UDP/TLS)?RTP/S?AVPF?")) {
+ mediaDescription.payloads = [];
+ payloadTypes = fmt;
+ }
+ payloadTypes.forEach(function (payloadType) {
+ var payload = { "type": payloadType };
+ var rtpmapLine = fillTemplate(regexps.rtpmap, payload);
+ var rtpmap = match(mblock, rtpmapLine, "m");
+ if (rtpmap) {
+ payload.encodingName = rtpmap[1];
+ payload.clockRate = parseInt(rtpmap[2]);
+ if (mediaDescription.type == "audio")
+ payload.channels = parseInt(rtpmap[3]) || 1;
+ else if (mediaDescription.type == "video") {
+ var nackLine = fillTemplate(regexps.nack, payload);
+ payload.nack = !!match(mblock, nackLine, "m");
+ var nackpliLine = fillTemplate(regexps.nackpli, payload);
+ payload.nackpli = !!match(mblock, nackpliLine, "m");
+ var ccmfirLine = fillTemplate(regexps.ccmfir, payload);
+ payload.ccmfir = !!match(mblock, ccmfirLine, "m");
+ var ericScreamLine = fillTemplate(regexps.ericscream, payload);
+ payload.ericscream = !!match(mblock, ericScreamLine, "m");
+ }
+ } else if (payloadType == 0 || payloadType == 8) {
+ payload.encodingName = payloadType == 8 ? "PCMA" : "PCMU";
+ payload.clockRate = 8000;
+ payload.channels = 1;
+ }
+ var fmtpLine = fillTemplate(regexps.fmtp, payload);
+ var fmtp = match(mblock, fmtpLine, "m");
+ if (fmtp) {
+ payload.parameters = {};
+ fmtp[1].replace(new RegExp(regexps.param, "g"),
+ function(_, key, value) {
+ key = key.replace(/-([a-z])/g, function (_, c) {
+ return c.toUpperCase();
+ });
+ payload.parameters[key] = isNaN(+value) ? value : +value;
+ });
+ }
+ mediaDescription.payloads.push(payload);
+ });
+
+ var rtcp = match(mblock, regexps.rtcp, "m");
+ if (rtcp) {
+ mediaDescription.rtcp = {
+ "netType": "IN",
+ "port": parseInt(rtcp[1])
+ };
+ if (rtcp[2]) {
+ mediaDescription.rtcp.addressType = rtcp[3];
+ mediaDescription.rtcp.address = rtcp[4];
+ }
+ }
+ var rtcpmux = match(mblock, regexps.rtcpmux, "m", sblock);
+ if (rtcpmux) {
+ if (!mediaDescription.rtcp)
+ mediaDescription.rtcp = {};
+ mediaDescription.rtcp.mux = true;
+ }
+
+ var cnameLines = match(mblock, regexps.cname, "mg");
+ if (cnameLines) {
+ mediaDescription.ssrcs = [];
+ cnameLines.forEach(function (line) {
+ var cname = match(line, regexps.cname, "m");
+ mediaDescription.ssrcs.push(parseInt(cname[1]));
+ if (!mediaDescription.cname)
+ mediaDescription.cname = cname[2];
+ });
+ }
+
+ if (hasMediaStreamId) {
+ var msid = match(mblock, regexps.msid, "m");
+ if (msid) {
+ mediaDescription.mediaStreamId = msid[2];
+ mediaDescription.mediaStreamTrackId = msid[3];
+ }
+ }
+
+ var ufrag = match(mblock, regexps.ufrag, "m", sblock);
+ var pwd = match(mblock, regexps.pwd, "m", sblock);
+ if (ufrag && pwd) {
+ mediaDescription.ice = {
+ "ufrag": ufrag[1],
+ "password": pwd[1]
+ };
+ }
+ var candidateLines = match(mblock, regexps.candidate, "mig");
+ if (candidateLines) {
+ if (!mediaDescription.ice)
+ mediaDescription.ice = {};
+ mediaDescription.ice.candidates = [];
+ candidateLines.forEach(function (line) {
+ var candidateLine = match(line, regexps.candidate, "mi");
+ var candidate = {
+ "foundation": candidateLine[1],
+ "componentId": parseInt(candidateLine[2]),
+ "transport": candidateLine[3].toUpperCase(),
+ "priority": parseInt(candidateLine[4]),
+ "address": candidateLine[5],
+ "port": parseInt(candidateLine[6]),
+ "type": candidateLine[7]
+ };
+ if (candidateLine[9])
+ candidate.relatedAddress = candidateLine[9];
+ if (!isNaN(candidateLine[10]))
+ candidate.relatedPort = parseInt(candidateLine[10]);
+ if (candidateLine[12])
+ candidate.tcpType = candidateLine[12];
+ else if (candidate.transport == "TCP") {
+ if (candidate.port == 0 || candidate.port == 9) {
+ candidate.tcpType = "active";
+ candidate.port = 9;
+ } else {
+ return;
+ }
+ }
+ mediaDescription.ice.candidates.push(candidate);
+ });
+ }
+
+ var fingerprint = match(mblock, regexps.fingerprint, "mi", sblock);
+ if (fingerprint) {
+ mediaDescription.dtls = {
+ "fingerprintHashFunction": fingerprint[1].toLowerCase(),
+ "fingerprint": fingerprint[2].toUpperCase()
+ };
+ }
+ var setup = match(mblock, regexps.setup, "m", sblock);
+ if (setup) {
+ if (!mediaDescription.dtls)
+ mediaDescription.dtls = {};
+ mediaDescription.dtls.setup = setup[1];
+ }
+
+ if (mediaDescription.protocol == "DTLS/SCTP") {
+ mediaDescription.sctp = {
+ "port": fmt[0]
+ };
+ var sctpmapLine = fillTemplate(regexps.sctpmap, mediaDescription.sctp);
+ var sctpmap = match(mblock, sctpmapLine, "m");
+ if (sctpmap) {
+ mediaDescription.sctp.app = sctpmap[1];
+ if (sctpmap[2])
+ mediaDescription.sctp.streams = parseInt(sctpmap[2]);
+ }
+ }
+
+ sdpObj.mediaDescriptions.push(mediaDescription);
+ }
+
+ return sdpObj;
+ };
+
+ SDP.generate = function (sdpObj) {
+ sdpObj = JSON.parse(JSON.stringify(sdpObj));
+ addDefaults(sdpObj, {
+ "version": 0,
+ "originator": {},
+ "sessionName": "-",
+ "startTime": 0,
+ "stopTime": 0,
+ "mediaDescriptions": []
+ });
+ addDefaults(sdpObj.originator, {
+ "username": "-",
+ "sessionId": "" + Math.floor((Math.random() + +new Date()) * 1e6),
+ "sessionVersion": 1,
+ "netType": "IN",
+ "addressType": "IP4",
+ "address": "127.0.0.1"
+ });
+ var sdpText = fillTemplate(templates.sdp, sdpObj);
+ sdpText = fillTemplate(sdpText, sdpObj.originator);
+
+ var msidsemanticLine = "";
+ var mediaStreamIds = [];
+ sdpObj.mediaDescriptions.forEach(function (mdesc) {
+ if (mdesc.mediaStreamId && mdesc.mediaStreamTrackId
+ && mediaStreamIds.indexOf(mdesc.mediaStreamId) == -1)
+ mediaStreamIds.push(mdesc.mediaStreamId);
+ });
+ if (mediaStreamIds.length) {
+ var msidsemanticLine = fillTemplate(templates.msidsemantic,
+ { "mediaStreamIds": mediaStreamIds.join(" ") });
+ }
+ sdpText = fillTemplate(sdpText, { "msidsemanticLine": msidsemanticLine });
+
+ sdpObj.mediaDescriptions.forEach(function (mediaDescription) {
+ addDefaults(mediaDescription, {
+ "port": 9,
+ "protocol": "UDP/TLS/RTP/SAVPF",
+ "netType": "IN",
+ "addressType": "IP4",
+ "address": "0.0.0.0",
+ "mode": "sendrecv",
+ "payloads": [],
+ "rtcp": {}
+ });
+ var mblock = fillTemplate(templates.mblock, mediaDescription);
+
+ var midInfo = {"midLine": ""};
+ if (mediaDescription.mid)
+ midInfo.midLine = fillTemplate(templates.mid, mediaDescription);
+ mblock = fillTemplate(mblock, midInfo);
+
+ var payloadInfo = {"rtpMapLines": "", "fmtpLines": "", "nackLines": "",
+ "nackpliLines": "", "ccmfirLines": "", "ericScreamLines": ""};
+ mediaDescription.payloads.forEach(function (payload) {
+ if (payloadInfo.fmt)
+ payloadInfo.fmt += " " + payload.type;
+ else
+ payloadInfo.fmt = payload.type;
+ if (!payload.channels || payload.channels == 1)
+ payload.channels = null;
+ payloadInfo.rtpMapLines += fillTemplate(templates.rtpMap, payload);
+ if (payload.parameters) {
+ var fmtpInfo = { "type": payload.type, "parameters": "" };
+ for (var p in payload.parameters) {
+ var param = p.replace(/([A-Z])([a-z])/g, function (_, a, b) {
+ return "-" + a.toLowerCase() + b;
+ });
+ if (fmtpInfo.parameters)
+ fmtpInfo.parameters += ";";
+ fmtpInfo.parameters += param + "=" + payload.parameters[p];
+ }
+ payloadInfo.fmtpLines += fillTemplate(templates.fmtp, fmtpInfo);
+ }
+ if (payload.nack)
+ payloadInfo.nackLines += fillTemplate(templates.nack, payload);
+ if (payload.nackpli)
+ payloadInfo.nackpliLines += fillTemplate(templates.nackpli, payload);
+ if (payload.ccmfir)
+ payloadInfo.ccmfirLines += fillTemplate(templates.ccmfir, payload);
+ if (payload.ericscream)
+ payloadInfo.ericScreamLines += fillTemplate(templates.ericscream, payload);
+ });
+ mblock = fillTemplate(mblock, payloadInfo);
+
+ var rtcpInfo = {"rtcpLine": "", "rtcpMuxLine": ""};
+ if (mediaDescription.rtcp.port) {
+ addDefaults(mediaDescription.rtcp, {
+ "netType": "IN",
+ "addressType": "IP4",
+ "address": ""
+ });
+ if (!mediaDescription.rtcp.address)
+ mediaDescription.rtcp.netType = mediaDescription.rtcp.addressType = "";
+ rtcpInfo.rtcpLine = fillTemplate(templates.rtcp, mediaDescription.rtcp);
+ }
+ if (mediaDescription.rtcp.mux)
+ rtcpInfo.rtcpMuxLine = templates.rtcpMux;
+ mblock = fillTemplate(mblock, rtcpInfo);
+
+ var srcAttributeLines = { "cnameLines": "", "msidLines": "" };
+ var srcAttributes = {
+ "cname": mediaDescription.cname,
+ "mediaStreamId": mediaDescription.mediaStreamId,
+ "mediaStreamTrackId": mediaDescription.mediaStreamTrackId
+ };
+ if (mediaDescription.cname && mediaDescription.ssrcs) {
+ mediaDescription.ssrcs.forEach(function (ssrc) {
+ srcAttributes.ssrc = ssrc;
+ srcAttributeLines.cnameLines += fillTemplate(templates.cname, srcAttributes);
+ if (mediaDescription.mediaStreamId && mediaDescription.mediaStreamTrackId)
+ srcAttributeLines.msidLines += fillTemplate(templates.msid, srcAttributes);
+ });
+ } else if (mediaDescription.mediaStreamId && mediaDescription.mediaStreamTrackId) {
+ srcAttributes.ssrc = null;
+ srcAttributeLines.msidLines += fillTemplate(templates.msid, srcAttributes);
+ }
+ mblock = fillTemplate(mblock, srcAttributeLines);
+
+ var iceInfo = {"iceCredentialLines": "", "candidateLines": ""};
+ if (mediaDescription.ice) {
+ iceInfo.iceCredentialLines = fillTemplate(templates.iceCredentials,
+ mediaDescription.ice);
+ if (mediaDescription.ice.candidates) {
+ mediaDescription.ice.candidates.forEach(function (candidate) {
+ addDefaults(candidate, {
+ "relatedAddress": null,
+ "relatedPort": null,
+ "tcpType": null
+ });
+ iceInfo.candidateLines += fillTemplate(templates.candidate, candidate);
+ });
+ }
+ }
+ mblock = fillTemplate(mblock, iceInfo);
+
+ var dtlsInfo = { "dtlsFingerprintLine": "", "dtlsSetupLine": "" };
+ if (mediaDescription.dtls) {
+ if (mediaDescription.dtls.fingerprint) {
+ dtlsInfo.dtlsFingerprintLine = fillTemplate(templates.dtlsFingerprint,
+ mediaDescription.dtls);
+ }
+ addDefaults(mediaDescription.dtls, {"setup": "actpass"});
+ dtlsInfo.dtlsSetupLine = fillTemplate(templates.dtlsSetup, mediaDescription.dtls);
+ }
+ mblock = fillTemplate(mblock, dtlsInfo);
+
+ var sctpInfo = {"sctpmapLine": "", "fmt": ""};
+ if (mediaDescription.sctp) {
+ addDefaults(mediaDescription.sctp, {"streams": null});
+ sctpInfo.sctpmapLine = fillTemplate(templates.sctpmap, mediaDescription.sctp);
+ sctpInfo.fmt = mediaDescription.sctp.port;
+ }
+ mblock = fillTemplate(mblock, sctpInfo);
+
+ sdpText += mblock;
+ });
+
+ return sdpText;
+ };
+
+ SDP.generateCandidateLine = function (candidateObj) {
+ addDefaults(candidateObj, {
+ "relatedAddress": null,
+ "relatedPort": null,
+ "tcpType": null
+ });
+
+ return fillTemplate(templates.candidate, candidateObj);
+ };
+
+ var expectedProperties = {
+ "session": [ "version", "originator", "sessionName", "startTime", "stopTime" ],
+ "mline": [ "type", "port", "protocol", "mode", "payloads", "rtcp", "dtls", "ice" ],
+ "mlineSubObjects": {
+ "rtcp": [ "mux" ],
+ "ice": [ "ufrag", "password" ],
+ "dtls": [ "setup", "fingerprintHashFunction", "fingerprint" ]
+ }
+ };
+
+ function hasAllProperties(object, properties) {
+ var missing = properties.filter(function (property) {
+ return !object.hasOwnProperty(property);
+ });
+
+ return !missing.length;
+ }
+
+ SDP.verifyObject = function (sdpObj) {
+ if (!hasAllProperties(sdpObj, expectedProperties.session))
+ return false;
+
+ for (var i = 0; i < sdpObj.mediaDescriptions.length; i++) {
+ var mediaDescription = sdpObj.mediaDescriptions[i];
+
+ if (!hasAllProperties(mediaDescription, expectedProperties.mline))
+ return false;
+
+ for (var p in expectedProperties.mlineSubObjects) {
+ if (!hasAllProperties(mediaDescription[p], expectedProperties.mlineSubObjects[p]))
+ return false;
+ }
+ }
+
+ return true;
+ };
+
+})();
+
+function generate(json) {
+ var object = JSON.parse(json);
+ return SDP.generate(object);
+}
+
+function parse(sdp) {
+ var object = SDP.parse(sdp);
+
+ if (!SDP.verifyObject(object))
+ return "ParseError";
+
+ return JSON.stringify(object);
+}
+
+function generateCandidateLine(json) {
+ var candidate = JSON.parse(json);
+ return SDP.generateCandidateLine(candidate).substr(2);
+}
+
+function parseCandidateLine(candidateLine) {
+ var mdesc = SDP.parse("m=application 0 NONE\r\na=" + candidateLine + "\r\n").mediaDescriptions[0];
+ if (!mdesc.ice)
+ return "ParseError";
+
+ return JSON.stringify(mdesc.ice.candidates[0]);
+}
+
+if (typeof(module) != "undefined" && typeof(exports) != "undefined")
+ module.exports = SDP;
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/airplay-button.css b/Source/WebCore/Modules/modern-media-controls/controls/airplay-button.css
new file mode 100644
index 000000000..3856c8db6
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/airplay-button.css
@@ -0,0 +1,29 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+button.airplay.on {
+ background-color: -apple-wireless-playback-target-active !important;
+ mix-blend-mode: normal !important;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/airplay-button.js b/Source/WebCore/Modules/modern-media-controls/controls/airplay-button.js
new file mode 100644
index 000000000..3b6c9ed30
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/airplay-button.js
@@ -0,0 +1,38 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class AirplayButton extends IconButton
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ cssClassName: "airplay",
+ iconName: Icons.Airplay,
+ layoutDelegate
+ });
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/airplay-placard.js b/Source/WebCore/Modules/modern-media-controls/controls/airplay-placard.js
new file mode 100644
index 000000000..f167f48e7
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/airplay-placard.js
@@ -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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class AirplayPlacard extends Placard
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ iconName: Icons.AirplayPlacard,
+ title: UIString("AirPlay"),
+ description: UIString("This video is playing on your Apple TV"),
+ layoutDelegate
+ });
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/background-tint.css b/Source/WebCore/Modules/modern-media-controls/controls/background-tint.css
new file mode 100644
index 000000000..7de98049f
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/background-tint.css
@@ -0,0 +1,48 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+.background-tint {
+ pointer-events: none;
+}
+
+.background-tint,
+.background-tint > div {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+}
+
+.background-tint > .blur {
+ /* FIXME: we want to use the real System Dark treatment here, see <rdar://problem/19993961> */
+ background-color: rgba(30, 30, 30, 0.45);
+ -webkit-backdrop-filter: saturate(180%) blur(20px);
+}
+
+.background-tint > .tint {
+ background-color: rgb(41, 41, 41);
+ mix-blend-mode: lighten;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/background-tint.js b/Source/WebCore/Modules/modern-media-controls/controls/background-tint.js
new file mode 100644
index 000000000..29df8eeb7
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/background-tint.js
@@ -0,0 +1,34 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class BackgroundTint extends LayoutNode
+{
+
+ constructor()
+ {
+ super(`<div class="background-tint"><div class="blur"></div><div class="tint"></div></div>`);
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/button.css b/Source/WebCore/Modules/modern-media-controls/controls/button.css
new file mode 100644
index 000000000..bf16ceff2
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/button.css
@@ -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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+button {
+ position: absolute;
+ top: 0;
+ left: 0;
+ border: 0;
+ -webkit-appearance: none;
+ -webkit-user-select: none;
+ -webkit-tap-highlight-color: transparent;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/button.js b/Source/WebCore/Modules/modern-media-controls/controls/button.js
new file mode 100644
index 000000000..6df091699
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/button.js
@@ -0,0 +1,83 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class Button extends LayoutItem
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ element: "<button />",
+ layoutDelegate
+ });
+
+ this._enabled = true;
+
+ if (GestureRecognizer.SupportsTouches)
+ this._tapGestureRecognizer = new TapGestureRecognizer(this.element, this);
+ else
+ this.element.addEventListener("click", this);
+ }
+
+ // Public
+
+ get enabled()
+ {
+ return this._enabled;
+ }
+
+ set enabled(flag)
+ {
+ if (this._enabled === flag)
+ return;
+
+ this._enabled = flag;
+ if (this.layoutDelegate && typeof this.layoutDelegate.layout === "function")
+ this.layoutDelegate.layout();
+ }
+
+ // Protected
+
+ handleEvent(event)
+ {
+ if (event.type === "click" && event.currentTarget === this.element)
+ this._notifyDelegateOfActivation();
+ }
+
+ gestureRecognizerStateDidChange(recognizer)
+ {
+ if (this._tapGestureRecognizer === recognizer && recognizer.state === GestureRecognizer.States.Recognized)
+ this._notifyDelegateOfActivation();
+ }
+
+ // Private
+
+ _notifyDelegateOfActivation()
+ {
+ if (this._enabled && this.uiDelegate && typeof this.uiDelegate.buttonWasPressed === "function")
+ this.uiDelegate.buttonWasPressed(this);
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/buttons-container.css b/Source/WebCore/Modules/modern-media-controls/controls/buttons-container.css
new file mode 100644
index 000000000..23469b8f3
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/buttons-container.css
@@ -0,0 +1,29 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+.buttons-container {
+ position: absolute;
+ height: 100%;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/buttons-container.js b/Source/WebCore/Modules/modern-media-controls/controls/buttons-container.js
new file mode 100644
index 000000000..0af2602ef
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/buttons-container.js
@@ -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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class ButtonsContainer extends LayoutNode
+{
+
+ constructor({ buttons = [], leftMargin = 24, rightMargin = 24, buttonMargin = 24, cssClassName = "" } = {})
+ {
+ super(`<div class="buttons-container ${cssClassName}"></div>`);
+
+ this.buttons = buttons;
+ this.leftMargin = leftMargin;
+ this.rightMargin = rightMargin;
+ this.buttonMargin = buttonMargin;
+ }
+
+ // Public
+
+ get buttons()
+ {
+ return this._buttons;
+ }
+
+ set buttons(buttons)
+ {
+ if (!Array.isArray(buttons))
+ return;
+
+ this._buttons = buttons;
+ this.needsLayout = true;
+ }
+
+ layout()
+ {
+ super.layout();
+
+ const children = [];
+ let x = this.leftMargin;
+
+ this._buttons.forEach(button => {
+ if (!button.enabled || button.dropped)
+ return;
+ button.x = x;
+ x += button.width + this.buttonMargin;
+ children.push(button);
+ });
+
+ if (children.length)
+ this.width = x - this.buttonMargin + this.rightMargin;
+ else
+ this.width = this.buttonMargin + this.rightMargin;
+
+ this.children = children;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/controls-bar.css b/Source/WebCore/Modules/modern-media-controls/controls/controls-bar.css
new file mode 100644
index 000000000..e72aea9cc
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/controls-bar.css
@@ -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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+.controls-bar {
+ position: absolute;
+ transition: opacity 0.1s linear;
+}
+
+.controls-bar.faded {
+ opacity: 0;
+ pointer-events: none;
+ transition-duration: 0.25s;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/controls-bar.js b/Source/WebCore/Modules/modern-media-controls/controls/controls-bar.js
new file mode 100644
index 000000000..25146b833
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/controls-bar.js
@@ -0,0 +1,248 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class ControlsBar extends LayoutNode
+{
+
+ constructor(mediaControls)
+ {
+ super(`<div class="controls-bar"></div>`);
+
+ this._translation = new DOMPoint;
+ this._mediaControls = mediaControls;
+
+ if (GestureRecognizer.SupportsTouches)
+ this._tapGestureRecognizer = new TapGestureRecognizer(this._mediaControls.element, this);
+
+ this.autoHideDelay = ControlsBar.DefaultAutoHideDelay;
+
+ this.fadesWhileIdle = false;
+ this.userInteractionEnabled = true;
+ }
+
+ // Public
+
+ get translation()
+ {
+ return new DOMPoint(this._translation.x, this._translation.y);
+ }
+
+ set translation(point)
+ {
+ if (this._translation.x === point.x && this._translation.y === point.y)
+ return;
+
+ this._translation = new DOMPoint(point.x, point.y);
+ this.markDirtyProperty("translation");
+ }
+
+ get userInteractionEnabled()
+ {
+ return this._userInteractionEnabled;
+ }
+
+ set userInteractionEnabled(flag)
+ {
+ if (this._userInteractionEnabled === flag)
+ return;
+
+ this._userInteractionEnabled = flag;
+ this.markDirtyProperty("userInteractionEnabled");
+ }
+
+ get fadesWhileIdle()
+ {
+ return this._fadesWhileIdle;
+ }
+
+ set fadesWhileIdle(flag)
+ {
+ if (this._fadesWhileIdle == flag)
+ return;
+
+ this._fadesWhileIdle = flag;
+
+ if (GestureRecognizer.SupportsTouches)
+ this._tapGestureRecognizer.enabled = flag;
+ else {
+ if (flag) {
+ this._mediaControls.element.addEventListener("mousemove", this);
+ this._mediaControls.element.addEventListener("mouseleave", this);
+ this.element.addEventListener("mouseenter", this);
+ this.element.addEventListener("mouseleave", this);
+ } else {
+ this._mediaControls.element.removeEventListener("mousemove", this);
+ this._mediaControls.element.removeEventListener("mouseleave", this);
+ this.element.removeEventListener("mouseenter", this);
+ this.element.removeEventListener("mouseleave", this);
+ }
+ }
+
+ if (flag && !this.faded)
+ this._resetAutoHideTimer(false);
+ else if (!flag)
+ this.faded = false;
+ }
+
+ get visible()
+ {
+ return super.visible;
+ }
+
+ set visible(flag)
+ {
+ if (this.visible === flag)
+ return;
+
+ // If we just got made visible again, let's fade the controls in.
+ if (flag && !this.visible)
+ this.faded = false;
+ else if (!flag)
+ this._cancelNonEnforcedAutoHideTimer();
+
+ super.visible = flag;
+
+ this._mediaControls.controlsBarVisibilityDidChange(this);
+ }
+
+ get faded()
+ {
+ return !!this._faded;
+ }
+
+ set faded(flag)
+ {
+ if (this._faded === flag)
+ return;
+
+ this._faded = flag;
+ if (!flag)
+ this._resetAutoHideTimer(true);
+ else
+ delete this._enforceAutoHideTimer;
+
+ this.markDirtyProperty("faded");
+ }
+
+ // Protected
+
+ handleEvent(event)
+ {
+ if (event.currentTarget === this._mediaControls.element) {
+ if (event.type === "mousemove") {
+ this.faded = false;
+ this._resetAutoHideTimer(true);
+ } else if (event.type === "mouseleave" && this._fadesWhileIdle && !this._enforceAutoHideTimer)
+ this.faded = true;
+ } else if (event.currentTarget === this.element) {
+ if (event.type === "mouseenter") {
+ this._disableAutoHiding = true;
+ this._cancelNonEnforcedAutoHideTimer();
+ } else if (event.type === "mouseleave") {
+ delete this._disableAutoHiding;
+ this._resetAutoHideTimer(true);
+ } else if (event.type === "focus")
+ this.faded = false;
+ }
+ }
+
+ gestureRecognizerStateDidChange(recognizer)
+ {
+ if (this._tapGestureRecognizer !== recognizer || recognizer.state !== GestureRecognizer.States.Recognized)
+ return;
+
+ if (this.faded)
+ this.faded = false;
+ else {
+ let ancestor = this.element.parentNode;
+ while (ancestor && !(ancestor instanceof ShadowRoot))
+ ancestor = ancestor.parentNode;
+
+ const shadowRoot = ancestor;
+ if (!shadowRoot)
+ return;
+
+ const tapLocation = recognizer.locationInClient();
+ const tappedElement = shadowRoot.elementFromPoint(tapLocation.x, tapLocation.y);
+ if (!this.element.contains(tappedElement))
+ this.faded = true;
+ }
+ }
+
+ commitProperty(propertyName)
+ {
+ if (propertyName === "translation")
+ this.element.style.transform = `translate(${this._translation.x}px, ${this._translation.y}px)`;
+ else if (propertyName === "faded")
+ this.element.classList.toggle("faded", this.faded);
+ else if (propertyName === "userInteractionEnabled")
+ this.element.style.pointerEvents = this._userInteractionEnabled ? "all" : "none";
+ else
+ super.commitProperty(propertyName);
+ }
+
+ // Private
+
+ _cancelNonEnforcedAutoHideTimer()
+ {
+ if (!this._enforceAutoHideTimer)
+ this._cancelAutoHideTimer();
+
+ }
+
+ _cancelAutoHideTimer()
+ {
+ window.clearTimeout(this._autoHideTimer);
+ delete this._autoHideTimer;
+ }
+
+ _resetAutoHideTimer(cancelable)
+ {
+ if (cancelable && this._enforceAutoHideTimer)
+ return;
+
+ this._cancelAutoHideTimer();
+
+ if (cancelable)
+ delete this._enforceAutoHideTimer;
+ else
+ this._enforceAutoHideTimer = true;
+
+ this._autoHideTimer = window.setTimeout(this._autoHideTimerFired.bind(this), this.autoHideDelay);
+ }
+
+ _autoHideTimerFired()
+ {
+ delete this._enforceAutoHideTimer;
+ if (this._disableAutoHiding)
+ return;
+
+ this._cancelAutoHideTimer();
+ this.faded = this._fadesWhileIdle;
+ }
+
+}
+
+ControlsBar.DefaultAutoHideDelay = 4000;
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/forward-button.js b/Source/WebCore/Modules/modern-media-controls/controls/forward-button.js
new file mode 100644
index 000000000..5a21221d2
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/forward-button.js
@@ -0,0 +1,38 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class ForwardButton extends SeekButton
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ cssClassName: "forward",
+ iconName: Icons.Forward,
+ layoutDelegate
+ });
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/fullscreen-button.js b/Source/WebCore/Modules/modern-media-controls/controls/fullscreen-button.js
new file mode 100644
index 000000000..9c7bfd792
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/fullscreen-button.js
@@ -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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class FullscreenButton extends IconButton
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ cssClassName: "fullscreen",
+ layoutDelegate
+ });
+
+ this.iconName = this.layoutTraits & LayoutTraits.Fullscreen ? Icons.ExitFullscreen : Icons.EnterFullscreen;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/icon-button.css b/Source/WebCore/Modules/modern-media-controls/controls/icon-button.css
new file mode 100644
index 000000000..465f5d367
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/icon-button.css
@@ -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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+button.icon {
+ -webkit-mask-repeat: no-repeat;
+}
+
+button.icon:active,
+button.icon.on {
+ background-color: white !important;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/icon-button.js b/Source/WebCore/Modules/modern-media-controls/controls/icon-button.js
new file mode 100644
index 000000000..53e8c3b73
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/icon-button.js
@@ -0,0 +1,134 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class IconButton extends Button
+{
+
+ constructor({ layoutDelegate = null, cssClassName = "", iconName = "" } = {})
+ {
+ super(layoutDelegate);
+
+ this.element.classList.add("icon");
+ if (!!cssClassName)
+ this.element.classList.add(cssClassName);
+
+ this._image = null;
+ this._iconName = "";
+ this._iconLayoutTraits = LayoutTraits.Unknown;
+
+ if (!!iconName)
+ this.iconName = iconName;
+ }
+
+ // Public
+
+ get iconName()
+ {
+ return this._iconName;
+ }
+
+ set iconName(iconName)
+ {
+ if (this._iconName === iconName)
+ return;
+
+ this._loadImage(iconName);
+ }
+
+ get on()
+ {
+ return this.element.classList.contains("on");
+ }
+
+ set on(flag) {
+ this.element.classList.toggle("on", flag);
+ }
+
+ layoutTraitsDidChange()
+ {
+ if (this._iconLayoutTraits !== this.layoutTraits)
+ this._loadImage(this._iconName);
+ }
+
+ // Protected
+
+ handleEvent(event)
+ {
+ if (event.type === "load" && event.target === this._image)
+ this._imageDidLoad();
+ else
+ super.handleEvent(event);
+ }
+
+ layout()
+ {
+ super.layout();
+
+ this.element.style.webkitMaskImage = `url(${this._image.src})`;
+ this.element.style.webkitMaskSize = `${this.width}px ${this.height}px`;
+ }
+
+ // Private
+
+ _loadImage(iconName)
+ {
+ if (this._image)
+ this._image.removeEventListener("load", this);
+
+ this._iconLayoutTraits = this.layoutTraits;
+ this._image = iconService.imageForIconNameAndLayoutTraits(iconName, this._iconLayoutTraits);
+
+ this._iconName = iconName;
+
+ if (this._image.complete)
+ this._updateImage();
+ else
+ this._image.addEventListener("load", this);
+ }
+
+ _imageDidLoad()
+ {
+ this._image.removeEventListener("load", this);
+ this._updateImage();
+ }
+
+ _updateImage()
+ {
+ this.needsLayout = true;
+
+ const width = this._image.width / window.devicePixelRatio;
+ const height = this._image.height / window.devicePixelRatio;
+
+ if (this.width === width && this.height === height)
+ return;
+
+ this.width = width;
+ this.height = height;
+
+ if (this.layoutDelegate)
+ this.layoutDelegate.needsLayout = true;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/icon-service.js b/Source/WebCore/Modules/modern-media-controls/controls/icon-service.js
new file mode 100644
index 000000000..417e9f51d
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/icon-service.js
@@ -0,0 +1,102 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+const Icons = {
+ Airplay : "airplay",
+ AirplayPlacard : "airplay-placard",
+ EnterFullscreen : "enter-fullscreen",
+ EnterPiP : "pip-in",
+ ExitFullscreen : "exit-fullscreen",
+ Forward : "forward",
+ InvalidPlacard : "invalid-placard",
+ Pause : "pause",
+ PiPPlacard : "pip-placard",
+ Play : "play",
+ Rewind : "rewind",
+ ScaleToFill : "scale-to-fill",
+ ScaleToFit : "scale-to-fit",
+ SkipBack : "interval-skip-back",
+ Start : "start",
+ Tracks : "media-selection",
+ Volume : "volume",
+ VolumeDown : "volume-down",
+ VolumeMuted : "volume-mute",
+ VolumeUp : "volume-up"
+};
+
+const IconsWithFullscreenVariants = [Icons.Airplay, Icons.Tracks, Icons.Pause, Icons.EnterPiP, Icons.Play, Icons.VolumeDown, Icons.VolumeUp];
+const IconsWithCompactVariants = [Icons.Play, Icons.Pause, Icons.SkipBack, Icons.Volume, Icons.VolumeMuted, Icons.EnterFullscreen];
+
+const iconService = new class IconService {
+
+ constructor()
+ {
+ this.images = {};
+ }
+
+ // Public
+
+ imageForIconNameAndLayoutTraits(iconName, layoutTraits)
+ {
+ const [fileName, platform] = this._fileNameAndPlatformForIconNameAndLayoutTraits(iconName, layoutTraits);
+ const path = `${platform}/${fileName}.png`;
+
+ let image = this.images[path];
+ if (image)
+ return image;
+
+ image = this.images[path] = new Image;
+
+ if (this.mediaControlsHost)
+ image.src = "data:image/png;base64," + this.mediaControlsHost.base64StringForIconAndPlatform(fileName, platform);
+ else
+ image.src = `${this.directoryPath}/${path}`;
+
+ return image;
+ }
+
+ // Private
+
+ _fileNameAndPlatformForIconNameAndLayoutTraits(iconName, layoutTraits)
+ {
+ let platform;
+ if (layoutTraits & LayoutTraits.macOS)
+ platform = "macOS";
+ else if (layoutTraits & LayoutTraits.iOS)
+ platform = "iOS";
+ else
+ throw "Could not identify icon's platform from layout traits.";
+
+ if (layoutTraits & LayoutTraits.Fullscreen && IconsWithFullscreenVariants.includes(iconName))
+ iconName += "-fullscreen";
+ else if (layoutTraits & LayoutTraits.Compact && IconsWithCompactVariants.includes(iconName))
+ iconName += "-compact";
+
+ const fileName = `${iconName}@${window.devicePixelRatio}x`;
+
+ return [fileName, platform];
+ }
+
+};
diff --git a/Source/WebCore/Modules/mediastream/MediaTrackConstraintSet.idl b/Source/WebCore/Modules/modern-media-controls/controls/invalid-placard.js
index 82754c8c6..18d3af29b 100644
--- a/Source/WebCore/Modules/mediastream/MediaTrackConstraintSet.idl
+++ b/Source/WebCore/Modules/modern-media-controls/controls/invalid-placard.js
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * 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
@@ -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 GOOGLE 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 GOOGLE 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
@@ -23,11 +23,15 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-[
- Conditional=MEDIA_STREAM,
- NoInterfaceObject,
-] interface MediaTrackConstraintSet {
- // FIXME: Not implemented.
- // https://bugs.webkit.org/show_bug.cgi?id=121954
-};
+class InvalidPlacard extends Placard
+{
+ constructor(layoutDelegate)
+ {
+ super({
+ iconName: Icons.InvalidPlacard,
+ layoutDelegate
+ });
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/ios-inline-media-controls.css b/Source/WebCore/Modules/modern-media-controls/controls/ios-inline-media-controls.css
new file mode 100644
index 000000000..ba683a9d3
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/ios-inline-media-controls.css
@@ -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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+/* Controls bar */
+
+.media-controls.ios.inline > .controls-bar {
+ left: 0;
+ right: 0;
+ bottom: 0;
+ height: 50px;
+}
+
+.media-controls.ios.inline > .controls-bar:before {
+ content: "";
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ -webkit-appearance: media-controls-light-bar-background;
+ will-change: transform;
+}
+
+:host(audio) .media-controls.ios.inline > .controls-bar:before {
+ -webkit-appearance: none;
+ background-color: rgb(212, 212, 212);
+}
+
+.media-controls.ios.inline .time-control {
+ position: absolute;
+}
+
+/* Buttons */
+
+.media-controls.ios.inline button {
+ background-color: black;
+}
+
+.media-controls.ios.inline button:active {
+ background-color: white;
+}
+
+.media-controls.ios.inline > .controls-bar button {
+ height: 100% !important;
+}
+
+/* Make right container flush to the right */
+
+.media-controls.ios.inline .buttons-container.right {
+ right: 0;
+}
+
+/* Buttons placement */
+
+.media-controls.ios.inline button.play-pause {
+ -webkit-mask-position-y: 12px;
+}
+
+.media-controls.ios.inline button.skip-back {
+ -webkit-mask-position-y: 10px;
+}
+
+.media-controls.ios.inline button.airplay {
+ -webkit-mask-position-y: 13px;
+}
+
+.media-controls.ios.inline button.pip {
+ -webkit-mask-position-y: 13px;
+}
+
+.media-controls.ios.inline button.fullscreen {
+ -webkit-mask-position-y: 13px;
+}
+
+/* Time labels */
+
+.media-controls.ios.inline .time-label {
+ top: 14.5px;
+ color: black;
+}
+
+/* Scrubber */
+
+.media-controls.ios.inline .scrubber.slider {
+ top: 13px;
+}
+
+.media-controls.ios.inline .scrubber.slider > div {
+ position: absolute;
+ top: 10px;
+ left: 2px;
+ right: 2px;
+ height: 3px;
+ border-radius: 1.5px;
+ background-color: rgba(0, 0, 0, 0.55);
+ mix-blend-mode: plus-darker;
+}
+
+.media-controls.ios.inline .scrubber.slider > input::-webkit-slider-thumb {
+ width: 15px;
+ height: 50px;
+ background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAiCAYAAABIiGl0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAxNpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iUGl4ZWxtYXRvciAyLjIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NTkwQ0UwODdBQTcxMTFFNEE5QTZGQTVGMjFBNkUxN0UiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NTkwQ0UwODhBQTcxMTFFNEE5QTZGQTVGMjFBNkUxN0UiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo1OTBDRTA4NUFBNzExMUU0QTlBNkZBNUYyMUE2RTE3RSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo1OTBDRTA4NkFBNzExMUU0QTlBNkZBNUYyMUE2RTE3RSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pofoz4wAAAJGSURBVHjaxJfNTsJAEMfbLmoNaKIhIE18A+LReOXmi/gQnnwSLz6Fnjh4Id4kHElI5EDwpIkgImWdf/Nfs5ISCrR1kkmg7c5vZz/mw3WSy66oL7ojqqiQkPotOhGdaq1XGnMTvC+KlprN5kW9Xr8sFotnhUKhCsUHs9lsCB2NRu1Op3PfaDRa8vhDdCSqnQ0E3lX7/f6NGH7RCQXfYgzG0sZacthut6/G4/Gj3lAwFjZgKyn0qNfrXYdh+Ka3FNiALdhc6Sk+1CkL4Us997E0aXga5zmX3Y87vdVt9jTJnvPA/blJJZxEnbHwtJds8Mk6V2ZTAQMsAD1EJAQHpdSpk7GAARaYAPuISE5OQpYfeYwwmBeYrMhjZeJuHkKWAtj7B7DnJchQaQsylgvwHGktL6qwXsH0TE7NETw091gjiecFFtYzmNFSo3LICyysBzBxsI5xoWUJWllHL8lSfTnViFyTyGO4PhgMbrP2lgxNpnPAdBXkkBYDsg48lqbRDLrd7t18Pn9P21PYhG3zl8yoXsY+1zCjDEufgIxjMqPCHMm5wpdBBsVeQK2QpUzZs8dKsGY+SrG8DSxvj8j6DdOKHUPZhkO3KOiDBWiZDGW3MC4f7FPVYvKQyuE8YQvzFJMUcJg+qfit3YVKc4clqB8H3zAThWzmJmzstGN1fI511LVJXYzl7hZQ00F+2dA4sH3P7Am4a0zAeDklEODZYueoVgz+DS4rWk5tTdj2cmqt4tr9sccJFqyG3N4CGxrSu3AZ0MiPAAMAZrLkuVVmRJsAAAAASUVORK5CYII=");
+ background-repeat: no-repeat;
+ background-size: 15px 17px;
+ background-position: 0px 18px;
+ background-color: transparent;
+ box-shadow: none;
+ border: none;
+ -webkit-appearance: none !important;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/ios-inline-media-controls.js b/Source/WebCore/Modules/modern-media-controls/controls/ios-inline-media-controls.js
new file mode 100644
index 000000000..963e22b01
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/ios-inline-media-controls.js
@@ -0,0 +1,109 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class IOSInlineMediaControls extends MediaControls
+{
+
+ constructor(options = {})
+ {
+ options.layoutTraits = LayoutTraits.iOS;
+
+ super(options);
+
+ this.element.classList.add("ios");
+ this.element.classList.add("inline");
+
+ this._leftContainer = new ButtonsContainer({
+ buttons: [this.playPauseButton, this.skipBackButton],
+ cssClassName: "left"
+ });
+
+ this._rightContainer = new ButtonsContainer({
+ buttons: [this.airplayButton, this.pipButton, this.fullscreenButton],
+ cssClassName: "right"
+ });
+
+ this.controlsBar.children = [this._leftContainer, this._rightContainer];
+
+ this._pinchGestureRecognizer = new PinchGestureRecognizer(this.element, this);
+ }
+
+ // Protected
+
+ gestureRecognizerStateDidChange(recognizer)
+ {
+ if (this._pinchGestureRecognizer !== recognizer)
+ return;
+
+ if (recognizer.state !== GestureRecognizer.States.Ended && recognizer.state !== GestureRecognizer.States.Changed)
+ return;
+
+ if (recognizer.scale > IOSInlineMediaControls.MinimumScaleToEnterFullscreen && this.delegate && typeof this.delegate.iOSInlineMediaControlsRecognizedPinchInGesture === "function")
+ this.delegate.iOSInlineMediaControlsRecognizedPinchInGesture();
+ }
+
+ // Public
+
+ layout()
+ {
+ super.layout();
+
+ // Reset dropped buttons.
+ for (let button of this._rightContainer.buttons)
+ delete button.dropped;
+
+ this._leftContainer.layout();
+ this._rightContainer.layout();
+
+ this.timeControl.width = this.width - this._leftContainer.width - this._rightContainer.width;
+
+ if (this.timeControl.isSufficientlyWide) {
+ this.controlsBar.insertBefore(this.timeControl, this._rightContainer);
+ this.timeControl.x = this._leftContainer.width;
+ } else {
+ this.timeControl.remove();
+ // Since we don't have enough space to display the scrubber, we may also not have
+ // enough space to display all buttons in the left and right containers, so gradually drop them.
+ for (let control of [this.airplayButton, this.pipButton, this.skipBackButton, this.fullscreenButton]) {
+ // Nothing left to do if the combined container widths is shorter than the available width.
+ if (this._leftContainer.width + this._rightContainer.width < this.width)
+ break;
+
+ // If the control was already not participating in layout, we can skip it.
+ if (!control.visible)
+ continue;
+
+ // This control must now be dropped.
+ control.dropped = true;
+
+ this._leftContainer.layout();
+ this._rightContainer.layout();
+ }
+ }
+ }
+
+}
+
+IOSInlineMediaControls.MinimumScaleToEnterFullscreen = 1.5;
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/layout-item.js b/Source/WebCore/Modules/modern-media-controls/controls/layout-item.js
new file mode 100644
index 000000000..a8d188a32
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/layout-item.js
@@ -0,0 +1,53 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+const LayoutTraits = {
+ Unknown : 0,
+ macOS : 1 << 0,
+ iOS : 1 << 1,
+ Fullscreen : 1 << 2,
+ Compact : 1 << 3,
+ ReducedPadding : 1 << 4,
+ TightPadding : 1 << 5
+};
+
+class LayoutItem extends LayoutNode
+{
+
+ constructor({ element = null, layoutDelegate = null } = {})
+ {
+ super(element);
+
+ this.layoutDelegate = layoutDelegate;
+ }
+
+ // Public
+
+ get layoutTraits()
+ {
+ return (this.layoutDelegate && this.layoutDelegate.layoutTraits) || LayoutTraits.Unknown;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/layout-node.js b/Source/WebCore/Modules/modern-media-controls/controls/layout-node.js
new file mode 100644
index 000000000..2a87f27d6
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/layout-node.js
@@ -0,0 +1,307 @@
+
+const dirtyNodes = new Set;
+const nodesRequiringChildrenUpdate = new Set;
+
+class LayoutNode
+{
+
+ constructor(stringOrElement)
+ {
+
+ if (!stringOrElement)
+ this.element = document.createElement("div");
+ else if (stringOrElement instanceof Element)
+ this.element = stringOrElement;
+ else if (typeof stringOrElement === "string" || stringOrElement instanceof String)
+ this.element = elementFromString(stringOrElement);
+
+ this._parent = null;
+ this._children = [];
+
+ this._x = 0;
+ this._y = 0;
+ this._width = 0;
+ this._height = 0;
+ this._visible = true;
+
+ this._needsLayout = false;
+ this._dirtyProperties = new Set;
+
+ this._pendingDOMManipulation = LayoutNode.DOMManipulation.None;
+ }
+
+ get x()
+ {
+ return this._x;
+ }
+
+ set x(x)
+ {
+ if (x === this._x)
+ return;
+
+ this._x = x;
+ this.markDirtyProperty("x");
+ }
+
+ get y()
+ {
+ return this._y;
+ }
+
+ set y(y)
+ {
+ if (y === this._y)
+ return;
+
+ this._y = y;
+ this.markDirtyProperty("y");
+ }
+
+ get width()
+ {
+ return this._width;
+ }
+
+ set width(width)
+ {
+ if (width === this._width)
+ return;
+
+ this._width = width;
+ this.markDirtyProperty("width");
+ }
+
+ get height()
+ {
+ return this._height;
+ }
+
+ set height(height)
+ {
+ if (height === this._height)
+ return;
+
+ this._height = height;
+ this.markDirtyProperty("height");
+ }
+
+ get visible()
+ {
+ return this._visible;
+ }
+
+ set visible(flag)
+ {
+ if (flag === this._visible)
+ return;
+
+ this._visible = flag;
+ this.markDirtyProperty("visible");
+ }
+
+ get needsLayout()
+ {
+ return this._needsLayout || this._pendingDOMManipulation !== LayoutNode.DOMManipulation.None || this._dirtyProperties.size > 0;
+ }
+
+ set needsLayout(flag)
+ {
+ if (this.needsLayout === flag)
+ return;
+
+ this._needsLayout = flag;
+ this._updateDirtyState();
+ }
+
+ get parent()
+ {
+ return this._parent;
+ }
+
+ get children()
+ {
+ return this._children;
+ }
+
+ set children(children)
+ {
+ while (this._children.length)
+ this.removeChild(this._children[0]);
+
+ for (let child of children)
+ this.addChild(child);
+ }
+
+ parentOfType(type)
+ {
+ let node = this;
+ while (node = node._parent) {
+ if (node instanceof type)
+ return node;
+ }
+ return null;
+ }
+
+ addChild(child, index)
+ {
+ child.remove();
+
+ if (index === undefined || index < 0 || index > this._children.length)
+ index = this._children.length;
+
+ this._children.splice(index, 0, child);
+ child._parent = this;
+
+ child._markNodeManipulation(LayoutNode.DOMManipulation.Addition);
+
+ return child;
+ }
+
+ insertBefore(newSibling, referenceSibling)
+ {
+ return this.addChild(newSibling, this._children.indexOf(referenceSibling));
+ }
+
+ insertAfter(newSibling, referenceSibling)
+ {
+ const index = this._children.indexOf(referenceSibling);
+ return this.addChild(newSibling, index + 1);
+ }
+
+ removeChild(child)
+ {
+ if (child._parent !== this)
+ return;
+
+ const index = this._children.indexOf(child);
+ if (index === -1)
+ return;
+
+ this._children.splice(index, 1);
+ child._parent = null;
+
+ child._markNodeManipulation(LayoutNode.DOMManipulation.Removal);
+
+ return child;
+ }
+
+ remove()
+ {
+ if (this._parent instanceof LayoutNode)
+ return this._parent.removeChild(this);
+ }
+
+ markDirtyProperty(propertyName)
+ {
+ const hadProperty = this._dirtyProperties.has(propertyName);
+ this._dirtyProperties.add(propertyName);
+
+ if (!hadProperty)
+ this._updateDirtyState();
+ }
+
+ commitProperty(propertyName)
+ {
+ const style = this.element.style;
+
+ switch (propertyName) {
+ case "x":
+ style.left = `${this._x}px`;
+ break;
+ case "y":
+ style.top = `${this._y}px`;
+ break;
+ case "width":
+ style.width = `${this._width}px`;
+ break;
+ case "height":
+ style.height = `${this._height}px`;
+ break;
+ case "visible":
+ style.display = this._visible ? "inherit" : "none";
+ break;
+ }
+ }
+
+ layout()
+ {
+ if (this._pendingDOMManipulation === LayoutNode.DOMManipulation.Removal) {
+ const parent = this.element.parentNode;
+ if (parent)
+ parent.removeChild(this.element);
+ }
+
+ for (let propertyName of this._dirtyProperties)
+ this.commitProperty(propertyName);
+
+ this._dirtyProperties.clear();
+
+ if (this._pendingDOMManipulation === LayoutNode.DOMManipulation.Addition)
+ nodesRequiringChildrenUpdate.add(this.parent);
+ }
+
+ // Private
+
+ _markNodeManipulation(manipulation)
+ {
+ this._pendingDOMManipulation = manipulation;
+ this._updateDirtyState();
+ }
+
+ _updateDirtyState()
+ {
+ if (this.needsLayout) {
+ dirtyNodes.add(this);
+ scheduler.scheduleLayout(performScheduledLayout);
+ } else {
+ dirtyNodes.delete(this);
+ if (dirtyNodes.size === 0)
+ scheduler.unscheduleLayout(performScheduledLayout);
+ }
+ }
+
+ _updateChildren()
+ {
+ let nextChildElement = null;
+ const element = this.element;
+ for (let i = this.children.length - 1; i >= 0; --i) {
+ let child = this.children[i];
+ let childElement = child.element;
+
+ if (child._pendingDOMManipulation === LayoutNode.DOMManipulation.Addition) {
+ element.insertBefore(childElement, nextChildElement);
+ child._pendingDOMManipulation = LayoutNode.DOMManipulation.None;
+ }
+
+ nextChildElement = childElement;
+ }
+ }
+
+}
+
+LayoutNode.DOMManipulation = {
+ None: 0,
+ Removal: 1,
+ Addition: 2
+};
+
+function performScheduledLayout()
+{
+ const previousDirtyNodes = Array.from(dirtyNodes);
+ dirtyNodes.clear();
+ previousDirtyNodes.forEach(node => {
+ node._needsLayout = false;
+ node.layout();
+ });
+
+ nodesRequiringChildrenUpdate.forEach(node => node._updateChildren());
+ nodesRequiringChildrenUpdate.clear();
+}
+
+function elementFromString(elementString)
+{
+ const element = document.createElement("div");
+ element.innerHTML = elementString;
+ return element.firstElementChild;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/macos-compact-inline-media-controls.css b/Source/WebCore/Modules/modern-media-controls/controls/macos-compact-inline-media-controls.css
new file mode 100644
index 000000000..456d78957
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/macos-compact-inline-media-controls.css
@@ -0,0 +1,71 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+/* Controls bar */
+
+.media-controls.mac.inline.compact > .controls-bar {
+ height: 25px;
+}
+
+/* Controls placement */
+
+.media-controls.mac.inline.compact button.play-pause {
+ -webkit-mask-position-y: 6px;
+}
+
+.media-controls.mac.inline.compact button.skip-back {
+ -webkit-mask-position-y: 6px;
+}
+
+.media-controls.mac.inline.compact .scrubber.slider {
+ top: 12px;
+}
+
+.media-controls.mac.inline.compact button.mute {
+ -webkit-mask-position-y: 6px;
+}
+
+.media-controls.mac.inline.compact button.fullscreen {
+ -webkit-mask-position-y: 6.5px;
+}
+
+/* Labels */
+
+.media-controls.mac.inline.compact .time-label,
+.media-controls.mac.inline.compact .status-label {
+ top: 5px;
+ font-size: 12px;
+}
+
+/* Volume slider */
+
+.media-controls.mac.inline.compact .volume-slider-container {
+ height: 21px;
+ transform: rotate(-90deg) translate(5px, -33px);
+}
+
+.media-controls.mac.inline.compact .volume.slider {
+ top: 5px;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/macos-fullscreen-media-controls.css b/Source/WebCore/Modules/modern-media-controls/controls/macos-fullscreen-media-controls.css
new file mode 100644
index 000000000..2d7c9766b
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/macos-fullscreen-media-controls.css
@@ -0,0 +1,119 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+/* Controls bar */
+
+.media-controls.mac.fullscreen {
+ --controls-bar-width: 468px;
+ --tracks-panel-right-margin: 40px;
+
+ width: 100% !important;
+ height: 100% !important;
+}
+
+.media-controls.mac.fullscreen > .controls-bar {
+ left: calc((100% - var(--controls-bar-width)) / 2);
+ bottom: 25px;
+ width: var(--controls-bar-width);
+ height: 75px;
+ overflow: hidden;
+}
+
+.media-controls.mac.fullscreen > .controls-bar > .background-tint > div {
+ border-radius: 8px;
+}
+
+/* Volume slider */
+
+.media-controls.mac.fullscreen .volume.slider {
+ left: 31px;
+ top: 19px;
+}
+
+.media-controls.mac.fullscreen:not(.uses-ltr-user-interface-layout-direction) .volume.slider {
+ transform: scaleX(-1);
+}
+
+.media-controls.mac.fullscreen button.volume-down {
+ -webkit-mask-position-y: 18px;
+}
+
+.media-controls.mac.fullscreen button.volume-up {
+ -webkit-mask-position-y: 17px;
+}
+
+/* Button containers */
+
+.media-controls.mac.fullscreen .buttons-container {
+ height: 44px;
+}
+
+.media-controls.mac.fullscreen .buttons-container.center {
+ left: 50%;
+ top: 0;
+ transform: translateX(-50%);
+}
+
+/* Buttons placement for center container */
+
+.media-controls.mac.fullscreen button.rewind {
+ -webkit-mask-position-y: 17px;
+}
+
+.media-controls.mac.fullscreen button.play-pause {
+ -webkit-mask-position-y: 12px;
+}
+
+.media-controls.mac.fullscreen button.forward {
+ -webkit-mask-position-y: 17px;
+}
+
+/* Make right container flush to the right */
+
+.media-controls.mac.fullscreen .buttons-container.right {
+ right: 0;
+}
+
+/* Buttons placement for right container */
+
+.media-controls.mac.fullscreen .buttons-container.right button {
+ -webkit-mask-position-y: 18px;
+}
+
+/* Scrubber */
+
+.media-controls.mac.fullscreen .time-control {
+ position: absolute;
+ left: 10px;
+ top: 48px;
+}
+
+.media-controls.mac.fullscreen .time-label {
+ font-size: 12px;
+}
+
+.media-controls.mac.fullscreen .scrubber {
+ top: -3px;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/macos-fullscreen-media-controls.js b/Source/WebCore/Modules/modern-media-controls/controls/macos-fullscreen-media-controls.js
new file mode 100644
index 000000000..2bebd171c
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/macos-fullscreen-media-controls.js
@@ -0,0 +1,173 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+const ButtonMarginForThreeButtonsOrLess = 24;
+const ButtonMarginForFourButtons = 16;
+const ButtonMarginForFiveButtons = 12;
+const FullscreenTimeControlWidth = 457;
+
+class MacOSFullscreenMediaControls extends MacOSMediaControls
+{
+
+ constructor(options = {})
+ {
+ options.layoutTraits = LayoutTraits.macOS | LayoutTraits.Fullscreen;
+
+ super(options);
+
+ this.element.classList.add("fullscreen");
+
+ // Set up fullscreen-specific buttons.
+ this.volumeDownButton = new VolumeDownButton(this);
+ this.volumeUpButton = new VolumeUpButton(this);
+ this.rewindButton = new RewindButton(this);
+ this.forwardButton = new ForwardButton(this);
+ this.fullscreenButton.isFullscreen = true;
+
+ this.volumeSlider.width = 60;
+
+ this._leftContainer = new ButtonsContainer({
+ buttons: [this.volumeDownButton, this.volumeSlider, this.volumeUpButton],
+ cssClassName: "left",
+ leftMargin: 12,
+ rightMargin: 0,
+ buttonMargin: 6
+ });
+
+ this._centerContainer = new ButtonsContainer({
+ buttons: [this.rewindButton, this.playPauseButton, this.forwardButton],
+ cssClassName: "center",
+ leftMargin: 27,
+ rightMargin: 27,
+ buttonMargin: 27
+ });
+
+ this._rightContainer = new ButtonsContainer({
+ buttons: [this.airplayButton, this.pipButton, this.tracksButton, this.fullscreenButton],
+ cssClassName: "right",
+ leftMargin: 12,
+ rightMargin: 12
+ });
+
+ this.controlsBar.children = [new BackgroundTint, this._leftContainer, this._centerContainer, this._rightContainer, this.timeControl];
+
+ this.element.addEventListener("mousedown", this);
+ }
+
+ // Public
+
+ showTracksPanel()
+ {
+ super.showTracksPanel();
+
+ const tracksButtonBounds = this.tracksButton.element.getBoundingClientRect();
+ this.tracksPanel.rightX = window.innerWidth - tracksButtonBounds.right;
+ this.tracksPanel.bottomY = window.innerHeight - tracksButtonBounds.top + 1;
+ }
+
+ // Protected
+
+ handleEvent(event)
+ {
+ switch (event.type) {
+ case "mousedown":
+ this._handleMousedown(event);
+ break;
+ case "mousemove":
+ this._handleMousemove(event);
+ break;
+ case "mouseup":
+ this._handleMouseup(event);
+ break;
+ }
+ }
+
+ layout()
+ {
+ super.layout();
+
+ const numberOfEnabledButtons = this._rightContainer.buttons.filter(button => button.enabled).length;
+
+ let buttonMargin = ButtonMarginForFiveButtons;
+ if (numberOfEnabledButtons === 4)
+ buttonMargin = ButtonMarginForFourButtons;
+ else if (numberOfEnabledButtons <= 3)
+ buttonMargin = ButtonMarginForThreeButtonsOrLess;
+
+ this._rightContainer.buttonMargin = buttonMargin;
+
+ this._centerContainer.layout();
+ this._rightContainer.layout();
+
+ this.timeControl.width = FullscreenTimeControlWidth;
+ }
+
+ // Private
+
+ _handleMousedown(event)
+ {
+ super.handleEvent(event);
+
+ if (event.target !== this.controlsBar.element)
+ return;
+
+ event.preventDefault();
+
+ this._lastDragPoint = this._pointForEvent(event);
+
+ this.element.addEventListener("mousemove", this, true);
+ this.element.addEventListener("mouseup", this, true);
+ }
+
+ _handleMousemove(event)
+ {
+ event.preventDefault();
+
+ const currentDragPoint = this._pointForEvent(event);
+
+ this.controlsBar.translation = new DOMPoint(
+ this.controlsBar.translation.x + currentDragPoint.x - this._lastDragPoint.x,
+ this.controlsBar.translation.y + currentDragPoint.y - this._lastDragPoint.y
+ );
+
+ this._lastDragPoint = currentDragPoint;
+ }
+
+ _handleMouseup(event)
+ {
+ event.preventDefault();
+
+ delete this._lastDragPoint;
+
+ this.element.removeEventListener("mousemove", this, true);
+ this.element.removeEventListener("mouseup", this, true);
+ }
+
+ _pointForEvent(event)
+ {
+ return new DOMPoint(event.clientX, event.clientY);
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/macos-inline-media-controls.css b/Source/WebCore/Modules/modern-media-controls/controls/macos-inline-media-controls.css
new file mode 100644
index 000000000..1c5892dc9
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/macos-inline-media-controls.css
@@ -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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+/* Controls bar */
+
+.media-controls.mac.inline > .controls-bar {
+ left: 0;
+ bottom: 0;
+ width: 100%;
+ height: 50px;
+}
+
+.media-controls.mac.inline > .controls-bar > * {
+ position: absolute;
+}
+
+/* Start button blur */
+
+.media-controls.mac.inline > button.start > div {
+ -webkit-backdrop-filter: saturate(180%) blur(20px);
+}
+
+/* Controls placement */
+
+.media-controls.mac.inline button.play-pause {
+ -webkit-mask-position-y: 12px;
+}
+
+.media-controls.mac.inline button.skip-back {
+ -webkit-mask-position-y: 10px;
+}
+
+.media-controls.mac.inline .scrubber.slider {
+ top: 13px;
+}
+
+.media-controls.mac.inline button.mute {
+ -webkit-mask-position-y: 10px;
+}
+
+.media-controls.mac.inline button.airplay {
+ -webkit-mask-position-y: 13px;
+}
+
+.media-controls.mac.inline button.pip {
+ -webkit-mask-position-y: 13px;
+}
+
+.media-controls.mac.inline button.tracks {
+ -webkit-mask-position-y: 15px;
+}
+
+.media-controls.mac.inline button.fullscreen {
+ -webkit-mask-position-y: 13px;
+}
+
+/* Labels */
+
+.media-controls.mac.inline .time-label,
+.media-controls.mac.inline .status-label {
+ top: 14.5px;
+}
+
+/* Volume slider */
+
+.media-controls.mac.inline .volume-slider-container {
+ position: absolute;
+
+ bottom: 50px;
+ width: 81px;
+ height: 31px;
+ transform: rotate(-90deg) translate(25px, -25px);
+}
+
+.media-controls.mac.inline .volume-slider-container > .background-tint {
+ top: 0;
+ left: 1px;
+ right: 0;
+ bottom: 0px;
+ width: auto;
+}
+
+.media-controls.mac.inline .volume-slider-container > .background-tint > div {
+ border-radius: 0 4px 4px 0;
+}
+
+.media-controls.mac.inline .volume.slider {
+ left: 11px;
+ top: 10px;
+}
+
+/* Tracks Panel */
+
+.media-controls.mac.inline .tracks-panel {
+ bottom: 51px;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/macos-inline-media-controls.js b/Source/WebCore/Modules/modern-media-controls/controls/macos-inline-media-controls.js
new file mode 100644
index 000000000..29e69705a
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/macos-inline-media-controls.js
@@ -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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class MacOSInlineMediaControls extends MacOSMediaControls
+{
+
+ constructor(options)
+ {
+ super(options);
+
+ this.element.classList.add("inline");
+
+ this.leftContainer = new ButtonsContainer({
+ buttons: [this.playPauseButton, this.skipBackButton],
+ cssClassName: "left"
+ });
+
+ this.rightContainer = new ButtonsContainer({
+ cssClassName: "right"
+ });
+
+ this._matchLayoutTraits();
+
+ this._backgroundTint = new BackgroundTint;
+
+ this._volumeSliderContainer = new LayoutNode(`<div class="volume-slider-container"></div>`);
+ this._volumeSliderContainer.children = [new BackgroundTint, this.volumeSlider];
+ this._volumeSliderContainer.visible = false;
+ this.volumeSlider.width = 60;
+
+ // Wire up events to display the volume slider.
+ this.muteButton.element.addEventListener("mouseenter", this);
+ this.muteButton.element.addEventListener("mouseleave", this);
+ this._volumeSliderContainer.element.addEventListener("mouseleave", this);
+
+ this.controlsBar.children = [this._backgroundTint, this.leftContainer, this.rightContainer, this._volumeSliderContainer];
+ }
+
+ // Public
+
+ get layoutTraits()
+ {
+ return this._layoutTraits;
+ }
+
+ set layoutTraits(layoutTraits)
+ {
+ if (this._layoutTraits === layoutTraits)
+ return;
+
+ this._layoutTraits = layoutTraits;
+ this._matchLayoutTraits();
+ }
+
+ layout()
+ {
+ super.layout();
+
+ if (!this.controlsBar.visible)
+ return;
+
+ // Reset dropped buttons.
+ this.rightContainer.buttons.concat(this.leftContainer.buttons).forEach(button => delete button.dropped);
+
+ this.leftContainer.layout();
+ this.rightContainer.layout();
+
+ const middleContainer = this.statusLabel.enabled ? this.statusLabel : this.timeControl;
+ this.controlsBar.children = [this._backgroundTint, this.leftContainer, middleContainer, this.rightContainer, this._volumeSliderContainer];
+
+ if (middleContainer === this.timeControl)
+ this.timeControl.width = this.width - this.leftContainer.width - this.rightContainer.width;
+
+ if (middleContainer === this.timeControl && this.timeControl.isSufficientlyWide)
+ this.timeControl.x = this.leftContainer.width;
+ else {
+ this.timeControl.remove();
+
+ let droppedControls = false;
+
+ // Since we don't have enough space to display the scrubber, we may also not have
+ // enough space to display all buttons in the left and right containers, so gradually drop them.
+ for (let button of [this.airplayButton, this.pipButton, this.tracksButton, this.muteButton, this.skipBackButton, this.fullscreenButton]) {
+ // Nothing left to do if the combined container widths is shorter than the available width.
+ if (this.leftContainer.width + this.rightContainer.width < this.width)
+ break;
+
+ droppedControls = true;
+
+ // If the button was already not participating in layout, we can skip it.
+ if (!button.visible)
+ continue;
+
+ // This button must now be dropped.
+ button.dropped = true;
+
+ this.leftContainer.layout();
+ this.rightContainer.layout();
+ }
+
+ // We didn't need to drop controls and we have status text to show.
+ if (!droppedControls && middleContainer === this.statusLabel) {
+ this.statusLabel.x = this.leftContainer.width;
+ this.statusLabel.width = this.width - this.leftContainer.width - this.rightContainer.width;
+ }
+ }
+
+ this.rightContainer.x = this.width - this.rightContainer.width;
+ this._volumeSliderContainer.x = this.rightContainer.x + this.muteButton.x;
+ }
+
+ showTracksPanel()
+ {
+ super.showTracksPanel();
+ this.tracksPanel.rightX = this.rightContainer.width - this.tracksButton.x - this.tracksButton.width;
+ }
+
+ // Protected
+
+ handleEvent(event)
+ {
+ super.handleEvent(event);
+ this._volumeSliderContainer.visible = event.type === "mouseenter" || event.relatedTarget === this._volumeSliderContainer.element;
+ }
+
+ controlsBarVisibilityDidChange(controlsBar)
+ {
+ if (controlsBar.visible)
+ this.layout();
+ }
+
+ // Private
+
+ _matchLayoutTraits()
+ {
+ if (!this.leftContainer || !this.rightContainer)
+ return;
+
+ const layoutTraits = this.layoutTraits;
+ if (layoutTraits & LayoutTraits.Compact) {
+ this.leftContainer.leftMargin = 8;
+ this.leftContainer.rightMargin = 12;
+ this.leftContainer.buttonMargin = 12;
+ this.rightContainer.leftMargin = 12;
+ this.rightContainer.rightMargin = 8;
+ this.rightContainer.buttonMargin = 12;
+ } else if (layoutTraits & LayoutTraits.TightPadding) {
+ this.leftContainer.leftMargin = 12;
+ this.leftContainer.rightMargin = 12;
+ this.leftContainer.buttonMargin = 12;
+ this.rightContainer.leftMargin = 12;
+ this.rightContainer.rightMargin = 12;
+ this.rightContainer.buttonMargin = 12;
+ } else if (layoutTraits & LayoutTraits.ReducedPadding) {
+ this.leftContainer.leftMargin = 12;
+ this.leftContainer.rightMargin = 16;
+ this.leftContainer.buttonMargin = 16;
+ this.rightContainer.leftMargin = 0;
+ this.rightContainer.rightMargin = 12;
+ this.rightContainer.buttonMargin = 16;
+ } else {
+ this.leftContainer.leftMargin = 24;
+ this.leftContainer.rightMargin = 24;
+ this.leftContainer.buttonMargin = 24;
+ this.rightContainer.leftMargin = 24;
+ this.rightContainer.rightMargin = 24;
+ this.rightContainer.buttonMargin = 24;
+ }
+
+ if (layoutTraits & LayoutTraits.Compact)
+ this.rightContainer.buttons = [this.muteButton, this.fullscreenButton];
+ else
+ this.rightContainer.buttons = [this.muteButton, this.airplayButton, this.pipButton, this.tracksButton, this.fullscreenButton];
+
+ this.leftContainer.buttons.forEach(button => button.layoutTraitsDidChange());
+ this.rightContainer.buttons.forEach(button => button.layoutTraitsDidChange());
+
+ this.element.classList.toggle("compact", layoutTraits & LayoutTraits.Compact);
+ }
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/macos-media-controls.css b/Source/WebCore/Modules/modern-media-controls/controls/macos-media-controls.css
new file mode 100644
index 000000000..bb16d9f9d
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/macos-media-controls.css
@@ -0,0 +1,51 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+/* Buttons in controls bar */
+
+.media-controls.mac button:active {
+ background-color: white;
+}
+
+.media-controls.mac > .controls-bar button {
+ height: 100% !important;
+ background-color: rgba(255, 255, 255, 0.45);
+ mix-blend-mode: plus-lighter;
+}
+
+.media-controls.mac > .controls-bar .time-label {
+ color: rgba(255, 255, 255, 0.45);
+ mix-blend-mode: plus-lighter;
+}
+
+.media-controls.mac > .controls-bar .slider > canvas {
+ mix-blend-mode: plus-lighter;
+}
+
+.media-controls.mac > .controls-bar .slider > input::-webkit-slider-thumb {
+ width: 3px !important;
+ height: 100% !important;
+ -webkit-appearance: none !important;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/macos-media-controls.js b/Source/WebCore/Modules/modern-media-controls/controls/macos-media-controls.js
new file mode 100644
index 000000000..85cb875a6
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/macos-media-controls.js
@@ -0,0 +1,81 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class MacOSMediaControls extends MediaControls
+{
+
+ constructor({ width, height, layoutTraits = LayoutTraits.macOS } = {})
+ {
+ super({ width, height, layoutTraits });
+
+ this.element.classList.add("mac");
+
+ this.muteButton = new MuteButton(this);
+ this.tracksButton = new TracksButton(this);
+ this.tracksPanel = new TracksPanel;
+ this.volumeSlider = new VolumeSlider;
+
+ this.element.addEventListener("mousedown", this);
+ this.element.addEventListener("click", this);
+ }
+
+ // Public
+
+ showTracksPanel()
+ {
+ this.tracksButton.on = true;
+ this.tracksButton.element.blur();
+ this.controlsBar.userInteractionEnabled = false;
+ this.tracksPanel.presentInParent(this);
+ }
+
+ hideTracksPanel()
+ {
+ this.tracksButton.on = false;
+ this.tracksButton.element.focus();
+ this.controlsBar.userInteractionEnabled = true;
+ this.tracksPanel.hide();
+ }
+
+ // Protected
+
+ handleEvent(event)
+ {
+ if (event.currentTarget !== this.element)
+ return;
+
+ // Only notify that the background was clicked when the "mousedown" event
+ // was also received, which wouldn't happen if the "mousedown" event caused
+ // the tracks panel to be hidden.
+ if (event.type === "mousedown")
+ this._receivedMousedown = true;
+ else if (event.type === "click") {
+ if (this._receivedMousedown && event.target === this.element && this.delegate && typeof this.delegate.macOSControlsBackgroundWasClicked === "function")
+ this.delegate.macOSControlsBackgroundWasClicked();
+ delete this._receivedMousedown
+ }
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/media-controls.css b/Source/WebCore/Modules/modern-media-controls/controls/media-controls.css
new file mode 100644
index 000000000..d5d02595c
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/media-controls.css
@@ -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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+:host(audio) {
+ width: 200px;
+ height: 50px;
+}
+
+/* We need to use relative positioning due to webkit.org/b/163603 */
+.media-controls-container {
+ position: relative;
+}
+
+.media-controls-container,
+.media-controls-container > * {
+ left: 0;
+ top: 0;
+ width: 100%;
+}
+
+.media-controls-container,
+.media-controls-container * {
+ -webkit-text-zoom: reset;
+ -webkit-text-size-adjust: auto;
+}
+
+.media-controls-container > * {
+ position: absolute;
+}
+
+.media-controls {
+ height: 100%;
+ font-family: -apple-system;
+ -webkit-user-select: none;
+ white-space: nowrap;
+ cursor: default;
+}
+
+:host(:-webkit-animating-full-screen-transition) .media-controls {
+ display: none;
+}
+
+.media-controls > .controls-bar {
+ position: absolute;
+}
+
+.media-controls.fade-in {
+ animation-name: fade-in;
+ animation-duration: 350ms;
+}
+
+@keyframes fade-in {
+ from { opacity: 0 }
+ to { opacity: 1 }
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/media-controls.js b/Source/WebCore/Modules/modern-media-controls/controls/media-controls.js
new file mode 100644
index 000000000..30687c5ea
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/media-controls.js
@@ -0,0 +1,148 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class MediaControls extends LayoutNode
+{
+
+ constructor({ width = 300, height = 150, layoutTraits = LayoutTraits.Unknown } = {})
+ {
+ super(`<div class="media-controls"></div>`);
+
+ this._scaleFactor = 1;
+
+ this.width = width;
+ this.height = height;
+ this.layoutTraits = layoutTraits;
+
+ this.startButton = new StartButton(this);
+
+ this.playPauseButton = new PlayPauseButton(this);
+ this.skipBackButton = new SkipBackButton(this);
+ this.airplayButton = new AirplayButton(this);
+ this.pipButton = new PiPButton(this);
+ this.fullscreenButton = new FullscreenButton(this);
+
+ this.statusLabel = new StatusLabel(this);
+ this.timeControl = new TimeControl(this);
+
+ this.controlsBar = new ControlsBar(this);
+
+ this.airplayPlacard = new AirplayPlacard(this);
+ this.invalidPlacard = new InvalidPlacard(this);
+ this.pipPlacard = new PiPPlacard(this);
+
+ this.showsStartButton = false;
+ }
+
+ // Public
+
+ get showsStartButton()
+ {
+ return !!this._showsStartButton;
+ }
+
+ set showsStartButton(flag)
+ {
+ if (this._showsStartButton === flag)
+ return;
+
+ this._showsStartButton = flag;
+ this._invalidateChildren();
+ }
+
+ get usesLTRUserInterfaceLayoutDirection()
+ {
+ return this.element.classList.contains("uses-ltr-user-interface-layout-direction");
+ }
+
+ set usesLTRUserInterfaceLayoutDirection(flag)
+ {
+ this.element.classList.toggle("uses-ltr-user-interface-layout-direction", flag);
+ }
+
+ get scaleFactor()
+ {
+ return this._scaleFactor;
+ }
+
+ set scaleFactor(scaleFactor)
+ {
+ if (this._scaleFactor === scaleFactor)
+ return;
+
+ this._scaleFactor = scaleFactor;
+ this.markDirtyProperty("scaleFactor");
+ }
+
+ get showsPlacard()
+ {
+ return this.children[0] instanceof Placard;
+ }
+
+ showPlacard(placard)
+ {
+ const children = [placard];
+ if (placard === this.airplayPlacard)
+ children.push(this.controlsBar);
+
+ this.children = children;
+ }
+
+ hidePlacard()
+ {
+ if (this.showsPlacard)
+ this.children[0].remove();
+ this._invalidateChildren();
+ }
+
+ fadeIn()
+ {
+ this.element.classList.add("fade-in");
+ }
+
+ // Protected
+
+ commitProperty(propertyName)
+ {
+ if (propertyName === "scaleFactor")
+ this.element.style.zoom = 1 / this._scaleFactor;
+ else
+ super.commitProperty(propertyName);
+ }
+
+ controlsBarVisibilityDidChange(controlsBar)
+ {
+ // Implemented by subclasses as needed.
+ }
+
+ // Private
+
+ _invalidateChildren()
+ {
+ if (!this.showsPlacard)
+ this.children = [this._showsStartButton ? this.startButton : this.controlsBar];
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/mute-button.js b/Source/WebCore/Modules/modern-media-controls/controls/mute-button.js
new file mode 100644
index 000000000..a879a4ff1
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/mute-button.js
@@ -0,0 +1,53 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class MuteButton extends IconButton
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ cssClassName: "mute",
+ iconName: Icons.Volume,
+ layoutDelegate
+ });
+ }
+
+ // Public
+
+ get muted()
+ {
+ return this.iconName === Icons.VolumeMuted;
+ }
+
+ set muted(flag)
+ {
+ if (this.muted === flag)
+ return;
+
+ this.iconName = flag ? Icons.VolumeMuted : Icons.Volume;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/pip-button.js b/Source/WebCore/Modules/modern-media-controls/controls/pip-button.js
new file mode 100644
index 000000000..fe0780921
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/pip-button.js
@@ -0,0 +1,38 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class PiPButton extends IconButton
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ cssClassName: "pip",
+ iconName: Icons.EnterPiP,
+ layoutDelegate
+ });
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/pip-placard.js b/Source/WebCore/Modules/modern-media-controls/controls/pip-placard.js
new file mode 100644
index 000000000..1ee8bb82a
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/pip-placard.js
@@ -0,0 +1,38 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class PiPPlacard extends Placard
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ iconName: Icons.PiPPlacard,
+ description: UIString("This video is playing in Picture in Picture"),
+ layoutDelegate
+ });
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/placard.css b/Source/WebCore/Modules/modern-media-controls/controls/placard.css
new file mode 100644
index 000000000..a8415a69c
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/placard.css
@@ -0,0 +1,70 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+.placard {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+
+ background-color: black;
+ color: rgb(164, 164, 164);
+}
+
+.placard .container {
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ max-width: 402px;
+
+ transform: translate(-50%, -50%);
+}
+
+.placard .icon {
+ position: relative;
+ left: 50%;
+ -webkit-mask-repeat: no-repeat;
+ background-color: rgb(164, 164, 164) !important;
+
+ margin-bottom: 10px;
+
+ transform: translateX(-50%);
+
+ pointer-events: none;
+}
+
+.placard .title,
+.placard .description {
+ text-align: center;
+}
+
+.placard .title {
+ font-size: 20px;
+}
+
+.placard .description {
+ font-size: 13px;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/placard.js b/Source/WebCore/Modules/modern-media-controls/controls/placard.js
new file mode 100644
index 000000000..73ad5e10f
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/placard.js
@@ -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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class Placard extends LayoutItem
+{
+
+ constructor({ iconName = null, title = "", description = "", layoutDelegate = null } = {})
+ {
+ super({
+ element: `<div class="placard"></div>`,
+ layoutDelegate
+ });
+
+ const container = this.addChild(new LayoutNode(`<div class="container"></div>`));
+
+ if (iconName)
+ container.addChild(new IconButton(this)).iconName = iconName;
+
+ if (!!title)
+ container.addChild(new LayoutNode(`<div class="title">${title}</div>`));
+
+ if (!!description)
+ container.addChild(new LayoutNode(`<div class="description">${description}</div>`));
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/play-pause-button.js b/Source/WebCore/Modules/modern-media-controls/controls/play-pause-button.js
new file mode 100644
index 000000000..4b9314939
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/play-pause-button.js
@@ -0,0 +1,53 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class PlayPauseButton extends IconButton
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ cssClassName: "play-pause",
+ iconName: Icons.Play,
+ layoutDelegate
+ });
+ }
+
+ // Public
+
+ get playing()
+ {
+ return this.iconName === Icons.Pause;
+ }
+
+ set playing(flag)
+ {
+ if (this.playing === flag)
+ return;
+
+ this.iconName = flag ? Icons.Pause : Icons.Play;
+ }
+
+}
diff --git a/Source/WebCore/Modules/mediastream/HTMLMediaElementMediaStream.idl b/Source/WebCore/Modules/modern-media-controls/controls/rewind-button.js
index a3ba17d67..9f2bab5f3 100644
--- a/Source/WebCore/Modules/mediastream/HTMLMediaElementMediaStream.idl
+++ b/Source/WebCore/Modules/modern-media-controls/controls/rewind-button.js
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
+ * 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
@@ -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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT HOLDERS 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
@@ -23,11 +23,16 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-[
- Conditional=VIDEO&MEDIA_STREAM,
-] partial interface HTMLMediaElement
+class RewindButton extends SeekButton
{
-#if !defined(LANGUAGE_GOBJECT) || !LANGUAGE_GOBJECT
- attribute MediaStream? srcObject;
-#endif
-};
+
+ constructor(layoutDelegate)
+ {
+ super({
+ cssClassName: "rewind",
+ iconName: Icons.Rewind,
+ layoutDelegate
+ });
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/scheduler.js b/Source/WebCore/Modules/modern-media-controls/controls/scheduler.js
new file mode 100644
index 000000000..f85beb037
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/scheduler.js
@@ -0,0 +1,66 @@
+
+const scheduler = new class
+{
+
+ constructor()
+ {
+ this._frameID = -1;
+ this._layoutCallbacks = new Set;
+ }
+
+ // Public
+
+ get hasScheduledLayoutCallbacks()
+ {
+ return this._frameID !== -1 || this._layoutCallbacks.size > 0;
+ }
+
+ scheduleLayout(callback)
+ {
+ if (typeof callback !== "function")
+ return;
+
+ this._layoutCallbacks.add(callback);
+ this._requestFrameIfNeeded();
+ }
+
+ unscheduleLayout(callback)
+ {
+ if (typeof callback !== "function")
+ return;
+
+ this._layoutCallbacks.delete(callback);
+ }
+
+ // Private
+
+ _requestFrameIfNeeded()
+ {
+ if (this._frameID === -1 && this._layoutCallbacks.size > 0)
+ this._frameID = window.requestAnimationFrame(this._frameDidFire.bind(this));
+ }
+
+ _frameDidFire()
+ {
+ if (typeof scheduler.frameWillFire === "function")
+ scheduler.frameWillFire();
+
+ this._layout();
+ this._frameID = -1;
+ this._requestFrameIfNeeded();
+
+ if (typeof scheduler.frameDidFire === "function")
+ scheduler.frameDidFire();
+ }
+
+ _layout()
+ {
+ // Layouts are not re-entrant.
+ const layoutCallbacks = this._layoutCallbacks;
+ this._layoutCallbacks = new Set;
+
+ for (let callback of layoutCallbacks)
+ callback();
+ }
+
+};
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/scrubber.js b/Source/WebCore/Modules/modern-media-controls/controls/scrubber.js
new file mode 100644
index 000000000..499399636
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/scrubber.js
@@ -0,0 +1,156 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class Scrubber extends Slider
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ cssClassName: "scrubber",
+ layoutDelegate
+ });
+
+ this.height = 23;
+
+ // Add the element used to draw the track on iOS.
+ if (this.layoutTraits & LayoutTraits.iOS)
+ this.addChild(new LayoutNode(`<div></div>`), 0);
+
+ this._buffered = 0;
+ }
+
+ // Public
+
+ get buffered()
+ {
+ return this._buffered;
+ }
+
+ set buffered(buffered)
+ {
+ if (this._buffered === buffered)
+ return;
+
+ this._buffered = buffered;
+ this.needsLayout = true;
+ }
+
+ // Protected
+
+ draw(ctx)
+ {
+ const width = this.width;
+ const height = this.height;
+
+ if (!this.width || !this.height)
+ return;
+
+ const dpr = window.devicePixelRatio;
+
+ ctx.save();
+ ctx.scale(dpr, dpr);
+ ctx.clearRect(0, 0, width, height);
+
+ const layoutTraits = this.layoutTraits;
+ if (layoutTraits & LayoutTraits.macOS)
+ this._drawMacOS(ctx, width, height);
+ else if (layoutTraits & LayoutTraits.iOS)
+ this._drawiOS(ctx, width, height);
+
+ ctx.restore();
+ }
+
+ _drawMacOS(ctx, width, height)
+ {
+ const trackHeight = 3;
+ const trackY = (height - trackHeight) / 2;
+ const scrubberWidth = 4;
+ const scrubberHeight = height;
+ const borderSize = 2;
+ const scrubberPosition = Math.max(0, Math.min(width - scrubberWidth, Math.round(width * this.value)));
+
+ // Draw background.
+ ctx.save();
+ ctx.beginPath();
+ addRoundedRect(ctx, 0, trackY, width, trackHeight, trackHeight / 2);
+ ctx.closePath();
+ ctx.clip();
+ ctx.fillStyle = "rgb(20, 20, 20)";
+ ctx.fillRect(0, 0, width, height);
+ ctx.restore();
+
+ // Buffered.
+ ctx.save();
+ ctx.beginPath();
+ addRoundedRect(ctx, 0, trackY, width, trackHeight, trackHeight / 2);
+ ctx.closePath();
+ ctx.clip();
+ ctx.fillStyle = "rgb(39, 39, 39)";
+ ctx.fillRect(0, 0, width * this._buffered, height);
+ ctx.restore();
+
+ // Draw played section.
+ ctx.save();
+ ctx.beginPath();
+ addRoundedRect(ctx, 0, trackY, width, trackHeight, trackHeight / 2);
+ ctx.closePath();
+ ctx.clip();
+ ctx.fillStyle = "rgb(84, 84, 84)";
+ ctx.fillRect(0, 0, width * this.value, height);
+ ctx.restore();
+
+ // Draw the scrubber.
+ ctx.save();
+ ctx.clearRect(scrubberPosition - 1, 0, scrubberWidth + borderSize, height, 0);
+ ctx.beginPath();
+ addRoundedRect(ctx, scrubberPosition, 0, scrubberWidth, scrubberHeight, 1);
+ ctx.closePath();
+ ctx.clip();
+ ctx.fillStyle = "rgb(138, 138, 138)";
+ ctx.fillRect(0, 0, width, height);
+ ctx.restore();
+ }
+
+ _drawiOS(ctx, width, height)
+ {
+ const trackHeight = 3;
+ const trackY = (height - trackHeight) / 2;
+ const scrubberWidth = 15;
+ const leftPadding = 2;
+ const rightPadding = 2;
+ const trackWidth = this.width - leftPadding - rightPadding - scrubberWidth;
+
+ // Draw played section.
+ ctx.save();
+ ctx.beginPath();
+ addRoundedRect(ctx, leftPadding, trackY, scrubberWidth / 2 + trackWidth * this.value, trackHeight, trackHeight / 2);
+ ctx.closePath();
+ ctx.fillStyle = "white";
+ ctx.fill();
+ ctx.restore();
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/seek-button.js b/Source/WebCore/Modules/modern-media-controls/controls/seek-button.js
new file mode 100644
index 000000000..c72c1f165
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/seek-button.js
@@ -0,0 +1,73 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class SeekButton extends IconButton
+{
+
+ constructor(options)
+ {
+ super(options);
+
+ this.element.addEventListener("mousedown", this);
+ }
+
+ // Protected
+
+ handleEvent(event)
+ {
+ if (event.type === "mousedown" && event.currentTarget === this.element)
+ this._didStartPressing();
+ else if (event.type === "mouseup")
+ this._didStopPressing();
+ else
+ super.handleEvent(event);
+ }
+
+ // Private
+
+ _didStartPressing()
+ {
+ const mediaControls = this.parentOfType(MediaControls);
+ if (!mediaControls)
+ return;
+
+ this._mouseupTarget = mediaControls.element;
+ this._mouseupTarget.addEventListener("mouseup", this, true);
+ this._notifyDelegateOfPressingState(true);
+ }
+
+ _didStopPressing()
+ {
+ this._mouseupTarget.removeEventListener("mouseup", this, true);
+ this._notifyDelegateOfPressingState(false);
+ }
+
+ _notifyDelegateOfPressingState(isPressed)
+ {
+ if (this._enabled && this.uiDelegate && typeof this.uiDelegate.buttonPressedStateDidChange === "function")
+ this.uiDelegate.buttonPressedStateDidChange(this, isPressed);
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/skip-back-button.js b/Source/WebCore/Modules/modern-media-controls/controls/skip-back-button.js
new file mode 100644
index 000000000..c5c2d0c2a
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/skip-back-button.js
@@ -0,0 +1,38 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class SkipBackButton extends IconButton
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ cssClassName: "skip-back",
+ iconName: Icons.SkipBack,
+ layoutDelegate
+ });
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/slider.css b/Source/WebCore/Modules/modern-media-controls/controls/slider.css
new file mode 100644
index 000000000..cb5ab15b7
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/slider.css
@@ -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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+.slider {
+ position: absolute;
+}
+
+.slider > canvas,
+.slider > input {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+}
+
+.slider > canvas {
+ pointer-events: none;
+}
+
+.slider > input {
+ margin: 0;
+ background-color: transparent;
+ -webkit-appearance: none !important;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/slider.js b/Source/WebCore/Modules/modern-media-controls/controls/slider.js
new file mode 100644
index 000000000..c32d662b8
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/slider.js
@@ -0,0 +1,152 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class Slider extends LayoutItem
+{
+
+ constructor({ layoutDelegate = null, cssClassName = "" } = {})
+ {
+ super({
+ element: `<div class="slider ${cssClassName}"></div>`,
+ layoutDelegate
+ });
+
+ this._canvas = new LayoutNode(`<canvas></canvas>`);
+
+ this._input = new LayoutNode(`<input type="range" min="0" max="1" step="0.001" />`);
+ this._input.element.addEventListener("change", this);
+ this._input.element.addEventListener("input", this);
+
+ this.value = 0;
+
+ this.children = [this._canvas, this._input];
+ }
+
+ // Public
+
+ get value()
+ {
+ if (this._value !== undefined)
+ return this._value;
+ return parseFloat(this._input.element.value);
+ }
+
+ set value(value)
+ {
+ if (this._valueIsChanging)
+ return;
+
+ this._value = value;
+ this.markDirtyProperty("value");
+ this.needsLayout = true;
+ }
+
+ get width()
+ {
+ return super.width;
+ }
+
+ set width(width)
+ {
+ super.width = width;
+ this.needsLayout = true;
+ }
+
+ // Protected
+
+ handleEvent(event)
+ {
+ switch (event.type) {
+ case "input":
+ this._handleInputEvent();
+ break;
+ case "change":
+ this._handleChangeEvent();
+ break;
+ }
+ }
+
+ commitProperty(propertyName)
+ {
+ switch (propertyName) {
+ case "value":
+ this._input.element.value = this._value;
+ delete this._value;
+ break;
+ case "width":
+ this._canvas.element.width = this.width * window.devicePixelRatio;
+ case "height":
+ this._canvas.element.height = this.height * window.devicePixelRatio;
+ default :
+ super.commitProperty(propertyName);
+ break;
+ }
+ }
+
+ layout()
+ {
+ super.layout();
+ this.draw(this._canvas.element.getContext("2d"));
+ }
+
+ draw(ctx)
+ {
+ // Implemented by subclasses.
+ }
+
+ // Private
+
+ _handleInputEvent()
+ {
+ if (!this._valueIsChanging && this.uiDelegate && typeof this.uiDelegate.controlValueWillStartChanging === "function")
+ this.uiDelegate.controlValueWillStartChanging(this);
+ this._valueIsChanging = true;
+ if (this.uiDelegate && typeof this.uiDelegate.controlValueDidChange === "function")
+ this.uiDelegate.controlValueDidChange(this);
+
+ this.needsLayout = true;
+ }
+
+ _handleChangeEvent()
+ {
+ delete this._valueIsChanging;
+ if (this.uiDelegate && typeof this.uiDelegate.controlValueDidStopChanging === "function")
+ this.uiDelegate.controlValueDidStopChanging(this);
+
+ this.needsLayout = true;
+ }
+
+}
+
+function addRoundedRect(ctx, x, y, width, height, radius) {
+ ctx.moveTo(x + radius, y);
+ ctx.arcTo(x + width, y, x + width, y + radius, radius);
+ ctx.lineTo(x + width, y + height - radius);
+ ctx.arcTo(x + width, y + height, x + width - radius, y + height, radius);
+ ctx.lineTo(x + radius, y + height);
+ ctx.arcTo(x, y + height, x, y + height - radius, radius);
+ ctx.lineTo(x, y + radius);
+ ctx.arcTo(x, y, x + radius, y, radius);
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/start-button.css b/Source/WebCore/Modules/modern-media-controls/controls/start-button.css
new file mode 100644
index 000000000..82e172dc4
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/start-button.css
@@ -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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+button.start {
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ width: 70px;
+ height: 70px;
+
+ background-color: transparent !important;
+
+ transform: translate3d(-50%, -50%, 0);
+}
+
+button.start > * {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+}
+
+button.start > div {
+ -webkit-appearance: media-controls-light-bar-background;
+ -webkit-clip-path: circle(50%);
+ will-change: transform;
+}
+
+button.start > img {
+ opacity: 0.6;
+}
+
+button.start:active > img {
+ opacity: 1;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/start-button.js b/Source/WebCore/Modules/modern-media-controls/controls/start-button.js
new file mode 100644
index 000000000..9860c7e79
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/start-button.js
@@ -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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class StartButton extends Button
+{
+
+ constructor(layoutDelegate)
+ {
+ super(layoutDelegate);
+
+ this.element.classList.add("start");
+
+ const background = this.element.appendChild(document.createElement("div"));
+ background.className = "background";
+
+ const image = this.element.appendChild(new Image);
+ image.src = iconService.imageForIconNameAndLayoutTraits(Icons.Start, this.layoutTraits).src;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/status-label.css b/Source/WebCore/Modules/modern-media-controls/controls/status-label.css
new file mode 100644
index 000000000..a05b80bc7
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/status-label.css
@@ -0,0 +1,37 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+.status-label {
+ position: absolute;
+
+ text-align: left;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+
+ font-size: 14px;
+
+ color: rgba(255, 255, 255, 0.572);
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/status-label.js b/Source/WebCore/Modules/modern-media-controls/controls/status-label.js
new file mode 100644
index 000000000..b39cc79dc
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/status-label.js
@@ -0,0 +1,85 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class StatusLabel extends LayoutItem
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ element: `<div class="status-label"></div>`,
+ layoutDelegate
+ });
+
+ this._text = "";
+ this._enabled = false;
+ }
+
+ // Public
+
+ get text()
+ {
+ return this._text;
+ }
+
+ set text(text)
+ {
+ if (text === this._text)
+ return;
+
+ this._text = text;
+ this.markDirtyProperty("text");
+
+ if (this.layoutDelegate)
+ this.layoutDelegate.needsLayout = true;
+ }
+
+ get enabled()
+ {
+ return this._enabled;
+ }
+
+ set enabled(enabled)
+ {
+ if (enabled === this._enabled)
+ return;
+
+ this._enabled = enabled;
+
+ if (this.layoutDelegate)
+ this.layoutDelegate.needsLayout = true;
+ }
+
+ // Protected
+
+ commitProperty(propertyName)
+ {
+ if (propertyName === "text")
+ this.element.textContent = this._text;
+ else
+ super.commitProperty(propertyName);
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/text-tracks.css b/Source/WebCore/Modules/modern-media-controls/controls/text-tracks.css
new file mode 100644
index 000000000..ab65e22c2
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/text-tracks.css
@@ -0,0 +1,82 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+video::-webkit-media-text-track-container {
+ bottom: 50px;
+ 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 auto;
+
+ -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 {
+ display: none;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/time-control.js b/Source/WebCore/Modules/modern-media-controls/controls/time-control.js
new file mode 100644
index 000000000..8a78ccd16
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/time-control.js
@@ -0,0 +1,93 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+const MinimumScrubberWidth = 168;
+const ElapsedTimeLabelLeftMargin = -2;
+const ElapsedTimeLabelWidth = 40;
+const RemainingTimeLabelWidth = 49;
+const AdditionalTimeLabelWidthOverAnHour = 22;
+const ScrubberMargin = 5;
+
+class TimeControl extends LayoutItem
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ element: `<div class="time-control"></div>`,
+ layoutDelegate
+ });
+
+ this.elapsedTimeLabel = new TimeLabel;
+ this.scrubber = new Scrubber(layoutDelegate);
+ this.remainingTimeLabel = new TimeLabel;
+
+ this.children = [this.elapsedTimeLabel, this.scrubber, this.remainingTimeLabel];
+
+ this._labelsMayDisplayTimesOverAnHour = false;
+ }
+
+ // Public
+
+ get labelsMayDisplayTimesOverAnHour()
+ {
+ return this._labelsMayDisplayTimesOverAnHour;
+ }
+
+ set labelsMayDisplayTimesOverAnHour(flag)
+ {
+ if (this._labelsMayDisplayTimesOverAnHour === flag)
+ return;
+
+ this._labelsMayDisplayTimesOverAnHour = flag;
+ this.layout();
+ }
+
+ get width()
+ {
+ return super.width;
+ }
+
+ set width(width)
+ {
+ super.width = width;
+
+ const extraWidth = this._labelsMayDisplayTimesOverAnHour ? AdditionalTimeLabelWidthOverAnHour : 0;
+ const elapsedTimeLabelWidth = ElapsedTimeLabelWidth + extraWidth;
+ const remainingTimeLabelWidth = RemainingTimeLabelWidth + extraWidth;
+
+ this.elapsedTimeLabel.x = ElapsedTimeLabelLeftMargin;
+ this.elapsedTimeLabel.width = elapsedTimeLabelWidth;
+ this.scrubber.x = this.elapsedTimeLabel.x + elapsedTimeLabelWidth + ScrubberMargin;
+ this.scrubber.width = this._width - elapsedTimeLabelWidth - ScrubberMargin - remainingTimeLabelWidth;
+ this.remainingTimeLabel.x = this.scrubber.x + this.scrubber.width + ScrubberMargin;
+ }
+
+ get isSufficientlyWide()
+ {
+ return this.scrubber.width >= MinimumScrubberWidth;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/time-label.css b/Source/WebCore/Modules/modern-media-controls/controls/time-label.css
new file mode 100644
index 000000000..76d2e91ee
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/time-label.css
@@ -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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+.time-label {
+ position: absolute;
+
+ font-family: -apple-system-monospaced-numbers;
+ font-size: 14px;
+
+ text-align: right;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/time-label.js b/Source/WebCore/Modules/modern-media-controls/controls/time-label.js
new file mode 100644
index 000000000..cd0e753ab
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/time-label.js
@@ -0,0 +1,80 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class TimeLabel extends LayoutNode
+{
+
+ constructor()
+ {
+ super(`<div class="time-label"></div>`);
+
+ this.value = 0;
+ }
+
+ // Public
+
+ get value()
+ {
+ return this._value;
+ }
+
+ set value(value)
+ {
+ if (value === this._value)
+ return;
+
+ this._value = value;
+ this.markDirtyProperty("value");
+ }
+
+ // Protected
+
+ commitProperty(propertyName)
+ {
+ if (propertyName === "value")
+ this.element.textContent = this._formattedTime();
+ else
+ super.commitProperty(propertyName);
+ }
+
+ // Private
+
+ _formattedTime()
+ {
+ const time = this._value || 0;
+ const absTime = Math.abs(time);
+ const intSeconds = Math.floor(absTime % 60).toFixed(0);
+ const intMinutes = Math.floor((absTime / 60) % 60).toFixed(0);
+ const intHours = Math.floor(absTime / (60 * 60)).toFixed(0);
+
+ const timeStrings = [intMinutes, intSeconds];
+ if (intHours > 0)
+ timeStrings.unshift(intHours);
+
+ const sign = time < 0 ? "-" : "";
+ return sign + timeStrings.map(x => `00${x}`.slice(-2)).join(":");
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/tracks-button.js b/Source/WebCore/Modules/modern-media-controls/controls/tracks-button.js
new file mode 100644
index 000000000..5505f2c0b
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/tracks-button.js
@@ -0,0 +1,38 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class TracksButton extends IconButton
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ cssClassName: "tracks",
+ iconName: Icons.Tracks,
+ layoutDelegate
+ });
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.css b/Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.css
new file mode 100644
index 000000000..b0717483f
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.css
@@ -0,0 +1,105 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+.tracks-panel {
+ position: absolute;
+ display: inline-block;
+}
+
+.tracks-panel > .background-tint > div {
+ border-radius: 7px;
+}
+
+.tracks-panel * {
+ font-size: 14px;
+ font-weight: 500;
+}
+
+.tracks-panel.fade-out {
+ transition-property: opacity;
+ transition-duration: 265ms;
+ opacity: 0;
+}
+
+.tracks-panel > section {
+ border-top: 2px solid rgb(104, 104, 104);
+ will-change: transform;
+}
+
+.tracks-panel > section:first-of-type {
+ border-top: 0;
+}
+
+.tracks-panel > section > h3 {
+ color: rgb(150, 150, 150);
+ padding: 5px 20px 1px 21px;
+ margin: 0;
+}
+
+.tracks-panel > section > ul {
+ list-style-type: none;
+ margin-top: 0;
+ padding: 0;
+}
+
+.tracks-panel > section > ul > li {
+ position: relative;
+ padding: 1px 20px 1px 33px;
+ color: white;
+}
+
+.tracks-panel > section > ul > li:focus {
+ background-color: rgba(26, 68, 243, 0.6);
+ -webkit-backdrop-filter: saturate(180%) blur(20px);
+ outline: none;
+}
+
+.tracks-panel > section > ul > li.selected:before {
+ position: absolute;
+ top: 3px;
+ left: 12px;
+ width: 12px;
+ display: inline-block;
+ content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 300"><polygon fill="white" points="252.301,4.477 126.667,194.104 43.358,108.3 6.868,161.408 132.515,290.814 297.732,49.926"/></svg>');
+}
+
+.tracks-panel > section > ul > li.animated {
+ animation-name: tracks-panel-item-selection;
+ animation-duration: 150ms;
+ animation-timing-function: step-end;
+ animation-fill-mode: forwards;
+}
+
+@keyframes tracks-panel-item-selection {
+ 0%, 55.55% {
+ background-color: rgba(26, 68, 243, 0.6);
+ -webkit-backdrop-filter: saturate(180%) blur(20px);
+ }
+
+ 22.22% {
+ background: none;
+ -webkit-backdrop-filter: none;
+ }
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.js b/Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.js
new file mode 100644
index 000000000..4a69bb6e2
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.js
@@ -0,0 +1,305 @@
+
+class TracksPanel extends LayoutNode
+{
+
+ constructor()
+ {
+ super(`<div class="tracks-panel"></div>`);
+ this._backgroundTint = new BackgroundTint;
+ this._rightX = 0;
+ this._bottomY = 0;
+ }
+
+ // Public
+
+ get presented()
+ {
+ return !!this.parent;
+ }
+
+ presentInParent(node)
+ {
+ if (this.parent === node)
+ return;
+
+ this.children = this._childrenFromDataSource();
+
+ node.addChild(this);
+
+ this.element.removeEventListener("transitionend", this);
+ this.element.classList.remove("fade-out");
+
+ this._mousedownTarget().addEventListener("mousedown", this, true);
+ window.addEventListener("keydown", this, true);
+
+ this._focusedTrackNode = null;
+ }
+
+ hide()
+ {
+ if (!this.presented)
+ return;
+
+ this._mousedownTarget().removeEventListener("mousedown", this, true);
+ window.removeEventListener("keydown", this, true);
+
+ this.element.addEventListener("transitionend", this);
+
+ // Ensure a transition will indeed happen by starting it only on the next frame.
+ window.requestAnimationFrame(() => { this.element.classList.add("fade-out"); });
+ }
+
+ get bottomY()
+ {
+ return this._bottomY;
+ }
+
+ set bottomY(bottomY)
+ {
+ if (this._bottomY === bottomY)
+ return;
+
+ this._bottomY = bottomY;
+ this.markDirtyProperty("bottomY");
+ }
+
+ get rightX()
+ {
+ return this._rightX;
+ }
+
+ set rightX(x)
+ {
+ if (this._rightX === x)
+ return;
+
+ this._rightX = x;
+ this.markDirtyProperty("rightX");
+ }
+
+ // Protected
+
+ trackNodeSelectionAnimationDidEnd(trackNode)
+ {
+ if (this.uiDelegate && typeof this.uiDelegate.tracksPanelSelectionDidChange === "function")
+ this.uiDelegate.tracksPanelSelectionDidChange(trackNode.index, trackNode.sectionIndex);
+ }
+
+ mouseMovedOverTrackNode(trackNode)
+ {
+ this._focusTrackNode(trackNode);
+ }
+
+ mouseExitedTrackNode(trackNode)
+ {
+ this._focusedTrackNode.element.blur();
+ delete this._focusedTrackNode;
+ }
+
+ commitProperty(propertyName)
+ {
+ if (propertyName === "rightX")
+ this.element.style.right = `${this._rightX}px`;
+ else if (propertyName === "bottomY")
+ this.element.style.bottom = `${this._bottomY}px`;
+ else
+ super.commitProperty(propertyName);
+ }
+
+ handleEvent(event)
+ {
+ switch (event.type) {
+ case "mousedown":
+ this._handleMousedown(event);
+ break;
+ case "keydown":
+ this._handleKeydown(event);
+ break;
+ case "transitionend":
+ this.remove();
+ break;
+ }
+ }
+
+ // Private
+
+ _mousedownTarget()
+ {
+ const mediaControls = this.parentOfType(MacOSFullscreenMediaControls);
+ if (mediaControls)
+ return mediaControls.element;
+ return window;
+ }
+
+ _childrenFromDataSource()
+ {
+ const children = [this._backgroundTint];
+
+ this._trackNodes = [];
+
+ const dataSource = this.dataSource;
+ if (!dataSource)
+ return children;
+
+ const numberOfSections = dataSource.tracksPanelNumberOfSections();
+ if (numberOfSections === 0)
+ return children;
+
+ for (let sectionIndex = 0; sectionIndex < numberOfSections; ++sectionIndex) {
+ let sectionNode = new LayoutNode(`<section></section>`);
+ sectionNode.addChild(new LayoutNode(`<h3>${dataSource.tracksPanelTitleForSection(sectionIndex)}</h3>`));
+
+ let tracksListNode = sectionNode.addChild(new LayoutNode(`<ul></ul>`));
+ let numberOfTracks = dataSource.tracksPanelNumberOfTracksInSection(sectionIndex);
+ for (let trackIndex = 0; trackIndex < numberOfTracks; ++trackIndex) {
+ let trackTitle = dataSource.tracksPanelTitleForTrackInSection(trackIndex, sectionIndex);
+ let trackSelected = dataSource.tracksPanelIsTrackInSectionSelected(trackIndex, sectionIndex);
+ let trackNode = tracksListNode.addChild(new TrackNode(trackIndex, sectionIndex, trackTitle, trackSelected, this));
+ this._trackNodes.push(trackNode);
+ }
+ children.push(sectionNode);
+ }
+
+ return children;
+ }
+
+ _handleMousedown(event)
+ {
+ if (this.element.contains(event.target))
+ return;
+
+ this._dismiss();
+
+ event.preventDefault();
+ event.stopPropagation();
+ }
+
+ _handleKeydown(event)
+ {
+ switch (event.key) {
+ case "Home":
+ case "PageUp":
+ this._focusFirstTrackNode();
+ break;
+ case "End":
+ case "PageDown":
+ this._focusLastTrackNode();
+ break;
+ case "ArrowDown":
+ if (event.altKey || event.metaKey)
+ this._focusLastTrackNode();
+ else
+ this._focusNextTrackNode();
+ break;
+ case "ArrowUp":
+ if (event.altKey || event.metaKey)
+ this._focusFirstTrackNode();
+ else
+ this._focusPreviousTrackNode();
+ break;
+ case " ":
+ case "Enter":
+ if (this._focusedTrackNode)
+ this._focusedTrackNode.activate();
+ break;
+ case "Escape":
+ this._dismiss();
+ break;
+ }
+ }
+
+ _dismiss()
+ {
+ if (this.parent && typeof this.parent.hideTracksPanel === "function")
+ this.parent.hideTracksPanel();
+ }
+
+ _focusTrackNode(trackNode)
+ {
+ if (!trackNode || trackNode === this._focusedTrackNode)
+ return;
+
+ trackNode.element.focus();
+ this._focusedTrackNode = trackNode;
+ }
+
+ _focusPreviousTrackNode()
+ {
+ const previousIndex = this._focusedTrackNode ? this._trackNodes.indexOf(this._focusedTrackNode) - 1 : this._trackNodes.length - 1;
+ this._focusTrackNode(this._trackNodes[previousIndex]);
+ }
+
+ _focusNextTrackNode()
+ {
+ this._focusTrackNode(this._trackNodes[this._trackNodes.indexOf(this._focusedTrackNode) + 1]);
+ }
+
+ _focusFirstTrackNode()
+ {
+ this._focusTrackNode(this._trackNodes[0]);
+ }
+
+ _focusLastTrackNode()
+ {
+ this._focusTrackNode(this._trackNodes[this._trackNodes.length - 1]);
+ }
+
+}
+
+class TrackNode extends LayoutNode
+{
+
+ constructor(index, sectionIndex, title, selected, panel)
+ {
+ super(`<li tabindex="0">${title}</li>`);
+
+ this.index = index;
+ this.sectionIndex = sectionIndex;
+ this._panel = panel;
+ this._selected = selected;
+
+ if (selected)
+ this.element.classList.add("selected");
+
+ this.element.addEventListener("mousemove", this);
+ this.element.addEventListener("mouseleave", this);
+ this.element.addEventListener("click", this);
+ }
+
+ // Public
+
+ activate()
+ {
+ this.element.addEventListener("animationend", this);
+ this.element.classList.add("animated");
+ }
+
+ // Protected
+
+ handleEvent(event)
+ {
+ switch (event.type) {
+ case "mousemove":
+ this._panel.mouseMovedOverTrackNode(this);
+ break;
+ case "mouseleave":
+ this._panel.mouseExitedTrackNode(this);
+ break;
+ case "click":
+ this.activate();
+ break;
+ case "animationend":
+ this._animationDidEnd();
+ break;
+ }
+ }
+
+ // Private
+
+ _animationDidEnd()
+ {
+ this.element.removeEventListener("animationend", this);
+ this._panel.trackNodeSelectionAnimationDidEnd(this);
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/volume-down-button.js b/Source/WebCore/Modules/modern-media-controls/controls/volume-down-button.js
new file mode 100644
index 000000000..b5c9d1daf
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/volume-down-button.js
@@ -0,0 +1,38 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class VolumeDownButton extends IconButton
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ cssClassName: "volume-down",
+ iconName: Icons.VolumeDown,
+ layoutDelegate
+ });
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/volume-slider.css b/Source/WebCore/Modules/modern-media-controls/controls/volume-slider.css
new file mode 100644
index 000000000..42b904bd6
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/volume-slider.css
@@ -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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+.volume.slider > input::-webkit-slider-thumb {
+ width: 11px;
+ height: 11px;
+ border-radius: 5.5px;
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/volume-slider.js b/Source/WebCore/Modules/modern-media-controls/controls/volume-slider.js
new file mode 100644
index 000000000..993eb16f1
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/volume-slider.js
@@ -0,0 +1,120 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class VolumeSlider extends Slider
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ cssClassName: "volume",
+ layoutDelegate
+ });
+
+ this.height = 11;
+ this.enabled = true;
+
+ this._active = false;
+ this.element.addEventListener("mousedown", this);
+ }
+
+ // Protected
+
+ handleEvent(event)
+ {
+ super.handleEvent(event);
+
+ if (event instanceof MouseEvent) {
+ this._active = event.type === "mousedown";
+ if (event.type === "mousedown")
+ window.addEventListener("mouseup", this, true);
+ else
+ window.removeEventListener("mouseup", this, true);
+ }
+ }
+
+ draw(ctx)
+ {
+ const width = this.width;
+ const height = this.height;
+
+ if (!this.width || !this.height)
+ return;
+
+ const dpr = window.devicePixelRatio;
+
+ ctx.save();
+ ctx.scale(dpr, dpr);
+ ctx.clearRect(0, 0, width, height);
+
+ const trackHeight = 3;
+ const knobRadius = 5.5;
+ const knobDiameter = 2 * knobRadius;
+ const borderSize = 2;
+ const knobX = Math.round(this.value * (width - knobDiameter - borderSize));
+
+ // Draw portion of volume under slider thumb.
+ ctx.save();
+ ctx.beginPath();
+ addRoundedRect(ctx, 0, 4, knobX + 2, trackHeight, trackHeight / 2);
+ ctx.closePath();
+ ctx.clip();
+ ctx.fillStyle = "rgb(84, 84, 84)";
+ ctx.fillRect(0, 0, width, height);
+ ctx.restore();
+
+ // Draw portion of volume above slider thumb.
+ ctx.save();
+ ctx.beginPath();
+ addRoundedRect(ctx, knobX, 4, width - knobX, trackHeight, trackHeight / 2);
+ ctx.closePath();
+ ctx.clip();
+ ctx.fillStyle = "rgb(39, 39, 39)";
+ ctx.fillRect(0, 0, width, height);
+ ctx.restore();
+
+ // Clear a hole in the slider for the scrubber.
+ ctx.save();
+ ctx.beginPath();
+ addRoundedRect(ctx, knobX, 0, knobDiameter + borderSize, height, (knobDiameter + borderSize) / 2);
+ ctx.closePath();
+ ctx.clip();
+ ctx.clearRect(0, 0, width, height);
+ ctx.restore();
+
+ // Draw scrubber.
+ ctx.save();
+ ctx.beginPath();
+ addRoundedRect(ctx, knobX + 1, 0, knobDiameter, knobDiameter, knobRadius);
+ ctx.closePath();
+ ctx.clip();
+ ctx.fillStyle = this._active ? "white" : "rgb(138, 138, 138)";
+ ctx.fillRect(0, 0, width, height);
+ ctx.restore();
+
+ ctx.restore();
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/volume-up-button.js b/Source/WebCore/Modules/modern-media-controls/controls/volume-up-button.js
new file mode 100644
index 000000000..8d9432436
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/volume-up-button.js
@@ -0,0 +1,38 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class VolumeUpButton extends IconButton
+{
+
+ constructor(layoutDelegate)
+ {
+ super({
+ cssClassName: "volume-up",
+ iconName: Icons.VolumeUp,
+ layoutDelegate
+ });
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/gesture-recognizers/gesture-recognizer.js b/Source/WebCore/Modules/modern-media-controls/gesture-recognizers/gesture-recognizer.js
new file mode 100644
index 000000000..c1ec8572a
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/gesture-recognizers/gesture-recognizer.js
@@ -0,0 +1,379 @@
+
+class GestureRecognizer
+{
+
+ constructor(target = null, delegate = null)
+ {
+ this._targetTouches = [];
+
+ this.modifierKeys = {
+ alt : false,
+ ctrl : false,
+ meta : false,
+ shift : false
+ };
+
+ this._state = GestureRecognizer.States.Possible;
+ this._enabled = true;
+
+ this.target = target;
+ this.delegate = delegate;
+ }
+
+ // Public
+
+ get state()
+ {
+ return this._state;
+ }
+
+ set state(state)
+ {
+ if (this._state === state && state !== GestureRecognizer.States.Changed)
+ return;
+
+ this._state = state;
+ if (this.delegate && typeof this.delegate.gestureRecognizerStateDidChange === "function")
+ this.delegate.gestureRecognizerStateDidChange(this);
+ }
+
+ get target()
+ {
+ return this._target;
+ }
+
+ set target(target)
+ {
+ if (!target || this._target === target)
+ return;
+
+ this._target = target;
+ this._initRecognizer();
+ }
+
+ get numberOfTouches()
+ {
+ return this._targetTouches.length;
+ }
+
+ get enabled()
+ {
+ return this._enabled;
+ }
+
+ set enabled(enabled)
+ {
+ if (this._enabled === enabled)
+ return;
+
+ this._enabled = enabled;
+
+ if (!enabled) {
+ if (this.numberOfTouches === 0) {
+ this._removeTrackingListeners();
+ this.reset();
+ } else
+ this.enterCancelledState();
+ }
+
+ this._updateBaseListeners();
+ }
+
+ reset()
+ {
+ // Implemented by subclasses.
+ }
+
+ locationInElement(element)
+ {
+ const p = new DOMPoint;
+ const touches = this._targetTouches;
+ const count = touches.length;
+ for (let i = 0; i < count; ++i) {
+ const touch = touches[i];
+ p.x += touch.pageX;
+ p.y += touch.pageY;
+ }
+ p.x /= count;
+ p.y /= count;
+
+ if (!element)
+ return p;
+
+ // FIXME: are WebKitPoint and DOMPoint interchangeable?
+ const wkPoint = window.webkitConvertPointFromPageToNode(element, new WebKitPoint(p.x, p.y));
+ return new DOMPoint(wkPoint.x, wkPoint.y);
+ }
+
+ locationInClient()
+ {
+ const p = new DOMPoint;
+ const touches = this._targetTouches;
+ const count = touches.length;
+ for (let i = 0; i < count; ++i) {
+ const touch = touches[i];
+ p.x += touch.clientX;
+ p.y += touch.clientY;
+ }
+ p.x /= count;
+ p.y /= count;
+
+ return p;
+ }
+
+ locationOfTouchInElement(touchIndex, element)
+ {
+ const touch = this._targetTouches[touchIndex];
+ if (!touch)
+ return new DOMPoint;
+
+ const touchLocation = new DOMPoint(touch.pageX, touch.pageY);
+ if (!element)
+ return touchLocation;
+
+ // FIXME: are WebKitPoint and DOMPoint interchangeable?
+ const wkPoint = window.webkitConvertPointFromPageToNode(element, new WebKitPoint(touchLocation.x, touchLocation.y));
+ return new DOMPoint(wkPoint.x, wkPoint.y);
+ }
+
+ touchesBegan(event)
+ {
+ if (event.currentTarget !== this._target)
+ return;
+
+ window.addEventListener(GestureRecognizer.Events.TouchMove, this, true);
+ window.addEventListener(GestureRecognizer.Events.TouchEnd, this, true);
+ window.addEventListener(GestureRecognizer.Events.TouchCancel, this, true);
+ this.enterPossibleState();
+ }
+
+ touchesMoved(event)
+ {
+ // Implemented by subclasses.
+ }
+
+ touchesEnded(event)
+ {
+ // Implemented by subclasses.
+ }
+
+ touchesCancelled(event)
+ {
+ // Implemented by subclasses.
+ }
+
+ gestureBegan(event)
+ {
+ if (event.currentTarget !== this._target)
+ return;
+
+ window.addEventListener(GestureRecognizer.Events.GestureChange, this, true);
+ window.addEventListener(GestureRecognizer.Events.GestureEnd, this, true);
+ this.enterPossibleState();
+ }
+
+ gestureChanged(event)
+ {
+ // Implemented by subclasses.
+ }
+
+ gestureEnded(event)
+ {
+ // Implemented by subclasses.
+ }
+
+ enterPossibleState()
+ {
+ this.state = GestureRecognizer.States.Possible;
+ }
+
+ enterBeganState()
+ {
+ if (this.delegate && typeof this.delegate.gestureRecognizerShouldBegin === "function" && !this.delegate.gestureRecognizerShouldBegin(this)) {
+ this.enterFailedState();
+ return;
+ }
+ this.state = GestureRecognizer.States.Began;
+ }
+
+ enterEndedState()
+ {
+ this.state = GestureRecognizer.States.Ended;
+ this._removeTrackingListeners();
+ this.reset();
+ }
+
+ enterCancelledState()
+ {
+ this.state = GestureRecognizer.States.Cancelled;
+ this._removeTrackingListeners();
+ this.reset();
+ }
+
+ enterFailedState()
+ {
+ this.state = GestureRecognizer.States.Failed;
+ this._removeTrackingListeners();
+ this.reset();
+ }
+
+ enterChangedState()
+ {
+ this.state = GestureRecognizer.States.Changed;
+ }
+
+ enterRecognizedState()
+ {
+ this.state = GestureRecognizer.States.Recognized;
+ }
+
+ // Protected
+
+ handleEvent(event)
+ {
+ this._updateTargetTouches(event);
+ this._updateKeyboardModifiers(event);
+
+ switch (event.type) {
+ case GestureRecognizer.Events.TouchStart:
+ this.touchesBegan(event);
+ break;
+ case GestureRecognizer.Events.TouchMove:
+ this.touchesMoved(event);
+ break;
+ case GestureRecognizer.Events.TouchEnd:
+ this.touchesEnded(event);
+ break;
+ case GestureRecognizer.Events.TouchCancel:
+ this.touchesCancelled(event);
+ break;
+ case GestureRecognizer.Events.GestureStart:
+ this.gestureBegan(event);
+ break;
+ case GestureRecognizer.Events.GestureChange:
+ this.gestureChanged(event);
+ break;
+ case GestureRecognizer.Events.GestureEnd:
+ this.gestureEnded(event);
+ break;
+ }
+ }
+
+ // Private
+
+ _initRecognizer()
+ {
+ this.reset();
+ this.state = GestureRecognizer.States.Possible;
+
+ this._updateBaseListeners();
+ }
+
+ _updateBaseListeners()
+ {
+ if (!this._target)
+ return;
+
+ if (this._enabled) {
+ this._target.addEventListener(GestureRecognizer.Events.TouchStart, this);
+ if (GestureRecognizer.SupportsGestures)
+ this._target.addEventListener(GestureRecognizer.Events.GestureStart, this);
+ } else {
+ this._target.removeEventListener(GestureRecognizer.Events.TouchStart, this);
+ if (GestureRecognizer.SupportsGestures)
+ this._target.removeEventListener(GestureRecognizer.Events.GestureStart, this);
+ }
+ }
+
+ _removeTrackingListeners()
+ {
+ window.removeEventListener(GestureRecognizer.Events.TouchMove, this, true);
+ window.removeEventListener(GestureRecognizer.Events.TouchEnd, this, true);
+ window.removeEventListener(GestureRecognizer.Events.GestureChange, this, true);
+ window.removeEventListener(GestureRecognizer.Events.GestureEnd, this, true);
+ }
+
+ _updateTargetTouches(event)
+ {
+ if (!GestureRecognizer.SupportsTouches) {
+ if (event.type === GestureRecognizer.Events.TouchEnd)
+ this._targetTouches = [];
+ else
+ this._targetTouches = [event];
+ return;
+ }
+
+ if (!(event instanceof TouchEvent))
+ return;
+
+ // With a touchstart event, event.targetTouches is accurate so
+ // we simply add all of those.
+ if (event.type === GestureRecognizer.Events.TouchStart) {
+ this._targetTouches = [];
+ let touches = event.targetTouches;
+ for (let i = 0, count = touches.length; i < count; ++i)
+ this._targetTouches.push(touches[i]);
+ return;
+ }
+
+ // With a touchmove event, the target is window so event.targetTouches is
+ // inaccurate so we add all touches that we knew about previously.
+ if (event.type === GestureRecognizer.Events.TouchMove) {
+ let targetIdentifiers = this._targetTouches.map(function(touch) {
+ return touch.identifier;
+ });
+
+ this._targetTouches = [];
+ let touches = event.touches;
+ for (let i = 0, count = touches.length; i < count; ++i) {
+ let touch = touches[i];
+ if (targetIdentifiers.indexOf(touch.identifier) !== -1)
+ this._targetTouches.push(touch);
+ }
+ return;
+ }
+
+ // With a touchend or touchcancel event, we only keep the existing touches
+ // that are also found in event.touches.
+ let allTouches = event.touches;
+ let existingIdentifiers = [];
+ for (let i = 0, count = allTouches.length; i < count; ++i)
+ existingIdentifiers.push(allTouches[i].identifier);
+
+ this._targetTouches = this._targetTouches.filter(function(touch) {
+ return existingIdentifiers.indexOf(touch.identifier) !== -1;
+ });
+ }
+
+ _updateKeyboardModifiers(event)
+ {
+ this.modifierKeys.alt = event.altKey;
+ this.modifierKeys.ctrl = event.ctrlKey;
+ this.modifierKeys.meta = event.metaKey;
+ this.modifierKeys.shift = event.shiftKey;
+ }
+
+}
+
+GestureRecognizer.SupportsTouches = "createTouch" in document;
+GestureRecognizer.SupportsGestures = !!window.GestureEvent;
+
+GestureRecognizer.States = {
+ Possible : "possible",
+ Began : "began",
+ Changed : "changed",
+ Ended : "ended",
+ Cancelled : "cancelled",
+ Failed : "failed",
+ Recognized : "ended"
+};
+
+GestureRecognizer.Events = {
+ TouchStart : GestureRecognizer.SupportsTouches ? "touchstart" : "mousedown",
+ TouchMove : GestureRecognizer.SupportsTouches ? "touchmove" : "mousemove",
+ TouchEnd : GestureRecognizer.SupportsTouches ? "touchend" : "mouseup",
+ TouchCancel : "touchcancel",
+ GestureStart : "gesturestart",
+ GestureChange : "gesturechange",
+ GestureEnd : "gestureend"
+};
diff --git a/Source/WebCore/Modules/modern-media-controls/gesture-recognizers/pinch.js b/Source/WebCore/Modules/modern-media-controls/gesture-recognizers/pinch.js
new file mode 100644
index 000000000..12bf0713a
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/gesture-recognizers/pinch.js
@@ -0,0 +1,205 @@
+
+const MAXIMUM_TIME_FOR_RECORDING_GESTURES = 100;
+const MAXIMUM_DECELERATION_TIME = 500;
+
+class PinchGestureRecognizer extends GestureRecognizer
+{
+
+ constructor(target, delegate)
+ {
+ super(target, delegate);
+
+ this.scaleThreshold = 0;
+ this._scaledMinimumAmount = false;
+ }
+
+ // Public
+
+ get velocity()
+ {
+ const lastGesture = this._gestures[this._gestures.length - 1];
+ if (!lastGesture)
+ return this._velocity;
+
+ const elapsedTime = Date.now() - (lastGesture.timeStamp + MAXIMUM_TIME_FOR_RECORDING_GESTURES);
+ if (elapsedTime <= 0)
+ return this._velocity;
+
+ const f = Math.max((MAXIMUM_DECELERATION_TIME - elapsedTime) / MAXIMUM_DECELERATION_TIME, 0);
+ return this._velocity * f;
+ }
+
+ // Protected
+
+ touchesBegan(event)
+ {
+ if (event.currentTarget !== this.target)
+ return;
+
+ // Additional setup for when the the platform doesn't natively
+ // provide us with gesture events.
+ if (!GestureRecognizer.SupportsGestures) {
+ // A pinch gesture can only be performed with 2 fingers, anything more
+ // and we failed our gesture.
+ if (this.numberOfTouches > 2) {
+ this.enterFailedState();
+ return;
+ }
+
+ // We can only start tracking touches with 2 fingers.
+ if (this.numberOfTouches !== 2)
+ return;
+
+ this._startDistance = this._distance();
+
+ // We manually add a start value so that we always have 2 entries in the
+ // _gestures array so that we don't have to check for the existence of 2
+ // entries when computing velocity.
+ this._recordGesture(1);
+
+ this._scaledMinimumAmount = false;
+ this._updateStateWithEvent(event);
+ } else if (this.numberOfTouches !== 2) {
+ // When we support gesture events, we only care about the case where we're
+ // using two fingers.
+ return;
+ }
+
+ super.touchesBegan(event);
+ }
+
+ touchesMoved(event)
+ {
+ // This method only needs to be overriden in the case where the platform
+ // doesn't natively provide us with gesture events.
+ if (GestureRecognizer.SupportsGestures)
+ return;
+
+ if (this.numberOfTouches !== 2)
+ return;
+
+ this._updateStateWithEvent(event);
+ }
+
+ touchesEnded(event)
+ {
+ // This method only needs to be overriden in the case where the platform
+ // doesn't natively provide us with gesture events.
+ if (GestureRecognizer.SupportsGestures)
+ return;
+
+ // If we don't have the required number of touches or have not event
+ // obtained 2 fingers, then there's nothing for us to do.
+ if (this.numberOfTouches >= 2 || !this._startDistance)
+ return;
+
+ if (this._scaledMinimumAmount)
+ this.enterEndedState();
+ else
+ this.enterFailedState();
+ }
+
+ gestureBegan(event)
+ {
+ super.gestureBegan(event);
+
+ // We manually add a start value so that we always have 2 entries in the
+ // _gestures array so that we don't have to check for the existence of 2
+ // entries when computing velocity.
+ this._recordGesture(event.scale);
+
+ this._scaledMinimumAmount = false;
+ this._updateStateWithEvent(event);
+
+ event.preventDefault();
+ }
+
+ gestureChanged(event)
+ {
+ event.preventDefault();
+
+ this._updateStateWithEvent(event);
+ }
+
+ gestureEnded(event)
+ {
+ if (this._scaledMinimumAmount)
+ this.enterEndedState();
+ else
+ this.enterFailedState();
+ }
+
+ reset()
+ {
+ this.scale = 1;
+ this._velocity = 0;
+ this._gestures = [];
+ delete this._startDistance;
+ }
+
+ // Private
+
+ _recordGesture(scale)
+ {
+ const currentTime = Date.now();
+ const count = this._gestures.push({
+ scale: scale,
+ timeStamp: currentTime
+ });
+
+ // We want to keep at least two gestures at all times.
+ if (count <= 2)
+ return;
+
+ const scaleDirection = this._gestures[count - 1].scale >= this._gestures[count - 2].scale;
+ let i = count - 3;
+ for (; i >= 0; --i) {
+ let gesture = this._gestures[i];
+ if (currentTime - gesture.timeStamp > MAXIMUM_TIME_FOR_RECORDING_GESTURES ||
+ this._gestures[i + 1].scale >= gesture.scale !== scaleDirection)
+ break;
+ }
+
+ if (i > 0)
+ this._gestures = this._gestures.slice(i + 1);
+ }
+
+ _updateStateWithEvent(event)
+ {
+ const scaleSinceStart = GestureRecognizer.SupportsGestures ? event.scale : this._distance() / this._startDistance;
+
+ if (!this._scaledMinimumAmount) {
+ if (Math.abs(1 - scaleSinceStart) >= this.scaleThreshold) {
+ this._scaledMinimumAmount = true;
+ this.scale = 1;
+ this.enterBeganState();
+ }
+ return;
+ }
+
+ this._recordGesture(scaleSinceStart);
+
+ const oldestGesture = this._gestures[0];
+ const ds = scaleSinceStart - oldestGesture.scale;
+ const dt = Date.now() - oldestGesture.timeStamp;
+ this._velocity = (dt === 0) ? 0 : ds / dt * 1000;
+
+ this.scale *= scaleSinceStart / this._gestures[this._gestures.length - 2].scale;
+
+ this.enterChangedState();
+ }
+
+ _distance()
+ {
+ console.assert(this.numberOfTouches === 2);
+
+ const firstTouch = this._targetTouches[0];
+ const firstTouchPoint = new DOMPoint(firstTouch.pageX, firstTouch.pageY);
+
+ const secondTouch = this._targetTouches[1];
+ const secondTouchPoint = new DOMPoint(secondTouch.pageX, secondTouch.pageY);
+
+ return Math.sqrt(Math.pow(firstTouchPoint.x - secondTouchPoint.x, 2) + Math.pow(firstTouchPoint.y - secondTouchPoint.y, 2));
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/gesture-recognizers/tap.js b/Source/WebCore/Modules/modern-media-controls/gesture-recognizers/tap.js
new file mode 100644
index 000000000..6f2dbf885
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/gesture-recognizers/tap.js
@@ -0,0 +1,109 @@
+
+const MOVE_TOLERANCE = GestureRecognizer.SupportsTouches ? 40 : 0;
+const WAITING_FOR_NEXT_TAP_TO_START_TIMEOUT = 350;
+const WAITING_FOR_TAP_COMPLETION_TIMEOUT = 750;
+
+class TapGestureRecognizer extends GestureRecognizer
+{
+
+ constructor(target, delegate)
+ {
+ super(target, delegate);
+
+ this.numberOfTapsRequired = 1;
+ this.numberOfTouchesRequired = 1;
+ this.allowsRightMouseButton = false;
+ }
+
+ // Protected
+
+ touchesBegan(event)
+ {
+ if (event.currentTarget !== this.target)
+ return;
+
+ if (event.button === 2 && !this.allowsRightMouseButton)
+ return;
+
+ super.touchesBegan(event);
+
+ if (this.numberOfTouches !== this.numberOfTouchesRequired) {
+ this.enterFailedState();
+ return;
+ }
+
+ this._startPoint = super.locationInElement();
+ this._startClientPoint = super.locationInClient();
+
+ this._rewindTimer(WAITING_FOR_TAP_COMPLETION_TIMEOUT);
+ }
+
+ touchesMoved(event)
+ {
+ event.preventDefault();
+
+ const touchLocation = super.locationInElement();
+ const distance = Math.sqrt(Math.pow(this._startPoint.x - touchLocation.x, 2) + Math.pow(this._startPoint.y - touchLocation.y, 2));
+ if (distance > MOVE_TOLERANCE)
+ this.enterFailedState();
+ }
+
+ touchesEnded(event)
+ {
+ this._taps++;
+
+ if (this._taps === this.numberOfTapsRequired) {
+ // We call prevent default here to override the potential double-tap-to-zoom
+ // behavior of the browser.
+ event.preventDefault();
+
+ this.enterRecognizedState();
+ this.reset();
+ }
+
+ this._rewindTimer(WAITING_FOR_NEXT_TAP_TO_START_TIMEOUT);
+ }
+
+ reset()
+ {
+ this._taps = 0;
+ this._clearTimer();
+ }
+
+ locationInElement(element)
+ {
+ const p = this._startPoint || new DOMPoint;
+
+ if (!element)
+ return p;
+
+ // FIXME: are WebKitPoint and DOMPoint interchangeable?
+ const wkPoint = window.webkitConvertPointFromPageToNode(element, new WebKitPoint(p.x, p.y));
+ return new DOMPoint(wkPoint.x, wkPoint.y);
+ }
+
+ locationInClient()
+ {
+ return this._startClientPoint || new DOMPoint;
+ }
+
+ // Private
+
+ _clearTimer()
+ {
+ window.clearTimeout(this._timerId);
+ delete this._timerId;
+ }
+
+ _rewindTimer(timeout)
+ {
+ this._clearTimer();
+ this._timerId = window.setTimeout(this._timerFired.bind(this), timeout);
+ }
+
+ _timerFired()
+ {
+ this.enterFailedState();
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@1x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@1x.png
new file mode 100644
index 000000000..86061b233
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@2x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@2x.png
new file mode 100644
index 000000000..83c72eac3
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@3x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@3x.png
new file mode 100644
index 000000000..b8aaef1b9
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay-placard@3x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay@1x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay@1x.png
new file mode 100644
index 000000000..0b472b915
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay@2x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay@2x.png
new file mode 100644
index 000000000..818c9c200
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/airplay@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@1x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@1x.png
new file mode 100644
index 000000000..5a2fd22d6
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@2x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@2x.png
new file mode 100644
index 000000000..2cb880ac0
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@3x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@3x.png
new file mode 100644
index 000000000..6db049b38
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/enter-fullscreen@3x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@1x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@1x.png
new file mode 100644
index 000000000..0c1d8b2ef
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@2x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@2x.png
new file mode 100644
index 000000000..40d12e427
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@3x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@3x.png
new file mode 100644
index 000000000..c31c36bad
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/interval-skip-back@3x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@1x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@1x.png
new file mode 100644
index 000000000..fc4369453
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@2x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@2x.png
new file mode 100644
index 000000000..e14d3d478
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@3x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@3x.png
new file mode 100644
index 000000000..bb760ad52
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/invalid-placard@3x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/pause@1x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/pause@1x.png
new file mode 100644
index 000000000..bddf4abc6
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/pause@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/pause@2x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/pause@2x.png
new file mode 100644
index 000000000..c3226a1c5
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/pause@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/pause@3x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/pause@3x.png
new file mode 100644
index 000000000..cf391a6bf
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/pause@3x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@1x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@1x.png
new file mode 100644
index 000000000..1c8d80b4d
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@2x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@2x.png
new file mode 100644
index 000000000..f7d2b921d
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@3x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@3x.png
new file mode 100644
index 000000000..e8b107ad0
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-in@3x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@1x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@1x.png
new file mode 100644
index 000000000..423963a46
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@2x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@2x.png
new file mode 100644
index 000000000..bbd4cd84b
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@3x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@3x.png
new file mode 100644
index 000000000..412eaba59
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/pip-placard@3x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/play@1x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/play@1x.png
new file mode 100644
index 000000000..a49f216b5
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/play@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/play@2x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/play@2x.png
new file mode 100644
index 000000000..fa77a634f
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/play@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/play@3x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/play@3x.png
new file mode 100644
index 000000000..73e376eae
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/play@3x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/slider-thumb@2x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/slider-thumb@2x.png
new file mode 100644
index 000000000..6c8bd93f4
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/slider-thumb@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/start@1x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/start@1x.png
new file mode 100644
index 000000000..e7467373e
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/start@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/start@2x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/start@2x.png
new file mode 100644
index 000000000..b1264e8b2
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/start@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/iOS/start@3x.png b/Source/WebCore/Modules/modern-media-controls/images/iOS/start@3x.png
new file mode 100644
index 000000000..a520a18b3
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/iOS/start@3x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-fullscreen@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-fullscreen@1x.png
new file mode 100644
index 000000000..747927b4b
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-fullscreen@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-fullscreen@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-fullscreen@2x.png
new file mode 100644
index 000000000..9b4f46f9d
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-fullscreen@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-placard@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-placard@1x.png
new file mode 100644
index 000000000..86061b233
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-placard@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-placard@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-placard@2x.png
new file mode 100644
index 000000000..83c72eac3
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay-placard@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay@1x.png
new file mode 100644
index 000000000..0b472b915
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay@2x.png
new file mode 100644
index 000000000..818c9c200
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/airplay@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen-compact@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen-compact@1x.png
new file mode 100644
index 000000000..3549780cb
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen-compact@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen-compact@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen-compact@2x.png
new file mode 100644
index 000000000..e592d7912
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen-compact@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen@1x.png
new file mode 100644
index 000000000..5a2fd22d6
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen@2x.png
new file mode 100644
index 000000000..2cb880ac0
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/enter-fullscreen@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/exit-fullscreen@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/exit-fullscreen@1x.png
new file mode 100644
index 000000000..ecc432210
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/exit-fullscreen@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/exit-fullscreen@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/exit-fullscreen@2x.png
new file mode 100644
index 000000000..77a206527
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/exit-fullscreen@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/forward@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/forward@1x.png
new file mode 100644
index 000000000..ee795a8c2
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/forward@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/forward@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/forward@2x.png
new file mode 100644
index 000000000..691015fb5
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/forward@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back-compact@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back-compact@1x.png
new file mode 100644
index 000000000..4ce2060c0
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back-compact@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back-compact@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back-compact@2x.png
new file mode 100644
index 000000000..5bf10d364
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back-compact@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back@1x.png
new file mode 100644
index 000000000..0c1d8b2ef
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back@2x.png
new file mode 100644
index 000000000..40d12e427
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/interval-skip-back@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/invalid-placard@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/invalid-placard@1x.png
new file mode 100644
index 000000000..fc4369453
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/invalid-placard@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/invalid-placard@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/invalid-placard@2x.png
new file mode 100644
index 000000000..e14d3d478
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/invalid-placard@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection-fullscreen@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection-fullscreen@1x.png
new file mode 100644
index 000000000..9d0764363
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection-fullscreen@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection-fullscreen@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection-fullscreen@2x.png
new file mode 100644
index 000000000..f50ca3b18
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection-fullscreen@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection@1x.png
new file mode 100644
index 000000000..5df5edb35
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection@2x.png
new file mode 100644
index 000000000..0cfb6b2be
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/media-selection@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/pause-compact@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause-compact@1x.png
new file mode 100644
index 000000000..6a489258f
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause-compact@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/pause-compact@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause-compact@2x.png
new file mode 100644
index 000000000..50ed11f20
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause-compact@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/pause-fullscreen@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause-fullscreen@1x.png
new file mode 100644
index 000000000..ee41eb7c9
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause-fullscreen@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/pause-fullscreen@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause-fullscreen@2x.png
new file mode 100644
index 000000000..dd49890a8
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause-fullscreen@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/pause@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause@1x.png
new file mode 100644
index 000000000..873b93020
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/pause@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause@2x.png
new file mode 100644
index 000000000..b93a5e509
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pause@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in-fullscreen@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in-fullscreen@1x.png
new file mode 100644
index 000000000..f7a64bbd1
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in-fullscreen@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in-fullscreen@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in-fullscreen@2x.png
new file mode 100644
index 000000000..fe721f737
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in-fullscreen@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in@1x.png
new file mode 100644
index 000000000..1c8d80b4d
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in@2x.png
new file mode 100644
index 000000000..f7d2b921d
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-in@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-placard@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-placard@1x.png
new file mode 100644
index 000000000..423963a46
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-placard@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-placard@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-placard@2x.png
new file mode 100644
index 000000000..bbd4cd84b
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/pip-placard@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/play-compact@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/play-compact@1x.png
new file mode 100644
index 000000000..5c8ad4a15
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/play-compact@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/play-compact@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/play-compact@2x.png
new file mode 100644
index 000000000..b6755cd39
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/play-compact@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/play-fullscreen@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/play-fullscreen@1x.png
new file mode 100644
index 000000000..a49f216b5
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/play-fullscreen@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/play-fullscreen@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/play-fullscreen@2x.png
new file mode 100644
index 000000000..fa77a634f
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/play-fullscreen@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/play@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/play@1x.png
new file mode 100644
index 000000000..a49f216b5
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/play@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/play@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/play@2x.png
new file mode 100644
index 000000000..c67d8e3dd
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/play@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/rewind@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/rewind@1x.png
new file mode 100644
index 000000000..659e984ce
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/rewind@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/rewind@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/rewind@2x.png
new file mode 100644
index 000000000..876ab734b
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/rewind@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fill@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fill@1x.png
new file mode 100644
index 000000000..67171b6ab
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fill@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fill@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fill@2x.png
new file mode 100644
index 000000000..b2c1df756
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fill@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fit@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fit@1x.png
new file mode 100644
index 000000000..7034aacfd
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fit@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fit@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fit@2x.png
new file mode 100644
index 000000000..324de8b1f
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/scale-to-fit@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/start@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/start@1x.png
new file mode 100644
index 000000000..e7467373e
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/start@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/start@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/start@2x.png
new file mode 100644
index 000000000..b1264e8b2
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/start@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-compact@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-compact@1x.png
new file mode 100644
index 000000000..dc65736b2
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-compact@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-compact@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-compact@2x.png
new file mode 100644
index 000000000..bfe1a8b87
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-compact@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-down-fullscreen@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-down-fullscreen@1x.png
new file mode 100644
index 000000000..1660737ad
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-down-fullscreen@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-down-fullscreen@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-down-fullscreen@2x.png
new file mode 100644
index 000000000..5e35ea528
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-down-fullscreen@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-mute@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-mute@1x.png
new file mode 100644
index 000000000..3ba71892f
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-mute@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-mute@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-mute@2x.png
new file mode 100644
index 000000000..67c2034ab
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-mute@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-up-fullscreen@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-up-fullscreen@1x.png
new file mode 100644
index 000000000..4c1cef883
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-up-fullscreen@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-up-fullscreen@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-up-fullscreen@2x.png
new file mode 100644
index 000000000..238cb645d
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume-up-fullscreen@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/volume@1x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume@1x.png
new file mode 100644
index 000000000..b45490ae3
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume@1x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/images/macOS/volume@2x.png b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume@2x.png
new file mode 100644
index 000000000..5bec45c16
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/images/macOS/volume@2x.png
Binary files differ
diff --git a/Source/WebCore/Modules/modern-media-controls/js-files b/Source/WebCore/Modules/modern-media-controls/js-files
new file mode 100644
index 000000000..b5ca1eea6
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/js-files
@@ -0,0 +1,63 @@
+gesture-recognizers/gesture-recognizer.js
+gesture-recognizers/tap.js
+gesture-recognizers/pinch.js
+controls/scheduler.js
+controls/layout-node.js
+controls/layout-item.js
+controls/icon-service.js
+controls/background-tint.js
+controls/time-control.js
+controls/time-label.js
+controls/slider.js
+controls/volume-slider.js
+controls/scrubber.js
+controls/button.js
+controls/start-button.js
+controls/icon-button.js
+controls/play-pause-button.js
+controls/skip-back-button.js
+controls/mute-button.js
+controls/airplay-button.js
+controls/pip-button.js
+controls/tracks-button.js
+controls/fullscreen-button.js
+controls/seek-button.js
+controls/rewind-button.js
+controls/forward-button.js
+controls/volume-down-button.js
+controls/volume-up-button.js
+controls/buttons-container.js
+controls/status-label.js
+controls/controls-bar.js
+controls/tracks-panel.js
+controls/media-controls.js
+controls/ios-inline-media-controls.js
+controls/macos-media-controls.js
+controls/macos-inline-media-controls.js
+controls/macos-fullscreen-media-controls.js
+controls/placard.js
+controls/airplay-placard.js
+controls/invalid-placard.js
+controls/pip-placard.js
+media/media-controller-support.js
+media/airplay-support.js
+media/controls-visibility-support.js
+media/fullscreen-support.js
+media/mute-support.js
+media/pip-support.js
+media/placard-support.js
+media/playback-support.js
+media/scrubbing-support.js
+media/seek-support.js
+media/seek-backward-support.js
+media/seek-forward-support.js
+media/skip-back-support.js
+media/start-support.js
+media/status-support.js
+media/time-labels-support.js
+media/tracks-support.js
+media/volume-down-support.js
+media/volume-support.js
+media/volume-up-support.js
+media/media-controller.js
+main.js
diff --git a/Source/WebCore/Modules/modern-media-controls/main.js b/Source/WebCore/Modules/modern-media-controls/main.js
new file mode 100644
index 000000000..237335b7a
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/main.js
@@ -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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+// This is called from HTMLMediaElement::ensureMediaControlsInjectedScript().
+function createControls(shadowRoot, media, host)
+{
+ if (host) {
+ iconService.mediaControlsHost = host;
+ shadowRoot.appendChild(document.createElement("style")).textContent = host.shadowRootCSSText;
+ }
+
+ return new MediaController(shadowRoot, media, host);
+}
+
+function UIString(string)
+{
+ if (!("UIStrings" in window))
+ return string;
+
+ if (string in UIStrings)
+ return UIStrings[string];
+
+ console.error(`Localization for "${string}" not found.`);
+ return "LOCALIZED STRING NOT FOUND";
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/airplay-support.js b/Source/WebCore/Modules/modern-media-controls/media/airplay-support.js
new file mode 100644
index 000000000..e3d725c05
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/airplay-support.js
@@ -0,0 +1,60 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class AirplaySupport extends MediaControllerSupport
+{
+
+ // Protected
+
+ get control()
+ {
+ return this.mediaController.controls.airplayButton;
+ }
+
+ get mediaEvents()
+ {
+ return ["webkitplaybacktargetavailabilitychanged", "webkitcurrentplaybacktargetiswirelesschanged"];
+ }
+
+ buttonWasPressed(control)
+ {
+ this.mediaController.media.webkitShowPlaybackTargetPicker();
+ }
+
+ handleEvent(event)
+ {
+ if (event.type === "webkitplaybacktargetavailabilitychanged")
+ this._routesAvailable = event.availability === "available";
+
+ super.handleEvent(event);
+ }
+
+ syncControl()
+ {
+ this.control.enabled = !!this._routesAvailable;
+ this.control.on = this.mediaController.media.webkitCurrentPlaybackTargetIsWireless;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/controls-visibility-support.js b/Source/WebCore/Modules/modern-media-controls/media/controls-visibility-support.js
new file mode 100644
index 000000000..498fa544d
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/controls-visibility-support.js
@@ -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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class ControlsVisibilitySupport extends MediaControllerSupport
+{
+
+ constructor(mediaController)
+ {
+ super(mediaController);
+
+ this._controlsAttributeObserver = new MutationObserver(this._updateControls.bind(this));
+ this._controlsAttributeObserver.observe(mediaController.media, { attributes: true, attributeFilter: ["controls"] });
+
+ this._updateControls();
+ }
+
+ // Protected
+
+ destroy()
+ {
+ this._controlsAttributeObserver.disconnect();
+ }
+
+ get mediaEvents()
+ {
+ return ["loadedmetadata", "play", "pause"];
+ }
+
+ get tracksToMonitor()
+ {
+ return [this.mediaController.media.videoTracks];
+ }
+
+ handleEvent()
+ {
+ this._updateControls();
+ }
+
+ // Private
+
+ _updateControls()
+ {
+ const media = this.mediaController.media;
+ const isVideo = media instanceof HTMLVideoElement && media.videoTracks.length > 0;
+ let shouldShowControls = media.controls && !!media.currentSrc;
+ if (isVideo)
+ shouldShowControls = shouldShowControls && media.readyState > HTMLMediaElement.HAVE_NOTHING;
+
+ const controls = this.mediaController.controls;
+ controls.startButton.visible = shouldShowControls;
+ controls.controlsBar.visible = shouldShowControls;
+ controls.controlsBar.fadesWhileIdle = isVideo ? !media.paused : false;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/fullscreen-support.js b/Source/WebCore/Modules/modern-media-controls/media/fullscreen-support.js
new file mode 100644
index 000000000..168da00b6
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/fullscreen-support.js
@@ -0,0 +1,76 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class FullscreenSupport extends MediaControllerSupport
+{
+
+ constructor(mediaController)
+ {
+ super(mediaController);
+
+ if (mediaController.controls instanceof IOSInlineMediaControls)
+ mediaController.controls.delegate = this;
+ }
+
+ // Protected
+
+ get control()
+ {
+ return this.mediaController.controls.fullscreenButton;
+ }
+
+ get mediaEvents()
+ {
+ return ["loadedmetadata", "error"];
+ }
+
+ get tracksToMonitor()
+ {
+ return [this.mediaController.media.videoTracks];
+ }
+
+ buttonWasPressed(control)
+ {
+ const media = this.mediaController.media;
+ if (media.webkitDisplayingFullscreen)
+ media.webkitExitFullscreen();
+ else
+ media.webkitEnterFullscreen();
+ }
+
+ iOSInlineMediaControlsRecognizedPinchInGesture()
+ {
+ this.mediaController.media.webkitEnterFullscreen();
+ }
+
+ syncControl()
+ {
+ const control = this.control;
+ const media = this.mediaController.media;
+ control.enabled = media.webkitSupportsFullscreen && media.videoTracks.length > 0;
+ control.isFullScreen = media.webkitDisplayingFullscreen;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/media-controller-support.js b/Source/WebCore/Modules/modern-media-controls/media/media-controller-support.js
new file mode 100644
index 000000000..55c42b175
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/media-controller-support.js
@@ -0,0 +1,101 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class MediaControllerSupport
+{
+
+ constructor(mediaController)
+ {
+ this.mediaController = mediaController;
+
+ for (let eventType of this.mediaEvents)
+ mediaController.media.addEventListener(eventType, this);
+
+ for (let tracks of this.tracksToMonitor) {
+ for (let eventType of ["change", "addtrack", "removetrack"])
+ tracks.addEventListener(eventType, this);
+ }
+
+ if (!this.control)
+ return;
+
+ this.syncControl();
+
+ this.control.uiDelegate = this;
+ }
+
+ // Public
+
+ destroy()
+ {
+ const media = this.mediaController.media;
+ for (let eventType of this.mediaEvents)
+ media.removeEventListener(eventType, this);
+
+ for (let tracks of this.tracksToMonitor) {
+ for (let eventType of ["change", "addtrack", "removetrack"])
+ tracks.removeEventListener(eventType, this);
+ }
+
+ if (this.control)
+ this.control.uiDelegate = null;
+ }
+
+ // Protected
+
+ get control()
+ {
+ // Implemented by subclasses.
+ }
+
+ get mediaEvents()
+ {
+ // Implemented by subclasses.
+ return [];
+ }
+
+ get tracksToMonitor()
+ {
+ // Implemented by subclasses.
+ return [];
+ }
+
+ buttonWasPressed(control)
+ {
+ // Implemented by subclasses.
+ }
+
+ handleEvent(event)
+ {
+ // Implemented by subclasses.
+ if (this.control)
+ this.syncControl();
+ }
+
+ syncControl()
+ {
+ // Implemented by subclasses.
+ }
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/media-controller.js b/Source/WebCore/Modules/modern-media-controls/media/media-controller.js
new file mode 100644
index 000000000..710517482
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/media-controller.js
@@ -0,0 +1,203 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+const CompactModeMaxWidth = 241;
+const ReducedPaddingMaxWidth = 300;
+const AudioTightPaddingMaxWidth = 400;
+
+class MediaController
+{
+
+ constructor(shadowRoot, media, host)
+ {
+ this.shadowRoot = shadowRoot;
+ this.media = media;
+ this.host = host;
+
+ this.container = shadowRoot.appendChild(document.createElement("div"));
+ this.container.className = "media-controls-container";
+ this.container.addEventListener("click", this, true);
+
+ if (host) {
+ host.controlsDependOnPageScaleFactor = this.layoutTraits & LayoutTraits.iOS;
+ this.container.appendChild(host.textTrackContainer);
+ }
+
+ this._updateControlsIfNeeded();
+
+ shadowRoot.addEventListener("resize", this);
+
+ media.videoTracks.addEventListener("addtrack", this);
+ media.videoTracks.addEventListener("removetrack", this);
+
+ if (media.webkitSupportsPresentationMode)
+ media.addEventListener("webkitpresentationmodechanged", this);
+ else
+ media.addEventListener("webkitfullscreenchange", this);
+ }
+
+ // Public
+
+ get layoutTraits()
+ {
+ let traits = window.navigator.platform === "MacIntel" ? LayoutTraits.macOS : LayoutTraits.iOS;
+ if (this.media.webkitSupportsPresentationMode) {
+ if (this.media.webkitPresentationMode === "fullscreen")
+ return traits | LayoutTraits.Fullscreen;
+ } else if (this.media.webkitDisplayingFullscreen)
+ return traits | LayoutTraits.Fullscreen;
+
+ const controlsWidth = this._controlsWidth();
+ if (controlsWidth <= CompactModeMaxWidth)
+ return traits | LayoutTraits.Compact;
+
+ const isAudio = this.media instanceof HTMLAudioElement || this.media.videoTracks.length === 0;
+ if (isAudio && controlsWidth <= AudioTightPaddingMaxWidth)
+ return traits | LayoutTraits.TightPadding;
+
+ if (!isAudio && controlsWidth <= ReducedPaddingMaxWidth)
+ return traits | LayoutTraits.ReducedPadding;
+
+ return traits;
+ }
+
+ togglePlayback()
+ {
+ if (this.media.paused)
+ this.media.play();
+ else
+ this.media.pause();
+ }
+
+ // Protected
+
+ set pageScaleFactor(pageScaleFactor)
+ {
+ this.controls.scaleFactor = pageScaleFactor;
+ this._updateControlsSize();
+ }
+
+ set usesLTRUserInterfaceLayoutDirection(flag)
+ {
+ this.controls.usesLTRUserInterfaceLayoutDirection = flag;
+ }
+
+ macOSControlsBackgroundWasClicked()
+ {
+ // Toggle playback when clicking on the video but not on any controls on macOS.
+ if (this.media.controls)
+ this.togglePlayback();
+ }
+
+ handleEvent(event)
+ {
+ if (event instanceof TrackEvent && event.currentTarget === this.media.videoTracks)
+ this._updateControlsIfNeeded();
+ else if (event.type === "resize" && event.currentTarget === this.shadowRoot)
+ this._updateControlsIfNeeded();
+ else if (event.type === "click" && event.currentTarget === this.container)
+ this._containerWasClicked(event);
+ else if (event.currentTarget === this.media) {
+ this._updateControlsIfNeeded();
+ if (event.type === "webkitpresentationmodechanged")
+ this._returnMediaLayerToInlineIfNeeded();
+ }
+ }
+
+ // Private
+
+ _containerWasClicked(event)
+ {
+ // We need to call preventDefault() here since, in the case of Media Documents,
+ // playback may be toggled when clicking on the video.
+ event.preventDefault();
+ }
+
+ _updateControlsIfNeeded()
+ {
+ const layoutTraits = this.layoutTraits;
+ const previousControls = this.controls;
+ const ControlsClass = this._controlsClassForLayoutTraits(layoutTraits);
+ if (previousControls && previousControls.constructor === ControlsClass) {
+ this.controls.layoutTraits = layoutTraits;
+ this._updateControlsSize();
+ return;
+ }
+
+ // Before we reset the .controls property, we need to destroy the previous
+ // supporting objects so we don't leak.
+ if (this._supportingObjects) {
+ for (let supportingObject of this._supportingObjects)
+ supportingObject.destroy();
+ }
+
+ this.controls = new ControlsClass;
+ this.controls.delegate = this;
+
+ if (this.shadowRoot.host && this.shadowRoot.host.dataset.autoHideDelay)
+ this.controls.controlsBar.autoHideDelay = this.shadowRoot.host.dataset.autoHideDelay;
+
+ if (previousControls) {
+ this.controls.fadeIn();
+ this.container.replaceChild(this.controls.element, previousControls.element);
+ this.controls.usesLTRUserInterfaceLayoutDirection = previousControls.usesLTRUserInterfaceLayoutDirection;
+ } else
+ this.container.appendChild(this.controls.element);
+
+ this.controls.layoutTraits = layoutTraits;
+ this._updateControlsSize();
+
+ this._supportingObjects = [AirplaySupport, ControlsVisibilitySupport, FullscreenSupport, MuteSupport, PiPSupport, PlacardSupport, PlaybackSupport, ScrubbingSupport, SeekBackwardSupport, SeekForwardSupport, SkipBackSupport, StartSupport, StatusSupport, TimeLabelsSupport, TracksSupport, VolumeSupport, VolumeDownSupport, VolumeUpSupport].map(SupportClass => {
+ return new SupportClass(this);
+ }, this);
+ }
+
+ _updateControlsSize()
+ {
+ this.controls.width = this._controlsWidth();
+ this.controls.height = Math.round(this.media.offsetHeight * this.controls.scaleFactor);
+ }
+
+ _controlsWidth()
+ {
+ return Math.round(this.media.offsetWidth * (this.controls ? this.controls.scaleFactor : 1));
+ }
+
+ _returnMediaLayerToInlineIfNeeded()
+ {
+ if (this.host)
+ window.requestAnimationFrame(() => this.host.setPreparedToReturnVideoLayerToInline(this.media.webkitPresentationMode !== PiPMode));
+ }
+
+ _controlsClassForLayoutTraits(layoutTraits)
+ {
+ if (layoutTraits & LayoutTraits.iOS)
+ return IOSInlineMediaControls;
+ if (layoutTraits & LayoutTraits.Fullscreen)
+ return MacOSFullscreenMediaControls;
+ return MacOSInlineMediaControls;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/mute-support.js b/Source/WebCore/Modules/modern-media-controls/media/mute-support.js
new file mode 100644
index 000000000..d733e423b
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/mute-support.js
@@ -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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class MuteSupport extends MediaControllerSupport
+{
+
+ // Protected
+
+ get control()
+ {
+ return this.mediaController.controls.muteButton;
+ }
+
+ get mediaEvents()
+ {
+ return ["volumechange"];
+ }
+
+ buttonWasPressed(control)
+ {
+ const media = this.mediaController.media;
+ media.muted = !media.muted;
+ }
+
+ syncControl()
+ {
+ this.control.muted = this.mediaController.media.muted;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/pip-support.js b/Source/WebCore/Modules/modern-media-controls/media/pip-support.js
new file mode 100644
index 000000000..2a73a3182
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/pip-support.js
@@ -0,0 +1,64 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+const PiPMode = "picture-in-picture";
+const InlineMode = "inline";
+
+class PiPSupport extends MediaControllerSupport
+{
+
+ // Protected
+
+ get control()
+ {
+ return this.mediaController.controls.pipButton;
+ }
+
+ get mediaEvents()
+ {
+ return ["loadedmetadata", "error", "webkitpresentationmodechanged", "webkitcurrentplaybacktargetiswirelesschanged"];
+ }
+
+ get tracksToMonitor()
+ {
+ return [this.mediaController.media.videoTracks];
+ }
+
+ buttonWasPressed(control)
+ {
+ const media = this.mediaController.media;
+ media.webkitSetPresentationMode(media.webkitPresentationMode === PiPMode ? InlineMode : PiPMode);
+ }
+
+ syncControl()
+ {
+ const media = this.mediaController.media;
+ if (media.webkitSupportsPresentationMode)
+ this.control.enabled = media instanceof HTMLVideoElement && media.webkitSupportsPresentationMode(PiPMode) && !media.webkitCurrentPlaybackTargetIsWireless;
+ else
+ this.control.enabled = false;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/placard-support.js b/Source/WebCore/Modules/modern-media-controls/media/placard-support.js
new file mode 100644
index 000000000..5ef401c26
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/placard-support.js
@@ -0,0 +1,64 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class PlacardSupport extends MediaControllerSupport
+{
+
+ constructor(mediaController)
+ {
+ super(mediaController);
+ this._updatePlacard();
+ }
+
+ // Protected
+
+ get mediaEvents()
+ {
+ return ["error", "webkitpresentationmodechanged", "webkitcurrentplaybacktargetiswirelesschanged"];
+ }
+
+ handleEvent(event)
+ {
+ this._updatePlacard();
+ }
+
+ // Private
+
+ _updatePlacard()
+ {
+ const controls = this.mediaController.controls;
+
+ const media = this.mediaController.media;
+ if (media.webkitPresentationMode === "picture-in-picture")
+ controls.showPlacard(controls.pipPlacard);
+ else if (media.webkitCurrentPlaybackTargetIsWireless)
+ controls.showPlacard(controls.airplayPlacard);
+ else if (media instanceof HTMLVideoElement && media.error !== null && media.played.length === 0)
+ controls.showPlacard(controls.invalidPlacard);
+ else
+ controls.hidePlacard();
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/playback-support.js b/Source/WebCore/Modules/modern-media-controls/media/playback-support.js
new file mode 100644
index 000000000..9e27e43e5
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/playback-support.js
@@ -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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class PlaybackSupport extends MediaControllerSupport
+{
+
+ // Protected
+
+ get control()
+ {
+ return this.mediaController.controls.playPauseButton;
+ }
+
+ get mediaEvents()
+ {
+ return ["play", "pause"];
+ }
+
+ buttonWasPressed(control)
+ {
+ this.mediaController.togglePlayback();
+ }
+
+ syncControl()
+ {
+ this.control.playing = !this.mediaController.media.paused;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/scrubbing-support.js b/Source/WebCore/Modules/modern-media-controls/media/scrubbing-support.js
new file mode 100644
index 000000000..3d9bcd68d
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/scrubbing-support.js
@@ -0,0 +1,79 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class ScrubbingSupport extends MediaControllerSupport
+{
+
+ // Protected
+
+ get control()
+ {
+ return this.mediaController.controls.timeControl.scrubber;
+ }
+
+ get mediaEvents()
+ {
+ return ["timeupdate", "progress"];
+ }
+
+ controlValueWillStartChanging(control)
+ {
+ const media = this.mediaController.media;
+ const isPaused = media.paused;
+ if (!isPaused)
+ media.pause();
+
+ this._wasPausedWhenScrubbingStarted = isPaused;
+ }
+
+ controlValueDidChange(control)
+ {
+ const media = this.mediaController.media;
+ media.fastSeek(control.value * media.duration);
+ }
+
+ controlValueDidStopChanging(control)
+ {
+ if (!this._wasPausedWhenScrubbingStarted)
+ this.mediaController.media.play();
+
+ delete this._wasPausedWhenScrubbingStarted;
+ }
+
+ syncControl()
+ {
+ const media = this.mediaController.media;
+ if (isNaN(media.duration))
+ return;
+
+ let buffered = 0;
+ for (let i = 0, count = media.buffered.length; i < count; ++i)
+ buffered = Math.max(media.buffered.end(i), buffered);
+
+ this.control.buffered = buffered / media.duration;
+ this.control.value = media.currentTime / media.duration;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/seek-backward-support.js b/Source/WebCore/Modules/modern-media-controls/media/seek-backward-support.js
new file mode 100644
index 000000000..cc00201f8
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/seek-backward-support.js
@@ -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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class SeekBackwardSupport extends SeekSupport
+{
+
+ get control()
+ {
+ return this.mediaController.controls.rewindButton;
+ }
+
+ get multiplier()
+ {
+ return -1;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/seek-forward-support.js b/Source/WebCore/Modules/modern-media-controls/media/seek-forward-support.js
new file mode 100644
index 000000000..6c98df24f
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/seek-forward-support.js
@@ -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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class SeekForwardSupport extends SeekSupport
+{
+
+ get control()
+ {
+ return this.mediaController.controls.forwardButton;
+ }
+
+ get multiplier()
+ {
+ return 1;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/seek-support.js b/Source/WebCore/Modules/modern-media-controls/media/seek-support.js
new file mode 100644
index 000000000..596f2bc80
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/seek-support.js
@@ -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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class SeekSupport extends MediaControllerSupport
+{
+
+ // Protected
+
+ get multiplier()
+ {
+ // Implemented by subclasses.
+ }
+
+ buttonPressedStateDidChange(control, isPressed)
+ {
+ if (isPressed)
+ this._startSeeking();
+ else
+ this._stopSeeking();
+ }
+
+ // Private
+
+ _startSeeking()
+ {
+ const media = this.mediaController.media;
+ const isPaused = media.paused;
+ if (isPaused)
+ media.play();
+
+ this._wasPausedWhenSeekingStarted = isPaused;
+ this._interval = window.setInterval(this._seek.bind(this), SeekSupport.SeekDelay);
+ this._seek();
+ }
+
+ _stopSeeking()
+ {
+ const media = this.mediaController.media;
+ media.playbackRate = media.defaultPlaybackRate;
+ if (this._wasPausedWhenSeekingStarted)
+ media.pause();
+ if (this._interval)
+ window.clearInterval(this._interval);
+ }
+
+ _seek()
+ {
+ const media = this.mediaController.media;
+ media.playbackRate = Math.min(SeekSupport.MaximumSeekRate, Math.abs(media.playbackRate * 2)) * this.multiplier;
+ }
+
+}
+
+SeekSupport.MaximumSeekRate = 8;
+SeekSupport.SeekDelay = 1500;
diff --git a/Source/WebCore/Modules/modern-media-controls/media/skip-back-support.js b/Source/WebCore/Modules/modern-media-controls/media/skip-back-support.js
new file mode 100644
index 000000000..ea07c703a
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/skip-back-support.js
@@ -0,0 +1,54 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+const SkipBackSeconds = 30;
+
+class SkipBackSupport extends MediaControllerSupport
+{
+
+ // Protected
+
+ get control()
+ {
+ return this.mediaController.controls.skipBackButton;
+ }
+
+ get mediaEvents()
+ {
+ return ["durationchange"];
+ }
+
+ buttonWasPressed(control)
+ {
+ const media = this.mediaController.media;
+ media.currentTime = Math.max(media.currentTime - SkipBackSeconds, media.seekable.start(0));
+ }
+
+ syncControl()
+ {
+ this.control.enabled = this.mediaController.media.duration !== Number.POSITIVE_INFINITY;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/start-support.js b/Source/WebCore/Modules/modern-media-controls/media/start-support.js
new file mode 100644
index 000000000..bd9d7a312
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/start-support.js
@@ -0,0 +1,93 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class StartSupport extends MediaControllerSupport
+{
+
+ // Protected
+
+ get control()
+ {
+ return this.mediaController.controls.startButton;
+ }
+
+ get mediaEvents()
+ {
+ return ["loadedmetadata", "play", "error", "webkitfullscreenchange"];
+ }
+
+ buttonWasPressed(control)
+ {
+ this.mediaController.media.play();
+ }
+
+ handleEvent(event)
+ {
+ if (event.type === "play")
+ this._hasPlayed = true;
+
+ super.handleEvent(event);
+ }
+
+ syncControl()
+ {
+ this.mediaController.controls.showsStartButton = this._shouldShowStartButton();
+ }
+
+ // Private
+
+ _shouldShowStartButton()
+ {
+ const media = this.mediaController.media;
+
+ if (this._hasPlayed || media.played.length)
+ return false;
+
+ if (!media.paused)
+ return false;
+
+ if (media.autoplay)
+ return false;
+
+ if (media instanceof HTMLAudioElement)
+ return false;
+
+ if (media.webkitDisplayingFullscreen)
+ return false;
+
+ if (!media.currentSrc)
+ return false;
+
+ if (media.error)
+ return false;
+
+ const host = this.mediaController.host;
+ if (!media.controls && host && host.allowsInlineMediaPlayback)
+ return false;
+
+ return true;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/status-support.js b/Source/WebCore/Modules/modern-media-controls/media/status-support.js
new file mode 100644
index 000000000..206c73032
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/status-support.js
@@ -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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class StatusSupport extends MediaControllerSupport
+{
+
+ // Protected
+
+ get control()
+ {
+ return this.mediaController.controls.statusLabel;
+ }
+
+ get mediaEvents()
+ {
+ return ["durationchange", "loadstart", "error", "abort", "suspend", "stalled", "waiting", "emptied", "loadedmetadata", "loadeddata", "canplay", "canplaythrough"];
+ }
+
+ syncControl()
+ {
+ const media = this.mediaController.media;
+ const isLiveBroadcast = media.duration === Number.POSITIVE_INFINITY;
+ const isPlayable = media.readyState > HTMLMediaElement.HAVE_METADATA && !media.error;
+
+ if (!!media.error)
+ this.control.text = UIString("Error");
+ else if (isLiveBroadcast && media.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA)
+ this.control.text = UIString("Live Broadcast");
+ else if (!isPlayable && media.networkState === HTMLMediaElement.NETWORK_LOADING)
+ this.control.text = UIString("Loading");
+ else
+ this.control.text = "";
+
+ this.control.enabled = isLiveBroadcast || !isPlayable;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/time-labels-support.js b/Source/WebCore/Modules/modern-media-controls/media/time-labels-support.js
new file mode 100644
index 000000000..e9b72d227
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/time-labels-support.js
@@ -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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class TimeLabelsSupport extends MediaControllerSupport
+{
+
+ // Protected
+
+ get control()
+ {
+ return this.mediaController.controls.timeControl;
+ }
+
+ get mediaEvents()
+ {
+ return ["timeupdate", "durationchange"];
+ }
+
+ syncControl()
+ {
+ const media = this.mediaController.media;
+ const shouldShowZeroDurations = isNaN(media.duration) || media.duration === Number.POSITIVE_INFINITY;
+
+ this.control.elapsedTimeLabel.value = shouldShowZeroDurations ? 0 : media.currentTime;
+ this.control.remainingTimeLabel.value = shouldShowZeroDurations ? 0 : (media.currentTime - media.duration);
+ this.control.labelsMayDisplayTimesOverAnHour = !shouldShowZeroDurations && media.duration >= (60 * 60);
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/tracks-support.js b/Source/WebCore/Modules/modern-media-controls/media/tracks-support.js
new file mode 100644
index 000000000..d0bdf4b99
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/tracks-support.js
@@ -0,0 +1,150 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class TracksSupport extends MediaControllerSupport
+{
+
+ constructor(mediaController)
+ {
+ super(mediaController);
+
+ if (!this.control)
+ return;
+
+ this.mediaController.controls.tracksPanel.dataSource = this;
+ this.mediaController.controls.tracksPanel.uiDelegate = this;
+ }
+
+ // Protected
+
+ get control()
+ {
+ return this.mediaController.controls.tracksButton;
+ }
+
+ get mediaEvents()
+ {
+ return ["loadedmetadata"];
+ }
+
+ get tracksToMonitor()
+ {
+ return [this.mediaController.media.audioTracks, this.mediaController.media.textTracks];
+ }
+
+ buttonWasPressed(control)
+ {
+ this.mediaController.controls.showTracksPanel();
+ }
+
+ tracksPanelNumberOfSections()
+ {
+ let numberOfSections = 0;
+ if (this._canPickAudioTracks())
+ numberOfSections++;
+ if (this._canPickTextTracks())
+ numberOfSections++;
+ return numberOfSections;
+ }
+
+ tracksPanelTitleForSection(sectionIndex)
+ {
+ if (sectionIndex == 0 && this._canPickAudioTracks())
+ return UIString("Audio");
+ return UIString("Subtitles");
+ }
+
+ tracksPanelNumberOfTracksInSection(sectionIndex)
+ {
+ if (sectionIndex == 0 && this._canPickAudioTracks())
+ return this._audioTracks().length;
+ return this._textTracks().length;
+ }
+
+ tracksPanelTitleForTrackInSection(trackIndex, sectionIndex)
+ {
+ let track;
+ if (sectionIndex == 0 && this._canPickAudioTracks())
+ track = this._audioTracks()[trackIndex];
+ else
+ track = this._textTracks()[trackIndex];
+
+ if (this.mediaController.host)
+ return this.mediaController.host.displayNameForTrack(track);
+ return track.label;
+ }
+
+ tracksPanelIsTrackInSectionSelected(trackIndex, sectionIndex)
+ {
+ if (sectionIndex == 0 && this._canPickAudioTracks())
+ return this._audioTracks()[trackIndex].enabled;
+ return this._textTracks()[trackIndex].mode !== "disabled";
+ }
+
+ tracksPanelSelectionDidChange(trackIndex, sectionIndex)
+ {
+ if (sectionIndex == 0 && this._canPickAudioTracks())
+ this._audioTracks().forEach((audioTrack, index) => audioTrack.enabled = index === trackIndex);
+ else
+ this._textTracks().forEach((textTrack, index) => textTrack.mode = index === trackIndex ? "showing" : "disabled");
+
+ this.mediaController.controls.hideTracksPanel();
+ }
+
+ syncControl()
+ {
+ this.control.enabled = this._canPickAudioTracks() || this._canPickTextTracks();
+ }
+
+ // Private
+
+ _textTracks()
+ {
+ return this._sortedTrackList(this.mediaController.media.textTracks);
+ }
+
+ _audioTracks()
+ {
+ return this._sortedTrackList(this.mediaController.media.audioTracks);
+ }
+
+ _canPickAudioTracks()
+ {
+ const audioTracks = this._audioTracks();
+ return audioTracks && audioTracks.length > 1;
+ }
+
+ _canPickTextTracks()
+ {
+ const textTracks = this._textTracks();
+ return textTracks && textTracks.length > 0;
+ }
+
+ _sortedTrackList(tracks)
+ {
+ return Array.from(this.mediaController.host ? this.mediaController.host.sortedTrackListForMenu(tracks) : tracks);
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/volume-down-support.js b/Source/WebCore/Modules/modern-media-controls/media/volume-down-support.js
new file mode 100644
index 000000000..94c20c5ad
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/volume-down-support.js
@@ -0,0 +1,41 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class VolumeDownSupport extends MediaControllerSupport
+{
+
+ // Protected
+
+ get control()
+ {
+ return this.mediaController.controls.volumeDownButton;
+ }
+
+ buttonWasPressed(control)
+ {
+ this.mediaController.media.volume = 0;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/volume-support.js b/Source/WebCore/Modules/modern-media-controls/media/volume-support.js
new file mode 100644
index 000000000..4a41be650
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/volume-support.js
@@ -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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class VolumeSupport extends MediaControllerSupport
+{
+
+ // Protected
+
+ get control()
+ {
+ return this.mediaController.controls.volumeSlider;
+ }
+
+ get mediaEvents()
+ {
+ return ["volumechange"];
+ }
+
+ controlValueWillStartChanging(control)
+ {
+ this.mediaController.media.muted = false;
+ }
+
+ controlValueDidChange(control)
+ {
+ this.mediaController.media.volume = control.value;
+ }
+
+ syncControl()
+ {
+ const media = this.mediaController.media;
+ this.control.value = media.muted ? 0 : media.volume;
+ }
+
+}
diff --git a/Source/WebCore/Modules/modern-media-controls/media/volume-up-support.js b/Source/WebCore/Modules/modern-media-controls/media/volume-up-support.js
new file mode 100644
index 000000000..5edfdc575
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/media/volume-up-support.js
@@ -0,0 +1,41 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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.
+ */
+
+class VolumeUpSupport extends MediaControllerSupport
+{
+
+ // Protected
+
+ get control()
+ {
+ return this.mediaController.controls.volumeUpButton;
+ }
+
+ buttonWasPressed(control)
+ {
+ this.mediaController.media.volume = 1;
+ }
+
+}
diff --git a/Source/WebCore/Modules/navigatorcontentutils/NavigatorContentUtils.cpp b/Source/WebCore/Modules/navigatorcontentutils/NavigatorContentUtils.cpp
index b5fb0266f..010efb77e 100644
--- a/Source/WebCore/Modules/navigatorcontentutils/NavigatorContentUtils.cpp
+++ b/Source/WebCore/Modules/navigatorcontentutils/NavigatorContentUtils.cpp
@@ -35,129 +35,91 @@
#include "Navigator.h"
#include "Page.h"
#include <wtf/HashSet.h>
+#include <wtf/NeverDestroyed.h>
namespace WebCore {
-static HashSet<String>* protocolWhitelist;
-
-static void initProtocolHandlerWhitelist()
-{
- protocolWhitelist = new HashSet<String>;
- static const char* protocols[] = {
- "bitcoin",
- "geo",
- "im",
- "irc",
- "ircs",
- "magnet",
- "mailto",
- "mms",
- "news",
- "nntp",
- "sip",
- "sms",
- "smsto",
- "ssh",
- "tel",
- "urn",
- "webcal",
- "wtai",
- "xmpp",
- };
- for (size_t i = 0; i < WTF_ARRAY_LENGTH(protocols); ++i)
- protocolWhitelist->add(protocols[i]);
-}
-
-static bool verifyCustomHandlerURL(const String& baseURL, const String& url, ExceptionCode& ec)
+static bool verifyCustomHandlerURL(const URL& baseURL, const String& url)
{
// The specification requires that it is a SYNTAX_ERR if the "%s" token is
// not present.
static const char token[] = "%s";
int index = url.find(token);
- if (-1 == index) {
- ec = SYNTAX_ERR;
+ if (-1 == index)
return false;
- }
// It is also a SYNTAX_ERR if the custom handler URL, as created by removing
// the "%s" token and prepending the base url, does not resolve.
String newURL = url;
newURL.remove(index, WTF_ARRAY_LENGTH(token) - 1);
- URL base(ParsedURLString, baseURL);
- URL kurl(base, newURL);
+ URL kurl(baseURL, newURL);
- if (kurl.isEmpty() || !kurl.isValid()) {
- ec = SYNTAX_ERR;
+ if (kurl.isEmpty() || !kurl.isValid())
return false;
- }
return true;
}
-static bool isProtocolWhitelisted(const String& scheme)
+static inline bool isProtocolWhitelisted(const String& scheme)
{
- if (!protocolWhitelist)
- initProtocolHandlerWhitelist();
- return protocolWhitelist->contains(scheme);
+ static NeverDestroyed<HashSet<String, ASCIICaseInsensitiveHash>> protocolWhitelist = []() {
+ HashSet<String, ASCIICaseInsensitiveHash> set;
+ for (auto* protocol : { "bitcoin", "geo", "im", "irc", "ircs", "magnet", "mailto", "mms", "news", "nntp", "sip", "sms", "smsto", "ssh", "tel", "urn", "webcal", "wtai", "xmpp" })
+ set.add(protocol);
+ return set;
+ }();
+ return protocolWhitelist.get().contains(scheme);
}
-static bool verifyProtocolHandlerScheme(const String& scheme, ExceptionCode& ec)
+static bool verifyProtocolHandlerScheme(const String& scheme)
{
+ if (isProtocolWhitelisted(scheme))
+ return true;
+
+ // FIXME: Should this be case sensitive, or should it be ASCII case-insensitive?
if (scheme.startsWith("web+")) {
- // The specification requires that the length of scheme is at least five characteres (including 'web+' prefix).
+ // The specification requires that the length of scheme is at least five characters (including 'web+' prefix).
if (scheme.length() >= 5 && isValidProtocol(scheme))
return true;
- ec = SECURITY_ERR;
- return false;
}
- if (isProtocolWhitelisted(scheme))
- return true;
- ec = SECURITY_ERR;
return false;
}
NavigatorContentUtils* NavigatorContentUtils::from(Page* page)
{
- return static_cast<NavigatorContentUtils*>(RefCountedSupplement<Page, NavigatorContentUtils>::from(page, NavigatorContentUtils::supplementName()));
+ return static_cast<NavigatorContentUtils*>(Supplement<Page>::from(page, supplementName()));
}
NavigatorContentUtils::~NavigatorContentUtils()
{
}
-PassRefPtr<NavigatorContentUtils> NavigatorContentUtils::create(NavigatorContentUtilsClient* client)
-{
- return adoptRef(new NavigatorContentUtils(client));
-}
-
-void NavigatorContentUtils::registerProtocolHandler(Navigator* navigator, const String& scheme, const String& url, const String& title, ExceptionCode& ec)
+ExceptionOr<void> NavigatorContentUtils::registerProtocolHandler(Navigator& navigator, const String& scheme, const String& url, const String& title)
{
- if (!navigator->frame())
- return;
+ if (!navigator.frame())
+ return { };
- Document* document = navigator->frame()->document();
- if (!document)
- return;
+ URL baseURL = navigator.frame()->document()->baseURL();
- String baseURL = document->baseURL().baseAsString();
+ if (!verifyCustomHandlerURL(baseURL, url))
+ return Exception { SYNTAX_ERR };
- if (!verifyCustomHandlerURL(baseURL, url, ec))
- return;
+ if (!verifyProtocolHandlerScheme(scheme))
+ return Exception { SECURITY_ERR };
- if (!verifyProtocolHandlerScheme(scheme, ec))
- return;
-
- NavigatorContentUtils::from(navigator->frame()->page())->client()->registerProtocolHandler(scheme, baseURL, url, navigator->frame()->displayStringModifiedByEncoding(title));
+ NavigatorContentUtils::from(navigator.frame()->page())->client()->registerProtocolHandler(scheme, baseURL, URL(ParsedURLString, url), navigator.frame()->displayStringModifiedByEncoding(title));
+ return { };
}
#if ENABLE(CUSTOM_SCHEME_HANDLER)
+
static String customHandlersStateString(const NavigatorContentUtilsClient::CustomHandlersState state)
{
- DEFINE_STATIC_LOCAL(const String, newHandler, (ASCIILiteral("new")));
- DEFINE_STATIC_LOCAL(const String, registeredHandler, (ASCIILiteral("registered")));
- DEFINE_STATIC_LOCAL(const String, declinedHandler, (ASCIILiteral("declined")));
+ static NeverDestroyed<String> newHandler(ASCIILiteral("new"));
+ static NeverDestroyed<String> registeredHandler(ASCIILiteral("registered"));
+ static NeverDestroyed<String> declinedHandler(ASCIILiteral("declined"));
switch (state) {
case NavigatorContentUtilsClient::CustomHandlersNew:
@@ -172,41 +134,41 @@ static String customHandlersStateString(const NavigatorContentUtilsClient::Custo
return String();
}
-String NavigatorContentUtils::isProtocolHandlerRegistered(Navigator* navigator, const String& scheme, const String& url, ExceptionCode& ec)
+ExceptionOr<String> NavigatorContentUtils::isProtocolHandlerRegistered(Navigator& navigator, const String& scheme, const String& url)
{
- DEFINE_STATIC_LOCAL(const String, declined, ("declined"));
+ static NeverDestroyed<String> declined(ASCIILiteral("declined"));
- if (!navigator->frame())
- return declined;
+ if (!navigator.frame())
+ return String { declined };
- Document* document = navigator->frame()->document();
- String baseURL = document->baseURL().baseAsString();
+ URL baseURL = navigator.frame()->document()->baseURL();
- if (!verifyCustomHandlerURL(baseURL, url, ec))
- return declined;
+ if (!verifyCustomHandlerURL(baseURL, url))
+ return Exception { SYNTAX_ERR };
- if (!verifyProtocolHandlerScheme(scheme, ec))
- return declined;
+ if (!verifyProtocolHandlerScheme(scheme))
+ return Exception { SECURITY_ERR };
- return customHandlersStateString(NavigatorContentUtils::from(navigator->frame()->page())->client()->isProtocolHandlerRegistered(scheme, baseURL, url));
+ return customHandlersStateString(NavigatorContentUtils::from(navigator.frame()->page())->client()->isProtocolHandlerRegistered(scheme, baseURL, URL(ParsedURLString, url)));
}
-void NavigatorContentUtils::unregisterProtocolHandler(Navigator* navigator, const String& scheme, const String& url, ExceptionCode& ec)
+ExceptionOr<void> NavigatorContentUtils::unregisterProtocolHandler(Navigator& navigator, const String& scheme, const String& url)
{
- if (!navigator->frame())
- return;
+ if (!navigator.frame())
+ return { };
- Document* document = navigator->frame()->document();
- String baseURL = document->baseURL().baseAsString();
+ URL baseURL = navigator.frame()->document()->baseURL();
- if (!verifyCustomHandlerURL(baseURL, url, ec))
- return;
+ if (!verifyCustomHandlerURL(baseURL, url))
+ return Exception { SYNTAX_ERR };
- if (!verifyProtocolHandlerScheme(scheme, ec))
- return;
+ if (!verifyProtocolHandlerScheme(scheme))
+ return Exception { SECURITY_ERR };
- NavigatorContentUtils::from(navigator->frame()->page())->client()->unregisterProtocolHandler(scheme, baseURL, url);
+ NavigatorContentUtils::from(navigator.frame()->page())->client()->unregisterProtocolHandler(scheme, baseURL, URL(ParsedURLString, url));
+ return { };
}
+
#endif
const char* NavigatorContentUtils::supplementName()
@@ -214,12 +176,11 @@ const char* NavigatorContentUtils::supplementName()
return "NavigatorContentUtils";
}
-void provideNavigatorContentUtilsTo(Page* page, NavigatorContentUtilsClient* client)
+void provideNavigatorContentUtilsTo(Page* page, std::unique_ptr<NavigatorContentUtilsClient> client)
{
- RefCountedSupplement<Page, NavigatorContentUtils>::provideTo(page, NavigatorContentUtils::supplementName(), NavigatorContentUtils::create(client));
+ NavigatorContentUtils::provideTo(page, NavigatorContentUtils::supplementName(), std::make_unique<NavigatorContentUtils>(WTFMove(client)));
}
} // namespace WebCore
#endif // ENABLE(NAVIGATOR_CONTENT_UTILS)
-
diff --git a/Source/WebCore/Modules/navigatorcontentutils/NavigatorContentUtils.h b/Source/WebCore/Modules/navigatorcontentutils/NavigatorContentUtils.h
index 99151c287..bf2647751 100644
--- a/Source/WebCore/Modules/navigatorcontentutils/NavigatorContentUtils.h
+++ b/Source/WebCore/Modules/navigatorcontentutils/NavigatorContentUtils.h
@@ -24,15 +24,13 @@
* DAMAGE.
*/
-#ifndef NavigatorContentUtils_h
-#define NavigatorContentUtils_h
+#pragma once
#if ENABLE(NAVIGATOR_CONTENT_UTILS)
+#include "ExceptionOr.h"
#include "NavigatorContentUtilsClient.h"
-#include "RefCountedSupplement.h"
-#include <wtf/PassRefPtr.h>
-#include <wtf/text/WTFString.h>
+#include "Supplementable.h"
namespace WebCore {
@@ -41,34 +39,30 @@ class Navigator;
typedef int ExceptionCode;
-class NavigatorContentUtils : public RefCountedSupplement<Page, NavigatorContentUtils> {
+class NavigatorContentUtils final : public Supplement<Page> {
public:
+ explicit NavigatorContentUtils(std::unique_ptr<NavigatorContentUtilsClient> client)
+ : m_client(WTFMove(client))
+ { }
+
virtual ~NavigatorContentUtils();
static const char* supplementName();
static NavigatorContentUtils* from(Page*);
- static void registerProtocolHandler(Navigator*, const String& scheme, const String& url, const String& title, ExceptionCode&);
+ static ExceptionOr<void> registerProtocolHandler(Navigator&, const String& scheme, const String& url, const String& title);
#if ENABLE(CUSTOM_SCHEME_HANDLER)
- static String isProtocolHandlerRegistered(Navigator*, const String& scheme, const String& url, ExceptionCode&);
- static void unregisterProtocolHandler(Navigator*, const String& scheme, const String& url, ExceptionCode&);
+ static ExceptionOr<String> isProtocolHandlerRegistered(Navigator&, const String& scheme, const String& url);
+ static ExceptionOr<void> unregisterProtocolHandler(Navigator&, const String& scheme, const String& url);
#endif
- static PassRefPtr<NavigatorContentUtils> create(NavigatorContentUtilsClient*);
-
private:
- explicit NavigatorContentUtils(NavigatorContentUtilsClient* client)
- : m_client(client)
- { }
-
- NavigatorContentUtilsClient* client() { return m_client; }
+ NavigatorContentUtilsClient* client() { return m_client.get(); }
- NavigatorContentUtilsClient* m_client;
+ std::unique_ptr<NavigatorContentUtilsClient> m_client;
};
} // namespace WebCore
#endif // ENABLE(NAVIGATOR_CONTENT_UTILS)
-
-#endif // NavigatorContentUtils_h
diff --git a/Source/WebCore/Modules/navigatorcontentutils/NavigatorContentUtils.idl b/Source/WebCore/Modules/navigatorcontentutils/NavigatorContentUtils.idl
index a9f4b3114..f3eca86ba 100644
--- a/Source/WebCore/Modules/navigatorcontentutils/NavigatorContentUtils.idl
+++ b/Source/WebCore/Modules/navigatorcontentutils/NavigatorContentUtils.idl
@@ -20,8 +20,7 @@
// http://www.w3.org/TR/html5/system-state-and-capabilities.html#custom-handlers
partial interface Navigator {
- [Conditional=NAVIGATOR_CONTENT_UTILS, RaisesException] void registerProtocolHandler(DOMString scheme, DOMString url, DOMString title);
- [Conditional=NAVIGATOR_CONTENT_UTILS&CUSTOM_SCHEME_HANDLER, RaisesException] DOMString isProtocolHandlerRegistered(DOMString scheme, DOMString url);
- [Conditional=NAVIGATOR_CONTENT_UTILS&CUSTOM_SCHEME_HANDLER, RaisesException] void unregisterProtocolHandler(DOMString scheme, DOMString url);
+ [Conditional=NAVIGATOR_CONTENT_UTILS, MayThrowException] void registerProtocolHandler(DOMString scheme, DOMString url, DOMString title);
+ [Conditional=NAVIGATOR_CONTENT_UTILS&CUSTOM_SCHEME_HANDLER, MayThrowException] DOMString isProtocolHandlerRegistered(DOMString scheme, DOMString url);
+ [Conditional=NAVIGATOR_CONTENT_UTILS&CUSTOM_SCHEME_HANDLER, MayThrowException] void unregisterProtocolHandler(DOMString scheme, DOMString url);
};
-
diff --git a/Source/WebCore/Modules/navigatorcontentutils/NavigatorContentUtilsClient.h b/Source/WebCore/Modules/navigatorcontentutils/NavigatorContentUtilsClient.h
index 25d67cc10..b70e896db 100644
--- a/Source/WebCore/Modules/navigatorcontentutils/NavigatorContentUtilsClient.h
+++ b/Source/WebCore/Modules/navigatorcontentutils/NavigatorContentUtilsClient.h
@@ -23,11 +23,11 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef NavigatorContentUtilsClient_h
-#define NavigatorContentUtilsClient_h
+#pragma once
#if ENABLE(NAVIGATOR_CONTENT_UTILS)
+#include "URL.h"
#include <wtf/text/WTFString.h>
namespace WebCore {
@@ -37,7 +37,7 @@ class Page;
class NavigatorContentUtilsClient {
public:
virtual ~NavigatorContentUtilsClient() { }
- virtual void registerProtocolHandler(const String& scheme, const String& baseURL, const String& url, const String& title) = 0;
+ virtual void registerProtocolHandler(const String& scheme, const URL& baseURL, const URL&, const String& title) = 0;
#if ENABLE(CUSTOM_SCHEME_HANDLER)
enum CustomHandlersState {
@@ -46,14 +46,13 @@ public:
CustomHandlersDeclined
};
- virtual CustomHandlersState isProtocolHandlerRegistered(const String& scheme, const String& baseURL, const String& url) = 0;
- virtual void unregisterProtocolHandler(const String& scheme, const String& baseURL, const String& url) = 0;
+ virtual CustomHandlersState isProtocolHandlerRegistered(const String& scheme, const URL& baseURL, const URL&) = 0;
+ virtual void unregisterProtocolHandler(const String& scheme, const URL& baseURL, const URL&) = 0;
#endif
};
-void provideNavigatorContentUtilsTo(Page*, NavigatorContentUtilsClient*);
+void provideNavigatorContentUtilsTo(Page*, std::unique_ptr<NavigatorContentUtilsClient>);
-}
+} // namespace WebCore
#endif // ENABLE(NAVIGATOR_CONTENT_UTILS)
-#endif // NavigatorContentUtilsClient_h
diff --git a/Source/WebCore/Modules/notifications/DOMWindowNotifications.cpp b/Source/WebCore/Modules/notifications/DOMWindowNotifications.cpp
index 21a6b9352..be9d681b3 100644
--- a/Source/WebCore/Modules/notifications/DOMWindowNotifications.cpp
+++ b/Source/WebCore/Modules/notifications/DOMWindowNotifications.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
@@ -56,27 +56,28 @@ DOMWindowNotifications* DOMWindowNotifications::from(DOMWindow* window)
{
DOMWindowNotifications* supplement = static_cast<DOMWindowNotifications*>(Supplement<DOMWindow>::from(window, supplementName()));
if (!supplement) {
- supplement = new DOMWindowNotifications(window);
- Supplement<DOMWindow>::provideTo(window, supplementName(), adoptPtr(supplement));
+ auto newSupplement = std::make_unique<DOMWindowNotifications>(window);
+ supplement = newSupplement.get();
+ provideTo(window, supplementName(), WTFMove(newSupplement));
}
return supplement;
}
-NotificationCenter* DOMWindowNotifications::webkitNotifications(DOMWindow* window)
+NotificationCenter* DOMWindowNotifications::webkitNotifications(DOMWindow& window)
{
- return DOMWindowNotifications::from(window)->webkitNotifications();
+ return DOMWindowNotifications::from(&window)->webkitNotifications();
}
-void DOMWindowNotifications::disconnectFrameForPageCache()
+void DOMWindowNotifications::disconnectFrameForDocumentSuspension()
{
- m_suspendedNotificationCenter = m_notificationCenter.release();
- DOMWindowProperty::disconnectFrameForPageCache();
+ m_suspendedNotificationCenter = WTFMove(m_notificationCenter);
+ DOMWindowProperty::disconnectFrameForDocumentSuspension();
}
-void DOMWindowNotifications::reconnectFrameFromPageCache(Frame* frame)
+void DOMWindowNotifications::reconnectFrameFromDocumentSuspension(Frame* frame)
{
- DOMWindowProperty::reconnectFrameFromPageCache(frame);
- m_notificationCenter = m_suspendedNotificationCenter.release();
+ DOMWindowProperty::reconnectFrameFromDocumentSuspension(frame);
+ m_notificationCenter = WTFMove(m_suspendedNotificationCenter);
}
void DOMWindowNotifications::willDestroyGlobalObjectInCachedFrame()
@@ -100,23 +101,24 @@ void DOMWindowNotifications::willDetachGlobalObjectFromFrame()
NotificationCenter* DOMWindowNotifications::webkitNotifications()
{
if (!m_window->isCurrentlyDisplayedInFrame())
- return 0;
+ return nullptr;
if (m_notificationCenter)
return m_notificationCenter.get();
- Document* document = m_window->document();
+ auto* document = m_window->document();
if (!document)
- return 0;
+ return nullptr;
- Page* page = document->page();
+ auto* page = document->page();
if (!page)
- return 0;
+ return nullptr;
- NotificationClient* provider = NotificationController::clientFrom(page);
- if (provider)
- m_notificationCenter = NotificationCenter::create(document, provider);
+ auto* provider = NotificationController::clientFrom(*page);
+ if (!provider)
+ return nullptr;
+ m_notificationCenter = NotificationCenter::create(*document, provider);
return m_notificationCenter.get();
}
diff --git a/Source/WebCore/Modules/notifications/DOMWindowNotifications.h b/Source/WebCore/Modules/notifications/DOMWindowNotifications.h
index c8e2206b5..7fd5ba671 100644
--- a/Source/WebCore/Modules/notifications/DOMWindowNotifications.h
+++ b/Source/WebCore/Modules/notifications/DOMWindowNotifications.h
@@ -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
@@ -24,8 +24,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DOMWindowNotifications_h
-#define DOMWindowNotifications_h
+#pragma once
#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
@@ -40,20 +39,19 @@ class NotificationCenter;
class DOMWindowNotifications : public Supplement<DOMWindow>, public DOMWindowProperty {
public:
+ explicit DOMWindowNotifications(DOMWindow*);
virtual ~DOMWindowNotifications();
- static NotificationCenter* webkitNotifications(DOMWindow*);
+ static NotificationCenter* webkitNotifications(DOMWindow&);
static DOMWindowNotifications* from(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 DOMWindowNotifications(DOMWindow*);
-
NotificationCenter* webkitNotifications();
static const char* supplementName();
@@ -65,5 +63,3 @@ private:
} // namespace WebCore
#endif // ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
-
-#endif // DOMWindowNotifications_h
diff --git a/Source/WebCore/Modules/notifications/DOMWindowNotifications.idl b/Source/WebCore/Modules/notifications/DOMWindowNotifications.idl
index 0e5039bcc..50ddfb2db 100644
--- a/Source/WebCore/Modules/notifications/DOMWindowNotifications.idl
+++ b/Source/WebCore/Modules/notifications/DOMWindowNotifications.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
diff --git a/Source/WebCore/Modules/notifications/Notification.cpp b/Source/WebCore/Modules/notifications/Notification.cpp
index 20cc0c309..1e02ce1cf 100644
--- a/Source/WebCore/Modules/notifications/Notification.cpp
+++ b/Source/WebCore/Modules/notifications/Notification.cpp
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2009 Google Inc. All rights reserved.
- * Copyright (C) 2009, 2011, 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2009, 2011, 2012, 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
@@ -37,60 +37,46 @@
#include "DOMWindow.h"
#include "DOMWindowNotifications.h"
-#include "Dictionary.h"
#include "Document.h"
-#include "ErrorEvent.h"
+#include "Event.h"
#include "EventNames.h"
+#include "ExceptionCode.h"
#include "NotificationCenter.h"
-#include "NotificationClient.h"
#include "NotificationController.h"
#include "NotificationPermissionCallback.h"
-#include "ResourceRequest.h"
-#include "ResourceResponse.h"
-#include "ThreadableLoader.h"
+#include "VoidCallback.h"
#include "WindowFocusAllowedIndicator.h"
-#include "WorkerGlobalScope.h"
namespace WebCore {
-Notification::Notification()
- : ActiveDOMObject(0)
-{
-}
-
#if ENABLE(LEGACY_NOTIFICATIONS)
-Notification::Notification(const String& title, const String& body, const String& iconURI, ScriptExecutionContext* context, ExceptionCode& ec, PassRefPtr<NotificationCenter> provider)
- : ActiveDOMObject(context)
+
+Notification::Notification(const String& title, const String& body, URL&& iconURL, ScriptExecutionContext& context, NotificationCenter& notificationCenter)
+ : ActiveDOMObject(&context)
+ , m_icon(WTFMove(iconURL))
, m_title(title)
, m_body(body)
- , m_state(Idle)
- , m_notificationCenter(provider)
+ , m_notificationCenter(&notificationCenter)
{
- if (m_notificationCenter->checkPermission() != NotificationClient::PermissionAllowed) {
- ec = SECURITY_ERR;
- return;
- }
-
- m_icon = iconURI.isEmpty() ? URL() : scriptExecutionContext()->completeURL(iconURI);
- if (!m_icon.isEmpty() && !m_icon.isValid()) {
- ec = SYNTAX_ERR;
- return;
- }
}
+
#endif
#if ENABLE(NOTIFICATIONS)
-Notification::Notification(ScriptExecutionContext& context, const String& title)
- : ActiveDOMObject(&context)
+
+Notification::Notification(Document& document, const String& title)
+ : ActiveDOMObject(&document)
, m_title(title)
, m_state(Idle)
- , m_taskTimer(adoptPtr(new Timer<Notification>(this, &Notification::taskTimerFired)))
+ , m_notificationCenter(DOMWindowNotifications::webkitNotifications(*document.domWindow()))
+ , m_taskTimer(std::make_unique<Timer>([this] () { show(); }))
{
- m_notificationCenter = DOMWindowNotifications::webkitNotifications(toDocument(context).domWindow());
-
+ // FIXME: Seems that m_notificationCenter can be null so should not be changed from RefPtr to Ref.
+ // But the rest of the code in this class isn't trying to handle that case.
ASSERT(m_notificationCenter->client());
m_taskTimer->startOneShot(0);
}
+
#endif
Notification::~Notification()
@@ -98,36 +84,57 @@ Notification::~Notification()
}
#if ENABLE(LEGACY_NOTIFICATIONS)
-PassRefPtr<Notification> Notification::create(const String& title, const String& body, const String& iconURI, ScriptExecutionContext* context, ExceptionCode& ec, PassRefPtr<NotificationCenter> provider)
+
+ExceptionOr<Ref<Notification>> Notification::create(const String& title, const String& body, const String& iconURL, ScriptExecutionContext& context, NotificationCenter& provider)
{
- RefPtr<Notification> notification(adoptRef(new Notification(title, body, iconURI, context, ec, provider)));
- notification->suspendIfNeeded();
- return notification.release();
+ if (provider.checkPermission() != NotificationClient::PermissionAllowed)
+ return Exception { SECURITY_ERR };
+
+ URL completedIconURL = iconURL.isEmpty() ? URL() : context.completeURL(iconURL);
+ if (!completedIconURL.isEmpty() && !completedIconURL.isValid())
+ return Exception { SYNTAX_ERR };
+
+ auto notification = adoptRef(*new Notification(title, body, WTFMove(completedIconURL), context, provider));
+ notification.get().suspendIfNeeded();
+ return WTFMove(notification);
}
+
#endif
#if ENABLE(NOTIFICATIONS)
-PassRefPtr<Notification> Notification::create(ScriptExecutionContext& context, const String& title, const Dictionary& options)
-{
- RefPtr<Notification> notification(adoptRef(new Notification(context, title)));
- String argument;
- if (options.get("body", argument))
- notification->setBody(argument);
- if (options.get("tag", argument))
- notification->setTag(argument);
- if (options.get("lang", argument))
- notification->setLang(argument);
- if (options.get("dir", argument))
- notification->setDir(argument);
- if (options.get("icon", argument)) {
- URL iconURI = argument.isEmpty() ? URL() : context.completeURL(argument);
- if (!iconURI.isEmpty() && iconURI.isValid())
- notification->setIconURL(iconURI);
+
+static String directionString(Notification::Direction direction)
+{
+ // FIXME: Storing this as a string is not the right way to do it.
+ // FIXME: Seems highly unlikely that this does the right thing for Auto.
+ switch (direction) {
+ case Notification::Direction::Auto:
+ return ASCIILiteral("auto");
+ case Notification::Direction::Ltr:
+ return ASCIILiteral("ltr");
+ case Notification::Direction::Rtl:
+ return ASCIILiteral("rtl");
}
+ ASSERT_NOT_REACHED();
+ return { };
+}
- notification->suspendIfNeeded();
- return notification.release();
+Ref<Notification> Notification::create(Document& context, const String& title, const Options& options)
+{
+ auto notification = adoptRef(*new Notification(context, title));
+ notification.get().m_body = options.body;
+ notification.get().m_tag = options.tag;
+ notification.get().m_lang = options.lang;
+ notification.get().m_direction = directionString(options.dir);
+ if (!options.icon.isEmpty()) {
+ auto iconURL = context.completeURL(options.icon);
+ if (iconURL.isValid())
+ notification.get().m_icon = iconURL;
+ }
+ notification.get().suspendIfNeeded();
+ return notification;
}
+
#endif
void Notification::show()
@@ -135,9 +142,10 @@ void Notification::show()
// prevent double-showing
if (m_state == Idle && m_notificationCenter->client()) {
#if ENABLE(NOTIFICATIONS)
- if (!toDocument(scriptExecutionContext())->page())
+ auto* page = downcast<Document>(*scriptExecutionContext()).page();
+ if (!page)
return;
- if (NotificationController::from(toDocument(scriptExecutionContext())->page())->client()->checkPermission(scriptExecutionContext()) != NotificationClient::PermissionAllowed) {
+ if (NotificationController::from(page)->client().checkPermission(scriptExecutionContext()) != NotificationClient::PermissionAllowed) {
dispatchErrorEvent();
return;
}
@@ -170,6 +178,17 @@ void Notification::contextDestroyed()
m_notificationCenter->client()->notificationObjectDestroyed(this);
}
+const char* Notification::activeDOMObjectName() const
+{
+ return "Notification";
+}
+
+bool Notification::canSuspendForDocumentSuspension() const
+{
+ // We can suspend if the Notification is not shown yet or after it is closed.
+ return m_state == Idle || m_state == Closed;
+}
+
void Notification::finalize()
{
if (m_state == Closed)
@@ -201,23 +220,13 @@ void Notification::dispatchErrorEvent()
}
#if ENABLE(NOTIFICATIONS)
-void Notification::taskTimerFired(Timer<Notification>& timer)
-{
- ASSERT(scriptExecutionContext()->isDocument());
- ASSERT_UNUSED(timer, &timer == m_taskTimer.get());
- show();
-}
-#endif
-
-#if ENABLE(NOTIFICATIONS)
-const String Notification::permission(ScriptExecutionContext* context)
+String Notification::permission(Document& document)
{
- ASSERT(toDocument(context)->page());
- return permissionString(NotificationController::from(toDocument(context)->page())->client()->checkPermission(context));
+ return permissionString(NotificationController::from(document.page())->client().checkPermission(&document));
}
-const String Notification::permissionString(NotificationClient::Permission permission)
+String Notification::permissionString(NotificationClient::Permission permission)
{
switch (permission) {
case NotificationClient::PermissionAllowed:
@@ -227,16 +236,15 @@ const String Notification::permissionString(NotificationClient::Permission permi
case NotificationClient::PermissionNotAllowed:
return ASCIILiteral("default");
}
-
ASSERT_NOT_REACHED();
- return String();
+ return { };
}
-void Notification::requestPermission(ScriptExecutionContext* context, PassRefPtr<NotificationPermissionCallback> callback)
+void Notification::requestPermission(Document& document, RefPtr<NotificationPermissionCallback>&& callback)
{
- ASSERT(toDocument(context)->page());
- NotificationController::from(toDocument(context)->page())->client()->requestPermission(context, callback);
+ NotificationController::from(document.page())->client().requestPermission(&document, WTFMove(callback));
}
+
#endif
} // namespace WebCore
diff --git a/Source/WebCore/Modules/notifications/Notification.h b/Source/WebCore/Modules/notifications/Notification.h
index 004f412d5..3b21bbc88 100644
--- a/Source/WebCore/Modules/notifications/Notification.h
+++ b/Source/WebCore/Modules/notifications/Notification.h
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2009 Google Inc. All rights reserved.
- * Copyright (C) 2009, 2011, 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2009, 2011, 2012, 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
@@ -29,140 +29,100 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef Notification_h
-#define Notification_h
+#pragma once
+
+#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
#include "ActiveDOMObject.h"
-#include "EventNames.h"
#include "EventTarget.h"
-#include "URL.h"
+#include "ExceptionOr.h"
#include "NotificationClient.h"
-#include "SharedBuffer.h"
-#include "TextDirection.h"
-#include "ThreadableLoaderClient.h"
-#include <wtf/OwnPtr.h>
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/RefPtr.h>
-#include <wtf/text/AtomicStringHash.h>
-
-#if ENABLE(NOTIFICATIONS)
#include "Timer.h"
-#endif
+#include "URL.h"
+#include "WritingMode.h"
-#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
namespace WebCore {
-class Dictionary;
+class Document;
class NotificationCenter;
class NotificationPermissionCallback;
-class ResourceError;
-class ResourceResponse;
-class ScriptExecutionContext;
-class ThreadableLoader;
-
-typedef int ExceptionCode;
class Notification final : public RefCounted<Notification>, public ActiveDOMObject, public EventTargetWithInlineData {
WTF_MAKE_FAST_ALLOCATED;
public:
- Notification();
#if ENABLE(LEGACY_NOTIFICATIONS)
- static PassRefPtr<Notification> create(const String& title, const String& body, const String& iconURI, ScriptExecutionContext*, ExceptionCode&, PassRefPtr<NotificationCenter> provider);
+ static ExceptionOr<Ref<Notification>> create(const String& title, const String& body, const String& iconURL, ScriptExecutionContext&, NotificationCenter&);
#endif
+
#if ENABLE(NOTIFICATIONS)
- static PassRefPtr<Notification> create(ScriptExecutionContext&, const String& title, const Dictionary& options);
+ enum class Direction { Auto, Ltr, Rtl };
+ struct Options {
+ Direction dir;
+ String lang;
+ String body;
+ String tag;
+ String icon;
+ };
+ static Ref<Notification> create(Document&, const String& title, const Options&);
#endif
virtual ~Notification();
void show();
-#if ENABLE(LEGACY_NOTIFICATIONS)
- void cancel() { close(); }
-#endif
void close();
- URL iconURL() const { return m_icon; }
- void setIconURL(const URL& url) { m_icon = url; }
+ const URL& iconURL() const { return m_icon; }
+ const String& title() const { return m_title; }
+ const String& body() const { return m_body; }
+ const String& lang() const { return m_lang; }
- String title() const { return m_title; }
- String body() const { return m_body; }
-
- String lang() const { return m_lang; }
- void setLang(const String& lang) { m_lang = lang; }
-
- String dir() const { return m_direction; }
+ const String& dir() const { return m_direction; }
void setDir(const String& dir) { m_direction = dir; }
-#if ENABLE(LEGACY_NOTIFICATIONS)
- String replaceId() const { return tag(); }
- void setReplaceId(const String& replaceId) { setTag(replaceId); }
-#endif
+ const String& replaceId() const { return m_tag; }
+ void setReplaceId(const String& replaceId) { m_tag = replaceId; }
- String tag() const { return m_tag; }
+ const String& tag() const { return m_tag; }
void setTag(const String& tag) { m_tag = tag; }
- TextDirection direction() const { return dir() == "rtl" ? RTL : LTR; }
+ TextDirection direction() const { return m_direction == "rtl" ? RTL : LTR; }
-#if ENABLE(LEGACY_NOTIFICATIONS)
- EventListener* ondisplay() { return getAttributeEventListener(eventNames().showEvent); }
- void setOndisplay(PassRefPtr<EventListener> listener) { setAttributeEventListener(eventNames().showEvent, listener); }
-#endif
- DEFINE_ATTRIBUTE_EVENT_LISTENER(show);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(error);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(close);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(click);
-
- void dispatchClickEvent();
- void dispatchCloseEvent();
- void dispatchErrorEvent();
- void dispatchShowEvent();
-
- using RefCounted<Notification>::ref;
- using RefCounted<Notification>::deref;
-
- // EventTarget interface
- virtual EventTargetInterface eventTargetInterface() const override { return NotificationEventTargetInterfaceType; }
- virtual ScriptExecutionContext* scriptExecutionContext() const override { return ActiveDOMObject::scriptExecutionContext(); }
+ WEBCORE_EXPORT void dispatchClickEvent();
+ WEBCORE_EXPORT void dispatchCloseEvent();
+ WEBCORE_EXPORT void dispatchErrorEvent();
+ WEBCORE_EXPORT void dispatchShowEvent();
- void stopLoadingIcon();
-
- // Deprecated. Use functions from NotificationCenter.
- void detachPresenter() { }
-
- void finalize();
+ WEBCORE_EXPORT void finalize();
#if ENABLE(NOTIFICATIONS)
- static const String permission(ScriptExecutionContext*);
- static const String permissionString(NotificationClient::Permission);
- static void requestPermission(ScriptExecutionContext*, PassRefPtr<NotificationPermissionCallback> = 0);
+ static String permission(Document&);
+ WEBCORE_EXPORT static String permissionString(NotificationClient::Permission);
+ static void requestPermission(Document&, RefPtr<NotificationPermissionCallback>&&);
#endif
+ ScriptExecutionContext* scriptExecutionContext() const final { return ActiveDOMObject::scriptExecutionContext(); }
+
+ using RefCounted::ref;
+ using RefCounted::deref;
+
private:
#if ENABLE(LEGACY_NOTIFICATIONS)
- Notification(const String& title, const String& body, const String& iconURI, ScriptExecutionContext*, ExceptionCode&, PassRefPtr<NotificationCenter>);
+ Notification(const String& title, const String& body, URL&& iconURL, ScriptExecutionContext&, NotificationCenter&);
#endif
+
#if ENABLE(NOTIFICATIONS)
- Notification(ScriptExecutionContext&, const String& title);
+ Notification(Document&, const String& title);
#endif
- void setBody(const String& body) { m_body = body; }
-
- // ActiveDOMObject interface
- virtual void contextDestroyed() override;
+ EventTargetInterface eventTargetInterface() const final { return NotificationEventTargetInterfaceType; }
- // EventTarget interface
- virtual void refEventTarget() override { ref(); }
- virtual void derefEventTarget() override { deref(); }
+ void contextDestroyed() final;
+ const char* activeDOMObjectName() const final;
+ bool canSuspendForDocumentSuspension() const final;
- void startLoadingIcon();
- void finishLoadingIcon();
-
-#if ENABLE(NOTIFICATIONS)
- void taskTimerFired(Timer<Notification>&);
-#endif
+ void refEventTarget() final { ref(); }
+ void derefEventTarget() final { deref(); }
- // Text notifications.
URL m_icon;
String m_title;
String m_body;
@@ -170,23 +130,16 @@ private:
String m_lang;
String m_tag;
- enum NotificationState {
- Idle = 0,
- Showing = 1,
- Closed = 2,
- };
-
- NotificationState m_state;
+ enum State { Idle, Showing, Closed };
+ State m_state { Idle };
RefPtr<NotificationCenter> m_notificationCenter;
#if ENABLE(NOTIFICATIONS)
- OwnPtr<Timer<Notification>> m_taskTimer;
+ std::unique_ptr<Timer> m_taskTimer;
#endif
};
} // namespace WebCore
#endif // ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
-
-#endif // Notifications_h
diff --git a/Source/WebCore/Modules/notifications/Notification.idl b/Source/WebCore/Modules/notifications/Notification.idl
index 6b0e14021..cb3fbee74 100644
--- a/Source/WebCore/Modules/notifications/Notification.idl
+++ b/Source/WebCore/Modules/notifications/Notification.idl
@@ -32,49 +32,37 @@
[
Conditional=NOTIFICATIONS|LEGACY_NOTIFICATIONS,
ActiveDOMObject,
- EventTarget,
+ ExportMacro=WEBCORE_EXPORT,
#if defined(ENABLE_NOTIFICATIONS) && ENABLE_NOTIFICATIONS
- Constructor(DOMString title, [Default=Undefined] optional Dictionary options),
- ConstructorCallWith=ScriptExecutionContext,
+ Constructor(DOMString title, optional NotificationOptions options),
+ ConstructorCallWith=Document,
#endif
-] interface Notification {
+] interface Notification : EventTarget {
void show();
-#if defined(ENABLE_LEGACY_NOTIFICATIONS) && ENABLE_LEGACY_NOTIFICATIONS
- void cancel();
-#endif
-#if defined(ENABLE_NOTIFICATIONS) && ENABLE_NOTIFICATIONS
- void close();
-#endif
+ [Conditional=LEGACY_NOTIFICATIONS, ImplementedAs=close] void cancel();
+ [Conditional=NOTIFICATIONS] void close();
+ [Conditional=NOTIFICATIONS, CallWith=Document] static readonly attribute DOMString permission;
+ [Conditional=NOTIFICATIONS, CallWith=Document] static void requestPermission(optional NotificationPermissionCallback? callback);
-#if defined(ENABLE_NOTIFICATIONS) && ENABLE_NOTIFICATIONS
- [CallWith=ScriptExecutionContext] static readonly attribute DOMString permission;
- [CallWith=ScriptExecutionContext] static void requestPermission(optional NotificationPermissionCallback callback);
-#endif
-
- attribute EventListener onshow;
-#if defined(ENABLE_LEGACY_NOTIFICATIONS) && ENABLE_LEGACY_NOTIFICATIONS
- attribute EventListener ondisplay;
-#endif
- attribute EventListener onerror;
- attribute EventListener onclose;
- attribute EventListener onclick;
+ attribute EventHandler onclick;
+ attribute EventHandler onclose;
+ [ImplementedAs=onshow] attribute EventHandler ondisplay;
+ attribute EventHandler onerror;
+ attribute EventHandler onshow;
-#if defined(ENABLE_LEGACY_NOTIFICATIONS) && ENABLE_LEGACY_NOTIFICATIONS
- attribute DOMString dir;
- attribute DOMString replaceId;
-#endif
-#if defined(ENABLE_NOTIFICATIONS) && ENABLE_NOTIFICATIONS
- attribute DOMString tag;
-#endif
+ [Conditional=LEGACY_NOTIFICATIONS] attribute DOMString dir;
+ [Conditional=LEGACY_NOTIFICATIONS] attribute DOMString replaceId;
- // EventTarget interface
- void addEventListener(DOMString type,
- EventListener listener,
- optional boolean useCapture);
- void removeEventListener(DOMString type,
- EventListener listener,
- optional boolean useCapture);
- [RaisesException] boolean dispatchEvent(Event evt);
+ [Conditional=NOTIFICATIONS] attribute DOMString tag;
};
+[Conditional=NOTIFICATIONS] enum NotificationDirection { "auto", "ltr", "rtl" };
+
+[Conditional=NOTIFICATIONS] dictionary NotificationOptions {
+ NotificationDirection dir = "auto";
+ DOMString lang = "";
+ DOMString body = "";
+ DOMString tag = "";
+ DOMString icon;
+};
diff --git a/Source/WebCore/Modules/notifications/NotificationCenter.cpp b/Source/WebCore/Modules/notifications/NotificationCenter.cpp
index 13f77de4a..a9298f752 100644
--- a/Source/WebCore/Modules/notifications/NotificationCenter.cpp
+++ b/Source/WebCore/Modules/notifications/NotificationCenter.cpp
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2009 Google Inc. All rights reserved.
- * Copyright (C) 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2012, 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
@@ -35,30 +35,40 @@
#include "NotificationCenter.h"
-#include "Document.h"
-#include "NotificationClient.h"
+#include "ExceptionCode.h"
+#include "Notification.h"
+#include "ScriptExecutionContext.h"
#include "SecurityOrigin.h"
-#include "WorkerGlobalScope.h"
+#include "VoidCallback.h"
namespace WebCore {
-PassRefPtr<NotificationCenter> NotificationCenter::create(ScriptExecutionContext* context, NotificationClient* client)
+Ref<NotificationCenter> NotificationCenter::create(ScriptExecutionContext& context, NotificationClient* client)
{
- RefPtr<NotificationCenter> notificationCenter(adoptRef(new NotificationCenter(context, client)));
+ auto notificationCenter = adoptRef(*new NotificationCenter(context, client));
notificationCenter->suspendIfNeeded();
- return notificationCenter.release();
+ return notificationCenter;
}
-NotificationCenter::NotificationCenter(ScriptExecutionContext* context, NotificationClient* client)
- : ActiveDOMObject(context)
+NotificationCenter::NotificationCenter(ScriptExecutionContext& context, NotificationClient* client)
+ : ActiveDOMObject(&context)
, m_client(client)
+ , m_timer([this]() { timerFired(); })
{
}
#if ENABLE(LEGACY_NOTIFICATIONS)
+
+ExceptionOr<Ref<Notification>> NotificationCenter::createNotification(const String& iconURI, const String& title, const String& body)
+{
+ if (!m_client || !scriptExecutionContext())
+ return Exception { INVALID_STATE_ERR };
+ return Notification::create(title, body, iconURI, *scriptExecutionContext(), *this);
+}
+
int NotificationCenter::checkPermission()
{
- if (!client() || !scriptExecutionContext())
+ if (!m_client || !scriptExecutionContext())
return NotificationClient::PermissionDenied;
switch (scriptExecutionContext()->securityOrigin()->canShowNotifications()) {
@@ -73,67 +83,74 @@ int NotificationCenter::checkPermission()
ASSERT_NOT_REACHED();
return m_client->checkPermission(scriptExecutionContext());
}
-#endif
-#if ENABLE(LEGACY_NOTIFICATIONS)
-void NotificationCenter::requestPermission(PassRefPtr<VoidCallback> callback)
+void NotificationCenter::requestPermission(RefPtr<VoidCallback>&& callback)
{
- if (!client() || !scriptExecutionContext())
+ if (!m_client || !scriptExecutionContext())
return;
switch (scriptExecutionContext()->securityOrigin()->canShowNotifications()) {
case SecurityOrigin::AlwaysAllow:
- case SecurityOrigin::AlwaysDeny: {
- m_callbacks.add(NotificationRequestCallback::createAndStartTimer(this, callback));
+ case SecurityOrigin::AlwaysDeny:
+ if (m_callbacks.isEmpty()) {
+ ref(); // Balanced by the derefs in NotificationCenter::stop and NotificationCenter::timerFired.
+ m_timer.startOneShot(0);
+ }
+ m_callbacks.append([callback]() {
+ if (callback)
+ callback->handleEvent();
+ });
return;
- }
case SecurityOrigin::Ask:
- return m_client->requestPermission(scriptExecutionContext(), callback);
+ m_client->requestPermission(scriptExecutionContext(), WTFMove(callback));
+ return;
}
ASSERT_NOT_REACHED();
- m_client->requestPermission(scriptExecutionContext(), callback);
+ m_client->requestPermission(scriptExecutionContext(), WTFMove(callback));
}
+
#endif
void NotificationCenter::stop()
{
if (!m_client)
return;
- m_client->cancelRequestsForPermission(scriptExecutionContext());
- m_client->clearNotifications(scriptExecutionContext());
- m_client = 0;
-}
-void NotificationCenter::requestTimedOut(NotificationCenter::NotificationRequestCallback* request)
-{
- m_callbacks.remove(request);
-}
+ // Clear m_client immediately to guarantee reentrant calls to NotificationCenter do nothing.
+ // Also protect |this| so it's not indirectly destroyed under us by work done by the client.
+ auto& client = *std::exchange(m_client, nullptr);
+ Ref<NotificationCenter> protectedThis(*this);
-PassRefPtr<NotificationCenter::NotificationRequestCallback> NotificationCenter::NotificationRequestCallback::createAndStartTimer(NotificationCenter* center, PassRefPtr<VoidCallback> callback)
-{
- RefPtr<NotificationCenter::NotificationRequestCallback> requestCallback = adoptRef(new NotificationCenter::NotificationRequestCallback(center, callback));
- requestCallback->startTimer();
- return requestCallback.release();
+ if (!m_callbacks.isEmpty())
+ deref(); // Balanced by the ref in NotificationCenter::requestPermission.
+
+ m_timer.stop();
+ m_callbacks.clear();
+
+ client.cancelRequestsForPermission(scriptExecutionContext());
+ client.clearNotifications(scriptExecutionContext());
}
-NotificationCenter::NotificationRequestCallback::NotificationRequestCallback(NotificationCenter* center, PassRefPtr<VoidCallback> callback)
- : m_notificationCenter(center)
- , m_timer(this, &NotificationCenter::NotificationRequestCallback::timerFired)
- , m_callback(callback)
+const char* NotificationCenter::activeDOMObjectName() const
{
+ return "NotificationCenter";
}
-void NotificationCenter::NotificationRequestCallback::startTimer()
+bool NotificationCenter::canSuspendForDocumentSuspension() const
{
- m_timer.startOneShot(0);
+ // We don't need to worry about Notifications because those are ActiveDOMObject too.
+ // The NotificationCenter can safely be suspended if there are no pending permission requests.
+ return m_callbacks.isEmpty() && (!m_client || !m_client->hasPendingPermissionRequests(scriptExecutionContext()));
}
-void NotificationCenter::NotificationRequestCallback::timerFired(Timer<NotificationCenter::NotificationRequestCallback>&)
+void NotificationCenter::timerFired()
{
- if (m_callback)
- m_callback->handleEvent();
- m_notificationCenter->requestTimedOut(this);
+ ASSERT(m_client);
+ auto callbacks = WTFMove(m_callbacks);
+ for (auto& callback : callbacks)
+ callback();
+ deref(); // Balanced by the ref in NotificationCenter::requestPermission.
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/notifications/NotificationCenter.h b/Source/WebCore/Modules/notifications/NotificationCenter.h
index d27b45e4f..4646e182e 100644
--- a/Source/WebCore/Modules/notifications/NotificationCenter.h
+++ b/Source/WebCore/Modules/notifications/NotificationCenter.h
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2009 Google Inc. All rights reserved.
- * Copyright (C) 2011, 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2012, 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
@@ -29,75 +29,50 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef NotificationCenter_h
-#define NotificationCenter_h
+#pragma once
-#include "ExceptionCode.h"
-#include "Notification.h"
-#include "ScriptExecutionContext.h"
+#include "ActiveDOMObject.h"
+#include "ExceptionOr.h"
#include "Timer.h"
-#include "VoidCallback.h"
-#include <wtf/OwnPtr.h>
-#include <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>
-#include <wtf/RefPtr.h>
#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
namespace WebCore {
+class Notification;
class NotificationClient;
class VoidCallback;
-class NotificationCenter : public RefCounted<NotificationCenter>, public ActiveDOMObject {
+class NotificationCenter final : public RefCounted<NotificationCenter>, private ActiveDOMObject {
public:
- static PassRefPtr<NotificationCenter> create(ScriptExecutionContext*, NotificationClient*);
+ static Ref<NotificationCenter> create(ScriptExecutionContext&, NotificationClient*);
#if ENABLE(LEGACY_NOTIFICATIONS)
- PassRefPtr<Notification> createNotification(const String& iconURI, const String& title, const String& body, ExceptionCode& ec)
- {
- if (!client()) {
- ec = INVALID_STATE_ERR;
- return 0;
- }
- return Notification::create(title, body, iconURI, scriptExecutionContext(), ec, this);
- }
-#endif
-
- NotificationClient* client() const { return m_client; }
+ ExceptionOr<Ref<Notification>> createNotification(const String& iconURL, const String& title, const String& body);
-#if ENABLE(LEGACY_NOTIFICATIONS)
int checkPermission();
- void requestPermission(PassRefPtr<VoidCallback> = 0);
+ void requestPermission(RefPtr<VoidCallback>&&);
#endif
-private:
- NotificationCenter(ScriptExecutionContext*, NotificationClient*);
+ NotificationClient* client() const { return m_client; }
- // ActiveDOMObject
- virtual void stop() override;
+ using ActiveDOMObject::hasPendingActivity;
- class NotificationRequestCallback : public RefCounted<NotificationRequestCallback> {
- public:
- static PassRefPtr<NotificationRequestCallback> createAndStartTimer(NotificationCenter*, PassRefPtr<VoidCallback>);
- void startTimer();
- void timerFired(Timer<NotificationRequestCallback>&);
- private:
- NotificationRequestCallback(NotificationCenter*, PassRefPtr<VoidCallback>);
+private:
+ NotificationCenter(ScriptExecutionContext&, NotificationClient*);
- RefPtr<NotificationCenter> m_notificationCenter;
- Timer<NotificationRequestCallback> m_timer;
- RefPtr<VoidCallback> m_callback;
- };
+ void stop() final;
+ const char* activeDOMObjectName() const final;
+ bool canSuspendForDocumentSuspension() const final;
- void requestTimedOut(NotificationRequestCallback*);
+ void timerFired();
NotificationClient* m_client;
- HashSet<RefPtr<NotificationRequestCallback>> m_callbacks;
+ Vector<std::function<void ()>> m_callbacks;
+ Timer m_timer;
};
} // namespace WebCore
#endif // ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
-
-#endif // NotificationCenter_h
diff --git a/Source/WebCore/Modules/notifications/NotificationCenter.idl b/Source/WebCore/Modules/notifications/NotificationCenter.idl
index 54343cfa9..07a380fc4 100644
--- a/Source/WebCore/Modules/notifications/NotificationCenter.idl
+++ b/Source/WebCore/Modules/notifications/NotificationCenter.idl
@@ -30,13 +30,12 @@
*/
[
- NoInterfaceObject,
- Conditional=LEGACY_NOTIFICATIONS,
ActiveDOMObject,
+ Conditional=LEGACY_NOTIFICATIONS,
+ NoInterfaceObject,
] interface NotificationCenter {
- [RaisesException] Notification createNotification(DOMString iconUrl, DOMString title, DOMString body);
+ [MayThrowException] Notification createNotification(DOMString iconUrl, DOMString title, DOMString body);
- int checkPermission();
- void requestPermission(optional VoidCallback callback);
+ long checkPermission();
+ void requestPermission(optional VoidCallback? callback);
};
-
diff --git a/Source/WebCore/Modules/notifications/NotificationClient.h b/Source/WebCore/Modules/notifications/NotificationClient.h
index 22815bf7a..b03dfd92a 100644
--- a/Source/WebCore/Modules/notifications/NotificationClient.h
+++ b/Source/WebCore/Modules/notifications/NotificationClient.h
@@ -29,23 +29,19 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef NotificationClient_h
-#define NotificationClient_h
+#pragma once
-#include "NotificationPermissionCallback.h"
-#include "VoidCallback.h"
-#include <wtf/PassRefPtr.h>
+#include <wtf/Forward.h>
namespace WebCore {
-class Document;
-class URL;
class Notification;
+class NotificationPermissionCallback;
class Page;
class ScriptExecutionContext;
+class VoidCallback;
class NotificationClient {
-
public:
enum Permission {
PermissionAllowed, // User has allowed notifications
@@ -71,16 +67,18 @@ public:
// Informs the presenter the controller attached to the page has been destroyed.
virtual void notificationControllerDestroyed() = 0;
-#if ENABLE(LEGACY_NOTIFICATIONS)
// Requests user permission to show desktop notifications from a particular
// script context. The callback parameter should be run when the user has
// made a decision.
- virtual void requestPermission(ScriptExecutionContext*, PassRefPtr<VoidCallback>) = 0;
+#if ENABLE(LEGACY_NOTIFICATIONS)
+ virtual void requestPermission(ScriptExecutionContext*, RefPtr<VoidCallback>&&) = 0;
#endif
#if ENABLE(NOTIFICATIONS)
- virtual void requestPermission(ScriptExecutionContext*, PassRefPtr<NotificationPermissionCallback>) = 0;
+ virtual void requestPermission(ScriptExecutionContext*, RefPtr<NotificationPermissionCallback>&&) = 0;
#endif
+ virtual bool hasPendingPermissionRequests(ScriptExecutionContext*) const = 0;
+
// Cancel all outstanding requests for the ScriptExecutionContext
virtual void cancelRequestsForPermission(ScriptExecutionContext*) = 0;
@@ -91,8 +89,6 @@ protected:
virtual ~NotificationClient() { }
};
-void provideNotification(Page*, NotificationClient*);
+WEBCORE_EXPORT void provideNotification(Page*, NotificationClient*);
} // namespace WebCore
-
-#endif // NotificationClient_h
diff --git a/Source/WebCore/Modules/notifications/NotificationController.cpp b/Source/WebCore/Modules/notifications/NotificationController.cpp
index 0a90889d4..fc08b276f 100644
--- a/Source/WebCore/Modules/notifications/NotificationController.cpp
+++ b/Source/WebCore/Modules/notifications/NotificationController.cpp
@@ -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
@@ -29,31 +29,26 @@
#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
#include "NotificationClient.h"
-#include <wtf/PassOwnPtr.h>
namespace WebCore {
NotificationController::NotificationController(NotificationClient* client)
- : m_client(client)
+ : m_client(*client)
{
+ ASSERT(client);
}
NotificationController::~NotificationController()
{
- if (m_client)
- m_client->notificationControllerDestroyed();
+ m_client.notificationControllerDestroyed();
}
-PassOwnPtr<NotificationController> NotificationController::create(NotificationClient* client)
+NotificationClient* NotificationController::clientFrom(Page& page)
{
- return adoptPtr(new NotificationController(client));
-}
-
-NotificationClient* NotificationController::clientFrom(Page* page)
-{
- if (NotificationController* controller = NotificationController::from(page))
- return controller->client();
- return 0;
+ auto* controller = NotificationController::from(&page);
+ if (!controller)
+ return nullptr;
+ return &controller->client();
}
const char* NotificationController::supplementName()
@@ -63,7 +58,7 @@ const char* NotificationController::supplementName()
void provideNotification(Page* page, NotificationClient* client)
{
- NotificationController::provideTo(page, NotificationController::supplementName(), NotificationController::create(client));
+ NotificationController::provideTo(page, NotificationController::supplementName(), std::make_unique<NotificationController>(client));
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/notifications/NotificationController.h b/Source/WebCore/Modules/notifications/NotificationController.h
index 751d4779e..25fca7422 100644
--- a/Source/WebCore/Modules/notifications/NotificationController.h
+++ b/Source/WebCore/Modules/notifications/NotificationController.h
@@ -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
@@ -23,39 +23,33 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef NotificationController_h
-#define NotificationController_h
+#pragma once
#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
#include "Page.h"
#include <wtf/Forward.h>
-#include <wtf/Noncopyable.h>
namespace WebCore {
class NotificationClient;
class NotificationController : public Supplement<Page> {
- WTF_MAKE_NONCOPYABLE(NotificationController);
+ WTF_MAKE_FAST_ALLOCATED;
public:
+ explicit NotificationController(NotificationClient*);
~NotificationController();
- static PassOwnPtr<NotificationController> create(NotificationClient*);
static const char* supplementName();
static NotificationController* from(Page* page) { return static_cast<NotificationController*>(Supplement<Page>::from(page, supplementName())); }
- static NotificationClient* clientFrom(Page*);
+ WEBCORE_EXPORT static NotificationClient* clientFrom(Page&);
- NotificationClient* client() { return m_client; }
-
-private:
- explicit NotificationController(NotificationClient*);
+ NotificationClient& client() { return m_client; }
- NotificationClient* m_client;
+private:
+ NotificationClient& m_client;
};
} // namespace WebCore
#endif // ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
-
-#endif // NotificationController_h
diff --git a/Source/WebCore/Modules/notifications/NotificationPermissionCallback.h b/Source/WebCore/Modules/notifications/NotificationPermissionCallback.h
index 84e732a7f..6fd86df64 100644
--- a/Source/WebCore/Modules/notifications/NotificationPermissionCallback.h
+++ b/Source/WebCore/Modules/notifications/NotificationPermissionCallback.h
@@ -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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef NotificationPermissionCallback_h
-#define NotificationPermissionCallback_h
+#pragma once
#if ENABLE(NOTIFICATIONS)
@@ -42,5 +41,3 @@ public:
} // namespace WebCore
#endif // ENABLE(NOTIFICATIONS)
-
-#endif // NotificationPermissionCallback_h
diff --git a/Source/WebCore/Modules/notifications/NotificationPermissionCallback.idl b/Source/WebCore/Modules/notifications/NotificationPermissionCallback.idl
index ca4879435..6d3d984f5 100644
--- a/Source/WebCore/Modules/notifications/NotificationPermissionCallback.idl
+++ b/Source/WebCore/Modules/notifications/NotificationPermissionCallback.idl
@@ -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,7 +25,5 @@
[
Conditional=NOTIFICATIONS,
-] callback interface NotificationPermissionCallback {
- boolean handleEvent(DOMString permission);
-};
+] callback NotificationPermissionCallback = void (DOMString permission);
diff --git a/Source/WebCore/Modules/notifications/WorkerGlobalScopeNotifications.cpp b/Source/WebCore/Modules/notifications/WorkerGlobalScopeNotifications.cpp
index acf48f0f7..022163d4b 100644
--- a/Source/WebCore/Modules/notifications/WorkerGlobalScopeNotifications.cpp
+++ b/Source/WebCore/Modules/notifications/WorkerGlobalScopeNotifications.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
@@ -36,8 +36,8 @@
namespace WebCore {
-WorkerGlobalScopeNotifications::WorkerGlobalScopeNotifications(WorkerGlobalScope* context)
- : m_context(context)
+WorkerGlobalScopeNotifications::WorkerGlobalScopeNotifications(WorkerGlobalScope& scope)
+ : m_context(&scope)
{
}
@@ -50,25 +50,29 @@ const char* WorkerGlobalScopeNotifications::supplementName()
return "WorkerGlobalScopeNotifications";
}
-WorkerGlobalScopeNotifications* WorkerGlobalScopeNotifications::from(WorkerGlobalScope* context)
+WorkerGlobalScopeNotifications* WorkerGlobalScopeNotifications::from(WorkerGlobalScope& scope)
{
- WorkerGlobalScopeNotifications* supplement = static_cast<WorkerGlobalScopeNotifications*>(Supplement<ScriptExecutionContext>::from(context, supplementName()));
+ WorkerGlobalScopeNotifications* supplement = static_cast<WorkerGlobalScopeNotifications*>(Supplement<WorkerGlobalScope>::from(&scope, supplementName()));
if (!supplement) {
- supplement = new WorkerGlobalScopeNotifications(context);
- Supplement<ScriptExecutionContext>::provideTo(context, supplementName(), adoptPtr(supplement));
+ auto newSupplement = std::make_unique<WorkerGlobalScopeNotifications>(scope);
+ supplement = newSupplement.get();
+ provideTo(&scope, supplementName(), WTFMove(newSupplement));
}
return supplement;
}
-NotificationCenter* WorkerGlobalScopeNotifications::webkitNotifications(WorkerGlobalScope* context)
+NotificationCenter* WorkerGlobalScopeNotifications::webkitNotifications(WorkerGlobalScope& scope)
{
- return WorkerGlobalScopeNotifications::from(context)->webkitNotifications();
+ return WorkerGlobalScopeNotifications::from(scope)->webkitNotifications();
}
NotificationCenter* WorkerGlobalScopeNotifications::webkitNotifications()
{
+ // FIXME: As of this writing, this always passes nullptr for the client.
+ // If it wasn't for that, the notification center create function could be taking a reference.
+ // How is it useful to create a notification center with no client?
if (!m_notificationCenter)
- m_notificationCenter = NotificationCenter::create(m_context, m_context->thread()->getNotificationClient());
+ m_notificationCenter = NotificationCenter::create(*m_context, m_context->thread().getNotificationClient());
return m_notificationCenter.get();
}
diff --git a/Source/WebCore/Modules/notifications/WorkerGlobalScopeNotifications.h b/Source/WebCore/Modules/notifications/WorkerGlobalScopeNotifications.h
index 455c15133..6760e6e11 100644
--- a/Source/WebCore/Modules/notifications/WorkerGlobalScopeNotifications.h
+++ b/Source/WebCore/Modules/notifications/WorkerGlobalScopeNotifications.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,8 +24,7 @@
*
*/
-#ifndef WorkerGlobalScopeNotifications_h
-#define WorkerGlobalScopeNotifications_h
+#pragma once
#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
@@ -34,19 +33,17 @@
namespace WebCore {
class NotificationCenter;
-class ScriptExecutionContext;
class WorkerGlobalScope;
-class WorkerGlobalScopeNotifications : public Supplement<ScriptExecutionContext> {
+class WorkerGlobalScopeNotifications : public Supplement<WorkerGlobalScope> {
public:
+ explicit WorkerGlobalScopeNotifications(WorkerGlobalScope&);
virtual ~WorkerGlobalScopeNotifications();
- static NotificationCenter* webkitNotifications(WorkerGlobalScope*);
- static WorkerGlobalScopeNotifications* from(WorkerGlobalScope*);
+ static NotificationCenter* webkitNotifications(WorkerGlobalScope&);
+ static WorkerGlobalScopeNotifications* from(WorkerGlobalScope&);
private:
- explicit WorkerGlobalScopeNotifications(WorkerGlobalScope*);
-
NotificationCenter* webkitNotifications();
static const char* supplementName();
@@ -57,5 +54,3 @@ private:
} // namespace WebCore
#endif // ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
-
-#endif // WorkerGlobalScopeNotifications_h
diff --git a/Source/WebCore/Modules/notifications/WorkerGlobalScopeNotifications.idl b/Source/WebCore/Modules/notifications/WorkerGlobalScopeNotifications.idl
index 0f721afef..4eb249d56 100644
--- a/Source/WebCore/Modules/notifications/WorkerGlobalScopeNotifications.idl
+++ b/Source/WebCore/Modules/notifications/WorkerGlobalScopeNotifications.idl
@@ -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
diff --git a/Source/WebCore/Modules/plugins/PluginReplacement.h b/Source/WebCore/Modules/plugins/PluginReplacement.h
index b08f4e476..b54c66b0f 100644
--- a/Source/WebCore/Modules/plugins/PluginReplacement.h
+++ b/Source/WebCore/Modules/plugins/PluginReplacement.h
@@ -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
@@ -23,11 +23,9 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef PluginReplacement_h
-#define PluginReplacement_h
+#pragma once
#include "RenderPtr.h"
-#include <wtf/RefCounted.h>
#include <wtf/text/WTFString.h>
namespace JSC {
@@ -39,32 +37,36 @@ namespace WebCore {
class HTMLPlugInElement;
class RenderElement;
class RenderStyle;
+class RenderTreePosition;
+class Settings;
class ShadowRoot;
+class URL;
class PluginReplacement : public RefCounted<PluginReplacement> {
public:
virtual ~PluginReplacement() { }
- virtual bool installReplacement(ShadowRoot*) = 0;
- virtual JSC::JSObject* scriptObject() { return 0; }
+ virtual bool installReplacement(ShadowRoot&) = 0;
+ virtual JSC::JSObject* scriptObject() { return nullptr; }
virtual bool willCreateRenderer() { return false; }
- virtual RenderPtr<RenderElement> createElementRenderer(HTMLPlugInElement&, PassRef<RenderStyle>) = 0;
-
-protected:
- PluginReplacement() { }
+ virtual RenderPtr<RenderElement> createElementRenderer(HTMLPlugInElement&, RenderStyle&&, const RenderTreePosition&) = 0;
};
-typedef PassRefPtr<PluginReplacement> (*CreatePluginReplacement)(HTMLPlugInElement&, const Vector<String>& paramNames, const Vector<String>& paramValues);
+typedef Ref<PluginReplacement> (*CreatePluginReplacement)(HTMLPlugInElement&, const Vector<String>& paramNames, const Vector<String>& paramValues);
typedef bool (*PluginReplacementSupportsType)(const String&);
typedef bool (*PluginReplacementSupportsFileExtension)(const String&);
+typedef bool (*PluginReplacementSupportsURL)(const URL&);
+typedef bool (*PluginReplacementEnabledForSettings)(const Settings&);
class ReplacementPlugin {
public:
- ReplacementPlugin(CreatePluginReplacement constructor, PluginReplacementSupportsType supportsType, PluginReplacementSupportsFileExtension supportsFileExtension)
+ ReplacementPlugin(CreatePluginReplacement constructor, PluginReplacementSupportsType supportsType, PluginReplacementSupportsFileExtension supportsFileExtension, PluginReplacementSupportsURL supportsURL, PluginReplacementEnabledForSettings isEnabledBySettings)
: m_constructor(constructor)
, m_supportsType(supportsType)
, m_supportsFileExtension(supportsFileExtension)
+ , m_supportsURL(supportsURL)
+ , m_isEnabledBySettings(isEnabledBySettings)
{
}
@@ -72,21 +74,25 @@ public:
: m_constructor(other.m_constructor)
, m_supportsType(other.m_supportsType)
, m_supportsFileExtension(other.m_supportsFileExtension)
+ , m_supportsURL(other.m_supportsURL)
+ , m_isEnabledBySettings(other.m_isEnabledBySettings)
{
}
- PassRefPtr<PluginReplacement> create(HTMLPlugInElement& element, const Vector<String>& paramNames, const Vector<String>& paramValues) const { return m_constructor(element, paramNames, paramValues); }
+ Ref<PluginReplacement> create(HTMLPlugInElement& element, const Vector<String>& paramNames, const Vector<String>& paramValues) const { return m_constructor(element, paramNames, paramValues); }
bool supportsType(const String& mimeType) const { return m_supportsType(mimeType); }
bool supportsFileExtension(const String& extension) const { return m_supportsFileExtension(extension); }
+ bool supportsURL(const URL& url) const { return m_supportsURL(url); }
+ bool isEnabledBySettings(const Settings& settings) const { return m_isEnabledBySettings(settings); };
private:
CreatePluginReplacement m_constructor;
PluginReplacementSupportsType m_supportsType;
PluginReplacementSupportsFileExtension m_supportsFileExtension;
+ PluginReplacementSupportsURL m_supportsURL;
+ PluginReplacementEnabledForSettings m_isEnabledBySettings;
};
typedef void (*PluginReplacementRegistrar)(const ReplacementPlugin&);
}
-
-#endif
diff --git a/Source/WebCore/Modules/plugins/QuickTimePluginReplacement.css b/Source/WebCore/Modules/plugins/QuickTimePluginReplacement.css
new file mode 100644
index 000000000..9219747e3
--- /dev/null
+++ b/Source/WebCore/Modules/plugins/QuickTimePluginReplacement.css
@@ -0,0 +1,33 @@
+/*
+ * 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,
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+embed::-webkit-plugin-replacement,
+object::-webkit-plugin-replacement
+{
+ position: relative;
+ display: inline-block;
+ width: 100%;
+ height: 100%;
+}
+
diff --git a/Source/WebCore/Modules/plugins/QuickTimePluginReplacement.h b/Source/WebCore/Modules/plugins/QuickTimePluginReplacement.h
new file mode 100644
index 000000000..d574aa0d3
--- /dev/null
+++ b/Source/WebCore/Modules/plugins/QuickTimePluginReplacement.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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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
+
+#include "PluginReplacement.h"
+
+namespace WebCore {
+
+class DOMWrapperWorld;
+class HTMLVideoElement;
+
+class QuickTimePluginReplacement final : public PluginReplacement {
+public:
+ static void registerPluginReplacement(PluginReplacementRegistrar);
+
+ virtual ~QuickTimePluginReplacement();
+
+ unsigned long long movieSize() const;
+ void postEvent(const String&);
+
+ HTMLVideoElement* parentElement() { return m_mediaElement.get(); }
+
+private:
+ QuickTimePluginReplacement(HTMLPlugInElement&, const Vector<String>& paramNames, const Vector<String>& paramValues);
+ static Ref<PluginReplacement> create(HTMLPlugInElement&, const Vector<String>& paramNames, const Vector<String>& paramValues);
+ static bool supportsMimeType(const String&);
+ static bool supportsFileExtension(const String&);
+ static bool supportsURL(const URL&) { return true; }
+ static bool isEnabledBySettings(const Settings&);
+
+ bool installReplacement(ShadowRoot&) final;
+ JSC::JSObject* scriptObject() final { return m_scriptObject; }
+
+ bool willCreateRenderer() final { return m_mediaElement; }
+ RenderPtr<RenderElement> createElementRenderer(HTMLPlugInElement&, RenderStyle&&, const RenderTreePosition&) final;
+
+ bool ensureReplacementScriptInjected();
+ DOMWrapperWorld& isolatedWorld();
+
+ HTMLPlugInElement* m_parentElement;
+ RefPtr<HTMLVideoElement> m_mediaElement;
+ const Vector<String> m_names;
+ const Vector<String> m_values;
+ JSC::JSObject* m_scriptObject; // FIXME: Why is it safe to have this pointer here? What keeps it alive during GC?
+};
+
+}
diff --git a/Source/WebCore/Modules/mediastream/AllAudioCapabilities.idl b/Source/WebCore/Modules/plugins/QuickTimePluginReplacement.idl
index dfb3a6e0c..983d95f90 100644
--- a/Source/WebCore/Modules/mediastream/AllAudioCapabilities.idl
+++ b/Source/WebCore/Modules/plugins/QuickTimePluginReplacement.idl
@@ -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,9 +25,11 @@
[
NoInterfaceObject,
- Conditional=MEDIA_STREAM,
JSGenerateToJSObject,
-] interface AllAudioCapabilities : MediaStreamCapabilities {
- readonly attribute DOMString[] sourceId;
- readonly attribute CapabilityRange volume;
+] interface QuickTimePluginReplacement {
+ readonly attribute unsigned long long movieSize;
+ [CustomGetter] readonly attribute any timedMetaData;
+ [CustomGetter] readonly attribute any accessLog;
+ [CustomGetter] readonly attribute any errorLog;
+ void postEvent(DOMString eventName);
};
diff --git a/Source/WebCore/Modules/plugins/QuickTimePluginReplacement.js b/Source/WebCore/Modules/plugins/QuickTimePluginReplacement.js
new file mode 100644
index 000000000..e59f835b0
--- /dev/null
+++ b/Source/WebCore/Modules/plugins/QuickTimePluginReplacement.js
@@ -0,0 +1,350 @@
+
+function createPluginReplacement(root, parent, host, attributeNames, attributeValues)
+{
+ return new Replacement(root, parent, host, attributeNames, attributeValues);
+};
+
+function Replacement(root, parent, host, attributeNames, attributeValues)
+{
+ this.root = root;
+ this.parent = parent;
+ this.host = host;
+ this.listeners = {};
+ this.scriptObject = {};
+
+ this.autoExitFullScreen = true;
+ this.postEvents = false;
+ this.height = 0;
+ this.width = 0;
+ this.src = "";
+ this.autohref = false;
+ this.href = "";
+ this.qtsrc = "";
+ this.baseUrl = "";
+ this.target = "";
+
+ this.createVideoElement(attributeNames, attributeValues);
+ this.createScriptInterface();
+};
+
+Replacement.prototype = {
+
+ HandledVideoEvents: {
+ loadstart: 'handleLoadStart',
+ error: 'handleError',
+ loadedmetadata: 'handleLoadedMetaData',
+ canplay: 'qt_canplay',
+ canplaythrough: 'qt_canplaythrough',
+ play: 'qt_play',
+ pause: 'qt_pause',
+ ended: 'handleEnded',
+ webkitfullscreenchange: 'handleFullscreenChange',
+ },
+
+ AttributeMap: {
+ autoexitfullscreen: 'autoExitFullScreen',
+ postdomevents: 'postEvents',
+ height: 'height',
+ width: 'width',
+ qtsrc: 'qtsrc',
+ src: 'src',
+ airplay: 'x-webkit-airplay=',
+ href: 'href',
+ target: 'target',
+ },
+
+ MethodMap: {
+ SetURL : 'setURL',
+ GetURL : 'url',
+ Play : 'play',
+ Stop : 'pause',
+ GetRate : 'rate',
+ SetRate : 'setRate',
+ IsFullScreen : 'isFullScreen',
+ ExitFullScreen : 'exitFullScreen',
+ GetPluginStatus : 'pluginStatus',
+ GetTime : 'currentTime',
+ SetTime : 'setCurrentTime',
+ SeekToDate : 'seekToDate',
+ GetDate : 'date',
+ GetDuration : 'duration',
+ GetTimeScale : 'timeScale',
+ GetMaxTimeLoaded : 'maxTimeLoaded',
+ GetMaxBytesLoaded : 'maxBytesLoaded',
+ GetMovieSize : 'movieSize',
+ GetTimedMetadataUpdates : 'timedMetadataUpdates',
+ GetAccessLog : 'accessLog',
+ GetErrorLog : 'errorLog',
+ },
+
+ TimeScale: 30000,
+
+ createVideoElement: function(attributeNames, attributeValues)
+ {
+ var video = this.video = document.createElement('video');
+
+ for (name in this.HandledVideoEvents)
+ video.addEventListener(name, this, false);
+
+ for (i = 0; i < attributeNames.length; i++) {
+ var property = this.AttributeMap[attributeNames[i]];
+ if (this[property] != undefined)
+ this[property] = attributeValues[i];
+ }
+
+ video.setAttribute('pseudo', '-webkit-plugin-replacement');
+ video.setAttribute('controls', 'controls');
+ this.setStatus('Waiting');
+
+ var src = this.resolveRelativeToUrl(this.src, "");
+ this.baseUrl = src;
+
+ // The 'qtsrc' attribute is used when a page author wanted to always use the QuickTime plug-in
+ // to load a media type even if another plug-in was registered for that type. It tells the
+ // plug-in to ignore the 'src' url, and to load the 'qtsrc' url instead.
+ if (this.qtsrc)
+ src = this.resolveRelativeToUrl(this.qtsrc, this.src);
+ if (this.href && this.target) {
+ src = this.resolveRelativeToUrl(this.href, this.src);
+ video.poster = this.src;
+ video.setAttribute('preload', 'none');
+ }
+
+ if (src.length) {
+ this.setStatus('Validating');
+ this.video.src = src;
+ }
+
+ this.root.appendChild(video);
+ },
+
+ resolveRelativeToUrl: function(url, baseUrl)
+ {
+ if (url.indexOf('://') != -1)
+ return url;
+ if (baseUrl.indexOf('://') == -1)
+ baseUrl = this.resolveRelativeToUrl(baseUrl, document.baseURI);
+
+ var base = document.createElement('base');
+ base.href = baseUrl;
+ document.head.appendChild(base);
+
+ var resolver = document.createElement('a');
+ resolver.href = url;
+ url = resolver.href;
+
+ document.head.removeChild(base);
+ base = null;
+
+ return url;
+ },
+
+ createScriptInterface: function()
+ {
+ for (name in this.MethodMap) {
+ var methodName = this.MethodMap[name];
+ this.scriptObject[name] = this[methodName].bind(this);
+ }
+ },
+
+ handleEvent: function(event)
+ {
+ if (event.target !== this.video)
+ return;
+
+ try {
+ var eventData = this.HandledVideoEvents[event.type];
+ if (!eventData)
+ return;
+
+ if (this[eventData] && typeof this[eventData] === "function")
+ this[eventData].call(this, event);
+ else
+ this.postEvent(eventData);
+ } catch(e) {
+ if (window.console)
+ console.error(e);
+ }
+ },
+
+ postEvent: function(eventName)
+ {
+ try {
+ if (this.postEvents)
+ this.host.postEvent(eventName);
+ } catch(e) { }
+ },
+
+ setStatus: function(status)
+ {
+ this.status = status;
+ },
+
+ handleLoadedMetaData: function(event)
+ {
+ this.setStatus('Playable');
+ this.postEvent('qt_validated');
+ this.postEvent('qt_loadedfirstframe');
+ this.postEvent('qt_loadedmetadata');
+ },
+
+ handleFullscreenChange: function(event)
+ {
+ this.postEvent(this.isFullScreen() ? 'qt_enterfullscreen' : 'qt_exitfullscreen');
+ },
+
+ handleError: function(event)
+ {
+ this.setStatus('Error');
+ this.postEvent('qt_error');
+ },
+
+ handleLoadStart:function(event)
+ {
+ if (this.video.poster)
+ this.setStatus('Waiting');
+ else
+ this.setStatus('Loading');
+ this.postEvent('qt_begin');
+ },
+
+ handleEnded: function(event)
+ {
+ this.postEvent('qt_ended');
+ if (this.isFullScreen() && this.autoExitFullScreen)
+ document.webkitExitFullscreen();
+ },
+
+ isFullScreen: function()
+ {
+ return document.webkitCurrentFullScreenElement === this.video;
+ },
+
+ setURL: function(url)
+ {
+ this.setStatus('Validating');
+ if (url.length)
+ url = this.resolveRelativeToUrl(url, this.baseUrl);
+ this.video.src = url;
+ },
+
+ url: function()
+ {
+ return this.video.currentSrc;
+ },
+
+ play: function()
+ {
+ this.video.play();
+ },
+
+ pause: function()
+ {
+ this.video.playbackRate = 0;
+ this.video.pause();
+ },
+
+ rate: function()
+ {
+ return this.video.paused ? 0 : 1;
+ },
+
+ setRate: function(rate)
+ {
+ if (rate)
+ this.video.play();
+ else
+ this.video.pause();
+ },
+
+ exitFullScreen: function()
+ {
+ document.webkitExitFullscreen();
+ },
+
+ pluginStatus: function()
+ {
+ return this.status;
+ },
+
+ currentTime: function()
+ {
+ return this.video.currentTime * this.TimeScale;
+ },
+
+ setCurrentTime: function(time)
+ {
+ this.video.currentTime = time / this.TimeScale;
+ },
+
+ seekToDate: function()
+ {
+ // FIXME: not implemented yet.
+ },
+
+ date: function()
+ {
+ return new Date();
+ },
+
+ duration: function()
+ {
+ return this.video.duration * this.TimeScale;
+ },
+
+ timeScale: function()
+ {
+ // Note: QuickTime movies and MPEG-4 files have a timescale, but it is not exposed by all media engines.
+ // 30000 works well with common frame rates, eg. 29.97 NTSC can be represented accurately as a time
+ // scale of 30000 and frame duration of 1001.
+ return 30000;
+ },
+
+ maxTimeLoaded: function()
+ {
+ return this.video.duration * this.TimeScale;
+ },
+
+ maxBytesLoaded: function()
+ {
+ var percentLoaded = this.video.buffered.end(0) / this.video.duration;
+ return percentLoaded * this.movieSize();
+ },
+
+ movieSize: function()
+ {
+ try {
+ return this.host.movieSize;
+ } catch(e) { }
+
+ return 0;
+ },
+
+ timedMetadataUpdates: function()
+ {
+ try {
+ return this.host.timedMetaData;
+ } catch(e) { }
+
+ return null;
+ },
+
+ accessLog: function()
+ {
+ try {
+ return this.host.accessLog;
+ } catch(e) { }
+
+ return null;
+ },
+
+ errorLog: function()
+ {
+ try {
+ return this.host.errorLog;
+ } catch(e) { }
+
+ return null;
+ },
+};
+
diff --git a/Source/WebCore/Modules/plugins/YouTubePluginReplacement.cpp b/Source/WebCore/Modules/plugins/YouTubePluginReplacement.cpp
new file mode 100644
index 000000000..b497adc77
--- /dev/null
+++ b/Source/WebCore/Modules/plugins/YouTubePluginReplacement.cpp
@@ -0,0 +1,354 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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 "YouTubePluginReplacement.h"
+
+#include "HTMLIFrameElement.h"
+#include "HTMLNames.h"
+#include "HTMLParserIdioms.h"
+#include "HTMLPlugInElement.h"
+#include "RenderElement.h"
+#include "Settings.h"
+#include "ShadowRoot.h"
+#include "YouTubeEmbedShadowElement.h"
+#include <wtf/text/StringBuilder.h>
+
+namespace WebCore {
+
+void YouTubePluginReplacement::registerPluginReplacement(PluginReplacementRegistrar registrar)
+{
+ registrar(ReplacementPlugin(create, supportsMimeType, supportsFileExtension, supportsURL, isEnabledBySettings));
+}
+
+Ref<PluginReplacement> YouTubePluginReplacement::create(HTMLPlugInElement& plugin, const Vector<String>& paramNames, const Vector<String>& paramValues)
+{
+ return adoptRef(*new YouTubePluginReplacement(plugin, paramNames, paramValues));
+}
+
+bool YouTubePluginReplacement::supportsMimeType(const String& mimeType)
+{
+ return equalLettersIgnoringASCIICase(mimeType, "application/x-shockwave-flash")
+ || equalLettersIgnoringASCIICase(mimeType, "application/futuresplash");
+}
+
+bool YouTubePluginReplacement::supportsFileExtension(const String& extension)
+{
+ return equalLettersIgnoringASCIICase(extension, "spl") || equalLettersIgnoringASCIICase(extension, "swf");
+}
+
+YouTubePluginReplacement::YouTubePluginReplacement(HTMLPlugInElement& plugin, const Vector<String>& paramNames, const Vector<String>& paramValues)
+ : m_parentElement(&plugin)
+{
+ ASSERT(paramNames.size() == paramValues.size());
+ for (size_t i = 0; i < paramNames.size(); ++i)
+ m_attributes.add(paramNames[i], paramValues[i]);
+}
+
+RenderPtr<RenderElement> YouTubePluginReplacement::createElementRenderer(HTMLPlugInElement& plugin, RenderStyle&& style, const RenderTreePosition& insertionPosition)
+{
+ ASSERT_UNUSED(plugin, m_parentElement == &plugin);
+
+ if (!m_embedShadowElement)
+ return nullptr;
+
+ return m_embedShadowElement->createElementRenderer(WTFMove(style), insertionPosition);
+}
+
+bool YouTubePluginReplacement::installReplacement(ShadowRoot& root)
+{
+ m_embedShadowElement = YouTubeEmbedShadowElement::create(m_parentElement->document());
+
+ root.appendChild(*m_embedShadowElement);
+
+ auto iframeElement = HTMLIFrameElement::create(HTMLNames::iframeTag, m_parentElement->document());
+ if (m_attributes.contains("width"))
+ iframeElement->setAttributeWithoutSynchronization(HTMLNames::widthAttr, AtomicString("100%", AtomicString::ConstructFromLiteral));
+
+ const auto& heightValue = m_attributes.find("height");
+ if (heightValue != m_attributes.end()) {
+ iframeElement->setAttribute(HTMLNames::styleAttr, AtomicString("max-height: 100%", AtomicString::ConstructFromLiteral));
+ iframeElement->setAttributeWithoutSynchronization(HTMLNames::heightAttr, heightValue->value);
+ }
+
+ iframeElement->setAttributeWithoutSynchronization(HTMLNames::srcAttr, youTubeURL(m_attributes.get("src")));
+ iframeElement->setAttributeWithoutSynchronization(HTMLNames::frameborderAttr, AtomicString("0", AtomicString::ConstructFromLiteral));
+
+ // Disable frame flattening for this iframe.
+ iframeElement->setAttributeWithoutSynchronization(HTMLNames::scrollingAttr, AtomicString("no", AtomicString::ConstructFromLiteral));
+ m_embedShadowElement->appendChild(iframeElement);
+
+ return true;
+}
+
+static inline URL createYouTubeURL(const String& videoID, const String& timeID)
+{
+ ASSERT(!videoID.isEmpty());
+ ASSERT(videoID != "/");
+
+ URL result(URL(), "youtube:" + videoID);
+ if (!timeID.isEmpty())
+ result.setQuery("t=" + timeID);
+
+ return result;
+}
+
+static YouTubePluginReplacement::KeyValueMap queryKeysAndValues(const String& queryString)
+{
+ YouTubePluginReplacement::KeyValueMap queryDictionary;
+
+ size_t queryLength = queryString.length();
+ if (!queryLength)
+ return queryDictionary;
+
+ size_t equalSearchLocation = 0;
+ size_t equalSearchLength = queryLength;
+
+ while (equalSearchLocation < queryLength - 1 && equalSearchLength) {
+
+ // Search for "=".
+ size_t equalLocation = queryString.find('=', equalSearchLocation);
+ if (equalLocation == notFound)
+ break;
+
+ size_t indexAfterEqual = equalLocation + 1;
+ if (indexAfterEqual > queryLength - 1)
+ break;
+
+ // Get the key before the "=".
+ size_t keyLocation = equalSearchLocation;
+ size_t keyLength = equalLocation - equalSearchLocation;
+
+ // Seach for the ampersand.
+ size_t ampersandLocation = queryString.find('&', indexAfterEqual);
+
+ // Get the value after the "=", before the ampersand.
+ size_t valueLocation = indexAfterEqual;
+ size_t valueLength;
+ if (ampersandLocation != notFound)
+ valueLength = ampersandLocation - indexAfterEqual;
+ else
+ valueLength = queryLength - indexAfterEqual;
+
+ // Save the key and the value.
+ if (keyLength && valueLength) {
+ String key = queryString.substring(keyLocation, keyLength).convertToASCIILowercase();
+ String value = queryString.substring(valueLocation, valueLength);
+ value.replace('+', ' ');
+
+ if (!key.isEmpty() && !value.isEmpty())
+ queryDictionary.add(key, value);
+ }
+
+ if (ampersandLocation == notFound)
+ break;
+
+ // Continue searching after the ampersand.
+ size_t indexAfterAmpersand = ampersandLocation + 1;
+ equalSearchLocation = indexAfterAmpersand;
+ equalSearchLength = queryLength - indexAfterAmpersand;
+ }
+
+ return queryDictionary;
+}
+
+static bool hasCaseInsensitivePrefix(const String& input, const String& prefix)
+{
+ return input.startsWith(prefix, false);
+}
+
+static bool isYouTubeURL(const URL& url)
+{
+ String hostName = url.host();
+ return equalLettersIgnoringASCIICase(hostName, "m.youtube.com")
+ || equalLettersIgnoringASCIICase(hostName, "youtu.be")
+ || equalLettersIgnoringASCIICase(hostName, "www.youtube.com")
+ || equalLettersIgnoringASCIICase(hostName, "youtube.com")
+ || equalLettersIgnoringASCIICase(hostName, "www.youtube-nocookie.com")
+ || equalLettersIgnoringASCIICase(hostName, "youtube-nocookie.com");
+}
+
+static const String& valueForKey(const YouTubePluginReplacement::KeyValueMap& dictionary, const String& key)
+{
+ const auto& value = dictionary.find(key);
+ if (value == dictionary.end())
+ return emptyString();
+
+ return value->value;
+}
+
+static URL processAndCreateYouTubeURL(const URL& url, bool& isYouTubeShortenedURL, String& outPathAfterFirstAmpersand)
+{
+ if (!url.protocolIsInHTTPFamily())
+ return URL();
+
+ // Bail out early if we aren't even on www.youtube.com or youtube.com.
+ if (!isYouTubeURL(url))
+ return URL();
+
+ String hostName = url.host();
+ bool isYouTubeMobileWebAppURL = equalLettersIgnoringASCIICase(hostName, "m.youtube.com");
+ isYouTubeShortenedURL = equalLettersIgnoringASCIICase(hostName, "youtu.be");
+
+ // Short URL of the form: http://youtu.be/v1d301D
+ if (isYouTubeShortenedURL) {
+ String videoID = url.lastPathComponent();
+ if (videoID.isEmpty() || videoID == "/")
+ return URL();
+ return createYouTubeURL(videoID, emptyString());
+ }
+
+ String path = url.path();
+ String query = url.query();
+ String fragment = url.fragmentIdentifier();
+
+ // On the YouTube mobile web app, the path and query string are put into the
+ // fragment so that one web page is only ever loaded (see <rdar://problem/9550639>).
+ if (isYouTubeMobileWebAppURL) {
+ size_t location = fragment.find('?');
+ if (location == notFound) {
+ path = fragment;
+ query = emptyString();
+ } else {
+ path = fragment.substring(0, location);
+ query = fragment.substring(location + 1);
+ }
+ fragment = emptyString();
+ }
+
+ if (equalLettersIgnoringASCIICase(path, "/watch")) {
+ if (!query.isEmpty()) {
+ const auto& queryDictionary = queryKeysAndValues(query);
+ String videoID = valueForKey(queryDictionary, "v");
+
+ if (!videoID.isEmpty()) {
+ const auto& fragmentDictionary = queryKeysAndValues(url.fragmentIdentifier());
+ String timeID = valueForKey(fragmentDictionary, "t");
+ return createYouTubeURL(videoID, timeID);
+ }
+ }
+
+ // May be a new-style link (see <rdar://problem/7733692>).
+ if (fragment.startsWith('!')) {
+ query = fragment.substring(1);
+
+ if (!query.isEmpty()) {
+ const auto& queryDictionary = queryKeysAndValues(query);
+ String videoID = valueForKey(queryDictionary, "v");
+
+ if (!videoID.isEmpty()) {
+ String timeID = valueForKey(queryDictionary, "t");
+ return createYouTubeURL(videoID, timeID);
+ }
+ }
+ }
+ } else if (hasCaseInsensitivePrefix(path, "/v/") || hasCaseInsensitivePrefix(path, "/e/")) {
+ String lastPathComponent = url.lastPathComponent();
+ String videoID;
+ String pathAfterFirstAmpersand;
+
+ size_t ampersandLocation = lastPathComponent.find('&');
+ if (ampersandLocation != notFound) {
+ // Some URLs we care about use & in place of ? for the first query parameter.
+ videoID = lastPathComponent.substring(0, ampersandLocation);
+ pathAfterFirstAmpersand = lastPathComponent.substring(ampersandLocation + 1, lastPathComponent.length() - ampersandLocation);
+ } else
+ videoID = lastPathComponent;
+
+ if (!videoID.isEmpty()) {
+ outPathAfterFirstAmpersand = pathAfterFirstAmpersand;
+ return createYouTubeURL(videoID, emptyString());
+ }
+ }
+
+ return URL();
+}
+
+String YouTubePluginReplacement::youTubeURL(const String& srcString)
+{
+ URL srcURL = m_parentElement->document().completeURL(stripLeadingAndTrailingHTMLSpaces(srcString));
+ return youTubeURLFromAbsoluteURL(srcURL, srcString);
+}
+
+String YouTubePluginReplacement::youTubeURLFromAbsoluteURL(const URL& srcURL, const String& srcString)
+{
+ bool isYouTubeShortenedURL = false;
+ String possibleMalformedQuery;
+ URL youTubeURL = processAndCreateYouTubeURL(srcURL, isYouTubeShortenedURL, possibleMalformedQuery);
+ if (srcURL.isEmpty() || youTubeURL.isEmpty())
+ return srcString;
+
+ // Transform the youtubeURL (youtube:VideoID) to iframe embed url which has the format: http://www.youtube.com/embed/VideoID
+ const String& srcPath = srcURL.path();
+ const String& videoID = youTubeURL.string().substring(youTubeURL.protocol().length() + 1);
+ size_t locationOfVideoIDInPath = srcPath.find(videoID);
+
+ size_t locationOfPathBeforeVideoID = notFound;
+ if (locationOfVideoIDInPath != notFound) {
+ ASSERT(locationOfVideoIDInPath);
+
+ // From the original URL, we need to get the part before /path/VideoId.
+ locationOfPathBeforeVideoID = srcString.find(srcPath.substring(0, locationOfVideoIDInPath));
+ } else if (equalLettersIgnoringASCIICase(srcPath, "/watch")) {
+ // From the original URL, we need to get the part before /watch/#!v=VideoID
+ // FIXME: Shouldn't this be ASCII case-insensitive?
+ locationOfPathBeforeVideoID = srcString.find("/watch");
+ } else
+ return srcString;
+
+ ASSERT(locationOfPathBeforeVideoID != notFound);
+
+ const String& srcURLPrefix = srcString.substring(0, locationOfPathBeforeVideoID);
+ String query = srcURL.query();
+ // If the URL has no query, use the possibly malformed query we found.
+ if (query.isEmpty())
+ query = possibleMalformedQuery;
+
+ // Append the query string if it is valid.
+ StringBuilder finalURL;
+ if (isYouTubeShortenedURL)
+ finalURL.appendLiteral("http://www.youtube.com");
+ else
+ finalURL.append(srcURLPrefix);
+ finalURL.appendLiteral("/embed/");
+ finalURL.append(videoID);
+ if (!query.isEmpty()) {
+ finalURL.append('?');
+ finalURL.append(query);
+ }
+ return finalURL.toString();
+}
+
+bool YouTubePluginReplacement::supportsURL(const URL& url)
+{
+ return isYouTubeURL(url);
+}
+
+bool YouTubePluginReplacement::isEnabledBySettings(const Settings& settings)
+{
+ return settings.youTubeFlashPluginReplacementEnabled();
+}
+
+}
diff --git a/Source/WebCore/Modules/plugins/YouTubePluginReplacement.h b/Source/WebCore/Modules/plugins/YouTubePluginReplacement.h
new file mode 100644
index 000000000..dc8c0974d
--- /dev/null
+++ b/Source/WebCore/Modules/plugins/YouTubePluginReplacement.h
@@ -0,0 +1,63 @@
+/*
+ * 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. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED 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
+
+#include "PluginReplacement.h"
+#include <wtf/HashMap.h>
+
+namespace WebCore {
+
+class YouTubeEmbedShadowElement;
+
+class YouTubePluginReplacement final : public PluginReplacement {
+public:
+ static void registerPluginReplacement(PluginReplacementRegistrar);
+
+ typedef HashMap<String, String> KeyValueMap;
+
+ WEBCORE_EXPORT static String youTubeURLFromAbsoluteURL(const URL& srcURL, const String& srcString);
+
+private:
+ YouTubePluginReplacement(HTMLPlugInElement&, const Vector<String>& paramNames, const Vector<String>& paramValues);
+ static Ref<PluginReplacement> create(HTMLPlugInElement&, const Vector<String>& paramNames, const Vector<String>& paramValues);
+ static bool supportsMimeType(const String&);
+ static bool supportsFileExtension(const String&);
+ static bool supportsURL(const URL&);
+ static bool isEnabledBySettings(const Settings&);
+
+ bool installReplacement(ShadowRoot&) final;
+
+ String youTubeURL(const String& rawURL);
+
+ bool willCreateRenderer() final { return m_embedShadowElement; }
+ RenderPtr<RenderElement> createElementRenderer(HTMLPlugInElement&, RenderStyle&&, const RenderTreePosition&) final;
+
+ HTMLPlugInElement* m_parentElement;
+ RefPtr<YouTubeEmbedShadowElement> m_embedShadowElement;
+ KeyValueMap m_attributes;
+};
+
+}
diff --git a/Source/WebCore/Modules/proximity/DeviceProximityClient.h b/Source/WebCore/Modules/proximity/DeviceProximityClient.h
index c9c15a552..91e0dd567 100644
--- a/Source/WebCore/Modules/proximity/DeviceProximityClient.h
+++ b/Source/WebCore/Modules/proximity/DeviceProximityClient.h
@@ -23,8 +23,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DeviceProximityClient_h
-#define DeviceProximityClient_h
+#pragma once
#if ENABLE(PROXIMITY_EVENTS)
@@ -49,4 +48,3 @@ void provideDeviceProximityTo(Page*, DeviceProximityClient*);
} // namespace WebCore
#endif // PROXIMITY_EVENTS
-#endif // DeviceProximityClient_h
diff --git a/Source/WebCore/Modules/proximity/DeviceProximityController.cpp b/Source/WebCore/Modules/proximity/DeviceProximityController.cpp
index a64d47ac6..68ea296af 100644
--- a/Source/WebCore/Modules/proximity/DeviceProximityController.cpp
+++ b/Source/WebCore/Modules/proximity/DeviceProximityController.cpp
@@ -40,11 +40,6 @@ DeviceProximityController::DeviceProximityController(DeviceProximityClient* clie
ASSERT(m_client);
}
-PassOwnPtr<DeviceProximityController> DeviceProximityController::create(DeviceProximityClient* client)
-{
- return adoptPtr(new DeviceProximityController(client));
-}
-
void DeviceProximityController::didChangeDeviceProximity(const double value, const double min, const double max)
{
ASSERT(value >= min && value <= max);
@@ -62,7 +57,7 @@ bool DeviceProximityController::hasLastData()
return deviceProximityClient()->hasLastData();
}
-PassRefPtr<Event> DeviceProximityController::getLastEvent()
+RefPtr<Event> DeviceProximityController::getLastEvent()
{
return DeviceProximityEvent::create(eventNames().webkitdeviceproximityEvent, deviceProximityClient()->value(), deviceProximityClient()->min(), deviceProximityClient()->max());
}
@@ -86,7 +81,7 @@ bool DeviceProximityController::isActiveAt(Page* page)
void provideDeviceProximityTo(Page* page, DeviceProximityClient* client)
{
- DeviceProximityController::provideTo(page, DeviceProximityController::supplementName(), DeviceProximityController::create(client));
+ DeviceProximityController::provideTo(page, DeviceProximityController::supplementName(), std::make_unique<DeviceProximityController>(client));
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/proximity/DeviceProximityController.h b/Source/WebCore/Modules/proximity/DeviceProximityController.h
index 9fd6ffb6d..5b30dfe47 100644
--- a/Source/WebCore/Modules/proximity/DeviceProximityController.h
+++ b/Source/WebCore/Modules/proximity/DeviceProximityController.h
@@ -23,8 +23,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DeviceProximityController_h
-#define DeviceProximityController_h
+#pragma once
#if ENABLE(PROXIMITY_EVENTS)
@@ -36,25 +35,20 @@ class DeviceProximityClient;
class DeviceProximityController : public DeviceController {
public:
+ explicit DeviceProximityController(DeviceProximityClient*);
~DeviceProximityController() { }
- static PassOwnPtr<DeviceProximityController> create(DeviceProximityClient*);
-
void didChangeDeviceProximity(const double value, const double min, const double max);
DeviceProximityClient* deviceProximityClient();
virtual bool hasLastData();
- virtual PassRefPtr<Event> getLastEvent();
+ virtual RefPtr<Event> getLastEvent();
static const char* supplementName();
static DeviceProximityController* from(Page*);
static bool isActiveAt(Page*);
-
-private:
- explicit DeviceProximityController(DeviceProximityClient*);
};
} // namespace WebCore
#endif // PROXIMITY_EVENTS
-#endif // DeviceProximityController_h
diff --git a/Source/WebCore/Modules/proximity/DeviceProximityEvent.cpp b/Source/WebCore/Modules/proximity/DeviceProximityEvent.cpp
index e32c0cb6d..06bf01eb9 100644
--- a/Source/WebCore/Modules/proximity/DeviceProximityEvent.cpp
+++ b/Source/WebCore/Modules/proximity/DeviceProximityEvent.cpp
@@ -39,11 +39,11 @@ DeviceProximityEvent::DeviceProximityEvent(const AtomicString& eventType, const
{
}
-DeviceProximityEvent::DeviceProximityEvent(const AtomicString& eventType, const DeviceProximityEventInit& initializer)
- : Event(eventType, initializer)
- , m_value(initializer.value)
- , m_min(initializer.min)
- , m_max(initializer.max)
+DeviceProximityEvent::DeviceProximityEvent(const AtomicString& eventType, const Init& initializer, IsTrusted isTrusted)
+ : Event(eventType, initializer, isTrusted)
+ , m_value(initializer.value ? *initializer.value : std::numeric_limits<double>::infinity())
+ , m_min(initializer.min ? *initializer.min : -std::numeric_limits<double>::infinity())
+ , m_max(initializer.max ? *initializer.max : std::numeric_limits<double>::infinity())
{
}
diff --git a/Source/WebCore/Modules/proximity/DeviceProximityEvent.h b/Source/WebCore/Modules/proximity/DeviceProximityEvent.h
index 0ece25413..373ac451e 100644
--- a/Source/WebCore/Modules/proximity/DeviceProximityEvent.h
+++ b/Source/WebCore/Modules/proximity/DeviceProximityEvent.h
@@ -17,8 +17,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef DeviceProximityEvent_h
-#define DeviceProximityEvent_h
+#pragma once
#if ENABLE(PROXIMITY_EVENTS)
@@ -26,39 +25,29 @@
namespace WebCore {
-struct DeviceProximityEventInit : public EventInit {
- DeviceProximityEventInit()
- : value(std::numeric_limits<double>::infinity())
- , min(-std::numeric_limits<double>::infinity())
- , max(std::numeric_limits<double>::infinity())
- {
- // Default value of bubbles is true by the Proximity Events spec.
- // http://www.w3.org/TR/proximity/#deviceproximityevent-interface
- bubbles = true;
- };
-
- double value;
- double min;
- double max;
-};
-
class DeviceProximityEvent : public Event {
public:
~DeviceProximityEvent() { }
- static PassRefPtr<DeviceProximityEvent> create()
+ static Ref<DeviceProximityEvent> create()
{
- return adoptRef(new DeviceProximityEvent());
+ return adoptRef(*new DeviceProximityEvent());
}
- static PassRefPtr<DeviceProximityEvent> create(const AtomicString& eventType, const double value, const double min, const double max)
+ static Ref<DeviceProximityEvent> create(const AtomicString& eventType, const double value, const double min, const double max)
{
- return adoptRef(new DeviceProximityEvent(eventType, value, min, max));
+ return adoptRef(*new DeviceProximityEvent(eventType, value, min, max));
}
- static PassRefPtr<DeviceProximityEvent> create(const AtomicString& type, const DeviceProximityEventInit& initializer)
+ struct Init : EventInit {
+ std::optional<double> value;
+ std::optional<double> min;
+ std::optional<double> max;
+ };
+
+ static Ref<DeviceProximityEvent> create(const AtomicString& type, const Init& initializer, IsTrusted isTrusted = IsTrusted::No)
{
- return adoptRef(new DeviceProximityEvent(type, initializer));
+ return adoptRef(*new DeviceProximityEvent(type, initializer, isTrusted));
}
double value() { return m_value; }
@@ -70,7 +59,7 @@ public:
private:
DeviceProximityEvent();
DeviceProximityEvent(const AtomicString& eventType, const double value, const double min, const double max);
- DeviceProximityEvent(const AtomicString& eventType, const DeviceProximityEventInit&);
+ DeviceProximityEvent(const AtomicString& eventType, const Init&, IsTrusted);
double m_value;
double m_min;
@@ -80,4 +69,3 @@ private:
} // namespace WebCore
#endif // DeviceProximityEvent_h
-#endif // PROXIMITY_EVENTS
diff --git a/Source/WebCore/Modules/proximity/DeviceProximityEvent.idl b/Source/WebCore/Modules/proximity/DeviceProximityEvent.idl
index 7fe014adf..7ecbfe78e 100644
--- a/Source/WebCore/Modules/proximity/DeviceProximityEvent.idl
+++ b/Source/WebCore/Modules/proximity/DeviceProximityEvent.idl
@@ -19,10 +19,15 @@
[
Conditional=PROXIMITY_EVENTS,
- ConstructorTemplate=Event,
+ Constructor(DOMString type, optional DeviceProximityEventInit eventInitDict),
] interface DeviceProximityEvent : Event {
- [InitializedByEventConstructor] readonly attribute double value;
- [InitializedByEventConstructor] readonly attribute double min;
- [InitializedByEventConstructor] readonly attribute double max;
+ readonly attribute unrestricted double value;
+ readonly attribute unrestricted double min;
+ readonly attribute unrestricted double max;
};
+dictionary DeviceProximityEventInit : EventInit {
+ double value;
+ double min;
+ double max;
+};
diff --git a/Source/WebCore/Modules/quota/DOMWindowQuota.cpp b/Source/WebCore/Modules/quota/DOMWindowQuota.cpp
index ea3d65938..ec7a1b12e 100644
--- a/Source/WebCore/Modules/quota/DOMWindowQuota.cpp
+++ b/Source/WebCore/Modules/quota/DOMWindowQuota.cpp
@@ -37,7 +37,6 @@
#include "Document.h"
#include "Frame.h"
#include "StorageInfo.h"
-#include <wtf/PassRefPtr.h>
namespace WebCore {
@@ -60,8 +59,9 @@ DOMWindowQuota* DOMWindowQuota::from(DOMWindow* window)
{
DOMWindowQuota* supplement = static_cast<DOMWindowQuota*>(Supplement<DOMWindow>::from(window, supplementName()));
if (!supplement) {
- supplement = new DOMWindowQuota(window);
- provideTo(window, supplementName(), adoptPtr(supplement));
+ auto newSupplement = std::make_unique<DOMWindowQuota>(window);
+ supplement = newSupplement.get();
+ provideTo(window, supplementName(), WTFMove(newSupplement));
}
return supplement;
}
@@ -75,7 +75,7 @@ StorageInfo* DOMWindowQuota::webkitStorageInfo(DOMWindow* window)
StorageInfo* DOMWindowQuota::webkitStorageInfo() const
{
if (!m_storageInfo && frame()) {
- frame()->document()->addConsoleMessage(JSMessageSource, WarningMessageLevel, "window.webkitStorageInfo is deprecated. Use navigator.webkitTemporaryStorage or navigator.webkitPersistentStorage instead.");
+ frame()->document()->addConsoleMessage(MessageSource::JS, MessageLevel::Warning, ASCIILiteral("window.webkitStorageInfo is deprecated. Use navigator.webkitTemporaryStorage or navigator.webkitPersistentStorage instead."));
m_storageInfo = StorageInfo::create();
}
return m_storageInfo.get();
diff --git a/Source/WebCore/Modules/quota/DOMWindowQuota.h b/Source/WebCore/Modules/quota/DOMWindowQuota.h
index ccd434aa3..62d0812db 100644
--- a/Source/WebCore/Modules/quota/DOMWindowQuota.h
+++ b/Source/WebCore/Modules/quota/DOMWindowQuota.h
@@ -28,8 +28,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DOMWindowQuota_h
-#define DOMWindowQuota_h
+#pragma once
#if ENABLE(QUOTA)
@@ -43,13 +42,13 @@ class StorageInfo;
class DOMWindowQuota : public Supplement<DOMWindow>, public DOMWindowProperty {
public:
+ explicit DOMWindowQuota(DOMWindow*);
virtual ~DOMWindowQuota();
static DOMWindowQuota* from(DOMWindow*);
static StorageInfo* webkitStorageInfo(DOMWindow*);
StorageInfo* webkitStorageInfo() const;
private:
- explicit DOMWindowQuota(DOMWindow*);
static const char* supplementName();
mutable RefPtr<StorageInfo> m_storageInfo;
@@ -58,5 +57,3 @@ private:
} // namespace WebCore
#endif // ENABLE(QUOTA)
-
-#endif // DOMWindowQuota_h
diff --git a/Source/WebCore/Modules/quota/DOMWindowQuota.idl b/Source/WebCore/Modules/quota/DOMWindowQuota.idl
index 08c53060a..026b3d256 100644
--- a/Source/WebCore/Modules/quota/DOMWindowQuota.idl
+++ b/Source/WebCore/Modules/quota/DOMWindowQuota.idl
@@ -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
diff --git a/Source/WebCore/Modules/quota/NavigatorStorageQuota.cpp b/Source/WebCore/Modules/quota/NavigatorStorageQuota.cpp
index 2e0950208..0d86fe729 100644
--- a/Source/WebCore/Modules/quota/NavigatorStorageQuota.cpp
+++ b/Source/WebCore/Modules/quota/NavigatorStorageQuota.cpp
@@ -57,8 +57,9 @@ NavigatorStorageQuota* NavigatorStorageQuota::from(Navigator* navigator)
{
NavigatorStorageQuota* supplement = static_cast<NavigatorStorageQuota*>(Supplement<Navigator>::from(navigator, supplementName()));
if (!supplement) {
- supplement = new NavigatorStorageQuota(navigator->frame());
- provideTo(navigator, supplementName(), adoptPtr(supplement));
+ auto newSupplement = std::make_unique<NavigatorStorageQuota>(window);
+ supplement = newSupplement.get();
+ provideTo(navigator, supplementName(), WTFMove(newSupplement));
}
return supplement;
}
diff --git a/Source/WebCore/Modules/quota/NavigatorStorageQuota.h b/Source/WebCore/Modules/quota/NavigatorStorageQuota.h
index 9c1d9ef26..8eb5d423a 100644
--- a/Source/WebCore/Modules/quota/NavigatorStorageQuota.h
+++ b/Source/WebCore/Modules/quota/NavigatorStorageQuota.h
@@ -28,8 +28,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef NavigatorStorageQuota_h
-#define NavigatorStorageQuota_h
+#pragma once
#if ENABLE(QUOTA)
@@ -44,6 +43,7 @@ class Navigator;
class NavigatorStorageQuota : public Supplement<Navigator>, public DOMWindowProperty {
public:
+ explicit NavigatorStorageQuota(Frame*);
virtual ~NavigatorStorageQuota();
static NavigatorStorageQuota* from(Navigator*);
@@ -53,7 +53,6 @@ public:
StorageQuota* webkitPersistentStorage() const;
private:
- explicit NavigatorStorageQuota(Frame*);
static const char* supplementName();
mutable RefPtr<StorageQuota> m_temporaryStorage;
@@ -63,5 +62,3 @@ private:
} // namespace WebCore
#endif // ENABLE(QUOTA)
-
-#endif // NavigatorStorageQuota_h
diff --git a/Source/WebCore/Modules/quota/StorageErrorCallback.cpp b/Source/WebCore/Modules/quota/StorageErrorCallback.cpp
index 983a542b7..6baf4bcbd 100644
--- a/Source/WebCore/Modules/quota/StorageErrorCallback.cpp
+++ b/Source/WebCore/Modules/quota/StorageErrorCallback.cpp
@@ -39,20 +39,14 @@
namespace WebCore {
-StorageErrorCallback::CallbackTask::CallbackTask(PassRefPtr<StorageErrorCallback> callback, ExceptionCode ec)
- : m_callback(callback)
- , m_ec(ec)
+StorageErrorCallback::CallbackTask::CallbackTask(RefPtr<StorageErrorCallback>&& callback, ExceptionCode ec)
+ : ScriptExecutionContext::Task([callback, ec] (ScriptExecutionContext*) {
+ if (callback)
+ callback->handleEvent(DOMCoreException::create(ExceptionCodeDescription(ec)).get());
+ })
{
}
-void StorageErrorCallback::CallbackTask::performTask(ScriptExecutionContext*)
-{
- if (!m_callback)
- return;
- ExceptionCodeDescription description(m_ec);
- m_callback->handleEvent(DOMCoreException::create(description).get());
-}
-
} // namespace WebCore
#endif // ENABLE(QUOTA)
diff --git a/Source/WebCore/Modules/quota/StorageErrorCallback.h b/Source/WebCore/Modules/quota/StorageErrorCallback.h
index ffce7b675..2c980426d 100644
--- a/Source/WebCore/Modules/quota/StorageErrorCallback.h
+++ b/Source/WebCore/Modules/quota/StorageErrorCallback.h
@@ -28,13 +28,11 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef StorageErrorCallback_h
-#define StorageErrorCallback_h
+#pragma once
#if ENABLE(QUOTA)
#include "ScriptExecutionContext.h"
-#include <wtf/PassOwnPtr.h>
#include <wtf/RefCounted.h>
namespace WebCore {
@@ -50,16 +48,9 @@ public:
class CallbackTask : public ScriptExecutionContext::Task {
public:
- static PassOwnPtr<CallbackTask> create(PassRefPtr<StorageErrorCallback> callback, ExceptionCode ec)
- {
- return adoptPtr(new CallbackTask(callback, ec));
- }
-
- virtual void performTask(ScriptExecutionContext*);
+ CallbackTask(StorageErrorCallback*, ExceptionCode);
private:
- CallbackTask(PassRefPtr<StorageErrorCallback>, ExceptionCode);
-
RefPtr<StorageErrorCallback> m_callback;
ExceptionCode m_ec;
};
@@ -68,5 +59,3 @@ public:
} // namespace WebCore
#endif // ENABLE(QUOTA)
-
-#endif // StorageErrorCallback_h
diff --git a/Source/WebCore/Modules/quota/StorageErrorCallback.idl b/Source/WebCore/Modules/quota/StorageErrorCallback.idl
index b37fbd4fa..ece8ce778 100644
--- a/Source/WebCore/Modules/quota/StorageErrorCallback.idl
+++ b/Source/WebCore/Modules/quota/StorageErrorCallback.idl
@@ -30,6 +30,4 @@
[
Conditional=QUOTA,
-] callback interface StorageErrorCallback {
- boolean handleEvent(DOMCoreException error);
-};
+] callback StorageErrorCallback = void (DOMCoreException error);
diff --git a/Source/WebCore/Modules/quota/StorageInfo.cpp b/Source/WebCore/Modules/quota/StorageInfo.cpp
index 6275ef9d5..cd83a4a92 100644
--- a/Source/WebCore/Modules/quota/StorageInfo.cpp
+++ b/Source/WebCore/Modules/quota/StorageInfo.cpp
@@ -52,28 +52,28 @@ StorageInfo::~StorageInfo()
{
}
-void StorageInfo::queryUsageAndQuota(ScriptExecutionContext* scriptExecutionContext, int storageType, PassRefPtr<StorageUsageCallback> successCallback, PassRefPtr<StorageErrorCallback> errorCallback)
+void StorageInfo::queryUsageAndQuota(ScriptExecutionContext& scriptExecutionContext, int storageType, RefPtr<StorageUsageCallback>&& successCallback, RefPtr<StorageErrorCallback>&& errorCallback)
{
// Dispatching the request to StorageQuota, as this interface is deprecated in favor of StorageQuota.
StorageQuota* storageQuota = getStorageQuota(storageType);
if (!storageQuota) {
// Unknown storage type is requested.
- scriptExecutionContext->postTask(StorageErrorCallback::CallbackTask::create(errorCallback, NOT_SUPPORTED_ERR));
+ scriptExecutionContext->postTask(StorageErrorCallback::CallbackTask::create(WTFMove(errorCallback), NOT_SUPPORTED_ERR));
return;
}
- storageQuota->queryUsageAndQuota(scriptExecutionContext, successCallback, errorCallback);
+ storageQuota->queryUsageAndQuota(scriptExecutionContext, WTFMove(successCallback), WTFMove(errorCallback));
}
-void StorageInfo::requestQuota(ScriptExecutionContext* scriptExecutionContext, int storageType, unsigned long long newQuotaInBytes, PassRefPtr<StorageQuotaCallback> successCallback, PassRefPtr<StorageErrorCallback> errorCallback)
+void StorageInfo::requestQuota(ScriptExecutionContext& scriptExecutionContext, int storageType, unsigned long long newQuotaInBytes, RefPtr<StorageQuotaCallback>&& successCallback, RefPtr<StorageErrorCallback>&& errorCallback)
{
// Dispatching the request to StorageQuota, as this interface is deprecated in favor of StorageQuota.
StorageQuota* storageQuota = getStorageQuota(storageType);
if (!storageQuota) {
// Unknown storage type is requested.
- scriptExecutionContext->postTask(StorageErrorCallback::CallbackTask::create(errorCallback, NOT_SUPPORTED_ERR));
+ scriptExecutionContext->postTask(StorageErrorCallback::CallbackTask::create(WTFMove(errorCallback), NOT_SUPPORTED_ERR));
return;
}
- storageQuota->requestQuota(scriptExecutionContext, newQuotaInBytes, successCallback, errorCallback);
+ storageQuota->requestQuota(scriptExecutionContext, newQuotaInBytes, WTFMove(successCallback), WTFMove(errorCallback));
}
StorageQuota* StorageInfo::getStorageQuota(int storageType)
diff --git a/Source/WebCore/Modules/quota/StorageInfo.h b/Source/WebCore/Modules/quota/StorageInfo.h
index dd50579bf..e56d7b054 100644
--- a/Source/WebCore/Modules/quota/StorageInfo.h
+++ b/Source/WebCore/Modules/quota/StorageInfo.h
@@ -28,12 +28,10 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef StorageInfo_h
-#define StorageInfo_h
+#pragma once
#if ENABLE(QUOTA)
-#include <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>
#include <wtf/RefPtr.h>
@@ -52,14 +50,14 @@ public:
PERSISTENT,
};
- static PassRefPtr<StorageInfo> create()
+ static Ref<StorageInfo> create()
{
- return adoptRef(new StorageInfo());
+ return adoptRef(*new StorageInfo());
}
- void queryUsageAndQuota(ScriptExecutionContext*, int storageType, PassRefPtr<StorageUsageCallback>, PassRefPtr<StorageErrorCallback>);
+ void queryUsageAndQuota(ScriptExecutionContext&, int storageType, RefPtr<StorageUsageCallback>&&, RefPtr<StorageErrorCallback>&&);
- void requestQuota(ScriptExecutionContext*, int storageType, unsigned long long newQuotaInBytes, PassRefPtr<StorageQuotaCallback>, PassRefPtr<StorageErrorCallback>);
+ void requestQuota(ScriptExecutionContext&, int storageType, unsigned long long newQuotaInBytes, RefPtr<StorageQuotaCallback>&&, RefPtr<StorageErrorCallback>&&);
~StorageInfo();
@@ -75,5 +73,3 @@ private:
} // namespace WebCore
#endif // ENABLE(QUOTA)
-
-#endif // StorageInfo_h
diff --git a/Source/WebCore/Modules/quota/StorageInfo.idl b/Source/WebCore/Modules/quota/StorageInfo.idl
index e6157a0d3..062638123 100644
--- a/Source/WebCore/Modules/quota/StorageInfo.idl
+++ b/Source/WebCore/Modules/quota/StorageInfo.idl
@@ -31,6 +31,6 @@
const unsigned short TEMPORARY = 0;
const unsigned short PERSISTENT = 1;
- [CallWith=ScriptExecutionContext] void queryUsageAndQuota(unsigned short storageType, optional StorageUsageCallback usageCallback, optional StorageErrorCallback errorCallback);
- [CallWith=ScriptExecutionContext] void requestQuota(unsigned short storageType, unsigned long long newQuotaInBytes, optional StorageQuotaCallback quotaCallback, optional StorageErrorCallback errorCallback);
+ [CallWith=ScriptExecutionContext] void queryUsageAndQuota(unsigned short storageType, optional StorageUsageCallback? usageCallback, optional StorageErrorCallback? errorCallback);
+ [CallWith=ScriptExecutionContext] void requestQuota(unsigned short storageType, unsigned long long newQuotaInBytes, optional StorageQuotaCallback? quotaCallback, optional StorageErrorCallback? errorCallback);
};
diff --git a/Source/WebCore/Modules/quota/StorageQuota.h b/Source/WebCore/Modules/quota/StorageQuota.h
index 26504f976..7c8d98bb2 100644
--- a/Source/WebCore/Modules/quota/StorageQuota.h
+++ b/Source/WebCore/Modules/quota/StorageQuota.h
@@ -28,12 +28,10 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef StorageQuota_h
-#define StorageQuota_h
+#pragma once
#if ENABLE(QUOTA)
-#include <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>
namespace WebCore {
@@ -50,14 +48,14 @@ public:
Persistent,
};
- static PassRefPtr<StorageQuota> create(Type type)
+ static Ref<StorageQuota> create(Type type)
{
- return adoptRef(new StorageQuota(type));
+ return adoptRef(*new StorageQuota(type));
}
- void queryUsageAndQuota(ScriptExecutionContext*, PassRefPtr<StorageUsageCallback>, PassRefPtr<StorageErrorCallback>);
+ void queryUsageAndQuota(ScriptExecutionContext&, RefPtr<StorageUsageCallback>&&, RefPtr<StorageErrorCallback>&&);
- void requestQuota(ScriptExecutionContext*, unsigned long long newQuotaInBytes, PassRefPtr<StorageQuotaCallback>, PassRefPtr<StorageErrorCallback>);
+ void requestQuota(ScriptExecutionContext&, unsigned long long newQuotaInBytes, RefPtr<StorageQuotaCallback>&&, RefPtr<StorageErrorCallback>&&);
~StorageQuota();
@@ -69,5 +67,3 @@ private:
} // namespace WebCore
#endif // ENABLE(QUOTA)
-
-#endif // StorageQuota_h
diff --git a/Source/WebCore/Modules/quota/StorageQuota.idl b/Source/WebCore/Modules/quota/StorageQuota.idl
index 937dc4e18..e596aa3f4 100644
--- a/Source/WebCore/Modules/quota/StorageQuota.idl
+++ b/Source/WebCore/Modules/quota/StorageQuota.idl
@@ -28,6 +28,6 @@
Conditional=QUOTA,
ImplementationLacksVTable,
] interface StorageQuota {
- [CallWith=ScriptExecutionContext] void queryUsageAndQuota(StorageUsageCallback usageCallback, optional StorageErrorCallback errorCallback);
- [CallWith=ScriptExecutionContext] void requestQuota(unsigned long long newQuotaInBytes, optional StorageQuotaCallback quotaCallback, optional StorageErrorCallback errorCallback);
+ [CallWith=ScriptExecutionContext] void queryUsageAndQuota(StorageUsageCallback usageCallback, optional StorageErrorCallback? errorCallback);
+ [CallWith=ScriptExecutionContext] void requestQuota(unsigned long long newQuotaInBytes, optional StorageQuotaCallback? quotaCallback, optional StorageErrorCallback? errorCallback);
};
diff --git a/Source/WebCore/Modules/quota/StorageQuotaCallback.h b/Source/WebCore/Modules/quota/StorageQuotaCallback.h
index a7db0d391..dcd734c7b 100644
--- a/Source/WebCore/Modules/quota/StorageQuotaCallback.h
+++ b/Source/WebCore/Modules/quota/StorageQuotaCallback.h
@@ -28,8 +28,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef StorageQuotaCallback_h
-#define StorageQuotaCallback_h
+#pragma once
#if ENABLE(QUOTA)
@@ -43,8 +42,6 @@ public:
virtual bool handleEvent(unsigned long long grantedQuotaInBytes) = 0;
};
-} // namespace
+} // namespace WebCore
#endif // ENABLE(QUOTA)
-
-#endif // StorageQuotaCallback_h
diff --git a/Source/WebCore/Modules/quota/StorageQuotaCallback.idl b/Source/WebCore/Modules/quota/StorageQuotaCallback.idl
index 4359f2627..0481a9576 100644
--- a/Source/WebCore/Modules/quota/StorageQuotaCallback.idl
+++ b/Source/WebCore/Modules/quota/StorageQuotaCallback.idl
@@ -30,6 +30,4 @@
[
Conditional=QUOTA,
-] callback interface StorageQuotaCallback {
- boolean handleEvent(unsigned long long grantedQuotaInBytes);
-};
+] callback StorageQuotaCallback = void (unsigned long long grantedQuotaInBytes);
diff --git a/Source/WebCore/Modules/quota/StorageUsageCallback.h b/Source/WebCore/Modules/quota/StorageUsageCallback.h
index 5d99f9e68..c424dd619 100644
--- a/Source/WebCore/Modules/quota/StorageUsageCallback.h
+++ b/Source/WebCore/Modules/quota/StorageUsageCallback.h
@@ -28,8 +28,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef StorageUsageCallback_h
-#define StorageUsageCallback_h
+#pragma once
#if ENABLE(QUOTA)
@@ -43,8 +42,6 @@ public:
virtual bool handleEvent(unsigned long long currentUsageInBytes, unsigned long long currentQuotaInBytes) = 0;
};
-} // namespace
+} // namespace WebCore
#endif // ENABLE(QUOTA)
-
-#endif // StorageUsageCallback_h
diff --git a/Source/WebCore/Modules/quota/StorageUsageCallback.idl b/Source/WebCore/Modules/quota/StorageUsageCallback.idl
index 5ea7a0ebc..d6bd5cae5 100644
--- a/Source/WebCore/Modules/quota/StorageUsageCallback.idl
+++ b/Source/WebCore/Modules/quota/StorageUsageCallback.idl
@@ -30,6 +30,4 @@
[
Conditional=QUOTA,
-] callback interface StorageUsageCallback {
- boolean handleEvent(unsigned long long currentUsageInBytes, unsigned long long currentQuotaInBytes);
-};
+] callback StorageUsageCallback = void (unsigned long long currentUsageInBytes, unsigned long long currentQuotaInBytes);
diff --git a/Source/WebCore/Modules/quota/WorkerNavigatorStorageQuota.cpp b/Source/WebCore/Modules/quota/WorkerNavigatorStorageQuota.cpp
index 90d135b4d..fd5ef4d17 100644
--- a/Source/WebCore/Modules/quota/WorkerNavigatorStorageQuota.cpp
+++ b/Source/WebCore/Modules/quota/WorkerNavigatorStorageQuota.cpp
@@ -55,8 +55,9 @@ WorkerNavigatorStorageQuota* WorkerNavigatorStorageQuota::from(WorkerNavigator*
{
WorkerNavigatorStorageQuota* supplement = static_cast<WorkerNavigatorStorageQuota*>(Supplement<WorkerNavigator>::from(navigator, supplementName()));
if (!supplement) {
- supplement = new WorkerNavigatorStorageQuota();
- provideTo(navigator, supplementName(), adoptPtr(supplement));
+ auto newSupplement = std::make_unique<WorkerNavigatorStorageQuota>(window);
+ supplement = newSupplement.get();
+ provideTo(navigator, supplementName(), WTFMove(newSupplement));
}
return supplement;
}
diff --git a/Source/WebCore/Modules/quota/WorkerNavigatorStorageQuota.h b/Source/WebCore/Modules/quota/WorkerNavigatorStorageQuota.h
index a7699f6b4..3456e68f1 100644
--- a/Source/WebCore/Modules/quota/WorkerNavigatorStorageQuota.h
+++ b/Source/WebCore/Modules/quota/WorkerNavigatorStorageQuota.h
@@ -28,8 +28,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef WorkerNavigatorStorageQuota_h
-#define WorkerNavigatorStorageQuota_h
+#pragma once
#if ENABLE(QUOTA)
@@ -44,6 +43,7 @@ class WorkerNavigator;
class WorkerNavigatorStorageQuota : public Supplement<WorkerNavigator> {
public:
+ explicit WorkerNavigatorStorageQuota();
virtual ~WorkerNavigatorStorageQuota();
static WorkerNavigatorStorageQuota* from(WorkerNavigator*);
@@ -53,7 +53,6 @@ public:
StorageQuota* webkitPersistentStorage() const;
private:
- explicit WorkerNavigatorStorageQuota();
static const char* supplementName();
mutable RefPtr<StorageQuota> m_temporaryStorage;
@@ -63,5 +62,3 @@ private:
} // namespace WebCore
#endif // ENABLE(QUOTA)
-
-#endif // WorkerNavigatorStorageQuota_h
diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionSync.cpp b/Source/WebCore/Modules/speech/DOMWindowSpeechSynthesis.cpp
index 6443c9a05..5ef87234a 100644
--- a/Source/WebCore/Modules/webdatabase/SQLTransactionSync.cpp
+++ b/Source/WebCore/Modules/speech/DOMWindowSpeechSynthesis.cpp
@@ -1,6 +1,5 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * 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
@@ -30,32 +29,53 @@
*/
#include "config.h"
-#include "SQLTransactionSync.h"
+#include "DOMWindowSpeechSynthesis.h"
-#if ENABLE(SQL_DATABASE)
+#if ENABLE(SPEECH_SYNTHESIS)
-#include "DatabaseSync.h"
-#include "SQLTransactionSyncCallback.h"
-#include "ScriptExecutionContext.h"
+#include "DOMWindow.h"
namespace WebCore {
-PassRefPtr<SQLTransactionSync> SQLTransactionSync::create(DatabaseSync* db, PassRefPtr<SQLTransactionSyncCallback> callback, bool readOnly)
+DOMWindowSpeechSynthesis::DOMWindowSpeechSynthesis(DOMWindow* window)
+ : DOMWindowProperty(window->frame())
{
- return adoptRef(new SQLTransactionSync(db, callback, readOnly));
}
-SQLTransactionSync::SQLTransactionSync(DatabaseSync* db, PassRefPtr<SQLTransactionSyncCallback> callback, bool readOnly)
- : SQLTransactionBackendSync(db, callback, readOnly)
+DOMWindowSpeechSynthesis::~DOMWindowSpeechSynthesis()
{
- ASSERT(m_database->scriptExecutionContext()->isContextThread());
}
-SQLTransactionSync* SQLTransactionSync::from(SQLTransactionBackendSync* backend)
+const char* DOMWindowSpeechSynthesis::supplementName()
{
- return static_cast<SQLTransactionSync*>(backend);
+ return "DOMWindowSpeechSynthesis";
+}
+
+// static
+DOMWindowSpeechSynthesis* DOMWindowSpeechSynthesis::from(DOMWindow* window)
+{
+ DOMWindowSpeechSynthesis* supplement = static_cast<DOMWindowSpeechSynthesis*>(Supplement<DOMWindow>::from(window, supplementName()));
+ if (!supplement) {
+ auto newSupplement = std::make_unique<DOMWindowSpeechSynthesis>(window);
+ supplement = newSupplement.get();
+ provideTo(window, supplementName(), WTFMove(newSupplement));
+ }
+ return supplement;
+}
+
+// static
+SpeechSynthesis* DOMWindowSpeechSynthesis::speechSynthesis(DOMWindow& window)
+{
+ return DOMWindowSpeechSynthesis::from(&window)->speechSynthesis();
+}
+
+SpeechSynthesis* DOMWindowSpeechSynthesis::speechSynthesis()
+{
+ if (!m_speechSynthesis && frame())
+ m_speechSynthesis = SpeechSynthesis::create();
+ return m_speechSynthesis.get();
}
} // namespace WebCore
-#endif // ENABLE(SQL_DATABASE)
+#endif // ENABLE(SPEECH_SYNTHESIS)
diff --git a/Source/WebCore/Modules/speech/DOMWindowSpeechSynthesis.h b/Source/WebCore/Modules/speech/DOMWindowSpeechSynthesis.h
new file mode 100644
index 000000000..a381093fd
--- /dev/null
+++ b/Source/WebCore/Modules/speech/DOMWindowSpeechSynthesis.h
@@ -0,0 +1,55 @@
+/*
+ * 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(SPEECH_SYNTHESIS)
+
+#include "DOMWindowProperty.h"
+#include "SpeechSynthesis.h"
+#include "Supplementable.h"
+
+namespace WebCore {
+
+class DOMWindow;
+
+class DOMWindowSpeechSynthesis : public Supplement<DOMWindow>, public DOMWindowProperty {
+public:
+ explicit DOMWindowSpeechSynthesis(DOMWindow*);
+ virtual ~DOMWindowSpeechSynthesis();
+
+ WEBCORE_EXPORT static SpeechSynthesis* speechSynthesis(DOMWindow&);
+ static DOMWindowSpeechSynthesis* from(DOMWindow*);
+
+private:
+ SpeechSynthesis* speechSynthesis();
+ static const char* supplementName();
+
+ RefPtr<SpeechSynthesis> m_speechSynthesis;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(SPEECH_SYNTHESIS)
diff --git a/Source/WebCore/Modules/mediastream/MediaTrackConstraint.idl b/Source/WebCore/Modules/speech/DOMWindowSpeechSynthesis.idl
index 54af7b920..37040777e 100644
--- a/Source/WebCore/Modules/mediastream/MediaTrackConstraint.idl
+++ b/Source/WebCore/Modules/speech/DOMWindowSpeechSynthesis.idl
@@ -10,22 +10,22 @@
* notice, this 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
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
[
- Conditional=MEDIA_STREAM,
- NoInterfaceObject,
-] interface MediaTrackConstraint {
-};
+ Conditional=SPEECH_SYNTHESIS,
+] partial interface DOMWindow {
+ readonly attribute SpeechSynthesis speechSynthesis;
+};
diff --git a/Source/WebCore/Modules/speech/SpeechSynthesis.cpp b/Source/WebCore/Modules/speech/SpeechSynthesis.cpp
new file mode 100644
index 000000000..9b632b24f
--- /dev/null
+++ b/Source/WebCore/Modules/speech/SpeechSynthesis.cpp
@@ -0,0 +1,242 @@
+/*
+ * 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 "SpeechSynthesis.h"
+
+#if ENABLE(SPEECH_SYNTHESIS)
+
+#include "EventNames.h"
+#include "PlatformSpeechSynthesisVoice.h"
+#include "PlatformSpeechSynthesizer.h"
+#include "ScriptController.h"
+#include "SpeechSynthesisEvent.h"
+#include "SpeechSynthesisUtterance.h"
+#include <wtf/CurrentTime.h>
+#include <wtf/NeverDestroyed.h>
+
+namespace WebCore {
+
+Ref<SpeechSynthesis> SpeechSynthesis::create()
+{
+ return adoptRef(*new SpeechSynthesis);
+}
+
+SpeechSynthesis::SpeechSynthesis()
+ : m_currentSpeechUtterance(nullptr)
+ , m_isPaused(false)
+#if PLATFORM(IOS)
+ , m_restrictions(RequireUserGestureForSpeechStartRestriction)
+#endif
+{
+}
+
+void SpeechSynthesis::setPlatformSynthesizer(std::unique_ptr<PlatformSpeechSynthesizer> synthesizer)
+{
+ m_platformSpeechSynthesizer = WTFMove(synthesizer);
+ m_voiceList.clear();
+ m_currentSpeechUtterance = nullptr;
+ m_utteranceQueue.clear();
+ m_isPaused = false;
+}
+
+void SpeechSynthesis::voicesDidChange()
+{
+ m_voiceList.clear();
+}
+
+const Vector<Ref<SpeechSynthesisVoice>>& SpeechSynthesis::getVoices()
+{
+ if (m_voiceList.size())
+ return m_voiceList;
+
+ if (!m_platformSpeechSynthesizer)
+ m_platformSpeechSynthesizer = std::make_unique<PlatformSpeechSynthesizer>(this);
+
+ // If the voiceList is empty, that's the cue to get the voices from the platform again.
+ for (auto& voice : m_platformSpeechSynthesizer->voiceList())
+ m_voiceList.append(SpeechSynthesisVoice::create(*voice));
+
+ return m_voiceList;
+}
+
+bool SpeechSynthesis::speaking() const
+{
+ // If we have a current speech utterance, then that means we're assumed to be in a speaking state.
+ // This state is independent of whether the utterance happens to be paused.
+ return m_currentSpeechUtterance;
+}
+
+bool SpeechSynthesis::pending() const
+{
+ // This is true if there are any utterances that have not started.
+ // That means there will be more than one in the queue.
+ return m_utteranceQueue.size() > 1;
+}
+
+bool SpeechSynthesis::paused() const
+{
+ return m_isPaused;
+}
+
+void SpeechSynthesis::startSpeakingImmediately(SpeechSynthesisUtterance& utterance)
+{
+ ASSERT(!m_currentSpeechUtterance);
+ utterance.setStartTime(monotonicallyIncreasingTime());
+ m_currentSpeechUtterance = &utterance;
+ m_isPaused = false;
+
+ // Zero lengthed strings should immediately notify that the event is complete.
+ if (utterance.text().isEmpty()) {
+ handleSpeakingCompleted(utterance, false);
+ return;
+ }
+
+ if (!m_platformSpeechSynthesizer)
+ m_platformSpeechSynthesizer = std::make_unique<PlatformSpeechSynthesizer>(this);
+ m_platformSpeechSynthesizer->speak(utterance.platformUtterance());
+}
+
+void SpeechSynthesis::speak(SpeechSynthesisUtterance& utterance)
+{
+ // Like Audio, we should require that the user interact to start a speech synthesis session.
+#if PLATFORM(IOS)
+ if (ScriptController::processingUserGesture())
+ removeBehaviorRestriction(RequireUserGestureForSpeechStartRestriction);
+ else if (userGestureRequiredForSpeechStart())
+ return;
+#endif
+
+ m_utteranceQueue.append(utterance);
+
+ // If the queue was empty, speak this immediately and add it to the queue.
+ if (m_utteranceQueue.size() == 1)
+ startSpeakingImmediately(m_utteranceQueue.first());
+}
+
+void SpeechSynthesis::cancel()
+{
+ // Remove all the items from the utterance queue.
+ // Hold on to the current utterance so the platform synthesizer can have a chance to clean up.
+ RefPtr<SpeechSynthesisUtterance> current = m_currentSpeechUtterance;
+ m_utteranceQueue.clear();
+ if (m_platformSpeechSynthesizer)
+ m_platformSpeechSynthesizer->cancel();
+ current = nullptr;
+
+ // The platform should have called back immediately and cleared the current utterance.
+ ASSERT(!m_currentSpeechUtterance);
+}
+
+void SpeechSynthesis::pause()
+{
+ if (!m_isPaused && m_platformSpeechSynthesizer)
+ m_platformSpeechSynthesizer->pause();
+}
+
+void SpeechSynthesis::resume()
+{
+ if (m_currentSpeechUtterance && m_platformSpeechSynthesizer)
+ m_platformSpeechSynthesizer->resume();
+}
+
+void SpeechSynthesis::fireEvent(const AtomicString& type, SpeechSynthesisUtterance& utterance, unsigned long charIndex, const String& name)
+{
+ utterance.dispatchEvent(SpeechSynthesisEvent::create(type, charIndex, (monotonicallyIncreasingTime() - utterance.startTime()), name));
+}
+
+void SpeechSynthesis::handleSpeakingCompleted(SpeechSynthesisUtterance& utterance, bool errorOccurred)
+{
+ ASSERT(m_currentSpeechUtterance);
+ Ref<SpeechSynthesisUtterance> protect(utterance);
+
+ m_currentSpeechUtterance = nullptr;
+
+ fireEvent(errorOccurred ? eventNames().errorEvent : eventNames().endEvent, utterance, 0, String());
+
+ if (m_utteranceQueue.size()) {
+ Ref<SpeechSynthesisUtterance> firstUtterance = m_utteranceQueue.takeFirst();
+ ASSERT(&utterance == firstUtterance.ptr());
+
+ // Start the next job if there is one pending.
+ if (!m_utteranceQueue.isEmpty())
+ startSpeakingImmediately(m_utteranceQueue.first());
+ }
+}
+
+void SpeechSynthesis::boundaryEventOccurred(PlatformSpeechSynthesisUtterance& utterance, SpeechBoundary boundary, unsigned charIndex)
+{
+ static NeverDestroyed<const String> wordBoundaryString(ASCIILiteral("word"));
+ static NeverDestroyed<const String> sentenceBoundaryString(ASCIILiteral("sentence"));
+
+ ASSERT(utterance.client());
+
+ switch (boundary) {
+ case SpeechWordBoundary:
+ fireEvent(eventNames().boundaryEvent, static_cast<SpeechSynthesisUtterance&>(*utterance.client()), charIndex, wordBoundaryString);
+ break;
+ case SpeechSentenceBoundary:
+ fireEvent(eventNames().boundaryEvent, static_cast<SpeechSynthesisUtterance&>(*utterance.client()), charIndex, sentenceBoundaryString);
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ }
+}
+
+void SpeechSynthesis::didStartSpeaking(PlatformSpeechSynthesisUtterance& utterance)
+{
+ if (utterance.client())
+ fireEvent(eventNames().startEvent, static_cast<SpeechSynthesisUtterance&>(*utterance.client()), 0, String());
+}
+
+void SpeechSynthesis::didPauseSpeaking(PlatformSpeechSynthesisUtterance& utterance)
+{
+ m_isPaused = true;
+ if (utterance.client())
+ fireEvent(eventNames().pauseEvent, static_cast<SpeechSynthesisUtterance&>(*utterance.client()), 0, String());
+}
+
+void SpeechSynthesis::didResumeSpeaking(PlatformSpeechSynthesisUtterance& utterance)
+{
+ m_isPaused = false;
+ if (utterance.client())
+ fireEvent(eventNames().resumeEvent, static_cast<SpeechSynthesisUtterance&>(*utterance.client()), 0, String());
+}
+
+void SpeechSynthesis::didFinishSpeaking(PlatformSpeechSynthesisUtterance& utterance)
+{
+ if (utterance.client())
+ handleSpeakingCompleted(static_cast<SpeechSynthesisUtterance&>(*utterance.client()), false);
+}
+
+void SpeechSynthesis::speakingErrorOccurred(PlatformSpeechSynthesisUtterance& utterance)
+{
+ if (utterance.client())
+ handleSpeakingCompleted(static_cast<SpeechSynthesisUtterance&>(*utterance.client()), true);
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(SPEECH_SYNTHESIS)
diff --git a/Source/WebCore/Modules/speech/SpeechSynthesis.h b/Source/WebCore/Modules/speech/SpeechSynthesis.h
new file mode 100644
index 000000000..d46c3181c
--- /dev/null
+++ b/Source/WebCore/Modules/speech/SpeechSynthesis.h
@@ -0,0 +1,100 @@
+/*
+ * 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(SPEECH_SYNTHESIS)
+
+#include "PlatformExportMacros.h"
+#include "PlatformSpeechSynthesisUtterance.h"
+#include "PlatformSpeechSynthesizer.h"
+#include "SpeechSynthesisUtterance.h"
+#include "SpeechSynthesisVoice.h"
+#include <wtf/Deque.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class PlatformSpeechSynthesizerClient;
+class SpeechSynthesisVoice;
+
+class SpeechSynthesis : public PlatformSpeechSynthesizerClient, public RefCounted<SpeechSynthesis> {
+public:
+ static Ref<SpeechSynthesis> create();
+
+ bool pending() const;
+ bool speaking() const;
+ bool paused() const;
+
+ void speak(SpeechSynthesisUtterance&);
+ void cancel();
+ void pause();
+ void resume();
+
+ const Vector<Ref<SpeechSynthesisVoice>>& getVoices();
+
+ // Used in testing to use a mock platform synthesizer
+ WEBCORE_EXPORT void setPlatformSynthesizer(std::unique_ptr<PlatformSpeechSynthesizer>);
+
+private:
+ SpeechSynthesis();
+
+ // PlatformSpeechSynthesizerClient override methods.
+ void voicesDidChange() override;
+ void didStartSpeaking(PlatformSpeechSynthesisUtterance&) override;
+ void didPauseSpeaking(PlatformSpeechSynthesisUtterance&) override;
+ void didResumeSpeaking(PlatformSpeechSynthesisUtterance&) override;
+ void didFinishSpeaking(PlatformSpeechSynthesisUtterance&) override;
+ void speakingErrorOccurred(PlatformSpeechSynthesisUtterance&) override;
+ void boundaryEventOccurred(PlatformSpeechSynthesisUtterance&, SpeechBoundary, unsigned charIndex) override;
+
+ void startSpeakingImmediately(SpeechSynthesisUtterance&);
+ void handleSpeakingCompleted(SpeechSynthesisUtterance&, bool errorOccurred);
+ void fireEvent(const AtomicString& type, SpeechSynthesisUtterance&, unsigned long charIndex, const String& name);
+
+#if PLATFORM(IOS)
+ // Restrictions to change default behaviors.
+ enum BehaviorRestrictionFlags {
+ NoRestrictions = 0,
+ RequireUserGestureForSpeechStartRestriction = 1 << 0,
+ };
+ typedef unsigned BehaviorRestrictions;
+
+ bool userGestureRequiredForSpeechStart() const { return m_restrictions & RequireUserGestureForSpeechStartRestriction; }
+ void removeBehaviorRestriction(BehaviorRestrictions restriction) { m_restrictions &= ~restriction; }
+#endif
+ std::unique_ptr<PlatformSpeechSynthesizer> m_platformSpeechSynthesizer;
+ Vector<Ref<SpeechSynthesisVoice>> m_voiceList;
+ SpeechSynthesisUtterance* m_currentSpeechUtterance;
+ Deque<Ref<SpeechSynthesisUtterance>> m_utteranceQueue;
+ bool m_isPaused;
+#if PLATFORM(IOS)
+ BehaviorRestrictions m_restrictions;
+#endif
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(SPEECH_SYNTHESIS)
diff --git a/Source/WebCore/Modules/speech/SpeechSynthesis.idl b/Source/WebCore/Modules/speech/SpeechSynthesis.idl
new file mode 100644
index 000000000..025003e4c
--- /dev/null
+++ b/Source/WebCore/Modules/speech/SpeechSynthesis.idl
@@ -0,0 +1,39 @@
+/*
+ * 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=SPEECH_SYNTHESIS,
+ NoInterfaceObject,
+] interface SpeechSynthesis {
+ readonly attribute boolean pending;
+ readonly attribute boolean speaking;
+ readonly attribute boolean paused;
+
+ void speak(SpeechSynthesisUtterance utterance);
+ void cancel();
+ void pause();
+ void resume();
+ sequence<SpeechSynthesisVoice> getVoices();
+};
diff --git a/Source/WebCore/Modules/webdatabase/AbstractSQLStatementBackend.h b/Source/WebCore/Modules/speech/SpeechSynthesisEvent.cpp
index 045076322..ec4a7f7b1 100644
--- a/Source/WebCore/Modules/webdatabase/AbstractSQLStatementBackend.h
+++ b/Source/WebCore/Modules/speech/SpeechSynthesisEvent.cpp
@@ -20,30 +20,29 @@
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AbstractSQLStatementBackend_h
-#define AbstractSQLStatementBackend_h
+#include "config.h"
+#include "SpeechSynthesisEvent.h"
-#if ENABLE(SQL_DATABASE)
-
-#include "SQLError.h"
-#include "SQLResultSet.h"
-#include <wtf/ThreadSafeRefCounted.h>
+#if ENABLE(SPEECH_SYNTHESIS)
namespace WebCore {
-class AbstractSQLStatementBackend : public ThreadSafeRefCounted<AbstractSQLStatementBackend> {
-public:
- virtual ~AbstractSQLStatementBackend() { }
-
- virtual PassRefPtr<SQLError> sqlError() const = 0;
- virtual PassRefPtr<SQLResultSet> sqlResultSet() const = 0;
-};
-
+Ref<SpeechSynthesisEvent> SpeechSynthesisEvent::create(const AtomicString& type, unsigned charIndex, float elapsedTime, const String& name)
+{
+ return adoptRef(*new SpeechSynthesisEvent(type, charIndex, elapsedTime, name));
+}
+
+SpeechSynthesisEvent::SpeechSynthesisEvent(const AtomicString& type, unsigned charIndex, float elapsedTime, const String& name)
+ : Event(type, false, false)
+ , m_charIndex(charIndex)
+ , m_elapsedTime(elapsedTime)
+ , m_name(name)
+{
+}
+
} // namespace WebCore
-#endif // ENABLE(SQL_DATABASE)
-
-#endif // AbstractSQLStatementBackend_h
+#endif // ENABLE(SPEECH_SYNTHESIS)
diff --git a/Source/WebCore/Modules/webdatabase/AbstractSQLTransaction.h b/Source/WebCore/Modules/speech/SpeechSynthesisEvent.h
index b009587e6..6e7204b2b 100644
--- a/Source/WebCore/Modules/webdatabase/AbstractSQLTransaction.h
+++ b/Source/WebCore/Modules/speech/SpeechSynthesisEvent.h
@@ -20,36 +20,35 @@
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AbstractSQLTransaction_h
-#define AbstractSQLTransaction_h
+#pragma once
-#if ENABLE(SQL_DATABASE)
+#if ENABLE(SPEECH_SYNTHESIS)
-#include "SQLTransactionState.h"
-#include <wtf/ThreadSafeRefCounted.h>
+#include "Event.h"
namespace WebCore {
-class AbstractSQLTransactionBackend;
-
-class AbstractSQLTransaction : public ThreadSafeRefCounted<AbstractSQLTransaction> {
+class SpeechSynthesisEvent : public Event {
public:
- virtual ~AbstractSQLTransaction() { }
+ static Ref<SpeechSynthesisEvent> create(const AtomicString& type, unsigned charIndex, float elapsedTime, const String& name);
+
+ unsigned long charIndex() const { return m_charIndex; }
+ float elapsedTime() const { return m_elapsedTime; }
+ const String& name() const { return m_name; }
- virtual void requestTransitToState(SQLTransactionState) = 0;
+ virtual EventInterface eventInterface() const { return SpeechSynthesisEventInterfaceType; }
- // Used during initialization only.
- virtual bool hasCallback() const = 0;
- virtual bool hasSuccessCallback() const = 0;
- virtual bool hasErrorCallback() const = 0;
- virtual void setBackend(AbstractSQLTransactionBackend*) = 0;
+private:
+ SpeechSynthesisEvent(const AtomicString& type, unsigned charIndex, float elapsedTime, const String& name);
+
+ unsigned long m_charIndex;
+ float m_elapsedTime;
+ String m_name;
};
} // namespace WebCore
-#endif // ENABLE(SQL_DATABASE)
-
-#endif // AbstractSQLTransaction_h
+#endif // ENABLE(SPEECH_SYNTHESIS)
diff --git a/Source/WebCore/Modules/mediastream/MediaStreamCapabilities.idl b/Source/WebCore/Modules/speech/SpeechSynthesisEvent.idl
index dd71ccbf6..8403f1398 100644
--- a/Source/WebCore/Modules/mediastream/MediaStreamCapabilities.idl
+++ b/Source/WebCore/Modules/speech/SpeechSynthesisEvent.idl
@@ -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
@@ -22,12 +22,11 @@
* (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=MEDIA_STREAM,
- NoInterfaceObject,
- SkipVTableValidation,
- CustomToJSObject,
-] interface MediaStreamCapabilities {
+ Conditional=SPEECH_SYNTHESIS
+] interface SpeechSynthesisEvent : Event {
+ readonly attribute unsigned long charIndex;
+ readonly attribute unrestricted float elapsedTime;
+ readonly attribute DOMString name;
};
-
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseBackendSync.cpp b/Source/WebCore/Modules/speech/SpeechSynthesisUtterance.cpp
index a29b483b9..6746f4ef4 100644
--- a/Source/WebCore/Modules/webdatabase/DatabaseBackendSync.cpp
+++ b/Source/WebCore/Modules/speech/SpeechSynthesisUtterance.cpp
@@ -20,43 +20,51 @@
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
-#include "DatabaseBackendSync.h"
+#include "SpeechSynthesisUtterance.h"
-#if ENABLE(SQL_DATABASE)
-
-#include "DatabaseBackendContext.h"
-#include "DatabaseTracker.h"
+#if ENABLE(SPEECH_SYNTHESIS)
namespace WebCore {
+
+Ref<SpeechSynthesisUtterance> SpeechSynthesisUtterance::create(ScriptExecutionContext& context, const String& text)
+{
+ return adoptRef(*new SpeechSynthesisUtterance(context, text));
+}
+
+SpeechSynthesisUtterance::SpeechSynthesisUtterance(ScriptExecutionContext& context, const String& text)
+ : ContextDestructionObserver(&context)
+ , m_platformUtterance(PlatformSpeechSynthesisUtterance::create(*this))
+{
+ m_platformUtterance->setText(text);
+}
-DatabaseBackendSync::DatabaseBackendSync(PassRefPtr<DatabaseBackendContext> databaseContext, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize)
- : DatabaseBackendBase(databaseContext, name, expectedVersion, displayName, estimatedSize, DatabaseType::Sync)
+SpeechSynthesisUtterance::~SpeechSynthesisUtterance()
{
+ m_platformUtterance->setClient(nullptr);
}
-DatabaseBackendSync::~DatabaseBackendSync()
+SpeechSynthesisVoice* SpeechSynthesisUtterance::voice() const
{
- // SQLite is "multi-thread safe", but each database handle can only be used
- // on a single thread at a time.
- //
- // For DatabaseBackendSync, we open the SQLite database on the script context
- // thread. And hence we should also close it on that same thread. This means
- // that the SQLite database need to be closed here in the destructor.
-
- ASSERT(m_databaseContext->isContextThread());
- if (opened())
- closeDatabase();
+ return m_voice.get();
}
-bool DatabaseBackendSync::openAndVerifyVersion(bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage)
+void SpeechSynthesisUtterance::setVoice(SpeechSynthesisVoice* voice)
{
- return performOpenAndVerify(setVersionInNewDatabase, error, errorMessage);
+ if (!voice)
+ return;
+
+ // Cache our own version of the SpeechSynthesisVoice so that we don't have to do some lookup
+ // to go from the platform voice back to the speech synthesis voice in the read property.
+ m_voice = voice;
+
+ if (voice)
+ m_platformUtterance->setVoice(voice->platformVoice());
}
} // namespace WebCore
-#endif // ENABLE(SQL_DATABASE)
+#endif // ENABLE(SPEECH_SYNTHESIS)
diff --git a/Source/WebCore/Modules/speech/SpeechSynthesisUtterance.h b/Source/WebCore/Modules/speech/SpeechSynthesisUtterance.h
new file mode 100644
index 000000000..b5cb52002
--- /dev/null
+++ b/Source/WebCore/Modules/speech/SpeechSynthesisUtterance.h
@@ -0,0 +1,84 @@
+/*
+ * 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(SPEECH_SYNTHESIS)
+
+#include "ContextDestructionObserver.h"
+#include "EventTarget.h"
+#include "PlatformSpeechSynthesisUtterance.h"
+#include "SpeechSynthesisVoice.h"
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class SpeechSynthesisUtterance final : public PlatformSpeechSynthesisUtteranceClient, public RefCounted<SpeechSynthesisUtterance>, public ContextDestructionObserver, public EventTargetWithInlineData {
+public:
+ static Ref<SpeechSynthesisUtterance> create(ScriptExecutionContext&, const String&);
+
+ virtual ~SpeechSynthesisUtterance();
+
+ const String& text() const { return m_platformUtterance->text(); }
+ void setText(const String& text) { m_platformUtterance->setText(text); }
+
+ const String& lang() const { return m_platformUtterance->lang(); }
+ void setLang(const String& lang) { m_platformUtterance->setLang(lang); }
+
+ SpeechSynthesisVoice* voice() const;
+ void setVoice(SpeechSynthesisVoice*);
+
+ float volume() const { return m_platformUtterance->volume(); }
+ void setVolume(float volume) { m_platformUtterance->setVolume(volume); }
+
+ float rate() const { return m_platformUtterance->rate(); }
+ void setRate(float rate) { m_platformUtterance->setRate(rate); }
+
+ float pitch() const { return m_platformUtterance->pitch(); }
+ void setPitch(float pitch) { m_platformUtterance->setPitch(pitch); }
+
+ double startTime() const { return m_platformUtterance->startTime(); }
+ void setStartTime(double startTime) { m_platformUtterance->setStartTime(startTime); }
+
+ using RefCounted::ref;
+ using RefCounted::deref;
+
+ PlatformSpeechSynthesisUtterance* platformUtterance() const { return m_platformUtterance.get(); }
+
+private:
+ SpeechSynthesisUtterance(ScriptExecutionContext&, const String&);
+
+ ScriptExecutionContext* scriptExecutionContext() const final { return ContextDestructionObserver::scriptExecutionContext(); }
+ EventTargetInterface eventTargetInterface() const final { return SpeechSynthesisUtteranceEventTargetInterfaceType; }
+ void refEventTarget() final { ref(); }
+ void derefEventTarget() final { deref(); }
+
+ RefPtr<PlatformSpeechSynthesisUtterance> m_platformUtterance;
+ RefPtr<SpeechSynthesisVoice> m_voice;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(SPEECH_SYNTHESIS)
diff --git a/Source/WebCore/Modules/speech/SpeechSynthesisUtterance.idl b/Source/WebCore/Modules/speech/SpeechSynthesisUtterance.idl
new file mode 100644
index 000000000..e781f5244
--- /dev/null
+++ b/Source/WebCore/Modules/speech/SpeechSynthesisUtterance.idl
@@ -0,0 +1,45 @@
+/*
+ * 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=SPEECH_SYNTHESIS,
+ ConstructorCallWith=ScriptExecutionContext,
+ Constructor(optional DOMString text)
+] interface SpeechSynthesisUtterance : EventTarget {
+ attribute DOMString text;
+ attribute DOMString lang;
+ attribute SpeechSynthesisVoice? voice; // FIXME: should not be nullable.
+ attribute unrestricted float volume;
+ attribute unrestricted float rate;
+ attribute unrestricted float pitch;
+
+ attribute EventHandler onstart;
+ attribute EventHandler onend;
+ attribute EventHandler onerror;
+ attribute EventHandler onpause;
+ attribute EventHandler onresume;
+ attribute EventHandler onmark;
+ attribute EventHandler onboundary;
+};
diff --git a/Source/WebCore/Modules/speech/SpeechSynthesisVoice.cpp b/Source/WebCore/Modules/speech/SpeechSynthesisVoice.cpp
new file mode 100644
index 000000000..fccd7fd64
--- /dev/null
+++ b/Source/WebCore/Modules/speech/SpeechSynthesisVoice.cpp
@@ -0,0 +1,45 @@
+/*
+ * 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 "SpeechSynthesisVoice.h"
+
+#if ENABLE(SPEECH_SYNTHESIS)
+
+namespace WebCore {
+
+Ref<SpeechSynthesisVoice> SpeechSynthesisVoice::create(PlatformSpeechSynthesisVoice& voice)
+{
+ return adoptRef(*new SpeechSynthesisVoice(voice));
+}
+
+SpeechSynthesisVoice::SpeechSynthesisVoice(PlatformSpeechSynthesisVoice& voice)
+ : m_platformVoice(voice)
+{
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(SPEECH_SYNTHESIS)
diff --git a/Source/WebCore/Modules/webdatabase/AbstractSQLTransactionBackend.h b/Source/WebCore/Modules/speech/SpeechSynthesisVoice.h
index 01d9c95dd..7fade0d1d 100644
--- a/Source/WebCore/Modules/webdatabase/AbstractSQLTransactionBackend.h
+++ b/Source/WebCore/Modules/speech/SpeechSynthesisVoice.h
@@ -20,42 +20,38 @@
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AbstractSQLTransactionBackend_h
-#define AbstractSQLTransactionBackend_h
+#pragma once
-#if ENABLE(SQL_DATABASE)
+#if ENABLE(SPEECH_SYNTHESIS)
-#include "AbstractSQLStatement.h"
-#include "SQLError.h"
-#include "SQLTransactionState.h"
-#include "SQLValue.h"
-#include <wtf/PassOwnPtr.h>
-#include <wtf/ThreadSafeRefCounted.h>
-#include <wtf/Vector.h>
+#include "PlatformSpeechSynthesisVoice.h"
+#include <wtf/RefCounted.h>
#include <wtf/text/WTFString.h>
namespace WebCore {
-class AbstractSQLTransactionBackend : public ThreadSafeRefCounted<AbstractSQLTransactionBackend> {
+class SpeechSynthesisVoice : public RefCounted<SpeechSynthesisVoice> {
public:
- virtual ~AbstractSQLTransactionBackend() { }
+ virtual ~SpeechSynthesisVoice() { }
+ static Ref<SpeechSynthesisVoice> create(PlatformSpeechSynthesisVoice&);
- virtual void requestTransitToState(SQLTransactionState) = 0;
+ const String& voiceURI() const { return m_platformVoice->voiceURI(); }
+ const String& name() const { return m_platformVoice->name(); }
+ const String& lang() const { return m_platformVoice->lang(); }
+ bool localService() const { return m_platformVoice->localService(); }
+ bool isDefault() const { return m_platformVoice->isDefault(); }
- virtual PassRefPtr<SQLError> transactionError() = 0;
- virtual AbstractSQLStatement* currentStatement() = 0;
- virtual void setShouldRetryCurrentStatement(bool) = 0;
+ PlatformSpeechSynthesisVoice* platformVoice() { return m_platformVoice.ptr(); }
- virtual void executeSQL(PassOwnPtr<AbstractSQLStatement>, const String& statement,
- const Vector<SQLValue>& arguments, int permissions) = 0;
+private:
+ explicit SpeechSynthesisVoice(PlatformSpeechSynthesisVoice&);
+ Ref<PlatformSpeechSynthesisVoice> m_platformVoice;
};
} // namespace WebCore
-#endif // ENABLE(SQL_DATABASE)
-
-#endif // AbstractSQLTransactionBackend_h
+#endif // ENABLE(SPEECH_SYNTHESIS)
diff --git a/Source/WebCore/Modules/mediastream/CapabilityRange.idl b/Source/WebCore/Modules/speech/SpeechSynthesisVoice.idl
index 3dc6f13f0..9aa489e05 100644
--- a/Source/WebCore/Modules/mediastream/CapabilityRange.idl
+++ b/Source/WebCore/Modules/speech/SpeechSynthesisVoice.idl
@@ -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
@@ -22,12 +22,14 @@
* (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=MEDIA_STREAM,
-] interface CapabilityRange {
- [CallWith=ScriptState] readonly attribute any max;
- [CallWith=ScriptState] readonly attribute any min;
- readonly attribute boolean supported;
+ Conditional=SPEECH_SYNTHESIS
+] interface SpeechSynthesisVoice {
+ readonly attribute DOMString voiceURI;
+ readonly attribute DOMString name;
+ readonly attribute DOMString lang;
+ readonly attribute boolean localService;
+ [ImplementedAs=isDefault] readonly attribute boolean default;
};
diff --git a/Source/WebCore/Modules/streams/ByteLengthQueuingStrategy.idl b/Source/WebCore/Modules/streams/ByteLengthQueuingStrategy.idl
new file mode 100644
index 000000000..ac6ba9495
--- /dev/null
+++ b/Source/WebCore/Modules/streams/ByteLengthQueuingStrategy.idl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 Canon Inc.
+ * Copyright (C) 2015 Igalia S.L.
+ *
+ * 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.
+ */
+
+[
+ JSBuiltin,
+ Constructor,
+ Conditional=READABLE_STREAM_API|WRITABLE_STREAM_API,
+ Exposed=(Window,Worker),
+] interface ByteLengthQueuingStrategy {
+ double size();
+};
diff --git a/Source/WebCore/Modules/streams/ByteLengthQueuingStrategy.js b/Source/WebCore/Modules/streams/ByteLengthQueuingStrategy.js
new file mode 100644
index 000000000..3dd793700
--- /dev/null
+++ b/Source/WebCore/Modules/streams/ByteLengthQueuingStrategy.js
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2015 Canon Inc.
+ * Copyright (C) 2015 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 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(READABLE_STREAM_API) || ENABLE(WRITABLE_STREAM_API)
+
+function size(chunk)
+{
+ "use strict";
+
+ return chunk.byteLength;
+}
+
+function initializeByteLengthQueuingStrategy(parameters)
+{
+ "use strict";
+
+ @Object.@defineProperty(this, "highWaterMark", {
+ value: parameters.highWaterMark,
+ configurable: true,
+ enumerable: true,
+ writable: true
+ });
+}
diff --git a/Source/WebCore/Modules/streams/CountQueuingStrategy.idl b/Source/WebCore/Modules/streams/CountQueuingStrategy.idl
new file mode 100644
index 000000000..3eee7a284
--- /dev/null
+++ b/Source/WebCore/Modules/streams/CountQueuingStrategy.idl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 Canon Inc.
+ * Copyright (C) 2015 Igalia S.L.
+ *
+ * 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.
+ */
+
+[
+ JSBuiltin,
+ Constructor,
+ Conditional=READABLE_STREAM_API|WRITABLE_STREAM_API,
+ Exposed=(Window,Worker),
+] interface CountQueuingStrategy {
+ double size();
+};
diff --git a/Source/WebCore/Modules/streams/CountQueuingStrategy.js b/Source/WebCore/Modules/streams/CountQueuingStrategy.js
new file mode 100644
index 000000000..ab6697836
--- /dev/null
+++ b/Source/WebCore/Modules/streams/CountQueuingStrategy.js
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 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 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(READABLE_STREAM_API) || ENABLE(WRITABLE_STREAM_API)
+
+function size()
+{
+ "use strict";
+
+ return 1;
+}
+
+function initializeCountQueuingStrategy(parameters)
+{
+ "use strict";
+
+ @Object.@defineProperty(this, "highWaterMark", {
+ value: parameters.highWaterMark,
+ configurable: true,
+ enumerable: true,
+ writable: true
+ });
+}
diff --git a/Source/WebCore/Modules/streams/ReadableByteStreamController.idl b/Source/WebCore/Modules/streams/ReadableByteStreamController.idl
new file mode 100644
index 000000000..d0dc23f01
--- /dev/null
+++ b/Source/WebCore/Modules/streams/ReadableByteStreamController.idl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 Canon Inc.
+ * Copyright (C) 2015 Igalia S.L.
+ *
+ * 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=READABLE_STREAM_API&READABLE_BYTE_STREAM_API,
+ CustomConstructor(ReadableStream stream, any underlyingByteSource, unsigned long highWaterMark),
+ Exposed=(Window,Worker),
+ JSBuiltin,
+ NoInterfaceObject
+] interface ReadableByteStreamController {
+ [NotEnumerable] void enqueue(optional any chunk);
+ [NotEnumerable] void close();
+ [NotEnumerable] void error(optional any error);
+
+ //FIXME: Add byobRequest when implemented.
+ [NotEnumerable] readonly attribute double desiredSize;
+};
diff --git a/Source/WebCore/Modules/streams/ReadableByteStreamController.js b/Source/WebCore/Modules/streams/ReadableByteStreamController.js
new file mode 100644
index 000000000..c2feb1579
--- /dev/null
+++ b/Source/WebCore/Modules/streams/ReadableByteStreamController.js
@@ -0,0 +1,95 @@
+/*
+ * 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 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(READABLE_STREAM_API) && ENABLE(READABLE_BYTE_STREAM_API)
+
+function enqueue(chunk)
+{
+ "use strict";
+
+ if (!@isReadableByteStreamController(this))
+ throw @makeThisTypeError("ReadableByteStreamController", "enqueue");
+
+ if (this.@closeRequested)
+ @throwTypeError("ReadableByteStreamController is requested to close");
+
+ if (this.@controlledReadableStream.@state !== @streamReadable)
+ @throwTypeError("ReadableStream is not readable");
+
+ if (!@isObject(chunk))
+ @throwTypeError("Provided chunk is not an object");
+
+ if (!@ArrayBuffer.@isView(chunk))
+ @throwTypeError("Provided chunk is not an ArrayBuffer view");
+
+ return @readableByteStreamControllerEnqueue(this, chunk);
+}
+
+function error(error)
+{
+ "use strict";
+
+ if (!@isReadableByteStreamController(this))
+ throw @makeThisTypeError("ReadableByteStreamController", "error");
+
+ if (this.@controlledReadableStream.@state !== @streamReadable)
+ @throwTypeError("ReadableStream is not readable");
+
+ @readableByteStreamControllerError(this, error);
+}
+
+function close()
+{
+ "use strict";
+
+ if (!@isReadableByteStreamController(this))
+ throw @makeThisTypeError("ReadableByteStreamController", "close");
+
+ if (this.@closeRequested)
+ @throwTypeError("Close has already been requested");
+
+ if (this.@controlledReadableStream.@state !== @streamReadable)
+ @throwTypeError("ReadableStream is not readable");
+
+ @readableByteStreamControllerClose(this);
+}
+
+function byobRequest()
+{
+ "use strict";
+
+ //FIXME: Implement appropriate behavior.
+ @throwTypeError("ReadableByteStreamController byobRequest is not implemented");
+}
+
+function desiredSize()
+{
+ "use strict";
+
+ if (!@isReadableByteStreamController(this))
+ throw @makeGetterTypeError("ReadableByteStreamController", "desiredSize");
+
+ return @readableByteStreamControllerGetDesiredSize(this);
+}
diff --git a/Source/WebCore/Modules/streams/ReadableByteStreamInternals.js b/Source/WebCore/Modules/streams/ReadableByteStreamInternals.js
new file mode 100644
index 000000000..3e7354560
--- /dev/null
+++ b/Source/WebCore/Modules/streams/ReadableByteStreamInternals.js
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2016 Canon Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this 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(READABLE_STREAM_API) && ENABLE(READABLE_BYTE_STREAM_API)
+// @internal
+
+function privateInitializeReadableByteStreamController(stream, underlyingByteSource, highWaterMark)
+{
+ "use strict";
+
+ if (!@isReadableStream(stream))
+ @throwTypeError("ReadableByteStreamController needs a ReadableStream");
+
+ // readableStreamController is initialized with null value.
+ if (stream.@readableStreamController !== null)
+ @throwTypeError("ReadableStream already has a controller");
+
+ this.@controlledReadableStream = stream;
+ this.@underlyingByteSource = underlyingByteSource;
+ this.@pullAgain = false;
+ this.@pulling = false;
+ @readableByteStreamControllerClearPendingPullIntos(this);
+ this.@queue = [];
+ this.@totalQueuedBytes = 0;
+ this.@started = false;
+ this.@closeRequested = false;
+
+ let hwm = @Number(highWaterMark);
+ if (@isNaN(hwm) || hwm < 0)
+ @throwRangeError("highWaterMark value is negative or not a number");
+ this.@strategyHWM = hwm;
+
+ let autoAllocateChunkSize = underlyingByteSource.autoAllocateChunkSize;
+ if (autoAllocateChunkSize !== @undefined) {
+ autoAllocateChunkSize = @Number(autoAllocateChunkSize);
+ if (autoAllocateChunkSize <= 0 || autoAllocateChunkSize === @Number.POSITIVE_INFINITY || autoAllocateChunkSize === @Number.NEGATIVE_INFINITY)
+ @throwRangeError("autoAllocateChunkSize value is negative or equal to positive or negative infinity");
+ }
+ this.@autoAllocateChunkSize = autoAllocateChunkSize;
+ this.@pendingPullIntos = [];
+
+ const controller = this;
+ const startResult = @promiseInvokeOrNoopNoCatch(underlyingByteSource, "start", [this]).@then(() => {
+ controller.@started = true;
+ @assert(!controller.@pulling);
+ @assert(!controller.@pullAgain);
+ @readableByteStreamControllerCallPullIfNeeded(controller);
+ }, (error) => {
+ if (stream.@state === @streamReadable)
+ @readableByteStreamControllerError(controller, error);
+ });
+
+ this.@cancel = @readableByteStreamControllerCancel;
+ this.@pull = @readableByteStreamControllerPull;
+
+ return this;
+}
+
+function isReadableByteStreamController(controller)
+{
+ "use strict";
+
+ // Same test mechanism as in isReadableStreamDefaultController (ReadableStreamInternals.js).
+ // See corresponding function for explanations.
+ return @isObject(controller) && !!controller.@underlyingByteSource;
+}
+
+function isReadableStreamBYOBReader(reader)
+{
+ "use strict";
+
+ // FIXME: Since BYOBReader is not yet implemented, always return false.
+ // To be implemented at the same time as BYOBReader (see isReadableStreamDefaultReader
+ // to apply same model).
+ return false;
+}
+
+function readableByteStreamControllerCancel(controller, reason)
+{
+ "use strict";
+
+ if (controller.@pendingPullIntos.length > 0)
+ controller.@pendingPullIntos[0].bytesFilled = 0;
+ controller.@queue = [];
+ controller.@totalQueuedBytes = 0;
+ return @promiseInvokeOrNoop(controller.@underlyingByteSource, "cancel", [reason]);
+}
+
+function readableByteStreamControllerError(controller, e)
+{
+ "use strict";
+
+ @assert(controller.@controlledReadableStream.@state === @streamReadable);
+ @readableByteStreamControllerClearPendingPullIntos(controller);
+ controller.@queue = [];
+ @readableStreamError(controller.@controlledReadableStream, e);
+}
+
+function readableByteStreamControllerClose(controller)
+{
+ "use strict";
+
+ @assert(!controller.@closeRequested);
+ @assert(controller.@controlledReadableStream.@state === @streamReadable);
+
+ if (controller.@totalQueuedBytes > 0) {
+ controller.@closeRequested = true;
+ return;
+ }
+
+ if (controller.@pendingPullIntos.length > 0) {
+ if (controller.@pendingPullIntos[0].bytesFilled > 0) {
+ const e = new @TypeError("Close requested while there remain pending bytes");
+ @readableByteStreamControllerError(controller, e);
+ throw e;
+ }
+ }
+
+ @readableStreamClose(controller.@controlledReadableStream);
+}
+
+function readableByteStreamControllerClearPendingPullIntos(controller)
+{
+ "use strict";
+
+ // FIXME: To be implemented in conjunction with ReadableStreamBYOBRequest.
+}
+
+function readableByteStreamControllerGetDesiredSize(controller)
+{
+ "use strict";
+
+ return controller.@strategyHWM - controller.@totalQueuedBytes;
+}
+
+function readableStreamHasBYOBReader(stream)
+{
+ "use strict";
+
+ return stream.@reader !== @undefined && @isReadableStreamBYOBReader(stream.@reader);
+}
+
+function readableStreamHasDefaultReader(stream)
+{
+ "use strict";
+
+ return stream.@reader !== @undefined && @isReadableStreamDefaultReader(stream.@reader);
+}
+
+function readableByteStreamControllerHandleQueueDrain(controller) {
+
+ "use strict";
+
+ @assert(controller.@controlledReadableStream.@state === @streamReadable);
+ if (!controller.@totalQueuedBytes && controller.@closeRequested)
+ @readableStreamClose(controller.@controlledReadableStream);
+ else
+ @readableByteStreamControllerCallPullIfNeeded(controller);
+}
+
+function readableByteStreamControllerPull(controller)
+{
+ "use strict";
+
+ const stream = controller.@controlledReadableStream;
+ @assert(@readableStreamHasDefaultReader(stream));
+
+ if (controller.@totalQueuedBytes > 0) {
+ @assert(stream.@reader.@readRequests.length === 0);
+ const entry = controller.@queue.@shift();
+ controller.@totalQueuedBytes -= entry.byteLength;
+ @readableByteStreamControllerHandleQueueDrain(controller);
+ let view;
+ try {
+ view = new @Uint8Array(entry.buffer, entry.byteOffset, entry.byteLength);
+ } catch (error) {
+ return @Promise.@reject(error);
+ }
+ return @Promise.@resolve({value: view, done: false});
+ }
+
+ if (controller.@autoAllocateChunkSize !== @undefined) {
+ let buffer;
+ try {
+ buffer = new @ArrayBuffer(controller.@autoAllocateChunkSize);
+ } catch (error) {
+ return @Promise.@reject(error);
+ }
+ const pullIntoDescriptor = {
+ buffer,
+ byteOffset: 0,
+ byteLength: controller.@autoAllocateChunkSize,
+ bytesFilled: 0,
+ elementSize: 1,
+ ctor: @Uint8Array,
+ readerType: 'default'
+ };
+ controller.@pendingPullIntos.@push(pullIntoDescriptor);
+ }
+
+ const promise = @readableStreamAddReadRequest(stream);
+ @readableByteStreamControllerCallPullIfNeeded(controller);
+ return promise;
+}
+
+function readableByteStreamControllerShouldCallPull(controller)
+{
+ "use strict";
+
+ const stream = controller.@controlledReadableStream;
+
+ if (stream.@state !== @streamReadable)
+ return false;
+ if (controller.@closeRequested)
+ return false;
+ if (!controller.@started)
+ return false;
+ if (@readableStreamHasDefaultReader(stream) && stream.@reader.@readRequests.length > 0)
+ return true;
+ if (@readableStreamHasBYOBReader(stream) && stream.@reader.@readIntoRequests.length > 0)
+ return true;
+ if (@readableByteStreamControllerGetDesiredSize(controller) > 0)
+ return true;
+ return false;
+}
+
+function readableByteStreamControllerCallPullIfNeeded(controller)
+{
+ "use strict";
+
+ if (!@readableByteStreamControllerShouldCallPull(controller))
+ return;
+
+ if (controller.@pulling) {
+ controller.@pullAgain = true;
+ return;
+ }
+
+ @assert(!controller.@pullAgain);
+ controller.@pulling = true;
+ @promiseInvokeOrNoop(controller.@underlyingByteSource, "pull", [controller]).@then(() => {
+ controller.@pulling = false;
+ if (controller.@pullAgain) {
+ controller.@pullAgain = false;
+ @readableByteStreamControllerCallPullIfNeeded(controller);
+ }
+ }, (error) => {
+ if (controller.@controlledReadableStream.@state === @streamReadable)
+ @readableByteStreamControllerError(controller, error);
+ });
+}
+
+function transferBufferToCurrentRealm(buffer)
+{
+ "use strict";
+
+ // FIXME: Determine what should be done here exactly (what is already existing in current
+ // codebase and what has to be added). According to spec, Transfer operation should be
+ // performed in order to transfer buffer to current realm. For the moment, simply return
+ // received buffer.
+ return buffer;
+}
+
+function readableByteStreamControllerEnqueue(controller, chunk)
+{
+ "use strict";
+
+ const stream = controller.@controlledReadableStream;
+ @assert(!controller.@closeRequested);
+ @assert(stream.@state === @streamReadable);
+ const buffer = chunk.buffer;
+ const byteOffset = chunk.byteOffset;
+ const byteLength = chunk.byteLength;
+ const transferredBuffer = @transferBufferToCurrentRealm(buffer);
+
+ if (@readableStreamHasDefaultReader(stream)) {
+ if (!stream.@reader.@readRequests.length)
+ @readableByteStreamControllerEnqueueChunkToQueue(controller, transferredBuffer, byteOffset, byteLength);
+ else {
+ @assert(!controller.@queue.length);
+ let transferredView = new @Uint8Array(transferredBuffer, byteOffset, byteLength);
+ @readableStreamFulfillReadRequest(stream, transferredView, false);
+ }
+ return;
+ }
+
+ if (@readableStreamHasBYOBReader(stream)) {
+ // FIXME: To be implemented once ReadableStreamBYOBReader has been implemented (for the moment,
+ // test cannot be true).
+ @throwTypeError("ReadableByteStreamController enqueue operation has no support for BYOB reader");
+ return;
+ }
+
+ @assert(!@isReadableStreamLocked(stream));
+ @readableByteStreamControllerEnqueueChunkToQueue(controller, transferredBuffer, byteOffset, byteLength);
+}
+
+function readableByteStreamControllerEnqueueChunkToQueue(controller, buffer, byteOffset, byteLength)
+{
+ "use strict";
+
+ controller.@queue.@push({
+ buffer: buffer,
+ byteOffset: byteOffset,
+ byteLength: byteLength
+ });
+ controller.@totalQueuedBytes += byteLength;
+}
diff --git a/Source/WebCore/Modules/streams/ReadableStream.idl b/Source/WebCore/Modules/streams/ReadableStream.idl
new file mode 100644
index 000000000..3d841481b
--- /dev/null
+++ b/Source/WebCore/Modules/streams/ReadableStream.idl
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 Canon Inc.
+ * Copyright (C) 2015 Igalia S.L.
+ *
+ * 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=READABLE_STREAM_API,
+ Constructor(optional any underlyingSource, optional any options),
+ Exposed=(Window,Worker),
+ JSBuiltin,
+ PrivateIdentifier,
+ PublicIdentifier
+] interface ReadableStream {
+ [NotEnumerable] Promise<any> cancel(optional any reason);
+ [NotEnumerable] Object getReader(optional any options);
+ [NotEnumerable] Promise<any> pipeTo(any streams, optional any options);
+ [NotEnumerable] Object pipeThrough(any dest, any options);
+ [NotEnumerable] Object tee();
+
+ [NotEnumerable] readonly attribute boolean locked;
+};
diff --git a/Source/WebCore/Modules/streams/ReadableStream.js b/Source/WebCore/Modules/streams/ReadableStream.js
new file mode 100644
index 000000000..faf6c1611
--- /dev/null
+++ b/Source/WebCore/Modules/streams/ReadableStream.js
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2015 Canon Inc.
+ * Copyright (C) 2015 Igalia.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this 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(READABLE_STREAM_API)
+
+function initializeReadableStream(underlyingSource, strategy)
+{
+ "use strict";
+
+ if (underlyingSource === @undefined)
+ underlyingSource = { };
+ if (strategy === @undefined)
+ strategy = { highWaterMark: 1, size: function() { return 1; } };
+
+ if (!@isObject(underlyingSource))
+ @throwTypeError("ReadableStream constructor takes an object as first argument");
+
+ if (strategy !== @undefined && !@isObject(strategy))
+ @throwTypeError("ReadableStream constructor takes an object as second argument, if any");
+
+ this.@state = @streamReadable;
+ this.@reader = @undefined;
+ this.@storedError = @undefined;
+ this.@disturbed = false;
+ // Initialized with null value to enable distinction with undefined case.
+ this.@readableStreamController = null;
+
+ const type = underlyingSource.type;
+ const typeString = @String(type);
+
+ if (typeString === "bytes") {
+ if (strategy.highWaterMark === @undefined)
+ strategy.highWaterMark = 0;
+ // FIXME: When ReadableByteStreamController is no more dependent on a compile flag, specific error handling can be removed.
+ // Constructor is not necessarily available if the byteStream part of Readeable Stream API is not activated. Therefore, a
+ // specific handling of error is done.
+ try {
+ let readableByteStreamControllerConstructor = @ReadableByteStreamController;
+ } catch (e) {
+ @throwTypeError("ReadableByteStreamController is not implemented");
+ }
+ this.@readableStreamController = new @ReadableByteStreamController(this, underlyingSource, strategy.highWaterMark);
+ } else if (type === @undefined) {
+ if (strategy.highWaterMark === @undefined)
+ strategy.highWaterMark = 1;
+ this.@readableStreamController = new @ReadableStreamDefaultController(this, underlyingSource, strategy.size, strategy.highWaterMark);
+ } else
+ @throwRangeError("Invalid type for underlying source");
+
+ return this;
+}
+
+function cancel(reason)
+{
+ "use strict";
+
+ if (!@isReadableStream(this))
+ return @Promise.@reject(@makeThisTypeError("ReadableStream", "cancel"));
+
+ if (@isReadableStreamLocked(this))
+ return @Promise.@reject(new @TypeError("ReadableStream is locked"));
+
+ return @readableStreamCancel(this, reason);
+}
+
+function getReader(options)
+{
+ "use strict";
+
+ if (!@isReadableStream(this))
+ throw @makeThisTypeError("ReadableStream", "getReader");
+
+ if (options === @undefined)
+ options = { };
+
+ if (options.mode === 'byob') {
+ // FIXME: Update once ReadableByteStreamContoller and ReadableStreamBYOBReader are implemented.
+ @throwTypeError("ReadableStreamBYOBReader is not implemented");
+ }
+
+ if (options.mode === @undefined)
+ return new @ReadableStreamDefaultReader(this);
+
+ @throwRangeError("Invalid mode is specified");
+}
+
+function pipeThrough(streams, options)
+{
+ "use strict";
+
+ const writable = streams.writable;
+ const readable = streams.readable;
+ this.pipeTo(writable, options);
+ return readable;
+}
+
+function pipeTo(destination)
+{
+ "use strict";
+
+ // FIXME: https://bugs.webkit.org/show_bug.cgi?id=159869.
+ // Built-in generator should be able to parse function signature to compute the function length correctly.
+ const options = arguments[1];
+
+ // FIXME: rewrite pipeTo so as to require to have 'this' as a ReadableStream and destination be a WritableStream.
+ // See https://github.com/whatwg/streams/issues/407.
+ // We should shield the pipeTo implementation at the same time.
+
+ const preventClose = @isObject(options) && !!options.preventClose;
+ const preventAbort = @isObject(options) && !!options.preventAbort;
+ const preventCancel = @isObject(options) && !!options.preventCancel;
+
+ const source = this;
+
+ let reader;
+ let lastRead;
+ let lastWrite;
+ let closedPurposefully = false;
+ let promiseCapability;
+
+ function doPipe() {
+ lastRead = reader.read();
+ @Promise.prototype.@then.@call(@Promise.all([lastRead, destination.ready]), function([{ value, done }]) {
+ if (done)
+ closeDestination();
+ else if (destination.state === "writable") {
+ lastWrite = destination.write(value);
+ doPipe();
+ }
+ }, function(e) {
+ throw e;
+ });
+ }
+
+ function cancelSource(reason) {
+ if (!preventCancel) {
+ reader.cancel(reason);
+ reader.releaseLock();
+ promiseCapability.@reject.@call(@undefined, reason);
+ } else {
+ @Promise.prototype.@then.@call(lastRead, function() {
+ reader.releaseLock();
+ promiseCapability.@reject.@call(@undefined, reason);
+ });
+ }
+ }
+
+ function closeDestination() {
+ reader.releaseLock();
+
+ const destinationState = destination.state;
+ if (!preventClose && (destinationState === "waiting" || destinationState === "writable")) {
+ closedPurposefully = true;
+ @Promise.prototype.@then.@call(destination.close(), promiseCapability.@resolve, promiseCapability.@reject);
+ } else if (lastWrite !== @undefined)
+ @Promise.prototype.@then.@call(lastWrite, promiseCapability.@resolve, promiseCapability.@reject);
+ else
+ promiseCapability.@resolve.@call();
+
+ }
+
+ function abortDestination(reason) {
+ reader.releaseLock();
+
+ if (!preventAbort)
+ destination.abort(reason);
+ promiseCapability.@reject.@call(@undefined, reason);
+ }
+
+ promiseCapability = @newPromiseCapability(@Promise);
+
+ reader = source.getReader();
+
+ @Promise.prototype.@then.@call(reader.closed, @undefined, abortDestination);
+ @Promise.prototype.@then.@call(destination.closed,
+ function() {
+ if (!closedPurposefully)
+ cancelSource(new @TypeError('destination is closing or closed and cannot be piped to anymore'));
+ },
+ cancelSource
+ );
+
+ doPipe();
+
+ return promiseCapability.@promise;
+}
+
+function tee()
+{
+ "use strict";
+
+ if (!@isReadableStream(this))
+ throw @makeThisTypeError("ReadableStream", "tee");
+
+ return @readableStreamTee(this, false);
+}
+
+function locked()
+{
+ "use strict";
+
+ if (!@isReadableStream(this))
+ throw @makeGetterTypeError("ReadableStream", "locked");
+
+ return @isReadableStreamLocked(this);
+}
diff --git a/Source/WebCore/Modules/streams/ReadableStreamDefaultController.idl b/Source/WebCore/Modules/streams/ReadableStreamDefaultController.idl
new file mode 100644
index 000000000..9d05c952a
--- /dev/null
+++ b/Source/WebCore/Modules/streams/ReadableStreamDefaultController.idl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 Canon Inc.
+ * Copyright (C) 2015 Igalia S.L.
+ *
+ * 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=READABLE_STREAM_API,
+ CustomConstructor(ReadableStream stream, any underlyingSource, unsigned long size, unsigned long highWaterMark),
+ Exposed=(Window,Worker),
+ JSBuiltin,
+ NoInterfaceObject
+] interface ReadableStreamDefaultController {
+ [NotEnumerable] void enqueue(optional any chunk);
+ [NotEnumerable] void close();
+ [NotEnumerable] void error(optional any error);
+
+ [NotEnumerable] readonly attribute double desiredSize;
+};
diff --git a/Source/WebCore/Modules/streams/ReadableStreamDefaultController.js b/Source/WebCore/Modules/streams/ReadableStreamDefaultController.js
new file mode 100644
index 000000000..e9c94998a
--- /dev/null
+++ b/Source/WebCore/Modules/streams/ReadableStreamDefaultController.js
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2015 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 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(READABLE_STREAM_API)
+
+function enqueue(chunk)
+{
+ "use strict";
+
+ if (!@isReadableStreamDefaultController(this))
+ throw @makeThisTypeError("ReadableStreamDefaultController", "enqueue");
+
+ if (this.@closeRequested)
+ @throwTypeError("ReadableStreamDefaultController is requested to close");
+
+ if (this.@controlledReadableStream.@state !== @streamReadable)
+ @throwTypeError("ReadableStream is not readable");
+
+ return @readableStreamDefaultControllerEnqueue(this, chunk);
+}
+
+function error(error)
+{
+ "use strict";
+
+ if (!@isReadableStreamDefaultController(this))
+ throw @makeThisTypeError("ReadableStreamDefaultController", "error");
+
+ if (this.@controlledReadableStream.@state !== @streamReadable)
+ @throwTypeError("ReadableStream is not readable");
+
+ @readableStreamDefaultControllerError(this, error);
+}
+
+function close()
+{
+ "use strict";
+
+ if (!@isReadableStreamDefaultController(this))
+ throw @makeThisTypeError("ReadableStreamDefaultController", "close");
+
+ if (this.@closeRequested)
+ @throwTypeError("ReadableStreamDefaultController is already requested to close");
+
+ if (this.@controlledReadableStream.@state !== @streamReadable)
+ @throwTypeError("ReadableStream is not readable");
+
+ @readableStreamDefaultControllerClose(this);
+}
+
+function desiredSize()
+{
+ "use strict";
+
+ if (!@isReadableStreamDefaultController(this))
+ throw @makeGetterTypeError("ReadableStreamDefaultController", "desiredSize");
+
+ return @readableStreamDefaultControllerGetDesiredSize(this);
+}
+
diff --git a/Source/WebCore/Modules/streams/ReadableStreamDefaultReader.idl b/Source/WebCore/Modules/streams/ReadableStreamDefaultReader.idl
new file mode 100644
index 000000000..fd2926fc4
--- /dev/null
+++ b/Source/WebCore/Modules/streams/ReadableStreamDefaultReader.idl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 Canon Inc.
+ * Copyright (C) 2015 Igalia S.L.
+ *
+ * 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=READABLE_STREAM_API,
+ CustomConstructor(ReadableStream stream),
+ Exposed=(Window,Worker),
+ JSBuiltin,
+ NoInterfaceObject
+] interface ReadableStreamDefaultReader {
+ [NotEnumerable] Promise<any> read();
+ [NotEnumerable] Promise<any> cancel(optional any reason);
+ [NotEnumerable] void releaseLock();
+
+ [NotEnumerable] readonly attribute Promise<boolean> closed;
+};
diff --git a/Source/WebCore/Modules/streams/ReadableStreamDefaultReader.js b/Source/WebCore/Modules/streams/ReadableStreamDefaultReader.js
new file mode 100644
index 000000000..d5a51c3a0
--- /dev/null
+++ b/Source/WebCore/Modules/streams/ReadableStreamDefaultReader.js
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2015 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 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(READABLE_STREAM_API)
+
+function cancel(reason)
+{
+ "use strict";
+
+ if (!@isReadableStreamDefaultReader(this))
+ return @Promise.@reject(@makeThisTypeError("ReadableStreamDefaultReader", "cancel"));
+
+ if (!this.@ownerReadableStream)
+ return @Promise.@reject(new @TypeError("cancel() called on a reader owned by no readable stream"));
+
+ return @readableStreamReaderGenericCancel(this, reason);
+}
+
+function read()
+{
+ "use strict";
+
+ if (!@isReadableStreamDefaultReader(this))
+ return @Promise.@reject(@makeThisTypeError("ReadableStreamDefaultReader", "read"));
+ if (!this.@ownerReadableStream)
+ return @Promise.@reject(new @TypeError("read() called on a reader owned by no readable stream"));
+
+ return @readableStreamDefaultReaderRead(this);
+}
+
+function releaseLock()
+{
+ "use strict";
+
+ if (!@isReadableStreamDefaultReader(this))
+ throw @makeThisTypeError("ReadableStreamDefaultReader", "releaseLock");
+
+ if (!this.@ownerReadableStream)
+ return;
+
+ if (this.@readRequests.length)
+ @throwTypeError("There are still pending read requests, cannot release the lock");
+
+ @readableStreamReaderGenericRelease(this);
+}
+
+function closed()
+{
+ "use strict";
+
+ if (!@isReadableStreamDefaultReader(this))
+ return @Promise.@reject(@makeGetterTypeError("ReadableStreamDefaultReader", "closed"));
+
+ return this.@closedPromiseCapability.@promise;
+}
diff --git a/Source/WebCore/Modules/streams/ReadableStreamInternals.js b/Source/WebCore/Modules/streams/ReadableStreamInternals.js
new file mode 100644
index 000000000..87094bf6a
--- /dev/null
+++ b/Source/WebCore/Modules/streams/ReadableStreamInternals.js
@@ -0,0 +1,503 @@
+/*
+ * Copyright (C) 2015 Canon Inc. All rights reserved.
+ * Copyright (C) 2015 Igalia.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this 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(READABLE_STREAM_API)
+// @internal
+
+function privateInitializeReadableStreamDefaultReader(stream)
+{
+ "use strict";
+
+ if (!@isReadableStream(stream))
+ @throwTypeError("ReadableStreamDefaultReader needs a ReadableStream");
+ if (@isReadableStreamLocked(stream))
+ @throwTypeError("ReadableStream is locked");
+
+ @readableStreamReaderGenericInitialize(this, stream);
+ this.@readRequests = [];
+
+ return this;
+}
+
+function readableStreamReaderGenericInitialize(reader, stream)
+{
+ "use strict";
+
+ reader.@ownerReadableStream = stream;
+ stream.@reader = reader;
+ if (stream.@state === @streamReadable)
+ reader.@closedPromiseCapability = @newPromiseCapability(@Promise);
+ else if (stream.@state === @streamClosed)
+ reader.@closedPromiseCapability = { @promise: @Promise.@resolve() };
+ else {
+ @assert(stream.@state === @streamErrored);
+ reader.@closedPromiseCapability = { @promise: @Promise.@reject(stream.@storedError) };
+ }
+}
+
+function privateInitializeReadableStreamDefaultController(stream, underlyingSource, size, highWaterMark)
+{
+ "use strict";
+
+ if (!@isReadableStream(stream))
+ @throwTypeError("ReadableStreamDefaultController needs a ReadableStream");
+
+ // readableStreamController is initialized with null value.
+ if (stream.@readableStreamController !== null)
+ @throwTypeError("ReadableStream already has a controller");
+
+ this.@controlledReadableStream = stream;
+ this.@underlyingSource = underlyingSource;
+ this.@queue = @newQueue();
+ this.@started = false;
+ this.@closeRequested = false;
+ this.@pullAgain = false;
+ this.@pulling = false;
+ this.@strategy = @validateAndNormalizeQueuingStrategy(size, highWaterMark);
+
+ const controller = this;
+ @promiseInvokeOrNoopNoCatch(underlyingSource, "start", [this]).@then(() => {
+ controller.@started = true;
+ @assert(!controller.@pulling);
+ @assert(!controller.@pullAgain);
+ @readableStreamDefaultControllerCallPullIfNeeded(controller);
+ }, (error) => {
+ if (stream.@state === @streamReadable)
+ @readableStreamDefaultControllerError(controller, error);
+ });
+
+ this.@cancel = @readableStreamDefaultControllerCancel;
+
+ this.@pull = @readableStreamDefaultControllerPull;
+
+ return this;
+}
+
+function readableStreamDefaultControllerError(controller, error)
+{
+ "use strict";
+
+ const stream = controller.@controlledReadableStream;
+ @assert(stream.@state === @streamReadable);
+ controller.@queue = @newQueue();
+ @readableStreamError(stream, error);
+}
+
+function readableStreamTee(stream, shouldClone)
+{
+ "use strict";
+
+ @assert(@isReadableStream(stream));
+ @assert(typeof(shouldClone) === "boolean");
+
+ const reader = new @ReadableStreamDefaultReader(stream);
+
+ const teeState = {
+ closedOrErrored: false,
+ canceled1: false,
+ canceled2: false,
+ reason1: @undefined,
+ reason2: @undefined,
+ };
+
+ teeState.cancelPromiseCapability = @newPromiseCapability(@InternalPromise);
+
+ const pullFunction = @readableStreamTeePullFunction(teeState, reader, shouldClone);
+
+ const branch1 = new @ReadableStream({
+ "pull": pullFunction,
+ "cancel": @readableStreamTeeBranch1CancelFunction(teeState, stream)
+ });
+ const branch2 = new @ReadableStream({
+ "pull": pullFunction,
+ "cancel": @readableStreamTeeBranch2CancelFunction(teeState, stream)
+ });
+
+ reader.@closedPromiseCapability.@promise.@then(@undefined, function(e) {
+ if (teeState.closedOrErrored)
+ return;
+ @readableStreamDefaultControllerError(branch1.@readableStreamController, e);
+ @readableStreamDefaultControllerError(branch2.@readableStreamController, e);
+ teeState.closedOrErrored = true;
+ });
+
+ // Additional fields compared to the spec, as they are needed within pull/cancel functions.
+ teeState.branch1 = branch1;
+ teeState.branch2 = branch2;
+
+ return [branch1, branch2];
+}
+
+function doStructuredClone(object)
+{
+ "use strict";
+
+ // FIXME: We should implement http://w3c.github.io/html/infrastructure.html#ref-for-structured-clone-4
+ // Implementation is currently limited to ArrayBuffer/ArrayBufferView to meet Fetch API needs.
+
+ if (object instanceof @ArrayBuffer)
+ return @structuredCloneArrayBuffer(object);
+
+ if (@ArrayBuffer.@isView(object))
+ return @structuredCloneArrayBufferView(object);
+
+ @throwTypeError("structuredClone not implemented for: " + object);
+}
+
+function readableStreamTeePullFunction(teeState, reader, shouldClone)
+{
+ "use strict";
+
+ return function() {
+ @Promise.prototype.@then.@call(@readableStreamDefaultReaderRead(reader), function(result) {
+ @assert(@isObject(result));
+ @assert(typeof result.done === "boolean");
+ if (result.done && !teeState.closedOrErrored) {
+ if (!teeState.canceled1)
+ @readableStreamDefaultControllerClose(teeState.branch1.@readableStreamController);
+ if (!teeState.canceled2)
+ @readableStreamDefaultControllerClose(teeState.branch2.@readableStreamController);
+ teeState.closedOrErrored = true;
+ }
+ if (teeState.closedOrErrored)
+ return;
+ if (!teeState.canceled1)
+ @readableStreamDefaultControllerEnqueue(teeState.branch1.@readableStreamController, result.value);
+ if (!teeState.canceled2)
+ @readableStreamDefaultControllerEnqueue(teeState.branch2.@readableStreamController, shouldClone ? @doStructuredClone(result.value) : result.value);
+ });
+ }
+}
+
+function readableStreamTeeBranch1CancelFunction(teeState, stream)
+{
+ "use strict";
+
+ return function(r) {
+ teeState.canceled1 = true;
+ teeState.reason1 = r;
+ if (teeState.canceled2) {
+ @readableStreamCancel(stream, [teeState.reason1, teeState.reason2]).@then(
+ teeState.cancelPromiseCapability.@resolve,
+ teeState.cancelPromiseCapability.@reject);
+ }
+ return teeState.cancelPromiseCapability.@promise;
+ }
+}
+
+function readableStreamTeeBranch2CancelFunction(teeState, stream)
+{
+ "use strict";
+
+ return function(r) {
+ teeState.canceled2 = true;
+ teeState.reason2 = r;
+ if (teeState.canceled1) {
+ @readableStreamCancel(stream, [teeState.reason1, teeState.reason2]).@then(
+ teeState.cancelPromiseCapability.@resolve,
+ teeState.cancelPromiseCapability.@reject);
+ }
+ return teeState.cancelPromiseCapability.@promise;
+ }
+}
+
+function isReadableStream(stream)
+{
+ "use strict";
+
+ // Spec tells to return true only if stream has a readableStreamController internal slot.
+ // However, since it is a private slot, it cannot be checked using hasOwnProperty().
+ // Therefore, readableStreamController is initialized with null value.
+ return @isObject(stream) && stream.@readableStreamController !== @undefined;
+}
+
+function isReadableStreamDefaultReader(reader)
+{
+ "use strict";
+
+ // Spec tells to return true only if reader has a readRequests internal slot.
+ // However, since it is a private slot, it cannot be checked using hasOwnProperty().
+ // Since readRequests is initialized with an empty array, the following test is ok.
+ return @isObject(reader) && !!reader.@readRequests;
+}
+
+function isReadableStreamDefaultController(controller)
+{
+ "use strict";
+
+ // Spec tells to return true only if controller has an underlyingSource internal slot.
+ // However, since it is a private slot, it cannot be checked using hasOwnProperty().
+ // underlyingSource is obtained in ReadableStream constructor: if undefined, it is set
+ // to an empty object. Therefore, following test is ok.
+ return @isObject(controller) && !!controller.@underlyingSource;
+}
+
+function readableStreamError(stream, error)
+{
+ "use strict";
+
+ @assert(@isReadableStream(stream));
+ @assert(stream.@state === @streamReadable);
+ stream.@state = @streamErrored;
+ stream.@storedError = error;
+
+ if (!stream.@reader)
+ return;
+
+ const reader = stream.@reader;
+
+ if (@isReadableStreamDefaultReader(reader)) {
+ const requests = reader.@readRequests;
+ for (let index = 0, length = requests.length; index < length; ++index)
+ requests[index].@reject.@call(@undefined, error);
+ reader.@readRequests = [];
+ } else
+ // FIXME: Implement ReadableStreamBYOBReader.
+ @throwTypeError("Only ReadableStreamDefaultReader is currently supported");
+
+ reader.@closedPromiseCapability.@reject.@call(@undefined, error);
+}
+
+function readableStreamDefaultControllerCallPullIfNeeded(controller)
+{
+ "use strict";
+
+ const stream = controller.@controlledReadableStream;
+
+ if (stream.@state === @streamClosed || stream.@state === @streamErrored)
+ return;
+ if (controller.@closeRequested)
+ return;
+ if (!controller.@started)
+ return;
+ if ((!@isReadableStreamLocked(stream) || !stream.@reader.@readRequests.length) && @readableStreamDefaultControllerGetDesiredSize(controller) <= 0)
+ return;
+
+ if (controller.@pulling) {
+ controller.@pullAgain = true;
+ return;
+ }
+
+ @assert(!controller.@pullAgain);
+ controller.@pulling = true;
+
+ @promiseInvokeOrNoop(controller.@underlyingSource, "pull", [controller]).@then(function() {
+ controller.@pulling = false;
+ if (controller.@pullAgain) {
+ controller.@pullAgain = false;
+ @readableStreamDefaultControllerCallPullIfNeeded(controller);
+ }
+ }, function(error) {
+ if (stream.@state === @streamReadable)
+ @readableStreamDefaultControllerError(controller, error);
+ });
+}
+
+function isReadableStreamLocked(stream)
+{
+ "use strict";
+
+ @assert(@isReadableStream(stream));
+ return !!stream.@reader;
+}
+
+function readableStreamDefaultControllerGetDesiredSize(controller)
+{
+ "use strict";
+
+ return controller.@strategy.highWaterMark - controller.@queue.size;
+}
+
+
+function readableStreamReaderGenericCancel(reader, reason)
+{
+ "use strict";
+
+ const stream = reader.@ownerReadableStream;
+ @assert(!!stream);
+ return @readableStreamCancel(stream, reason);
+}
+
+function readableStreamCancel(stream, reason)
+{
+ "use strict";
+
+ stream.@disturbed = true;
+ if (stream.@state === @streamClosed)
+ return @Promise.@resolve();
+ if (stream.@state === @streamErrored)
+ return @Promise.@reject(stream.@storedError);
+ @readableStreamClose(stream);
+ return stream.@readableStreamController.@cancel(stream.@readableStreamController, reason).@then(function() { });
+}
+
+function readableStreamDefaultControllerCancel(controller, reason)
+{
+ "use strict";
+
+ controller.@queue = @newQueue();
+ return @promiseInvokeOrNoop(controller.@underlyingSource, "cancel", [reason]);
+}
+
+function readableStreamDefaultControllerPull(controller)
+{
+ "use strict";
+
+ const stream = controller.@controlledReadableStream;
+ if (controller.@queue.content.length) {
+ const chunk = @dequeueValue(controller.@queue);
+ if (controller.@closeRequested && controller.@queue.content.length === 0)
+ @readableStreamClose(stream);
+ else
+ @readableStreamDefaultControllerCallPullIfNeeded(controller);
+ return @Promise.@resolve({value: chunk, done: false});
+ }
+ const pendingPromise = @readableStreamAddReadRequest(stream);
+ @readableStreamDefaultControllerCallPullIfNeeded(controller);
+ return pendingPromise;
+}
+
+function readableStreamDefaultControllerClose(controller)
+{
+ "use strict";
+
+ const stream = controller.@controlledReadableStream;
+ @assert(!controller.@closeRequested);
+ @assert(stream.@state === @streamReadable);
+ controller.@closeRequested = true;
+ if (controller.@queue.content.length === 0)
+ @readableStreamClose(stream);
+}
+
+function readableStreamClose(stream)
+{
+ "use strict";
+
+ @assert(stream.@state === @streamReadable);
+ stream.@state = @streamClosed;
+ const reader = stream.@reader;
+
+ if (!reader)
+ return;
+
+ if (@isReadableStreamDefaultReader(reader)) {
+ const requests = reader.@readRequests;
+ for (let index = 0, length = requests.length; index < length; ++index)
+ requests[index].@resolve.@call(@undefined, {value:@undefined, done: true});
+ reader.@readRequests = [];
+ }
+
+ reader.@closedPromiseCapability.@resolve.@call();
+}
+
+function readableStreamFulfillReadRequest(stream, chunk, done)
+{
+ "use strict";
+
+ stream.@reader.@readRequests.@shift().@resolve.@call(@undefined, {value: chunk, done: done});
+}
+
+function readableStreamDefaultControllerEnqueue(controller, chunk)
+{
+ "use strict";
+
+ const stream = controller.@controlledReadableStream;
+ @assert(!controller.@closeRequested);
+ @assert(stream.@state === @streamReadable);
+
+ if (@isReadableStreamLocked(stream) && stream.@reader.@readRequests.length) {
+ @readableStreamFulfillReadRequest(stream, chunk, false);
+ @readableStreamDefaultControllerCallPullIfNeeded(controller);
+ return;
+ }
+
+ try {
+ let chunkSize = 1;
+ if (controller.@strategy.size !== @undefined)
+ chunkSize = controller.@strategy.size(chunk);
+ @enqueueValueWithSize(controller.@queue, chunk, chunkSize);
+ }
+ catch(error) {
+ if (stream.@state === @streamReadable)
+ @readableStreamDefaultControllerError(controller, error);
+ throw error;
+ }
+ @readableStreamDefaultControllerCallPullIfNeeded(controller);
+}
+
+function readableStreamDefaultReaderRead(reader)
+{
+ "use strict";
+
+ const stream = reader.@ownerReadableStream;
+ @assert(!!stream);
+
+ stream.@disturbed = true;
+ if (stream.@state === @streamClosed)
+ return @Promise.@resolve({value: @undefined, done: true});
+ if (stream.@state === @streamErrored)
+ return @Promise.@reject(stream.@storedError);
+ @assert(stream.@state === @streamReadable);
+
+ return stream.@readableStreamController.@pull(stream.@readableStreamController);
+}
+
+function readableStreamAddReadRequest(stream)
+{
+ "use strict";
+
+ @assert(@isReadableStreamDefaultReader(stream.@reader));
+ @assert(stream.@state == @streamReadable);
+
+ const readRequest = @newPromiseCapability(@Promise);
+ stream.@reader.@readRequests.@push(readRequest);
+
+ return readRequest.@promise;
+}
+
+function isReadableStreamDisturbed(stream)
+{
+ "use strict";
+
+ @assert(@isReadableStream(stream));
+ return stream.@disturbed;
+}
+
+function readableStreamReaderGenericRelease(reader)
+{
+ "use strict";
+
+ @assert(!!reader.@ownerReadableStream);
+ @assert(reader.@ownerReadableStream.@reader === reader);
+
+ if (reader.@ownerReadableStream.@state === @streamReadable)
+ reader.@closedPromiseCapability.@reject.@call(@undefined, new @TypeError("releasing lock of reader whose stream is still in readable state"));
+ else
+ reader.@closedPromiseCapability = { @promise: @Promise.@reject(new @TypeError("reader released lock")) };
+
+ reader.@ownerReadableStream.@reader = @undefined;
+ reader.@ownerReadableStream = null;
+}
diff --git a/Source/WebCore/Modules/streams/ReadableStreamSource.h b/Source/WebCore/Modules/streams/ReadableStreamSource.h
new file mode 100644
index 000000000..65919f294
--- /dev/null
+++ b/Source/WebCore/Modules/streams/ReadableStreamSource.h
@@ -0,0 +1,121 @@
+/*
+ * 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(READABLE_STREAM_API)
+
+#include "JSDOMPromise.h"
+#include "ReadableStreamDefaultController.h"
+#include <wtf/Optional.h>
+
+namespace WebCore {
+
+class ReadableStreamSource : public RefCounted<ReadableStreamSource> {
+public:
+ virtual ~ReadableStreamSource() { }
+
+ void start(ReadableStreamDefaultController&&, DOMPromise<void>&&);
+ void pull(DOMPromise<void>&&);
+ void cancel(JSC::JSValue);
+
+ bool isPulling() const { return !!m_promise; }
+
+protected:
+ ReadableStreamDefaultController& controller() { return m_controller.value(); }
+ const ReadableStreamDefaultController& controller() const { return m_controller.value(); }
+
+ void startFinished();
+ void pullFinished();
+ void cancelFinished();
+ void clean();
+
+ virtual void setActive() = 0;
+ virtual void setInactive() = 0;
+
+ virtual void doStart() = 0;
+ virtual void doPull() = 0;
+ virtual void doCancel() = 0;
+
+private:
+ std::optional<DOMPromise<void>> m_promise;
+ std::optional<ReadableStreamDefaultController> m_controller;
+};
+
+inline void ReadableStreamSource::start(ReadableStreamDefaultController&& controller, DOMPromise<void>&& promise)
+{
+ ASSERT(!m_promise);
+ m_promise = WTFMove(promise);
+ m_controller = WTFMove(controller);
+
+ setActive();
+ doStart();
+}
+
+inline void ReadableStreamSource::pull(DOMPromise<void>&& promise)
+{
+ ASSERT(!m_promise);
+ ASSERT(m_controller);
+
+ m_promise = WTFMove(promise);
+
+ setActive();
+ doPull();
+}
+
+inline void ReadableStreamSource::startFinished()
+{
+ ASSERT(m_promise);
+ std::exchange(m_promise, std::nullopt).value().resolve();
+ setInactive();
+}
+
+inline void ReadableStreamSource::pullFinished()
+{
+ ASSERT(m_promise);
+ std::exchange(m_promise, std::nullopt).value().resolve();
+ setInactive();
+}
+
+inline void ReadableStreamSource::cancel(JSC::JSValue)
+{
+ clean();
+ doCancel();
+}
+
+inline void ReadableStreamSource::clean()
+{
+ if (m_promise) {
+ m_promise = std::nullopt;
+ setInactive();
+ }
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(READABLE_STREAM_API)
diff --git a/Source/WebCore/Modules/streams/ReadableStreamSource.idl b/Source/WebCore/Modules/streams/ReadableStreamSource.idl
new file mode 100644
index 000000000..ad3afcc99
--- /dev/null
+++ b/Source/WebCore/Modules/streams/ReadableStreamSource.idl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+[
+ NoInterfaceObject,
+ Conditional=READABLE_STREAM_API,
+ SkipVTableValidation
+] interface ReadableStreamSource {
+ [Custom] Promise<void> start(ReadableStreamDefaultController controller);
+ [Custom] Promise<void> pull(ReadableStreamDefaultController controller);
+ void cancel(any reason);
+
+ // Place holder to keep the controller linked to the source.
+ [CachedAttribute, CustomGetter] readonly attribute any controller;
+};
diff --git a/Source/WebCore/Modules/streams/StreamInternals.js b/Source/WebCore/Modules/streams/StreamInternals.js
new file mode 100644
index 000000000..94a96e396
--- /dev/null
+++ b/Source/WebCore/Modules/streams/StreamInternals.js
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2015 Canon Inc.
+ * Copyright (C) 2015 Igalia.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this 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(READABLE_STREAM_API) || ENABLE(WRITABLE_STREAM_API)
+// @internal
+
+function shieldingPromiseResolve(result)
+{
+ const promise = @Promise.@resolve(result);
+ if (promise.@then === @undefined)
+ promise.@then = @Promise.prototype.@then;
+ return promise;
+}
+
+function promiseInvokeOrNoopNoCatch(object, key, args)
+{
+ "use strict";
+
+ const method = object[key];
+ if (method === @undefined)
+ return @Promise.@resolve();
+ return @shieldingPromiseResolve(method.@apply(object, args));
+}
+
+function promiseInvokeOrNoop(object, key, args)
+{
+ "use strict";
+
+ try {
+ return @promiseInvokeOrNoopNoCatch(object, key, args);
+ }
+ catch(error) {
+ return @Promise.@reject(error);
+ }
+
+}
+
+function promiseInvokeOrFallbackOrNoop(object, key1, args1, key2, args2)
+{
+ "use strict";
+
+ try {
+ const method = object[key1];
+ if (method === @undefined)
+ return @promiseInvokeOrNoopNoCatch(object, key2, args2);
+ return @shieldingPromiseResolve(method.@apply(object, args1));
+ }
+ catch(error) {
+ return @Promise.@reject(error);
+ }
+}
+
+function validateAndNormalizeQueuingStrategy(size, highWaterMark)
+{
+ "use strict";
+
+ if (size !== @undefined && typeof size !== "function")
+ @throwTypeError("size parameter must be a function");
+
+ const normalizedStrategy = { };
+
+ normalizedStrategy.size = size;
+ normalizedStrategy.highWaterMark = @Number(highWaterMark);
+
+ if (@isNaN(normalizedStrategy.highWaterMark) || normalizedStrategy.highWaterMark < 0)
+ @throwRangeError("highWaterMark value is negative or not a number");
+
+ return normalizedStrategy;
+}
+
+function newQueue()
+{
+ "use strict";
+
+ return { content: [], size: 0 };
+}
+
+function dequeueValue(queue)
+{
+ "use strict";
+
+ const record = queue.content.@shift();
+ queue.size -= record.size;
+ return record.value;
+}
+
+function enqueueValueWithSize(queue, value, size)
+{
+ "use strict";
+
+ size = @Number(size);
+ if (!@isFinite(size) || size < 0)
+ @throwRangeError("size has an incorrect value");
+ queue.content.@push({ value: value, size: size });
+ queue.size += size;
+}
+
+function peekQueueValue(queue)
+{
+ "use strict";
+
+ @assert(queue.content.length > 0);
+
+ return queue.content[0].value;
+}
diff --git a/Source/WebCore/Modules/streams/WritableStream.idl b/Source/WebCore/Modules/streams/WritableStream.idl
new file mode 100644
index 000000000..62e92234c
--- /dev/null
+++ b/Source/WebCore/Modules/streams/WritableStream.idl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 Canon Inc.
+ * Copyright (C) 2015 Igalia S.L.
+ *
+ * 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=WRITABLE_STREAM_API,
+ Constructor,
+ JSBuiltin
+] interface WritableStream {
+ Promise<any> abort(optional any reason);
+ Promise<any> close();
+ Promise<any> write(any chunk);
+
+ readonly attribute Promise<boolean> closed;
+ readonly attribute Promise<boolean> ready;
+ readonly attribute DOMString state;
+};
diff --git a/Source/WebCore/Modules/streams/WritableStream.js b/Source/WebCore/Modules/streams/WritableStream.js
new file mode 100644
index 000000000..ac6ca2649
--- /dev/null
+++ b/Source/WebCore/Modules/streams/WritableStream.js
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2015 Canon Inc.
+ * Copyright (C) 2015 Igalia
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this 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(WRITABLE_STREAM_API)
+
+function initializeWritableStream(underlyingSink, strategy)
+{
+ "use strict";
+
+ if (underlyingSink === @undefined)
+ underlyingSink = { };
+ if (strategy === @undefined)
+ strategy = { highWaterMark: 0, size: function() { return 1; } };
+
+ if (!@isObject(underlyingSink))
+ @throwTypeError("WritableStream constructor takes an object as first argument");
+
+ if (!@isObject(strategy))
+ @throwTypeError("WritableStream constructor takes an object as second argument, if any");
+
+ this.@underlyingSink = underlyingSink;
+ this.@closedPromiseCapability = @newPromiseCapability(@Promise);
+ this.@readyPromiseCapability = { @promise: @Promise.@resolve() };
+ this.@queue = @newQueue();
+ this.@state = @streamWritable;
+ this.@started = false;
+ this.@writing = false;
+
+ this.@strategy = @validateAndNormalizeQueuingStrategy(strategy.size, strategy.highWaterMark);
+
+ @syncWritableStreamStateWithQueue(this);
+
+ const errorFunction = (e) => {
+ @errorWritableStream(this, e);
+ };
+ this.@startedPromise = @promiseInvokeOrNoopNoCatch(underlyingSink, "start", [errorFunction]);
+ this.@startedPromise.@then(() => {
+ this.@started = true;
+ this.@startedPromise = @undefined;
+ }, errorFunction);
+
+ return this;
+}
+
+function abort(reason)
+{
+ "use strict";
+
+ if (!@isWritableStream(this))
+ return @Promise.@reject(new @TypeError("The WritableStream.abort method can only be used on instances of WritableStream"));
+
+ if (this.@state === @streamClosed)
+ return @Promise.@resolve();
+
+ if (this.@state === @streamErrored)
+ return @Promise.@reject(this.@storedError);
+
+ @errorWritableStream(this, reason);
+
+ return @promiseInvokeOrFallbackOrNoop(this.@underlyingSink, "abort", [reason], "close", []).@then(function() { });
+}
+
+function close()
+{
+ "use strict";
+
+ if (!@isWritableStream(this))
+ return @Promise.@reject(new @TypeError("The WritableStream.close method can only be used on instances of WritableStream"));
+
+ if (this.@state === @streamClosed || this.@state === @streamClosing)
+ return @Promise.@reject(new @TypeError("Cannot close a WritableString that is closed or closing"));
+
+ if (this.@state === @streamErrored)
+ return @Promise.@reject(this.@storedError);
+
+ if (this.@state === @streamWaiting)
+ this.@readyPromiseCapability.@resolve.@call();
+
+ this.@state = @streamClosing;
+ @enqueueValueWithSize(this.@queue, "close", 0);
+ @callOrScheduleWritableStreamAdvanceQueue(this);
+
+ return this.@closedPromiseCapability.@promise;
+}
+
+function write(chunk)
+{
+ "use strict";
+
+ if (!@isWritableStream(this))
+ return @Promise.@reject(new @TypeError("The WritableStream.write method can only be used on instances of WritableStream"));
+
+ if (this.@state === @streamClosed || this.@state === @streamClosing)
+ return @Promise.@reject(new @TypeError("Cannot write on a WritableString that is closed or closing"));
+
+ if (this.@state === @streamErrored)
+ return @Promise.@reject(this.@storedError);
+
+ @assert(this.@state === @streamWritable || this.@state === @streamWaiting);
+
+ let chunkSize = 1;
+ if (this.@strategy.size !== @undefined) {
+ try {
+ chunkSize = this.@strategy.size.@call(@undefined, chunk);
+ } catch(e) {
+ @errorWritableStream(this, e);
+ return @Promise.@reject(e);
+ }
+ }
+
+ const promiseCapability = @newPromiseCapability(@Promise);
+ try {
+ @enqueueValueWithSize(this.@queue, { promiseCapability: promiseCapability, chunk: chunk }, chunkSize);
+ } catch (e) {
+ @errorWritableStream(this, e);
+ return @Promise.@reject(e);
+ }
+
+ @syncWritableStreamStateWithQueue(this);
+ @callOrScheduleWritableStreamAdvanceQueue(this);
+
+ return promiseCapability.@promise;
+}
+
+function closed()
+{
+ "use strict";
+
+ if (!@isWritableStream(this))
+ return @Promise.@reject(new @TypeError("The WritableStream.closed getter can only be used on instances of WritableStream"));
+
+ return this.@closedPromiseCapability.@promise;
+}
+
+function ready()
+{
+ "use strict";
+
+ if (!@isWritableStream(this))
+ return @Promise.@reject(new @TypeError("The WritableStream.ready getter can only be used on instances of WritableStream"));
+
+ return this.@readyPromiseCapability.@promise;
+}
+
+function state()
+{
+ "use strict";
+
+ if (!@isWritableStream(this))
+ @throwTypeError("The WritableStream.state getter can only be used on instances of WritableStream");
+
+ switch(this.@state) {
+ case @streamClosed:
+ return "closed";
+ case @streamClosing:
+ return "closing";
+ case @streamErrored:
+ return "errored";
+ case @streamWaiting:
+ return "waiting";
+ case @streamWritable:
+ return "writable";
+ }
+
+ @assert(false);
+}
diff --git a/Source/WebCore/Modules/streams/WritableStreamInternals.js b/Source/WebCore/Modules/streams/WritableStreamInternals.js
new file mode 100644
index 000000000..cffea5a3d
--- /dev/null
+++ b/Source/WebCore/Modules/streams/WritableStreamInternals.js
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2015 Canon Inc.
+ * Copyright (C) 2015 Igalia
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this 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(WRITABLE_STREAM_API)
+// @internal
+
+function isWritableStream(stream)
+{
+ "use strict";
+
+ return @isObject(stream) && !!stream.@underlyingSink;
+}
+
+function syncWritableStreamStateWithQueue(stream)
+{
+ "use strict";
+
+ if (stream.@state === @streamClosing)
+ return;
+
+ @assert(stream.@state === @streamWritable || stream.@state === @streamWaiting);
+
+ const shouldApplyBackpressure = stream.@queue.size > stream.@strategy.highWaterMark;
+ if (shouldApplyBackpressure && stream.@state === @streamWritable) {
+ stream.@state = @streamWaiting;
+ stream.@readyPromiseCapability = @newPromiseCapability(@Promise);
+ }
+ if (!shouldApplyBackpressure && stream.@state === @streamWaiting) {
+ stream.@state = @streamWritable;
+ stream.@readyPromiseCapability.@resolve.@call();
+ }
+}
+
+function errorWritableStream(stream, e)
+{
+ "use strict";
+
+ if (stream.@state === @streamClosed || stream.@state === @streamErrored)
+ return;
+ while (stream.@queue.content.length > 0) {
+ const writeRecord = @dequeueValue(stream.@queue);
+ if (writeRecord !== "close")
+ writeRecord.promiseCapability.@reject.@call(@undefined, e);
+ }
+ stream.@storedError = e;
+ if (stream.@state === @streamWaiting)
+ stream.@readyPromiseCapability.@resolve.@call();
+ stream.@closedPromiseCapability.@reject.@call(@undefined, e);
+ stream.@state = @streamErrored;
+}
+
+function callOrScheduleWritableStreamAdvanceQueue(stream)
+{
+ "use strict";
+
+ if (!stream.@started)
+ stream.@startedPromise.@then(function() { @writableStreamAdvanceQueue(stream); });
+ else
+ @writableStreamAdvanceQueue(stream);
+}
+
+function writableStreamAdvanceQueue(stream)
+{
+ "use strict";
+
+ if (stream.@queue.content.length === 0 || stream.@writing)
+ return;
+
+ const writeRecord = @peekQueueValue(stream.@queue);
+ if (writeRecord === "close") {
+ @assert(stream.@state === @streamClosing);
+ @dequeueValue(stream.@queue);
+ @assert(stream.@queue.content.length === 0);
+ @closeWritableStream(stream);
+ return;
+ }
+
+ stream.@writing = true;
+ @promiseInvokeOrNoop(stream.@underlyingSink, "write", [writeRecord.chunk]).@then(
+ function() {
+ if (stream.@state === @streamErrored)
+ return;
+ stream.@writing = false;
+ writeRecord.promiseCapability.@resolve.@call();
+ @dequeueValue(stream.@queue);
+ @syncWritableStreamStateWithQueue(stream);
+ @writableStreamAdvanceQueue(stream);
+ },
+ function(r) {
+ @errorWritableStream(stream, r);
+ }
+ );
+}
+
+function closeWritableStream(stream)
+{
+ "use strict";
+
+ @assert(stream.@state === @streamClosing);
+ @promiseInvokeOrNoop(stream.@underlyingSink, "close").@then(
+ function() {
+ if (stream.@state === @streamErrored)
+ return;
+ @assert(stream.@state === @streamClosing);
+ stream.@closedPromiseCapability.@resolve.@call();
+ stream.@state = @streamClosed;
+ },
+ function(r) {
+ @errorWritableStream(stream, r);
+ }
+ );
+}
diff --git a/Source/WebCore/Modules/battery/BatteryStatus.cpp b/Source/WebCore/Modules/vibration/NavigatorVibration.cpp
index 00f530bf8..f56e5401d 100644
--- a/Source/WebCore/Modules/battery/BatteryStatus.cpp
+++ b/Source/WebCore/Modules/vibration/NavigatorVibration.cpp
@@ -18,41 +18,43 @@
*/
#include "config.h"
-#include "BatteryStatus.h"
+#include "NavigatorVibration.h"
-#if ENABLE(BATTERY_STATUS)
+#if ENABLE(VIBRATION)
-#include <limits>
+#include "Frame.h"
+#include "Navigator.h"
+#include "Page.h"
+#include "Vibration.h"
+#include <runtime/Uint32Array.h>
namespace WebCore {
-PassRefPtr<BatteryStatus> BatteryStatus::create()
+NavigatorVibration::NavigatorVibration()
{
- return adoptRef(new BatteryStatus);
}
-PassRefPtr<BatteryStatus> BatteryStatus::create(bool charging, double chargingTime, double dischargingTime, double level)
+NavigatorVibration::~NavigatorVibration()
{
- return adoptRef(new BatteryStatus(charging, chargingTime, dischargingTime, level));
}
-BatteryStatus::BatteryStatus()
- : m_charging(true)
- , m_chargingTime(0)
- , m_dischargingTime(std::numeric_limits<double>::infinity())
- , m_level(1)
+bool NavigatorVibration::vibrate(Navigator& navigator, unsigned time)
{
+ return NavigatorVibration::vibrate(navigator, VibrationPattern(1, time));
}
-BatteryStatus::BatteryStatus(bool charging, double chargingTime, double dischargingTime, double level)
- : m_charging(charging)
- , m_chargingTime(chargingTime)
- , m_dischargingTime(dischargingTime)
- , m_level(level)
+bool NavigatorVibration::vibrate(Navigator& navigator, const VibrationPattern& pattern)
{
+ if (!navigator.frame()->page())
+ return false;
+
+ if (navigator.frame()->page()->visibilityState() == PageVisibilityState::Hidden)
+ return false;
+
+ return Vibration::from(navigator.frame()->page())->vibrate(pattern);
}
} // namespace WebCore
-#endif // BATTERY_STATUS
+#endif // ENABLE(VIBRATION)
diff --git a/Source/WebCore/Modules/battery/NavigatorBattery.h b/Source/WebCore/Modules/vibration/NavigatorVibration.h
index af7114f42..3d4ffdc3a 100644
--- a/Source/WebCore/Modules/battery/NavigatorBattery.h
+++ b/Source/WebCore/Modules/vibration/NavigatorVibration.h
@@ -17,39 +17,30 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef NavigatorBattery_h
-#define NavigatorBattery_h
+#pragma once
-#if ENABLE(BATTERY_STATUS)
+#if ENABLE(VIBRATION)
-#include "Supplementable.h"
+#include "ExceptionCode.h"
+#include <wtf/Vector.h>
namespace WebCore {
-class BatteryManager;
class Navigator;
-class ScriptExecutionContext;
-class NavigatorBattery : public Supplement<Navigator> {
+class NavigatorVibration {
public:
- virtual ~NavigatorBattery();
+ typedef Vector<unsigned> VibrationPattern;
- static NavigatorBattery* from(Navigator*);
+ ~NavigatorVibration();
- static BatteryManager* webkitBattery(Navigator*);
- BatteryManager* batteryManager();
+ static bool vibrate(Navigator&, unsigned time);
+ static bool vibrate(Navigator&, const VibrationPattern&);
- private:
- NavigatorBattery();
- static const char* supplementName();
-
- RefPtr<BatteryManager> m_batteryManager;
+private:
+ NavigatorVibration();
};
} // namespace WebCore
-#endif // ENABLE(BATTERY_STATUS)
-
-#endif // NavigatorBattery_h
-
-
+#endif // ENABLE(VIBRATION)
diff --git a/Source/WebCore/Modules/battery/NavigatorBattery.idl b/Source/WebCore/Modules/vibration/NavigatorVibration.idl
index f69199fd2..92150149f 100644
--- a/Source/WebCore/Modules/battery/NavigatorBattery.idl
+++ b/Source/WebCore/Modules/vibration/NavigatorVibration.idl
@@ -18,7 +18,9 @@
*/
[
- Conditional=BATTERY_STATUS,
+ Conditional=VIBRATION,
] partial interface Navigator {
- readonly attribute BatteryManager webkitBattery;
+ boolean vibrate(sequence<unsigned long> pattern);
+ boolean vibrate(unsigned long time);
};
+
diff --git a/Source/WebCore/Modules/vibration/Vibration.cpp b/Source/WebCore/Modules/vibration/Vibration.cpp
new file mode 100644
index 000000000..860914209
--- /dev/null
+++ b/Source/WebCore/Modules/vibration/Vibration.cpp
@@ -0,0 +1,130 @@
+/*
+ * 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 "Vibration.h"
+
+#if ENABLE(VIBRATION)
+
+#include "VibrationClient.h"
+
+namespace WebCore {
+
+// Maximum number of entries in a vibration pattern.
+const unsigned MaxVibrationPatternLength = 99;
+// Maximum duration of a vibration in 10 seconds.
+const unsigned MaxVibrationDuration = 10000;
+
+Vibration::Vibration(VibrationClient* client)
+ : m_vibrationClient(client)
+ , m_timer(*this, &Vibration::timerFired)
+ , m_state(State::Idle)
+{
+}
+
+Vibration::~Vibration()
+{
+ m_vibrationClient->vibrationDestroyed();
+}
+
+bool Vibration::vibrate(const VibrationPattern& pattern)
+{
+ VibrationPattern& sanitized = const_cast<VibrationPattern&>(pattern);
+ size_t length = sanitized.size();
+
+ // If the pattern is too long then truncate it.
+ if (length > MaxVibrationPatternLength) {
+ sanitized.shrink(MaxVibrationPatternLength);
+ length = MaxVibrationPatternLength;
+ }
+
+ // If the length of pattern is even and is not zero, then remove the last entry in pattern.
+ if (length && !(length % 2)) {
+ sanitized.removeLast();
+ length--;
+ }
+
+ // If any pattern entry is too long then truncate it.
+ for (auto& duration : sanitized)
+ duration = std::min(duration, MaxVibrationDuration);
+
+ // Pre-exisiting instance need to be canceled when vibrate() is called.
+ if (isVibrating())
+ cancelVibration();
+
+ // If pattern is an empty list, then return true and terminate these steps.
+ if (!length || (length == 1 && !sanitized[0])) {
+ cancelVibration();
+ return true;
+ }
+
+ m_pattern = sanitized;
+
+ m_timer.startOneShot(0);
+ m_state = State::Waiting;
+ return true;
+}
+
+void Vibration::cancelVibration()
+{
+ m_pattern.clear();
+ if (isVibrating()) {
+ m_timer.stop();
+ m_state = State::Idle;
+ m_vibrationClient->cancelVibration();
+ }
+}
+
+void Vibration::timerFired()
+{
+ m_timer.stop();
+
+ if (m_pattern.isEmpty()) {
+ m_state = State::Idle;
+ return;
+ }
+
+ switch (m_state) {
+ case State::Vibrating:
+ m_state = State::Waiting;
+ break;
+ case State::Waiting:
+ case State::Idle:
+ m_state = State::Vibrating;
+ m_vibrationClient->vibrate(m_pattern[0]);
+ break;
+ }
+ m_timer.startOneShot(m_pattern[0] / 1000.0);
+ m_pattern.remove(0);
+}
+
+const char* Vibration::supplementName()
+{
+ return "Vibration";
+}
+
+void provideVibrationTo(Page* page, VibrationClient* client)
+{
+ Vibration::provideTo(page, Vibration::supplementName(), std::make_unique<Vibration>(client));
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(VIBRATION)
+
diff --git a/Source/WebCore/Modules/battery/BatteryStatus.h b/Source/WebCore/Modules/vibration/Vibration.h
index 2c451ab4e..aa1311f34 100644
--- a/Source/WebCore/Modules/battery/BatteryStatus.h
+++ b/Source/WebCore/Modules/vibration/Vibration.h
@@ -17,38 +17,44 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef BatteryStatus_h
-#define BatteryStatus_h
+#pragma once
-#if ENABLE(BATTERY_STATUS)
+#if ENABLE(VIBRATION)
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
+#include "Page.h"
+#include "Timer.h"
namespace WebCore {
-class BatteryStatus : public RefCounted<BatteryStatus> {
+class VibrationClient;
+
+class Vibration : public Supplement<Page> {
public:
- static PassRefPtr<BatteryStatus> create();
- static PassRefPtr<BatteryStatus> create(bool charging, double chargingTime, double dischargingTime, double level);
+ typedef Vector<unsigned> VibrationPattern;
+
+ explicit Vibration(VibrationClient*);
+ ~Vibration();
+
+ bool vibrate(const VibrationPattern&);
+ // FIXME: When a visibilitychange event is dispatched while vibrating, the vibration should be canceled.
+ void cancelVibration();
- bool charging() const { return m_charging; }
- double chargingTime() const { return m_chargingTime; }
- double dischargingTime() const { return m_dischargingTime; }
- double level() const { return m_level; }
+ void timerFired();
+
+ static const char* supplementName();
+ static Vibration* from(Page* page) { return static_cast<Vibration*>(Supplement<Page>::from(page, supplementName())); }
+
+ bool isVibrating() { return m_state != State::Idle; }
private:
- BatteryStatus();
- BatteryStatus(bool charging, double chargingTime, double dischargingTime, double level);
+ enum class State { Idle, Vibrating, Waiting };
- bool m_charging;
- double m_chargingTime;
- double m_dischargingTime;
- double m_level;
+ VibrationClient* m_vibrationClient;
+ Timer m_timer;
+ State m_state;
+ VibrationPattern m_pattern;
};
} // namespace WebCore
-#endif // BATTERY_STATUS
-#endif // BatteryStatus_h
-
+#endif // ENABLE(VIBRATION)
diff --git a/Source/WebCore/Modules/battery/BatteryClient.h b/Source/WebCore/Modules/vibration/VibrationClient.h
index 2c87a44bb..383445f48 100644
--- a/Source/WebCore/Modules/battery/BatteryClient.h
+++ b/Source/WebCore/Modules/vibration/VibrationClient.h
@@ -17,28 +17,22 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef BatteryClient_h
-#define BatteryClient_h
-
-#if ENABLE(BATTERY_STATUS)
+#pragma once
namespace WebCore {
class Page;
-class BatteryClient {
+class VibrationClient {
public:
- virtual ~BatteryClient() { }
-
- virtual void startUpdating() = 0;
- virtual void stopUpdating() = 0;
- virtual void batteryControllerDestroyed() = 0;
-};
+ virtual ~VibrationClient() { }
-void provideBatteryTo(Page*, BatteryClient*);
+ virtual void vibrate(const unsigned& time) = 0;
+ virtual void cancelVibration() = 0;
-}
+ virtual void vibrationDestroyed() = 0;
+};
-#endif // BATTERY_STATUS
-#endif // BatteryClient_h
+void provideVibrationTo(Page*, VibrationClient*);
+} // namespace WebCore
diff --git a/Source/WebCore/Modules/webaudio/AnalyserNode.cpp b/Source/WebCore/Modules/webaudio/AnalyserNode.cpp
index b54d3ae28..6aff9a63d 100644
--- a/Source/WebCore/Modules/webaudio/AnalyserNode.cpp
+++ b/Source/WebCore/Modules/webaudio/AnalyserNode.cpp
@@ -30,11 +30,10 @@
#include "AudioNodeInput.h"
#include "AudioNodeOutput.h"
-#include "ExceptionCode.h"
namespace WebCore {
-AnalyserNode::AnalyserNode(AudioContext* context, float sampleRate)
+AnalyserNode::AnalyserNode(AudioContext& context, float sampleRate)
: AudioBasicInspectorNode(context, sampleRate, 2)
{
setNodeType(NodeTypeAnalyser);
@@ -72,40 +71,38 @@ void AnalyserNode::reset()
m_analyser.reset();
}
-void AnalyserNode::setFftSize(unsigned size, ExceptionCode& ec)
+ExceptionOr<void> AnalyserNode::setFftSize(unsigned size)
{
if (!m_analyser.setFftSize(size))
- ec = INDEX_SIZE_ERR;
+ return Exception { INDEX_SIZE_ERR };
+ return { };
}
-void AnalyserNode::setMinDecibels(double k, ExceptionCode& ec)
+ExceptionOr<void> AnalyserNode::setMinDecibels(double k)
{
- if (k > maxDecibels()) {
- ec = INDEX_SIZE_ERR;
- return;
- }
+ if (k > maxDecibels())
+ return Exception { INDEX_SIZE_ERR };
m_analyser.setMinDecibels(k);
+ return { };
}
-void AnalyserNode::setMaxDecibels(double k, ExceptionCode& ec)
+ExceptionOr<void> AnalyserNode::setMaxDecibels(double k)
{
- if (k < minDecibels()) {
- ec = INDEX_SIZE_ERR;
- return;
- }
+ if (k < minDecibels())
+ return Exception { INDEX_SIZE_ERR };
m_analyser.setMaxDecibels(k);
+ return { };
}
-void AnalyserNode::setSmoothingTimeConstant(double k, ExceptionCode& ec)
+ExceptionOr<void> AnalyserNode::setSmoothingTimeConstant(double k)
{
- if (k < 0 || k > 1) {
- ec = INDEX_SIZE_ERR;
- return;
- }
+ if (k < 0 || k > 1)
+ return Exception { INDEX_SIZE_ERR };
m_analyser.setSmoothingTimeConstant(k);
+ return { };
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/webaudio/AnalyserNode.h b/Source/WebCore/Modules/webaudio/AnalyserNode.h
index 290c67aa9..8bc63b442 100644
--- a/Source/WebCore/Modules/webaudio/AnalyserNode.h
+++ b/Source/WebCore/Modules/webaudio/AnalyserNode.h
@@ -22,56 +22,50 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AnalyserNode_h
-#define AnalyserNode_h
+#pragma once
#include "AudioBasicInspectorNode.h"
#include "RealtimeAnalyser.h"
-#include <wtf/Forward.h>
namespace WebCore {
-class AnalyserNode : public AudioBasicInspectorNode {
+class AnalyserNode final : public AudioBasicInspectorNode {
public:
- static PassRefPtr<AnalyserNode> create(AudioContext* context, float sampleRate)
+ static Ref<AnalyserNode> create(AudioContext& context, float sampleRate)
{
- return adoptRef(new AnalyserNode(context, sampleRate));
+ return adoptRef(*new AnalyserNode(context, sampleRate));
}
virtual ~AnalyserNode();
-
- // AudioNode
- virtual void process(size_t framesToProcess) override;
- virtual void reset() override;
- // Javascript bindings
unsigned fftSize() const { return m_analyser.fftSize(); }
- void setFftSize(unsigned size, ExceptionCode&);
+ ExceptionOr<void> setFftSize(unsigned);
unsigned frequencyBinCount() const { return m_analyser.frequencyBinCount(); }
- void setMinDecibels(double k, ExceptionCode&);
+ ExceptionOr<void> setMinDecibels(double);
double minDecibels() const { return m_analyser.minDecibels(); }
- void setMaxDecibels(double k, ExceptionCode&);
+ ExceptionOr<void> setMaxDecibels(double);
double maxDecibels() const { return m_analyser.maxDecibels(); }
- void setSmoothingTimeConstant(double k, ExceptionCode&);
+ ExceptionOr<void> setSmoothingTimeConstant(double);
double smoothingTimeConstant() const { return m_analyser.smoothingTimeConstant(); }
- void getFloatFrequencyData(JSC::Float32Array* array) { m_analyser.getFloatFrequencyData(array); }
- void getByteFrequencyData(JSC::Uint8Array* array) { m_analyser.getByteFrequencyData(array); }
- void getByteTimeDomainData(JSC::Uint8Array* array) { m_analyser.getByteTimeDomainData(array); }
+ void getFloatFrequencyData(const RefPtr<JSC::Float32Array>& array) { m_analyser.getFloatFrequencyData(array.get()); }
+ void getByteFrequencyData(const RefPtr<JSC::Uint8Array>& array) { m_analyser.getByteFrequencyData(array.get()); }
+ void getByteTimeDomainData(const RefPtr<JSC::Uint8Array>& array) { m_analyser.getByteTimeDomainData(array.get()); }
private:
- virtual double tailTime() const override { return 0; }
- virtual double latencyTime() const override { return 0; }
+ AnalyserNode(AudioContext&, float sampleRate);
- AnalyserNode(AudioContext*, float sampleRate);
+ void process(size_t framesToProcess) final;
+ void reset() final;
+
+ double tailTime() const final { return 0; }
+ double latencyTime() const final { return 0; }
RealtimeAnalyser m_analyser;
};
} // namespace WebCore
-
-#endif // AnalyserNode_h
diff --git a/Source/WebCore/Modules/webaudio/AnalyserNode.idl b/Source/WebCore/Modules/webaudio/AnalyserNode.idl
index 458df84a6..d43f95910 100644
--- a/Source/WebCore/Modules/webaudio/AnalyserNode.idl
+++ b/Source/WebCore/Modules/webaudio/AnalyserNode.idl
@@ -26,21 +26,21 @@
Conditional=WEB_AUDIO,
JSGenerateToJSObject,
] interface AnalyserNode : AudioNode {
- [SetterRaisesException] attribute unsigned long fftSize;
+ [SetterMayThrowException] attribute unsigned long fftSize;
readonly attribute unsigned long frequencyBinCount;
// minDecibels / maxDecibels represent the range to scale the FFT analysis data for conversion to unsigned byte values.
- [SetterRaisesException] attribute double minDecibels;
- [SetterRaisesException] attribute double maxDecibels;
+ [SetterMayThrowException] attribute unrestricted double minDecibels;
+ [SetterMayThrowException] attribute unrestricted double maxDecibels;
// A value from 0.0 -> 1.0 where 0.0 represents no time averaging with the last analysis frame.
- [SetterRaisesException] attribute double smoothingTimeConstant;
+ [SetterMayThrowException] attribute unrestricted double smoothingTimeConstant;
// Copies the current frequency data into the passed array.
// If the array has fewer elements than the frequencyBinCount, the excess elements will be dropped.
- void getFloatFrequencyData(Float32Array array);
- void getByteFrequencyData(Uint8Array array);
+ void getFloatFrequencyData(Float32Array? array); // FIXME: The parameter should not be nullable.
+ void getByteFrequencyData(Uint8Array? array); // FIXME: The parameter should not be nullable.
// Real-time waveform data
- void getByteTimeDomainData(Uint8Array array);
+ void getByteTimeDomainData(Uint8Array? array); // FIXME: The parameter should not be nullable.
};
diff --git a/Source/WebCore/Modules/webaudio/AsyncAudioDecoder.cpp b/Source/WebCore/Modules/webaudio/AsyncAudioDecoder.cpp
index 3698bf5ef..292f05b8c 100644
--- a/Source/WebCore/Modules/webaudio/AsyncAudioDecoder.cpp
+++ b/Source/WebCore/Modules/webaudio/AsyncAudioDecoder.cpp
@@ -38,7 +38,7 @@ namespace WebCore {
AsyncAudioDecoder::AsyncAudioDecoder()
{
// Start worker thread.
- MutexLocker lock(m_threadCreationMutex);
+ LockHolder lock(m_threadCreationMutex);
m_threadID = createThread(AsyncAudioDecoder::threadEntry, this, "Audio Decoder");
}
@@ -51,15 +51,12 @@ AsyncAudioDecoder::~AsyncAudioDecoder()
m_threadID = 0;
}
-void AsyncAudioDecoder::decodeAsync(ArrayBuffer* audioData, float sampleRate, PassRefPtr<AudioBufferCallback> successCallback, PassRefPtr<AudioBufferCallback> errorCallback)
+void AsyncAudioDecoder::decodeAsync(Ref<ArrayBuffer>&& audioData, float sampleRate, RefPtr<AudioBufferCallback>&& successCallback, RefPtr<AudioBufferCallback>&& errorCallback)
{
ASSERT(isMainThread());
- ASSERT(audioData);
- if (!audioData)
- return;
- auto decodingTask = DecodingTask::create(audioData, sampleRate, successCallback, errorCallback);
- m_queue.append(std::move(decodingTask)); // note that ownership of the task is effectively taken by the queue.
+ auto decodingTask = std::make_unique<DecodingTask>(WTFMove(audioData), sampleRate, WTFMove(successCallback), WTFMove(errorCallback));
+ m_queue.append(WTFMove(decodingTask)); // note that ownership of the task is effectively taken by the queue.
}
// Asynchronously decode in this thread.
@@ -76,7 +73,7 @@ void AsyncAudioDecoder::runLoop()
{
// Wait for until we have m_threadID established before starting the run loop.
- MutexLocker lock(m_threadCreationMutex);
+ LockHolder lock(m_threadCreationMutex);
}
// Keep running decoding tasks until we're killed.
@@ -87,40 +84,23 @@ void AsyncAudioDecoder::runLoop()
}
}
-std::unique_ptr<AsyncAudioDecoder::DecodingTask> AsyncAudioDecoder::DecodingTask::create(ArrayBuffer* audioData, float sampleRate, PassRefPtr<AudioBufferCallback> successCallback, PassRefPtr<AudioBufferCallback> errorCallback)
-{
- return std::unique_ptr<DecodingTask>(new DecodingTask(audioData, sampleRate, successCallback, errorCallback));
-}
-
-AsyncAudioDecoder::DecodingTask::DecodingTask(ArrayBuffer* audioData, float sampleRate, PassRefPtr<AudioBufferCallback> successCallback, PassRefPtr<AudioBufferCallback> errorCallback)
- : m_audioData(audioData)
+AsyncAudioDecoder::DecodingTask::DecodingTask(Ref<ArrayBuffer>&& audioData, float sampleRate, RefPtr<AudioBufferCallback>&& successCallback, RefPtr<AudioBufferCallback>&& errorCallback)
+ : m_audioData(WTFMove(audioData))
, m_sampleRate(sampleRate)
- , m_successCallback(successCallback)
- , m_errorCallback(errorCallback)
+ , m_successCallback(WTFMove(successCallback))
+ , m_errorCallback(WTFMove(errorCallback))
{
}
void AsyncAudioDecoder::DecodingTask::decode()
{
- ASSERT(m_audioData.get());
- if (!m_audioData.get())
- return;
-
// Do the actual decoding and invoke the callback.
m_audioBuffer = AudioBuffer::createFromAudioFileData(m_audioData->data(), m_audioData->byteLength(), false, sampleRate());
// Decoding is finished, but we need to do the callbacks on the main thread.
- callOnMainThread(notifyCompleteDispatch, this);
-}
-
-void AsyncAudioDecoder::DecodingTask::notifyCompleteDispatch(void* userData)
-{
- AsyncAudioDecoder::DecodingTask* task = reinterpret_cast<AsyncAudioDecoder::DecodingTask*>(userData);
- ASSERT(task);
- if (!task)
- return;
-
- task->notifyComplete();
+ callOnMainThread([this] {
+ notifyComplete();
+ });
}
void AsyncAudioDecoder::DecodingTask::notifyComplete()
diff --git a/Source/WebCore/Modules/webaudio/AsyncAudioDecoder.h b/Source/WebCore/Modules/webaudio/AsyncAudioDecoder.h
index ff096ecaf..bb5c5e337 100644
--- a/Source/WebCore/Modules/webaudio/AsyncAudioDecoder.h
+++ b/Source/WebCore/Modules/webaudio/AsyncAudioDecoder.h
@@ -22,13 +22,11 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AsyncAudioDecoder_h
-#define AsyncAudioDecoder_h
+#pragma once
#include <memory>
#include <wtf/Forward.h>
#include <wtf/MessageQueue.h>
-#include <wtf/PassRefPtr.h>
#include <wtf/RefPtr.h>
#include <wtf/Threading.h>
@@ -51,29 +49,25 @@ public:
~AsyncAudioDecoder();
// Must be called on the main thread.
- void decodeAsync(JSC::ArrayBuffer* audioData, float sampleRate, PassRefPtr<AudioBufferCallback> successCallback, PassRefPtr<AudioBufferCallback> errorCallback);
+ void decodeAsync(Ref<JSC::ArrayBuffer>&& audioData, float sampleRate, RefPtr<AudioBufferCallback>&& successCallback, RefPtr<AudioBufferCallback>&& errorCallback);
private:
class DecodingTask {
WTF_MAKE_NONCOPYABLE(DecodingTask);
public:
- static std::unique_ptr<DecodingTask> create(JSC::ArrayBuffer* audioData, float sampleRate, PassRefPtr<AudioBufferCallback> successCallback, PassRefPtr<AudioBufferCallback> errorCallback);
-
+ DecodingTask(Ref<JSC::ArrayBuffer>&& audioData, float sampleRate, RefPtr<AudioBufferCallback>&& successCallback, RefPtr<AudioBufferCallback>&& errorCallback);
void decode();
private:
- DecodingTask(JSC::ArrayBuffer* audioData, float sampleRate, PassRefPtr<AudioBufferCallback> successCallback, PassRefPtr<AudioBufferCallback> errorCallback);
-
- JSC::ArrayBuffer* audioData() { return m_audioData.get(); }
+ JSC::ArrayBuffer& audioData() { return m_audioData; }
float sampleRate() const { return m_sampleRate; }
AudioBufferCallback* successCallback() { return m_successCallback.get(); }
AudioBufferCallback* errorCallback() { return m_errorCallback.get(); }
AudioBuffer* audioBuffer() { return m_audioBuffer.get(); }
- static void notifyCompleteDispatch(void* userData);
void notifyComplete();
- RefPtr<JSC::ArrayBuffer> m_audioData;
+ Ref<JSC::ArrayBuffer> m_audioData;
float m_sampleRate;
RefPtr<AudioBufferCallback> m_successCallback;
RefPtr<AudioBufferCallback> m_errorCallback;
@@ -84,10 +78,8 @@ private:
void runLoop();
WTF::ThreadIdentifier m_threadID;
- Mutex m_threadCreationMutex;
+ Lock m_threadCreationMutex;
MessageQueue<DecodingTask> m_queue;
};
} // namespace WebCore
-
-#endif // AsyncAudioDecoder_h
diff --git a/Source/WebCore/Modules/webaudio/AudioBasicInspectorNode.cpp b/Source/WebCore/Modules/webaudio/AudioBasicInspectorNode.cpp
index 58fd9b2ab..27e56c4c6 100644
--- a/Source/WebCore/Modules/webaudio/AudioBasicInspectorNode.cpp
+++ b/Source/WebCore/Modules/webaudio/AudioBasicInspectorNode.cpp
@@ -28,15 +28,13 @@
#include "AudioBasicInspectorNode.h"
-#include "AudioContext.h"
#include "AudioNodeInput.h"
#include "AudioNodeOutput.h"
namespace WebCore {
-AudioBasicInspectorNode::AudioBasicInspectorNode(AudioContext* context, float sampleRate, unsigned outputChannelCount)
+AudioBasicInspectorNode::AudioBasicInspectorNode(AudioContext& context, float sampleRate, unsigned outputChannelCount)
: AudioNode(context, sampleRate)
- , m_needAutomaticPull(false)
{
addInput(std::make_unique<AudioNodeInput>(this));
addOutput(std::make_unique<AudioNodeOutput>(this, outputChannelCount));
@@ -51,29 +49,31 @@ void AudioBasicInspectorNode::pullInputs(size_t framesToProcess)
input(0)->pull(output(0)->bus(), framesToProcess);
}
-void AudioBasicInspectorNode::connect(AudioNode* destination, unsigned outputIndex, unsigned inputIndex, ExceptionCode& ec)
+ExceptionOr<void> AudioBasicInspectorNode::connect(AudioNode& destination, unsigned outputIndex, unsigned inputIndex)
{
ASSERT(isMainThread());
- AudioContext::AutoLocker locker(*context());
+ AudioContext::AutoLocker locker(context());
- AudioNode::connect(destination, outputIndex, inputIndex, ec);
+ auto result = AudioNode::connect(destination, outputIndex, inputIndex);
updatePullStatus();
+ return result;
}
-void AudioBasicInspectorNode::disconnect(unsigned outputIndex, ExceptionCode& ec)
+ExceptionOr<void> AudioBasicInspectorNode::disconnect(unsigned outputIndex)
{
ASSERT(isMainThread());
- AudioContext::AutoLocker locker(*context());
+ AudioContext::AutoLocker locker(context());
- AudioNode::disconnect(outputIndex, ec);
+ auto result = AudioNode::disconnect(outputIndex);
updatePullStatus();
+ return result;
}
void AudioBasicInspectorNode::checkNumberOfChannelsForInput(AudioNodeInput* input)
{
- ASSERT(context()->isAudioThread() && context()->isGraphOwner());
+ ASSERT(context().isAudioThread() && context().isGraphOwner());
ASSERT(input == this->input(0));
if (input != this->input(0))
@@ -93,13 +93,13 @@ void AudioBasicInspectorNode::checkNumberOfChannelsForInput(AudioNodeInput* inpu
void AudioBasicInspectorNode::updatePullStatus()
{
- ASSERT(context()->isGraphOwner());
+ ASSERT(context().isGraphOwner());
if (output(0)->isConnected()) {
// When an AudioBasicInspectorNode is connected to a downstream node, it will get pulled by the
// downstream node, thus remove it from the context's automatic pull list.
if (m_needAutomaticPull) {
- context()->removeAutomaticPullNode(this);
+ context().removeAutomaticPullNode(this);
m_needAutomaticPull = false;
}
} else {
@@ -107,11 +107,11 @@ void AudioBasicInspectorNode::updatePullStatus()
if (numberOfInputConnections && !m_needAutomaticPull) {
// When an AudioBasicInspectorNode is not connected to any downstream node while still connected from
// upstream node(s), add it to the context's automatic pull list.
- context()->addAutomaticPullNode(this);
+ context().addAutomaticPullNode(this);
m_needAutomaticPull = true;
} else if (!numberOfInputConnections && m_needAutomaticPull) {
// The AudioBasicInspectorNode is connected to nothing, remove it from the context's automatic pull list.
- context()->removeAutomaticPullNode(this);
+ context().removeAutomaticPullNode(this);
m_needAutomaticPull = false;
}
}
diff --git a/Source/WebCore/Modules/webaudio/AudioBasicInspectorNode.h b/Source/WebCore/Modules/webaudio/AudioBasicInspectorNode.h
index 2f4258ea3..b86f1a387 100644
--- a/Source/WebCore/Modules/webaudio/AudioBasicInspectorNode.h
+++ b/Source/WebCore/Modules/webaudio/AudioBasicInspectorNode.h
@@ -22,8 +22,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AudioBasicInspectorNode_h
-#define AudioBasicInspectorNode_h
+#pragma once
#include "AudioNode.h"
@@ -34,19 +33,17 @@ namespace WebCore {
// AudioContext before the end of each render quantum so that it can inspect the audio stream.
class AudioBasicInspectorNode : public AudioNode {
public:
- AudioBasicInspectorNode(AudioContext*, float sampleRate, unsigned outputChannelCount);
-
- // AudioNode
- virtual void pullInputs(size_t framesToProcess) override;
- virtual void connect(AudioNode*, unsigned outputIndex, unsigned inputIndex, ExceptionCode&) override;
- virtual void disconnect(unsigned outputIndex, ExceptionCode&) override;
- virtual void checkNumberOfChannelsForInput(AudioNodeInput*) override;
+ AudioBasicInspectorNode(AudioContext&, float sampleRate, unsigned outputChannelCount);
private:
+ void pullInputs(size_t framesToProcess) override;
+ ExceptionOr<void> connect(AudioNode&, unsigned outputIndex, unsigned inputIndex) override;
+ ExceptionOr<void> disconnect(unsigned outputIndex) override;
+ void checkNumberOfChannelsForInput(AudioNodeInput*) override;
+
void updatePullStatus();
- bool m_needAutomaticPull; // When setting to true, AudioBasicInspectorNode will be pulled automaticlly by AudioContext before the end of each render quantum.
+
+ bool m_needAutomaticPull { false }; // When setting to true, AudioBasicInspectorNode will be pulled automatically by AudioContext before the end of each render quantum.
};
} // namespace WebCore
-
-#endif // AudioBasicInspectorNode_h
diff --git a/Source/WebCore/Modules/webaudio/AudioBasicProcessorNode.cpp b/Source/WebCore/Modules/webaudio/AudioBasicProcessorNode.cpp
index afa67a9ad..4279a0916 100644
--- a/Source/WebCore/Modules/webaudio/AudioBasicProcessorNode.cpp
+++ b/Source/WebCore/Modules/webaudio/AudioBasicProcessorNode.cpp
@@ -36,7 +36,7 @@
namespace WebCore {
-AudioBasicProcessorNode::AudioBasicProcessorNode(AudioContext* context, float sampleRate)
+AudioBasicProcessorNode::AudioBasicProcessorNode(AudioContext& context, float sampleRate)
: AudioNode(context, sampleRate)
{
addInput(std::make_unique<AudioNodeInput>(this));
@@ -102,7 +102,7 @@ void AudioBasicProcessorNode::reset()
// uninitialize and then re-initialize with the new channel count.
void AudioBasicProcessorNode::checkNumberOfChannelsForInput(AudioNodeInput* input)
{
- ASSERT(context()->isAudioThread() && context()->isGraphOwner());
+ ASSERT(context().isAudioThread() && context().isGraphOwner());
ASSERT(input == this->input(0));
if (input != this->input(0))
diff --git a/Source/WebCore/Modules/webaudio/AudioBasicProcessorNode.h b/Source/WebCore/Modules/webaudio/AudioBasicProcessorNode.h
index d8946e1c9..e7e116b75 100644
--- a/Source/WebCore/Modules/webaudio/AudioBasicProcessorNode.h
+++ b/Source/WebCore/Modules/webaudio/AudioBasicProcessorNode.h
@@ -22,12 +22,10 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AudioBasicProcessorNode_h
-#define AudioBasicProcessorNode_h
+#pragma once
#include "AudioNode.h"
#include <memory>
-#include <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>
#include <wtf/Threading.h>
@@ -40,29 +38,27 @@ class AudioProcessor;
// AudioBasicProcessorNode is an AudioNode with one input and one output where the input and output have the same number of channels.
class AudioBasicProcessorNode : public AudioNode {
public:
- AudioBasicProcessorNode(AudioContext*, float sampleRate);
+ AudioBasicProcessorNode(AudioContext&, float sampleRate);
// AudioNode
- virtual void process(size_t framesToProcess) override;
- virtual void pullInputs(size_t framesToProcess) override;
- virtual void reset() override;
- virtual void initialize() override;
- virtual void uninitialize() override;
+ void process(size_t framesToProcess) override;
+ void pullInputs(size_t framesToProcess) override;
+ void reset() override;
+ void initialize() override;
+ void uninitialize() override;
// Called in the main thread when the number of channels for the input may have changed.
- virtual void checkNumberOfChannelsForInput(AudioNodeInput*) override;
+ void checkNumberOfChannelsForInput(AudioNodeInput*) override;
// Returns the number of channels for both the input and the output.
unsigned numberOfChannels();
protected:
- virtual double tailTime() const override;
- virtual double latencyTime() const override;
+ double tailTime() const override;
+ double latencyTime() const override;
AudioProcessor* processor() { return m_processor.get(); }
std::unique_ptr<AudioProcessor> m_processor;
};
} // namespace WebCore
-
-#endif // AudioBasicProcessorNode_h
diff --git a/Source/WebCore/Modules/webaudio/AudioBuffer.cpp b/Source/WebCore/Modules/webaudio/AudioBuffer.cpp
index 609ab567e..d14e6694c 100644
--- a/Source/WebCore/Modules/webaudio/AudioBuffer.cpp
+++ b/Source/WebCore/Modules/webaudio/AudioBuffer.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.
*
@@ -32,101 +32,110 @@
#include "AudioBuffer.h"
-#include "AudioBus.h"
#include "AudioContext.h"
#include "AudioFileReader.h"
-#include "ExceptionCode.h"
-#include "ExceptionCodePlaceholder.h"
-
-#include <runtime/Operations.h>
+#include <runtime/JSCInlines.h>
#include <runtime/TypedArrayInlines.h>
namespace WebCore {
-PassRefPtr<AudioBuffer> AudioBuffer::create(unsigned numberOfChannels, size_t numberOfFrames, float sampleRate)
+RefPtr<AudioBuffer> AudioBuffer::create(unsigned numberOfChannels, size_t numberOfFrames, float sampleRate)
{
if (sampleRate < 22050 || sampleRate > 96000 || numberOfChannels > AudioContext::maxNumberOfChannels() || !numberOfFrames)
return nullptr;
-
- return adoptRef(new AudioBuffer(numberOfChannels, numberOfFrames, sampleRate));
+
+ auto buffer = adoptRef(*new AudioBuffer(numberOfChannels, numberOfFrames, sampleRate));
+ if (!buffer->m_length)
+ return nullptr;
+
+ return WTFMove(buffer);
}
-PassRefPtr<AudioBuffer> AudioBuffer::createFromAudioFileData(const void* data, size_t dataSize, bool mixToMono, float sampleRate)
+RefPtr<AudioBuffer> AudioBuffer::createFromAudioFileData(const void* data, size_t dataSize, bool mixToMono, float sampleRate)
{
RefPtr<AudioBus> bus = createBusFromInMemoryAudioFile(data, dataSize, mixToMono, sampleRate);
- if (bus.get())
- return adoptRef(new AudioBuffer(bus.get()));
-
- return nullptr;
+ if (!bus)
+ return nullptr;
+ return adoptRef(*new AudioBuffer(*bus));
}
AudioBuffer::AudioBuffer(unsigned numberOfChannels, size_t numberOfFrames, float sampleRate)
- : m_gain(1.0)
- , m_sampleRate(sampleRate)
+ : m_sampleRate(sampleRate)
, m_length(numberOfFrames)
{
m_channels.reserveCapacity(numberOfChannels);
for (unsigned i = 0; i < numberOfChannels; ++i) {
- RefPtr<Float32Array> channelDataArray = Float32Array::create(m_length);
+ auto channelDataArray = Float32Array::create(m_length);
+ if (!channelDataArray) {
+ invalidate();
+ break;
+ }
+
channelDataArray->setNeuterable(false);
- m_channels.append(channelDataArray);
+ m_channels.append(WTFMove(channelDataArray));
}
}
-AudioBuffer::AudioBuffer(AudioBus* bus)
- : m_gain(1.0)
- , m_sampleRate(bus->sampleRate())
- , m_length(bus->length())
+AudioBuffer::AudioBuffer(AudioBus& bus)
+ : m_sampleRate(bus.sampleRate())
+ , m_length(bus.length())
{
// Copy audio data from the bus to the Float32Arrays we manage.
- unsigned numberOfChannels = bus->numberOfChannels();
+ unsigned numberOfChannels = bus.numberOfChannels();
m_channels.reserveCapacity(numberOfChannels);
for (unsigned i = 0; i < numberOfChannels; ++i) {
- RefPtr<Float32Array> channelDataArray = Float32Array::create(m_length);
+ auto channelDataArray = Float32Array::create(m_length);
+ if (!channelDataArray) {
+ invalidate();
+ break;
+ }
+
channelDataArray->setNeuterable(false);
- channelDataArray->setRange(bus->channel(i)->data(), m_length, 0);
- m_channels.append(channelDataArray);
+ channelDataArray->setRange(bus.channel(i)->data(), m_length, 0);
+ m_channels.append(WTFMove(channelDataArray));
}
}
+void AudioBuffer::invalidate()
+{
+ releaseMemory();
+ m_length = 0;
+}
+
void AudioBuffer::releaseMemory()
{
m_channels.clear();
}
-PassRefPtr<Float32Array> AudioBuffer::getChannelData(unsigned channelIndex, ExceptionCode& ec)
+ExceptionOr<Ref<Float32Array>> AudioBuffer::getChannelData(unsigned channelIndex)
{
- if (channelIndex >= m_channels.size()) {
- ec = SYNTAX_ERR;
- return nullptr;
- }
-
- Float32Array* channelData = m_channels[channelIndex].get();
- return Float32Array::create(channelData->buffer(), channelData->byteOffset(), channelData->length());
+ if (channelIndex >= m_channels.size())
+ return Exception { SYNTAX_ERR };
+ auto& channelData = *m_channels[channelIndex];
+ auto array = Float32Array::create(channelData.unsharedBuffer(), channelData.byteOffset(), channelData.length());
+ RELEASE_ASSERT(array);
+ return array.releaseNonNull();
}
-Float32Array* AudioBuffer::getChannelData(unsigned channelIndex)
+Float32Array* AudioBuffer::channelData(unsigned channelIndex)
{
if (channelIndex >= m_channels.size())
return nullptr;
-
return m_channels[channelIndex].get();
}
void AudioBuffer::zero()
{
- for (unsigned i = 0; i < m_channels.size(); ++i) {
- if (getChannelData(i))
- getChannelData(i)->zeroRange(0, length());
- }
+ for (auto& channel : m_channels)
+ channel->zeroRange(0, length());
}
size_t AudioBuffer::memoryCost() const
{
size_t cost = 0;
- for (unsigned i = 0; i < m_channels.size() ; ++i)
- cost += m_channels[i]->byteLength();
+ for (auto& channel : m_channels)
+ cost += channel->byteLength();
return cost;
}
diff --git a/Source/WebCore/Modules/webaudio/AudioBuffer.h b/Source/WebCore/Modules/webaudio/AudioBuffer.h
index d52c02805..9c9dd1dac 100644
--- a/Source/WebCore/Modules/webaudio/AudioBuffer.h
+++ b/Source/WebCore/Modules/webaudio/AudioBuffer.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,27 +26,22 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AudioBuffer_h
-#define AudioBuffer_h
+#pragma once
+#include "ExceptionOr.h"
#include <runtime/Float32Array.h>
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/RefPtr.h>
#include <wtf/Vector.h>
namespace WebCore {
class AudioBus;
-typedef int ExceptionCode;
-
class AudioBuffer : public RefCounted<AudioBuffer> {
public:
- static PassRefPtr<AudioBuffer> create(unsigned numberOfChannels, size_t numberOfFrames, float sampleRate);
+ static RefPtr<AudioBuffer> create(unsigned numberOfChannels, size_t numberOfFrames, float sampleRate);
- // Returns 0 if data is not a valid audio file.
- static PassRefPtr<AudioBuffer> createFromAudioFileData(const void* data, size_t dataSize, bool mixToMono, float sampleRate);
+ // Returns nullptr if data is not a valid audio file.
+ static RefPtr<AudioBuffer> createFromAudioFileData(const void* data, size_t dataSize, bool mixToMono, float sampleRate);
// Format
size_t length() const { return m_length; }
@@ -55,32 +50,31 @@ public:
// Channel data access
unsigned numberOfChannels() const { return m_channels.size(); }
- PassRefPtr<Float32Array> getChannelData(unsigned channelIndex, ExceptionCode&);
- Float32Array* getChannelData(unsigned channelIndex);
+ ExceptionOr<Ref<Float32Array>> getChannelData(unsigned channelIndex);
+ Float32Array* channelData(unsigned channelIndex);
void zero();
// Scalar gain
double gain() const { return m_gain; }
void setGain(double gain) { m_gain = gain; }
- // Because an AudioBuffer has a JavaScript wrapper, which will be garbage collected, it may take awhile for this object to be deleted.
+ // Because an AudioBuffer has a JavaScript wrapper, which will be garbage collected, it may take a while for this object to be deleted.
// releaseMemory() can be called when the AudioContext goes away, so we can release the memory earlier than when the garbage collection happens.
// Careful! Only call this when the page unloads, after the AudioContext is no longer processing.
void releaseMemory();
size_t memoryCost() const;
-protected:
+private:
AudioBuffer(unsigned numberOfChannels, size_t numberOfFrames, float sampleRate);
- explicit AudioBuffer(AudioBus*);
+ explicit AudioBuffer(AudioBus&);
+
+ void invalidate();
- double m_gain; // scalar gain
+ double m_gain { 1.0 }; // scalar gain
float m_sampleRate;
size_t m_length;
-
Vector<RefPtr<Float32Array>> m_channels;
};
} // namespace WebCore
-
-#endif // AudioBuffer_h
diff --git a/Source/WebCore/Modules/webaudio/AudioBuffer.idl b/Source/WebCore/Modules/webaudio/AudioBuffer.idl
index 37a570040..a1a902e06 100644
--- a/Source/WebCore/Modules/webaudio/AudioBuffer.idl
+++ b/Source/WebCore/Modules/webaudio/AudioBuffer.idl
@@ -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.
*
@@ -29,14 +29,15 @@
[
Conditional=WEB_AUDIO,
ImplementationLacksVTable,
+ ReportExtraMemoryCost,
] interface AudioBuffer {
readonly attribute long length; // in sample-frames
- readonly attribute float duration; // in seconds
- readonly attribute float sampleRate; // in sample-frames per second
+ readonly attribute unrestricted float duration; // in seconds
+ readonly attribute unrestricted float sampleRate; // in sample-frames per second
- attribute float gain; // linear gain (default 1.0)
+ attribute unrestricted float gain; // linear gain (default 1.0)
// Channel access
readonly attribute unsigned long numberOfChannels;
- [RaisesException] Float32Array getChannelData(unsigned long channelIndex);
+ [MayThrowException] Float32Array getChannelData(unsigned long channelIndex);
};
diff --git a/Source/WebCore/Modules/webaudio/AudioBufferCallback.h b/Source/WebCore/Modules/webaudio/AudioBufferCallback.h
index feebc469b..27cc6ac79 100644
--- a/Source/WebCore/Modules/webaudio/AudioBufferCallback.h
+++ b/Source/WebCore/Modules/webaudio/AudioBufferCallback.h
@@ -22,8 +22,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AudioBufferCallback_h
-#define AudioBufferCallback_h
+#pragma once
#if ENABLE(WEB_AUDIO)
@@ -39,8 +38,6 @@ public:
virtual bool handleEvent(AudioBuffer*) = 0;
};
-} // namespace
+} // namespace WebCore
#endif // ENABLE(WEB_AUDIO)
-
-#endif // AudioBufferCallback_h
diff --git a/Source/WebCore/Modules/webaudio/AudioBufferCallback.idl b/Source/WebCore/Modules/webaudio/AudioBufferCallback.idl
index 913f57779..74577fd0e 100644
--- a/Source/WebCore/Modules/webaudio/AudioBufferCallback.idl
+++ b/Source/WebCore/Modules/webaudio/AudioBufferCallback.idl
@@ -25,6 +25,4 @@
[
Conditional=WEB_AUDIO,
JSGenerateToJSObject,
-] callback interface AudioBufferCallback {
- boolean handleEvent(AudioBuffer audioBuffer);
-};
+] callback AudioBufferCallback = void (AudioBuffer audioBuffer);
diff --git a/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.cpp b/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.cpp
index 8ee6d61b9..11319a85c 100644
--- a/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.cpp
+++ b/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.cpp
@@ -28,17 +28,14 @@
#include "AudioBufferSourceNode.h"
+#include "AudioBuffer.h"
#include "AudioContext.h"
#include "AudioNodeOutput.h"
+#include "AudioParam.h"
#include "AudioUtilities.h"
#include "FloatConversion.h"
-#include "ScriptCallStack.h"
-#include "ScriptController.h"
+#include "PannerNode.h"
#include "ScriptExecutionContext.h"
-#include <algorithm>
-#include <wtf/MainThread.h>
-#include <wtf/MathExtras.h>
-#include <wtf/StdLibExtras.h>
namespace WebCore {
@@ -49,14 +46,14 @@ const double DefaultGrainDuration = 0.020; // 20ms
// to minimize linear interpolation aliasing.
const double MaxRate = 1024;
-PassRefPtr<AudioBufferSourceNode> AudioBufferSourceNode::create(AudioContext* context, float sampleRate)
+Ref<AudioBufferSourceNode> AudioBufferSourceNode::create(AudioContext& context, float sampleRate)
{
- return adoptRef(new AudioBufferSourceNode(context, sampleRate));
+ return adoptRef(*new AudioBufferSourceNode(context, sampleRate));
}
-AudioBufferSourceNode::AudioBufferSourceNode(AudioContext* context, float sampleRate)
+AudioBufferSourceNode::AudioBufferSourceNode(AudioContext& context, float sampleRate)
: AudioScheduledSourceNode(context, sampleRate)
- , m_buffer(0)
+ , m_buffer(nullptr)
, m_isLooping(false)
, m_loopStart(0)
, m_loopEnd(0)
@@ -65,12 +62,12 @@ AudioBufferSourceNode::AudioBufferSourceNode(AudioContext* context, float sample
, m_grainOffset(0.0)
, m_grainDuration(DefaultGrainDuration)
, m_lastGain(1.0)
- , m_pannerNode(0)
+ , m_pannerNode(nullptr)
{
setNodeType(NodeTypeAudioBufferSource);
m_gain = AudioParam::create(context, "gain", 1.0, 0.0, 1.0);
- m_playbackRate = AudioParam::create(context, "playbackRate", 1.0, 0.0, MaxRate);
+ m_playbackRate = AudioParam::create(context, "playbackRate", 1.0, -MaxRate, MaxRate);
// Default to mono. A call to setBuffer() will set the number of output channels to that of the buffer.
addOutput(std::make_unique<AudioNodeOutput>(this, 1));
@@ -86,23 +83,23 @@ AudioBufferSourceNode::~AudioBufferSourceNode()
void AudioBufferSourceNode::process(size_t framesToProcess)
{
- AudioBus* outputBus = output(0)->bus();
+ auto& outputBus = *output(0)->bus();
if (!isInitialized()) {
- outputBus->zero();
+ outputBus.zero();
return;
}
// The audio thread can't block on this lock, so we use std::try_to_lock instead.
- std::unique_lock<std::mutex> lock(m_processMutex, std::try_to_lock);
+ std::unique_lock<Lock> lock(m_processMutex, std::try_to_lock);
if (!lock.owns_lock()) {
// Too bad - the try_lock() failed. We must be in the middle of changing buffers and were already outputting silence anyway.
- outputBus->zero();
+ outputBus.zero();
return;
}
if (!buffer()) {
- outputBus->zero();
+ outputBus.zero();
return;
}
@@ -110,33 +107,32 @@ void AudioBufferSourceNode::process(size_t framesToProcess)
// before the output bus is updated to the new number of channels because of use of tryLocks() in the context's updating system.
// In this case, if the the buffer has just been changed and we're not quite ready yet, then just output silence.
if (numberOfChannels() != buffer()->numberOfChannels()) {
- outputBus->zero();
+ outputBus.zero();
return;
}
size_t quantumFrameOffset;
size_t bufferFramesToProcess;
-
updateSchedulingInfo(framesToProcess, outputBus, quantumFrameOffset, bufferFramesToProcess);
if (!bufferFramesToProcess) {
- outputBus->zero();
+ outputBus.zero();
return;
}
- for (unsigned i = 0; i < outputBus->numberOfChannels(); ++i)
- m_destinationChannels[i] = outputBus->channel(i)->mutableData();
+ for (unsigned i = 0; i < outputBus.numberOfChannels(); ++i)
+ m_destinationChannels[i] = outputBus.channel(i)->mutableData();
// Render by reading directly from the buffer.
- if (!renderFromBuffer(outputBus, quantumFrameOffset, bufferFramesToProcess)) {
- outputBus->zero();
+ if (!renderFromBuffer(&outputBus, quantumFrameOffset, bufferFramesToProcess)) {
+ outputBus.zero();
return;
}
// Apply the gain (in-place) to the output bus.
float totalGain = gain()->value() * m_buffer->gain();
- outputBus->copyWithGainFrom(*outputBus, &m_lastGain, totalGain);
- outputBus->clearSilentFlag();
+ outputBus.copyWithGainFrom(outputBus, &m_lastGain, totalGain);
+ outputBus.clearSilentFlag();
}
// Returns true if we're finished.
@@ -160,7 +156,7 @@ bool AudioBufferSourceNode::renderSilenceAndFinishIfNotLooping(AudioBus*, unsign
bool AudioBufferSourceNode::renderFromBuffer(AudioBus* bus, unsigned destinationFrameOffset, size_t numberOfFrames)
{
- ASSERT(context()->isAudioThread());
+ ASSERT(context().isAudioThread());
// Basic sanity checking
ASSERT(bus);
@@ -200,47 +196,54 @@ bool AudioBufferSourceNode::renderFromBuffer(AudioBus* bus, unsigned destination
size_t bufferLength = buffer()->length();
double bufferSampleRate = buffer()->sampleRate();
+ double pitchRate = totalPitchRate();
+ bool reverse = pitchRate < 0;
// Avoid converting from time to sample-frames twice by computing
// the grain end time first before computing the sample frame.
- unsigned endFrame = m_isGrain ? AudioUtilities::timeToSampleFrame(m_grainOffset + m_grainDuration, bufferSampleRate) : bufferLength;
-
- // This is a HACK to allow for HRTF tail-time - avoids glitch at end.
- // FIXME: implement tailTime for each AudioNode for a more general solution to this problem.
- // https://bugs.webkit.org/show_bug.cgi?id=77224
+ unsigned maxFrame;
if (m_isGrain)
- endFrame += 512;
+ maxFrame = AudioUtilities::timeToSampleFrame(m_grainOffset + m_grainDuration, bufferSampleRate);
+ else
+ maxFrame = bufferLength;
// Do some sanity checking.
- if (endFrame > bufferLength)
- endFrame = bufferLength;
- if (m_virtualReadIndex >= endFrame)
+ if (maxFrame > bufferLength)
+ maxFrame = bufferLength;
+ if (reverse && m_virtualReadIndex <= 0)
+ m_virtualReadIndex = maxFrame - 1;
+ else if (!reverse && m_virtualReadIndex >= maxFrame)
m_virtualReadIndex = 0; // reset to start
// If the .loop attribute is true, then values of m_loopStart == 0 && m_loopEnd == 0 implies
// that we should use the entire buffer as the loop, otherwise use the loop values in m_loopStart and m_loopEnd.
- double virtualEndFrame = endFrame;
- double virtualDeltaFrames = endFrame;
+ double virtualMaxFrame = maxFrame;
+ double virtualMinFrame = 0;
+ double virtualDeltaFrames = maxFrame;
if (loop() && (m_loopStart || m_loopEnd) && m_loopStart >= 0 && m_loopEnd > 0 && m_loopStart < m_loopEnd) {
// Convert from seconds to sample-frames.
- double loopStartFrame = m_loopStart * buffer()->sampleRate();
- double loopEndFrame = m_loopEnd * buffer()->sampleRate();
+ double loopMinFrame = m_loopStart * buffer()->sampleRate();
+ double loopMaxFrame = m_loopEnd * buffer()->sampleRate();
- virtualEndFrame = std::min(loopEndFrame, virtualEndFrame);
- virtualDeltaFrames = virtualEndFrame - loopStartFrame;
+ virtualMaxFrame = std::min(loopMaxFrame, virtualMaxFrame);
+ virtualMinFrame = std::max(loopMinFrame, virtualMinFrame);
+ virtualDeltaFrames = virtualMaxFrame - virtualMinFrame;
}
- double pitchRate = totalPitchRate();
-
// Sanity check that our playback rate isn't larger than the loop size.
- if (pitchRate >= virtualDeltaFrames)
+ if (fabs(pitchRate) >= virtualDeltaFrames)
return false;
// Get local copy.
double virtualReadIndex = m_virtualReadIndex;
+ bool needsInterpolation = virtualReadIndex != floor(virtualReadIndex)
+ || virtualDeltaFrames != floor(virtualDeltaFrames)
+ || virtualMaxFrame != floor(virtualMaxFrame)
+ || virtualMinFrame != floor(virtualMinFrame);
+
// Render loop - reading from the source buffer to the destination using linear interpolation.
int framesToProcess = numberOfFrames;
@@ -249,14 +252,12 @@ bool AudioBufferSourceNode::renderFromBuffer(AudioBus* bus, unsigned destination
// Optimize for the very common case of playing back with pitchRate == 1.
// We can avoid the linear interpolation.
- if (pitchRate == 1 && virtualReadIndex == floor(virtualReadIndex)
- && virtualDeltaFrames == floor(virtualDeltaFrames)
- && virtualEndFrame == floor(virtualEndFrame)) {
+ if (pitchRate == 1 && !needsInterpolation) {
unsigned readIndex = static_cast<unsigned>(virtualReadIndex);
unsigned deltaFrames = static_cast<unsigned>(virtualDeltaFrames);
- endFrame = static_cast<unsigned>(virtualEndFrame);
+ maxFrame = static_cast<unsigned>(virtualMaxFrame);
while (framesToProcess > 0) {
- int framesToEnd = endFrame - readIndex;
+ int framesToEnd = maxFrame - readIndex;
int framesThisTime = std::min(framesToProcess, framesToEnd);
framesThisTime = std::max(0, framesThisTime);
@@ -268,13 +269,83 @@ bool AudioBufferSourceNode::renderFromBuffer(AudioBus* bus, unsigned destination
framesToProcess -= framesThisTime;
// Wrap-around.
- if (readIndex >= endFrame) {
+ if (readIndex >= maxFrame) {
readIndex -= deltaFrames;
if (renderSilenceAndFinishIfNotLooping(bus, writeIndex, framesToProcess))
break;
}
}
virtualReadIndex = readIndex;
+ } else if (pitchRate == -1 && !needsInterpolation) {
+ int readIndex = static_cast<int>(virtualReadIndex);
+ int deltaFrames = static_cast<int>(virtualDeltaFrames);
+ int minFrame = static_cast<int>(virtualMinFrame) - 1;
+ while (framesToProcess > 0) {
+ int framesToEnd = readIndex - minFrame;
+ int framesThisTime = std::min<int>(framesToProcess, framesToEnd);
+ framesThisTime = std::max<int>(0, framesThisTime);
+
+ while (framesThisTime--) {
+ for (unsigned i = 0; i < numberOfChannels; ++i) {
+ float* destination = destinationChannels[i];
+ const float* source = sourceChannels[i];
+
+ destination[writeIndex] = source[readIndex];
+ }
+
+ ++writeIndex;
+ --readIndex;
+ --framesToProcess;
+ }
+
+ // Wrap-around.
+ if (readIndex <= minFrame) {
+ readIndex += deltaFrames;
+ if (renderSilenceAndFinishIfNotLooping(bus, writeIndex, framesToProcess))
+ break;
+ }
+ }
+ virtualReadIndex = readIndex;
+ } else if (!pitchRate) {
+ unsigned readIndex = static_cast<unsigned>(virtualReadIndex);
+
+ for (unsigned i = 0; i < numberOfChannels; ++i)
+ std::fill_n(destinationChannels[i], framesToProcess, sourceChannels[i][readIndex]);
+ } else if (reverse) {
+ unsigned maxFrame = static_cast<unsigned>(virtualMaxFrame);
+ unsigned minFrame = static_cast<unsigned>(floorf(virtualMinFrame));
+
+ while (framesToProcess--) {
+ unsigned readIndex = static_cast<unsigned>(floorf(virtualReadIndex));
+ double interpolationFactor = virtualReadIndex - readIndex;
+
+ unsigned readIndex2 = readIndex + 1;
+ if (readIndex2 >= maxFrame)
+ readIndex2 = loop() ? minFrame : maxFrame - 1;
+
+ // Linear interpolation.
+ for (unsigned i = 0; i < numberOfChannels; ++i) {
+ float* destination = destinationChannels[i];
+ const float* source = sourceChannels[i];
+
+ double sample1 = source[readIndex];
+ double sample2 = source[readIndex2];
+ double sample = (1.0 - interpolationFactor) * sample1 + interpolationFactor * sample2;
+
+ destination[writeIndex] = narrowPrecisionToFloat(sample);
+ }
+
+ writeIndex++;
+
+ virtualReadIndex += pitchRate;
+
+ // Wrap-around, retaining sub-sample position since virtualReadIndex is floating-point.
+ if (virtualReadIndex < virtualMinFrame) {
+ virtualReadIndex += virtualDeltaFrames;
+ if (renderSilenceAndFinishIfNotLooping(bus, writeIndex, framesToProcess))
+ break;
+ }
+ }
} else {
while (framesToProcess--) {
unsigned readIndex = static_cast<unsigned>(virtualReadIndex);
@@ -311,7 +382,7 @@ bool AudioBufferSourceNode::renderFromBuffer(AudioBus* bus, unsigned destination
virtualReadIndex += pitchRate;
// Wrap-around, retaining sub-sample position since virtualReadIndex is floating-point.
- if (virtualReadIndex >= virtualEndFrame) {
+ if (virtualReadIndex >= virtualMaxFrame) {
virtualReadIndex -= virtualDeltaFrames;
if (renderSilenceAndFinishIfNotLooping(bus, writeIndex, framesToProcess))
break;
@@ -333,22 +404,20 @@ void AudioBufferSourceNode::reset()
m_lastGain = gain()->value();
}
-bool AudioBufferSourceNode::setBuffer(AudioBuffer* buffer)
+void AudioBufferSourceNode::setBuffer(RefPtr<AudioBuffer>&& buffer)
{
ASSERT(isMainThread());
// The context must be locked since changing the buffer can re-configure the number of channels that are output.
- AudioContext::AutoLocker contextLocker(*context());
+ AudioContext::AutoLocker contextLocker(context());
// This synchronizes with process().
- std::lock_guard<std::mutex> lock(m_processMutex);
+ std::lock_guard<Lock> lock(m_processMutex);
if (buffer) {
// Do any necesssary re-configuration to the buffer's number of channels.
unsigned numberOfChannels = buffer->numberOfChannels();
-
- if (numberOfChannels > AudioContext::maxNumberOfChannels())
- return false;
+ ASSERT(numberOfChannels <= AudioContext::maxNumberOfChannels());
output(0)->setNumberOfChannels(numberOfChannels);
@@ -356,13 +425,11 @@ bool AudioBufferSourceNode::setBuffer(AudioBuffer* buffer)
m_destinationChannels = std::make_unique<float*[]>(numberOfChannels);
for (unsigned i = 0; i < numberOfChannels; ++i)
- m_sourceChannels[i] = buffer->getChannelData(i)->data();
+ m_sourceChannels[i] = buffer->channelData(i)->data();
}
m_virtualReadIndex = 0;
- m_buffer = buffer;
-
- return true;
+ m_buffer = WTFMove(buffer);
}
unsigned AudioBufferSourceNode::numberOfChannels()
@@ -370,61 +437,67 @@ unsigned AudioBufferSourceNode::numberOfChannels()
return output(0)->numberOfChannels();
}
-void AudioBufferSourceNode::startGrain(double when, double grainOffset, ExceptionCode& ec)
+ExceptionOr<void> AudioBufferSourceNode::start(double when, double grainOffset, std::optional<double> optionalGrainDuration)
{
- // Duration of 0 has special value, meaning calculate based on the entire buffer's duration.
- startGrain(when, grainOffset, 0, ec);
+ double grainDuration = 0;
+ if (optionalGrainDuration)
+ grainDuration = optionalGrainDuration.value();
+ else if (buffer())
+ grainDuration = buffer()->duration() - grainOffset;
+
+ return startPlaying(Partial, when, grainOffset, grainDuration);
}
-void AudioBufferSourceNode::startGrain(double when, double grainOffset, double grainDuration, ExceptionCode& ec)
+ExceptionOr<void> AudioBufferSourceNode::startPlaying(BufferPlaybackMode playbackMode, double when, double grainOffset, double grainDuration)
{
ASSERT(isMainThread());
- if (ScriptController::processingUserGesture())
- context()->removeBehaviorRestriction(AudioContext::RequireUserGestureForAudioStartRestriction);
+ context().nodeWillBeginPlayback();
- if (m_playbackState != UNSCHEDULED_STATE) {
- ec = INVALID_STATE_ERR;
- return;
- }
+ if (m_playbackState != UNSCHEDULED_STATE)
+ return Exception { INVALID_STATE_ERR };
+
+ if (!std::isfinite(when) || (when < 0))
+ return Exception { INVALID_STATE_ERR };
+
+ if (!std::isfinite(grainOffset) || (grainOffset < 0))
+ return Exception { INVALID_STATE_ERR };
+
+ if (!std::isfinite(grainDuration) || (grainDuration < 0))
+ return Exception { INVALID_STATE_ERR };
if (!buffer())
- return;
-
- // Do sanity checking of grain parameters versus buffer size.
- double bufferDuration = buffer()->duration();
+ return { };
- grainOffset = std::max(0.0, grainOffset);
- grainOffset = std::min(bufferDuration, grainOffset);
- m_grainOffset = grainOffset;
+ m_isGrain = playbackMode == Partial;
+ if (m_isGrain) {
+ // Do sanity checking of grain parameters versus buffer size.
+ double bufferDuration = buffer()->duration();
- // Handle default/unspecified duration.
- double maxDuration = bufferDuration - grainOffset;
- if (!grainDuration)
- grainDuration = maxDuration;
+ m_grainOffset = std::min(bufferDuration, grainOffset);
- grainDuration = std::max(0.0, grainDuration);
- grainDuration = std::min(maxDuration, grainDuration);
- m_grainDuration = grainDuration;
+ double maxDuration = bufferDuration - m_grainOffset;
+ m_grainDuration = std::min(maxDuration, grainDuration);
+ } else {
+ m_grainOffset = 0.0;
+ m_grainDuration = buffer()->duration();
+ }
- m_isGrain = true;
m_startTime = when;
// We call timeToSampleFrame here since at playbackRate == 1 we don't want to go through linear interpolation
// at a sub-sample position since it will degrade the quality.
// When aligned to the sample-frame the playback will be identical to the PCM data stored in the buffer.
// Since playbackRate == 1 is very common, it's worth considering quality.
- m_virtualReadIndex = AudioUtilities::timeToSampleFrame(m_grainOffset, buffer()->sampleRate());
+ if (totalPitchRate() < 0)
+ m_virtualReadIndex = AudioUtilities::timeToSampleFrame(m_grainOffset + m_grainDuration, buffer()->sampleRate()) - 1;
+ else
+ m_virtualReadIndex = AudioUtilities::timeToSampleFrame(m_grainOffset, buffer()->sampleRate());
m_playbackState = SCHEDULED_STATE;
-}
-#if ENABLE(LEGACY_WEB_AUDIO)
-void AudioBufferSourceNode::noteGrainOn(double when, double grainOffset, double grainDuration, ExceptionCode& ec)
-{
- startGrain(when, grainOffset, grainDuration, ec);
+ return { };
}
-#endif
double AudioBufferSourceNode::totalPitchRate()
{
@@ -442,11 +515,7 @@ double AudioBufferSourceNode::totalPitchRate()
double totalRate = dopplerRate * sampleRateFactor * basePitchRate;
- // Sanity check the total rate. It's very important that the resampler not get any bad rate values.
- totalRate = std::max(0.0, totalRate);
- if (!totalRate)
- totalRate = 1; // zero rate is considered illegal
- totalRate = std::min(MaxRate, totalRate);
+ totalRate = std::max(-MaxRate, std::min(MaxRate, totalRate));
bool isTotalRateValid = !std::isnan(totalRate) && !std::isinf(totalRate);
ASSERT(isTotalRateValid);
@@ -459,8 +528,8 @@ double AudioBufferSourceNode::totalPitchRate()
bool AudioBufferSourceNode::looping()
{
static bool firstTime = true;
- if (firstTime && context() && context()->scriptExecutionContext()) {
- context()->scriptExecutionContext()->addConsoleMessage(JSMessageSource, WarningMessageLevel, "AudioBufferSourceNode 'looping' attribute is deprecated. Use 'loop' instead.");
+ if (firstTime && context().scriptExecutionContext()) {
+ context().scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Warning, ASCIILiteral("AudioBufferSourceNode 'looping' attribute is deprecated. Use 'loop' instead."));
firstTime = false;
}
@@ -470,8 +539,8 @@ bool AudioBufferSourceNode::looping()
void AudioBufferSourceNode::setLooping(bool looping)
{
static bool firstTime = true;
- if (firstTime && context() && context()->scriptExecutionContext()) {
- context()->scriptExecutionContext()->addConsoleMessage(JSMessageSource, WarningMessageLevel, "AudioBufferSourceNode 'looping' attribute is deprecated. Use 'loop' instead.");
+ if (firstTime && context().scriptExecutionContext()) {
+ context().scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Warning, ASCIILiteral("AudioBufferSourceNode 'looping' attribute is deprecated. Use 'loop' instead."));
firstTime = false;
}
@@ -499,7 +568,7 @@ void AudioBufferSourceNode::clearPannerNode()
{
if (m_pannerNode) {
m_pannerNode->deref(AudioNode::RefTypeConnection);
- m_pannerNode = 0;
+ m_pannerNode = nullptr;
}
}
diff --git a/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.h b/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.h
index 15fc56a0b..f4e63a859 100644
--- a/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.h
+++ b/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.h
@@ -22,40 +22,32 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AudioBufferSourceNode_h
-#define AudioBufferSourceNode_h
+#pragma once
-#include "AudioBuffer.h"
-#include "AudioBus.h"
-#include "AudioParam.h"
#include "AudioScheduledSourceNode.h"
-#include "ExceptionCode.h"
-#include "PannerNode.h"
-#include <memory>
-#include <mutex>
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefPtr.h>
+#include <wtf/Lock.h>
namespace WebCore {
-class AudioContext;
+class AudioBuffer;
+class PannerNode;
// AudioBufferSourceNode is an AudioNode representing an audio source from an in-memory audio asset represented by an AudioBuffer.
// It generally will be used for short sounds which require a high degree of scheduling flexibility (can playback in rhythmically perfect ways).
-class AudioBufferSourceNode : public AudioScheduledSourceNode {
+class AudioBufferSourceNode final : public AudioScheduledSourceNode {
public:
- static PassRefPtr<AudioBufferSourceNode> create(AudioContext*, float sampleRate);
+ static Ref<AudioBufferSourceNode> create(AudioContext&, float sampleRate);
virtual ~AudioBufferSourceNode();
// AudioNode
- virtual void process(size_t framesToProcess) override;
- virtual void reset() override;
+ void process(size_t framesToProcess) final;
+ void reset() final;
// setBuffer() is called on the main thread. This is the buffer we use for playback.
// returns true on success.
- bool setBuffer(AudioBuffer*);
+ void setBuffer(RefPtr<AudioBuffer>&&);
AudioBuffer* buffer() { return m_buffer.get(); }
// numberOfChannels() returns the number of output channels. This value equals the number of channels from the buffer.
@@ -63,12 +55,7 @@ public:
unsigned numberOfChannels();
// Play-state
- void startGrain(double when, double grainOffset, ExceptionCode&);
- void startGrain(double when, double grainOffset, double grainDuration, ExceptionCode&);
-
-#if ENABLE(LEGACY_WEB_AUDIO)
- void noteGrainOn(double when, double grainOffset, double grainDuration, ExceptionCode&);
-#endif
+ ExceptionOr<void> start(double when, double grainOffset, std::optional<double> grainDuration);
// Note: the attribute was originally exposed as .looping, but to be more consistent in naming with <audio>
// and with how it's described in the specification, the proper attribute name is .loop
@@ -94,16 +81,23 @@ public:
void clearPannerNode();
// If we are no longer playing, propogate silence ahead to downstream nodes.
- virtual bool propagatesSilence() const override;
+ bool propagatesSilence() const final;
// AudioScheduledSourceNode
- virtual void finish() override;
+ void finish() final;
private:
- AudioBufferSourceNode(AudioContext*, float sampleRate);
+ AudioBufferSourceNode(AudioContext&, float sampleRate);
+
+ double tailTime() const final { return 0; }
+ double latencyTime() const final { return 0; }
- virtual double tailTime() const override { return 0; }
- virtual double latencyTime() const override { return 0; }
+ enum BufferPlaybackMode {
+ Entire,
+ Partial
+ };
+
+ ExceptionOr<void> startPlaying(BufferPlaybackMode, double when, double grainOffset, double grainDuration);
// Returns true on success.
bool renderFromBuffer(AudioBus*, unsigned destinationFrameOffset, size_t numberOfFrames);
@@ -150,9 +144,7 @@ private:
PannerNode* m_pannerNode;
// This synchronizes process() with setBuffer() which can cause dynamic channel count changes.
- mutable std::mutex m_processMutex;
+ mutable Lock m_processMutex;
};
} // namespace WebCore
-
-#endif // AudioBufferSourceNode_h
diff --git a/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.idl b/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.idl
index d792846ee..f42bfe959 100644
--- a/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.idl
+++ b/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.idl
@@ -27,7 +27,7 @@
Conditional=WEB_AUDIO,
JSGenerateToJSObject,
] interface AudioBufferSourceNode : AudioNode {
- [CustomSetter, SetterRaisesException] attribute AudioBuffer buffer;
+ attribute AudioBuffer? buffer;
const unsigned short UNSCHEDULED_STATE = 0;
const unsigned short SCHEDULED_STATE = 1;
@@ -39,21 +39,13 @@
readonly attribute AudioParam gain;
readonly attribute AudioParam playbackRate;
- attribute boolean loop; // This is the proper attribute name from the specification.
+ attribute boolean loop;
- attribute double loopStart;
- attribute double loopEnd;
+ attribute unrestricted double loopStart;
+ attribute unrestricted double loopEnd;
- [RaisesException] void start(double when);
- [ImplementedAs=startGrain, RaisesException] void start(double when, double grainOffset);
- [ImplementedAs=startGrain, RaisesException] void start(double when, double grainOffset, double grainDuration);
- [RaisesException] void stop(double when);
+ [MayThrowException] void start(optional unrestricted double when = 0, optional unrestricted double grainOffset = 0, optional unrestricted double grainDuration);
+ [MayThrowException] void stop(optional unrestricted double when = 0);
- [Conditional=LEGACY_WEB_AUDIO] attribute boolean looping; // This is an alias for the .loop attribute for backwards compatibility.
-
- [Conditional=LEGACY_WEB_AUDIO, RaisesException] void noteOn(double when);
- [Conditional=LEGACY_WEB_AUDIO, RaisesException] void noteGrainOn(double when, double grainOffset, double grainDuration);
- [Conditional=LEGACY_WEB_AUDIO, RaisesException] void noteOff(double when);
-
- attribute EventListener onended;
+ attribute EventHandler onended;
};
diff --git a/Source/WebCore/Modules/webaudio/AudioContext.cpp b/Source/WebCore/Modules/webaudio/AudioContext.cpp
index 4854ff03b..0e1c78f1c 100644
--- a/Source/WebCore/Modules/webaudio/AudioContext.cpp
+++ b/Source/WebCore/Modules/webaudio/AudioContext.cpp
@@ -1,5 +1,6 @@
/*
- * Copyright (C) 2010, Google Inc. All rights reserved.
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ * 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
@@ -44,21 +45,26 @@
#include "DelayNode.h"
#include "Document.h"
#include "DynamicsCompressorNode.h"
+#include "EventNames.h"
#include "ExceptionCode.h"
#include "FFTFrame.h"
+#include "Frame.h"
#include "GainNode.h"
+#include "GenericEventQueue.h"
#include "HRTFDatabaseLoader.h"
#include "HRTFPanner.h"
+#include "JSDOMPromise.h"
+#include "NetworkingContext.h"
#include "OfflineAudioCompletionEvent.h"
#include "OfflineAudioDestinationNode.h"
#include "OscillatorNode.h"
#include "Page.h"
#include "PannerNode.h"
#include "PeriodicWave.h"
-#include "ScriptCallStack.h"
#include "ScriptController.h"
#include "ScriptProcessorNode.h"
#include "WaveShaperNode.h"
+#include <inspector/ScriptCallStack.h>
#if ENABLE(MEDIA_STREAM)
#include "MediaStream.h"
@@ -110,69 +116,45 @@ bool AudioContext::isSampleRateRangeGood(float sampleRate)
const unsigned MaxHardwareContexts = 4;
unsigned AudioContext::s_hardwareContextCount = 0;
-PassRefPtr<AudioContext> AudioContext::create(Document& document, ExceptionCode& ec)
+RefPtr<AudioContext> AudioContext::create(Document& document)
{
- UNUSED_PARAM(ec);
-
ASSERT(isMainThread());
if (s_hardwareContextCount >= MaxHardwareContexts)
return nullptr;
RefPtr<AudioContext> audioContext(adoptRef(new AudioContext(document)));
audioContext->suspendIfNeeded();
- return audioContext.release();
+ return audioContext;
}
// Constructor for rendering to the audio hardware.
AudioContext::AudioContext(Document& document)
: ActiveDOMObject(&document)
- , m_isStopScheduled(false)
- , m_isInitialized(false)
- , m_isAudioThreadFinished(false)
- , m_destinationNode(0)
- , m_isDeletionScheduled(false)
- , m_automaticPullNodesNeedUpdating(false)
- , m_connectionCount(0)
- , m_audioThread(0)
+ , m_mediaSession(PlatformMediaSession::create(*this))
+ , m_eventQueue(std::make_unique<GenericEventQueue>(*this))
, m_graphOwnerThread(UndefinedThreadIdentifier)
- , m_isOfflineContext(false)
- , m_activeSourceCount(0)
- , m_restrictions(NoRestrictions)
{
constructCommon();
- m_destinationNode = DefaultAudioDestinationNode::create(this);
+ m_destinationNode = DefaultAudioDestinationNode::create(*this);
- // This sets in motion an asynchronous loading mechanism on another thread.
- // We can check m_hrtfDatabaseLoader->isLoaded() to find out whether or not it has been fully loaded.
- // It's not that useful to have a callback function for this since the audio thread automatically starts rendering on the graph
- // when this has finished (see AudioDestinationNode).
- m_hrtfDatabaseLoader = HRTFDatabaseLoader::createAndLoadAsynchronouslyIfNecessary(sampleRate());
+ // Initialize the destination node's muted state to match the page's current muted state.
+ pageMutedStateDidChange();
}
// Constructor for offline (non-realtime) rendering.
AudioContext::AudioContext(Document& document, unsigned numberOfChannels, size_t numberOfFrames, float sampleRate)
: ActiveDOMObject(&document)
- , m_isStopScheduled(false)
- , m_isInitialized(false)
- , m_isAudioThreadFinished(false)
- , m_destinationNode(0)
- , m_automaticPullNodesNeedUpdating(false)
- , m_connectionCount(0)
- , m_audioThread(0)
- , m_graphOwnerThread(UndefinedThreadIdentifier)
, m_isOfflineContext(true)
- , m_activeSourceCount(0)
- , m_restrictions(NoRestrictions)
+ , m_mediaSession(PlatformMediaSession::create(*this))
+ , m_eventQueue(std::make_unique<GenericEventQueue>(*this))
+ , m_graphOwnerThread(UndefinedThreadIdentifier)
{
constructCommon();
- // FIXME: the passed in sampleRate MUST match the hardware sample-rate since HRTFDatabaseLoader is a singleton.
- m_hrtfDatabaseLoader = HRTFDatabaseLoader::createAndLoadAsynchronouslyIfNecessary(sampleRate);
-
// Create a new destination for offline rendering.
m_renderTarget = AudioBuffer::create(numberOfChannels, numberOfFrames, sampleRate);
- m_destinationNode = OfflineAudioDestinationNode::create(this, m_renderTarget.get());
+ m_destinationNode = OfflineAudioDestinationNode::create(*this, m_renderTarget.get());
}
void AudioContext::constructCommon()
@@ -190,13 +172,13 @@ void AudioContext::constructCommon()
m_listener = AudioListener::create();
#if PLATFORM(IOS)
- if (!document()->settings() || document()->settings()->mediaPlaybackRequiresUserGesture())
+ if (document()->settings().audioPlaybackRequiresUserGesture())
addBehaviorRestriction(RequireUserGestureForAudioStartRestriction);
else
m_restrictions = NoRestrictions;
#endif
-#if PLATFORM(MAC)
+#if PLATFORM(COCOA)
addBehaviorRestriction(RequirePageConsentForAudioStartRestriction);
#endif
}
@@ -206,47 +188,50 @@ AudioContext::~AudioContext()
#if DEBUG_AUDIONODE_REFERENCES
fprintf(stderr, "%p: AudioContext::~AudioContext()\n", this);
#endif
- // AudioNodes keep a reference to their context, so there should be no way to be in the destructor if there are still AudioNodes around.
ASSERT(!m_isInitialized);
ASSERT(m_isStopScheduled);
- ASSERT(!m_nodesToDelete.size());
- ASSERT(!m_referencedNodes.size());
- ASSERT(!m_finishedNodes.size());
- ASSERT(!m_automaticPullNodes.size());
+ ASSERT(m_nodesToDelete.isEmpty());
+ ASSERT(m_referencedNodes.isEmpty());
+ ASSERT(m_finishedNodes.isEmpty()); // FIXME (bug 105870): This assertion fails on tests sometimes.
+ ASSERT(m_automaticPullNodes.isEmpty());
if (m_automaticPullNodesNeedUpdating)
m_renderingAutomaticPullNodes.resize(m_automaticPullNodes.size());
- ASSERT(!m_renderingAutomaticPullNodes.size());
+ ASSERT(m_renderingAutomaticPullNodes.isEmpty());
+ // FIXME: Can we assert that m_deferredFinishDerefList is empty?
}
void AudioContext::lazyInitialize()
{
- if (!m_isInitialized) {
- // Don't allow the context to initialize a second time after it's already been explicitly uninitialized.
- ASSERT(!m_isAudioThreadFinished);
- if (!m_isAudioThreadFinished) {
- if (m_destinationNode.get()) {
- m_destinationNode->initialize();
-
- if (!isOfflineContext()) {
- // This starts the audio thread. The destination node's provideInput() method will now be called repeatedly to render audio.
- // Each time provideInput() is called, a portion of the audio stream is rendered. Let's call this time period a "render quantum".
- // NOTE: for now default AudioContext does not need an explicit startRendering() call from JavaScript.
- // We may want to consider requiring it for symmetry with OfflineAudioContext.
- startRendering();
- ++s_hardwareContextCount;
- }
-
- }
- m_isInitialized = true;
+ if (m_isInitialized)
+ return;
+
+ // Don't allow the context to initialize a second time after it's already been explicitly uninitialized.
+ ASSERT(!m_isAudioThreadFinished);
+ if (m_isAudioThreadFinished)
+ return;
+
+ if (m_destinationNode) {
+ m_destinationNode->initialize();
+
+ if (!isOfflineContext()) {
+ document()->addAudioProducer(this);
+
+ // This starts the audio thread. The destination node's provideInput() method will now be called repeatedly to render audio.
+ // Each time provideInput() is called, a portion of the audio stream is rendered. Let's call this time period a "render quantum".
+ // NOTE: for now default AudioContext does not need an explicit startRendering() call from JavaScript.
+ // We may want to consider requiring it for symmetry with OfflineAudioContext.
+ startRendering();
+ ++s_hardwareContextCount;
}
}
+ m_isInitialized = true;
}
void AudioContext::clear()
{
// We have to release our reference to the destination node before the context will ever be deleted since the destination node holds a reference to the context.
if (m_destinationNode)
- m_destinationNode.clear();
+ m_destinationNode = nullptr;
// Audio thread is dead. Nobody will schedule node deletion action. Let's do it ourselves.
do {
@@ -273,8 +258,13 @@ void AudioContext::uninitialize()
m_isAudioThreadFinished = true;
if (!isOfflineContext()) {
+ document()->removeAudioProducer(this);
+
ASSERT(s_hardwareContextCount);
--s_hardwareContextCount;
+
+ // Offline contexts move to 'Closed' state when dispatching the completion event.
+ setState(State::Closed);
}
// Get rid of the sources which may still be playing.
@@ -288,329 +278,293 @@ bool AudioContext::isInitialized() const
return m_isInitialized;
}
-bool AudioContext::isRunnable() const
+void AudioContext::addReaction(State state, DOMPromise<void>&& promise)
{
- if (!isInitialized())
- return false;
-
- // Check with the HRTF spatialization system to see if it's finished loading.
- return m_hrtfDatabaseLoader->isLoaded();
+ size_t stateIndex = static_cast<size_t>(state);
+ if (stateIndex >= m_stateReactions.size())
+ m_stateReactions.resize(stateIndex + 1);
+
+ m_stateReactions[stateIndex].append(WTFMove(promise));
}
-void AudioContext::stopDispatch(void* userData)
+void AudioContext::setState(State state)
{
- AudioContext* context = reinterpret_cast<AudioContext*>(userData);
- ASSERT(context);
- if (!context)
+ if (m_state == state)
+ return;
+
+ m_state = state;
+ m_eventQueue->enqueueEvent(Event::create(eventNames().statechangeEvent, true, false));
+
+ size_t stateIndex = static_cast<size_t>(state);
+ if (stateIndex >= m_stateReactions.size())
return;
- context->uninitialize();
- context->clear();
+ Vector<DOMPromise<void>> reactions;
+ m_stateReactions[stateIndex].swap(reactions);
+
+ for (auto& promise : reactions)
+ promise.resolve();
}
void AudioContext::stop()
{
+ ASSERT(isMainThread());
+
// Usually ScriptExecutionContext calls stop twice.
if (m_isStopScheduled)
return;
m_isStopScheduled = true;
+ document()->updateIsPlayingMedia();
+
+ m_eventQueue->close();
+
// Don't call uninitialize() immediately here because the ScriptExecutionContext is in the middle
// of dealing with all of its ActiveDOMObjects at this point. uninitialize() can de-reference other
// ActiveDOMObjects so let's schedule uninitialize() to be called later.
// FIXME: see if there's a more direct way to handle this issue.
- callOnMainThread(stopDispatch, this);
+ // FIXME: This sounds very wrong. The whole idea of stop() is that it stops everything, and if we
+ // schedule some observable work for later, the work likely happens at an inappropriate time.
+ callOnMainThread([this] {
+ uninitialize();
+ clear();
+ });
}
-Document* AudioContext::document() const
+bool AudioContext::canSuspendForDocumentSuspension() const
{
- ASSERT(m_scriptExecutionContext && m_scriptExecutionContext->isDocument());
- return static_cast<Document*>(m_scriptExecutionContext);
+ // FIXME: We should be able to suspend while rendering as well with some more code.
+ return m_state == State::Suspended || m_state == State::Closed;
}
-PassRefPtr<AudioBuffer> AudioContext::createBuffer(unsigned numberOfChannels, size_t numberOfFrames, float sampleRate, ExceptionCode& ec)
+const char* AudioContext::activeDOMObjectName() const
{
- RefPtr<AudioBuffer> audioBuffer = AudioBuffer::create(numberOfChannels, numberOfFrames, sampleRate);
- if (!audioBuffer.get()) {
- ec = NOT_SUPPORTED_ERR;
- return nullptr;
- }
+ return "AudioContext";
+}
- return audioBuffer;
+Document* AudioContext::document() const
+{
+ ASSERT(m_scriptExecutionContext);
+ return downcast<Document>(m_scriptExecutionContext);
}
-PassRefPtr<AudioBuffer> AudioContext::createBuffer(ArrayBuffer* arrayBuffer, bool mixToMono, ExceptionCode& ec)
+const Document* AudioContext::hostingDocument() const
{
- ASSERT(arrayBuffer);
- if (!arrayBuffer) {
- ec = SYNTAX_ERR;
- return nullptr;
- }
+ return downcast<Document>(m_scriptExecutionContext);
+}
- RefPtr<AudioBuffer> audioBuffer = AudioBuffer::createFromAudioFileData(arrayBuffer->data(), arrayBuffer->byteLength(), mixToMono, sampleRate());
- if (!audioBuffer.get()) {
- ec = SYNTAX_ERR;
- return nullptr;
+String AudioContext::sourceApplicationIdentifier() const
+{
+ Document* document = this->document();
+ if (Frame* frame = document ? document->frame() : nullptr) {
+ if (NetworkingContext* networkingContext = frame->loader().networkingContext())
+ return networkingContext->sourceApplicationIdentifier();
}
+ return emptyString();
+}
- return audioBuffer;
+ExceptionOr<Ref<AudioBuffer>> AudioContext::createBuffer(unsigned numberOfChannels, size_t numberOfFrames, float sampleRate)
+{
+ auto audioBuffer = AudioBuffer::create(numberOfChannels, numberOfFrames, sampleRate);
+ if (!audioBuffer)
+ return Exception { NOT_SUPPORTED_ERR };
+ return audioBuffer.releaseNonNull();
}
-void AudioContext::decodeAudioData(ArrayBuffer* audioData, PassRefPtr<AudioBufferCallback> successCallback, PassRefPtr<AudioBufferCallback> errorCallback, ExceptionCode& ec)
+ExceptionOr<Ref<AudioBuffer>> AudioContext::createBuffer(ArrayBuffer& arrayBuffer, bool mixToMono)
{
- if (!audioData) {
- ec = SYNTAX_ERR;
- return;
- }
- m_audioDecoder.decodeAsync(audioData, sampleRate(), successCallback, errorCallback);
+ auto audioBuffer = AudioBuffer::createFromAudioFileData(arrayBuffer.data(), arrayBuffer.byteLength(), mixToMono, sampleRate());
+ if (!audioBuffer)
+ return Exception { SYNTAX_ERR };
+ return audioBuffer.releaseNonNull();
+}
+
+void AudioContext::decodeAudioData(Ref<ArrayBuffer>&& audioData, RefPtr<AudioBufferCallback>&& successCallback, RefPtr<AudioBufferCallback>&& errorCallback)
+{
+ m_audioDecoder.decodeAsync(WTFMove(audioData), sampleRate(), WTFMove(successCallback), WTFMove(errorCallback));
}
-PassRefPtr<AudioBufferSourceNode> AudioContext::createBufferSource()
+Ref<AudioBufferSourceNode> AudioContext::createBufferSource()
{
ASSERT(isMainThread());
lazyInitialize();
- RefPtr<AudioBufferSourceNode> node = AudioBufferSourceNode::create(this, m_destinationNode->sampleRate());
+ Ref<AudioBufferSourceNode> node = AudioBufferSourceNode::create(*this, m_destinationNode->sampleRate());
// Because this is an AudioScheduledSourceNode, the context keeps a reference until it has finished playing.
// When this happens, AudioScheduledSourceNode::finish() calls AudioContext::notifyNodeFinishedProcessing().
- refNode(node.get());
+ refNode(node);
return node;
}
#if ENABLE(VIDEO)
-PassRefPtr<MediaElementAudioSourceNode> AudioContext::createMediaElementSource(HTMLMediaElement* mediaElement, ExceptionCode& ec)
+
+ExceptionOr<Ref<MediaElementAudioSourceNode>> AudioContext::createMediaElementSource(HTMLMediaElement& mediaElement)
{
- ASSERT(mediaElement);
- if (!mediaElement) {
- ec = INVALID_STATE_ERR;
- return nullptr;
- }
-
ASSERT(isMainThread());
lazyInitialize();
- // First check if this media element already has a source node.
- if (mediaElement->audioSourceNode()) {
- ec = INVALID_STATE_ERR;
- return nullptr;
- }
-
- RefPtr<MediaElementAudioSourceNode> node = MediaElementAudioSourceNode::create(this, mediaElement);
+ if (mediaElement.audioSourceNode())
+ return Exception { INVALID_STATE_ERR };
+
+ auto node = MediaElementAudioSourceNode::create(*this, mediaElement);
- mediaElement->setAudioSourceNode(node.get());
+ mediaElement.setAudioSourceNode(node.ptr());
refNode(node.get()); // context keeps reference until node is disconnected
- return node;
+ return WTFMove(node);
}
+
#endif
#if ENABLE(MEDIA_STREAM)
-PassRefPtr<MediaStreamAudioSourceNode> AudioContext::createMediaStreamSource(MediaStream* mediaStream, ExceptionCode& ec)
-{
- ASSERT(mediaStream);
- if (!mediaStream) {
- ec = INVALID_STATE_ERR;
- return nullptr;
- }
+ExceptionOr<Ref<MediaStreamAudioSourceNode>> AudioContext::createMediaStreamSource(MediaStream& mediaStream)
+{
ASSERT(isMainThread());
- lazyInitialize();
-
- AudioSourceProvider* provider = 0;
- Vector<RefPtr<MediaStreamTrack>> audioTracks = mediaStream->getAudioTracks();
- RefPtr<MediaStreamTrack> audioTrack;
+ auto audioTracks = mediaStream.getAudioTracks();
+ if (audioTracks.isEmpty())
+ return Exception { INVALID_STATE_ERR };
- // FIXME: get a provider for non-local MediaStreams (like from a remote peer).
- for (size_t i = 0; i < audioTracks.size(); ++i) {
- audioTrack = audioTracks[i];
- if (audioTrack->source()->isAudioStreamSource()) {
- auto source = static_cast<MediaStreamAudioSource*>(audioTrack->source());
- ASSERT(!source->deviceId().isEmpty());
- destination()->enableInput(source->deviceId());
- provider = destination()->localAudioInputProvider();
+ MediaStreamTrack* providerTrack = nullptr;
+ for (auto& track : audioTracks) {
+ if (track->audioSourceProvider()) {
+ providerTrack = track.get();
break;
}
}
+ if (!providerTrack)
+ return Exception { INVALID_STATE_ERR };
- RefPtr<MediaStreamAudioSourceNode> node = MediaStreamAudioSourceNode::create(this, mediaStream, audioTrack.get(), provider);
+ lazyInitialize();
- // FIXME: Only stereo streams are supported right now. We should be able to accept multi-channel streams.
+ auto node = MediaStreamAudioSourceNode::create(*this, mediaStream, *providerTrack);
node->setFormat(2, sampleRate());
- refNode(node.get()); // context keeps reference until node is disconnected
- return node;
+ refNode(node); // context keeps reference until node is disconnected
+ return WTFMove(node);
}
-PassRefPtr<MediaStreamAudioDestinationNode> AudioContext::createMediaStreamDestination()
+Ref<MediaStreamAudioDestinationNode> AudioContext::createMediaStreamDestination()
{
// FIXME: Add support for an optional argument which specifies the number of channels.
// FIXME: The default should probably be stereo instead of mono.
- return MediaStreamAudioDestinationNode::create(this, 1);
+ return MediaStreamAudioDestinationNode::create(*this, 1);
}
#endif
-PassRefPtr<ScriptProcessorNode> AudioContext::createScriptProcessor(size_t bufferSize, ExceptionCode& ec)
-{
- // Set number of input/output channels to stereo by default.
- return createScriptProcessor(bufferSize, 2, 2, ec);
-}
-
-PassRefPtr<ScriptProcessorNode> AudioContext::createScriptProcessor(size_t bufferSize, size_t numberOfInputChannels, ExceptionCode& ec)
-{
- // Set number of output channels to stereo by default.
- return createScriptProcessor(bufferSize, numberOfInputChannels, 2, ec);
-}
-
-PassRefPtr<ScriptProcessorNode> AudioContext::createScriptProcessor(size_t bufferSize, size_t numberOfInputChannels, size_t numberOfOutputChannels, ExceptionCode& ec)
+ExceptionOr<Ref<ScriptProcessorNode>> AudioContext::createScriptProcessor(size_t bufferSize, size_t numberOfInputChannels, size_t numberOfOutputChannels)
{
ASSERT(isMainThread());
lazyInitialize();
- RefPtr<ScriptProcessorNode> node = ScriptProcessorNode::create(this, m_destinationNode->sampleRate(), bufferSize, numberOfInputChannels, numberOfOutputChannels);
+ auto node = ScriptProcessorNode::create(*this, m_destinationNode->sampleRate(), bufferSize, numberOfInputChannels, numberOfOutputChannels);
- if (!node.get()) {
- ec = INDEX_SIZE_ERR;
- return nullptr;
- }
+ if (!node)
+ return Exception { INDEX_SIZE_ERR };
- refNode(node.get()); // context keeps reference until we stop making javascript rendering callbacks
- return node;
+ refNode(*node); // context keeps reference until we stop making javascript rendering callbacks
+ return node.releaseNonNull();
}
-PassRefPtr<BiquadFilterNode> AudioContext::createBiquadFilter()
+Ref<BiquadFilterNode> AudioContext::createBiquadFilter()
{
ASSERT(isMainThread());
lazyInitialize();
- return BiquadFilterNode::create(this, m_destinationNode->sampleRate());
+ return BiquadFilterNode::create(*this, m_destinationNode->sampleRate());
}
-PassRefPtr<WaveShaperNode> AudioContext::createWaveShaper()
+Ref<WaveShaperNode> AudioContext::createWaveShaper()
{
ASSERT(isMainThread());
lazyInitialize();
- return WaveShaperNode::create(this);
+ return WaveShaperNode::create(*this);
}
-PassRefPtr<PannerNode> AudioContext::createPanner()
+Ref<PannerNode> AudioContext::createPanner()
{
ASSERT(isMainThread());
lazyInitialize();
- return PannerNode::create(this, m_destinationNode->sampleRate());
+ return PannerNode::create(*this, m_destinationNode->sampleRate());
}
-PassRefPtr<ConvolverNode> AudioContext::createConvolver()
+Ref<ConvolverNode> AudioContext::createConvolver()
{
ASSERT(isMainThread());
lazyInitialize();
- return ConvolverNode::create(this, m_destinationNode->sampleRate());
+ return ConvolverNode::create(*this, m_destinationNode->sampleRate());
}
-PassRefPtr<DynamicsCompressorNode> AudioContext::createDynamicsCompressor()
+Ref<DynamicsCompressorNode> AudioContext::createDynamicsCompressor()
{
ASSERT(isMainThread());
lazyInitialize();
- return DynamicsCompressorNode::create(this, m_destinationNode->sampleRate());
+ return DynamicsCompressorNode::create(*this, m_destinationNode->sampleRate());
}
-PassRefPtr<AnalyserNode> AudioContext::createAnalyser()
+Ref<AnalyserNode> AudioContext::createAnalyser()
{
ASSERT(isMainThread());
lazyInitialize();
- return AnalyserNode::create(this, m_destinationNode->sampleRate());
+ return AnalyserNode::create(*this, m_destinationNode->sampleRate());
}
-PassRefPtr<GainNode> AudioContext::createGain()
+Ref<GainNode> AudioContext::createGain()
{
ASSERT(isMainThread());
lazyInitialize();
- return GainNode::create(this, m_destinationNode->sampleRate());
+ return GainNode::create(*this, m_destinationNode->sampleRate());
}
-PassRefPtr<DelayNode> AudioContext::createDelay(ExceptionCode& ec)
-{
- const double defaultMaxDelayTime = 1;
- return createDelay(defaultMaxDelayTime, ec);
-}
-
-PassRefPtr<DelayNode> AudioContext::createDelay(double maxDelayTime, ExceptionCode& ec)
+ExceptionOr<Ref<DelayNode>> AudioContext::createDelay(double maxDelayTime)
{
ASSERT(isMainThread());
lazyInitialize();
- RefPtr<DelayNode> node = DelayNode::create(this, m_destinationNode->sampleRate(), maxDelayTime, ec);
- if (ec)
- return nullptr;
- return node;
+ return DelayNode::create(*this, m_destinationNode->sampleRate(), maxDelayTime);
}
-PassRefPtr<ChannelSplitterNode> AudioContext::createChannelSplitter(ExceptionCode& ec)
-{
- const unsigned ChannelSplitterDefaultNumberOfOutputs = 6;
- return createChannelSplitter(ChannelSplitterDefaultNumberOfOutputs, ec);
-}
-
-PassRefPtr<ChannelSplitterNode> AudioContext::createChannelSplitter(size_t numberOfOutputs, ExceptionCode& ec)
+ExceptionOr<Ref<ChannelSplitterNode>> AudioContext::createChannelSplitter(size_t numberOfOutputs)
{
ASSERT(isMainThread());
lazyInitialize();
-
- RefPtr<ChannelSplitterNode> node = ChannelSplitterNode::create(this, m_destinationNode->sampleRate(), numberOfOutputs);
-
- if (!node.get()) {
- ec = SYNTAX_ERR;
- return nullptr;
- }
-
- return node;
+ auto node = ChannelSplitterNode::create(*this, m_destinationNode->sampleRate(), numberOfOutputs);
+ if (!node)
+ return Exception { INDEX_SIZE_ERR };
+ return node.releaseNonNull();
}
-PassRefPtr<ChannelMergerNode> AudioContext::createChannelMerger(ExceptionCode& ec)
-{
- const unsigned ChannelMergerDefaultNumberOfInputs = 6;
- return createChannelMerger(ChannelMergerDefaultNumberOfInputs, ec);
-}
-
-PassRefPtr<ChannelMergerNode> AudioContext::createChannelMerger(size_t numberOfInputs, ExceptionCode& ec)
+ExceptionOr<Ref<ChannelMergerNode>> AudioContext::createChannelMerger(size_t numberOfInputs)
{
ASSERT(isMainThread());
lazyInitialize();
-
- RefPtr<ChannelMergerNode> node = ChannelMergerNode::create(this, m_destinationNode->sampleRate(), numberOfInputs);
-
- if (!node.get()) {
- ec = SYNTAX_ERR;
- return nullptr;
- }
-
- return node;
+ auto node = ChannelMergerNode::create(*this, m_destinationNode->sampleRate(), numberOfInputs);
+ if (!node)
+ return Exception { INDEX_SIZE_ERR };
+ return node.releaseNonNull();
}
-PassRefPtr<OscillatorNode> AudioContext::createOscillator()
+Ref<OscillatorNode> AudioContext::createOscillator()
{
ASSERT(isMainThread());
lazyInitialize();
- RefPtr<OscillatorNode> node = OscillatorNode::create(this, m_destinationNode->sampleRate());
+ Ref<OscillatorNode> node = OscillatorNode::create(*this, m_destinationNode->sampleRate());
// Because this is an AudioScheduledSourceNode, the context keeps a reference until it has finished playing.
// When this happens, AudioScheduledSourceNode::finish() calls AudioContext::notifyNodeFinishedProcessing().
- refNode(node.get());
+ refNode(node);
return node;
}
-PassRefPtr<PeriodicWave> AudioContext::createPeriodicWave(Float32Array* real, Float32Array* imag, ExceptionCode& ec)
+ExceptionOr<Ref<PeriodicWave>> AudioContext::createPeriodicWave(Float32Array& real, Float32Array& imaginary)
{
ASSERT(isMainThread());
-
- if (!real || !imag || (real->length() != imag->length() || (real->length() > MaxPeriodicWaveLength) || (real->length() <= 0))) {
- ec = SYNTAX_ERR;
- return nullptr;
- }
-
+ if (real.length() != imaginary.length() || (real.length() > MaxPeriodicWaveLength) || !real.length())
+ return Exception { INDEX_SIZE_ERR };
lazyInitialize();
- return PeriodicWave::create(sampleRate(), real, imag);
+ return PeriodicWave::create(sampleRate(), real, imaginary);
}
void AudioContext::notifyNodeFinishedProcessing(AudioNode* node)
@@ -623,40 +577,36 @@ void AudioContext::derefFinishedSourceNodes()
{
ASSERT(isGraphOwner());
ASSERT(isAudioThread() || isAudioThreadFinished());
- for (unsigned i = 0; i < m_finishedNodes.size(); i++)
- derefNode(m_finishedNodes[i]);
+ for (auto& node : m_finishedNodes)
+ derefNode(*node);
m_finishedNodes.clear();
}
-void AudioContext::refNode(AudioNode* node)
+void AudioContext::refNode(AudioNode& node)
{
ASSERT(isMainThread());
AutoLocker locker(*this);
- node->ref(AudioNode::RefTypeConnection);
- m_referencedNodes.append(node);
+ node.ref(AudioNode::RefTypeConnection);
+ m_referencedNodes.append(&node);
}
-void AudioContext::derefNode(AudioNode* node)
+void AudioContext::derefNode(AudioNode& node)
{
ASSERT(isGraphOwner());
- node->deref(AudioNode::RefTypeConnection);
+ node.deref(AudioNode::RefTypeConnection);
- for (unsigned i = 0; i < m_referencedNodes.size(); ++i) {
- if (node == m_referencedNodes[i]) {
- m_referencedNodes.remove(i);
- break;
- }
- }
+ ASSERT(m_referencedNodes.contains(&node));
+ m_referencedNodes.removeFirst(&node);
}
void AudioContext::derefUnfinishedSourceNodes()
{
ASSERT(isMainThread() && isAudioThreadFinished());
- for (unsigned i = 0; i < m_referencedNodes.size(); ++i)
- m_referencedNodes[i]->deref(AudioNode::RefTypeConnection);
+ for (auto& node : m_referencedNodes)
+ node->deref(AudioNode::RefTypeConnection);
m_referencedNodes.clear();
}
@@ -788,10 +738,8 @@ void AudioContext::handlePostRenderTasks()
void AudioContext::handleDeferredFinishDerefs()
{
ASSERT(isAudioThread() && isGraphOwner());
- for (unsigned i = 0; i < m_deferredFinishDerefList.size(); ++i) {
- AudioNode* node = m_deferredFinishDerefList[i];
+ for (auto& node : m_deferredFinishDerefList)
node->finishDeref(AudioNode::RefTypeConnection);
- }
m_deferredFinishDerefList.clear();
}
@@ -826,36 +774,23 @@ void AudioContext::scheduleNodeDeletion()
m_isDeletionScheduled = true;
- // Don't let ourself get deleted before the callback.
- // See matching deref() in deleteMarkedNodesDispatch().
- ref();
- callOnMainThread(deleteMarkedNodesDispatch, this);
+ callOnMainThread([protectedThis = makeRef(*this)]() mutable {
+ protectedThis->deleteMarkedNodes();
+ });
}
}
-void AudioContext::deleteMarkedNodesDispatch(void* userData)
-{
- AudioContext* context = reinterpret_cast<AudioContext*>(userData);
- ASSERT(context);
- if (!context)
- return;
-
- context->deleteMarkedNodes();
- context->deref();
-}
-
void AudioContext::deleteMarkedNodes()
{
ASSERT(isMainThread());
// Protect this object from being deleted before we release the mutex locked by AutoLocker.
- Ref<AudioContext> protect(*this);
+ Ref<AudioContext> protectedThis(*this);
{
AutoLocker locker(*this);
- while (size_t n = m_nodesToDelete.size()) {
- AudioNode* node = m_nodesToDelete[n - 1];
- m_nodesToDelete.removeLast();
+ while (m_nodesToDelete.size()) {
+ AudioNode* node = m_nodesToDelete.takeLast();
// Before deleting the node, clear out any AudioNodeInputs from m_dirtySummingJunctions.
unsigned numberOfInputs = node->numberOfInputs();
@@ -897,8 +832,8 @@ void AudioContext::handleDirtyAudioSummingJunctions()
{
ASSERT(isGraphOwner());
- for (HashSet<AudioSummingJunction*>::iterator i = m_dirtySummingJunctions.begin(); i != m_dirtySummingJunctions.end(); ++i)
- (*i)->updateRenderingState();
+ for (auto& junction : m_dirtySummingJunctions)
+ junction->updateRenderingState();
m_dirtySummingJunctions.clear();
}
@@ -907,8 +842,8 @@ void AudioContext::handleDirtyAudioNodeOutputs()
{
ASSERT(isGraphOwner());
- for (HashSet<AudioNodeOutput*>::iterator i = m_dirtyAudioNodeOutputs.begin(); i != m_dirtyAudioNodeOutputs.end(); ++i)
- (*i)->updateRenderingState();
+ for (auto& output : m_dirtyAudioNodeOutputs)
+ output->updateRenderingState();
m_dirtyAudioNodeOutputs.clear();
}
@@ -937,11 +872,9 @@ void AudioContext::updateAutomaticPullNodes()
// Copy from m_automaticPullNodes to m_renderingAutomaticPullNodes.
m_renderingAutomaticPullNodes.resize(m_automaticPullNodes.size());
- unsigned j = 0;
- for (HashSet<AudioNode*>::iterator i = m_automaticPullNodes.begin(); i != m_automaticPullNodes.end(); ++i, ++j) {
- AudioNode* output = *i;
- m_renderingAutomaticPullNodes[j] = output;
- }
+ unsigned i = 0;
+ for (auto& output : m_automaticPullNodes)
+ m_renderingAutomaticPullNodes[i++] = output;
m_automaticPullNodesNeedUpdating = false;
}
@@ -951,8 +884,8 @@ void AudioContext::processAutomaticPullNodes(size_t framesToProcess)
{
ASSERT(isAudioThread());
- for (unsigned i = 0; i < m_renderingAutomaticPullNodes.size(); ++i)
- m_renderingAutomaticPullNodes[i]->processIfNecessary(framesToProcess);
+ for (auto& node : m_renderingAutomaticPullNodes)
+ node->processIfNecessary(framesToProcess);
}
ScriptExecutionContext* AudioContext::scriptExecutionContext() const
@@ -960,24 +893,97 @@ ScriptExecutionContext* AudioContext::scriptExecutionContext() const
return m_isStopScheduled ? 0 : ActiveDOMObject::scriptExecutionContext();
}
-void AudioContext::startRendering()
+void AudioContext::nodeWillBeginPlayback()
+{
+ // Called by scheduled AudioNodes when clients schedule their start times.
+ // Prior to the introduction of suspend(), resume(), and stop(), starting
+ // a scheduled AudioNode would remove the user-gesture restriction, if present,
+ // and would thus unmute the context. Now that AudioContext stays in the
+ // "suspended" state if a user-gesture restriction is present, starting a
+ // schedule AudioNode should set the state to "running", but only if the
+ // user-gesture restriction is set.
+ if (userGestureRequiredForAudioStart())
+ startRendering();
+}
+
+bool AudioContext::willBeginPlayback()
{
- if (ScriptController::processingUserGesture())
+ if (userGestureRequiredForAudioStart()) {
+ if (!ScriptController::processingUserGestureForMedia())
+ return false;
removeBehaviorRestriction(AudioContext::RequireUserGestureForAudioStartRestriction);
+ }
if (pageConsentRequiredForAudioStart()) {
Page* page = document()->page();
- if (page && !page->canStartMedia())
+ if (page && !page->canStartMedia()) {
document()->addMediaCanStartListener(this);
- else
- removeBehaviorRestriction(AudioContext::RequirePageConsentForAudioStartRestriction);
+ return false;
+ }
+ removeBehaviorRestriction(AudioContext::RequirePageConsentForAudioStartRestriction);
+ }
+
+ return m_mediaSession->clientWillBeginPlayback();
+}
+
+bool AudioContext::willPausePlayback()
+{
+ if (userGestureRequiredForAudioStart()) {
+ if (!ScriptController::processingUserGestureForMedia())
+ return false;
+ removeBehaviorRestriction(AudioContext::RequireUserGestureForAudioStartRestriction);
}
+
+ if (pageConsentRequiredForAudioStart()) {
+ Page* page = document()->page();
+ if (page && !page->canStartMedia()) {
+ document()->addMediaCanStartListener(this);
+ return false;
+ }
+ removeBehaviorRestriction(AudioContext::RequirePageConsentForAudioStartRestriction);
+ }
+
+ return m_mediaSession->clientWillPausePlayback();
+}
+
+void AudioContext::startRendering()
+{
+ if (!willBeginPlayback())
+ return;
+
destination()->startRendering();
+ setState(State::Running);
}
-void AudioContext::mediaCanStart()
+void AudioContext::mediaCanStart(Document& document)
{
+ ASSERT_UNUSED(document, &document == this->document());
removeBehaviorRestriction(AudioContext::RequirePageConsentForAudioStartRestriction);
+ mayResumePlayback(true);
+}
+
+MediaProducer::MediaStateFlags AudioContext::mediaState() const
+{
+ if (!m_isStopScheduled && m_destinationNode && m_destinationNode->isPlayingAudio())
+ return MediaProducer::IsPlayingAudio;
+
+ return MediaProducer::IsNotPlaying;
+}
+
+void AudioContext::pageMutedStateDidChange()
+{
+ if (m_destinationNode && document()->page())
+ m_destinationNode->setMuted(document()->page()->isAudioMuted());
+}
+
+void AudioContext::isPlayingAudioDidChange()
+{
+ // Make sure to call Document::updateIsPlayingMedia() on the main thread, since
+ // we could be on the audio I/O thread here and the call into WebCore could block.
+ callOnMainThread([protectedThis = makeRef(*this)] {
+ if (protectedThis->document())
+ protectedThis->document()->updateIsPlayingMedia();
+ });
}
void AudioContext::fireCompletionEvent()
@@ -987,6 +993,7 @@ void AudioContext::fireCompletionEvent()
return;
AudioBuffer* renderedBuffer = m_renderTarget.get();
+ setState(State::Closed);
ASSERT(renderedBuffer);
if (!renderedBuffer)
@@ -995,7 +1002,7 @@ void AudioContext::fireCompletionEvent()
// Avoid firing the event if the document has already gone away.
if (scriptExecutionContext()) {
// Call the offline rendering completion event listener.
- dispatchEvent(OfflineAudioCompletionEvent::create(renderedBuffer));
+ m_eventQueue->enqueueEvent(OfflineAudioCompletionEvent::create(renderedBuffer));
}
}
@@ -1009,6 +1016,127 @@ void AudioContext::decrementActiveSourceCount()
--m_activeSourceCount;
}
+void AudioContext::suspend(DOMPromise<void>&& promise)
+{
+ if (isOfflineContext()) {
+ promise.reject(INVALID_STATE_ERR);
+ return;
+ }
+
+ if (m_state == State::Suspended) {
+ promise.resolve();
+ return;
+ }
+
+ if (m_state == State::Closed || m_state == State::Interrupted || !m_destinationNode) {
+ promise.reject();
+ return;
+ }
+
+ addReaction(State::Suspended, WTFMove(promise));
+
+ if (!willPausePlayback())
+ return;
+
+ lazyInitialize();
+
+ m_destinationNode->suspend([this, protectedThis = makeRef(*this)] {
+ setState(State::Suspended);
+ });
+}
+
+void AudioContext::resume(DOMPromise<void>&& promise)
+{
+ if (isOfflineContext()) {
+ promise.reject(INVALID_STATE_ERR);
+ return;
+ }
+
+ if (m_state == State::Running) {
+ promise.resolve();
+ return;
+ }
+
+ if (m_state == State::Closed || !m_destinationNode) {
+ promise.reject();
+ return;
+ }
+
+ addReaction(State::Running, WTFMove(promise));
+
+ if (!willBeginPlayback())
+ return;
+
+ lazyInitialize();
+
+ m_destinationNode->resume([this, protectedThis = makeRef(*this)] {
+ setState(State::Running);
+ });
+}
+
+void AudioContext::close(DOMPromise<void>&& promise)
+{
+ if (isOfflineContext()) {
+ promise.reject(INVALID_STATE_ERR);
+ return;
+ }
+
+ if (m_state == State::Closed || !m_destinationNode) {
+ promise.resolve();
+ return;
+ }
+
+ addReaction(State::Closed, WTFMove(promise));
+
+ lazyInitialize();
+
+ m_destinationNode->close([this, protectedThis = makeRef(*this)] {
+ setState(State::Closed);
+ uninitialize();
+ });
+}
+
+
+void AudioContext::suspendPlayback()
+{
+ if (!m_destinationNode || m_state == State::Closed)
+ return;
+
+ if (m_state == State::Suspended) {
+ if (m_mediaSession->state() == PlatformMediaSession::Interrupted)
+ setState(State::Interrupted);
+ return;
+ }
+
+ lazyInitialize();
+
+ m_destinationNode->suspend([this, protectedThis = makeRef(*this)] {
+ bool interrupted = m_mediaSession->state() == PlatformMediaSession::Interrupted;
+ setState(interrupted ? State::Interrupted : State::Suspended);
+ });
+}
+
+void AudioContext::mayResumePlayback(bool shouldResume)
+{
+ if (!m_destinationNode || m_state == State::Closed || m_state == State::Running)
+ return;
+
+ if (!shouldResume) {
+ setState(State::Suspended);
+ return;
+ }
+
+ if (!willBeginPlayback())
+ return;
+
+ lazyInitialize();
+
+ m_destinationNode->resume([this, protectedThis = makeRef(*this)] {
+ setState(State::Running);
+ });
+}
+
+
} // namespace WebCore
#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/Modules/webaudio/AudioContext.h b/Source/WebCore/Modules/webaudio/AudioContext.h
index 1e965d9ad..c631f1f19 100644
--- a/Source/WebCore/Modules/webaudio/AudioContext.h
+++ b/Source/WebCore/Modules/webaudio/AudioContext.h
@@ -1,5 +1,6 @@
/*
- * Copyright (C) 2010, Google Inc. All rights reserved.
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ * 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
@@ -22,8 +23,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AudioContext_h
-#define AudioContext_h
+#pragma once
#include "ActiveDOMObject.h"
#include "AsyncAudioDecoder.h"
@@ -31,12 +31,13 @@
#include "AudioDestinationNode.h"
#include "EventListener.h"
#include "EventTarget.h"
+#include "JSDOMPromise.h"
#include "MediaCanStartListener.h"
+#include "MediaProducer.h"
+#include "PlatformMediaSession.h"
#include <atomic>
#include <wtf/HashSet.h>
#include <wtf/MainThread.h>
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
#include <wtf/RefPtr.h>
#include <wtf/ThreadSafeRefCounted.h>
#include <wtf/Threading.h>
@@ -45,41 +46,39 @@
namespace WebCore {
+class AnalyserNode;
class AudioBuffer;
class AudioBufferCallback;
class AudioBufferSourceNode;
-class MediaElementAudioSourceNode;
-class MediaStreamAudioDestinationNode;
-class MediaStreamAudioSourceNode;
-class HRTFDatabaseLoader;
-class HTMLMediaElement;
-class ChannelMergerNode;
-class ChannelSplitterNode;
-class GainNode;
-class PannerNode;
class AudioListener;
class AudioSummingJunction;
class BiquadFilterNode;
+class ChannelMergerNode;
+class ChannelSplitterNode;
+class ConvolverNode;
class DelayNode;
class Document;
-class ConvolverNode;
class DynamicsCompressorNode;
-class AnalyserNode;
-class WaveShaperNode;
-class ScriptProcessorNode;
+class GainNode;
+class GenericEventQueue;
+class HTMLMediaElement;
+class MediaElementAudioSourceNode;
+class MediaStream;
+class MediaStreamAudioDestinationNode;
+class MediaStreamAudioSourceNode;
class OscillatorNode;
+class PannerNode;
class PeriodicWave;
+class ScriptProcessorNode;
+class WaveShaperNode;
// AudioContext is the cornerstone of the web audio API and all AudioNodes are created from it.
// For thread safety between the audio thread and the main thread, it has a rendering graph locking mechanism.
-class AudioContext : public ActiveDOMObject, public ThreadSafeRefCounted<AudioContext>, public EventTargetWithInlineData, public MediaCanStartListener {
+class AudioContext : public ActiveDOMObject, public ThreadSafeRefCounted<AudioContext>, public EventTargetWithInlineData, public MediaCanStartListener, public MediaProducer, private PlatformMediaSessionClient {
public:
// Create an AudioContext for rendering to the audio hardware.
- static PassRefPtr<AudioContext> create(Document&, ExceptionCode&);
-
- // Create an AudioContext for offline (non-realtime) rendering.
- static PassRefPtr<AudioContext> createOfflineContext(Document*, unsigned numberOfChannels, size_t numberOfFrames, float sampleRate, ExceptionCode&);
+ static RefPtr<AudioContext> create(Document&);
virtual ~AudioContext();
@@ -87,16 +86,10 @@ public:
bool isOfflineContext() { return m_isOfflineContext; }
- // Returns true when initialize() was called AND all asynchronous initialization has completed.
- bool isRunnable() const;
-
- HRTFDatabaseLoader* hrtfDatabaseLoader() const { return m_hrtfDatabaseLoader.get(); }
-
- // Document notification
- virtual void stop() override;
-
Document* document() const; // ASSERTs if document no longer exists.
+ const Document* hostingDocument() const override;
+
AudioDestinationNode* destination() { return m_destinationNode.get(); }
size_t currentSampleFrame() const { return m_destinationNode->currentSampleFrame(); }
double currentTime() const { return m_destinationNode->currentTime(); }
@@ -106,41 +99,46 @@ public:
void incrementActiveSourceCount();
void decrementActiveSourceCount();
- PassRefPtr<AudioBuffer> createBuffer(unsigned numberOfChannels, size_t numberOfFrames, float sampleRate, ExceptionCode&);
- PassRefPtr<AudioBuffer> createBuffer(ArrayBuffer*, bool mixToMono, ExceptionCode&);
+ ExceptionOr<Ref<AudioBuffer>> createBuffer(unsigned numberOfChannels, size_t numberOfFrames, float sampleRate);
+ ExceptionOr<Ref<AudioBuffer>> createBuffer(ArrayBuffer&, bool mixToMono);
// Asynchronous audio file data decoding.
- void decodeAudioData(ArrayBuffer*, PassRefPtr<AudioBufferCallback>, PassRefPtr<AudioBufferCallback>, ExceptionCode& ec);
+ void decodeAudioData(Ref<ArrayBuffer>&&, RefPtr<AudioBufferCallback>&&, RefPtr<AudioBufferCallback>&&);
AudioListener* listener() { return m_listener.get(); }
+ using ActiveDOMObject::suspend;
+ using ActiveDOMObject::resume;
+
+ void suspend(DOMPromise<void>&&);
+ void resume(DOMPromise<void>&&);
+ void close(DOMPromise<void>&&);
+
+ enum class State { Suspended, Running, Interrupted, Closed };
+ State state() const;
+
// The AudioNode create methods are called on the main thread (from JavaScript).
- PassRefPtr<AudioBufferSourceNode> createBufferSource();
+ Ref<AudioBufferSourceNode> createBufferSource();
#if ENABLE(VIDEO)
- PassRefPtr<MediaElementAudioSourceNode> createMediaElementSource(HTMLMediaElement*, ExceptionCode&);
+ ExceptionOr<Ref<MediaElementAudioSourceNode>> createMediaElementSource(HTMLMediaElement&);
#endif
#if ENABLE(MEDIA_STREAM)
- PassRefPtr<MediaStreamAudioSourceNode> createMediaStreamSource(MediaStream*, ExceptionCode&);
- PassRefPtr<MediaStreamAudioDestinationNode> createMediaStreamDestination();
+ ExceptionOr<Ref<MediaStreamAudioSourceNode>> createMediaStreamSource(MediaStream&);
+ Ref<MediaStreamAudioDestinationNode> createMediaStreamDestination();
#endif
- PassRefPtr<GainNode> createGain();
- PassRefPtr<BiquadFilterNode> createBiquadFilter();
- PassRefPtr<WaveShaperNode> createWaveShaper();
- PassRefPtr<DelayNode> createDelay(ExceptionCode&);
- PassRefPtr<DelayNode> createDelay(double maxDelayTime, ExceptionCode&);
- PassRefPtr<PannerNode> createPanner();
- PassRefPtr<ConvolverNode> createConvolver();
- PassRefPtr<DynamicsCompressorNode> createDynamicsCompressor();
- PassRefPtr<AnalyserNode> createAnalyser();
- PassRefPtr<ScriptProcessorNode> createScriptProcessor(size_t bufferSize, ExceptionCode&);
- PassRefPtr<ScriptProcessorNode> createScriptProcessor(size_t bufferSize, size_t numberOfInputChannels, ExceptionCode&);
- PassRefPtr<ScriptProcessorNode> createScriptProcessor(size_t bufferSize, size_t numberOfInputChannels, size_t numberOfOutputChannels, ExceptionCode&);
- PassRefPtr<ChannelSplitterNode> createChannelSplitter(ExceptionCode&);
- PassRefPtr<ChannelSplitterNode> createChannelSplitter(size_t numberOfOutputs, ExceptionCode&);
- PassRefPtr<ChannelMergerNode> createChannelMerger(ExceptionCode&);
- PassRefPtr<ChannelMergerNode> createChannelMerger(size_t numberOfInputs, ExceptionCode&);
- PassRefPtr<OscillatorNode> createOscillator();
- PassRefPtr<PeriodicWave> createPeriodicWave(Float32Array* real, Float32Array* imag, ExceptionCode&);
+ Ref<GainNode> createGain();
+ Ref<BiquadFilterNode> createBiquadFilter();
+ Ref<WaveShaperNode> createWaveShaper();
+ ExceptionOr<Ref<DelayNode>> createDelay(double maxDelayTime);
+ Ref<PannerNode> createPanner();
+ Ref<ConvolverNode> createConvolver();
+ Ref<DynamicsCompressorNode> createDynamicsCompressor();
+ Ref<AnalyserNode> createAnalyser();
+ ExceptionOr<Ref<ScriptProcessorNode>> createScriptProcessor(size_t bufferSize, size_t numberOfInputChannels, size_t numberOfOutputChannels);
+ ExceptionOr<Ref<ChannelSplitterNode>> createChannelSplitter(size_t numberOfOutputs);
+ ExceptionOr<Ref<ChannelMergerNode>> createChannelMerger(size_t numberOfInputs);
+ Ref<OscillatorNode> createOscillator();
+ ExceptionOr<Ref<PeriodicWave>> createPeriodicWave(Float32Array& real, Float32Array& imaginary);
// When a source node has no more processing to do (has finished playing), then it tells the context to dereference it.
void notifyNodeFinishedProcessing(AudioNode*);
@@ -198,8 +196,8 @@ public:
// Returns true if this thread owns the context's lock.
bool isGraphOwner() const;
- // Returns the maximum numuber of channels we can support.
- static unsigned maxNumberOfChannels() { return MaxNumberOfChannels;}
+ // Returns the maximum number of channels we can support.
+ static unsigned maxNumberOfChannels() { return MaxNumberOfChannels; }
class AutoLocker {
public:
@@ -234,14 +232,12 @@ public:
void removeMarkedSummingJunction(AudioSummingJunction*);
// EventTarget
- virtual EventTargetInterface eventTargetInterface() const override final { return AudioContextEventTargetInterfaceType; }
- virtual ScriptExecutionContext* scriptExecutionContext() const override final;
-
- DEFINE_ATTRIBUTE_EVENT_LISTENER(complete);
+ EventTargetInterface eventTargetInterface() const final { return AudioContextEventTargetInterfaceType; }
+ ScriptExecutionContext* scriptExecutionContext() const final;
// Reconcile ref/deref which are defined both in ThreadSafeRefCounted and EventTarget.
- using ThreadSafeRefCounted<AudioContext>::ref;
- using ThreadSafeRefCounted<AudioContext>::deref;
+ using ThreadSafeRefCounted::ref;
+ using ThreadSafeRefCounted::deref;
void startRendering();
void fireCompletionEvent();
@@ -256,12 +252,14 @@ public:
};
typedef unsigned BehaviorRestrictions;
- bool userGestureRequiredForAudioStart() const { return m_restrictions & RequireUserGestureForAudioStartRestriction; }
- bool pageConsentRequiredForAudioStart() const { return m_restrictions & RequirePageConsentForAudioStartRestriction; }
-
+ BehaviorRestrictions behaviorRestrictions() const { return m_restrictions; }
void addBehaviorRestriction(BehaviorRestrictions restriction) { m_restrictions |= restriction; }
void removeBehaviorRestriction(BehaviorRestrictions restriction) { m_restrictions &= ~restriction; }
+ void isPlayingAudioDidChange();
+
+ void nodeWillBeginPlayback();
+
protected:
explicit AudioContext(Document&);
AudioContext(Document&, unsigned numberOfChannels, size_t numberOfFrames, float sampleRate);
@@ -274,33 +272,62 @@ private:
void lazyInitialize();
void uninitialize();
- // ScriptExecutionContext calls stop twice.
- // We'd like to schedule only one stop action for them.
- bool m_isStopScheduled;
- static void stopDispatch(void* userData);
+ bool willBeginPlayback();
+ bool willPausePlayback();
+
+ bool userGestureRequiredForAudioStart() const { return m_restrictions & RequireUserGestureForAudioStartRestriction; }
+ bool pageConsentRequiredForAudioStart() const { return m_restrictions & RequirePageConsentForAudioStartRestriction; }
+
+ void setState(State);
+
void clear();
void scheduleNodeDeletion();
- static void deleteMarkedNodesDispatch(void* userData);
- virtual void mediaCanStart() override;
+ void mediaCanStart(Document&) override;
- bool m_isInitialized;
- bool m_isAudioThreadFinished;
+ // MediaProducer
+ MediaProducer::MediaStateFlags mediaState() const override;
+ void pageMutedStateDidChange() override;
// The context itself keeps a reference to all source nodes. The source nodes, then reference all nodes they're connected to.
// In turn, these nodes reference all nodes they're connected to. All nodes are ultimately connected to the AudioDestinationNode.
// When the context dereferences a source node, it will be deactivated from the rendering graph along with all other nodes it is
// uniquely connected to. See the AudioNode::ref() and AudioNode::deref() methods for more details.
- void refNode(AudioNode*);
- void derefNode(AudioNode*);
+ void refNode(AudioNode&);
+ void derefNode(AudioNode&);
+
+ // ActiveDOMObject API.
+ void stop() override;
+ bool canSuspendForDocumentSuspension() const override;
+ const char* activeDOMObjectName() const override;
// When the context goes away, there might still be some sources which haven't finished playing.
// Make sure to dereference them here.
void derefUnfinishedSourceNodes();
- RefPtr<AudioDestinationNode> m_destinationNode;
- RefPtr<AudioListener> m_listener;
+ // PlatformMediaSessionClient
+ PlatformMediaSession::MediaType mediaType() const override { return PlatformMediaSession::WebAudio; }
+ PlatformMediaSession::MediaType presentationType() const override { return PlatformMediaSession::WebAudio; }
+ PlatformMediaSession::CharacteristicsFlags characteristics() const override { return m_state == State::Running ? PlatformMediaSession::HasAudio : PlatformMediaSession::HasNothing; }
+ void mayResumePlayback(bool shouldResume) override;
+ void suspendPlayback() override;
+ bool canReceiveRemoteControlCommands() const override { return false; }
+ void didReceiveRemoteControlCommand(PlatformMediaSession::RemoteControlCommandType, const PlatformMediaSession::RemoteCommandArgument*) override { }
+ bool supportsSeeking() const override { return false; }
+ bool shouldOverrideBackgroundPlaybackRestriction(PlatformMediaSession::InterruptionType) const override { return false; }
+ String sourceApplicationIdentifier() const override;
+ bool canProduceAudio() const final { return true; }
+
+ // EventTarget
+ void refEventTarget() override { ref(); }
+ void derefEventTarget() override { deref(); }
+
+ void handleDirtyAudioSummingJunctions();
+ void handleDirtyAudioNodeOutputs();
+
+ void addReaction(State, DOMPromise<void>&&);
+ void updateAutomaticPullNodes();
// Only accessed in the audio thread.
Vector<AudioNode*> m_finishedNodes;
@@ -318,42 +345,39 @@ private:
// They will be scheduled for deletion (on the main thread) at the end of a render cycle (in realtime thread).
Vector<AudioNode*> m_nodesToDelete;
- bool m_isDeletionScheduled;
+
+ bool m_isDeletionScheduled { false };
+ bool m_isStopScheduled { false };
+ bool m_isInitialized { false };
+ bool m_isAudioThreadFinished { false };
+ bool m_automaticPullNodesNeedUpdating { false };
+ bool m_isOfflineContext { false };
// Only accessed when the graph lock is held.
HashSet<AudioSummingJunction*> m_dirtySummingJunctions;
HashSet<AudioNodeOutput*> m_dirtyAudioNodeOutputs;
- void handleDirtyAudioSummingJunctions();
- void handleDirtyAudioNodeOutputs();
// For the sake of thread safety, we maintain a seperate Vector of automatic pull nodes for rendering in m_renderingAutomaticPullNodes.
// It will be copied from m_automaticPullNodes by updateAutomaticPullNodes() at the very start or end of the rendering quantum.
HashSet<AudioNode*> m_automaticPullNodes;
Vector<AudioNode*> m_renderingAutomaticPullNodes;
- // m_automaticPullNodesNeedUpdating keeps track if m_automaticPullNodes is modified.
- bool m_automaticPullNodesNeedUpdating;
- void updateAutomaticPullNodes();
-
- unsigned m_connectionCount;
-
- // Graph locking.
- Mutex m_contextGraphMutex;
- volatile ThreadIdentifier m_audioThread;
- volatile ThreadIdentifier m_graphOwnerThread; // if the lock is held then this is the thread which owns it, otherwise == UndefinedThreadIdentifier
-
// Only accessed in the audio thread.
Vector<AudioNode*> m_deferredFinishDerefList;
-
- // HRTF Database loader
- RefPtr<HRTFDatabaseLoader> m_hrtfDatabaseLoader;
+ Vector<Vector<DOMPromise<void>>> m_stateReactions;
- // EventTarget
- virtual void refEventTarget() override { ref(); }
- virtual void derefEventTarget() override { deref(); }
+ std::unique_ptr<PlatformMediaSession> m_mediaSession;
+ std::unique_ptr<GenericEventQueue> m_eventQueue;
RefPtr<AudioBuffer> m_renderTarget;
-
- bool m_isOfflineContext;
+ RefPtr<AudioDestinationNode> m_destinationNode;
+ RefPtr<AudioListener> m_listener;
+
+ unsigned m_connectionCount { 0 };
+
+ // Graph locking.
+ Lock m_contextGraphMutex;
+ volatile ThreadIdentifier m_audioThread { 0 };
+ volatile ThreadIdentifier m_graphOwnerThread; // if the lock is held then this is the thread which owns it, otherwise == UndefinedThreadIdentifier
AsyncAudioDecoder m_audioDecoder;
@@ -362,11 +386,28 @@ private:
enum { MaxNumberOfChannels = 32 };
// Number of AudioBufferSourceNodes that are active (playing).
- std::atomic<int> m_activeSourceCount;
+ std::atomic<int> m_activeSourceCount { 0 };
- BehaviorRestrictions m_restrictions;
+ BehaviorRestrictions m_restrictions { NoRestrictions };
+
+ State m_state { State::Suspended };
};
-} // WebCore
+// FIXME: Find out why these ==/!= functions are needed and remove them if possible.
-#endif // AudioContext_h
+inline bool operator==(const AudioContext& lhs, const AudioContext& rhs)
+{
+ return &lhs == &rhs;
+}
+
+inline bool operator!=(const AudioContext& lhs, const AudioContext& rhs)
+{
+ return &lhs != &rhs;
+}
+
+inline AudioContext::State AudioContext::state() const
+{
+ return m_state;
+}
+
+} // WebCore
diff --git a/Source/WebCore/Modules/webaudio/AudioContext.idl b/Source/WebCore/Modules/webaudio/AudioContext.idl
index 8d684299f..e5226a3f1 100644
--- a/Source/WebCore/Modules/webaudio/AudioContext.idl
+++ b/Source/WebCore/Modules/webaudio/AudioContext.idl
@@ -23,72 +23,78 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+enum AudioContextState {
+ "suspended",
+ "running",
+ "interrupted",
+ "closed"
+};
+
[
- EnabledBySetting=WebAudio,
- Conditional=WEB_AUDIO,
ActiveDOMObject,
- CustomConstructor,
- EventTarget,
+ Conditional=WEB_AUDIO,
+ Constructor,
+ ConstructorCallWith=Document,
+ EnabledBySetting=WebAudio,
+ ExportMacro=WEBCORE_EXPORT,
InterfaceName=webkitAudioContext,
-] interface AudioContext {
+] interface AudioContext : EventTarget {
// All rendered audio ultimately connects to destination, which represents the audio hardware.
readonly attribute AudioDestinationNode destination;
// All scheduled times are relative to this time in seconds.
- readonly attribute double currentTime;
+ readonly attribute unrestricted double currentTime;
// All AudioNodes in the context run at this sample-rate (sample-frames per second).
- readonly attribute float sampleRate;
+ readonly attribute unrestricted float sampleRate;
// All panning is relative to this listener.
readonly attribute AudioListener listener;
+ Promise<void> suspend();
+ Promise<void> resume();
+ Promise<void> close();
+
+ readonly attribute AudioContextState state;
+ attribute EventHandler onstatechange;
+
// Number of AudioBufferSourceNodes that are currently playing.
readonly attribute unsigned long activeSourceCount;
- [RaisesException] AudioBuffer createBuffer(unsigned long numberOfChannels, unsigned long numberOfFrames, float sampleRate);
- [RaisesException] AudioBuffer createBuffer(ArrayBuffer? buffer, boolean mixToMono);
+ [MayThrowException] AudioBuffer createBuffer(unsigned long numberOfChannels, unsigned long numberOfFrames, unrestricted float sampleRate);
+ [MayThrowException] AudioBuffer createBuffer(ArrayBuffer buffer, boolean mixToMono);
// Asynchronous audio file data decoding.
- [RaisesException] void decodeAudioData(ArrayBuffer audioData, AudioBufferCallback successCallback, optional AudioBufferCallback errorCallback);
+ // FIXME: successCallback should be optional and the callbacks should not be nullable. This should also return a Promise.
+ void decodeAudioData(ArrayBuffer audioData, AudioBufferCallback? successCallback, optional AudioBufferCallback? errorCallback);
// Sources
AudioBufferSourceNode createBufferSource();
-#if defined(ENABLE_VIDEO) && ENABLE_VIDEO
- [RaisesException] MediaElementAudioSourceNode createMediaElementSource(HTMLMediaElement mediaElement);
-#endif
+ [Conditional=VIDEO, MayThrowException] MediaElementAudioSourceNode createMediaElementSource(HTMLMediaElement mediaElement);
-#if defined(ENABLE_MEDIA_STREAM) && ENABLE_MEDIA_STREAM
- [RaisesException] MediaStreamAudioSourceNode createMediaStreamSource(MediaStream mediaStream);
- MediaStreamAudioDestinationNode createMediaStreamDestination();
-#endif
+ [Conditional=MEDIA_STREAM, MayThrowException] MediaStreamAudioSourceNode createMediaStreamSource(MediaStream mediaStream);
+ [Conditional=MEDIA_STREAM] MediaStreamAudioDestinationNode createMediaStreamDestination();
// Processing nodes
GainNode createGain();
- [RaisesException] DelayNode createDelay(optional double maxDelayTime);
+ [MayThrowException] DelayNode createDelay(optional unrestricted double maxDelayTime = 1);
BiquadFilterNode createBiquadFilter();
WaveShaperNode createWaveShaper();
PannerNode createPanner();
ConvolverNode createConvolver();
DynamicsCompressorNode createDynamicsCompressor();
AnalyserNode createAnalyser();
- [RaisesException] ScriptProcessorNode createScriptProcessor(unsigned long bufferSize, optional unsigned long numberOfInputChannels, optional unsigned long numberOfOutputChannels);
+ [MayThrowException] ScriptProcessorNode createScriptProcessor(unsigned long bufferSize, optional unsigned long numberOfInputChannels = 2, optional unsigned long numberOfOutputChannels = 2);
OscillatorNode createOscillator();
- [RaisesException] PeriodicWave createPeriodicWave(Float32Array real, Float32Array imag);
+ [MayThrowException] PeriodicWave createPeriodicWave(Float32Array real, Float32Array imag);
// Channel splitting and merging
- [RaisesException] ChannelSplitterNode createChannelSplitter(optional unsigned long numberOfOutputs);
- [RaisesException] ChannelMergerNode createChannelMerger(optional unsigned long numberOfInputs);
+ [MayThrowException] ChannelSplitterNode createChannelSplitter(optional unsigned long numberOfOutputs = 6);
+ [MayThrowException] ChannelMergerNode createChannelMerger(optional unsigned long numberOfInputs = 6);
// Offline rendering
- // void prepareOfflineBufferRendering(unsigned long numberOfChannels, unsigned long numberOfFrames, float sampleRate);
- attribute EventListener oncomplete;
+ // void prepareOfflineBufferRendering(unsigned long numberOfChannels, unsigned long numberOfFrames, unrestricted float sampleRate);
+ attribute EventHandler oncomplete;
void startRendering();
-
- [Conditional=LEGACY_WEB_AUDIO, ImplementedAs=createGain] GainNode createGainNode();
- [Conditional=LEGACY_WEB_AUDIO, ImplementedAs=createDelay, RaisesException] DelayNode createDelayNode(optional double maxDelayTime);
-
- [Conditional=LEGACY_WEB_AUDIO, ImplementedAs=createScriptProcessor, RaisesException] ScriptProcessorNode createJavaScriptNode(unsigned long bufferSize, optional unsigned long numberOfInputChannels, optional unsigned long numberOfOutputChannels);
-
};
diff --git a/Source/WebCore/Modules/webaudio/AudioDestinationNode.cpp b/Source/WebCore/Modules/webaudio/AudioDestinationNode.cpp
index cbe89a9a8..511281851 100644
--- a/Source/WebCore/Modules/webaudio/AudioDestinationNode.cpp
+++ b/Source/WebCore/Modules/webaudio/AudioDestinationNode.cpp
@@ -36,9 +36,12 @@
namespace WebCore {
-AudioDestinationNode::AudioDestinationNode(AudioContext* context, float sampleRate)
+AudioDestinationNode::AudioDestinationNode(AudioContext& context, float sampleRate)
: AudioNode(context, sampleRate)
, m_currentSampleFrame(0)
+ , m_isSilent(true)
+ , m_isEffectivelyPlayingAudio(false)
+ , m_muted(false)
{
addInput(std::make_unique<AudioNodeInput>(this));
@@ -50,36 +53,30 @@ AudioDestinationNode::~AudioDestinationNode()
uninitialize();
}
-void AudioDestinationNode::render(AudioBus* sourceBus, AudioBus* destinationBus, size_t numberOfFrames)
+void AudioDestinationNode::render(AudioBus*, AudioBus* destinationBus, size_t numberOfFrames)
{
// We don't want denormals slowing down any of the audio processing
// since they can very seriously hurt performance.
// This will take care of all AudioNodes because they all process within this scope.
DenormalDisabler denormalDisabler;
- context()->setAudioThread(currentThread());
+ context().setAudioThread(currentThread());
- if (!context()->isRunnable()) {
+ if (!context().isInitialized()) {
destinationBus->zero();
+ setIsSilent(true);
return;
}
- if (context()->userGestureRequiredForAudioStart()) {
- destinationBus->zero();
- return;
- }
-
- if (context()->pageConsentRequiredForAudioStart()) {
+ ASSERT(numberOfFrames);
+ if (!numberOfFrames) {
destinationBus->zero();
+ setIsSilent(true);
return;
}
// Let the context take care of any business at the start of each render quantum.
- context()->handlePreRenderTasks();
-
- // Prepare the local audio input provider for this render quantum.
- if (sourceBus)
- m_localAudioInputProvider.set(sourceBus);
+ context().handlePreRenderTasks();
// This will cause the node(s) connected to us to process, which in turn will pull on their input(s),
// all the way backwards through the rendering graph.
@@ -93,13 +90,44 @@ void AudioDestinationNode::render(AudioBus* sourceBus, AudioBus* destinationBus,
}
// Process nodes which need a little extra help because they are not connected to anything, but still need to process.
- context()->processAutomaticPullNodes(numberOfFrames);
+ context().processAutomaticPullNodes(numberOfFrames);
// Let the context take care of any business at the end of each render quantum.
- context()->handlePostRenderTasks();
+ context().handlePostRenderTasks();
// Advance current sample-frame.
m_currentSampleFrame += numberOfFrames;
+
+ setIsSilent(destinationBus->isSilent());
+
+ // The reason we are handling mute after the call to setIsSilent() is because the muted state does
+ // not affect the audio destination node's effective playing state.
+ if (m_muted)
+ destinationBus->zero();
+}
+
+void AudioDestinationNode::isPlayingDidChange()
+{
+ updateIsEffectivelyPlayingAudio();
+}
+
+void AudioDestinationNode::setIsSilent(bool isSilent)
+{
+ if (m_isSilent == isSilent)
+ return;
+
+ m_isSilent = isSilent;
+ updateIsEffectivelyPlayingAudio();
+}
+
+void AudioDestinationNode::updateIsEffectivelyPlayingAudio()
+{
+ bool isEffectivelyPlayingAudio = isPlaying() && !m_isSilent;
+ if (m_isEffectivelyPlayingAudio == isEffectivelyPlayingAudio)
+ return;
+
+ m_isEffectivelyPlayingAudio = isEffectivelyPlayingAudio;
+ context().isPlayingAudioDidChange();
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/webaudio/AudioDestinationNode.h b/Source/WebCore/Modules/webaudio/AudioDestinationNode.h
index e742c605e..9ebaf1399 100644
--- a/Source/WebCore/Modules/webaudio/AudioDestinationNode.h
+++ b/Source/WebCore/Modules/webaudio/AudioDestinationNode.h
@@ -22,83 +22,63 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AudioDestinationNode_h
-#define AudioDestinationNode_h
+#pragma once
#include "AudioBuffer.h"
#include "AudioBus.h"
#include "AudioIOCallback.h"
#include "AudioNode.h"
#include "AudioSourceProvider.h"
+#include <wtf/Function.h>
namespace WebCore {
-class AudioBus;
class AudioContext;
-
+
class AudioDestinationNode : public AudioNode, public AudioIOCallback {
public:
- AudioDestinationNode(AudioContext*, float sampleRate);
+ AudioDestinationNode(AudioContext&, float sampleRate);
virtual ~AudioDestinationNode();
// AudioNode
- virtual void process(size_t) override { }; // we're pulled by hardware so this is never called
- virtual void reset() override { m_currentSampleFrame = 0; }
+ void process(size_t) override { }; // we're pulled by hardware so this is never called
+ void reset() override { m_currentSampleFrame = 0; }
// The audio hardware calls render() to get the next render quantum of audio into destinationBus.
// It will optionally give us local/live audio input in sourceBus (if it's not 0).
- virtual void render(AudioBus* sourceBus, AudioBus* destinationBus, size_t numberOfFrames) override;
+ void render(AudioBus* sourceBus, AudioBus* destinationBus, size_t numberOfFrames) override;
size_t currentSampleFrame() const { return m_currentSampleFrame; }
double currentTime() const { return currentSampleFrame() / static_cast<double>(sampleRate()); }
- virtual unsigned long maxChannelCount() const { return 0; }
+ virtual unsigned maxChannelCount() const { return 0; }
// Enable local/live input for the specified device.
virtual void enableInput(const String& inputDeviceId) = 0;
virtual void startRendering() = 0;
+ virtual void resume(WTF::Function<void ()>&&) { }
+ virtual void suspend(WTF::Function<void ()>&&) { }
+ virtual void close(WTF::Function<void ()>&&) { }
- AudioSourceProvider* localAudioInputProvider() { return &m_localAudioInputProvider; }
-
-protected:
- // LocalAudioInputProvider allows us to expose an AudioSourceProvider for local/live audio input.
- // If there is local/live audio input, we call set() with the audio input data every render quantum.
- class LocalAudioInputProvider : public AudioSourceProvider {
- public:
- LocalAudioInputProvider()
- : m_sourceBus(AudioBus::create(2, AudioNode::ProcessingSizeInFrames)) // FIXME: handle non-stereo local input.
- {
- }
-
- void set(AudioBus* bus)
- {
- if (bus)
- m_sourceBus->copyFrom(*bus);
- }
-
- // AudioSourceProvider.
- virtual void provideInput(AudioBus* destinationBus, size_t numberOfFrames) override
- {
- bool isGood = destinationBus && destinationBus->length() == numberOfFrames && m_sourceBus->length() == numberOfFrames;
- ASSERT(isGood);
- if (isGood)
- destinationBus->copyFrom(*m_sourceBus);
- }
+ virtual bool isPlaying() { return false; }
+ void isPlayingDidChange() override;
+ bool isPlayingAudio() const { return m_isEffectivelyPlayingAudio; }
+ void setMuted(bool muted) { m_muted = muted; }
- private:
- RefPtr<AudioBus> m_sourceBus;
- };
+protected:
+ double tailTime() const override { return 0; }
+ double latencyTime() const override { return 0; }
- virtual double tailTime() const override { return 0; }
- virtual double latencyTime() const override { return 0; }
+ void setIsSilent(bool);
+ void updateIsEffectivelyPlayingAudio();
// Counts the number of sample-frames processed by the destination.
size_t m_currentSampleFrame;
- LocalAudioInputProvider m_localAudioInputProvider;
+ bool m_isSilent;
+ bool m_isEffectivelyPlayingAudio;
+ bool m_muted;
};
} // namespace WebCore
-
-#endif // AudioDestinationNode_h
diff --git a/Source/WebCore/Modules/webaudio/AudioListener.cpp b/Source/WebCore/Modules/webaudio/AudioListener.cpp
index 2f1ef764d..476d92d68 100644
--- a/Source/WebCore/Modules/webaudio/AudioListener.cpp
+++ b/Source/WebCore/Modules/webaudio/AudioListener.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.
*
diff --git a/Source/WebCore/Modules/webaudio/AudioListener.h b/Source/WebCore/Modules/webaudio/AudioListener.h
index 8b5d8ad1a..12e79779d 100644
--- a/Source/WebCore/Modules/webaudio/AudioListener.h
+++ b/Source/WebCore/Modules/webaudio/AudioListener.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,11 +26,10 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AudioListener_h
-#define AudioListener_h
+#pragma once
#include "FloatPoint3D.h"
-#include <wtf/PassRefPtr.h>
+#include <wtf/Ref.h>
#include <wtf/RefCounted.h>
namespace WebCore {
@@ -39,9 +38,9 @@ namespace WebCore {
class AudioListener : public RefCounted<AudioListener> {
public:
- static PassRefPtr<AudioListener> create()
+ static Ref<AudioListener> create()
{
- return adoptRef(new AudioListener());
+ return adoptRef(*new AudioListener);
}
// Position
@@ -89,6 +88,4 @@ private:
double m_speedOfSound;
};
-} // WebCore
-
-#endif // AudioListener_h
+} // namespace WebCore
diff --git a/Source/WebCore/Modules/webaudio/AudioListener.idl b/Source/WebCore/Modules/webaudio/AudioListener.idl
index 8ec2a9073..17018b52b 100644
--- a/Source/WebCore/Modules/webaudio/AudioListener.idl
+++ b/Source/WebCore/Modules/webaudio/AudioListener.idl
@@ -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.
*
@@ -30,10 +30,10 @@
Conditional=WEB_AUDIO,
ImplementationLacksVTable,
] interface AudioListener {
- attribute float dopplerFactor; // same as OpenAL (default 1.0)
- attribute float speedOfSound; // in meters / second (default 343.3)
+ attribute unrestricted float dopplerFactor; // same as OpenAL (default 1.0)
+ attribute unrestricted float speedOfSound; // in meters / second (default 343.3)
- void setPosition(float x, float y, float z);
- void setOrientation(float x, float y, float z, float xUp, float yUp, float zUp);
- void setVelocity(float x, float y, float z);
+ void setPosition(unrestricted float x, unrestricted float y, unrestricted float z);
+ void setOrientation(unrestricted float x, unrestricted float y, unrestricted float z, unrestricted float xUp, unrestricted float yUp, unrestricted float zUp);
+ void setVelocity(unrestricted float x, unrestricted float y, unrestricted float z);
};
diff --git a/Source/WebCore/Modules/webaudio/AudioNode.cpp b/Source/WebCore/Modules/webaudio/AudioNode.cpp
index 514ec7a3e..d0d42335f 100644
--- a/Source/WebCore/Modules/webaudio/AudioNode.cpp
+++ b/Source/WebCore/Modules/webaudio/AudioNode.cpp
@@ -42,7 +42,7 @@
namespace WebCore {
-AudioNode::AudioNode(AudioContext* context, float sampleRate)
+AudioNode::AudioNode(AudioContext& context, float sampleRate)
: m_isInitialized(false)
, m_nodeType(NodeTypeUnknown)
, m_context(context)
@@ -67,6 +67,7 @@ AudioNode::AudioNode(AudioContext* context, float sampleRate)
AudioNode::~AudioNode()
{
+ ASSERT(isMainThread());
#if DEBUG_AUDIONODE_REFERENCES
--s_nodeCount[nodeType()];
fprintf(stderr, "%p: %d: AudioNode::~AudioNode() %d %d\n", this, nodeType(), m_normalRefCount.load(), m_connectionRefCount);
@@ -100,12 +101,12 @@ void AudioNode::lazyInitialize()
void AudioNode::addInput(std::unique_ptr<AudioNodeInput> input)
{
- m_inputs.append(std::move(input));
+ m_inputs.append(WTFMove(input));
}
void AudioNode::addOutput(std::unique_ptr<AudioNodeOutput> output)
{
- m_outputs.append(std::move(output));
+ m_outputs.append(WTFMove(output));
}
AudioNodeInput* AudioNode::input(unsigned i)
@@ -122,117 +123,103 @@ AudioNodeOutput* AudioNode::output(unsigned i)
return nullptr;
}
-void AudioNode::connect(AudioNode* destination, unsigned outputIndex, unsigned inputIndex, ExceptionCode& ec)
+ExceptionOr<void> AudioNode::connect(AudioNode& destination, unsigned outputIndex, unsigned inputIndex)
{
ASSERT(isMainThread());
- AudioContext::AutoLocker locker(*context());
-
- if (!destination) {
- ec = SYNTAX_ERR;
- return;
- }
+ AudioContext::AutoLocker locker(context());
// Sanity check input and output indices.
- if (outputIndex >= numberOfOutputs()) {
- ec = INDEX_SIZE_ERR;
- return;
- }
+ if (outputIndex >= numberOfOutputs())
+ return Exception { INDEX_SIZE_ERR };
- if (destination && inputIndex >= destination->numberOfInputs()) {
- ec = INDEX_SIZE_ERR;
- return;
- }
+ if (inputIndex >= destination.numberOfInputs())
+ return Exception { INDEX_SIZE_ERR };
- if (context() != destination->context()) {
- ec = SYNTAX_ERR;
- return;
- }
+ if (context() != destination.context())
+ return Exception { SYNTAX_ERR };
- AudioNodeInput* input = destination->input(inputIndex);
- AudioNodeOutput* output = this->output(outputIndex);
+ auto* input = destination.input(inputIndex);
+ auto* output = this->output(outputIndex);
input->connect(output);
// Let context know that a connection has been made.
- context()->incrementConnectionCount();
+ context().incrementConnectionCount();
+
+ return { };
}
-void AudioNode::connect(AudioParam* param, unsigned outputIndex, ExceptionCode& ec)
+ExceptionOr<void> AudioNode::connect(AudioParam& param, unsigned outputIndex)
{
ASSERT(isMainThread());
- AudioContext::AutoLocker locker(*context());
+ AudioContext::AutoLocker locker(context());
- if (!param) {
- ec = SYNTAX_ERR;
- return;
- }
+ if (outputIndex >= numberOfOutputs())
+ return Exception { INDEX_SIZE_ERR };
- if (outputIndex >= numberOfOutputs()) {
- ec = INDEX_SIZE_ERR;
- return;
- }
+ if (context() != param.context())
+ return Exception { SYNTAX_ERR };
- if (context() != param->context()) {
- ec = SYNTAX_ERR;
- return;
- }
+ auto* output = this->output(outputIndex);
+ param.connect(output);
- AudioNodeOutput* output = this->output(outputIndex);
- param->connect(output);
+ return { };
}
-void AudioNode::disconnect(unsigned outputIndex, ExceptionCode& ec)
+ExceptionOr<void> AudioNode::disconnect(unsigned outputIndex)
{
ASSERT(isMainThread());
- AudioContext::AutoLocker locker(*context());
+ AudioContext::AutoLocker locker(context());
// Sanity check input and output indices.
- if (outputIndex >= numberOfOutputs()) {
- ec = INDEX_SIZE_ERR;
- return;
- }
+ if (outputIndex >= numberOfOutputs())
+ return Exception { INDEX_SIZE_ERR };
- AudioNodeOutput* output = this->output(outputIndex);
+ auto* output = this->output(outputIndex);
output->disconnectAll();
+
+ return { };
}
-unsigned long AudioNode::channelCount()
+unsigned AudioNode::channelCount()
{
return m_channelCount;
}
-void AudioNode::setChannelCount(unsigned long channelCount, ExceptionCode& ec)
+ExceptionOr<void> AudioNode::setChannelCount(unsigned channelCount)
{
ASSERT(isMainThread());
- AudioContext::AutoLocker locker(*context());
+ AudioContext::AutoLocker locker(context());
- if (channelCount > 0 && channelCount <= AudioContext::maxNumberOfChannels()) {
- if (m_channelCount != channelCount) {
- m_channelCount = channelCount;
- if (m_channelCountMode != Max)
- updateChannelsForInputs();
- }
- } else
- ec = INVALID_STATE_ERR;
+ if (!(channelCount > 0 && channelCount <= AudioContext::maxNumberOfChannels()))
+ return Exception { INVALID_STATE_ERR };
+
+ if (m_channelCount == channelCount)
+ return { };
+
+ m_channelCount = channelCount;
+ if (m_channelCountMode != Max)
+ updateChannelsForInputs();
+ return { };
}
String AudioNode::channelCountMode()
{
switch (m_channelCountMode) {
case Max:
- return "max";
+ return ASCIILiteral("max");
case ClampedMax:
- return "clamped-max";
+ return ASCIILiteral("clamped-max");
case Explicit:
- return "explicit";
+ return ASCIILiteral("explicit");
}
ASSERT_NOT_REACHED();
- return "";
+ return emptyString();
}
-void AudioNode::setChannelCountMode(const String& mode, ExceptionCode& ec)
+ExceptionOr<void> AudioNode::setChannelCountMode(const String& mode)
{
ASSERT(isMainThread());
- AudioContext::AutoLocker locker(*context());
+ AudioContext::AutoLocker locker(context());
ChannelCountMode oldMode = m_channelCountMode;
@@ -243,41 +230,45 @@ void AudioNode::setChannelCountMode(const String& mode, ExceptionCode& ec)
else if (mode == "explicit")
m_channelCountMode = Explicit;
else
- ec = INVALID_STATE_ERR;
+ return Exception { INVALID_STATE_ERR };
if (m_channelCountMode != oldMode)
updateChannelsForInputs();
+
+ return { };
}
String AudioNode::channelInterpretation()
{
switch (m_channelInterpretation) {
case AudioBus::Speakers:
- return "speakers";
+ return ASCIILiteral("speakers");
case AudioBus::Discrete:
- return "discrete";
+ return ASCIILiteral("discrete");
}
ASSERT_NOT_REACHED();
- return "";
+ return emptyString();
}
-void AudioNode::setChannelInterpretation(const String& interpretation, ExceptionCode& ec)
+ExceptionOr<void> AudioNode::setChannelInterpretation(const String& interpretation)
{
ASSERT(isMainThread());
- AudioContext::AutoLocker locker(*context());
+ AudioContext::AutoLocker locker(context());
if (interpretation == "speakers")
m_channelInterpretation = AudioBus::Speakers;
else if (interpretation == "discrete")
m_channelInterpretation = AudioBus::Discrete;
else
- ec = INVALID_STATE_ERR;
+ return Exception { INVALID_STATE_ERR };
+
+ return { };
}
void AudioNode::updateChannelsForInputs()
{
- for (unsigned i = 0; i < m_inputs.size(); ++i)
- input(i)->changedOutputs();
+ for (auto& input : m_inputs)
+ input->changedOutputs();
}
EventTargetInterface AudioNode::eventTargetInterface() const
@@ -287,12 +278,12 @@ EventTargetInterface AudioNode::eventTargetInterface() const
ScriptExecutionContext* AudioNode::scriptExecutionContext() const
{
- return const_cast<AudioNode*>(this)->context()->scriptExecutionContext();
+ return const_cast<AudioNode*>(this)->context().scriptExecutionContext();
}
void AudioNode::processIfNecessary(size_t framesToProcess)
{
- ASSERT(context()->isAudioThread());
+ ASSERT(context().isAudioThread());
if (!isInitialized())
return;
@@ -301,7 +292,7 @@ void AudioNode::processIfNecessary(size_t framesToProcess)
// This handles the "fanout" problem where an output is connected to multiple inputs.
// The first time we're called during this time slice we process, but after that we don't want to re-process,
// instead our output(s) will already have the results cached in their bus;
- double currentTime = context()->currentTime();
+ double currentTime = context().currentTime();
if (m_lastProcessingTime != currentTime) {
m_lastProcessingTime = currentTime; // important to first update this time because of feedback loops in the rendering graph
@@ -309,7 +300,7 @@ void AudioNode::processIfNecessary(size_t framesToProcess)
bool silentInputs = inputsAreSilent();
if (!silentInputs)
- m_lastNonSilentTime = (context()->currentSampleFrame() + framesToProcess) / static_cast<double>(m_sampleRate);
+ m_lastNonSilentTime = (context().currentSampleFrame() + framesToProcess) / static_cast<double>(m_sampleRate);
if (silentInputs && propagatesSilence())
silenceOutputs();
@@ -322,9 +313,9 @@ void AudioNode::processIfNecessary(size_t framesToProcess)
void AudioNode::checkNumberOfChannelsForInput(AudioNodeInput* input)
{
- ASSERT(context()->isAudioThread() && context()->isGraphOwner());
+ ASSERT(context().isAudioThread() && context().isGraphOwner());
- for (const std::unique_ptr<AudioNodeInput>& savedInput : m_inputs) {
+ for (auto& savedInput : m_inputs) {
if (input == savedInput.get()) {
input->updateInternalBus();
return;
@@ -336,22 +327,22 @@ void AudioNode::checkNumberOfChannelsForInput(AudioNodeInput* input)
bool AudioNode::propagatesSilence() const
{
- return m_lastNonSilentTime + latencyTime() + tailTime() < context()->currentTime();
+ return m_lastNonSilentTime + latencyTime() + tailTime() < context().currentTime();
}
void AudioNode::pullInputs(size_t framesToProcess)
{
- ASSERT(context()->isAudioThread());
+ ASSERT(context().isAudioThread());
// Process all of the AudioNodes connected to our inputs.
- for (unsigned i = 0; i < m_inputs.size(); ++i)
- input(i)->pull(0, framesToProcess);
+ for (auto& input : m_inputs)
+ input->pull(0, framesToProcess);
}
bool AudioNode::inputsAreSilent()
{
- for (unsigned i = 0; i < m_inputs.size(); ++i) {
- if (!input(i)->bus()->isSilent())
+ for (auto& input : m_inputs) {
+ if (!input->bus()->isSilent())
return false;
}
return true;
@@ -359,25 +350,25 @@ bool AudioNode::inputsAreSilent()
void AudioNode::silenceOutputs()
{
- for (unsigned i = 0; i < m_outputs.size(); ++i)
- output(i)->bus()->zero();
+ for (auto& output : m_outputs)
+ output->bus()->zero();
}
void AudioNode::unsilenceOutputs()
{
- for (unsigned i = 0; i < m_outputs.size(); ++i)
- output(i)->bus()->clearSilentFlag();
+ for (auto& output : m_outputs)
+ output->bus()->clearSilentFlag();
}
void AudioNode::enableOutputsIfNecessary()
{
if (m_isDisabled && m_connectionRefCount > 0) {
ASSERT(isMainThread());
- AudioContext::AutoLocker locker(*context());
+ AudioContext::AutoLocker locker(context());
m_isDisabled = false;
- for (unsigned i = 0; i < m_outputs.size(); ++i)
- output(i)->enable();
+ for (auto& output : m_outputs)
+ output->enable();
}
}
@@ -401,8 +392,8 @@ void AudioNode::disableOutputsIfNecessary()
// longer any active connections.
if (nodeType() != NodeTypeConvolver && nodeType() != NodeTypeDelay) {
m_isDisabled = true;
- for (unsigned i = 0; i < m_outputs.size(); ++i)
- output(i)->disable();
+ for (auto& output : m_outputs)
+ output->disable();
}
}
}
@@ -438,11 +429,11 @@ void AudioNode::deref(RefType refType)
bool hasLock = false;
bool mustReleaseLock = false;
- if (context()->isAudioThread()) {
+ if (context().isAudioThread()) {
// Real-time audio thread must not contend lock (to avoid glitches).
- hasLock = context()->tryLock(mustReleaseLock);
+ hasLock = context().tryLock(mustReleaseLock);
} else {
- context()->lock(mustReleaseLock);
+ context().lock(mustReleaseLock);
hasLock = true;
}
@@ -451,24 +442,24 @@ void AudioNode::deref(RefType refType)
finishDeref(refType);
if (mustReleaseLock)
- context()->unlock();
+ context().unlock();
} else {
// We were unable to get the lock, so put this in a list to finish up later.
- ASSERT(context()->isAudioThread());
+ ASSERT(context().isAudioThread());
ASSERT(refType == RefTypeConnection);
- context()->addDeferredFinishDeref(this);
+ context().addDeferredFinishDeref(this);
}
// Once AudioContext::uninitialize() is called there's no more chances for deleteMarkedNodes() to get called, so we call here.
// We can't call in AudioContext::~AudioContext() since it will never be called as long as any AudioNode is alive
// because AudioNodes keep a reference to the context.
- if (context()->isAudioThreadFinished())
- context()->deleteMarkedNodes();
+ if (context().isAudioThreadFinished())
+ context().deleteMarkedNodes();
}
void AudioNode::finishDeref(RefType refType)
{
- ASSERT(context()->isGraphOwner());
+ ASSERT(context().isGraphOwner());
switch (refType) {
case RefTypeNormal:
@@ -491,11 +482,11 @@ void AudioNode::finishDeref(RefType refType)
if (!m_normalRefCount) {
if (!m_isMarkedForDeletion) {
// All references are gone - we need to go away.
- for (unsigned i = 0; i < m_outputs.size(); ++i)
- output(i)->disconnectAll(); // This will deref() nodes we're connected to.
+ for (auto& output : m_outputs)
+ output->disconnectAll(); // This will deref() nodes we're connected to.
// Mark for deletion at end of each render quantum or when context shuts down.
- context()->markForDeletion(this);
+ context().markForDeletion(this);
m_isMarkedForDeletion = true;
}
} else if (refType == RefTypeConnection)
diff --git a/Source/WebCore/Modules/webaudio/AudioNode.h b/Source/WebCore/Modules/webaudio/AudioNode.h
index 1536056a0..72f8a4af7 100644
--- a/Source/WebCore/Modules/webaudio/AudioNode.h
+++ b/Source/WebCore/Modules/webaudio/AudioNode.h
@@ -22,15 +22,12 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AudioNode_h
-#define AudioNode_h
+#pragma once
#include "AudioBus.h"
#include "EventTarget.h"
-#include <memory>
+#include "ExceptionOr.h"
#include <wtf/Forward.h>
-#include <wtf/RefPtr.h>
-#include <wtf/Vector.h>
#define DEBUG_AUDIONODE_REFERENCES 0
@@ -41,8 +38,6 @@ class AudioNodeInput;
class AudioNodeOutput;
class AudioParam;
-typedef int ExceptionCode;
-
// An AudioNode is the basic building block for handling audio within an AudioContext.
// It may be an audio source, an intermediate processing module, or an audio destination.
// Each AudioNode can have inputs and/or outputs. An AudioSourceNode has no inputs and a single output.
@@ -53,11 +48,11 @@ class AudioNode : public EventTargetWithInlineData {
public:
enum { ProcessingSizeInFrames = 128 };
- AudioNode(AudioContext*, float sampleRate);
+ AudioNode(AudioContext&, float sampleRate);
virtual ~AudioNode();
- AudioContext* context() { return m_context.get(); }
- const AudioContext* context() const { return m_context.get(); }
+ AudioContext& context() { return m_context.get(); }
+ const AudioContext& context() const { return m_context.get(); }
enum NodeType {
NodeTypeUnknown,
@@ -126,9 +121,9 @@ public:
AudioNodeOutput* output(unsigned);
// Called from main thread by corresponding JavaScript methods.
- virtual void connect(AudioNode*, unsigned outputIndex, unsigned inputIndex, ExceptionCode&);
- void connect(AudioParam*, unsigned outputIndex, ExceptionCode&);
- virtual void disconnect(unsigned outputIndex, ExceptionCode&);
+ virtual ExceptionOr<void> connect(AudioNode&, unsigned outputIndex, unsigned inputIndex);
+ ExceptionOr<void> connect(AudioParam&, unsigned outputIndex);
+ virtual ExceptionOr<void> disconnect(unsigned outputIndex);
virtual float sampleRate() const { return m_sampleRate; }
@@ -166,21 +161,21 @@ public:
void enableOutputsIfNecessary();
void disableOutputsIfNecessary();
- unsigned long channelCount();
- virtual void setChannelCount(unsigned long, ExceptionCode&);
+ unsigned channelCount();
+ virtual ExceptionOr<void> setChannelCount(unsigned);
String channelCountMode();
- void setChannelCountMode(const String&, ExceptionCode&);
+ ExceptionOr<void> setChannelCountMode(const String&);
String channelInterpretation();
- void setChannelInterpretation(const String&, ExceptionCode&);
+ ExceptionOr<void> setChannelInterpretation(const String&);
ChannelCountMode internalChannelCountMode() const { return m_channelCountMode; }
AudioBus::ChannelInterpretation internalChannelInterpretation() const { return m_channelInterpretation; }
// EventTarget
- virtual EventTargetInterface eventTargetInterface() const override;
- virtual ScriptExecutionContext* scriptExecutionContext() const override final;
+ EventTargetInterface eventTargetInterface() const override;
+ ScriptExecutionContext* scriptExecutionContext() const final;
protected:
// Inputs and outputs must be created before the AudioNode is initialized.
@@ -198,7 +193,7 @@ protected:
private:
volatile bool m_isInitialized;
NodeType m_nodeType;
- RefPtr<AudioContext> m_context;
+ Ref<AudioContext> m_context;
float m_sampleRate;
Vector<std::unique_ptr<AudioNodeInput>> m_inputs;
Vector<std::unique_ptr<AudioNodeOutput>> m_outputs;
@@ -218,8 +213,8 @@ private:
static int s_nodeCount[NodeTypeEnd];
#endif
- virtual void refEventTarget() override { ref(); }
- virtual void derefEventTarget() override { deref(); }
+ void refEventTarget() override { ref(); }
+ void derefEventTarget() override { deref(); }
protected:
unsigned m_channelCount;
@@ -228,5 +223,3 @@ protected:
};
} // namespace WebCore
-
-#endif // AudioNode_h
diff --git a/Source/WebCore/Modules/webaudio/AudioNode.idl b/Source/WebCore/Modules/webaudio/AudioNode.idl
index 9167e5789..2ba0500c7 100644
--- a/Source/WebCore/Modules/webaudio/AudioNode.idl
+++ b/Source/WebCore/Modules/webaudio/AudioNode.idl
@@ -24,30 +24,17 @@
[
Conditional=WEB_AUDIO,
- JSGenerateToJSObject,
- JSGenerateToNativeObject,
GenerateIsReachable=Impl,
- EventTarget,
] interface AudioNode : EventTarget {
readonly attribute AudioContext context;
readonly attribute unsigned long numberOfInputs;
readonly attribute unsigned long numberOfOutputs;
- [SetterRaisesException] attribute unsigned long channelCount;
+ [SetterMayThrowException] attribute unsigned long channelCount;
+ [SetterMayThrowException] attribute DOMString channelCountMode;
+ [SetterMayThrowException] attribute DOMString channelInterpretation;
- [SetterRaisesException] attribute DOMString channelCountMode;
-
- [SetterRaisesException] attribute DOMString channelInterpretation;
-
- [RaisesException] void connect(AudioNode? destination, [Default=Undefined] optional unsigned long output, [Default=Undefined] optional unsigned long input);
-
- [RaisesException] void connect(AudioParam? destination, [Default=Undefined] optional unsigned long output);
-
- [RaisesException] void disconnect([Default=Undefined] optional unsigned long output);
-
- void addEventListener(DOMString type, EventListener listener, optional boolean useCapture);
-
- void removeEventListener(DOMString type, EventListener listener, optional boolean useCapture);
-
- [RaisesException] boolean dispatchEvent(Event event);
+ [MayThrowException] void connect(AudioNode destination, optional unsigned long output = 0, optional unsigned long input = 0);
+ [MayThrowException] void connect(AudioParam destination, optional unsigned long output = 0);
+ [MayThrowException] void disconnect(optional unsigned long output = 0);
};
diff --git a/Source/WebCore/Modules/webaudio/AudioNodeInput.cpp b/Source/WebCore/Modules/webaudio/AudioNodeInput.cpp
index c62333d2b..ccaa51d82 100644
--- a/Source/WebCore/Modules/webaudio/AudioNodeInput.cpp
+++ b/Source/WebCore/Modules/webaudio/AudioNodeInput.cpp
@@ -45,7 +45,7 @@ AudioNodeInput::AudioNodeInput(AudioNode* node)
void AudioNodeInput::connect(AudioNodeOutput* output)
{
- ASSERT(context()->isGraphOwner());
+ ASSERT(context().isGraphOwner());
ASSERT(output && node());
if (!output || !node())
@@ -64,7 +64,7 @@ void AudioNodeInput::connect(AudioNodeOutput* output)
void AudioNodeInput::disconnect(AudioNodeOutput* output)
{
- ASSERT(context()->isGraphOwner());
+ ASSERT(context().isGraphOwner());
ASSERT(output && node());
if (!output || !node())
@@ -90,7 +90,7 @@ void AudioNodeInput::disconnect(AudioNodeOutput* output)
void AudioNodeInput::disable(AudioNodeOutput* output)
{
- ASSERT(context()->isGraphOwner());
+ ASSERT(context().isGraphOwner());
ASSERT(output && node());
if (!output || !node())
@@ -108,7 +108,7 @@ void AudioNodeInput::disable(AudioNodeOutput* output)
void AudioNodeInput::enable(AudioNodeOutput* output)
{
- ASSERT(context()->isGraphOwner());
+ ASSERT(context().isGraphOwner());
ASSERT(output && node());
if (!output || !node())
@@ -132,7 +132,7 @@ void AudioNodeInput::didUpdate()
void AudioNodeInput::updateInternalBus()
{
- ASSERT(context()->isAudioThread() && context()->isGraphOwner());
+ ASSERT(context().isAudioThread() && context().isGraphOwner());
unsigned numberOfInputChannels = numberOfChannels();
@@ -151,8 +151,7 @@ unsigned AudioNodeInput::numberOfChannels() const
// Find the number of channels of the connection with the largest number of channels.
unsigned maxChannels = 1; // one channel is the minimum allowed
- for (HashSet<AudioNodeOutput*>::iterator i = m_outputs.begin(); i != m_outputs.end(); ++i) {
- AudioNodeOutput* output = *i;
+ for (auto& output : m_outputs) {
// Use output()->numberOfChannels() instead of output->bus()->numberOfChannels(),
// because the calling of AudioNodeOutput::bus() is not safe here.
maxChannels = std::max(maxChannels, output->numberOfChannels());
@@ -166,7 +165,7 @@ unsigned AudioNodeInput::numberOfChannels() const
AudioBus* AudioNodeInput::bus()
{
- ASSERT(context()->isAudioThread());
+ ASSERT(context().isAudioThread());
// Handle single connection specially to allow for in-place processing.
if (numberOfRenderingConnections() == 1 && node()->internalChannelCountMode() == AudioNode::Max)
@@ -178,14 +177,14 @@ AudioBus* AudioNodeInput::bus()
AudioBus* AudioNodeInput::internalSummingBus()
{
- ASSERT(context()->isAudioThread());
+ ASSERT(context().isAudioThread());
return m_internalSummingBus.get();
}
void AudioNodeInput::sumAllConnections(AudioBus* summingBus, size_t framesToProcess)
{
- ASSERT(context()->isAudioThread());
+ ASSERT(context().isAudioThread());
// We shouldn't be calling this method if there's only one connection, since it's less efficient.
ASSERT(numberOfRenderingConnections() > 1 || node()->internalChannelCountMode() != AudioNode::Max);
@@ -198,8 +197,7 @@ void AudioNodeInput::sumAllConnections(AudioBus* summingBus, size_t framesToProc
AudioBus::ChannelInterpretation interpretation = node()->internalChannelInterpretation();
- for (unsigned i = 0; i < numberOfRenderingConnections(); ++i) {
- AudioNodeOutput* output = renderingOutput(i);
+ for (auto& output : m_renderingOutputs) {
ASSERT(output);
// Render audio from this output.
@@ -212,7 +210,7 @@ void AudioNodeInput::sumAllConnections(AudioBus* summingBus, size_t framesToProc
AudioBus* AudioNodeInput::pull(AudioBus* inPlaceBus, size_t framesToProcess)
{
- ASSERT(context()->isAudioThread());
+ ASSERT(context().isAudioThread());
// Handle single connection case.
if (numberOfRenderingConnections() == 1 && node()->internalChannelCountMode() == AudioNode::Max) {
diff --git a/Source/WebCore/Modules/webaudio/AudioNodeInput.h b/Source/WebCore/Modules/webaudio/AudioNodeInput.h
index 5afcded37..3e8cc40ed 100644
--- a/Source/WebCore/Modules/webaudio/AudioNodeInput.h
+++ b/Source/WebCore/Modules/webaudio/AudioNodeInput.h
@@ -22,14 +22,12 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AudioNodeInput_h
-#define AudioNodeInput_h
+#pragma once
#include "AudioBus.h"
#include "AudioNode.h"
#include "AudioSummingJunction.h"
#include <wtf/HashSet.h>
-#include <wtf/Vector.h>
namespace WebCore {
@@ -45,8 +43,8 @@ public:
explicit AudioNodeInput(AudioNode*);
// AudioSummingJunction
- virtual bool canUpdateState() override { return !node()->isMarkedForDeletion(); }
- virtual void didUpdate() override;
+ bool canUpdateState() override { return !node()->isMarkedForDeletion(); }
+ void didUpdate() override;
// Can be called from any thread.
AudioNode* node() const { return m_node; }
@@ -95,5 +93,3 @@ private:
};
} // namespace WebCore
-
-#endif // AudioNodeInput_h
diff --git a/Source/WebCore/Modules/webaudio/AudioNodeOutput.cpp b/Source/WebCore/Modules/webaudio/AudioNodeOutput.cpp
index f9a4516f9..b810c27f4 100644
--- a/Source/WebCore/Modules/webaudio/AudioNodeOutput.cpp
+++ b/Source/WebCore/Modules/webaudio/AudioNodeOutput.cpp
@@ -53,16 +53,16 @@ AudioNodeOutput::AudioNodeOutput(AudioNode* node, unsigned numberOfChannels)
void AudioNodeOutput::setNumberOfChannels(unsigned numberOfChannels)
{
ASSERT(numberOfChannels <= AudioContext::maxNumberOfChannels());
- ASSERT(context()->isGraphOwner());
+ ASSERT(context().isGraphOwner());
m_desiredNumberOfChannels = numberOfChannels;
- if (context()->isAudioThread()) {
+ if (context().isAudioThread()) {
// If we're in the audio thread then we can take care of it right away (we should be at the very start or end of a rendering quantum).
updateNumberOfChannels();
} else {
// Let the context take care of it in the audio thread in the pre and post render tasks.
- context()->markAudioNodeOutputDirty(this);
+ context().markAudioNodeOutputDirty(this);
}
}
@@ -83,7 +83,7 @@ void AudioNodeOutput::updateRenderingState()
void AudioNodeOutput::updateNumberOfChannels()
{
- ASSERT(context()->isAudioThread() && context()->isGraphOwner());
+ ASSERT(context().isAudioThread() && context().isGraphOwner());
if (m_numberOfChannels != m_desiredNumberOfChannels) {
m_numberOfChannels = m_desiredNumberOfChannels;
@@ -94,12 +94,11 @@ void AudioNodeOutput::updateNumberOfChannels()
void AudioNodeOutput::propagateChannelCount()
{
- ASSERT(context()->isAudioThread() && context()->isGraphOwner());
+ ASSERT(context().isAudioThread() && context().isGraphOwner());
if (isChannelCountKnown()) {
// Announce to any nodes we're connected to that we changed our channel count for its input.
- for (InputsIterator i = m_inputs.begin(); i != m_inputs.end(); ++i) {
- AudioNodeInput* input = *i;
+ for (auto& input : m_inputs) {
AudioNode* connectionNode = input->node();
connectionNode->checkNumberOfChannelsForInput(input);
}
@@ -108,7 +107,7 @@ void AudioNodeOutput::propagateChannelCount()
AudioBus* AudioNodeOutput::pull(AudioBus* inPlaceBus, size_t framesToProcess)
{
- ASSERT(context()->isAudioThread());
+ ASSERT(context().isAudioThread());
ASSERT(m_renderingFanOutCount > 0 || m_renderingParamFanOutCount > 0);
// Causes our AudioNode to process if it hasn't already for this render quantum.
@@ -127,19 +126,19 @@ AudioBus* AudioNodeOutput::pull(AudioBus* inPlaceBus, size_t framesToProcess)
AudioBus* AudioNodeOutput::bus() const
{
- ASSERT(const_cast<AudioNodeOutput*>(this)->context()->isAudioThread());
+ ASSERT(const_cast<AudioNodeOutput*>(this)->context().isAudioThread());
return m_isInPlace ? m_inPlaceBus.get() : m_internalBus.get();
}
unsigned AudioNodeOutput::fanOutCount()
{
- ASSERT(context()->isGraphOwner());
+ ASSERT(context().isGraphOwner());
return m_inputs.size();
}
unsigned AudioNodeOutput::paramFanOutCount()
{
- ASSERT(context()->isGraphOwner());
+ ASSERT(context().isGraphOwner());
return m_params.size();
}
@@ -155,7 +154,7 @@ unsigned AudioNodeOutput::renderingParamFanOutCount() const
void AudioNodeOutput::addInput(AudioNodeInput* input)
{
- ASSERT(context()->isGraphOwner());
+ ASSERT(context().isGraphOwner());
ASSERT(input);
if (!input)
@@ -166,7 +165,7 @@ void AudioNodeOutput::addInput(AudioNodeInput* input)
void AudioNodeOutput::removeInput(AudioNodeInput* input)
{
- ASSERT(context()->isGraphOwner());
+ ASSERT(context().isGraphOwner());
ASSERT(input);
if (!input)
@@ -177,7 +176,7 @@ void AudioNodeOutput::removeInput(AudioNodeInput* input)
void AudioNodeOutput::disconnectAllInputs()
{
- ASSERT(context()->isGraphOwner());
+ ASSERT(context().isGraphOwner());
// AudioNodeInput::disconnect() changes m_inputs by calling removeInput().
while (!m_inputs.isEmpty()) {
@@ -188,7 +187,7 @@ void AudioNodeOutput::disconnectAllInputs()
void AudioNodeOutput::addParam(AudioParam* param)
{
- ASSERT(context()->isGraphOwner());
+ ASSERT(context().isGraphOwner());
ASSERT(param);
if (!param)
@@ -199,7 +198,7 @@ void AudioNodeOutput::addParam(AudioParam* param)
void AudioNodeOutput::removeParam(AudioParam* param)
{
- ASSERT(context()->isGraphOwner());
+ ASSERT(context().isGraphOwner());
ASSERT(param);
if (!param)
@@ -210,7 +209,7 @@ void AudioNodeOutput::removeParam(AudioParam* param)
void AudioNodeOutput::disconnectAllParams()
{
- ASSERT(context()->isGraphOwner());
+ ASSERT(context().isGraphOwner());
// AudioParam::disconnect() changes m_params by calling removeParam().
while (!m_params.isEmpty()) {
@@ -227,26 +226,22 @@ void AudioNodeOutput::disconnectAll()
void AudioNodeOutput::disable()
{
- ASSERT(context()->isGraphOwner());
+ ASSERT(context().isGraphOwner());
if (m_isEnabled) {
- for (InputsIterator i = m_inputs.begin(); i != m_inputs.end(); ++i) {
- AudioNodeInput* input = *i;
+ for (auto& input : m_inputs)
input->disable(this);
- }
m_isEnabled = false;
}
}
void AudioNodeOutput::enable()
{
- ASSERT(context()->isGraphOwner());
+ ASSERT(context().isGraphOwner());
if (!m_isEnabled) {
- for (InputsIterator i = m_inputs.begin(); i != m_inputs.end(); ++i) {
- AudioNodeInput* input = *i;
+ for (auto& input : m_inputs)
input->enable(this);
- }
m_isEnabled = true;
}
}
diff --git a/Source/WebCore/Modules/webaudio/AudioNodeOutput.h b/Source/WebCore/Modules/webaudio/AudioNodeOutput.h
index ece31e4ef..e88a730ef 100644
--- a/Source/WebCore/Modules/webaudio/AudioNodeOutput.h
+++ b/Source/WebCore/Modules/webaudio/AudioNodeOutput.h
@@ -22,15 +22,13 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AudioNodeOutput_h
-#define AudioNodeOutput_h
+#pragma once
#include "AudioBus.h"
#include "AudioNode.h"
#include "AudioParam.h"
#include <wtf/HashSet.h>
#include <wtf/RefPtr.h>
-#include <wtf/Vector.h>
namespace WebCore {
@@ -47,7 +45,7 @@ public:
// Can be called from any thread.
AudioNode* node() const { return m_node; }
- AudioContext* context() { return m_node->context(); }
+ AudioContext& context() { return m_node->context(); }
// Causes our AudioNode to process if it hasn't already for this render quantum.
// It returns the bus containing the processed audio for this output, returning inPlaceBus if in-place processing was possible.
@@ -149,5 +147,3 @@ private:
};
} // namespace WebCore
-
-#endif // AudioNodeOutput_h
diff --git a/Source/WebCore/Modules/webaudio/AudioParam.cpp b/Source/WebCore/Modules/webaudio/AudioParam.cpp
index 3e4899f50..0ff0e2043 100644
--- a/Source/WebCore/Modules/webaudio/AudioParam.cpp
+++ b/Source/WebCore/Modules/webaudio/AudioParam.cpp
@@ -43,7 +43,7 @@ const double AudioParam::SnapThreshold = 0.001;
float AudioParam::value()
{
// Update value for timeline.
- if (context() && context()->isAudioThread()) {
+ if (context().isAudioThread()) {
bool hasValue;
float timelineValue = m_timeline.valueForContextTime(context(), narrowPrecisionToFloat(m_value), hasValue);
@@ -72,9 +72,8 @@ bool AudioParam::smooth()
// If values have been explicitly scheduled on the timeline, then use the exact value.
// Smoothing effectively is performed by the timeline.
bool useTimelineValue = false;
- if (context())
- m_value = m_timeline.valueForContextTime(context(), narrowPrecisionToFloat(m_value), useTimelineValue);
-
+ m_value = m_timeline.valueForContextTime(context(), narrowPrecisionToFloat(m_value), useTimelineValue);
+
if (m_smoothedValue == m_value) {
// Smoothed value has already approached and snapped to value.
return true;
@@ -103,7 +102,7 @@ float AudioParam::finalValue()
void AudioParam::calculateSampleAccurateValues(float* values, unsigned numberOfValues)
{
- bool isSafe = context() && context()->isAudioThread() && values && numberOfValues;
+ bool isSafe = context().isAudioThread() && values && numberOfValues;
ASSERT(isSafe);
if (!isSafe)
return;
@@ -113,7 +112,7 @@ void AudioParam::calculateSampleAccurateValues(float* values, unsigned numberOfV
void AudioParam::calculateFinalValues(float* values, unsigned numberOfValues, bool sampleAccurate)
{
- bool isGood = context() && context()->isAudioThread() && values && numberOfValues;
+ bool isGood = context().isAudioThread() && values && numberOfValues;
ASSERT(isGood);
if (!isGood)
return;
@@ -139,8 +138,7 @@ void AudioParam::calculateFinalValues(float* values, unsigned numberOfValues, bo
RefPtr<AudioBus> summingBus = AudioBus::create(1, numberOfValues, false);
summingBus->setChannelMemory(0, values, numberOfValues);
- for (unsigned i = 0; i < numberOfRenderingConnections(); ++i) {
- AudioNodeOutput* output = renderingOutput(i);
+ for (auto& output : m_renderingOutputs) {
ASSERT(output);
// Render audio from this output.
@@ -155,8 +153,8 @@ void AudioParam::calculateTimelineValues(float* values, unsigned numberOfValues)
{
// Calculate values for this render quantum.
// Normally numberOfValues will equal AudioNode::ProcessingSizeInFrames (the render quantum size).
- double sampleRate = context()->sampleRate();
- double startTime = context()->currentTime();
+ double sampleRate = context().sampleRate();
+ double startTime = context().currentTime();
double endTime = startTime + numberOfValues / sampleRate;
// Note we're running control rate at the sample-rate.
@@ -166,7 +164,7 @@ void AudioParam::calculateTimelineValues(float* values, unsigned numberOfValues)
void AudioParam::connect(AudioNodeOutput* output)
{
- ASSERT(context()->isGraphOwner());
+ ASSERT(context().isGraphOwner());
ASSERT(output);
if (!output)
@@ -181,7 +179,7 @@ void AudioParam::connect(AudioNodeOutput* output)
void AudioParam::disconnect(AudioNodeOutput* output)
{
- ASSERT(context()->isGraphOwner());
+ ASSERT(context().isGraphOwner());
ASSERT(output);
if (!output)
diff --git a/Source/WebCore/Modules/webaudio/AudioParam.h b/Source/WebCore/Modules/webaudio/AudioParam.h
index f8c79340d..b9ee132d4 100644
--- a/Source/WebCore/Modules/webaudio/AudioParam.h
+++ b/Source/WebCore/Modules/webaudio/AudioParam.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,15 +26,13 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AudioParam_h
-#define AudioParam_h
+#pragma once
#include "AudioContext.h"
#include "AudioParamTimeline.h"
#include "AudioSummingJunction.h"
#include <runtime/Float32Array.h>
#include <sys/types.h>
-#include <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>
#include <wtf/text/WTFString.h>
@@ -47,14 +45,14 @@ public:
static const double DefaultSmoothingConstant;
static const double SnapThreshold;
- static PassRefPtr<AudioParam> create(AudioContext* context, const String& name, double defaultValue, double minValue, double maxValue, unsigned units = 0)
+ static Ref<AudioParam> create(AudioContext& context, const String& name, double defaultValue, double minValue, double maxValue, unsigned units = 0)
{
- return adoptRef(new AudioParam(context, name, defaultValue, minValue, maxValue, units));
+ return adoptRef(*new AudioParam(context, name, defaultValue, minValue, maxValue, units));
}
// AudioSummingJunction
- virtual bool canUpdateState() override { return true; }
- virtual void didUpdate() override { }
+ bool canUpdateState() override { return true; }
+ void didUpdate() override { }
// Intrinsic value.
float value();
@@ -89,7 +87,7 @@ public:
void linearRampToValueAtTime(float value, float time) { m_timeline.linearRampToValueAtTime(value, time); }
void exponentialRampToValueAtTime(float value, float time) { m_timeline.exponentialRampToValueAtTime(value, time); }
void setTargetAtTime(float target, float time, float timeConstant) { m_timeline.setTargetAtTime(target, time, timeConstant); }
- void setValueCurveAtTime(Float32Array* curve, float time, float duration) { m_timeline.setValueCurveAtTime(curve, time, duration); }
+ void setValueCurveAtTime(const RefPtr<Float32Array>& curve, float time, float duration) { m_timeline.setValueCurveAtTime(curve.get(), time, duration); }
void cancelScheduledValues(float startTime) { m_timeline.cancelScheduledValues(startTime); }
bool hasSampleAccurateValues() { return m_timeline.hasValues() || numberOfRenderingConnections(); }
@@ -103,7 +101,7 @@ public:
void disconnect(AudioNodeOutput*);
protected:
- AudioParam(AudioContext* context, const String& name, double defaultValue, double minValue, double maxValue, unsigned units = 0)
+ AudioParam(AudioContext& context, const String& name, double defaultValue, double minValue, double maxValue, unsigned units = 0)
: AudioSummingJunction(context)
, m_name(name)
, m_value(defaultValue)
@@ -136,5 +134,3 @@ private:
};
} // namespace WebCore
-
-#endif // AudioParam_h
diff --git a/Source/WebCore/Modules/webaudio/AudioParam.idl b/Source/WebCore/Modules/webaudio/AudioParam.idl
index 605462b46..cda277762 100644
--- a/Source/WebCore/Modules/webaudio/AudioParam.idl
+++ b/Source/WebCore/Modules/webaudio/AudioParam.idl
@@ -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.
*
@@ -29,10 +29,10 @@
[
Conditional=WEB_AUDIO,
] interface AudioParam {
- attribute float value;
- readonly attribute float minValue;
- readonly attribute float maxValue;
- readonly attribute float defaultValue;
+ attribute unrestricted float value;
+ readonly attribute unrestricted float minValue;
+ readonly attribute unrestricted float maxValue;
+ readonly attribute unrestricted float defaultValue;
readonly attribute DOMString name;
@@ -40,20 +40,17 @@
readonly attribute unsigned short units;
// Parameter automation.
- void setValueAtTime(float value, float time);
- void linearRampToValueAtTime(float value, float time);
- void exponentialRampToValueAtTime(float value, float time);
+ void setValueAtTime(unrestricted float value, unrestricted float time);
+ void linearRampToValueAtTime(unrestricted float value, unrestricted float time);
+ void exponentialRampToValueAtTime(unrestricted float value, unrestricted float time);
// Exponentially approach the target with a rate having the given time constant.
- void setTargetAtTime(float target, float time, float timeConstant);
+ void setTargetAtTime(unrestricted float target, unrestricted float time, unrestricted float timeConstant);
// Sets an array of arbitrary parameter values starting at time for the given duration.
// The number of values will be scaled to fit into the desired duration.
- void setValueCurveAtTime(Float32Array values, float time, float duration);
+ void setValueCurveAtTime(Float32Array? values, unrestricted float time, unrestricted float duration); // FIXME: values should not be nullable.
// Cancels all scheduled parameter changes with times greater than or equal to startTime.
- void cancelScheduledValues(float startTime);
-
- [Conditional=LEGACY_WEB_AUDIO, ImplementedAs=setTargetAtTime] void setTargetValueAtTime(float targetValue, float time, float timeConstant);
-
+ void cancelScheduledValues(unrestricted float startTime);
};
diff --git a/Source/WebCore/Modules/webaudio/AudioParamTimeline.cpp b/Source/WebCore/Modules/webaudio/AudioParamTimeline.cpp
index 1c7e4e5f8..fdc1d3c7c 100644
--- a/Source/WebCore/Modules/webaudio/AudioParamTimeline.cpp
+++ b/Source/WebCore/Modules/webaudio/AudioParamTimeline.cpp
@@ -80,19 +80,21 @@ void AudioParamTimeline::insertEvent(const ParamEvent& event)
if (!isValid)
return;
- std::lock_guard<std::mutex> lock(m_eventsMutex);
+ std::lock_guard<Lock> lock(m_eventsMutex);
unsigned i = 0;
float insertTime = event.time();
- for (i = 0; i < m_events.size(); ++i) {
+ for (auto& paramEvent : m_events) {
// Overwrite same event type and time.
- if (m_events[i].time() == insertTime && m_events[i].type() == event.type()) {
- m_events[i] = event;
+ if (paramEvent.time() == insertTime && paramEvent.type() == event.type()) {
+ paramEvent = event;
return;
}
- if (m_events[i].time() > insertTime)
+ if (paramEvent.time() > insertTime)
break;
+
+ ++i;
}
m_events.insert(i, event);
@@ -100,7 +102,7 @@ void AudioParamTimeline::insertEvent(const ParamEvent& event)
void AudioParamTimeline::cancelScheduledValues(float startTime)
{
- std::lock_guard<std::mutex> lock(m_eventsMutex);
+ std::lock_guard<Lock> lock(m_eventsMutex);
// Remove all events starting at startTime.
for (unsigned i = 0; i < m_events.size(); ++i) {
@@ -111,13 +113,11 @@ void AudioParamTimeline::cancelScheduledValues(float startTime)
}
}
-float AudioParamTimeline::valueForContextTime(AudioContext* context, float defaultValue, bool& hasValue)
+float AudioParamTimeline::valueForContextTime(AudioContext& context, float defaultValue, bool& hasValue)
{
- ASSERT(context);
-
{
- std::unique_lock<std::mutex> lock(m_eventsMutex, std::try_to_lock);
- if (!lock.owns_lock() || !context || !m_events.size() || context->currentTime() < m_events[0].time()) {
+ std::unique_lock<Lock> lock(m_eventsMutex, std::try_to_lock);
+ if (!lock.owns_lock() || !m_events.size() || context.currentTime() < m_events[0].time()) {
hasValue = false;
return defaultValue;
}
@@ -125,8 +125,8 @@ float AudioParamTimeline::valueForContextTime(AudioContext* context, float defau
// Ask for just a single value.
float value;
- double sampleRate = context->sampleRate();
- double startTime = context->currentTime();
+ double sampleRate = context.sampleRate();
+ double startTime = context.currentTime();
double endTime = startTime + 1.1 / sampleRate; // time just beyond one sample-frame
double controlRate = sampleRate / AudioNode::ProcessingSizeInFrames; // one parameter change per render quantum
value = valuesForTimeRange(startTime, endTime, defaultValue, &value, 1, sampleRate, controlRate);
@@ -138,7 +138,7 @@ float AudioParamTimeline::valueForContextTime(AudioContext* context, float defau
float AudioParamTimeline::valuesForTimeRange(double startTime, double endTime, float defaultValue, float* values, unsigned numberOfValues, double sampleRate, double controlRate)
{
// We can't contend the lock in the realtime audio thread.
- std::unique_lock<std::mutex> lock(m_eventsMutex, std::try_to_lock);
+ std::unique_lock<Lock> lock(m_eventsMutex, std::try_to_lock);
if (!lock.owns_lock()) {
if (values) {
for (unsigned i = 0; i < numberOfValues; ++i)
diff --git a/Source/WebCore/Modules/webaudio/AudioParamTimeline.h b/Source/WebCore/Modules/webaudio/AudioParamTimeline.h
index 4e9f0a4a3..148391731 100644
--- a/Source/WebCore/Modules/webaudio/AudioParamTimeline.h
+++ b/Source/WebCore/Modules/webaudio/AudioParamTimeline.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,13 +26,11 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AudioParamTimeline_h
-#define AudioParamTimeline_h
+#pragma once
#include "AudioContext.h"
-#include <mutex>
#include <runtime/Float32Array.h>
-#include <wtf/PassRefPtr.h>
+#include <wtf/Lock.h>
#include <wtf/RefCounted.h>
#include <wtf/Vector.h>
@@ -53,7 +51,7 @@ public:
// hasValue is set to true if a valid timeline value is returned.
// otherwise defaultValue is returned.
- float valueForContextTime(AudioContext*, float defaultValue, bool& hasValue);
+ float valueForContextTime(AudioContext&, float defaultValue, bool& hasValue);
// Given the time range, calculates parameter values into the values buffer
// and returns the last parameter value calculated for "values" or the defaultValue if none were calculated.
@@ -76,13 +74,13 @@ private:
LastType
};
- ParamEvent(Type type, float value, float time, float timeConstant, float duration, PassRefPtr<Float32Array> curve)
+ ParamEvent(Type type, float value, float time, float timeConstant, float duration, RefPtr<Float32Array>&& curve)
: m_type(type)
, m_value(value)
, m_time(time)
, m_timeConstant(timeConstant)
, m_duration(duration)
- , m_curve(curve)
+ , m_curve(WTFMove(curve))
{
}
@@ -107,9 +105,7 @@ private:
Vector<ParamEvent> m_events;
- std::mutex m_eventsMutex;
+ Lock m_eventsMutex;
};
} // namespace WebCore
-
-#endif // AudioParamTimeline_h
diff --git a/Source/WebCore/Modules/webaudio/AudioProcessingEvent.cpp b/Source/WebCore/Modules/webaudio/AudioProcessingEvent.cpp
index 2df9b1c9f..257250710 100644
--- a/Source/WebCore/Modules/webaudio/AudioProcessingEvent.cpp
+++ b/Source/WebCore/Modules/webaudio/AudioProcessingEvent.cpp
@@ -33,24 +33,15 @@
namespace WebCore {
-PassRefPtr<AudioProcessingEvent> AudioProcessingEvent::create()
-{
- return adoptRef(new AudioProcessingEvent);
-}
-
-PassRefPtr<AudioProcessingEvent> AudioProcessingEvent::create(PassRefPtr<AudioBuffer> inputBuffer, PassRefPtr<AudioBuffer> outputBuffer)
-{
- return adoptRef(new AudioProcessingEvent(inputBuffer, outputBuffer));
-}
-
AudioProcessingEvent::AudioProcessingEvent()
{
}
-AudioProcessingEvent::AudioProcessingEvent(PassRefPtr<AudioBuffer> inputBuffer, PassRefPtr<AudioBuffer> outputBuffer)
+AudioProcessingEvent::AudioProcessingEvent(RefPtr<AudioBuffer>&& inputBuffer, RefPtr<AudioBuffer>&& outputBuffer, double playbackTime)
: Event(eventNames().audioprocessEvent, true, false)
- , m_inputBuffer(inputBuffer)
- , m_outputBuffer(outputBuffer)
+ , m_inputBuffer(WTFMove(inputBuffer))
+ , m_outputBuffer(WTFMove(outputBuffer))
+ , m_playbackTime(playbackTime)
{
}
diff --git a/Source/WebCore/Modules/webaudio/AudioProcessingEvent.h b/Source/WebCore/Modules/webaudio/AudioProcessingEvent.h
index 4b1f9eba8..cc8cce3aa 100644
--- a/Source/WebCore/Modules/webaudio/AudioProcessingEvent.h
+++ b/Source/WebCore/Modules/webaudio/AudioProcessingEvent.h
@@ -22,12 +22,10 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AudioProcessingEvent_h
-#define AudioProcessingEvent_h
+#pragma once
#include "AudioBuffer.h"
#include "Event.h"
-#include <wtf/PassRefPtr.h>
#include <wtf/RefPtr.h>
namespace WebCore {
@@ -36,24 +34,31 @@ class AudioBuffer;
class AudioProcessingEvent : public Event {
public:
- static PassRefPtr<AudioProcessingEvent> create();
- static PassRefPtr<AudioProcessingEvent> create(PassRefPtr<AudioBuffer> inputBuffer, PassRefPtr<AudioBuffer> outputBuffer);
+ static Ref<AudioProcessingEvent> create(RefPtr<AudioBuffer>&& inputBuffer, RefPtr<AudioBuffer>&& outputBuffer, double playbackTime)
+ {
+ return adoptRef(*new AudioProcessingEvent(WTFMove(inputBuffer), WTFMove(outputBuffer), playbackTime));
+ }
+
+ static Ref<AudioProcessingEvent> createForBindings()
+ {
+ return adoptRef(*new AudioProcessingEvent);
+ }
virtual ~AudioProcessingEvent();
AudioBuffer* inputBuffer() { return m_inputBuffer.get(); }
AudioBuffer* outputBuffer() { return m_outputBuffer.get(); }
+ double playbackTime() const { return m_playbackTime; }
- virtual EventInterface eventInterface() const override;
+ EventInterface eventInterface() const override;
private:
AudioProcessingEvent();
- AudioProcessingEvent(PassRefPtr<AudioBuffer> inputBuffer, PassRefPtr<AudioBuffer> outputBuffer);
+ AudioProcessingEvent(RefPtr<AudioBuffer>&& inputBuffer, RefPtr<AudioBuffer>&& outputBuffer, double playbackTime);
RefPtr<AudioBuffer> m_inputBuffer;
RefPtr<AudioBuffer> m_outputBuffer;
+ double m_playbackTime;
};
} // namespace WebCore
-
-#endif // AudioProcessingEvent_h
diff --git a/Source/WebCore/Modules/webaudio/AudioProcessingEvent.idl b/Source/WebCore/Modules/webaudio/AudioProcessingEvent.idl
index 5b498d2eb..d46b138c3 100644
--- a/Source/WebCore/Modules/webaudio/AudioProcessingEvent.idl
+++ b/Source/WebCore/Modules/webaudio/AudioProcessingEvent.idl
@@ -26,6 +26,7 @@
Conditional=WEB_AUDIO,
JSGenerateToJSObject
] interface AudioProcessingEvent : Event {
+ readonly attribute unrestricted double playbackTime;
readonly attribute AudioBuffer inputBuffer;
readonly attribute AudioBuffer outputBuffer;
};
diff --git a/Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.cpp b/Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.cpp
index 0dc2a829a..e2cfc9009 100644
--- a/Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.cpp
+++ b/Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.cpp
@@ -31,6 +31,7 @@
#include "AudioContext.h"
#include "AudioUtilities.h"
#include "Event.h"
+#include "EventNames.h"
#include "ScriptController.h"
#include <algorithm>
#include <wtf/MathExtras.h>
@@ -43,24 +44,14 @@ namespace WebCore {
const double AudioScheduledSourceNode::UnknownTime = -1;
-AudioScheduledSourceNode::AudioScheduledSourceNode(AudioContext* context, float sampleRate)
+AudioScheduledSourceNode::AudioScheduledSourceNode(AudioContext& context, float sampleRate)
: AudioNode(context, sampleRate)
- , m_playbackState(UNSCHEDULED_STATE)
- , m_startTime(0)
, m_endTime(UnknownTime)
- , m_hasEndedListener(false)
{
}
-void AudioScheduledSourceNode::updateSchedulingInfo(size_t quantumFrameSize,
- AudioBus* outputBus,
- size_t& quantumFrameOffset,
- size_t& nonSilentFramesToProcess)
+void AudioScheduledSourceNode::updateSchedulingInfo(size_t quantumFrameSize, AudioBus& outputBus, size_t& quantumFrameOffset, size_t& nonSilentFramesToProcess)
{
- ASSERT(outputBus);
- if (!outputBus)
- return;
-
ASSERT(quantumFrameSize == AudioNode::ProcessingSizeInFrames);
if (quantumFrameSize != AudioNode::ProcessingSizeInFrames)
return;
@@ -71,7 +62,7 @@ void AudioScheduledSourceNode::updateSchedulingInfo(size_t quantumFrameSize,
// quantumEndFrame : End frame of the current time quantum.
// startFrame : Start frame for this source.
// endFrame : End frame for this source.
- size_t quantumStartFrame = context()->currentSampleFrame();
+ size_t quantumStartFrame = context().currentSampleFrame();
size_t quantumEndFrame = quantumStartFrame + quantumFrameSize;
size_t startFrame = AudioUtilities::timeToSampleFrame(m_startTime, sampleRate);
size_t endFrame = m_endTime == UnknownTime ? 0 : AudioUtilities::timeToSampleFrame(m_endTime, sampleRate);
@@ -82,7 +73,7 @@ void AudioScheduledSourceNode::updateSchedulingInfo(size_t quantumFrameSize,
if (m_playbackState == UNSCHEDULED_STATE || m_playbackState == FINISHED_STATE || startFrame >= quantumEndFrame) {
// Output silence.
- outputBus->zero();
+ outputBus.zero();
nonSilentFramesToProcess = 0;
return;
}
@@ -91,7 +82,7 @@ void AudioScheduledSourceNode::updateSchedulingInfo(size_t quantumFrameSize,
if (m_playbackState == SCHEDULED_STATE) {
// Increment the active source count only if we're transitioning from SCHEDULED_STATE to PLAYING_STATE.
m_playbackState = PLAYING_STATE;
- context()->incrementActiveSourceCount();
+ context().incrementActiveSourceCount();
}
quantumFrameOffset = startFrame > quantumStartFrame ? startFrame - quantumStartFrame : 0;
@@ -100,15 +91,15 @@ void AudioScheduledSourceNode::updateSchedulingInfo(size_t quantumFrameSize,
if (!nonSilentFramesToProcess) {
// Output silence.
- outputBus->zero();
+ outputBus.zero();
return;
}
// Handle silence before we start playing.
// Zero any initial frames representing silence leading up to a rendering start time in the middle of the quantum.
if (quantumFrameOffset) {
- for (unsigned i = 0; i < outputBus->numberOfChannels(); ++i)
- memset(outputBus->channel(i)->mutableData(), 0, sizeof(float) * quantumFrameOffset);
+ for (unsigned i = 0; i < outputBus.numberOfChannels(); ++i)
+ memset(outputBus.channel(i)->mutableData(), 0, sizeof(float) * quantumFrameOffset);
}
// Handle silence after we're done playing.
@@ -127,89 +118,81 @@ void AudioScheduledSourceNode::updateSchedulingInfo(size_t quantumFrameSize,
else
nonSilentFramesToProcess -= framesToZero;
- for (unsigned i = 0; i < outputBus->numberOfChannels(); ++i)
- memset(outputBus->channel(i)->mutableData() + zeroStartFrame, 0, sizeof(float) * framesToZero);
+ for (unsigned i = 0; i < outputBus.numberOfChannels(); ++i)
+ memset(outputBus.channel(i)->mutableData() + zeroStartFrame, 0, sizeof(float) * framesToZero);
}
finish();
}
-
- return;
}
-void AudioScheduledSourceNode::start(double when, ExceptionCode& ec)
+ExceptionOr<void> AudioScheduledSourceNode::start(double when)
{
ASSERT(isMainThread());
- if (ScriptController::processingUserGesture())
- context()->removeBehaviorRestriction(AudioContext::RequireUserGestureForAudioStartRestriction);
+ context().nodeWillBeginPlayback();
- if (m_playbackState != UNSCHEDULED_STATE) {
- ec = INVALID_STATE_ERR;
- return;
- }
+ if (m_playbackState != UNSCHEDULED_STATE)
+ return Exception { INVALID_STATE_ERR };
+ if (!std::isfinite(when) || when < 0)
+ return Exception { INVALID_STATE_ERR };
m_startTime = when;
m_playbackState = SCHEDULED_STATE;
+
+ return { };
}
-void AudioScheduledSourceNode::stop(double when, ExceptionCode& ec)
+ExceptionOr<void> AudioScheduledSourceNode::stop(double when)
{
ASSERT(isMainThread());
- if (!(m_playbackState == SCHEDULED_STATE || m_playbackState == PLAYING_STATE) || (m_endTime != UnknownTime)) {
- ec = INVALID_STATE_ERR;
- return;
- }
-
- when = std::max<double>(0, when);
- m_endTime = when;
-}
-#if ENABLE(LEGACY_WEB_AUDIO)
-void AudioScheduledSourceNode::noteOn(double when, ExceptionCode& ec)
-{
- start(when, ec);
-}
+ if (m_playbackState == UNSCHEDULED_STATE || m_endTime != UnknownTime)
+ return Exception { INVALID_STATE_ERR };
+ if (!std::isfinite(when) || when < 0)
+ return Exception { INVALID_STATE_ERR };
-void AudioScheduledSourceNode::noteOff(double when, ExceptionCode& ec)
-{
- stop(when, ec);
-}
-#endif
+ m_endTime = when;
-void AudioScheduledSourceNode::setOnended(PassRefPtr<EventListener> listener)
-{
- m_hasEndedListener = listener;
- setAttributeEventListener(eventNames().endedEvent, listener);
+ return { };
}
void AudioScheduledSourceNode::finish()
{
if (m_playbackState != FINISHED_STATE) {
// Let the context dereference this AudioNode.
- context()->notifyNodeFinishedProcessing(this);
+ context().notifyNodeFinishedProcessing(this);
m_playbackState = FINISHED_STATE;
- context()->decrementActiveSourceCount();
+ context().decrementActiveSourceCount();
}
- if (m_hasEndedListener)
- callOnMainThread(&AudioScheduledSourceNode::notifyEndedDispatch, this);
+ if (m_hasEndedListener) {
+ callOnMainThread([strongThis = makeRef(*this)] () mutable {
+ strongThis->dispatchEvent(Event::create(eventNames().endedEvent, false, false));
+ });
+ }
}
-void AudioScheduledSourceNode::notifyEndedDispatch(void* userData)
+bool AudioScheduledSourceNode::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options)
{
- static_cast<AudioScheduledSourceNode*>(userData)->notifyEnded();
+ bool success = AudioNode::addEventListener(eventType, WTFMove(listener), options);
+ if (success && eventType == eventNames().endedEvent)
+ m_hasEndedListener = hasEventListeners(eventNames().endedEvent);
+ return success;
}
-void AudioScheduledSourceNode::notifyEnded()
+bool AudioScheduledSourceNode::removeEventListener(const AtomicString& eventType, EventListener& listener, const ListenerOptions& options)
{
- EventListener* listener = onended();
- if (!listener)
- return;
+ bool success = AudioNode::removeEventListener(eventType, listener, options);
+ if (success && eventType == eventNames().endedEvent)
+ m_hasEndedListener = hasEventListeners(eventNames().endedEvent);
+ return success;
+}
- RefPtr<Event> event = Event::create(eventNames().endedEvent, FALSE, FALSE);
- event->setTarget(this);
- listener->handleEvent(context()->scriptExecutionContext(), event.get());
+void AudioScheduledSourceNode::removeAllEventListeners()
+{
+ m_hasEndedListener = false;
+ AudioNode::removeAllEventListeners();
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.h b/Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.h
index 05bff2b37..f6a1b2c1b 100644
--- a/Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.h
+++ b/Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.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,22 +26,18 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AudioScheduledSourceNode_h
-#define AudioScheduledSourceNode_h
+#pragma once
#include "AudioNode.h"
-#include "ExceptionCode.h"
namespace WebCore {
-class AudioBus;
-
class AudioScheduledSourceNode : public AudioNode {
public:
// These are the possible states an AudioScheduledSourceNode can be in:
//
// UNSCHEDULED_STATE - Initial playback state. Created, but not yet scheduled.
- // SCHEDULED_STATE - Scheduled to play (via noteOn() or noteGrainOn()), but not yet playing.
+ // SCHEDULED_STATE - Scheduled to play but not yet playing.
// PLAYING_STATE - Generating sound.
// FINISHED_STATE - Finished generating sound.
//
@@ -55,58 +51,45 @@ public:
FINISHED_STATE = 3
};
- AudioScheduledSourceNode(AudioContext*, float sampleRate);
-
- // Scheduling.
- void start(double when, ExceptionCode&);
- void stop(double when, ExceptionCode&);
+ AudioScheduledSourceNode(AudioContext&, float sampleRate);
-#if ENABLE(LEGACY_WEB_AUDIO)
- void noteOn(double when, ExceptionCode&);
- void noteOff(double when, ExceptionCode&);
-#endif
+ ExceptionOr<void> start(double when);
+ ExceptionOr<void> stop(double when);
unsigned short playbackState() const { return static_cast<unsigned short>(m_playbackState); }
bool isPlayingOrScheduled() const { return m_playbackState == PLAYING_STATE || m_playbackState == SCHEDULED_STATE; }
bool hasFinished() const { return m_playbackState == FINISHED_STATE; }
- EventListener* onended() { return getAttributeEventListener(eventNames().endedEvent); }
- void setOnended(PassRefPtr<EventListener> listener);
-
protected:
// Get frame information for the current time quantum.
// We handle the transition into PLAYING_STATE and FINISHED_STATE here,
// zeroing out portions of the outputBus which are outside the range of startFrame and endFrame.
- //
// Each frame time is relative to the context's currentSampleFrame().
- // quantumFrameOffset : Offset frame in this time quantum to start rendering.
- // nonSilentFramesToProcess : Number of frames rendering non-silence (will be <= quantumFrameSize).
- void updateSchedulingInfo(size_t quantumFrameSize,
- AudioBus* outputBus,
- size_t& quantumFrameOffset,
- size_t& nonSilentFramesToProcess);
+ // quantumFrameOffset: Offset frame in this time quantum to start rendering.
+ // nonSilentFramesToProcess: Number of frames rendering non-silence (will be <= quantumFrameSize).
+ void updateSchedulingInfo(size_t quantumFrameSize, AudioBus& outputBus, size_t& quantumFrameOffset, size_t& nonSilentFramesToProcess);
// Called when we have no more sound to play or the noteOff() time has been reached.
virtual void finish();
- static void notifyEndedDispatch(void*);
- void notifyEnded();
-
- PlaybackState m_playbackState;
+ PlaybackState m_playbackState { UNSCHEDULED_STATE };
// m_startTime is the time to start playing based on the context's timeline (0 or a time less than the context's current time means "now").
- double m_startTime; // in seconds
+ double m_startTime { 0 }; // in seconds
// m_endTime is the time to stop playing based on the context's timeline (0 or a time less than the context's current time means "now").
// If it hasn't been set explicitly, then the sound will not stop playing (if looping) or will stop when the end of the AudioBuffer
// has been reached.
double m_endTime; // in seconds
- bool m_hasEndedListener;
+ bool m_hasEndedListener { false };
static const double UnknownTime;
+
+private:
+ bool addEventListener(const AtomicString& eventType, Ref<EventListener>&&, const AddEventListenerOptions&) override;
+ bool removeEventListener(const AtomicString& eventType, EventListener&, const ListenerOptions&) override;
+ void removeAllEventListeners() override;
};
} // namespace WebCore
-
-#endif // AudioScheduledSourceNode_h
diff --git a/Source/WebCore/Modules/webaudio/AudioSummingJunction.cpp b/Source/WebCore/Modules/webaudio/AudioSummingJunction.cpp
index ed417fadb..ee600a629 100644
--- a/Source/WebCore/Modules/webaudio/AudioSummingJunction.cpp
+++ b/Source/WebCore/Modules/webaudio/AudioSummingJunction.cpp
@@ -34,7 +34,7 @@
namespace WebCore {
-AudioSummingJunction::AudioSummingJunction(AudioContext* context)
+AudioSummingJunction::AudioSummingJunction(AudioContext& context)
: m_context(context)
, m_renderingStateNeedUpdating(false)
{
@@ -42,30 +42,29 @@ AudioSummingJunction::AudioSummingJunction(AudioContext* context)
AudioSummingJunction::~AudioSummingJunction()
{
- if (m_renderingStateNeedUpdating && m_context.get())
- m_context->removeMarkedSummingJunction(this);
+ if (m_renderingStateNeedUpdating)
+ context().removeMarkedSummingJunction(this);
}
void AudioSummingJunction::changedOutputs()
{
- ASSERT(context()->isGraphOwner());
+ ASSERT(context().isGraphOwner());
if (!m_renderingStateNeedUpdating && canUpdateState()) {
- context()->markSummingJunctionDirty(this);
+ context().markSummingJunctionDirty(this);
m_renderingStateNeedUpdating = true;
}
}
void AudioSummingJunction::updateRenderingState()
{
- ASSERT(context()->isAudioThread() && context()->isGraphOwner());
+ ASSERT(context().isAudioThread() && context().isGraphOwner());
if (m_renderingStateNeedUpdating && canUpdateState()) {
// Copy from m_outputs to m_renderingOutputs.
m_renderingOutputs.resize(m_outputs.size());
- unsigned j = 0;
- for (HashSet<AudioNodeOutput*>::iterator i = m_outputs.begin(); i != m_outputs.end(); ++i, ++j) {
- AudioNodeOutput* output = *i;
- m_renderingOutputs[j] = output;
+ unsigned i = 0;
+ for (auto& output : m_outputs) {
+ m_renderingOutputs[i++] = output;
output->updateRenderingState();
}
diff --git a/Source/WebCore/Modules/webaudio/AudioSummingJunction.h b/Source/WebCore/Modules/webaudio/AudioSummingJunction.h
index b3b4f35fe..c94ff6d6a 100644
--- a/Source/WebCore/Modules/webaudio/AudioSummingJunction.h
+++ b/Source/WebCore/Modules/webaudio/AudioSummingJunction.h
@@ -22,8 +22,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AudioSummingJunction_h
-#define AudioSummingJunction_h
+#pragma once
#include "AudioBus.h"
#include <wtf/HashSet.h>
@@ -38,11 +37,11 @@ class AudioNodeOutput;
class AudioSummingJunction {
public:
- explicit AudioSummingJunction(AudioContext*);
+ explicit AudioSummingJunction(AudioContext&);
virtual ~AudioSummingJunction();
// Can be called from any thread.
- AudioContext* context() { return m_context.get(); }
+ AudioContext& context() { return m_context.get(); }
// This must be called whenever we modify m_outputs.
void changedOutputs();
@@ -61,7 +60,7 @@ public:
virtual void didUpdate() = 0;
protected:
- RefPtr<AudioContext> m_context;
+ Ref<AudioContext> m_context;
// m_outputs contains the AudioNodeOutputs representing current connections which are not disabled.
// The rendering code should never use this directly, but instead uses m_renderingOutputs.
@@ -82,5 +81,3 @@ protected:
};
} // namespace WebCore
-
-#endif // AudioSummingJunction_h
diff --git a/Source/WebCore/Modules/webaudio/BiquadDSPKernel.cpp b/Source/WebCore/Modules/webaudio/BiquadDSPKernel.cpp
index 37ef51e05..5561dfd64 100644
--- a/Source/WebCore/Modules/webaudio/BiquadDSPKernel.cpp
+++ b/Source/WebCore/Modules/webaudio/BiquadDSPKernel.cpp
@@ -76,35 +76,35 @@ void BiquadDSPKernel::updateCoefficientsIfNecessary(bool useSmoothing, bool forc
// Configure the biquad with the new filter parameters for the appropriate type of filter.
switch (biquadProcessor()->type()) {
- case BiquadProcessor::LowPass:
+ case BiquadFilterType::Lowpass:
m_biquad.setLowpassParams(normalizedFrequency, value2);
break;
- case BiquadProcessor::HighPass:
+ case BiquadFilterType::Highpass:
m_biquad.setHighpassParams(normalizedFrequency, value2);
break;
- case BiquadProcessor::BandPass:
+ case BiquadFilterType::Bandpass:
m_biquad.setBandpassParams(normalizedFrequency, value2);
break;
- case BiquadProcessor::LowShelf:
+ case BiquadFilterType::Lowshelf:
m_biquad.setLowShelfParams(normalizedFrequency, gain);
break;
- case BiquadProcessor::HighShelf:
+ case BiquadFilterType::Highshelf:
m_biquad.setHighShelfParams(normalizedFrequency, gain);
break;
- case BiquadProcessor::Peaking:
+ case BiquadFilterType::Peaking:
m_biquad.setPeakingParams(normalizedFrequency, value2, gain);
break;
- case BiquadProcessor::Notch:
+ case BiquadFilterType::Notch:
m_biquad.setNotchParams(normalizedFrequency, value2);
break;
- case BiquadProcessor::Allpass:
+ case BiquadFilterType::Allpass:
m_biquad.setAllpassParams(normalizedFrequency, value2);
break;
}
diff --git a/Source/WebCore/Modules/webaudio/BiquadDSPKernel.h b/Source/WebCore/Modules/webaudio/BiquadDSPKernel.h
index f3eb0f5b0..935552446 100644
--- a/Source/WebCore/Modules/webaudio/BiquadDSPKernel.h
+++ b/Source/WebCore/Modules/webaudio/BiquadDSPKernel.h
@@ -22,8 +22,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef BiquadDSPKernel_h
-#define BiquadDSPKernel_h
+#pragma once
#include "AudioDSPKernel.h"
#include "Biquad.h"
@@ -43,8 +42,8 @@ public:
}
// AudioDSPKernel
- virtual void process(const float* source, float* dest, size_t framesToProcess) override;
- virtual void reset() override { m_biquad.reset(); }
+ void process(const float* source, float* dest, size_t framesToProcess) override;
+ void reset() override { m_biquad.reset(); }
// Get the magnitude and phase response of the filter at the given
// set of frequencies (in Hz). The phase response is in radians.
@@ -53,8 +52,8 @@ public:
float* magResponse,
float* phaseResponse);
- virtual double tailTime() const override;
- virtual double latencyTime() const override;
+ double tailTime() const override;
+ double latencyTime() const override;
protected:
Biquad m_biquad;
@@ -71,5 +70,3 @@ protected:
};
} // namespace WebCore
-
-#endif // BiquadDSPKernel_h
diff --git a/Source/WebCore/Modules/webaudio/BiquadFilterNode.cpp b/Source/WebCore/Modules/webaudio/BiquadFilterNode.cpp
index b05b1445c..c5eaab004 100644
--- a/Source/WebCore/Modules/webaudio/BiquadFilterNode.cpp
+++ b/Source/WebCore/Modules/webaudio/BiquadFilterNode.cpp
@@ -28,11 +28,9 @@
#include "BiquadFilterNode.h"
-#include "ExceptionCode.h"
-
namespace WebCore {
-BiquadFilterNode::BiquadFilterNode(AudioContext* context, float sampleRate)
+BiquadFilterNode::BiquadFilterNode(AudioContext& context, float sampleRate)
: AudioBasicProcessorNode(context, sampleRate)
{
// Initially setup as lowpass filter.
@@ -40,78 +38,25 @@ BiquadFilterNode::BiquadFilterNode(AudioContext* context, float sampleRate)
setNodeType(NodeTypeBiquadFilter);
}
-String BiquadFilterNode::type() const
+BiquadFilterType BiquadFilterNode::type() const
{
- switch (const_cast<BiquadFilterNode*>(this)->biquadProcessor()->type()) {
- case BiquadProcessor::LowPass:
- return "lowpass";
- case BiquadProcessor::HighPass:
- return "highpass";
- case BiquadProcessor::BandPass:
- return "bandpass";
- case BiquadProcessor::LowShelf:
- return "lowshelf";
- case BiquadProcessor::HighShelf:
- return "highshelf";
- case BiquadProcessor::Peaking:
- return "peaking";
- case BiquadProcessor::Notch:
- return "notch";
- case BiquadProcessor::Allpass:
- return "allpass";
- default:
- ASSERT_NOT_REACHED();
- return "lowpass";
- }
+ return const_cast<BiquadFilterNode*>(this)->biquadProcessor()->type();
}
-void BiquadFilterNode::setType(const String& type)
+void BiquadFilterNode::setType(BiquadFilterType type)
{
- if (type == "lowpass")
- setType(BiquadProcessor::LowPass);
- else if (type == "highpass")
- setType(BiquadProcessor::HighPass);
- else if (type == "bandpass")
- setType(BiquadProcessor::BandPass);
- else if (type == "lowshelf")
- setType(BiquadProcessor::LowShelf);
- else if (type == "highshelf")
- setType(BiquadProcessor::HighShelf);
- else if (type == "peaking")
- setType(BiquadProcessor::Peaking);
- else if (type == "notch")
- setType(BiquadProcessor::Notch);
- else if (type == "allpass")
- setType(BiquadProcessor::Allpass);
- else
- ASSERT_NOT_REACHED();
-}
-
-bool BiquadFilterNode::setType(unsigned type)
-{
- if (type > BiquadProcessor::Allpass)
- return false;
-
- biquadProcessor()->setType(static_cast<BiquadProcessor::FilterType>(type));
- return true;
+ biquadProcessor()->setType(type);
}
-void BiquadFilterNode::getFrequencyResponse(const Float32Array* frequencyHz,
- Float32Array* magResponse,
- Float32Array* phaseResponse)
+void BiquadFilterNode::getFrequencyResponse(const RefPtr<Float32Array>& frequencyHz, const RefPtr<Float32Array>& magResponse, const RefPtr<Float32Array>& phaseResponse)
{
if (!frequencyHz || !magResponse || !phaseResponse)
return;
- int n = std::min(frequencyHz->length(),
- std::min(magResponse->length(), phaseResponse->length()));
+ int n = std::min(frequencyHz->length(), std::min(magResponse->length(), phaseResponse->length()));
- if (n) {
- biquadProcessor()->getFrequencyResponse(n,
- frequencyHz->data(),
- magResponse->data(),
- phaseResponse->data());
- }
+ if (n)
+ biquadProcessor()->getFrequencyResponse(n, frequencyHz->data(), magResponse->data(), phaseResponse->data());
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/webaudio/BiquadFilterNode.h b/Source/WebCore/Modules/webaudio/BiquadFilterNode.h
index dde2aec77..986d28607 100644
--- a/Source/WebCore/Modules/webaudio/BiquadFilterNode.h
+++ b/Source/WebCore/Modules/webaudio/BiquadFilterNode.h
@@ -22,8 +22,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef BiquadFilterNode_h
-#define BiquadFilterNode_h
+#pragma once
#include "AudioBasicProcessorNode.h"
#include "BiquadProcessor.h"
@@ -31,29 +30,16 @@
namespace WebCore {
class AudioParam;
-
+
class BiquadFilterNode : public AudioBasicProcessorNode {
public:
- // These must be defined as in the .idl file and must match those in the BiquadProcessor class.
- enum {
- LOWPASS = 0,
- HIGHPASS = 1,
- BANDPASS = 2,
- LOWSHELF = 3,
- HIGHSHELF = 4,
- PEAKING = 5,
- NOTCH = 6,
- ALLPASS = 7
- };
-
- static PassRefPtr<BiquadFilterNode> create(AudioContext* context, float sampleRate)
+ static Ref<BiquadFilterNode> create(AudioContext& context, float sampleRate)
{
- return adoptRef(new BiquadFilterNode(context, sampleRate));
+ return adoptRef(*new BiquadFilterNode(context, sampleRate));
}
- String type() const;
- bool setType(unsigned); // Returns true on success.
- void setType(const String&);
+ BiquadFilterType type() const;
+ void setType(BiquadFilterType);
AudioParam* frequency() { return biquadProcessor()->parameter1(); }
AudioParam* q() { return biquadProcessor()->parameter2(); }
@@ -62,16 +48,12 @@ public:
// Get the magnitude and phase response of the filter at the given
// set of frequencies (in Hz). The phase response is in radians.
- void getFrequencyResponse(const Float32Array* frequencyHz,
- Float32Array* magResponse,
- Float32Array* phaseResponse);
+ void getFrequencyResponse(const RefPtr<Float32Array>& frequencyHz, const RefPtr<Float32Array>& magResponse, const RefPtr<Float32Array>& phaseResponse);
private:
- BiquadFilterNode(AudioContext*, float sampleRate);
+ BiquadFilterNode(AudioContext&, float sampleRate);
BiquadProcessor* biquadProcessor() { return static_cast<BiquadProcessor*>(processor()); }
};
} // namespace WebCore
-
-#endif // BiquadFilterNode_h
diff --git a/Source/WebCore/Modules/webaudio/BiquadFilterNode.idl b/Source/WebCore/Modules/webaudio/BiquadFilterNode.idl
index 40424ef51..c54bd49ad 100644
--- a/Source/WebCore/Modules/webaudio/BiquadFilterNode.idl
+++ b/Source/WebCore/Modules/webaudio/BiquadFilterNode.idl
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2011, Google Inc. All rights reserved.
+ * 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
@@ -24,26 +25,29 @@
[
Conditional=WEB_AUDIO,
+ ImplementedAs=BiquadFilterType
+] enum BiquadFilterType {
+ "lowpass",
+ "highpass",
+ "bandpass",
+ "lowshelf",
+ "highshelf",
+ "peaking",
+ "notch",
+ "allpass"
+};
+
+[
+ Conditional=WEB_AUDIO,
JSGenerateToJSObject,
] interface BiquadFilterNode : AudioNode {
- // Filter type.
- const unsigned short LOWPASS = 0;
- const unsigned short HIGHPASS = 1;
- const unsigned short BANDPASS = 2;
- const unsigned short LOWSHELF = 3;
- const unsigned short HIGHSHELF = 4;
- const unsigned short PEAKING = 5;
- const unsigned short NOTCH = 6;
- const unsigned short ALLPASS = 7;
-
- [CustomSetter] attribute DOMString type;
+ attribute BiquadFilterType type;
readonly attribute AudioParam frequency; // in Hertz
readonly attribute AudioParam detune; // in Cents
readonly attribute AudioParam Q; // Quality factor
readonly attribute AudioParam gain; // in Decibels
- void getFrequencyResponse(Float32Array frequencyHz,
- Float32Array magResponse,
- Float32Array phaseResponse);
+ // FIXME: the parameters should not be nullable.
+ void getFrequencyResponse(Float32Array? frequencyHz, Float32Array? magResponse, Float32Array? phaseResponse);
};
diff --git a/Source/WebCore/Modules/webaudio/BiquadProcessor.cpp b/Source/WebCore/Modules/webaudio/BiquadProcessor.cpp
index 9c98e3dd5..0e4e3685b 100644
--- a/Source/WebCore/Modules/webaudio/BiquadProcessor.cpp
+++ b/Source/WebCore/Modules/webaudio/BiquadProcessor.cpp
@@ -32,9 +32,9 @@
namespace WebCore {
-BiquadProcessor::BiquadProcessor(AudioContext* context, float sampleRate, size_t numberOfChannels, bool autoInitialize)
+BiquadProcessor::BiquadProcessor(AudioContext& context, float sampleRate, size_t numberOfChannels, bool autoInitialize)
: AudioDSPKernelProcessor(sampleRate, numberOfChannels)
- , m_type(LowPass)
+ , m_type(BiquadFilterType::Lowpass)
, m_parameter1(0)
, m_parameter2(0)
, m_parameter3(0)
@@ -111,7 +111,7 @@ void BiquadProcessor::process(const AudioBus* source, AudioBus* destination, siz
m_kernels[i]->process(source->channel(i)->data(), destination->channel(i)->mutableData(), framesToProcess);
}
-void BiquadProcessor::setType(FilterType type)
+void BiquadProcessor::setType(BiquadFilterType type)
{
if (type != m_type) {
m_type = type;
@@ -119,10 +119,7 @@ void BiquadProcessor::setType(FilterType type)
}
}
-void BiquadProcessor::getFrequencyResponse(int nFrequencies,
- const float* frequencyHz,
- float* magResponse,
- float* phaseResponse)
+void BiquadProcessor::getFrequencyResponse(int nFrequencies, const float* frequencyHz, float* magResponse, float* phaseResponse)
{
// Compute the frequency response on a separate temporary kernel
// to avoid interfering with the processing running in the audio
diff --git a/Source/WebCore/Modules/webaudio/BiquadProcessor.h b/Source/WebCore/Modules/webaudio/BiquadProcessor.h
index db6de3e1a..5079f1375 100644
--- a/Source/WebCore/Modules/webaudio/BiquadProcessor.h
+++ b/Source/WebCore/Modules/webaudio/BiquadProcessor.h
@@ -22,8 +22,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef BiquadProcessor_h
-#define BiquadProcessor_h
+#pragma once
#include "AudioDSPKernel.h"
#include "AudioDSPKernelProcessor.h"
@@ -37,26 +36,26 @@ namespace WebCore {
// BiquadProcessor is an AudioDSPKernelProcessor which uses Biquad objects to implement several common filters.
+enum class BiquadFilterType {
+ Lowpass,
+ Highpass,
+ Bandpass,
+ Lowshelf,
+ Highshelf,
+ Peaking,
+ Notch,
+ Allpass
+};
+
class BiquadProcessor : public AudioDSPKernelProcessor {
public:
- enum FilterType {
- LowPass = 0,
- HighPass = 1,
- BandPass = 2,
- LowShelf = 3,
- HighShelf = 4,
- Peaking = 5,
- Notch = 6,
- Allpass = 7
- };
-
- BiquadProcessor(AudioContext*, float sampleRate, size_t numberOfChannels, bool autoInitialize);
+ BiquadProcessor(AudioContext&, float sampleRate, size_t numberOfChannels, bool autoInitialize);
virtual ~BiquadProcessor();
- virtual std::unique_ptr<AudioDSPKernel> createKernel() override;
+ std::unique_ptr<AudioDSPKernel> createKernel() override;
- virtual void process(const AudioBus* source, AudioBus* destination, size_t framesToProcess) override;
+ void process(const AudioBus* source, AudioBus* destination, size_t framesToProcess) override;
// Get the magnitude and phase response of the filter at the given
// set of frequencies (in Hz). The phase response is in radians.
@@ -75,11 +74,11 @@ public:
AudioParam* parameter3() { return m_parameter3.get(); }
AudioParam* parameter4() { return m_parameter4.get(); }
- FilterType type() const { return m_type; }
- void setType(FilterType);
+ BiquadFilterType type() const { return m_type; }
+ void setType(BiquadFilterType);
private:
- FilterType m_type;
+ BiquadFilterType m_type;
RefPtr<AudioParam> m_parameter1;
RefPtr<AudioParam> m_parameter2;
@@ -94,5 +93,3 @@ private:
};
} // namespace WebCore
-
-#endif // BiquadProcessor_h
diff --git a/Source/WebCore/Modules/webaudio/ChannelMergerNode.cpp b/Source/WebCore/Modules/webaudio/ChannelMergerNode.cpp
index 9f1989a92..b83c66769 100644
--- a/Source/WebCore/Modules/webaudio/ChannelMergerNode.cpp
+++ b/Source/WebCore/Modules/webaudio/ChannelMergerNode.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.
*
@@ -40,15 +40,15 @@ const unsigned DefaultNumberOfOutputChannels = 1;
namespace WebCore {
-PassRefPtr<ChannelMergerNode> ChannelMergerNode::create(AudioContext* context, float sampleRate, unsigned numberOfInputs)
+RefPtr<ChannelMergerNode> ChannelMergerNode::create(AudioContext& context, float sampleRate, unsigned numberOfInputs)
{
if (!numberOfInputs || numberOfInputs > AudioContext::maxNumberOfChannels())
return nullptr;
- return adoptRef(new ChannelMergerNode(context, sampleRate, numberOfInputs));
+ return adoptRef(*new ChannelMergerNode(context, sampleRate, numberOfInputs));
}
-ChannelMergerNode::ChannelMergerNode(AudioContext* context, float sampleRate, unsigned numberOfInputs)
+ChannelMergerNode::ChannelMergerNode(AudioContext& context, float sampleRate, unsigned numberOfInputs)
: AudioNode(context, sampleRate)
, m_desiredNumberOfOutputChannels(DefaultNumberOfOutputChannels)
{
@@ -104,7 +104,7 @@ void ChannelMergerNode::reset()
// number of channels of our output.
void ChannelMergerNode::checkNumberOfChannelsForInput(AudioNodeInput* input)
{
- ASSERT(context()->isAudioThread() && context()->isGraphOwner());
+ ASSERT(context().isAudioThread() && context().isGraphOwner());
// Count how many channels we have all together from all of the inputs.
unsigned numberOfOutputChannels = 0;
diff --git a/Source/WebCore/Modules/webaudio/ChannelMergerNode.h b/Source/WebCore/Modules/webaudio/ChannelMergerNode.h
index 0cc783e45..3071b5605 100644
--- a/Source/WebCore/Modules/webaudio/ChannelMergerNode.h
+++ b/Source/WebCore/Modules/webaudio/ChannelMergerNode.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,11 +26,9 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef ChannelMergerNode_h
-#define ChannelMergerNode_h
+#pragma once
#include "AudioNode.h"
-#include <wtf/PassRefPtr.h>
namespace WebCore {
@@ -38,24 +36,22 @@ class AudioContext;
class ChannelMergerNode : public AudioNode {
public:
- static PassRefPtr<ChannelMergerNode> create(AudioContext*, float sampleRate, unsigned numberOfInputs);
+ static RefPtr<ChannelMergerNode> create(AudioContext&, float sampleRate, unsigned numberOfInputs);
// AudioNode
- virtual void process(size_t framesToProcess) override;
- virtual void reset() override;
+ void process(size_t framesToProcess) override;
+ void reset() override;
// Called in the audio thread (pre-rendering task) when the number of channels for an input may have changed.
- virtual void checkNumberOfChannelsForInput(AudioNodeInput*) override;
+ void checkNumberOfChannelsForInput(AudioNodeInput*) override;
private:
unsigned m_desiredNumberOfOutputChannels;
- virtual double tailTime() const override { return 0; }
- virtual double latencyTime() const override { return 0; }
+ double tailTime() const override { return 0; }
+ double latencyTime() const override { return 0; }
- ChannelMergerNode(AudioContext*, float sampleRate, unsigned numberOfInputs);
+ ChannelMergerNode(AudioContext&, float sampleRate, unsigned numberOfInputs);
};
} // namespace WebCore
-
-#endif // ChannelMergerNode_h
diff --git a/Source/WebCore/Modules/webaudio/ChannelMergerNode.idl b/Source/WebCore/Modules/webaudio/ChannelMergerNode.idl
index 8158d7c22..caf989d80 100644
--- a/Source/WebCore/Modules/webaudio/ChannelMergerNode.idl
+++ b/Source/WebCore/Modules/webaudio/ChannelMergerNode.idl
@@ -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.
*
diff --git a/Source/WebCore/Modules/webaudio/ChannelSplitterNode.cpp b/Source/WebCore/Modules/webaudio/ChannelSplitterNode.cpp
index 88ed29d6c..74eff1d78 100644
--- a/Source/WebCore/Modules/webaudio/ChannelSplitterNode.cpp
+++ b/Source/WebCore/Modules/webaudio/ChannelSplitterNode.cpp
@@ -34,15 +34,15 @@
namespace WebCore {
-PassRefPtr<ChannelSplitterNode> ChannelSplitterNode::create(AudioContext* context, float sampleRate, unsigned numberOfOutputs)
+RefPtr<ChannelSplitterNode> ChannelSplitterNode::create(AudioContext& context, float sampleRate, unsigned numberOfOutputs)
{
if (!numberOfOutputs || numberOfOutputs > AudioContext::maxNumberOfChannels())
return nullptr;
- return adoptRef(new ChannelSplitterNode(context, sampleRate, numberOfOutputs));
+ return adoptRef(*new ChannelSplitterNode(context, sampleRate, numberOfOutputs));
}
-ChannelSplitterNode::ChannelSplitterNode(AudioContext* context, float sampleRate, unsigned numberOfOutputs)
+ChannelSplitterNode::ChannelSplitterNode(AudioContext& context, float sampleRate, unsigned numberOfOutputs)
: AudioNode(context, sampleRate)
{
addInput(std::make_unique<AudioNodeInput>(this));
diff --git a/Source/WebCore/Modules/webaudio/ChannelSplitterNode.h b/Source/WebCore/Modules/webaudio/ChannelSplitterNode.h
index 9d07279e2..acddc6c70 100644
--- a/Source/WebCore/Modules/webaudio/ChannelSplitterNode.h
+++ b/Source/WebCore/Modules/webaudio/ChannelSplitterNode.h
@@ -22,11 +22,9 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef ChannelSplitterNode_h
-#define ChannelSplitterNode_h
+#pragma once
#include "AudioNode.h"
-#include <wtf/PassRefPtr.h>
namespace WebCore {
@@ -34,19 +32,17 @@ class AudioContext;
class ChannelSplitterNode : public AudioNode {
public:
- static PassRefPtr<ChannelSplitterNode> create(AudioContext*, float sampleRate, unsigned numberOfOutputs);
+ static RefPtr<ChannelSplitterNode> create(AudioContext&, float sampleRate, unsigned numberOfOutputs);
// AudioNode
- virtual void process(size_t framesToProcess) override;
- virtual void reset() override;
+ void process(size_t framesToProcess) override;
+ void reset() override;
private:
- virtual double tailTime() const override { return 0; }
- virtual double latencyTime() const override { return 0; }
+ double tailTime() const override { return 0; }
+ double latencyTime() const override { return 0; }
- ChannelSplitterNode(AudioContext*, float sampleRate, unsigned numberOfOutputs);
+ ChannelSplitterNode(AudioContext&, float sampleRate, unsigned numberOfOutputs);
};
} // namespace WebCore
-
-#endif // ChannelSplitterNode_h
diff --git a/Source/WebCore/Modules/webaudio/ConvolverNode.cpp b/Source/WebCore/Modules/webaudio/ConvolverNode.cpp
index c5ac5ea52..03e0a009b 100644
--- a/Source/WebCore/Modules/webaudio/ConvolverNode.cpp
+++ b/Source/WebCore/Modules/webaudio/ConvolverNode.cpp
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2010, Google Inc. All rights reserved.
+ * 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
@@ -28,12 +29,9 @@
#include "ConvolverNode.h"
-#include "AudioBuffer.h"
-#include "AudioContext.h"
#include "AudioNodeInput.h"
#include "AudioNodeOutput.h"
#include "Reverb.h"
-#include <wtf/MainThread.h>
// Note about empirical tuning:
// The maximum FFT size affects reverb performance and accuracy.
@@ -45,9 +43,8 @@ const size_t MaxFFTSize = 32768;
namespace WebCore {
-ConvolverNode::ConvolverNode(AudioContext* context, float sampleRate)
+ConvolverNode::ConvolverNode(AudioContext& context, float sampleRate)
: AudioNode(context, sampleRate)
- , m_normalize(true)
{
addInput(std::make_unique<AudioNodeInput>(this));
addOutput(std::make_unique<AudioNodeOutput>(this, 2));
@@ -73,7 +70,7 @@ void ConvolverNode::process(size_t framesToProcess)
ASSERT(outputBus);
// Synchronize with possible dynamic changes to the impulse response.
- std::unique_lock<std::mutex> lock(m_processMutex, std::try_to_lock);
+ std::unique_lock<Lock> lock(m_processMutex, std::try_to_lock);
if (!lock.owns_lock()) {
// Too bad - the try_lock() failed. We must be in the middle of setting a new impulse response.
outputBus->zero();
@@ -93,7 +90,7 @@ void ConvolverNode::process(size_t framesToProcess)
void ConvolverNode::reset()
{
- std::lock_guard<std::mutex> lock(m_processMutex);
+ std::lock_guard<Lock> lock(m_processMutex);
if (m_reverb)
m_reverb->reset();
}
@@ -102,7 +99,7 @@ void ConvolverNode::initialize()
{
if (isInitialized())
return;
-
+
AudioNode::initialize();
}
@@ -115,40 +112,46 @@ void ConvolverNode::uninitialize()
AudioNode::uninitialize();
}
-void ConvolverNode::setBuffer(AudioBuffer* buffer)
+ExceptionOr<void> ConvolverNode::setBuffer(AudioBuffer* buffer)
{
ASSERT(isMainThread());
if (!buffer)
- return;
+ return { };
+
+ if (buffer->sampleRate() != context().sampleRate())
+ return Exception { NOT_SUPPORTED_ERR };
unsigned numberOfChannels = buffer->numberOfChannels();
size_t bufferLength = buffer->length();
- // The current implementation supports up to four channel impulse responses, which are interpreted as true-stereo (see Reverb class).
- bool isBufferGood = numberOfChannels > 0 && numberOfChannels <= 4 && bufferLength;
- ASSERT(isBufferGood);
- if (!isBufferGood)
- return;
+ // The current implementation supports only 1-, 2-, or 4-channel impulse responses, with the
+ // 4-channel response being interpreted as true-stereo (see Reverb class).
+ bool isChannelCountGood = (numberOfChannels == 1 || numberOfChannels == 2 || numberOfChannels == 4) && bufferLength;
+
+ if (!isChannelCountGood)
+ return Exception { NOT_SUPPORTED_ERR };
// Wrap the AudioBuffer by an AudioBus. It's an efficient pointer set and not a memcpy().
// This memory is simply used in the Reverb constructor and no reference to it is kept for later use in that class.
- RefPtr<AudioBus> bufferBus = AudioBus::create(numberOfChannels, bufferLength, false);
+ auto bufferBus = AudioBus::create(numberOfChannels, bufferLength, false);
for (unsigned i = 0; i < numberOfChannels; ++i)
- bufferBus->setChannelMemory(i, buffer->getChannelData(i)->data(), bufferLength);
+ bufferBus->setChannelMemory(i, buffer->channelData(i)->data(), bufferLength);
bufferBus->setSampleRate(buffer->sampleRate());
// Create the reverb with the given impulse response.
- bool useBackgroundThreads = !context()->isOfflineContext();
+ bool useBackgroundThreads = !context().isOfflineContext();
auto reverb = std::make_unique<Reverb>(bufferBus.get(), AudioNode::ProcessingSizeInFrames, MaxFFTSize, 2, useBackgroundThreads, m_normalize);
{
// Synchronize with process().
- std::lock_guard<std::mutex> lock(m_processMutex);
- m_reverb = std::move(reverb);
+ std::lock_guard<Lock> lock(m_processMutex);
+ m_reverb = WTFMove(reverb);
m_buffer = buffer;
}
+
+ return { };
}
AudioBuffer* ConvolverNode::buffer()
diff --git a/Source/WebCore/Modules/webaudio/ConvolverNode.h b/Source/WebCore/Modules/webaudio/ConvolverNode.h
index 4417a0121..c0074d9a6 100644
--- a/Source/WebCore/Modules/webaudio/ConvolverNode.h
+++ b/Source/WebCore/Modules/webaudio/ConvolverNode.h
@@ -22,57 +22,50 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef ConvolverNode_h
-#define ConvolverNode_h
+#pragma once
#include "AudioNode.h"
-#include <memory>
-#include <mutex>
-#include <wtf/RefPtr.h>
+#include <wtf/Lock.h>
namespace WebCore {
class AudioBuffer;
class Reverb;
-class ConvolverNode : public AudioNode {
+class ConvolverNode final : public AudioNode {
public:
- static PassRefPtr<ConvolverNode> create(AudioContext* context, float sampleRate)
+ static Ref<ConvolverNode> create(AudioContext& context, float sampleRate)
{
- return adoptRef(new ConvolverNode(context, sampleRate));
+ return adoptRef(*new ConvolverNode(context, sampleRate));
}
virtual ~ConvolverNode();
- // AudioNode
- virtual void process(size_t framesToProcess) override;
- virtual void reset() override;
- virtual void initialize() override;
- virtual void uninitialize() override;
-
- // Impulse responses
- void setBuffer(AudioBuffer*);
+ ExceptionOr<void> setBuffer(AudioBuffer*);
AudioBuffer* buffer();
bool normalize() const { return m_normalize; }
void setNormalize(bool normalize) { m_normalize = normalize; }
private:
- ConvolverNode(AudioContext*, float sampleRate);
+ ConvolverNode(AudioContext&, float sampleRate);
+
+ double tailTime() const final;
+ double latencyTime() const final;
- virtual double tailTime() const override;
- virtual double latencyTime() const override;
+ void process(size_t framesToProcess) final;
+ void reset() final;
+ void initialize() final;
+ void uninitialize() final;
std::unique_ptr<Reverb> m_reverb;
RefPtr<AudioBuffer> m_buffer;
// This synchronizes dynamic changes to the convolution impulse response with process().
- mutable std::mutex m_processMutex;
+ mutable Lock m_processMutex;
- // Normalize the impulse response or not. Must default to true.
- bool m_normalize;
+ // Normalize the impulse response or not.
+ bool m_normalize { true };
};
} // namespace WebCore
-
-#endif // ConvolverNode_h
diff --git a/Source/WebCore/Modules/webaudio/ConvolverNode.idl b/Source/WebCore/Modules/webaudio/ConvolverNode.idl
index 5451d4e58..e661b5766 100644
--- a/Source/WebCore/Modules/webaudio/ConvolverNode.idl
+++ b/Source/WebCore/Modules/webaudio/ConvolverNode.idl
@@ -27,6 +27,6 @@
Conditional=WEB_AUDIO,
JSGenerateToJSObject
] interface ConvolverNode : AudioNode {
- attribute AudioBuffer buffer;
+ [SetterMayThrowException] attribute AudioBuffer? buffer;
attribute boolean normalize;
};
diff --git a/Source/WebCore/Modules/webaudio/DefaultAudioDestinationNode.cpp b/Source/WebCore/Modules/webaudio/DefaultAudioDestinationNode.cpp
index 6b4f9b85a..b2542d326 100644
--- a/Source/WebCore/Modules/webaudio/DefaultAudioDestinationNode.cpp
+++ b/Source/WebCore/Modules/webaudio/DefaultAudioDestinationNode.cpp
@@ -28,17 +28,18 @@
#include "DefaultAudioDestinationNode.h"
-#include "ExceptionCode.h"
+#include "AudioContext.h"
+#include "AudioDestination.h"
#include "Logging.h"
+#include "ScriptExecutionContext.h"
#include <wtf/MainThread.h>
const unsigned EnabledInputChannels = 2;
namespace WebCore {
-DefaultAudioDestinationNode::DefaultAudioDestinationNode(AudioContext* context)
+DefaultAudioDestinationNode::DefaultAudioDestinationNode(AudioContext& context)
: AudioDestinationNode(context, AudioDestination::hardwareSampleRate())
- , m_numberOfInputChannels(0)
{
// Node-specific default mixing rules.
m_channelCount = 2;
@@ -104,12 +105,38 @@ void DefaultAudioDestinationNode::startRendering()
m_destination->start();
}
-unsigned long DefaultAudioDestinationNode::maxChannelCount() const
+void DefaultAudioDestinationNode::resume(Function<void ()>&& function)
+{
+ ASSERT(isInitialized());
+ if (isInitialized())
+ m_destination->start();
+ if (auto scriptExecutionContext = context().scriptExecutionContext())
+ scriptExecutionContext->postTask(WTFMove(function));
+}
+
+void DefaultAudioDestinationNode::suspend(Function<void ()>&& function)
+{
+ ASSERT(isInitialized());
+ if (isInitialized())
+ m_destination->stop();
+ if (auto scriptExecutionContext = context().scriptExecutionContext())
+ scriptExecutionContext->postTask(WTFMove(function));
+}
+
+void DefaultAudioDestinationNode::close(Function<void()>&& function)
+{
+ ASSERT(isInitialized());
+ uninitialize();
+ if (auto scriptExecutionContext = context().scriptExecutionContext())
+ scriptExecutionContext->postTask(WTFMove(function));
+}
+
+unsigned DefaultAudioDestinationNode::maxChannelCount() const
{
return AudioDestination::maxChannelCount();
}
-void DefaultAudioDestinationNode::setChannelCount(unsigned long channelCount, ExceptionCode& ec)
+ExceptionOr<void> DefaultAudioDestinationNode::setChannelCount(unsigned channelCount)
{
// The channelCount for the input to this node controls the actual number of channels we
// send to the audio hardware. It can only be set depending on the maximum number of
@@ -117,20 +144,27 @@ void DefaultAudioDestinationNode::setChannelCount(unsigned long channelCount, Ex
ASSERT(isMainThread());
- if (!maxChannelCount() || channelCount > maxChannelCount()) {
- ec = INVALID_STATE_ERR;
- return;
- }
+ if (!maxChannelCount() || channelCount > maxChannelCount())
+ return Exception { INVALID_STATE_ERR };
- unsigned long oldChannelCount = this->channelCount();
- AudioNode::setChannelCount(channelCount, ec);
+ auto oldChannelCount = this->channelCount();
+ auto result = AudioNode::setChannelCount(channelCount);
+ if (result.hasException())
+ return result;
- if (!ec && this->channelCount() != oldChannelCount && isInitialized()) {
+ if (this->channelCount() != oldChannelCount && isInitialized()) {
// Re-create destination.
m_destination->stop();
createDestination();
m_destination->start();
}
+
+ return { };
+}
+
+bool DefaultAudioDestinationNode::isPlaying()
+{
+ return m_destination && m_destination->isPlaying();
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/webaudio/DefaultAudioDestinationNode.h b/Source/WebCore/Modules/webaudio/DefaultAudioDestinationNode.h
index 574fed222..d8f6d478a 100644
--- a/Source/WebCore/Modules/webaudio/DefaultAudioDestinationNode.h
+++ b/Source/WebCore/Modules/webaudio/DefaultAudioDestinationNode.h
@@ -22,45 +22,42 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DefaultAudioDestinationNode_h
-#define DefaultAudioDestinationNode_h
+#pragma once
-#include "AudioDestination.h"
#include "AudioDestinationNode.h"
-#include <memory>
namespace WebCore {
-class AudioContext;
+class AudioDestination;
-class DefaultAudioDestinationNode : public AudioDestinationNode {
+class DefaultAudioDestinationNode final : public AudioDestinationNode {
public:
- static PassRefPtr<DefaultAudioDestinationNode> create(AudioContext* context)
+ static Ref<DefaultAudioDestinationNode> create(AudioContext& context)
{
- return adoptRef(new DefaultAudioDestinationNode(context));
+ return adoptRef(*new DefaultAudioDestinationNode(context));
}
virtual ~DefaultAudioDestinationNode();
- // AudioNode
- virtual void initialize() override;
- virtual void uninitialize() override;
- virtual void setChannelCount(unsigned long, ExceptionCode&) override;
-
- // AudioDestinationNode
- virtual void enableInput(const String& inputDeviceId) override;
- virtual void startRendering() override;
- virtual unsigned long maxChannelCount() const override;
-
private:
- explicit DefaultAudioDestinationNode(AudioContext*);
+ explicit DefaultAudioDestinationNode(AudioContext&);
void createDestination();
+ void initialize() final;
+ void uninitialize() final;
+ ExceptionOr<void> setChannelCount(unsigned) final;
+
+ void enableInput(const String& inputDeviceId) final;
+ void startRendering() final;
+ void resume(Function<void ()>&&) final;
+ void suspend(Function<void ()>&&) final;
+ void close(Function<void ()>&&) final;
+ unsigned maxChannelCount() const final;
+ bool isPlaying() final;
+
std::unique_ptr<AudioDestination> m_destination;
String m_inputDeviceId;
- unsigned m_numberOfInputChannels;
+ unsigned m_numberOfInputChannels { 0 };
};
} // namespace WebCore
-
-#endif // DefaultAudioDestinationNode_h
diff --git a/Source/WebCore/Modules/webaudio/DelayDSPKernel.h b/Source/WebCore/Modules/webaudio/DelayDSPKernel.h
index f231d4423..b249afb00 100644
--- a/Source/WebCore/Modules/webaudio/DelayDSPKernel.h
+++ b/Source/WebCore/Modules/webaudio/DelayDSPKernel.h
@@ -22,8 +22,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DelayDSPKernel_h
-#define DelayDSPKernel_h
+#pragma once
#include "AudioArray.h"
#include "AudioDSPKernel.h"
@@ -38,15 +37,15 @@ public:
explicit DelayDSPKernel(DelayProcessor*);
DelayDSPKernel(double maxDelayTime, float sampleRate);
- virtual void process(const float* source, float* destination, size_t framesToProcess) override;
- virtual void reset() override;
+ void process(const float* source, float* destination, size_t framesToProcess) override;
+ void reset() override;
double maxDelayTime() const { return m_maxDelayTime; }
void setDelayFrames(double numberOfFrames) { m_desiredDelayFrames = numberOfFrames; }
- virtual double tailTime() const override;
- virtual double latencyTime() const override;
+ double tailTime() const override;
+ double latencyTime() const override;
private:
AudioFloatArray m_buffer;
@@ -64,5 +63,3 @@ private:
};
} // namespace WebCore
-
-#endif // DelayDSPKernel_h
diff --git a/Source/WebCore/Modules/webaudio/DelayNode.cpp b/Source/WebCore/Modules/webaudio/DelayNode.cpp
index 0b0452ceb..7eae8091c 100644
--- a/Source/WebCore/Modules/webaudio/DelayNode.cpp
+++ b/Source/WebCore/Modules/webaudio/DelayNode.cpp
@@ -28,24 +28,29 @@
#include "DelayNode.h"
+#include "DelayProcessor.h"
+
namespace WebCore {
const double maximumAllowedDelayTime = 180;
-DelayNode::DelayNode(AudioContext* context, float sampleRate, double maxDelayTime, ExceptionCode& ec)
+inline DelayNode::DelayNode(AudioContext& context, float sampleRate, double maxDelayTime)
: AudioBasicProcessorNode(context, sampleRate)
{
- if (maxDelayTime <= 0 || maxDelayTime >= maximumAllowedDelayTime) {
- ec = NOT_SUPPORTED_ERR;
- return;
- }
m_processor = std::make_unique<DelayProcessor>(context, sampleRate, 1, maxDelayTime);
setNodeType(NodeTypeDelay);
}
+ExceptionOr<Ref<DelayNode>> DelayNode::create(AudioContext& context, float sampleRate, double maxDelayTime)
+{
+ if (maxDelayTime <= 0 || maxDelayTime >= maximumAllowedDelayTime)
+ return Exception { NOT_SUPPORTED_ERR };
+ return adoptRef(*new DelayNode(context, sampleRate, maxDelayTime));
+}
+
AudioParam* DelayNode::delayTime()
{
- return delayProcessor()->delayTime();
+ return static_cast<DelayProcessor&>(*m_processor).delayTime();
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/webaudio/DelayNode.h b/Source/WebCore/Modules/webaudio/DelayNode.h
index da61e69e6..06ca9a7a6 100644
--- a/Source/WebCore/Modules/webaudio/DelayNode.h
+++ b/Source/WebCore/Modules/webaudio/DelayNode.h
@@ -22,33 +22,20 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DelayNode_h
-#define DelayNode_h
+#pragma once
#include "AudioBasicProcessorNode.h"
-#include "DelayProcessor.h"
-#include "ExceptionCode.h"
-#include <wtf/PassRefPtr.h>
namespace WebCore {
-class AudioParam;
-
class DelayNode : public AudioBasicProcessorNode {
public:
- static PassRefPtr<DelayNode> create(AudioContext* context, float sampleRate, double maxDelayTime, ExceptionCode& ec)
- {
- return adoptRef(new DelayNode(context, sampleRate, maxDelayTime, ec));
- }
+ static ExceptionOr<Ref<DelayNode>> create(AudioContext&, float sampleRate, double maxDelayTime);
AudioParam* delayTime();
private:
- DelayNode(AudioContext*, float sampleRate, double maxDelayTime, ExceptionCode&);
-
- DelayProcessor* delayProcessor() { return static_cast<DelayProcessor*>(processor()); }
+ DelayNode(AudioContext&, float sampleRate, double maxDelayTime);
};
} // namespace WebCore
-
-#endif // DelayNode_h
diff --git a/Source/WebCore/Modules/webaudio/DelayProcessor.cpp b/Source/WebCore/Modules/webaudio/DelayProcessor.cpp
index a5cc77559..a75ba6b7c 100644
--- a/Source/WebCore/Modules/webaudio/DelayProcessor.cpp
+++ b/Source/WebCore/Modules/webaudio/DelayProcessor.cpp
@@ -32,7 +32,7 @@
namespace WebCore {
-DelayProcessor::DelayProcessor(AudioContext* context, float sampleRate, unsigned numberOfChannels, double maxDelayTime)
+DelayProcessor::DelayProcessor(AudioContext& context, float sampleRate, unsigned numberOfChannels, double maxDelayTime)
: AudioDSPKernelProcessor(sampleRate, numberOfChannels)
, m_maxDelayTime(maxDelayTime)
{
diff --git a/Source/WebCore/Modules/webaudio/DelayProcessor.h b/Source/WebCore/Modules/webaudio/DelayProcessor.h
index 042395869..d08257703 100644
--- a/Source/WebCore/Modules/webaudio/DelayProcessor.h
+++ b/Source/WebCore/Modules/webaudio/DelayProcessor.h
@@ -22,8 +22,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DelayProcessor_h
-#define DelayProcessor_h
+#pragma once
#include "AudioDSPKernelProcessor.h"
#include "AudioParam.h"
@@ -36,10 +35,10 @@ class AudioDSPKernel;
class DelayProcessor : public AudioDSPKernelProcessor {
public:
- DelayProcessor(AudioContext*, float sampleRate, unsigned numberOfChannels, double maxDelayTime);
+ DelayProcessor(AudioContext&, float sampleRate, unsigned numberOfChannels, double maxDelayTime);
virtual ~DelayProcessor();
- virtual std::unique_ptr<AudioDSPKernel> createKernel() override;
+ std::unique_ptr<AudioDSPKernel> createKernel() override;
AudioParam* delayTime() const { return m_delayTime.get(); }
@@ -51,5 +50,3 @@ private:
};
} // namespace WebCore
-
-#endif // DelayProcessor_h
diff --git a/Source/WebCore/Modules/webaudio/DynamicsCompressorNode.cpp b/Source/WebCore/Modules/webaudio/DynamicsCompressorNode.cpp
index 7b80242e4..a1ae66a28 100644
--- a/Source/WebCore/Modules/webaudio/DynamicsCompressorNode.cpp
+++ b/Source/WebCore/Modules/webaudio/DynamicsCompressorNode.cpp
@@ -38,7 +38,7 @@ static const unsigned defaultNumberOfOutputChannels = 2;
namespace WebCore {
-DynamicsCompressorNode::DynamicsCompressorNode(AudioContext* context, float sampleRate)
+DynamicsCompressorNode::DynamicsCompressorNode(AudioContext& context, float sampleRate)
: AudioNode(context, sampleRate)
{
addInput(std::make_unique<AudioNodeInput>(this));
diff --git a/Source/WebCore/Modules/webaudio/DynamicsCompressorNode.h b/Source/WebCore/Modules/webaudio/DynamicsCompressorNode.h
index 123c0ca92..7f9d83558 100644
--- a/Source/WebCore/Modules/webaudio/DynamicsCompressorNode.h
+++ b/Source/WebCore/Modules/webaudio/DynamicsCompressorNode.h
@@ -22,8 +22,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DynamicsCompressorNode_h
-#define DynamicsCompressorNode_h
+#pragma once
#include "AudioNode.h"
#include "AudioParam.h"
@@ -35,18 +34,18 @@ class DynamicsCompressor;
class DynamicsCompressorNode : public AudioNode {
public:
- static PassRefPtr<DynamicsCompressorNode> create(AudioContext* context, float sampleRate)
+ static Ref<DynamicsCompressorNode> create(AudioContext& context, float sampleRate)
{
- return adoptRef(new DynamicsCompressorNode(context, sampleRate));
+ return adoptRef(*new DynamicsCompressorNode(context, sampleRate));
}
virtual ~DynamicsCompressorNode();
// AudioNode
- virtual void process(size_t framesToProcess) override;
- virtual void reset() override;
- virtual void initialize() override;
- virtual void uninitialize() override;
+ void process(size_t framesToProcess) override;
+ void reset() override;
+ void initialize() override;
+ void uninitialize() override;
// Static compression curve parameters.
AudioParam* threshold() { return m_threshold.get(); }
@@ -59,10 +58,10 @@ public:
AudioParam* reduction() { return m_reduction.get(); }
private:
- virtual double tailTime() const override;
- virtual double latencyTime() const override;
+ double tailTime() const override;
+ double latencyTime() const override;
- DynamicsCompressorNode(AudioContext*, float sampleRate);
+ DynamicsCompressorNode(AudioContext&, float sampleRate);
std::unique_ptr<DynamicsCompressor> m_dynamicsCompressor;
RefPtr<AudioParam> m_threshold;
@@ -74,5 +73,3 @@ private:
};
} // namespace WebCore
-
-#endif // DynamicsCompressorNode_h
diff --git a/Source/WebCore/Modules/webaudio/GainNode.cpp b/Source/WebCore/Modules/webaudio/GainNode.cpp
index d322c133a..fba5ec459 100644
--- a/Source/WebCore/Modules/webaudio/GainNode.cpp
+++ b/Source/WebCore/Modules/webaudio/GainNode.cpp
@@ -34,7 +34,7 @@
namespace WebCore {
-GainNode::GainNode(AudioContext* context, float sampleRate)
+GainNode::GainNode(AudioContext& context, float sampleRate)
: AudioNode(context, sampleRate)
, m_lastGain(1.0)
, m_sampleAccurateGainValues(AudioNode::ProcessingSizeInFrames) // FIXME: can probably share temp buffer in context
@@ -91,7 +91,7 @@ void GainNode::reset()
// uninitialize and then re-initialize with the new channel count.
void GainNode::checkNumberOfChannelsForInput(AudioNodeInput* input)
{
- ASSERT(context()->isAudioThread() && context()->isGraphOwner());
+ ASSERT(context().isAudioThread() && context().isGraphOwner());
ASSERT(input && input == this->input(0));
if (input != this->input(0))
diff --git a/Source/WebCore/Modules/webaudio/GainNode.h b/Source/WebCore/Modules/webaudio/GainNode.h
index 3a8c5dfd8..94352b0d7 100644
--- a/Source/WebCore/Modules/webaudio/GainNode.h
+++ b/Source/WebCore/Modules/webaudio/GainNode.h
@@ -22,12 +22,10 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef GainNode_h
-#define GainNode_h
+#pragma once
#include "AudioNode.h"
#include "AudioParam.h"
-#include <wtf/PassRefPtr.h>
#include <wtf/Threading.h>
namespace WebCore {
@@ -39,26 +37,26 @@ class AudioContext;
class GainNode : public AudioNode {
public:
- static PassRefPtr<GainNode> create(AudioContext* context, float sampleRate)
+ static Ref<GainNode> create(AudioContext& context, float sampleRate)
{
- return adoptRef(new GainNode(context, sampleRate));
+ return adoptRef(*new GainNode(context, sampleRate));
}
// AudioNode
- virtual void process(size_t framesToProcess) override;
- virtual void reset() override;
+ void process(size_t framesToProcess) override;
+ void reset() override;
// Called in the main thread when the number of channels for the input may have changed.
- virtual void checkNumberOfChannelsForInput(AudioNodeInput*) override;
+ void checkNumberOfChannelsForInput(AudioNodeInput*) override;
// JavaScript interface
AudioParam* gain() { return m_gain.get(); }
private:
- virtual double tailTime() const override { return 0; }
- virtual double latencyTime() const override { return 0; }
+ double tailTime() const override { return 0; }
+ double latencyTime() const override { return 0; }
- GainNode(AudioContext*, float sampleRate);
+ GainNode(AudioContext&, float sampleRate);
float m_lastGain; // for de-zippering
RefPtr<AudioParam> m_gain;
@@ -67,5 +65,3 @@ private:
};
} // namespace WebCore
-
-#endif // GainNode_h
diff --git a/Source/WebCore/Modules/webaudio/MediaElementAudioSourceNode.cpp b/Source/WebCore/Modules/webaudio/MediaElementAudioSourceNode.cpp
index 0e50258ac..77c7fbccf 100644
--- a/Source/WebCore/Modules/webaudio/MediaElementAudioSourceNode.cpp
+++ b/Source/WebCore/Modules/webaudio/MediaElementAudioSourceNode.cpp
@@ -40,13 +40,13 @@ const unsigned maxSampleRate = 192000;
namespace WebCore {
-PassRefPtr<MediaElementAudioSourceNode> MediaElementAudioSourceNode::create(AudioContext* context, HTMLMediaElement* mediaElement)
+Ref<MediaElementAudioSourceNode> MediaElementAudioSourceNode::create(AudioContext& context, HTMLMediaElement& mediaElement)
{
- return adoptRef(new MediaElementAudioSourceNode(context, mediaElement));
+ return adoptRef(*new MediaElementAudioSourceNode(context, mediaElement));
}
-MediaElementAudioSourceNode::MediaElementAudioSourceNode(AudioContext* context, HTMLMediaElement* mediaElement)
- : AudioNode(context, context->sampleRate())
+MediaElementAudioSourceNode::MediaElementAudioSourceNode(AudioContext& context, HTMLMediaElement& mediaElement)
+ : AudioNode(context, context.sampleRate())
, m_mediaElement(mediaElement)
, m_sourceNumberOfChannels(0)
, m_sourceSampleRate(0)
@@ -61,7 +61,7 @@ MediaElementAudioSourceNode::MediaElementAudioSourceNode(AudioContext* context,
MediaElementAudioSourceNode::~MediaElementAudioSourceNode()
{
- m_mediaElement->setAudioSourceNode(0);
+ m_mediaElement->setAudioSourceNode(nullptr);
uninitialize();
}
@@ -92,7 +92,7 @@ void MediaElementAudioSourceNode::setFormat(size_t numberOfChannels, float sourc
{
// The context must be locked when changing the number of output channels.
- AudioContext::AutoLocker contextLocker(*context());
+ AudioContext::AutoLocker contextLocker(context());
// Do any necesssary re-configuration to the output's number of channels.
output(0)->setNumberOfChannels(numberOfChannels);
@@ -104,7 +104,7 @@ void MediaElementAudioSourceNode::process(size_t numberOfFrames)
{
AudioBus* outputBus = output(0)->bus();
- if (!mediaElement() || !m_sourceNumberOfChannels || !m_sourceSampleRate) {
+ if (!m_sourceNumberOfChannels || !m_sourceSampleRate) {
outputBus->zero();
return;
}
@@ -112,14 +112,14 @@ void MediaElementAudioSourceNode::process(size_t numberOfFrames)
// Use a std::try_to_lock to avoid contention in the real-time audio thread.
// If we fail to acquire the lock then the HTMLMediaElement must be in the middle of
// reconfiguring its playback engine, so we output silence in this case.
- std::unique_lock<std::mutex> lock(m_processMutex, std::try_to_lock);
+ std::unique_lock<Lock> lock(m_processMutex, std::try_to_lock);
if (!lock.owns_lock()) {
// We failed to acquire the lock.
outputBus->zero();
return;
}
- if (AudioSourceProvider* provider = mediaElement()->audioSourceProvider()) {
+ if (AudioSourceProvider* provider = mediaElement().audioSourceProvider()) {
if (m_multiChannelResampler.get()) {
ASSERT(m_sourceSampleRate != sampleRate());
m_multiChannelResampler->process(provider, outputBus, numberOfFrames);
diff --git a/Source/WebCore/Modules/webaudio/MediaElementAudioSourceNode.h b/Source/WebCore/Modules/webaudio/MediaElementAudioSourceNode.h
index 7f58ae7b2..0cafdc945 100644
--- a/Source/WebCore/Modules/webaudio/MediaElementAudioSourceNode.h
+++ b/Source/WebCore/Modules/webaudio/MediaElementAudioSourceNode.h
@@ -22,8 +22,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef MediaElementAudioSourceNode_h
-#define MediaElementAudioSourceNode_h
+#pragma once
#if ENABLE(WEB_AUDIO) && ENABLE(VIDEO)
@@ -32,8 +31,7 @@
#include "HTMLMediaElement.h"
#include "MultiChannelResampler.h"
#include <memory>
-#include <mutex>
-#include <wtf/PassRefPtr.h>
+#include <wtf/Lock.h>
namespace WebCore {
@@ -41,33 +39,33 @@ class AudioContext;
class MediaElementAudioSourceNode : public AudioNode, public AudioSourceProviderClient {
public:
- static PassRefPtr<MediaElementAudioSourceNode> create(AudioContext*, HTMLMediaElement*);
+ static Ref<MediaElementAudioSourceNode> create(AudioContext&, HTMLMediaElement&);
virtual ~MediaElementAudioSourceNode();
- HTMLMediaElement* mediaElement() { return m_mediaElement.get(); }
+ HTMLMediaElement& mediaElement() { return m_mediaElement; }
// AudioNode
- virtual void process(size_t framesToProcess) override;
- virtual void reset() override;
+ void process(size_t framesToProcess) override;
+ void reset() override;
// AudioSourceProviderClient
- virtual void setFormat(size_t numberOfChannels, float sampleRate) override;
+ void setFormat(size_t numberOfChannels, float sampleRate) override;
void lock();
void unlock();
private:
- MediaElementAudioSourceNode(AudioContext*, HTMLMediaElement*);
+ MediaElementAudioSourceNode(AudioContext&, HTMLMediaElement&);
- virtual double tailTime() const override { return 0; }
- virtual double latencyTime() const override { return 0; }
+ double tailTime() const override { return 0; }
+ double latencyTime() const override { return 0; }
// As an audio source, we will never propagate silence.
- virtual bool propagatesSilence() const override { return false; }
+ bool propagatesSilence() const override { return false; }
- RefPtr<HTMLMediaElement> m_mediaElement;
- std::mutex m_processMutex;
+ Ref<HTMLMediaElement> m_mediaElement;
+ Lock m_processMutex;
unsigned m_sourceNumberOfChannels;
double m_sourceSampleRate;
@@ -78,5 +76,3 @@ private:
} // namespace WebCore
#endif // ENABLE(WEB_AUDIO) && ENABLE(VIDEO)
-
-#endif // MediaElementAudioSourceNode_h
diff --git a/Source/WebCore/Modules/webaudio/MediaStreamAudioDestinationNode.cpp b/Source/WebCore/Modules/webaudio/MediaStreamAudioDestinationNode.cpp
index 2103edf5c..1b9072ae2 100644
--- a/Source/WebCore/Modules/webaudio/MediaStreamAudioDestinationNode.cpp
+++ b/Source/WebCore/Modules/webaudio/MediaStreamAudioDestinationNode.cpp
@@ -23,42 +23,40 @@
*/
#include "config.h"
+#include "MediaStreamAudioDestinationNode.h"
#if ENABLE(WEB_AUDIO) && ENABLE(MEDIA_STREAM)
-#include "MediaStreamAudioDestinationNode.h"
-
#include "AudioContext.h"
#include "AudioNodeInput.h"
#include "MediaStream.h"
#include "MediaStreamAudioSource.h"
-#include "RTCPeerConnectionHandler.h"
#include <wtf/Locker.h>
namespace WebCore {
-PassRefPtr<MediaStreamAudioDestinationNode> MediaStreamAudioDestinationNode::create(AudioContext* context, size_t numberOfChannels)
+Ref<MediaStreamAudioDestinationNode> MediaStreamAudioDestinationNode::create(AudioContext& context, size_t numberOfChannels)
{
- return adoptRef(new MediaStreamAudioDestinationNode(context, numberOfChannels));
+ return adoptRef(*new MediaStreamAudioDestinationNode(context, numberOfChannels));
}
-MediaStreamAudioDestinationNode::MediaStreamAudioDestinationNode(AudioContext* context, size_t numberOfChannels)
- : AudioBasicInspectorNode(context, context->sampleRate(), numberOfChannels)
+MediaStreamAudioDestinationNode::MediaStreamAudioDestinationNode(AudioContext& context, size_t numberOfChannels)
+ : AudioBasicInspectorNode(context, context.sampleRate(), numberOfChannels)
, m_mixBus(AudioBus::create(numberOfChannels, ProcessingSizeInFrames))
{
setNodeType(NodeTypeMediaStreamAudioDestination);
m_source = MediaStreamAudioSource::create();
- Vector<RefPtr<MediaStreamSource>> audioSources;
- audioSources.append(m_source);
- m_stream = MediaStream::create(*context->scriptExecutionContext(), MediaStreamPrivate::create(audioSources, Vector<RefPtr<MediaStreamSource>>()));
+ Vector<Ref<RealtimeMediaSource>> audioSources;
+ audioSources.append(*m_source);
+ m_stream = MediaStream::create(*context.scriptExecutionContext(), MediaStreamPrivate::create(audioSources, { }));
- m_source->setAudioFormat(numberOfChannels, context->sampleRate());
+ m_source->setAudioFormat(numberOfChannels, context.sampleRate());
initialize();
}
-MediaStreamSource* MediaStreamAudioDestinationNode::mediaStreamSource()
+RealtimeMediaSource* MediaStreamAudioDestinationNode::mediaStreamSource()
{
return m_source.get();
}
diff --git a/Source/WebCore/Modules/webaudio/MediaStreamAudioDestinationNode.h b/Source/WebCore/Modules/webaudio/MediaStreamAudioDestinationNode.h
index 7f300076f..f17c10f2a 100644
--- a/Source/WebCore/Modules/webaudio/MediaStreamAudioDestinationNode.h
+++ b/Source/WebCore/Modules/webaudio/MediaStreamAudioDestinationNode.h
@@ -22,15 +22,13 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef MediaStreamAudioDestinationNode_h
-#define MediaStreamAudioDestinationNode_h
+#pragma once
#if ENABLE(WEB_AUDIO) && ENABLE(MEDIA_STREAM)
#include "AudioBasicInspectorNode.h"
#include "AudioBus.h"
#include "MediaStream.h"
-#include <wtf/PassRefPtr.h>
namespace WebCore {
@@ -39,26 +37,26 @@ class MediaStreamAudioSource;
class MediaStreamAudioDestinationNode : public AudioBasicInspectorNode {
public:
- static PassRefPtr<MediaStreamAudioDestinationNode> create(AudioContext*, size_t numberOfChannels);
+ static Ref<MediaStreamAudioDestinationNode> create(AudioContext&, size_t numberOfChannels);
virtual ~MediaStreamAudioDestinationNode();
MediaStream* stream() { return m_stream.get(); }
// AudioNode.
- virtual void process(size_t framesToProcess);
- virtual void reset();
+ void process(size_t framesToProcess) override;
+ void reset() override;
- MediaStreamSource* mediaStreamSource();
+ RealtimeMediaSource* mediaStreamSource();
private:
- MediaStreamAudioDestinationNode(AudioContext*, size_t numberOfChannels);
+ MediaStreamAudioDestinationNode(AudioContext&, size_t numberOfChannels);
- virtual double tailTime() const override { return 0; }
- virtual double latencyTime() const override { return 0; }
+ double tailTime() const override { return 0; }
+ double latencyTime() const override { return 0; }
// As an audio source, we will never propagate silence.
- virtual bool propagatesSilence() const override { return false; }
+ bool propagatesSilence() const override { return false; }
RefPtr<MediaStream> m_stream;
RefPtr<MediaStreamAudioSource> m_source;
@@ -68,5 +66,3 @@ private:
} // namespace WebCore
#endif // ENABLE(WEB_AUDIO) && ENABLE(MEDIA_STREAM)
-
-#endif // MediaStreamAudioDestinationNode_h
diff --git a/Source/WebCore/Modules/webaudio/MediaStreamAudioSource.cpp b/Source/WebCore/Modules/webaudio/MediaStreamAudioSource.cpp
index ffabd5ed2..4c274aee5 100644
--- a/Source/WebCore/Modules/webaudio/MediaStreamAudioSource.cpp
+++ b/Source/WebCore/Modules/webaudio/MediaStreamAudioSource.cpp
@@ -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,27 +24,27 @@
*/
#include "config.h"
+#include "MediaStreamAudioSource.h"
#if ENABLE(MEDIA_STREAM)
-#include "MediaStreamAudioSource.h"
-
+#include "AudioSourceProvider.h"
#include "NotImplemented.h"
#include "UUID.h"
namespace WebCore {
-RefPtr<MediaStreamAudioSource> MediaStreamAudioSource::create()
+Ref<MediaStreamAudioSource> MediaStreamAudioSource::create()
{
- return adoptRef(new MediaStreamAudioSource());
+ return adoptRef(*new MediaStreamAudioSource());
}
MediaStreamAudioSource::MediaStreamAudioSource()
- : MediaStreamSource(ASCIILiteral("WebAudio-") + createCanonicalUUIDString(), MediaStreamSource::Audio, "MediaStreamAudioDestinationNode")
+ : RealtimeMediaSource(ASCIILiteral("WebAudio-") + createCanonicalUUIDString(), RealtimeMediaSource::Audio, "MediaStreamAudioDestinationNode")
{
}
-RefPtr<MediaStreamSourceCapabilities> MediaStreamAudioSource::capabilities() const
+RefPtr<RealtimeMediaSourceCapabilities> MediaStreamAudioSource::capabilities() const
{
// FIXME: implement this.
// https://bugs.webkit.org/show_bug.cgi?id=122430
@@ -52,24 +52,30 @@ RefPtr<MediaStreamSourceCapabilities> MediaStreamAudioSource::capabilities() con
return nullptr;
}
-const MediaStreamSourceStates& MediaStreamAudioSource::states()
+const RealtimeMediaSourceSettings& MediaStreamAudioSource::settings() const
{
// FIXME: implement this.
// https://bugs.webkit.org/show_bug.cgi?id=122430
notImplemented();
- return m_currentStates;
-
+ return m_currentSettings;
+}
+
+AudioSourceProvider* MediaStreamAudioSource::audioSourceProvider()
+{
+ // FIXME: implement this.
+ notImplemented();
+ return nullptr;
}
-void MediaStreamAudioSource::addAudioConsumer(PassRefPtr<AudioDestinationConsumer> consumer)
+void MediaStreamAudioSource::addAudioConsumer(AudioDestinationConsumer* consumer)
{
- MutexLocker locker(m_audioConsumersLock);
+ LockHolder locker(m_audioConsumersLock);
m_audioConsumers.append(consumer);
}
bool MediaStreamAudioSource::removeAudioConsumer(AudioDestinationConsumer* consumer)
{
- MutexLocker locker(m_audioConsumersLock);
+ LockHolder locker(m_audioConsumersLock);
size_t pos = m_audioConsumers.find(consumer);
if (pos != notFound) {
m_audioConsumers.remove(pos);
@@ -80,16 +86,16 @@ bool MediaStreamAudioSource::removeAudioConsumer(AudioDestinationConsumer* consu
void MediaStreamAudioSource::setAudioFormat(size_t numberOfChannels, float sampleRate)
{
- MutexLocker locker(m_audioConsumersLock);
- for (Vector<RefPtr<AudioDestinationConsumer>>::iterator it = m_audioConsumers.begin(); it != m_audioConsumers.end(); ++it)
- (*it)->setFormat(numberOfChannels, sampleRate);
+ LockHolder locker(m_audioConsumersLock);
+ for (auto& consumer : m_audioConsumers)
+ consumer->setFormat(numberOfChannels, sampleRate);
}
void MediaStreamAudioSource::consumeAudio(AudioBus* bus, size_t numberOfFrames)
{
- MutexLocker locker(m_audioConsumersLock);
- for (Vector<RefPtr<AudioDestinationConsumer>>::iterator it = m_audioConsumers.begin(); it != m_audioConsumers.end(); ++it)
- (*it)->consumeAudio(bus, numberOfFrames);
+ LockHolder locker(m_audioConsumersLock);
+ for (auto& consumer : m_audioConsumers)
+ consumer->consumeAudio(bus, numberOfFrames);
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/webaudio/MediaStreamAudioSource.h b/Source/WebCore/Modules/webaudio/MediaStreamAudioSource.h
index 5cebac1b7..a3eed5147 100644
--- a/Source/WebCore/Modules/webaudio/MediaStreamAudioSource.h
+++ b/Source/WebCore/Modules/webaudio/MediaStreamAudioSource.h
@@ -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
@@ -23,13 +23,13 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef MediaStreamAudioSource_h
-#define MediaStreamAudioSource_h
+#pragma once
#if ENABLE(MEDIA_STREAM)
#include "AudioDestinationConsumer.h"
-#include "MediaStreamSource.h"
+#include "RealtimeMediaSource.h"
+#include <wtf/Lock.h>
#include <wtf/RefCounted.h>
#include <wtf/ThreadingPrimitives.h>
#include <wtf/Vector.h>
@@ -38,40 +38,38 @@
namespace WebCore {
class AudioBus;
-class MediaStreamSourceCapabilities;
+class RealtimeMediaSourceCapabilities;
-class MediaStreamAudioSource : public MediaStreamSource {
+class MediaStreamAudioSource final : public RealtimeMediaSource {
public:
- static RefPtr<MediaStreamAudioSource> create();
+ static Ref<MediaStreamAudioSource> create();
~MediaStreamAudioSource() { }
- virtual bool useIDForTrackID() const { return true; }
+ RefPtr<RealtimeMediaSourceCapabilities> capabilities() const final;
+ const RealtimeMediaSourceSettings& settings() const final;
- virtual RefPtr<MediaStreamSourceCapabilities> capabilities() const;
- virtual const MediaStreamSourceStates& states();
-
const String& deviceId() const { return m_deviceId; }
void setDeviceId(const String& deviceId) { m_deviceId = deviceId; }
void setAudioFormat(size_t numberOfChannels, float sampleRate);
void consumeAudio(AudioBus*, size_t numberOfFrames);
- void addAudioConsumer(PassRefPtr<AudioDestinationConsumer>);
+ void addAudioConsumer(AudioDestinationConsumer*);
bool removeAudioConsumer(AudioDestinationConsumer*);
const Vector<RefPtr<AudioDestinationConsumer>>& audioConsumers() const { return m_audioConsumers; }
private:
MediaStreamAudioSource();
+ AudioSourceProvider* audioSourceProvider() override;
+
String m_deviceId;
- Mutex m_audioConsumersLock;
+ Lock m_audioConsumersLock;
Vector<RefPtr<AudioDestinationConsumer>> m_audioConsumers;
- MediaStreamSourceStates m_currentStates;
+ RealtimeMediaSourceSettings m_currentSettings;
};
} // namespace WebCore
#endif // ENABLE(MEDIA_STREAM)
-
-#endif // MediaStreamAudioSource_h
diff --git a/Source/WebCore/Modules/webaudio/MediaStreamAudioSourceNode.cpp b/Source/WebCore/Modules/webaudio/MediaStreamAudioSourceNode.cpp
index 867212e51..f640a6826 100644
--- a/Source/WebCore/Modules/webaudio/MediaStreamAudioSourceNode.cpp
+++ b/Source/WebCore/Modules/webaudio/MediaStreamAudioSourceNode.cpp
@@ -35,18 +35,21 @@
namespace WebCore {
-PassRefPtr<MediaStreamAudioSourceNode> MediaStreamAudioSourceNode::create(AudioContext* context, MediaStream* mediaStream, MediaStreamTrack* audioTrack, AudioSourceProvider* audioSourceProvider)
+Ref<MediaStreamAudioSourceNode> MediaStreamAudioSourceNode::create(AudioContext& context, MediaStream& mediaStream, MediaStreamTrack& audioTrack)
{
- return adoptRef(new MediaStreamAudioSourceNode(context, mediaStream, audioTrack, audioSourceProvider));
+ return adoptRef(*new MediaStreamAudioSourceNode(context, mediaStream, audioTrack));
}
-MediaStreamAudioSourceNode::MediaStreamAudioSourceNode(AudioContext* context, MediaStream* mediaStream, MediaStreamTrack* audioTrack, AudioSourceProvider* audioSourceProvider)
- : AudioNode(context, context->sampleRate())
+MediaStreamAudioSourceNode::MediaStreamAudioSourceNode(AudioContext& context, MediaStream& mediaStream, MediaStreamTrack& audioTrack)
+ : AudioNode(context, context.sampleRate())
, m_mediaStream(mediaStream)
, m_audioTrack(audioTrack)
- , m_audioSourceProvider(audioSourceProvider)
- , m_sourceNumberOfChannels(0)
{
+ AudioSourceProvider* audioSourceProvider = m_audioTrack->audioSourceProvider();
+ ASSERT(audioSourceProvider);
+
+ audioSourceProvider->setClient(this);
+
// Default to stereo. This could change depending on the format of the MediaStream's audio track.
addOutput(std::make_unique<AudioNodeOutput>(this, 2));
@@ -57,45 +60,56 @@ MediaStreamAudioSourceNode::MediaStreamAudioSourceNode(AudioContext* context, Me
MediaStreamAudioSourceNode::~MediaStreamAudioSourceNode()
{
+ AudioSourceProvider* audioSourceProvider = m_audioTrack->audioSourceProvider();
+ ASSERT(audioSourceProvider);
+ audioSourceProvider->setClient(nullptr);
uninitialize();
}
void MediaStreamAudioSourceNode::setFormat(size_t numberOfChannels, float sourceSampleRate)
{
- if (numberOfChannels != m_sourceNumberOfChannels || sourceSampleRate != sampleRate()) {
- // The sample-rate must be equal to the context's sample-rate.
- if (!numberOfChannels || numberOfChannels > AudioContext::maxNumberOfChannels() || sourceSampleRate != sampleRate()) {
- // process() will generate silence for these uninitialized values.
- LOG(Media, "MediaStreamAudioSourceNode::setFormat(%u, %f) - unhandled format change", static_cast<unsigned>(numberOfChannels), sourceSampleRate);
- m_sourceNumberOfChannels = 0;
- return;
- }
-
- // Synchronize with process().
- std::lock_guard<std::mutex> lock(m_processMutex);
-
- m_sourceNumberOfChannels = numberOfChannels;
-
- {
- // The context must be locked when changing the number of output channels.
- AudioContext::AutoLocker contextLocker(*context());
-
- // Do any necesssary re-configuration to the output's number of channels.
- output(0)->setNumberOfChannels(numberOfChannels);
- }
+ float sampleRate = this->sampleRate();
+ if (numberOfChannels == m_sourceNumberOfChannels && sourceSampleRate == sampleRate)
+ return;
+
+ // The sample-rate must be equal to the context's sample-rate.
+ if (!numberOfChannels || numberOfChannels > AudioContext::maxNumberOfChannels() || sourceSampleRate != sampleRate) {
+ // process() will generate silence for these uninitialized values.
+ LOG(Media, "MediaStreamAudioSourceNode::setFormat(%u, %f) - unhandled format change", static_cast<unsigned>(numberOfChannels), sourceSampleRate);
+ m_sourceNumberOfChannels = 0;
+ return;
+ }
+
+ // Synchronize with process().
+ std::lock_guard<Lock> lock(m_processMutex);
+
+ m_sourceNumberOfChannels = numberOfChannels;
+ m_sourceSampleRate = sourceSampleRate;
+
+ if (sourceSampleRate == sampleRate)
+ m_multiChannelResampler = nullptr;
+ else {
+ double scaleFactor = sourceSampleRate / sampleRate;
+ m_multiChannelResampler = std::make_unique<MultiChannelResampler>(scaleFactor, numberOfChannels);
+ }
+
+ m_sourceNumberOfChannels = numberOfChannels;
+
+ {
+ // The context must be locked when changing the number of output channels.
+ AudioContext::AutoLocker contextLocker(context());
+
+ // Do any necesssary re-configuration to the output's number of channels.
+ output(0)->setNumberOfChannels(numberOfChannels);
}
}
void MediaStreamAudioSourceNode::process(size_t numberOfFrames)
{
AudioBus* outputBus = output(0)->bus();
+ AudioSourceProvider* provider = m_audioTrack->audioSourceProvider();
- if (!audioSourceProvider()) {
- outputBus->zero();
- return;
- }
-
- if (!mediaStream() || m_sourceNumberOfChannels != outputBus->numberOfChannels()) {
+ if (!mediaStream() || !m_sourceNumberOfChannels || !m_sourceSampleRate || !provider) {
outputBus->zero();
return;
}
@@ -103,18 +117,21 @@ void MediaStreamAudioSourceNode::process(size_t numberOfFrames)
// Use std::try_to_lock to avoid contention in the real-time audio thread.
// If we fail to acquire the lock then the MediaStream must be in the middle of
// a format change, so we output silence in this case.
- std::unique_lock<std::mutex> lock(m_processMutex, std::try_to_lock);
+ std::unique_lock<Lock> lock(m_processMutex, std::try_to_lock);
if (!lock.owns_lock()) {
// We failed to acquire the lock.
outputBus->zero();
return;
}
- audioSourceProvider()->provideInput(outputBus, numberOfFrames);
-}
-
-void MediaStreamAudioSourceNode::reset()
-{
+ if (m_multiChannelResampler.get()) {
+ ASSERT(m_sourceSampleRate != sampleRate());
+ m_multiChannelResampler->process(provider, outputBus, numberOfFrames);
+ } else {
+ // Bypass the resampler completely if the source is at the context's sample-rate.
+ ASSERT(m_sourceSampleRate == sampleRate());
+ provider->provideInput(outputBus, numberOfFrames);
+ }
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/webaudio/MediaStreamAudioSourceNode.h b/Source/WebCore/Modules/webaudio/MediaStreamAudioSourceNode.h
index 7ff95adfe..bcdcfc66f 100644
--- a/Source/WebCore/Modules/webaudio/MediaStreamAudioSourceNode.h
+++ b/Source/WebCore/Modules/webaudio/MediaStreamAudioSourceNode.h
@@ -22,8 +22,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef MediaStreamAudioSourceNode_h
-#define MediaStreamAudioSourceNode_h
+#pragma once
#if ENABLE(WEB_AUDIO) && ENABLE(MEDIA_STREAM)
@@ -31,50 +30,49 @@
#include "AudioSourceProvider.h"
#include "AudioSourceProviderClient.h"
#include "MediaStream.h"
-#include <mutex>
-#include <wtf/PassRefPtr.h>
+#include "MultiChannelResampler.h"
+#include <wtf/Lock.h>
+#include <wtf/RefPtr.h>
namespace WebCore {
class AudioContext;
+class MultiChannelResampler;
class MediaStreamAudioSourceNode : public AudioNode, public AudioSourceProviderClient {
public:
- static PassRefPtr<MediaStreamAudioSourceNode> create(AudioContext*, MediaStream*, MediaStreamTrack*, AudioSourceProvider*);
+ static Ref<MediaStreamAudioSourceNode> create(AudioContext&, MediaStream&, MediaStreamTrack&);
virtual ~MediaStreamAudioSourceNode();
- MediaStream* mediaStream() { return m_mediaStream.get(); }
+ MediaStream* mediaStream() { return &m_mediaStream.get(); }
// AudioNode
- virtual void process(size_t framesToProcess);
- virtual void reset();
+ void process(size_t framesToProcess) override;
+ void reset() override { }
// AudioSourceProviderClient
- virtual void setFormat(size_t numberOfChannels, float sampleRate);
-
- AudioSourceProvider* audioSourceProvider() const { return m_audioSourceProvider; }
+ void setFormat(size_t numberOfChannels, float sampleRate) override;
private:
- MediaStreamAudioSourceNode(AudioContext*, MediaStream*, MediaStreamTrack*, AudioSourceProvider*);
+ MediaStreamAudioSourceNode(AudioContext&, MediaStream&, MediaStreamTrack&);
- virtual double tailTime() const override { return 0; }
- virtual double latencyTime() const override { return 0; }
+ double tailTime() const override { return 0; }
+ double latencyTime() const override { return 0; }
// As an audio source, we will never propagate silence.
- virtual bool propagatesSilence() const override { return false; }
+ bool propagatesSilence() const override { return false; }
- RefPtr<MediaStream> m_mediaStream;
- RefPtr<MediaStreamTrack> m_audioTrack;
- AudioSourceProvider* m_audioSourceProvider;
+ Ref<MediaStream> m_mediaStream;
+ Ref<MediaStreamTrack> m_audioTrack;
+ std::unique_ptr<MultiChannelResampler> m_multiChannelResampler;
- std::mutex m_processMutex;
+ Lock m_processMutex;
- unsigned m_sourceNumberOfChannels;
+ unsigned m_sourceNumberOfChannels { 0 };
+ double m_sourceSampleRate { 0 };
};
} // namespace WebCore
#endif // ENABLE(WEB_AUDIO) && ENABLE(MEDIA_STREAM)
-
-#endif // MediaStreamAudioSourceNode_h
diff --git a/Source/WebCore/Modules/webaudio/OfflineAudioCompletionEvent.cpp b/Source/WebCore/Modules/webaudio/OfflineAudioCompletionEvent.cpp
index 7db5d78b7..f56ae51aa 100644
--- a/Source/WebCore/Modules/webaudio/OfflineAudioCompletionEvent.cpp
+++ b/Source/WebCore/Modules/webaudio/OfflineAudioCompletionEvent.cpp
@@ -33,24 +33,14 @@
namespace WebCore {
-PassRefPtr<OfflineAudioCompletionEvent> OfflineAudioCompletionEvent::create()
+Ref<OfflineAudioCompletionEvent> OfflineAudioCompletionEvent::create(RefPtr<AudioBuffer>&& renderedBuffer)
{
- return adoptRef(new OfflineAudioCompletionEvent);
+ return adoptRef(*new OfflineAudioCompletionEvent(WTFMove(renderedBuffer)));
}
-PassRefPtr<OfflineAudioCompletionEvent> OfflineAudioCompletionEvent::create(PassRefPtr<AudioBuffer> renderedBuffer)
-{
- return adoptRef(new OfflineAudioCompletionEvent(renderedBuffer));
-}
-
-OfflineAudioCompletionEvent::OfflineAudioCompletionEvent()
-{
-}
-
-
-OfflineAudioCompletionEvent::OfflineAudioCompletionEvent(PassRefPtr<AudioBuffer> renderedBuffer)
+OfflineAudioCompletionEvent::OfflineAudioCompletionEvent(RefPtr<AudioBuffer>&& renderedBuffer)
: Event(eventNames().completeEvent, true, false)
- , m_renderedBuffer(renderedBuffer)
+ , m_renderedBuffer(WTFMove(renderedBuffer))
{
}
diff --git a/Source/WebCore/Modules/webaudio/OfflineAudioCompletionEvent.h b/Source/WebCore/Modules/webaudio/OfflineAudioCompletionEvent.h
index 72a7b7921..7ac3b0e2c 100644
--- a/Source/WebCore/Modules/webaudio/OfflineAudioCompletionEvent.h
+++ b/Source/WebCore/Modules/webaudio/OfflineAudioCompletionEvent.h
@@ -22,36 +22,28 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef OfflineAudioCompletionEvent_h
-#define OfflineAudioCompletionEvent_h
+#pragma once
#include "AudioBuffer.h"
#include "Event.h"
-#include <wtf/PassRefPtr.h>
#include <wtf/RefPtr.h>
namespace WebCore {
-class AudioBuffer;
-
class OfflineAudioCompletionEvent : public Event {
public:
- static PassRefPtr<OfflineAudioCompletionEvent> create();
- static PassRefPtr<OfflineAudioCompletionEvent> create(PassRefPtr<AudioBuffer> renderedBuffer);
+ static Ref<OfflineAudioCompletionEvent> create(RefPtr<AudioBuffer>&& renderedBuffer);
virtual ~OfflineAudioCompletionEvent();
AudioBuffer* renderedBuffer() { return m_renderedBuffer.get(); }
- virtual EventInterface eventInterface() const override;
+ EventInterface eventInterface() const override;
private:
- OfflineAudioCompletionEvent();
- explicit OfflineAudioCompletionEvent(PassRefPtr<AudioBuffer> renderedBuffer);
+ explicit OfflineAudioCompletionEvent(RefPtr<AudioBuffer>&& renderedBuffer);
RefPtr<AudioBuffer> m_renderedBuffer;
};
} // namespace WebCore
-
-#endif // OfflineAudioCompletionEvent_h
diff --git a/Source/WebCore/Modules/webaudio/OfflineAudioContext.cpp b/Source/WebCore/Modules/webaudio/OfflineAudioContext.cpp
index 3bece3019..ba5e49fae 100644
--- a/Source/WebCore/Modules/webaudio/OfflineAudioContext.cpp
+++ b/Source/WebCore/Modules/webaudio/OfflineAudioContext.cpp
@@ -29,38 +29,24 @@
#include "OfflineAudioContext.h"
#include "Document.h"
-#include "ExceptionCode.h"
-#include "ScriptExecutionContext.h"
namespace WebCore {
-PassRefPtr<OfflineAudioContext> OfflineAudioContext::create(ScriptExecutionContext& context, unsigned numberOfChannels, size_t numberOfFrames, float sampleRate, ExceptionCode& ec)
-{
- // FIXME: add support for workers.
- if (!context.isDocument()) {
- ec = NOT_SUPPORTED_ERR;
- return nullptr;
- }
-
- Document& document = toDocument(context);
-
- if (numberOfChannels > 10 || !isSampleRateRangeGood(sampleRate)) {
- ec = SYNTAX_ERR;
- return nullptr;
- }
-
- RefPtr<OfflineAudioContext> audioContext(adoptRef(new OfflineAudioContext(document, numberOfChannels, numberOfFrames, sampleRate)));
- audioContext->suspendIfNeeded();
- return audioContext.release();
-}
-
-OfflineAudioContext::OfflineAudioContext(Document& document, unsigned numberOfChannels, size_t numberOfFrames, float sampleRate)
+inline OfflineAudioContext::OfflineAudioContext(Document& document, unsigned numberOfChannels, size_t numberOfFrames, float sampleRate)
: AudioContext(document, numberOfChannels, numberOfFrames, sampleRate)
{
}
-OfflineAudioContext::~OfflineAudioContext()
+ExceptionOr<Ref<OfflineAudioContext>> OfflineAudioContext::create(ScriptExecutionContext& context, unsigned numberOfChannels, size_t numberOfFrames, float sampleRate)
{
+ // FIXME: Add support for workers.
+ if (!is<Document>(context))
+ return Exception { NOT_SUPPORTED_ERR };
+ if (!numberOfChannels || numberOfChannels > 10 || !numberOfFrames || !isSampleRateRangeGood(sampleRate))
+ return Exception { SYNTAX_ERR };
+ auto audioContext = adoptRef(*new OfflineAudioContext(downcast<Document>(context), numberOfChannels, numberOfFrames, sampleRate));
+ audioContext->suspendIfNeeded();
+ return WTFMove(audioContext);
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/webaudio/OfflineAudioContext.h b/Source/WebCore/Modules/webaudio/OfflineAudioContext.h
index 35bbc9663..5d2c1d4f9 100644
--- a/Source/WebCore/Modules/webaudio/OfflineAudioContext.h
+++ b/Source/WebCore/Modules/webaudio/OfflineAudioContext.h
@@ -22,23 +22,18 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef OfflineAudioContext_h
-#define OfflineAudioContext_h
+#pragma once
#include "AudioContext.h"
namespace WebCore {
-class OfflineAudioContext : public AudioContext {
+class OfflineAudioContext final : public AudioContext {
public:
- static PassRefPtr<OfflineAudioContext> create(ScriptExecutionContext&, unsigned numberOfChannels, size_t numberOfFrames, float sampleRate, ExceptionCode&);
-
- virtual ~OfflineAudioContext();
+ static ExceptionOr<Ref<OfflineAudioContext>> create(ScriptExecutionContext&, unsigned numberOfChannels, size_t numberOfFrames, float sampleRate);
private:
OfflineAudioContext(Document&, unsigned numberOfChannels, size_t numberOfFrames, float sampleRate);
};
} // namespace WebCore
-
-#endif // OfflineAudioContext_h
diff --git a/Source/WebCore/Modules/webaudio/OfflineAudioContext.idl b/Source/WebCore/Modules/webaudio/OfflineAudioContext.idl
index e4ecd5d8d..c6e0d74f0 100644
--- a/Source/WebCore/Modules/webaudio/OfflineAudioContext.idl
+++ b/Source/WebCore/Modules/webaudio/OfflineAudioContext.idl
@@ -24,12 +24,10 @@
[
Conditional=WEB_AUDIO,
- EventTarget,
JSGenerateToJSObject,
- Constructor(unsigned long numberOfChannels, unsigned long numberOfFrames, float sampleRate),
- ConstructorRaisesException,
+ Constructor(unsigned long numberOfChannels, unsigned long numberOfFrames, unrestricted float sampleRate),
+ ConstructorMayThrowException,
ConstructorCallWith=ScriptExecutionContext,
InterfaceName=webkitOfflineAudioContext
] interface OfflineAudioContext : AudioContext {
-
};
diff --git a/Source/WebCore/Modules/webaudio/OfflineAudioDestinationNode.cpp b/Source/WebCore/Modules/webaudio/OfflineAudioDestinationNode.cpp
index fd3123a91..505230419 100644
--- a/Source/WebCore/Modules/webaudio/OfflineAudioDestinationNode.cpp
+++ b/Source/WebCore/Modules/webaudio/OfflineAudioDestinationNode.cpp
@@ -38,7 +38,7 @@ namespace WebCore {
const size_t renderQuantumSize = 128;
-OfflineAudioDestinationNode::OfflineAudioDestinationNode(AudioContext* context, AudioBuffer* renderTarget)
+OfflineAudioDestinationNode::OfflineAudioDestinationNode(AudioContext& context, AudioBuffer* renderTarget)
: AudioDestinationNode(context, renderTarget->sampleRate())
, m_renderTarget(renderTarget)
, m_renderThread(0)
@@ -101,7 +101,12 @@ void OfflineAudioDestinationNode::offlineRender()
ASSERT(m_renderBus.get());
if (!m_renderBus.get())
return;
-
+
+ bool isAudioContextInitialized = context().isInitialized();
+ ASSERT(isAudioContextInitialized);
+ if (!isAudioContextInitialized)
+ return;
+
bool channelsMatch = m_renderBus->numberOfChannels() == m_renderTarget->numberOfChannels();
ASSERT(channelsMatch);
if (!channelsMatch)
@@ -112,15 +117,6 @@ void OfflineAudioDestinationNode::offlineRender()
if (!isRenderBusAllocated)
return;
- // Synchronize with HRTFDatabaseLoader.
- // The database must be loaded before we can proceed.
- HRTFDatabaseLoader* loader = context()->hrtfDatabaseLoader();
- ASSERT(loader);
- if (!loader)
- return;
-
- loader->waitForLoaderThreadCompletion();
-
// Break up the render target into smaller "render quantize" sized pieces.
// Render until we're finished.
size_t framesToProcess = m_renderTarget->length();
@@ -135,7 +131,7 @@ void OfflineAudioDestinationNode::offlineRender()
for (unsigned channelIndex = 0; channelIndex < numberOfChannels; ++channelIndex) {
const float* source = m_renderBus->channel(channelIndex)->data();
- float* destination = m_renderTarget->getChannelData(channelIndex)->data();
+ float* destination = m_renderTarget->channelData(channelIndex)->data();
memcpy(destination + n, source, sizeof(float) * framesAvailableToCopy);
}
@@ -144,23 +140,15 @@ void OfflineAudioDestinationNode::offlineRender()
}
// Our work is done. Let the AudioContext know.
- callOnMainThread(notifyCompleteDispatch, this);
-}
-
-void OfflineAudioDestinationNode::notifyCompleteDispatch(void* userData)
-{
- OfflineAudioDestinationNode* destinationNode = static_cast<OfflineAudioDestinationNode*>(userData);
- ASSERT(destinationNode);
- if (!destinationNode)
- return;
-
- destinationNode->notifyComplete();
- destinationNode->deref();
+ callOnMainThread([this] {
+ notifyComplete();
+ deref();
+ });
}
void OfflineAudioDestinationNode::notifyComplete()
{
- context()->fireCompletionEvent();
+ context().fireCompletionEvent();
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/webaudio/OfflineAudioDestinationNode.h b/Source/WebCore/Modules/webaudio/OfflineAudioDestinationNode.h
index eb0ca426c..755604647 100644
--- a/Source/WebCore/Modules/webaudio/OfflineAudioDestinationNode.h
+++ b/Source/WebCore/Modules/webaudio/OfflineAudioDestinationNode.h
@@ -22,12 +22,10 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef OfflineAudioDestinationNode_h
-#define OfflineAudioDestinationNode_h
+#pragma once
#include "AudioBuffer.h"
#include "AudioDestinationNode.h"
-#include <wtf/PassRefPtr.h>
#include <wtf/RefPtr.h>
#include <wtf/Threading.h>
@@ -38,25 +36,25 @@ class AudioContext;
class OfflineAudioDestinationNode : public AudioDestinationNode {
public:
- static PassRefPtr<OfflineAudioDestinationNode> create(AudioContext* context, AudioBuffer* renderTarget)
+ static Ref<OfflineAudioDestinationNode> create(AudioContext& context, AudioBuffer* renderTarget)
{
- return adoptRef(new OfflineAudioDestinationNode(context, renderTarget));
+ return adoptRef(*new OfflineAudioDestinationNode(context, renderTarget));
}
virtual ~OfflineAudioDestinationNode();
// AudioNode
- virtual void initialize() override;
- virtual void uninitialize() override;
+ void initialize() override;
+ void uninitialize() override;
// AudioDestinationNode
- virtual void enableInput(const String&) override { }
- virtual void startRendering() override;
+ void enableInput(const String&) override { }
+ void startRendering() override;
- virtual float sampleRate() const override { return m_renderTarget->sampleRate(); }
+ float sampleRate() const override { return m_renderTarget->sampleRate(); }
private:
- OfflineAudioDestinationNode(AudioContext*, AudioBuffer* renderTarget);
+ OfflineAudioDestinationNode(AudioContext&, AudioBuffer* renderTarget);
// This AudioNode renders into this AudioBuffer.
RefPtr<AudioBuffer> m_renderTarget;
@@ -71,10 +69,7 @@ private:
void offlineRender();
// For completion callback on main thread.
- static void notifyCompleteDispatch(void* userData);
void notifyComplete();
};
} // namespace WebCore
-
-#endif // OfflineAudioDestinationNode_h
diff --git a/Source/WebCore/Modules/webaudio/OscillatorNode.cpp b/Source/WebCore/Modules/webaudio/OscillatorNode.cpp
index 070dc9e1f..d38f9f3c6 100644
--- a/Source/WebCore/Modules/webaudio/OscillatorNode.cpp
+++ b/Source/WebCore/Modules/webaudio/OscillatorNode.cpp
@@ -28,32 +28,27 @@
#include "OscillatorNode.h"
-#include "AudioContext.h"
#include "AudioNodeOutput.h"
-#include "AudioUtilities.h"
-#include "ExceptionCode.h"
+#include "AudioParam.h"
#include "PeriodicWave.h"
#include "VectorMath.h"
-#include <algorithm>
-#include <wtf/MathExtras.h>
namespace WebCore {
using namespace VectorMath;
-PeriodicWave* OscillatorNode::s_periodicWaveSine = 0;
-PeriodicWave* OscillatorNode::s_periodicWaveSquare = 0;
-PeriodicWave* OscillatorNode::s_periodicWaveSawtooth = 0;
-PeriodicWave* OscillatorNode::s_periodicWaveTriangle = 0;
+PeriodicWave* OscillatorNode::s_periodicWaveSine = nullptr;
+PeriodicWave* OscillatorNode::s_periodicWaveSquare = nullptr;
+PeriodicWave* OscillatorNode::s_periodicWaveSawtooth = nullptr;
+PeriodicWave* OscillatorNode::s_periodicWaveTriangle = nullptr;
-PassRefPtr<OscillatorNode> OscillatorNode::create(AudioContext* context, float sampleRate)
+Ref<OscillatorNode> OscillatorNode::create(AudioContext& context, float sampleRate)
{
- return adoptRef(new OscillatorNode(context, sampleRate));
+ return adoptRef(*new OscillatorNode(context, sampleRate));
}
-OscillatorNode::OscillatorNode(AudioContext* context, float sampleRate)
+OscillatorNode::OscillatorNode(AudioContext& context, float sampleRate)
: AudioScheduledSourceNode(context, sampleRate)
- , m_type(SINE)
, m_firstRender(true)
, m_virtualReadIndex(0)
, m_phaseIncrements(AudioNode::ProcessingSizeInFrames)
@@ -80,75 +75,41 @@ OscillatorNode::~OscillatorNode()
uninitialize();
}
-String OscillatorNode::type() const
+ExceptionOr<void> OscillatorNode::setType(Type type)
{
- switch (m_type) {
- case SINE:
- return "sine";
- case SQUARE:
- return "square";
- case SAWTOOTH:
- return "sawtooth";
- case TRIANGLE:
- return "triangle";
- case CUSTOM:
- return "custom";
- default:
- ASSERT_NOT_REACHED();
- return "custom";
- }
-}
-
-void OscillatorNode::setType(const String& type)
-{
- if (type == "sine")
- setType(SINE);
- else if (type == "square")
- setType(SQUARE);
- else if (type == "sawtooth")
- setType(SAWTOOTH);
- else if (type == "triangle")
- setType(TRIANGLE);
- else
- ASSERT_NOT_REACHED();
-}
-
-bool OscillatorNode::setType(unsigned type)
-{
- PeriodicWave* periodicWave = 0;
- float sampleRate = this->sampleRate();
+ PeriodicWave* periodicWave = nullptr;
switch (type) {
- case SINE:
+ case Type::Sine:
if (!s_periodicWaveSine)
- s_periodicWaveSine = PeriodicWave::createSine(sampleRate).leakRef();
+ s_periodicWaveSine = &PeriodicWave::createSine(sampleRate()).leakRef();
periodicWave = s_periodicWaveSine;
break;
- case SQUARE:
+ case Type::Square:
if (!s_periodicWaveSquare)
- s_periodicWaveSquare = PeriodicWave::createSquare(sampleRate).leakRef();
+ s_periodicWaveSquare = &PeriodicWave::createSquare(sampleRate()).leakRef();
periodicWave = s_periodicWaveSquare;
break;
- case SAWTOOTH:
+ case Type::Sawtooth:
if (!s_periodicWaveSawtooth)
- s_periodicWaveSawtooth = PeriodicWave::createSawtooth(sampleRate).leakRef();
+ s_periodicWaveSawtooth = &PeriodicWave::createSawtooth(sampleRate()).leakRef();
periodicWave = s_periodicWaveSawtooth;
break;
- case TRIANGLE:
+ case Type::Triangle:
if (!s_periodicWaveTriangle)
- s_periodicWaveTriangle = PeriodicWave::createTriangle(sampleRate).leakRef();
+ s_periodicWaveTriangle = &PeriodicWave::createTriangle(sampleRate()).leakRef();
periodicWave = s_periodicWaveTriangle;
break;
- case CUSTOM:
- default:
- // Return error for invalid types, including CUSTOM since setPeriodicWave() method must be
- // called explicitly.
- return false;
+ case Type::Custom:
+ if (m_type != Type::Custom)
+ return Exception { INVALID_STATE_ERR };
+ return { };
}
setPeriodicWave(periodicWave);
m_type = type;
- return true;
+
+ return { };
}
bool OscillatorNode::calculateSampleAccuratePhaseIncrements(size_t framesToProcess)
@@ -219,10 +180,10 @@ bool OscillatorNode::calculateSampleAccuratePhaseIncrements(size_t framesToProce
void OscillatorNode::process(size_t framesToProcess)
{
- AudioBus* outputBus = output(0)->bus();
+ auto& outputBus = *output(0)->bus();
- if (!isInitialized() || !outputBus->numberOfChannels()) {
- outputBus->zero();
+ if (!isInitialized() || !outputBus.numberOfChannels()) {
+ outputBus.zero();
return;
}
@@ -231,33 +192,32 @@ void OscillatorNode::process(size_t framesToProcess)
return;
// The audio thread can't block on this lock, so we use std::try_to_lock instead.
- std::unique_lock<std::mutex> lock(m_processMutex, std::try_to_lock);
+ std::unique_lock<Lock> lock(m_processMutex, std::try_to_lock);
if (!lock.owns_lock()) {
// Too bad - the try_lock() failed. We must be in the middle of changing wave-tables.
- outputBus->zero();
+ outputBus.zero();
return;
}
// We must access m_periodicWave only inside the lock.
if (!m_periodicWave.get()) {
- outputBus->zero();
+ outputBus.zero();
return;
}
size_t quantumFrameOffset;
size_t nonSilentFramesToProcess;
-
updateSchedulingInfo(framesToProcess, outputBus, quantumFrameOffset, nonSilentFramesToProcess);
if (!nonSilentFramesToProcess) {
- outputBus->zero();
+ outputBus.zero();
return;
}
unsigned periodicWaveSize = m_periodicWave->periodicWaveSize();
double invPeriodicWaveSize = 1.0 / periodicWaveSize;
- float* destP = outputBus->channel(0)->mutableData();
+ float* destP = outputBus.channel(0)->mutableData();
ASSERT(quantumFrameOffset <= framesToProcess);
@@ -269,8 +229,8 @@ void OscillatorNode::process(size_t framesToProcess)
bool hasSampleAccurateValues = calculateSampleAccuratePhaseIncrements(framesToProcess);
float frequency = 0;
- float* higherWaveData = 0;
- float* lowerWaveData = 0;
+ float* higherWaveData = nullptr;
+ float* lowerWaveData = nullptr;
float tableInterpolationFactor;
if (!hasSampleAccurateValues) {
@@ -327,7 +287,7 @@ void OscillatorNode::process(size_t framesToProcess)
m_virtualReadIndex = virtualReadIndex;
- outputBus->clearSilentFlag();
+ outputBus.clearSilentFlag();
}
void OscillatorNode::reset()
@@ -340,9 +300,9 @@ void OscillatorNode::setPeriodicWave(PeriodicWave* periodicWave)
ASSERT(isMainThread());
// This synchronizes with process().
- std::lock_guard<std::mutex> lock(m_processMutex);
+ std::lock_guard<Lock> lock(m_processMutex);
m_periodicWave = periodicWave;
- m_type = CUSTOM;
+ m_type = Type::Custom;
}
bool OscillatorNode::propagatesSilence() const
diff --git a/Source/WebCore/Modules/webaudio/OscillatorNode.h b/Source/WebCore/Modules/webaudio/OscillatorNode.h
index 05969266f..790f01507 100644
--- a/Source/WebCore/Modules/webaudio/OscillatorNode.h
+++ b/Source/WebCore/Modules/webaudio/OscillatorNode.h
@@ -22,47 +22,34 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef OscillatorNode_h
-#define OscillatorNode_h
+#pragma once
-#include "AudioBus.h"
-#include "AudioParam.h"
#include "AudioScheduledSourceNode.h"
-#include <mutex>
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefPtr.h>
+#include <wtf/Lock.h>
namespace WebCore {
-class AudioContext;
class PeriodicWave;
// OscillatorNode is an audio generator of periodic waveforms.
-class OscillatorNode : public AudioScheduledSourceNode {
+class OscillatorNode final : public AudioScheduledSourceNode {
public:
// The waveform type.
- // These must be defined as in the .idl file.
- enum {
- SINE = 0,
- SQUARE = 1,
- SAWTOOTH = 2,
- TRIANGLE = 3,
- CUSTOM = 4
+ enum class Type {
+ Sine,
+ Square,
+ Sawtooth,
+ Triangle,
+ Custom
};
- static PassRefPtr<OscillatorNode> create(AudioContext*, float sampleRate);
+ static Ref<OscillatorNode> create(AudioContext&, float sampleRate);
virtual ~OscillatorNode();
-
- // AudioNode
- virtual void process(size_t framesToProcess) override;
- virtual void reset() override;
-
- String type() const;
- bool setType(unsigned); // Returns true on success.
- void setType(const String&);
+ Type type() const { return m_type; }
+ ExceptionOr<void> setType(Type);
AudioParam* frequency() { return m_frequency.get(); }
AudioParam* detune() { return m_detune.get(); }
@@ -70,18 +57,21 @@ public:
void setPeriodicWave(PeriodicWave*);
private:
- OscillatorNode(AudioContext*, float sampleRate);
+ OscillatorNode(AudioContext&, float sampleRate);
- virtual double tailTime() const override { return 0; }
- virtual double latencyTime() const override { return 0; }
+ void process(size_t framesToProcess) final;
+ void reset() final;
+
+ double tailTime() const final { return 0; }
+ double latencyTime() const final { return 0; }
// Returns true if there are sample-accurate timeline parameter changes.
bool calculateSampleAccuratePhaseIncrements(size_t framesToProcess);
- virtual bool propagatesSilence() const override;
+ bool propagatesSilence() const final;
// One of the waveform types defined in the enum.
- unsigned short m_type;
+ Type m_type { Type::Sine };
// Frequency value in Hertz.
RefPtr<AudioParam> m_frequency;
@@ -96,7 +86,7 @@ private:
double m_virtualReadIndex;
// This synchronizes process().
- mutable std::mutex m_processMutex;
+ mutable Lock m_processMutex;
// Stores sample-accurate values calculated according to frequency and detune.
AudioFloatArray m_phaseIncrements;
@@ -112,5 +102,3 @@ private:
};
} // namespace WebCore
-
-#endif // OscillatorNode_h
diff --git a/Source/WebCore/Modules/webaudio/OscillatorNode.idl b/Source/WebCore/Modules/webaudio/OscillatorNode.idl
index 8bf310d48..b5edc10a4 100644
--- a/Source/WebCore/Modules/webaudio/OscillatorNode.idl
+++ b/Source/WebCore/Modules/webaudio/OscillatorNode.idl
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2012, Google Inc. All rights reserved.
+ * 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
@@ -22,20 +23,19 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-// OscillatorNode is an audio generator of periodic waveforms.
+enum OscillatorType {
+ "sine",
+ "square",
+ "sawtooth",
+ "triangle",
+ "custom"
+};
+
[
Conditional=WEB_AUDIO,
JSGenerateToJSObject,
] interface OscillatorNode : AudioNode {
-
- // Type constants.
- const unsigned short SINE = 0;
- const unsigned short SQUARE = 1;
- const unsigned short SAWTOOTH = 2;
- const unsigned short TRIANGLE = 3;
- const unsigned short CUSTOM = 4;
-
- [CustomSetter] attribute DOMString type;
+ [SetterMayThrowException] attribute OscillatorType type;
// Playback state constants.
const unsigned short UNSCHEDULED_STATE = 0;
@@ -48,13 +48,10 @@
readonly attribute AudioParam frequency; // in Hertz
readonly attribute AudioParam detune; // in Cents
- [RaisesException] void start(double when);
- [RaisesException] void stop(double when);
-
- [Conditional=LEGACY_WEB_AUDIO, RaisesException] void noteOn(double when);
- [Conditional=LEGACY_WEB_AUDIO, RaisesException] void noteOff(double when);
+ [MayThrowException] void start(optional unrestricted double when = 0);
+ [MayThrowException] void stop(optional unrestricted double when = 0);
- void setPeriodicWave(PeriodicWave wave);
+ void setPeriodicWave(PeriodicWave? wave); // FIXME: The parameter should not be nullable.
- attribute EventListener onended;
+ attribute EventHandler onended;
};
diff --git a/Source/WebCore/Modules/webaudio/PannerNode.cpp b/Source/WebCore/Modules/webaudio/PannerNode.cpp
index 17bf13042..d7f92cabc 100644
--- a/Source/WebCore/Modules/webaudio/PannerNode.cpp
+++ b/Source/WebCore/Modules/webaudio/PannerNode.cpp
@@ -23,17 +23,15 @@
*/
#include "config.h"
+#include "PannerNode.h"
#if ENABLE(WEB_AUDIO)
-#include "PannerNode.h"
-
#include "AudioBufferSourceNode.h"
#include "AudioBus.h"
#include "AudioContext.h"
#include "AudioNodeInput.h"
#include "AudioNodeOutput.h"
-#include "ExceptionCode.h"
#include "HRTFPanner.h"
#include "ScriptExecutionContext.h"
#include <wtf/MathExtras.h>
@@ -46,12 +44,15 @@ static void fixNANs(double &x)
x = 0.0;
}
-PannerNode::PannerNode(AudioContext* context, float sampleRate)
+PannerNode::PannerNode(AudioContext& context, float sampleRate)
: AudioNode(context, sampleRate)
- , m_panningModel(Panner::PanningModelHRTF)
+ , m_panningModel(PanningModelType::HRTF)
, m_lastGain(-1.0)
, m_connectionCount(0)
{
+ // Load the HRTF database asynchronously so we don't block the Javascript thread while creating the HRTF database.
+ m_hrtfDatabaseLoader = HRTFDatabaseLoader::createAndLoadAsynchronouslyIfNecessary(context.sampleRate());
+
addInput(std::make_unique<AudioNodeInput>(this));
addOutput(std::make_unique<AudioNodeOutput>(this, 2));
@@ -81,11 +82,12 @@ void PannerNode::pullInputs(size_t framesToProcess)
{
// We override pullInputs(), so we can detect new AudioSourceNodes which have connected to us when new connections are made.
// These AudioSourceNodes need to be made aware of our existence in order to handle doppler shift pitch changes.
- if (m_connectionCount != context()->connectionCount()) {
- m_connectionCount = context()->connectionCount();
+ if (m_connectionCount != context().connectionCount()) {
+ m_connectionCount = context().connectionCount();
// Recursively go through all nodes connected to us.
- notifyAudioSourcesConnectedToNode(this);
+ HashSet<AudioNode*> visitedNodes;
+ notifyAudioSourcesConnectedToNode(this, visitedNodes);
}
AudioNode::pullInputs(framesToProcess);
@@ -101,14 +103,23 @@ void PannerNode::process(size_t framesToProcess)
}
AudioBus* source = input(0)->bus();
-
if (!source) {
destination->zero();
return;
}
+ // HRTFDatabase should be loaded before proceeding for offline audio context when panningModel() is "HRTF".
+ if (panningModel() == PanningModelType::HRTF && !m_hrtfDatabaseLoader->isLoaded()) {
+ if (context().isOfflineContext())
+ m_hrtfDatabaseLoader->waitForLoaderThreadCompletion();
+ else {
+ destination->zero();
+ return;
+ }
+ }
+
// The audio thread can't block on this lock, so we use std::try_to_lock instead.
- std::unique_lock<std::mutex> lock(m_pannerMutex, std::try_to_lock);
+ std::unique_lock<Lock> lock(m_pannerMutex, std::try_to_lock);
if (!lock.owns_lock()) {
// Too bad - The try_lock() failed. We must be in the middle of changing the panner.
destination->zero();
@@ -144,7 +155,7 @@ void PannerNode::initialize()
if (isInitialized())
return;
- m_panner = Panner::create(m_panningModel, sampleRate(), context()->hrtfDatabaseLoader());
+ m_panner = Panner::create(m_panningModel, sampleRate(), m_hrtfDatabaseLoader.get());
AudioNode::initialize();
}
@@ -160,100 +171,28 @@ void PannerNode::uninitialize()
AudioListener* PannerNode::listener()
{
- return context()->listener();
+ return context().listener();
}
-String PannerNode::panningModel() const
+void PannerNode::setPanningModel(PanningModelType model)
{
- switch (m_panningModel) {
- case EQUALPOWER:
- return "equalpower";
- case HRTF:
- return "HRTF";
- case SOUNDFIELD:
- return "soundfield";
- default:
- ASSERT_NOT_REACHED();
- return "HRTF";
- }
-}
+ if (!m_panner.get() || model != m_panningModel) {
+ // This synchronizes with process().
+ std::lock_guard<Lock> lock(m_pannerMutex);
-void PannerNode::setPanningModel(const String& model)
-{
- if (model == "equalpower")
- setPanningModel(EQUALPOWER);
- else if (model == "HRTF")
- setPanningModel(HRTF);
- else if (model == "soundfield")
- setPanningModel(SOUNDFIELD);
- else
- ASSERT_NOT_REACHED();
-}
-
-bool PannerNode::setPanningModel(unsigned model)
-{
- switch (model) {
- case EQUALPOWER:
- case HRTF:
- if (!m_panner.get() || model != m_panningModel) {
- // This synchronizes with process().
- std::lock_guard<std::mutex> lock(m_pannerMutex);
-
- m_panner = Panner::create(model, sampleRate(), context()->hrtfDatabaseLoader());
- m_panningModel = model;
- }
- break;
- case SOUNDFIELD:
- // FIXME: Implement sound field model. See // https://bugs.webkit.org/show_bug.cgi?id=77367.
- context()->scriptExecutionContext()->addConsoleMessage(JSMessageSource, WarningMessageLevel, "'soundfield' panning model not implemented.");
- break;
- default:
- return false;
- }
-
- return true;
-}
-
-String PannerNode::distanceModel() const
-{
- switch (const_cast<PannerNode*>(this)->m_distanceEffect.model()) {
- case DistanceEffect::ModelLinear:
- return "linear";
- case DistanceEffect::ModelInverse:
- return "inverse";
- case DistanceEffect::ModelExponential:
- return "exponential";
- default:
- ASSERT_NOT_REACHED();
- return "inverse";
+ m_panner = Panner::create(model, sampleRate(), m_hrtfDatabaseLoader.get());
+ m_panningModel = model;
}
}
-void PannerNode::setDistanceModel(const String& model)
+DistanceModelType PannerNode::distanceModel() const
{
- if (model == "linear")
- setDistanceModel(DistanceEffect::ModelLinear);
- else if (model == "inverse")
- setDistanceModel(DistanceEffect::ModelInverse);
- else if (model == "exponential")
- setDistanceModel(DistanceEffect::ModelExponential);
- else
- ASSERT_NOT_REACHED();
+ return const_cast<PannerNode*>(this)->m_distanceEffect.model();
}
-bool PannerNode::setDistanceModel(unsigned model)
+void PannerNode::setDistanceModel(DistanceModelType model)
{
- switch (model) {
- case DistanceEffect::ModelLinear:
- case DistanceEffect::ModelInverse:
- case DistanceEffect::ModelExponential:
- m_distanceEffect.setModel(static_cast<DistanceEffect::ModelType>(model), true);
- break;
- default:
- return false;
- }
-
- return true;
+ m_distanceEffect.setModel(model, true);
}
void PannerNode::getAzimuthElevation(double* outAzimuth, double* outElevation)
@@ -385,7 +324,7 @@ float PannerNode::distanceConeGain()
return float(distanceGain * coneGain);
}
-void PannerNode::notifyAudioSourcesConnectedToNode(AudioNode* node)
+void PannerNode::notifyAudioSourcesConnectedToNode(AudioNode* node, HashSet<AudioNode*>& visitedNodes)
{
ASSERT(node);
if (!node)
@@ -404,7 +343,11 @@ void PannerNode::notifyAudioSourcesConnectedToNode(AudioNode* node)
for (unsigned j = 0; j < input->numberOfRenderingConnections(); ++j) {
AudioNodeOutput* connectedOutput = input->renderingOutput(j);
AudioNode* connectedNode = connectedOutput->node();
- notifyAudioSourcesConnectedToNode(connectedNode); // recurse
+ if (visitedNodes.contains(connectedNode))
+ continue;
+
+ visitedNodes.add(connectedNode);
+ notifyAudioSourcesConnectedToNode(connectedNode, visitedNodes);
}
}
}
diff --git a/Source/WebCore/Modules/webaudio/PannerNode.h b/Source/WebCore/Modules/webaudio/PannerNode.h
index 190eb79d5..d5f6d2862 100644
--- a/Source/WebCore/Modules/webaudio/PannerNode.h
+++ b/Source/WebCore/Modules/webaudio/PannerNode.h
@@ -22,8 +22,9 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef PannerNode_h
-#define PannerNode_h
+#pragma once
+
+#if ENABLE(WEB_AUDIO)
#include "AudioBus.h"
#include "AudioListener.h"
@@ -32,9 +33,11 @@
#include "Cone.h"
#include "Distance.h"
#include "FloatPoint3D.h"
+#include "HRTFDatabaseLoader.h"
#include "Panner.h"
#include <memory>
-#include <mutex>
+#include <wtf/HashSet.h>
+#include <wtf/Lock.h>
namespace WebCore {
@@ -47,42 +50,26 @@ namespace WebCore {
class PannerNode : public AudioNode {
public:
- // These must be defined as in the .idl file and must match those in the Panner class.
- enum {
- EQUALPOWER = 0,
- HRTF = 1,
- SOUNDFIELD = 2,
- };
-
- // These must be defined as in the .idl file and must match those
- // in the DistanceEffect class.
- enum {
- LINEAR_DISTANCE = 0,
- INVERSE_DISTANCE = 1,
- EXPONENTIAL_DISTANCE = 2,
- };
-
- static PassRefPtr<PannerNode> create(AudioContext* context, float sampleRate)
+ static Ref<PannerNode> create(AudioContext& context, float sampleRate)
{
- return adoptRef(new PannerNode(context, sampleRate));
+ return adoptRef(*new PannerNode(context, sampleRate));
}
virtual ~PannerNode();
// AudioNode
- virtual void process(size_t framesToProcess) override;
- virtual void pullInputs(size_t framesToProcess) override;
- virtual void reset() override;
- virtual void initialize() override;
- virtual void uninitialize() override;
+ void process(size_t framesToProcess) override;
+ void pullInputs(size_t framesToProcess) override;
+ void reset() override;
+ void initialize() override;
+ void uninitialize() override;
// Listener
AudioListener* listener();
// Panning model
- String panningModel() const;
- bool setPanningModel(unsigned); // Returns true on success.
- void setPanningModel(const String&);
+ PanningModelType panningModel() const { return m_panningModel; }
+ void setPanningModel(PanningModelType);
// Position
FloatPoint3D position() const { return m_position; }
@@ -97,9 +84,8 @@ public:
void setVelocity(float x, float y, float z) { m_velocity = FloatPoint3D(x, y, z); }
// Distance parameters
- String distanceModel() const;
- bool setDistanceModel(unsigned); // Returns true on success.
- void setDistanceModel(const String&);
+ DistanceModelType distanceModel() const;
+ void setDistanceModel(DistanceModelType);
double refDistance() { return m_distanceEffect.refDistance(); }
void setRefDistance(double refDistance) { m_distanceEffect.setRefDistance(refDistance); }
@@ -127,21 +113,21 @@ public:
AudioParam* distanceGain() { return m_distanceGain.get(); }
AudioParam* coneGain() { return m_coneGain.get(); }
- virtual double tailTime() const override { return m_panner ? m_panner->tailTime() : 0; }
- virtual double latencyTime() const override { return m_panner ? m_panner->latencyTime() : 0; }
+ double tailTime() const override { return m_panner ? m_panner->tailTime() : 0; }
+ double latencyTime() const override { return m_panner ? m_panner->latencyTime() : 0; }
private:
- PannerNode(AudioContext*, float sampleRate);
+ PannerNode(AudioContext&, float sampleRate);
// Returns the combined distance and cone gain attenuation.
float distanceConeGain();
// Notifies any AudioBufferSourceNodes connected to us either directly or indirectly about our existence.
// This is in order to handle the pitch change necessary for the doppler shift.
- void notifyAudioSourcesConnectedToNode(AudioNode*);
+ void notifyAudioSourcesConnectedToNode(AudioNode*, HashSet<AudioNode*>& visitedNodes);
std::unique_ptr<Panner> m_panner;
- unsigned m_panningModel;
+ PanningModelType m_panningModel;
FloatPoint3D m_position;
FloatPoint3D m_orientation;
@@ -154,12 +140,15 @@ private:
ConeEffect m_coneEffect;
float m_lastGain;
+ // HRTF Database loader
+ RefPtr<HRTFDatabaseLoader> m_hrtfDatabaseLoader;
+
unsigned m_connectionCount;
// Synchronize process() and setPanningModel() which can change the panner.
- mutable std::mutex m_pannerMutex;
+ mutable Lock m_pannerMutex;
};
} // namespace WebCore
-#endif // PannerNode_h
+#endif
diff --git a/Source/WebCore/Modules/webaudio/PannerNode.idl b/Source/WebCore/Modules/webaudio/PannerNode.idl
index ee0bb287f..fc991bf94 100644
--- a/Source/WebCore/Modules/webaudio/PannerNode.idl
+++ b/Source/WebCore/Modules/webaudio/PannerNode.idl
@@ -24,36 +24,43 @@
[
Conditional=WEB_AUDIO,
+ ImplementedAs=PanningModelType
+] enum PanningModelType {
+ "equalpower",
+ "HRTF"
+};
+
+[
+ Conditional=WEB_AUDIO,
+ ImplementedAs=DistanceModelType
+] enum DistanceModelType {
+ "linear",
+ "inverse",
+ "exponential"
+};
+
+[
+ Conditional=WEB_AUDIO,
JSGenerateToJSObject,
InterfaceName=webkitAudioPannerNode,
] interface PannerNode : AudioNode {
- // Panning model
- const unsigned short EQUALPOWER = 0;
- const unsigned short HRTF = 1;
- const unsigned short SOUNDFIELD = 2;
-
- // Distance model
- const unsigned short LINEAR_DISTANCE = 0;
- const unsigned short INVERSE_DISTANCE = 1;
- const unsigned short EXPONENTIAL_DISTANCE = 2;
-
// Default model for stereo is HRTF
- [CustomSetter] attribute DOMString panningModel;
+ attribute PanningModelType panningModel;
// Uses a 3D cartesian coordinate system
- void setPosition(float x, float y, float z);
- void setOrientation(float x, float y, float z);
- void setVelocity(float x, float y, float z);
+ void setPosition(unrestricted float x, unrestricted float y, unrestricted float z);
+ void setOrientation(unrestricted float x, unrestricted float y, unrestricted float z);
+ void setVelocity(unrestricted float x, unrestricted float y, unrestricted float z);
// Distance model
- [CustomSetter] attribute DOMString distanceModel;
+ attribute DistanceModelType distanceModel;
- attribute double refDistance;
- attribute double maxDistance;
- attribute double rolloffFactor;
+ attribute unrestricted double refDistance;
+ attribute unrestricted double maxDistance;
+ attribute unrestricted double rolloffFactor;
// Directional sound cone
- attribute double coneInnerAngle;
- attribute double coneOuterAngle;
- attribute double coneOuterGain;
+ attribute unrestricted double coneInnerAngle;
+ attribute unrestricted double coneOuterAngle;
+ attribute unrestricted double coneOuterGain;
};
diff --git a/Source/WebCore/Modules/webaudio/PeriodicWave.cpp b/Source/WebCore/Modules/webaudio/PeriodicWave.cpp
index 3044c21c1..bb2a07d23 100644
--- a/Source/WebCore/Modules/webaudio/PeriodicWave.cpp
+++ b/Source/WebCore/Modules/webaudio/PeriodicWave.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.
*
@@ -33,7 +33,6 @@
#include "PeriodicWave.h"
#include "FFTFrame.h"
-#include "OscillatorNode.h"
#include "VectorMath.h"
#include <algorithm>
@@ -45,44 +44,40 @@ namespace WebCore {
using namespace VectorMath;
-PassRefPtr<PeriodicWave> PeriodicWave::create(float sampleRate, Float32Array* real, Float32Array* imag)
+Ref<PeriodicWave> PeriodicWave::create(float sampleRate, Float32Array& real, Float32Array& imaginary)
{
- bool isGood = real && imag && real->length() == imag->length();
- ASSERT(isGood);
- if (isGood) {
- RefPtr<PeriodicWave> waveTable = adoptRef(new PeriodicWave(sampleRate));
- size_t numberOfComponents = real->length();
- waveTable->createBandLimitedTables(real->data(), imag->data(), numberOfComponents);
- return waveTable;
- }
- return nullptr;
+ ASSERT(real.length() == imaginary.length());
+
+ auto waveTable = adoptRef(*new PeriodicWave(sampleRate));
+ waveTable->createBandLimitedTables(real.data(), imaginary.data(), real.length());
+ return waveTable;
}
-PassRefPtr<PeriodicWave> PeriodicWave::createSine(float sampleRate)
+Ref<PeriodicWave> PeriodicWave::createSine(float sampleRate)
{
- RefPtr<PeriodicWave> waveTable = adoptRef(new PeriodicWave(sampleRate));
- waveTable->generateBasicWaveform(OscillatorNode::SINE);
+ Ref<PeriodicWave> waveTable = adoptRef(*new PeriodicWave(sampleRate));
+ waveTable->generateBasicWaveform(Type::Sine);
return waveTable;
}
-PassRefPtr<PeriodicWave> PeriodicWave::createSquare(float sampleRate)
+Ref<PeriodicWave> PeriodicWave::createSquare(float sampleRate)
{
- RefPtr<PeriodicWave> waveTable = adoptRef(new PeriodicWave(sampleRate));
- waveTable->generateBasicWaveform(OscillatorNode::SQUARE);
+ Ref<PeriodicWave> waveTable = adoptRef(*new PeriodicWave(sampleRate));
+ waveTable->generateBasicWaveform(Type::Square);
return waveTable;
}
-PassRefPtr<PeriodicWave> PeriodicWave::createSawtooth(float sampleRate)
+Ref<PeriodicWave> PeriodicWave::createSawtooth(float sampleRate)
{
- RefPtr<PeriodicWave> waveTable = adoptRef(new PeriodicWave(sampleRate));
- waveTable->generateBasicWaveform(OscillatorNode::SAWTOOTH);
+ Ref<PeriodicWave> waveTable = adoptRef(*new PeriodicWave(sampleRate));
+ waveTable->generateBasicWaveform(Type::Sawtooth);
return waveTable;
}
-PassRefPtr<PeriodicWave> PeriodicWave::createTriangle(float sampleRate)
+Ref<PeriodicWave> PeriodicWave::createTriangle(float sampleRate)
{
- RefPtr<PeriodicWave> waveTable = adoptRef(new PeriodicWave(sampleRate));
- waveTable->generateBasicWaveform(OscillatorNode::TRIANGLE);
+ Ref<PeriodicWave> waveTable = adoptRef(*new PeriodicWave(sampleRate));
+ waveTable->generateBasicWaveform(Type::Triangle);
return waveTable;
}
@@ -217,7 +212,7 @@ void PeriodicWave::createBandLimitedTables(const float* realData, const float* i
}
}
-void PeriodicWave::generateBasicWaveform(int shape)
+void PeriodicWave::generateBasicWaveform(Type shape)
{
unsigned fftSize = periodicWaveSize();
unsigned halfSize = fftSize / 2;
@@ -242,31 +237,26 @@ void PeriodicWave::generateBasicWaveform(int shape)
// Calculate Fourier coefficients depending on the shape.
// Note that the overall scaling (magnitude) of the waveforms is normalized in createBandLimitedTables().
switch (shape) {
- case OscillatorNode::SINE:
+ case Type::Sine:
// Standard sine wave function.
a = 0;
b = (n == 1) ? 1 : 0;
break;
- case OscillatorNode::SQUARE:
+ case Type::Square:
// Square-shaped waveform with the first half its maximum value and the second half its minimum value.
a = 0;
b = invOmega * ((n & 1) ? 2 : 0);
break;
- case OscillatorNode::SAWTOOTH:
+ case Type::Sawtooth:
// Sawtooth-shaped waveform with the first half ramping from zero to maximum and the second half from minimum to zero.
a = 0;
b = -invOmega * cos(0.5 * omega);
break;
- case OscillatorNode::TRIANGLE:
+ case Type::Triangle:
// Triangle-shaped waveform going from its maximum value to its minimum value then back to the maximum value.
a = (4 - 4 * cos(0.5 * omega)) / (n * n * piFloat * piFloat);
b = 0;
break;
- default:
- ASSERT_NOT_REACHED();
- a = 0;
- b = 0;
- break;
}
realP[n] = a;
diff --git a/Source/WebCore/Modules/webaudio/PeriodicWave.h b/Source/WebCore/Modules/webaudio/PeriodicWave.h
index 79a098c55..0a1253c89 100644
--- a/Source/WebCore/Modules/webaudio/PeriodicWave.h
+++ b/Source/WebCore/Modules/webaudio/PeriodicWave.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,13 +26,11 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef PeriodicWave_h
-#define PeriodicWave_h
+#pragma once
#include "AudioArray.h"
#include <memory>
#include <runtime/Float32Array.h>
-#include <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>
#include <wtf/RefPtr.h>
#include <wtf/Vector.h>
@@ -41,13 +39,13 @@ namespace WebCore {
class PeriodicWave : public RefCounted<PeriodicWave> {
public:
- static PassRefPtr<PeriodicWave> createSine(float sampleRate);
- static PassRefPtr<PeriodicWave> createSquare(float sampleRate);
- static PassRefPtr<PeriodicWave> createSawtooth(float sampleRate);
- static PassRefPtr<PeriodicWave> createTriangle(float sampleRate);
+ static Ref<PeriodicWave> createSine(float sampleRate);
+ static Ref<PeriodicWave> createSquare(float sampleRate);
+ static Ref<PeriodicWave> createSawtooth(float sampleRate);
+ static Ref<PeriodicWave> createTriangle(float sampleRate);
// Creates an arbitrary wave given the frequency components (Fourier coefficients).
- static PassRefPtr<PeriodicWave> create(float sampleRate, Float32Array* real, Float32Array* imag);
+ static Ref<PeriodicWave> create(float sampleRate, Float32Array& real, Float32Array& imag);
// Returns pointers to the lower and higher wave data for the pitch range containing
// the given fundamental frequency. These two tables are in adjacent "pitch" ranges
@@ -64,9 +62,16 @@ public:
float sampleRate() const { return m_sampleRate; }
private:
+ enum class Type {
+ Sine,
+ Square,
+ Sawtooth,
+ Triangle,
+ };
+
explicit PeriodicWave(float sampleRate);
- void generateBasicWaveform(int);
+ void generateBasicWaveform(Type);
float m_sampleRate;
unsigned m_periodicWaveSize;
@@ -93,5 +98,3 @@ private:
};
} // namespace WebCore
-
-#endif // PeriodicWave_h
diff --git a/Source/WebCore/Modules/webaudio/RealtimeAnalyser.cpp b/Source/WebCore/Modules/webaudio/RealtimeAnalyser.cpp
index f4ff708a7..035dc2b20 100644
--- a/Source/WebCore/Modules/webaudio/RealtimeAnalyser.cpp
+++ b/Source/WebCore/Modules/webaudio/RealtimeAnalyser.cpp
@@ -30,7 +30,6 @@
#include "AudioBus.h"
#include "AudioUtilities.h"
-#include "FFTFrame.h"
#include "VectorMath.h"
#include <algorithm>
#include <complex>
@@ -185,7 +184,7 @@ void RealtimeAnalyser::doFFTAnalysis()
imagP[0] = 0;
// Normalize so than an input sine wave at 0dBfs registers as 0dBfs (undo FFT scaling factor).
- const double magnitudeScale = 1.0 / DefaultFFTSize;
+ const double magnitudeScale = 1.0 / fftSize;
// A value of 0 does no averaging with the previous result. Larger values produce slower, but smoother changes.
double k = m_smoothingTimeConstant;
diff --git a/Source/WebCore/Modules/webaudio/RealtimeAnalyser.h b/Source/WebCore/Modules/webaudio/RealtimeAnalyser.h
index aa0f877ac..13f94739d 100644
--- a/Source/WebCore/Modules/webaudio/RealtimeAnalyser.h
+++ b/Source/WebCore/Modules/webaudio/RealtimeAnalyser.h
@@ -22,10 +22,10 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef RealtimeAnalyser_h
-#define RealtimeAnalyser_h
+#pragma once
#include "AudioArray.h"
+#include "FFTFrame.h"
#include <memory>
#include <runtime/Float32Array.h>
#include <runtime/Uint8Array.h>
@@ -35,7 +35,6 @@
namespace WebCore {
class AudioBus;
-class FFTFrame;
class RealtimeAnalyser {
WTF_MAKE_NONCOPYABLE(RealtimeAnalyser);
@@ -97,5 +96,3 @@ private:
};
} // namespace WebCore
-
-#endif // RealtimeAnalyser_h
diff --git a/Source/WebCore/Modules/webaudio/ScriptProcessorNode.cpp b/Source/WebCore/Modules/webaudio/ScriptProcessorNode.cpp
index cb3297bd4..b95e093d0 100644
--- a/Source/WebCore/Modules/webaudio/ScriptProcessorNode.cpp
+++ b/Source/WebCore/Modules/webaudio/ScriptProcessorNode.cpp
@@ -35,12 +35,13 @@
#include "AudioNodeOutput.h"
#include "AudioProcessingEvent.h"
#include "Document.h"
+#include "EventNames.h"
#include <runtime/Float32Array.h>
#include <wtf/MainThread.h>
namespace WebCore {
-PassRefPtr<ScriptProcessorNode> ScriptProcessorNode::create(AudioContext* context, float sampleRate, size_t bufferSize, unsigned numberOfInputChannels, unsigned numberOfOutputChannels)
+RefPtr<ScriptProcessorNode> ScriptProcessorNode::create(AudioContext& context, float sampleRate, size_t bufferSize, unsigned numberOfInputChannels, unsigned numberOfOutputChannels)
{
// Check for valid buffer size.
switch (bufferSize) {
@@ -65,10 +66,10 @@ PassRefPtr<ScriptProcessorNode> ScriptProcessorNode::create(AudioContext* contex
if (numberOfOutputChannels > AudioContext::maxNumberOfChannels())
return nullptr;
- return adoptRef(new ScriptProcessorNode(context, sampleRate, bufferSize, numberOfInputChannels, numberOfOutputChannels));
+ return adoptRef(*new ScriptProcessorNode(context, sampleRate, bufferSize, numberOfInputChannels, numberOfOutputChannels));
}
-ScriptProcessorNode::ScriptProcessorNode(AudioContext* context, float sampleRate, size_t bufferSize, unsigned numberOfInputChannels, unsigned numberOfOutputChannels)
+ScriptProcessorNode::ScriptProcessorNode(AudioContext& context, float sampleRate, size_t bufferSize, unsigned numberOfInputChannels, unsigned numberOfOutputChannels)
: AudioNode(context, sampleRate)
, m_doubleBufferIndex(0)
, m_doubleBufferIndexForEvent(0)
@@ -104,7 +105,7 @@ void ScriptProcessorNode::initialize()
if (isInitialized())
return;
- float sampleRate = context()->sampleRate();
+ float sampleRate = context().sampleRate();
// Create double buffers on both the input and output sides.
// These AudioBuffers will be directly accessed in the main thread by JavaScript.
@@ -182,14 +183,14 @@ void ScriptProcessorNode::process(size_t framesToProcess)
return;
for (unsigned i = 0; i < numberOfInputChannels; i++)
- m_internalInputBus->setChannelMemory(i, inputBuffer->getChannelData(i)->data() + m_bufferReadWriteIndex, framesToProcess);
+ m_internalInputBus->setChannelMemory(i, inputBuffer->channelData(i)->data() + m_bufferReadWriteIndex, framesToProcess);
if (numberOfInputChannels)
m_internalInputBus->copyFrom(*inputBus);
// Copy from the output buffer to the output.
for (unsigned i = 0; i < numberOfOutputChannels; ++i)
- memcpy(outputBus->channel(i)->mutableData(), outputBuffer->getChannelData(i)->data() + m_bufferReadWriteIndex, sizeof(float) * framesToProcess);
+ memcpy(outputBus->channel(i)->mutableData(), outputBuffer->channelData(i)->data() + m_bufferReadWriteIndex, sizeof(float) * framesToProcess);
// Update the buffering index.
m_bufferReadWriteIndex = (m_bufferReadWriteIndex + framesToProcess) % bufferSize();
@@ -210,30 +211,20 @@ void ScriptProcessorNode::process(size_t framesToProcess)
// Fire the event on the main thread, not this one (which is the realtime audio thread).
m_doubleBufferIndexForEvent = m_doubleBufferIndex;
m_isRequestOutstanding = true;
- callOnMainThread(fireProcessEventDispatch, this);
- }
-
- swapBuffers();
- }
-}
-void ScriptProcessorNode::setOnaudioprocess(PassRefPtr<EventListener> listener)
-{
- m_hasAudioProcessListener = listener;
- setAttributeEventListener(eventNames().audioprocessEvent, listener);
-}
+ callOnMainThread([this] {
+ if (!m_hasAudioProcessListener)
+ return;
-void ScriptProcessorNode::fireProcessEventDispatch(void* userData)
-{
- ScriptProcessorNode* jsAudioNode = static_cast<ScriptProcessorNode*>(userData);
- ASSERT(jsAudioNode);
- if (!jsAudioNode)
- return;
+ fireProcessEvent();
- jsAudioNode->fireProcessEvent();
+ // De-reference to match the ref() call in process().
+ deref();
+ });
+ }
- // De-reference to match the ref() call in process().
- jsAudioNode->deref();
+ swapBuffers();
+ }
}
void ScriptProcessorNode::fireProcessEvent()
@@ -252,12 +243,16 @@ void ScriptProcessorNode::fireProcessEvent()
return;
// Avoid firing the event if the document has already gone away.
- if (context()->scriptExecutionContext()) {
+ if (context().scriptExecutionContext()) {
// Let the audio thread know we've gotten to the point where it's OK for it to make another request.
m_isRequestOutstanding = false;
-
+
+ // Calculate playbackTime with the buffersize which needs to be processed each time when onaudioprocess is called.
+ // The outputBuffer being passed to JS will be played after exhausting previous outputBuffer by double-buffering.
+ double playbackTime = (context().currentSampleFrame() + m_bufferSize) / static_cast<double>(context().sampleRate());
+
// Call the JavaScript event handler which will do the audio processing.
- dispatchEvent(AudioProcessingEvent::create(inputBuffer, outputBuffer));
+ dispatchEvent(AudioProcessingEvent::create(inputBuffer, outputBuffer, playbackTime));
}
}
@@ -282,6 +277,28 @@ double ScriptProcessorNode::latencyTime() const
return std::numeric_limits<double>::infinity();
}
+bool ScriptProcessorNode::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options)
+{
+ bool success = AudioNode::addEventListener(eventType, WTFMove(listener), options);
+ if (success && eventType == eventNames().audioprocessEvent)
+ m_hasAudioProcessListener = hasEventListeners(eventNames().audioprocessEvent);
+ return success;
+}
+
+bool ScriptProcessorNode::removeEventListener(const AtomicString& eventType, EventListener& listener, const ListenerOptions& options)
+{
+ bool success = AudioNode::removeEventListener(eventType, listener, options);
+ if (success && eventType == eventNames().audioprocessEvent)
+ m_hasAudioProcessListener = hasEventListeners(eventNames().audioprocessEvent);
+ return success;
+}
+
+void ScriptProcessorNode::removeAllEventListeners()
+{
+ m_hasAudioProcessListener = false;
+ AudioNode::removeAllEventListeners();
+}
+
} // namespace WebCore
#endif // ENABLE(WEB_AUDIO)
diff --git a/Source/WebCore/Modules/webaudio/ScriptProcessorNode.h b/Source/WebCore/Modules/webaudio/ScriptProcessorNode.h
index 15bb27b29..4c6bafe94 100644
--- a/Source/WebCore/Modules/webaudio/ScriptProcessorNode.h
+++ b/Source/WebCore/Modules/webaudio/ScriptProcessorNode.h
@@ -22,8 +22,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef ScriptProcessorNode_h
-#define ScriptProcessorNode_h
+#pragma once
#include "ActiveDOMObject.h"
#include "AudioBus.h"
@@ -31,7 +30,6 @@
#include "EventListener.h"
#include "EventTarget.h"
#include <wtf/Forward.h>
-#include <wtf/PassRefPtr.h>
#include <wtf/RefPtr.h>
#include <wtf/Vector.h>
@@ -53,30 +51,30 @@ public:
// This value controls how frequently the onaudioprocess event handler is called and how many sample-frames need to be processed each call.
// Lower numbers for bufferSize will result in a lower (better) latency. Higher numbers will be necessary to avoid audio breakup and glitches.
// The value chosen must carefully balance between latency and audio quality.
- static PassRefPtr<ScriptProcessorNode> create(AudioContext*, float sampleRate, size_t bufferSize, unsigned numberOfInputChannels, unsigned numberOfOutputChannels);
+ static RefPtr<ScriptProcessorNode> create(AudioContext&, float sampleRate, size_t bufferSize, unsigned numberOfInputChannels, unsigned numberOfOutputChannels);
virtual ~ScriptProcessorNode();
// AudioNode
- virtual void process(size_t framesToProcess) override;
- virtual void reset() override;
- virtual void initialize() override;
- virtual void uninitialize() override;
+ void process(size_t framesToProcess) override;
+ void reset() override;
+ void initialize() override;
+ void uninitialize() override;
size_t bufferSize() const { return m_bufferSize; }
- EventListener* onaudioprocess() { return getAttributeEventListener(eventNames().audioprocessEvent); }
- void setOnaudioprocess(PassRefPtr<EventListener>);
-
private:
- virtual double tailTime() const override;
- virtual double latencyTime() const override;
+ double tailTime() const override;
+ double latencyTime() const override;
- ScriptProcessorNode(AudioContext*, float sampleRate, size_t bufferSize, unsigned numberOfInputChannels, unsigned numberOfOutputChannels);
+ ScriptProcessorNode(AudioContext&, float sampleRate, size_t bufferSize, unsigned numberOfInputChannels, unsigned numberOfOutputChannels);
- static void fireProcessEventDispatch(void* userData);
void fireProcessEvent();
+ bool addEventListener(const AtomicString& eventType, Ref<EventListener>&&, const AddEventListenerOptions&) override;
+ bool removeEventListener(const AtomicString& eventType, EventListener&, const ListenerOptions&) override;
+ void removeAllEventListeners() override;
+
// Double buffering
unsigned doubleBufferIndex() const { return m_doubleBufferIndex; }
void swapBuffers() { m_doubleBufferIndex = 1 - m_doubleBufferIndex; }
@@ -97,5 +95,3 @@ private:
};
} // namespace WebCore
-
-#endif // ScriptProcessorNode_h
diff --git a/Source/WebCore/Modules/webaudio/ScriptProcessorNode.idl b/Source/WebCore/Modules/webaudio/ScriptProcessorNode.idl
index d5dcd5033..45cc63083 100644
--- a/Source/WebCore/Modules/webaudio/ScriptProcessorNode.idl
+++ b/Source/WebCore/Modules/webaudio/ScriptProcessorNode.idl
@@ -29,7 +29,7 @@
JSGenerateToNativeObject
] interface ScriptProcessorNode : AudioNode {
// Rendering callback
- attribute EventListener onaudioprocess;
+ attribute EventHandler onaudioprocess;
readonly attribute long bufferSize;
};
diff --git a/Source/WebCore/Modules/webaudio/WaveShaperDSPKernel.h b/Source/WebCore/Modules/webaudio/WaveShaperDSPKernel.h
index 7f5335a49..43a686eb9 100644
--- a/Source/WebCore/Modules/webaudio/WaveShaperDSPKernel.h
+++ b/Source/WebCore/Modules/webaudio/WaveShaperDSPKernel.h
@@ -22,8 +22,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef WaveShaperDSPKernel_h
-#define WaveShaperDSPKernel_h
+#pragma once
#include "AudioArray.h"
#include "AudioDSPKernel.h"
@@ -34,8 +33,6 @@
namespace WebCore {
-class WaveShaperProcessor;
-
// WaveShaperDSPKernel is an AudioDSPKernel and is responsible for non-linear distortion on one channel.
class WaveShaperDSPKernel : public AudioDSPKernel {
@@ -43,10 +40,10 @@ public:
explicit WaveShaperDSPKernel(WaveShaperProcessor*);
// AudioDSPKernel
- virtual void process(const float* source, float* dest, size_t framesToProcess);
- virtual void reset();
- virtual double tailTime() const override { return 0; }
- virtual double latencyTime() const override;
+ void process(const float* source, float* dest, size_t framesToProcess) override;
+ void reset() override;
+ double tailTime() const override { return 0; }
+ double latencyTime() const override;
// Oversampling requires more resources, so let's only allocate them if needed.
void lazyInitializeOversampling();
@@ -71,5 +68,3 @@ protected:
};
} // namespace WebCore
-
-#endif // WaveShaperDSPKernel_h
diff --git a/Source/WebCore/Modules/webaudio/WaveShaperNode.cpp b/Source/WebCore/Modules/webaudio/WaveShaperNode.cpp
index 69ec27a2c..50cf0630c 100644
--- a/Source/WebCore/Modules/webaudio/WaveShaperNode.cpp
+++ b/Source/WebCore/Modules/webaudio/WaveShaperNode.cpp
@@ -23,29 +23,28 @@
*/
#include "config.h"
+#include "WaveShaperNode.h"
#if ENABLE(WEB_AUDIO)
-#include "WaveShaperNode.h"
-
-#include "ExceptionCode.h"
+#include "AudioContext.h"
#include <wtf/MainThread.h>
namespace WebCore {
-WaveShaperNode::WaveShaperNode(AudioContext* context)
- : AudioBasicProcessorNode(context, context->sampleRate())
+WaveShaperNode::WaveShaperNode(AudioContext& context)
+ : AudioBasicProcessorNode(context, context.sampleRate())
{
- m_processor = std::make_unique<WaveShaperProcessor>(context->sampleRate(), 1);
+ m_processor = std::make_unique<WaveShaperProcessor>(context.sampleRate(), 1);
setNodeType(NodeTypeWaveShaper);
initialize();
}
-void WaveShaperNode::setCurve(Float32Array* curve)
+void WaveShaperNode::setCurve(Float32Array& curve)
{
ASSERT(isMainThread());
- waveShaperProcessor()->setCurve(curve);
+ waveShaperProcessor()->setCurve(&curve);
}
Float32Array* WaveShaperNode::curve()
@@ -53,36 +52,41 @@ Float32Array* WaveShaperNode::curve()
return waveShaperProcessor()->curve();
}
-void WaveShaperNode::setOversample(const String& type, ExceptionCode& ec)
+static inline WaveShaperProcessor::OverSampleType processorType(WaveShaperNode::OverSampleType type)
+{
+ switch (type) {
+ case WaveShaperNode::OverSampleType::None:
+ return WaveShaperProcessor::OverSampleNone;
+ case WaveShaperNode::OverSampleType::_2x:
+ return WaveShaperProcessor::OverSample2x;
+ case WaveShaperNode::OverSampleType::_4x:
+ return WaveShaperProcessor::OverSample4x;
+ }
+ ASSERT_NOT_REACHED();
+ return WaveShaperProcessor::OverSampleNone;
+}
+
+void WaveShaperNode::setOversample(OverSampleType type)
{
ASSERT(isMainThread());
// Synchronize with any graph changes or changes to channel configuration.
- AudioContext::AutoLocker contextLocker(*context());
-
- if (type == "none")
- waveShaperProcessor()->setOversample(WaveShaperProcessor::OverSampleNone);
- else if (type == "2x")
- waveShaperProcessor()->setOversample(WaveShaperProcessor::OverSample2x);
- else if (type == "4x")
- waveShaperProcessor()->setOversample(WaveShaperProcessor::OverSample4x);
- else
- ec = INVALID_STATE_ERR;
+ AudioContext::AutoLocker contextLocker(context());
+ waveShaperProcessor()->setOversample(processorType(type));
}
-String WaveShaperNode::oversample() const
+auto WaveShaperNode::oversample() const -> OverSampleType
{
switch (const_cast<WaveShaperNode*>(this)->waveShaperProcessor()->oversample()) {
case WaveShaperProcessor::OverSampleNone:
- return "none";
+ return OverSampleType::None;
case WaveShaperProcessor::OverSample2x:
- return "2x";
+ return OverSampleType::_2x;
case WaveShaperProcessor::OverSample4x:
- return "4x";
- default:
- ASSERT_NOT_REACHED();
- return "none";
+ return OverSampleType::_4x;
}
+ ASSERT_NOT_REACHED();
+ return OverSampleType::None;
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/webaudio/WaveShaperNode.h b/Source/WebCore/Modules/webaudio/WaveShaperNode.h
index cb65372a1..773c04fc0 100644
--- a/Source/WebCore/Modules/webaudio/WaveShaperNode.h
+++ b/Source/WebCore/Modules/webaudio/WaveShaperNode.h
@@ -22,38 +22,35 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef WaveShaperNode_h
-#define WaveShaperNode_h
+#pragma once
#include "AudioBasicProcessorNode.h"
-#include "BiquadProcessor.h"
#include "WaveShaperProcessor.h"
#include <wtf/Forward.h>
namespace WebCore {
-
-class WaveShaperNode : public AudioBasicProcessorNode {
+
+class WaveShaperNode final : public AudioBasicProcessorNode {
public:
- static PassRefPtr<WaveShaperNode> create(AudioContext* context)
+ static Ref<WaveShaperNode> create(AudioContext& context)
{
- return adoptRef(new WaveShaperNode(context));
+ return adoptRef(*new WaveShaperNode(context));
}
// setCurve() is called on the main thread.
- void setCurve(Float32Array*);
+ void setCurve(Float32Array&);
Float32Array* curve();
- void setOversample(const String& , ExceptionCode&);
- String oversample() const;
+ enum class OverSampleType { None, _2x, _4x };
+ void setOversample(OverSampleType);
+ OverSampleType oversample() const;
double latency() const { return latencyTime(); }
private:
- explicit WaveShaperNode(AudioContext*);
+ explicit WaveShaperNode(AudioContext&);
WaveShaperProcessor* waveShaperProcessor() { return static_cast<WaveShaperProcessor*>(processor()); }
};
} // namespace WebCore
-
-#endif // WaveShaperNode_h
diff --git a/Source/WebCore/Modules/webaudio/WaveShaperNode.idl b/Source/WebCore/Modules/webaudio/WaveShaperNode.idl
index 8a377536c..965656a73 100644
--- a/Source/WebCore/Modules/webaudio/WaveShaperNode.idl
+++ b/Source/WebCore/Modules/webaudio/WaveShaperNode.idl
@@ -33,5 +33,5 @@ enum OverSampleType {
JSGenerateToJSObject
] interface WaveShaperNode : AudioNode {
attribute Float32Array curve;
- [SetterRaisesException] attribute OverSampleType oversample;
+ attribute OverSampleType oversample;
};
diff --git a/Source/WebCore/Modules/webaudio/WaveShaperProcessor.cpp b/Source/WebCore/Modules/webaudio/WaveShaperProcessor.cpp
index c2d6de0af..289c39f5f 100644
--- a/Source/WebCore/Modules/webaudio/WaveShaperProcessor.cpp
+++ b/Source/WebCore/Modules/webaudio/WaveShaperProcessor.cpp
@@ -52,7 +52,7 @@ std::unique_ptr<AudioDSPKernel> WaveShaperProcessor::createKernel()
void WaveShaperProcessor::setCurve(Float32Array* curve)
{
// This synchronizes with process().
- std::lock_guard<std::mutex> lock(m_processMutex);
+ std::lock_guard<Lock> lock(m_processMutex);
m_curve = curve;
}
@@ -60,14 +60,14 @@ void WaveShaperProcessor::setCurve(Float32Array* curve)
void WaveShaperProcessor::setOversample(OverSampleType oversample)
{
// This synchronizes with process().
- std::lock_guard<std::mutex> lock(m_processMutex);
+ std::lock_guard<Lock> lock(m_processMutex);
m_oversample = oversample;
if (oversample != OverSampleNone) {
- for (unsigned i = 0; i < m_kernels.size(); ++i) {
- WaveShaperDSPKernel* kernel = static_cast<WaveShaperDSPKernel*>(m_kernels[i].get());
- kernel->lazyInitializeOversampling();
+ for (auto& audioDSPKernel : m_kernels) {
+ WaveShaperDSPKernel& kernel = static_cast<WaveShaperDSPKernel&>(*audioDSPKernel);
+ kernel.lazyInitializeOversampling();
}
}
}
@@ -85,7 +85,7 @@ void WaveShaperProcessor::process(const AudioBus* source, AudioBus* destination,
return;
// The audio thread can't block on this lock, so we use std::try_to_lock instead.
- std::unique_lock<std::mutex> lock(m_processMutex, std::try_to_lock);
+ std::unique_lock<Lock> lock(m_processMutex, std::try_to_lock);
if (!lock.owns_lock()) {
// Too bad - the try_lock() failed. We must be in the middle of a setCurve() call.
destination->zero();
diff --git a/Source/WebCore/Modules/webaudio/WaveShaperProcessor.h b/Source/WebCore/Modules/webaudio/WaveShaperProcessor.h
index 7bda31da7..51e39394c 100644
--- a/Source/WebCore/Modules/webaudio/WaveShaperProcessor.h
+++ b/Source/WebCore/Modules/webaudio/WaveShaperProcessor.h
@@ -22,15 +22,14 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef WaveShaperProcessor_h
-#define WaveShaperProcessor_h
+#pragma once
#include "AudioDSPKernel.h"
#include "AudioDSPKernelProcessor.h"
#include "AudioNode.h"
#include <memory>
-#include <mutex>
#include <runtime/Float32Array.h>
+#include <wtf/Lock.h>
#include <wtf/RefPtr.h>
namespace WebCore {
@@ -49,9 +48,9 @@ public:
virtual ~WaveShaperProcessor();
- virtual std::unique_ptr<AudioDSPKernel> createKernel() override;
+ std::unique_ptr<AudioDSPKernel> createKernel() override;
- virtual void process(const AudioBus* source, AudioBus* destination, size_t framesToProcess) override;
+ void process(const AudioBus* source, AudioBus* destination, size_t framesToProcess) override;
void setCurve(Float32Array*);
Float32Array* curve() { return m_curve.get(); }
@@ -66,9 +65,7 @@ private:
OverSampleType m_oversample;
// This synchronizes process() with setCurve().
- mutable std::mutex m_processMutex;
+ mutable Lock m_processMutex;
};
} // namespace WebCore
-
-#endif // WaveShaperProcessor_h
diff --git a/Source/WebCore/Modules/webdatabase/AbstractDatabaseServer.h b/Source/WebCore/Modules/webdatabase/AbstractDatabaseServer.h
deleted file mode 100644
index 56c74252c..000000000
--- a/Source/WebCore/Modules/webdatabase/AbstractDatabaseServer.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2012 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.
- */
-
-#ifndef AbstractDatabaseServer_h
-#define AbstractDatabaseServer_h
-
-#if ENABLE(SQL_DATABASE)
-
-#include "DatabaseBasicTypes.h"
-#include "DatabaseDetails.h"
-#include "DatabaseError.h"
-#include <wtf/RefPtr.h>
-#include <wtf/Vector.h>
-#include <wtf/text/WTFString.h>
-
-namespace WebCore {
-
-class DatabaseBackendBase;
-class DatabaseBackendContext;
-class DatabaseManagerClient;
-class SecurityOrigin;
-
-class AbstractDatabaseServer {
-public:
- virtual void initialize(const String& databasePath) = 0;
-
- virtual void setClient(DatabaseManagerClient*) = 0;
- virtual String databaseDirectoryPath() const = 0;
- virtual void setDatabaseDirectoryPath(const String&) = 0;
-
- virtual String fullPathForDatabase(SecurityOrigin*, const String& name, bool createIfDoesNotExist = true) = 0;
-
- enum OpenAttempt {
- FirstTryToOpenDatabase,
- RetryOpenDatabase
- };
-
- virtual PassRefPtr<DatabaseBackendBase> openDatabase(RefPtr<DatabaseBackendContext>&, DatabaseType,
- const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize,
- bool setVersionInNewDatabase, DatabaseError&, String& errorMessage, OpenAttempt = FirstTryToOpenDatabase) = 0;
-
- virtual bool hasEntryForOrigin(SecurityOrigin*) = 0;
- virtual void origins(Vector<RefPtr<SecurityOrigin>>& result) = 0;
- virtual bool databaseNamesForOrigin(SecurityOrigin*, Vector<String>& result) = 0;
- virtual DatabaseDetails detailsForNameAndOrigin(const String&, SecurityOrigin*) = 0;
-
- virtual unsigned long long usageForOrigin(SecurityOrigin*) = 0;
- virtual unsigned long long quotaForOrigin(SecurityOrigin*) = 0;
-
- virtual void setQuota(SecurityOrigin*, unsigned long long) = 0;
-
- virtual void deleteAllDatabases() = 0;
- virtual bool deleteOrigin(SecurityOrigin*) = 0;
- virtual bool deleteDatabase(SecurityOrigin*, const String& name) = 0;
-
- virtual void interruptAllDatabasesForContext(const DatabaseBackendContext*) = 0;
-
-protected:
- AbstractDatabaseServer() { }
- virtual ~AbstractDatabaseServer() { }
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
-
-#endif // AbstractDatabaseServer_h
diff --git a/Source/WebCore/Modules/webdatabase/ChangeVersionData.h b/Source/WebCore/Modules/webdatabase/ChangeVersionData.h
index 2ab196045..deac98fc4 100644
--- a/Source/WebCore/Modules/webdatabase/ChangeVersionData.h
+++ b/Source/WebCore/Modules/webdatabase/ChangeVersionData.h
@@ -23,10 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef ChangeVersionData_h
-#define ChangeVersionData_h
-
-#if ENABLE(SQL_DATABASE)
+#pragma once
#include <wtf/text/WTFString.h>
@@ -46,7 +43,3 @@ private:
};
} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
-
-#endif // ChangeVersionData_h
diff --git a/Source/WebCore/Modules/webdatabase/ChangeVersionWrapper.cpp b/Source/WebCore/Modules/webdatabase/ChangeVersionWrapper.cpp
index 6a397a671..82e780265 100644
--- a/Source/WebCore/Modules/webdatabase/ChangeVersionWrapper.cpp
+++ b/Source/WebCore/Modules/webdatabase/ChangeVersionWrapper.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.
*
@@ -28,11 +28,9 @@
#include "config.h"
#include "ChangeVersionWrapper.h"
-#if ENABLE(SQL_DATABASE)
-
#include "Database.h"
#include "SQLError.h"
-#include <wtf/PassRefPtr.h>
+#include "SQLTransactionBackend.h"
#include <wtf/RefPtr.h>
namespace WebCore {
@@ -43,17 +41,14 @@ ChangeVersionWrapper::ChangeVersionWrapper(const String& oldVersion, const Strin
{
}
-bool ChangeVersionWrapper::performPreflight(SQLTransactionBackend* transaction)
+bool ChangeVersionWrapper::performPreflight(SQLTransaction& transaction)
{
- ASSERT(transaction && transaction->database());
-
- DatabaseBackend* database = transaction->database();
+ Database& database = transaction.database();
String actualVersion;
- if (!database->getVersionFromDatabase(actualVersion)) {
- int sqliteError = database->sqliteDatabase().lastError();
- m_sqlError = SQLError::create(SQLError::UNKNOWN_ERR, "unable to read the current version",
- sqliteError, database->sqliteDatabase().lastErrorMsg());
+ if (!database.getVersionFromDatabase(actualVersion)) {
+ int sqliteError = database.sqliteDatabase().lastError();
+ m_sqlError = SQLError::create(SQLError::UNKNOWN_ERR, "unable to read the current version", sqliteError, database.sqliteDatabase().lastErrorMsg());
return false;
}
@@ -65,28 +60,23 @@ bool ChangeVersionWrapper::performPreflight(SQLTransactionBackend* transaction)
return true;
}
-bool ChangeVersionWrapper::performPostflight(SQLTransactionBackend* transaction)
+bool ChangeVersionWrapper::performPostflight(SQLTransaction& transaction)
{
- ASSERT(transaction && transaction->database());
+ Database& database = transaction.database();
- DatabaseBackend* database = transaction->database();
-
- if (!database->setVersionInDatabase(m_newVersion)) {
- int sqliteError = database->sqliteDatabase().lastError();
- m_sqlError = SQLError::create(SQLError::UNKNOWN_ERR, "unable to set new version in database",
- sqliteError, database->sqliteDatabase().lastErrorMsg());
+ if (!database.setVersionInDatabase(m_newVersion)) {
+ int sqliteError = database.sqliteDatabase().lastError();
+ m_sqlError = SQLError::create(SQLError::UNKNOWN_ERR, "unable to set new version in database", sqliteError, database.sqliteDatabase().lastErrorMsg());
return false;
}
- database->setExpectedVersion(m_newVersion);
+ database.setExpectedVersion(m_newVersion);
return true;
}
-void ChangeVersionWrapper::handleCommitFailedAfterPostflight(SQLTransactionBackend* transaction)
+void ChangeVersionWrapper::handleCommitFailedAfterPostflight(SQLTransaction& transaction)
{
- transaction->database()->setCachedVersion(m_oldVersion);
+ transaction.database().setCachedVersion(m_oldVersion);
}
} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
diff --git a/Source/WebCore/Modules/webdatabase/ChangeVersionWrapper.h b/Source/WebCore/Modules/webdatabase/ChangeVersionWrapper.h
index 3b1a2a058..eb7efe3c4 100644
--- a/Source/WebCore/Modules/webdatabase/ChangeVersionWrapper.h
+++ b/Source/WebCore/Modules/webdatabase/ChangeVersionWrapper.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,12 +25,10 @@
* (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 ChangeVersionWrapper_h
-#define ChangeVersionWrapper_h
-#if ENABLE(SQL_DATABASE)
+#pragma once
-#include "SQLTransactionBackend.h"
+#include "SQLTransaction.h"
#include <wtf/Forward.h>
namespace WebCore {
@@ -39,12 +37,12 @@ class SQLError;
class ChangeVersionWrapper : public SQLTransactionWrapper {
public:
- static PassRefPtr<ChangeVersionWrapper> create(const String& oldVersion, const String& newVersion) { return adoptRef(new ChangeVersionWrapper(oldVersion, newVersion)); }
+ static Ref<ChangeVersionWrapper> create(const String& oldVersion, const String& newVersion) { return adoptRef(*new ChangeVersionWrapper(oldVersion, newVersion)); }
- virtual bool performPreflight(SQLTransactionBackend*);
- virtual bool performPostflight(SQLTransactionBackend*);
- virtual SQLError* sqlError() const { return m_sqlError.get(); }
- virtual void handleCommitFailedAfterPostflight(SQLTransactionBackend*);
+ bool performPreflight(SQLTransaction&) override;
+ bool performPostflight(SQLTransaction&) override;
+ SQLError* sqlError() const override { return m_sqlError.get(); };
+ void handleCommitFailedAfterPostflight(SQLTransaction&) override;
private:
ChangeVersionWrapper(const String& oldVersion, const String& newVersion);
@@ -55,7 +53,3 @@ private:
};
} // namespace WebCore
-
-#endif
-
-#endif // ChangeVersionWrapper_h
diff --git a/Source/WebCore/Modules/webdatabase/DOMWindowWebDatabase.cpp b/Source/WebCore/Modules/webdatabase/DOMWindowWebDatabase.cpp
index 8ccaa5a80..641281ef5 100644
--- a/Source/WebCore/Modules/webdatabase/DOMWindowWebDatabase.cpp
+++ b/Source/WebCore/Modules/webdatabase/DOMWindowWebDatabase.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
@@ -25,39 +25,37 @@
*/
#include "config.h"
-
-#if ENABLE(SQL_DATABASE)
-
#include "DOMWindowWebDatabase.h"
#include "DOMWindow.h"
#include "Database.h"
-#include "DatabaseCallback.h"
#include "DatabaseManager.h"
#include "Document.h"
-#include "Frame.h"
+#include "ExceptionCode.h"
#include "SecurityOrigin.h"
namespace WebCore {
-PassRefPtr<Database> DOMWindowWebDatabase::openDatabase(DOMWindow* window, const String& name, const String& version, const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, ExceptionCode& ec)
+ExceptionOr<RefPtr<Database>> DOMWindowWebDatabase::openDatabase(DOMWindow& window, const String& name, const String& version, const String& displayName, unsigned estimatedSize, RefPtr<DatabaseCallback>&& creationCallback)
{
- if (!window->isCurrentlyDisplayedInFrame())
- return 0;
-
- RefPtr<Database> database = 0;
- DatabaseManager& dbManager = DatabaseManager::manager();
- DatabaseError error = DatabaseError::None;
- if (dbManager.isAvailable() && window->document()->securityOrigin()->canAccessDatabase(window->document()->topOrigin())) {
- database = dbManager.openDatabase(window->document(), name, version, displayName, estimatedSize, creationCallback, error);
- ASSERT(database || error != DatabaseError::None);
- ec = DatabaseManager::exceptionCodeForDatabaseError(error);
- } else
- ec = SECURITY_ERR;
-
- return database;
+ if (!window.isCurrentlyDisplayedInFrame())
+ return RefPtr<Database> { nullptr };
+ auto& manager = DatabaseManager::singleton();
+ if (!manager.isAvailable())
+ return Exception { SECURITY_ERR };
+ auto* document = window.document();
+ if (!document)
+ return Exception { SECURITY_ERR };
+ auto& securityOrigin = document->securityOrigin();
+ if (!securityOrigin.canAccessDatabase(document->topOrigin()))
+ return Exception { SECURITY_ERR };
+ auto result = manager.openDatabase(*window.document(), name, version, displayName, estimatedSize, WTFMove(creationCallback));
+ if (result.hasException()) {
+ // FIXME: To preserve our past behavior, this discards the error string in the exception.
+ // At a later time we may decide that we want to use the error strings, and if so we can just return the exception as is.
+ return Exception { result.releaseException().code() };
+ }
+ return RefPtr<Database> { result.releaseReturnValue() };
}
} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
diff --git a/Source/WebCore/Modules/webdatabase/DOMWindowWebDatabase.h b/Source/WebCore/Modules/webdatabase/DOMWindowWebDatabase.h
index 96b6cfc95..5ae95f175 100644
--- a/Source/WebCore/Modules/webdatabase/DOMWindowWebDatabase.h
+++ b/Source/WebCore/Modules/webdatabase/DOMWindowWebDatabase.h
@@ -11,47 +11,35 @@
* notice, this 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
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DOMWindowWebDatabase_h
-#define DOMWindowWebDatabase_h
+#pragma once
-#if ENABLE(SQL_DATABASE)
-#include "ExceptionCode.h"
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/RefPtr.h>
-#include <wtf/text/WTFString.h>
+#include "ExceptionOr.h"
namespace WebCore {
class DOMWindow;
class Database;
class DatabaseCallback;
-class Frame;
class DOMWindowWebDatabase {
public:
- static PassRefPtr<Database> openDatabase(DOMWindow*, const String& name, const String& version, const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, ExceptionCode&);
+ static ExceptionOr<RefPtr<Database>> openDatabase(DOMWindow&, const String& name, const String& version, const String& displayName, unsigned estimatedSize, RefPtr<DatabaseCallback>&& creationCallback);
-private:
- DOMWindowWebDatabase() { };
- ~DOMWindowWebDatabase() { };
+ DOMWindowWebDatabase() = delete;
+ ~DOMWindowWebDatabase() = delete;
};
} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
-
-#endif // DOMWindowWebDatabase_h
diff --git a/Source/WebCore/Modules/webdatabase/DOMWindowWebDatabase.idl b/Source/WebCore/Modules/webdatabase/DOMWindowWebDatabase.idl
index 375f0458c..e140cc4c2 100644
--- a/Source/WebCore/Modules/webdatabase/DOMWindowWebDatabase.idl
+++ b/Source/WebCore/Modules/webdatabase/DOMWindowWebDatabase.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
@@ -24,9 +24,6 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-[
- Conditional=SQL_DATABASE,
-] partial interface DOMWindow {
- [RaisesException] Database openDatabase(DOMString name, DOMString version, DOMString displayName, unsigned long estimatedSize, optional DatabaseCallback creationCallback);
+partial interface DOMWindow {
+ [MayThrowException] Database? openDatabase(DOMString name, DOMString version, DOMString displayName, unsigned long estimatedSize, optional DatabaseCallback? creationCallback);
};
-
diff --git a/Source/WebCore/Modules/webdatabase/Database.cpp b/Source/WebCore/Modules/webdatabase/Database.cpp
index e182795be..8869f6bf2 100644
--- a/Source/WebCore/Modules/webdatabase/Database.cpp
+++ b/Source/WebCore/Modules/webdatabase/Database.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.
*
@@ -29,11 +29,10 @@
#include "config.h"
#include "Database.h"
-#if ENABLE(SQL_DATABASE)
-
#include "ChangeVersionData.h"
-#include "CrossThreadTask.h"
-#include "DatabaseBackendContext.h"
+#include "ChangeVersionWrapper.h"
+#include "DOMWindow.h"
+#include "DatabaseAuthorizer.h"
#include "DatabaseCallback.h"
#include "DatabaseContext.h"
#include "DatabaseManager.h"
@@ -41,196 +40,666 @@
#include "DatabaseThread.h"
#include "DatabaseTracker.h"
#include "Document.h"
+#include "ExceptionCode.h"
#include "JSDOMWindow.h"
#include "Logging.h"
-#include "NotImplemented.h"
-#include "Page.h"
#include "SQLError.h"
#include "SQLTransaction.h"
#include "SQLTransactionCallback.h"
#include "SQLTransactionErrorCallback.h"
+#include "SQLiteDatabaseTracker.h"
#include "SQLiteStatement.h"
+#include "SQLiteTransaction.h"
#include "ScriptExecutionContext.h"
#include "SecurityOrigin.h"
#include "VoidCallback.h"
-#include <wtf/OwnPtr.h>
-#include <wtf/PassOwnPtr.h>
-#include <wtf/PassRefPtr.h>
+#include <wtf/NeverDestroyed.h>
#include <wtf/RefPtr.h>
#include <wtf/StdLibExtras.h>
#include <wtf/text/CString.h>
-#if PLATFORM(IOS)
-#include "SQLiteDatabaseTracker.h"
-#endif
-
namespace WebCore {
-PassRefPtr<Database> Database::create(ScriptExecutionContext*, PassRefPtr<DatabaseBackendBase> backend)
+// Registering "opened" databases with the DatabaseTracker
+// =======================================================
+// The DatabaseTracker maintains a list of databases that have been
+// "opened" so that the client can call interrupt or delete on every database
+// associated with a DatabaseContext.
+//
+// We will only call DatabaseTracker::addOpenDatabase() to add the database
+// to the tracker as opened when we've succeeded in opening the database,
+// and will set m_opened to true. Similarly, we only call
+// DatabaseTracker::removeOpenDatabase() to remove the database from the
+// tracker when we set m_opened to false in closeDatabase(). This sets up
+// a simple symmetry between open and close operations, and a direct
+// correlation to adding and removing databases from the tracker's list,
+// thus ensuring that we have a correct list for the interrupt and
+// delete operations to work on.
+//
+// The only databases instances not tracked by the tracker's open database
+// list are the ones that have not been added yet, or the ones that we
+// attempted an open on but failed to. Such instances only exist in the
+// factory functions for creating database backends.
+//
+// The factory functions will either call openAndVerifyVersion() or
+// performOpenAndVerify(). These methods will add the newly instantiated
+// database backend if they succeed in opening the requested database.
+// In the case of failure to open the database, the factory methods will
+// simply discard the newly instantiated database backend when they return.
+// The ref counting mechanims will automatically destruct the un-added
+// (and un-returned) databases instances.
+
+static const char versionKey[] = "WebKitDatabaseVersionKey";
+static const char unqualifiedInfoTableName[] = "__WebKitDatabaseInfoTable__";
+
+static const char* fullyQualifiedInfoTableName()
{
- // FIXME: Currently, we're only simulating the backend by return the
- // frontend database as its own the backend. When we split the 2 apart,
- // this create() function should be changed to be a factory method for
- // instantiating the backend.
- return static_cast<Database*>(backend.get());
+ static const char qualifier[] = "main.";
+ static char qualifiedName[sizeof(qualifier) + sizeof(unqualifiedInfoTableName) - 1];
+
+ static std::once_flag onceFlag;
+ std::call_once(onceFlag, [] {
+ strcpy(qualifiedName, qualifier);
+ strcpy(qualifiedName + sizeof(qualifier) - 1, unqualifiedInfoTableName);
+ });
+
+ return qualifiedName;
}
-Database::Database(PassRefPtr<DatabaseBackendContext> databaseContext,
- const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize)
- : DatabaseBase(databaseContext->scriptExecutionContext())
- , DatabaseBackend(databaseContext, name, expectedVersion, displayName, estimatedSize)
- , m_databaseContext(DatabaseBackend::databaseContext()->frontend())
- , m_deleted(false)
+static String formatErrorMessage(const char* message, int sqliteErrorCode, const char* sqliteErrorMessage)
{
- m_databaseThreadSecurityOrigin = m_contextThreadSecurityOrigin->isolatedCopy();
- setFrontend(this);
+ return String::format("%s (%d %s)", message, sqliteErrorCode, sqliteErrorMessage);
+}
+
+static bool setTextValueInDatabase(SQLiteDatabase& db, const String& query, const String& value)
+{
+ SQLiteStatement statement(db, query);
+ int result = statement.prepare();
+
+ if (result != SQLITE_OK) {
+ LOG_ERROR("Failed to prepare statement to set value in database (%s)", query.ascii().data());
+ return false;
+ }
+
+ statement.bindText(1, value);
+
+ result = statement.step();
+ if (result != SQLITE_DONE) {
+ LOG_ERROR("Failed to step statement to set value in database (%s)", query.ascii().data());
+ return false;
+ }
- ASSERT(m_databaseContext->databaseThread());
+ return true;
}
-class DerefContextTask : public ScriptExecutionContext::Task {
-public:
- static PassOwnPtr<DerefContextTask> create(PassRefPtr<ScriptExecutionContext> context)
- {
- return adoptPtr(new DerefContextTask(context));
+static bool retrieveTextResultFromDatabase(SQLiteDatabase& db, const String& query, String& resultString)
+{
+ SQLiteStatement statement(db, query);
+ int result = statement.prepare();
+
+ if (result != SQLITE_OK) {
+ LOG_ERROR("Error (%i) preparing statement to read text result from database (%s)", result, query.ascii().data());
+ return false;
}
- virtual void performTask(ScriptExecutionContext* context)
- {
- ASSERT_UNUSED(context, context == m_context);
- m_context.clear();
+ result = statement.step();
+ if (result == SQLITE_ROW) {
+ resultString = statement.getColumnText(0);
+ return true;
+ }
+ if (result == SQLITE_DONE) {
+ resultString = String();
+ return true;
}
- virtual bool isCleanupTask() const { return true; }
+ LOG_ERROR("Error (%i) reading text result from database (%s)", result, query.ascii().data());
+ return false;
+}
-private:
- DerefContextTask(PassRefPtr<ScriptExecutionContext> context)
- : m_context(context)
+// FIXME: move all guid-related functions to a DatabaseVersionTracker class.
+static StaticLock guidMutex;
+
+static HashMap<DatabaseGUID, String>& guidToVersionMap()
+{
+ static NeverDestroyed<HashMap<DatabaseGUID, String>> map;
+ return map;
+}
+
+// NOTE: Caller must lock guidMutex().
+static inline void updateGUIDVersionMap(DatabaseGUID guid, const String& newVersion)
+{
+ // Note: It is not safe to put an empty string into the guidToVersionMap() map.
+ // That's because the map is cross-thread, but empty strings are per-thread.
+ // The copy() function makes a version of the string you can use on the current
+ // thread, but we need a string we can keep in a cross-thread data structure.
+ // FIXME: This is a quite-awkward restriction to have to program with.
+
+ // Map empty string to null string (see comment above).
+ guidToVersionMap().set(guid, newVersion.isEmpty() ? String() : newVersion.isolatedCopy());
+}
+
+static HashMap<DatabaseGUID, HashSet<Database*>>& guidToDatabaseMap()
+{
+ static NeverDestroyed<HashMap<DatabaseGUID, HashSet<Database*>>> map;
+ return map;
+}
+
+static inline DatabaseGUID guidForOriginAndName(const String& origin, const String& name)
+{
+ static NeverDestroyed<HashMap<String, DatabaseGUID>> map;
+ return map.get().ensure(makeString(origin, '/', name), [] {
+ static DatabaseGUID lastUsedGUID;
+ return ++lastUsedGUID;
+ }).iterator->value;
+}
+
+Database::Database(DatabaseContext& context, const String& name, const String& expectedVersion, const String& displayName, unsigned estimatedSize)
+ : m_scriptExecutionContext(*context.scriptExecutionContext())
+ , m_contextThreadSecurityOrigin(m_scriptExecutionContext->securityOrigin()->isolatedCopy())
+ , m_databaseThreadSecurityOrigin(m_scriptExecutionContext->securityOrigin()->isolatedCopy())
+ , m_databaseContext(context)
+ , m_name((name.isNull() ? emptyString() : name).isolatedCopy())
+ , m_expectedVersion(expectedVersion.isolatedCopy())
+ , m_displayName(displayName.isolatedCopy())
+ , m_estimatedSize(estimatedSize)
+ , m_filename(DatabaseManager::singleton().fullPathForDatabase(*m_scriptExecutionContext->securityOrigin(), m_name))
+ , m_databaseAuthorizer(DatabaseAuthorizer::create(unqualifiedInfoTableName))
+{
{
+ std::lock_guard<StaticLock> locker(guidMutex);
+
+ m_guid = guidForOriginAndName(securityOrigin().securityOrigin()->toString(), name);
+ guidToDatabaseMap().ensure(m_guid, [] {
+ return HashSet<Database*>();
+ }).iterator->value.add(this);
}
-
- RefPtr<ScriptExecutionContext> m_context;
-};
+
+ m_databaseContext->databaseThread();
+
+ ASSERT(m_databaseContext->existingDatabaseThread());
+}
+
+DatabaseThread& Database::databaseThread()
+{
+ ASSERT(m_databaseContext->existingDatabaseThread());
+ return *m_databaseContext->existingDatabaseThread();
+}
Database::~Database()
{
// The reference to the ScriptExecutionContext needs to be cleared on the JavaScript thread. If we're on that thread already, we can just let the RefPtr's destruction do the dereffing.
if (!m_scriptExecutionContext->isContextThread()) {
- // Grab a pointer to the script execution here because we're releasing it when we pass it to
- // DerefContextTask::create.
- ScriptExecutionContext* scriptExecutionContext = m_scriptExecutionContext.get();
-
- scriptExecutionContext->postTask(DerefContextTask::create(m_scriptExecutionContext.release()));
+ auto passedContext = WTFMove(m_scriptExecutionContext);
+ auto& contextRef = passedContext.get();
+ contextRef.postTask({ScriptExecutionContext::Task::CleanupTask, [passedContext = WTFMove(passedContext), databaseContext = WTFMove(m_databaseContext)] (ScriptExecutionContext& context) {
+ ASSERT_UNUSED(context, &context == passedContext.ptr());
+ }});
}
+
+ // SQLite is "multi-thread safe", but each database handle can only be used
+ // on a single thread at a time.
+ //
+ // For DatabaseBackend, we open the SQLite database on the DatabaseThread,
+ // and hence we should also close it on that same thread. This means that the
+ // SQLite database need to be closed by another mechanism (see
+ // DatabaseContext::stopDatabases()). By the time we get here, the SQLite
+ // database should have already been closed.
+
+ ASSERT(!m_opened);
}
-Database* Database::from(DatabaseBackend* backend)
+ExceptionOr<void> Database::openAndVerifyVersion(bool setVersionInNewDatabase)
{
- return static_cast<Database*>(backend->m_frontend);
+ DatabaseTaskSynchronizer synchronizer;
+ auto& thread = databaseThread();
+ if (thread.terminationRequested(&synchronizer))
+ return Exception { INVALID_STATE_ERR };
+
+ ExceptionOr<void> result;
+ auto task = std::make_unique<DatabaseOpenTask>(*this, setVersionInNewDatabase, synchronizer, result);
+ thread.scheduleImmediateTask(WTFMove(task));
+ synchronizer.waitForTaskCompletion();
+
+ return result;
}
-PassRefPtr<DatabaseBackend> Database::backend()
+void Database::interrupt()
{
- return this;
+ // It is safe to call this from any thread for an opened or closed database.
+ m_sqliteDatabase.interrupt();
+}
+
+void Database::close()
+{
+ auto& thread = databaseThread();
+
+ DatabaseTaskSynchronizer synchronizer;
+ if (thread.terminationRequested(&synchronizer)) {
+ LOG(StorageAPI, "Database handle %p is on a terminated DatabaseThread, cannot be marked for normal closure\n", this);
+ return;
+ }
+
+ thread.scheduleImmediateTask(std::make_unique<DatabaseCloseTask>(*this, synchronizer));
+
+ // FIXME: iOS depends on this function blocking until the database is closed as part
+ // of closing all open databases from a process assertion expiration handler.
+ // See <https://bugs.webkit.org/show_bug.cgi?id=157184>.
+ synchronizer.waitForTaskCompletion();
+}
+
+void Database::performClose()
+{
+ ASSERT(currentThread() == databaseThread().getThreadID());
+
+ {
+ LockHolder locker(m_transactionInProgressMutex);
+
+ // Clean up transactions that have not been scheduled yet:
+ // Transaction phase 1 cleanup. See comment on "What happens if a
+ // transaction is interrupted?" at the top of SQLTransactionBackend.cpp.
+ while (!m_transactionQueue.isEmpty())
+ m_transactionQueue.takeFirst()->notifyDatabaseThreadIsShuttingDown();
+
+ m_isTransactionQueueEnabled = false;
+ m_transactionInProgress = false;
+ }
+
+ closeDatabase();
+
+ // DatabaseThread keeps databases alive by referencing them in its
+ // m_openDatabaseSet. DatabaseThread::recordDatabaseClose() will remove
+ // this database from that set (which effectively deref's it). We hold on
+ // to it with a local pointer here for a liitle longer, so that we can
+ // unschedule any DatabaseTasks that refer to it before the database gets
+ // deleted.
+ Ref<Database> protectedThis(*this);
+ auto& thread = databaseThread();
+ thread.recordDatabaseClosed(*this);
+ thread.unscheduleDatabaseTasks(*this);
+}
+
+class DoneCreatingDatabaseOnExitCaller {
+public:
+ DoneCreatingDatabaseOnExitCaller(Database& database)
+ : m_database(database)
+ {
+ }
+
+ ~DoneCreatingDatabaseOnExitCaller()
+ {
+ DatabaseTracker::singleton().doneCreatingDatabase(m_database);
+ }
+
+private:
+ Database& m_database;
+};
+
+ExceptionOr<void> Database::performOpenAndVerify(bool shouldSetVersionInNewDatabase)
+{
+ DoneCreatingDatabaseOnExitCaller onExitCaller(*this);
+
+ const int maxSqliteBusyWaitTime = 30000;
+
+#if PLATFORM(IOS)
+ {
+ // Make sure we wait till the background removal of the empty database files finished before trying to open any database.
+ LockHolder locker(DatabaseTracker::openDatabaseMutex());
+ }
+#endif
+
+ SQLiteTransactionInProgressAutoCounter transactionCounter;
+
+ if (!m_sqliteDatabase.open(m_filename, true))
+ return Exception { INVALID_STATE_ERR, formatErrorMessage("unable to open database", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()) };
+ if (!m_sqliteDatabase.turnOnIncrementalAutoVacuum())
+ LOG_ERROR("Unable to turn on incremental auto-vacuum (%d %s)", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
+
+ m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime);
+
+ String currentVersion;
+ {
+ std::lock_guard<StaticLock> locker(guidMutex);
+
+ auto entry = guidToVersionMap().find(m_guid);
+ if (entry != guidToVersionMap().end()) {
+ // Map null string to empty string (see updateGUIDVersionMap()).
+ currentVersion = entry->value.isNull() ? emptyString() : entry->value.isolatedCopy();
+ LOG(StorageAPI, "Current cached version for guid %i is %s", m_guid, currentVersion.ascii().data());
+ } else {
+ LOG(StorageAPI, "No cached version for guid %i", m_guid);
+
+ SQLiteTransaction transaction(m_sqliteDatabase);
+ transaction.begin();
+ if (!transaction.inProgress()) {
+ String message = formatErrorMessage("unable to open database, failed to start transaction", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
+ m_sqliteDatabase.close();
+ return Exception { INVALID_STATE_ERR, WTFMove(message) };
+ }
+
+ String tableName(unqualifiedInfoTableName);
+ if (!m_sqliteDatabase.tableExists(tableName)) {
+ m_new = true;
+
+ if (!m_sqliteDatabase.executeCommand("CREATE TABLE " + tableName + " (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,value TEXT NOT NULL ON CONFLICT FAIL);")) {
+ String message = formatErrorMessage("unable to open database, failed to create 'info' table", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
+ transaction.rollback();
+ m_sqliteDatabase.close();
+ return Exception { INVALID_STATE_ERR, WTFMove(message) };
+ }
+ } else if (!getVersionFromDatabase(currentVersion, false)) {
+ String message = formatErrorMessage("unable to open database, failed to read current version", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
+ transaction.rollback();
+ m_sqliteDatabase.close();
+ return Exception { INVALID_STATE_ERR, WTFMove(message) };
+ }
+
+ if (currentVersion.length()) {
+ LOG(StorageAPI, "Retrieved current version %s from database %s", currentVersion.ascii().data(), databaseDebugName().ascii().data());
+ } else if (!m_new || shouldSetVersionInNewDatabase) {
+ LOG(StorageAPI, "Setting version %s in database %s that was just created", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data());
+ if (!setVersionInDatabase(m_expectedVersion, false)) {
+ String message = formatErrorMessage("unable to open database, failed to write current version", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
+ transaction.rollback();
+ m_sqliteDatabase.close();
+ return Exception { INVALID_STATE_ERR, WTFMove(message) };
+ }
+ currentVersion = m_expectedVersion;
+ }
+ updateGUIDVersionMap(m_guid, currentVersion);
+ transaction.commit();
+ }
+ }
+
+ if (currentVersion.isNull()) {
+ LOG(StorageAPI, "Database %s does not have its version set", databaseDebugName().ascii().data());
+ currentVersion = emptyString();
+ }
+
+ // If the expected version isn't the empty string, ensure that the current database version we have matches that version. Otherwise, set an exception.
+ // If the expected version is the empty string, then we always return with whatever version of the database we have.
+ if ((!m_new || shouldSetVersionInNewDatabase) && m_expectedVersion.length() && m_expectedVersion != currentVersion) {
+ m_sqliteDatabase.close();
+ return Exception { INVALID_STATE_ERR, "unable to open database, version mismatch, '" + m_expectedVersion + "' does not match the currentVersion of '" + currentVersion + "'" };
+ }
+
+ m_sqliteDatabase.setAuthorizer(m_databaseAuthorizer.get());
+
+ DatabaseTracker::singleton().addOpenDatabase(*this);
+ m_opened = true;
+
+ if (m_new && !shouldSetVersionInNewDatabase)
+ m_expectedVersion = emptyString(); // The caller provided a creationCallback which will set the expected version.
+
+ databaseThread().recordDatabaseOpen(*this);
+
+ return { };
+}
+
+void Database::closeDatabase()
+{
+ if (!m_opened)
+ return;
+
+ m_sqliteDatabase.close();
+ m_opened = false;
+
+ // See comment at the top this file regarding calling removeOpenDatabase().
+ DatabaseTracker::singleton().removeOpenDatabase(*this);
+
+ {
+ std::lock_guard<StaticLock> locker(guidMutex);
+
+ auto it = guidToDatabaseMap().find(m_guid);
+ ASSERT(it != guidToDatabaseMap().end());
+ ASSERT(it->value.contains(this));
+ it->value.remove(this);
+ if (it->value.isEmpty()) {
+ guidToDatabaseMap().remove(it);
+ guidToVersionMap().remove(m_guid);
+ }
+ }
+}
+
+bool Database::getVersionFromDatabase(String& version, bool shouldCacheVersion)
+{
+ String query(String("SELECT value FROM ") + fullyQualifiedInfoTableName() + " WHERE key = '" + versionKey + "';");
+
+ m_databaseAuthorizer->disable();
+
+ bool result = retrieveTextResultFromDatabase(m_sqliteDatabase, query, version);
+ if (result) {
+ if (shouldCacheVersion)
+ setCachedVersion(version);
+ } else
+ LOG_ERROR("Failed to retrieve version from database %s", databaseDebugName().ascii().data());
+
+ m_databaseAuthorizer->enable();
+
+ return result;
+}
+
+bool Database::setVersionInDatabase(const String& version, bool shouldCacheVersion)
+{
+ // The INSERT will replace an existing entry for the database with the new version number, due to the UNIQUE ON CONFLICT REPLACE
+ // clause in the CREATE statement (see Database::performOpenAndVerify()).
+ String query(String("INSERT INTO ") + fullyQualifiedInfoTableName() + " (key, value) VALUES ('" + versionKey + "', ?);");
+
+ m_databaseAuthorizer->disable();
+
+ bool result = setTextValueInDatabase(m_sqliteDatabase, query, version);
+ if (result) {
+ if (shouldCacheVersion)
+ setCachedVersion(version);
+ } else
+ LOG_ERROR("Failed to set version %s in database (%s)", version.ascii().data(), query.ascii().data());
+
+ m_databaseAuthorizer->enable();
+
+ return result;
+}
+
+void Database::setExpectedVersion(const String& version)
+{
+ m_expectedVersion = version.isolatedCopy();
+}
+
+String Database::getCachedVersion() const
+{
+ std::lock_guard<StaticLock> locker(guidMutex);
+
+ return guidToVersionMap().get(m_guid).isolatedCopy();
+}
+
+void Database::setCachedVersion(const String& actualVersion)
+{
+ std::lock_guard<StaticLock> locker(guidMutex);
+
+ updateGUIDVersionMap(m_guid, actualVersion);
+}
+
+bool Database::getActualVersionForTransaction(String &actualVersion)
+{
+ ASSERT(m_sqliteDatabase.transactionInProgress());
+
+ // Note: In multi-process browsers the cached value may be inaccurate.
+ // So we retrieve the value from the database and update the cached value here.
+ return getVersionFromDatabase(actualVersion, true);
+}
+
+void Database::scheduleTransaction()
+{
+ ASSERT(!m_transactionInProgressMutex.tryLock()); // Locked by caller.
+
+ if (!m_isTransactionQueueEnabled || m_transactionQueue.isEmpty()) {
+ m_transactionInProgress = false;
+ return;
+ }
+
+ m_transactionInProgress = true;
+
+ auto transaction = m_transactionQueue.takeFirst();
+ auto task = std::make_unique<DatabaseTransactionTask>(WTFMove(transaction));
+ LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for transaction %p\n", task.get(), task->transaction());
+ databaseThread().scheduleTask(WTFMove(task));
+}
+
+void Database::scheduleTransactionStep(SQLTransaction& transaction)
+{
+ auto& thread = databaseThread();
+
+ auto task = std::make_unique<DatabaseTransactionTask>(&transaction);
+ LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for the transaction step\n", task.get());
+ thread.scheduleTask(WTFMove(task));
+}
+
+void Database::inProgressTransactionCompleted()
+{
+ LockHolder locker(m_transactionInProgressMutex);
+ m_transactionInProgress = false;
+ scheduleTransaction();
+}
+
+bool Database::hasPendingTransaction()
+{
+ LockHolder locker(m_transactionInProgressMutex);
+ return m_transactionInProgress || !m_transactionQueue.isEmpty();
+}
+
+SQLTransactionCoordinator* Database::transactionCoordinator()
+{
+ return databaseThread().transactionCoordinator();
}
String Database::version() const
{
if (m_deleted)
return String();
- return DatabaseBackendBase::version();
+
+ // Note: In multi-process browsers the cached value may be accurate, but we cannot read the
+ // actual version from the database without potentially inducing a deadlock.
+ // FIXME: Add an async version getter to the DatabaseAPI.
+ return getCachedVersion();
}
void Database::markAsDeletedAndClose()
{
- if (m_deleted || !databaseContext()->databaseThread())
+ if (m_deleted)
return;
LOG(StorageAPI, "Marking %s (%p) as deleted", stringIdentifier().ascii().data(), this);
m_deleted = true;
- DatabaseTaskSynchronizer synchronizer;
- if (databaseContext()->databaseThread()->terminationRequested(&synchronizer)) {
- LOG(StorageAPI, "Database handle %p is on a terminated DatabaseThread, cannot be marked for normal closure\n", this);
- return;
- }
+ close();
+}
- auto task = DatabaseCloseTask::create(this, &synchronizer);
- databaseContext()->databaseThread()->scheduleImmediateTask(std::move(task));
- synchronizer.waitForTaskCompletion();
+void Database::changeVersion(const String& oldVersion, const String& newVersion, RefPtr<SQLTransactionCallback>&& callback, RefPtr<SQLTransactionErrorCallback>&& errorCallback, RefPtr<VoidCallback>&& successCallback)
+{
+ runTransaction(WTFMove(callback), WTFMove(errorCallback), WTFMove(successCallback), ChangeVersionWrapper::create(oldVersion, newVersion), false);
}
-void Database::closeImmediately()
+void Database::transaction(RefPtr<SQLTransactionCallback>&& callback, RefPtr<SQLTransactionErrorCallback>&& errorCallback, RefPtr<VoidCallback>&& successCallback)
{
- ASSERT(m_scriptExecutionContext->isContextThread());
- DatabaseThread* databaseThread = databaseContext()->databaseThread();
- if (databaseThread && !databaseThread->terminationRequested() && opened()) {
- logErrorMessage("forcibly closing database");
- databaseThread->scheduleImmediateTask(DatabaseCloseTask::create(this, 0));
- }
+ runTransaction(WTFMove(callback), WTFMove(errorCallback), WTFMove(successCallback), nullptr, false);
}
-void Database::changeVersion(const String& oldVersion, const String& newVersion,
- PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback,
- PassRefPtr<VoidCallback> successCallback)
+void Database::readTransaction(RefPtr<SQLTransactionCallback>&& callback, RefPtr<SQLTransactionErrorCallback>&& errorCallback, RefPtr<VoidCallback>&& successCallback)
{
- ChangeVersionData data(oldVersion, newVersion);
- runTransaction(callback, errorCallback, successCallback, false, &data);
+ runTransaction(WTFMove(callback), WTFMove(errorCallback), WTFMove(successCallback), nullptr, true);
}
-void Database::transaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback)
+String Database::stringIdentifier() const
{
- runTransaction(callback, errorCallback, successCallback, false);
+ // Return a deep copy for ref counting thread safety
+ return m_name.isolatedCopy();
}
-void Database::readTransaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback)
+String Database::displayName() const
{
- runTransaction(callback, errorCallback, successCallback, true);
+ // Return a deep copy for ref counting thread safety
+ return m_displayName.isolatedCopy();
}
-static void callTransactionErrorCallback(ScriptExecutionContext*, PassRefPtr<SQLTransactionErrorCallback> callback, PassRefPtr<SQLError> error)
+unsigned Database::estimatedSize() const
{
- callback->handleEvent(error.get());
+ return m_estimatedSize;
}
-void Database::runTransaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback,
- PassRefPtr<VoidCallback> successCallback, bool readOnly, const ChangeVersionData* changeVersionData)
+String Database::fileName() const
{
- RefPtr<SQLTransactionErrorCallback> anotherRefToErrorCallback = errorCallback;
- RefPtr<SQLTransaction> transaction = SQLTransaction::create(this, callback, successCallback, anotherRefToErrorCallback, readOnly);
+ // Return a deep copy for ref counting thread safety
+ return m_filename.isolatedCopy();
+}
- RefPtr<SQLTransactionBackend> transactionBackend;
- transactionBackend = backend()->runTransaction(transaction.release(), readOnly, changeVersionData);
- if (!transactionBackend && anotherRefToErrorCallback) {
- RefPtr<SQLError> error = SQLError::create(SQLError::UNKNOWN_ERR, "database has been closed");
- scriptExecutionContext()->postTask(createCallbackTask(&callTransactionErrorCallback, anotherRefToErrorCallback, error.release()));
- }
+DatabaseDetails Database::details() const
+{
+ // This code path is only used for database quota delegate calls, so file dates are irrelevant and left uninitialized.
+ return DatabaseDetails(stringIdentifier(), displayName(), estimatedSize(), 0, 0, 0);
}
-class DeliverPendingCallbackTask : public ScriptExecutionContext::Task {
-public:
- static PassOwnPtr<DeliverPendingCallbackTask> create(PassRefPtr<SQLTransaction> transaction)
- {
- return adoptPtr(new DeliverPendingCallbackTask(transaction));
- }
+void Database::disableAuthorizer()
+{
+ m_databaseAuthorizer->disable();
+}
- virtual void performTask(ScriptExecutionContext*)
- {
- m_transaction->performPendingCallback();
- }
+void Database::enableAuthorizer()
+{
+ m_databaseAuthorizer->enable();
+}
-private:
- DeliverPendingCallbackTask(PassRefPtr<SQLTransaction> transaction)
- : m_transaction(transaction)
- {
+void Database::setAuthorizerPermissions(int permissions)
+{
+ m_databaseAuthorizer->setPermissions(permissions);
+}
+
+bool Database::lastActionChangedDatabase()
+{
+ return m_databaseAuthorizer->lastActionChangedDatabase();
+}
+
+bool Database::lastActionWasInsert()
+{
+ return m_databaseAuthorizer->lastActionWasInsert();
+}
+
+void Database::resetDeletes()
+{
+ m_databaseAuthorizer->resetDeletes();
+}
+
+bool Database::hadDeletes()
+{
+ return m_databaseAuthorizer->hadDeletes();
+}
+
+void Database::resetAuthorizer()
+{
+ m_databaseAuthorizer->reset();
+}
+
+void Database::runTransaction(RefPtr<SQLTransactionCallback>&& callback, RefPtr<SQLTransactionErrorCallback>&& errorCallback, RefPtr<VoidCallback>&& successCallback, RefPtr<SQLTransactionWrapper>&& wrapper, bool readOnly)
+{
+ LockHolder locker(m_transactionInProgressMutex);
+ if (!m_isTransactionQueueEnabled) {
+ if (errorCallback) {
+ RefPtr<SQLTransactionErrorCallback> errorCallbackProtector = WTFMove(errorCallback);
+ m_scriptExecutionContext->postTask([errorCallbackProtector](ScriptExecutionContext&) {
+ errorCallbackProtector->handleEvent(SQLError::create(SQLError::UNKNOWN_ERR, "database has been closed").ptr());
+ });
+ }
+ return;
}
- RefPtr<SQLTransaction> m_transaction;
-};
+ m_transactionQueue.append(SQLTransaction::create(*this, WTFMove(callback), WTFMove(successCallback), errorCallback.copyRef(), WTFMove(wrapper), readOnly));
+ if (!m_transactionInProgress)
+ scheduleTransaction();
+}
void Database::scheduleTransactionCallback(SQLTransaction* transaction)
{
- m_scriptExecutionContext->postTask(DeliverPendingCallbackTask::create(transaction));
+ RefPtr<SQLTransaction> transactionProtector(transaction);
+ m_scriptExecutionContext->postTask([transactionProtector] (ScriptExecutionContext&) {
+ transactionProtector->performPendingCallback();
+ });
}
Vector<String> Database::performGetTableNames()
@@ -238,7 +707,7 @@ Vector<String> Database::performGetTableNames()
disableAuthorizer();
SQLiteStatement statement(sqliteDatabase(), "SELECT name FROM sqlite_master WHERE type='table';");
- if (statement.prepare() != SQLResultOk) {
+ if (statement.prepare() != SQLITE_OK) {
LOG_ERROR("Unable to retrieve list of tables for database %s", databaseDebugName().ascii().data());
enableAuthorizer();
return Vector<String>();
@@ -246,15 +715,15 @@ Vector<String> Database::performGetTableNames()
Vector<String> tableNames;
int result;
- while ((result = statement.step()) == SQLResultRow) {
+ while ((result = statement.step()) == SQLITE_ROW) {
String name = statement.getColumnText(0);
- if (name != databaseInfoTableName())
+ if (name != unqualifiedInfoTableName)
tableNames.append(name);
}
enableAuthorizer();
- if (result != SQLResultDone) {
+ if (result != SQLITE_DONE) {
LOG_ERROR("Error getting tables for database %s", databaseDebugName().ascii().data());
return Vector<String>();
}
@@ -262,31 +731,77 @@ Vector<String> Database::performGetTableNames()
return tableNames;
}
+void Database::incrementalVacuumIfNeeded()
+{
+ SQLiteTransactionInProgressAutoCounter transactionCounter;
+
+ int64_t freeSpaceSize = m_sqliteDatabase.freeSpaceSize();
+ int64_t totalSize = m_sqliteDatabase.totalSize();
+ if (totalSize <= 10 * freeSpaceSize) {
+ int result = m_sqliteDatabase.runIncrementalVacuumCommand();
+ if (result != SQLITE_OK)
+ logErrorMessage(formatErrorMessage("error vacuuming database", result, m_sqliteDatabase.lastErrorMsg()));
+ }
+}
+
+void Database::logErrorMessage(const String& message)
+{
+ m_scriptExecutionContext->addConsoleMessage(MessageSource::Storage, MessageLevel::Error, message);
+}
+
Vector<String> Database::tableNames()
{
// FIXME: Not using isolatedCopy on these strings looks ok since threads take strict turns
// in dealing with them. However, if the code changes, this may not be true anymore.
Vector<String> result;
DatabaseTaskSynchronizer synchronizer;
- if (!databaseContext()->databaseThread() || databaseContext()->databaseThread()->terminationRequested(&synchronizer))
+ auto& thread = databaseThread();
+ if (thread.terminationRequested(&synchronizer))
return result;
- auto task = DatabaseTableNamesTask::create(this, &synchronizer, result);
- databaseContext()->databaseThread()->scheduleImmediateTask(std::move(task));
+ auto task = std::make_unique<DatabaseTableNamesTask>(*this, synchronizer, result);
+ thread.scheduleImmediateTask(WTFMove(task));
synchronizer.waitForTaskCompletion();
return result;
}
-SecurityOrigin* Database::securityOrigin() const
+SecurityOriginData Database::securityOrigin()
{
if (m_scriptExecutionContext->isContextThread())
- return m_contextThreadSecurityOrigin.get();
- if (currentThread() == databaseContext()->databaseThread()->getThreadID())
- return m_databaseThreadSecurityOrigin.get();
- return 0;
+ return SecurityOriginData::fromSecurityOrigin(m_contextThreadSecurityOrigin.get());
+ auto& thread = databaseThread();
+ if (currentThread() == thread.getThreadID())
+ return SecurityOriginData::fromSecurityOrigin(m_databaseThreadSecurityOrigin.get());
+ RELEASE_ASSERT_NOT_REACHED();
}
-} // namespace WebCore
+unsigned long long Database::maximumSize()
+{
+ return DatabaseTracker::singleton().maximumSize(*this);
+}
+
+void Database::didCommitWriteTransaction()
+{
+ DatabaseTracker::singleton().scheduleNotifyDatabaseChanged(securityOrigin(), stringIdentifier());
+}
+
+bool Database::didExceedQuota()
+{
+ ASSERT(databaseContext().scriptExecutionContext()->isContextThread());
+ auto& tracker = DatabaseTracker::singleton();
+ auto oldQuota = tracker.quota(securityOrigin());
+ databaseContext().databaseExceededQuota(stringIdentifier(), details());
+ return tracker.quota(securityOrigin()) > oldQuota;
+}
-#endif // ENABLE(SQL_DATABASE)
+#if !LOG_DISABLED || !ERROR_DISABLED
+
+String Database::databaseDebugName() const
+{
+ return m_contextThreadSecurityOrigin->toString() + "::" + m_name;
+}
+
+#endif
+
+} // namespace WebCore
diff --git a/Source/WebCore/Modules/webdatabase/Database.h b/Source/WebCore/Modules/webdatabase/Database.h
index 068468acb..348d6ae2f 100644
--- a/Source/WebCore/Modules/webdatabase/Database.h
+++ b/Source/WebCore/Modules/webdatabase/Database.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, 2008, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2007, 2008, 2013, 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
@@ -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,80 +26,157 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef Database_h
-#define Database_h
+#pragma once
-#if ENABLE(SQL_DATABASE)
-
-#include "DatabaseBackend.h"
-#include "DatabaseBase.h"
-#include "DatabaseBasicTypes.h"
-#include "DatabaseError.h"
-#include <wtf/text/WTFString.h>
+#include "ExceptionOr.h"
+#include "SQLiteDatabase.h"
+#include <wtf/Deque.h>
+#include <wtf/Lock.h>
namespace WebCore {
-class ChangeVersionData;
class DatabaseCallback;
class DatabaseContext;
+class DatabaseDetails;
+class DatabaseThread;
+class ScriptExecutionContext;
class SecurityOrigin;
class SQLTransaction;
class SQLTransactionBackend;
class SQLTransactionCallback;
+class SQLTransactionCoordinator;
class SQLTransactionErrorCallback;
+class SQLTransactionWrapper;
class VoidCallback;
+struct SecurityOriginData;
+
+using DatabaseGUID = int;
-class Database : public DatabaseBase, public DatabaseBackend {
+class Database : public ThreadSafeRefCounted<Database> {
public:
- virtual ~Database();
+ ~Database();
+
+ ExceptionOr<void> openAndVerifyVersion(bool setVersionInNewDatabase);
+ void close();
+
+ void interrupt();
+
+ bool opened() const { return m_opened; }
+ bool isNew() const { return m_new; }
+
+ unsigned long long maximumSize();
+
+ void scheduleTransactionStep(SQLTransaction&);
+ void inProgressTransactionCompleted();
+
+ bool hasPendingTransaction();
+
+ bool hasPendingCreationEvent() const { return m_hasPendingCreationEvent; }
+ void setHasPendingCreationEvent(bool value) { m_hasPendingCreationEvent = value; }
+
+ void didCommitWriteTransaction();
+ bool didExceedQuota();
+
+ SQLTransactionCoordinator* transactionCoordinator();
// Direct support for the DOM API
- virtual String version() const;
- void changeVersion(const String& oldVersion, const String& newVersion, PassRefPtr<SQLTransactionCallback>,
- PassRefPtr<SQLTransactionErrorCallback>, PassRefPtr<VoidCallback> successCallback);
- void transaction(PassRefPtr<SQLTransactionCallback>, PassRefPtr<SQLTransactionErrorCallback>, PassRefPtr<VoidCallback> successCallback);
- void readTransaction(PassRefPtr<SQLTransactionCallback>, PassRefPtr<SQLTransactionErrorCallback>, PassRefPtr<VoidCallback> successCallback);
+ String version() const;
+ void changeVersion(const String& oldVersion, const String& newVersion, RefPtr<SQLTransactionCallback>&&, RefPtr<SQLTransactionErrorCallback>&&, RefPtr<VoidCallback>&& successCallback);
+ void transaction(RefPtr<SQLTransactionCallback>&&, RefPtr<SQLTransactionErrorCallback>&&, RefPtr<VoidCallback>&& successCallback);
+ void readTransaction(RefPtr<SQLTransactionCallback>&&, RefPtr<SQLTransactionErrorCallback>&&, RefPtr<VoidCallback>&& successCallback);
// Internal engine support
- static Database* from(DatabaseBackend*);
- DatabaseContext* databaseContext() const { return m_databaseContext.get(); }
+ String stringIdentifier() const;
+ String displayName() const;
+ unsigned estimatedSize() const;
+ String fileName() const;
+ DatabaseDetails details() const;
+ SQLiteDatabase& sqliteDatabase() { return m_sqliteDatabase; }
+
+ void disableAuthorizer();
+ void enableAuthorizer();
+ void setAuthorizerPermissions(int);
+ bool lastActionChangedDatabase();
+ bool lastActionWasInsert();
+ void resetDeletes();
+ bool hadDeletes();
+ void resetAuthorizer();
+
+ DatabaseContext& databaseContext() { return m_databaseContext; }
+ DatabaseThread& databaseThread();
+ ScriptExecutionContext& scriptExecutionContext() { return m_scriptExecutionContext; }
+ void logErrorMessage(const String& message);
Vector<String> tableNames();
- virtual SecurityOrigin* securityOrigin() const;
+ SecurityOriginData securityOrigin();
- virtual void markAsDeletedAndClose();
+ void markAsDeletedAndClose();
bool deleted() const { return m_deleted; }
- virtual void closeImmediately();
-
void scheduleTransactionCallback(SQLTransaction*);
+ void incrementalVacuumIfNeeded();
+
+ // Called from DatabaseTask
+ ExceptionOr<void> performOpenAndVerify(bool shouldSetVersionInNewDatabase);
+ Vector<String> performGetTableNames();
+
+ // Called from DatabaseTask and DatabaseThread
+ void performClose();
+
private:
- Database(PassRefPtr<DatabaseBackendContext>, const String& name,
- const String& expectedVersion, const String& displayName, unsigned long estimatedSize);
- PassRefPtr<DatabaseBackend> backend();
- static PassRefPtr<Database> create(ScriptExecutionContext*, PassRefPtr<DatabaseBackendBase>);
+ Database(DatabaseContext&, const String& name, const String& expectedVersion, const String& displayName, unsigned estimatedSize);
- void runTransaction(PassRefPtr<SQLTransactionCallback>, PassRefPtr<SQLTransactionErrorCallback>,
- PassRefPtr<VoidCallback> successCallback, bool readOnly, const ChangeVersionData* = 0);
+ void closeDatabase();
- Vector<String> performGetTableNames();
+ bool getVersionFromDatabase(String& version, bool shouldCacheVersion = true);
+ bool setVersionInDatabase(const String& version, bool shouldCacheVersion = true);
+ void setExpectedVersion(const String&);
+ const String& expectedVersion() const { return m_expectedVersion; }
+ String getCachedVersion() const;
+ void setCachedVersion(const String&);
+ bool getActualVersionForTransaction(String& version);
+
+ void scheduleTransaction();
+
+ void runTransaction(RefPtr<SQLTransactionCallback>&&, RefPtr<SQLTransactionErrorCallback>&&, RefPtr<VoidCallback>&& successCallback, RefPtr<SQLTransactionWrapper>&&, bool readOnly);
+
+#if !LOG_DISABLED || !ERROR_DISABLED
+ String databaseDebugName() const;
+#endif
+
+ Ref<ScriptExecutionContext> m_scriptExecutionContext;
+ Ref<SecurityOrigin> m_contextThreadSecurityOrigin;
+ Ref<SecurityOrigin> m_databaseThreadSecurityOrigin;
+ Ref<DatabaseContext> m_databaseContext;
- RefPtr<SecurityOrigin> m_databaseThreadSecurityOrigin;
- RefPtr<DatabaseContext> m_databaseContext;
+ bool m_deleted { false };
+ bool m_hasPendingCreationEvent { false };
- bool m_deleted;
+ String m_name;
+ String m_expectedVersion;
+ String m_displayName;
+ unsigned m_estimatedSize;
+ String m_filename;
+ DatabaseGUID m_guid;
+ bool m_opened { false };
+ bool m_new { false };
+
+ SQLiteDatabase m_sqliteDatabase;
+
+ Ref<DatabaseAuthorizer> m_databaseAuthorizer;
+
+ Deque<Ref<SQLTransaction>> m_transactionQueue;
+ Lock m_transactionInProgressMutex;
+ bool m_transactionInProgress { false };
+ bool m_isTransactionQueueEnabled { true };
+
+ friend class ChangeVersionWrapper;
friend class DatabaseManager;
- friend class DatabaseServer; // FIXME: remove this when the backend has been split out.
- friend class DatabaseBackend; // FIXME: remove this when the backend has been split out.
- friend class SQLStatement;
friend class SQLTransaction;
+ friend class SQLTransactionBackend;
};
} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
-
-#endif // Database_h
diff --git a/Source/WebCore/Modules/webdatabase/Database.idl b/Source/WebCore/Modules/webdatabase/Database.idl
index 463fa406f..5fa67f486 100644
--- a/Source/WebCore/Modules/webdatabase/Database.idl
+++ b/Source/WebCore/Modules/webdatabase/Database.idl
@@ -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,13 +27,11 @@
*/
[
- NoInterfaceObject,
- Conditional=SQL_DATABASE,
- JSNoStaticTables,
+ ImplementationLacksVTable
] interface Database {
readonly attribute DOMString version;
- void changeVersion(DOMString oldVersion, DOMString newVersion, optional SQLTransactionCallback callback, optional SQLTransactionErrorCallback errorCallback, optional VoidCallback successCallback);
- void transaction(SQLTransactionCallback callback, optional SQLTransactionErrorCallback errorCallback, optional VoidCallback successCallback);
- void readTransaction(SQLTransactionCallback callback, optional SQLTransactionErrorCallback errorCallback, optional VoidCallback successCallback);
+ void changeVersion(DOMString oldVersion, DOMString newVersion, optional SQLTransactionCallback? callback, optional SQLTransactionErrorCallback? errorCallback, optional VoidCallback? successCallback);
+ void transaction(SQLTransactionCallback callback, optional SQLTransactionErrorCallback? errorCallback, optional VoidCallback? successCallback);
+ void readTransaction(SQLTransactionCallback callback, optional SQLTransactionErrorCallback? errorCallback, optional VoidCallback? successCallback);
};
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseAuthorizer.cpp b/Source/WebCore/Modules/webdatabase/DatabaseAuthorizer.cpp
index 13932fb01..f85f9207c 100644
--- a/Source/WebCore/Modules/webdatabase/DatabaseAuthorizer.cpp
+++ b/Source/WebCore/Modules/webdatabase/DatabaseAuthorizer.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.
*
@@ -29,14 +29,13 @@
#include "config.h"
#include "DatabaseAuthorizer.h"
-#include <wtf/PassRefPtr.h>
#include <wtf/text/WTFString.h>
namespace WebCore {
-PassRefPtr<DatabaseAuthorizer> DatabaseAuthorizer::create(const String& databaseInfoTableName)
+Ref<DatabaseAuthorizer> DatabaseAuthorizer::create(const String& databaseInfoTableName)
{
- return adoptRef(new DatabaseAuthorizer(databaseInfoTableName));
+ return adoptRef(*new DatabaseAuthorizer(databaseInfoTableName));
}
DatabaseAuthorizer::DatabaseAuthorizer(const String& databaseInfoTableName)
@@ -283,29 +282,14 @@ int DatabaseAuthorizer::dropTempView(const String&)
return SQLAuthAllow;
}
-int DatabaseAuthorizer::createVTable(const String& tableName, const String& moduleName)
+int DatabaseAuthorizer::createVTable(const String&, const String&)
{
- if (!allowWrite())
- return SQLAuthDeny;
-
- // Allow only the FTS3 extension
- if (!equalIgnoringCase(moduleName, "fts3"))
- return SQLAuthDeny;
-
- m_lastActionChangedDatabase = true;
- return denyBasedOnTableName(tableName);
+ return SQLAuthDeny;
}
-int DatabaseAuthorizer::dropVTable(const String& tableName, const String& moduleName)
+int DatabaseAuthorizer::dropVTable(const String&, const String&)
{
- if (!allowWrite())
- return SQLAuthDeny;
-
- // Allow only the FTS3 extension
- if (!equalIgnoringCase(moduleName, "fts3"))
- return SQLAuthDeny;
-
- return updateDeletesBasedOnTableName(tableName);
+ return SQLAuthDeny;
}
int DatabaseAuthorizer::allowDelete(const String& tableName)
@@ -344,7 +328,7 @@ int DatabaseAuthorizer::allowRead(const String& tableName, const String&)
{
if (m_permissions & NoAccessMask && m_securityEnabled)
return SQLAuthDeny;
-
+
return denyBasedOnTableName(tableName);
}
@@ -396,11 +380,6 @@ bool DatabaseAuthorizer::allowWrite()
return !(m_securityEnabled && (m_permissions & ReadOnlyMask || m_permissions & NoAccessMask));
}
-void DatabaseAuthorizer::setReadOnly()
-{
- m_permissions |= ReadOnlyMask;
-}
-
void DatabaseAuthorizer::setPermissions(int permissions)
{
m_permissions = permissions;
@@ -412,12 +391,14 @@ int DatabaseAuthorizer::denyBasedOnTableName(const String& tableName) const
return SQLAuthAllow;
// Sadly, normal creates and drops end up affecting sqlite_master in an authorizer callback, so
- // it will be tough to enforce all of the following policies
- //if (equalIgnoringCase(tableName, "sqlite_master") || equalIgnoringCase(tableName, "sqlite_temp_master") ||
- // equalIgnoringCase(tableName, "sqlite_sequence") || equalIgnoringCase(tableName, Database::databaseInfoTableName()))
- // return SQLAuthDeny;
-
- if (equalIgnoringCase(tableName, m_databaseInfoTableName))
+ // it will be tough to enforce all of the following policies.
+ // if (equalIgnoringASCIICase(tableName, "sqlite_master")
+ // || equalIgnoringASCIICase(tableName, "sqlite_temp_master")
+ // || equalIgnoringASCIICase(tableName, "sqlite_sequence")
+ // || equalIgnoringASCIICase(tableName, Database::databaseInfoTableName()))
+ // return SQLAuthDeny;
+
+ if (equalIgnoringASCIICase(tableName, m_databaseInfoTableName))
return SQLAuthDeny;
return SQLAuthAllow;
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseAuthorizer.h b/Source/WebCore/Modules/webdatabase/DatabaseAuthorizer.h
index 5625f039d..72d2b6788 100644
--- a/Source/WebCore/Modules/webdatabase/DatabaseAuthorizer.h
+++ b/Source/WebCore/Modules/webdatabase/DatabaseAuthorizer.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 DatabaseAuthorizer_h
-#define DatabaseAuthorizer_h
+
+#pragma once
#include <wtf/Forward.h>
#include <wtf/HashSet.h>
@@ -49,7 +49,7 @@ public:
NoAccessMask = 1 << 2
};
- static PassRefPtr<DatabaseAuthorizer> create(const String& databaseInfoTableName);
+ static Ref<DatabaseAuthorizer> create(const String& databaseInfoTableName);
int createTable(const String& tableName);
int createTempTable(const String& tableName);
@@ -93,7 +93,6 @@ public:
void disable();
void enable();
- void setReadOnly();
void setPermissions(int permissions);
void reset();
@@ -118,9 +117,7 @@ private:
const String m_databaseInfoTableName;
- HashSet<String, CaseFoldingHash> m_whitelistedFunctions;
+ HashSet<String, ASCIICaseInsensitiveHash> m_whitelistedFunctions;
};
} // namespace WebCore
-
-#endif // DatabaseAuthorizer_h
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseBackend.cpp b/Source/WebCore/Modules/webdatabase/DatabaseBackend.cpp
deleted file mode 100644
index 495e86a48..000000000
--- a/Source/WebCore/Modules/webdatabase/DatabaseBackend.cpp
+++ /dev/null
@@ -1,177 +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. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED 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 "DatabaseBackend.h"
-
-#if ENABLE(SQL_DATABASE)
-
-#include "ChangeVersionData.h"
-#include "ChangeVersionWrapper.h"
-#include "DatabaseBackendContext.h"
-#include "DatabaseTask.h"
-#include "DatabaseThread.h"
-#include "DatabaseTracker.h"
-#include "Logging.h"
-#include "SQLTransaction.h"
-#include "SQLTransactionBackend.h"
-#include "SQLTransactionClient.h"
-#include "SQLTransactionCoordinator.h"
-
-namespace WebCore {
-
-DatabaseBackend::DatabaseBackend(PassRefPtr<DatabaseBackendContext> databaseContext, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize)
- : DatabaseBackendBase(databaseContext, name, expectedVersion, displayName, estimatedSize, DatabaseType::Async)
- , m_transactionInProgress(false)
- , m_isTransactionQueueEnabled(true)
-{
-}
-
-bool DatabaseBackend::openAndVerifyVersion(bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage)
-{
- DatabaseTaskSynchronizer synchronizer;
- if (!databaseContext()->databaseThread() || databaseContext()->databaseThread()->terminationRequested(&synchronizer))
- return false;
-
- bool success = false;
- auto task = DatabaseOpenTask::create(this, setVersionInNewDatabase, &synchronizer, error, errorMessage, success);
- databaseContext()->databaseThread()->scheduleImmediateTask(std::move(task));
- synchronizer.waitForTaskCompletion();
-
- return success;
-}
-
-bool DatabaseBackend::performOpenAndVerify(bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage)
-{
- if (DatabaseBackendBase::performOpenAndVerify(setVersionInNewDatabase, error, errorMessage)) {
- if (databaseContext()->databaseThread())
- databaseContext()->databaseThread()->recordDatabaseOpen(this);
-
- return true;
- }
-
- return false;
-}
-
-void DatabaseBackend::close()
-{
- ASSERT(databaseContext()->databaseThread());
- ASSERT(currentThread() == databaseContext()->databaseThread()->getThreadID());
-
- {
- MutexLocker locker(m_transactionInProgressMutex);
-
- // Clean up transactions that have not been scheduled yet:
- // Transaction phase 1 cleanup. See comment on "What happens if a
- // transaction is interrupted?" at the top of SQLTransactionBackend.cpp.
- RefPtr<SQLTransactionBackend> transaction;
- while (!m_transactionQueue.isEmpty()) {
- transaction = m_transactionQueue.takeFirst();
- transaction->notifyDatabaseThreadIsShuttingDown();
- }
-
- m_isTransactionQueueEnabled = false;
- m_transactionInProgress = false;
- }
-
- closeDatabase();
-
- // DatabaseThread keeps databases alive by referencing them in its
- // m_openDatabaseSet. DatabaseThread::recordDatabaseClose() will remove
- // this database from that set (which effectively deref's it). We hold on
- // to it with a local pointer here for a liitle longer, so that we can
- // unschedule any DatabaseTasks that refer to it before the database gets
- // deleted.
- Ref<DatabaseBackend> protect(*this);
- databaseContext()->databaseThread()->recordDatabaseClosed(this);
- databaseContext()->databaseThread()->unscheduleDatabaseTasks(this);
-}
-
-PassRefPtr<SQLTransactionBackend> DatabaseBackend::runTransaction(PassRefPtr<SQLTransaction> transaction,
- bool readOnly, const ChangeVersionData* data)
-{
- MutexLocker locker(m_transactionInProgressMutex);
- if (!m_isTransactionQueueEnabled)
- return 0;
-
- RefPtr<SQLTransactionWrapper> wrapper;
- if (data)
- wrapper = ChangeVersionWrapper::create(data->oldVersion(), data->newVersion());
-
- RefPtr<SQLTransactionBackend> transactionBackend = SQLTransactionBackend::create(this, transaction, wrapper, readOnly);
- m_transactionQueue.append(transactionBackend);
- if (!m_transactionInProgress)
- scheduleTransaction();
-
- return transactionBackend;
-}
-
-void DatabaseBackend::inProgressTransactionCompleted()
-{
- MutexLocker locker(m_transactionInProgressMutex);
- m_transactionInProgress = false;
- scheduleTransaction();
-}
-
-void DatabaseBackend::scheduleTransaction()
-{
- ASSERT(!m_transactionInProgressMutex.tryLock()); // Locked by caller.
- RefPtr<SQLTransactionBackend> transaction;
-
- if (m_isTransactionQueueEnabled && !m_transactionQueue.isEmpty())
- transaction = m_transactionQueue.takeFirst();
-
- if (transaction && databaseContext()->databaseThread()) {
- auto task = DatabaseTransactionTask::create(transaction);
- LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for transaction %p\n", task.get(), task->transaction());
- m_transactionInProgress = true;
- databaseContext()->databaseThread()->scheduleTask(std::move(task));
- } else
- m_transactionInProgress = false;
-}
-
-void DatabaseBackend::scheduleTransactionStep(SQLTransactionBackend* transaction)
-{
- if (!databaseContext()->databaseThread())
- return;
-
- auto task = DatabaseTransactionTask::create(transaction);
- LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for the transaction step\n", task.get());
- databaseContext()->databaseThread()->scheduleTask(std::move(task));
-}
-
-SQLTransactionClient* DatabaseBackend::transactionClient() const
-{
- return databaseContext()->databaseThread()->transactionClient();
-}
-
-SQLTransactionCoordinator* DatabaseBackend::transactionCoordinator() const
-{
- return databaseContext()->databaseThread()->transactionCoordinator();
-}
-
-} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseBackend.h b/Source/WebCore/Modules/webdatabase/DatabaseBackend.h
deleted file mode 100644
index 8506e731f..000000000
--- a/Source/WebCore/Modules/webdatabase/DatabaseBackend.h
+++ /dev/null
@@ -1,87 +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. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED 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.
- */
-
-#ifndef DatabaseBackend_h
-#define DatabaseBackend_h
-
-#if ENABLE(SQL_DATABASE)
-
-#include "DatabaseBackendBase.h"
-#include <wtf/Deque.h>
-#include <wtf/text/WTFString.h>
-
-namespace WebCore {
-
-class ChangeVersionData;
-class Database;
-class DatabaseServer;
-class SQLTransaction;
-class SQLTransactionBackend;
-class SQLTransactionClient;
-class SQLTransactionCoordinator;
-
-// FIXME: This implementation of DatabaseBackend is only a place holder
-// for the split out of the Database backend to be done later. This
-// place holder is needed to allow other code that need to reference the
-// DatabaseBackend to do so before the proper backend split is
-// available. This should be replaced with the actual implementation later.
-
-class DatabaseBackend : public DatabaseBackendBase {
-public:
- DatabaseBackend(PassRefPtr<DatabaseBackendContext>, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize);
-
- virtual bool openAndVerifyVersion(bool setVersionInNewDatabase, DatabaseError&, String& errorMessage);
- void close();
-
- PassRefPtr<SQLTransactionBackend> runTransaction(PassRefPtr<SQLTransaction>, bool readOnly, const ChangeVersionData*);
- void scheduleTransactionStep(SQLTransactionBackend*);
- void inProgressTransactionCompleted();
-
- SQLTransactionClient* transactionClient() const;
- SQLTransactionCoordinator* transactionCoordinator() const;
-
-private:
- class DatabaseOpenTask;
- class DatabaseCloseTask;
- class DatabaseTransactionTask;
- class DatabaseTableNamesTask;
-
- virtual bool performOpenAndVerify(bool setVersionInNewDatabase, DatabaseError&, String& errorMessage);
-
- void scheduleTransaction();
-
- Deque<RefPtr<SQLTransactionBackend>> m_transactionQueue;
- Mutex m_transactionInProgressMutex;
- bool m_transactionInProgress;
- bool m_isTransactionQueueEnabled;
-
- friend class Database;
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
-
-#endif // DatabaseBackend_h
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseBackendBase.cpp b/Source/WebCore/Modules/webdatabase/DatabaseBackendBase.cpp
deleted file mode 100644
index ea3e6112c..000000000
--- a/Source/WebCore/Modules/webdatabase/DatabaseBackendBase.cpp
+++ /dev/null
@@ -1,626 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- * 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 "DatabaseBackendBase.h"
-
-#if ENABLE(SQL_DATABASE)
-
-#include "DatabaseAuthorizer.h"
-#include "DatabaseBackendContext.h"
-#include "DatabaseBase.h"
-#include "DatabaseContext.h"
-#include "DatabaseManager.h"
-#include "DatabaseTracker.h"
-#include "ExceptionCode.h"
-#include "Logging.h"
-#include "SQLiteDatabaseTracker.h"
-#include "SQLiteStatement.h"
-#include "SQLiteTransaction.h"
-#include "SecurityOrigin.h"
-#include <wtf/HashMap.h>
-#include <wtf/HashSet.h>
-#include <wtf/NeverDestroyed.h>
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefPtr.h>
-#include <wtf/StdLibExtras.h>
-#include <wtf/text/CString.h>
-#include <wtf/text/StringHash.h>
-
-// Registering "opened" databases with the DatabaseTracker
-// =======================================================
-// The DatabaseTracker maintains a list of databases that have been
-// "opened" so that the client can call interrupt or delete on every database
-// associated with a DatabaseBackendContext.
-//
-// We will only call DatabaseTracker::addOpenDatabase() to add the database
-// to the tracker as opened when we've succeeded in opening the database,
-// and will set m_opened to true. Similarly, we only call
-// DatabaseTracker::removeOpenDatabase() to remove the database from the
-// tracker when we set m_opened to false in closeDatabase(). This sets up
-// a simple symmetry between open and close operations, and a direct
-// correlation to adding and removing databases from the tracker's list,
-// thus ensuring that we have a correct list for the interrupt and
-// delete operations to work on.
-//
-// The only databases instances not tracked by the tracker's open database
-// list are the ones that have not been added yet, or the ones that we
-// attempted an open on but failed to. Such instances only exist in the
-// DatabaseServer's factory methods for creating database backends.
-//
-// The factory methods will either call openAndVerifyVersion() or
-// performOpenAndVerify(). These methods will add the newly instantiated
-// database backend if they succeed in opening the requested database.
-// In the case of failure to open the database, the factory methods will
-// simply discard the newly instantiated database backend when they return.
-// The ref counting mechanims will automatically destruct the un-added
-// (and un-returned) databases instances.
-
-namespace WebCore {
-
-static const char versionKey[] = "WebKitDatabaseVersionKey";
-static const char unqualifiedInfoTableName[] = "__WebKitDatabaseInfoTable__";
-
-const char* DatabaseBackendBase::databaseInfoTableName()
-{
- return unqualifiedInfoTableName;
-}
-
-static const char* fullyQualifiedInfoTableName()
-{
- static const char qualifier[] = "main.";
- static char qualifiedName[sizeof(qualifier) + sizeof(unqualifiedInfoTableName) - 1];
-
- static std::once_flag onceFlag;
- std::call_once(onceFlag, []{
- strcpy(qualifiedName, qualifier);
- strcpy(qualifiedName + strlen(qualifier), unqualifiedInfoTableName);
- });
-
- return qualifiedName;
-}
-
-static String formatErrorMessage(const char* message, int sqliteErrorCode, const char* sqliteErrorMessage)
-{
- return String::format("%s (%d %s)", message, sqliteErrorCode, sqliteErrorMessage);
-}
-
-static bool retrieveTextResultFromDatabase(SQLiteDatabase& db, const String& query, String& resultString)
-{
- SQLiteStatement statement(db, query);
- int result = statement.prepare();
-
- if (result != SQLResultOk) {
- LOG_ERROR("Error (%i) preparing statement to read text result from database (%s)", result, query.ascii().data());
- return false;
- }
-
- result = statement.step();
- if (result == SQLResultRow) {
- resultString = statement.getColumnText(0);
- return true;
- }
- if (result == SQLResultDone) {
- resultString = String();
- return true;
- }
-
- LOG_ERROR("Error (%i) reading text result from database (%s)", result, query.ascii().data());
- return false;
-}
-
-static bool setTextValueInDatabase(SQLiteDatabase& db, const String& query, const String& value)
-{
- SQLiteStatement statement(db, query);
- int result = statement.prepare();
-
- if (result != SQLResultOk) {
- LOG_ERROR("Failed to prepare statement to set value in database (%s)", query.ascii().data());
- return false;
- }
-
- statement.bindText(1, value);
-
- result = statement.step();
- if (result != SQLResultDone) {
- LOG_ERROR("Failed to step statement to set value in database (%s)", query.ascii().data());
- return false;
- }
-
- return true;
-}
-
-// FIXME: move all guid-related functions to a DatabaseVersionTracker class.
-static std::mutex& guidMutex()
-{
- static std::once_flag onceFlag;
- static std::mutex* mutex;
-
- std::call_once(onceFlag, []{
- mutex = std::make_unique<std::mutex>().release();
- });
-
- return *mutex;
-}
-
-typedef HashMap<DatabaseGuid, String> GuidVersionMap;
-static GuidVersionMap& guidToVersionMap()
-{
- // Ensure the the mutex is locked.
- ASSERT(!guidMutex().try_lock());
-
- static NeverDestroyed<GuidVersionMap> map;
- return map;
-}
-
-// NOTE: Caller must lock guidMutex().
-static inline void updateGuidVersionMap(DatabaseGuid guid, String newVersion)
-{
- // Ensure the the mutex is locked.
- ASSERT(!guidMutex().try_lock());
-
- // Note: It is not safe to put an empty string into the guidToVersionMap() map.
- // That's because the map is cross-thread, but empty strings are per-thread.
- // The copy() function makes a version of the string you can use on the current
- // thread, but we need a string we can keep in a cross-thread data structure.
- // FIXME: This is a quite-awkward restriction to have to program with.
-
- // Map null string to empty string (see comment above).
- guidToVersionMap().set(guid, newVersion.isEmpty() ? String() : newVersion.isolatedCopy());
-}
-
-typedef HashMap<DatabaseGuid, std::unique_ptr<HashSet<DatabaseBackendBase*>>> GuidDatabaseMap;
-
-static GuidDatabaseMap& guidToDatabaseMap()
-{
- // Ensure the the mutex is locked.
- ASSERT(!guidMutex().try_lock());
-
- static NeverDestroyed<GuidDatabaseMap> map;
- return map;
-}
-
-static DatabaseGuid guidForOriginAndName(const String& origin, const String& name)
-{
- // Ensure the the mutex is locked.
- ASSERT(!guidMutex().try_lock());
-
- String stringID = origin + "/" + name;
-
- typedef HashMap<String, int> IDGuidMap;
- static NeverDestroyed<HashMap<String, int>> map;
- DatabaseGuid guid = map.get().get(stringID);
- if (!guid) {
- static int currentNewGUID = 1;
- guid = currentNewGUID++;
- map.get().set(stringID, guid);
- }
-
- return guid;
-}
-
-#if !LOG_DISABLED || !ERROR_DISABLED
-String DatabaseBackendBase::databaseDebugName() const
-{
- return m_contextThreadSecurityOrigin->toString() + "::" + m_name;
-}
-#endif
-
-DatabaseBackendBase::DatabaseBackendBase(PassRefPtr<DatabaseBackendContext> databaseContext, const String& name,
- const String& expectedVersion, const String& displayName, unsigned long estimatedSize, DatabaseType databaseType)
- : m_databaseContext(databaseContext)
- , m_name(name.isolatedCopy())
- , m_expectedVersion(expectedVersion.isolatedCopy())
- , m_displayName(displayName.isolatedCopy())
- , m_estimatedSize(estimatedSize)
- , m_opened(false)
- , m_new(false)
- , m_isSyncDatabase(databaseType == DatabaseType::Sync)
-{
- m_contextThreadSecurityOrigin = m_databaseContext->securityOrigin()->isolatedCopy();
-
- m_databaseAuthorizer = DatabaseAuthorizer::create(unqualifiedInfoTableName);
-
- if (m_name.isNull())
- m_name = emptyString();
-
- {
- std::lock_guard<std::mutex> locker(guidMutex());
-
- m_guid = guidForOriginAndName(securityOrigin()->toString(), name);
- std::unique_ptr<HashSet<DatabaseBackendBase*>>& hashSet = guidToDatabaseMap().add(m_guid, nullptr).iterator->value;
- if (!hashSet)
- hashSet = std::make_unique<HashSet<DatabaseBackendBase*>>();
- hashSet->add(this);
- }
-
- m_filename = DatabaseManager::manager().fullPathForDatabase(securityOrigin(), m_name);
-}
-
-DatabaseBackendBase::~DatabaseBackendBase()
-{
- // SQLite is "multi-thread safe", but each database handle can only be used
- // on a single thread at a time.
- //
- // For DatabaseBackend, we open the SQLite database on the DatabaseThread,
- // and hence we should also close it on that same thread. This means that the
- // SQLite database need to be closed by another mechanism (see
- // DatabaseContext::stopDatabases()). By the time we get here, the SQLite
- // database should have already been closed.
-
- ASSERT(!m_opened);
-}
-
-void DatabaseBackendBase::closeDatabase()
-{
- if (!m_opened)
- return;
-
- m_sqliteDatabase.close();
- m_opened = false;
- // See comment at the top this file regarding calling removeOpenDatabase().
- DatabaseTracker::tracker().removeOpenDatabase(this);
- {
- std::lock_guard<std::mutex> locker(guidMutex());
-
- auto it = guidToDatabaseMap().find(m_guid);
- ASSERT(it != guidToDatabaseMap().end());
- ASSERT(it->value);
- ASSERT(it->value->contains(this));
- it->value->remove(this);
- if (it->value->isEmpty()) {
- guidToDatabaseMap().remove(it);
- guidToVersionMap().remove(m_guid);
- }
- }
-}
-
-String DatabaseBackendBase::version() const
-{
- // Note: In multi-process browsers the cached value may be accurate, but we cannot read the
- // actual version from the database without potentially inducing a deadlock.
- // FIXME: Add an async version getter to the DatabaseAPI.
- return getCachedVersion();
-}
-
-class DoneCreatingDatabaseOnExitCaller {
-public:
- DoneCreatingDatabaseOnExitCaller(DatabaseBackendBase* database)
- : m_database(database)
- , m_openSucceeded(false)
- {
- }
- ~DoneCreatingDatabaseOnExitCaller()
- {
- DatabaseTracker::tracker().doneCreatingDatabase(m_database);
- }
-
- void setOpenSucceeded() { m_openSucceeded = true; }
-
-private:
- DatabaseBackendBase* m_database;
- bool m_openSucceeded;
-};
-
-bool DatabaseBackendBase::performOpenAndVerify(bool shouldSetVersionInNewDatabase, DatabaseError& error, String& errorMessage)
-{
- DoneCreatingDatabaseOnExitCaller onExitCaller(this);
- ASSERT(errorMessage.isEmpty());
- ASSERT(error == DatabaseError::None); // Better not have any errors already.
- error = DatabaseError::InvalidDatabaseState; // Presumed failure. We'll clear it if we succeed below.
-
- const int maxSqliteBusyWaitTime = 30000;
-
-#if PLATFORM(IOS)
- {
- // Make sure we wait till the background removal of the empty database files finished before trying to open any database.
- MutexLocker locker(DatabaseTracker::openDatabaseMutex());
- }
-#endif
-
- SQLiteTransactionInProgressAutoCounter transactionCounter;
-
- if (!m_sqliteDatabase.open(m_filename, true)) {
- errorMessage = formatErrorMessage("unable to open database", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
- return false;
- }
- if (!m_sqliteDatabase.turnOnIncrementalAutoVacuum())
- LOG_ERROR("Unable to turn on incremental auto-vacuum (%d %s)", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
-
- m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime);
-
- String currentVersion;
- {
- std::lock_guard<std::mutex> locker(guidMutex());
-
- auto entry = guidToVersionMap().find(m_guid);
- if (entry != guidToVersionMap().end()) {
- // Map null string to empty string (see updateGuidVersionMap()).
- currentVersion = entry->value.isNull() ? emptyString() : entry->value.isolatedCopy();
- LOG(StorageAPI, "Current cached version for guid %i is %s", m_guid, currentVersion.ascii().data());
- } else {
- LOG(StorageAPI, "No cached version for guid %i", m_guid);
-
- SQLiteTransaction transaction(m_sqliteDatabase);
- transaction.begin();
- if (!transaction.inProgress()) {
- errorMessage = formatErrorMessage("unable to open database, failed to start transaction", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
- m_sqliteDatabase.close();
- return false;
- }
-
- String tableName(unqualifiedInfoTableName);
- if (!m_sqliteDatabase.tableExists(tableName)) {
- m_new = true;
-
- if (!m_sqliteDatabase.executeCommand("CREATE TABLE " + tableName + " (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,value TEXT NOT NULL ON CONFLICT FAIL);")) {
- errorMessage = formatErrorMessage("unable to open database, failed to create 'info' table", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
- transaction.rollback();
- m_sqliteDatabase.close();
- return false;
- }
- } else if (!getVersionFromDatabase(currentVersion, false)) {
- errorMessage = formatErrorMessage("unable to open database, failed to read current version", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
- transaction.rollback();
- m_sqliteDatabase.close();
- return false;
- }
-
- if (currentVersion.length()) {
- LOG(StorageAPI, "Retrieved current version %s from database %s", currentVersion.ascii().data(), databaseDebugName().ascii().data());
- } else if (!m_new || shouldSetVersionInNewDatabase) {
- LOG(StorageAPI, "Setting version %s in database %s that was just created", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data());
- if (!setVersionInDatabase(m_expectedVersion, false)) {
- errorMessage = formatErrorMessage("unable to open database, failed to write current version", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg());
- transaction.rollback();
- m_sqliteDatabase.close();
- return false;
- }
- currentVersion = m_expectedVersion;
- }
- updateGuidVersionMap(m_guid, currentVersion);
- transaction.commit();
- }
- }
-
- if (currentVersion.isNull()) {
- LOG(StorageAPI, "Database %s does not have its version set", databaseDebugName().ascii().data());
- currentVersion = "";
- }
-
- // If the expected version isn't the empty string, ensure that the current database version we have matches that version. Otherwise, set an exception.
- // If the expected version is the empty string, then we always return with whatever version of the database we have.
- if ((!m_new || shouldSetVersionInNewDatabase) && m_expectedVersion.length() && m_expectedVersion != currentVersion) {
- errorMessage = "unable to open database, version mismatch, '" + m_expectedVersion + "' does not match the currentVersion of '" + currentVersion + "'";
- m_sqliteDatabase.close();
- return false;
- }
-
- ASSERT(m_databaseAuthorizer);
- m_sqliteDatabase.setAuthorizer(m_databaseAuthorizer);
-
- // See comment at the top this file regarding calling addOpenDatabase().
- DatabaseTracker::tracker().addOpenDatabase(this);
- m_opened = true;
-
- // Declare success:
- error = DatabaseError::None; // Clear the presumed error from above.
- onExitCaller.setOpenSucceeded();
-
- if (m_new && !shouldSetVersionInNewDatabase)
- m_expectedVersion = ""; // The caller provided a creationCallback which will set the expected version.
- return true;
-}
-
-SecurityOrigin* DatabaseBackendBase::securityOrigin() const
-{
- return m_contextThreadSecurityOrigin.get();
-}
-
-String DatabaseBackendBase::stringIdentifier() const
-{
- // Return a deep copy for ref counting thread safety
- return m_name.isolatedCopy();
-}
-
-String DatabaseBackendBase::displayName() const
-{
- // Return a deep copy for ref counting thread safety
- return m_displayName.isolatedCopy();
-}
-
-unsigned long DatabaseBackendBase::estimatedSize() const
-{
- return m_estimatedSize;
-}
-
-String DatabaseBackendBase::fileName() const
-{
- // Return a deep copy for ref counting thread safety
- return m_filename.isolatedCopy();
-}
-
-DatabaseDetails DatabaseBackendBase::details() const
-{
- // This code path is only used for database quota delegate calls, so file dates are irrelevant and left uninitialized.
- return DatabaseDetails(stringIdentifier(), displayName(), estimatedSize(), 0, 0, 0);
-}
-
-bool DatabaseBackendBase::getVersionFromDatabase(String& version, bool shouldCacheVersion)
-{
- String query(String("SELECT value FROM ") + fullyQualifiedInfoTableName() + " WHERE key = '" + versionKey + "';");
-
- m_databaseAuthorizer->disable();
-
- bool result = retrieveTextResultFromDatabase(m_sqliteDatabase, query, version);
- if (result) {
- if (shouldCacheVersion)
- setCachedVersion(version);
- } else
- LOG_ERROR("Failed to retrieve version from database %s", databaseDebugName().ascii().data());
-
- m_databaseAuthorizer->enable();
-
- return result;
-}
-
-bool DatabaseBackendBase::setVersionInDatabase(const String& version, bool shouldCacheVersion)
-{
- // The INSERT will replace an existing entry for the database with the new version number, due to the UNIQUE ON CONFLICT REPLACE
- // clause in the CREATE statement (see Database::performOpenAndVerify()).
- String query(String("INSERT INTO ") + fullyQualifiedInfoTableName() + " (key, value) VALUES ('" + versionKey + "', ?);");
-
- m_databaseAuthorizer->disable();
-
- bool result = setTextValueInDatabase(m_sqliteDatabase, query, version);
- if (result) {
- if (shouldCacheVersion)
- setCachedVersion(version);
- } else
- LOG_ERROR("Failed to set version %s in database (%s)", version.ascii().data(), query.ascii().data());
-
- m_databaseAuthorizer->enable();
-
- return result;
-}
-
-void DatabaseBackendBase::setExpectedVersion(const String& version)
-{
- m_expectedVersion = version.isolatedCopy();
-}
-
-String DatabaseBackendBase::getCachedVersion() const
-{
- std::lock_guard<std::mutex> locker(guidMutex());
-
- return guidToVersionMap().get(m_guid).isolatedCopy();
-}
-
-void DatabaseBackendBase::setCachedVersion(const String& actualVersion)
-{
- // Update the in memory database version map.
- std::lock_guard<std::mutex> locker(guidMutex());
-
- updateGuidVersionMap(m_guid, actualVersion);
-}
-
-bool DatabaseBackendBase::getActualVersionForTransaction(String &actualVersion)
-{
- ASSERT(m_sqliteDatabase.transactionInProgress());
- // Note: In multi-process browsers the cached value may be inaccurate.
- // So we retrieve the value from the database and update the cached value here.
- return getVersionFromDatabase(actualVersion, true);
-}
-
-void DatabaseBackendBase::disableAuthorizer()
-{
- ASSERT(m_databaseAuthorizer);
- m_databaseAuthorizer->disable();
-}
-
-void DatabaseBackendBase::enableAuthorizer()
-{
- ASSERT(m_databaseAuthorizer);
- m_databaseAuthorizer->enable();
-}
-
-void DatabaseBackendBase::setAuthorizerReadOnly()
-{
- ASSERT(m_databaseAuthorizer);
- m_databaseAuthorizer->setReadOnly();
-}
-
-void DatabaseBackendBase::setAuthorizerPermissions(int permissions)
-{
- ASSERT(m_databaseAuthorizer);
- m_databaseAuthorizer->setPermissions(permissions);
-}
-
-bool DatabaseBackendBase::lastActionChangedDatabase()
-{
- ASSERT(m_databaseAuthorizer);
- return m_databaseAuthorizer->lastActionChangedDatabase();
-}
-
-bool DatabaseBackendBase::lastActionWasInsert()
-{
- ASSERT(m_databaseAuthorizer);
- return m_databaseAuthorizer->lastActionWasInsert();
-}
-
-void DatabaseBackendBase::resetDeletes()
-{
- ASSERT(m_databaseAuthorizer);
- m_databaseAuthorizer->resetDeletes();
-}
-
-bool DatabaseBackendBase::hadDeletes()
-{
- ASSERT(m_databaseAuthorizer);
- return m_databaseAuthorizer->hadDeletes();
-}
-
-void DatabaseBackendBase::resetAuthorizer()
-{
- if (m_databaseAuthorizer)
- m_databaseAuthorizer->reset();
-}
-
-unsigned long long DatabaseBackendBase::maximumSize() const
-{
- return DatabaseTracker::tracker().getMaxSizeForDatabase(this);
-}
-
-void DatabaseBackendBase::incrementalVacuumIfNeeded()
-{
- SQLiteTransactionInProgressAutoCounter transactionCounter;
-
- int64_t freeSpaceSize = m_sqliteDatabase.freeSpaceSize();
- int64_t totalSize = m_sqliteDatabase.totalSize();
- if (totalSize <= 10 * freeSpaceSize) {
- int result = m_sqliteDatabase.runIncrementalVacuumCommand();
- if (result != SQLResultOk)
- m_frontend->logErrorMessage(formatErrorMessage("error vacuuming database", result, m_sqliteDatabase.lastErrorMsg()));
- }
-}
-
-void DatabaseBackendBase::interrupt()
-{
- m_sqliteDatabase.interrupt();
-}
-
-bool DatabaseBackendBase::isInterrupted()
-{
- MutexLocker locker(m_sqliteDatabase.databaseMutex());
- return m_sqliteDatabase.isInterrupted();
-}
-
-} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseBackendBase.h b/Source/WebCore/Modules/webdatabase/DatabaseBackendBase.h
deleted file mode 100644
index 113737cc0..000000000
--- a/Source/WebCore/Modules/webdatabase/DatabaseBackendBase.h
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- * 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 DatabaseBackendBase_h
-#define DatabaseBackendBase_h
-
-#if ENABLE(SQL_DATABASE)
-
-#include "DatabaseBasicTypes.h"
-#include "DatabaseDetails.h"
-#include "DatabaseError.h"
-#include "SQLiteDatabase.h"
-#include <wtf/Forward.h>
-#include <wtf/RefPtr.h>
-#include <wtf/ThreadSafeRefCounted.h>
-#include <wtf/text/WTFString.h>
-
-namespace WebCore {
-
-class DatabaseAuthorizer;
-class DatabaseBackendContext;
-class DatabaseBase;
-class SecurityOrigin;
-
-class DatabaseBackendBase : public ThreadSafeRefCounted<DatabaseBackendBase> {
-public:
- virtual ~DatabaseBackendBase();
-
- virtual String version() const;
-
- bool opened() const { return m_opened; }
- bool isNew() const { return m_new; }
- bool isSyncDatabase() const { return m_isSyncDatabase; }
-
- virtual SecurityOrigin* securityOrigin() const;
- virtual String stringIdentifier() const;
- virtual String displayName() const;
- virtual unsigned long estimatedSize() const;
- virtual String fileName() const;
- virtual DatabaseDetails details() const;
- SQLiteDatabase& sqliteDatabase() { return m_sqliteDatabase; }
-
- unsigned long long maximumSize() const;
- void incrementalVacuumIfNeeded();
- void interrupt();
- bool isInterrupted();
-
- void disableAuthorizer();
- void enableAuthorizer();
- void setAuthorizerReadOnly();
- void setAuthorizerPermissions(int permissions);
- bool lastActionChangedDatabase();
- bool lastActionWasInsert();
- void resetDeletes();
- bool hadDeletes();
- void resetAuthorizer();
-
- virtual void markAsDeletedAndClose() = 0;
- virtual void closeImmediately() = 0;
-
- DatabaseBackendContext* databaseContext() const { return m_databaseContext.get(); }
- void setFrontend(DatabaseBase* frontend) { m_frontend = frontend; }
-
-protected:
- friend class ChangeVersionWrapper;
- friend class SQLStatementBackend;
- friend class SQLStatementSync;
- friend class SQLTransactionBackend;
- friend class SQLTransactionBackendSync;
-
- DatabaseBackendBase(PassRefPtr<DatabaseBackendContext>, const String& name, const String& expectedVersion,
- const String& displayName, unsigned long estimatedSize, DatabaseType);
-
- void closeDatabase();
-
- virtual bool openAndVerifyVersion(bool setVersionInNewDatabase, DatabaseError&, String& errorMessage) = 0;
- virtual bool performOpenAndVerify(bool shouldSetVersionInNewDatabase, DatabaseError&, String& errorMessage);
-
- bool getVersionFromDatabase(String& version, bool shouldCacheVersion = true);
- bool setVersionInDatabase(const String& version, bool shouldCacheVersion = true);
- void setExpectedVersion(const String&);
- const String& expectedVersion() const { return m_expectedVersion; }
- String getCachedVersion()const;
- void setCachedVersion(const String&);
- bool getActualVersionForTransaction(String& version);
-
- static const char* databaseInfoTableName();
-
-#if !LOG_DISABLED || !ERROR_DISABLED
- String databaseDebugName() const;
-#endif
-
- RefPtr<SecurityOrigin> m_contextThreadSecurityOrigin;
- RefPtr<DatabaseBackendContext> m_databaseContext; // Associated with m_scriptExecutionContext.
-
- String m_name;
- String m_expectedVersion;
- String m_displayName;
- unsigned long m_estimatedSize;
- String m_filename;
-
- DatabaseBase* m_frontend;
-
-private:
- DatabaseGuid m_guid;
- bool m_opened;
- bool m_new;
- const bool m_isSyncDatabase;
-
- SQLiteDatabase m_sqliteDatabase;
-
- RefPtr<DatabaseAuthorizer> m_databaseAuthorizer;
-
- friend class DatabaseServer;
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
-
-#endif // DatabaseBackendBase_h
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseCallback.h b/Source/WebCore/Modules/webdatabase/DatabaseCallback.h
index bfa7bc627..f7dc3a1f6 100644
--- a/Source/WebCore/Modules/webdatabase/DatabaseCallback.h
+++ b/Source/WebCore/Modules/webdatabase/DatabaseCallback.h
@@ -28,27 +28,18 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DatabaseCallback_h
-#define DatabaseCallback_h
-
-#if ENABLE(SQL_DATABASE)
+#pragma once
#include <wtf/ThreadSafeRefCounted.h>
namespace WebCore {
class Database;
-class DatabaseSync;
class DatabaseCallback : public ThreadSafeRefCounted<DatabaseCallback> {
public:
virtual ~DatabaseCallback() { }
virtual bool handleEvent(Database*) = 0;
- virtual bool handleEvent(DatabaseSync*) = 0;
};
-}
-
-#endif
-
-#endif // DatabaseCallback_h
+} // namespace WebCore
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseCallback.idl b/Source/WebCore/Modules/webdatabase/DatabaseCallback.idl
index f2aa31def..9e0a5de36 100644
--- a/Source/WebCore/Modules/webdatabase/DatabaseCallback.idl
+++ b/Source/WebCore/Modules/webdatabase/DatabaseCallback.idl
@@ -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,9 +26,4 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-[
- Conditional=SQL_DATABASE,
-] callback interface DatabaseCallback {
- boolean handleEvent(Database database);
- boolean handleEvent(DatabaseSync database);
-};
+callback DatabaseCallback = void (Database database);
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseContext.cpp b/Source/WebCore/Modules/webdatabase/DatabaseContext.cpp
index 752e29e48..3dcb56034 100644
--- a/Source/WebCore/Modules/webdatabase/DatabaseContext.cpp
+++ b/Source/WebCore/Modules/webdatabase/DatabaseContext.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
@@ -28,12 +28,9 @@
#include "config.h"
#include "DatabaseContext.h"
-#if ENABLE(SQL_DATABASE)
-
#include "Chrome.h"
#include "ChromeClient.h"
#include "Database.h"
-#include "DatabaseBackendContext.h"
#include "DatabaseManager.h"
#include "DatabaseTask.h"
#include "DatabaseThread.h"
@@ -42,7 +39,7 @@
#include "SchemeRegistry.h"
#include "ScriptExecutionContext.h"
#include "SecurityOrigin.h"
-#include "Settings.h"
+#include "SecurityOriginData.h"
namespace WebCore {
@@ -97,54 +94,32 @@ namespace WebCore {
// DatabaseContext will outlive both regardless of which of the 2 destructs first.
-DatabaseContext::DatabaseContext(ScriptExecutionContext* context)
- : ActiveDOMObject(context)
- , m_hasOpenDatabases(false)
- , m_isRegistered(true) // will register on construction below.
- , m_hasRequestedTermination(false)
-#if PLATFORM(IOS)
- , m_paused(false)
-#endif //PLATFORM(IOS)
+DatabaseContext::DatabaseContext(ScriptExecutionContext& context)
+ : ActiveDOMObject(&context)
{
// ActiveDOMObject expects this to be called to set internal flags.
suspendIfNeeded();
- context->setDatabaseContext(this);
-
- // For debug accounting only. We must do this before we register the
- // instance. The assertions assume this.
- DatabaseManager::manager().didConstructDatabaseContext();
-
- DatabaseManager::manager().registerDatabaseContext(this);
+ ASSERT(!context.databaseContext());
+ context.setDatabaseContext(this);
}
DatabaseContext::~DatabaseContext()
{
stopDatabases();
ASSERT(!m_databaseThread || m_databaseThread->terminationRequested());
-
- // For debug accounting only. We must call this last. The assertions assume
- // this.
- DatabaseManager::manager().didDestructDatabaseContext();
+ ASSERT(!scriptExecutionContext() || !scriptExecutionContext()->databaseContext());
}
-// This is called if the associated ScriptExecutionContext is destructing while
+// This is called if the associated ScriptExecutionContext is destroyed while
// we're still associated with it. That's our cue to disassociate and shutdown.
-// To do this, we stop the database and let everything shutdown naturally
-// because the database closing process may still make use of this context.
+// To do this, we stop the database and let everything shut down naturally
+// because the database closing process might still make use of this context.
// It is not safe to just delete the context here.
void DatabaseContext::contextDestroyed()
{
+ ActiveDOMObject::contextDestroyed();
stopDatabases();
-
- // Normally, willDestroyActiveDOMObject() is called in ~ActiveDOMObject().
- // However, we're here because the destructor hasn't been called, and the
- // ScriptExecutionContext we're associated with is about to be destructed.
- // So, go ahead an unregister self from the ActiveDOMObject list, and
- // set m_scriptExecutionContext to 0 so that ~ActiveDOMObject() doesn't
- // try to do so again.
- m_scriptExecutionContext->willDestroyActiveDOMObject(this);
- m_scriptExecutionContext = 0;
}
// stop() is from stopActiveDOMObjects() which indicates that the owner Frame
@@ -155,18 +130,17 @@ void DatabaseContext::stop()
stopDatabases();
}
-PassRefPtr<DatabaseBackendContext> DatabaseContext::backend()
+bool DatabaseContext::canSuspendForDocumentSuspension() const
{
- DatabaseBackendContext* backend = static_cast<DatabaseBackendContext*>(this);
- return backend;
+ if (!hasOpenDatabases() || !m_databaseThread)
+ return true;
+
+ return !m_databaseThread->hasPendingDatabaseActivity();
}
DatabaseThread* DatabaseContext::databaseThread()
{
if (!m_databaseThread && !m_hasOpenDatabases) {
-#if PLATFORM(IOS)
- MutexLocker lock(m_databaseThreadMutex);
-#endif //PLATFORM(IOS)
// It's OK to ask for the m_databaseThread after we've requested
// termination because we're still using it to execute the closing
// of the database. However, it is NOT OK to create a new thread
@@ -177,33 +151,16 @@ DatabaseThread* DatabaseContext::databaseThread()
// because in that case we already had a database thread and terminated it and should not create another.
m_databaseThread = DatabaseThread::create();
if (!m_databaseThread->start())
- m_databaseThread = 0;
-#if PLATFORM(IOS)
- if (m_databaseThread)
- m_databaseThread->setPaused(m_paused);
-#endif //PLATFORM(IOS)
+ m_databaseThread = nullptr;
}
return m_databaseThread.get();
}
-#if PLATFORM(IOS)
-void DatabaseContext::setPaused(bool paused)
-{
- MutexLocker lock(m_databaseThreadMutex);
-
- m_paused = paused;
- if (m_databaseThread)
- m_databaseThread->setPaused(m_paused);
-}
-#endif // PLATFORM(IOS)
-
-bool DatabaseContext::stopDatabases(DatabaseTaskSynchronizer* cleanupSync)
+bool DatabaseContext::stopDatabases(DatabaseTaskSynchronizer* synchronizer)
{
- if (m_isRegistered) {
- DatabaseManager::manager().unregisterDatabaseContext(this);
- m_isRegistered = false;
- }
+ // FIXME: What guarantees this is never called after the script execution context is null?
+ ASSERT(scriptExecutionContext());
// Though we initiate termination of the DatabaseThread here in
// stopDatabases(), we can't clear the m_databaseThread ref till we get to
@@ -214,20 +171,26 @@ bool DatabaseContext::stopDatabases(DatabaseTaskSynchronizer* cleanupSync)
// why our ref count is 0 then and we're destructing). Then, the
// m_databaseThread RefPtr destructor will deref and delete the
// DatabaseThread.
-
- if (m_databaseThread && !m_hasRequestedTermination) {
- m_databaseThread->requestTermination(cleanupSync);
+ bool result = m_databaseThread && !m_hasRequestedTermination;
+ if (result) {
+ m_databaseThread->requestTermination(synchronizer);
m_hasRequestedTermination = true;
- return true;
}
- return false;
+
+ auto& context = *scriptExecutionContext();
+ if (context.databaseContext()) {
+ ASSERT(context.databaseContext() == this);
+ context.setDatabaseContext(nullptr);
+ }
+
+ return result;
}
bool DatabaseContext::allowDatabaseAccess() const
{
- if (m_scriptExecutionContext->isDocument()) {
- Document* document = toDocument(m_scriptExecutionContext);
- if (!document->page() || (document->page()->settings().privateBrowsingEnabled() && !SchemeRegistry::allowsDatabaseAccessInPrivateBrowsing(document->securityOrigin()->protocol())))
+ if (is<Document>(*m_scriptExecutionContext)) {
+ Document& document = downcast<Document>(*m_scriptExecutionContext);
+ if (!document.page() || (document.page()->usesEphemeralSession() && !SchemeRegistry::allowsDatabaseAccessInPrivateBrowsing(document.securityOrigin().protocol())))
return false;
return true;
}
@@ -238,18 +201,23 @@ bool DatabaseContext::allowDatabaseAccess() const
void DatabaseContext::databaseExceededQuota(const String& name, DatabaseDetails details)
{
- if (m_scriptExecutionContext->isDocument()) {
- Document* document = toDocument(m_scriptExecutionContext);
- if (Page* page = document->page())
- page->chrome().client().exceededDatabaseQuota(document->frame(), name, details);
+ if (is<Document>(*m_scriptExecutionContext)) {
+ Document& document = downcast<Document>(*m_scriptExecutionContext);
+ if (Page* page = document.page())
+ page->chrome().client().exceededDatabaseQuota(*document.frame(), name, details);
return;
}
ASSERT(m_scriptExecutionContext->isWorkerGlobalScope());
- // FIXME: This needs a real implementation; this is a temporary solution for testing.
- const unsigned long long defaultQuota = 5 * 1024 * 1024;
- DatabaseManager::manager().setQuota(m_scriptExecutionContext->securityOrigin(), defaultQuota);
}
-} // namespace WebCore
+SecurityOriginData DatabaseContext::securityOrigin() const
+{
+ return SecurityOriginData::fromSecurityOrigin(*m_scriptExecutionContext->securityOrigin());
+}
+
+bool DatabaseContext::isContextThread() const
+{
+ return m_scriptExecutionContext->isContextThread();
+}
-#endif // ENABLE(SQL_DATABASE)
+} // namespace WebCore
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseContext.h b/Source/WebCore/Modules/webdatabase/DatabaseContext.h
index 48510d3d0..4c3c46363 100644
--- a/Source/WebCore/Modules/webdatabase/DatabaseContext.h
+++ b/Source/WebCore/Modules/webdatabase/DatabaseContext.h
@@ -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
@@ -25,72 +25,61 @@
*
*/
-#ifndef DatabaseContext_h
-#define DatabaseContext_h
-
-#if ENABLE(SQL_DATABASE)
+#pragma once
#include "ActiveDOMObject.h"
-#include "DatabaseDetails.h"
-#include <wtf/Assertions.h>
+#include <wtf/RefPtr.h>
#include <wtf/ThreadSafeRefCounted.h>
#if PLATFORM(IOS)
#include <wtf/Threading.h>
-#endif // PLATFORM(IOS)
+#endif
namespace WebCore {
class Database;
-class DatabaseBackendContext;
+class DatabaseDetails;
class DatabaseTaskSynchronizer;
class DatabaseThread;
-class ScriptExecutionContext;
+class SecurityOrigin;
+struct SecurityOriginData;
-class DatabaseContext : public ThreadSafeRefCounted<DatabaseContext>, ActiveDOMObject {
+class DatabaseContext final : public ThreadSafeRefCounted<DatabaseContext>, private ActiveDOMObject {
public:
virtual ~DatabaseContext();
- // For life-cycle management (inherited from ActiveDOMObject):
- virtual void contextDestroyed();
- virtual void stop();
-
- PassRefPtr<DatabaseBackendContext> backend();
+ DatabaseThread* existingDatabaseThread() const { return m_databaseThread.get(); }
DatabaseThread* databaseThread();
-#if PLATFORM(IOS)
- void setPaused(bool);
-#endif // PLATFORM(IOS)
void setHasOpenDatabases() { m_hasOpenDatabases = true; }
- bool hasOpenDatabases() { return m_hasOpenDatabases; }
+ bool hasOpenDatabases() const { return m_hasOpenDatabases; }
- // When the database cleanup is done, cleanupSync will be signalled.
+ // When the database cleanup is done, the sychronizer will be signalled.
bool stopDatabases(DatabaseTaskSynchronizer*);
bool allowDatabaseAccess() const;
void databaseExceededQuota(const String& name, DatabaseDetails);
+ using ActiveDOMObject::scriptExecutionContext;
+ SecurityOriginData securityOrigin() const;
+
+ bool isContextThread() const;
+
private:
- explicit DatabaseContext(ScriptExecutionContext*);
+ explicit DatabaseContext(ScriptExecutionContext&);
- void stopDatabases() { stopDatabases(0); }
+ void stopDatabases() { stopDatabases(nullptr); }
+
+ void contextDestroyed() override;
+ void stop() override;
+ bool canSuspendForDocumentSuspension() const override;
+ const char* activeDOMObjectName() const override { return "DatabaseContext"; }
RefPtr<DatabaseThread> m_databaseThread;
- bool m_hasOpenDatabases; // This never changes back to false, even after the database thread is closed.
- bool m_isRegistered;
- bool m_hasRequestedTermination;
+ bool m_hasOpenDatabases { false }; // This never changes back to false, even after the database thread is closed.
+ bool m_hasRequestedTermination { false };
- friend class DatabaseBackendContext;
friend class DatabaseManager;
-
-#if PLATFORM(IOS)
- Mutex m_databaseThreadMutex;
- bool m_paused;
-#endif // PLATFORM(IOS)
};
} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
-
-#endif // DatabaseContext_h
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseDetails.h b/Source/WebCore/Modules/webdatabase/DatabaseDetails.h
index 61cd7286d..239803884 100644
--- a/Source/WebCore/Modules/webdatabase/DatabaseDetails.h
+++ b/Source/WebCore/Modules/webdatabase/DatabaseDetails.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,10 +26,7 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DatabaseDetails_h
-#define DatabaseDetails_h
-
-#if ENABLE(SQL_DATABASE)
+#pragma once
#include <thread>
#include <wtf/text/WTFString.h>
@@ -85,7 +82,3 @@ private:
};
} // namespace WebCore
-
-#endif
-
-#endif // DatabaseDetails_h
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseManager.cpp b/Source/WebCore/Modules/webdatabase/DatabaseManager.cpp
index 0b60c96aa..deb8788ef 100644
--- a/Source/WebCore/Modules/webdatabase/DatabaseManager.cpp
+++ b/Source/WebCore/Modules/webdatabase/DatabaseManager.cpp
@@ -20,91 +20,71 @@
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "DatabaseManager.h"
-#if ENABLE(SQL_DATABASE)
-
-#include "AbstractDatabaseServer.h"
#include "Database.h"
-#include "DatabaseBackend.h"
-#include "DatabaseBackendBase.h"
-#include "DatabaseBackendContext.h"
-#include "DatabaseBackendSync.h"
#include "DatabaseCallback.h"
#include "DatabaseContext.h"
-#include "DatabaseStrategy.h"
-#include "DatabaseSync.h"
#include "DatabaseTask.h"
+#include "DatabaseTracker.h"
#include "ExceptionCode.h"
-#include "InspectorDatabaseInstrumentation.h"
+#include "InspectorInstrumentation.h"
#include "Logging.h"
#include "PlatformStrategies.h"
#include "ScriptController.h"
#include "ScriptExecutionContext.h"
#include "SecurityOrigin.h"
+#include "SecurityOriginData.h"
+#include <wtf/NeverDestroyed.h>
namespace WebCore {
-DatabaseManager::ProposedDatabase::ProposedDatabase(DatabaseManager& manager,
- SecurityOrigin* origin, const String& name, const String& displayName, unsigned long estimatedSize)
+class DatabaseManager::ProposedDatabase {
+public:
+ ProposedDatabase(DatabaseManager&, SecurityOrigin&, const String& name, const String& displayName, unsigned long estimatedSize);
+ ~ProposedDatabase();
+
+ SecurityOrigin& origin() { return m_origin; }
+ DatabaseDetails& details() { return m_details; }
+
+private:
+ DatabaseManager& m_manager;
+ Ref<SecurityOrigin> m_origin;
+ DatabaseDetails m_details;
+};
+
+DatabaseManager::ProposedDatabase::ProposedDatabase(DatabaseManager& manager, SecurityOrigin& origin, const String& name, const String& displayName, unsigned long estimatedSize)
: m_manager(manager)
- , m_origin(origin->isolatedCopy())
+ , m_origin(origin.isolatedCopy())
, m_details(name.isolatedCopy(), displayName.isolatedCopy(), estimatedSize, 0, 0, 0)
{
- m_manager.addProposedDatabase(this);
+ m_manager.addProposedDatabase(*this);
}
-DatabaseManager::ProposedDatabase::~ProposedDatabase()
+inline DatabaseManager::ProposedDatabase::~ProposedDatabase()
{
- m_manager.removeProposedDatabase(this);
+ m_manager.removeProposedDatabase(*this);
}
-DatabaseManager& DatabaseManager::manager()
+DatabaseManager& DatabaseManager::singleton()
{
- static DatabaseManager* dbManager = 0;
- // FIXME: The following is vulnerable to a race between threads. Need to
- // implement a thread safe on-first-use static initializer.
- if (!dbManager)
- dbManager = new DatabaseManager();
-
- return *dbManager;
-}
-
-DatabaseManager::DatabaseManager()
- : m_server(platformStrategies()->databaseStrategy()->getDatabaseServer())
- , m_client(0)
- , m_databaseIsAvailable(true)
-#if !ASSERT_DISABLED
- , m_databaseContextRegisteredCount(0)
- , m_databaseContextInstanceCount(0)
-#endif
-{
- ASSERT(m_server); // We should always have a server to work with.
+ static NeverDestroyed<DatabaseManager> instance;
+ return instance;
}
void DatabaseManager::initialize(const String& databasePath)
{
- m_server->initialize(databasePath);
+ DatabaseTracker::initializeTracker(databasePath);
}
void DatabaseManager::setClient(DatabaseManagerClient* client)
{
m_client = client;
- m_server->setClient(client);
-}
-
-String DatabaseManager::databaseDirectoryPath() const
-{
- return m_server->databaseDirectoryPath();
-}
-
-void DatabaseManager::setDatabaseDirectoryPath(const String& path)
-{
- m_server->setDatabaseDirectoryPath(path);
+ DatabaseTracker::singleton().setClient(client);
}
bool DatabaseManager::isAvailable()
@@ -117,359 +97,179 @@ void DatabaseManager::setIsAvailable(bool available)
m_databaseIsAvailable = available;
}
-class DatabaseCreationCallbackTask : public ScriptExecutionContext::Task {
-public:
- static PassOwnPtr<DatabaseCreationCallbackTask> create(PassRefPtr<Database> database, PassRefPtr<DatabaseCallback> creationCallback)
- {
- return adoptPtr(new DatabaseCreationCallbackTask(database, creationCallback));
- }
-
- virtual void performTask(ScriptExecutionContext*)
- {
- m_creationCallback->handleEvent(m_database.get());
- }
-
-private:
- DatabaseCreationCallbackTask(PassRefPtr<Database> database, PassRefPtr<DatabaseCallback> callback)
- : m_database(database)
- , m_creationCallback(callback)
- {
- }
-
- RefPtr<Database> m_database;
- RefPtr<DatabaseCallback> m_creationCallback;
-};
-
-PassRefPtr<DatabaseContext> DatabaseManager::existingDatabaseContextFor(ScriptExecutionContext* context)
+Ref<DatabaseContext> DatabaseManager::databaseContext(ScriptExecutionContext& context)
{
- std::lock_guard<std::mutex> lock(m_mutex);
-
- ASSERT(m_databaseContextRegisteredCount >= 0);
- ASSERT(m_databaseContextInstanceCount >= 0);
- ASSERT(m_databaseContextRegisteredCount <= m_databaseContextInstanceCount);
-
- RefPtr<DatabaseContext> databaseContext = adoptRef(m_contextMap.get(context));
- if (databaseContext) {
- // If we're instantiating a new DatabaseContext, the new instance would
- // carry a new refCount of 1. The client expects this and will simply
- // adoptRef the databaseContext without ref'ing it.
- // However, instead of instantiating a new instance, we're reusing
- // an existing one that corresponds to the specified ScriptExecutionContext.
- // Hence, that new refCount need to be attributed to the reused instance
- // to ensure that the refCount is accurate when the client adopts the ref.
- // We do this by ref'ing the reused databaseContext before returning it.
- databaseContext->ref();
- }
- return databaseContext.release();
+ if (auto databaseContext = context.databaseContext())
+ return *databaseContext;
+ return adoptRef(*new DatabaseContext(context));
}
-PassRefPtr<DatabaseContext> DatabaseManager::databaseContextFor(ScriptExecutionContext* context)
-{
- RefPtr<DatabaseContext> databaseContext = existingDatabaseContextFor(context);
- if (!databaseContext)
- databaseContext = adoptRef(new DatabaseContext(context));
- return databaseContext.release();
-}
+#if LOG_DISABLED
-void DatabaseManager::registerDatabaseContext(DatabaseContext* databaseContext)
+static inline void logOpenDatabaseError(ScriptExecutionContext&, const String&)
{
- std::lock_guard<std::mutex> lock(m_mutex);
-
- ScriptExecutionContext* context = databaseContext->scriptExecutionContext();
- m_contextMap.set(context, databaseContext);
-#if !ASSERT_DISABLED
- m_databaseContextRegisteredCount++;
-#endif
}
-void DatabaseManager::unregisterDatabaseContext(DatabaseContext* databaseContext)
-{
- std::lock_guard<std::mutex> lock(m_mutex);
+#else
- ScriptExecutionContext* context = databaseContext->scriptExecutionContext();
- ASSERT(m_contextMap.get(context));
-#if !ASSERT_DISABLED
- m_databaseContextRegisteredCount--;
-#endif
- m_contextMap.remove(context);
-}
-
-#if !ASSERT_DISABLED
-void DatabaseManager::didConstructDatabaseContext()
+static void logOpenDatabaseError(ScriptExecutionContext& context, const String& name)
{
- std::lock_guard<std::mutex> lock(m_mutex);
-
- m_databaseContextInstanceCount++;
+ LOG(StorageAPI, "Database %s for origin %s not allowed to be established", name.utf8().data(), context.securityOrigin()->toString().utf8().data());
}
-void DatabaseManager::didDestructDatabaseContext()
-{
- std::lock_guard<std::mutex> lock(m_mutex);
-
- m_databaseContextInstanceCount--;
- ASSERT(m_databaseContextRegisteredCount <= m_databaseContextInstanceCount);
-}
#endif
-ExceptionCode DatabaseManager::exceptionCodeForDatabaseError(DatabaseError error)
+ExceptionOr<Ref<Database>> DatabaseManager::openDatabaseBackend(ScriptExecutionContext& context, const String& name, const String& expectedVersion, const String& displayName, unsigned estimatedSize, bool setVersionInNewDatabase)
{
- switch (error) {
- case DatabaseError::None:
- return 0;
- case DatabaseError::DatabaseIsBeingDeleted:
- case DatabaseError::DatabaseSizeExceededQuota:
- case DatabaseError::DatabaseSizeOverflowed:
- case DatabaseError::GenericSecurityError:
- return SECURITY_ERR;
- case DatabaseError::InvalidDatabaseState:
- return INVALID_STATE_ERR;
- }
- ASSERT_NOT_REACHED();
- return 0; // Make some older compilers happy.
-}
-
-static void logOpenDatabaseError(ScriptExecutionContext* context, const String& name)
-{
- UNUSED_PARAM(context);
- UNUSED_PARAM(name);
- LOG(StorageAPI, "Database %s for origin %s not allowed to be established", name.ascii().data(),
- context->securityOrigin()->toString().ascii().data());
-}
-
-PassRefPtr<DatabaseBackendBase> DatabaseManager::openDatabaseBackend(ScriptExecutionContext* context,
- DatabaseType type, const String& name, const String& expectedVersion, const String& displayName,
- unsigned long estimatedSize, bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage)
-{
- ASSERT(error == DatabaseError::None);
+ auto backend = tryToOpenDatabaseBackend(context, name, expectedVersion, displayName, estimatedSize, setVersionInNewDatabase, FirstTryToOpenDatabase);
- RefPtr<DatabaseContext> databaseContext = databaseContextFor(context);
- RefPtr<DatabaseBackendContext> backendContext = databaseContext->backend();
-
- RefPtr<DatabaseBackendBase> backend = m_server->openDatabase(backendContext, type, name, expectedVersion,
- displayName, estimatedSize, setVersionInNewDatabase, error, errorMessage);
-
- if (!backend) {
- ASSERT(error != DatabaseError::None);
-
- switch (error) {
- case DatabaseError::DatabaseIsBeingDeleted:
- case DatabaseError::DatabaseSizeOverflowed:
- case DatabaseError::GenericSecurityError:
- logOpenDatabaseError(context, name);
- return 0;
-
- case DatabaseError::InvalidDatabaseState:
- logErrorMessage(context, errorMessage);
- return 0;
-
- case DatabaseError::DatabaseSizeExceededQuota:
+ if (backend.hasException()) {
+ if (backend.exception().code() == QUOTA_EXCEEDED_ERR) {
// Notify the client that we've exceeded the database quota.
// The client may want to increase the quota, and we'll give it
// one more try after if that is the case.
{
- ProposedDatabase proposedDb(*this, context->securityOrigin(), name, displayName, estimatedSize);
- databaseContext->databaseExceededQuota(name, proposedDb.details());
+ // FIXME: What guarantees context.securityOrigin() is non-null?
+ ProposedDatabase proposedDatabase { *this, *context.securityOrigin(), name, displayName, estimatedSize };
+ this->databaseContext(context)->databaseExceededQuota(name, proposedDatabase.details());
}
- error = DatabaseError::None;
-
- backend = m_server->openDatabase(backendContext, type, name, expectedVersion,
- displayName, estimatedSize, setVersionInNewDatabase, error, errorMessage,
- AbstractDatabaseServer::RetryOpenDatabase);
- break;
-
- default:
- ASSERT_NOT_REACHED();
+ backend = tryToOpenDatabaseBackend(context, name, expectedVersion, displayName, estimatedSize, setVersionInNewDatabase, RetryOpenDatabase);
}
+ }
- if (!backend) {
- ASSERT(error != DatabaseError::None);
-
- if (error == DatabaseError::InvalidDatabaseState) {
- logErrorMessage(context, errorMessage);
- return 0;
- }
-
+ if (backend.hasException()) {
+ if (backend.exception().code() == INVALID_STATE_ERR)
+ logErrorMessage(context, backend.exception().message());
+ else
logOpenDatabaseError(context, name);
- return 0;
- }
}
- return backend.release();
+ return backend;
}
-void DatabaseManager::addProposedDatabase(ProposedDatabase* proposedDb)
+ExceptionOr<Ref<Database>> DatabaseManager::tryToOpenDatabaseBackend(ScriptExecutionContext& scriptContext, const String& name, const String& expectedVersion, const String& displayName, unsigned estimatedSize, bool setVersionInNewDatabase,
+ OpenAttempt attempt)
{
- std::lock_guard<std::mutex> lock(m_mutex);
+ if (is<Document>(&scriptContext)) {
+ auto* page = downcast<Document>(scriptContext).page();
+ if (!page || page->usesEphemeralSession())
+ return Exception { SECURITY_ERR };
+ }
- m_proposedDatabases.add(proposedDb);
+ if (scriptContext.isWorkerGlobalScope()) {
+ ASSERT_NOT_REACHED();
+ return Exception { SECURITY_ERR };
+ }
+
+ auto backendContext = this->databaseContext(scriptContext);
+
+ ExceptionOr<void> preflightResult;
+ switch (attempt) {
+ case FirstTryToOpenDatabase:
+ preflightResult = DatabaseTracker::singleton().canEstablishDatabase(backendContext, name, estimatedSize);
+ break;
+ case RetryOpenDatabase:
+ preflightResult = DatabaseTracker::singleton().retryCanEstablishDatabase(backendContext, name, estimatedSize);
+ break;
+ }
+ if (preflightResult.hasException())
+ return preflightResult.releaseException();
+
+ auto database = adoptRef(*new Database(backendContext, name, expectedVersion, displayName, estimatedSize));
+
+ auto openResult = database->openAndVerifyVersion(setVersionInNewDatabase);
+ if (openResult.hasException())
+ return openResult.releaseException();
+
+ // FIXME: What guarantees backendContext.securityOrigin() is non-null?
+ DatabaseTracker::singleton().setDatabaseDetails(backendContext->securityOrigin(), name, displayName, estimatedSize);
+ return WTFMove(database);
}
-void DatabaseManager::removeProposedDatabase(ProposedDatabase* proposedDb)
+void DatabaseManager::addProposedDatabase(ProposedDatabase& database)
{
- std::lock_guard<std::mutex> lock(m_mutex);
+ std::lock_guard<Lock> lock { m_proposedDatabasesMutex };
+ m_proposedDatabases.add(&database);
+}
- m_proposedDatabases.remove(proposedDb);
+void DatabaseManager::removeProposedDatabase(ProposedDatabase& database)
+{
+ std::lock_guard<Lock> lock { m_proposedDatabasesMutex };
+ m_proposedDatabases.remove(&database);
}
-PassRefPtr<Database> DatabaseManager::openDatabase(ScriptExecutionContext* context,
- const String& name, const String& expectedVersion, const String& displayName,
- unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback,
- DatabaseError& error)
+ExceptionOr<Ref<Database>> DatabaseManager::openDatabase(ScriptExecutionContext& context, const String& name, const String& expectedVersion, const String& displayName, unsigned estimatedSize, RefPtr<DatabaseCallback>&& creationCallback)
{
ScriptController::initializeThreading();
- ASSERT(error == DatabaseError::None);
bool setVersionInNewDatabase = !creationCallback;
- String errorMessage;
- RefPtr<DatabaseBackendBase> backend = openDatabaseBackend(context, DatabaseType::Async, name,
- expectedVersion, displayName, estimatedSize, setVersionInNewDatabase, error, errorMessage);
- if (!backend)
- return 0;
+ auto openResult = openDatabaseBackend(context, name, expectedVersion, displayName, estimatedSize, setVersionInNewDatabase);
+ if (openResult.hasException())
+ return openResult.releaseException();
- RefPtr<Database> database = Database::create(context, backend);
+ RefPtr<Database> database = openResult.releaseReturnValue();
- RefPtr<DatabaseContext> databaseContext = databaseContextFor(context);
+ auto databaseContext = this->databaseContext(context);
databaseContext->setHasOpenDatabases();
- InspectorInstrumentation::didOpenDatabase(context, database, context->securityOrigin()->host(), name, expectedVersion);
+ InspectorInstrumentation::didOpenDatabase(&context, database.copyRef(), context.securityOrigin()->host(), name, expectedVersion);
- if (backend->isNew() && creationCallback.get()) {
+ if (database->isNew() && creationCallback.get()) {
LOG(StorageAPI, "Scheduling DatabaseCreationCallbackTask for database %p\n", database.get());
- database->m_scriptExecutionContext->postTask(DatabaseCreationCallbackTask::create(database, creationCallback));
- }
-
- ASSERT(database);
- return database.release();
-}
-
-PassRefPtr<DatabaseSync> DatabaseManager::openDatabaseSync(ScriptExecutionContext* context,
- const String& name, const String& expectedVersion, const String& displayName,
- unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, DatabaseError& error)
-{
- ASSERT(context->isContextThread());
- ASSERT(error == DatabaseError::None);
-
- bool setVersionInNewDatabase = !creationCallback;
- String errorMessage;
- RefPtr<DatabaseBackendBase> backend = openDatabaseBackend(context, DatabaseType::Sync, name,
- expectedVersion, displayName, estimatedSize, setVersionInNewDatabase, error, errorMessage);
- if (!backend)
- return 0;
-
- RefPtr<DatabaseSync> database = DatabaseSync::create(context, backend);
-
- if (backend->isNew() && creationCallback.get()) {
- LOG(StorageAPI, "Invoking the creation callback for database %p\n", database.get());
- creationCallback->handleEvent(database.get());
+ database->setHasPendingCreationEvent(true);
+ database->m_scriptExecutionContext->postTask([creationCallback, database] (ScriptExecutionContext&) {
+ creationCallback->handleEvent(database.get());
+ database->setHasPendingCreationEvent(false);
+ });
}
- ASSERT(database);
- return database.release();
+ return database.releaseNonNull();
}
-bool DatabaseManager::hasOpenDatabases(ScriptExecutionContext* context)
+bool DatabaseManager::hasOpenDatabases(ScriptExecutionContext& context)
{
- RefPtr<DatabaseContext> databaseContext = existingDatabaseContextFor(context);
- if (!databaseContext)
- return false;
- return databaseContext->hasOpenDatabases();
+ auto databaseContext = context.databaseContext();
+ return databaseContext && databaseContext->hasOpenDatabases();
}
-void DatabaseManager::stopDatabases(ScriptExecutionContext* context, DatabaseTaskSynchronizer* synchronizer)
+void DatabaseManager::stopDatabases(ScriptExecutionContext& context, DatabaseTaskSynchronizer* synchronizer)
{
- RefPtr<DatabaseContext> databaseContext = existingDatabaseContextFor(context);
- if (!databaseContext || !databaseContext->stopDatabases(synchronizer))
+ auto databaseContext = context.databaseContext();
+ if (!databaseContext || !databaseContext->stopDatabases(synchronizer)) {
if (synchronizer)
synchronizer->taskCompleted();
+ }
}
-String DatabaseManager::fullPathForDatabase(SecurityOrigin* origin, const String& name, bool createIfDoesNotExist)
+String DatabaseManager::fullPathForDatabase(SecurityOrigin& origin, const String& name, bool createIfDoesNotExist)
{
{
- std::lock_guard<std::mutex> lock(m_mutex);
-
+ std::lock_guard<Lock> lock { m_proposedDatabasesMutex };
for (auto* proposedDatabase : m_proposedDatabases) {
- if (proposedDatabase->details().name() == name && proposedDatabase->origin()->equal(origin))
+ if (proposedDatabase->details().name() == name && proposedDatabase->origin().equal(&origin))
return String();
}
}
-
- return m_server->fullPathForDatabase(origin, name, createIfDoesNotExist);
+ return DatabaseTracker::singleton().fullPathForDatabase(SecurityOriginData::fromSecurityOrigin(origin), name, createIfDoesNotExist);
}
-bool DatabaseManager::hasEntryForOrigin(SecurityOrigin* origin)
-{
- return m_server->hasEntryForOrigin(origin);
-}
-
-void DatabaseManager::origins(Vector<RefPtr<SecurityOrigin>>& result)
-{
- m_server->origins(result);
-}
-
-bool DatabaseManager::databaseNamesForOrigin(SecurityOrigin* origin, Vector<String>& result)
-{
- return m_server->databaseNamesForOrigin(origin, result);
-}
-
-DatabaseDetails DatabaseManager::detailsForNameAndOrigin(const String& name, SecurityOrigin* origin)
+DatabaseDetails DatabaseManager::detailsForNameAndOrigin(const String& name, SecurityOrigin& origin)
{
{
- std::lock_guard<std::mutex> lock(m_mutex);
-
+ std::lock_guard<Lock> lock { m_proposedDatabasesMutex };
for (auto* proposedDatabase : m_proposedDatabases) {
- if (proposedDatabase->details().name() == name && proposedDatabase->origin()->equal(origin)) {
+ if (proposedDatabase->details().name() == name && proposedDatabase->origin().equal(&origin)) {
ASSERT(proposedDatabase->details().threadID() == std::this_thread::get_id() || isMainThread());
-
return proposedDatabase->details();
}
}
}
-
- return m_server->detailsForNameAndOrigin(name, origin);
-}
-
-unsigned long long DatabaseManager::usageForOrigin(SecurityOrigin* origin)
-{
- return m_server->usageForOrigin(origin);
-}
-
-unsigned long long DatabaseManager::quotaForOrigin(SecurityOrigin* origin)
-{
- return m_server->quotaForOrigin(origin);
-}
-void DatabaseManager::setQuota(SecurityOrigin* origin, unsigned long long quotaSize)
-{
- m_server->setQuota(origin, quotaSize);
-}
-
-void DatabaseManager::deleteAllDatabases()
-{
- m_server->deleteAllDatabases();
-}
-
-bool DatabaseManager::deleteOrigin(SecurityOrigin* origin)
-{
- return m_server->deleteOrigin(origin);
+ return DatabaseTracker::singleton().detailsForNameAndOrigin(name, SecurityOriginData::fromSecurityOrigin(origin));
}
-bool DatabaseManager::deleteDatabase(SecurityOrigin* origin, const String& name)
+void DatabaseManager::logErrorMessage(ScriptExecutionContext& context, const String& message)
{
- return m_server->deleteDatabase(origin, name);
-}
-
-void DatabaseManager::interruptAllDatabasesForContext(ScriptExecutionContext* context)
-{
- RefPtr<DatabaseContext> databaseContext = existingDatabaseContextFor(context);
- if (databaseContext)
- m_server->interruptAllDatabasesForContext(databaseContext->backend().get());
-}
-
-void DatabaseManager::logErrorMessage(ScriptExecutionContext* context, const String& message)
-{
- context->addConsoleMessage(StorageMessageSource, ErrorMessageLevel, message);
+ context.addConsoleMessage(MessageSource::Storage, MessageLevel::Error, message);
}
} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseManager.h b/Source/WebCore/Modules/webdatabase/DatabaseManager.h
index 805b32c94..2ca2d077c 100644
--- a/Source/WebCore/Modules/webdatabase/DatabaseManager.h
+++ b/Source/WebCore/Modules/webdatabase/DatabaseManager.h
@@ -20,144 +20,75 @@
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DatabaseManager_h
-#define DatabaseManager_h
+#pragma once
-#if ENABLE(SQL_DATABASE)
-
-#include "DatabaseBasicTypes.h"
#include "DatabaseDetails.h"
-#include "DatabaseError.h"
-#include <mutex>
+#include "ExceptionOr.h"
#include <wtf/Assertions.h>
#include <wtf/HashMap.h>
#include <wtf/HashSet.h>
-#include <wtf/PassRefPtr.h>
+#include <wtf/Lock.h>
#include <wtf/Threading.h>
namespace WebCore {
-class AbstractDatabaseServer;
class Database;
-class DatabaseBackendBase;
class DatabaseCallback;
class DatabaseContext;
class DatabaseManagerClient;
-class DatabaseSync;
class DatabaseTaskSynchronizer;
+class Exception;
class SecurityOrigin;
class ScriptExecutionContext;
+struct SecurityOriginData;
class DatabaseManager {
- WTF_MAKE_NONCOPYABLE(DatabaseManager); WTF_MAKE_FAST_ALLOCATED;
+ WTF_MAKE_NONCOPYABLE(DatabaseManager);
+ friend class WTF::NeverDestroyed<DatabaseManager>;
public:
- static DatabaseManager& manager();
+ WEBCORE_EXPORT static DatabaseManager& singleton();
- void initialize(const String& databasePath);
- void setClient(DatabaseManagerClient*);
- String databaseDirectoryPath() const;
- void setDatabaseDirectoryPath(const String&);
+ WEBCORE_EXPORT void initialize(const String& databasePath);
+ WEBCORE_EXPORT void setClient(DatabaseManagerClient*);
bool isAvailable();
- void setIsAvailable(bool);
+ WEBCORE_EXPORT void setIsAvailable(bool);
// This gets a DatabaseContext for the specified ScriptExecutionContext.
// If one doesn't already exist, it will create a new one.
- PassRefPtr<DatabaseContext> databaseContextFor(ScriptExecutionContext*);
-
- // These 2 methods are for DatabaseContext (un)registration, and should only
- // be called by the DatabaseContext constructor and destructor.
- void registerDatabaseContext(DatabaseContext*);
- void unregisterDatabaseContext(DatabaseContext*);
+ Ref<DatabaseContext> databaseContext(ScriptExecutionContext&);
-#if !ASSERT_DISABLED
- void didConstructDatabaseContext();
- void didDestructDatabaseContext();
-#else
- void didConstructDatabaseContext() { }
- void didDestructDatabaseContext() { }
-#endif
+ ExceptionOr<Ref<Database>> openDatabase(ScriptExecutionContext&, const String& name, const String& expectedVersion, const String& displayName, unsigned estimatedSize, RefPtr<DatabaseCallback>&&);
- static ExceptionCode exceptionCodeForDatabaseError(DatabaseError);
+ WEBCORE_EXPORT bool hasOpenDatabases(ScriptExecutionContext&);
+ void stopDatabases(ScriptExecutionContext&, DatabaseTaskSynchronizer*);
- PassRefPtr<Database> openDatabase(ScriptExecutionContext*, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback>, DatabaseError&);
- PassRefPtr<DatabaseSync> openDatabaseSync(ScriptExecutionContext*, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback>, DatabaseError&);
+ String fullPathForDatabase(SecurityOrigin&, const String& name, bool createIfDoesNotExist = true);
- bool hasOpenDatabases(ScriptExecutionContext*);
- void stopDatabases(ScriptExecutionContext*, DatabaseTaskSynchronizer*);
+ WEBCORE_EXPORT DatabaseDetails detailsForNameAndOrigin(const String&, SecurityOrigin&);
- String fullPathForDatabase(SecurityOrigin*, const String& name, bool createIfDoesNotExist = true);
-
- bool hasEntryForOrigin(SecurityOrigin*);
- void origins(Vector<RefPtr<SecurityOrigin>>& result);
- bool databaseNamesForOrigin(SecurityOrigin*, Vector<String>& result);
- DatabaseDetails detailsForNameAndOrigin(const String&, SecurityOrigin*);
+private:
+ DatabaseManager() = default;
+ ~DatabaseManager() = delete;
- unsigned long long usageForOrigin(SecurityOrigin*);
- unsigned long long quotaForOrigin(SecurityOrigin*);
+ enum OpenAttempt { FirstTryToOpenDatabase, RetryOpenDatabase };
+ ExceptionOr<Ref<Database>> openDatabaseBackend(ScriptExecutionContext&, const String& name, const String& expectedVersion, const String& displayName, unsigned estimatedSize, bool setVersionInNewDatabase);
+ ExceptionOr<Ref<Database>> tryToOpenDatabaseBackend(ScriptExecutionContext&, const String& name, const String& expectedVersion, const String& displayName, unsigned estimatedSize, bool setVersionInNewDatabase, OpenAttempt);
- void setQuota(SecurityOrigin*, unsigned long long);
+ class ProposedDatabase;
+ void addProposedDatabase(ProposedDatabase&);
+ void removeProposedDatabase(ProposedDatabase&);
- void deleteAllDatabases();
- bool deleteOrigin(SecurityOrigin*);
- bool deleteDatabase(SecurityOrigin*, const String& name);
+ static void logErrorMessage(ScriptExecutionContext&, const String& message);
- void interruptAllDatabasesForContext(ScriptExecutionContext*);
+ DatabaseManagerClient* m_client { nullptr };
+ bool m_databaseIsAvailable { true };
-private:
- class ProposedDatabase {
- public:
- ProposedDatabase(DatabaseManager&, SecurityOrigin*,
- const String& name, const String& displayName, unsigned long estimatedSize);
- ~ProposedDatabase();
-
- SecurityOrigin* origin() { return m_origin.get(); }
- DatabaseDetails& details() { return m_details; }
-
- private:
- DatabaseManager& m_manager;
- RefPtr<SecurityOrigin> m_origin;
- DatabaseDetails m_details;
- };
-
- DatabaseManager();
- ~DatabaseManager() { }
-
- // This gets a DatabaseContext for the specified ScriptExecutionContext if
- // it already exist previously. Otherwise, it returns 0.
- PassRefPtr<DatabaseContext> existingDatabaseContextFor(ScriptExecutionContext*);
-
- PassRefPtr<DatabaseBackendBase> openDatabaseBackend(ScriptExecutionContext*,
- DatabaseType, const String& name, const String& expectedVersion, const String& displayName,
- unsigned long estimatedSize, bool setVersionInNewDatabase, DatabaseError&, String& errorMessage);
-
- void addProposedDatabase(ProposedDatabase*);
- void removeProposedDatabase(ProposedDatabase*);
-
- static void logErrorMessage(ScriptExecutionContext*, const String& message);
-
- AbstractDatabaseServer* m_server;
- DatabaseManagerClient* m_client;
- bool m_databaseIsAvailable;
-
- // Access to the following fields require locking m_lock below:
- typedef HashMap<ScriptExecutionContext*, DatabaseContext*> ContextMap;
- ContextMap m_contextMap;
-#if !ASSERT_DISABLED
- int m_databaseContextRegisteredCount;
- int m_databaseContextInstanceCount;
-#endif
+ Lock m_proposedDatabasesMutex;
HashSet<ProposedDatabase*> m_proposedDatabases;
-
- // This mutex protects m_contextMap, and m_proposedDatabases.
- std::mutex m_mutex;
};
} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
-
-#endif // DatabaseManager_h
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseManagerClient.h b/Source/WebCore/Modules/webdatabase/DatabaseManagerClient.h
index afc69f8cb..afd568ff4 100644
--- a/Source/WebCore/Modules/webdatabase/DatabaseManagerClient.h
+++ b/Source/WebCore/Modules/webdatabase/DatabaseManagerClient.h
@@ -22,32 +22,30 @@
* (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 DatabaseManagerClient_h
-#define DatabaseManagerClient_h
-#if ENABLE(SQL_DATABASE)
+#pragma once
#include <wtf/Forward.h>
namespace WebCore {
-class SecurityOrigin;
+struct SecurityOriginData;
class DatabaseManagerClient {
public:
virtual ~DatabaseManagerClient() { }
- virtual void dispatchDidModifyOrigin(SecurityOrigin*) = 0;
- virtual void dispatchDidModifyDatabase(SecurityOrigin*, const String& databaseName) = 0;
+ virtual void dispatchDidModifyOrigin(const SecurityOriginData&) = 0;
+ virtual void dispatchDidModifyDatabase(const SecurityOriginData&, const String& databaseName) = 0;
#if PLATFORM(IOS)
- virtual void dispatchDidAddNewOrigin(SecurityOrigin*) = 0;
+ virtual void dispatchDidAddNewOrigin() = 0;
virtual void dispatchDidDeleteDatabase() = 0;
virtual void dispatchDidDeleteDatabaseOrigin() = 0;
+#else
+ static void dispatchDidAddNewOrigin() { }
+ static void dispatchDidDeleteDatabase() { }
+ static void dispatchDidDeleteDatabaseOrigin() { }
#endif
};
} // namespace WebCore
-
-#endif
-
-#endif // DatabaseManagerClient_h
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseServer.cpp b/Source/WebCore/Modules/webdatabase/DatabaseServer.cpp
deleted file mode 100644
index 7f2cb3f8e..000000000
--- a/Source/WebCore/Modules/webdatabase/DatabaseServer.cpp
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright (C) 2012 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 "DatabaseServer.h"
-
-#if ENABLE(SQL_DATABASE)
-
-#include "Database.h"
-#include "DatabaseBackend.h"
-#include "DatabaseBackendContext.h"
-#include "DatabaseBackendSync.h"
-#include "DatabaseSync.h"
-#include "DatabaseTracker.h"
-
-namespace WebCore {
-
-void DatabaseServer::initialize(const String& databasePath)
-{
- DatabaseTracker::initializeTracker(databasePath);
-}
-
-void DatabaseServer::setClient(DatabaseManagerClient* client)
-{
- DatabaseTracker::tracker().setClient(client);
-}
-
-String DatabaseServer::databaseDirectoryPath() const
-{
- return DatabaseTracker::tracker().databaseDirectoryPath();
-}
-
-void DatabaseServer::setDatabaseDirectoryPath(const String& path)
-{
- DatabaseTracker::tracker().setDatabaseDirectoryPath(path);
-}
-
-String DatabaseServer::fullPathForDatabase(SecurityOrigin* origin, const String& name, bool createIfDoesNotExist)
-{
- return DatabaseTracker::tracker().fullPathForDatabase(origin, name, createIfDoesNotExist);
-}
-
-bool DatabaseServer::hasEntryForOrigin(SecurityOrigin* origin)
-{
- return DatabaseTracker::tracker().hasEntryForOrigin(origin);
-}
-
-void DatabaseServer::origins(Vector<RefPtr<SecurityOrigin>>& result)
-{
- DatabaseTracker::tracker().origins(result);
-}
-
-bool DatabaseServer::databaseNamesForOrigin(SecurityOrigin* origin, Vector<String>& result)
-{
- return DatabaseTracker::tracker().databaseNamesForOrigin(origin, result);
-}
-
-DatabaseDetails DatabaseServer::detailsForNameAndOrigin(const String& name, SecurityOrigin* origin)
-{
- return DatabaseTracker::tracker().detailsForNameAndOrigin(name, origin);
-}
-
-unsigned long long DatabaseServer::usageForOrigin(SecurityOrigin* origin)
-{
- return DatabaseTracker::tracker().usageForOrigin(origin);
-}
-
-unsigned long long DatabaseServer::quotaForOrigin(SecurityOrigin* origin)
-{
- return DatabaseTracker::tracker().quotaForOrigin(origin);
-}
-
-void DatabaseServer::setQuota(SecurityOrigin* origin, unsigned long long quotaSize)
-{
- DatabaseTracker::tracker().setQuota(origin, quotaSize);
-}
-
-void DatabaseServer::deleteAllDatabases()
-{
- DatabaseTracker::tracker().deleteAllDatabases();
-}
-
-bool DatabaseServer::deleteOrigin(SecurityOrigin* origin)
-{
- return DatabaseTracker::tracker().deleteOrigin(origin);
-}
-
-bool DatabaseServer::deleteDatabase(SecurityOrigin* origin, const String& name)
-{
- return DatabaseTracker::tracker().deleteDatabase(origin, name);
-}
-
-void DatabaseServer::interruptAllDatabasesForContext(const DatabaseBackendContext* context)
-{
- DatabaseTracker::tracker().interruptAllDatabasesForContext(context);
-}
-
-PassRefPtr<DatabaseBackendBase> DatabaseServer::openDatabase(RefPtr<DatabaseBackendContext>& backendContext,
- DatabaseType type, const String& name, const String& expectedVersion, const String& displayName,
- unsigned long estimatedSize, bool setVersionInNewDatabase, DatabaseError &error, String& errorMessage,
- OpenAttempt attempt)
-{
- RefPtr<DatabaseBackendBase> database;
- bool success = false; // Make some older compilers happy.
-
- switch (attempt) {
- case FirstTryToOpenDatabase:
- success = DatabaseTracker::tracker().canEstablishDatabase(backendContext.get(), name, estimatedSize, error);
- break;
- case RetryOpenDatabase:
- success = DatabaseTracker::tracker().retryCanEstablishDatabase(backendContext.get(), name, estimatedSize, error);
- }
-
- if (success)
- database = createDatabase(backendContext, type, name, expectedVersion, displayName, estimatedSize, setVersionInNewDatabase, error, errorMessage);
- return database.release();
-}
-
-PassRefPtr<DatabaseBackendBase> DatabaseServer::createDatabase(RefPtr<DatabaseBackendContext>& backendContext,
- DatabaseType type, const String& name, const String& expectedVersion, const String& displayName,
- unsigned long estimatedSize, bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage)
-{
- RefPtr<DatabaseBackendBase> database;
- switch (type) {
- case DatabaseType::Async:
- database = adoptRef(new Database(backendContext, name, expectedVersion, displayName, estimatedSize));
- break;
- case DatabaseType::Sync:
- database = adoptRef(new DatabaseSync(backendContext, name, expectedVersion, displayName, estimatedSize));
- }
-
- if (!database->openAndVerifyVersion(setVersionInNewDatabase, error, errorMessage))
- return 0;
-
- DatabaseTracker::tracker().setDatabaseDetails(backendContext->securityOrigin(), name, displayName, estimatedSize);
- return database.release();
-}
-
-} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseServer.h b/Source/WebCore/Modules/webdatabase/DatabaseServer.h
deleted file mode 100644
index 7cf9c9c9f..000000000
--- a/Source/WebCore/Modules/webdatabase/DatabaseServer.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2012 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.
- */
-
-#ifndef DatabaseServer_h
-#define DatabaseServer_h
-
-#if ENABLE(SQL_DATABASE)
-
-#include "AbstractDatabaseServer.h"
-
-namespace WebCore {
-
-class DatabaseServer: public AbstractDatabaseServer {
-public:
- DatabaseServer() { };
- virtual ~DatabaseServer() { }
-
- virtual void initialize(const String& databasePath);
-
- virtual void setClient(DatabaseManagerClient*);
- virtual String databaseDirectoryPath() const;
- virtual void setDatabaseDirectoryPath(const String&);
-
- virtual String fullPathForDatabase(SecurityOrigin*, const String& name, bool createIfDoesNotExist);
-
- virtual PassRefPtr<DatabaseBackendBase> openDatabase(RefPtr<DatabaseBackendContext>&, DatabaseType,
- const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize,
- bool setVersionInNewDatabase, DatabaseError&, String& errorMessage, OpenAttempt);
-
- virtual bool hasEntryForOrigin(SecurityOrigin*);
- virtual void origins(Vector<RefPtr<SecurityOrigin>>& result);
- virtual bool databaseNamesForOrigin(SecurityOrigin*, Vector<String>& result);
- virtual DatabaseDetails detailsForNameAndOrigin(const String&, SecurityOrigin*);
-
- virtual unsigned long long usageForOrigin(SecurityOrigin*);
- virtual unsigned long long quotaForOrigin(SecurityOrigin*);
-
- virtual void setQuota(SecurityOrigin*, unsigned long long);
-
- virtual void deleteAllDatabases();
- virtual bool deleteOrigin(SecurityOrigin*);
- virtual bool deleteDatabase(SecurityOrigin*, const String& name);
-
- virtual void interruptAllDatabasesForContext(const DatabaseBackendContext*);
-
-protected:
- virtual PassRefPtr<DatabaseBackendBase> createDatabase(RefPtr<DatabaseBackendContext>&, DatabaseType,
- const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize,
- bool setVersionInNewDatabase, DatabaseError&, String& errorMessage);
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
-
-#endif // DatabaseServer_h
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseSync.cpp b/Source/WebCore/Modules/webdatabase/DatabaseSync.cpp
deleted file mode 100644
index f92c34ff0..000000000
--- a/Source/WebCore/Modules/webdatabase/DatabaseSync.cpp
+++ /dev/null
@@ -1,195 +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:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google 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 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
- * OWNER 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 "DatabaseSync.h"
-
-#if ENABLE(SQL_DATABASE)
-
-#include "DatabaseBackendContext.h"
-#include "DatabaseBackendSync.h"
-#include "DatabaseCallback.h"
-#include "DatabaseContext.h"
-#include "DatabaseManager.h"
-#include "DatabaseTracker.h"
-#include "Logging.h"
-#include "SQLException.h"
-#include "SQLTransactionSync.h"
-#include "SQLTransactionSyncCallback.h"
-#include "ScriptExecutionContext.h"
-#include "SecurityOrigin.h"
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefPtr.h>
-#include <wtf/text/CString.h>
-
-namespace WebCore {
-
-PassRefPtr<DatabaseSync> DatabaseSync::create(ScriptExecutionContext*, PassRefPtr<DatabaseBackendBase> backend)
-{
- return static_cast<DatabaseSync*>(backend.get());
-}
-
-DatabaseSync::DatabaseSync(PassRefPtr<DatabaseBackendContext> databaseContext,
- const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize)
- : DatabaseBase(databaseContext->scriptExecutionContext())
- , DatabaseBackendSync(databaseContext, name, expectedVersion, displayName, estimatedSize)
-{
- setFrontend(this);
-}
-
-DatabaseSync::~DatabaseSync()
-{
- ASSERT(m_scriptExecutionContext->isContextThread());
-}
-
-PassRefPtr<DatabaseBackendSync> DatabaseSync::backend()
-{
- return this;
-}
-
-void DatabaseSync::changeVersion(const String& oldVersion, const String& newVersion, PassRefPtr<SQLTransactionSyncCallback> changeVersionCallback, ExceptionCode& ec)
-{
- ASSERT(m_scriptExecutionContext->isContextThread());
-
- if (sqliteDatabase().transactionInProgress()) {
- setLastErrorMessage("unable to changeVersion from within a transaction");
- ec = SQLException::DATABASE_ERR;
- return;
- }
-
- RefPtr<SQLTransactionSync> transaction = SQLTransactionSync::create(this, changeVersionCallback, false);
- if ((ec = transaction->begin())) {
- ASSERT(!lastErrorMessage().isEmpty());
- return;
- }
-
- String actualVersion;
- if (!getVersionFromDatabase(actualVersion)) {
- setLastErrorMessage("unable to read the current version", sqliteDatabase().lastError(), sqliteDatabase().lastErrorMsg());
- ec = SQLException::UNKNOWN_ERR;
- return;
- }
-
- if (actualVersion != oldVersion) {
- setLastErrorMessage("current version of the database and `oldVersion` argument do not match");
- ec = SQLException::VERSION_ERR;
- return;
- }
-
- if ((ec = transaction->execute())) {
- ASSERT(!lastErrorMessage().isEmpty());
- return;
- }
-
- if (!setVersionInDatabase(newVersion)) {
- setLastErrorMessage("unable to set the new version", sqliteDatabase().lastError(), sqliteDatabase().lastErrorMsg());
- ec = SQLException::UNKNOWN_ERR;
- return;
- }
-
- if ((ec = transaction->commit())) {
- ASSERT(!lastErrorMessage().isEmpty());
- setCachedVersion(oldVersion);
- return;
- }
-
- setExpectedVersion(newVersion);
- setLastErrorMessage("");
-}
-
-void DatabaseSync::transaction(PassRefPtr<SQLTransactionSyncCallback> callback, ExceptionCode& ec)
-{
- runTransaction(callback, false, ec);
-}
-
-void DatabaseSync::readTransaction(PassRefPtr<SQLTransactionSyncCallback> callback, ExceptionCode& ec)
-{
- runTransaction(callback, true, ec);
-}
-
-void DatabaseSync::runTransaction(PassRefPtr<SQLTransactionSyncCallback> callback, bool readOnly, ExceptionCode& ec)
-{
- ASSERT(m_scriptExecutionContext->isContextThread());
-
- if (sqliteDatabase().transactionInProgress()) {
- setLastErrorMessage("unable to start a transaction from within a transaction");
- ec = SQLException::DATABASE_ERR;
- return;
- }
-
- RefPtr<SQLTransactionSync> transaction = SQLTransactionSync::create(this, callback, readOnly);
- if ((ec = transaction->begin()) || (ec = transaction->execute()) || (ec = transaction->commit())) {
- ASSERT(!lastErrorMessage().isEmpty());
- transaction->rollback();
- }
-
- setLastErrorMessage("");
-}
-
-void DatabaseSync::markAsDeletedAndClose()
-{
- // FIXME: need to do something similar to closeImmediately(), but in a sync way
-}
-
-class CloseSyncDatabaseOnContextThreadTask : public ScriptExecutionContext::Task {
-public:
- static PassOwnPtr<CloseSyncDatabaseOnContextThreadTask> create(PassRefPtr<DatabaseSync> database)
- {
- return adoptPtr(new CloseSyncDatabaseOnContextThreadTask(database));
- }
-
- virtual void performTask(ScriptExecutionContext*)
- {
- m_database->closeImmediately();
- }
-
-private:
- CloseSyncDatabaseOnContextThreadTask(PassRefPtr<DatabaseSync> database)
- : m_database(database)
- {
- }
-
- RefPtr<DatabaseSync> m_database;
-};
-
-void DatabaseSync::closeImmediately()
-{
- ASSERT(m_scriptExecutionContext->isContextThread());
-
- if (!opened())
- return;
-
- logErrorMessage("forcibly closing database");
- closeDatabase();
-}
-
-} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseSync.h b/Source/WebCore/Modules/webdatabase/DatabaseSync.h
deleted file mode 100644
index 4119b5bbe..000000000
--- a/Source/WebCore/Modules/webdatabase/DatabaseSync.h
+++ /dev/null
@@ -1,89 +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:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google 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 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
- * OWNER 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.
- */
-
-#ifndef DatabaseSync_h
-#define DatabaseSync_h
-
-#if ENABLE(SQL_DATABASE)
-
-#include "DatabaseBackendSync.h"
-#include "DatabaseBase.h"
-#include "DatabaseBasicTypes.h"
-#include <wtf/Forward.h>
-#include <wtf/text/WTFString.h>
-
-namespace WebCore {
-
-class DatabaseCallback;
-class SQLTransactionSync;
-class SQLTransactionSyncCallback;
-
-// Instances of this class should be created and used only on the worker's context thread.
-class DatabaseSync : public DatabaseBase, public DatabaseBackendSync {
-public:
- virtual ~DatabaseSync();
-
- void changeVersion(const String& oldVersion, const String& newVersion, PassRefPtr<SQLTransactionSyncCallback>, ExceptionCode&);
- void transaction(PassRefPtr<SQLTransactionSyncCallback>, ExceptionCode&);
- void readTransaction(PassRefPtr<SQLTransactionSyncCallback>, ExceptionCode&);
-
- virtual void markAsDeletedAndClose();
- virtual void closeImmediately();
-
- const String& lastErrorMessage() const { return m_lastErrorMessage; }
- void setLastErrorMessage(const String& message) { m_lastErrorMessage = message; }
- void setLastErrorMessage(const char* message, int sqliteCode)
- {
- setLastErrorMessage(String::format("%s (%d)", message, sqliteCode));
- }
- void setLastErrorMessage(const char* message, int sqliteCode, const char* sqliteMessage)
- {
- setLastErrorMessage(String::format("%s (%d, %s)", message, sqliteCode, sqliteMessage));
- }
-
-private:
- DatabaseSync(PassRefPtr<DatabaseBackendContext>, const String& name,
- const String& expectedVersion, const String& displayName, unsigned long estimatedSize);
- PassRefPtr<DatabaseBackendSync> backend();
- static PassRefPtr<DatabaseSync> create(ScriptExecutionContext*, PassRefPtr<DatabaseBackendBase>);
-
- void runTransaction(PassRefPtr<SQLTransactionSyncCallback>, bool readOnly, ExceptionCode&);
-
- String m_lastErrorMessage;
-
- friend class DatabaseManager;
- friend class DatabaseServer; // FIXME: remove this when the backend has been split out.
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
-
-#endif
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseSync.idl b/Source/WebCore/Modules/webdatabase/DatabaseSync.idl
deleted file mode 100644
index 4b5ff2a6f..000000000
--- a/Source/WebCore/Modules/webdatabase/DatabaseSync.idl
+++ /dev/null
@@ -1,42 +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:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google 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 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
- * OWNER 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.
- */
-
-[
- NoInterfaceObject,
- Conditional=SQL_DATABASE,
- JSNoStaticTables,
-] interface DatabaseSync {
- readonly attribute DOMString version;
- readonly attribute DOMString lastErrorMessage;
- [RaisesException] void changeVersion(DOMString oldVersion, DOMString newVersion, optional SQLTransactionSyncCallback callback);
- [RaisesException] void transaction(SQLTransactionSyncCallback callback);
- [RaisesException] void readTransaction(SQLTransactionSyncCallback callback);
-};
-
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseTask.cpp b/Source/WebCore/Modules/webdatabase/DatabaseTask.cpp
index 8cf8ea21a..f1a54ac94 100644
--- a/Source/WebCore/Modules/webdatabase/DatabaseTask.cpp
+++ b/Source/WebCore/Modules/webdatabase/DatabaseTask.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, 2008, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2007, 2008, 2013, 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
@@ -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,22 +25,17 @@
* (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 "DatabaseTask.h"
-#if ENABLE(SQL_DATABASE)
-
#include "Database.h"
-#include "DatabaseBackend.h"
#include "Logging.h"
+#include "SQLTransaction.h"
namespace WebCore {
DatabaseTaskSynchronizer::DatabaseTaskSynchronizer()
- : m_taskCompleted(false)
-#ifndef NDEBUG
- , m_hasCheckedForTermination(false)
-#endif
{
}
@@ -56,48 +51,36 @@ void DatabaseTaskSynchronizer::taskCompleted()
{
m_synchronousMutex.lock();
m_taskCompleted = true;
- m_synchronousCondition.signal();
+ m_synchronousCondition.notifyOne();
m_synchronousMutex.unlock();
}
-DatabaseTask::DatabaseTask(DatabaseBackend* database, DatabaseTaskSynchronizer* synchronizer)
+DatabaseTask::DatabaseTask(Database& database, DatabaseTaskSynchronizer* synchronizer)
: m_database(database)
, m_synchronizer(synchronizer)
-#if !LOG_DISABLED
- , m_complete(false)
-#endif
{
}
DatabaseTask::~DatabaseTask()
{
-#if !LOG_DISABLED
ASSERT(m_complete || !m_synchronizer);
-#endif
}
void DatabaseTask::performTask()
{
// Database tasks are meant to be used only once, so make sure this one hasn't been performed before.
-#if !LOG_DISABLED
ASSERT(!m_complete);
-#endif
LOG(StorageAPI, "Performing %s %p\n", debugTaskName(), this);
-#if !PLATFORM(IOS)
- m_database->resetAuthorizer();
-#else
- if (m_database)
- m_database->resetAuthorizer();
-#endif
+ m_database.resetAuthorizer();
doPerformTask();
if (m_synchronizer)
m_synchronizer->taskCompleted();
-#if !LOG_DISABLED
+#if !ASSERT_DISABLED
m_complete = true;
#endif
}
@@ -105,62 +88,60 @@ void DatabaseTask::performTask()
// *** DatabaseOpenTask ***
// Opens the database file and verifies the version matches the expected version.
-DatabaseBackend::DatabaseOpenTask::DatabaseOpenTask(DatabaseBackend* database, bool setVersionInNewDatabase, DatabaseTaskSynchronizer* synchronizer, DatabaseError& error, String& errorMessage, bool& success)
- : DatabaseTask(database, synchronizer)
+DatabaseOpenTask::DatabaseOpenTask(Database& database, bool setVersionInNewDatabase, DatabaseTaskSynchronizer& synchronizer, ExceptionOr<void>& result)
+ : DatabaseTask(database, &synchronizer)
, m_setVersionInNewDatabase(setVersionInNewDatabase)
- , m_error(error)
- , m_errorMessage(errorMessage)
- , m_success(success)
+ , m_result(result)
{
- ASSERT(synchronizer); // A task with output parameters is supposed to be synchronous.
}
-void DatabaseBackend::DatabaseOpenTask::doPerformTask()
+void DatabaseOpenTask::doPerformTask()
{
- String errorMessage;
- m_success = database()->performOpenAndVerify(m_setVersionInNewDatabase, m_error, errorMessage);
- if (!m_success)
- m_errorMessage = errorMessage.isolatedCopy();
+ m_result = isolatedCopy(database().performOpenAndVerify(m_setVersionInNewDatabase));
}
#if !LOG_DISABLED
-const char* DatabaseBackend::DatabaseOpenTask::debugTaskName() const
+
+const char* DatabaseOpenTask::debugTaskName() const
{
return "DatabaseOpenTask";
}
+
#endif
// *** DatabaseCloseTask ***
// Closes the database.
-DatabaseBackend::DatabaseCloseTask::DatabaseCloseTask(DatabaseBackend* database, DatabaseTaskSynchronizer* synchronizer)
- : DatabaseTask(database, synchronizer)
+DatabaseCloseTask::DatabaseCloseTask(Database& database, DatabaseTaskSynchronizer& synchronizer)
+ : DatabaseTask(database, &synchronizer)
{
}
-void DatabaseBackend::DatabaseCloseTask::doPerformTask()
+void DatabaseCloseTask::doPerformTask()
{
- Database::from(database())->close();
+ database().performClose();
}
#if !LOG_DISABLED
-const char* DatabaseBackend::DatabaseCloseTask::debugTaskName() const
+
+const char* DatabaseCloseTask::debugTaskName() const
{
return "DatabaseCloseTask";
}
+
#endif
// *** DatabaseTransactionTask ***
// Starts a transaction that will report its results via a callback.
-DatabaseBackend::DatabaseTransactionTask::DatabaseTransactionTask(PassRefPtr<SQLTransactionBackend> transaction)
- : DatabaseTask(Database::from(transaction->database()), 0)
- , m_transaction(transaction)
+DatabaseTransactionTask::DatabaseTransactionTask(RefPtr<SQLTransaction>&& transaction)
+ : DatabaseTask(transaction->database(), 0)
+ , m_transaction(WTFMove(transaction))
, m_didPerformTask(false)
{
}
-DatabaseBackend::DatabaseTransactionTask::~DatabaseTransactionTask()
+DatabaseTransactionTask::~DatabaseTransactionTask()
{
// If the task is being destructed without the transaction ever being run,
// then we must either have an error or an interruption. Give the
@@ -174,48 +155,43 @@ DatabaseBackend::DatabaseTransactionTask::~DatabaseTransactionTask()
m_transaction->notifyDatabaseThreadIsShuttingDown();
}
-#if PLATFORM(IOS)
-bool Database::DatabaseTransactionTask::shouldPerformWhilePaused() const
-{
- return m_transaction->shouldPerformWhilePaused();
-}
-#endif
-
-void DatabaseBackend::DatabaseTransactionTask::doPerformTask()
+void DatabaseTransactionTask::doPerformTask()
{
m_transaction->performNextStep();
m_didPerformTask = true;
}
#if !LOG_DISABLED
-const char* DatabaseBackend::DatabaseTransactionTask::debugTaskName() const
+
+const char* DatabaseTransactionTask::debugTaskName() const
{
return "DatabaseTransactionTask";
}
+
#endif
// *** DatabaseTableNamesTask ***
// Retrieves a list of all tables in the database - for WebInspector support.
-DatabaseBackend::DatabaseTableNamesTask::DatabaseTableNamesTask(DatabaseBackend* database, DatabaseTaskSynchronizer* synchronizer, Vector<String>& names)
- : DatabaseTask(database, synchronizer)
- , m_tableNames(names)
+DatabaseTableNamesTask::DatabaseTableNamesTask(Database& database, DatabaseTaskSynchronizer& synchronizer, Vector<String>& result)
+ : DatabaseTask(database, &synchronizer)
+ , m_result(result)
{
- ASSERT(synchronizer); // A task with output parameters is supposed to be synchronous.
}
-void DatabaseBackend::DatabaseTableNamesTask::doPerformTask()
+void DatabaseTableNamesTask::doPerformTask()
{
- m_tableNames = Database::from(database())->performGetTableNames();
+ // FIXME: Why no need for an isolatedCopy here?
+ m_result = database().performGetTableNames();
}
#if !LOG_DISABLED
-const char* DatabaseBackend::DatabaseTableNamesTask::debugTaskName() const
+
+const char* DatabaseTableNamesTask::debugTaskName() const
{
return "DatabaseTableNamesTask";
}
+
#endif
} // namespace WebCore
-
-#endif
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseTask.h b/Source/WebCore/Modules/webdatabase/DatabaseTask.h
index 1edaf8661..fe49bcdc1 100644
--- a/Source/WebCore/Modules/webdatabase/DatabaseTask.h
+++ b/Source/WebCore/Modules/webdatabase/DatabaseTask.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, 2008, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2007, 2008, 2013, 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
@@ -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,21 +25,14 @@
* (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 DatabaseTask_h
-#define DatabaseTask_h
-#if ENABLE(SQL_DATABASE)
+#pragma once
-#include "DatabaseBackend.h"
-#include "DatabaseBasicTypes.h"
-#include "DatabaseError.h"
+#include "ExceptionOr.h"
#include "SQLTransactionBackend.h"
-#include <wtf/OwnPtr.h>
-#include <wtf/PassOwnPtr.h>
-#include <wtf/PassRefPtr.h>
-#include <wtf/Threading.h>
+#include <wtf/Condition.h>
+#include <wtf/Lock.h>
#include <wtf/Vector.h>
-#include <wtf/text/WTFString.h>
namespace WebCore {
@@ -62,143 +55,103 @@ public:
#endif
private:
- bool m_taskCompleted;
- Mutex m_synchronousMutex;
- ThreadCondition m_synchronousCondition;
+ bool m_taskCompleted { false };
+ Lock m_synchronousMutex;
+ Condition m_synchronousCondition;
#ifndef NDEBUG
- bool m_hasCheckedForTermination;
+ bool m_hasCheckedForTermination { false };
#endif
};
class DatabaseTask {
- WTF_MAKE_NONCOPYABLE(DatabaseTask); WTF_MAKE_FAST_ALLOCATED;
+ WTF_MAKE_FAST_ALLOCATED;
public:
virtual ~DatabaseTask();
-#if PLATFORM(IOS)
- virtual bool shouldPerformWhilePaused() const = 0;
-#endif
-
void performTask();
- DatabaseBackend* database() const { return m_database; }
-#ifndef NDEBUG
+ Database& database() const { return m_database; }
+
+#if !ASSERT_DISABLED
bool hasSynchronizer() const { return m_synchronizer; }
bool hasCheckedForTermination() const { return m_synchronizer->hasCheckedForTermination(); }
#endif
protected:
- DatabaseTask(DatabaseBackend*, DatabaseTaskSynchronizer*);
+ DatabaseTask(Database&, DatabaseTaskSynchronizer*);
private:
virtual void doPerformTask() = 0;
- DatabaseBackend* m_database;
+ Database& m_database;
DatabaseTaskSynchronizer* m_synchronizer;
#if !LOG_DISABLED
virtual const char* debugTaskName() const = 0;
- bool m_complete;
+#endif
+
+#if !ASSERT_DISABLED
+ bool m_complete { false };
#endif
};
-class DatabaseBackend::DatabaseOpenTask : public DatabaseTask {
+class DatabaseOpenTask final : public DatabaseTask {
public:
- static std::unique_ptr<DatabaseOpenTask> create(DatabaseBackend* db, bool setVersionInNewDatabase, DatabaseTaskSynchronizer* synchronizer, DatabaseError& error, String& errorMessage, bool& success)
- {
- return std::unique_ptr<DatabaseOpenTask>(new DatabaseOpenTask(db, setVersionInNewDatabase, synchronizer, error, errorMessage, success));
- }
-
-#if PLATFORM(IOS)
- virtual bool shouldPerformWhilePaused() const override { return true; }
-#endif
+ DatabaseOpenTask(Database&, bool setVersionInNewDatabase, DatabaseTaskSynchronizer&, ExceptionOr<void>& result);
private:
- DatabaseOpenTask(DatabaseBackend*, bool setVersionInNewDatabase, DatabaseTaskSynchronizer*, DatabaseError&, String& errorMessage, bool& success);
+ void doPerformTask() final;
- virtual void doPerformTask();
#if !LOG_DISABLED
- virtual const char* debugTaskName() const;
+ const char* debugTaskName() const final;
#endif
bool m_setVersionInNewDatabase;
- DatabaseError& m_error;
- String& m_errorMessage;
- bool& m_success;
+ ExceptionOr<void>& m_result;
};
-class DatabaseBackend::DatabaseCloseTask : public DatabaseTask {
+class DatabaseCloseTask final : public DatabaseTask {
public:
- static std::unique_ptr<DatabaseCloseTask> create(DatabaseBackend* db, DatabaseTaskSynchronizer* synchronizer)
- {
- return std::unique_ptr<DatabaseCloseTask>(new DatabaseCloseTask(db, synchronizer));
- }
-
-#if PLATFORM(IOS)
- virtual bool shouldPerformWhilePaused() const override { return true; }
-#endif
+ DatabaseCloseTask(Database&, DatabaseTaskSynchronizer&);
private:
- DatabaseCloseTask(DatabaseBackend*, DatabaseTaskSynchronizer*);
+ void doPerformTask() final;
- virtual void doPerformTask();
#if !LOG_DISABLED
- virtual const char* debugTaskName() const;
+ const char* debugTaskName() const final;
#endif
};
-class DatabaseBackend::DatabaseTransactionTask : public DatabaseTask {
+class DatabaseTransactionTask final : public DatabaseTask {
public:
+ explicit DatabaseTransactionTask(RefPtr<SQLTransaction>&&);
virtual ~DatabaseTransactionTask();
- // Transaction task is never synchronous, so no 'synchronizer' parameter.
- static std::unique_ptr<DatabaseTransactionTask> create(PassRefPtr<SQLTransactionBackend> transaction)
- {
- return std::unique_ptr<DatabaseTransactionTask>(new DatabaseTransactionTask(transaction));
- }
-
-#if PLATFORM(IOS)
- virtual bool shouldPerformWhilePaused() const override;
-#endif
-
- SQLTransactionBackend* transaction() const { return m_transaction.get(); }
+ SQLTransaction* transaction() const { return m_transaction.get(); }
private:
- explicit DatabaseTransactionTask(PassRefPtr<SQLTransactionBackend>);
+ void doPerformTask() final;
- virtual void doPerformTask();
#if !LOG_DISABLED
- virtual const char* debugTaskName() const;
+ const char* debugTaskName() const final;
#endif
- RefPtr<SQLTransactionBackend> m_transaction;
+ RefPtr<SQLTransaction> m_transaction;
bool m_didPerformTask;
};
-class DatabaseBackend::DatabaseTableNamesTask : public DatabaseTask {
+class DatabaseTableNamesTask final : public DatabaseTask {
public:
- static std::unique_ptr<DatabaseTableNamesTask> create(DatabaseBackend* db, DatabaseTaskSynchronizer* synchronizer, Vector<String>& names)
- {
- return std::unique_ptr<DatabaseTableNamesTask>(new DatabaseTableNamesTask(db, synchronizer, names));
- }
-
-#if PLATFORM(IOS)
- virtual bool shouldPerformWhilePaused() const override { return true; }
-#endif
+ DatabaseTableNamesTask(Database&, DatabaseTaskSynchronizer&, Vector<String>& result);
private:
- DatabaseTableNamesTask(DatabaseBackend*, DatabaseTaskSynchronizer*, Vector<String>& names);
+ void doPerformTask() final;
- virtual void doPerformTask();
#if !LOG_DISABLED
- virtual const char* debugTaskName() const;
+ const char* debugTaskName() const override;
#endif
- Vector<String>& m_tableNames;
+ Vector<String>& m_result;
};
} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
-
-#endif // DatabaseTask_h
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseThread.cpp b/Source/WebCore/Modules/webdatabase/DatabaseThread.cpp
index 7ee764ca0..0dde5f726 100644
--- a/Source/WebCore/Modules/webdatabase/DatabaseThread.cpp
+++ b/Source/WebCore/Modules/webdatabase/DatabaseThread.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.
*
@@ -29,25 +29,17 @@
#include "config.h"
#include "DatabaseThread.h"
-#if ENABLE(SQL_DATABASE)
-
#include "Database.h"
#include "DatabaseTask.h"
#include "Logging.h"
-#include "SQLTransactionClient.h"
+#include "SQLTransaction.h"
#include "SQLTransactionCoordinator.h"
#include <wtf/AutodrainedPool.h>
namespace WebCore {
DatabaseThread::DatabaseThread()
- : m_threadID(0)
-#if PLATFORM(IOS)
- , m_paused(false)
-#endif
- , m_transactionClient(adoptPtr(new SQLTransactionClient()))
- , m_transactionCoordinator(adoptPtr(new SQLTransactionCoordinator()))
- , m_cleanupSync(0)
+ : m_transactionCoordinator(std::make_unique<SQLTransactionCoordinator>())
{
m_selfRef = this;
}
@@ -66,7 +58,7 @@ DatabaseThread::~DatabaseThread()
bool DatabaseThread::start()
{
- MutexLocker lock(m_threadCreationMutex);
+ LockHolder lock(m_threadCreationMutex);
if (m_threadID)
return true;
@@ -76,13 +68,10 @@ bool DatabaseThread::start()
return m_threadID;
}
-void DatabaseThread::requestTermination(DatabaseTaskSynchronizer *cleanupSync)
+void DatabaseThread::requestTermination(DatabaseTaskSynchronizer* cleanupSync)
{
m_cleanupSync = cleanupSync;
LOG(StorageAPI, "DatabaseThread %p was asked to terminate\n", this);
-#if PLATFORM(IOS)
- m_pausedQueue.kill();
-#endif
m_queue.kill();
}
@@ -104,99 +93,18 @@ void DatabaseThread::databaseThreadStart(void* vDatabaseThread)
dbThread->databaseThread();
}
-#if PLATFORM(IOS)
-class DatabaseUnpauseTask : public DatabaseTask {
-public:
- static std::unique_ptr<DatabaseUnpauseTask> create(DatabaseThread* thread)
- {
- return std::unique_ptr<DatabaseUnpauseTask>(new DatabaseUnpauseTask(thread));
- }
-
- virtual bool shouldPerformWhilePaused() const
- {
- // Since we're not locking the DatabaseThread::m_paused in the main database thread loop, it's possible that
- // a DatabaseUnpauseTask might be added to the m_pausedQueue and performed from within ::handlePausedQueue.
- // To protect against this, we allow it to be performed even if the database is paused.
- // If the thread is paused when it is being performed, the tasks from the paused queue will simply be
- // requeued instead of performed.
- return true;
- }
-
-private:
- DatabaseUnpauseTask(DatabaseThread* thread)
- : DatabaseTask(0, 0)
- , m_thread(thread)
- {}
-
- virtual void doPerformTask()
- {
- m_thread->handlePausedQueue();
- }
-#if !LOG_DISABLED
- virtual const char* debugTaskName() const { return "DatabaseUnpauseTask"; }
-#endif
-
- DatabaseThread* m_thread;
-};
-
-
-void DatabaseThread::setPaused(bool paused)
-{
- if (m_paused == paused)
- return;
-
- MutexLocker pausedLocker(m_pausedMutex);
- m_paused = paused;
- if (!m_paused)
- scheduleTask(DatabaseUnpauseTask::create(this));
-}
-
-void DatabaseThread::handlePausedQueue()
-{
- Vector<std::unique_ptr<DatabaseTask> > pausedTasks;
- while (auto task = m_pausedQueue.tryGetMessage())
- pausedTasks.append(std::move(task));
-
- for (unsigned i = 0; i < pausedTasks.size(); ++i) {
- AutodrainedPool pool;
-
- std::unique_ptr<DatabaseTask> task(pausedTasks[i].release());
- {
- MutexLocker pausedLocker(m_pausedMutex);
- if (m_paused) {
- m_pausedQueue.append(std::move(task));
- continue;
- }
- }
-
- if (terminationRequested())
- break;
-
- task->performTask();
- }
-}
-#endif //PLATFORM(IOS)
-
-
void DatabaseThread::databaseThread()
{
{
// Wait for DatabaseThread::start() to complete.
- MutexLocker lock(m_threadCreationMutex);
+ LockHolder lock(m_threadCreationMutex);
LOG(StorageAPI, "Started DatabaseThread %p", this);
}
while (auto task = m_queue.waitForMessage()) {
AutodrainedPool pool;
-#if PLATFORM(IOS)
- if (!m_paused || task->shouldPerformWhilePaused())
- task->performTask();
- else
- m_pausedQueue.append(std::move(task));
-#else
task->performTask();
-#endif
}
// Clean up the list of all pending transactions on this database thread
@@ -206,69 +114,76 @@ void DatabaseThread::databaseThread()
// Close the databases that we ran transactions on. This ensures that if any transactions are still open, they are rolled back and we don't leave the database in an
// inconsistent or locked state.
- if (m_openDatabaseSet.size() > 0) {
- // As the call to close will modify the original set, we must take a copy to iterate over.
- DatabaseSet openSetCopy;
- openSetCopy.swap(m_openDatabaseSet);
- DatabaseSet::iterator end = openSetCopy.end();
- for (DatabaseSet::iterator it = openSetCopy.begin(); it != end; ++it)
- (*it).get()->close();
+ DatabaseSet openSetCopy;
+ {
+ LockHolder lock(m_openDatabaseSetMutex);
+ if (m_openDatabaseSet.size() > 0) {
+ // As the call to close will modify the original set, we must take a copy to iterate over.
+ openSetCopy.swap(m_openDatabaseSet);
+ }
}
+ for (auto& openDatabase : openSetCopy)
+ openDatabase->performClose();
+
// Detach the thread so its resources are no longer of any concern to anyone else
detachThread(m_threadID);
DatabaseTaskSynchronizer* cleanupSync = m_cleanupSync;
// Clear the self refptr, possibly resulting in deletion
- m_selfRef = 0;
+ m_selfRef = nullptr;
if (cleanupSync) // Someone wanted to know when we were done cleaning up.
cleanupSync->taskCompleted();
}
-void DatabaseThread::recordDatabaseOpen(DatabaseBackend* database)
+void DatabaseThread::recordDatabaseOpen(Database& database)
{
+ LockHolder lock(m_openDatabaseSetMutex);
+
ASSERT(currentThread() == m_threadID);
- ASSERT(database);
- ASSERT(!m_openDatabaseSet.contains(database));
- m_openDatabaseSet.add(database);
+ ASSERT(!m_openDatabaseSet.contains(&database));
+ m_openDatabaseSet.add(&database);
}
-void DatabaseThread::recordDatabaseClosed(DatabaseBackend* database)
+void DatabaseThread::recordDatabaseClosed(Database& database)
{
+ LockHolder lock(m_openDatabaseSetMutex);
+
ASSERT(currentThread() == m_threadID);
- ASSERT(database);
- ASSERT(m_queue.killed() || m_openDatabaseSet.contains(database));
- m_openDatabaseSet.remove(database);
+ ASSERT(m_queue.killed() || m_openDatabaseSet.contains(&database));
+ m_openDatabaseSet.remove(&database);
}
-void DatabaseThread::scheduleTask(std::unique_ptr<DatabaseTask> task)
+void DatabaseThread::scheduleTask(std::unique_ptr<DatabaseTask>&& task)
{
ASSERT(!task->hasSynchronizer() || task->hasCheckedForTermination());
- m_queue.append(std::move(task));
+ m_queue.append(WTFMove(task));
}
-void DatabaseThread::scheduleImmediateTask(std::unique_ptr<DatabaseTask> task)
+void DatabaseThread::scheduleImmediateTask(std::unique_ptr<DatabaseTask>&& task)
{
ASSERT(!task->hasSynchronizer() || task->hasCheckedForTermination());
- m_queue.prepend(std::move(task));
+ m_queue.prepend(WTFMove(task));
}
-class SameDatabasePredicate {
-public:
- SameDatabasePredicate(const DatabaseBackend* database) : m_database(database) { }
- bool operator()(const DatabaseTask& task) const { return task.database() == m_database; }
-private:
- const DatabaseBackend* m_database;
-};
+void DatabaseThread::unscheduleDatabaseTasks(Database& database)
+{
+ // The thread loop is running, sp some tasks for this database may still be executed. This is unavoidable.
+ m_queue.removeIf([&database] (const DatabaseTask& task) {
+ return &task.database() == &database;
+ });
+}
-void DatabaseThread::unscheduleDatabaseTasks(DatabaseBackend* database)
+bool DatabaseThread::hasPendingDatabaseActivity() const
{
- // Note that the thread loop is running, so some tasks for the database
- // may still be executed. This is unavoidable.
- SameDatabasePredicate predicate(database);
- m_queue.removeIf(predicate);
+ LockHolder lock(m_openDatabaseSetMutex);
+ for (auto& database : m_openDatabaseSet) {
+ if (database->hasPendingCreationEvent() || database->hasPendingTransaction())
+ return true;
+ }
+ return false;
}
+
} // namespace WebCore
-#endif
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseThread.h b/Source/WebCore/Modules/webdatabase/DatabaseThread.h
index c05979e5a..ec041e318 100644
--- a/Source/WebCore/Modules/webdatabase/DatabaseThread.h
+++ b/Source/WebCore/Modules/webdatabase/DatabaseThread.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,82 +25,62 @@
* (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 DatabaseThread_h
-#define DatabaseThread_h
-#if ENABLE(SQL_DATABASE)
+#pragma once
-#include <wtf/Deque.h>
-#include <wtf/HashMap.h>
+#include <memory>
#include <wtf/HashSet.h>
#include <wtf/MessageQueue.h>
-#include <wtf/OwnPtr.h>
-#include <wtf/PassOwnPtr.h>
-#include <wtf/PassRefPtr.h>
#include <wtf/RefPtr.h>
#include <wtf/Threading.h>
namespace WebCore {
-class DatabaseBackend;
+class Database;
class DatabaseTask;
class DatabaseTaskSynchronizer;
class Document;
-class SQLTransactionClient;
class SQLTransactionCoordinator;
class DatabaseThread : public ThreadSafeRefCounted<DatabaseThread> {
public:
- static PassRefPtr<DatabaseThread> create() { return adoptRef(new DatabaseThread); }
+ static Ref<DatabaseThread> create() { return adoptRef(*new DatabaseThread); }
~DatabaseThread();
bool start();
void requestTermination(DatabaseTaskSynchronizer* cleanupSync);
- bool terminationRequested(DatabaseTaskSynchronizer* taskSynchronizer = 0) const;
+ bool terminationRequested(DatabaseTaskSynchronizer* = nullptr) const;
- void scheduleTask(std::unique_ptr<DatabaseTask>);
- void scheduleImmediateTask(std::unique_ptr<DatabaseTask>); // This just adds the task to the front of the queue - the caller needs to be extremely careful not to create deadlocks when waiting for completion.
- void unscheduleDatabaseTasks(DatabaseBackend*);
+ void scheduleTask(std::unique_ptr<DatabaseTask>&&);
+ void scheduleImmediateTask(std::unique_ptr<DatabaseTask>&&); // This just adds the task to the front of the queue - the caller needs to be extremely careful not to create deadlocks when waiting for completion.
+ void unscheduleDatabaseTasks(Database&);
+ bool hasPendingDatabaseActivity() const;
- void recordDatabaseOpen(DatabaseBackend*);
- void recordDatabaseClosed(DatabaseBackend*);
+ void recordDatabaseOpen(Database&);
+ void recordDatabaseClosed(Database&);
ThreadIdentifier getThreadID() { return m_threadID; }
- SQLTransactionClient* transactionClient() { return m_transactionClient.get(); }
SQLTransactionCoordinator* transactionCoordinator() { return m_transactionCoordinator.get(); }
-#if PLATFORM(IOS)
- void setPaused(bool);
- void handlePausedQueue();
-#endif
-
private:
DatabaseThread();
static void databaseThreadStart(void*);
void databaseThread();
- Mutex m_threadCreationMutex;
- ThreadIdentifier m_threadID;
+ Lock m_threadCreationMutex;
+ ThreadIdentifier m_threadID { 0 };
RefPtr<DatabaseThread> m_selfRef;
MessageQueue<DatabaseTask> m_queue;
-#if PLATFORM(IOS)
- MessageQueue<DatabaseTask> m_pausedQueue;
- Mutex m_pausedMutex;
- volatile bool m_paused;
-#endif
// This set keeps track of the open databases that have been used on this thread.
- typedef HashSet<RefPtr<DatabaseBackend>> DatabaseSet;
+ using DatabaseSet = HashSet<RefPtr<Database>>;
+ mutable Lock m_openDatabaseSetMutex;
DatabaseSet m_openDatabaseSet;
- OwnPtr<SQLTransactionClient> m_transactionClient;
- OwnPtr<SQLTransactionCoordinator> m_transactionCoordinator;
- DatabaseTaskSynchronizer* m_cleanupSync;
+ std::unique_ptr<SQLTransactionCoordinator> m_transactionCoordinator;
+ DatabaseTaskSynchronizer* m_cleanupSync { nullptr };
};
} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
-#endif // DatabaseThread_h
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseTracker.cpp b/Source/WebCore/Modules/webdatabase/DatabaseTracker.cpp
index 8d3fa13a0..437097717 100644
--- a/Source/WebCore/Modules/webdatabase/DatabaseTracker.cpp
+++ b/Source/WebCore/Modules/webdatabase/DatabaseTracker.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.
*
@@ -29,27 +29,26 @@
#include "config.h"
#include "DatabaseTracker.h"
-#if ENABLE(SQL_DATABASE)
-
-#include "Chrome.h"
-#include "ChromeClient.h"
#include "Database.h"
-#include "DatabaseBackendBase.h"
-#include "DatabaseBackendContext.h"
+#include "DatabaseContext.h"
#include "DatabaseManager.h"
#include "DatabaseManagerClient.h"
#include "DatabaseThread.h"
+#include "ExceptionCode.h"
#include "FileSystem.h"
#include "Logging.h"
#include "OriginLock.h"
-#include "Page.h"
#include "SecurityOrigin.h"
+#include "SecurityOriginData.h"
#include "SecurityOriginHash.h"
#include "SQLiteFileSystem.h"
#include "SQLiteStatement.h"
+#include "UUID.h"
#include <wtf/MainThread.h>
+#include <wtf/NeverDestroyed.h>
#include <wtf/StdLibExtras.h>
#include <wtf/text/CString.h>
+#include <wtf/text/StringBuilder.h>
#if PLATFORM(IOS)
#include "WebCoreThread.h"
@@ -57,41 +56,40 @@
namespace WebCore {
-static DatabaseTracker* staticTracker = 0;
+static Vector<String> isolatedCopy(const Vector<String>& original)
+{
+ Vector<String> copy;
+ copy.reserveInitialCapacity(original.size());
+ for (auto& string : original)
+ copy.uncheckedAppend(string.isolatedCopy());
+ return copy;
+}
+
+std::unique_ptr<DatabaseTracker> DatabaseTracker::trackerWithDatabasePath(const String& databasePath)
+{
+ return std::unique_ptr<DatabaseTracker>(new DatabaseTracker(databasePath));
+}
+
+static DatabaseTracker* staticTracker = nullptr;
void DatabaseTracker::initializeTracker(const String& databasePath)
{
ASSERT(!staticTracker);
if (staticTracker)
return;
-
staticTracker = new DatabaseTracker(databasePath);
}
-DatabaseTracker& DatabaseTracker::tracker()
+DatabaseTracker& DatabaseTracker::singleton()
{
if (!staticTracker)
- staticTracker = new DatabaseTracker("");
-
+ staticTracker = new DatabaseTracker(emptyString());
return *staticTracker;
}
DatabaseTracker::DatabaseTracker(const String& databasePath)
- : m_client(0)
+ : m_databaseDirectoryPath(databasePath.isolatedCopy())
{
- setDatabaseDirectoryPath(databasePath);
-}
-
-void DatabaseTracker::setDatabaseDirectoryPath(const String& path)
-{
- MutexLocker lockDatabase(m_databaseGuard);
- ASSERT(!m_database.isOpen());
- m_databaseDirectoryPath = path.isolatedCopy();
-}
-
-String DatabaseTracker::databaseDirectoryPath() const
-{
- return m_databaseDirectoryPath.isolatedCopy();
}
String DatabaseTracker::trackerDatabasePath() const
@@ -118,7 +116,7 @@ void DatabaseTracker::openTrackerDatabase(TrackerCreationAction createAction)
if (!m_database.open(databasePath)) {
// FIXME: What do do here?
- LOG_ERROR("Failed to open databasePath %s.", databasePath.ascii().data());
+ LOG_ERROR("Failed to open databasePath %s.", databasePath.utf8().data());
return;
}
m_database.disableThreadingChecks();
@@ -138,47 +136,41 @@ void DatabaseTracker::openTrackerDatabase(TrackerCreationAction createAction)
}
}
-bool DatabaseTracker::hasAdequateQuotaForOrigin(SecurityOrigin* origin, unsigned long estimatedSize, DatabaseError& err)
+ExceptionOr<void> DatabaseTracker::hasAdequateQuotaForOrigin(const SecurityOriginData& origin, unsigned estimatedSize)
{
ASSERT(!m_databaseGuard.tryLock());
- unsigned long long usage = usageForOrigin(origin);
+ auto usage = this->usage(origin);
// If the database will fit, allow its creation.
- unsigned long long requirement = usage + std::max<unsigned long long>(1, estimatedSize);
+ auto requirement = usage + std::max<unsigned long long>(1, estimatedSize);
if (requirement < usage) {
// The estimated size is so big it causes an overflow; don't allow creation.
- err = DatabaseError::DatabaseSizeOverflowed;
- return false;
+ return Exception { SECURITY_ERR };
}
- if (requirement <= quotaForOriginNoLock(origin))
- return true;
-
- err = DatabaseError::DatabaseSizeExceededQuota;
- return false;
+ if (requirement > quotaNoLock(origin))
+ return Exception { QUOTA_EXCEEDED_ERR };
+ return { };
}
-bool DatabaseTracker::canEstablishDatabase(DatabaseBackendContext* context, const String& name, unsigned long estimatedSize, DatabaseError& error)
+ExceptionOr<void> DatabaseTracker::canEstablishDatabase(DatabaseContext& context, const String& name, unsigned estimatedSize)
{
- error = DatabaseError::None;
+ LockHolder lockDatabase(m_databaseGuard);
- MutexLocker lockDatabase(m_databaseGuard);
- SecurityOrigin* origin = context->securityOrigin();
+ // FIXME: What guarantees this context.securityOrigin() is non-null?
+ auto origin = context.securityOrigin();
- if (isDeletingDatabaseOrOriginFor(origin, name)) {
- error = DatabaseError::DatabaseIsBeingDeleted;
- return false;
- }
+ if (isDeletingDatabaseOrOriginFor(origin, name))
+ return Exception { SECURITY_ERR };
recordCreatingDatabase(origin, name);
// If a database already exists, ignore the passed-in estimated size and say it's OK.
if (hasEntryForDatabase(origin, name))
- return true;
+ return { };
- if (hasAdequateQuotaForOrigin(origin, estimatedSize, error)) {
- ASSERT(error == DatabaseError::None);
- return true;
- }
+ auto result = hasAdequateQuotaForOrigin(origin, estimatedSize);
+ if (!result.hasException())
+ return { };
// If we get here, then we do not have enough quota for one of the
// following reasons as indicated by the set error:
@@ -193,12 +185,11 @@ bool DatabaseTracker::canEstablishDatabase(DatabaseBackendContext* context, cons
// a chance to update the quota and call retryCanEstablishDatabase() to try
// again. Hence, we don't call doneCreatingDatabase() yet in that case.
- if (error == DatabaseError::DatabaseSizeOverflowed)
+ auto exception = result.releaseException();
+ if (exception.code() != QUOTA_EXCEEDED_ERR)
doneCreatingDatabase(origin, name);
- else
- ASSERT(error == DatabaseError::DatabaseSizeExceededQuota);
- return false;
+ return WTFMove(exception);
}
// Note: a thought about performance: hasAdequateQuotaForOrigin() was also
@@ -209,30 +200,30 @@ bool DatabaseTracker::canEstablishDatabase(DatabaseBackendContext* context, cons
// hasAdequateQuotaForOrigin() simple and correct (i.e. bug free), and just
// re-use it. Also note that the path for opening a database involves IO, and
// hence should not be a performance critical path anyway.
-bool DatabaseTracker::retryCanEstablishDatabase(DatabaseBackendContext* context, const String& name, unsigned long estimatedSize, DatabaseError& error)
+ExceptionOr<void> DatabaseTracker::retryCanEstablishDatabase(DatabaseContext& context, const String& name, unsigned estimatedSize)
{
- error = DatabaseError::None;
+ LockHolder lockDatabase(m_databaseGuard);
- MutexLocker lockDatabase(m_databaseGuard);
- SecurityOrigin* origin = context->securityOrigin();
+ // FIXME: What guarantees context.securityOrigin() is non-null?
+ auto origin = context.securityOrigin();
// We have already eliminated other types of errors in canEstablishDatabase().
// The only reason we're in retryCanEstablishDatabase() is because we gave
// the client a chance to update the quota and are rechecking it here.
// If we fail this check, the only possible reason this time should be due
// to inadequate quota.
- if (hasAdequateQuotaForOrigin(origin, estimatedSize, error)) {
- ASSERT(error == DatabaseError::None);
- return true;
- }
+ auto result = hasAdequateQuotaForOrigin(origin, estimatedSize);
+ if (!result.hasException())
+ return { };
- ASSERT(error == DatabaseError::DatabaseSizeExceededQuota);
+ auto exception = result.releaseException();
+ ASSERT(exception.code() == QUOTA_EXCEEDED_ERR);
doneCreatingDatabase(origin, name);
- return false;
+ return WTFMove(exception);
}
-bool DatabaseTracker::hasEntryForOriginNoLock(SecurityOrigin* origin)
+bool DatabaseTracker::hasEntryForOriginNoLock(const SecurityOriginData& origin)
{
ASSERT(!m_databaseGuard.tryLock());
openTrackerDatabase(DontCreateIfDoesNotExist);
@@ -240,23 +231,17 @@ bool DatabaseTracker::hasEntryForOriginNoLock(SecurityOrigin* origin)
return false;
SQLiteStatement statement(m_database, "SELECT origin FROM Origins where origin=?;");
- if (statement.prepare() != SQLResultOk) {
+ if (statement.prepare() != SQLITE_OK) {
LOG_ERROR("Failed to prepare statement.");
return false;
}
- statement.bindText(1, origin->databaseIdentifier());
-
- return statement.step() == SQLResultRow;
-}
+ statement.bindText(1, origin.databaseIdentifier());
-bool DatabaseTracker::hasEntryForOrigin(SecurityOrigin* origin)
-{
- MutexLocker lockDatabase(m_databaseGuard);
- return hasEntryForOriginNoLock(origin);
+ return statement.step() == SQLITE_ROW;
}
-bool DatabaseTracker::hasEntryForDatabase(SecurityOrigin* origin, const String& databaseIdentifier)
+bool DatabaseTracker::hasEntryForDatabase(const SecurityOriginData& origin, const String& databaseIdentifier)
{
ASSERT(!m_databaseGuard.tryLock());
openTrackerDatabase(DontCreateIfDoesNotExist);
@@ -268,25 +253,25 @@ bool DatabaseTracker::hasEntryForDatabase(SecurityOrigin* origin, const String&
// We've got a tracker database. Set up a query to ask for the db of interest:
SQLiteStatement statement(m_database, "SELECT guid FROM Databases WHERE origin=? AND name=?;");
- if (statement.prepare() != SQLResultOk)
+ if (statement.prepare() != SQLITE_OK)
return false;
- statement.bindText(1, origin->databaseIdentifier());
+ statement.bindText(1, origin.databaseIdentifier());
statement.bindText(2, databaseIdentifier);
- return statement.step() == SQLResultRow;
+ return statement.step() == SQLITE_ROW;
}
-unsigned long long DatabaseTracker::getMaxSizeForDatabase(const DatabaseBackendBase* database)
+unsigned long long DatabaseTracker::maximumSize(Database& database)
{
// The maximum size for a database is the full quota for its origin, minus the current usage within the origin,
// plus the current usage of the given database
- MutexLocker lockDatabase(m_databaseGuard);
- SecurityOrigin* origin = database->securityOrigin();
+ LockHolder lockDatabase(m_databaseGuard);
+ auto origin = database.securityOrigin();
- unsigned long long quota = quotaForOriginNoLock(origin);
- unsigned long long diskUsage = usageForOrigin(origin);
- unsigned long long databaseFileSize = SQLiteFileSystem::getDatabaseFileSize(database->fileName());
+ unsigned long long quota = quotaNoLock(origin);
+ unsigned long long diskUsage = usage(origin);
+ unsigned long long databaseFileSize = SQLiteFileSystem::getDatabaseFileSize(database.fileName());
ASSERT(databaseFileSize <= diskUsage);
if (diskUsage > quota)
@@ -302,45 +287,47 @@ unsigned long long DatabaseTracker::getMaxSizeForDatabase(const DatabaseBackendB
return maxSize;
}
-void DatabaseTracker::interruptAllDatabasesForContext(const DatabaseBackendContext* context)
+void DatabaseTracker::closeAllDatabases(CurrentQueryBehavior currentQueryBehavior)
{
- Vector<RefPtr<DatabaseBackendBase>> openDatabases;
+ Vector<Ref<Database>> openDatabases;
{
- MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
-
+ LockHolder openDatabaseMapLock(m_openDatabaseMapGuard);
if (!m_openDatabaseMap)
return;
-
- DatabaseNameMap* nameMap = m_openDatabaseMap->get(context->securityOrigin());
- if (!nameMap)
- return;
-
- DatabaseNameMap::const_iterator dbNameMapEndIt = nameMap->end();
- for (DatabaseNameMap::const_iterator dbNameMapIt = nameMap->begin(); dbNameMapIt != dbNameMapEndIt; ++dbNameMapIt) {
- DatabaseSet* databaseSet = dbNameMapIt->value;
- DatabaseSet::const_iterator dbSetEndIt = databaseSet->end();
- for (DatabaseSet::const_iterator dbSetIt = databaseSet->begin(); dbSetIt != dbSetEndIt; ++dbSetIt) {
- if ((*dbSetIt)->databaseContext() == context)
- openDatabases.append(*dbSetIt);
+ for (auto& nameMap : m_openDatabaseMap->values()) {
+ for (auto& set : nameMap->values()) {
+ for (auto& database : *set)
+ openDatabases.append(*database);
}
}
}
+ for (auto& database : openDatabases) {
+ if (currentQueryBehavior == CurrentQueryBehavior::Interrupt)
+ database->interrupt();
+ database->close();
+ }
+}
- Vector<RefPtr<DatabaseBackendBase>>::const_iterator openDatabasesEndIt = openDatabases.end();
- for (Vector<RefPtr<DatabaseBackendBase>>::const_iterator openDatabasesIt = openDatabases.begin(); openDatabasesIt != openDatabasesEndIt; ++openDatabasesIt)
- (*openDatabasesIt)->interrupt();
+String DatabaseTracker::originPath(const SecurityOriginData& origin) const
+{
+ return SQLiteFileSystem::appendDatabaseFileNameToPath(m_databaseDirectoryPath.isolatedCopy(), origin.databaseIdentifier());
}
-String DatabaseTracker::originPath(SecurityOrigin* origin) const
+static String generateDatabaseFileName()
{
- return SQLiteFileSystem::appendDatabaseFileNameToPath(m_databaseDirectoryPath.isolatedCopy(), origin->databaseIdentifier());
+ StringBuilder stringBuilder;
+
+ stringBuilder.append(createCanonicalUUIDString());
+ stringBuilder.appendLiteral(".db");
+
+ return stringBuilder.toString();
}
-String DatabaseTracker::fullPathForDatabaseNoLock(SecurityOrigin* origin, const String& name, bool createIfNotExists)
+String DatabaseTracker::fullPathForDatabaseNoLock(const SecurityOriginData& origin, const String& name, bool createIfNotExists)
{
ASSERT(!m_databaseGuard.tryLock());
- String originIdentifier = origin->databaseIdentifier();
+ String originIdentifier = origin.databaseIdentifier();
String originPath = this->originPath(origin);
// Make sure the path for this SecurityOrigin exists
@@ -352,7 +339,7 @@ String DatabaseTracker::fullPathForDatabaseNoLock(SecurityOrigin* origin, const
return String();
SQLiteStatement statement(m_database, "SELECT path FROM Databases WHERE origin=? AND name=?;");
- if (statement.prepare() != SQLResultOk)
+ if (statement.prepare() != SQLITE_OK)
return String();
statement.bindText(1, originIdentifier);
@@ -360,18 +347,19 @@ String DatabaseTracker::fullPathForDatabaseNoLock(SecurityOrigin* origin, const
int result = statement.step();
- if (result == SQLResultRow)
+ if (result == SQLITE_ROW)
return SQLiteFileSystem::appendDatabaseFileNameToPath(originPath, statement.getColumnText(0));
if (!createIfNotExists)
return String();
- if (result != SQLResultDone) {
- LOG_ERROR("Failed to retrieve filename from Database Tracker for origin %s, name %s", originIdentifier.ascii().data(), name.ascii().data());
+ if (result != SQLITE_DONE) {
+ LOG_ERROR("Failed to retrieve filename from Database Tracker for origin %s, name %s", originIdentifier.utf8().data(), name.utf8().data());
return String();
}
statement.finalize();
- String fileName = SQLiteFileSystem::getFileNameForNewDatabase(originPath, name, originIdentifier, &m_database);
+ String fileName = generateDatabaseFileName();
+
if (!addDatabase(origin, name, fileName))
return String();
@@ -382,102 +370,100 @@ String DatabaseTracker::fullPathForDatabaseNoLock(SecurityOrigin* origin, const
return fullFilePath;
}
-String DatabaseTracker::fullPathForDatabase(SecurityOrigin* origin, const String& name, bool createIfNotExists)
+String DatabaseTracker::fullPathForDatabase(const SecurityOriginData& origin, const String& name, bool createIfNotExists)
{
- MutexLocker lockDatabase(m_databaseGuard);
+ LockHolder lockDatabase(m_databaseGuard);
return fullPathForDatabaseNoLock(origin, name, createIfNotExists).isolatedCopy();
}
-void DatabaseTracker::origins(Vector<RefPtr<SecurityOrigin>>& originsResult)
+Vector<SecurityOriginData> DatabaseTracker::origins()
{
- MutexLocker lockDatabase(m_databaseGuard);
+ LockHolder lockDatabase(m_databaseGuard);
openTrackerDatabase(DontCreateIfDoesNotExist);
if (!m_database.isOpen())
- return;
+ return { };
SQLiteStatement statement(m_database, "SELECT origin FROM Origins");
- if (statement.prepare() != SQLResultOk) {
+ if (statement.prepare() != SQLITE_OK) {
LOG_ERROR("Failed to prepare statement.");
- return;
+ return { };
}
- int result;
- while ((result = statement.step()) == SQLResultRow) {
- RefPtr<SecurityOrigin> origin = SecurityOrigin::createFromDatabaseIdentifier(statement.getColumnText(0));
- originsResult.append(origin->isolatedCopy());
- }
- originsResult.shrinkToFit();
+ Vector<SecurityOriginData> origins;
+ int stepResult;
+ while ((stepResult = statement.step()) == SQLITE_ROW)
+ origins.append(SecurityOriginData::fromDatabaseIdentifier(statement.getColumnText(0))->isolatedCopy());
+ origins.shrinkToFit();
- if (result != SQLResultDone)
+ if (stepResult != SQLITE_DONE)
LOG_ERROR("Failed to read in all origins from the database.");
+
+ return origins;
}
-bool DatabaseTracker::databaseNamesForOriginNoLock(SecurityOrigin* origin, Vector<String>& resultVector)
+Vector<String> DatabaseTracker::databaseNamesNoLock(const SecurityOriginData& origin)
{
ASSERT(!m_databaseGuard.tryLock());
openTrackerDatabase(DontCreateIfDoesNotExist);
if (!m_database.isOpen())
- return false;
+ return { };
SQLiteStatement statement(m_database, "SELECT name FROM Databases where origin=?;");
+ if (statement.prepare() != SQLITE_OK)
+ return { };
- if (statement.prepare() != SQLResultOk)
- return false;
-
- statement.bindText(1, origin->databaseIdentifier());
+ statement.bindText(1, origin.databaseIdentifier());
+ Vector<String> names;
int result;
- while ((result = statement.step()) == SQLResultRow)
- resultVector.append(statement.getColumnText(0));
+ while ((result = statement.step()) == SQLITE_ROW)
+ names.append(statement.getColumnText(0));
+ names.shrinkToFit();
- if (result != SQLResultDone) {
- LOG_ERROR("Failed to retrieve all database names for origin %s", origin->databaseIdentifier().ascii().data());
- return false;
+ if (result != SQLITE_DONE) {
+ LOG_ERROR("Failed to retrieve all database names for origin %s", origin.databaseIdentifier().utf8().data());
+ return { };
}
- return true;
+ return names;
}
-bool DatabaseTracker::databaseNamesForOrigin(SecurityOrigin* origin, Vector<String>& resultVector)
+Vector<String> DatabaseTracker::databaseNames(const SecurityOriginData& origin)
{
- Vector<String> temp;
+ Vector<String> names;
{
- MutexLocker lockDatabase(m_databaseGuard);
- if (!databaseNamesForOriginNoLock(origin, temp))
- return false;
+ LockHolder lockDatabase(m_databaseGuard);
+ names = databaseNamesNoLock(origin);
}
-
- for (Vector<String>::iterator iter = temp.begin(); iter != temp.end(); ++iter)
- resultVector.append(iter->isolatedCopy());
- return true;
+ return isolatedCopy(names);
}
-DatabaseDetails DatabaseTracker::detailsForNameAndOrigin(const String& name, SecurityOrigin* origin)
+DatabaseDetails DatabaseTracker::detailsForNameAndOrigin(const String& name, const SecurityOriginData& origin)
{
- String originIdentifier = origin->databaseIdentifier();
+ String originIdentifier = origin.databaseIdentifier();
String displayName;
int64_t expectedUsage;
{
- MutexLocker lockDatabase(m_databaseGuard);
+ LockHolder lockDatabase(m_databaseGuard);
openTrackerDatabase(DontCreateIfDoesNotExist);
if (!m_database.isOpen())
return DatabaseDetails();
SQLiteStatement statement(m_database, "SELECT displayName, estimatedSize FROM Databases WHERE origin=? AND name=?");
- if (statement.prepare() != SQLResultOk)
+ if (statement.prepare() != SQLITE_OK)
return DatabaseDetails();
statement.bindText(1, originIdentifier);
statement.bindText(2, name);
int result = statement.step();
- if (result == SQLResultDone)
+ if (result == SQLITE_DONE)
return DatabaseDetails();
- if (result != SQLResultRow) {
- LOG_ERROR("Error retrieving details for database %s in origin %s from tracker database", name.ascii().data(), originIdentifier.ascii().data());
+ if (result != SQLITE_ROW) {
+ LOG_ERROR("Error retrieving details for database %s in origin %s from tracker database", name.utf8().data(), originIdentifier.utf8().data());
return DatabaseDetails();
}
displayName = statement.getColumnText(0);
@@ -490,51 +476,50 @@ DatabaseDetails DatabaseTracker::detailsForNameAndOrigin(const String& name, Sec
return DatabaseDetails(name, displayName, expectedUsage, SQLiteFileSystem::getDatabaseFileSize(path), SQLiteFileSystem::databaseCreationTime(path), SQLiteFileSystem::databaseModificationTime(path));
}
-void DatabaseTracker::setDatabaseDetails(SecurityOrigin* origin, const String& name, const String& displayName, unsigned long estimatedSize)
+void DatabaseTracker::setDatabaseDetails(const SecurityOriginData& origin, const String& name, const String& displayName, unsigned estimatedSize)
{
- String originIdentifier = origin->databaseIdentifier();
+ String originIdentifier = origin.databaseIdentifier();
int64_t guid = 0;
- MutexLocker lockDatabase(m_databaseGuard);
+ LockHolder lockDatabase(m_databaseGuard);
openTrackerDatabase(CreateIfDoesNotExist);
if (!m_database.isOpen())
return;
SQLiteStatement statement(m_database, "SELECT guid FROM Databases WHERE origin=? AND name=?");
- if (statement.prepare() != SQLResultOk)
+ if (statement.prepare() != SQLITE_OK)
return;
statement.bindText(1, originIdentifier);
statement.bindText(2, name);
int result = statement.step();
- if (result == SQLResultRow)
+ if (result == SQLITE_ROW)
guid = statement.getColumnInt64(0);
statement.finalize();
if (guid == 0) {
- if (result != SQLResultDone)
- LOG_ERROR("Error to determing existence of database %s in origin %s in tracker database", name.ascii().data(), originIdentifier.ascii().data());
+ if (result != SQLITE_DONE)
+ LOG_ERROR("Error to determing existence of database %s in origin %s in tracker database", name.utf8().data(), originIdentifier.utf8().data());
else {
// This case should never occur - we should never be setting database details for a database that doesn't already exist in the tracker
// But since the tracker file is an external resource not under complete control of our code, it's somewhat invalid to make this an ASSERT case
// So we'll print an error instead
- LOG_ERROR("Could not retrieve guid for database %s in origin %s from the tracker database - it is invalid to set database details on a database that doesn't already exist in the tracker",
- name.ascii().data(), originIdentifier.ascii().data());
+ LOG_ERROR("Could not retrieve guid for database %s in origin %s from the tracker database - it is invalid to set database details on a database that doesn't already exist in the tracker", name.utf8().data(), originIdentifier.utf8().data());
}
return;
}
SQLiteStatement updateStatement(m_database, "UPDATE Databases SET displayName=?, estimatedSize=? WHERE guid=?");
- if (updateStatement.prepare() != SQLResultOk)
+ if (updateStatement.prepare() != SQLITE_OK)
return;
updateStatement.bindText(1, displayName);
updateStatement.bindInt64(2, estimatedSize);
updateStatement.bindInt64(3, guid);
- if (updateStatement.step() != SQLResultDone) {
- LOG_ERROR("Failed to update details for database %s in origin %s", name.ascii().data(), originIdentifier.ascii().data());
+ if (updateStatement.step() != SQLITE_DONE) {
+ LOG_ERROR("Failed to update details for database %s in origin %s", name.utf8().data(), originIdentifier.utf8().data());
return;
}
@@ -542,108 +527,82 @@ void DatabaseTracker::setDatabaseDetails(SecurityOrigin* origin, const String& n
m_client->dispatchDidModifyDatabase(origin, name);
}
-void DatabaseTracker::doneCreatingDatabase(DatabaseBackendBase* database)
+void DatabaseTracker::doneCreatingDatabase(Database& database)
{
- MutexLocker lockDatabase(m_databaseGuard);
- doneCreatingDatabase(database->securityOrigin(), database->stringIdentifier());
+ LockHolder lockDatabase(m_databaseGuard);
+ doneCreatingDatabase(database.securityOrigin(), database.stringIdentifier());
}
-void DatabaseTracker::addOpenDatabase(DatabaseBackendBase* database)
+void DatabaseTracker::addOpenDatabase(Database& database)
{
- if (!database)
- return;
+ LockHolder openDatabaseMapLock(m_openDatabaseMapGuard);
- {
- MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
+ if (!m_openDatabaseMap)
+ m_openDatabaseMap = std::make_unique<DatabaseOriginMap>();
- if (!m_openDatabaseMap)
- m_openDatabaseMap = adoptPtr(new DatabaseOriginMap);
+ auto origin = database.securityOrigin();
- String name(database->stringIdentifier());
- DatabaseNameMap* nameMap = m_openDatabaseMap->get(database->securityOrigin());
- if (!nameMap) {
- nameMap = new DatabaseNameMap;
- m_openDatabaseMap->set(database->securityOrigin()->isolatedCopy(), nameMap);
- }
+ auto* nameMap = m_openDatabaseMap->get(origin);
+ if (!nameMap) {
+ nameMap = new DatabaseNameMap;
+ m_openDatabaseMap->add(origin.isolatedCopy(), nameMap);
+ }
- DatabaseSet* databaseSet = nameMap->get(name);
- if (!databaseSet) {
- databaseSet = new DatabaseSet;
- nameMap->set(name.isolatedCopy(), databaseSet);
- }
+ String name = database.stringIdentifier();
+ auto* databaseSet = nameMap->get(name);
+ if (!databaseSet) {
+ databaseSet = new DatabaseSet;
+ nameMap->set(name.isolatedCopy(), databaseSet);
+ }
- databaseSet->add(database);
+ databaseSet->add(&database);
- LOG(StorageAPI, "Added open Database %s (%p)\n", database->stringIdentifier().ascii().data(), database);
- }
+ LOG(StorageAPI, "Added open Database %s (%p)\n", database.stringIdentifier().utf8().data(), &database);
}
-void DatabaseTracker::removeOpenDatabase(DatabaseBackendBase* database)
+void DatabaseTracker::removeOpenDatabase(Database& database)
{
- if (!database)
- return;
+ LockHolder openDatabaseMapLock(m_openDatabaseMapGuard);
- {
- MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
-
- if (!m_openDatabaseMap) {
- ASSERT_NOT_REACHED();
- return;
- }
-
- String name(database->stringIdentifier());
- DatabaseNameMap* nameMap = m_openDatabaseMap->get(database->securityOrigin());
- if (!nameMap) {
- ASSERT_NOT_REACHED();
- return;
- }
-
- DatabaseSet* databaseSet = nameMap->get(name);
- if (!databaseSet) {
- ASSERT_NOT_REACHED();
- return;
- }
-
- databaseSet->remove(database);
-
- LOG(StorageAPI, "Removed open Database %s (%p)\n", database->stringIdentifier().ascii().data(), database);
+ if (!m_openDatabaseMap) {
+ ASSERT_NOT_REACHED();
+ return;
+ }
- if (!databaseSet->isEmpty())
- return;
+ DatabaseNameMap* nameMap = m_openDatabaseMap->get(database.securityOrigin());
+ if (!nameMap) {
+ ASSERT_NOT_REACHED();
+ return;
+ }
- nameMap->remove(name);
- delete databaseSet;
+ String name = database.stringIdentifier();
+ auto* databaseSet = nameMap->get(name);
+ if (!databaseSet) {
+ ASSERT_NOT_REACHED();
+ return;
+ }
- if (!nameMap->isEmpty())
- return;
+ databaseSet->remove(&database);
- m_openDatabaseMap->remove(database->securityOrigin());
- delete nameMap;
- }
-}
+ LOG(StorageAPI, "Removed open Database %s (%p)\n", database.stringIdentifier().utf8().data(), &database);
-void DatabaseTracker::getOpenDatabases(SecurityOrigin* origin, const String& name, HashSet<RefPtr<DatabaseBackendBase>>* databases)
-{
- MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
- if (!m_openDatabaseMap)
+ if (!databaseSet->isEmpty())
return;
- DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin);
- if (!nameMap)
- return;
+ nameMap->remove(name);
+ delete databaseSet;
- DatabaseSet* databaseSet = nameMap->get(name);
- if (!databaseSet)
+ if (!nameMap->isEmpty())
return;
- for (DatabaseSet::iterator it = databaseSet->begin(); it != databaseSet->end(); ++it)
- databases->add(*it);
+ m_openDatabaseMap->remove(database.securityOrigin());
+ delete nameMap;
}
-PassRefPtr<OriginLock> DatabaseTracker::originLockFor(SecurityOrigin* origin)
+RefPtr<OriginLock> DatabaseTracker::originLockFor(const SecurityOriginData& origin)
{
- MutexLocker lockDatabase(m_databaseGuard);
- String databaseIdentifier = origin->databaseIdentifier();
+ LockHolder lockDatabase(m_databaseGuard);
+ String databaseIdentifier = origin.databaseIdentifier();
// The originLockMap is accessed from multiple DatabaseThreads since
// different script contexts can be writing to different databases from
@@ -658,14 +617,14 @@ PassRefPtr<OriginLock> DatabaseTracker::originLockFor(SecurityOrigin* origin)
return addResult.iterator->value;
String path = originPath(origin);
- RefPtr<OriginLock> lock = adoptRef(new OriginLock(path));
+ RefPtr<OriginLock> lock = adoptRef(*new OriginLock(path));
ASSERT(lock);
addResult.iterator->value = lock;
- return lock.release();
+ return lock;
}
-void DatabaseTracker::deleteOriginLockFor(SecurityOrigin* origin)
+void DatabaseTracker::deleteOriginLockFor(const SecurityOriginData& origin)
{
ASSERT(!m_databaseGuard.tryLock());
@@ -680,29 +639,25 @@ void DatabaseTracker::deleteOriginLockFor(SecurityOrigin* origin)
// files in this origin. We'll give the OriginLock one chance to do an
// orderly clean up first when we remove its ref from the m_originLockMap.
// This may or may not be possible depending on whether other threads are
- // also using the OriginLock at the same time. After that, we will go ahead
- // and delete the lock file.
+ // also using the OriginLock at the same time. After that, we will delete the lock file.
- m_originLockMap.remove(origin->databaseIdentifier());
+ m_originLockMap.remove(origin.databaseIdentifier());
OriginLock::deleteLockFile(originPath(origin));
}
-unsigned long long DatabaseTracker::usageForOrigin(SecurityOrigin* origin)
+unsigned long long DatabaseTracker::usage(const SecurityOriginData& origin)
{
String originPath = this->originPath(origin);
unsigned long long diskUsage = 0;
- Vector<String> fileNames = listDirectory(originPath, String("*.db"));
- Vector<String>::iterator fileName = fileNames.begin();
- Vector<String>::iterator lastFileName = fileNames.end();
- for (; fileName != lastFileName; ++fileName) {
+ for (auto& fileName : listDirectory(originPath, ASCIILiteral("*.db"))) {
long long size;
- getFileSize(*fileName, size);
+ getFileSize(fileName, size);
diskUsage += size;
}
return diskUsage;
}
-unsigned long long DatabaseTracker::quotaForOriginNoLock(SecurityOrigin* origin)
+unsigned long long DatabaseTracker::quotaNoLock(const SecurityOriginData& origin)
{
ASSERT(!m_databaseGuard.tryLock());
unsigned long long quota = 0;
@@ -712,82 +667,73 @@ unsigned long long DatabaseTracker::quotaForOriginNoLock(SecurityOrigin* origin)
return quota;
SQLiteStatement statement(m_database, "SELECT quota FROM Origins where origin=?;");
- if (statement.prepare() != SQLResultOk) {
+ if (statement.prepare() != SQLITE_OK) {
LOG_ERROR("Failed to prepare statement.");
return quota;
}
- statement.bindText(1, origin->databaseIdentifier());
+ statement.bindText(1, origin.databaseIdentifier());
- if (statement.step() == SQLResultRow)
+ if (statement.step() == SQLITE_ROW)
quota = statement.getColumnInt64(0);
return quota;
}
-unsigned long long DatabaseTracker::quotaForOrigin(SecurityOrigin* origin)
+unsigned long long DatabaseTracker::quota(const SecurityOriginData& origin)
{
- MutexLocker lockDatabase(m_databaseGuard);
- return quotaForOriginNoLock(origin);
+ LockHolder lockDatabase(m_databaseGuard);
+ return quotaNoLock(origin);
}
-void DatabaseTracker::setQuota(SecurityOrigin* origin, unsigned long long quota)
+void DatabaseTracker::setQuota(const SecurityOriginData& origin, unsigned long long quota)
{
- MutexLocker lockDatabase(m_databaseGuard);
+ LockHolder lockDatabase(m_databaseGuard);
- if (quotaForOriginNoLock(origin) == quota)
+ if (quotaNoLock(origin) == quota)
return;
openTrackerDatabase(CreateIfDoesNotExist);
if (!m_database.isOpen())
return;
-#if PLATFORM(IOS)
bool insertedNewOrigin = false;
-#endif
bool originEntryExists = hasEntryForOriginNoLock(origin);
if (!originEntryExists) {
SQLiteStatement statement(m_database, "INSERT INTO Origins VALUES (?, ?)");
- if (statement.prepare() != SQLResultOk) {
- LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data());
+ if (statement.prepare() != SQLITE_OK) {
+ LOG_ERROR("Unable to establish origin %s in the tracker", origin.databaseIdentifier().utf8().data());
} else {
- statement.bindText(1, origin->databaseIdentifier());
+ statement.bindText(1, origin.databaseIdentifier());
statement.bindInt64(2, quota);
- if (statement.step() != SQLResultDone)
- LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data());
-#if PLATFORM(IOS)
+ if (statement.step() != SQLITE_DONE)
+ LOG_ERROR("Unable to establish origin %s in the tracker", origin.databaseIdentifier().utf8().data());
else
insertedNewOrigin = true;
-#endif
}
} else {
SQLiteStatement statement(m_database, "UPDATE Origins SET quota=? WHERE origin=?");
- bool error = statement.prepare() != SQLResultOk;
+ bool error = statement.prepare() != SQLITE_OK;
if (!error) {
statement.bindInt64(1, quota);
- statement.bindText(2, origin->databaseIdentifier());
+ statement.bindText(2, origin.databaseIdentifier());
error = !statement.executeCommand();
}
if (error)
- LOG_ERROR("Failed to set quota %llu in tracker database for origin %s", quota, origin->databaseIdentifier().ascii().data());
+ LOG_ERROR("Failed to set quota %llu in tracker database for origin %s", quota, origin.databaseIdentifier().utf8().data());
}
- if (m_client)
-#if PLATFORM(IOS)
- {
+ if (m_client) {
if (insertedNewOrigin)
- m_client->dispatchDidAddNewOrigin(origin);
-#endif
+ m_client->dispatchDidAddNewOrigin();
m_client->dispatchDidModifyOrigin(origin);
-#if PLATFORM(IOS)
}
-#endif
}
-bool DatabaseTracker::addDatabase(SecurityOrigin* origin, const String& name, const String& path)
+bool DatabaseTracker::addDatabase(const SecurityOriginData& origin, const String& name, const String& path)
{
ASSERT(!m_databaseGuard.tryLock());
openTrackerDatabase(CreateIfDoesNotExist);
@@ -799,15 +745,15 @@ bool DatabaseTracker::addDatabase(SecurityOrigin* origin, const String& name, co
SQLiteStatement statement(m_database, "INSERT INTO Databases (origin, name, path) VALUES (?, ?, ?);");
- if (statement.prepare() != SQLResultOk)
+ if (statement.prepare() != SQLITE_OK)
return false;
- statement.bindText(1, origin->databaseIdentifier());
+ statement.bindText(1, origin.databaseIdentifier());
statement.bindText(2, name);
statement.bindText(3, path);
if (!statement.executeCommand()) {
- LOG_ERROR("Failed to add database %s to origin %s: %s\n", name.ascii().data(), origin->databaseIdentifier().ascii().data(), m_database.lastErrorMsg());
+ LOG_ERROR("Failed to add database %s to origin %s: %s\n", name.utf8().data(), origin.databaseIdentifier().utf8().data(), m_database.lastErrorMsg());
return false;
}
@@ -817,32 +763,67 @@ bool DatabaseTracker::addDatabase(SecurityOrigin* origin, const String& name, co
return true;
}
-void DatabaseTracker::deleteAllDatabases()
+void DatabaseTracker::deleteAllDatabasesImmediately()
{
- Vector<RefPtr<SecurityOrigin>> originsCopy;
- origins(originsCopy);
+ // This method is only intended for use by DumpRenderTree / WebKitTestRunner.
+ // Actually deleting the databases is necessary to reset to a known state before running
+ // each test case, but may be unsafe in deployment use cases (where multiple applications
+ // may be accessing the same databases concurrently).
+ for (auto& origin : origins())
+ deleteOrigin(origin, DeletionMode::Immediate);
+}
+
+void DatabaseTracker::deleteDatabasesModifiedSince(std::chrono::system_clock::time_point time)
+{
+ for (auto& origin : origins()) {
+ Vector<String> databaseNames = this->databaseNames(origin);
+ Vector<String> databaseNamesToDelete;
+ databaseNamesToDelete.reserveInitialCapacity(databaseNames.size());
+ for (const auto& databaseName : databaseNames) {
+ auto fullPath = fullPathForDatabase(origin, databaseName, false);
+
+ time_t modificationTime;
+ if (!getFileModificationTime(fullPath, modificationTime))
+ continue;
+
+ if (modificationTime < std::chrono::system_clock::to_time_t(time))
+ continue;
- for (unsigned i = 0; i < originsCopy.size(); ++i)
- deleteOrigin(originsCopy[i].get());
+ databaseNamesToDelete.uncheckedAppend(databaseName);
+ }
+
+ if (databaseNames.size() == databaseNamesToDelete.size())
+ deleteOrigin(origin);
+ else {
+ for (const auto& databaseName : databaseNamesToDelete)
+ deleteDatabase(origin, databaseName);
+ }
+ }
}
// It is the caller's responsibility to make sure that nobody is trying to create, delete, open, or close databases in this origin while the deletion is
// taking place.
-bool DatabaseTracker::deleteOrigin(SecurityOrigin* origin)
+bool DatabaseTracker::deleteOrigin(const SecurityOriginData& origin)
+{
+ return deleteOrigin(origin, DeletionMode::Default);
+}
+
+bool DatabaseTracker::deleteOrigin(const SecurityOriginData& origin, DeletionMode deletionMode)
{
Vector<String> databaseNames;
{
- MutexLocker lockDatabase(m_databaseGuard);
+ LockHolder lockDatabase(m_databaseGuard);
openTrackerDatabase(DontCreateIfDoesNotExist);
if (!m_database.isOpen())
return false;
- if (!databaseNamesForOriginNoLock(origin, databaseNames)) {
- LOG_ERROR("Unable to retrieve list of database names for origin %s", origin->databaseIdentifier().ascii().data());
+ databaseNames = databaseNamesNoLock(origin);
+ if (databaseNames.isEmpty()) {
+ LOG_ERROR("Unable to retrieve list of database names for origin %s", origin.databaseIdentifier().utf8().data());
return false;
}
if (!canDeleteOrigin(origin)) {
- LOG_ERROR("Tried to delete an origin (%s) while either creating database in it or already deleting it", origin->databaseIdentifier().ascii().data());
+ LOG_ERROR("Tried to delete an origin (%s) while either creating database in it or already deleting it", origin.databaseIdentifier().utf8().data());
ASSERT_NOT_REACHED();
return false;
}
@@ -850,55 +831,54 @@ bool DatabaseTracker::deleteOrigin(SecurityOrigin* origin)
}
// We drop the lock here because holding locks during a call to deleteDatabaseFile will deadlock.
- for (unsigned i = 0; i < databaseNames.size(); ++i) {
- if (!deleteDatabaseFile(origin, databaseNames[i])) {
+ for (auto& name : databaseNames) {
+ if (!deleteDatabaseFile(origin, name, deletionMode)) {
// Even if the file can't be deleted, we want to try and delete the rest, don't return early here.
- LOG_ERROR("Unable to delete file for database %s in origin %s", databaseNames[i].ascii().data(), origin->databaseIdentifier().ascii().data());
+ LOG_ERROR("Unable to delete file for database %s in origin %s", name.utf8().data(), origin.databaseIdentifier().utf8().data());
}
}
{
- MutexLocker lockDatabase(m_databaseGuard);
+ LockHolder lockDatabase(m_databaseGuard);
deleteOriginLockFor(origin);
doneDeletingOrigin(origin);
SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=?");
- if (statement.prepare() != SQLResultOk) {
- LOG_ERROR("Unable to prepare deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());
+ if (statement.prepare() != SQLITE_OK) {
+ LOG_ERROR("Unable to prepare deletion of databases from origin %s from tracker", origin.databaseIdentifier().utf8().data());
return false;
}
- statement.bindText(1, origin->databaseIdentifier());
+ statement.bindText(1, origin.databaseIdentifier());
if (!statement.executeCommand()) {
- LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());
+ LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin.databaseIdentifier().utf8().data());
return false;
}
SQLiteStatement originStatement(m_database, "DELETE FROM Origins WHERE origin=?");
- if (originStatement.prepare() != SQLResultOk) {
- LOG_ERROR("Unable to prepare deletion of origin %s from tracker", origin->databaseIdentifier().ascii().data());
+ if (originStatement.prepare() != SQLITE_OK) {
+ LOG_ERROR("Unable to prepare deletion of origin %s from tracker", origin.databaseIdentifier().utf8().data());
return false;
}
- originStatement.bindText(1, origin->databaseIdentifier());
+ originStatement.bindText(1, origin.databaseIdentifier());
if (!originStatement.executeCommand()) {
- LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());
+ LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin.databaseIdentifier().utf8().data());
return false;
}
SQLiteFileSystem::deleteEmptyDatabaseDirectory(originPath(origin));
- RefPtr<SecurityOrigin> originPossiblyLastReference = origin;
bool isEmpty = true;
openTrackerDatabase(DontCreateIfDoesNotExist);
if (m_database.isOpen()) {
SQLiteStatement statement(m_database, "SELECT origin FROM Origins");
- if (statement.prepare() != SQLResultOk)
+ if (statement.prepare() != SQLITE_OK)
LOG_ERROR("Failed to prepare statement.");
- else if (statement.step() == SQLResultRow)
+ else if (statement.step() == SQLITE_ROW)
isEmpty = false;
}
@@ -912,134 +892,134 @@ bool DatabaseTracker::deleteOrigin(SecurityOrigin* origin)
if (m_client) {
m_client->dispatchDidModifyOrigin(origin);
-#if PLATFORM(IOS)
m_client->dispatchDidDeleteDatabaseOrigin();
-#endif
- for (unsigned i = 0; i < databaseNames.size(); ++i)
- m_client->dispatchDidModifyDatabase(origin, databaseNames[i]);
+ for (auto& name : databaseNames)
+ m_client->dispatchDidModifyDatabase(origin, name);
}
}
return true;
}
-bool DatabaseTracker::isDeletingDatabaseOrOriginFor(SecurityOrigin *origin, const String& name)
+bool DatabaseTracker::isDeletingDatabaseOrOriginFor(const SecurityOriginData& origin, const String& name)
{
ASSERT(!m_databaseGuard.tryLock());
// Can't create a database while someone else is deleting it; there's a risk of leaving untracked database debris on the disk.
return isDeletingDatabase(origin, name) || isDeletingOrigin(origin);
}
-void DatabaseTracker::recordCreatingDatabase(SecurityOrigin *origin, const String& name)
+void DatabaseTracker::recordCreatingDatabase(const SecurityOriginData& origin, const String& name)
{
ASSERT(!m_databaseGuard.tryLock());
- NameCountMap* nameMap = m_beingCreated.get(origin);
- if (!nameMap) {
- nameMap = new NameCountMap();
- m_beingCreated.set(origin->isolatedCopy(), nameMap);
+
+ // We don't use HashMap::ensure here to avoid making an isolated copy of the origin every time.
+ auto* nameSet = m_beingCreated.get(origin);
+ if (!nameSet) {
+ auto ownedSet = std::make_unique<HashCountedSet<String>>();
+ nameSet = ownedSet.get();
+ m_beingCreated.add(origin.isolatedCopy(), WTFMove(ownedSet));
}
- long count = nameMap->get(name);
- nameMap->set(name.isolatedCopy(), count + 1);
+ nameSet->add(name.isolatedCopy());
}
-void DatabaseTracker::doneCreatingDatabase(SecurityOrigin *origin, const String& name)
+void DatabaseTracker::doneCreatingDatabase(const SecurityOriginData& origin, const String& name)
{
ASSERT(!m_databaseGuard.tryLock());
- NameCountMap* nameMap = m_beingCreated.get(origin);
- ASSERT(nameMap);
- if (!nameMap)
+
+ ASSERT(m_beingCreated.contains(origin));
+
+ auto iterator = m_beingCreated.find(origin);
+ if (iterator == m_beingCreated.end())
return;
- long count = nameMap->get(name);
- ASSERT(count > 0);
- if (count <= 1) {
- nameMap->remove(name);
- if (nameMap->isEmpty()) {
- m_beingCreated.remove(origin);
- delete nameMap;
- }
- } else
- nameMap->set(name, count - 1);
+ auto& countedSet = *iterator->value;
+ ASSERT(countedSet.contains(name));
+
+ if (countedSet.remove(name) && countedSet.isEmpty())
+ m_beingCreated.remove(iterator);
}
-bool DatabaseTracker::creatingDatabase(SecurityOrigin *origin, const String& name)
+bool DatabaseTracker::creatingDatabase(const SecurityOriginData& origin, const String& name)
{
ASSERT(!m_databaseGuard.tryLock());
- NameCountMap* nameMap = m_beingCreated.get(origin);
- return nameMap && nameMap->get(name);
+
+ auto iterator = m_beingCreated.find(origin);
+ return iterator != m_beingCreated.end() && iterator->value->contains(name);
}
-bool DatabaseTracker::canDeleteDatabase(SecurityOrigin *origin, const String& name)
+bool DatabaseTracker::canDeleteDatabase(const SecurityOriginData& origin, const String& name)
{
ASSERT(!m_databaseGuard.tryLock());
return !creatingDatabase(origin, name) && !isDeletingDatabase(origin, name);
}
-void DatabaseTracker::recordDeletingDatabase(SecurityOrigin *origin, const String& name)
+void DatabaseTracker::recordDeletingDatabase(const SecurityOriginData& origin, const String& name)
{
ASSERT(!m_databaseGuard.tryLock());
ASSERT(canDeleteDatabase(origin, name));
- NameSet* nameSet = m_beingDeleted.get(origin);
+
+ // We don't use HashMap::ensure here to avoid making an isolated copy of the origin every time.
+ auto* nameSet = m_beingDeleted.get(origin);
if (!nameSet) {
- nameSet = new NameSet();
- m_beingDeleted.set(origin->isolatedCopy(), nameSet);
+ auto ownedSet = std::make_unique<HashSet<String>>();
+ nameSet = ownedSet.get();
+ m_beingDeleted.add(origin.isolatedCopy(), WTFMove(ownedSet));
}
ASSERT(!nameSet->contains(name));
nameSet->add(name.isolatedCopy());
}
-void DatabaseTracker::doneDeletingDatabase(SecurityOrigin *origin, const String& name)
+void DatabaseTracker::doneDeletingDatabase(const SecurityOriginData& origin, const String& name)
{
ASSERT(!m_databaseGuard.tryLock());
- NameSet* nameSet = m_beingDeleted.get(origin);
- ASSERT(nameSet);
- if (!nameSet)
+ ASSERT(m_beingDeleted.contains(origin));
+
+ auto iterator = m_beingDeleted.find(origin);
+ if (iterator == m_beingDeleted.end())
return;
- ASSERT(nameSet->contains(name));
- nameSet->remove(name);
- if (nameSet->isEmpty()) {
- m_beingDeleted.remove(origin);
- delete nameSet;
- }
+ ASSERT(iterator->value->contains(name));
+ iterator->value->remove(name);
+ if (iterator->value->isEmpty())
+ m_beingDeleted.remove(iterator);
}
-bool DatabaseTracker::isDeletingDatabase(SecurityOrigin *origin, const String& name)
+bool DatabaseTracker::isDeletingDatabase(const SecurityOriginData& origin, const String& name)
{
ASSERT(!m_databaseGuard.tryLock());
- NameSet* nameSet = m_beingDeleted.get(origin);
+ auto* nameSet = m_beingDeleted.get(origin);
return nameSet && nameSet->contains(name);
}
-bool DatabaseTracker::canDeleteOrigin(SecurityOrigin *origin)
+bool DatabaseTracker::canDeleteOrigin(const SecurityOriginData& origin)
{
ASSERT(!m_databaseGuard.tryLock());
return !(isDeletingOrigin(origin) || m_beingCreated.get(origin));
}
-bool DatabaseTracker::isDeletingOrigin(SecurityOrigin *origin)
+bool DatabaseTracker::isDeletingOrigin(const SecurityOriginData& origin)
{
ASSERT(!m_databaseGuard.tryLock());
return m_originsBeingDeleted.contains(origin);
}
-void DatabaseTracker::recordDeletingOrigin(SecurityOrigin *origin)
+void DatabaseTracker::recordDeletingOrigin(const SecurityOriginData& origin)
{
ASSERT(!m_databaseGuard.tryLock());
ASSERT(!isDeletingOrigin(origin));
- m_originsBeingDeleted.add(origin->isolatedCopy());
+ m_originsBeingDeleted.add(origin.isolatedCopy());
}
-void DatabaseTracker::doneDeletingOrigin(SecurityOrigin *origin)
+void DatabaseTracker::doneDeletingOrigin(const SecurityOriginData& origin)
{
ASSERT(!m_databaseGuard.tryLock());
ASSERT(isDeletingOrigin(origin));
m_originsBeingDeleted.remove(origin);
}
-bool DatabaseTracker::deleteDatabase(SecurityOrigin* origin, const String& name)
+bool DatabaseTracker::deleteDatabase(const SecurityOriginData& origin, const String& name)
{
{
- MutexLocker lockDatabase(m_databaseGuard);
+ LockHolder lockDatabase(m_databaseGuard);
openTrackerDatabase(DontCreateIfDoesNotExist);
if (!m_database.isOpen())
return false;
@@ -1052,27 +1032,27 @@ bool DatabaseTracker::deleteDatabase(SecurityOrigin* origin, const String& name)
}
// We drop the lock here because holding locks during a call to deleteDatabaseFile will deadlock.
- if (!deleteDatabaseFile(origin, name)) {
- LOG_ERROR("Unable to delete file for database %s in origin %s", name.ascii().data(), origin->databaseIdentifier().ascii().data());
- MutexLocker lockDatabase(m_databaseGuard);
+ if (!deleteDatabaseFile(origin, name, DeletionMode::Default)) {
+ LOG_ERROR("Unable to delete file for database %s in origin %s", name.utf8().data(), origin.databaseIdentifier().utf8().data());
+ LockHolder lockDatabase(m_databaseGuard);
doneDeletingDatabase(origin, name);
return false;
}
- MutexLocker lockDatabase(m_databaseGuard);
+ LockHolder lockDatabase(m_databaseGuard);
SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=? AND name=?");
- if (statement.prepare() != SQLResultOk) {
- LOG_ERROR("Unable to prepare deletion of database %s from origin %s from tracker", name.ascii().data(), origin->databaseIdentifier().ascii().data());
+ if (statement.prepare() != SQLITE_OK) {
+ LOG_ERROR("Unable to prepare deletion of database %s from origin %s from tracker", name.utf8().data(), origin.databaseIdentifier().utf8().data());
doneDeletingDatabase(origin, name);
return false;
}
- statement.bindText(1, origin->databaseIdentifier());
+ statement.bindText(1, origin.databaseIdentifier());
statement.bindText(2, name);
if (!statement.executeCommand()) {
- LOG_ERROR("Unable to execute deletion of database %s from origin %s from tracker", name.ascii().data(), origin->databaseIdentifier().ascii().data());
+ LOG_ERROR("Unable to execute deletion of database %s from origin %s from tracker", name.utf8().data(), origin.databaseIdentifier().utf8().data());
doneDeletingDatabase(origin, name);
return false;
}
@@ -1080,9 +1060,7 @@ bool DatabaseTracker::deleteDatabase(SecurityOrigin* origin, const String& name)
if (m_client) {
m_client->dispatchDidModifyOrigin(origin);
m_client->dispatchDidModifyDatabase(origin, name);
-#if PLATFORM(IOS)
m_client->dispatchDidDeleteDatabase();
-#endif
}
doneDeletingDatabase(origin, name);
@@ -1091,7 +1069,7 @@ bool DatabaseTracker::deleteDatabase(SecurityOrigin* origin, const String& name)
// deleteDatabaseFile has to release locks between looking up the list of databases to close and closing them. While this is in progress, the caller
// is responsible for making sure no new databases are opened in the file to be deleted.
-bool DatabaseTracker::deleteDatabaseFile(SecurityOrigin* origin, const String& name)
+bool DatabaseTracker::deleteDatabaseFile(const SecurityOriginData& origin, const String& name, DeletionMode deletionMode)
{
String fullPath = fullPathForDatabase(origin, name, false);
if (fullPath.isEmpty())
@@ -1099,54 +1077,52 @@ bool DatabaseTracker::deleteDatabaseFile(SecurityOrigin* origin, const String& n
#ifndef NDEBUG
{
- MutexLocker lockDatabase(m_databaseGuard);
+ LockHolder lockDatabase(m_databaseGuard);
ASSERT(isDeletingDatabaseOrOriginFor(origin, name));
}
#endif
- Vector<RefPtr<DatabaseBackendBase>> deletedDatabases;
+ Vector<Ref<Database>> deletedDatabases;
// Make sure not to hold the any locks when calling
// Database::markAsDeletedAndClose(), since that can cause a deadlock
// during the synchronous DatabaseThread call it triggers.
{
- MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
+ LockHolder openDatabaseMapLock(m_openDatabaseMapGuard);
if (m_openDatabaseMap) {
- // There are some open databases, lets check if they are for this origin.
- DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin);
- if (nameMap && nameMap->size()) {
- // There are some open databases for this origin, let's check
- // if they are this database by name.
- DatabaseSet* databaseSet = nameMap->get(name);
- if (databaseSet && databaseSet->size()) {
- // We have some database open with this name. Mark them as deleted.
- DatabaseSet::const_iterator end = databaseSet->end();
- for (DatabaseSet::const_iterator it = databaseSet->begin(); it != end; ++it)
- deletedDatabases.append(*it);
+ if (auto* nameMap = m_openDatabaseMap->get(origin)) {
+ if (auto* databaseSet = nameMap->get(name)) {
+ for (auto& database : *databaseSet)
+ deletedDatabases.append(*database);
}
}
}
}
- for (unsigned i = 0; i < deletedDatabases.size(); ++i)
- deletedDatabases[i]->markAsDeletedAndClose();
+ for (auto& database : deletedDatabases)
+ database->markAsDeletedAndClose();
-#if !PLATFORM(IOS)
- return SQLiteFileSystem::deleteDatabaseFile(fullPath);
-#else
- // On the phone, other background processes may still be accessing this database. Deleting the database directly
- // would nuke the POSIX file locks, potentially causing Safari/WebApp to corrupt the new db if it's running in the background.
- // We'll instead truncate the database file to 0 bytes. If another process is operating on this same database file after
- // the truncation, it should get an error since the database file is no longer valid. When Safari is launched
- // next time, it'll go through the database files and clean up any zero-bytes ones.
- SQLiteDatabase database;
- if (database.open(fullPath))
+#if PLATFORM(IOS)
+ if (deletionMode == DeletionMode::Deferred) {
+ // Other background processes may still be accessing this database. Deleting the database directly
+ // would nuke the POSIX file locks, potentially causing Safari/WebApp to corrupt the new db if it's running in the background.
+ // We'll instead truncate the database file to 0 bytes. If another process is operating on this same database file after
+ // the truncation, it should get an error since the database file is no longer valid. When Safari is launched
+ // next time, it'll go through the database files and clean up any zero-bytes ones.
+ SQLiteDatabase database;
+ if (!database.open(fullPath))
+ return false;
return SQLiteFileSystem::truncateDatabaseFile(database.sqlite3Handle());
- return false;
+ }
+#else
+ UNUSED_PARAM(deletionMode);
#endif
+
+ return SQLiteFileSystem::deleteDatabaseFile(fullPath);
}
#if PLATFORM(IOS)
+
void DatabaseTracker::removeDeletedOpenedDatabases()
{
// This is called when another app has deleted a database. Go through all opened databases in this
@@ -1154,7 +1130,7 @@ void DatabaseTracker::removeDeletedOpenedDatabases()
{
// Acquire the lock before calling openTrackerDatabase.
- MutexLocker lockDatabase(m_databaseGuard);
+ LockHolder lockDatabase(m_databaseGuard);
openTrackerDatabase(DontCreateIfDoesNotExist);
}
@@ -1162,43 +1138,36 @@ void DatabaseTracker::removeDeletedOpenedDatabases()
return;
// Keep track of which opened databases have been deleted.
- Vector<RefPtr<Database> > deletedDatabases;
- typedef HashMap<RefPtr<SecurityOrigin>, Vector<String> > DeletedDatabaseMap;
- DeletedDatabaseMap deletedDatabaseMap;
-
+ Vector<RefPtr<Database>> deletedDatabases;
+ Vector<std::pair<SecurityOriginData, Vector<String>>> deletedDatabaseNames;
+
// Make sure not to hold the m_openDatabaseMapGuard mutex when calling
// Database::markAsDeletedAndClose(), since that can cause a deadlock
// during the synchronous DatabaseThread call it triggers.
{
- MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
+ LockHolder openDatabaseMapLock(m_openDatabaseMapGuard);
if (m_openDatabaseMap) {
- DatabaseOriginMap::const_iterator originMapEnd = m_openDatabaseMap->end();
- for (DatabaseOriginMap::const_iterator originMapIt = m_openDatabaseMap->begin(); originMapIt != originMapEnd; ++originMapIt) {
- RefPtr<SecurityOrigin> origin = originMapIt->key;
- DatabaseNameMap* databaseNameMap = originMapIt->value;
+ for (auto& openDatabase : *m_openDatabaseMap) {
+ auto& origin = openDatabase.key;
+ DatabaseNameMap* databaseNameMap = openDatabase.value;
Vector<String> deletedDatabaseNamesForThisOrigin;
// Loop through all opened databases in this origin. Get the current database file path of each database and see if
// it still matches the path stored in the opened database object.
- DatabaseNameMap::const_iterator dbNameMapEnd = databaseNameMap->end();
- for (DatabaseNameMap::const_iterator dbNameMapIt = databaseNameMap->begin(); dbNameMapIt != dbNameMapEnd; ++dbNameMapIt) {
- String databaseName = dbNameMapIt->key;
+ for (auto& databases : *databaseNameMap) {
+ String databaseName = databases.key;
String databaseFileName;
SQLiteStatement statement(m_database, "SELECT path FROM Databases WHERE origin=? AND name=?;");
- if (statement.prepare() == SQLResultOk) {
- statement.bindText(1, origin->databaseIdentifier());
+ if (statement.prepare() == SQLITE_OK) {
+ statement.bindText(1, origin.databaseIdentifier());
statement.bindText(2, databaseName);
- if (statement.step() == SQLResultRow)
+ if (statement.step() == SQLITE_ROW)
databaseFileName = statement.getColumnText(0);
statement.finalize();
}
bool foundDeletedDatabase = false;
- DatabaseSet* databaseSet = dbNameMapIt->value;
- DatabaseSet::const_iterator dbEnd = databaseSet->end();
- for (DatabaseSet::const_iterator dbIt = databaseSet->begin(); dbIt != dbEnd; ++dbIt) {
- Database* db = static_cast<Database*>(*dbIt);
-
+ for (auto& db : *databases.value) {
// We are done if this database has already been marked as deleted.
if (db->deleted())
continue;
@@ -1210,31 +1179,25 @@ void DatabaseTracker::removeDeletedOpenedDatabases()
}
}
- // If the database no longer exists, we should remember to remove it from the OriginQuotaManager later.
- if (foundDeletedDatabase && databaseFileName.isNull())
+ // If the database no longer exists, we should remember to send that information to the client later.
+ if (m_client && foundDeletedDatabase && databaseFileName.isNull())
deletedDatabaseNamesForThisOrigin.append(databaseName);
}
if (!deletedDatabaseNamesForThisOrigin.isEmpty())
- deletedDatabaseMap.set(origin, deletedDatabaseNamesForThisOrigin);
+ deletedDatabaseNames.append({ origin, WTFMove(deletedDatabaseNamesForThisOrigin) });
}
}
}
- for (unsigned i = 0; i < deletedDatabases.size(); ++i)
- deletedDatabases[i]->markAsDeletedAndClose();
-
- DeletedDatabaseMap::const_iterator end = deletedDatabaseMap.end();
- for (DeletedDatabaseMap::const_iterator it = deletedDatabaseMap.begin(); it != end; ++it) {
- SecurityOrigin* origin = it->key.get();
- if (m_client)
- m_client->dispatchDidModifyOrigin(origin);
-
- const Vector<String>& databaseNames = it->value;
- for (unsigned i = 0; i < databaseNames.size(); ++i) {
- if (m_client)
- m_client->dispatchDidModifyDatabase(origin, databaseNames[i]);
- }
+ for (auto& deletedDatabase : deletedDatabases)
+ deletedDatabase->markAsDeletedAndClose();
+
+ for (auto& deletedDatabase : deletedDatabaseNames) {
+ auto& origin = deletedDatabase.first;
+ m_client->dispatchDidModifyOrigin(origin);
+ for (auto& databaseName : deletedDatabase.second)
+ m_client->dispatchDidModifyDatabase(origin, databaseName);
}
}
@@ -1256,20 +1219,20 @@ bool DatabaseTracker::deleteDatabaseFileIfEmpty(const String& path)
// Specify that we want the exclusive locking mode, so after the next read,
// we'll be holding the lock to this database file.
SQLiteStatement lockStatement(database, "PRAGMA locking_mode=EXCLUSIVE;");
- if (lockStatement.prepare() != SQLResultOk)
+ if (lockStatement.prepare() != SQLITE_OK)
return false;
int result = lockStatement.step();
- if (result != SQLResultRow && result != SQLResultDone)
+ if (result != SQLITE_ROW && result != SQLITE_DONE)
return false;
lockStatement.finalize();
// Every sqlite database has a sqlite_master table that contains the schema for the database.
// http://www.sqlite.org/faq.html#q7
SQLiteStatement readStatement(database, "SELECT * FROM sqlite_master LIMIT 1;");
- if (readStatement.prepare() != SQLResultOk)
+ if (readStatement.prepare() != SQLITE_OK)
return false;
// We shouldn't expect any result.
- if (readStatement.step() != SQLResultDone)
+ if (readStatement.step() != SQLITE_DONE)
return false;
readStatement.finalize();
@@ -1281,9 +1244,9 @@ bool DatabaseTracker::deleteDatabaseFileIfEmpty(const String& path)
return SQLiteFileSystem::deleteDatabaseFile(path);
}
-Mutex& DatabaseTracker::openDatabaseMutex()
+Lock& DatabaseTracker::openDatabaseMutex()
{
- DEFINE_STATIC_LOCAL(Mutex, mutex, ());
+ static NeverDestroyed<Lock> mutex;
return mutex;
}
@@ -1299,33 +1262,6 @@ void DatabaseTracker::emptyDatabaseFilesRemovalTaskDidFinish()
openDatabaseMutex().unlock();
}
-void DatabaseTracker::setDatabasesPaused(bool paused)
-{
- MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
- if (!m_openDatabaseMap)
- return;
-
- // This walking is - sadly - the only reliable way to get at each open database thread.
- // This will be cleaner once <rdar://problem/5680441> or some other DB thread consolidation takes place.
- DatabaseOriginMap::iterator i = m_openDatabaseMap.get()->begin();
- DatabaseOriginMap::iterator end = m_openDatabaseMap.get()->end();
-
- for (; i != end; ++i) {
- DatabaseNameMap* databaseNameMap = i->value;
- DatabaseNameMap::iterator j = databaseNameMap->begin();
- DatabaseNameMap::iterator dbNameMapEnd = databaseNameMap->end();
- for (; j != dbNameMapEnd; ++j) {
- DatabaseSet* databaseSet = j->value;
- DatabaseSet::iterator k = databaseSet->begin();
- DatabaseSet::iterator dbSetEnd = databaseSet->end();
- for (; k != dbSetEnd; ++k) {
- DatabaseContext* context = (*k)->databaseContext();
- context->setPaused(paused);
- }
- }
- }
-}
-
#endif
void DatabaseTracker::setClient(DatabaseManagerClient* client)
@@ -1333,25 +1269,24 @@ void DatabaseTracker::setClient(DatabaseManagerClient* client)
m_client = client;
}
-static Mutex& notificationMutex()
+static Lock& notificationMutex()
{
- DEFINE_STATIC_LOCAL(Mutex, mutex, ());
+ static NeverDestroyed<Lock> mutex;
return mutex;
}
-typedef Vector<std::pair<RefPtr<SecurityOrigin>, String>> NotificationQueue;
+using NotificationQueue = Vector<std::pair<SecurityOriginData, String>>;
static NotificationQueue& notificationQueue()
{
- DEFINE_STATIC_LOCAL(NotificationQueue, queue, ());
+ static NeverDestroyed<NotificationQueue> queue;
return queue;
}
-void DatabaseTracker::scheduleNotifyDatabaseChanged(SecurityOrigin* origin, const String& name)
+void DatabaseTracker::scheduleNotifyDatabaseChanged(const SecurityOriginData& origin, const String& name)
{
- MutexLocker locker(notificationMutex());
-
- notificationQueue().append(std::pair<RefPtr<SecurityOrigin>, String>(origin->isolatedCopy(), name.isolatedCopy()));
+ LockHolder locker(notificationMutex());
+ notificationQueue().append(std::make_pair(origin.isolatedCopy(), name.isolatedCopy()));
scheduleForNotification();
}
@@ -1362,33 +1297,32 @@ void DatabaseTracker::scheduleForNotification()
ASSERT(!notificationMutex().tryLock());
if (!notificationScheduled) {
- callOnMainThread(DatabaseTracker::notifyDatabasesChanged, 0);
+ callOnMainThread([] {
+ notifyDatabasesChanged();
+ });
notificationScheduled = true;
}
}
-void DatabaseTracker::notifyDatabasesChanged(void*)
+void DatabaseTracker::notifyDatabasesChanged()
{
// Note that if DatabaseTracker ever becomes non-singleton, we'll have to amend this notification
// mechanism to include which tracker the notification goes out on as well.
- DatabaseTracker& theTracker(tracker());
+ auto& tracker = DatabaseTracker::singleton();
NotificationQueue notifications;
{
- MutexLocker locker(notificationMutex());
-
+ LockHolder locker(notificationMutex());
notifications.swap(notificationQueue());
-
notificationScheduled = false;
}
- if (!theTracker.m_client)
+ if (!tracker.m_client)
return;
- for (unsigned i = 0; i < notifications.size(); ++i)
- theTracker.m_client->dispatchDidModifyDatabase(notifications[i].first.get(), notifications[i].second);
+ for (auto& notification : notifications)
+ tracker.m_client->dispatchDidModifyDatabase(notification.first, notification.second);
}
} // namespace WebCore
-#endif
diff --git a/Source/WebCore/Modules/webdatabase/DatabaseTracker.h b/Source/WebCore/Modules/webdatabase/DatabaseTracker.h
index acea6988f..af6cb5bf3 100644
--- a/Source/WebCore/Modules/webdatabase/DatabaseTracker.h
+++ b/Source/WebCore/Modules/webdatabase/DatabaseTracker.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,35 +26,38 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DatabaseTracker_h
-#define DatabaseTracker_h
-
-#if ENABLE(SQL_DATABASE)
+#pragma once
#include "DatabaseDetails.h"
-#include "DatabaseError.h"
+#include "ExceptionOr.h"
#include "SQLiteDatabase.h"
+#include "SecurityOriginData.h"
#include "SecurityOriginHash.h"
+#include <wtf/HashCountedSet.h>
#include <wtf/HashMap.h>
#include <wtf/HashSet.h>
-#include <wtf/OwnPtr.h>
#include <wtf/text/StringHash.h>
-#include <wtf/text/WTFString.h>
namespace WebCore {
-class DatabaseBackendBase;
-class DatabaseBackendContext;
+class Database;
+class DatabaseContext;
class DatabaseManagerClient;
class OriginLock;
class SecurityOrigin;
+struct SecurityOriginData;
+
+enum class CurrentQueryBehavior { Interrupt, RunToCompletion };
class DatabaseTracker {
WTF_MAKE_NONCOPYABLE(DatabaseTracker); WTF_MAKE_FAST_ALLOCATED;
public:
+ // FIXME: This is a hack so we can easily delete databases from the UI process in WebKit2.
+ WEBCORE_EXPORT static std::unique_ptr<DatabaseTracker> trackerWithDatabasePath(const String& databasePath);
+
static void initializeTracker(const String& databasePath);
- static DatabaseTracker& tracker();
+ WEBCORE_EXPORT static DatabaseTracker& singleton();
// This singleton will potentially be used from multiple worker threads and the page's context thread simultaneously. To keep this safe, it's
// currently using 4 locks. In order to avoid deadlock when taking multiple locks, you must take them in the correct order:
// m_databaseGuard before quotaManager if both locks are needed.
@@ -62,72 +65,63 @@ public:
// m_databaseGuard and m_openDatabaseMapGuard currently don't overlap.
// notificationMutex() is currently independent of the other locks.
- bool canEstablishDatabase(DatabaseBackendContext*, const String& name, unsigned long estimatedSize, DatabaseError&);
- bool retryCanEstablishDatabase(DatabaseBackendContext*, const String& name, unsigned long estimatedSize, DatabaseError&);
-
- void setDatabaseDetails(SecurityOrigin*, const String& name, const String& displayName, unsigned long estimatedSize);
- String fullPathForDatabase(SecurityOrigin*, const String& name, bool createIfDoesNotExist = true);
-
- void addOpenDatabase(DatabaseBackendBase*);
- void removeOpenDatabase(DatabaseBackendBase*);
- void getOpenDatabases(SecurityOrigin*, const String& name, HashSet<RefPtr<DatabaseBackendBase>>* databases);
+ ExceptionOr<void> canEstablishDatabase(DatabaseContext&, const String& name, unsigned estimatedSize);
+ ExceptionOr<void> retryCanEstablishDatabase(DatabaseContext&, const String& name, unsigned estimatedSize);
- unsigned long long getMaxSizeForDatabase(const DatabaseBackendBase*);
+ void setDatabaseDetails(const SecurityOriginData&, const String& name, const String& displayName, unsigned estimatedSize);
+ String fullPathForDatabase(const SecurityOriginData&, const String& name, bool createIfDoesNotExist);
- void interruptAllDatabasesForContext(const DatabaseBackendContext*);
+ void addOpenDatabase(Database&);
+ void removeOpenDatabase(Database&);
-private:
- explicit DatabaseTracker(const String& databasePath);
+ unsigned long long maximumSize(Database&);
- bool hasAdequateQuotaForOrigin(SecurityOrigin*, unsigned long estimatedSize, DatabaseError&);
+ WEBCORE_EXPORT void closeAllDatabases(CurrentQueryBehavior = CurrentQueryBehavior::RunToCompletion);
-public:
- void setDatabaseDirectoryPath(const String&);
- String databaseDirectoryPath() const;
+ WEBCORE_EXPORT Vector<SecurityOriginData> origins();
+ WEBCORE_EXPORT Vector<String> databaseNames(const SecurityOriginData&);
- void origins(Vector<RefPtr<SecurityOrigin>>& result);
- bool databaseNamesForOrigin(SecurityOrigin*, Vector<String>& result);
+ DatabaseDetails detailsForNameAndOrigin(const String&, const SecurityOriginData&);
- DatabaseDetails detailsForNameAndOrigin(const String&, SecurityOrigin*);
+ WEBCORE_EXPORT unsigned long long usage(const SecurityOriginData&);
+ WEBCORE_EXPORT unsigned long long quota(const SecurityOriginData&);
+ WEBCORE_EXPORT void setQuota(const SecurityOriginData&, unsigned long long);
+ RefPtr<OriginLock> originLockFor(const SecurityOriginData&);
- unsigned long long usageForOrigin(SecurityOrigin*);
- unsigned long long quotaForOrigin(SecurityOrigin*);
- void setQuota(SecurityOrigin*, unsigned long long);
- PassRefPtr<OriginLock> originLockFor(SecurityOrigin*);
-
- void deleteAllDatabases();
- bool deleteOrigin(SecurityOrigin*);
- bool deleteDatabase(SecurityOrigin*, const String& name);
+ WEBCORE_EXPORT void deleteAllDatabasesImmediately();
+ WEBCORE_EXPORT void deleteDatabasesModifiedSince(std::chrono::system_clock::time_point);
+ WEBCORE_EXPORT bool deleteOrigin(const SecurityOriginData&);
+ WEBCORE_EXPORT bool deleteDatabase(const SecurityOriginData&, const String& name);
#if PLATFORM(IOS)
- void removeDeletedOpenedDatabases();
- static bool deleteDatabaseFileIfEmpty(const String&);
+ WEBCORE_EXPORT void removeDeletedOpenedDatabases();
+ WEBCORE_EXPORT static bool deleteDatabaseFileIfEmpty(const String&);
// MobileSafari will grab this mutex on the main thread before dispatching the task to
// clean up zero byte database files. Any operations to open new database will have to
// wait for that task to finish by waiting on this mutex.
- static Mutex& openDatabaseMutex();
-
- static void emptyDatabaseFilesRemovalTaskWillBeScheduled();
- static void emptyDatabaseFilesRemovalTaskDidFinish();
+ static Lock& openDatabaseMutex();
- void setDatabasesPaused(bool);
+ WEBCORE_EXPORT static void emptyDatabaseFilesRemovalTaskWillBeScheduled();
+ WEBCORE_EXPORT static void emptyDatabaseFilesRemovalTaskDidFinish();
#endif
void setClient(DatabaseManagerClient*);
// From a secondary thread, must be thread safe with its data
- void scheduleNotifyDatabaseChanged(SecurityOrigin*, const String& name);
+ void scheduleNotifyDatabaseChanged(const SecurityOriginData&, const String& name);
- bool hasEntryForOrigin(SecurityOrigin*);
-
- void doneCreatingDatabase(DatabaseBackendBase*);
+ void doneCreatingDatabase(Database&);
private:
- bool hasEntryForOriginNoLock(SecurityOrigin* origin);
- String fullPathForDatabaseNoLock(SecurityOrigin*, const String& name, bool createIfDoesNotExist);
- bool databaseNamesForOriginNoLock(SecurityOrigin* origin, Vector<String>& resultVector);
- unsigned long long quotaForOriginNoLock(SecurityOrigin* origin);
+ explicit DatabaseTracker(const String& databasePath);
+
+ ExceptionOr<void> hasAdequateQuotaForOrigin(const SecurityOriginData&, unsigned estimatedSize);
+
+ bool hasEntryForOriginNoLock(const SecurityOriginData&);
+ String fullPathForDatabaseNoLock(const SecurityOriginData&, const String& name, bool createIfDoesNotExist);
+ Vector<String> databaseNamesNoLock(const SecurityOriginData&);
+ unsigned long long quotaNoLock(const SecurityOriginData&);
String trackerDatabasePath() const;
@@ -137,59 +131,65 @@ private:
};
void openTrackerDatabase(TrackerCreationAction);
- String originPath(SecurityOrigin*) const;
+ String originPath(const SecurityOriginData&) const;
+
+ bool hasEntryForDatabase(const SecurityOriginData&, const String& databaseIdentifier);
- bool hasEntryForDatabase(SecurityOrigin*, const String& databaseIdentifier);
+ bool addDatabase(const SecurityOriginData&, const String& name, const String& path);
- bool addDatabase(SecurityOrigin*, const String& name, const String& path);
+ enum class DeletionMode {
+ Immediate,
+#if PLATFORM(IOS)
+ // Deferred deletion is currently only supported on iOS
+ // (see removeDeletedOpenedDatabases etc, above).
+ Deferred,
+ Default = Deferred
+#else
+ Default = Immediate
+#endif
+ };
- bool deleteDatabaseFile(SecurityOrigin*, const String& name);
+ bool deleteOrigin(const SecurityOriginData&, DeletionMode);
+ bool deleteDatabaseFile(const SecurityOriginData&, const String& name, DeletionMode);
- void deleteOriginLockFor(SecurityOrigin*);
+ void deleteOriginLockFor(const SecurityOriginData&);
- typedef HashSet<DatabaseBackendBase*> DatabaseSet;
- typedef HashMap<String, DatabaseSet*> DatabaseNameMap;
- typedef HashMap<RefPtr<SecurityOrigin>, DatabaseNameMap*> DatabaseOriginMap;
+ using DatabaseSet = HashSet<Database*>;
+ using DatabaseNameMap = HashMap<String, DatabaseSet*>;
+ using DatabaseOriginMap = HashMap<SecurityOriginData, DatabaseNameMap*>;
- Mutex m_openDatabaseMapGuard;
- mutable OwnPtr<DatabaseOriginMap> m_openDatabaseMap;
+ Lock m_openDatabaseMapGuard;
+ mutable std::unique_ptr<DatabaseOriginMap> m_openDatabaseMap;
// This lock protects m_database, m_originLockMap, m_databaseDirectoryPath, m_originsBeingDeleted, m_beingCreated, and m_beingDeleted.
- Mutex m_databaseGuard;
+ Lock m_databaseGuard;
SQLiteDatabase m_database;
- typedef HashMap<String, RefPtr<OriginLock>> OriginLockMap;
+ using OriginLockMap = HashMap<String, RefPtr<OriginLock>>;
OriginLockMap m_originLockMap;
String m_databaseDirectoryPath;
- DatabaseManagerClient* m_client;
-
- typedef HashMap<String, long> NameCountMap;
- typedef HashMap<RefPtr<SecurityOrigin>, NameCountMap*, SecurityOriginHash> CreateSet;
- CreateSet m_beingCreated;
- typedef HashSet<String> NameSet;
- HashMap<RefPtr<SecurityOrigin>, NameSet*> m_beingDeleted;
- HashSet<RefPtr<SecurityOrigin>> m_originsBeingDeleted;
- bool isDeletingDatabaseOrOriginFor(SecurityOrigin*, const String& name);
- void recordCreatingDatabase(SecurityOrigin*, const String& name);
- void doneCreatingDatabase(SecurityOrigin*, const String& name);
- bool creatingDatabase(SecurityOrigin*, const String& name);
- bool canDeleteDatabase(SecurityOrigin*, const String& name);
- void recordDeletingDatabase(SecurityOrigin*, const String& name);
- void doneDeletingDatabase(SecurityOrigin*, const String& name);
- bool isDeletingDatabase(SecurityOrigin*, const String& name);
- bool canDeleteOrigin(SecurityOrigin*);
- bool isDeletingOrigin(SecurityOrigin*);
- void recordDeletingOrigin(SecurityOrigin*);
- void doneDeletingOrigin(SecurityOrigin*);
+ DatabaseManagerClient* m_client { nullptr };
+
+ HashMap<SecurityOriginData, std::unique_ptr<HashCountedSet<String>>> m_beingCreated;
+ HashMap<SecurityOriginData, std::unique_ptr<HashSet<String>>> m_beingDeleted;
+ HashSet<SecurityOriginData> m_originsBeingDeleted;
+ bool isDeletingDatabaseOrOriginFor(const SecurityOriginData&, const String& name);
+ void recordCreatingDatabase(const SecurityOriginData&, const String& name);
+ void doneCreatingDatabase(const SecurityOriginData&, const String& name);
+ bool creatingDatabase(const SecurityOriginData&, const String& name);
+ bool canDeleteDatabase(const SecurityOriginData&, const String& name);
+ void recordDeletingDatabase(const SecurityOriginData&, const String& name);
+ void doneDeletingDatabase(const SecurityOriginData&, const String& name);
+ bool isDeletingDatabase(const SecurityOriginData&, const String& name);
+ bool canDeleteOrigin(const SecurityOriginData&);
+ bool isDeletingOrigin(const SecurityOriginData&);
+ void recordDeletingOrigin(const SecurityOriginData&);
+ void doneDeletingOrigin(const SecurityOriginData&);
static void scheduleForNotification();
- static void notifyDatabasesChanged(void*);
+ static void notifyDatabasesChanged();
};
} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
-
-#endif // DatabaseTracker_h
diff --git a/Source/WebCore/Modules/webdatabase/OriginLock.cpp b/Source/WebCore/Modules/webdatabase/OriginLock.cpp
index 7361e3663..138025ac3 100644
--- a/Source/WebCore/Modules/webdatabase/OriginLock.cpp
+++ b/Source/WebCore/Modules/webdatabase/OriginLock.cpp
@@ -26,10 +26,7 @@
#include "config.h"
#include "OriginLock.h"
-#if ENABLE(SQL_DATABASE)
-
#include "FileSystem.h"
-#include <wtf/PassOwnPtr.h>
namespace WebCore {
@@ -97,5 +94,3 @@ void OriginLock::deleteLockFile(String originPath)
}
} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
diff --git a/Source/WebCore/Modules/webdatabase/OriginLock.h b/Source/WebCore/Modules/webdatabase/OriginLock.h
index 791a56a4a..d7151de78 100644
--- a/Source/WebCore/Modules/webdatabase/OriginLock.h
+++ b/Source/WebCore/Modules/webdatabase/OriginLock.h
@@ -23,14 +23,11 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef OriginLock_h
-#define OriginLock_h
-
-#if ENABLE(SQL_DATABASE)
+#pragma once
#include "FileSystem.h"
+#include <wtf/Lock.h>
#include <wtf/ThreadSafeRefCounted.h>
-#include <wtf/ThreadingPrimitives.h>
#include <wtf/text/WTFString.h>
namespace WebCore {
@@ -39,7 +36,7 @@ class OriginLock : public ThreadSafeRefCounted<OriginLock> {
WTF_MAKE_NONCOPYABLE(OriginLock); WTF_MAKE_FAST_ALLOCATED;
public:
OriginLock(String originPath);
- ~OriginLock();
+ WEBCORE_EXPORT ~OriginLock();
void lock();
void unlock();
@@ -50,14 +47,10 @@ private:
static String lockFileNameForPath(String originPath);
String m_lockFileName;
- Mutex m_mutex;
+ Lock m_mutex;
#if USE(FILE_LOCK)
PlatformFileHandle m_lockHandle;
#endif
};
} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
-
-#endif // OriginLock_h
diff --git a/Source/WebCore/Modules/webdatabase/SQLCallbackWrapper.h b/Source/WebCore/Modules/webdatabase/SQLCallbackWrapper.h
index c184a3738..d0d128096 100644
--- a/Source/WebCore/Modules/webdatabase/SQLCallbackWrapper.h
+++ b/Source/WebCore/Modules/webdatabase/SQLCallbackWrapper.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,13 +25,11 @@
* (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 SQLCallbackWrapper_h
-#define SQLCallbackWrapper_h
-#if ENABLE(SQL_DATABASE)
+#pragma once
#include "ScriptExecutionContext.h"
-#include <wtf/ThreadingPrimitives.h>
+#include <wtf/Lock.h>
namespace WebCore {
@@ -43,8 +41,8 @@ namespace WebCore {
// - by unwrapping and then dereferencing normally - on context thread only
template<typename T> class SQLCallbackWrapper {
public:
- SQLCallbackWrapper(PassRefPtr<T> callback, ScriptExecutionContext* scriptExecutionContext)
- : m_callback(callback)
+ SQLCallbackWrapper(RefPtr<T>&& callback, ScriptExecutionContext* scriptExecutionContext)
+ : m_callback(WTFMove(callback))
, m_scriptExecutionContext(m_callback ? scriptExecutionContext : 0)
{
ASSERT(!m_callback || (m_scriptExecutionContext.get() && m_scriptExecutionContext->isContextThread()));
@@ -57,69 +55,47 @@ public:
void clear()
{
- ScriptExecutionContext* context;
+ ScriptExecutionContext* scriptExecutionContextPtr;
T* callback;
{
- MutexLocker locker(m_mutex);
+ LockHolder locker(m_mutex);
if (!m_callback) {
ASSERT(!m_scriptExecutionContext);
return;
}
if (m_scriptExecutionContext->isContextThread()) {
- m_callback = 0;
- m_scriptExecutionContext = 0;
+ m_callback = nullptr;
+ m_scriptExecutionContext = nullptr;
return;
}
- context = m_scriptExecutionContext.release().leakRef();
- callback = m_callback.release().leakRef();
+ scriptExecutionContextPtr = m_scriptExecutionContext.leakRef();
+ callback = m_callback.leakRef();
}
- context->postTask(SafeReleaseTask::create(callback));
+ scriptExecutionContextPtr->postTask({
+ ScriptExecutionContext::Task::CleanupTask,
+ [callback, scriptExecutionContextPtr] (ScriptExecutionContext& context) {
+ ASSERT_UNUSED(context, &context == scriptExecutionContextPtr && context.isContextThread());
+ callback->deref();
+ scriptExecutionContextPtr->deref();
+ }
+ });
}
- PassRefPtr<T> unwrap()
+ RefPtr<T> unwrap()
{
- MutexLocker locker(m_mutex);
+ LockHolder locker(m_mutex);
ASSERT(!m_callback || m_scriptExecutionContext->isContextThread());
- m_scriptExecutionContext = 0;
- return m_callback.release();
+ m_scriptExecutionContext = nullptr;
+ return WTFMove(m_callback);
}
// Useful for optimizations only, please test the return value of unwrap to be sure.
bool hasCallback() const { return m_callback; }
private:
- class SafeReleaseTask : public ScriptExecutionContext::Task {
- public:
- static PassOwnPtr<SafeReleaseTask> create(T* callbackToRelease)
- {
- return adoptPtr(new SafeReleaseTask(callbackToRelease));
- }
-
- virtual void performTask(ScriptExecutionContext* context)
- {
- ASSERT(m_callbackToRelease && context && context->isContextThread());
- m_callbackToRelease->deref();
- context->deref();
- }
-
- virtual bool isCleanupTask() const { return true; }
-
- private:
- explicit SafeReleaseTask(T* callbackToRelease)
- : m_callbackToRelease(callbackToRelease)
- {
- }
-
- T* m_callbackToRelease;
- };
-
- Mutex m_mutex;
+ Lock m_mutex;
RefPtr<T> m_callback;
RefPtr<ScriptExecutionContext> m_scriptExecutionContext;
};
} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
-
-#endif // SQLCallbackWrapper_h
diff --git a/Source/WebCore/Modules/webdatabase/SQLError.h b/Source/WebCore/Modules/webdatabase/SQLError.h
index fd423abdb..4e276ca78 100644
--- a/Source/WebCore/Modules/webdatabase/SQLError.h
+++ b/Source/WebCore/Modules/webdatabase/SQLError.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,10 +26,7 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef SQLError_h
-#define SQLError_h
-
-#if ENABLE(SQL_DATABASE)
+#pragma once
#include <wtf/ThreadSafeRefCounted.h>
#include <wtf/text/WTFString.h>
@@ -38,12 +35,12 @@ namespace WebCore {
class SQLError : public ThreadSafeRefCounted<SQLError> {
public:
- static PassRefPtr<SQLError> create(unsigned code, const String& message) { return adoptRef(new SQLError(code, message)); }
- static PassRefPtr<SQLError> create(unsigned code, const char* message, int sqliteCode)
+ static Ref<SQLError> create(unsigned code, const String& message) { return adoptRef(*new SQLError(code, message)); }
+ static Ref<SQLError> create(unsigned code, const char* message, int sqliteCode)
{
return create(code, String::format("%s (%d)", message, sqliteCode));
}
- static PassRefPtr<SQLError> create(unsigned code, const char* message, int sqliteCode, const char* sqliteMessage)
+ static Ref<SQLError> create(unsigned code, const char* message, int sqliteCode, const char* sqliteMessage)
{
return create(code, String::format("%s (%d %s)", message, sqliteCode, sqliteMessage));
}
@@ -68,8 +65,4 @@ private:
String m_message;
};
-}
-
-#endif
-
-#endif // SQLError_h
+} // namespace WebCore
diff --git a/Source/WebCore/Modules/webdatabase/SQLError.idl b/Source/WebCore/Modules/webdatabase/SQLError.idl
index 8187f960e..b4e5db7b6 100644
--- a/Source/WebCore/Modules/webdatabase/SQLError.idl
+++ b/Source/WebCore/Modules/webdatabase/SQLError.idl
@@ -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,9 +27,6 @@
*/
[
- NoInterfaceObject,
- Conditional=SQL_DATABASE,
- JSNoStaticTables,
ImplementationLacksVTable
] interface SQLError {
readonly attribute unsigned long code;
diff --git a/Source/WebCore/Modules/webdatabase/SQLException.cpp b/Source/WebCore/Modules/webdatabase/SQLException.cpp
index e70d71f53..e9f880edc 100644
--- a/Source/WebCore/Modules/webdatabase/SQLException.cpp
+++ b/Source/WebCore/Modules/webdatabase/SQLException.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,14 +27,13 @@
*/
#include "config.h"
-
-#if ENABLE(SQL_DATABASE)
-
#include "SQLException.h"
+#include "ExceptionCodeDescription.h"
+
namespace WebCore {
-static struct SQLExceptionNameDescription {
+static const struct SQLExceptionNameDescription {
const char* const name;
const char* const description;
} sqlExceptions[] = {
@@ -67,5 +66,3 @@ bool SQLException::initializeDescription(ExceptionCode ec, ExceptionCodeDescript
}
} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
diff --git a/Source/WebCore/Modules/webdatabase/SQLException.h b/Source/WebCore/Modules/webdatabase/SQLException.h
index 7ea09982a..281a85e2b 100644
--- a/Source/WebCore/Modules/webdatabase/SQLException.h
+++ b/Source/WebCore/Modules/webdatabase/SQLException.h
@@ -28,10 +28,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef SQLException_h
-#define SQLException_h
-
-#if ENABLE(SQL_DATABASE)
+#pragma once
#include "ExceptionBase.h"
@@ -39,9 +36,9 @@ namespace WebCore {
class SQLException : public ExceptionBase {
public:
- static PassRefPtr<SQLException> create(const ExceptionCodeDescription& description)
+ static Ref<SQLException> create(const ExceptionCodeDescription& description)
{
- return adoptRef(new SQLException(description));
+ return adoptRef(*new SQLException(description));
}
static const int SQLExceptionOffset = 1000;
@@ -68,7 +65,3 @@ private:
};
} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
-
-#endif // SQLException_h
diff --git a/Source/WebCore/Modules/webdatabase/SQLException.idl b/Source/WebCore/Modules/webdatabase/SQLException.idl
index b9511b4dd..3dc160a8f 100644
--- a/Source/WebCore/Modules/webdatabase/SQLException.idl
+++ b/Source/WebCore/Modules/webdatabase/SQLException.idl
@@ -29,8 +29,6 @@
*/
[
- Conditional=SQL_DATABASE,
- JSNoStaticTables,
DoNotCheckConstants,
ImplementationLacksVTable
] exception SQLException {
diff --git a/Source/WebCore/Modules/webdatabase/SQLResultSet.cpp b/Source/WebCore/Modules/webdatabase/SQLResultSet.cpp
index 91a2d9108..da63f2b90 100644
--- a/Source/WebCore/Modules/webdatabase/SQLResultSet.cpp
+++ b/Source/WebCore/Modules/webdatabase/SQLResultSet.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.
*
@@ -29,54 +29,22 @@
#include "config.h"
#include "SQLResultSet.h"
-#if ENABLE(SQL_DATABASE)
-
#include "ExceptionCode.h"
namespace WebCore {
SQLResultSet::SQLResultSet()
: m_rows(SQLResultSetRowList::create())
- , m_insertId(0)
- , m_insertIdSet(false)
- , m_rowsAffected(0)
{
}
-int64_t SQLResultSet::insertId(ExceptionCode& e) const
+ExceptionOr<int64_t> SQLResultSet::insertId() const
{
// 4.11.4 - Return the id of the last row inserted as a result of the query
// If the query didn't result in any rows being added, raise an INVALID_ACCESS_ERR exception
- if (m_insertIdSet)
- return m_insertId;
-
- e = INVALID_ACCESS_ERR;
- return -1;
-}
-
-int SQLResultSet::rowsAffected() const
-{
- return m_rowsAffected;
-}
-
-SQLResultSetRowList* SQLResultSet::rows() const
-{
- return m_rows.get();
-}
-
-void SQLResultSet::setInsertId(int64_t id)
-{
- ASSERT(!m_insertIdSet);
-
- m_insertId = id;
- m_insertIdSet = true;
-}
-
-void SQLResultSet::setRowsAffected(int count)
-{
- m_rowsAffected = count;
+ if (!m_insertId)
+ return Exception { INVALID_ACCESS_ERR };
+ return m_insertId.value();
}
}
-
-#endif
diff --git a/Source/WebCore/Modules/webdatabase/SQLResultSet.h b/Source/WebCore/Modules/webdatabase/SQLResultSet.h
index 28ea43178..e2283999f 100644
--- a/Source/WebCore/Modules/webdatabase/SQLResultSet.h
+++ b/Source/WebCore/Modules/webdatabase/SQLResultSet.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,9 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef SQLResultSet_h
-#define SQLResultSet_h
+#pragma once
-#if ENABLE(SQL_DATABASE)
-
-#include "DatabaseBasicTypes.h"
+#include "ExceptionOr.h"
#include "SQLResultSetRowList.h"
#include <wtf/ThreadSafeRefCounted.h>
@@ -39,28 +36,33 @@ namespace WebCore {
class SQLResultSet : public ThreadSafeRefCounted<SQLResultSet> {
public:
- static PassRefPtr<SQLResultSet> create() { return adoptRef(new SQLResultSet); }
+ static Ref<SQLResultSet> create() { return adoptRef(*new SQLResultSet); }
- SQLResultSetRowList* rows() const;
+ SQLResultSetRowList& rows() { return m_rows.get(); }
- int64_t insertId(ExceptionCode&) const;
- int rowsAffected() const;
+ ExceptionOr<int64_t> insertId() const;
+ int rowsAffected() const { return m_rowsAffected; }
-// For internal (non-JS) use
void setInsertId(int64_t);
void setRowsAffected(int);
private:
SQLResultSet();
- RefPtr<SQLResultSetRowList> m_rows;
- int64_t m_insertId;
- bool m_insertIdSet;
- int m_rowsAffected;
+ Ref<SQLResultSetRowList> m_rows;
+ std::optional<int64_t> m_insertId;
+ int m_rowsAffected { 0 };
};
-} // namespace WebCore
+inline void SQLResultSet::setInsertId(int64_t id)
+{
+ ASSERT(!m_insertId);
+ m_insertId = id;
+}
-#endif
+inline void SQLResultSet::setRowsAffected(int count)
+{
+ m_rowsAffected = count;
+}
-#endif // SQLResultSet_h
+} // namespace WebCore
diff --git a/Source/WebCore/Modules/webdatabase/SQLResultSet.idl b/Source/WebCore/Modules/webdatabase/SQLResultSet.idl
index d3b93e40d..a381e5813 100644
--- a/Source/WebCore/Modules/webdatabase/SQLResultSet.idl
+++ b/Source/WebCore/Modules/webdatabase/SQLResultSet.idl
@@ -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,18 +27,9 @@
*/
[
- NoInterfaceObject,
- Conditional=SQL_DATABASE,
- JSNoStaticTables,
ImplementationLacksVTable
] interface SQLResultSet {
readonly attribute SQLResultSetRowList rows;
-
-#if !defined(LANGUAGE_CPP) || !LANGUAGE_CPP
- [GetterRaisesException] readonly attribute long insertId;
-#else
- // Explicitely choose 'long long' here to avoid a 64bit->32bit shortening warning for us.
- [GetterRaisesException] readonly attribute long long insertId;
-#endif
+ [GetterMayThrowException] readonly attribute long long insertId;
readonly attribute long rowsAffected;
};
diff --git a/Source/WebCore/Modules/webdatabase/SQLResultSetRowList.cpp b/Source/WebCore/Modules/webdatabase/SQLResultSetRowList.cpp
index c99b40a56..28dc3b36d 100644
--- a/Source/WebCore/Modules/webdatabase/SQLResultSetRowList.cpp
+++ b/Source/WebCore/Modules/webdatabase/SQLResultSetRowList.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.
*
@@ -29,13 +29,13 @@
#include "config.h"
#include "SQLResultSetRowList.h"
-#if ENABLE(SQL_DATABASE)
+#include "ExceptionCode.h"
namespace WebCore {
unsigned SQLResultSetRowList::length() const
{
- if (m_result.size() == 0)
+ if (m_result.isEmpty())
return 0;
ASSERT(m_result.size() % m_columns.size() == 0);
@@ -43,6 +43,19 @@ unsigned SQLResultSetRowList::length() const
return m_result.size() / m_columns.size();
}
+ExceptionOr<Vector<WTF::KeyValuePair<String, SQLValue>>> SQLResultSetRowList::item(unsigned index) const
+{
+ if (index >= length())
+ return Exception { INDEX_SIZE_ERR };
+
+ Vector<WTF::KeyValuePair<String, SQLValue>> result;
+
+ unsigned numberOfColumns = m_columns.size();
+ unsigned valuesIndex = index * numberOfColumns;
+ for (unsigned i = 0; i < numberOfColumns; i++)
+ result.append({ m_columns[i], m_result[valuesIndex + i] });
+
+ return WTFMove(result);
}
-#endif
+}
diff --git a/Source/WebCore/Modules/webdatabase/SQLResultSetRowList.h b/Source/WebCore/Modules/webdatabase/SQLResultSetRowList.h
index 45d88b6ef..b6fd7980c 100644
--- a/Source/WebCore/Modules/webdatabase/SQLResultSetRowList.h
+++ b/Source/WebCore/Modules/webdatabase/SQLResultSetRowList.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,20 +26,17 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef SQLResultSetRowList_h
-#define SQLResultSetRowList_h
+#pragma once
-#if ENABLE(SQL_DATABASE)
-
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
+#include "ExceptionOr.h"
#include "SQLValue.h"
+#include <wtf/HashTraits.h>
namespace WebCore {
class SQLResultSetRowList : public RefCounted<SQLResultSetRowList> {
public:
- static PassRefPtr<SQLResultSetRowList> create() { return adoptRef(new SQLResultSetRowList); }
+ static Ref<SQLResultSetRowList> create() { return adoptRef(*new SQLResultSetRowList); }
const Vector<String>& columnNames() const { return m_columns; }
const Vector<SQLValue>& values() const { return m_result; }
@@ -48,6 +45,7 @@ public:
void addResult(const SQLValue& result) { m_result.append(result); }
unsigned length() const;
+ ExceptionOr<Vector<WTF::KeyValuePair<String, SQLValue>>> item(unsigned index) const;
private:
SQLResultSetRowList() { }
@@ -56,8 +54,4 @@ private:
Vector<SQLValue> m_result;
};
-}
-
-#endif
-
-#endif
+} // namespace WebCore
diff --git a/Source/WebCore/Modules/webdatabase/SQLResultSetRowList.idl b/Source/WebCore/Modules/webdatabase/SQLResultSetRowList.idl
index 250a2aec7..5541c3559 100644
--- a/Source/WebCore/Modules/webdatabase/SQLResultSetRowList.idl
+++ b/Source/WebCore/Modules/webdatabase/SQLResultSetRowList.idl
@@ -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,11 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+typedef (DOMString? or unrestricted double) SQLValue;
+
[
- NoInterfaceObject,
- Conditional=SQL_DATABASE,
- JSNoStaticTables,
ImplementationLacksVTable,
] interface SQLResultSetRowList {
readonly attribute unsigned long length;
- [Custom] any item(unsigned long index);
+ [MayThrowException] record<DOMString, SQLValue> item(unsigned long index);
};
diff --git a/Source/WebCore/Modules/webdatabase/SQLStatement.cpp b/Source/WebCore/Modules/webdatabase/SQLStatement.cpp
index 92c7f4f46..ea892431c 100644
--- a/Source/WebCore/Modules/webdatabase/SQLStatement.cpp
+++ b/Source/WebCore/Modules/webdatabase/SQLStatement.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.
*
@@ -28,61 +28,186 @@
#include "config.h"
#include "SQLStatement.h"
-#if ENABLE(SQL_DATABASE)
-
-#include "AbstractDatabaseServer.h"
-#include "AbstractSQLStatementBackend.h"
#include "Database.h"
-#include "DatabaseManager.h"
#include "Logging.h"
+#include "SQLError.h"
+#include "SQLResultSet.h"
#include "SQLStatementCallback.h"
#include "SQLStatementErrorCallback.h"
-#include "SQLTransaction.h"
#include "SQLValue.h"
#include "SQLiteDatabase.h"
#include "SQLiteStatement.h"
#include <wtf/text/CString.h>
+
+// The Life-Cycle of a SQLStatement i.e. Who's keeping the SQLStatement alive?
+// ==========================================================================
+// The RefPtr chain goes something like this:
+//
+// At birth (in SQLTransactionBackend::executeSQL()):
+// =================================================
+// SQLTransactionBackend // Deque<RefPtr<SQLStatement>> m_statementQueue points to ...
+// --> SQLStatement // std::unique_ptr<SQLStatement> m_frontend points to ...
+// --> SQLStatement
+//
+// After grabbing the statement for execution (in SQLTransactionBackend::getNextStatement()):
+// =========================================================================================
+// SQLTransactionBackend // RefPtr<SQLStatement> m_currentStatementBackend points to ...
+// --> SQLStatement // std::unique_ptr<SQLStatement> m_frontend points to ...
+// --> SQLStatement
+//
+// Then we execute the statement in SQLTransactionBackend::runCurrentStatementAndGetNextState().
+// And we callback to the script in SQLTransaction::deliverStatementCallback() if
+// necessary.
+// - Inside SQLTransaction::deliverStatementCallback(), we operate on a raw SQLStatement*.
+// This pointer is valid because it is owned by SQLTransactionBackend's
+// SQLTransactionBackend::m_currentStatementBackend.
+//
+// After we're done executing the statement (in SQLTransactionBackend::getNextStatement()):
+// =======================================================================================
+// When we're done executing, we'll grab the next statement. But before we
+// do that, getNextStatement() nullify SQLTransactionBackend::m_currentStatementBackend.
+// This will trigger the deletion of the SQLStatement and SQLStatement.
+//
+// Note: unlike with SQLTransaction, there is no JS representation of SQLStatement.
+// Hence, there is no GC dependency at play here.
+
namespace WebCore {
-PassOwnPtr<SQLStatement> SQLStatement::create(Database* database,
- PassRefPtr<SQLStatementCallback> callback, PassRefPtr<SQLStatementErrorCallback> errorCallback)
+SQLStatement::SQLStatement(Database& database, const String& statement, Vector<SQLValue>&& arguments, RefPtr<SQLStatementCallback>&& callback, RefPtr<SQLStatementErrorCallback>&& errorCallback, int permissions)
+ : m_statement(statement.isolatedCopy())
+ , m_arguments(WTFMove(arguments))
+ , m_statementCallbackWrapper(WTFMove(callback), &database.scriptExecutionContext())
+ , m_statementErrorCallbackWrapper(WTFMove(errorCallback), &database.scriptExecutionContext())
+ , m_permissions(permissions)
{
- return adoptPtr(new SQLStatement(database, callback, errorCallback));
}
-SQLStatement::SQLStatement(Database* database, PassRefPtr<SQLStatementCallback> callback,
- PassRefPtr<SQLStatementErrorCallback> errorCallback)
- : m_statementCallbackWrapper(callback, database->scriptExecutionContext())
- , m_statementErrorCallbackWrapper(errorCallback, database->scriptExecutionContext())
+SQLStatement::~SQLStatement()
{
}
-void SQLStatement::setBackend(AbstractSQLStatementBackend* backend)
+SQLError* SQLStatement::sqlError() const
{
- m_backend = backend;
+ return m_error.get();
}
-bool SQLStatement::hasCallback()
+SQLResultSet* SQLStatement::sqlResultSet() const
{
- return m_statementCallbackWrapper.hasCallback();
+ return m_resultSet.get();
}
-bool SQLStatement::hasErrorCallback()
+bool SQLStatement::execute(Database& db)
{
- return m_statementErrorCallbackWrapper.hasCallback();
+ ASSERT(!m_resultSet);
+
+ // If we're re-running this statement after a quota violation, we need to clear that error now
+ clearFailureDueToQuota();
+
+ // This transaction might have been marked bad while it was being set up on the main thread,
+ // so if there is still an error, return false.
+ if (m_error)
+ return false;
+
+ db.setAuthorizerPermissions(m_permissions);
+
+ SQLiteDatabase& database = db.sqliteDatabase();
+
+ SQLiteStatement statement(database, m_statement);
+ int result = statement.prepare();
+
+ if (result != SQLITE_OK) {
+ LOG(StorageAPI, "Unable to verify correctness of statement %s - error %i (%s)", m_statement.ascii().data(), result, database.lastErrorMsg());
+ if (result == SQLITE_INTERRUPT)
+ m_error = SQLError::create(SQLError::DATABASE_ERR, "could not prepare statement", result, "interrupted");
+ else
+ m_error = SQLError::create(SQLError::SYNTAX_ERR, "could not prepare statement", result, database.lastErrorMsg());
+ return false;
+ }
+
+ // FIXME: If the statement uses the ?### syntax supported by sqlite, the bind parameter count is very likely off from the number of question marks.
+ // If this is the case, they might be trying to do something fishy or malicious
+ if (statement.bindParameterCount() != m_arguments.size()) {
+ LOG(StorageAPI, "Bind parameter count doesn't match number of question marks");
+ m_error = SQLError::create(SQLError::SYNTAX_ERR, "number of '?'s in statement string does not match argument count");
+ return false;
+ }
+
+ for (unsigned i = 0; i < m_arguments.size(); ++i) {
+ result = statement.bindValue(i + 1, m_arguments[i]);
+ if (result == SQLITE_FULL) {
+ setFailureDueToQuota();
+ return false;
+ }
+
+ if (result != SQLITE_OK) {
+ LOG(StorageAPI, "Failed to bind value index %i to statement for query '%s'", i + 1, m_statement.ascii().data());
+ m_error = SQLError::create(SQLError::DATABASE_ERR, "could not bind value", result, database.lastErrorMsg());
+ return false;
+ }
+ }
+
+ RefPtr<SQLResultSet> resultSet = SQLResultSet::create();
+
+ // Step so we can fetch the column names.
+ result = statement.step();
+ switch (result) {
+ case SQLITE_ROW: {
+ int columnCount = statement.columnCount();
+ auto& rows = resultSet->rows();
+
+ for (int i = 0; i < columnCount; i++)
+ rows.addColumn(statement.getColumnName(i));
+
+ do {
+ for (int i = 0; i < columnCount; i++)
+ rows.addResult(statement.getColumnValue(i));
+
+ result = statement.step();
+ } while (result == SQLITE_ROW);
+
+ if (result != SQLITE_DONE) {
+ m_error = SQLError::create(SQLError::DATABASE_ERR, "could not iterate results", result, database.lastErrorMsg());
+ return false;
+ }
+ break;
+ }
+ case SQLITE_DONE: {
+ // Didn't find anything, or was an insert
+ if (db.lastActionWasInsert())
+ resultSet->setInsertId(database.lastInsertRowID());
+ break;
+ }
+ case SQLITE_FULL:
+ // Return the Quota error - the delegate will be asked for more space and this statement might be re-run
+ setFailureDueToQuota();
+ return false;
+ case SQLITE_CONSTRAINT:
+ m_error = SQLError::create(SQLError::CONSTRAINT_ERR, "could not execute statement due to a constaint failure", result, database.lastErrorMsg());
+ return false;
+ default:
+ m_error = SQLError::create(SQLError::DATABASE_ERR, "could not execute statement", result, database.lastErrorMsg());
+ return false;
+ }
+
+ // FIXME: If the spec allows triggers, and we want to be "accurate" in a different way, we'd use
+ // sqlite3_total_changes() here instead of sqlite3_changed, because that includes rows modified from within a trigger
+ // For now, this seems sufficient
+ resultSet->setRowsAffected(database.lastChanges());
+
+ m_resultSet = resultSet;
+ return true;
}
bool SQLStatement::performCallback(SQLTransaction* transaction)
{
ASSERT(transaction);
- ASSERT(m_backend);
bool callbackError = false;
RefPtr<SQLStatementCallback> callback = m_statementCallbackWrapper.unwrap();
RefPtr<SQLStatementErrorCallback> errorCallback = m_statementErrorCallbackWrapper.unwrap();
- RefPtr<SQLError> error = m_backend->sqlError();
+ RefPtr<SQLError> error = sqlError();
// Call the appropriate statement callback and track if it resulted in an error,
// because then we need to jump to the transaction error callback.
@@ -90,13 +215,40 @@ bool SQLStatement::performCallback(SQLTransaction* transaction)
if (errorCallback)
callbackError = errorCallback->handleEvent(transaction, error.get());
} else if (callback) {
- RefPtr<SQLResultSet> resultSet = m_backend->sqlResultSet();
+ RefPtr<SQLResultSet> resultSet = sqlResultSet();
callbackError = !callback->handleEvent(transaction, resultSet.get());
}
return callbackError;
}
-} // namespace WebCore
+void SQLStatement::setDatabaseDeletedError()
+{
+ ASSERT(!m_error && !m_resultSet);
+ m_error = SQLError::create(SQLError::UNKNOWN_ERR, "unable to execute statement, because the user deleted the database");
+}
+
+void SQLStatement::setVersionMismatchedError()
+{
+ ASSERT(!m_error && !m_resultSet);
+ m_error = SQLError::create(SQLError::VERSION_ERR, "current version of the database and `oldVersion` argument do not match");
+}
-#endif // ENABLE(SQL_DATABASE)
+void SQLStatement::setFailureDueToQuota()
+{
+ ASSERT(!m_error && !m_resultSet);
+ m_error = SQLError::create(SQLError::QUOTA_ERR, "there was not enough remaining storage space, or the storage quota was reached and the user declined to allow more space");
+}
+
+void SQLStatement::clearFailureDueToQuota()
+{
+ if (lastExecutionFailedDueToQuota())
+ m_error = nullptr;
+}
+
+bool SQLStatement::lastExecutionFailedDueToQuota() const
+{
+ return m_error && m_error->code() == SQLError::QUOTA_ERR;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/Modules/webdatabase/SQLStatement.h b/Source/WebCore/Modules/webdatabase/SQLStatement.h
index 4fdb2dab1..239ea933c 100644
--- a/Source/WebCore/Modules/webdatabase/SQLStatement.h
+++ b/Source/WebCore/Modules/webdatabase/SQLStatement.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.
*
@@ -20,19 +20,17 @@
* 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
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (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 SQLStatement_h
-#define SQLStatement_h
-#if ENABLE(SQL_DATABASE)
+#pragma once
-#include "AbstractSQLStatement.h"
#include "SQLCallbackWrapper.h"
-#include "SQLResultSet.h"
+#include "SQLStatementCallback.h"
+#include "SQLStatementErrorCallback.h"
#include "SQLValue.h"
#include <wtf/Forward.h>
#include <wtf/Vector.h>
@@ -40,39 +38,42 @@
namespace WebCore {
-class AbstractSQLStatementBackend;
class Database;
class SQLError;
-class SQLStatementCallback;
-class SQLStatementErrorCallback;
-class SQLTransaction;
+class SQLResultSet;
+class SQLTransactionBackend;
-class SQLStatement : public AbstractSQLStatement {
+class SQLStatement {
public:
- static PassOwnPtr<SQLStatement> create(Database*,
- PassRefPtr<SQLStatementCallback>, PassRefPtr<SQLStatementErrorCallback>);
+ SQLStatement(Database&, const String&, Vector<SQLValue>&&, RefPtr<SQLStatementCallback>&&, RefPtr<SQLStatementErrorCallback>&&, int permissions);
+ ~SQLStatement();
+ bool execute(Database&);
+ bool lastExecutionFailedDueToQuota() const;
+
+ bool hasStatementCallback() const { return m_statementCallbackWrapper.hasCallback(); }
+ bool hasStatementErrorCallback() const { return m_statementErrorCallbackWrapper.hasCallback(); }
bool performCallback(SQLTransaction*);
- virtual void setBackend(AbstractSQLStatementBackend*);
+ void setDatabaseDeletedError();
+ void setVersionMismatchedError();
- virtual bool hasCallback();
- virtual bool hasErrorCallback();
+ SQLError* sqlError() const;
+ SQLResultSet* sqlResultSet() const;
private:
- SQLStatement(Database*, PassRefPtr<SQLStatementCallback>, PassRefPtr<SQLStatementErrorCallback>);
-
- // The AbstractSQLStatementBackend owns the SQLStatement. Hence, the backend is
- // guaranteed to be outlive the SQLStatement, and it is safe for us to refer
- // to the backend using a raw pointer here.
- AbstractSQLStatementBackend* m_backend;
+ void setFailureDueToQuota();
+ void clearFailureDueToQuota();
+ String m_statement;
+ Vector<SQLValue> m_arguments;
SQLCallbackWrapper<SQLStatementCallback> m_statementCallbackWrapper;
SQLCallbackWrapper<SQLStatementErrorCallback> m_statementErrorCallbackWrapper;
-};
-} // namespace WebCore
+ RefPtr<SQLError> m_error;
+ RefPtr<SQLResultSet> m_resultSet;
-#endif // ENABLE(SQL_DATABASE)
+ int m_permissions;
+};
-#endif // SQLStatement_h
+} // namespace WebCore
diff --git a/Source/WebCore/Modules/webdatabase/SQLStatementBackend.cpp b/Source/WebCore/Modules/webdatabase/SQLStatementBackend.cpp
deleted file mode 100644
index 3873e2237..000000000
--- a/Source/WebCore/Modules/webdatabase/SQLStatementBackend.cpp
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * Copyright (C) 2007, 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.
- * 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 "SQLStatementBackend.h"
-
-#if ENABLE(SQL_DATABASE)
-
-#include "AbstractSQLStatement.h"
-#include "DatabaseBackend.h"
-#include "Logging.h"
-#include "SQLError.h"
-#include "SQLValue.h"
-#include "SQLiteDatabase.h"
-#include "SQLiteStatement.h"
-#include <wtf/text/CString.h>
-
-
-// The Life-Cycle of a SQLStatement i.e. Who's keeping the SQLStatement alive?
-// ==========================================================================
-// The RefPtr chain goes something like this:
-//
-// At birth (in SQLTransactionBackend::executeSQL()):
-// =================================================
-// SQLTransactionBackend // Deque<RefPtr<SQLStatementBackend>> m_statementQueue points to ...
-// --> SQLStatementBackend // OwnPtr<SQLStatement> m_frontend points to ...
-// --> SQLStatement
-//
-// After grabbing the statement for execution (in SQLTransactionBackend::getNextStatement()):
-// =========================================================================================
-// SQLTransactionBackend // RefPtr<SQLStatementBackend> m_currentStatementBackend points to ...
-// --> SQLStatementBackend // OwnPtr<SQLStatement> m_frontend points to ...
-// --> SQLStatement
-//
-// Then we execute the statement in SQLTransactionBackend::runCurrentStatementAndGetNextState().
-// And we callback to the script in SQLTransaction::deliverStatementCallback() if
-// necessary.
-// - Inside SQLTransaction::deliverStatementCallback(), we operate on a raw SQLStatement*.
-// This pointer is valid because it is owned by SQLTransactionBackend's
-// SQLTransactionBackend::m_currentStatementBackend.
-//
-// After we're done executing the statement (in SQLTransactionBackend::getNextStatement()):
-// =======================================================================================
-// When we're done executing, we'll grab the next statement. But before we
-// do that, getNextStatement() nullify SQLTransactionBackend::m_currentStatementBackend.
-// This will trigger the deletion of the SQLStatementBackend and SQLStatement.
-//
-// Note: unlike with SQLTransaction, there is no JS representation of SQLStatement.
-// Hence, there is no GC dependency at play here.
-
-namespace WebCore {
-
-PassRefPtr<SQLStatementBackend> SQLStatementBackend::create(PassOwnPtr<AbstractSQLStatement> frontend,
- const String& statement, const Vector<SQLValue>& arguments, int permissions)
-{
- return adoptRef(new SQLStatementBackend(frontend, statement, arguments, permissions));
-}
-
-SQLStatementBackend::SQLStatementBackend(PassOwnPtr<AbstractSQLStatement> frontend,
- const String& statement, const Vector<SQLValue>& arguments, int permissions)
- : m_frontend(frontend)
- , m_statement(statement.isolatedCopy())
- , m_arguments(arguments)
- , m_hasCallback(m_frontend->hasCallback())
- , m_hasErrorCallback(m_frontend->hasErrorCallback())
- , m_permissions(permissions)
-{
- m_frontend->setBackend(this);
-}
-
-AbstractSQLStatement* SQLStatementBackend::frontend()
-{
- return m_frontend.get();
-}
-
-PassRefPtr<SQLError> SQLStatementBackend::sqlError() const
-{
- return m_error;
-}
-
-PassRefPtr<SQLResultSet> SQLStatementBackend::sqlResultSet() const
-{
- return m_resultSet;
-}
-
-bool SQLStatementBackend::execute(DatabaseBackend* db)
-{
- ASSERT(!m_resultSet);
-
- // If we're re-running this statement after a quota violation, we need to clear that error now
- clearFailureDueToQuota();
-
- // This transaction might have been marked bad while it was being set up on the main thread,
- // so if there is still an error, return false.
- if (m_error)
- return false;
-
- db->setAuthorizerPermissions(m_permissions);
-
- SQLiteDatabase* database = &db->sqliteDatabase();
-
- SQLiteStatement statement(*database, m_statement);
- int result = statement.prepare();
-
- if (result != SQLResultOk) {
- LOG(StorageAPI, "Unable to verify correctness of statement %s - error %i (%s)", m_statement.ascii().data(), result, database->lastErrorMsg());
- if (result == SQLResultInterrupt)
- m_error = SQLError::create(SQLError::DATABASE_ERR, "could not prepare statement", result, "interrupted");
- else
- m_error = SQLError::create(SQLError::SYNTAX_ERR, "could not prepare statement", result, database->lastErrorMsg());
- return false;
- }
-
- // FIXME: If the statement uses the ?### syntax supported by sqlite, the bind parameter count is very likely off from the number of question marks.
- // If this is the case, they might be trying to do something fishy or malicious
- if (statement.bindParameterCount() != m_arguments.size()) {
- LOG(StorageAPI, "Bind parameter count doesn't match number of question marks");
- m_error = SQLError::create(db->isInterrupted() ? SQLError::DATABASE_ERR : SQLError::SYNTAX_ERR, "number of '?'s in statement string does not match argument count");
- return false;
- }
-
- for (unsigned i = 0; i < m_arguments.size(); ++i) {
- result = statement.bindValue(i + 1, m_arguments[i]);
- if (result == SQLResultFull) {
- setFailureDueToQuota();
- return false;
- }
-
- if (result != SQLResultOk) {
- LOG(StorageAPI, "Failed to bind value index %i to statement for query '%s'", i + 1, m_statement.ascii().data());
- m_error = SQLError::create(SQLError::DATABASE_ERR, "could not bind value", result, database->lastErrorMsg());
- return false;
- }
- }
-
- RefPtr<SQLResultSet> resultSet = SQLResultSet::create();
-
- // Step so we can fetch the column names.
- result = statement.step();
- if (result == SQLResultRow) {
- int columnCount = statement.columnCount();
- SQLResultSetRowList* rows = resultSet->rows();
-
- for (int i = 0; i < columnCount; i++)
- rows->addColumn(statement.getColumnName(i));
-
- do {
- for (int i = 0; i < columnCount; i++)
- rows->addResult(statement.getColumnValue(i));
-
- result = statement.step();
- } while (result == SQLResultRow);
-
- if (result != SQLResultDone) {
- m_error = SQLError::create(SQLError::DATABASE_ERR, "could not iterate results", result, database->lastErrorMsg());
- return false;
- }
- } else if (result == SQLResultDone) {
- // Didn't find anything, or was an insert
- if (db->lastActionWasInsert())
- resultSet->setInsertId(database->lastInsertRowID());
- } else if (result == SQLResultFull) {
- // Return the Quota error - the delegate will be asked for more space and this statement might be re-run
- setFailureDueToQuota();
- return false;
- } else if (result == SQLResultConstraint) {
- m_error = SQLError::create(SQLError::CONSTRAINT_ERR, "could not execute statement due to a constaint failure", result, database->lastErrorMsg());
- return false;
- } else {
- m_error = SQLError::create(SQLError::DATABASE_ERR, "could not execute statement", result, database->lastErrorMsg());
- return false;
- }
-
- // FIXME: If the spec allows triggers, and we want to be "accurate" in a different way, we'd use
- // sqlite3_total_changes() here instead of sqlite3_changed, because that includes rows modified from within a trigger
- // For now, this seems sufficient
- resultSet->setRowsAffected(database->lastChanges());
-
- m_resultSet = resultSet;
- return true;
-}
-
-void SQLStatementBackend::setDatabaseDeletedError()
-{
- ASSERT(!m_error && !m_resultSet);
- m_error = SQLError::create(SQLError::UNKNOWN_ERR, "unable to execute statement, because the user deleted the database");
-}
-
-void SQLStatementBackend::setVersionMismatchedError()
-{
- ASSERT(!m_error && !m_resultSet);
- m_error = SQLError::create(SQLError::VERSION_ERR, "current version of the database and `oldVersion` argument do not match");
-}
-
-void SQLStatementBackend::setFailureDueToQuota()
-{
- ASSERT(!m_error && !m_resultSet);
- m_error = SQLError::create(SQLError::QUOTA_ERR, "there was not enough remaining storage space, or the storage quota was reached and the user declined to allow more space");
-}
-
-void SQLStatementBackend::clearFailureDueToQuota()
-{
- if (lastExecutionFailedDueToQuota())
- m_error = 0;
-}
-
-bool SQLStatementBackend::lastExecutionFailedDueToQuota() const
-{
- return m_error && m_error->code() == SQLError::QUOTA_ERR;
-}
-
-} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
diff --git a/Source/WebCore/Modules/webdatabase/SQLStatementBackend.h b/Source/WebCore/Modules/webdatabase/SQLStatementBackend.h
deleted file mode 100644
index a2d2690d9..000000000
--- a/Source/WebCore/Modules/webdatabase/SQLStatementBackend.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2007, 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.
- * 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 SQLStatementBackend_h
-#define SQLStatementBackend_h
-
-#if ENABLE(SQL_DATABASE)
-
-#include "AbstractSQLStatementBackend.h"
-#include "SQLValue.h"
-#include <wtf/Forward.h>
-#include <wtf/PassOwnPtr.h>
-#include <wtf/Vector.h>
-#include <wtf/text/WTFString.h>
-
-namespace WebCore {
-
-class AbstractSQLStatement;
-class DatabaseBackend;
-class SQLError;
-class SQLTransactionBackend;
-
-class SQLStatementBackend : public AbstractSQLStatementBackend {
-public:
- static PassRefPtr<SQLStatementBackend> create(PassOwnPtr<AbstractSQLStatement>,
- const String& sqlStatement, const Vector<SQLValue>& arguments, int permissions);
-
- bool execute(DatabaseBackend*);
- bool lastExecutionFailedDueToQuota() const;
-
- bool hasStatementCallback() const { return m_hasCallback; }
- bool hasStatementErrorCallback() const { return m_hasErrorCallback; }
-
- void setDatabaseDeletedError();
- void setVersionMismatchedError();
-
- AbstractSQLStatement* frontend();
- virtual PassRefPtr<SQLError> sqlError() const;
- virtual PassRefPtr<SQLResultSet> sqlResultSet() const;
-
-private:
- SQLStatementBackend(PassOwnPtr<AbstractSQLStatement>, const String& statement,
- const Vector<SQLValue>& arguments, int permissions);
-
- void setFailureDueToQuota();
- void clearFailureDueToQuota();
-
- OwnPtr<AbstractSQLStatement> m_frontend;
- String m_statement;
- Vector<SQLValue> m_arguments;
- bool m_hasCallback;
- bool m_hasErrorCallback;
-
- RefPtr<SQLError> m_error;
- RefPtr<SQLResultSet> m_resultSet;
-
- int m_permissions;
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
-
-#endif // SQLStatementBackend_h
diff --git a/Source/WebCore/Modules/webdatabase/SQLStatementCallback.h b/Source/WebCore/Modules/webdatabase/SQLStatementCallback.h
index 83201d2b8..6da7bc4f3 100644
--- a/Source/WebCore/Modules/webdatabase/SQLStatementCallback.h
+++ b/Source/WebCore/Modules/webdatabase/SQLStatementCallback.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,10 +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 SQLStatementCallback_h
-#define SQLStatementCallback_h
-#if ENABLE(SQL_DATABASE)
+#pragma once
#include <wtf/ThreadSafeRefCounted.h>
@@ -43,8 +41,4 @@ public:
virtual bool handleEvent(SQLTransaction*, SQLResultSet*) = 0;
};
-}
-
-#endif
-
-#endif // SQLStatementErrorCallback_h
+} // namespace WebCore
diff --git a/Source/WebCore/Modules/webdatabase/SQLStatementCallback.idl b/Source/WebCore/Modules/webdatabase/SQLStatementCallback.idl
index 58feec93b..75f81f59c 100644
--- a/Source/WebCore/Modules/webdatabase/SQLStatementCallback.idl
+++ b/Source/WebCore/Modules/webdatabase/SQLStatementCallback.idl
@@ -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,8 +26,4 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-[
- Conditional=SQL_DATABASE,
-] callback interface SQLStatementCallback {
- boolean handleEvent(SQLTransaction transaction, SQLResultSet resultSet);
-};
+callback SQLStatementCallback = void (SQLTransaction transaction, SQLResultSet resultSet);
diff --git a/Source/WebCore/Modules/webdatabase/SQLStatementErrorCallback.h b/Source/WebCore/Modules/webdatabase/SQLStatementErrorCallback.h
index c7c9f5450..ca5947ae9 100644
--- a/Source/WebCore/Modules/webdatabase/SQLStatementErrorCallback.h
+++ b/Source/WebCore/Modules/webdatabase/SQLStatementErrorCallback.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,10 +26,7 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef SQLStatementErrorCallback_h
-#define SQLStatementErrorCallback_h
-
-#if ENABLE(SQL_DATABASE)
+#pragma once
#include <wtf/ThreadSafeRefCounted.h>
@@ -44,8 +41,4 @@ public:
virtual bool handleEvent(SQLTransaction*, SQLError*) = 0;
};
-}
-
-#endif
-
-#endif // SQLStatementErrorCallback_h
+} // namespace WebCore
diff --git a/Source/WebCore/Modules/webdatabase/SQLStatementErrorCallback.idl b/Source/WebCore/Modules/webdatabase/SQLStatementErrorCallback.idl
index 5e3df6652..c68482315 100644
--- a/Source/WebCore/Modules/webdatabase/SQLStatementErrorCallback.idl
+++ b/Source/WebCore/Modules/webdatabase/SQLStatementErrorCallback.idl
@@ -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,7 +27,5 @@
*/
[
- Conditional=SQL_DATABASE,
-] callback interface SQLStatementErrorCallback {
- [Custom] boolean handleEvent(SQLTransaction transaction, SQLError error);
-};
+ Custom
+] callback SQLStatementErrorCallback = boolean (SQLTransaction transaction, SQLError error);
diff --git a/Source/WebCore/Modules/webdatabase/SQLStatementSync.cpp b/Source/WebCore/Modules/webdatabase/SQLStatementSync.cpp
deleted file mode 100644
index 30ad956e4..000000000
--- a/Source/WebCore/Modules/webdatabase/SQLStatementSync.cpp
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2007 Apple Inc. All rights reserved.
- * 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 "SQLStatementSync.h"
-
-#if ENABLE(SQL_DATABASE)
-
-#include "DatabaseSync.h"
-#include "SQLException.h"
-#include "SQLResultSet.h"
-#include "SQLValue.h"
-#include "SQLiteDatabase.h"
-#include "SQLiteStatement.h"
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefPtr.h>
-
-namespace WebCore {
-
-SQLStatementSync::SQLStatementSync(const String& statement, const Vector<SQLValue>& arguments, int permissions)
- : m_statement(statement)
- , m_arguments(arguments)
- , m_permissions(permissions)
-{
- ASSERT(!m_statement.isEmpty());
-}
-
-PassRefPtr<SQLResultSet> SQLStatementSync::execute(DatabaseSync* db, ExceptionCode& ec)
-{
- db->setAuthorizerPermissions(m_permissions);
-
- SQLiteDatabase* database = &db->sqliteDatabase();
-
- SQLiteStatement statement(*database, m_statement);
- int result = statement.prepare();
- if (result != SQLResultOk) {
- ec = (result == SQLResultInterrupt ? SQLException::DATABASE_ERR : SQLException::SYNTAX_ERR);
- db->setLastErrorMessage("could not prepare statement", result, database->lastErrorMsg());
- return 0;
- }
-
- if (statement.bindParameterCount() != m_arguments.size()) {
- ec = (db->isInterrupted()? SQLException::DATABASE_ERR : SQLException::SYNTAX_ERR);
- db->setLastErrorMessage("number of '?'s in statement string does not match argument count");
- return 0;
- }
-
- for (unsigned i = 0; i < m_arguments.size(); ++i) {
- result = statement.bindValue(i + 1, m_arguments[i]);
- if (result == SQLResultFull) {
- ec = SQLException::QUOTA_ERR;
- db->setLastErrorMessage("there was not enough remaining storage space");
- return 0;
- }
-
- if (result != SQLResultOk) {
- ec = SQLException::DATABASE_ERR;
- db->setLastErrorMessage("could not bind value", result, database->lastErrorMsg());
- return 0;
- }
- }
-
- RefPtr<SQLResultSet> resultSet = SQLResultSet::create();
-
- // Step so we can fetch the column names.
- result = statement.step();
- if (result == SQLResultRow) {
- int columnCount = statement.columnCount();
- SQLResultSetRowList* rows = resultSet->rows();
-
- for (int i = 0; i < columnCount; i++)
- rows->addColumn(statement.getColumnName(i));
-
- do {
- for (int i = 0; i < columnCount; i++)
- rows->addResult(statement.getColumnValue(i));
-
- result = statement.step();
- } while (result == SQLResultRow);
-
- if (result != SQLResultDone) {
- ec = SQLException::DATABASE_ERR;
- db->setLastErrorMessage("could not iterate results", result, database->lastErrorMsg());
- return 0;
- }
- } else if (result == SQLResultDone) {
- // Didn't find anything, or was an insert.
- if (db->lastActionWasInsert())
- resultSet->setInsertId(database->lastInsertRowID());
- } else if (result == SQLResultFull) {
- // Quota error, the delegate will be asked for more space and this statement might be re-run.
- ec = SQLException::QUOTA_ERR;
- db->setLastErrorMessage("there was not enough remaining storage space");
- return 0;
- } else if (result == SQLResultConstraint) {
- ec = SQLException::CONSTRAINT_ERR;
- db->setLastErrorMessage("statement failed due to a constraint failure");
- return 0;
- } else {
- ec = SQLException::DATABASE_ERR;
- db->setLastErrorMessage("could not execute statement", result, database->lastErrorMsg());
- return 0;
- }
-
- resultSet->setRowsAffected(database->lastChanges());
- return resultSet.release();
-}
-
-} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
diff --git a/Source/WebCore/Modules/webdatabase/SQLStatementSync.h b/Source/WebCore/Modules/webdatabase/SQLStatementSync.h
deleted file mode 100644
index 3278b9587..000000000
--- a/Source/WebCore/Modules/webdatabase/SQLStatementSync.h
+++ /dev/null
@@ -1,63 +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:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google 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 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
- * OWNER 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.
- */
-
-#ifndef SQLStatementSync_h
-#define SQLStatementSync_h
-
-#if ENABLE(SQL_DATABASE)
-
-#include "DatabaseBasicTypes.h"
-#include "SQLValue.h"
-#include <wtf/Forward.h>
-#include <wtf/Vector.h>
-#include <wtf/text/WTFString.h>
-
-namespace WebCore {
-
-class DatabaseSync;
-class SQLResultSet;
-
-class SQLStatementSync {
-public:
- SQLStatementSync(const String& statement, const Vector<SQLValue>& arguments, int permissions);
-
- PassRefPtr<SQLResultSet> execute(DatabaseSync*, ExceptionCode&);
-
-private:
- String m_statement;
- Vector<SQLValue> m_arguments;
- int m_permissions;
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
-
-#endif // SQLStatementSync_h
diff --git a/Source/WebCore/Modules/webdatabase/SQLTransaction.cpp b/Source/WebCore/Modules/webdatabase/SQLTransaction.cpp
index 2b4f7bb22..76cdd7c7b 100644
--- a/Source/WebCore/Modules/webdatabase/SQLTransaction.cpp
+++ b/Source/WebCore/Modules/webdatabase/SQLTransaction.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.
*
@@ -29,65 +29,110 @@
#include "config.h"
#include "SQLTransaction.h"
-#if ENABLE(SQL_DATABASE)
-
-#include "AbstractSQLTransactionBackend.h"
#include "Database.h"
#include "DatabaseAuthorizer.h"
#include "DatabaseContext.h"
+#include "DatabaseThread.h"
+#include "DatabaseTracker.h"
#include "ExceptionCode.h"
#include "Logging.h"
+#include "OriginLock.h"
#include "SQLError.h"
+#include "SQLStatement.h"
#include "SQLStatementCallback.h"
#include "SQLStatementErrorCallback.h"
+#include "SQLTransactionBackend.h"
#include "SQLTransactionCallback.h"
-#include "SQLTransactionClient.h" // FIXME: Should be used in the backend only.
+#include "SQLTransactionCoordinator.h"
#include "SQLTransactionErrorCallback.h"
+#include "SQLiteTransaction.h"
#include "VoidCallback.h"
#include <wtf/StdLibExtras.h>
#include <wtf/Vector.h>
namespace WebCore {
-PassRefPtr<SQLTransaction> SQLTransaction::create(Database* db, PassRefPtr<SQLTransactionCallback> callback,
- PassRefPtr<VoidCallback> successCallback, PassRefPtr<SQLTransactionErrorCallback> errorCallback,
- bool readOnly)
+Ref<SQLTransaction> SQLTransaction::create(Ref<Database>&& database, RefPtr<SQLTransactionCallback>&& callback, RefPtr<VoidCallback>&& successCallback, RefPtr<SQLTransactionErrorCallback>&& errorCallback, RefPtr<SQLTransactionWrapper>&& wrapper, bool readOnly)
{
- return adoptRef(new SQLTransaction(db, callback, successCallback, errorCallback, readOnly));
+ return adoptRef(*new SQLTransaction(WTFMove(database), WTFMove(callback), WTFMove(successCallback), WTFMove(errorCallback), WTFMove(wrapper), readOnly));
}
-SQLTransaction::SQLTransaction(Database* db, PassRefPtr<SQLTransactionCallback> callback,
- PassRefPtr<VoidCallback> successCallback, PassRefPtr<SQLTransactionErrorCallback> errorCallback,
- bool readOnly)
- : m_database(db)
- , m_callbackWrapper(callback, db->scriptExecutionContext())
- , m_successCallbackWrapper(successCallback, db->scriptExecutionContext())
- , m_errorCallbackWrapper(errorCallback, db->scriptExecutionContext())
- , m_executeSqlAllowed(false)
+SQLTransaction::SQLTransaction(Ref<Database>&& database, RefPtr<SQLTransactionCallback>&& callback, RefPtr<VoidCallback>&& successCallback, RefPtr<SQLTransactionErrorCallback>&& errorCallback, RefPtr<SQLTransactionWrapper>&& wrapper, bool readOnly)
+ : m_database(WTFMove(database))
+ , m_callbackWrapper(WTFMove(callback), &m_database->scriptExecutionContext())
+ , m_successCallbackWrapper(WTFMove(successCallback), &m_database->scriptExecutionContext())
+ , m_errorCallbackWrapper(WTFMove(errorCallback), &m_database->scriptExecutionContext())
+ , m_wrapper(WTFMove(wrapper))
+ , m_nextStep(&SQLTransaction::acquireLock)
, m_readOnly(readOnly)
+ , m_backend(*this)
+{
+}
+
+SQLTransaction::~SQLTransaction()
+{
+}
+
+ExceptionOr<void> SQLTransaction::executeSql(const String& sqlStatement, std::optional<Vector<SQLValue>>&& arguments, RefPtr<SQLStatementCallback>&& callback, RefPtr<SQLStatementErrorCallback>&& callbackError)
+{
+ if (!m_executeSqlAllowed || !m_database->opened())
+ return Exception { INVALID_STATE_ERR };
+
+ int permissions = DatabaseAuthorizer::ReadWriteMask;
+ if (!m_database->databaseContext().allowDatabaseAccess())
+ permissions |= DatabaseAuthorizer::NoAccessMask;
+ else if (m_readOnly)
+ permissions |= DatabaseAuthorizer::ReadOnlyMask;
+
+ auto statement = std::make_unique<SQLStatement>(m_database, sqlStatement, arguments.value_or(Vector<SQLValue> { }), WTFMove(callback), WTFMove(callbackError), permissions);
+
+ if (m_database->deleted())
+ statement->setDatabaseDeletedError();
+
+ enqueueStatement(WTFMove(statement));
+
+ return { };
+}
+
+void SQLTransaction::lockAcquired()
{
- ASSERT(m_database);
+ m_lockAcquired = true;
+
+ m_backend.m_requestedState = SQLTransactionState::OpenTransactionAndPreflight;
+ m_database->scheduleTransactionStep(*this);
}
-bool SQLTransaction::hasCallback() const
+void SQLTransaction::performNextStep()
{
- return m_callbackWrapper.hasCallback();
+ m_backend.computeNextStateAndCleanupIfNeeded();
+ m_backend.runStateMachine();
}
-bool SQLTransaction::hasSuccessCallback() const
+void SQLTransaction::performPendingCallback()
{
- return m_successCallbackWrapper.hasCallback();
+ LOG(StorageAPI, "Callback %s\n", debugStepName(m_nextStep));
+
+ ASSERT(m_nextStep == &SQLTransaction::deliverTransactionCallback
+ || m_nextStep == &SQLTransaction::deliverTransactionErrorCallback
+ || m_nextStep == &SQLTransaction::deliverStatementCallback
+ || m_nextStep == &SQLTransaction::deliverQuotaIncreaseCallback
+ || m_nextStep == &SQLTransaction::deliverSuccessCallback);
+
+ checkAndHandleClosedDatabase();
+
+ if (m_nextStep)
+ (this->*m_nextStep)();
}
-bool SQLTransaction::hasErrorCallback() const
+void SQLTransaction::notifyDatabaseThreadIsShuttingDown()
{
- return m_errorCallbackWrapper.hasCallback();
+ m_backend.notifyDatabaseThreadIsShuttingDown();
}
-void SQLTransaction::setBackend(AbstractSQLTransactionBackend* backend)
+void SQLTransaction::enqueueStatement(std::unique_ptr<SQLStatement> statement)
{
- ASSERT(!m_backend);
- m_backend = backend;
+ LockHolder locker(m_statementMutex);
+ m_statementQueue.append(WTFMove(statement));
}
SQLTransaction::StateFunction SQLTransaction::stateFunctionFor(SQLTransactionState state)
@@ -97,10 +142,10 @@ SQLTransaction::StateFunction SQLTransaction::stateFunctionFor(SQLTransactionSta
&SQLTransaction::unreachableState, // 1. idle
&SQLTransaction::unreachableState, // 2. acquireLock
&SQLTransaction::unreachableState, // 3. openTransactionAndPreflight
- &SQLTransaction::sendToBackendState, // 4. runStatements
+ &SQLTransaction::unreachableState, // 4. runStatements
&SQLTransaction::unreachableState, // 5. postflightAndCommit
- &SQLTransaction::sendToBackendState, // 6. cleanupAndTerminate
- &SQLTransaction::sendToBackendState, // 7. cleanupAfterTransactionErrorCallback
+ &SQLTransaction::unreachableState, // 6. cleanupAndTerminate
+ &SQLTransaction::unreachableState, // 7. cleanupAfterTransactionErrorCallback
&SQLTransaction::deliverTransactionCallback, // 8.
&SQLTransaction::deliverTransactionErrorCallback, // 9.
&SQLTransaction::deliverStatementCallback, // 10.
@@ -124,18 +169,201 @@ void SQLTransaction::requestTransitToState(SQLTransactionState nextState)
m_database->scheduleTransactionCallback(this);
}
-SQLTransactionState SQLTransaction::nextStateForTransactionError()
+void SQLTransaction::checkAndHandleClosedDatabase()
{
- ASSERT(m_transactionError);
- if (m_errorCallbackWrapper.hasCallback())
- return SQLTransactionState::DeliverTransactionErrorCallback;
+ if (m_database->opened())
+ return;
+
+ // If the database was stopped, don't do anything and cancel queued work
+ LOG(StorageAPI, "Database was stopped or interrupted - cancelling work for this transaction");
+
+ LockHolder locker(m_statementMutex);
+ m_statementQueue.clear();
+ m_nextStep = nullptr;
+
+ // Release the unneeded callbacks, to break reference cycles.
+ m_callbackWrapper.clear();
+ m_successCallbackWrapper.clear();
+ m_errorCallbackWrapper.clear();
+
+ // The next steps should be executed only if we're on the DB thread.
+ if (currentThread() != m_database->databaseThread().getThreadID())
+ return;
+
+ // The current SQLite transaction should be stopped, as well
+ if (m_sqliteTransaction) {
+ m_sqliteTransaction->stop();
+ m_sqliteTransaction = nullptr;
+ }
+
+ if (m_lockAcquired)
+ m_database->transactionCoordinator()->releaseLock(*this);
+}
+
+void SQLTransaction::scheduleCallback(void (SQLTransaction::*step)())
+{
+ m_nextStep = step;
+
+ LOG(StorageAPI, "Scheduling %s for transaction %p\n", debugStepName(step), this);
+ m_database->scheduleTransactionCallback(this);
+}
+
+void SQLTransaction::acquireLock()
+{
+ m_database->transactionCoordinator()->acquireLock(*this);
+}
+
+void SQLTransaction::openTransactionAndPreflight()
+{
+ ASSERT(!m_database->sqliteDatabase().transactionInProgress());
+ ASSERT(m_lockAcquired);
+
+ LOG(StorageAPI, "Opening and preflighting transaction %p", this);
+
+ // If the database was deleted, jump to the error callback
+ if (m_database->deleted()) {
+ m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unable to open a transaction, because the user deleted the database");
+
+ handleTransactionError();
+ return;
+ }
+
+ // Set the maximum usage for this transaction if this transactions is not read-only
+ if (!m_readOnly) {
+ acquireOriginLock();
+ m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize());
+ }
+
+ ASSERT(!m_sqliteTransaction);
+ m_sqliteTransaction = std::make_unique<SQLiteTransaction>(m_database->sqliteDatabase(), m_readOnly);
+
+ m_database->resetDeletes();
+ m_database->disableAuthorizer();
+ m_sqliteTransaction->begin();
+ m_database->enableAuthorizer();
+
+ // Spec 4.3.2.1+2: Open a transaction to the database, jumping to the error callback if that fails
+ if (!m_sqliteTransaction->inProgress()) {
+ ASSERT(!m_database->sqliteDatabase().transactionInProgress());
+ m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to begin transaction", m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg());
+ m_sqliteTransaction = nullptr;
+
+ handleTransactionError();
+ return;
+ }
+
+ // Note: We intentionally retrieve the actual version even with an empty expected version.
+ // In multi-process browsers, we take this opportinutiy to update the cached value for
+ // the actual version. In single-process browsers, this is just a map lookup.
+ String actualVersion;
+ if (!m_database->getActualVersionForTransaction(actualVersion)) {
+ m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to read version", m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg());
+ m_database->disableAuthorizer();
+ m_sqliteTransaction = nullptr;
+ m_database->enableAuthorizer();
+
+ handleTransactionError();
+ return;
+ }
+
+ m_hasVersionMismatch = !m_database->expectedVersion().isEmpty() && (m_database->expectedVersion() != actualVersion);
+
+ // Spec 4.3.2.3: Perform preflight steps, jumping to the error callback if they fail
+ if (m_wrapper && !m_wrapper->performPreflight(*this)) {
+ m_database->disableAuthorizer();
+ m_sqliteTransaction = nullptr;
+ m_database->enableAuthorizer();
+ m_transactionError = m_wrapper->sqlError();
+ if (!m_transactionError)
+ m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unknown error occurred during transaction preflight");
+
+ handleTransactionError();
+ return;
+ }
+
+ // Spec 4.3.2.4: Invoke the transaction callback with the new SQLTransaction object
+ if (m_callbackWrapper.hasCallback()) {
+ scheduleCallback(&SQLTransaction::deliverTransactionCallback);
+ return;
+ }
+
+ // If we have no callback to make, skip pass to the state after:
+ runStatements();
+}
+
+void SQLTransaction::runStatements()
+{
+ ASSERT(m_lockAcquired);
+
+ // If there is a series of statements queued up that are all successful and have no associated
+ // SQLStatementCallback objects, then we can burn through the queue
+ do {
+ if (m_shouldRetryCurrentStatement && !m_sqliteTransaction->wasRolledBackBySqlite()) {
+ m_shouldRetryCurrentStatement = false;
+ // FIXME - Another place that needs fixing up after <rdar://problem/5628468> is addressed.
+ // See ::openTransactionAndPreflight() for discussion
+
+ // Reset the maximum size here, as it was increased to allow us to retry this statement.
+ // m_shouldRetryCurrentStatement is set to true only when a statement exceeds
+ // the quota, which can happen only in a read-write transaction. Therefore, there
+ // is no need to check here if the transaction is read-write.
+ m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize());
+ } else {
+ // If the current statement has already been run, failed due to quota constraints, and we're not retrying it,
+ // that means it ended in an error. Handle it now
+ if (m_currentStatement && m_currentStatement->lastExecutionFailedDueToQuota()) {
+ handleCurrentStatementError();
+ break;
+ }
+
+ // Otherwise, advance to the next statement
+ getNextStatement();
+ }
+ } while (runCurrentStatement());
+
+ // If runCurrentStatement() returned false, that means either there was no current statement to run,
+ // or the current statement requires a callback to complete. In the later case, it also scheduled
+ // the callback or performed any other additional work so we can return.
+ if (!m_currentStatement)
+ postflightAndCommit();
+}
+
+void SQLTransaction::cleanupAndTerminate()
+{
+ ASSERT(m_lockAcquired);
+
+ // Spec 4.3.2.9: End transaction steps. There is no next step.
+ LOG(StorageAPI, "Transaction %p is complete\n", this);
+ ASSERT(!m_database->sqliteDatabase().transactionInProgress());
+
+ // Phase 5 cleanup. See comment on the SQLTransaction life-cycle above.
+ m_backend.doCleanup();
+ m_database->inProgressTransactionCompleted();
+}
- // No error callback, so fast-forward to:
- // Transaction Step 11 - Rollback the transaction.
- return SQLTransactionState::CleanupAfterTransactionErrorCallback;
+void SQLTransaction::cleanupAfterTransactionErrorCallback()
+{
+ ASSERT(m_lockAcquired);
+
+ LOG(StorageAPI, "Transaction %p is complete with an error\n", this);
+ m_database->disableAuthorizer();
+ if (m_sqliteTransaction) {
+ // Spec 4.3.2.10: Rollback the transaction.
+ m_sqliteTransaction->rollback();
+
+ ASSERT(!m_database->sqliteDatabase().transactionInProgress());
+ m_sqliteTransaction = nullptr;
+ }
+ m_database->enableAuthorizer();
+
+ releaseOriginLockIfNeeded();
+
+ ASSERT(!m_database->sqliteDatabase().transactionInProgress());
+
+ cleanupAndTerminate();
}
-SQLTransactionState SQLTransaction::deliverTransactionCallback()
+void SQLTransaction::deliverTransactionCallback()
{
bool shouldDeliverErrorCallback = false;
@@ -148,71 +376,66 @@ SQLTransactionState SQLTransaction::deliverTransactionCallback()
}
// Spec 4.3.2 5: If the transaction callback was null or raised an exception, jump to the error callback
- SQLTransactionState nextState = SQLTransactionState::RunStatements;
if (shouldDeliverErrorCallback) {
m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "the SQLTransactionCallback was null or threw an exception");
- nextState = SQLTransactionState::DeliverTransactionErrorCallback;
+ return deliverTransactionErrorCallback();
}
- return nextState;
+
+ m_backend.requestTransitToState(SQLTransactionState::RunStatements);
}
-SQLTransactionState SQLTransaction::deliverTransactionErrorCallback()
+void SQLTransaction::deliverTransactionErrorCallback()
{
+ ASSERT(m_transactionError);
+
// Spec 4.3.2.10: If exists, invoke error callback with the last
// error to have occurred in this transaction.
RefPtr<SQLTransactionErrorCallback> errorCallback = m_errorCallbackWrapper.unwrap();
- if (errorCallback) {
- // If we get here with an empty m_transactionError, then the backend
- // must be waiting in the idle state waiting for this state to finish.
- // Hence, it's thread safe to fetch the backend transactionError without
- // a lock.
- if (!m_transactionError)
- m_transactionError = m_backend->transactionError();
-
- ASSERT(m_transactionError);
+ if (errorCallback)
errorCallback->handleEvent(m_transactionError.get());
- m_transactionError = 0;
- }
-
clearCallbackWrappers();
// Spec 4.3.2.10: Rollback the transaction.
- return SQLTransactionState::CleanupAfterTransactionErrorCallback;
+ m_backend.requestTransitToState(SQLTransactionState::CleanupAfterTransactionErrorCallback);
}
-SQLTransactionState SQLTransaction::deliverStatementCallback()
+void SQLTransaction::deliverStatementCallback()
{
+ ASSERT(m_currentStatement);
+
// Spec 4.3.2.6.6 and 4.3.2.6.3: If the statement callback went wrong, jump to the transaction error callback
// Otherwise, continue to loop through the statement queue
m_executeSqlAllowed = true;
-
- AbstractSQLStatement* currentAbstractStatement = m_backend->currentStatement();
- SQLStatement* currentStatement = static_cast<SQLStatement*>(currentAbstractStatement);
- ASSERT(currentStatement);
-
- bool result = currentStatement->performCallback(this);
-
+ bool result = m_currentStatement->performCallback(this);
m_executeSqlAllowed = false;
if (result) {
m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "the statement callback raised an exception or statement error callback did not return false");
- return nextStateForTransactionError();
+
+ if (m_errorCallbackWrapper.hasCallback())
+ return deliverTransactionErrorCallback();
+
+ // No error callback, so fast-forward to:
+ // Transaction Step 11 - Rollback the transaction.
+ m_backend.requestTransitToState(SQLTransactionState::CleanupAfterTransactionErrorCallback);
+ return;
}
- return SQLTransactionState::RunStatements;
+
+ m_backend.requestTransitToState(SQLTransactionState::RunStatements);
}
-SQLTransactionState SQLTransaction::deliverQuotaIncreaseCallback()
+void SQLTransaction::deliverQuotaIncreaseCallback()
{
- ASSERT(m_backend->currentStatement());
+ ASSERT(m_currentStatement);
+ ASSERT(!m_shouldRetryCurrentStatement);
- bool shouldRetryCurrentStatement = m_database->transactionClient()->didExceedQuota(database());
- m_backend->setShouldRetryCurrentStatement(shouldRetryCurrentStatement);
+ m_shouldRetryCurrentStatement = m_database->didExceedQuota();
- return SQLTransactionState::RunStatements;
+ m_backend.requestTransitToState(SQLTransactionState::RunStatements);
}
-SQLTransactionState SQLTransaction::deliverSuccessCallback()
+void SQLTransaction::deliverSuccessCallback()
{
// Spec 4.3.2.8: Deliver success callback.
RefPtr<VoidCallback> successCallback = m_successCallbackWrapper.unwrap();
@@ -223,53 +446,22 @@ SQLTransactionState SQLTransaction::deliverSuccessCallback()
// Schedule a "post-success callback" step to return control to the database thread in case there
// are further transactions queued up for this Database
- return SQLTransactionState::CleanupAndTerminate;
+ m_backend.requestTransitToState(SQLTransactionState::CleanupAndTerminate);
}
// This state function is used as a stub function to plug unimplemented states
// in the state dispatch table. They are unimplemented because they should
// never be reached in the course of correct execution.
-SQLTransactionState SQLTransaction::unreachableState()
+void SQLTransaction::unreachableState()
{
ASSERT_NOT_REACHED();
- return SQLTransactionState::End;
-}
-
-SQLTransactionState SQLTransaction::sendToBackendState()
-{
- ASSERT(m_nextState != SQLTransactionState::Idle);
- m_backend->requestTransitToState(m_nextState);
- return SQLTransactionState::Idle;
-}
-
-void SQLTransaction::performPendingCallback()
-{
- computeNextStateAndCleanupIfNeeded();
- runStateMachine();
-}
-
-void SQLTransaction::executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, PassRefPtr<SQLStatementCallback> callback, PassRefPtr<SQLStatementErrorCallback> callbackError, ExceptionCode& e)
-{
- if (!m_executeSqlAllowed || !m_database->opened()) {
- e = INVALID_STATE_ERR;
- return;
- }
-
- int permissions = DatabaseAuthorizer::ReadWriteMask;
- if (!m_database->databaseContext()->allowDatabaseAccess())
- permissions |= DatabaseAuthorizer::NoAccessMask;
- else if (m_readOnly)
- permissions |= DatabaseAuthorizer::ReadOnlyMask;
-
- OwnPtr<SQLStatement> statement = SQLStatement::create(m_database.get(), callback, callbackError);
- m_backend->executeSQL(statement.release(), sqlStatement, arguments, permissions);
}
-bool SQLTransaction::computeNextStateAndCleanupIfNeeded()
+void SQLTransaction::computeNextStateAndCleanupIfNeeded()
{
// Only honor the requested state transition if we're not supposed to be
// cleaning up and shutting down:
- if (m_database->opened() && !m_database->isInterrupted()) {
+ if (m_database->opened()) {
setStateToRequestedState();
ASSERT(m_nextState == SQLTransactionState::End
|| m_nextState == SQLTransactionState::DeliverTransactionCallback
@@ -279,13 +471,11 @@ bool SQLTransaction::computeNextStateAndCleanupIfNeeded()
|| m_nextState == SQLTransactionState::DeliverSuccessCallback);
LOG(StorageAPI, "Callback %s\n", nameForSQLTransactionState(m_nextState));
- return false;
+ return;
}
clearCallbackWrappers();
- m_nextState = SQLTransactionState::CleanupAndTerminate;
-
- return true;
+ m_backend.requestTransitToState(SQLTransactionState::CleanupAndTerminate);
}
void SQLTransaction::clearCallbackWrappers()
@@ -296,6 +486,168 @@ void SQLTransaction::clearCallbackWrappers()
m_errorCallbackWrapper.clear();
}
-} // namespace WebCore
+void SQLTransaction::getNextStatement()
+{
+ m_currentStatement = nullptr;
+
+ LockHolder locker(m_statementMutex);
+ if (!m_statementQueue.isEmpty())
+ m_currentStatement = m_statementQueue.takeFirst();
+}
+
+bool SQLTransaction::runCurrentStatement()
+{
+ if (!m_currentStatement) {
+ // No more statements to run. So move on to the next state.
+ return false;
+ }
+
+ m_database->resetAuthorizer();
+
+ if (m_hasVersionMismatch)
+ m_currentStatement->setVersionMismatchedError();
+
+ if (m_currentStatement->execute(m_database)) {
+ if (m_database->lastActionChangedDatabase()) {
+ // Flag this transaction as having changed the database for later delegate notification
+ m_modifiedDatabase = true;
+ }
+
+ if (m_currentStatement->hasStatementCallback()) {
+ scheduleCallback(&SQLTransaction::deliverStatementCallback);
+ return false;
+ }
+
+ // If we get here, then the statement doesn't have a callback to invoke.
+ // We can move on to the next statement. Hence, stay in this state.
+ return true;
+ }
+
+ if (m_currentStatement->lastExecutionFailedDueToQuota()) {
+ scheduleCallback(&SQLTransaction::deliverQuotaIncreaseCallback);
+ return false;
+ }
-#endif // ENABLE(SQL_DATABASE)
+ handleCurrentStatementError();
+ return false;
+}
+
+void SQLTransaction::handleCurrentStatementError()
+{
+ // Spec 4.3.2.6.6: error - Call the statement's error callback, but if there was no error callback,
+ // or the transaction was rolled back, jump to the transaction error callback
+ if (m_currentStatement->hasStatementErrorCallback() && !m_sqliteTransaction->wasRolledBackBySqlite()) {
+ scheduleCallback(&SQLTransaction::deliverStatementCallback);
+ return;
+ }
+
+ m_transactionError = m_currentStatement->sqlError();
+ if (!m_transactionError)
+ m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "the statement failed to execute");
+
+ handleTransactionError();
+}
+
+void SQLTransaction::handleTransactionError()
+{
+ ASSERT(m_transactionError);
+ if (m_errorCallbackWrapper.hasCallback()) {
+ scheduleCallback(&SQLTransaction::deliverTransactionErrorCallback);
+ return;
+ }
+
+ // No error callback, so fast-forward to the next state and rollback the
+ // transaction.
+ m_backend.cleanupAfterTransactionErrorCallback();
+}
+
+void SQLTransaction::postflightAndCommit()
+{
+ ASSERT(m_lockAcquired);
+
+ // Spec 4.3.2.7: Perform postflight steps, jumping to the error callback if they fail.
+ if (m_wrapper && !m_wrapper->performPostflight(*this)) {
+ m_transactionError = m_wrapper->sqlError();
+ if (!m_transactionError)
+ m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unknown error occurred during transaction postflight");
+
+ handleTransactionError();
+ return;
+ }
+
+ // Spec 4.3.2.7: Commit the transaction, jumping to the error callback if that fails.
+ ASSERT(m_sqliteTransaction);
+
+ m_database->disableAuthorizer();
+ m_sqliteTransaction->commit();
+ m_database->enableAuthorizer();
+
+ releaseOriginLockIfNeeded();
+
+ // If the commit failed, the transaction will still be marked as "in progress"
+ if (m_sqliteTransaction->inProgress()) {
+ if (m_wrapper)
+ m_wrapper->handleCommitFailedAfterPostflight(*this);
+ m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to commit transaction", m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg());
+
+ handleTransactionError();
+ return;
+ }
+
+ // Vacuum the database if anything was deleted.
+ if (m_database->hadDeletes())
+ m_database->incrementalVacuumIfNeeded();
+
+ // The commit was successful. If the transaction modified this database, notify the delegates.
+ if (m_modifiedDatabase)
+ m_database->didCommitWriteTransaction();
+
+ // Spec 4.3.2.8: Deliver success callback, if there is one.
+ scheduleCallback(&SQLTransaction::deliverSuccessCallback);
+}
+
+void SQLTransaction::acquireOriginLock()
+{
+ ASSERT(!m_originLock);
+ m_originLock = DatabaseTracker::singleton().originLockFor(m_database->securityOrigin());
+ m_originLock->lock();
+}
+
+void SQLTransaction::releaseOriginLockIfNeeded()
+{
+ if (m_originLock) {
+ m_originLock->unlock();
+ m_originLock = nullptr;
+ }
+}
+
+#if !LOG_DISABLED
+const char* SQLTransaction::debugStepName(void (SQLTransaction::*step)())
+{
+ if (step == &SQLTransaction::acquireLock)
+ return "acquireLock";
+ if (step == &SQLTransaction::openTransactionAndPreflight)
+ return "openTransactionAndPreflight";
+ if (step == &SQLTransaction::runStatements)
+ return "runStatements";
+ if (step == &SQLTransaction::postflightAndCommit)
+ return "postflightAndCommit";
+ if (step == &SQLTransaction::cleanupAfterTransactionErrorCallback)
+ return "cleanupAfterTransactionErrorCallback";
+ if (step == &SQLTransaction::deliverTransactionCallback)
+ return "deliverTransactionCallback";
+ if (step == &SQLTransaction::deliverTransactionErrorCallback)
+ return "deliverTransactionErrorCallback";
+ if (step == &SQLTransaction::deliverStatementCallback)
+ return "deliverStatementCallback";
+ if (step == &SQLTransaction::deliverQuotaIncreaseCallback)
+ return "deliverQuotaIncreaseCallback";
+ if (step == &SQLTransaction::deliverSuccessCallback)
+ return "deliverSuccessCallback";
+
+ ASSERT_NOT_REACHED();
+ return "UNKNOWN";
+}
+#endif
+
+} // namespace WebCore
diff --git a/Source/WebCore/Modules/webdatabase/SQLTransaction.h b/Source/WebCore/Modules/webdatabase/SQLTransaction.h
index c24bcc008..81b668f7e 100644
--- a/Source/WebCore/Modules/webdatabase/SQLTransaction.h
+++ b/Source/WebCore/Modules/webdatabase/SQLTransaction.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,87 +26,125 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef SQLTransaction_h
-#define SQLTransaction_h
+#pragma once
-#if ENABLE(SQL_DATABASE)
-
-#include "AbstractSQLTransaction.h"
+#include "EventTarget.h"
+#include "ExceptionOr.h"
#include "SQLCallbackWrapper.h"
-#include "SQLStatement.h"
+#include "SQLTransactionBackend.h"
#include "SQLTransactionStateMachine.h"
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefPtr.h>
+#include "SQLValue.h"
+#include <wtf/Optional.h>
namespace WebCore {
-class AbstractSQLTransactionBackend;
class Database;
class SQLError;
class SQLStatementCallback;
class SQLStatementErrorCallback;
+class SQLTransactionBackend;
class SQLTransactionCallback;
class SQLTransactionErrorCallback;
-class SQLValue;
class VoidCallback;
-class SQLTransaction : public SQLTransactionStateMachine<SQLTransaction>, public AbstractSQLTransaction {
+class SQLTransactionWrapper : public ThreadSafeRefCounted<SQLTransactionWrapper> {
public:
- static PassRefPtr<SQLTransaction> create(Database*, PassRefPtr<SQLTransactionCallback>,
- PassRefPtr<VoidCallback> successCallback, PassRefPtr<SQLTransactionErrorCallback>,
- bool readOnly);
+ virtual ~SQLTransactionWrapper() { }
+ virtual bool performPreflight(SQLTransaction&) = 0;
+ virtual bool performPostflight(SQLTransaction&) = 0;
+ virtual SQLError* sqlError() const = 0;
+ virtual void handleCommitFailedAfterPostflight(SQLTransaction&) = 0;
+};
+
+class SQLTransaction : public ThreadSafeRefCounted<SQLTransaction>, public SQLTransactionStateMachine<SQLTransaction> {
+public:
+ static Ref<SQLTransaction> create(Ref<Database>&&, RefPtr<SQLTransactionCallback>&&, RefPtr<VoidCallback>&& successCallback, RefPtr<SQLTransactionErrorCallback>&&, RefPtr<SQLTransactionWrapper>&&, bool readOnly);
+ ~SQLTransaction();
+
+ ExceptionOr<void> executeSql(const String& sqlStatement, std::optional<Vector<SQLValue>>&& arguments, RefPtr<SQLStatementCallback>&&, RefPtr<SQLStatementErrorCallback>&&);
+ void lockAcquired();
+ void performNextStep();
void performPendingCallback();
- void executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments,
- PassRefPtr<SQLStatementCallback>, PassRefPtr<SQLStatementErrorCallback>, ExceptionCode&);
+ Database& database() { return m_database; }
+ bool isReadOnly() const { return m_readOnly; }
+ void notifyDatabaseThreadIsShuttingDown();
- Database* database() { return m_database.get(); }
+ // APIs called from the backend published via SQLTransaction:
+ void requestTransitToState(SQLTransactionState);
private:
- SQLTransaction(Database*, PassRefPtr<SQLTransactionCallback>,
- PassRefPtr<VoidCallback> successCallback, PassRefPtr<SQLTransactionErrorCallback>,
- bool readOnly);
+ friend class SQLTransactionBackend;
+
+ SQLTransaction(Ref<Database>&&, RefPtr<SQLTransactionCallback>&&, RefPtr<VoidCallback>&& successCallback, RefPtr<SQLTransactionErrorCallback>&&, RefPtr<SQLTransactionWrapper>&&, bool readOnly);
+
+ void enqueueStatement(std::unique_ptr<SQLStatement>);
+
+ void checkAndHandleClosedDatabase();
void clearCallbackWrappers();
- // APIs called from the backend published via AbstractSQLTransaction:
- virtual void requestTransitToState(SQLTransactionState) override;
- virtual bool hasCallback() const override;
- virtual bool hasSuccessCallback() const override;
- virtual bool hasErrorCallback() const override;
- virtual void setBackend(AbstractSQLTransactionBackend*) override;
+ void scheduleCallback(void (SQLTransaction::*)());
// State Machine functions:
- virtual StateFunction stateFunctionFor(SQLTransactionState) override;
- bool computeNextStateAndCleanupIfNeeded();
+ StateFunction stateFunctionFor(SQLTransactionState) override;
+ void computeNextStateAndCleanupIfNeeded();
// State functions:
- SQLTransactionState deliverTransactionCallback();
- SQLTransactionState deliverTransactionErrorCallback();
- SQLTransactionState deliverStatementCallback();
- SQLTransactionState deliverQuotaIncreaseCallback();
- SQLTransactionState deliverSuccessCallback();
-
- SQLTransactionState unreachableState();
- SQLTransactionState sendToBackendState();
-
- SQLTransactionState nextStateForTransactionError();
+ void acquireLock();
+ void openTransactionAndPreflight();
+ void runStatements();
+ void cleanupAndTerminate();
+ void cleanupAfterTransactionErrorCallback();
+ void deliverTransactionCallback();
+ void deliverTransactionErrorCallback();
+ void deliverStatementCallback();
+ void deliverQuotaIncreaseCallback();
+ void deliverSuccessCallback();
+
+ NO_RETURN_DUE_TO_ASSERT void unreachableState();
+
+ void getNextStatement();
+ bool runCurrentStatement();
+ void handleCurrentStatementError();
+ void handleTransactionError();
+ void postflightAndCommit();
+
+ void acquireOriginLock();
+ void releaseOriginLockIfNeeded();
+
+#if !LOG_DISABLED
+ static const char* debugStepName(void (SQLTransaction::*)());
+#endif
- RefPtr<Database> m_database;
- RefPtr<AbstractSQLTransactionBackend> m_backend;
+ Ref<Database> m_database;
SQLCallbackWrapper<SQLTransactionCallback> m_callbackWrapper;
SQLCallbackWrapper<VoidCallback> m_successCallbackWrapper;
SQLCallbackWrapper<SQLTransactionErrorCallback> m_errorCallbackWrapper;
- bool m_executeSqlAllowed;
+ RefPtr<SQLTransactionWrapper> m_wrapper;
+
+ void (SQLTransaction::*m_nextStep)();
+
+ bool m_executeSqlAllowed { false };
RefPtr<SQLError> m_transactionError;
- bool m_readOnly;
-};
+ bool m_shouldRetryCurrentStatement { false };
+ bool m_modifiedDatabase { false };
+ bool m_lockAcquired { false };
+ bool m_readOnly { false };
+ bool m_hasVersionMismatch { false };
-} // namespace WebCore
+ Lock m_statementMutex;
+ Deque<std::unique_ptr<SQLStatement>> m_statementQueue;
-#endif
+ std::unique_ptr<SQLStatement> m_currentStatement;
+
+ std::unique_ptr<SQLiteTransaction> m_sqliteTransaction;
+ RefPtr<OriginLock> m_originLock;
-#endif // SQLTransaction_h
+ SQLTransactionBackend m_backend;
+};
+
+} // namespace WebCore
diff --git a/Source/WebCore/Modules/webdatabase/SQLTransaction.idl b/Source/WebCore/Modules/webdatabase/SQLTransaction.idl
index 2eaecd2c4..1f53a1fe8 100644
--- a/Source/WebCore/Modules/webdatabase/SQLTransaction.idl
+++ b/Source/WebCore/Modules/webdatabase/SQLTransaction.idl
@@ -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,14 +26,10 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+typedef (DOMString? or unrestricted double) SQLValue;
+
[
- NoInterfaceObject,
- Conditional=SQL_DATABASE,
- JSNoStaticTables,
SkipVTableValidation,
] interface SQLTransaction {
- [Custom] void executeSql(DOMString sqlStatement,
- ObjectArray arguments,
- optional SQLStatementCallback callback,
- optional SQLStatementErrorCallback errorCallback);
+ [MayThrowException] void executeSql(DOMString sqlStatement, optional sequence<SQLValue>? arguments = [], optional SQLStatementCallback? callback, optional SQLStatementErrorCallback? errorCallback);
};
diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionBackend.cpp b/Source/WebCore/Modules/webdatabase/SQLTransactionBackend.cpp
index 244f28566..2ea93ddf2 100644
--- a/Source/WebCore/Modules/webdatabase/SQLTransactionBackend.cpp
+++ b/Source/WebCore/Modules/webdatabase/SQLTransactionBackend.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.
*
@@ -29,23 +29,19 @@
#include "config.h"
#include "SQLTransactionBackend.h"
-#if ENABLE(SQL_DATABASE)
-
-#include "AbstractSQLTransaction.h"
-#include "Database.h" // FIXME: Should only be used in the frontend.
+#include "Database.h"
#include "DatabaseAuthorizer.h"
-#include "DatabaseBackend.h"
-#include "DatabaseBackendContext.h"
+#include "DatabaseContext.h"
#include "DatabaseThread.h"
#include "DatabaseTracker.h"
-#include "ExceptionCode.h"
#include "Logging.h"
#include "OriginLock.h"
#include "SQLError.h"
-#include "SQLStatementBackend.h"
-#include "SQLTransactionClient.h"
+#include "SQLStatement.h"
+#include "SQLStatementCallback.h"
+#include "SQLStatementErrorCallback.h"
+#include "SQLTransaction.h"
#include "SQLTransactionCoordinator.h"
-#include "SQLValue.h"
#include "SQLiteTransaction.h"
#include <wtf/StdLibExtras.h>
#include <wtf/text/WTFString.h>
@@ -260,7 +256,7 @@
//
// When executing the transaction (in DatabaseThread::databaseThread()):
// ====================================================================
-// OwnPtr<DatabaseTask> task; // points to ...
+// std::unique_ptr<DatabaseTask> task; // points to ...
// --> DatabaseTransactionTask // RefPtr<SQLTransactionBackend> m_transaction points to ...
// --> SQLTransactionBackend // RefPtr<SQLTransaction> m_frontend;
// --> SQLTransaction // RefPtr<SQLTransactionBackend> m_backend points to ...
@@ -284,7 +280,7 @@
// However, there will still be a DatabaseTask pointing to the SQLTransactionBackend (see
// the "When executing the transaction" chain above). This will keep the
// SQLTransactionBackend alive until DatabaseThread::databaseThread() releases its
-// task OwnPtr.
+// task std::unique_ptr.
//
// What happens if a transaction is interrupted?
// ============================================
@@ -344,61 +340,38 @@
namespace WebCore {
-PassRefPtr<SQLTransactionBackend> SQLTransactionBackend::create(DatabaseBackend* db,
- PassRefPtr<AbstractSQLTransaction> frontend, PassRefPtr<SQLTransactionWrapper> wrapper, bool readOnly)
-{
- return adoptRef(new SQLTransactionBackend(db, frontend, wrapper, readOnly));
-}
-
-SQLTransactionBackend::SQLTransactionBackend(DatabaseBackend* db,
- PassRefPtr<AbstractSQLTransaction> frontend, PassRefPtr<SQLTransactionWrapper> wrapper, bool readOnly)
+SQLTransactionBackend::SQLTransactionBackend(SQLTransaction& frontend)
: m_frontend(frontend)
- , m_database(db)
- , m_wrapper(wrapper)
- , m_hasCallback(m_frontend->hasCallback())
- , m_hasSuccessCallback(m_frontend->hasSuccessCallback())
- , m_hasErrorCallback(m_frontend->hasErrorCallback())
- , m_shouldRetryCurrentStatement(false)
- , m_modifiedDatabase(false)
- , m_lockAcquired(false)
- , m_readOnly(readOnly)
- , m_hasVersionMismatch(false)
{
- ASSERT(m_database);
- m_frontend->setBackend(this);
m_requestedState = SQLTransactionState::AcquireLock;
}
SQLTransactionBackend::~SQLTransactionBackend()
{
- ASSERT(!m_sqliteTransaction);
+ ASSERT(!m_frontend.m_sqliteTransaction);
}
void SQLTransactionBackend::doCleanup()
{
- if (!m_frontend)
- return;
- m_frontend = 0; // Break the reference cycle. See comment about the life-cycle above.
-
- ASSERT(currentThread() == database()->databaseContext()->databaseThread()->getThreadID());
+ ASSERT(currentThread() == m_frontend.database().databaseThread().getThreadID());
- releaseOriginLockIfNeeded();
+ m_frontend.releaseOriginLockIfNeeded();
- MutexLocker locker(m_statementMutex);
- m_statementQueue.clear();
+ LockHolder locker(m_frontend.m_statementMutex);
+ m_frontend.m_statementQueue.clear();
- if (m_sqliteTransaction) {
+ if (m_frontend.m_sqliteTransaction) {
// In the event we got here because of an interruption or error (i.e. if
// the transaction is in progress), we should roll it back here. Clearing
// m_sqliteTransaction invokes SQLiteTransaction's destructor which does
// just that. We might as well do this unconditionally and free up its
// resources because we're already terminating.
- m_sqliteTransaction.clear();
+ m_frontend.m_sqliteTransaction = nullptr;
}
// Release the lock on this database
- if (m_lockAcquired)
- m_database->transactionCoordinator()->releaseLock(this);
+ if (m_frontend.m_lockAcquired)
+ m_frontend.m_database->transactionCoordinator()->releaseLock(m_frontend);
// Do some aggresive clean up here except for m_database.
//
@@ -423,23 +396,7 @@ void SQLTransactionBackend::doCleanup()
// SQLTransactionBackend is guaranteed to not destruct until the frontend
// is also destructing.
- m_wrapper = 0;
-}
-
-AbstractSQLStatement* SQLTransactionBackend::currentStatement()
-{
- return m_currentStatementBackend->frontend();
-}
-
-PassRefPtr<SQLError> SQLTransactionBackend::transactionError()
-{
- return m_transactionError;
-}
-
-void SQLTransactionBackend::setShouldRetryCurrentStatement(bool shouldRetry)
-{
- ASSERT(!m_shouldRetryCurrentStatement);
- m_shouldRetryCurrentStatement = shouldRetry;
+ m_frontend.m_wrapper = nullptr;
}
SQLTransactionBackend::StateFunction SQLTransactionBackend::stateFunctionFor(SQLTransactionState state)
@@ -450,14 +407,14 @@ SQLTransactionBackend::StateFunction SQLTransactionBackend::stateFunctionFor(SQL
&SQLTransactionBackend::acquireLock, // 2.
&SQLTransactionBackend::openTransactionAndPreflight, // 3.
&SQLTransactionBackend::runStatements, // 4.
- &SQLTransactionBackend::postflightAndCommit, // 5.
+ &SQLTransactionBackend::unreachableState, // 5. postflightAndCommit
&SQLTransactionBackend::cleanupAndTerminate, // 6.
&SQLTransactionBackend::cleanupAfterTransactionErrorCallback, // 7.
- &SQLTransactionBackend::sendToFrontendState, // 8. deliverTransactionCallback
- &SQLTransactionBackend::sendToFrontendState, // 9. deliverTransactionErrorCallback
- &SQLTransactionBackend::sendToFrontendState, // 10. deliverStatementCallback
- &SQLTransactionBackend::sendToFrontendState, // 11. deliverQuotaIncreaseCallback
- &SQLTransactionBackend::sendToFrontendState // 12. deliverSuccessCallback
+ &SQLTransactionBackend::unreachableState, // 8. deliverTransactionCallback
+ &SQLTransactionBackend::unreachableState, // 9. deliverTransactionErrorCallback
+ &SQLTransactionBackend::unreachableState, // 10. deliverStatementCallback
+ &SQLTransactionBackend::unreachableState, // 11. deliverQuotaIncreaseCallback
+ &SQLTransactionBackend::unreachableState // 12. deliverSuccessCallback
};
ASSERT(WTF_ARRAY_LENGTH(stateFunctions) == static_cast<int>(SQLTransactionState::NumberOfStates));
@@ -466,17 +423,11 @@ SQLTransactionBackend::StateFunction SQLTransactionBackend::stateFunctionFor(SQL
return stateFunctions[static_cast<int>(state)];
}
-void SQLTransactionBackend::enqueueStatementBackend(PassRefPtr<SQLStatementBackend> statementBackend)
-{
- MutexLocker locker(m_statementMutex);
- m_statementQueue.append(statementBackend);
-}
-
void SQLTransactionBackend::computeNextStateAndCleanupIfNeeded()
{
// Only honor the requested state transition if we're not supposed to be
// cleaning up and shutting down:
- if (m_database->opened() && !m_database->isInterrupted()) {
+ if (m_frontend.m_database->opened()) {
setStateToRequestedState();
ASSERT(m_nextState == SQLTransactionState::AcquireLock
|| m_nextState == SQLTransactionState::OpenTransactionAndPreflight
@@ -498,49 +449,23 @@ void SQLTransactionBackend::computeNextStateAndCleanupIfNeeded()
LOG(StorageAPI, "Database was stopped or interrupted - cancelling work for this transaction");
// The current SQLite transaction should be stopped, as well
- if (m_sqliteTransaction) {
- m_sqliteTransaction->stop();
- m_sqliteTransaction.clear();
+ if (m_frontend.m_sqliteTransaction) {
+ m_frontend.m_sqliteTransaction->stop();
+ m_frontend.m_sqliteTransaction = nullptr;
}
// Terminate the frontend state machine. This also gets the frontend to
// call computeNextStateAndCleanupIfNeeded() and clear its wrappers
// if needed.
- m_frontend->requestTransitToState(SQLTransactionState::End);
+ m_frontend.requestTransitToState(SQLTransactionState::End);
// Redirect to the end state to abort, clean up, and end the transaction.
doCleanup();
}
-void SQLTransactionBackend::performNextStep()
-{
- computeNextStateAndCleanupIfNeeded();
- runStateMachine();
-}
-
-#if PLATFORM(IOS)
-bool SQLTransactionBackend::shouldPerformWhilePaused() const
-{
- // SQLTransactions should only run-while-paused if they have progressed passed the first transaction step.
- return m_nextState != SQLTransactionState::AcquireLock;
-}
-#endif
-
-void SQLTransactionBackend::executeSQL(PassOwnPtr<AbstractSQLStatement> statement,
- const String& sqlStatement, const Vector<SQLValue>& arguments, int permissions)
-{
- RefPtr<SQLStatementBackend> statementBackend;
- statementBackend = SQLStatementBackend::create(statement, sqlStatement, arguments, permissions);
-
- if (Database::from(m_database.get())->deleted())
- statementBackend->setDatabaseDeletedError();
-
- enqueueStatementBackend(statementBackend);
-}
-
void SQLTransactionBackend::notifyDatabaseThreadIsShuttingDown()
{
- ASSERT(currentThread() == database()->databaseContext()->databaseThread()->getThreadID());
+ ASSERT(currentThread() == m_frontend.database().databaseThread().getThreadID());
// If the transaction is in progress, we should roll it back here, since this
// is our last opportunity to do something related to this transaction on the
@@ -550,264 +475,29 @@ void SQLTransactionBackend::notifyDatabaseThreadIsShuttingDown()
doCleanup();
}
-SQLTransactionState SQLTransactionBackend::acquireLock()
-{
- m_database->transactionCoordinator()->acquireLock(this);
- return SQLTransactionState::Idle;
-}
-
-void SQLTransactionBackend::lockAcquired()
-{
- m_lockAcquired = true;
- requestTransitToState(SQLTransactionState::OpenTransactionAndPreflight);
-}
-
-SQLTransactionState SQLTransactionBackend::openTransactionAndPreflight()
-{
- ASSERT(!m_database->sqliteDatabase().transactionInProgress());
- ASSERT(m_lockAcquired);
-
- LOG(StorageAPI, "Opening and preflighting transaction %p", this);
-
- // If the database was deleted, jump to the error callback
- if (Database::from(m_database.get())->deleted()) {
- m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unable to open a transaction, because the user deleted the database");
- return nextStateForTransactionError();
- }
-
- // Set the maximum usage for this transaction if this transactions is not read-only
- if (!m_readOnly) {
- acquireOriginLock();
- m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize());
- }
-
- ASSERT(!m_sqliteTransaction);
- m_sqliteTransaction = adoptPtr(new SQLiteTransaction(m_database->sqliteDatabase(), m_readOnly));
-
- m_database->resetDeletes();
- m_database->disableAuthorizer();
- m_sqliteTransaction->begin();
- m_database->enableAuthorizer();
-
- // Spec 4.3.2.1+2: Open a transaction to the database, jumping to the error callback if that fails
- if (!m_sqliteTransaction->inProgress()) {
- ASSERT(!m_database->sqliteDatabase().transactionInProgress());
- m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to begin transaction",
- m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg());
- m_sqliteTransaction.clear();
- return nextStateForTransactionError();
- }
-
- // Note: We intentionally retrieve the actual version even with an empty expected version.
- // In multi-process browsers, we take this opportinutiy to update the cached value for
- // the actual version. In single-process browsers, this is just a map lookup.
- String actualVersion;
- if (!m_database->getActualVersionForTransaction(actualVersion)) {
- m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to read version",
- m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg());
- m_database->disableAuthorizer();
- m_sqliteTransaction.clear();
- m_database->enableAuthorizer();
- return nextStateForTransactionError();
- }
- m_hasVersionMismatch = !m_database->expectedVersion().isEmpty() && (m_database->expectedVersion() != actualVersion);
-
- // Spec 4.3.2.3: Perform preflight steps, jumping to the error callback if they fail
- if (m_wrapper && !m_wrapper->performPreflight(this)) {
- m_database->disableAuthorizer();
- m_sqliteTransaction.clear();
- m_database->enableAuthorizer();
- m_transactionError = m_wrapper->sqlError();
- if (!m_transactionError)
- m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unknown error occurred during transaction preflight");
- return nextStateForTransactionError();
- }
-
- // Spec 4.3.2.4: Invoke the transaction callback with the new SQLTransaction object
- if (m_hasCallback)
- return SQLTransactionState::DeliverTransactionCallback;
-
- // If we have no callback to make, skip pass to the state after:
- return SQLTransactionState::RunStatements;
-}
-
-SQLTransactionState SQLTransactionBackend::runStatements()
+void SQLTransactionBackend::acquireLock()
{
- ASSERT(m_lockAcquired);
- SQLTransactionState nextState;
-
- // If there is a series of statements queued up that are all successful and have no associated
- // SQLStatementCallback objects, then we can burn through the queue
- do {
- if (m_shouldRetryCurrentStatement && !m_sqliteTransaction->wasRolledBackBySqlite()) {
- m_shouldRetryCurrentStatement = false;
- // FIXME - Another place that needs fixing up after <rdar://problem/5628468> is addressed.
- // See ::openTransactionAndPreflight() for discussion
-
- // Reset the maximum size here, as it was increased to allow us to retry this statement.
- // m_shouldRetryCurrentStatement is set to true only when a statement exceeds
- // the quota, which can happen only in a read-write transaction. Therefore, there
- // is no need to check here if the transaction is read-write.
- m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize());
- } else {
- // If the current statement has already been run, failed due to quota constraints, and we're not retrying it,
- // that means it ended in an error. Handle it now
- if (m_currentStatementBackend && m_currentStatementBackend->lastExecutionFailedDueToQuota()) {
- return nextStateForCurrentStatementError();
- }
-
- // Otherwise, advance to the next statement
- getNextStatement();
- }
- nextState = runCurrentStatementAndGetNextState();
- } while (nextState == SQLTransactionState::RunStatements);
-
- return nextState;
+ m_frontend.acquireLock();
}
-void SQLTransactionBackend::getNextStatement()
+void SQLTransactionBackend::openTransactionAndPreflight()
{
- m_currentStatementBackend = 0;
-
- MutexLocker locker(m_statementMutex);
- if (!m_statementQueue.isEmpty())
- m_currentStatementBackend = m_statementQueue.takeFirst();
-}
-
-SQLTransactionState SQLTransactionBackend::runCurrentStatementAndGetNextState()
-{
- if (!m_currentStatementBackend) {
- // No more statements to run. So move on to the next state.
- return SQLTransactionState::PostflightAndCommit;
- }
-
- m_database->resetAuthorizer();
-
- if (m_hasVersionMismatch)
- m_currentStatementBackend->setVersionMismatchedError();
-
- if (m_currentStatementBackend->execute(m_database.get())) {
- if (m_database->lastActionChangedDatabase()) {
- // Flag this transaction as having changed the database for later delegate notification
- m_modifiedDatabase = true;
- }
-
- if (m_currentStatementBackend->hasStatementCallback()) {
- return SQLTransactionState::DeliverStatementCallback;
- }
-
- // If we get here, then the statement doesn't have a callback to invoke.
- // We can move on to the next statement. Hence, stay in this state.
- return SQLTransactionState::RunStatements;
- }
-
- if (m_currentStatementBackend->lastExecutionFailedDueToQuota()) {
- return SQLTransactionState::DeliverQuotaIncreaseCallback;
- }
-
- return nextStateForCurrentStatementError();
-}
-
-SQLTransactionState SQLTransactionBackend::nextStateForCurrentStatementError()
-{
- // Spec 4.3.2.6.6: error - Call the statement's error callback, but if there was no error callback,
- // or the transaction was rolled back, jump to the transaction error callback
- if (m_currentStatementBackend->hasStatementErrorCallback() && !m_sqliteTransaction->wasRolledBackBySqlite())
- return SQLTransactionState::DeliverStatementCallback;
-
- m_transactionError = m_currentStatementBackend->sqlError();
- if (!m_transactionError)
- m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "the statement failed to execute");
- return nextStateForTransactionError();
-}
-
-SQLTransactionState SQLTransactionBackend::postflightAndCommit()
-{
- ASSERT(m_lockAcquired);
-
- // Spec 4.3.2.7: Perform postflight steps, jumping to the error callback if they fail.
- if (m_wrapper && !m_wrapper->performPostflight(this)) {
- m_transactionError = m_wrapper->sqlError();
- if (!m_transactionError)
- m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unknown error occurred during transaction postflight");
- return nextStateForTransactionError();
- }
-
- // Spec 4.3.2.7: Commit the transaction, jumping to the error callback if that fails.
- ASSERT(m_sqliteTransaction);
-
- m_database->disableAuthorizer();
- m_sqliteTransaction->commit();
- m_database->enableAuthorizer();
-
- releaseOriginLockIfNeeded();
-
- // If the commit failed, the transaction will still be marked as "in progress"
- if (m_sqliteTransaction->inProgress()) {
- if (m_wrapper)
- m_wrapper->handleCommitFailedAfterPostflight(this);
- m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to commit transaction",
- m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg());
- return nextStateForTransactionError();
- }
-
- // Vacuum the database if anything was deleted.
- if (m_database->hadDeletes())
- m_database->incrementalVacuumIfNeeded();
-
- // The commit was successful. If the transaction modified this database, notify the delegates.
- if (m_modifiedDatabase)
- m_database->transactionClient()->didCommitWriteTransaction(database());
-
- // Spec 4.3.2.8: Deliver success callback, if there is one.
- return SQLTransactionState::DeliverSuccessCallback;
+ m_frontend.openTransactionAndPreflight();
}
-SQLTransactionState SQLTransactionBackend::cleanupAndTerminate()
+void SQLTransactionBackend::runStatements()
{
- ASSERT(m_lockAcquired);
-
- // Spec 4.3.2.9: End transaction steps. There is no next step.
- LOG(StorageAPI, "Transaction %p is complete\n", this);
- ASSERT(!m_database->sqliteDatabase().transactionInProgress());
-
- // Phase 5 cleanup. See comment on the SQLTransaction life-cycle above.
- doCleanup();
- m_database->inProgressTransactionCompleted();
- return SQLTransactionState::End;
+ m_frontend.runStatements();
}
-SQLTransactionState SQLTransactionBackend::nextStateForTransactionError()
+void SQLTransactionBackend::cleanupAndTerminate()
{
- ASSERT(m_transactionError);
- if (m_hasErrorCallback)
- return SQLTransactionState::DeliverTransactionErrorCallback;
-
- // No error callback, so fast-forward to the next state and rollback the
- // transaction.
- return SQLTransactionState::CleanupAfterTransactionErrorCallback;
+ m_frontend.cleanupAndTerminate();
}
-SQLTransactionState SQLTransactionBackend::cleanupAfterTransactionErrorCallback()
+void SQLTransactionBackend::cleanupAfterTransactionErrorCallback()
{
- ASSERT(m_lockAcquired);
-
- LOG(StorageAPI, "Transaction %p is complete with an error\n", this);
- m_database->disableAuthorizer();
- if (m_sqliteTransaction) {
- // Spec 4.3.2.10: Rollback the transaction.
- m_sqliteTransaction->rollback();
-
- ASSERT(!m_database->sqliteDatabase().transactionInProgress());
- m_sqliteTransaction.clear();
- }
- m_database->enableAuthorizer();
-
- releaseOriginLockIfNeeded();
-
- ASSERT(!m_database->sqliteDatabase().transactionInProgress());
-
- return SQLTransactionState::CleanupAndTerminate;
+ m_frontend.cleanupAfterTransactionErrorCallback();
}
// requestTransitToState() can be called from the frontend. Hence, it should
@@ -818,40 +508,15 @@ void SQLTransactionBackend::requestTransitToState(SQLTransactionState nextState)
LOG(StorageAPI, "Scheduling %s for transaction %p\n", nameForSQLTransactionState(nextState), this);
m_requestedState = nextState;
ASSERT(m_requestedState != SQLTransactionState::End);
- m_database->scheduleTransactionStep(this);
+ m_frontend.m_database->scheduleTransactionStep(m_frontend);
}
// This state function is used as a stub function to plug unimplemented states
// in the state dispatch table. They are unimplemented because they should
// never be reached in the course of correct execution.
-SQLTransactionState SQLTransactionBackend::unreachableState()
+void SQLTransactionBackend::unreachableState()
{
ASSERT_NOT_REACHED();
- return SQLTransactionState::End;
-}
-
-SQLTransactionState SQLTransactionBackend::sendToFrontendState()
-{
- ASSERT(m_nextState != SQLTransactionState::Idle);
- m_frontend->requestTransitToState(m_nextState);
- return SQLTransactionState::Idle;
-}
-
-void SQLTransactionBackend::acquireOriginLock()
-{
- ASSERT(!m_originLock);
- m_originLock = DatabaseTracker::tracker().originLockFor(m_database->securityOrigin());
- m_originLock->lock();
-}
-
-void SQLTransactionBackend::releaseOriginLockIfNeeded()
-{
- if (m_originLock) {
- m_originLock->unlock();
- m_originLock.clear();
- }
}
} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionBackend.h b/Source/WebCore/Modules/webdatabase/SQLTransactionBackend.h
index 17a4c65f2..29e603c95 100644
--- a/Source/WebCore/Modules/webdatabase/SQLTransactionBackend.h
+++ b/Source/WebCore/Modules/webdatabase/SQLTransactionBackend.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2007, 2013, 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
@@ -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,122 +25,55 @@
* (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 SQLTransactionBackend_h
-#define SQLTransactionBackend_h
-#if ENABLE(SQL_DATABASE)
+#pragma once
-#include "AbstractSQLStatement.h"
-#include "AbstractSQLTransactionBackend.h"
-#include "DatabaseBasicTypes.h"
#include "SQLTransactionStateMachine.h"
+#include <memory>
#include <wtf/Deque.h>
#include <wtf/Forward.h>
+#include <wtf/Lock.h>
#include <wtf/text/WTFString.h>
namespace WebCore {
-class AbstractSQLTransaction;
-class DatabaseBackend;
+class Database;
class OriginLock;
class SQLError;
class SQLiteTransaction;
-class SQLStatementBackend;
-class SQLTransactionBackend;
-class SQLValue;
+class SQLStatement;
+class SQLTransaction;
+class SQLTransactionWrapper;
-class SQLTransactionWrapper : public ThreadSafeRefCounted<SQLTransactionWrapper> {
+class SQLTransactionBackend : public SQLTransactionStateMachine<SQLTransactionBackend> {
public:
- virtual ~SQLTransactionWrapper() { }
- virtual bool performPreflight(SQLTransactionBackend*) = 0;
- virtual bool performPostflight(SQLTransactionBackend*) = 0;
- virtual SQLError* sqlError() const = 0;
- virtual void handleCommitFailedAfterPostflight(SQLTransactionBackend*) = 0;
-};
-
-class SQLTransactionBackend : public SQLTransactionStateMachine<SQLTransactionBackend>, public AbstractSQLTransactionBackend {
-public:
- static PassRefPtr<SQLTransactionBackend> create(DatabaseBackend*,
- PassRefPtr<AbstractSQLTransaction>, PassRefPtr<SQLTransactionWrapper>, bool readOnly);
-
- virtual ~SQLTransactionBackend();
-
- void lockAcquired();
- void performNextStep();
+ explicit SQLTransactionBackend(SQLTransaction&);
+ ~SQLTransactionBackend();
-#if PLATFORM(IOS)
- bool shouldPerformWhilePaused() const;
-#endif
-
- DatabaseBackend* database() { return m_database.get(); }
- bool isReadOnly() { return m_readOnly; }
void notifyDatabaseThreadIsShuttingDown();
-private:
- SQLTransactionBackend(DatabaseBackend*, PassRefPtr<AbstractSQLTransaction>,
- PassRefPtr<SQLTransactionWrapper>, bool readOnly);
+ // API called from the frontend published via SQLTransactionBackend:
+ void requestTransitToState(SQLTransactionState);
- // APIs called from the frontend published via AbstractSQLTransactionBackend:
- virtual void requestTransitToState(SQLTransactionState) override;
- virtual PassRefPtr<SQLError> transactionError() override;
- virtual AbstractSQLStatement* currentStatement() override;
- virtual void setShouldRetryCurrentStatement(bool) override;
- virtual void executeSQL(PassOwnPtr<AbstractSQLStatement>, const String& statement,
- const Vector<SQLValue>& arguments, int permissions) override;
+private:
+ friend class SQLTransaction;
void doCleanup();
- void enqueueStatementBackend(PassRefPtr<SQLStatementBackend>);
-
// State Machine functions:
- virtual StateFunction stateFunctionFor(SQLTransactionState) override;
+ StateFunction stateFunctionFor(SQLTransactionState) override;
void computeNextStateAndCleanupIfNeeded();
// State functions:
- SQLTransactionState acquireLock();
- SQLTransactionState openTransactionAndPreflight();
- SQLTransactionState runStatements();
- SQLTransactionState postflightAndCommit();
- SQLTransactionState cleanupAndTerminate();
- SQLTransactionState cleanupAfterTransactionErrorCallback();
+ void acquireLock();
+ void openTransactionAndPreflight();
+ void runStatements();
+ void cleanupAndTerminate();
+ void cleanupAfterTransactionErrorCallback();
- SQLTransactionState unreachableState();
- SQLTransactionState sendToFrontendState();
+ NO_RETURN_DUE_TO_ASSERT void unreachableState();
- SQLTransactionState nextStateForCurrentStatementError();
- SQLTransactionState nextStateForTransactionError();
- SQLTransactionState runCurrentStatementAndGetNextState();
-
- void getNextStatement();
-
- void acquireOriginLock();
- void releaseOriginLockIfNeeded();
-
- RefPtr<AbstractSQLTransaction> m_frontend; // Has a reference cycle, and will break in doCleanup().
- RefPtr<SQLStatementBackend> m_currentStatementBackend;
-
- RefPtr<DatabaseBackend> m_database;
- RefPtr<SQLTransactionWrapper> m_wrapper;
- RefPtr<SQLError> m_transactionError;
-
- bool m_hasCallback;
- bool m_hasSuccessCallback;
- bool m_hasErrorCallback;
- bool m_shouldRetryCurrentStatement;
- bool m_modifiedDatabase;
- bool m_lockAcquired;
- bool m_readOnly;
- bool m_hasVersionMismatch;
-
- Mutex m_statementMutex;
- Deque<RefPtr<SQLStatementBackend>> m_statementQueue;
-
- OwnPtr<SQLiteTransaction> m_sqliteTransaction;
- RefPtr<OriginLock> m_originLock;
+ SQLTransaction& m_frontend;
};
} // namespace WebCore
-
-#endif
-
-#endif // SQLTransactionBackend_h
diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionBackendSync.cpp b/Source/WebCore/Modules/webdatabase/SQLTransactionBackendSync.cpp
deleted file mode 100644
index 552226405..000000000
--- a/Source/WebCore/Modules/webdatabase/SQLTransactionBackendSync.cpp
+++ /dev/null
@@ -1,237 +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:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google 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 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
- * OWNER 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 "SQLTransactionBackendSync.h"
-
-#if ENABLE(SQL_DATABASE)
-
-#include "DatabaseAuthorizer.h"
-#include "DatabaseBackendContext.h"
-#include "DatabaseSync.h"
-#include "SQLException.h"
-#include "SQLResultSet.h"
-#include "SQLStatementSync.h"
-#include "SQLTransactionClient.h"
-#include "SQLTransactionSync.h"
-#include "SQLTransactionSyncCallback.h"
-#include "SQLValue.h"
-#include "SQLiteTransaction.h"
-#include "ScriptExecutionContext.h"
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefPtr.h>
-#include <wtf/text/WTFString.h>
-
-namespace WebCore {
-
-SQLTransactionBackendSync::SQLTransactionBackendSync(DatabaseSync* db, PassRefPtr<SQLTransactionSyncCallback> callback, bool readOnly)
- : m_database(db)
- , m_callback(callback)
- , m_readOnly(readOnly)
- , m_hasVersionMismatch(false)
- , m_modifiedDatabase(false)
- , m_transactionClient(adoptPtr(new SQLTransactionClient()))
-{
- ASSERT(m_database->scriptExecutionContext()->isContextThread());
-}
-
-SQLTransactionBackendSync::~SQLTransactionBackendSync()
-{
- ASSERT(m_database->scriptExecutionContext()->isContextThread());
- if (m_sqliteTransaction && m_sqliteTransaction->inProgress())
- rollback();
-}
-
-PassRefPtr<SQLResultSet> SQLTransactionBackendSync::executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, ExceptionCode& ec)
-{
- ASSERT(m_database->scriptExecutionContext()->isContextThread());
-
- m_database->setLastErrorMessage("");
-
- if (!m_database->opened()) {
- m_database->setLastErrorMessage("cannot executeSQL because the database is not open");
- ec = SQLException::UNKNOWN_ERR;
- return 0;
- }
-
- if (m_hasVersionMismatch) {
- m_database->setLastErrorMessage("cannot executeSQL because there is a version mismatch");
- ec = SQLException::VERSION_ERR;
- return 0;
- }
-
- if (sqlStatement.isEmpty())
- return 0;
-
- int permissions = DatabaseAuthorizer::ReadWriteMask;
- if (!m_database->databaseContext()->allowDatabaseAccess())
- permissions |= DatabaseAuthorizer::NoAccessMask;
- else if (m_readOnly)
- permissions |= DatabaseAuthorizer::ReadOnlyMask;
-
- SQLStatementSync statement(sqlStatement, arguments, permissions);
-
- m_database->resetAuthorizer();
- bool retryStatement = true;
- RefPtr<SQLResultSet> resultSet;
- while (retryStatement) {
- retryStatement = false;
- resultSet = statement.execute(m_database.get(), ec);
- if (!resultSet) {
- if (m_sqliteTransaction->wasRolledBackBySqlite())
- return 0;
-
- if (ec == SQLException::QUOTA_ERR) {
- if (m_transactionClient->didExceedQuota(database())) {
- ec = 0;
- retryStatement = true;
- } else {
- m_database->setLastErrorMessage("there was not enough remaining storage space");
- return 0;
- }
- }
- }
- }
-
- if (m_database->lastActionChangedDatabase())
- m_modifiedDatabase = true;
-
- return resultSet.release();
-}
-
-ExceptionCode SQLTransactionBackendSync::begin()
-{
- ASSERT(m_database->scriptExecutionContext()->isContextThread());
- if (!m_database->opened()) {
- m_database->setLastErrorMessage("cannot begin transaction because the database is not open");
- return SQLException::UNKNOWN_ERR;
- }
-
- ASSERT(!m_database->sqliteDatabase().transactionInProgress());
-
- // Set the maximum usage for this transaction if this transactions is not read-only.
- if (!m_readOnly)
- m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize());
-
- ASSERT(!m_sqliteTransaction);
- m_sqliteTransaction = adoptPtr(new SQLiteTransaction(m_database->sqliteDatabase(), m_readOnly));
-
- m_database->resetDeletes();
- m_database->disableAuthorizer();
- m_sqliteTransaction->begin();
- m_database->enableAuthorizer();
-
- // Check if begin() succeeded.
- if (!m_sqliteTransaction->inProgress()) {
- ASSERT(!m_database->sqliteDatabase().transactionInProgress());
- m_database->setLastErrorMessage("unable to begin transaction",
- m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg());
- m_sqliteTransaction.clear();
- return SQLException::DATABASE_ERR;
- }
-
- // Note: We intentionally retrieve the actual version even with an empty expected version.
- // In multi-process browsers, we take this opportinutiy to update the cached value for
- // the actual version. In single-process browsers, this is just a map lookup.
- String actualVersion;
- if (!m_database->getActualVersionForTransaction(actualVersion)) {
- m_database->setLastErrorMessage("unable to read version",
- m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg());
- rollback();
- return SQLException::DATABASE_ERR;
- }
- m_hasVersionMismatch = !m_database->expectedVersion().isEmpty() && (m_database->expectedVersion() != actualVersion);
- return 0;
-}
-
-ExceptionCode SQLTransactionBackendSync::execute()
-{
- ASSERT(m_database->scriptExecutionContext()->isContextThread());
- if (!m_database->opened() || (m_callback && !m_callback->handleEvent(SQLTransactionSync::from(this)))) {
- if (m_database->lastErrorMessage().isEmpty())
- m_database->setLastErrorMessage("failed to execute transaction callback");
- m_callback = 0;
- return SQLException::UNKNOWN_ERR;
- }
-
- m_callback = 0;
- return 0;
-}
-
-ExceptionCode SQLTransactionBackendSync::commit()
-{
- ASSERT(m_database->scriptExecutionContext()->isContextThread());
- if (!m_database->opened()) {
- m_database->setLastErrorMessage("unable to commit transaction because the database is not open.");
- return SQLException::UNKNOWN_ERR;
- }
-
- ASSERT(m_sqliteTransaction);
-
- m_database->disableAuthorizer();
- m_sqliteTransaction->commit();
- m_database->enableAuthorizer();
-
- // If the commit failed, the transaction will still be marked as "in progress"
- if (m_sqliteTransaction->inProgress()) {
- m_database->setLastErrorMessage("unable to commit transaction",
- m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg());
- return SQLException::DATABASE_ERR;
- }
-
- m_sqliteTransaction.clear();
-
- // Vacuum the database if anything was deleted.
- if (m_database->hadDeletes())
- m_database->incrementalVacuumIfNeeded();
-
- // The commit was successful. If the transaction modified this database, notify the delegates.
- if (m_modifiedDatabase)
- m_transactionClient->didCommitWriteTransaction(database());
- return 0;
-}
-
-void SQLTransactionBackendSync::rollback()
-{
- ASSERT(m_database->scriptExecutionContext()->isContextThread());
- m_database->disableAuthorizer();
- if (m_sqliteTransaction) {
- m_sqliteTransaction->rollback();
- m_sqliteTransaction.clear();
- }
- m_database->enableAuthorizer();
-
- ASSERT(!m_database->sqliteDatabase().transactionInProgress());
-}
-
-} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionBackendSync.h b/Source/WebCore/Modules/webdatabase/SQLTransactionBackendSync.h
deleted file mode 100644
index 73ab48de0..000000000
--- a/Source/WebCore/Modules/webdatabase/SQLTransactionBackendSync.h
+++ /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:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google 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 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
- * OWNER 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.
- */
-
-#ifndef SQLTransactionBackendSync_h
-#define SQLTransactionBackendSync_h
-
-#if ENABLE(SQL_DATABASE)
-
-#include "DatabaseBasicTypes.h"
-#include <wtf/Forward.h>
-#include <wtf/RefCounted.h>
-#include <wtf/Vector.h>
-#include <wtf/text/WTFString.h>
-
-namespace WebCore {
-
-class DatabaseSync;
-class SQLResultSet;
-class SQLTransactionClient;
-class SQLTransactionSyncCallback;
-class SQLValue;
-class SQLiteTransaction;
-
-// Instances of this class should be created and used only on the worker's context thread.
-class SQLTransactionBackendSync : public RefCounted<SQLTransactionBackendSync> {
-public:
- ~SQLTransactionBackendSync();
-
- PassRefPtr<SQLResultSet> executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, ExceptionCode&);
-
- DatabaseSync* database() { return m_database.get(); }
- bool isReadOnly() const { return m_readOnly; }
-
- ExceptionCode begin();
- ExceptionCode execute();
- ExceptionCode commit();
- void rollback();
-
-private:
- SQLTransactionBackendSync(DatabaseSync*, PassRefPtr<SQLTransactionSyncCallback>, bool readOnly);
-
- RefPtr<DatabaseSync> m_database;
- RefPtr<SQLTransactionSyncCallback> m_callback;
- bool m_readOnly;
- bool m_hasVersionMismatch;
-
- bool m_modifiedDatabase;
- OwnPtr<SQLTransactionClient> m_transactionClient;
- OwnPtr<SQLiteTransaction> m_sqliteTransaction;
-
- friend class SQLTransactionSync; // FIXME: Remove this once the front-end has been properly isolated.
-};
-
-} // namespace WebCore
-
-#endif
-
-#endif // SQLTransactionBackendSync_h
diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionCallback.h b/Source/WebCore/Modules/webdatabase/SQLTransactionCallback.h
index 208ce111b..52cbe69e5 100644
--- a/Source/WebCore/Modules/webdatabase/SQLTransactionCallback.h
+++ b/Source/WebCore/Modules/webdatabase/SQLTransactionCallback.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,10 +26,7 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef SQLTransactionCallback_h
-#define SQLTransactionCallback_h
-
-#if ENABLE(SQL_DATABASE)
+#pragma once
#include <wtf/ThreadSafeRefCounted.h>
@@ -43,8 +40,4 @@ public:
virtual bool handleEvent(SQLTransaction*) = 0;
};
-}
-
-#endif
-
-#endif // SQLTransactionCallback_h
+} // namespace WebCore
diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionCallback.idl b/Source/WebCore/Modules/webdatabase/SQLTransactionCallback.idl
index 0c4c32ae0..24f9fa8e7 100644
--- a/Source/WebCore/Modules/webdatabase/SQLTransactionCallback.idl
+++ b/Source/WebCore/Modules/webdatabase/SQLTransactionCallback.idl
@@ -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,8 +26,4 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-[
- Conditional=SQL_DATABASE,
-] callback interface SQLTransactionCallback {
- boolean handleEvent(SQLTransaction transaction);
-};
+callback SQLTransactionCallback = void (SQLTransaction transaction);
diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionClient.cpp b/Source/WebCore/Modules/webdatabase/SQLTransactionClient.cpp
deleted file mode 100644
index 938153eec..000000000
--- a/Source/WebCore/Modules/webdatabase/SQLTransactionClient.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2009 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:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google 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 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
- * OWNER 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 "SQLTransactionClient.h"
-
-#if ENABLE(SQL_DATABASE)
-
-#include "DatabaseBackendBase.h"
-#include "DatabaseBackendContext.h"
-#include "DatabaseManager.h"
-#include "DatabaseTracker.h"
-#include "ScriptExecutionContext.h"
-#include "SecurityOrigin.h"
-
-namespace WebCore {
-
-void SQLTransactionClient::didCommitWriteTransaction(DatabaseBackendBase* database)
-{
- DatabaseTracker::tracker().scheduleNotifyDatabaseChanged(
- database->securityOrigin(), database->stringIdentifier());
-}
-
-bool SQLTransactionClient::didExceedQuota(DatabaseBackendBase* database)
-{
- ASSERT(database->databaseContext()->scriptExecutionContext()->isContextThread());
- unsigned long long currentQuota = DatabaseManager::manager().quotaForOrigin(database->securityOrigin());
- database->databaseContext()->databaseExceededQuota(database->stringIdentifier(), database->details());
- unsigned long long newQuota = DatabaseManager::manager().quotaForOrigin(database->securityOrigin());
- return (newQuota > currentQuota);
-}
-
-}
-
-#endif // ENABLE(SQL_DATABASE)
diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionClient.h b/Source/WebCore/Modules/webdatabase/SQLTransactionClient.h
deleted file mode 100644
index cd65eafa6..000000000
--- a/Source/WebCore/Modules/webdatabase/SQLTransactionClient.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2009 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:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google 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 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
- * OWNER 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.
- */
-
-#ifndef SQLTransactionClient_h
-#define SQLTransactionClient_h
-
-#if ENABLE(SQL_DATABASE)
-
-#include <wtf/FastMalloc.h>
-#include <wtf/Noncopyable.h>
-
-namespace WebCore {
-
-class DatabaseBackendBase;
-
-// A client to the SQLTransaction class. Allows SQLTransaction to notify interested
-// parties that certain things have happened in a transaction.
-class SQLTransactionClient {
- WTF_MAKE_NONCOPYABLE(SQLTransactionClient); WTF_MAKE_FAST_ALLOCATED;
-public:
- SQLTransactionClient() { }
- void didCommitWriteTransaction(DatabaseBackendBase*);
- bool didExceedQuota(DatabaseBackendBase*);
-};
-
-}
-
-#endif // ENABLE(SQL_DATABASE)
-
-#endif // SQLTransactionClient_h
diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.cpp b/Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.cpp
index bbb45a810..3aed24b0c 100644
--- a/Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.cpp
+++ b/Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.cpp
@@ -32,23 +32,18 @@
#include "config.h"
#include "SQLTransactionCoordinator.h"
-#if ENABLE(SQL_DATABASE)
-
-#include "DatabaseBackend.h"
-#include "SQLTransactionBackend.h"
+#include "Database.h"
+#include "SQLTransaction.h"
#include "SecurityOrigin.h"
+#include "SecurityOriginData.h"
#include <wtf/Deque.h>
-#include <wtf/HashMap.h>
-#include <wtf/HashSet.h>
#include <wtf/RefPtr.h>
namespace WebCore {
-static String getDatabaseIdentifier(SQLTransactionBackend* transaction)
+static String getDatabaseIdentifier(SQLTransaction& transaction)
{
- DatabaseBackend* database = transaction->database();
- ASSERT(database);
- return database->securityOrigin()->databaseIdentifier();
+ return transaction.database().securityOrigin().databaseIdentifier();
}
SQLTransactionCoordinator::SQLTransactionCoordinator()
@@ -61,7 +56,7 @@ void SQLTransactionCoordinator::processPendingTransactions(CoordinationInfo& inf
if (info.activeWriteTransaction || info.pendingTransactions.isEmpty())
return;
- RefPtr<SQLTransactionBackend> firstPendingTransaction = info.pendingTransactions.first();
+ RefPtr<SQLTransaction> firstPendingTransaction = info.pendingTransactions.first();
if (firstPendingTransaction->isReadOnly()) {
do {
firstPendingTransaction = info.pendingTransactions.takeFirst();
@@ -75,7 +70,7 @@ void SQLTransactionCoordinator::processPendingTransactions(CoordinationInfo& inf
}
}
-void SQLTransactionCoordinator::acquireLock(SQLTransactionBackend* transaction)
+void SQLTransactionCoordinator::acquireLock(SQLTransaction& transaction)
{
ASSERT(!m_isShuttingDown);
@@ -88,11 +83,11 @@ void SQLTransactionCoordinator::acquireLock(SQLTransactionBackend* transaction)
}
CoordinationInfo& info = coordinationInfoIterator->value;
- info.pendingTransactions.append(transaction);
+ info.pendingTransactions.append(&transaction);
processPendingTransactions(info);
}
-void SQLTransactionCoordinator::releaseLock(SQLTransactionBackend* transaction)
+void SQLTransactionCoordinator::releaseLock(SQLTransaction& transaction)
{
if (m_isShuttingDown)
return;
@@ -103,12 +98,12 @@ void SQLTransactionCoordinator::releaseLock(SQLTransactionBackend* transaction)
ASSERT(coordinationInfoIterator != m_coordinationInfoMap.end());
CoordinationInfo& info = coordinationInfoIterator->value;
- if (transaction->isReadOnly()) {
- ASSERT(info.activeReadTransactions.contains(transaction));
- info.activeReadTransactions.remove(transaction);
+ if (transaction.isReadOnly()) {
+ ASSERT(info.activeReadTransactions.contains(&transaction));
+ info.activeReadTransactions.remove(&transaction);
} else {
- ASSERT(info.activeWriteTransaction == transaction);
- info.activeWriteTransaction = 0;
+ ASSERT(info.activeWriteTransaction == &transaction);
+ info.activeWriteTransaction = nullptr;
}
processPendingTransactions(info);
@@ -121,27 +116,20 @@ void SQLTransactionCoordinator::shutdown()
m_isShuttingDown = true;
// Notify all transactions in progress that the database thread is shutting down
- for (CoordinationInfoMap::iterator coordinationInfoIterator = m_coordinationInfoMap.begin();
- coordinationInfoIterator != m_coordinationInfoMap.end(); ++coordinationInfoIterator) {
- CoordinationInfo& info = coordinationInfoIterator->value;
-
+ for (auto& info : m_coordinationInfoMap.values()) {
// Clean up transactions that have reached "lockAcquired":
// Transaction phase 4 cleanup. See comment on "What happens if a
// transaction is interrupted?" at the top of SQLTransactionBackend.cpp.
if (info.activeWriteTransaction)
info.activeWriteTransaction->notifyDatabaseThreadIsShuttingDown();
- for (HashSet<RefPtr<SQLTransactionBackend>>::iterator activeReadTransactionsIterator =
- info.activeReadTransactions.begin();
- activeReadTransactionsIterator != info.activeReadTransactions.end();
- ++activeReadTransactionsIterator) {
- (*activeReadTransactionsIterator)->notifyDatabaseThreadIsShuttingDown();
- }
+ for (auto& transaction : info.activeReadTransactions)
+ transaction->notifyDatabaseThreadIsShuttingDown();
// Clean up transactions that have NOT reached "lockAcquired":
// Transaction phase 3 cleanup. See comment on "What happens if a
// transaction is interrupted?" at the top of SQLTransactionBackend.cpp.
while (!info.pendingTransactions.isEmpty()) {
- RefPtr<SQLTransactionBackend> transaction = info.pendingTransactions.first();
+ RefPtr<SQLTransaction> transaction = info.pendingTransactions.first();
transaction->notifyDatabaseThreadIsShuttingDown();
}
}
@@ -151,5 +139,3 @@ void SQLTransactionCoordinator::shutdown()
}
} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.h b/Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.h
index ce87e055e..f9c89d812 100644
--- a/Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.h
+++ b/Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.h
@@ -29,10 +29,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef SQLTransactionCoordinator_h
-#define SQLTransactionCoordinator_h
-
-#if ENABLE(SQL_DATABASE)
+#pragma once
#include <wtf/Deque.h>
#include <wtf/HashMap.h>
@@ -42,21 +39,21 @@
namespace WebCore {
-class SQLTransactionBackend;
+class SQLTransaction;
class SQLTransactionCoordinator {
WTF_MAKE_NONCOPYABLE(SQLTransactionCoordinator); WTF_MAKE_FAST_ALLOCATED;
public:
SQLTransactionCoordinator();
- void acquireLock(SQLTransactionBackend*);
- void releaseLock(SQLTransactionBackend*);
+ void acquireLock(SQLTransaction&);
+ void releaseLock(SQLTransaction&);
void shutdown();
private:
- typedef Deque<RefPtr<SQLTransactionBackend>> TransactionsQueue;
+ typedef Deque<RefPtr<SQLTransaction>> TransactionsQueue;
struct CoordinationInfo {
TransactionsQueue pendingTransactions;
- HashSet<RefPtr<SQLTransactionBackend>> activeReadTransactions;
- RefPtr<SQLTransactionBackend> activeWriteTransaction;
+ HashSet<RefPtr<SQLTransaction>> activeReadTransactions;
+ RefPtr<SQLTransaction> activeWriteTransaction;
};
// Maps database names to information about pending transactions
typedef HashMap<String, CoordinationInfo> CoordinationInfoMap;
@@ -67,7 +64,3 @@ private:
};
} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
-
-#endif // SQLTransactionCoordinator_h
diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionErrorCallback.h b/Source/WebCore/Modules/webdatabase/SQLTransactionErrorCallback.h
index 56727cc05..89f207ebe 100644
--- a/Source/WebCore/Modules/webdatabase/SQLTransactionErrorCallback.h
+++ b/Source/WebCore/Modules/webdatabase/SQLTransactionErrorCallback.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,10 +26,7 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef SQLTransactionErrorCallback_h
-#define SQLTransactionErrorCallback_h
-
-#if ENABLE(SQL_DATABASE)
+#pragma once
#include <wtf/ThreadSafeRefCounted.h>
@@ -43,8 +40,4 @@ public:
virtual bool handleEvent(SQLError*) = 0;
};
-}
-
-#endif
-
-#endif // SQLTransactionErrorCallback_h
+} // namespace WebCore
diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionErrorCallback.idl b/Source/WebCore/Modules/webdatabase/SQLTransactionErrorCallback.idl
index bcf3b8184..450a46071 100644
--- a/Source/WebCore/Modules/webdatabase/SQLTransactionErrorCallback.idl
+++ b/Source/WebCore/Modules/webdatabase/SQLTransactionErrorCallback.idl
@@ -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,8 +26,4 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-[
- Conditional=SQL_DATABASE,
-] callback interface SQLTransactionErrorCallback {
- boolean handleEvent(SQLError error);
-};
+callback SQLTransactionErrorCallback = void (SQLError error);
diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionState.h b/Source/WebCore/Modules/webdatabase/SQLTransactionState.h
index f8a5adf62..eedf28f1b 100644
--- a/Source/WebCore/Modules/webdatabase/SQLTransactionState.h
+++ b/Source/WebCore/Modules/webdatabase/SQLTransactionState.h
@@ -23,10 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef SQLTransactionState_h
-#define SQLTransactionState_h
-
-#if ENABLE(SQL_DATABASE)
+#pragma once
namespace WebCore {
@@ -48,7 +45,3 @@ enum class SQLTransactionState {
};
} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
-
-#endif // SQLTransactionState_h
diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionStateMachine.cpp b/Source/WebCore/Modules/webdatabase/SQLTransactionStateMachine.cpp
index 194f862b4..ccd894033 100644
--- a/Source/WebCore/Modules/webdatabase/SQLTransactionStateMachine.cpp
+++ b/Source/WebCore/Modules/webdatabase/SQLTransactionStateMachine.cpp
@@ -26,8 +26,6 @@
#include "config.h"
#include "SQLTransactionStateMachine.h"
-#if ENABLE(SQL_DATABASE)
-
#include "Logging.h"
#include <wtf/Assertions.h>
@@ -70,5 +68,3 @@ const char* nameForSQLTransactionState(SQLTransactionState state)
#endif
} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionStateMachine.h b/Source/WebCore/Modules/webdatabase/SQLTransactionStateMachine.h
index fff11a8ce..b30fa28d8 100644
--- a/Source/WebCore/Modules/webdatabase/SQLTransactionStateMachine.h
+++ b/Source/WebCore/Modules/webdatabase/SQLTransactionStateMachine.h
@@ -23,10 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef SQLTransactionStateMachine_h
-#define SQLTransactionStateMachine_h
-
-#if ENABLE(SQL_DATABASE)
+#pragma once
#include "SQLTransactionState.h"
#include <wtf/ThreadSafeRefCounted.h>
@@ -41,7 +38,7 @@ public:
protected:
SQLTransactionStateMachine();
- typedef SQLTransactionState (T::* StateFunction)();
+ typedef void (T::*StateFunction)();
virtual StateFunction stateFunctionFor(SQLTransactionState) = 0;
void setStateToRequestedState();
@@ -92,21 +89,22 @@ template<typename T>
void SQLTransactionStateMachine<T>::runStateMachine()
{
ASSERT(SQLTransactionState::End < SQLTransactionState::Idle);
- while (m_nextState > SQLTransactionState::Idle) {
- ASSERT(m_nextState < SQLTransactionState::NumberOfStates);
- StateFunction stateFunction = stateFunctionFor(m_nextState);
- ASSERT(stateFunction);
+
+ if (m_nextState <= SQLTransactionState::Idle)
+ return;
+
+ ASSERT(m_nextState < SQLTransactionState::NumberOfStates);
+
+ StateFunction stateFunction = stateFunctionFor(m_nextState);
+ ASSERT(stateFunction);
#ifndef NDEBUG
- m_stateAuditTrail[m_nextStateAuditEntry] = m_nextState;
- m_nextStateAuditEntry = (m_nextStateAuditEntry + 1) % s_sizeOfStateAuditTrail;
+ m_stateAuditTrail[m_nextStateAuditEntry] = m_nextState;
+ m_nextStateAuditEntry = (m_nextStateAuditEntry + 1) % s_sizeOfStateAuditTrail;
#endif
- m_nextState = (static_cast<T*>(this)->*stateFunction)();
- }
+
+ (static_cast<T*>(this)->*stateFunction)();
+ m_nextState = SQLTransactionState::Idle;
}
} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
-
-#endif // SQLTransactionStateMachine_h
diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionSync.h b/Source/WebCore/Modules/webdatabase/SQLTransactionSync.h
deleted file mode 100644
index 78473f851..000000000
--- a/Source/WebCore/Modules/webdatabase/SQLTransactionSync.h
+++ /dev/null
@@ -1,56 +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:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google 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 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
- * OWNER 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.
- */
-
-#ifndef SQLTransactionSync_h
-#define SQLTransactionSync_h
-
-#if ENABLE(SQL_DATABASE)
-
-#include "SQLTransactionBackendSync.h"
-
-namespace WebCore {
-
-// Instances of this class should be created and used only on the worker's context thread.
-class SQLTransactionSync : public SQLTransactionBackendSync {
-public:
- static PassRefPtr<SQLTransactionSync> create(DatabaseSync*, PassRefPtr<SQLTransactionSyncCallback>, bool readOnly = false);
-
- static SQLTransactionSync* from(SQLTransactionBackendSync*);
-
-private:
- SQLTransactionSync(DatabaseSync*, PassRefPtr<SQLTransactionSyncCallback>, bool readOnly);
-};
-
-} // namespace WebCore
-
-#endif
-
-#endif // SQLTransactionSync_h
diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionSyncCallback.h b/Source/WebCore/Modules/webdatabase/SQLTransactionSyncCallback.h
deleted file mode 100644
index 0c5f8fd63..000000000
--- a/Source/WebCore/Modules/webdatabase/SQLTransactionSyncCallback.h
+++ /dev/null
@@ -1,53 +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:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google 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 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
- * OWNER 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.
- */
-
-#ifndef SQLTransactionSyncCallback_h
-#define SQLTransactionSyncCallback_h
-
-#if ENABLE(SQL_DATABASE)
-
-#include <wtf/RefCounted.h>
-
-namespace WebCore {
-
-class SQLTransactionSync;
-
-// Instances of this class should be created and used only on the worker's context thread.
-class SQLTransactionSyncCallback : public RefCounted<SQLTransactionSyncCallback> {
-public:
- virtual ~SQLTransactionSyncCallback() { }
- virtual bool handleEvent(SQLTransactionSync*) = 0;
-};
-
-}
-
-#endif
-
-#endif // SQLTransactionSyncCallback_h
diff --git a/Source/WebCore/Modules/webdatabase/WorkerGlobalScopeWebDatabase.cpp b/Source/WebCore/Modules/webdatabase/WorkerGlobalScopeWebDatabase.cpp
deleted file mode 100644
index d30ae8191..000000000
--- a/Source/WebCore/Modules/webdatabase/WorkerGlobalScopeWebDatabase.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2008 Apple Inc. All Rights Reserved.
- * Copyright (C) 2009, 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 COMPUTER, 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
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (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(SQL_DATABASE)
-
-#include "WorkerGlobalScopeWebDatabase.h"
-
-#include "Database.h"
-#include "DatabaseCallback.h"
-#include "DatabaseManager.h"
-#include "DatabaseSync.h"
-#include "SecurityOrigin.h"
-#include "WorkerGlobalScope.h"
-
-namespace WebCore {
-
-PassRefPtr<Database> WorkerGlobalScopeWebDatabase::openDatabase(WorkerGlobalScope* context, const String& name, const String& version, const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, ExceptionCode& ec)
-{
- DatabaseManager& dbManager = DatabaseManager::manager();
- RefPtr<Database> database;
- DatabaseError error = DatabaseError::None;
- if (dbManager.isAvailable() && context->securityOrigin()->canAccessDatabase(context->topOrigin())) {
- database = dbManager.openDatabase(context, name, version, displayName, estimatedSize, creationCallback, error);
- ASSERT(database || error != DatabaseError::None);
- ec = DatabaseManager::exceptionCodeForDatabaseError(error);
- } else
- ec = SECURITY_ERR;
-
- return database.release();
-}
-
-PassRefPtr<DatabaseSync> WorkerGlobalScopeWebDatabase::openDatabaseSync(WorkerGlobalScope* context, const String& name, const String& version, const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, ExceptionCode& ec)
-{
- DatabaseManager& dbManager = DatabaseManager::manager();
- RefPtr<DatabaseSync> database;
- DatabaseError error = DatabaseError::None;
- if (dbManager.isAvailable() && context->securityOrigin()->canAccessDatabase(context->topOrigin())) {
- database = dbManager.openDatabaseSync(context, name, version, displayName, estimatedSize, creationCallback, error);
-
- ASSERT(database || error != DatabaseError::None);
- ec = DatabaseManager::exceptionCodeForDatabaseError(error);
- } else
- ec = SECURITY_ERR;
-
- return database.release();
-}
-
-} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
diff --git a/Source/WebCore/Modules/webdatabase/WorkerGlobalScopeWebDatabase.h b/Source/WebCore/Modules/webdatabase/WorkerGlobalScopeWebDatabase.h
deleted file mode 100644
index fcccab1e3..000000000
--- a/Source/WebCore/Modules/webdatabase/WorkerGlobalScopeWebDatabase.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2008, 2009 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 COMPUTER, 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
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (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 WorkerGlobalScopeWebDatabase_h
-#define WorkerGlobalScopeWebDatabase_h
-
-#if ENABLE(SQL_DATABASE)
-
-#include "ExceptionCode.h"
-#include <wtf/Forward.h>
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/RefPtr.h>
-
-namespace WebCore {
-
-class Database;
-class DatabaseCallback;
-class DatabaseSync;
-class WorkerGlobalScope;
-
-class WorkerGlobalScopeWebDatabase {
-public:
- static PassRefPtr<Database> openDatabase(WorkerGlobalScope*, const String& name, const String& version, const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, ExceptionCode&);
- static PassRefPtr<DatabaseSync> openDatabaseSync(WorkerGlobalScope*, const String& name, const String& version, const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, ExceptionCode&);
-
-private:
- WorkerGlobalScopeWebDatabase() { };
- ~WorkerGlobalScopeWebDatabase() { };
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(SQL_DATABASE)
-
-#endif // WorkerGlobalScopeWebDatabase_h
diff --git a/Source/WebCore/Modules/webdatabase/WorkerGlobalScopeWebDatabase.idl b/Source/WebCore/Modules/webdatabase/WorkerGlobalScopeWebDatabase.idl
deleted file mode 100644
index d22d08765..000000000
--- a/Source/WebCore/Modules/webdatabase/WorkerGlobalScopeWebDatabase.idl
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2008 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 COMPUTER, 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
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (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=SQL_DATABASE,
-] partial interface WorkerGlobalScope {
- [RaisesException] Database openDatabase(DOMString name, DOMString version, DOMString displayName, unsigned long estimatedSize, optional DatabaseCallback creationCallback);
-
- [RaisesException] DatabaseSync openDatabaseSync(DOMString name, DOMString version, DOMString displayName, unsigned long estimatedSize, optional DatabaseCallback creationCallback);
-};
-
diff --git a/Source/WebCore/Modules/webdriver/NavigatorWebDriver.cpp b/Source/WebCore/Modules/webdriver/NavigatorWebDriver.cpp
new file mode 100644
index 000000000..381b4daff
--- /dev/null
+++ b/Source/WebCore/Modules/webdriver/NavigatorWebDriver.cpp
@@ -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.
+ */
+
+#include "config.h"
+#include "NavigatorWebDriver.h"
+
+#include "Frame.h"
+#include "JSNavigator.h"
+#include "Navigator.h"
+#include "Page.h"
+#include <JavaScriptCore/JSCInlines.h>
+
+using namespace JSC;
+
+namespace WebCore {
+
+NavigatorWebDriver::NavigatorWebDriver(Frame* frame)
+ : m_frame(frame)
+{
+}
+
+NavigatorWebDriver::~NavigatorWebDriver()
+{
+}
+
+const char* NavigatorWebDriver::supplementName()
+{
+ return "NavigatorWebDriver";
+}
+
+bool NavigatorWebDriver::isControlledByAutomation() const
+{
+ if (!m_frame || !m_frame->page())
+ return false;
+
+ return m_frame->page()->isControlledByAutomation();
+}
+
+NavigatorWebDriver* NavigatorWebDriver::from(Navigator* navigator)
+{
+ NavigatorWebDriver* supplement = static_cast<NavigatorWebDriver*>(Supplement<Navigator>::from(navigator, supplementName()));
+ if (!supplement) {
+ auto newSupplement = std::make_unique<NavigatorWebDriver>(navigator->frame());
+ supplement = newSupplement.get();
+ provideTo(navigator, supplementName(), WTFMove(newSupplement));
+ }
+ return supplement;
+}
+
+JSValue JSNavigator::webdriver(ExecState&) const
+{
+ bool isControlledByAutomation = NavigatorWebDriver::from(&wrapped())->isControlledByAutomation();
+ return isControlledByAutomation ? jsBoolean(true) : jsUndefined();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/Modules/webdriver/NavigatorWebDriver.h b/Source/WebCore/Modules/webdriver/NavigatorWebDriver.h
new file mode 100644
index 000000000..0fc111154
--- /dev/null
+++ b/Source/WebCore/Modules/webdriver/NavigatorWebDriver.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
+
+#include "Supplementable.h"
+
+namespace WebCore {
+
+class Frame;
+class Navigator;
+
+class NavigatorWebDriver final : public Supplement<Navigator> {
+public:
+ explicit NavigatorWebDriver(Frame*);
+ virtual ~NavigatorWebDriver();
+
+ bool isControlledByAutomation() const;
+
+ static NavigatorWebDriver* from(Navigator*);
+ static bool isControlledByAutomation(Navigator& navigator);
+private:
+ static const char* supplementName();
+
+ Frame* m_frame { nullptr };
+};
+
+} // namespace WebCore
diff --git a/Source/WebCore/Modules/webdriver/NavigatorWebDriver.idl b/Source/WebCore/Modules/webdriver/NavigatorWebDriver.idl
new file mode 100644
index 000000000..c7e22ab41
--- /dev/null
+++ b/Source/WebCore/Modules/webdriver/NavigatorWebDriver.idl
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+partial interface Navigator {
+ [NotEnumerable, Custom] readonly attribute boolean webdriver;
+};
diff --git a/Source/WebCore/Modules/websockets/CloseEvent.h b/Source/WebCore/Modules/websockets/CloseEvent.h
index fda51d8d7..85954f07f 100644
--- a/Source/WebCore/Modules/websockets/CloseEvent.h
+++ b/Source/WebCore/Modules/websockets/CloseEvent.h
@@ -28,41 +28,29 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef CloseEvent_h
-#define CloseEvent_h
+#pragma once
#include "Event.h"
#include "EventNames.h"
namespace WebCore {
-struct CloseEventInit : public EventInit {
- CloseEventInit()
- : wasClean(false)
- , code(0)
- {
- };
-
- bool wasClean;
- unsigned short code;
- String reason;
-};
-
class CloseEvent : public Event {
public:
- static PassRefPtr<CloseEvent> create()
+ static Ref<CloseEvent> create(bool wasClean, unsigned short code, const String& reason)
{
- return adoptRef(new CloseEvent());
+ return adoptRef(*new CloseEvent(wasClean, code, reason));
}
- static PassRefPtr<CloseEvent> create(bool wasClean, unsigned short code, const String& reason)
- {
- return adoptRef(new CloseEvent(wasClean, code, reason));
- }
+ struct Init : EventInit {
+ bool wasClean { false };
+ unsigned short code { 0 };
+ String reason;
+ };
- static PassRefPtr<CloseEvent> create(const AtomicString& type, const CloseEventInit& initializer)
+ static Ref<CloseEvent> create(const AtomicString& type, const Init& initializer, IsTrusted isTrusted = IsTrusted::No)
{
- return adoptRef(new CloseEvent(type, initializer));
+ return adoptRef(*new CloseEvent(type, initializer, isTrusted));
}
bool wasClean() const { return m_wasClean; }
@@ -70,16 +58,9 @@ public:
String reason() const { return m_reason; }
// Event function.
- virtual EventInterface eventInterface() const override { return CloseEventInterfaceType; }
+ EventInterface eventInterface() const override { return CloseEventInterfaceType; }
private:
- CloseEvent()
- : Event(eventNames().closeEvent, false, false)
- , m_wasClean(false)
- , m_code(0)
- {
- }
-
CloseEvent(bool wasClean, int code, const String& reason)
: Event(eventNames().closeEvent, false, false)
, m_wasClean(wasClean)
@@ -88,8 +69,8 @@ private:
{
}
- CloseEvent(const AtomicString& type, const CloseEventInit& initializer)
- : Event(type, initializer)
+ CloseEvent(const AtomicString& type, const Init& initializer, IsTrusted isTrusted)
+ : Event(type, initializer, isTrusted)
, m_wasClean(initializer.wasClean)
, m_code(initializer.code)
, m_reason(initializer.reason)
@@ -102,5 +83,3 @@ private:
};
} // namespace WebCore
-
-#endif // CloseEvent_h
diff --git a/Source/WebCore/Modules/websockets/CloseEvent.idl b/Source/WebCore/Modules/websockets/CloseEvent.idl
index 3f29a4959..db536c227 100644
--- a/Source/WebCore/Modules/websockets/CloseEvent.idl
+++ b/Source/WebCore/Modules/websockets/CloseEvent.idl
@@ -28,12 +28,17 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+// FIXME: This should be exposed to workers as well.
[
- JSNoStaticTables,
- ConstructorTemplate=Event
+ Constructor(DOMString type, optional CloseEventInit eventInitDict),
] interface CloseEvent : Event {
- [InitializedByEventConstructor] readonly attribute boolean wasClean;
- [InitializedByEventConstructor] readonly attribute unsigned short code;
- [InitializedByEventConstructor] readonly attribute DOMString reason;
+ readonly attribute boolean wasClean;
+ readonly attribute unsigned short code;
+ readonly attribute USVString reason;
};
+dictionary CloseEventInit : EventInit {
+ boolean wasClean = false;
+ unsigned short code = 0;
+ USVString reason = "";
+};
diff --git a/Source/WebCore/Modules/websockets/ThreadableWebSocketChannel.cpp b/Source/WebCore/Modules/websockets/ThreadableWebSocketChannel.cpp
index 919c60501..18aaa8c12 100644
--- a/Source/WebCore/Modules/websockets/ThreadableWebSocketChannel.cpp
+++ b/Source/WebCore/Modules/websockets/ThreadableWebSocketChannel.cpp
@@ -31,7 +31,6 @@
#include "config.h"
#if ENABLE(WEB_SOCKETS)
-
#include "ThreadableWebSocketChannel.h"
#include "Document.h"
@@ -43,27 +42,18 @@
#include "WorkerRunLoop.h"
#include "WorkerThread.h"
#include "WorkerThreadableWebSocketChannel.h"
-#include <wtf/PassRefPtr.h>
-#include <wtf/text/WTFString.h>
namespace WebCore {
-static const char webSocketChannelMode[] = "webSocketChannelMode";
-
-PassRefPtr<ThreadableWebSocketChannel> ThreadableWebSocketChannel::create(ScriptExecutionContext* context, WebSocketChannelClient* client)
+Ref<ThreadableWebSocketChannel> ThreadableWebSocketChannel::create(ScriptExecutionContext& context, WebSocketChannelClient& client, SocketProvider& provider)
{
- ASSERT(context);
- ASSERT(client);
-
- if (context->isWorkerGlobalScope()) {
- WorkerGlobalScope* workerGlobalScope = static_cast<WorkerGlobalScope*>(context);
- WorkerRunLoop& runLoop = workerGlobalScope->thread()->runLoop();
- String mode = webSocketChannelMode;
- mode.append(String::number(runLoop.createUniqueId()));
- return WorkerThreadableWebSocketChannel::create(workerGlobalScope, client, mode);
+ if (is<WorkerGlobalScope>(context)) {
+ WorkerGlobalScope& workerGlobalScope = downcast<WorkerGlobalScope>(context);
+ WorkerRunLoop& runLoop = workerGlobalScope.thread().runLoop();
+ return WorkerThreadableWebSocketChannel::create(workerGlobalScope, client, makeString("webSocketChannelMode", String::number(runLoop.createUniqueId())), provider);
}
- return WebSocketChannel::create(toDocument(context), client);
+ return WebSocketChannel::create(downcast<Document>(context), client, provider);
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/websockets/ThreadableWebSocketChannel.h b/Source/WebCore/Modules/websockets/ThreadableWebSocketChannel.h
index ded34c85b..9327e2f02 100644
--- a/Source/WebCore/Modules/websockets/ThreadableWebSocketChannel.h
+++ b/Source/WebCore/Modules/websockets/ThreadableWebSocketChannel.h
@@ -28,18 +28,15 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef ThreadableWebSocketChannel_h
-#define ThreadableWebSocketChannel_h
+#pragma once
#if ENABLE(WEB_SOCKETS)
#include <wtf/Forward.h>
#include <wtf/Noncopyable.h>
-#include <wtf/PassRefPtr.h>
namespace JSC {
class ArrayBuffer;
-class ArrayBufferView;
}
namespace WebCore {
@@ -47,18 +44,18 @@ namespace WebCore {
class Blob;
class URL;
class ScriptExecutionContext;
+class SocketProvider;
class WebSocketChannelClient;
class ThreadableWebSocketChannel {
WTF_MAKE_NONCOPYABLE(ThreadableWebSocketChannel);
public:
+ static Ref<ThreadableWebSocketChannel> create(ScriptExecutionContext&, WebSocketChannelClient&, SocketProvider&);
ThreadableWebSocketChannel() { }
- static PassRefPtr<ThreadableWebSocketChannel> create(ScriptExecutionContext*, WebSocketChannelClient*);
enum SendResult {
SendSuccess,
- SendFail,
- InvalidMessage
+ SendFail
};
virtual void connect(const URL&, const String& protocol) = 0;
@@ -66,8 +63,8 @@ public:
virtual String extensions() = 0; // Will be available after didConnect() callback is invoked.
virtual SendResult send(const String& message) = 0;
virtual SendResult send(const JSC::ArrayBuffer&, unsigned byteOffset, unsigned byteLength) = 0;
- virtual SendResult send(const Blob&) = 0;
- virtual unsigned long bufferedAmount() const = 0;
+ virtual SendResult send(Blob&) = 0;
+ virtual unsigned bufferedAmount() const = 0;
virtual void close(int code, const String& reason) = 0;
// Log the reason text and close the connection. Will call didClose().
virtual void fail(const String& reason) = 0;
@@ -88,5 +85,3 @@ protected:
} // namespace WebCore
#endif // ENABLE(WEB_SOCKETS)
-
-#endif // ThreadableWebSocketChannel_h
diff --git a/Source/WebCore/Modules/websockets/ThreadableWebSocketChannelClientWrapper.cpp b/Source/WebCore/Modules/websockets/ThreadableWebSocketChannelClientWrapper.cpp
index 0ab3c5eb1..d19f6cc9e 100644
--- a/Source/WebCore/Modules/websockets/ThreadableWebSocketChannelClientWrapper.cpp
+++ b/Source/WebCore/Modules/websockets/ThreadableWebSocketChannelClientWrapper.cpp
@@ -32,19 +32,17 @@
#if ENABLE(WEB_SOCKETS)
#include "ThreadableWebSocketChannelClientWrapper.h"
-#include "CrossThreadCopier.h"
-#include "CrossThreadTask.h"
#include "ScriptExecutionContext.h"
#include "WebSocketChannelClient.h"
-#include <wtf/PassRefPtr.h>
#include <wtf/RefPtr.h>
+#include <wtf/text/StringView.h>
namespace WebCore {
-ThreadableWebSocketChannelClientWrapper::ThreadableWebSocketChannelClientWrapper(ScriptExecutionContext* context, WebSocketChannelClient* client)
+ThreadableWebSocketChannelClientWrapper::ThreadableWebSocketChannelClientWrapper(ScriptExecutionContext& context, WebSocketChannelClient& client)
: m_context(context)
- , m_client(client)
- , m_peer(0)
+ , m_client(&client)
+ , m_peer(nullptr)
, m_failedWebSocketChannelCreation(false)
, m_syncMethodDone(true)
, m_sendRequestResult(ThreadableWebSocketChannel::SendFail)
@@ -53,9 +51,9 @@ ThreadableWebSocketChannelClientWrapper::ThreadableWebSocketChannelClientWrapper
{
}
-PassRefPtr<ThreadableWebSocketChannelClientWrapper> ThreadableWebSocketChannelClientWrapper::create(ScriptExecutionContext* context, WebSocketChannelClient* client)
+Ref<ThreadableWebSocketChannelClientWrapper> ThreadableWebSocketChannelClientWrapper::create(ScriptExecutionContext& context, WebSocketChannelClient& client)
{
- return adoptRef(new ThreadableWebSocketChannelClientWrapper(context, client));
+ return adoptRef(*new ThreadableWebSocketChannelClientWrapper(context, client));
}
void ThreadableWebSocketChannelClientWrapper::clearSyncMethodDone()
@@ -86,7 +84,7 @@ void ThreadableWebSocketChannelClientWrapper::didCreateWebSocketChannel(WorkerTh
void ThreadableWebSocketChannelClientWrapper::clearPeer()
{
- m_peer = 0;
+ m_peer = nullptr;
}
bool ThreadableWebSocketChannelClientWrapper::failedWebSocketChannelCreation() const
@@ -110,8 +108,7 @@ void ThreadableWebSocketChannelClientWrapper::setSubprotocol(const String& subpr
{
unsigned length = subprotocol.length();
m_subprotocol.resize(length);
- if (length)
- memcpy(m_subprotocol.data(), subprotocol.deprecatedCharacters(), sizeof(UChar) * length);
+ StringView(subprotocol).getCharactersWithUpconvert(m_subprotocol.data());
}
String ThreadableWebSocketChannelClientWrapper::extensions() const
@@ -125,8 +122,7 @@ void ThreadableWebSocketChannelClientWrapper::setExtensions(const String& extens
{
unsigned length = extensions.length();
m_extensions.resize(length);
- if (length)
- memcpy(m_extensions.data(), extensions.deprecatedCharacters(), sizeof(UChar) * length);
+ StringView(extensions).getCharactersWithUpconvert(m_extensions.data());
}
ThreadableWebSocketChannel::SendResult ThreadableWebSocketChannelClientWrapper::sendRequestResult() const
@@ -140,12 +136,12 @@ void ThreadableWebSocketChannelClientWrapper::setSendRequestResult(ThreadableWeb
m_syncMethodDone = true;
}
-unsigned long ThreadableWebSocketChannelClientWrapper::bufferedAmount() const
+unsigned ThreadableWebSocketChannelClientWrapper::bufferedAmount() const
{
return m_bufferedAmount;
}
-void ThreadableWebSocketChannelClientWrapper::setBufferedAmount(unsigned long bufferedAmount)
+void ThreadableWebSocketChannelClientWrapper::setBufferedAmount(unsigned bufferedAmount)
{
m_bufferedAmount = bufferedAmount;
m_syncMethodDone = true;
@@ -153,54 +149,93 @@ void ThreadableWebSocketChannelClientWrapper::setBufferedAmount(unsigned long bu
void ThreadableWebSocketChannelClientWrapper::clearClient()
{
- m_client = 0;
+ m_client = nullptr;
}
void ThreadableWebSocketChannelClientWrapper::didConnect()
{
- m_pendingTasks.append(createCallbackTask(&didConnectCallback, this));
+ m_pendingTasks.append(std::make_unique<ScriptExecutionContext::Task>([this, protectedThis = makeRef(*this)] (ScriptExecutionContext&) {
+ if (m_client)
+ m_client->didConnect();
+ }));
+
if (!m_suspended)
processPendingTasks();
}
void ThreadableWebSocketChannelClientWrapper::didReceiveMessage(const String& message)
{
- m_pendingTasks.append(createCallbackTask(&didReceiveMessageCallback, this, message));
+ m_pendingTasks.append(std::make_unique<ScriptExecutionContext::Task>([this, protectedThis = makeRef(*this), message = message.isolatedCopy()] (ScriptExecutionContext&) {
+ if (m_client)
+ m_client->didReceiveMessage(message);
+ }));
+
if (!m_suspended)
processPendingTasks();
}
-void ThreadableWebSocketChannelClientWrapper::didReceiveBinaryData(PassOwnPtr<Vector<char>> binaryData)
+void ThreadableWebSocketChannelClientWrapper::didReceiveBinaryData(Vector<uint8_t>&& binaryData)
{
- m_pendingTasks.append(createCallbackTask(&didReceiveBinaryDataCallback, this, binaryData));
+ m_pendingTasks.append(std::make_unique<ScriptExecutionContext::Task>([this, protectedThis = makeRef(*this), binaryData = WTFMove(binaryData)] (ScriptExecutionContext&) mutable {
+ if (m_client)
+ m_client->didReceiveBinaryData(WTFMove(binaryData));
+ }));
+
if (!m_suspended)
processPendingTasks();
}
-void ThreadableWebSocketChannelClientWrapper::didUpdateBufferedAmount(unsigned long bufferedAmount)
+void ThreadableWebSocketChannelClientWrapper::didUpdateBufferedAmount(unsigned bufferedAmount)
{
- m_pendingTasks.append(createCallbackTask(&didUpdateBufferedAmountCallback, this, bufferedAmount));
+ m_pendingTasks.append(std::make_unique<ScriptExecutionContext::Task>([this, protectedThis = makeRef(*this), bufferedAmount] (ScriptExecutionContext&) {
+ if (m_client)
+ m_client->didUpdateBufferedAmount(bufferedAmount);
+ }));
+
if (!m_suspended)
processPendingTasks();
}
void ThreadableWebSocketChannelClientWrapper::didStartClosingHandshake()
{
- m_pendingTasks.append(createCallbackTask(&didStartClosingHandshakeCallback, this));
+ m_pendingTasks.append(std::make_unique<ScriptExecutionContext::Task>([this, protectedThis = makeRef(*this)] (ScriptExecutionContext&) {
+ if (m_client)
+ m_client->didStartClosingHandshake();
+ }));
+
if (!m_suspended)
processPendingTasks();
}
-void ThreadableWebSocketChannelClientWrapper::didClose(unsigned long unhandledBufferedAmount, WebSocketChannelClient::ClosingHandshakeCompletionStatus closingHandshakeCompletion, unsigned short code, const String& reason)
+void ThreadableWebSocketChannelClientWrapper::didClose(unsigned unhandledBufferedAmount, WebSocketChannelClient::ClosingHandshakeCompletionStatus closingHandshakeCompletion, unsigned short code, const String& reason)
{
- m_pendingTasks.append(createCallbackTask(&didCloseCallback, this, unhandledBufferedAmount, closingHandshakeCompletion, code, reason));
+ m_pendingTasks.append(std::make_unique<ScriptExecutionContext::Task>([this, protectedThis = makeRef(*this), unhandledBufferedAmount, closingHandshakeCompletion, code, reason = reason.isolatedCopy()] (ScriptExecutionContext&) {
+ if (m_client)
+ m_client->didClose(unhandledBufferedAmount, closingHandshakeCompletion, code, reason);
+ }));
+
if (!m_suspended)
processPendingTasks();
}
void ThreadableWebSocketChannelClientWrapper::didReceiveMessageError()
{
- m_pendingTasks.append(createCallbackTask(&didReceiveMessageErrorCallback, this));
+ m_pendingTasks.append(std::make_unique<ScriptExecutionContext::Task>([this, protectedThis = makeRef(*this)] (ScriptExecutionContext&) {
+ if (m_client)
+ m_client->didReceiveMessageError();
+ }));
+
+ if (!m_suspended)
+ processPendingTasks();
+}
+
+void ThreadableWebSocketChannelClientWrapper::didUpgradeURL()
+{
+ m_pendingTasks.append(std::make_unique<ScriptExecutionContext::Task>([this, protectedThis = makeRef(*this)] (ScriptExecutionContext&) {
+ if (m_client)
+ m_client->didUpgradeURL();
+ }));
+
if (!m_suspended)
processPendingTasks();
}
@@ -216,12 +251,6 @@ void ThreadableWebSocketChannelClientWrapper::resume()
processPendingTasks();
}
-void ThreadableWebSocketChannelClientWrapper::processPendingTasksCallback(ScriptExecutionContext* context, PassRefPtr<ThreadableWebSocketChannelClientWrapper> wrapper)
-{
- ASSERT_UNUSED(context, context->isWorkerGlobalScope());
- wrapper->processPendingTasks();
-}
-
void ThreadableWebSocketChannelClientWrapper::processPendingTasks()
{
if (m_suspended)
@@ -229,62 +258,16 @@ void ThreadableWebSocketChannelClientWrapper::processPendingTasks()
if (!m_syncMethodDone) {
// When a synchronous operation is in progress (i.e. the execution stack contains
// WorkerThreadableWebSocketChannel::waitForMethodCompletion()), we cannot invoke callbacks in this run loop.
- m_context->postTask(createCallbackTask(&ThreadableWebSocketChannelClientWrapper::processPendingTasksCallback, this));
+ m_context.postTask([this, protectedThis = makeRef(*this)] (ScriptExecutionContext& context) {
+ ASSERT_UNUSED(context, context.isWorkerGlobalScope());
+ processPendingTasks();
+ });
return;
}
- Vector<OwnPtr<ScriptExecutionContext::Task>> tasks;
- tasks.swap(m_pendingTasks);
- for (Vector<OwnPtr<ScriptExecutionContext::Task>>::const_iterator iter = tasks.begin(); iter != tasks.end(); ++iter)
- (*iter)->performTask(0);
-}
-void ThreadableWebSocketChannelClientWrapper::didConnectCallback(ScriptExecutionContext* context, PassRefPtr<ThreadableWebSocketChannelClientWrapper> wrapper)
-{
- ASSERT_UNUSED(context, !context);
- if (wrapper->m_client)
- wrapper->m_client->didConnect();
-}
-
-void ThreadableWebSocketChannelClientWrapper::didReceiveMessageCallback(ScriptExecutionContext* context, PassRefPtr<ThreadableWebSocketChannelClientWrapper> wrapper, const String& message)
-{
- ASSERT_UNUSED(context, !context);
- if (wrapper->m_client)
- wrapper->m_client->didReceiveMessage(message);
-}
-
-void ThreadableWebSocketChannelClientWrapper::didReceiveBinaryDataCallback(ScriptExecutionContext* context, PassRefPtr<ThreadableWebSocketChannelClientWrapper> wrapper, PassOwnPtr<Vector<char>> binaryData)
-{
- ASSERT_UNUSED(context, !context);
- if (wrapper->m_client)
- wrapper->m_client->didReceiveBinaryData(binaryData);
-}
-
-void ThreadableWebSocketChannelClientWrapper::didUpdateBufferedAmountCallback(ScriptExecutionContext* context, PassRefPtr<ThreadableWebSocketChannelClientWrapper> wrapper, unsigned long bufferedAmount)
-{
- ASSERT_UNUSED(context, !context);
- if (wrapper->m_client)
- wrapper->m_client->didUpdateBufferedAmount(bufferedAmount);
-}
-
-void ThreadableWebSocketChannelClientWrapper::didStartClosingHandshakeCallback(ScriptExecutionContext* context, PassRefPtr<ThreadableWebSocketChannelClientWrapper> wrapper)
-{
- ASSERT_UNUSED(context, !context);
- if (wrapper->m_client)
- wrapper->m_client->didStartClosingHandshake();
-}
-
-void ThreadableWebSocketChannelClientWrapper::didCloseCallback(ScriptExecutionContext* context, PassRefPtr<ThreadableWebSocketChannelClientWrapper> wrapper, unsigned long unhandledBufferedAmount, WebSocketChannelClient::ClosingHandshakeCompletionStatus closingHandshakeCompletion, unsigned short code, const String& reason)
-{
- ASSERT_UNUSED(context, !context);
- if (wrapper->m_client)
- wrapper->m_client->didClose(unhandledBufferedAmount, closingHandshakeCompletion, code, reason);
-}
-
-void ThreadableWebSocketChannelClientWrapper::didReceiveMessageErrorCallback(ScriptExecutionContext* context, PassRefPtr<ThreadableWebSocketChannelClientWrapper> wrapper)
-{
- ASSERT_UNUSED(context, !context);
- if (wrapper->m_client)
- wrapper->m_client->didReceiveMessageError();
+ Vector<std::unique_ptr<ScriptExecutionContext::Task>> pendingTasks = WTFMove(m_pendingTasks);
+ for (auto& task : pendingTasks)
+ task->performTask(m_context);
}
} // namespace WebCore
diff --git a/Source/WebCore/Modules/websockets/ThreadableWebSocketChannelClientWrapper.h b/Source/WebCore/Modules/websockets/ThreadableWebSocketChannelClientWrapper.h
index 29f54950a..492f0ca2a 100644
--- a/Source/WebCore/Modules/websockets/ThreadableWebSocketChannelClientWrapper.h
+++ b/Source/WebCore/Modules/websockets/ThreadableWebSocketChannelClientWrapper.h
@@ -28,8 +28,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef ThreadableWebSocketChannelClientWrapper_h
-#define ThreadableWebSocketChannelClientWrapper_h
+#pragma once
#if ENABLE(WEB_SOCKETS)
@@ -37,9 +36,8 @@
#include "ThreadableWebSocketChannel.h"
#include "WebSocketChannelClient.h"
#include "WorkerThreadableWebSocketChannel.h"
+#include <memory>
#include <wtf/Forward.h>
-#include <wtf/OwnPtr.h>
-#include <wtf/PassOwnPtr.h>
#include <wtf/Threading.h>
#include <wtf/Vector.h>
#include <wtf/text/WTFString.h>
@@ -51,7 +49,7 @@ class WebSocketChannelClient;
class ThreadableWebSocketChannelClientWrapper : public ThreadSafeRefCounted<ThreadableWebSocketChannelClientWrapper> {
public:
- static PassRefPtr<ThreadableWebSocketChannelClientWrapper> create(ScriptExecutionContext*, WebSocketChannelClient*);
+ static Ref<ThreadableWebSocketChannelClientWrapper> create(ScriptExecutionContext&, WebSocketChannelClient&);
void clearSyncMethodDone();
void setSyncMethodDone();
@@ -73,37 +71,29 @@ public:
ThreadableWebSocketChannel::SendResult sendRequestResult() const;
void setSendRequestResult(ThreadableWebSocketChannel::SendResult);
- unsigned long bufferedAmount() const;
- void setBufferedAmount(unsigned long);
+ unsigned bufferedAmount() const;
+ void setBufferedAmount(unsigned);
void clearClient();
void didConnect();
void didReceiveMessage(const String& message);
- void didReceiveBinaryData(PassOwnPtr<Vector<char>>);
- void didUpdateBufferedAmount(unsigned long bufferedAmount);
+ void didReceiveBinaryData(Vector<uint8_t>&&);
+ void didUpdateBufferedAmount(unsigned bufferedAmount);
void didStartClosingHandshake();
- void didClose(unsigned long unhandledBufferedAmount, WebSocketChannelClient::ClosingHandshakeCompletionStatus, unsigned short code, const String& reason);
+ void didClose(unsigned unhandledBufferedAmount, WebSocketChannelClient::ClosingHandshakeCompletionStatus, unsigned short code, const String& reason);
void didReceiveMessageError();
+ void didUpgradeURL();
void suspend();
void resume();
private:
- ThreadableWebSocketChannelClientWrapper(ScriptExecutionContext*, WebSocketChannelClient*);
+ ThreadableWebSocketChannelClientWrapper(ScriptExecutionContext&, WebSocketChannelClient&);
void processPendingTasks();
- static void didConnectCallback(ScriptExecutionContext*, PassRefPtr<ThreadableWebSocketChannelClientWrapper>);
- static void didReceiveMessageCallback(ScriptExecutionContext*, PassRefPtr<ThreadableWebSocketChannelClientWrapper>, const String& message);
- static void didReceiveBinaryDataCallback(ScriptExecutionContext*, PassRefPtr<ThreadableWebSocketChannelClientWrapper>, PassOwnPtr<Vector<char>>);
- static void didUpdateBufferedAmountCallback(ScriptExecutionContext*, PassRefPtr<ThreadableWebSocketChannelClientWrapper>, unsigned long bufferedAmount);
- static void didStartClosingHandshakeCallback(ScriptExecutionContext*, PassRefPtr<ThreadableWebSocketChannelClientWrapper>);
- static void didCloseCallback(ScriptExecutionContext*, PassRefPtr<ThreadableWebSocketChannelClientWrapper>, unsigned long unhandledBufferedAmount, WebSocketChannelClient::ClosingHandshakeCompletionStatus, unsigned short code, const String& reason);
- static void processPendingTasksCallback(ScriptExecutionContext*, PassRefPtr<ThreadableWebSocketChannelClientWrapper>);
- static void didReceiveMessageErrorCallback(ScriptExecutionContext*, PassRefPtr<ThreadableWebSocketChannelClientWrapper>);
-
- ScriptExecutionContext* m_context;
+ ScriptExecutionContext& m_context;
WebSocketChannelClient* m_client;
WorkerThreadableWebSocketChannel::Peer* m_peer;
bool m_failedWebSocketChannelCreation;
@@ -112,13 +102,11 @@ private:
Vector<UChar> m_subprotocol;
Vector<UChar> m_extensions;
ThreadableWebSocketChannel::SendResult m_sendRequestResult;
- unsigned long m_bufferedAmount;
+ unsigned m_bufferedAmount;
bool m_suspended;
- Vector<OwnPtr<ScriptExecutionContext::Task>> m_pendingTasks;
+ Vector<std::unique_ptr<ScriptExecutionContext::Task>> m_pendingTasks;
};
} // namespace WebCore
#endif // ENABLE(WEB_SOCKETS)
-
-#endif // ThreadableWebSocketChannelClientWrapper_h
diff --git a/Source/WebCore/Modules/websockets/WebSocket.cpp b/Source/WebCore/Modules/websockets/WebSocket.cpp
index 72b192af3..bed936267 100644
--- a/Source/WebCore/Modules/websockets/WebSocket.cpp
+++ b/Source/WebCore/Modules/websockets/WebSocket.cpp
@@ -1,5 +1,6 @@
/*
* 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
@@ -35,35 +36,38 @@
#include "WebSocket.h"
#include "Blob.h"
-#include "BlobData.h"
#include "CloseEvent.h"
#include "ContentSecurityPolicy.h"
#include "DOMWindow.h"
#include "Document.h"
#include "Event.h"
-#include "EventException.h"
#include "EventListener.h"
#include "EventNames.h"
#include "ExceptionCode.h"
#include "Frame.h"
#include "Logging.h"
#include "MessageEvent.h"
-#include "ScriptCallStack.h"
+#include "ResourceLoadObserver.h"
#include "ScriptController.h"
#include "ScriptExecutionContext.h"
#include "SecurityOrigin.h"
+#include "SocketProvider.h"
#include "ThreadableWebSocketChannel.h"
#include "WebSocketChannel.h"
+#include <inspector/ScriptCallStack.h>
#include <runtime/ArrayBuffer.h>
#include <runtime/ArrayBufferView.h>
#include <wtf/HashSet.h>
-#include <wtf/OwnPtr.h>
-#include <wtf/PassOwnPtr.h>
+#include <wtf/RunLoop.h>
#include <wtf/StdLibExtras.h>
#include <wtf/text/CString.h>
#include <wtf/text/StringBuilder.h>
#include <wtf/text/WTFString.h>
+#if USE(WEB_THREAD)
+#include "WebCoreThreadRun.h"
+#endif
+
namespace WebCore {
const size_t maxReasonSizeInBytes = 123;
@@ -81,12 +85,12 @@ static inline bool isValidProtocolCharacter(UChar character)
&& character != '{' && character != '}';
}
-static bool isValidProtocolString(const String& protocol)
+static bool isValidProtocolString(StringView protocol)
{
if (protocol.isEmpty())
return false;
- for (size_t i = 0; i < protocol.length(); ++i) {
- if (!isValidProtocolCharacter(protocol[i]))
+ for (auto codeUnit : protocol.codeUnits()) {
+ if (!isValidProtocolCharacter(codeUnit))
return false;
}
return true;
@@ -99,7 +103,7 @@ static String encodeProtocolString(const String& protocol)
if (protocol[i] < 0x20 || protocol[i] > 0x7E)
builder.append(String::format("\\u%04X", protocol[i]));
else if (protocol[i] == 0x5c)
- builder.append("\\\\");
+ builder.appendLiteral("\\\\");
else
builder.append(protocol[i]);
}
@@ -117,10 +121,10 @@ static String joinStrings(const Vector<String>& strings, const char* separator)
return builder.toString();
}
-static unsigned long saturateAdd(unsigned long a, unsigned long b)
+static unsigned saturateAdd(unsigned a, unsigned b)
{
- if (std::numeric_limits<unsigned long>::max() - a < b)
- return std::numeric_limits<unsigned long>::max();
+ if (std::numeric_limits<unsigned>::max() - a < b)
+ return std::numeric_limits<unsigned>::max();
return a + b;
}
@@ -136,19 +140,16 @@ bool WebSocket::isAvailable()
return webSocketsAvailable;
}
-const char* WebSocket::subProtocolSeperator()
+const char* WebSocket::subprotocolSeparator()
{
return ", ";
}
WebSocket::WebSocket(ScriptExecutionContext& context)
: ActiveDOMObject(&context)
- , m_state(CONNECTING)
- , m_bufferedAmount(0)
- , m_bufferedAmountAfterClose(0)
- , m_binaryType(BinaryTypeBlob)
- , m_subprotocol("")
- , m_extensions("")
+ , m_subprotocol(emptyString())
+ , m_extensions(emptyString())
+ , m_resumeTimer(*this, &WebSocket::resumeTimerFired)
{
}
@@ -158,102 +159,95 @@ WebSocket::~WebSocket()
m_channel->disconnect();
}
-PassRefPtr<WebSocket> WebSocket::create(ScriptExecutionContext& context)
-{
- RefPtr<WebSocket> webSocket(adoptRef(new WebSocket(context)));
- webSocket->suspendIfNeeded();
- return webSocket.release();
-}
-
-PassRefPtr<WebSocket> WebSocket::create(ScriptExecutionContext& context, const String& url, ExceptionCode& ec)
+ExceptionOr<Ref<WebSocket>> WebSocket::create(ScriptExecutionContext& context, const String& url)
{
- Vector<String> protocols;
- return WebSocket::create(context, url, protocols, ec);
+ return create(context, url, Vector<String> { });
}
-PassRefPtr<WebSocket> WebSocket::create(ScriptExecutionContext& context, const String& url, const Vector<String>& protocols, ExceptionCode& ec)
+ExceptionOr<Ref<WebSocket>> WebSocket::create(ScriptExecutionContext& context, const String& url, const Vector<String>& protocols)
{
- if (url.isNull()) {
- ec = SYNTAX_ERR;
- return 0;
- }
+ if (url.isNull())
+ return Exception { SYNTAX_ERR };
- RefPtr<WebSocket> webSocket(adoptRef(new WebSocket(context)));
- webSocket->suspendIfNeeded();
+ auto socket = adoptRef(*new WebSocket(context));
+ socket->suspendIfNeeded();
- webSocket->connect(context.completeURL(url), protocols, ec);
- if (ec)
- return 0;
+ auto result = socket->connect(context.completeURL(url), protocols);
+ if (result.hasException())
+ return result.releaseException();
- return webSocket.release();
+ return WTFMove(socket);
}
-PassRefPtr<WebSocket> WebSocket::create(ScriptExecutionContext& context, const String& url, const String& protocol, ExceptionCode& ec)
+ExceptionOr<Ref<WebSocket>> WebSocket::create(ScriptExecutionContext& context, const String& url, const String& protocol)
{
- Vector<String> protocols;
- protocols.append(protocol);
- return WebSocket::create(context, url, protocols, ec);
+ return create(context, url, Vector<String> { 1, protocol });
}
-void WebSocket::connect(const String& url, ExceptionCode& ec)
+ExceptionOr<void> WebSocket::connect(const String& url)
{
- Vector<String> protocols;
- connect(url, protocols, ec);
+ return connect(url, Vector<String> { });
}
-void WebSocket::connect(const String& url, const String& protocol, ExceptionCode& ec)
+ExceptionOr<void> WebSocket::connect(const String& url, const String& protocol)
{
- Vector<String> protocols;
- protocols.append(protocol);
- connect(url, protocols, ec);
+ return connect(url, Vector<String> { 1, protocol });
}
-void WebSocket::connect(const String& url, const Vector<String>& protocols, ExceptionCode& ec)
+ExceptionOr<void> WebSocket::connect(const String& url, const Vector<String>& protocols)
{
LOG(Network, "WebSocket %p connect() url='%s'", this, url.utf8().data());
m_url = URL(URL(), url);
+ ASSERT(scriptExecutionContext());
+ auto& context = *scriptExecutionContext();
+
if (!m_url.isValid()) {
- scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Invalid url for WebSocket " + m_url.stringCenterEllipsizedToLength());
+ context.addConsoleMessage(MessageSource::JS, MessageLevel::Error, "Invalid url for WebSocket " + m_url.stringCenterEllipsizedToLength());
m_state = CLOSED;
- ec = SYNTAX_ERR;
- return;
+ return Exception { SYNTAX_ERR };
}
if (!m_url.protocolIs("ws") && !m_url.protocolIs("wss")) {
- scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Wrong url scheme for WebSocket " + m_url.stringCenterEllipsizedToLength());
+ context.addConsoleMessage(MessageSource::JS, MessageLevel::Error, "Wrong url scheme for WebSocket " + m_url.stringCenterEllipsizedToLength());
m_state = CLOSED;
- ec = SYNTAX_ERR;
- return;
+ return Exception { SYNTAX_ERR };
}
if (m_url.hasFragmentIdentifier()) {
- scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "URL has fragment component " + m_url.stringCenterEllipsizedToLength());
+ context.addConsoleMessage(MessageSource::JS, MessageLevel::Error, "URL has fragment component " + m_url.stringCenterEllipsizedToLength());
m_state = CLOSED;
- ec = SYNTAX_ERR;
- return;
+ return Exception { SYNTAX_ERR };
}
+
+ ASSERT(context.contentSecurityPolicy());
+ auto& contentSecurityPolicy = *context.contentSecurityPolicy();
+
+ contentSecurityPolicy.upgradeInsecureRequestIfNeeded(m_url, ContentSecurityPolicy::InsecureRequestType::Load);
+
if (!portAllowed(m_url)) {
- scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "WebSocket port " + String::number(m_url.port()) + " blocked");
+ String message;
+ if (m_url.port())
+ message = makeString("WebSocket port ", String::number(m_url.port().value()), " blocked");
+ else
+ message = ASCIILiteral("WebSocket without port blocked");
+ context.addConsoleMessage(MessageSource::JS, MessageLevel::Error, message);
m_state = CLOSED;
- ec = SECURITY_ERR;
- return;
+ return Exception { SECURITY_ERR };
}
// FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved.
- bool shouldBypassMainWorldContentSecurityPolicy = false;
- if (scriptExecutionContext()->isDocument()) {
- Document* document = toDocument(scriptExecutionContext());
- shouldBypassMainWorldContentSecurityPolicy = document->frame()->script().shouldBypassMainWorldContentSecurityPolicy();
- }
- if (!shouldBypassMainWorldContentSecurityPolicy && !scriptExecutionContext()->contentSecurityPolicy()->allowConnectToSource(m_url)) {
+ if (!context.shouldBypassMainWorldContentSecurityPolicy() && !contentSecurityPolicy.allowConnectToSource(m_url)) {
m_state = CLOSED;
// FIXME: Should this be throwing an exception?
- ec = SECURITY_ERR;
- return;
+ return Exception { SECURITY_ERR };
}
- m_channel = ThreadableWebSocketChannel::create(scriptExecutionContext(), this);
+ if (auto* provider = context.socketProvider())
+ m_channel = ThreadableWebSocketChannel::create(*scriptExecutionContext(), *this, *provider);
+
+ // Every ScriptExecutionContext should have a SocketProvider.
+ RELEASE_ASSERT(m_channel);
// FIXME: There is a disagreement about restriction of subprotocols between WebSocket API and hybi-10 protocol
// draft. The former simply says "only characters in the range U+0021 to U+007E are allowed," while the latter
@@ -262,138 +256,155 @@ void WebSocket::connect(const String& url, const Vector<String>& protocols, Exce
//
// Here, we throw SYNTAX_ERR if the given protocols do not meet the latter criteria. This behavior does not
// comply with WebSocket API specification, but it seems to be the only reasonable way to handle this conflict.
- for (size_t i = 0; i < protocols.size(); ++i) {
- if (!isValidProtocolString(protocols[i])) {
- scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Wrong protocol for WebSocket '" + encodeProtocolString(protocols[i]) + "'");
+ for (auto& protocol : protocols) {
+ if (!isValidProtocolString(protocol)) {
+ context.addConsoleMessage(MessageSource::JS, MessageLevel::Error, "Wrong protocol for WebSocket '" + encodeProtocolString(protocol) + "'");
m_state = CLOSED;
- ec = SYNTAX_ERR;
- return;
+ return Exception { SYNTAX_ERR };
}
}
HashSet<String> visited;
- for (size_t i = 0; i < protocols.size(); ++i) {
- if (!visited.add(protocols[i]).isNewEntry) {
- scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "WebSocket protocols contain duplicates: '" + encodeProtocolString(protocols[i]) + "'");
+ for (auto& protocol : protocols) {
+ if (!visited.add(protocol).isNewEntry) {
+ context.addConsoleMessage(MessageSource::JS, MessageLevel::Error, "WebSocket protocols contain duplicates: '" + encodeProtocolString(protocol) + "'");
m_state = CLOSED;
- ec = SYNTAX_ERR;
- return;
+ return Exception { SYNTAX_ERR };
}
}
+ if (is<Document>(context)) {
+ Document& document = downcast<Document>(context);
+ if (!document.frame()->loader().mixedContentChecker().canRunInsecureContent(document.securityOrigin(), m_url)) {
+ // Balanced by the call to ActiveDOMObject::unsetPendingActivity() in WebSocket::stop().
+ ActiveDOMObject::setPendingActivity(this);
+
+ // We must block this connection. Instead of throwing an exception, we indicate this
+ // using the error event. But since this code executes as part of the WebSocket's
+ // constructor, we have to wait until the constructor has completed before firing the
+ // event; otherwise, users can't connect to the event.
+#if USE(WEB_THREAD)
+ ref();
+ dispatch_async(dispatch_get_main_queue(), ^{
+ WebThreadRun(^{
+ dispatchOrQueueErrorEvent();
+ stop();
+ deref();
+ });
+ });
+#else
+ RunLoop::main().dispatch([this, protectedThis = makeRef(*this)]() {
+ dispatchOrQueueErrorEvent();
+ stop();
+ });
+#endif
+ return { };
+ } else
+ ResourceLoadObserver::sharedObserver().logWebSocketLoading(document.frame(), m_url);
+ }
+
String protocolString;
if (!protocols.isEmpty())
- protocolString = joinStrings(protocols, subProtocolSeperator());
+ protocolString = joinStrings(protocols, subprotocolSeparator());
m_channel->connect(m_url, protocolString);
ActiveDOMObject::setPendingActivity(this);
+
+ return { };
}
-void WebSocket::send(const String& message, ExceptionCode& ec)
+ExceptionOr<void> WebSocket::send(const String& message)
{
LOG(Network, "WebSocket %p send() Sending String '%s'", this, message.utf8().data());
- if (m_state == CONNECTING) {
- ec = INVALID_STATE_ERR;
- return;
- }
+ if (m_state == CONNECTING)
+ return Exception { INVALID_STATE_ERR };
// No exception is raised if the connection was once established but has subsequently been closed.
if (m_state == CLOSING || m_state == CLOSED) {
size_t payloadSize = message.utf8().length();
m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, payloadSize);
m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, getFramingOverhead(payloadSize));
- return;
+ return { };
}
ASSERT(m_channel);
- ThreadableWebSocketChannel::SendResult result = m_channel->send(message);
- if (result == ThreadableWebSocketChannel::InvalidMessage) {
- scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Websocket message contains invalid character(s).");
- ec = SYNTAX_ERR;
- return;
- }
+ m_channel->send(message);
+ return { };
}
-void WebSocket::send(ArrayBuffer* binaryData, ExceptionCode& ec)
+ExceptionOr<void> WebSocket::send(ArrayBuffer& binaryData)
{
- LOG(Network, "WebSocket %p send() Sending ArrayBuffer %p", this, binaryData);
- ASSERT(binaryData);
- if (m_state == CONNECTING) {
- ec = INVALID_STATE_ERR;
- return;
- }
+ LOG(Network, "WebSocket %p send() Sending ArrayBuffer %p", this, &binaryData);
+ if (m_state == CONNECTING)
+ return Exception { INVALID_STATE_ERR };
if (m_state == CLOSING || m_state == CLOSED) {
- unsigned payloadSize = binaryData->byteLength();
+ unsigned payloadSize = binaryData.byteLength();
m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, payloadSize);
m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, getFramingOverhead(payloadSize));
- return;
+ return { };
}
ASSERT(m_channel);
- m_channel->send(*binaryData, 0, binaryData->byteLength());
+ m_channel->send(binaryData, 0, binaryData.byteLength());
+ return { };
}
-void WebSocket::send(ArrayBufferView* arrayBufferView, ExceptionCode& ec)
+ExceptionOr<void> WebSocket::send(ArrayBufferView& arrayBufferView)
{
- LOG(Network, "WebSocket %p send() Sending ArrayBufferView %p", this, arrayBufferView);
- ASSERT(arrayBufferView);
- if (m_state == CONNECTING) {
- ec = INVALID_STATE_ERR;
- return;
- }
+ LOG(Network, "WebSocket %p send() Sending ArrayBufferView %p", this, &arrayBufferView);
+
+ if (m_state == CONNECTING)
+ return Exception { INVALID_STATE_ERR };
if (m_state == CLOSING || m_state == CLOSED) {
- unsigned payloadSize = arrayBufferView->byteLength();
+ unsigned payloadSize = arrayBufferView.byteLength();
m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, payloadSize);
m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, getFramingOverhead(payloadSize));
- return;
+ return { };
}
ASSERT(m_channel);
- RefPtr<ArrayBuffer> arrayBuffer(arrayBufferView->buffer());
- m_channel->send(*arrayBuffer, arrayBufferView->byteOffset(), arrayBufferView->byteLength());
+ m_channel->send(*arrayBufferView.unsharedBuffer(), arrayBufferView.byteOffset(), arrayBufferView.byteLength());
+ return { };
}
-void WebSocket::send(Blob* binaryData, ExceptionCode& ec)
+ExceptionOr<void> WebSocket::send(Blob& binaryData)
{
- LOG(Network, "WebSocket %p send() Sending Blob '%s'", this, binaryData->url().stringCenterEllipsizedToLength().utf8().data());
- ASSERT(binaryData);
- if (m_state == CONNECTING) {
- ec = INVALID_STATE_ERR;
- return;
- }
+ LOG(Network, "WebSocket %p send() Sending Blob '%s'", this, binaryData.url().stringCenterEllipsizedToLength().utf8().data());
+ if (m_state == CONNECTING)
+ return Exception { INVALID_STATE_ERR };
if (m_state == CLOSING || m_state == CLOSED) {
- unsigned long payloadSize = static_cast<unsigned long>(binaryData->size());
+ unsigned payloadSize = static_cast<unsigned>(binaryData.size());
m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, payloadSize);
m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, getFramingOverhead(payloadSize));
- return;
+ return { };
}
ASSERT(m_channel);
- m_channel->send(*binaryData);
+ m_channel->send(binaryData);
+ return { };
}
-void WebSocket::close(int code, const String& reason, ExceptionCode& ec)
+ExceptionOr<void> WebSocket::close(std::optional<unsigned short> optionalCode, const String& reason)
{
+ int code = optionalCode ? optionalCode.value() : static_cast<int>(WebSocketChannel::CloseEventCodeNotSpecified);
if (code == WebSocketChannel::CloseEventCodeNotSpecified)
LOG(Network, "WebSocket %p close() without code and reason", this);
else {
LOG(Network, "WebSocket %p close() code=%d reason='%s'", this, code, reason.utf8().data());
- if (!(code == WebSocketChannel::CloseEventCodeNormalClosure || (WebSocketChannel::CloseEventCodeMinimumUserDefined <= code && code <= WebSocketChannel::CloseEventCodeMaximumUserDefined))) {
- ec = INVALID_ACCESS_ERR;
- return;
- }
+ if (!(code == WebSocketChannel::CloseEventCodeNormalClosure || (WebSocketChannel::CloseEventCodeMinimumUserDefined <= code && code <= WebSocketChannel::CloseEventCodeMaximumUserDefined)))
+ return Exception { INVALID_ACCESS_ERR };
CString utf8 = reason.utf8(StrictConversionReplacingUnpairedSurrogatesWithFFFD);
if (utf8.length() > maxReasonSizeInBytes) {
- scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "WebSocket close message is too long.");
- ec = SYNTAX_ERR;
- return;
+ scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, ASCIILiteral("WebSocket close message is too long."));
+ return Exception { SYNTAX_ERR };
}
}
if (m_state == CLOSING || m_state == CLOSED)
- return;
+ return { };
if (m_state == CONNECTING) {
m_state = CLOSING;
m_channel->fail("WebSocket is closed before the connection is established.");
- return;
+ return { };
}
m_state = CLOSING;
if (m_channel)
m_channel->close(code, reason);
+ return { };
}
const URL& WebSocket::url() const
@@ -406,7 +417,7 @@ WebSocket::State WebSocket::readyState() const
return m_state;
}
-unsigned long WebSocket::bufferedAmount() const
+unsigned WebSocket::bufferedAmount() const
{
return saturateAdd(m_bufferedAmount, m_bufferedAmountAfterClose);
}
@@ -424,26 +435,27 @@ String WebSocket::extensions() const
String WebSocket::binaryType() const
{
switch (m_binaryType) {
- case BinaryTypeBlob:
- return "blob";
- case BinaryTypeArrayBuffer:
- return "arraybuffer";
+ case BinaryType::Blob:
+ return ASCIILiteral("blob");
+ case BinaryType::ArrayBuffer:
+ return ASCIILiteral("arraybuffer");
}
ASSERT_NOT_REACHED();
return String();
}
-void WebSocket::setBinaryType(const String& binaryType)
+ExceptionOr<void> WebSocket::setBinaryType(const String& binaryType)
{
if (binaryType == "blob") {
- m_binaryType = BinaryTypeBlob;
- return;
+ m_binaryType = BinaryType::Blob;
+ return { };
}
if (binaryType == "arraybuffer") {
- m_binaryType = BinaryTypeArrayBuffer;
- return;
+ m_binaryType = BinaryType::ArrayBuffer;
+ return { };
}
- scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "'" + binaryType + "' is not a valid value for binaryType; binaryType remains unchanged.");
+ scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "'" + binaryType + "' is not a valid value for binaryType; binaryType remains unchanged.");
+ return Exception { SYNTAX_ERR };
}
EventTargetInterface WebSocket::eventTargetInterface() const
@@ -464,21 +476,49 @@ void WebSocket::contextDestroyed()
ActiveDOMObject::contextDestroyed();
}
-bool WebSocket::canSuspend() const
+bool WebSocket::canSuspendForDocumentSuspension() const
{
- return !m_channel;
+ return true;
}
-void WebSocket::suspend(ReasonForSuspension)
+void WebSocket::suspend(ReasonForSuspension reason)
{
- if (m_channel)
- m_channel->suspend();
+ if (m_resumeTimer.isActive())
+ m_resumeTimer.stop();
+
+ m_shouldDelayEventFiring = true;
+
+ if (m_channel) {
+ if (reason == ActiveDOMObject::PageCache) {
+ // This will cause didClose() to be called.
+ m_channel->fail("WebSocket is closed due to suspension.");
+ } else
+ m_channel->suspend();
+ }
}
void WebSocket::resume()
{
if (m_channel)
m_channel->resume();
+ else if (!m_pendingEvents.isEmpty() && !m_resumeTimer.isActive()) {
+ // Fire the pending events in a timer as we are not allowed to execute arbitrary JS from resume().
+ m_resumeTimer.startOneShot(0);
+ }
+
+ m_shouldDelayEventFiring = false;
+}
+
+void WebSocket::resumeTimerFired()
+{
+ Ref<WebSocket> protectedThis(*this);
+
+ ASSERT(!m_pendingEvents.isEmpty());
+
+ // Check m_shouldDelayEventFiring when iterating in case firing an event causes
+ // suspend() to be called.
+ while (!m_pendingEvents.isEmpty() && !m_shouldDelayEventFiring)
+ dispatchEvent(m_pendingEvents.takeFirst());
}
void WebSocket::stop()
@@ -486,18 +526,24 @@ void WebSocket::stop()
bool pending = hasPendingActivity();
if (m_channel)
m_channel->disconnect();
- m_channel = 0;
+ m_channel = nullptr;
m_state = CLOSED;
+ m_pendingEvents.clear();
ActiveDOMObject::stop();
if (pending)
ActiveDOMObject::unsetPendingActivity(this);
}
+const char* WebSocket::activeDOMObjectName() const
+{
+ return "WebSocket";
+}
+
void WebSocket::didConnect()
{
LOG(Network, "WebSocket %p didConnect()", this);
if (m_state != CONNECTING) {
- didClose(0, ClosingHandshakeIncomplete, WebSocketChannel::CloseEventCodeAbnormalClosure, "");
+ didClose(0, ClosingHandshakeIncomplete, WebSocketChannel::CloseEventCodeAbnormalClosure, emptyString());
return;
}
ASSERT(scriptExecutionContext());
@@ -516,23 +562,16 @@ void WebSocket::didReceiveMessage(const String& msg)
dispatchEvent(MessageEvent::create(msg, SecurityOrigin::create(m_url)->toString()));
}
-void WebSocket::didReceiveBinaryData(PassOwnPtr<Vector<char>> binaryData)
+void WebSocket::didReceiveBinaryData(Vector<uint8_t>&& binaryData)
{
- LOG(Network, "WebSocket %p didReceiveBinaryData() %lu byte binary message", this, static_cast<unsigned long>(binaryData->size()));
+ LOG(Network, "WebSocket %p didReceiveBinaryData() %u byte binary message", this, static_cast<unsigned>(binaryData.size()));
switch (m_binaryType) {
- case BinaryTypeBlob: {
- size_t size = binaryData->size();
- RefPtr<RawData> rawData = RawData::create();
- binaryData->swap(*rawData->mutableData());
- auto blobData = std::make_unique<BlobData>();
- blobData->appendData(rawData.release(), 0, BlobDataItem::toEndOfFile);
- RefPtr<Blob> blob = Blob::create(std::move(blobData), size);
- dispatchEvent(MessageEvent::create(blob.release(), SecurityOrigin::create(m_url)->toString()));
+ case BinaryType::Blob:
+ // FIXME: We just received the data from NetworkProcess, and are sending it back. This is inefficient.
+ dispatchEvent(MessageEvent::create(Blob::create(WTFMove(binaryData), emptyString()), SecurityOrigin::create(m_url)->toString()));
break;
- }
-
- case BinaryTypeArrayBuffer:
- dispatchEvent(MessageEvent::create(ArrayBuffer::create(binaryData->data(), binaryData->size()), SecurityOrigin::create(m_url)->toString()));
+ case BinaryType::ArrayBuffer:
+ dispatchEvent(MessageEvent::create(ArrayBuffer::create(binaryData.data(), binaryData.size()), SecurityOrigin::create(m_url)->toString()));
break;
}
}
@@ -540,13 +579,14 @@ void WebSocket::didReceiveBinaryData(PassOwnPtr<Vector<char>> binaryData)
void WebSocket::didReceiveMessageError()
{
LOG(Network, "WebSocket %p didReceiveErrorMessage()", this);
+ m_state = CLOSED;
ASSERT(scriptExecutionContext());
- dispatchEvent(Event::create(eventNames().errorEvent, false, false));
+ dispatchOrQueueErrorEvent();
}
-void WebSocket::didUpdateBufferedAmount(unsigned long bufferedAmount)
+void WebSocket::didUpdateBufferedAmount(unsigned bufferedAmount)
{
- LOG(Network, "WebSocket %p didUpdateBufferedAmount() New bufferedAmount is %lu", this, bufferedAmount);
+ LOG(Network, "WebSocket %p didUpdateBufferedAmount() New bufferedAmount is %u", this, bufferedAmount);
if (m_state == CLOSED)
return;
m_bufferedAmount = bufferedAmount;
@@ -558,7 +598,7 @@ void WebSocket::didStartClosingHandshake()
m_state = CLOSING;
}
-void WebSocket::didClose(unsigned long unhandledBufferedAmount, ClosingHandshakeCompletionStatus closingHandshakeCompletion, unsigned short code, const String& reason)
+void WebSocket::didClose(unsigned unhandledBufferedAmount, ClosingHandshakeCompletionStatus closingHandshakeCompletion, unsigned short code, const String& reason)
{
LOG(Network, "WebSocket %p didClose()", this);
if (!m_channel)
@@ -567,16 +607,23 @@ void WebSocket::didClose(unsigned long unhandledBufferedAmount, ClosingHandshake
m_state = CLOSED;
m_bufferedAmount = unhandledBufferedAmount;
ASSERT(scriptExecutionContext());
- RefPtr<CloseEvent> event = CloseEvent::create(wasClean, code, reason);
- dispatchEvent(event);
+
+ dispatchOrQueueEvent(CloseEvent::create(wasClean, code, reason));
+
if (m_channel) {
m_channel->disconnect();
- m_channel = 0;
+ m_channel = nullptr;
}
if (hasPendingActivity())
ActiveDOMObject::unsetPendingActivity(this);
}
+void WebSocket::didUpgradeURL()
+{
+ ASSERT(m_url.protocolIs("ws"));
+ m_url.setProtocol("wss");
+}
+
size_t WebSocket::getFramingOverhead(size_t payloadSize)
{
static const size_t hybiBaseFramingOverhead = 2; // Every frame has at least two-byte header.
@@ -591,6 +638,23 @@ size_t WebSocket::getFramingOverhead(size_t payloadSize)
return overhead;
}
+void WebSocket::dispatchOrQueueErrorEvent()
+{
+ if (m_dispatchedErrorEvent)
+ return;
+
+ m_dispatchedErrorEvent = true;
+ dispatchOrQueueEvent(Event::create(eventNames().errorEvent, false, false));
+}
+
+void WebSocket::dispatchOrQueueEvent(Ref<Event>&& event)
+{
+ if (m_shouldDelayEventFiring)
+ m_pendingEvents.append(WTFMove(event));
+ else
+ dispatchEvent(event);
+}
+
} // namespace WebCore
#endif
diff --git a/Source/WebCore/Modules/websockets/WebSocket.h b/Source/WebCore/Modules/websockets/WebSocket.h
index 75482d811..7d43f0436 100644
--- a/Source/WebCore/Modules/websockets/WebSocket.h
+++ b/Source/WebCore/Modules/websockets/WebSocket.h
@@ -28,37 +28,38 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef WebSocket_h
-#define WebSocket_h
+#pragma once
#if ENABLE(WEB_SOCKETS)
#include "ActiveDOMObject.h"
-#include "EventListener.h"
-#include "EventNames.h"
#include "EventTarget.h"
+#include "ExceptionOr.h"
+#include "Timer.h"
#include "URL.h"
-#include "WebSocketChannel.h"
#include "WebSocketChannelClient.h"
-#include <wtf/Forward.h>
-#include <wtf/OwnPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/text/AtomicStringHash.h>
+#include <wtf/Deque.h>
+
+namespace JSC {
+class ArrayBuffer;
+class ArrayBufferView;
+}
namespace WebCore {
class Blob;
class ThreadableWebSocketChannel;
-class WebSocket final : public RefCounted<WebSocket>, public EventTargetWithInlineData, public ActiveDOMObject, public WebSocketChannelClient {
+class WebSocket final : public RefCounted<WebSocket>, public EventTargetWithInlineData, public ActiveDOMObject, private WebSocketChannelClient {
public:
static void setIsAvailable(bool);
static bool isAvailable();
- static const char* subProtocolSeperator();
- static PassRefPtr<WebSocket> create(ScriptExecutionContext&);
- static PassRefPtr<WebSocket> create(ScriptExecutionContext&, const String& url, ExceptionCode&);
- static PassRefPtr<WebSocket> create(ScriptExecutionContext&, const String& url, const String& protocol, ExceptionCode&);
- static PassRefPtr<WebSocket> create(ScriptExecutionContext&, const String& url, const Vector<String>& protocols, ExceptionCode&);
+
+ static const char* subprotocolSeparator();
+
+ static ExceptionOr<Ref<WebSocket>> create(ScriptExecutionContext&, const String& url);
+ static ExceptionOr<Ref<WebSocket>> create(ScriptExecutionContext&, const String& url, const String& protocol);
+ static ExceptionOr<Ref<WebSocket>> create(ScriptExecutionContext&, const String& url, const Vector<String>& protocols);
virtual ~WebSocket();
enum State {
@@ -68,83 +69,79 @@ public:
CLOSED = 3
};
- void connect(const String& url, ExceptionCode&);
- void connect(const String& url, const String& protocol, ExceptionCode&);
- void connect(const String& url, const Vector<String>& protocols, ExceptionCode&);
+ ExceptionOr<void> connect(const String& url);
+ ExceptionOr<void> connect(const String& url, const String& protocol);
+ ExceptionOr<void> connect(const String& url, const Vector<String>& protocols);
- void send(const String& message, ExceptionCode&);
- void send(JSC::ArrayBuffer*, ExceptionCode&);
- void send(JSC::ArrayBufferView*, ExceptionCode&);
- void send(Blob*, ExceptionCode&);
+ ExceptionOr<void> send(const String& message);
+ ExceptionOr<void> send(JSC::ArrayBuffer&);
+ ExceptionOr<void> send(JSC::ArrayBufferView&);
+ ExceptionOr<void> send(Blob&);
- void close(int code, const String& reason, ExceptionCode&);
- void close(ExceptionCode& ec) { close(WebSocketChannel::CloseEventCodeNotSpecified, String(), ec); }
- void close(int code, ExceptionCode& ec) { close(code, String(), ec); }
+ ExceptionOr<void> close(std::optional<unsigned short> code, const String& reason);
const URL& url() const;
State readyState() const;
- unsigned long bufferedAmount() const;
+ unsigned bufferedAmount() const;
String protocol() const;
String extensions() const;
String binaryType() const;
- void setBinaryType(const String&);
+ ExceptionOr<void> setBinaryType(const String&);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(open);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(message);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(error);
- DEFINE_ATTRIBUTE_EVENT_LISTENER(close);
+ using RefCounted::ref;
+ using RefCounted::deref;
- // EventTarget functions.
- virtual EventTargetInterface eventTargetInterface() const override;
- virtual ScriptExecutionContext* scriptExecutionContext() const override;
+private:
+ explicit WebSocket(ScriptExecutionContext&);
- using RefCounted<WebSocket>::ref;
- using RefCounted<WebSocket>::deref;
+ void resumeTimerFired();
+ void dispatchOrQueueErrorEvent();
+ void dispatchOrQueueEvent(Ref<Event>&&);
- // WebSocketChannelClient functions.
- virtual void didConnect() override;
- virtual void didReceiveMessage(const String& message) override;
- virtual void didReceiveBinaryData(PassOwnPtr<Vector<char>>) override;
- virtual void didReceiveMessageError() override;
- virtual void didUpdateBufferedAmount(unsigned long bufferedAmount) override;
- virtual void didStartClosingHandshake() override;
- virtual void didClose(unsigned long unhandledBufferedAmount, ClosingHandshakeCompletionStatus, unsigned short code, const String& reason) override;
+ void contextDestroyed() final;
+ bool canSuspendForDocumentSuspension() const final;
+ void suspend(ReasonForSuspension) final;
+ void resume() final;
+ void stop() final;
+ const char* activeDOMObjectName() const final;
-private:
- explicit WebSocket(ScriptExecutionContext&);
+ EventTargetInterface eventTargetInterface() const final;
+ ScriptExecutionContext* scriptExecutionContext() const final;
- // ActiveDOMObject functions.
- virtual void contextDestroyed() override;
- virtual bool canSuspend() const override;
- virtual void suspend(ReasonForSuspension) override;
- virtual void resume() override;
- virtual void stop() override;
+ void refEventTarget() final { ref(); }
+ void derefEventTarget() final { deref(); }
- virtual void refEventTarget() override { ref(); }
- virtual void derefEventTarget() override { deref(); }
+ void didConnect() final;
+ void didReceiveMessage(const String& message) final;
+ void didReceiveBinaryData(Vector<uint8_t>&&) final;
+ void didReceiveMessageError() final;
+ void didUpdateBufferedAmount(unsigned bufferedAmount) final;
+ void didStartClosingHandshake() final;
+ void didClose(unsigned unhandledBufferedAmount, ClosingHandshakeCompletionStatus, unsigned short code, const String& reason) final;
+ void didUpgradeURL() final;
size_t getFramingOverhead(size_t payloadSize);
- enum BinaryType {
- BinaryTypeBlob,
- BinaryTypeArrayBuffer
- };
+ enum class BinaryType { Blob, ArrayBuffer };
RefPtr<ThreadableWebSocketChannel> m_channel;
- State m_state;
+ State m_state { CONNECTING };
URL m_url;
- unsigned long m_bufferedAmount;
- unsigned long m_bufferedAmountAfterClose;
- BinaryType m_binaryType;
+ unsigned m_bufferedAmount { 0 };
+ unsigned m_bufferedAmountAfterClose { 0 };
+ BinaryType m_binaryType { BinaryType::Blob };
String m_subprotocol;
String m_extensions;
+
+ Timer m_resumeTimer;
+ bool m_shouldDelayEventFiring { false };
+ Deque<Ref<Event>> m_pendingEvents;
+ bool m_dispatchedErrorEvent { false };
};
} // namespace WebCore
#endif // ENABLE(WEB_SOCKETS)
-
-#endif // WebSocket_h
diff --git a/Source/WebCore/Modules/websockets/WebSocket.idl b/Source/WebCore/Modules/websockets/WebSocket.idl
index 59de969b9..d71ed2b86 100644
--- a/Source/WebCore/Modules/websockets/WebSocket.idl
+++ b/Source/WebCore/Modules/websockets/WebSocket.idl
@@ -30,22 +30,18 @@
*/
[
- GlobalContext=DOMWindow&WorkerGlobalScope,
- EnabledAtRuntime,
- Conditional=WEB_SOCKETS,
ActiveDOMObject,
- Constructor(DOMString url),
- Constructor(DOMString url, sequence<DOMString> protocols),
- Constructor(DOMString url, DOMString protocol),
- ConstructorRaisesException,
+ Conditional=WEB_SOCKETS,
+ Constructor(USVString url, optional sequence<DOMString> protocols = []),
+ Constructor(USVString url, DOMString protocol),
+ ConstructorMayThrowException,
ConstructorCallWith=ScriptExecutionContext,
- EventTarget,
- JSNoStaticTables,
-] interface WebSocket {
- readonly attribute DOMString URL; // Lowercased .url is the one in the spec, but leaving .URL for compatibility reasons.
- readonly attribute DOMString url;
+ EnabledAtRuntime,
+ Exposed=(Window,Worker),
+] interface WebSocket : EventTarget {
+ readonly attribute USVString URL; // Lowercased .url is the one in the spec, but leaving .URL for compatibility reasons.
+ readonly attribute USVString url;
- // ready state
const unsigned short CONNECTING = 0;
const unsigned short OPEN = 1;
const unsigned short CLOSING = 2;
@@ -54,30 +50,20 @@
readonly attribute unsigned long bufferedAmount;
- // networking
- attribute EventListener onopen;
- attribute EventListener onmessage;
- attribute EventListener onerror;
- attribute EventListener onclose;
-
- [TreatReturnedNullStringAs=Undefined] readonly attribute DOMString protocol;
- [TreatReturnedNullStringAs=Undefined] readonly attribute DOMString extensions;
+ attribute EventHandler onopen;
+ attribute EventHandler onmessage;
+ attribute EventHandler onerror;
+ attribute EventHandler onclose;
- attribute DOMString binaryType;
+ readonly attribute DOMString? protocol;
+ readonly attribute DOMString? extensions;
- [RaisesException] void send(ArrayBuffer data);
- [RaisesException] void send(ArrayBufferView data);
- [RaisesException] void send(Blob data);
- [RaisesException] void send(DOMString data);
+ [SetterMayThrowException] attribute DOMString binaryType;
- [RaisesException] void close([Clamp] optional unsigned short code, optional DOMString reason);
+ [MayThrowException] void send(ArrayBuffer data);
+ [MayThrowException] void send(ArrayBufferView data);
+ [MayThrowException] void send(Blob data);
+ [MayThrowException] void send(USVString data);
- // EventTarget interface
- void addEventListener(DOMString type,
- EventListener listener,
- optional boolean useCapture);
- void removeEventListener(DOMString type,
- EventListener listener,
- optional boolean useCapture);
- [RaisesException] boolean dispatchEvent(Event evt);
+ [MayThrowException] void close([Clamp] optional unsigned short code, optional DOMString reason);
};
diff --git a/Source/WebCore/Modules/websockets/WebSocketChannel.cpp b/Source/WebCore/Modules/websockets/WebSocketChannel.cpp
index e13d63885..e76f6b9cf 100644
--- a/Source/WebCore/Modules/websockets/WebSocketChannel.cpp
+++ b/Source/WebCore/Modules/websockets/WebSocketChannel.cpp
@@ -37,31 +37,24 @@
#include "Blob.h"
#include "CookieJar.h"
#include "Document.h"
-#include "ExceptionCodePlaceholder.h"
#include "FileError.h"
#include "FileReaderLoader.h"
#include "Frame.h"
-#include "FrameLoader.h"
-#include "FrameLoaderClient.h"
#include "InspectorInstrumentation.h"
#include "Logging.h"
#include "Page.h"
#include "ProgressTracker.h"
#include "ResourceRequest.h"
-#include "ScriptCallStack.h"
#include "ScriptExecutionContext.h"
-#include "Settings.h"
+#include "SocketProvider.h"
#include "SocketStreamError.h"
#include "SocketStreamHandle.h"
+#include "UserContentProvider.h"
#include "WebSocketChannelClient.h"
#include "WebSocketHandshake.h"
-
#include <runtime/ArrayBuffer.h>
-#include <wtf/Deque.h>
#include <wtf/FastMalloc.h>
#include <wtf/HashMap.h>
-#include <wtf/OwnPtr.h>
-#include <wtf/PassOwnPtr.h>
#include <wtf/text/CString.h>
#include <wtf/text/StringHash.h>
#include <wtf/text/WTFString.h>
@@ -70,29 +63,17 @@ namespace WebCore {
const double TCPMaximumSegmentLifetime = 2 * 60.0;
-WebSocketChannel::WebSocketChannel(Document* document, WebSocketChannelClient* client)
- : m_document(document)
- , m_client(client)
- , m_resumeTimer(this, &WebSocketChannel::resumeTimerFired)
- , m_suspended(false)
- , m_closing(false)
- , m_receivedClosingHandshake(false)
- , m_closingTimer(this, &WebSocketChannel::closingTimerFired)
- , m_closed(false)
- , m_shouldDiscardReceivedData(false)
- , m_unhandledBufferedAmount(0)
- , m_identifier(0)
- , m_hasContinuousFrame(false)
- , m_closeEventCode(CloseEventCodeAbnormalClosure)
- , m_outgoingFrameQueueStatus(OutgoingFrameQueueOpen)
-#if ENABLE(BLOB)
- , m_blobLoaderStatus(BlobLoaderNotStarted)
-#endif
+WebSocketChannel::WebSocketChannel(Document& document, WebSocketChannelClient& client, SocketProvider& provider)
+ : m_document(&document)
+ , m_client(&client)
+ , m_resumeTimer(*this, &WebSocketChannel::resumeTimerFired)
+ , m_closingTimer(*this, &WebSocketChannel::closingTimerFired)
+ , m_socketProvider(provider)
{
- if (Page* page = m_document->page())
+ if (Page* page = document.page())
m_identifier = page->progress().createUniqueIdentifier();
- LOG(Network, "WebSocketChannel %p ctor, identifier %lu", this, m_identifier);
+ LOG(Network, "WebSocketChannel %p ctor, identifier %u", this, m_identifier);
}
WebSocketChannel::~WebSocketChannel()
@@ -100,29 +81,62 @@ WebSocketChannel::~WebSocketChannel()
LOG(Network, "WebSocketChannel %p dtor", this);
}
-void WebSocketChannel::connect(const URL& url, const String& protocol)
+void WebSocketChannel::connect(const URL& requestedURL, const String& protocol)
{
LOG(Network, "WebSocketChannel %p connect()", this);
+
+ URL url = requestedURL;
+ bool allowCookies = true;
+#if ENABLE(CONTENT_EXTENSIONS)
+ if (auto* page = m_document->page()) {
+ if (auto* documentLoader = m_document->loader()) {
+ auto blockedStatus = page->userContentProvider().processContentExtensionRulesForLoad(url, ResourceType::Raw, *documentLoader);
+ if (blockedStatus.blockedLoad) {
+ Ref<WebSocketChannel> protectedThis(*this);
+ callOnMainThread([protectedThis = WTFMove(protectedThis)] {
+ if (protectedThis->m_client)
+ protectedThis->m_client->didReceiveMessageError();
+ });
+ return;
+ }
+ if (blockedStatus.madeHTTPS) {
+ ASSERT(url.protocolIs("ws"));
+ url.setProtocol("wss");
+ if (m_client)
+ m_client->didUpgradeURL();
+ }
+ if (blockedStatus.blockedCookies)
+ allowCookies = false;
+ }
+ }
+#endif
+
ASSERT(!m_handle);
ASSERT(!m_suspended);
- m_handshake = adoptPtr(new WebSocketHandshake(url, protocol, m_document));
+ m_handshake = std::make_unique<WebSocketHandshake>(url, protocol, m_document, allowCookies);
m_handshake->reset();
if (m_deflateFramer.canDeflate())
m_handshake->addExtensionProcessor(m_deflateFramer.createExtensionProcessor());
if (m_identifier)
- InspectorInstrumentation::didCreateWebSocket(m_document, m_identifier, url, m_document->url(), protocol);
- ref();
- m_handle = SocketStreamHandle::create(m_handshake->url(), this);
+ InspectorInstrumentation::didCreateWebSocket(m_document, m_identifier, url);
+
+ if (Frame* frame = m_document->frame()) {
+ ref();
+ Page* page = frame->page();
+ SessionID sessionID = page ? page->sessionID() : SessionID::defaultSessionID();
+ String partition = m_document->topDocument().securityOrigin().domainForCachePartition();
+ m_handle = m_socketProvider->createSocketStreamHandle(m_handshake->url(), *this, sessionID, partition);
+ }
}
String WebSocketChannel::subprotocol()
{
LOG(Network, "WebSocketChannel %p subprotocol()", this);
if (!m_handshake || m_handshake->mode() != WebSocketHandshake::Connected)
- return "";
+ return emptyString();
String serverProtocol = m_handshake->serverWebSocketProtocol();
if (serverProtocol.isNull())
- return "";
+ return emptyString();
return serverProtocol;
}
@@ -130,10 +144,10 @@ String WebSocketChannel::extensions()
{
LOG(Network, "WebSocketChannel %p extensions()", this);
if (!m_handshake || m_handshake->mode() != WebSocketHandshake::Connected)
- return "";
+ return emptyString();
String extensions = m_handshake->acceptedExtensions();
if (extensions.isNull())
- return "";
+ return emptyString();
return extensions;
}
@@ -160,9 +174,9 @@ ThreadableWebSocketChannel::SendResult WebSocketChannel::send(const ArrayBuffer&
return ThreadableWebSocketChannel::SendSuccess;
}
-ThreadableWebSocketChannel::SendResult WebSocketChannel::send(const Blob& binaryData)
+ThreadableWebSocketChannel::SendResult WebSocketChannel::send(Blob& binaryData)
{
- LOG(Network, "WebSocketChannel %p send() Sending Blob '%s'", this, binaryData.url().stringCenterEllipsizedToLength().utf8().data());
+ LOG(Network, "WebSocketChannel %p send() Sending Blob '%s'", this, binaryData.url().string().utf8().data());
enqueueBlobFrame(WebSocketFrame::OpCodeBinary, binaryData);
processOutgoingFrameQueue();
return ThreadableWebSocketChannel::SendSuccess;
@@ -176,7 +190,7 @@ bool WebSocketChannel::send(const char* data, int length)
return true;
}
-unsigned long WebSocketChannel::bufferedAmount() const
+unsigned WebSocketChannel::bufferedAmount() const
{
LOG(Network, "WebSocketChannel %p bufferedAmount()", this);
ASSERT(m_handle);
@@ -190,7 +204,7 @@ void WebSocketChannel::close(int code, const String& reason)
ASSERT(!m_suspended);
if (!m_handle)
return;
- Ref<WebSocketChannel> protect(*this); // An attempt to send closing handshake may fail, which will get the channel closed and dereferenced.
+ Ref<WebSocketChannel> protectedThis(*this); // An attempt to send closing handshake may fail, which will get the channel closed and dereferenced.
startClosingHandshake(code, reason);
if (m_closing && !m_closingTimer.isActive())
m_closingTimer.startOneShot(2 * TCPMaximumSegmentLifetime);
@@ -202,12 +216,19 @@ void WebSocketChannel::fail(const String& reason)
ASSERT(!m_suspended);
if (m_document) {
InspectorInstrumentation::didReceiveWebSocketFrameError(m_document, m_identifier, reason);
- m_document->addConsoleMessage(NetworkMessageSource, ErrorMessageLevel, "WebSocket connection to '" + m_handshake->url().stringCenterEllipsizedToLength() + "' failed: " + reason);
+
+ String consoleMessage;
+ if (m_handshake)
+ consoleMessage = makeString("WebSocket connection to '", m_handshake->url().stringCenterEllipsizedToLength(), "' failed: ", reason);
+ else
+ consoleMessage = makeString("WebSocket connection failed: ", reason);
+
+ m_document->addConsoleMessage(MessageSource::Network, MessageLevel::Error, consoleMessage);
}
// Hybi-10 specification explicitly states we must not continue to handle incoming data
// once the WebSocket connection is failed (section 7.1.7).
- Ref<WebSocketChannel> protect(*this); // The client can close the channel, potentially removing the last reference.
+ Ref<WebSocketChannel> protectedThis(*this); // The client can close the channel, potentially removing the last reference.
m_shouldDiscardReceivedData = true;
if (!m_buffer.isEmpty())
skipBuffer(m_buffer.size()); // Save memory.
@@ -219,7 +240,8 @@ void WebSocketChannel::fail(const String& reason)
if (m_handle && !m_closed)
m_handle->disconnect(); // Will call didClose().
- ASSERT(m_closed);
+ // We should be closed by now, but if we never got a handshake then we never even opened.
+ ASSERT(m_closed || !m_handshake);
}
void WebSocketChannel::disconnect()
@@ -228,9 +250,9 @@ void WebSocketChannel::disconnect()
if (m_identifier && m_document)
InspectorInstrumentation::didCloseWebSocket(m_document, m_identifier);
if (m_handshake)
- m_handshake->clearScriptExecutionContext();
- m_client = 0;
- m_document = 0;
+ m_handshake->clearDocument();
+ m_client = nullptr;
+ m_document = nullptr;
if (m_handle)
m_handle->disconnect();
}
@@ -247,33 +269,25 @@ void WebSocketChannel::resume()
m_resumeTimer.startOneShot(0);
}
-void WebSocketChannel::willOpenSocketStream(SocketStreamHandle* handle)
-{
- LOG(Network, "WebSocketChannel %p willOpenSocketStream()", this);
- ASSERT(handle);
- if (m_document->frame())
- m_document->frame()->loader().client().dispatchWillOpenSocketStream(handle);
-}
-
-void WebSocketChannel::didOpenSocketStream(SocketStreamHandle* handle)
+void WebSocketChannel::didOpenSocketStream(SocketStreamHandle& handle)
{
LOG(Network, "WebSocketChannel %p didOpenSocketStream()", this);
- ASSERT(handle == m_handle);
+ ASSERT(&handle == m_handle);
if (!m_document)
return;
if (m_identifier)
InspectorInstrumentation::willSendWebSocketHandshakeRequest(m_document, m_identifier, m_handshake->clientHandshakeRequest());
CString handshakeMessage = m_handshake->clientHandshakeMessage();
- if (!handle->send(handshakeMessage.data(), handshakeMessage.length()))
+ if (!handle.send(handshakeMessage.data(), handshakeMessage.length()))
fail("Failed to send WebSocket handshake.");
}
-void WebSocketChannel::didCloseSocketStream(SocketStreamHandle* handle)
+void WebSocketChannel::didCloseSocketStream(SocketStreamHandle& handle)
{
LOG(Network, "WebSocketChannel %p didCloseSocketStream()", this);
if (m_identifier && m_document)
InspectorInstrumentation::didCloseWebSocket(m_document, m_identifier);
- ASSERT_UNUSED(handle, handle == m_handle || !m_handle);
+ ASSERT_UNUSED(handle, &handle == m_handle || !m_handle);
m_closed = true;
if (m_closingTimer.isActive())
m_closingTimer.stop();
@@ -284,54 +298,58 @@ void WebSocketChannel::didCloseSocketStream(SocketStreamHandle* handle)
if (m_suspended)
return;
WebSocketChannelClient* client = m_client;
- m_client = 0;
- m_document = 0;
- m_handle = 0;
+ m_client = nullptr;
+ m_document = nullptr;
+ m_handle = nullptr;
if (client)
client->didClose(m_unhandledBufferedAmount, m_receivedClosingHandshake ? WebSocketChannelClient::ClosingHandshakeComplete : WebSocketChannelClient::ClosingHandshakeIncomplete, m_closeEventCode, m_closeEventReason);
}
deref();
}
-void WebSocketChannel::didReceiveSocketStreamData(SocketStreamHandle* handle, const char* data, int len)
+void WebSocketChannel::didReceiveSocketStreamData(SocketStreamHandle& handle, const char* data, std::optional<size_t> len)
{
- LOG(Network, "WebSocketChannel %p didReceiveSocketStreamData() Received %d bytes", this, len);
- Ref<WebSocketChannel> protect(*this); // The client can close the channel, potentially removing the last reference.
- ASSERT(handle == m_handle);
+ if (len)
+ LOG(Network, "WebSocketChannel %p didReceiveSocketStreamData() Received %zu bytes", this, len.value());
+ else
+ LOG(Network, "WebSocketChannel %p didReceiveSocketStreamData() Received no bytes", this);
+ Ref<WebSocketChannel> protectedThis(*this); // The client can close the channel, potentially removing the last reference.
+ ASSERT(&handle == m_handle);
if (!m_document) {
return;
}
- if (len <= 0) {
- handle->disconnect();
+ if (!len || !len.value()) {
+ handle.disconnect();
return;
}
if (!m_client) {
m_shouldDiscardReceivedData = true;
- handle->disconnect();
+ handle.disconnect();
return;
}
if (m_shouldDiscardReceivedData)
return;
- if (!appendToBuffer(data, len)) {
+ if (!appendToBuffer(data, len.value())) {
m_shouldDiscardReceivedData = true;
fail("Ran out of memory while receiving WebSocket data.");
return;
}
- while (!m_suspended && m_client && !m_buffer.isEmpty())
+ while (!m_suspended && m_client && !m_buffer.isEmpty()) {
if (!processBuffer())
break;
+ }
}
-void WebSocketChannel::didUpdateBufferedAmount(SocketStreamHandle*, size_t bufferedAmount)
+void WebSocketChannel::didUpdateBufferedAmount(SocketStreamHandle&, size_t bufferedAmount)
{
if (m_client)
m_client->didUpdateBufferedAmount(bufferedAmount);
}
-void WebSocketChannel::didFailSocketStream(SocketStreamHandle* handle, const SocketStreamError& error)
+void WebSocketChannel::didFailSocketStream(SocketStreamHandle& handle, const SocketStreamError& error)
{
LOG(Network, "WebSocketChannel %p didFailSocketStream()", this);
- ASSERT(handle == m_handle || !m_handle);
+ ASSERT(&handle == m_handle || !m_handle);
if (m_document) {
String message;
if (error.isNull())
@@ -341,21 +359,12 @@ void WebSocketChannel::didFailSocketStream(SocketStreamHandle* handle, const Soc
else
message = "WebSocket network error: " + error.localizedDescription();
InspectorInstrumentation::didReceiveWebSocketFrameError(m_document, m_identifier, message);
- m_document->addConsoleMessage(NetworkMessageSource, ErrorMessageLevel, message);
+ m_document->addConsoleMessage(MessageSource::Network, MessageLevel::Error, message);
}
m_shouldDiscardReceivedData = true;
- handle->disconnect();
-}
-
-void WebSocketChannel::didReceiveAuthenticationChallenge(SocketStreamHandle*, const AuthenticationChallenge&)
-{
+ handle.disconnect();
}
-void WebSocketChannel::didCancelAuthenticationChallenge(SocketStreamHandle*, const AuthenticationChallenge&)
-{
-}
-
-#if ENABLE(BLOB)
void WebSocketChannel::didStartLoading()
{
LOG(Network, "WebSocketChannel %p didStartLoading()", this);
@@ -385,18 +394,17 @@ void WebSocketChannel::didFail(int errorCode)
LOG(Network, "WebSocketChannel %p didFail() errorCode=%d", this, errorCode);
ASSERT(m_blobLoader);
ASSERT(m_blobLoaderStatus == BlobLoaderStarted);
- m_blobLoader.clear();
+ m_blobLoader = nullptr;
m_blobLoaderStatus = BlobLoaderFailed;
fail("Failed to load Blob: error code = " + String::number(errorCode)); // FIXME: Generate human-friendly reason message.
deref();
}
-#endif
bool WebSocketChannel::appendToBuffer(const char* data, size_t len)
{
size_t newBufferSize = m_buffer.size() + len;
if (newBufferSize < m_buffer.size()) {
- LOG(Network, "WebSocketChannel %p appendToBuffer() Buffer overflow (%lu bytes already in receive buffer and appending %lu bytes)", this, static_cast<unsigned long>(m_buffer.size()), static_cast<unsigned long>(len));
+ LOG(Network, "WebSocketChannel %p appendToBuffer() Buffer overflow (%u bytes already in receive buffer and appending %u bytes)", this, static_cast<unsigned>(m_buffer.size()), static_cast<unsigned>(len));
return false;
}
m_buffer.append(data, len);
@@ -415,7 +423,7 @@ bool WebSocketChannel::processBuffer()
ASSERT(!m_suspended);
ASSERT(m_client);
ASSERT(!m_buffer.isEmpty());
- LOG(Network, "WebSocketChannel %p processBuffer() Receive buffer has %lu bytes", this, static_cast<unsigned long>(m_buffer.size()));
+ LOG(Network, "WebSocketChannel %p processBuffer() Receive buffer has %u bytes", this, static_cast<unsigned>(m_buffer.size()));
if (m_shouldDiscardReceivedData)
return false;
@@ -425,7 +433,7 @@ bool WebSocketChannel::processBuffer()
return false;
}
- Ref<WebSocketChannel> protect(*this); // The client can close the channel, potentially removing the last reference.
+ Ref<WebSocketChannel> protectedThis(*this); // The client can close the channel, potentially removing the last reference.
if (m_handshake->mode() == WebSocketHandshake::Incomplete) {
int headerLength = m_handshake->readServerHandshake(m_buffer.data(), m_buffer.size());
@@ -435,16 +443,16 @@ bool WebSocketChannel::processBuffer()
if (m_identifier)
InspectorInstrumentation::didReceiveWebSocketHandshakeResponse(m_document, m_identifier, m_handshake->serverHandshakeResponse());
if (!m_handshake->serverSetCookie().isEmpty()) {
- if (cookiesEnabled(m_document)) {
+ if (m_document && cookiesEnabled(*m_document)) {
// Exception (for sandboxed documents) ignored.
- m_document->setCookie(m_handshake->serverSetCookie(), IGNORE_EXCEPTION);
+ m_document->setCookie(m_handshake->serverSetCookie());
}
}
// FIXME: handle set-cookie2.
LOG(Network, "WebSocketChannel %p Connected", this);
skipBuffer(headerLength);
m_client->didConnect();
- LOG(Network, "WebSocketChannel %p %lu bytes remaining in m_buffer", this, static_cast<unsigned long>(m_buffer.size()));
+ LOG(Network, "WebSocketChannel %p %u bytes remaining in m_buffer", this, static_cast<unsigned>(m_buffer.size()));
return !m_buffer.isEmpty();
}
ASSERT(m_handshake->mode() == WebSocketHandshake::Failed);
@@ -460,16 +468,14 @@ bool WebSocketChannel::processBuffer()
return processFrame();
}
-void WebSocketChannel::resumeTimerFired(Timer<WebSocketChannel>* timer)
+void WebSocketChannel::resumeTimerFired()
{
- ASSERT_UNUSED(timer, timer == &m_resumeTimer);
-
- Ref<WebSocketChannel> protect(*this); // The client can close the channel, potentially removing the last reference.
+ Ref<WebSocketChannel> protectedThis(*this); // The client can close the channel, potentially removing the last reference.
while (!m_suspended && m_client && !m_buffer.isEmpty())
if (!processBuffer())
break;
if (!m_suspended && m_client && m_closed && m_handle)
- didCloseSocketStream(m_handle.get());
+ didCloseSocketStream(*m_handle);
}
void WebSocketChannel::startClosingHandshake(int code, const String& reason)
@@ -489,7 +495,7 @@ void WebSocketChannel::startClosingHandshake(int code, const String& reason)
buf.append(reason.utf8().data(), reason.utf8().length());
}
enqueueRawFrame(WebSocketFrame::OpCodeClose, buf.data(), buf.size());
- Ref<WebSocketChannel> protect(*this); // An attempt to send closing handshake may fail, which will get the channel closed and dereferenced.
+ Ref<WebSocketChannel> protectedThis(*this); // An attempt to send closing handshake may fail, which will get the channel closed and dereferenced.
processOutgoingFrameQueue();
if (m_closed) {
@@ -502,10 +508,9 @@ void WebSocketChannel::startClosingHandshake(int code, const String& reason)
m_client->didStartClosingHandshake();
}
-void WebSocketChannel::closingTimerFired(Timer<WebSocketChannel>* timer)
+void WebSocketChannel::closingTimerFired()
{
LOG(Network, "WebSocketChannel %p closingTimerFired()", this);
- ASSERT_UNUSED(timer, &m_closingTimer == timer);
if (m_handle)
m_handle->disconnect();
}
@@ -529,7 +534,7 @@ bool WebSocketChannel::processFrame()
ASSERT(m_buffer.data() < frameEnd);
ASSERT(frameEnd <= m_buffer.data() + m_buffer.size());
- OwnPtr<InflateResultHolder> inflateResult = m_deflateFramer.inflate(frame);
+ auto inflateResult = m_deflateFramer.inflate(frame);
if (!inflateResult->succeeded()) {
fail(inflateResult->failureReason());
return false;
@@ -587,22 +592,20 @@ bool WebSocketChannel::processFrame()
// so we should pretend that we have finished to read this frame and
// make sure that the member variables are in a consistent state before
// the handler is invoked.
- // Vector<char>::swap() is used here to clear m_continuousFrameData.
- OwnPtr<Vector<char>> continuousFrameData = adoptPtr(new Vector<char>);
- m_continuousFrameData.swap(*continuousFrameData);
+ Vector<uint8_t> continuousFrameData = WTFMove(m_continuousFrameData);
m_hasContinuousFrame = false;
if (m_continuousFrameOpCode == WebSocketFrame::OpCodeText) {
String message;
- if (continuousFrameData->size())
- message = String::fromUTF8(continuousFrameData->data(), continuousFrameData->size());
+ if (continuousFrameData.size())
+ message = String::fromUTF8(continuousFrameData.data(), continuousFrameData.size());
else
- message = "";
+ message = emptyString();
if (message.isNull())
fail("Could not decode a text frame as UTF-8.");
else
m_client->didReceiveMessage(message);
} else if (m_continuousFrameOpCode == WebSocketFrame::OpCodeBinary)
- m_client->didReceiveBinaryData(continuousFrameData.release());
+ m_client->didReceiveBinaryData(WTFMove(continuousFrameData));
}
break;
@@ -612,7 +615,7 @@ bool WebSocketChannel::processFrame()
if (frame.payloadLength)
message = String::fromUTF8(frame.payload, frame.payloadLength);
else
- message = "";
+ message = emptyString();
skipBuffer(frameEnd - m_buffer.data());
if (message.isNull())
fail("Could not decode a text frame as UTF-8.");
@@ -629,10 +632,10 @@ bool WebSocketChannel::processFrame()
case WebSocketFrame::OpCodeBinary:
if (frame.final) {
- OwnPtr<Vector<char>> binaryData = adoptPtr(new Vector<char>(frame.payloadLength));
- memcpy(binaryData->data(), frame.payload, frame.payloadLength);
+ Vector<uint8_t> binaryData(frame.payloadLength);
+ memcpy(binaryData.data(), frame.payload, frame.payloadLength);
skipBuffer(frameEnd - m_buffer.data());
- m_client->didReceiveBinaryData(binaryData.release());
+ m_client->didReceiveBinaryData(WTFMove(binaryData));
} else {
m_hasContinuousFrame = true;
m_continuousFrameOpCode = WebSocketFrame::OpCodeBinary;
@@ -662,12 +665,13 @@ bool WebSocketChannel::processFrame()
if (frame.payloadLength >= 3)
m_closeEventReason = String::fromUTF8(&frame.payload[2], frame.payloadLength - 2);
else
- m_closeEventReason = "";
+ m_closeEventReason = emptyString();
skipBuffer(frameEnd - m_buffer.data());
m_receivedClosingHandshake = true;
startClosingHandshake(m_closeEventCode, m_closeEventReason);
if (m_closing) {
- m_outgoingFrameQueueStatus = OutgoingFrameQueueClosing;
+ if (m_outgoingFrameQueueStatus == OutgoingFrameQueueOpen)
+ m_outgoingFrameQueueStatus = OutgoingFrameQueueClosing;
processOutgoingFrameQueue();
}
break;
@@ -696,33 +700,33 @@ bool WebSocketChannel::processFrame()
void WebSocketChannel::enqueueTextFrame(const CString& string)
{
ASSERT(m_outgoingFrameQueueStatus == OutgoingFrameQueueOpen);
- OwnPtr<QueuedFrame> frame = adoptPtr(new QueuedFrame);
+ auto frame = std::make_unique<QueuedFrame>();
frame->opCode = WebSocketFrame::OpCodeText;
frame->frameType = QueuedFrameTypeString;
frame->stringData = string;
- m_outgoingFrameQueue.append(frame.release());
+ m_outgoingFrameQueue.append(WTFMove(frame));
}
void WebSocketChannel::enqueueRawFrame(WebSocketFrame::OpCode opCode, const char* data, size_t dataLength)
{
ASSERT(m_outgoingFrameQueueStatus == OutgoingFrameQueueOpen);
- OwnPtr<QueuedFrame> frame = adoptPtr(new QueuedFrame);
+ auto frame = std::make_unique<QueuedFrame>();
frame->opCode = opCode;
frame->frameType = QueuedFrameTypeVector;
frame->vectorData.resize(dataLength);
if (dataLength)
memcpy(frame->vectorData.data(), data, dataLength);
- m_outgoingFrameQueue.append(frame.release());
+ m_outgoingFrameQueue.append(WTFMove(frame));
}
-void WebSocketChannel::enqueueBlobFrame(WebSocketFrame::OpCode opCode, const Blob& blob)
+void WebSocketChannel::enqueueBlobFrame(WebSocketFrame::OpCode opCode, Blob& blob)
{
ASSERT(m_outgoingFrameQueueStatus == OutgoingFrameQueueOpen);
- OwnPtr<QueuedFrame> frame = adoptPtr(new QueuedFrame);
+ auto frame = std::make_unique<QueuedFrame>();
frame->opCode = opCode;
frame->frameType = QueuedFrameTypeBlob;
- frame->blobData = Blob::create(blob.url(), blob.type(), blob.size());
- m_outgoingFrameQueue.append(frame.release());
+ frame->blobData = &blob;
+ m_outgoingFrameQueue.append(WTFMove(frame));
}
void WebSocketChannel::processOutgoingFrameQueue()
@@ -730,10 +734,10 @@ void WebSocketChannel::processOutgoingFrameQueue()
if (m_outgoingFrameQueueStatus == OutgoingFrameQueueClosed)
return;
- Ref<WebSocketChannel> protect(*this); // Any call to fail() will get the channel closed and dereferenced.
+ Ref<WebSocketChannel> protectedThis(*this); // Any call to fail() will get the channel closed and dereferenced.
while (!m_outgoingFrameQueue.isEmpty()) {
- OwnPtr<QueuedFrame> frame = m_outgoingFrameQueue.takeFirst();
+ auto frame = m_outgoingFrameQueue.takeFirst();
switch (frame->frameType) {
case QueuedFrameTypeString: {
if (!sendFrame(frame->opCode, frame->stringData.data(), frame->stringData.length()))
@@ -747,34 +751,31 @@ void WebSocketChannel::processOutgoingFrameQueue()
break;
case QueuedFrameTypeBlob: {
-#if ENABLE(BLOB)
switch (m_blobLoaderStatus) {
case BlobLoaderNotStarted:
ref(); // Will be derefed after didFinishLoading() or didFail().
ASSERT(!m_blobLoader);
- m_blobLoader = adoptPtr(new FileReaderLoader(FileReaderLoader::ReadAsArrayBuffer, this));
+ ASSERT(frame->blobData);
+ m_blobLoader = std::make_unique<FileReaderLoader>(FileReaderLoader::ReadAsArrayBuffer, this);
m_blobLoaderStatus = BlobLoaderStarted;
- m_blobLoader->start(m_document, frame->blobData.get());
- m_outgoingFrameQueue.prepend(frame.release());
+ m_blobLoader->start(m_document, *frame->blobData);
+ m_outgoingFrameQueue.prepend(WTFMove(frame));
return;
case BlobLoaderStarted:
case BlobLoaderFailed:
- m_outgoingFrameQueue.prepend(frame.release());
+ m_outgoingFrameQueue.prepend(WTFMove(frame));
return;
case BlobLoaderFinished: {
RefPtr<ArrayBuffer> result = m_blobLoader->arrayBufferResult();
- m_blobLoader.clear();
+ m_blobLoader = nullptr;
m_blobLoaderStatus = BlobLoaderNotStarted;
if (!sendFrame(frame->opCode, static_cast<const char*>(result->data()), result->byteLength()))
fail("Failed to send WebSocket frame.");
break;
}
}
-#else
- fail("FileReader is not available. Could not send a Blob as WebSocket binary message.");
-#endif
break;
}
@@ -795,12 +796,10 @@ void WebSocketChannel::abortOutgoingFrameQueue()
{
m_outgoingFrameQueue.clear();
m_outgoingFrameQueueStatus = OutgoingFrameQueueClosed;
-#if ENABLE(BLOB)
if (m_blobLoaderStatus == BlobLoaderStarted) {
m_blobLoader->cancel();
didFail(FileError::ABORT_ERR);
}
-#endif
}
bool WebSocketChannel::sendFrame(WebSocketFrame::OpCode opCode, const char* data, size_t dataLength)
@@ -811,7 +810,7 @@ bool WebSocketChannel::sendFrame(WebSocketFrame::OpCode opCode, const char* data
WebSocketFrame frame(opCode, true, false, true, data, dataLength);
InspectorInstrumentation::didSendWebSocketFrame(m_document, m_identifier, frame);
- OwnPtr<DeflateResultHolder> deflateResult = m_deflateFramer.deflate(frame);
+ auto deflateResult = m_deflateFramer.deflate(frame);
if (!deflateResult->succeeded()) {
fail(deflateResult->failureReason());
return false;
diff --git a/Source/WebCore/Modules/websockets/WebSocketChannel.h b/Source/WebCore/Modules/websockets/WebSocketChannel.h
index 1102f6aaf..b3a52ccc7 100644
--- a/Source/WebCore/Modules/websockets/WebSocketChannel.h
+++ b/Source/WebCore/Modules/websockets/WebSocketChannel.h
@@ -28,8 +28,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef WebSocketChannel_h
-#define WebSocketChannel_h
+#pragma once
#if ENABLE(WEB_SOCKETS)
@@ -39,7 +38,6 @@
#include "Timer.h"
#include "WebSocketDeflateFramer.h"
#include "WebSocketFrame.h"
-#include "WebSocketHandshake.h"
#include <wtf/Deque.h>
#include <wtf/Forward.h>
#include <wtf/RefCounted.h>
@@ -51,46 +49,42 @@ namespace WebCore {
class Blob;
class Document;
class FileReaderLoader;
+class SocketProvider;
class SocketStreamHandle;
class SocketStreamError;
class WebSocketChannelClient;
+class WebSocketHandshake;
-class WebSocketChannel : public RefCounted<WebSocketChannel>, public SocketStreamHandleClient, public ThreadableWebSocketChannel
-#if ENABLE(BLOB)
- , public FileReaderLoaderClient
-#endif
+class WebSocketChannel : public RefCounted<WebSocketChannel>, public SocketStreamHandleClient, public ThreadableWebSocketChannel, public FileReaderLoaderClient
{
WTF_MAKE_FAST_ALLOCATED;
public:
- static PassRefPtr<WebSocketChannel> create(Document* document, WebSocketChannelClient* client) { return adoptRef(new WebSocketChannel(document, client)); }
+ static Ref<WebSocketChannel> create(Document& document, WebSocketChannelClient& client, SocketProvider& provider) { return adoptRef(*new WebSocketChannel(document, client, provider)); }
virtual ~WebSocketChannel();
bool send(const char* data, int length);
// ThreadableWebSocketChannel functions.
- virtual void connect(const URL&, const String& protocol) override;
- virtual String subprotocol() override;
- virtual String extensions() override;
- virtual ThreadableWebSocketChannel::SendResult send(const String& message) override;
- virtual ThreadableWebSocketChannel::SendResult send(const JSC::ArrayBuffer&, unsigned byteOffset, unsigned byteLength) override;
- virtual ThreadableWebSocketChannel::SendResult send(const Blob&) override;
- virtual unsigned long bufferedAmount() const override;
- virtual void close(int code, const String& reason) override; // Start closing handshake.
- virtual void fail(const String& reason) override;
- virtual void disconnect() override;
-
- virtual void suspend() override;
- virtual void resume() override;
+ void connect(const URL&, const String& protocol) override;
+ String subprotocol() override;
+ String extensions() override;
+ ThreadableWebSocketChannel::SendResult send(const String& message) override;
+ ThreadableWebSocketChannel::SendResult send(const JSC::ArrayBuffer&, unsigned byteOffset, unsigned byteLength) override;
+ ThreadableWebSocketChannel::SendResult send(Blob&) override;
+ unsigned bufferedAmount() const override;
+ void close(int code, const String& reason) override; // Start closing handshake.
+ void fail(const String& reason) override;
+ void disconnect() override;
+
+ void suspend() override;
+ void resume() override;
// SocketStreamHandleClient functions.
- virtual void willOpenSocketStream(SocketStreamHandle*) override;
- virtual void didOpenSocketStream(SocketStreamHandle*) override;
- virtual void didCloseSocketStream(SocketStreamHandle*) override;
- virtual void didReceiveSocketStreamData(SocketStreamHandle*, const char*, int) override;
- virtual void didUpdateBufferedAmount(SocketStreamHandle*, size_t bufferedAmount) override;
- virtual void didFailSocketStream(SocketStreamHandle*, const SocketStreamError&) override;
- virtual void didReceiveAuthenticationChallenge(SocketStreamHandle*, const AuthenticationChallenge&) override;
- virtual void didCancelAuthenticationChallenge(SocketStreamHandle*, const AuthenticationChallenge&) override;
+ void didOpenSocketStream(SocketStreamHandle&) final;
+ void didCloseSocketStream(SocketStreamHandle&) final;
+ void didReceiveSocketStreamData(SocketStreamHandle&, const char*, std::optional<size_t>) final;
+ void didUpdateBufferedAmount(SocketStreamHandle&, size_t bufferedAmount) final;
+ void didFailSocketStream(SocketStreamHandle&, const SocketStreamError&) final;
enum CloseEventCode {
CloseEventCodeNotSpecified = -1,
@@ -111,30 +105,28 @@ public:
CloseEventCodeMaximumUserDefined = 4999
};
-#if ENABLE(BLOB)
// FileReaderLoaderClient functions.
- virtual void didStartLoading();
- virtual void didReceiveData();
- virtual void didFinishLoading();
- virtual void didFail(int errorCode);
-#endif
+ void didStartLoading() override;
+ void didReceiveData() override;
+ void didFinishLoading() override;
+ void didFail(int errorCode) override;
using RefCounted<WebSocketChannel>::ref;
using RefCounted<WebSocketChannel>::deref;
protected:
- virtual void refThreadableWebSocketChannel() { ref(); }
- virtual void derefThreadableWebSocketChannel() { deref(); }
+ void refThreadableWebSocketChannel() override { ref(); }
+ void derefThreadableWebSocketChannel() override { deref(); }
private:
- WebSocketChannel(Document*, WebSocketChannelClient*);
+ WEBCORE_EXPORT WebSocketChannel(Document&, WebSocketChannelClient&, SocketProvider&);
bool appendToBuffer(const char* data, size_t len);
void skipBuffer(size_t len);
bool processBuffer();
- void resumeTimerFired(Timer<WebSocketChannel>*);
+ void resumeTimerFired();
void startClosingHandshake(int code, const String& reason);
- void closingTimerFired(Timer<WebSocketChannel>*);
+ void closingTimerFired();
bool processFrame();
@@ -161,7 +153,7 @@ private:
};
void enqueueTextFrame(const CString&);
void enqueueRawFrame(WebSocketFrame::OpCode, const char* data, size_t dataLength);
- void enqueueBlobFrame(WebSocketFrame::OpCode, const Blob&);
+ void enqueueBlobFrame(WebSocketFrame::OpCode, Blob&);
void processOutgoingFrameQueue();
void abortOutgoingFrameQueue();
@@ -182,53 +174,48 @@ private:
// instead of call sendFrame() directly.
bool sendFrame(WebSocketFrame::OpCode, const char* data, size_t dataLength);
-#if ENABLE(BLOB)
enum BlobLoaderStatus {
BlobLoaderNotStarted,
BlobLoaderStarted,
BlobLoaderFinished,
BlobLoaderFailed
};
-#endif
Document* m_document;
WebSocketChannelClient* m_client;
- OwnPtr<WebSocketHandshake> m_handshake;
+ std::unique_ptr<WebSocketHandshake> m_handshake;
RefPtr<SocketStreamHandle> m_handle;
Vector<char> m_buffer;
- Timer<WebSocketChannel> m_resumeTimer;
- bool m_suspended;
- bool m_closing;
- bool m_receivedClosingHandshake;
- Timer<WebSocketChannel> m_closingTimer;
- bool m_closed;
- bool m_shouldDiscardReceivedData;
- unsigned long m_unhandledBufferedAmount;
+ Timer m_resumeTimer;
+ bool m_suspended { false };
+ bool m_closing { false };
+ bool m_receivedClosingHandshake { false };
+ Timer m_closingTimer;
+ bool m_closed { false };
+ bool m_shouldDiscardReceivedData { false };
+ unsigned m_unhandledBufferedAmount { 0 };
- unsigned long m_identifier; // m_identifier == 0 means that we could not obtain a valid identifier.
+ unsigned m_identifier { 0 }; // m_identifier == 0 means that we could not obtain a valid identifier.
// Private members only for hybi-10 protocol.
- bool m_hasContinuousFrame;
+ bool m_hasContinuousFrame { false };
WebSocketFrame::OpCode m_continuousFrameOpCode;
- Vector<char> m_continuousFrameData;
- unsigned short m_closeEventCode;
+ Vector<uint8_t> m_continuousFrameData;
+ unsigned short m_closeEventCode { CloseEventCodeAbnormalClosure };
String m_closeEventReason;
- Deque<OwnPtr<QueuedFrame>> m_outgoingFrameQueue;
- OutgoingFrameQueueStatus m_outgoingFrameQueueStatus;
+ Deque<std::unique_ptr<QueuedFrame>> m_outgoingFrameQueue;
+ OutgoingFrameQueueStatus m_outgoingFrameQueueStatus { OutgoingFrameQueueOpen };
-#if ENABLE(BLOB)
// FIXME: Load two or more Blobs simultaneously for better performance.
- OwnPtr<FileReaderLoader> m_blobLoader;
- BlobLoaderStatus m_blobLoaderStatus;
-#endif
+ std::unique_ptr<FileReaderLoader> m_blobLoader;
+ BlobLoaderStatus m_blobLoaderStatus { BlobLoaderNotStarted };
WebSocketDeflateFramer m_deflateFramer;
+ Ref<SocketProvider> m_socketProvider;
};
} // namespace WebCore
#endif // ENABLE(WEB_SOCKETS)
-
-#endif // WebSocketChannel_h
diff --git a/Source/WebCore/Modules/websockets/WebSocketChannelClient.h b/Source/WebCore/Modules/websockets/WebSocketChannelClient.h
index 46641c06c..b7b7fcd2d 100644
--- a/Source/WebCore/Modules/websockets/WebSocketChannelClient.h
+++ b/Source/WebCore/Modules/websockets/WebSocketChannelClient.h
@@ -28,38 +28,35 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef WebSocketChannelClient_h
-#define WebSocketChannelClient_h
+#pragma once
#if ENABLE(WEB_SOCKETS)
#include <wtf/Forward.h>
-#include <wtf/PassOwnPtr.h>
#include <wtf/Vector.h>
namespace WebCore {
- class WebSocketChannelClient {
- public:
- virtual ~WebSocketChannelClient() { }
- virtual void didConnect() { }
- virtual void didReceiveMessage(const String&) { }
- virtual void didReceiveBinaryData(PassOwnPtr<Vector<char>>) { }
- virtual void didReceiveMessageError() { }
- virtual void didUpdateBufferedAmount(unsigned long /* bufferedAmount */) { }
- virtual void didStartClosingHandshake() { }
- enum ClosingHandshakeCompletionStatus {
- ClosingHandshakeIncomplete,
- ClosingHandshakeComplete
- };
- virtual void didClose(unsigned long /* unhandledBufferedAmount */, ClosingHandshakeCompletionStatus, unsigned short /* code */, const String& /* reason */) { }
-
- protected:
- WebSocketChannelClient() { }
+class WebSocketChannelClient {
+public:
+ virtual ~WebSocketChannelClient() { }
+ virtual void didConnect() = 0;
+ virtual void didReceiveMessage(const String&) = 0;
+ virtual void didReceiveBinaryData(Vector<uint8_t>&&) = 0;
+ virtual void didReceiveMessageError() = 0;
+ virtual void didUpdateBufferedAmount(unsigned bufferedAmount) = 0;
+ virtual void didStartClosingHandshake() = 0;
+ enum ClosingHandshakeCompletionStatus {
+ ClosingHandshakeIncomplete,
+ ClosingHandshakeComplete
};
+ virtual void didClose(unsigned unhandledBufferedAmount, ClosingHandshakeCompletionStatus, unsigned short code, const String& reason) = 0;
+ virtual void didUpgradeURL() = 0;
+
+protected:
+ WebSocketChannelClient() { }
+};
} // namespace WebCore
#endif // ENABLE(WEB_SOCKETS)
-
-#endif // WebSocketChannelClient_h
diff --git a/Source/WebCore/Modules/websockets/WebSocketDeflateFramer.cpp b/Source/WebCore/Modules/websockets/WebSocketDeflateFramer.cpp
index f3aa3f081..4677c053d 100644
--- a/Source/WebCore/Modules/websockets/WebSocketDeflateFramer.cpp
+++ b/Source/WebCore/Modules/websockets/WebSocketDeflateFramer.cpp
@@ -43,19 +43,14 @@ namespace WebCore {
class WebSocketExtensionDeflateFrame : public WebSocketExtensionProcessor {
WTF_MAKE_FAST_ALLOCATED;
public:
- static PassOwnPtr<WebSocketExtensionDeflateFrame> create(WebSocketDeflateFramer* framer)
- {
- return adoptPtr(new WebSocketExtensionDeflateFrame(framer));
- }
+ explicit WebSocketExtensionDeflateFrame(WebSocketDeflateFramer*);
virtual ~WebSocketExtensionDeflateFrame() { }
- virtual String handshakeString() override;
- virtual bool processResponse(const HashMap<String, String>&) override;
- virtual String failureReason() override { return m_failureReason; }
+ String handshakeString() override;
+ bool processResponse(const HashMap<String, String>&) override;
+ String failureReason() override { return m_failureReason; }
private:
- WebSocketExtensionDeflateFrame(WebSocketDeflateFramer*);
-
WebSocketDeflateFramer* m_framer;
bool m_responseProcessed;
String m_failureReason;
@@ -84,7 +79,7 @@ bool WebSocketExtensionDeflateFrame::processResponse(const HashMap<String, Strin
}
m_responseProcessed = true;
- int expectedNumParameters = 0;
+ unsigned expectedNumParameters = 0;
int windowBits = 15;
HashMap<String, String>::const_iterator parameter = serverParameters.find("max_window_bits");
if (parameter != serverParameters.end()) {
@@ -161,9 +156,9 @@ WebSocketDeflateFramer::WebSocketDeflateFramer()
{
}
-PassOwnPtr<WebSocketExtensionProcessor> WebSocketDeflateFramer::createExtensionProcessor()
+std::unique_ptr<WebSocketExtensionProcessor> WebSocketDeflateFramer::createExtensionProcessor()
{
- return WebSocketExtensionDeflateFrame::create(this);
+ return std::make_unique<WebSocketExtensionDeflateFrame>(this);
}
bool WebSocketDeflateFramer::canDeflate() const
@@ -178,33 +173,33 @@ bool WebSocketDeflateFramer::canDeflate() const
#if USE(ZLIB)
void WebSocketDeflateFramer::enableDeflate(int windowBits, WebSocketDeflater::ContextTakeOverMode mode)
{
- m_deflater = WebSocketDeflater::create(windowBits, mode);
- m_inflater = WebSocketInflater::create();
+ m_deflater = std::make_unique<WebSocketDeflater>(windowBits, mode);
+ m_inflater = std::make_unique<WebSocketInflater>();
if (!m_deflater->initialize() || !m_inflater->initialize()) {
- m_deflater.clear();
- m_inflater.clear();
+ m_deflater = nullptr;
+ m_inflater = nullptr;
return;
}
m_enabled = true;
}
#endif
-PassOwnPtr<DeflateResultHolder> WebSocketDeflateFramer::deflate(WebSocketFrame& frame)
+std::unique_ptr<DeflateResultHolder> WebSocketDeflateFramer::deflate(WebSocketFrame& frame)
{
#if USE(ZLIB)
- OwnPtr<DeflateResultHolder> result = DeflateResultHolder::create(this);
+ auto result = std::make_unique<DeflateResultHolder>(this);
if (!enabled() || !WebSocketFrame::isNonControlOpCode(frame.opCode) || !frame.payloadLength)
- return result.release();
+ return result;
if (!m_deflater->addBytes(frame.payload, frame.payloadLength) || !m_deflater->finish()) {
result->fail("Failed to compress frame");
- return result.release();
+ return result;
}
frame.compress = true;
frame.payload = m_deflater->data();
frame.payloadLength = m_deflater->size();
- return result.release();
+ return result;
#else
- return DeflateResultHolder::create(this);
+ return std::make_unique<DeflateResultHolder>(this);
#endif
}
@@ -216,30 +211,30 @@ void WebSocketDeflateFramer::resetDeflateContext()
#endif
}
-PassOwnPtr<InflateResultHolder> WebSocketDeflateFramer::inflate(WebSocketFrame& frame)
+std::unique_ptr<InflateResultHolder> WebSocketDeflateFramer::inflate(WebSocketFrame& frame)
{
- OwnPtr<InflateResultHolder> result = InflateResultHolder::create(this);
+ auto result = std::make_unique<InflateResultHolder>(this);
if (!enabled() && frame.compress) {
result->fail("Compressed bit must be 0 if no negotiated deflate-frame extension");
- return result.release();
+ return result;
}
#if USE(ZLIB)
if (!frame.compress)
- return result.release();
+ return result;
if (!WebSocketFrame::isNonControlOpCode(frame.opCode)) {
result->fail("Received unexpected compressed frame");
- return result.release();
+ return result;
}
if (!m_inflater->addBytes(frame.payload, frame.payloadLength) || !m_inflater->finish()) {
result->fail("Failed to decompress frame");
- return result.release();
+ return result;
}
frame.compress = false;
frame.payload = m_inflater->data();
frame.payloadLength = m_inflater->size();
- return result.release();
+ return result;
#else
- return result.release();
+ return result;
#endif
}
diff --git a/Source/WebCore/Modules/websockets/WebSocketDeflateFramer.h b/Source/WebCore/Modules/websockets/WebSocketDeflateFramer.h
index f3232af70..7cbc06da8 100644
--- a/Source/WebCore/Modules/websockets/WebSocketDeflateFramer.h
+++ b/Source/WebCore/Modules/websockets/WebSocketDeflateFramer.h
@@ -28,8 +28,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef WebSocketDeflateFramer_h
-#define WebSocketDeflateFramer_h
+#pragma once
#if ENABLE(WEB_SOCKETS)
@@ -38,8 +37,6 @@
#endif
#include "WebSocketExtensionProcessor.h"
#include "WebSocketFrame.h"
-#include <wtf/OwnPtr.h>
-#include <wtf/PassOwnPtr.h>
namespace WebCore {
@@ -48,11 +45,7 @@ class WebSocketDeflateFramer;
class DeflateResultHolder {
WTF_MAKE_FAST_ALLOCATED;
public:
- static PassOwnPtr<DeflateResultHolder> create(WebSocketDeflateFramer* framer)
- {
- return adoptPtr(new DeflateResultHolder(framer));
- }
-
+ explicit DeflateResultHolder(WebSocketDeflateFramer*);
~DeflateResultHolder();
bool succeeded() const { return m_succeeded; }
@@ -61,8 +54,6 @@ public:
void fail(const String& failureReason);
private:
- explicit DeflateResultHolder(WebSocketDeflateFramer*);
-
WebSocketDeflateFramer* m_framer;
bool m_succeeded;
String m_failureReason;
@@ -71,11 +62,7 @@ private:
class InflateResultHolder {
WTF_MAKE_FAST_ALLOCATED;
public:
- static PassOwnPtr<InflateResultHolder> create(WebSocketDeflateFramer* framer)
- {
- return adoptPtr(new InflateResultHolder(framer));
- }
-
+ explicit InflateResultHolder(WebSocketDeflateFramer*);
~InflateResultHolder();
bool succeeded() const { return m_succeeded; }
@@ -84,8 +71,6 @@ public:
void fail(const String& failureReason);
private:
- explicit InflateResultHolder(WebSocketDeflateFramer*);
-
WebSocketDeflateFramer* m_framer;
bool m_succeeded;
String m_failureReason;
@@ -95,14 +80,14 @@ class WebSocketDeflateFramer {
public:
WebSocketDeflateFramer();
- PassOwnPtr<WebSocketExtensionProcessor> createExtensionProcessor();
+ std::unique_ptr<WebSocketExtensionProcessor> createExtensionProcessor();
bool canDeflate() const;
bool enabled() const { return m_enabled; }
- PassOwnPtr<DeflateResultHolder> deflate(WebSocketFrame&);
+ std::unique_ptr<DeflateResultHolder> deflate(WebSocketFrame&);
void resetDeflateContext();
- PassOwnPtr<InflateResultHolder> inflate(WebSocketFrame&);
+ std::unique_ptr<InflateResultHolder> inflate(WebSocketFrame&);
void resetInflateContext();
void didFail();
@@ -114,13 +99,11 @@ public:
private:
bool m_enabled;
#if USE(ZLIB)
- OwnPtr<WebSocketDeflater> m_deflater;
- OwnPtr<WebSocketInflater> m_inflater;
+ std::unique_ptr<WebSocketDeflater> m_deflater;
+ std::unique_ptr<WebSocketInflater> m_inflater;
#endif
};
-}
+} // namespace WebCore
#endif // ENABLE(WEB_SOCKETS)
-
-#endif // WebSocketDeflateFramer_h
diff --git a/Source/WebCore/Modules/websockets/WebSocketDeflater.cpp b/Source/WebCore/Modules/websockets/WebSocketDeflater.cpp
index aef4b1e9d..497b293a6 100644
--- a/Source/WebCore/Modules/websockets/WebSocketDeflater.cpp
+++ b/Source/WebCore/Modules/websockets/WebSocketDeflater.cpp
@@ -47,18 +47,13 @@ namespace WebCore {
static const int defaultMemLevel = 1;
static const size_t bufferIncrementUnit = 4096;
-PassOwnPtr<WebSocketDeflater> WebSocketDeflater::create(int windowBits, ContextTakeOverMode contextTakeOverMode)
-{
- return adoptPtr(new WebSocketDeflater(windowBits, contextTakeOverMode));
-}
-
WebSocketDeflater::WebSocketDeflater(int windowBits, ContextTakeOverMode contextTakeOverMode)
: m_windowBits(windowBits)
, m_contextTakeOverMode(contextTakeOverMode)
{
ASSERT(m_windowBits >= 8);
ASSERT(m_windowBits <= 15);
- m_stream = adoptPtr(new z_stream);
+ m_stream = std::make_unique<z_stream>();
memset(m_stream.get(), 0, sizeof(z_stream));
}
@@ -127,15 +122,10 @@ void WebSocketDeflater::reset()
deflateReset(m_stream.get());
}
-PassOwnPtr<WebSocketInflater> WebSocketInflater::create(int windowBits)
-{
- return adoptPtr(new WebSocketInflater(windowBits));
-}
-
WebSocketInflater::WebSocketInflater(int windowBits)
: m_windowBits(windowBits)
{
- m_stream = adoptPtr(new z_stream);
+ m_stream = std::make_unique<z_stream>();
memset(m_stream.get(), 0, sizeof(z_stream));
}
diff --git a/Source/WebCore/Modules/websockets/WebSocketDeflater.h b/Source/WebCore/Modules/websockets/WebSocketDeflater.h
index f73eb20d3..8296c47ed 100644
--- a/Source/WebCore/Modules/websockets/WebSocketDeflater.h
+++ b/Source/WebCore/Modules/websockets/WebSocketDeflater.h
@@ -28,14 +28,11 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef WebSocketDeflater_h
-#define WebSocketDeflater_h
+#pragma once
#if ENABLE(WEB_SOCKETS)
#include <wtf/Noncopyable.h>
-#include <wtf/OwnPtr.h>
-#include <wtf/PassOwnPtr.h>
#include <wtf/Vector.h>
struct z_stream_s;
@@ -50,8 +47,8 @@ public:
DoNotTakeOverContext,
TakeOverContext
};
- static PassOwnPtr<WebSocketDeflater> create(int windowBits, ContextTakeOverMode = TakeOverContext);
+ explicit WebSocketDeflater(int windowBits, ContextTakeOverMode = TakeOverContext);
~WebSocketDeflater();
bool initialize();
@@ -62,19 +59,16 @@ public:
void reset();
private:
- WebSocketDeflater(int windowBits, ContextTakeOverMode);
-
int m_windowBits;
ContextTakeOverMode m_contextTakeOverMode;
Vector<char> m_buffer;
- OwnPtr<z_stream> m_stream;
+ std::unique_ptr<z_stream> m_stream;
};
class WebSocketInflater {
WTF_MAKE_FAST_ALLOCATED;
public:
- static PassOwnPtr<WebSocketInflater> create(int windowBits = 15);
-
+ explicit WebSocketInflater(int windowBits = 15);
~WebSocketInflater();
bool initialize();
@@ -85,15 +79,11 @@ public:
void reset();
private:
- explicit WebSocketInflater(int windowBits);
-
int m_windowBits;
Vector<char> m_buffer;
- OwnPtr<z_stream> m_stream;
+ std::unique_ptr<z_stream> m_stream;
};
-}
+} // namespace WebCore
#endif // ENABLE(WEB_SOCKETS)
-
-#endif // WebSocketDeflater_h
diff --git a/Source/WebCore/Modules/websockets/WebSocketExtensionDispatcher.cpp b/Source/WebCore/Modules/websockets/WebSocketExtensionDispatcher.cpp
index 06b719465..d834c733e 100644
--- a/Source/WebCore/Modules/websockets/WebSocketExtensionDispatcher.cpp
+++ b/Source/WebCore/Modules/websockets/WebSocketExtensionDispatcher.cpp
@@ -48,16 +48,16 @@ void WebSocketExtensionDispatcher::reset()
m_processors.clear();
}
-void WebSocketExtensionDispatcher::addProcessor(PassOwnPtr<WebSocketExtensionProcessor> processor)
+void WebSocketExtensionDispatcher::addProcessor(std::unique_ptr<WebSocketExtensionProcessor> processor)
{
- for (size_t i = 0; i < m_processors.size(); ++i) {
- if (m_processors[i]->extensionToken() == processor->extensionToken())
+ for (auto& extensionProcessor : m_processors) {
+ if (extensionProcessor->extensionToken() == processor->extensionToken())
return;
}
ASSERT(processor->handshakeString().length());
ASSERT(!processor->handshakeString().contains('\n'));
ASSERT(!processor->handshakeString().contains(static_cast<UChar>('\0')));
- m_processors.append(processor);
+ m_processors.append(WTFMove(processor));
}
const String WebSocketExtensionDispatcher::createHeaderValue() const
@@ -69,7 +69,7 @@ const String WebSocketExtensionDispatcher::createHeaderValue() const
StringBuilder builder;
builder.append(m_processors[0]->handshakeString());
for (size_t i = 1; i < numProcessors; ++i) {
- builder.append(", ");
+ builder.appendLiteral(", ");
builder.append(m_processors[i]->handshakeString());
}
return builder.toString();
@@ -78,15 +78,15 @@ const String WebSocketExtensionDispatcher::createHeaderValue() const
void WebSocketExtensionDispatcher::appendAcceptedExtension(const String& extensionToken, HashMap<String, String>& extensionParameters)
{
if (!m_acceptedExtensionsBuilder.isEmpty())
- m_acceptedExtensionsBuilder.append(", ");
+ m_acceptedExtensionsBuilder.appendLiteral(", ");
m_acceptedExtensionsBuilder.append(extensionToken);
// FIXME: Should use ListHashSet to keep the order of the parameters.
- for (HashMap<String, String>::const_iterator iterator = extensionParameters.begin(); iterator != extensionParameters.end(); ++iterator) {
- m_acceptedExtensionsBuilder.append("; ");
- m_acceptedExtensionsBuilder.append(iterator->key);
- if (!iterator->value.isNull()) {
- m_acceptedExtensionsBuilder.append("=");
- m_acceptedExtensionsBuilder.append(iterator->value);
+ for (auto& parameter : extensionParameters) {
+ m_acceptedExtensionsBuilder.appendLiteral("; ");
+ m_acceptedExtensionsBuilder.append(parameter.key);
+ if (!parameter.value.isNull()) {
+ m_acceptedExtensionsBuilder.append('=');
+ m_acceptedExtensionsBuilder.append(parameter.value);
}
}
}
@@ -118,9 +118,8 @@ bool WebSocketExtensionDispatcher::processHeaderValue(const String& headerValue)
return false;
}
- size_t index;
- for (index = 0; index < m_processors.size(); ++index) {
- WebSocketExtensionProcessor* processor = m_processors[index].get();
+ size_t index = 0;
+ for (auto& processor : m_processors) {
if (extensionToken == processor->extensionToken()) {
if (processor->processResponse(extensionParameters)) {
appendAcceptedExtension(extensionToken, extensionParameters);
@@ -129,6 +128,7 @@ bool WebSocketExtensionDispatcher::processHeaderValue(const String& headerValue)
fail(processor->failureReason());
return false;
}
+ ++index;
}
// There is no extension which can process the response.
if (index == m_processors.size()) {
diff --git a/Source/WebCore/Modules/websockets/WebSocketExtensionDispatcher.h b/Source/WebCore/Modules/websockets/WebSocketExtensionDispatcher.h
index 2430017d5..3b2568cee 100644
--- a/Source/WebCore/Modules/websockets/WebSocketExtensionDispatcher.h
+++ b/Source/WebCore/Modules/websockets/WebSocketExtensionDispatcher.h
@@ -28,14 +28,11 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef WebSocketExtensionDispatcher_h
-#define WebSocketExtensionDispatcher_h
+#pragma once
#if ENABLE(WEB_SOCKETS)
#include "WebSocketExtensionProcessor.h"
-#include <wtf/OwnPtr.h>
-#include <wtf/PassOwnPtr.h>
#include <wtf/Vector.h>
#include <wtf/text/StringBuilder.h>
#include <wtf/text/WTFString.h>
@@ -47,7 +44,7 @@ public:
WebSocketExtensionDispatcher() { }
void reset();
- void addProcessor(PassOwnPtr<WebSocketExtensionProcessor>);
+ void addProcessor(std::unique_ptr<WebSocketExtensionProcessor>);
const String createHeaderValue() const;
bool processHeaderValue(const String&);
@@ -58,13 +55,11 @@ private:
void appendAcceptedExtension(const String& extensionToken, HashMap<String, String>& extensionParameters);
void fail(const String& reason);
- Vector<OwnPtr<WebSocketExtensionProcessor>> m_processors;
+ Vector<std::unique_ptr<WebSocketExtensionProcessor>> m_processors;
StringBuilder m_acceptedExtensionsBuilder;
String m_failureReason;
};
-}
+} // namespace WebCore
#endif // ENABLE(WEB_SOCKETS)
-
-#endif // WebSocketExtensionDispatcher_h
diff --git a/Source/WebCore/Modules/websockets/WebSocketExtensionParser.h b/Source/WebCore/Modules/websockets/WebSocketExtensionParser.h
index c7b0e3646..3247768a8 100644
--- a/Source/WebCore/Modules/websockets/WebSocketExtensionParser.h
+++ b/Source/WebCore/Modules/websockets/WebSocketExtensionParser.h
@@ -28,8 +28,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef WebSocketExtensionParser_h
-#define WebSocketExtensionParser_h
+#pragma once
#if ENABLE(WEB_SOCKETS)
@@ -70,5 +69,3 @@ private:
} // namespace WebCore
#endif // ENABLE(WEB_SOCKETS)
-
-#endif // WebSocketExtensionParser_h
diff --git a/Source/WebCore/Modules/websockets/WebSocketExtensionProcessor.h b/Source/WebCore/Modules/websockets/WebSocketExtensionProcessor.h
index 167460c76..023a98c12 100644
--- a/Source/WebCore/Modules/websockets/WebSocketExtensionProcessor.h
+++ b/Source/WebCore/Modules/websockets/WebSocketExtensionProcessor.h
@@ -28,8 +28,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef WebSocketExtensionProcessor_h
-#define WebSocketExtensionProcessor_h
+#pragma once
#if ENABLE(WEB_SOCKETS)
@@ -67,8 +66,6 @@ private:
String m_extensionToken;
};
-}
+} // namespace WebCore
#endif // ENABLE(WEB_SOCKETS)
-
-#endif // WebSocketExtensionProcessor_h
diff --git a/Source/WebCore/Modules/websockets/WebSocketFrame.h b/Source/WebCore/Modules/websockets/WebSocketFrame.h
index c1893624f..687ccd0e8 100644
--- a/Source/WebCore/Modules/websockets/WebSocketFrame.h
+++ b/Source/WebCore/Modules/websockets/WebSocketFrame.h
@@ -28,8 +28,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef WebSocketFrame_h
-#define WebSocketFrame_h
+#pragma once
#if ENABLE(WEB_SOCKETS)
@@ -61,7 +60,7 @@ struct WebSocketFrame {
static bool needsExtendedLengthField(size_t payloadLength);
static ParseFrameResult parseFrame(char* data, size_t dataLength, WebSocketFrame&, const char*& frameEnd, String& errorString); // May modify part of data to unmask the frame.
- WebSocketFrame(OpCode = OpCodeInvalid, bool final = false, bool compress = false, bool masked = false, const char* payload = 0, size_t payloadLength = 0);
+ WebSocketFrame(OpCode = OpCodeInvalid, bool final = false, bool compress = false, bool masked = false, const char* payload = nullptr, size_t payloadLength = 0);
void makeFrameData(Vector<char>& frameData);
OpCode opCode;
@@ -77,5 +76,3 @@ struct WebSocketFrame {
} // namespace WebCore
#endif // ENABLE(WEB_SOCKETS)
-
-#endif // WebSocketFrame_h
diff --git a/Source/WebCore/Modules/websockets/WebSocketHandshake.cpp b/Source/WebCore/Modules/websockets/WebSocketHandshake.cpp
index e6782cf8f..dd3b9a4e4 100644
--- a/Source/WebCore/Modules/websockets/WebSocketHandshake.cpp
+++ b/Source/WebCore/Modules/websockets/WebSocketHandshake.cpp
@@ -40,13 +40,14 @@
#include "CookieJar.h"
#include "Document.h"
#include "HTTPHeaderMap.h"
+#include "HTTPHeaderNames.h"
#include "HTTPParsers.h"
#include "URL.h"
#include "Logging.h"
#include "ResourceRequest.h"
-#include "ScriptCallStack.h"
#include "ScriptExecutionContext.h"
#include "SecurityOrigin.h"
+#include <wtf/ASCIICType.h>
#include <wtf/CryptographicallyRandomNumber.h>
#include <wtf/MD5.h>
#include <wtf/SHA1.h>
@@ -56,6 +57,7 @@
#include <wtf/text/Base64.h>
#include <wtf/text/CString.h>
#include <wtf/text/StringBuilder.h>
+#include <wtf/text/StringView.h>
#include <wtf/text/WTFString.h>
#include <wtf/unicode/CharacterNames.h>
@@ -81,10 +83,10 @@ static String hostName(const URL& url, bool secure)
{
ASSERT(url.protocolIs("wss") == secure);
StringBuilder builder;
- builder.append(url.host().lower());
- if (url.port() && ((!secure && url.port() != 80) || (secure && url.port() != 443))) {
+ builder.append(url.host().convertToASCIILowercase());
+ if (url.port() && ((!secure && url.port().value() != 80) || (secure && url.port().value() != 443))) {
builder.append(':');
- builder.appendNumber(url.port());
+ builder.appendNumber(url.port().value());
}
return builder.toString();
}
@@ -118,12 +120,13 @@ String WebSocketHandshake::getExpectedWebSocketAccept(const String& secWebSocket
return base64Encode(hash.data(), SHA1::hashSize);
}
-WebSocketHandshake::WebSocketHandshake(const URL& url, const String& protocol, ScriptExecutionContext* context)
+WebSocketHandshake::WebSocketHandshake(const URL& url, const String& protocol, Document* document, bool allowCookies)
: m_url(url)
, m_clientProtocol(protocol)
, m_secure(m_url.protocolIs("wss"))
- , m_context(context)
+ , m_document(document)
, m_mode(Incomplete)
+ , m_allowCookies(allowCookies)
{
m_secWebSocketKey = generateSecWebSocketKey();
m_expectedAccept = getExpectedWebSocketAccept(m_secWebSocketKey);
@@ -140,12 +143,13 @@ const URL& WebSocketHandshake::url() const
void WebSocketHandshake::setURL(const URL& url)
{
- m_url = url.copy();
+ m_url = url.isolatedCopy();
}
+// FIXME: Return type should just be String, not const String.
const String WebSocketHandshake::host() const
{
- return m_url.host().lower();
+ return m_url.host().convertToASCIILowercase();
}
const String& WebSocketHandshake::clientProtocol() const
@@ -165,14 +169,14 @@ bool WebSocketHandshake::secure() const
String WebSocketHandshake::clientOrigin() const
{
- return m_context->securityOrigin()->toString();
+ return m_document->securityOrigin().toString();
}
String WebSocketHandshake::clientLocation() const
{
StringBuilder builder;
builder.append(m_secure ? "wss" : "ws");
- builder.append("://");
+ builder.appendLiteral("://");
builder.append(hostName(m_url, m_secure));
builder.append(resourceName(m_url));
return builder.toString();
@@ -183,9 +187,9 @@ CString WebSocketHandshake::clientHandshakeMessage() const
// Keep the following consistent with clientHandshakeRequest().
StringBuilder builder;
- builder.append("GET ");
+ builder.appendLiteral("GET ");
builder.append(resourceName(m_url));
- builder.append(" HTTP/1.1\r\n");
+ builder.appendLiteral(" HTTP/1.1\r\n");
Vector<String> fields;
fields.append("Upgrade: websocket");
@@ -196,12 +200,10 @@ CString WebSocketHandshake::clientHandshakeMessage() const
fields.append("Sec-WebSocket-Protocol: " + m_clientProtocol);
URL url = httpURLForAuthenticationAndCookies();
- if (m_context->isDocument()) {
- Document* document = toDocument(m_context);
- String cookie = cookieRequestHeaderFieldValue(document, url);
+ if (m_allowCookies && m_document) {
+ String cookie = cookieRequestHeaderFieldValue(*m_document, url);
if (!cookie.isEmpty())
fields.append("Cookie: " + cookie);
- // Set "Cookie2: <cookie>" if cookies 2 exists for url?
}
// Add no-cache headers to avoid compatibility issue.
@@ -218,18 +220,18 @@ CString WebSocketHandshake::clientHandshakeMessage() const
fields.append("Sec-WebSocket-Extensions: " + extensionValue);
// Add a User-Agent header.
- fields.append("User-Agent: " + m_context->userAgent(m_context->url()));
+ fields.append("User-Agent: " + m_document->userAgent(m_document->url()));
// Fields in the handshake are sent by the client in a random order; the
// order is not meaningful. Thus, it's ok to send the order we constructed
// the fields.
- for (size_t i = 0; i < fields.size(); i++) {
- builder.append(fields[i]);
- builder.append("\r\n");
+ for (auto& field : fields) {
+ builder.append(field);
+ builder.appendLiteral("\r\n");
}
- builder.append("\r\n");
+ builder.appendLiteral("\r\n");
return builder.toString().utf8();
}
@@ -237,37 +239,33 @@ CString WebSocketHandshake::clientHandshakeMessage() const
ResourceRequest WebSocketHandshake::clientHandshakeRequest() const
{
// Keep the following consistent with clientHandshakeMessage().
- // FIXME: do we need to store m_secWebSocketKey1, m_secWebSocketKey2 and
- // m_key3 in the request?
ResourceRequest request(m_url);
request.setHTTPMethod("GET");
- request.addHTTPHeaderField("Connection", "Upgrade");
- request.addHTTPHeaderField("Host", hostName(m_url, m_secure));
- request.addHTTPHeaderField("Origin", clientOrigin());
+ request.setHTTPHeaderField(HTTPHeaderName::Connection, "Upgrade");
+ request.setHTTPHeaderField(HTTPHeaderName::Host, hostName(m_url, m_secure));
+ request.setHTTPHeaderField(HTTPHeaderName::Origin, clientOrigin());
if (!m_clientProtocol.isEmpty())
- request.addHTTPHeaderField("Sec-WebSocket-Protocol", m_clientProtocol);
+ request.setHTTPHeaderField(HTTPHeaderName::SecWebSocketProtocol, m_clientProtocol);
URL url = httpURLForAuthenticationAndCookies();
- if (m_context->isDocument()) {
- Document* document = toDocument(m_context);
- String cookie = cookieRequestHeaderFieldValue(document, url);
+ if (m_allowCookies && m_document) {
+ String cookie = cookieRequestHeaderFieldValue(*m_document, url);
if (!cookie.isEmpty())
- request.addHTTPHeaderField("Cookie", cookie);
- // Set "Cookie2: <cookie>" if cookies 2 exists for url?
+ request.setHTTPHeaderField(HTTPHeaderName::Cookie, cookie);
}
- request.addHTTPHeaderField("Pragma", "no-cache");
- request.addHTTPHeaderField("Cache-Control", "no-cache");
+ request.setHTTPHeaderField(HTTPHeaderName::Pragma, "no-cache");
+ request.setHTTPHeaderField(HTTPHeaderName::CacheControl, "no-cache");
- request.addHTTPHeaderField("Sec-WebSocket-Key", m_secWebSocketKey);
- request.addHTTPHeaderField("Sec-WebSocket-Version", "13");
+ request.setHTTPHeaderField(HTTPHeaderName::SecWebSocketKey, m_secWebSocketKey);
+ request.setHTTPHeaderField(HTTPHeaderName::SecWebSocketVersion, "13");
const String extensionValue = m_extensionDispatcher.createHeaderValue();
if (extensionValue.length())
- request.addHTTPHeaderField("Sec-WebSocket-Extensions", extensionValue);
+ request.setHTTPHeaderField(HTTPHeaderName::SecWebSocketExtensions, extensionValue);
// Add a User-Agent header.
- request.addHTTPHeaderField("User-Agent", m_context->userAgent(m_context->url()));
+ request.setHTTPHeaderField(HTTPHeaderName::UserAgent, m_document->userAgent(m_document->url()));
return request;
}
@@ -278,9 +276,9 @@ void WebSocketHandshake::reset()
m_extensionDispatcher.reset();
}
-void WebSocketHandshake::clearScriptExecutionContext()
+void WebSocketHandshake::clearDocument()
{
- m_context = 0;
+ m_document = nullptr;
}
int WebSocketHandshake::readServerHandshake(const char* header, size_t len)
@@ -303,7 +301,7 @@ int WebSocketHandshake::readServerHandshake(const char* header, size_t len)
if (statusCode != 101) {
m_mode = Failed;
- m_failureReason = "Unexpected response code: " + String::number(statusCode);
+ m_failureReason = makeString("Unexpected response code: ", String::number(statusCode));
return len;
}
m_mode = Normal;
@@ -340,32 +338,27 @@ String WebSocketHandshake::failureReason() const
String WebSocketHandshake::serverWebSocketProtocol() const
{
- return m_serverHandshakeResponse.httpHeaderFields().get("sec-websocket-protocol");
+ return m_serverHandshakeResponse.httpHeaderFields().get(HTTPHeaderName::SecWebSocketProtocol);
}
String WebSocketHandshake::serverSetCookie() const
{
- return m_serverHandshakeResponse.httpHeaderFields().get("set-cookie");
-}
-
-String WebSocketHandshake::serverSetCookie2() const
-{
- return m_serverHandshakeResponse.httpHeaderFields().get("set-cookie2");
+ return m_serverHandshakeResponse.httpHeaderFields().get(HTTPHeaderName::SetCookie);
}
String WebSocketHandshake::serverUpgrade() const
{
- return m_serverHandshakeResponse.httpHeaderFields().get("upgrade");
+ return m_serverHandshakeResponse.httpHeaderFields().get(HTTPHeaderName::Upgrade);
}
String WebSocketHandshake::serverConnection() const
{
- return m_serverHandshakeResponse.httpHeaderFields().get("connection");
+ return m_serverHandshakeResponse.httpHeaderFields().get(HTTPHeaderName::Connection);
}
String WebSocketHandshake::serverWebSocketAccept() const
{
- return m_serverHandshakeResponse.httpHeaderFields().get("sec-websocket-accept");
+ return m_serverHandshakeResponse.httpHeaderFields().get(HTTPHeaderName::SecWebSocketAccept);
}
String WebSocketHandshake::acceptedExtensions() const
@@ -378,19 +371,56 @@ const ResourceResponse& WebSocketHandshake::serverHandshakeResponse() const
return m_serverHandshakeResponse;
}
-void WebSocketHandshake::addExtensionProcessor(PassOwnPtr<WebSocketExtensionProcessor> processor)
+void WebSocketHandshake::addExtensionProcessor(std::unique_ptr<WebSocketExtensionProcessor> processor)
{
- m_extensionDispatcher.addProcessor(processor);
+ m_extensionDispatcher.addProcessor(WTFMove(processor));
}
URL WebSocketHandshake::httpURLForAuthenticationAndCookies() const
{
- URL url = m_url.copy();
+ URL url = m_url.isolatedCopy();
bool couldSetProtocol = url.setProtocol(m_secure ? "https" : "http");
ASSERT_UNUSED(couldSetProtocol, couldSetProtocol);
return url;
}
+// https://tools.ietf.org/html/rfc6455#section-4.1
+// "The HTTP version MUST be at least 1.1."
+static inline bool headerHasValidHTTPVersion(StringView httpStatusLine)
+{
+ const char* httpVersionStaticPreambleLiteral = "HTTP/";
+ StringView httpVersionStaticPreamble(reinterpret_cast<const LChar*>(httpVersionStaticPreambleLiteral), strlen(httpVersionStaticPreambleLiteral));
+ if (!httpStatusLine.startsWith(httpVersionStaticPreamble))
+ return false;
+
+ // Check that there is a version number which should be at least three characters after "HTTP/"
+ unsigned preambleLength = httpVersionStaticPreamble.length();
+ if (httpStatusLine.length() < preambleLength + 3)
+ return false;
+
+ auto dotPosition = httpStatusLine.find('.', preambleLength);
+ if (dotPosition == notFound)
+ return false;
+
+ StringView majorVersionView = httpStatusLine.substring(preambleLength, dotPosition - preambleLength);
+ bool isValid;
+ int majorVersion = majorVersionView.toIntStrict(isValid);
+ if (!isValid)
+ return false;
+
+ unsigned minorVersionLength;
+ unsigned charactersLeftAfterDotPosition = httpStatusLine.length() - dotPosition;
+ for (minorVersionLength = 1; minorVersionLength < charactersLeftAfterDotPosition; minorVersionLength++) {
+ if (!isASCIIDigit(httpStatusLine[dotPosition + minorVersionLength]))
+ break;
+ }
+ int minorVersion = (httpStatusLine.substring(dotPosition + 1, minorVersionLength)).toIntStrict(isValid);
+ if (!isValid)
+ return false;
+
+ return (majorVersion >= 1 && minorVersion >= 1) || majorVersion >= 2;
+}
+
// Returns the header length (including "\r\n"), or -1 if we have not received enough data yet.
// If the line is malformed or the status code is not a 3-digit number,
// statusCode and statusText will be set to -1 and a null string, respectively.
@@ -403,8 +433,8 @@ int WebSocketHandshake::readStatusLine(const char* header, size_t headerLength,
statusCode = -1;
statusText = String();
- const char* space1 = 0;
- const char* space2 = 0;
+ const char* space1 = nullptr;
+ const char* space2 = nullptr;
const char* p;
size_t consumedLength;
@@ -418,7 +448,10 @@ int WebSocketHandshake::readStatusLine(const char* header, size_t headerLength,
// The caller isn't prepared to deal with null bytes in status
// line. WebSockets specification doesn't prohibit this, but HTTP
// does, so we'll just treat this as an error.
- m_failureReason = "Status line contains embedded null";
+ m_failureReason = ASCIILiteral("Status line contains embedded null");
+ return p + 1 - header;
+ } else if (!isASCII(*p)) {
+ m_failureReason = ASCIILiteral("Status line contains non-ASCII character");
return p + 1 - header;
} else if (*p == '\n')
break;
@@ -429,32 +462,38 @@ int WebSocketHandshake::readStatusLine(const char* header, size_t headerLength,
const char* end = p + 1;
int lineLength = end - header;
if (lineLength > maximumLength) {
- m_failureReason = "Status line is too long";
+ m_failureReason = ASCIILiteral("Status line is too long");
return maximumLength;
}
// The line must end with "\r\n".
if (lineLength < 2 || *(end - 2) != '\r') {
- m_failureReason = "Status line does not end with CRLF";
+ m_failureReason = ASCIILiteral("Status line does not end with CRLF");
return lineLength;
}
if (!space1 || !space2) {
- m_failureReason = "No response code found: " + trimInputSample(header, lineLength - 2);
+ m_failureReason = makeString("No response code found: ", trimInputSample(header, lineLength - 2));
+ return lineLength;
+ }
+
+ StringView httpStatusLine(reinterpret_cast<const LChar*>(header), space1 - header);
+ if (!headerHasValidHTTPVersion(httpStatusLine)) {
+ m_failureReason = makeString("Invalid HTTP version string: ", httpStatusLine);
return lineLength;
}
- String statusCodeString(space1 + 1, space2 - space1 - 1);
+ StringView statusCodeString(reinterpret_cast<const LChar*>(space1 + 1), space2 - space1 - 1);
if (statusCodeString.length() != 3) // Status code must consist of three digits.
return lineLength;
for (int i = 0; i < 3; ++i)
- if (statusCodeString[i] < '0' || statusCodeString[i] > '9') {
- m_failureReason = "Invalid status code: " + statusCodeString;
+ if (!isASCIIDigit(statusCodeString[i])) {
+ m_failureReason = makeString("Invalid status code: ", statusCodeString);
return lineLength;
}
bool ok = false;
- statusCode = statusCodeString.toInt(&ok);
+ statusCode = statusCodeString.toIntStrict(ok);
ASSERT(ok);
statusText = String(space2 + 1, end - space2 - 3); // Exclude "\r\n".
@@ -463,7 +502,7 @@ int WebSocketHandshake::readStatusLine(const char* header, size_t headerLength,
const char* WebSocketHandshake::readHTTPHeaders(const char* start, const char* end)
{
- AtomicString name;
+ StringView name;
String value;
bool sawSecWebSocketExtensionsHeaderField = false;
bool sawSecWebSocketAcceptHeaderField = false;
@@ -472,39 +511,57 @@ const char* WebSocketHandshake::readHTTPHeaders(const char* start, const char* e
for (; p < end; p++) {
size_t consumedLength = parseHTTPHeader(p, end - p, m_failureReason, name, value);
if (!consumedLength)
- return 0;
+ return nullptr;
p += consumedLength;
// Stop once we consumed an empty line.
if (name.isEmpty())
break;
- if (equalIgnoringCase("sec-websocket-extensions", name)) {
+ HTTPHeaderName headerName;
+ if (!findHTTPHeaderName(name, headerName)) {
+ // Evidence in the wild shows that services make use of custom headers in the handshake
+ m_serverHandshakeResponse.addHTTPHeaderField(name.toString(), value);
+ continue;
+ }
+
+ // https://tools.ietf.org/html/rfc7230#section-3.2.4
+ // "Newly defined header fields SHOULD limit their field values to US-ASCII octets."
+ if ((headerName == HTTPHeaderName::SecWebSocketExtensions
+ || headerName == HTTPHeaderName::SecWebSocketAccept
+ || headerName == HTTPHeaderName::SecWebSocketProtocol)
+ && !value.containsOnlyASCII()) {
+ m_failureReason = makeString(name, " header value should only contain ASCII characters");
+ return nullptr;
+ }
+
+ if (headerName == HTTPHeaderName::SecWebSocketExtensions) {
if (sawSecWebSocketExtensionsHeaderField) {
- m_failureReason = "The Sec-WebSocket-Extensions header MUST NOT appear more than once in an HTTP response";
- return 0;
+ m_failureReason = ASCIILiteral("The Sec-WebSocket-Extensions header must not appear more than once in an HTTP response");
+ return nullptr;
}
if (!m_extensionDispatcher.processHeaderValue(value)) {
m_failureReason = m_extensionDispatcher.failureReason();
- return 0;
+ return nullptr;
}
sawSecWebSocketExtensionsHeaderField = true;
- } else if (equalIgnoringCase("Sec-WebSocket-Accept", name)) {
- if (sawSecWebSocketAcceptHeaderField) {
- m_failureReason = "The Sec-WebSocket-Accept header MUST NOT appear more than once in an HTTP response";
- return 0;
+ } else {
+ if (headerName == HTTPHeaderName::SecWebSocketAccept) {
+ if (sawSecWebSocketAcceptHeaderField) {
+ m_failureReason = ASCIILiteral("The Sec-WebSocket-Accept header must not appear more than once in an HTTP response");
+ return nullptr;
+ }
+ sawSecWebSocketAcceptHeaderField = true;
+ } else if (headerName == HTTPHeaderName::SecWebSocketProtocol) {
+ if (sawSecWebSocketProtocolHeaderField) {
+ m_failureReason = ASCIILiteral("The Sec-WebSocket-Protocol header must not appear more than once in an HTTP response");
+ return nullptr;
+ }
+ sawSecWebSocketProtocolHeaderField = true;
}
- m_serverHandshakeResponse.addHTTPHeaderField(name, value);
- sawSecWebSocketAcceptHeaderField = true;
- } else if (equalIgnoringCase("Sec-WebSocket-Protocol", name)) {
- if (sawSecWebSocketProtocolHeaderField) {
- m_failureReason = "The Sec-WebSocket-Protocol header MUST NOT appear more than once in an HTTP response";
- return 0;
- }
- m_serverHandshakeResponse.addHTTPHeaderField(name, value);
- sawSecWebSocketProtocolHeaderField = true;
- } else
- m_serverHandshakeResponse.addHTTPHeaderField(name, value);
+
+ m_serverHandshakeResponse.addHTTPHeaderField(headerName, value);
+ }
}
return p;
}
@@ -517,40 +574,40 @@ bool WebSocketHandshake::checkResponseHeaders()
const String& serverWebSocketAccept = this->serverWebSocketAccept();
if (serverUpgrade.isNull()) {
- m_failureReason = "Error during WebSocket handshake: 'Upgrade' header is missing";
+ m_failureReason = ASCIILiteral("Error during WebSocket handshake: 'Upgrade' header is missing");
return false;
}
if (serverConnection.isNull()) {
- m_failureReason = "Error during WebSocket handshake: 'Connection' header is missing";
+ m_failureReason = ASCIILiteral("Error during WebSocket handshake: 'Connection' header is missing");
return false;
}
if (serverWebSocketAccept.isNull()) {
- m_failureReason = "Error during WebSocket handshake: 'Sec-WebSocket-Accept' header is missing";
+ m_failureReason = ASCIILiteral("Error during WebSocket handshake: 'Sec-WebSocket-Accept' header is missing");
return false;
}
- if (!equalIgnoringCase(serverUpgrade, "websocket")) {
- m_failureReason = "Error during WebSocket handshake: 'Upgrade' header value is not 'WebSocket'";
+ if (!equalLettersIgnoringASCIICase(serverUpgrade, "websocket")) {
+ m_failureReason = ASCIILiteral("Error during WebSocket handshake: 'Upgrade' header value is not 'WebSocket'");
return false;
}
- if (!equalIgnoringCase(serverConnection, "upgrade")) {
- m_failureReason = "Error during WebSocket handshake: 'Connection' header value is not 'Upgrade'";
+ if (!equalLettersIgnoringASCIICase(serverConnection, "upgrade")) {
+ m_failureReason = ASCIILiteral("Error during WebSocket handshake: 'Connection' header value is not 'Upgrade'");
return false;
}
if (serverWebSocketAccept != m_expectedAccept) {
- m_failureReason = "Error during WebSocket handshake: Sec-WebSocket-Accept mismatch";
+ m_failureReason = ASCIILiteral("Error during WebSocket handshake: Sec-WebSocket-Accept mismatch");
return false;
}
if (!serverWebSocketProtocol.isNull()) {
if (m_clientProtocol.isEmpty()) {
- m_failureReason = "Error during WebSocket handshake: Sec-WebSocket-Protocol mismatch";
+ m_failureReason = ASCIILiteral("Error during WebSocket handshake: Sec-WebSocket-Protocol mismatch");
return false;
}
Vector<String> result;
- m_clientProtocol.split(String(WebSocket::subProtocolSeperator()), result);
+ m_clientProtocol.split(WebSocket::subprotocolSeparator(), result);
if (!result.contains(serverWebSocketProtocol)) {
- m_failureReason = "Error during WebSocket handshake: Sec-WebSocket-Protocol mismatch";
+ m_failureReason = ASCIILiteral("Error during WebSocket handshake: Sec-WebSocket-Protocol mismatch");
return false;
}
}
diff --git a/Source/WebCore/Modules/websockets/WebSocketHandshake.h b/Source/WebCore/Modules/websockets/WebSocketHandshake.h
index ccab04501..9f701c530 100644
--- a/Source/WebCore/Modules/websockets/WebSocketHandshake.h
+++ b/Source/WebCore/Modules/websockets/WebSocketHandshake.h
@@ -28,8 +28,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef WebSocketHandshake_h
-#define WebSocketHandshake_h
+#pragma once
#if ENABLE(WEB_SOCKETS)
@@ -37,13 +36,12 @@
#include "ResourceResponse.h"
#include "WebSocketExtensionDispatcher.h"
#include "WebSocketExtensionProcessor.h"
-#include <wtf/PassOwnPtr.h>
#include <wtf/text/WTFString.h>
namespace WebCore {
+class Document;
class ResourceRequest;
-class ScriptExecutionContext;
class WebSocketHandshake {
WTF_MAKE_NONCOPYABLE(WebSocketHandshake); WTF_MAKE_FAST_ALLOCATED;
@@ -51,7 +49,7 @@ public:
enum Mode {
Incomplete, Normal, Failed, Connected
};
- WebSocketHandshake(const URL&, const String& protocol, ScriptExecutionContext*);
+ WebSocketHandshake(const URL&, const String& protocol, Document*, bool allowCookies);
~WebSocketHandshake();
const URL& url() const;
@@ -70,7 +68,7 @@ public:
ResourceRequest clientHandshakeRequest() const;
void reset();
- void clearScriptExecutionContext();
+ void clearDocument();
int readServerHandshake(const char* header, size_t len);
Mode mode() const;
@@ -78,7 +76,6 @@ public:
String serverWebSocketProtocol() const;
String serverSetCookie() const;
- String serverSetCookie2() const;
String serverUpgrade() const;
String serverConnection() const;
String serverWebSocketAccept() const;
@@ -86,7 +83,7 @@ public:
const ResourceResponse& serverHandshakeResponse() const;
- void addExtensionProcessor(PassOwnPtr<WebSocketExtensionProcessor>);
+ void addExtensionProcessor(std::unique_ptr<WebSocketExtensionProcessor>);
static String getExpectedWebSocketAccept(const String& secWebSocketKey);
@@ -103,9 +100,10 @@ private:
URL m_url;
String m_clientProtocol;
bool m_secure;
- ScriptExecutionContext* m_context;
+ Document* m_document;
Mode m_mode;
+ bool m_allowCookies;
ResourceResponse m_serverHandshakeResponse;
@@ -120,5 +118,3 @@ private:
} // namespace WebCore
#endif // ENABLE(WEB_SOCKETS)
-
-#endif // WebSocketHandshake_h
diff --git a/Source/WebCore/Modules/websockets/WorkerThreadableWebSocketChannel.cpp b/Source/WebCore/Modules/websockets/WorkerThreadableWebSocketChannel.cpp
index 65b8d2bc1..f0d86b615 100644
--- a/Source/WebCore/Modules/websockets/WorkerThreadableWebSocketChannel.cpp
+++ b/Source/WebCore/Modules/websockets/WorkerThreadableWebSocketChannel.cpp
@@ -35,9 +35,9 @@
#include "WorkerThreadableWebSocketChannel.h"
#include "Blob.h"
-#include "CrossThreadTask.h"
#include "Document.h"
#include "ScriptExecutionContext.h"
+#include "SocketProvider.h"
#include "ThreadableWebSocketChannelClientWrapper.h"
#include "WebSocketChannel.h"
#include "WebSocketChannelClient.h"
@@ -47,15 +47,15 @@
#include "WorkerThread.h"
#include <runtime/ArrayBuffer.h>
#include <wtf/MainThread.h>
-#include <wtf/PassRefPtr.h>
#include <wtf/text/WTFString.h>
namespace WebCore {
-WorkerThreadableWebSocketChannel::WorkerThreadableWebSocketChannel(WorkerGlobalScope* context, WebSocketChannelClient* client, const String& taskMode)
+WorkerThreadableWebSocketChannel::WorkerThreadableWebSocketChannel(WorkerGlobalScope& context, WebSocketChannelClient& client, const String& taskMode, SocketProvider& provider)
: m_workerGlobalScope(context)
, m_workerClientWrapper(ThreadableWebSocketChannelClientWrapper::create(context, client))
- , m_bridge(Bridge::create(m_workerClientWrapper, m_workerGlobalScope, taskMode))
+ , m_bridge(Bridge::create(m_workerClientWrapper.copyRef(), m_workerGlobalScope.copyRef(), taskMode, provider))
+ , m_socketProvider(provider)
{
m_bridge->initialize();
}
@@ -74,13 +74,11 @@ void WorkerThreadableWebSocketChannel::connect(const URL& url, const String& pro
String WorkerThreadableWebSocketChannel::subprotocol()
{
- ASSERT(m_workerClientWrapper);
return m_workerClientWrapper->subprotocol();
}
String WorkerThreadableWebSocketChannel::extensions()
{
- ASSERT(m_workerClientWrapper);
return m_workerClientWrapper->extensions();
}
@@ -98,14 +96,14 @@ ThreadableWebSocketChannel::SendResult WorkerThreadableWebSocketChannel::send(co
return m_bridge->send(binaryData, byteOffset, byteLength);
}
-ThreadableWebSocketChannel::SendResult WorkerThreadableWebSocketChannel::send(const Blob& binaryData)
+ThreadableWebSocketChannel::SendResult WorkerThreadableWebSocketChannel::send(Blob& binaryData)
{
if (!m_bridge)
return ThreadableWebSocketChannel::SendFail;
return m_bridge->send(binaryData);
}
-unsigned long WorkerThreadableWebSocketChannel::bufferedAmount() const
+unsigned WorkerThreadableWebSocketChannel::bufferedAmount() const
{
if (!m_bridge)
return 0;
@@ -127,7 +125,7 @@ void WorkerThreadableWebSocketChannel::fail(const String& reason)
void WorkerThreadableWebSocketChannel::disconnect()
{
m_bridge->disconnect();
- m_bridge.clear();
+ m_bridge = nullptr;
}
void WorkerThreadableWebSocketChannel::suspend()
@@ -144,10 +142,10 @@ void WorkerThreadableWebSocketChannel::resume()
m_bridge->resume();
}
-WorkerThreadableWebSocketChannel::Peer::Peer(PassRefPtr<ThreadableWebSocketChannelClientWrapper> clientWrapper, WorkerLoaderProxy& loaderProxy, ScriptExecutionContext* context, const String& taskMode)
- : m_workerClientWrapper(clientWrapper)
+WorkerThreadableWebSocketChannel::Peer::Peer(Ref<ThreadableWebSocketChannelClientWrapper>&& clientWrapper, WorkerLoaderProxy& loaderProxy, ScriptExecutionContext& context, const String& taskMode, SocketProvider& provider)
+ : m_workerClientWrapper(WTFMove(clientWrapper))
, m_loaderProxy(loaderProxy)
- , m_mainWebSocketChannel(WebSocketChannel::create(toDocument(context), this))
+ , m_mainWebSocketChannel(WebSocketChannel::create(downcast<Document>(context), *this, provider))
, m_taskMode(taskMode)
{
ASSERT(isMainThread());
@@ -168,52 +166,53 @@ void WorkerThreadableWebSocketChannel::Peer::connect(const URL& url, const Strin
m_mainWebSocketChannel->connect(url, protocol);
}
-static void workerGlobalScopeDidSend(ScriptExecutionContext* context, PassRefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper, ThreadableWebSocketChannel::SendResult sendRequestResult)
-{
- ASSERT_UNUSED(context, context->isWorkerGlobalScope());
- workerClientWrapper->setSendRequestResult(sendRequestResult);
-}
-
void WorkerThreadableWebSocketChannel::Peer::send(const String& message)
{
ASSERT(isMainThread());
- if (!m_mainWebSocketChannel || !m_workerClientWrapper)
+ if (!m_mainWebSocketChannel)
return;
+
ThreadableWebSocketChannel::SendResult sendRequestResult = m_mainWebSocketChannel->send(message);
- m_loaderProxy.postTaskForModeToWorkerGlobalScope(createCallbackTask(&workerGlobalScopeDidSend, m_workerClientWrapper, sendRequestResult), m_taskMode);
+ m_loaderProxy.postTaskForModeToWorkerGlobalScope([workerClientWrapper = m_workerClientWrapper.copyRef(), sendRequestResult](ScriptExecutionContext&) mutable {
+ workerClientWrapper->setSendRequestResult(sendRequestResult);
+ }, m_taskMode);
}
void WorkerThreadableWebSocketChannel::Peer::send(const ArrayBuffer& binaryData)
{
ASSERT(isMainThread());
- if (!m_mainWebSocketChannel || !m_workerClientWrapper)
+ if (!m_mainWebSocketChannel)
return;
+
ThreadableWebSocketChannel::SendResult sendRequestResult = m_mainWebSocketChannel->send(binaryData, 0, binaryData.byteLength());
- m_loaderProxy.postTaskForModeToWorkerGlobalScope(createCallbackTask(&workerGlobalScopeDidSend, m_workerClientWrapper, sendRequestResult), m_taskMode);
+ m_loaderProxy.postTaskForModeToWorkerGlobalScope([workerClientWrapper = m_workerClientWrapper.copyRef(), sendRequestResult](ScriptExecutionContext&) mutable {
+ workerClientWrapper->setSendRequestResult(sendRequestResult);
+ }, m_taskMode);
}
-void WorkerThreadableWebSocketChannel::Peer::send(const Blob& binaryData)
+void WorkerThreadableWebSocketChannel::Peer::send(Blob& binaryData)
{
ASSERT(isMainThread());
- if (!m_mainWebSocketChannel || !m_workerClientWrapper)
+ if (!m_mainWebSocketChannel)
return;
- ThreadableWebSocketChannel::SendResult sendRequestResult = m_mainWebSocketChannel->send(binaryData);
- m_loaderProxy.postTaskForModeToWorkerGlobalScope(createCallbackTask(&workerGlobalScopeDidSend, m_workerClientWrapper, sendRequestResult), m_taskMode);
-}
-static void workerGlobalScopeDidGetBufferedAmount(ScriptExecutionContext* context, PassRefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper, unsigned long bufferedAmount)
-{
- ASSERT_UNUSED(context, context->isWorkerGlobalScope());
- workerClientWrapper->setBufferedAmount(bufferedAmount);
+ ThreadableWebSocketChannel::SendResult sendRequestResult = m_mainWebSocketChannel->send(binaryData);
+ m_loaderProxy.postTaskForModeToWorkerGlobalScope([workerClientWrapper = m_workerClientWrapper.copyRef(), sendRequestResult](ScriptExecutionContext&) mutable {
+ workerClientWrapper->setSendRequestResult(sendRequestResult);
+ }, m_taskMode);
}
void WorkerThreadableWebSocketChannel::Peer::bufferedAmount()
{
ASSERT(isMainThread());
- if (!m_mainWebSocketChannel || !m_workerClientWrapper)
+ if (!m_mainWebSocketChannel)
return;
- unsigned long bufferedAmount = m_mainWebSocketChannel->bufferedAmount();
- m_loaderProxy.postTaskForModeToWorkerGlobalScope(createCallbackTask(&workerGlobalScopeDidGetBufferedAmount, m_workerClientWrapper, bufferedAmount), m_taskMode);
+
+ unsigned bufferedAmount = m_mainWebSocketChannel->bufferedAmount();
+ m_loaderProxy.postTaskForModeToWorkerGlobalScope([workerClientWrapper = m_workerClientWrapper.copyRef(), bufferedAmount](ScriptExecutionContext& context) mutable {
+ ASSERT_UNUSED(context, context.isWorkerGlobalScope());
+ workerClientWrapper->setBufferedAmount(bufferedAmount);
+ }, m_taskMode);
}
void WorkerThreadableWebSocketChannel::Peer::close(int code, const String& reason)
@@ -238,7 +237,7 @@ void WorkerThreadableWebSocketChannel::Peer::disconnect()
if (!m_mainWebSocketChannel)
return;
m_mainWebSocketChannel->disconnect();
- m_mainWebSocketChannel = 0;
+ m_mainWebSocketChannel = nullptr;
}
void WorkerThreadableWebSocketChannel::Peer::suspend()
@@ -257,101 +256,98 @@ void WorkerThreadableWebSocketChannel::Peer::resume()
m_mainWebSocketChannel->resume();
}
-static void workerGlobalScopeDidConnect(ScriptExecutionContext* context, PassRefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper, const String& subprotocol, const String& extensions)
-{
- ASSERT_UNUSED(context, context->isWorkerGlobalScope());
- workerClientWrapper->setSubprotocol(subprotocol);
- workerClientWrapper->setExtensions(extensions);
- workerClientWrapper->didConnect();
-}
-
void WorkerThreadableWebSocketChannel::Peer::didConnect()
{
ASSERT(isMainThread());
- m_loaderProxy.postTaskForModeToWorkerGlobalScope(createCallbackTask(&workerGlobalScopeDidConnect, m_workerClientWrapper, m_mainWebSocketChannel->subprotocol(), m_mainWebSocketChannel->extensions()), m_taskMode);
-}
-static void workerGlobalScopeDidReceiveMessage(ScriptExecutionContext* context, PassRefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper, const String& message)
-{
- ASSERT_UNUSED(context, context->isWorkerGlobalScope());
- workerClientWrapper->didReceiveMessage(message);
+ String subprotocol = m_mainWebSocketChannel->subprotocol();
+ String extensions = m_mainWebSocketChannel->extensions();
+ m_loaderProxy.postTaskForModeToWorkerGlobalScope([workerClientWrapper = m_workerClientWrapper.copyRef(), subprotocol = subprotocol.isolatedCopy(), extensions = extensions.isolatedCopy()](ScriptExecutionContext& context) mutable {
+ ASSERT_UNUSED(context, context.isWorkerGlobalScope());
+ workerClientWrapper->setSubprotocol(subprotocol);
+ workerClientWrapper->setExtensions(extensions);
+ workerClientWrapper->didConnect();
+ }, m_taskMode);
}
void WorkerThreadableWebSocketChannel::Peer::didReceiveMessage(const String& message)
{
ASSERT(isMainThread());
- m_loaderProxy.postTaskForModeToWorkerGlobalScope(createCallbackTask(&workerGlobalScopeDidReceiveMessage, m_workerClientWrapper, message), m_taskMode);
-}
-static void workerGlobalScopeDidReceiveBinaryData(ScriptExecutionContext* context, PassRefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper, PassOwnPtr<Vector<char>> binaryData)
-{
- ASSERT_UNUSED(context, context->isWorkerGlobalScope());
- workerClientWrapper->didReceiveBinaryData(binaryData);
+ m_loaderProxy.postTaskForModeToWorkerGlobalScope([workerClientWrapper = m_workerClientWrapper.copyRef(), message = message.isolatedCopy()](ScriptExecutionContext& context) mutable {
+ ASSERT_UNUSED(context, context.isWorkerGlobalScope());
+ workerClientWrapper->didReceiveMessage(message);
+ }, m_taskMode);
}
-void WorkerThreadableWebSocketChannel::Peer::didReceiveBinaryData(PassOwnPtr<Vector<char>> binaryData)
+void WorkerThreadableWebSocketChannel::Peer::didReceiveBinaryData(Vector<uint8_t>&& binaryData)
{
ASSERT(isMainThread());
- m_loaderProxy.postTaskForModeToWorkerGlobalScope(createCallbackTask(&workerGlobalScopeDidReceiveBinaryData, m_workerClientWrapper, binaryData), m_taskMode);
-}
-static void workerGlobalScopeDidUpdateBufferedAmount(ScriptExecutionContext* context, PassRefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper, unsigned long bufferedAmount)
-{
- ASSERT_UNUSED(context, context->isWorkerGlobalScope());
- workerClientWrapper->didUpdateBufferedAmount(bufferedAmount);
+ m_loaderProxy.postTaskForModeToWorkerGlobalScope([workerClientWrapper = m_workerClientWrapper.copyRef(), binaryData = WTFMove(binaryData)](ScriptExecutionContext& context) mutable {
+ ASSERT_UNUSED(context, context.isWorkerGlobalScope());
+ workerClientWrapper->didReceiveBinaryData(WTFMove(binaryData));
+ }, m_taskMode);
}
-void WorkerThreadableWebSocketChannel::Peer::didUpdateBufferedAmount(unsigned long bufferedAmount)
+void WorkerThreadableWebSocketChannel::Peer::didUpdateBufferedAmount(unsigned bufferedAmount)
{
ASSERT(isMainThread());
- m_loaderProxy.postTaskForModeToWorkerGlobalScope(createCallbackTask(&workerGlobalScopeDidUpdateBufferedAmount, m_workerClientWrapper, bufferedAmount), m_taskMode);
-}
-static void workerGlobalScopeDidStartClosingHandshake(ScriptExecutionContext* context, PassRefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper)
-{
- ASSERT_UNUSED(context, context->isWorkerGlobalScope());
- workerClientWrapper->didStartClosingHandshake();
+ m_loaderProxy.postTaskForModeToWorkerGlobalScope([workerClientWrapper = m_workerClientWrapper.copyRef(), bufferedAmount](ScriptExecutionContext& context) mutable {
+ ASSERT_UNUSED(context, context.isWorkerGlobalScope());
+ workerClientWrapper->didUpdateBufferedAmount(bufferedAmount);
+ }, m_taskMode);
}
void WorkerThreadableWebSocketChannel::Peer::didStartClosingHandshake()
{
ASSERT(isMainThread());
- m_loaderProxy.postTaskForModeToWorkerGlobalScope(createCallbackTask(&workerGlobalScopeDidStartClosingHandshake, m_workerClientWrapper), m_taskMode);
-}
-static void workerGlobalScopeDidClose(ScriptExecutionContext* context, PassRefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper, unsigned long unhandledBufferedAmount, WebSocketChannelClient::ClosingHandshakeCompletionStatus closingHandshakeCompletion, unsigned short code, const String& reason)
-{
- ASSERT_UNUSED(context, context->isWorkerGlobalScope());
- workerClientWrapper->didClose(unhandledBufferedAmount, closingHandshakeCompletion, code, reason);
+ m_loaderProxy.postTaskForModeToWorkerGlobalScope([workerClientWrapper = m_workerClientWrapper.copyRef()](ScriptExecutionContext& context) mutable {
+ ASSERT_UNUSED(context, context.isWorkerGlobalScope());
+ workerClientWrapper->didStartClosingHandshake();
+ }, m_taskMode);
}
-void WorkerThreadableWebSocketChannel::Peer::didClose(unsigned long unhandledBufferedAmount, ClosingHandshakeCompletionStatus closingHandshakeCompletion, unsigned short code, const String& reason)
+void WorkerThreadableWebSocketChannel::Peer::didClose(unsigned unhandledBufferedAmount, ClosingHandshakeCompletionStatus closingHandshakeCompletion, unsigned short code, const String& reason)
{
ASSERT(isMainThread());
- m_mainWebSocketChannel = 0;
- m_loaderProxy.postTaskForModeToWorkerGlobalScope(createCallbackTask(&workerGlobalScopeDidClose, m_workerClientWrapper, unhandledBufferedAmount, closingHandshakeCompletion, code, reason), m_taskMode);
+ m_mainWebSocketChannel = nullptr;
+
+ m_loaderProxy.postTaskForModeToWorkerGlobalScope([workerClientWrapper = m_workerClientWrapper.copyRef(), unhandledBufferedAmount, closingHandshakeCompletion, code, reason = reason.isolatedCopy()](ScriptExecutionContext& context) mutable {
+ ASSERT_UNUSED(context, context.isWorkerGlobalScope());
+ workerClientWrapper->didClose(unhandledBufferedAmount, closingHandshakeCompletion, code, reason);
+ }, m_taskMode);
}
-static void workerGlobalScopeDidReceiveMessageError(ScriptExecutionContext* context, PassRefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper)
+void WorkerThreadableWebSocketChannel::Peer::didReceiveMessageError()
{
- ASSERT_UNUSED(context, context->isWorkerGlobalScope());
- workerClientWrapper->didReceiveMessageError();
+ ASSERT(isMainThread());
+
+ m_loaderProxy.postTaskForModeToWorkerGlobalScope([workerClientWrapper = m_workerClientWrapper.copyRef()](ScriptExecutionContext& context) mutable {
+ ASSERT_UNUSED(context, context.isWorkerGlobalScope());
+ workerClientWrapper->didReceiveMessageError();
+ }, m_taskMode);
}
-void WorkerThreadableWebSocketChannel::Peer::didReceiveMessageError()
+void WorkerThreadableWebSocketChannel::Peer::didUpgradeURL()
{
ASSERT(isMainThread());
- m_loaderProxy.postTaskForModeToWorkerGlobalScope(createCallbackTask(&workerGlobalScopeDidReceiveMessageError, m_workerClientWrapper), m_taskMode);
+
+ m_loaderProxy.postTaskForModeToWorkerGlobalScope([workerClientWrapper = m_workerClientWrapper.copyRef()](ScriptExecutionContext& context) mutable {
+ ASSERT_UNUSED(context, context.isWorkerGlobalScope());
+ workerClientWrapper->didUpgradeURL();
+ }, m_taskMode);
}
-WorkerThreadableWebSocketChannel::Bridge::Bridge(PassRefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper, PassRefPtr<WorkerGlobalScope> workerGlobalScope, const String& taskMode)
- : m_workerClientWrapper(workerClientWrapper)
- , m_workerGlobalScope(workerGlobalScope)
- , m_loaderProxy(m_workerGlobalScope->thread()->workerLoaderProxy())
+WorkerThreadableWebSocketChannel::Bridge::Bridge(Ref<ThreadableWebSocketChannelClientWrapper>&& workerClientWrapper, Ref<WorkerGlobalScope>&& workerGlobalScope, const String& taskMode, Ref<SocketProvider>&& socketProvider)
+ : m_workerClientWrapper(WTFMove(workerClientWrapper))
+ , m_workerGlobalScope(WTFMove(workerGlobalScope))
+ , m_loaderProxy(m_workerGlobalScope->thread().workerLoaderProxy())
, m_taskMode(taskMode)
- , m_peer(0)
+ , m_socketProvider(WTFMove(socketProvider))
{
- ASSERT(m_workerClientWrapper.get());
}
WorkerThreadableWebSocketChannel::Bridge::~Bridge()
@@ -359,273 +355,210 @@ WorkerThreadableWebSocketChannel::Bridge::~Bridge()
disconnect();
}
-class WorkerThreadableWebSocketChannel::WorkerGlobalScopeDidInitializeTask : public ScriptExecutionContext::Task {
-public:
- static PassOwnPtr<ScriptExecutionContext::Task> create(WorkerThreadableWebSocketChannel::Peer* peer,
- WorkerLoaderProxy* loaderProxy,
- PassRefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper)
- {
- return adoptPtr(new WorkerGlobalScopeDidInitializeTask(peer, loaderProxy, workerClientWrapper));
- }
-
- virtual ~WorkerGlobalScopeDidInitializeTask() { }
- virtual void performTask(ScriptExecutionContext* context) override
- {
- ASSERT_UNUSED(context, context->isWorkerGlobalScope());
- if (m_workerClientWrapper->failedWebSocketChannelCreation()) {
- // If Bridge::initialize() quitted earlier, we need to kick mainThreadDestroy() to delete the peer.
- OwnPtr<WorkerThreadableWebSocketChannel::Peer> peer = adoptPtr(m_peer);
- m_peer = 0;
- m_loaderProxy->postTaskToLoader(createCallbackTask(&WorkerThreadableWebSocketChannel::mainThreadDestroy, peer.release()));
- } else
- m_workerClientWrapper->didCreateWebSocketChannel(m_peer);
- }
- virtual bool isCleanupTask() const override { return true; }
-
-private:
- WorkerGlobalScopeDidInitializeTask(WorkerThreadableWebSocketChannel::Peer* peer,
- WorkerLoaderProxy* loaderProxy,
- PassRefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper)
- : m_peer(peer)
- , m_loaderProxy(loaderProxy)
- , m_workerClientWrapper(workerClientWrapper)
- {
- }
-
- WorkerThreadableWebSocketChannel::Peer* m_peer;
- WorkerLoaderProxy* m_loaderProxy;
- RefPtr<ThreadableWebSocketChannelClientWrapper> m_workerClientWrapper;
-};
-
-void WorkerThreadableWebSocketChannel::Bridge::mainThreadInitialize(ScriptExecutionContext* context, WorkerLoaderProxy* loaderProxy, PassRefPtr<ThreadableWebSocketChannelClientWrapper> prpClientWrapper, const String& taskMode)
+void WorkerThreadableWebSocketChannel::Bridge::mainThreadInitialize(ScriptExecutionContext& context, WorkerLoaderProxy& loaderProxy, Ref<ThreadableWebSocketChannelClientWrapper>&& clientWrapper, const String& taskMode, Ref<SocketProvider>&& provider)
{
ASSERT(isMainThread());
- ASSERT_UNUSED(context, context->isDocument());
-
- RefPtr<ThreadableWebSocketChannelClientWrapper> clientWrapper = prpClientWrapper;
-
- Peer* peer = Peer::create(clientWrapper, *loaderProxy, context, taskMode);
- bool sent = loaderProxy->postTaskForModeToWorkerGlobalScope(
- WorkerThreadableWebSocketChannel::WorkerGlobalScopeDidInitializeTask::create(peer, loaderProxy, clientWrapper), taskMode);
- if (!sent) {
+ ASSERT(context.isDocument());
+
+ bool sent = loaderProxy.postTaskForModeToWorkerGlobalScope({
+ ScriptExecutionContext::Task::CleanupTask,
+ [clientWrapper = clientWrapper.copyRef(), &loaderProxy, peer = std::make_unique<Peer>(clientWrapper.copyRef(), loaderProxy, context, taskMode, WTFMove(provider))](ScriptExecutionContext& context) mutable {
+ ASSERT_UNUSED(context, context.isWorkerGlobalScope());
+ if (clientWrapper->failedWebSocketChannelCreation()) {
+ // If Bridge::initialize() quitted earlier, we need to kick mainThreadDestroy() to delete the peer.
+ loaderProxy.postTaskToLoader([peer = WTFMove(peer)](ScriptExecutionContext& context) {
+ ASSERT(isMainThread());
+ ASSERT_UNUSED(context, context.isDocument());
+ });
+ } else
+ clientWrapper->didCreateWebSocketChannel(peer.release());
+ }
+ }, taskMode);
+
+ if (!sent)
clientWrapper->clearPeer();
- delete peer;
- }
}
void WorkerThreadableWebSocketChannel::Bridge::initialize()
{
ASSERT(!m_peer);
setMethodNotCompleted();
- Ref<Bridge> protect(*this);
- m_loaderProxy.postTaskToLoader(
- createCallbackTask(&Bridge::mainThreadInitialize,
- AllowCrossThreadAccess(&m_loaderProxy), m_workerClientWrapper, m_taskMode));
+ Ref<Bridge> protectedThis(*this);
+
+ m_loaderProxy.postTaskToLoader([&loaderProxy = m_loaderProxy, workerClientWrapper = m_workerClientWrapper.copyRef(), taskMode = m_taskMode.isolatedCopy(), provider = m_socketProvider.copyRef()](ScriptExecutionContext& context) mutable {
+ mainThreadInitialize(context, loaderProxy, WTFMove(workerClientWrapper), taskMode, WTFMove(provider));
+ });
waitForMethodCompletion();
+
// m_peer may be null when the nested runloop exited before a peer has created.
m_peer = m_workerClientWrapper->peer();
if (!m_peer)
m_workerClientWrapper->setFailedWebSocketChannelCreation();
}
-void WorkerThreadableWebSocketChannel::mainThreadConnect(ScriptExecutionContext* context, Peer* peer, const URL& url, const String& protocol)
-{
- ASSERT(isMainThread());
- ASSERT_UNUSED(context, context->isDocument());
- ASSERT(peer);
-
- peer->connect(url, protocol);
-}
-
void WorkerThreadableWebSocketChannel::Bridge::connect(const URL& url, const String& protocol)
{
- ASSERT(m_workerClientWrapper);
if (!m_peer)
return;
- m_loaderProxy.postTaskToLoader(createCallbackTask(&WorkerThreadableWebSocketChannel::mainThreadConnect, AllowCrossThreadAccess(m_peer), url, protocol));
-}
-void WorkerThreadableWebSocketChannel::mainThreadSend(ScriptExecutionContext* context, Peer* peer, const String& message)
-{
- ASSERT(isMainThread());
- ASSERT_UNUSED(context, context->isDocument());
- ASSERT(peer);
+ m_loaderProxy.postTaskToLoader([peer = m_peer, url = url.isolatedCopy(), protocol = protocol.isolatedCopy()](ScriptExecutionContext& context) {
+ ASSERT(isMainThread());
+ ASSERT_UNUSED(context, context.isDocument());
+ ASSERT(peer);
- peer->send(message);
-}
-
-void WorkerThreadableWebSocketChannel::mainThreadSendArrayBuffer(ScriptExecutionContext* context, Peer* peer, PassOwnPtr<Vector<char>> data)
-{
- ASSERT(isMainThread());
- ASSERT_UNUSED(context, context->isDocument());
- ASSERT(peer);
-
- RefPtr<ArrayBuffer> arrayBuffer = ArrayBuffer::create(data->data(), data->size());
- peer->send(*arrayBuffer);
-}
-
-void WorkerThreadableWebSocketChannel::mainThreadSendBlob(ScriptExecutionContext* context, Peer* peer, const URL& url, const String& type, long long size)
-{
- ASSERT(isMainThread());
- ASSERT_UNUSED(context, context->isDocument());
- ASSERT(peer);
-
- RefPtr<Blob> blob = Blob::create(url, type, size);
- peer->send(*blob);
+ peer->connect(url, protocol);
+ });
}
ThreadableWebSocketChannel::SendResult WorkerThreadableWebSocketChannel::Bridge::send(const String& message)
{
- if (!m_workerClientWrapper || !m_peer)
+ if (!m_peer)
return ThreadableWebSocketChannel::SendFail;
setMethodNotCompleted();
- m_loaderProxy.postTaskToLoader(createCallbackTask(&WorkerThreadableWebSocketChannel::mainThreadSend, AllowCrossThreadAccess(m_peer), message));
- Ref<Bridge> protect(*this);
+
+ m_loaderProxy.postTaskToLoader([peer = m_peer, message = message.isolatedCopy()](ScriptExecutionContext& context) {
+ ASSERT(isMainThread());
+ ASSERT_UNUSED(context, context.isDocument());
+ ASSERT(peer);
+
+ peer->send(message);
+ });
+
+ Ref<Bridge> protectedThis(*this);
waitForMethodCompletion();
- ThreadableWebSocketChannelClientWrapper* clientWrapper = m_workerClientWrapper.get();
- if (!clientWrapper)
- return ThreadableWebSocketChannel::SendFail;
- return clientWrapper->sendRequestResult();
+ return m_workerClientWrapper->sendRequestResult();
}
ThreadableWebSocketChannel::SendResult WorkerThreadableWebSocketChannel::Bridge::send(const ArrayBuffer& binaryData, unsigned byteOffset, unsigned byteLength)
{
- if (!m_workerClientWrapper || !m_peer)
+ if (!m_peer)
return ThreadableWebSocketChannel::SendFail;
+
// ArrayBuffer isn't thread-safe, hence the content of ArrayBuffer is copied into Vector<char>.
- OwnPtr<Vector<char>> data = adoptPtr(new Vector<char>(byteLength));
+ Vector<char> data(byteLength);
if (binaryData.byteLength())
- memcpy(data->data(), static_cast<const char*>(binaryData.data()) + byteOffset, byteLength);
+ memcpy(data.data(), static_cast<const char*>(binaryData.data()) + byteOffset, byteLength);
setMethodNotCompleted();
- m_loaderProxy.postTaskToLoader(createCallbackTask(&WorkerThreadableWebSocketChannel::mainThreadSendArrayBuffer, AllowCrossThreadAccess(m_peer), data.release()));
- Ref<Bridge> protect(*this);
+
+ m_loaderProxy.postTaskToLoader([peer = m_peer, data = WTFMove(data)](ScriptExecutionContext& context) {
+ ASSERT(isMainThread());
+ ASSERT_UNUSED(context, context.isDocument());
+ ASSERT(peer);
+
+ auto arrayBuffer = ArrayBuffer::create(data.data(), data.size());
+ peer->send(arrayBuffer);
+ });
+
+ Ref<Bridge> protectedThis(*this);
waitForMethodCompletion();
- ThreadableWebSocketChannelClientWrapper* clientWrapper = m_workerClientWrapper.get();
- if (!clientWrapper)
- return ThreadableWebSocketChannel::SendFail;
- return clientWrapper->sendRequestResult();
+ return m_workerClientWrapper->sendRequestResult();
}
-ThreadableWebSocketChannel::SendResult WorkerThreadableWebSocketChannel::Bridge::send(const Blob& binaryData)
+ThreadableWebSocketChannel::SendResult WorkerThreadableWebSocketChannel::Bridge::send(Blob& binaryData)
{
- if (!m_workerClientWrapper || !m_peer)
+ if (!m_peer)
return ThreadableWebSocketChannel::SendFail;
setMethodNotCompleted();
- m_loaderProxy.postTaskToLoader(createCallbackTask(&WorkerThreadableWebSocketChannel::mainThreadSendBlob, AllowCrossThreadAccess(m_peer), binaryData.url(), binaryData.type(), binaryData.size()));
- Ref<Bridge> protect(*this);
- waitForMethodCompletion();
- ThreadableWebSocketChannelClientWrapper* clientWrapper = m_workerClientWrapper.get();
- if (!clientWrapper)
- return ThreadableWebSocketChannel::SendFail;
- return clientWrapper->sendRequestResult();
-}
-void WorkerThreadableWebSocketChannel::mainThreadBufferedAmount(ScriptExecutionContext* context, Peer* peer)
-{
- ASSERT(isMainThread());
- ASSERT_UNUSED(context, context->isDocument());
- ASSERT(peer);
+ m_loaderProxy.postTaskToLoader([peer = m_peer, url = binaryData.url().isolatedCopy(), type = binaryData.type().isolatedCopy(), size = binaryData.size()](ScriptExecutionContext& context) {
+ ASSERT(isMainThread());
+ ASSERT_UNUSED(context, context.isDocument());
+ ASSERT(peer);
- peer->bufferedAmount();
+ peer->send(Blob::deserialize(url, type, size, { }));
+ });
+
+ Ref<Bridge> protectedThis(*this);
+ waitForMethodCompletion();
+ return m_workerClientWrapper->sendRequestResult();
}
-unsigned long WorkerThreadableWebSocketChannel::Bridge::bufferedAmount()
+unsigned WorkerThreadableWebSocketChannel::Bridge::bufferedAmount()
{
- if (!m_workerClientWrapper || !m_peer)
+ if (!m_peer)
return 0;
setMethodNotCompleted();
- m_loaderProxy.postTaskToLoader(createCallbackTask(&WorkerThreadableWebSocketChannel::mainThreadBufferedAmount, AllowCrossThreadAccess(m_peer)));
- Ref<Bridge> protect(*this);
- waitForMethodCompletion();
- ThreadableWebSocketChannelClientWrapper* clientWrapper = m_workerClientWrapper.get();
- if (clientWrapper)
- return clientWrapper->bufferedAmount();
- return 0;
-}
-void WorkerThreadableWebSocketChannel::mainThreadClose(ScriptExecutionContext* context, Peer* peer, int code, const String& reason)
-{
- ASSERT(isMainThread());
- ASSERT_UNUSED(context, context->isDocument());
- ASSERT(peer);
+ m_loaderProxy.postTaskToLoader([peer = m_peer](ScriptExecutionContext& context) {
+ ASSERT(isMainThread());
+ ASSERT_UNUSED(context, context.isDocument());
+ ASSERT(peer);
- peer->close(code, reason);
+ peer->bufferedAmount();
+ });
+
+ Ref<Bridge> protectedThis(*this);
+ waitForMethodCompletion();
+ return m_workerClientWrapper->bufferedAmount();
}
void WorkerThreadableWebSocketChannel::Bridge::close(int code, const String& reason)
{
if (!m_peer)
return;
- m_loaderProxy.postTaskToLoader(createCallbackTask(&WorkerThreadableWebSocketChannel::mainThreadClose, AllowCrossThreadAccess(m_peer), code, reason));
-}
-void WorkerThreadableWebSocketChannel::mainThreadFail(ScriptExecutionContext* context, Peer* peer, const String& reason)
-{
- ASSERT(isMainThread());
- ASSERT_UNUSED(context, context->isDocument());
- ASSERT(peer);
+ m_loaderProxy.postTaskToLoader([peer = m_peer, code, reason = reason.isolatedCopy()](ScriptExecutionContext& context) {
+ ASSERT(isMainThread());
+ ASSERT_UNUSED(context, context.isDocument());
+ ASSERT(peer);
- peer->fail(reason);
+ peer->close(code, reason);
+ });
}
void WorkerThreadableWebSocketChannel::Bridge::fail(const String& reason)
{
if (!m_peer)
return;
- m_loaderProxy.postTaskToLoader(createCallbackTask(&WorkerThreadableWebSocketChannel::mainThreadFail, AllowCrossThreadAccess(m_peer), reason));
-}
-void WorkerThreadableWebSocketChannel::mainThreadDestroy(ScriptExecutionContext* context, PassOwnPtr<Peer> peer)
-{
- ASSERT(isMainThread());
- ASSERT_UNUSED(context, context->isDocument());
- ASSERT_UNUSED(peer, peer);
+ m_loaderProxy.postTaskToLoader([peer = m_peer, reason = reason.isolatedCopy()](ScriptExecutionContext& context) {
+ ASSERT(isMainThread());
+ ASSERT_UNUSED(context, context.isDocument());
+ ASSERT(peer);
- // Peer object will be deleted even if the task does not run in the main thread's cleanup period, because
- // the destructor for the task object (created by createCallbackTask()) will automatically delete the peer.
+ peer->fail(reason);
+ });
}
void WorkerThreadableWebSocketChannel::Bridge::disconnect()
{
clearClientWrapper();
if (m_peer) {
- OwnPtr<Peer> peer = adoptPtr(m_peer);
- m_peer = 0;
- m_loaderProxy.postTaskToLoader(createCallbackTask(&WorkerThreadableWebSocketChannel::mainThreadDestroy, peer.release()));
+ m_loaderProxy.postTaskToLoader([peer = std::unique_ptr<Peer>(m_peer)](ScriptExecutionContext& context) {
+ ASSERT(isMainThread());
+ ASSERT_UNUSED(context, context.isDocument());
+ });
+ m_peer = nullptr;
}
- m_workerGlobalScope = 0;
-}
-
-void WorkerThreadableWebSocketChannel::mainThreadSuspend(ScriptExecutionContext* context, Peer* peer)
-{
- ASSERT(isMainThread());
- ASSERT_UNUSED(context, context->isDocument());
- ASSERT(peer);
-
- peer->suspend();
+ m_workerGlobalScope = nullptr;
}
void WorkerThreadableWebSocketChannel::Bridge::suspend()
{
if (!m_peer)
return;
- m_loaderProxy.postTaskToLoader(createCallbackTask(&WorkerThreadableWebSocketChannel::mainThreadSuspend, AllowCrossThreadAccess(m_peer)));
-}
-void WorkerThreadableWebSocketChannel::mainThreadResume(ScriptExecutionContext* context, Peer* peer)
-{
- ASSERT(isMainThread());
- ASSERT_UNUSED(context, context->isDocument());
- ASSERT(peer);
+ m_loaderProxy.postTaskToLoader([peer = m_peer](ScriptExecutionContext& context) {
+ ASSERT(isMainThread());
+ ASSERT_UNUSED(context, context.isDocument());
+ ASSERT(peer);
- peer->resume();
+ peer->suspend();
+ });
}
void WorkerThreadableWebSocketChannel::Bridge::resume()
{
if (!m_peer)
return;
- m_loaderProxy.postTaskToLoader(createCallbackTask(&WorkerThreadableWebSocketChannel::mainThreadResume, AllowCrossThreadAccess(m_peer)));
+
+ m_loaderProxy.postTaskToLoader([peer = m_peer](ScriptExecutionContext& context) {
+ ASSERT(isMainThread());
+ ASSERT_UNUSED(context, context.isDocument());
+ ASSERT(peer);
+
+ peer->resume();
+ });
}
void WorkerThreadableWebSocketChannel::Bridge::clearClientWrapper()
@@ -635,7 +568,6 @@ void WorkerThreadableWebSocketChannel::Bridge::clearClientWrapper()
void WorkerThreadableWebSocketChannel::Bridge::setMethodNotCompleted()
{
- ASSERT(m_workerClientWrapper);
m_workerClientWrapper->clearSyncMethodDone();
}
@@ -645,12 +577,12 @@ void WorkerThreadableWebSocketChannel::Bridge::waitForMethodCompletion()
{
if (!m_workerGlobalScope)
return;
- WorkerRunLoop& runLoop = m_workerGlobalScope->thread()->runLoop();
+ WorkerRunLoop& runLoop = m_workerGlobalScope->thread().runLoop();
MessageQueueWaitResult result = MessageQueueMessageReceived;
- ThreadableWebSocketChannelClientWrapper* clientWrapper = m_workerClientWrapper.get();
+ ThreadableWebSocketChannelClientWrapper* clientWrapper = m_workerClientWrapper.ptr();
while (m_workerGlobalScope && clientWrapper && !clientWrapper->syncMethodDone() && result != MessageQueueTerminated) {
result = runLoop.runInMode(m_workerGlobalScope.get(), m_taskMode); // May cause this bridge to get disconnected, which makes m_workerGlobalScope become null.
- clientWrapper = m_workerClientWrapper.get();
+ clientWrapper = m_workerClientWrapper.ptr();
}
}
diff --git a/Source/WebCore/Modules/websockets/WorkerThreadableWebSocketChannel.h b/Source/WebCore/Modules/websockets/WorkerThreadableWebSocketChannel.h
index 90b0ea07e..be90d12f1 100644
--- a/Source/WebCore/Modules/websockets/WorkerThreadableWebSocketChannel.h
+++ b/Source/WebCore/Modules/websockets/WorkerThreadableWebSocketChannel.h
@@ -28,8 +28,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef WorkerThreadableWebSocketChannel_h
-#define WorkerThreadableWebSocketChannel_h
+#pragma once
#if ENABLE(WEB_SOCKETS)
@@ -37,7 +36,6 @@
#include "WebSocketChannelClient.h"
#include "WorkerGlobalScope.h"
-#include <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>
#include <wtf/RefPtr.h>
#include <wtf/Threading.h>
@@ -55,41 +53,38 @@ class WorkerRunLoop;
class WorkerThreadableWebSocketChannel : public RefCounted<WorkerThreadableWebSocketChannel>, public ThreadableWebSocketChannel {
WTF_MAKE_FAST_ALLOCATED;
public:
- static PassRefPtr<ThreadableWebSocketChannel> create(WorkerGlobalScope* workerGlobalScope, WebSocketChannelClient* client, const String& taskMode)
+ static Ref<ThreadableWebSocketChannel> create(WorkerGlobalScope& workerGlobalScope, WebSocketChannelClient& client, const String& taskMode, SocketProvider& provider)
{
- return adoptRef(new WorkerThreadableWebSocketChannel(workerGlobalScope, client, taskMode));
+ return adoptRef(*new WorkerThreadableWebSocketChannel(workerGlobalScope, client, taskMode, provider));
}
virtual ~WorkerThreadableWebSocketChannel();
// ThreadableWebSocketChannel functions.
- virtual void connect(const URL&, const String& protocol) override;
- virtual String subprotocol() override;
- virtual String extensions() override;
- virtual ThreadableWebSocketChannel::SendResult send(const String& message) override;
- virtual ThreadableWebSocketChannel::SendResult send(const JSC::ArrayBuffer&, unsigned byteOffset, unsigned byteLength) override;
- virtual ThreadableWebSocketChannel::SendResult send(const Blob&) override;
- virtual unsigned long bufferedAmount() const override;
- virtual void close(int code, const String& reason) override;
- virtual void fail(const String& reason) override;
- virtual void disconnect() override; // Will suppress didClose().
- virtual void suspend() override;
- virtual void resume() override;
+ void connect(const URL&, const String& protocol) override;
+ String subprotocol() override;
+ String extensions() override;
+ ThreadableWebSocketChannel::SendResult send(const String& message) override;
+ ThreadableWebSocketChannel::SendResult send(const JSC::ArrayBuffer&, unsigned byteOffset, unsigned byteLength) override;
+ ThreadableWebSocketChannel::SendResult send(Blob&) override;
+ unsigned bufferedAmount() const override;
+ void close(int code, const String& reason) override;
+ void fail(const String& reason) override;
+ void disconnect() override; // Will suppress didClose().
+ void suspend() override;
+ void resume() override;
// Generated by the bridge. The Peer and its bridge should have identical
// lifetimes.
class Peer : public WebSocketChannelClient {
WTF_MAKE_NONCOPYABLE(Peer); WTF_MAKE_FAST_ALLOCATED;
public:
- static Peer* create(PassRefPtr<ThreadableWebSocketChannelClientWrapper> clientWrapper, WorkerLoaderProxy& loaderProxy, ScriptExecutionContext* context, const String& taskMode)
- {
- return new Peer(clientWrapper, loaderProxy, context, taskMode);
- }
+ Peer(Ref<ThreadableWebSocketChannelClientWrapper>&&, WorkerLoaderProxy&, ScriptExecutionContext&, const String& taskMode, SocketProvider&);
~Peer();
void connect(const URL&, const String& protocol);
void send(const String& message);
void send(const JSC::ArrayBuffer&);
- void send(const Blob&);
+ void send(Blob&);
void bufferedAmount();
void close(int code, const String& reason);
void fail(const String& reason);
@@ -98,18 +93,17 @@ public:
void resume();
// WebSocketChannelClient functions.
- virtual void didConnect() override;
- virtual void didReceiveMessage(const String& message) override;
- virtual void didReceiveBinaryData(PassOwnPtr<Vector<char>>) override;
- virtual void didUpdateBufferedAmount(unsigned long bufferedAmount) override;
- virtual void didStartClosingHandshake() override;
- virtual void didClose(unsigned long unhandledBufferedAmount, ClosingHandshakeCompletionStatus, unsigned short code, const String& reason) override;
- virtual void didReceiveMessageError() override;
+ void didConnect() final;
+ void didReceiveMessage(const String& message) final;
+ void didReceiveBinaryData(Vector<uint8_t>&&) final;
+ void didUpdateBufferedAmount(unsigned bufferedAmount) final;
+ void didStartClosingHandshake() final;
+ void didClose(unsigned unhandledBufferedAmount, ClosingHandshakeCompletionStatus, unsigned short code, const String& reason) final;
+ void didReceiveMessageError() final;
+ void didUpgradeURL() final;
private:
- Peer(PassRefPtr<ThreadableWebSocketChannelClientWrapper>, WorkerLoaderProxy&, ScriptExecutionContext*, const String& taskMode);
-
- RefPtr<ThreadableWebSocketChannelClientWrapper> m_workerClientWrapper;
+ Ref<ThreadableWebSocketChannelClientWrapper> m_workerClientWrapper;
WorkerLoaderProxy& m_loaderProxy;
RefPtr<ThreadableWebSocketChannel> m_mainWebSocketChannel;
String m_taskMode;
@@ -119,24 +113,24 @@ public:
using RefCounted<WorkerThreadableWebSocketChannel>::deref;
protected:
- virtual void refThreadableWebSocketChannel() { ref(); }
- virtual void derefThreadableWebSocketChannel() { deref(); }
+ void refThreadableWebSocketChannel() override { ref(); }
+ void derefThreadableWebSocketChannel() override { deref(); }
private:
// Bridge for Peer. Running on the worker thread.
class Bridge : public RefCounted<Bridge> {
public:
- static PassRefPtr<Bridge> create(PassRefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper, PassRefPtr<WorkerGlobalScope> workerGlobalScope, const String& taskMode)
+ static Ref<Bridge> create(Ref<ThreadableWebSocketChannelClientWrapper>&& workerClientWrapper, Ref<WorkerGlobalScope>&& workerGlobalScope, const String& taskMode, Ref<SocketProvider>&& provider)
{
- return adoptRef(new Bridge(workerClientWrapper, workerGlobalScope, taskMode));
+ return adoptRef(*new Bridge(WTFMove(workerClientWrapper), WTFMove(workerGlobalScope), taskMode, WTFMove(provider)));
}
~Bridge();
void initialize();
void connect(const URL&, const String& protocol);
ThreadableWebSocketChannel::SendResult send(const String& message);
ThreadableWebSocketChannel::SendResult send(const JSC::ArrayBuffer&, unsigned byteOffset, unsigned byteLength);
- ThreadableWebSocketChannel::SendResult send(const Blob&);
- unsigned long bufferedAmount();
+ ThreadableWebSocketChannel::SendResult send(Blob&);
+ unsigned bufferedAmount();
void close(int code, const String& reason);
void fail(const String& reason);
void disconnect();
@@ -147,12 +141,12 @@ private:
using RefCounted<Bridge>::deref;
private:
- Bridge(PassRefPtr<ThreadableWebSocketChannelClientWrapper>, PassRefPtr<WorkerGlobalScope>, const String& taskMode);
+ Bridge(Ref<ThreadableWebSocketChannelClientWrapper>&&, Ref<WorkerGlobalScope>&&, const String& taskMode, Ref<SocketProvider>&&);
- static void setWebSocketChannel(ScriptExecutionContext*, Bridge* thisPtr, Peer*, PassRefPtr<ThreadableWebSocketChannelClientWrapper>);
+ static void setWebSocketChannel(ScriptExecutionContext*, Bridge* thisPtr, Peer*, Ref<ThreadableWebSocketChannelClientWrapper>&&);
// Executed on the main thread to create a Peer for this bridge.
- static void mainThreadInitialize(ScriptExecutionContext*, WorkerLoaderProxy*, PassRefPtr<ThreadableWebSocketChannelClientWrapper>, const String& taskMode);
+ static void mainThreadInitialize(ScriptExecutionContext&, WorkerLoaderProxy&, Ref<ThreadableWebSocketChannelClientWrapper>&&, const String& taskMode, Ref<SocketProvider>&&);
// Executed on the worker context's thread.
void clearClientWrapper();
@@ -160,35 +154,24 @@ private:
void setMethodNotCompleted();
void waitForMethodCompletion();
- RefPtr<ThreadableWebSocketChannelClientWrapper> m_workerClientWrapper;
+ Ref<ThreadableWebSocketChannelClientWrapper> m_workerClientWrapper;
RefPtr<WorkerGlobalScope> m_workerGlobalScope;
WorkerLoaderProxy& m_loaderProxy;
String m_taskMode;
- Peer* m_peer;
+ Peer* m_peer { nullptr };
+ Ref<SocketProvider> m_socketProvider;
};
- WorkerThreadableWebSocketChannel(WorkerGlobalScope*, WebSocketChannelClient*, const String& taskMode);
-
- static void mainThreadConnect(ScriptExecutionContext*, Peer*, const URL&, const String& protocol);
- static void mainThreadSend(ScriptExecutionContext*, Peer*, const String& message);
- static void mainThreadSendArrayBuffer(ScriptExecutionContext*, Peer*, PassOwnPtr<Vector<char>>);
- static void mainThreadSendBlob(ScriptExecutionContext*, Peer*, const URL&, const String& type, long long size);
- static void mainThreadBufferedAmount(ScriptExecutionContext*, Peer*);
- static void mainThreadClose(ScriptExecutionContext*, Peer*, int code, const String& reason);
- static void mainThreadFail(ScriptExecutionContext*, Peer*, const String& reason);
- static void mainThreadDestroy(ScriptExecutionContext*, PassOwnPtr<Peer>);
- static void mainThreadSuspend(ScriptExecutionContext*, Peer*);
- static void mainThreadResume(ScriptExecutionContext*, Peer*);
+ WEBCORE_EXPORT WorkerThreadableWebSocketChannel(WorkerGlobalScope&, WebSocketChannelClient&, const String& taskMode, SocketProvider&);
class WorkerGlobalScopeDidInitializeTask;
- RefPtr<WorkerGlobalScope> m_workerGlobalScope;
- RefPtr<ThreadableWebSocketChannelClientWrapper> m_workerClientWrapper;
+ Ref<WorkerGlobalScope> m_workerGlobalScope;
+ Ref<ThreadableWebSocketChannelClientWrapper> m_workerClientWrapper;
RefPtr<Bridge> m_bridge;
+ Ref<SocketProvider> m_socketProvider;
};
} // namespace WebCore
#endif // ENABLE(WEB_SOCKETS)
-
-#endif // WorkerThreadableWebSocketChannel_h