rui_orig/session.go

680 lines
19 KiB
Go
Raw Normal View History

2021-09-07 17:36:50 +03:00
package rui
import (
"fmt"
2022-08-18 18:18:36 +03:00
"net/url"
2021-09-07 17:36:50 +03:00
"strconv"
"strings"
)
2022-11-02 21:05:55 +03:00
type webBridge interface {
2022-10-31 16:07:59 +03:00
startUpdateScript(htmlID string) bool
finishUpdateScript(htmlID string)
2022-11-02 20:10:19 +03:00
callFunc(funcName string, args ...any) bool
2022-10-30 17:22:33 +03:00
updateInnerHTML(htmlID, html string)
appendToInnerHTML(htmlID, html string)
updateCSSProperty(htmlID, property, value string)
2022-10-31 16:07:59 +03:00
updateProperty(htmlID, property string, value any)
2022-10-30 17:22:33 +03:00
removeProperty(htmlID, property string)
2022-10-27 16:14:30 +03:00
readMessage() (string, bool)
writeMessage(text string) bool
2022-11-08 16:31:21 +03:00
addAnimationCSS(css string)
clearAnimation()
2022-11-23 15:10:29 +03:00
canvasStart(htmlID string)
2022-11-02 20:08:02 +03:00
callCanvasFunc(funcName string, args ...any)
callCanvasVarFunc(v any, funcName string, args ...any)
callCanvasImageFunc(url string, property string, funcName string, args ...any)
createCanvasVar(funcName string, args ...any) any
updateCanvasProperty(property string, value any)
2022-11-23 15:10:29 +03:00
canvasFinish()
canvasTextMetrics(htmlID, font, text string) TextMetrics
htmlPropertyValue(htmlID, name string) string
2022-10-27 16:14:30 +03:00
answerReceived(answer DataObject)
close()
remoteAddr() string
}
2021-09-07 17:36:50 +03:00
// SessionContent is the interface of a session content
type SessionContent interface {
CreateRootView(session Session) View
}
// Session provide interface to session parameters assess
type Session interface {
// App return the current application interface
App() Application
// ID return the id of the session
ID() int
// DarkTheme returns "true" if the dark theme is used
DarkTheme() bool
// Mobile returns "true" if current session is displayed on a touch screen device
TouchScreen() bool
// PixelRatio returns the ratio of the resolution in physical pixels to the resolution
// in logical pixels for the current display device.
PixelRatio() float64
// TextDirection returns the default text direction (LeftToRightDirection (1) or RightToLeftDirection (2))
TextDirection() int
// Constant returns the constant with "tag" name or "" if it is not exists
Constant(tag string) (string, bool)
// Color returns the color with "tag" name or 0 if it is not exists
Color(tag string) (Color, bool)
2022-04-23 18:13:35 +03:00
// ImageConstant returns the image constant with "tag" name or "" if it is not exists
ImageConstant(tag string) (string, bool)
2021-09-07 17:36:50 +03:00
// SetCustomTheme set the custom theme
SetCustomTheme(name string) bool
// UserAgent returns the "user-agent" text of the client browser
UserAgent() string
// RemoteAddr returns the client address.
RemoteAddr() string
2021-09-07 17:36:50 +03:00
// Language returns the current session language
Language() string
// SetLanguage set the current session language
SetLanguage(lang string)
// GetString returns the text for the current language
GetString(tag string) (string, bool)
// Content returns the SessionContent of session
2021-09-07 17:36:50 +03:00
Content() SessionContent
2022-11-01 20:13:09 +03:00
setContent(content SessionContent) bool
2021-09-07 17:36:50 +03:00
// SetTitle sets the text of the browser title/tab
SetTitle(title string)
// SetTitleColor sets the color of the browser navigation bar. Supported only in Safari and Chrome for android
SetTitleColor(color Color)
2021-09-07 17:36:50 +03:00
// RootView returns the root view of the session
RootView() View
// Get returns a value of the view (with id defined by the first argument) property with name defined by the second argument.
// The type of return value depends on the property. If the property is not set then nil is returned.
2022-07-26 18:36:00 +03:00
Get(viewID, tag string) any
2021-09-07 17:36:50 +03:00
// Set sets the value (third argument) of the property (second argument) of the view with id defined by the first argument.
// Return "true" if the value has been set, in the opposite case "false" are returned and
// a description of the error is written to the log
2022-07-26 18:36:00 +03:00
Set(viewID, tag string, value any) bool
2021-09-07 17:36:50 +03:00
// DownloadFile downloads (saves) on the client side the file located at the specified path on the server.
2021-11-07 09:43:13 +03:00
DownloadFile(path string)
//DownloadFileData downloads (saves) on the client side a file with a specified name and specified content.
2021-11-07 09:43:13 +03:00
DownloadFileData(filename string, data []byte)
2022-08-18 18:18:36 +03:00
// OpenURL opens the url in the new browser tab
OpenURL(url string)
2021-11-07 09:43:13 +03:00
ClientItem(key string) (string, bool)
SetClientItem(key, value string)
RemoveAllClientItems()
2022-11-01 20:13:09 +03:00
getCurrentTheme() Theme
registerAnimation(props []AnimatedProperty) string
2021-09-07 17:36:50 +03:00
resolveConstants(value string) (string, bool)
checkboxOffImage() string
checkboxOnImage() string
radiobuttonOffImage() string
radiobuttonOnImage() string
viewByHTMLID(id string) View
nextViewID() string
2022-07-26 18:36:00 +03:00
styleProperty(styleTag, property string) any
2021-09-07 17:36:50 +03:00
2022-11-02 21:05:55 +03:00
setBridge(events chan DataObject, bridge webBridge)
2021-09-07 17:36:50 +03:00
writeInitScript(writer *strings.Builder)
2022-11-02 20:10:19 +03:00
callFunc(funcName string, args ...any)
2022-10-30 17:22:33 +03:00
updateInnerHTML(htmlID, html string)
appendToInnerHTML(htmlID, html string)
updateCSSProperty(htmlID, property, value string)
2022-10-31 16:07:59 +03:00
updateProperty(htmlID, property string, value any)
2022-10-30 17:22:33 +03:00
removeProperty(htmlID, property string)
2022-10-31 16:07:59 +03:00
startUpdateScript(htmlID string) bool
finishUpdateScript(htmlID string)
2022-11-08 16:31:21 +03:00
addAnimationCSS(css string)
clearAnimation()
2022-11-23 15:10:29 +03:00
canvasStart(htmlID string)
2022-11-02 20:08:02 +03:00
callCanvasFunc(funcName string, args ...any)
createCanvasVar(funcName string, args ...any) any
callCanvasVarFunc(v any, funcName string, args ...any)
callCanvasImageFunc(url string, property string, funcName string, args ...any)
updateCanvasProperty(property string, value any)
2022-11-23 15:10:29 +03:00
canvasFinish()
canvasTextMetrics(htmlID, font, text string) TextMetrics
htmlPropertyValue(htmlID, name string) string
2021-09-07 17:36:50 +03:00
handleAnswer(data DataObject)
handleRootSize(data DataObject)
2021-09-07 17:36:50 +03:00
handleResize(data DataObject)
2022-10-27 16:14:30 +03:00
handleEvent(command string, data DataObject)
2021-09-07 17:36:50 +03:00
close()
onStart()
onFinish()
onPause()
onResume()
onDisconnect()
onReconnect()
ignoreViewUpdates() bool
setIgnoreViewUpdates(ignore bool)
popupManager() *popupManager
imageManager() *imageManager
}
type sessionData struct {
customTheme Theme
currentTheme Theme
darkTheme bool
touchScreen bool
screenWidth int
screenHeight int
textDirection int
pixelRatio float64
userAgent string
language string
languages []string
checkboxOff string
checkboxOn string
radiobuttonOff string
radiobuttonOn string
app Application
sessionID int
viewCounter int
content SessionContent
rootView View
ignoreUpdates bool
popups *popupManager
images *imageManager
2022-11-02 21:05:55 +03:00
bridge webBridge
events chan DataObject
animationCounter int
animationCSS string
2022-07-22 13:10:55 +03:00
updateScripts map[string]*strings.Builder
clientStorage map[string]string
2021-09-07 17:36:50 +03:00
}
func newSession(app Application, id int, customTheme string, params DataObject) Session {
session := new(sessionData)
session.app = app
session.sessionID = id
session.darkTheme = false
session.touchScreen = false
session.pixelRatio = 1
session.textDirection = LeftToRightDirection
session.languages = []string{}
session.viewCounter = 0
session.ignoreUpdates = false
session.animationCounter = 0
session.animationCSS = ""
2022-07-22 13:10:55 +03:00
session.updateScripts = map[string]*strings.Builder{}
session.clientStorage = map[string]string{}
2021-09-07 17:36:50 +03:00
if customTheme != "" {
if theme, ok := CreateThemeFromText(customTheme); ok {
2021-09-07 17:36:50 +03:00
session.customTheme = theme
session.currentTheme = nil
2021-09-07 17:36:50 +03:00
}
}
2022-10-27 16:14:30 +03:00
if params != nil {
session.handleSessionInfo(params)
2021-09-07 17:36:50 +03:00
}
return session
}
func (session *sessionData) App() Application {
return session.app
}
func (session *sessionData) ID() int {
return session.sessionID
}
2022-11-02 21:05:55 +03:00
func (session *sessionData) setBridge(events chan DataObject, bridge webBridge) {
2021-09-07 17:36:50 +03:00
session.events = events
2022-11-02 21:05:55 +03:00
session.bridge = bridge
2021-09-07 17:36:50 +03:00
}
func (session *sessionData) close() {
if session.events != nil {
session.events <- ParseDataText(`session-close{session="` + strconv.Itoa(session.sessionID) + `"}`)
}
}
2022-07-26 18:36:00 +03:00
func (session *sessionData) styleProperty(styleTag, propertyTag string) any {
2022-05-23 15:22:14 +03:00
if style := session.getCurrentTheme().style(styleTag); style != nil {
return style.getRaw(propertyTag)
2021-09-07 17:36:50 +03:00
}
//errorLogF(`property "%v" not found`, propertyTag)
return nil
}
func (session *sessionData) nextViewID() string {
session.viewCounter++
return fmt.Sprintf("id%06d", session.viewCounter)
}
func (session *sessionData) viewByHTMLID(id string) View {
if session.rootView == nil {
return nil
}
popupManager := session.popupManager()
for _, popup := range popupManager.popups {
if view := popup.viewByHTMLID(id); view != nil {
return view
}
}
return viewByHTMLID(id, session.rootView)
}
func (session *sessionData) Content() SessionContent {
return session.content
}
2022-11-01 20:13:09 +03:00
func (session *sessionData) setContent(content SessionContent) bool {
2021-09-07 17:36:50 +03:00
if content != nil {
session.content = content
2022-11-01 20:13:09 +03:00
session.rootView = content.CreateRootView(session)
2021-09-07 17:36:50 +03:00
if session.rootView != nil {
2022-05-17 10:46:00 +03:00
session.rootView.setParentID("ruiRootView")
2021-09-07 17:36:50 +03:00
return true
}
}
return false
}
func (session *sessionData) RootView() View {
return session.rootView
}
func (session *sessionData) writeInitScript(writer *strings.Builder) {
if css := session.getCurrentTheme().cssText(session); css != "" {
css = strings.ReplaceAll(css, "\n", `\n`)
css = strings.ReplaceAll(css, "\t", `\t`)
2021-09-07 17:36:50 +03:00
writer.WriteString(`document.querySelector('style').textContent += "`)
writer.WriteString(css)
writer.WriteString("\";\n")
}
if session.rootView != nil {
writer.WriteString(`document.getElementById('ruiRootView').innerHTML = '`)
2022-11-23 15:10:29 +03:00
buffer := allocStringBuilder()
defer freeStringBuilder(buffer)
viewHTML(session.rootView, buffer)
text := strings.ReplaceAll(buffer.String(), "'", `\'`)
writer.WriteString(text)
2021-09-07 17:36:50 +03:00
writer.WriteString("';\nscanElementsSize();")
}
}
func (session *sessionData) reload() {
buffer := allocStringBuilder()
defer freeStringBuilder(buffer)
css := appStyles + session.getCurrentTheme().cssText(session) + session.animationCSS
css = strings.ReplaceAll(css, "\n", `\n`)
css = strings.ReplaceAll(css, "\t", `\t`)
buffer.WriteString(`document.querySelector('style').textContent = "`)
buffer.WriteString(css)
buffer.WriteString("\";\n")
if session.rootView != nil {
buffer.WriteString(`document.getElementById('ruiRootView').innerHTML = '`)
viewHTML(session.rootView, buffer)
buffer.WriteString("';\nscanElementsSize();")
}
2022-11-08 16:31:21 +03:00
session.bridge.writeMessage(buffer.String())
2021-09-07 17:36:50 +03:00
}
func (session *sessionData) ignoreViewUpdates() bool {
2022-11-02 21:05:55 +03:00
return session.bridge == nil || session.ignoreUpdates
2021-09-07 17:36:50 +03:00
}
func (session *sessionData) setIgnoreViewUpdates(ignore bool) {
session.ignoreUpdates = ignore
}
2022-07-26 18:36:00 +03:00
func (session *sessionData) Get(viewID, tag string) any {
2021-09-07 17:36:50 +03:00
if view := ViewByID(session.RootView(), viewID); view != nil {
return view.Get(tag)
}
return nil
}
2022-07-26 18:36:00 +03:00
func (session *sessionData) Set(viewID, tag string, value any) bool {
2021-09-07 17:36:50 +03:00
if view := ViewByID(session.RootView(), viewID); view != nil {
return view.Set(tag, value)
}
return false
}
func (session *sessionData) popupManager() *popupManager {
if session.popups == nil {
session.popups = new(popupManager)
session.popups.popups = []Popup{}
}
return session.popups
}
func (session *sessionData) imageManager() *imageManager {
if session.images == nil {
session.images = new(imageManager)
session.images.images = make(map[string]*imageData)
}
return session.images
}
2022-11-02 20:10:19 +03:00
func (session *sessionData) callFunc(funcName string, args ...any) {
2022-11-02 21:05:55 +03:00
if session.bridge != nil {
session.bridge.callFunc(funcName, args...)
2022-10-29 20:16:40 +03:00
} else {
ErrorLog("No connection")
}
}
2022-10-30 17:22:33 +03:00
func (session *sessionData) updateInnerHTML(htmlID, html string) {
if !session.ignoreViewUpdates() {
2022-11-02 21:05:55 +03:00
if session.bridge != nil {
session.bridge.updateInnerHTML(htmlID, html)
2022-10-30 17:22:33 +03:00
} else {
ErrorLog("No connection")
}
}
}
func (session *sessionData) appendToInnerHTML(htmlID, html string) {
if !session.ignoreViewUpdates() {
2022-11-02 21:05:55 +03:00
if session.bridge != nil {
session.bridge.appendToInnerHTML(htmlID, html)
2022-10-30 17:22:33 +03:00
} else {
ErrorLog("No connection")
}
}
}
func (session *sessionData) updateCSSProperty(htmlID, property, value string) {
2022-11-02 21:05:55 +03:00
if !session.ignoreViewUpdates() && session.bridge != nil {
session.bridge.updateCSSProperty(htmlID, property, value)
2022-10-30 17:22:33 +03:00
}
}
2022-10-31 16:07:59 +03:00
func (session *sessionData) updateProperty(htmlID, property string, value any) {
2022-11-02 21:05:55 +03:00
if !session.ignoreViewUpdates() && session.bridge != nil {
session.bridge.updateProperty(htmlID, property, value)
2022-10-30 17:22:33 +03:00
}
}
2022-10-31 16:07:59 +03:00
func (session *sessionData) removeProperty(htmlID, property string) {
2022-11-02 21:05:55 +03:00
if !session.ignoreViewUpdates() && session.bridge != nil {
session.bridge.removeProperty(htmlID, property)
2022-10-30 17:22:33 +03:00
}
}
2022-10-31 16:07:59 +03:00
func (session *sessionData) startUpdateScript(htmlID string) bool {
2022-11-02 21:05:55 +03:00
if session.bridge != nil {
return session.bridge.startUpdateScript(htmlID)
2022-10-31 16:07:59 +03:00
}
return false
}
func (session *sessionData) finishUpdateScript(htmlID string) {
2022-11-02 21:05:55 +03:00
if session.bridge != nil {
session.bridge.finishUpdateScript(htmlID)
2022-10-30 17:22:33 +03:00
}
}
2022-11-08 16:31:21 +03:00
func (session *sessionData) addAnimationCSS(css string) {
if session.bridge != nil {
session.bridge.addAnimationCSS(css)
}
}
func (session *sessionData) clearAnimation() {
if session.bridge != nil {
session.bridge.clearAnimation()
}
}
2022-11-23 15:10:29 +03:00
func (session *sessionData) canvasStart(htmlID string) {
2022-11-02 21:05:55 +03:00
if session.bridge != nil {
2022-11-23 15:10:29 +03:00
session.bridge.canvasStart(htmlID)
2022-11-02 20:08:02 +03:00
}
}
func (session *sessionData) callCanvasFunc(funcName string, args ...any) {
2022-11-02 21:05:55 +03:00
if session.bridge != nil {
session.bridge.callCanvasFunc(funcName, args...)
2022-11-02 20:08:02 +03:00
}
}
func (session *sessionData) updateCanvasProperty(property string, value any) {
2022-11-02 21:05:55 +03:00
if session.bridge != nil {
session.bridge.updateCanvasProperty(property, value)
2022-11-02 20:08:02 +03:00
}
}
func (session *sessionData) createCanvasVar(funcName string, args ...any) any {
2022-11-02 21:05:55 +03:00
if session.bridge != nil {
return session.bridge.createCanvasVar(funcName, args...)
2022-11-02 20:08:02 +03:00
}
return nil
}
func (session *sessionData) callCanvasVarFunc(v any, funcName string, args ...any) {
2022-11-02 21:05:55 +03:00
if session.bridge != nil && v != nil {
session.bridge.callCanvasVarFunc(v, funcName, args...)
2022-11-02 20:08:02 +03:00
}
}
func (session *sessionData) callCanvasImageFunc(url string, property string, funcName string, args ...any) {
2022-11-02 21:05:55 +03:00
if session.bridge != nil {
session.bridge.callCanvasImageFunc(url, property, funcName, args...)
2022-11-02 20:08:02 +03:00
}
}
2022-11-23 15:10:29 +03:00
func (session *sessionData) canvasFinish() {
2022-11-02 21:05:55 +03:00
if session.bridge != nil {
2022-11-23 15:10:29 +03:00
session.bridge.canvasFinish()
2022-11-02 20:08:02 +03:00
}
}
func (session *sessionData) canvasTextMetrics(htmlID, font, text string) TextMetrics {
2022-11-02 21:05:55 +03:00
if session.bridge != nil {
return session.bridge.canvasTextMetrics(htmlID, font, text)
2021-09-07 17:36:50 +03:00
}
ErrorLog("No connection")
return TextMetrics{Width: 0}
}
func (session *sessionData) htmlPropertyValue(htmlID, name string) string {
2022-11-02 21:05:55 +03:00
if session.bridge != nil {
return session.bridge.htmlPropertyValue(htmlID, name)
}
ErrorLog("No connection")
return ""
2021-09-07 17:36:50 +03:00
}
func (session *sessionData) handleAnswer(data DataObject) {
2022-11-02 21:05:55 +03:00
session.bridge.answerReceived(data)
2021-09-07 17:36:50 +03:00
}
func (session *sessionData) handleRootSize(data DataObject) {
getValue := func(tag string) int {
if value, ok := data.PropertyValue(tag); ok {
float, err := strconv.ParseFloat(value, 64)
if err == nil {
return int(float)
}
ErrorLog(`Resize event error: ` + err.Error())
} else {
ErrorLogF(`Resize event error: the property "%s" not found`, tag)
}
return 0
}
if w := getValue("width"); w > 0 {
session.screenWidth = w
}
if h := getValue("height"); h > 0 {
session.screenHeight = h
}
}
2021-09-07 17:36:50 +03:00
func (session *sessionData) handleResize(data DataObject) {
if node := data.PropertyByTag("views"); node != nil && node.Type() == ArrayNode {
2021-09-07 17:36:50 +03:00
for _, el := range node.ArrayElements() {
if el.IsObject() {
obj := el.Object()
getFloat := func(tag string) float64 {
if value, ok := obj.PropertyValue(tag); ok {
f, err := strconv.ParseFloat(value, 64)
if err == nil {
return f
}
ErrorLog(`Resize event error: ` + err.Error())
} else {
ErrorLogF(`Resize event error: the property "%s" not found`, tag)
}
return 0
}
if viewID, ok := obj.PropertyValue("id"); ok {
if n := strings.IndexRune(viewID, '-'); n > 0 {
if view := session.viewByHTMLID(viewID[:n]); view != nil {
view.onItemResize(view, viewID[n+1:], getFloat("x"), getFloat("y"), getFloat("width"), getFloat("height"))
2021-09-07 17:36:50 +03:00
} else {
DebugLogF(`View with id == %s not found`, viewID[:n])
2021-09-07 17:36:50 +03:00
}
} else if view := session.viewByHTMLID(viewID); view != nil {
view.onResize(view, getFloat("x"), getFloat("y"), getFloat("width"), getFloat("height"))
view.setScroll(getFloat("scroll-x"), getFloat("scroll-y"), getFloat("scroll-width"), getFloat("scroll-height"))
} else {
DebugLogF(`View with id == %s not found`, viewID)
2021-09-07 17:36:50 +03:00
}
} else {
ErrorLog(`"id" property not found`)
}
} else {
ErrorLog(`Resize event error: views element is not object`)
}
}
} else {
ErrorLog(`Resize event error: invalid "views" property`)
}
}
2022-10-27 16:14:30 +03:00
func (session *sessionData) handleSessionInfo(params DataObject) {
if value, ok := params.PropertyValue("touch"); ok {
session.touchScreen = (value == "1" || value == "true")
}
if value, ok := params.PropertyValue("user-agent"); ok {
session.userAgent = value
}
if value, ok := params.PropertyValue("direction"); ok {
if value == "rtl" {
session.textDirection = RightToLeftDirection
}
}
if value, ok := params.PropertyValue("language"); ok {
session.language = value
}
if value, ok := params.PropertyValue("languages"); ok {
session.languages = strings.Split(value, ",")
}
if value, ok := params.PropertyValue("dark"); ok {
session.darkTheme = (value == "1" || value == "true")
}
if value, ok := params.PropertyValue("pixel-ratio"); ok {
if f, err := strconv.ParseFloat(value, 64); err != nil {
ErrorLog(err.Error())
} else {
session.pixelRatio = f
}
}
if node := params.PropertyByTag("storage"); node != nil && node.Type() == ObjectNode {
if obj := node.Object(); obj != nil {
for i := 0; i < obj.PropertyCount(); i++ {
if element := obj.Property(i); element.Type() == TextNode {
session.clientStorage[element.Tag()] = element.Text()
}
}
}
}
2022-10-27 16:14:30 +03:00
}
func (session *sessionData) handleEvent(command string, data DataObject) {
switch command {
case "session-pause":
session.onPause()
case "session-resume":
session.onResume()
case "root-size":
session.handleRootSize(data)
case "resize":
session.handleResize(data)
case "sessionInfo":
session.handleSessionInfo(data)
case "storageError":
if text, ok := data.PropertyValue("error"); ok {
ErrorLog(text)
}
2022-10-27 16:14:30 +03:00
default:
if viewID, ok := data.PropertyValue("id"); ok {
if view := session.viewByHTMLID(viewID); view != nil {
view.handleCommand(view, command, data)
}
} else if command != "clickOutsidePopup" {
ErrorLog(`"id" property not found. Event: ` + command)
2021-09-07 17:36:50 +03:00
}
}
}
func (session *sessionData) SetTitle(title string) {
title, _ = session.GetString(title)
2022-11-02 20:10:19 +03:00
session.callFunc("setTitle", title)
}
func (session *sessionData) SetTitleColor(color Color) {
2022-11-02 20:10:19 +03:00
session.callFunc("setTitleColor", color.cssString())
}
func (session *sessionData) RemoteAddr() string {
2022-11-02 21:05:55 +03:00
return session.bridge.remoteAddr()
}
2022-08-18 18:18:36 +03:00
func (session *sessionData) OpenURL(urlStr string) {
if _, err := url.ParseRequestURI(urlStr); err != nil {
ErrorLog(err.Error())
return
}
2022-11-02 20:10:19 +03:00
session.callFunc("openURL", urlStr)
2022-08-18 18:18:36 +03:00
}
func (session *sessionData) ClientItem(key string) (string, bool) {
value, ok := session.clientStorage[key]
return value, ok
}
func (session *sessionData) SetClientItem(key, value string) {
session.clientStorage[key] = value
session.bridge.callFunc("localStorageSet", key, value)
}
func (session *sessionData) RemoveAllClientItems() {
session.clientStorage = map[string]string{}
session.bridge.callFunc("localStorageClear")
}