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