Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement Web Worker support #1030

Merged
merged 1 commit into from
Jul 19, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions hive/lib/hive.dart
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ import 'dart:math';
import 'dart:typed_data';

import 'package:crypto/crypto.dart';
import 'package:hive/src/backend/storage_backend.dart';
import 'package:hive/src/box/default_compaction_strategy.dart';
import 'package:hive/src/box/default_key_comparator.dart';
import 'package:hive/src/crypto/aes_cbc_pkcs7.dart';
@@ -18,9 +19,14 @@ import 'package:hive/src/object/hive_object.dart';
import 'package:hive/src/util/extensions.dart';
import 'package:meta/meta.dart';

export 'src/box_collection/box_collection_stub.dart'
if (dart.library.html) 'package:hive/src/box_collection/box_collection_indexed_db.dart'
if (dart.library.io) 'package:hive/src/box_collection/box_collection.dart';
import 'src/backend/js/web_worker/web_worker_stub.dart'
if (dart.library.html) 'src/backend/js/web_worker/web_worker.dart';

export 'package:hive/src/box_collection/box_collection.dart';

export 'src/backend/js/web_worker/web_worker_stub.dart'
if (dart.library.html) 'src/backend/js/web_worker/web_worker.dart';
export 'src/backend/storage_backend_memory.dart';
export 'src/object/hive_object.dart' show HiveObject, HiveObjectMixin;

part 'src/annotations/hive_field.dart';
25 changes: 20 additions & 5 deletions hive/lib/src/backend/js/backend_manager.dart
Original file line number Diff line number Diff line change
@@ -1,18 +1,33 @@
import 'package:hive/hive.dart';
import 'package:hive/src/backend/storage_backend.dart';
import 'package:hive/src/backend/stub/backend_manager_memory.dart';

import 'native/backend_manager.dart' as native;
import 'web_worker/backend_manager.dart' as web_worker;

