1package runtimeprofile
2
3import (
4 "crypto/rand"
5 "encoding/hex"
6 "fmt"
7 "path/filepath"
8 "regexp"
9)
10
11var instanceNamePattern = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9_-]{0,31}$`)
12
13// WithBrowserProfile selects an isolated persistent CEF profile while keeping
14// Dumber application data (configuration, history and favorites) shared.
15func (p Profile) WithBrowserProfile(name, runtimeBase string) (Profile, error) {
16 if p.Engine != engineCEF {
17 return Profile{}, fmt.Errorf("--profile requires the CEF engine")
18 }
19 if !instanceNamePattern.MatchString(name) {
20 return Profile{}, fmt.Errorf(
21 "invalid profile name %q: use 1-32 ASCII letters, digits, underscores or hyphens, starting with a letter or digit",
22 name,
23 )
24 }
25 if !filepath.IsAbs(runtimeBase) {
26 return Profile{}, fmt.Errorf("profile runtime base must be absolute")
27 }
28 p.InstanceRoot = filepath.Join(p.EnginePaths.RootDir, "profiles", name)
29 p.InstanceAppIDSuffix = "profile-" + name
30 p.IPC.RuntimeDir = filepath.Join(runtimeBase, "dumber", "profiles", name)
31 p.IPC.BrowserLaunchSocket = filepath.Join(p.IPC.RuntimeDir, browserLaunchSocketName)
32 if len(p.IPC.BrowserLaunchSocket) >= devIPCSocketPathLimit {
33 return Profile{}, fmt.Errorf("profile socket path too long")
34 }
35 return p, nil
36}
37
38// WithEphemeralBrowserProfile selects a fresh temporary CEF profile. The
39// caller owns cleanup after the host exits.
40func (p Profile) WithEphemeralBrowserProfile(runtimeBase string) (Profile, error) {
41 if p.Engine != engineCEF {
42 return Profile{}, fmt.Errorf("--ephemeral requires the CEF engine")
43 }
44 if !filepath.IsAbs(runtimeBase) {
45 return Profile{}, fmt.Errorf("profile runtime base must be absolute")
46 }
47 var nonce [8]byte
48 if _, err := rand.Read(nonce[:]); err != nil {
49 return Profile{}, fmt.Errorf("generate ephemeral profile identity: %w", err)
50 }
51 key := "ephemeral-" + hex.EncodeToString(nonce[:])
52 p.InstanceRoot = filepath.Join(runtimeBase, "dumber", key)
53 p.InstanceAppIDSuffix = key
54 p.IPC.RuntimeDir = p.InstanceRoot
55 p.IPC.BrowserLaunchSocket = filepath.Join(p.IPC.RuntimeDir, browserLaunchSocketName)
56 if len(p.IPC.BrowserLaunchSocket) >= devIPCSocketPathLimit {
57 return Profile{}, fmt.Errorf("ephemeral profile socket path too long")
58 }
59 return p, nil
60}