open_db_request.go raw
1 //go:build js && wasm
2 // +build js,wasm
3
4 package idb
5
6 import (
7 "context"
8 "fmt"
9 "log"
10
11 "github.com/hack-pad/safejs"
12 )
13
14 // OpenDBRequest provides access to the results of requests to open or delete databases (performed using Factory.open and Factory.DeleteDatabase).
15 type OpenDBRequest struct {
16 *Request
17 }
18
19 // Upgrader is a function that can upgrade the given database from an old version to a new one.
20 type Upgrader func(db *Database, oldVersion, newVersion uint) error
21
22 func newOpenDBRequest(ctx context.Context, req *Request, upgrader Upgrader) (*OpenDBRequest, error) {
23 ctx, cancel := context.WithCancel(ctx)
24
25 err := req.ListenSuccess(ctx, func() {
26 defer cancel()
27 err := openDBListenSuccess(req)
28 if err != nil {
29 panic(err)
30 }
31 })
32 if err != nil {
33 return nil, err
34 }
35
36 upgrade, err := safejs.FuncOf(func(this safejs.Value, args []safejs.Value) interface{} {
37 err := openDBUpgradeNeeded(req, upgrader, args)
38 if err != nil {
39 panic(err)
40 }
41 return nil
42 })
43 if err != nil {
44 return nil, err
45 }
46 _, err = req.jsRequest.Call(addEventListener, "upgradeneeded", upgrade)
47 if err != nil {
48 return nil, tryAsDOMException(err)
49 }
50 go func() {
51 <-ctx.Done()
52 _, err := req.jsRequest.Call(removeEventListener, "upgradeneeded", upgrade)
53 if err != nil {
54 panic(err)
55 }
56 upgrade.Release()
57 }()
58 return &OpenDBRequest{req}, nil
59 }
60
61 func openDBListenSuccess(req *Request) error {
62 jsDB, err := req.Result()
63 if err != nil {
64 return err
65 }
66 versionChange, err := safejs.FuncOf(func(safejs.Value, []safejs.Value) interface{} {
67 log.Println("Version change detected, closing DB...")
68 _, closeErr := jsDB.Call("close")
69 if closeErr != nil {
70 log.Println("Error closing DB:", closeErr)
71 }
72 return nil
73 })
74 if err != nil {
75 return err
76 }
77 _, err = jsDB.Call(addEventListener, "versionchange", versionChange)
78 return tryAsDOMException(err)
79 }
80
81 func openDBUpgradeNeeded(req *Request, upgrader Upgrader, args []safejs.Value) error {
82 event := args[0]
83 jsDatabase, err := req.Result()
84 if err != nil {
85 return err
86 }
87 db := wrapDatabase(jsDatabase)
88 oldVersionValue, err := event.Get("oldVersion")
89 if err != nil {
90 return err
91 }
92 oldVersion, err := oldVersionValue.Int()
93 if err != nil {
94 return err
95 }
96 newVersionValue, err := event.Get("newVersion")
97 if err != nil {
98 return err
99 }
100 newVersion, err := newVersionValue.Int()
101 if err != nil {
102 return err
103 }
104 if oldVersion < 0 || newVersion < 0 {
105 return fmt.Errorf("Unexpected negative oldVersion or newVersion: %d, %d", oldVersion, newVersion)
106 }
107 return upgrader(db, uint(oldVersion), uint(newVersion))
108 }
109
110 // Result returns the result of the request. If the request failed and the result is not available, an error is returned.
111 func (o *OpenDBRequest) Result() (*Database, error) {
112 db, err := o.Request.Result()
113 if err != nil {
114 return nil, err
115 }
116 return wrapDatabase(db), nil
117 }
118
119 // Await waits for success or failure, then returns the results.
120 func (o *OpenDBRequest) Await(ctx context.Context) (*Database, error) {
121 db, err := o.Request.Await(ctx)
122 if err != nil {
123 return nil, err
124 }
125 return wrapDatabase(db), nil
126 }
127