/// Opens IndexedDB databases
abstract class BackendManager {
BackendManager._();
// caching the backend manager as it makes no sense to have different backends
// within the same application
static BackendManagerInterface? _manager;

const BackendManager._();

// dummy implementation as the WebWorker branch is not stable yet
static BackendManagerInterface select(
[HiveStorageBackendPreference? backendPreference]) {
switch (backendPreference) {
default:
return native.BackendManager();
if (_manager == null) {
if (backendPreference is HiveStorageBackendPreferenceWebWorker) {
_manager = web_worker.BackendManagerWebWorker(backendPreference);
} else if (backendPreference == HiveStorageBackendPreference.native ||
backendPreference == null) {
_manager = native.BackendManager();
} else if (backendPreference == HiveStorageBackendPreference.memory) {
_manager = BackendManagerMemory();
} else {
throw UnimplementedError(
'$backendPreference is not a known HiveStorageBackendPreference');
}
}
return _manager!;
}
}
44 changes: 38 additions & 6 deletions hive/lib/src/backend/js/native/backend_manager.dart
Original file line number Diff line number Diff line change
@@ -30,8 +30,6 @@ class BackendManager implements BackendManagerInterface {
// in case the objectStore is not contained, re-open the db and
// update version
if (!(db.objectStoreNames ?? []).contains(objectStoreName)) {
print(
'Creating objectStore $objectStoreName in database $databaseName...');
db = await indexedDB!.open(
databaseName,
version: (db.version ?? 1) + 1,
@@ -44,15 +42,49 @@ class BackendManager implements BackendManagerInterface {
);
}

print('Got object store $objectStoreName in database $databaseName.');

return StorageBackendJs(db, cipher, objectStoreName);
}

@override
Future<void> deleteBox(String name, String? path, String? collection) async {
print('Delete $name // $collection from disk');
Future<Map<String, StorageBackend>> openCollection(
Set<String> names,
String? path,
bool crashRecovery,
HiveCipher? cipher,
String collection) async {
var db =
await indexedDB!.open(collection, version: 1, onUpgradeNeeded: (e) {
var db = e.target.result as Database;
for (var objectStoreName in names) {
if (!(db.objectStoreNames ?? []).contains(objectStoreName)) {
db.createObjectStore(objectStoreName);
}
}
});

// in case the objectStore is not contained, re-open the db and
// update version
if (!(names.every((objectStoreName) =>
(db.objectStoreNames ?? []).contains(objectStoreName)))) {
db = await indexedDB!.open(
collection,
version: (db.version ?? 1) + 1,
onUpgradeNeeded: (e) {
var db = e.target.result as Database;
for (var objectStoreName in names) {
if (!(db.objectStoreNames ?? []).contains(objectStoreName)) {
db.createObjectStore(objectStoreName);
}
}
},
);
}
return Map.fromEntries(
names.map((e) => MapEntry(e, StorageBackendJs(db, cipher, e))));
}

@override
Future<void> deleteBox(String name, String? path, String? collection) async {
// compatibility for old store format
final databaseName = collection ?? name;
final objectStoreName = collection == null ? 'box' : name;
2 changes: 0 additions & 2 deletions hive/lib/src/backend/js/native/storage_backend_js.dart
Original file line number Diff line number Diff line change
@@ -205,8 +205,6 @@ class StorageBackendJs extends StorageBackend {
? window.indexedDB
: WorkerGlobalScope.instance.indexedDB;

print('Delete ${_db.name} // $objectStoreName from disk');

// directly deleting the entire DB if a non-collection Box
if (_db.objectStoreNames?.length == 1) {
await indexDB!.deleteDatabase(_db.name!);
98 changes: 98 additions & 0 deletions hive/lib/src/backend/js/web_worker/backend_manager.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import 'dart:async';
import 'dart:html';
import 'dart:indexed_db';
import 'dart:js' as js;
import 'package:hive/hive.dart';
import 'package:hive/src/backend/js/web_worker/web_worker_interface.dart';

import '../../storage_backend.dart';
import 'storage_backend_web_worker.dart';

/// Opens IndexedDB databases
class BackendManagerWebWorker implements BackendManagerInterface {
final HiveStorageBackendPreferenceWebWorker backendPreference;

WebWorkerInterface? _interface;

WebWorkerInterface get interface {
_interface ??= WebWorkerInterface(
backendPreference.path, backendPreference.onStackTrace);
return _interface!;
}

BackendManagerWebWorker(this.backendPreference);

IdbFactory? get indexedDB => js.context.hasProperty('window')
? window.indexedDB
: WorkerGlobalScope.instance.indexedDB;

@override
Future<StorageBackendWebWorker> open(String name, String? path,
bool crashRecovery, HiveCipher? cipher, String? collection) async {
// compatibility for old store format
final databaseName = collection ?? name;
final objectStoreName = collection == null ? 'box' : name;

await interface.query('open', databaseName, objectStoreName);
return StorageBackendWebWorker(
interface, databaseName, cipher, objectStoreName);
}

@override
Future<Map<String, StorageBackend>> openCollection(
Set<String> names,
String? path,
bool crashRecovery,
HiveCipher? cipher,
String collection) async {
// for collections, create an additional interface
final interface = WebWorkerInterface(
backendPreference.path, backendPreference.onStackTrace);
await interface.query('open', collection, null, null, names);
return Map.fromEntries(names.map(
(e) => MapEntry(
e,
StorageBackendWebWorker(interface, collection, cipher, e),
),
));
}

@override
Future<void> deleteBox(String name, String? path, String? collection) async {
// compatibility for old store format
final databaseName = collection ?? name;
final objectStoreName = collection == null ? 'box' : name;

// cipher doesn't matter on delete operation
await interface.query('delete', databaseName, objectStoreName);
}

@override
Future<bool> boxExists(String name, String? path, String? collection) async {
// TODO(TheOneWithTheBraid): migrate into web worker

// compatibility for old store format
final databaseName = collection ?? name;
final objectStoreName = collection == null ? 'box' : name;
// https://stackoverflow.com/a/17473952
try {
var _exists = true;
if (collection == null) {
await indexedDB!.open(databaseName, version: 1, onUpgradeNeeded: (e) {
e.target.transaction!.abort();
_exists = false;
});
} else {
final db =
await indexedDB!.open(collection, version: 1, onUpgradeNeeded: (e) {
var db = e.target.result as Database;
_exists = (db.objectStoreNames ?? []).contains(objectStoreName);
});
_exists = (db.objectStoreNames ?? []).contains(objectStoreName);
}
return _exists;
} catch (error) {
return false;
}
}
}
Loading