添加 本地和内置方式直接加载资源

This commit is contained in:
杨红岩 2023-08-24 17:50:42 +08:00
parent bdad0db0dc
commit 209e13975c
9 changed files with 345 additions and 13 deletions

View File

@ -29,6 +29,7 @@ var (
BrowserWindow = &browserWindow{
browserEvent: &BrowserEvent{},
Config: &browserConfig{
LocalResource: localLoadResource,
WindowProperty: NewWindowProperty(),
},
windowInfo: make(map[int32]IBrowserWindow),

View File

@ -20,7 +20,8 @@ type browserWindowOnEventCallback func(event *BrowserEvent, window IBrowserWindo
// 创建主窗口指定的一些快捷配置属性
type browserConfig struct {
WindowProperty // 部分参数仅在窗口初始化期间生效
WindowProperty // 部分参数仅在窗口初始化期间生效
LocalResource *LocalLoadResource
config *TCefChromiumConfig // 主窗体浏览器配置
browserWindowOnEventCallback browserWindowOnEventCallback // 主窗口初始化回调
}

View File

@ -50,6 +50,7 @@ type BrowserEvent struct {
onBeforeContextMenu chromiumEventOnBeforeContextMenuEx //default can cover
onBeforeResourceLoad chromiumEventOnBeforeResourceLoad //default
onRenderCompMsg chromiumEventOnCompMsg //default windows
onGetResourceHandler chromiumEventOnGetResourceHandlerEx //default
}
// LCLBrowserWindow
@ -287,6 +288,22 @@ func (m *BrowserEvent) SetOnRenderCompMsg(event chromiumEventOnCompMsg) {
}
}
// SetOnGetResourceHandler
// 获取资源处理器,通过该函数自己处理资源获取
// 返回 false 并且设置[本地|内置FS]资源加载时开启并继续执行默认实现
func (m *BrowserEvent) SetOnGetResourceHandler(event chromiumEventOnGetResourceHandlerEx) {
if Args.IsMain() {
if BrowserWindow.Config.LocalResource.enable {
m.onGetResourceHandler = event
} else {
m.ChromiumEvent().SetOnGetResourceHandler(func(sender lcl.IObject, browser *ICefBrowser, frame *ICefFrame, request *ICefRequest) (resourceHandler *ICefResourceHandler) {
resourceHandler, _ = event(sender, browser, frame, request)
return
})
}
}
}
func (m *BrowserEvent) ChromiumEvent() IChromiumEvent {
return m.chromium
}

View File

@ -919,6 +919,18 @@ func (m *LCLBrowserWindow) registerDefaultEvent() {
m.setDraggableRegions()
})
}
if BrowserWindow.Config.LocalResource.enable {
m.Chromium().SetOnGetResourceHandler(func(sender lcl.IObject, browser *ICefBrowser, frame *ICefFrame, request *ICefRequest) (resourceHandler *ICefResourceHandler) {
var flag bool
if bwEvent.onGetResourceHandler != nil {
resourceHandler, flag = bwEvent.onGetResourceHandler(sender, browser, frame, request)
}
if !flag {
resourceHandler = getResourceHandler(browser, frame, request)
}
return
})
}
}
// registerPopupEvent 注册弹出子窗口事件

View File

@ -346,6 +346,18 @@ func (m *ViewsFrameworkBrowserWindow) registerDefaultEvent() {
m.windowComponent.SetDraggableRegions(regions.Regions())
})
}
if BrowserWindow.Config.LocalResource.enable {
m.Chromium().SetOnGetResourceHandler(func(sender lcl.IObject, browser *ICefBrowser, frame *ICefFrame, request *ICefRequest) (resourceHandler *ICefResourceHandler) {
var flag bool
if bwEvent.onGetResourceHandler != nil {
resourceHandler, flag = bwEvent.onGetResourceHandler(sender, browser, frame, request)
}
if !flag {
resourceHandler = getResourceHandler(browser, frame, request)
}
return
})
}
}
// EnableAllDefaultEvent 启用所有默认事件行为

View File

@ -328,3 +328,17 @@ func chromiumOnContextMenuCommand(window IBrowserWindow, browser *ICefBrowser, f
func chromiumOnBeforePopup(sender lcl.IObject, browser *ICefBrowser, frame *ICefFrame, beforePopupInfo *BeforePopupInfo, client *ICefClient, settings *TCefBrowserSettings, extraInfo *ICefDictionaryValue, noJavascriptAccess *bool, result *bool) {
}
// getResourceHandler
// 资源处理器默认实现,使用本地资源加载时开启
func getResourceHandler(browser *ICefBrowser, frame *ICefFrame, request *ICefRequest) (resourceHandler *ICefResourceHandler) {
if localLoadResource.enable {
if source, ok := localLoadResource.checkRequest(request); ok {
resourceHandler = ResourceHandlerRef.New(browser, frame, string(localLoadResource.scheme), request)
resourceHandler.Open(source.open)
resourceHandler.GetResponseHeaders(source.response)
resourceHandler.Read(source.read)
}
}
return
}

View File

@ -65,6 +65,7 @@ type chromiumEventOnFileDialog func(sender lcl.IObject, browser *ICefBrowser, mo
type chromiumEventOnGetAccessibilityHandler func(sender lcl.IObject, accessibilityHandler *ICefAccessibilityHandler)
type chromiumEventOnGetAudioParameters func(sender lcl.IObject, browser *ICefBrowser, params *TCefAudioParameters) bool
type chromiumEventOnGetResourceHandler func(sender lcl.IObject, browser *ICefBrowser, frame *ICefFrame, request *ICefRequest) (resourceHandler *ICefResourceHandler)
type chromiumEventOnGetResourceHandlerEx func(sender lcl.IObject, browser *ICefBrowser, frame *ICefFrame, request *ICefRequest) (resourceHandler *ICefResourceHandler, result bool)
type chromiumEventOnGetResourceRequestHandlerReqCtxHdlr func(sender lcl.IObject, browser *ICefBrowser, frame *ICefFrame, request *ICefRequest, isNavigation, isDownload bool, requestInitiator string) (disableDefaultHandling bool, resourceRequestHandler *ICefResourceRequestHandler)
type chromiumEventOnGetResourceRequestHandlerReqHdlr func(sender lcl.IObject, browser *ICefBrowser, frame *ICefFrame, request *ICefRequest, isNavigation, isDownload bool, requestInitiator string) (disableDefaultHandling bool, resourceRequestHandler *ICefResourceRequestHandler)
type chromiumEventOnGetResourceResponseFilter func(sender lcl.IObject, browser *ICefBrowser, frame *ICefFrame, request *ICefRequest, response *ICefResponse) (responseFilter *ICefResponseFilter)

274
cef/local-load-resource.go Normal file
View File

@ -0,0 +1,274 @@
//----------------------------------------
//
// Copyright © yanghy. All Rights Reserved.
//
// Licensed under Apache License Version 2.0, January 2004
//
// https://www.apache.org/licenses/LICENSE-2.0
//
//----------------------------------------
package cef
import (
"embed"
"fmt"
"github.com/energye/energy/v2/consts"
"github.com/energye/energy/v2/logger"
"io/ioutil"
"os"
"path/filepath"
"strings"
"unsafe"
)
const (
localDomain = "energy" //
sf = ":"
sff = "://"
)
// LocalCustomerScheme 本地资源加载自定义协议
// file, fs
type LocalCustomerScheme string
const (
LocalCSFile LocalCustomerScheme = "file" // 本地目录 file://energy/index.html
LocalCSFS LocalCustomerScheme = "fs" // 内置 fs://energy/index.html
)
// 本地加载资源
var localLoadResource = &LocalLoadResource{
mimeType: make(map[string]string),
sourceCache: make(map[string]*source),
domain: localDomain,
scheme: LocalCSFS,
}
// LocalLoadResource 初始化时设置
// 本地或内置加载资源
// domain: 自定义域名称默认energy, 不能为空
// scheme: 自定义协议默认fs, 可选[file: 在本地目录加载, fs: 在内置exe加载]
// fileRoot: 资源加载根目录, scheme是file时为本地目录(默认当前程序执行目录), scheme是fs时为资源文件夹名
// fs: 内置加载资源对象, scheme是fs时必须填入
type LocalLoadResource struct {
enable bool
enableCache bool
mimeType map[string]string
sourceCache map[string]*source
domain string
scheme LocalCustomerScheme
fileRoot string
fs *embed.FS
}
type source struct {
path string // file source path
fileExt string // file ext
bytes []byte // file source byte
err error // file source read byte error
readPosition int // file source read byte position
status int32 // response code status
statusText string // response status text
mimeType string // response source mime type
resourceType consts.TCefResourceType
}
func init() {
if localLoadResource.enable {
wd, _ := os.Getwd()
localLoadResource.fileRoot = wd
}
}
// SetFileRoot
// 设置资源加载根目录
// scheme是file时为本地目录(默认当前程序执行目录)
// scheme是fs时为资源文件夹名
func (m *LocalLoadResource) SetFileRoot(v string) {
m.fileRoot = v
}
// SetFS
// 设置内置加载资源对象, scheme是fs时必须填入
func (m *LocalLoadResource) SetFS(v *embed.FS) {
m.fs = v
}
// SetEnableCache
// 设置是否启用本地资源缓存到内存, 默认false: 未启用
func (m *LocalLoadResource) SetEnableCache(v bool) {
m.enableCache = v
}
// SetEnable
// 设置是否启用本地资源加载, 默认false: 未启用
func (m *LocalLoadResource) SetEnable(v bool) {
m.enable = v
}
// SetDomain
// 设置本地资源加载自定义域
func (m *LocalLoadResource) SetDomain(domain string) {
if domain == "" {
m.domain = localDomain
return
}
m.domain = domain
}
// SetScheme
// 设置本地资源加载自定义协议
func (m *LocalLoadResource) SetScheme(scheme LocalCustomerScheme) {
if scheme == "" {
m.scheme = LocalCSFS
return
}
m.scheme = scheme
}
// getMimeType
// 获取资源mime type
func (m *LocalLoadResource) getMimeType(extension string) string {
if mimeType, ok := m.mimeType[extension]; ok {
return mimeType
} else {
mimeType = GetMimeType(extension)
m.mimeType[extension] = mimeType
return mimeType
}
}
func (m *LocalLoadResource) ext(path string) string {
for i := len(path) - 1; i >= 0 && !os.IsPathSeparator(path[i]); i-- {
if path[i] == '.' {
return path[i+1:]
}
}
return ""
}
func (m *LocalLoadResource) checkRequest(request *ICefRequest) (*source, bool) {
rt := request.ResourceType()
switch rt {
case consts.RT_XHR:
return nil, false
}
url := request.URL()
logger.Debug("URL:", url)
idx := strings.Index(url, ":")
if idx != -1 {
scheme := url[:idx]
if scheme != string(m.scheme) {
logger.Error("Local load resource, scheme invalid should:", LocalCSFS, "or", LocalCSFile, "source url:", url)
return nil, false
}
idx = strings.Index(url, "://")
if idx == -1 {
logger.Error("Incorrect protocol domain address should: [fs | file]://energy/index.html", "source url:", url)
return nil, false
}
domainPath := url[idx+3:]
idx = strings.Index(domainPath, "/")
if idx == -1 {
logger.Error("Incorrect protocol domain path should: [fs | file]://energy/index.html", "source url:", url)
return nil, false
}
domain := domainPath[:idx]
if domain != m.domain {
logger.Error("Incorrect protocol domain should: [fs | file]://energy/index.html", "source url:", url)
return nil, false
}
idx = strings.Index(domainPath, "/")
if idx == -1 {
logger.Error("Incorrect protocol path should: [fs | file]://energy/index.html", "source url:", url)
return nil, false
}
path := domainPath[idx:]
ext := m.ext(path)
if ext == "" {
logger.Error("Incorrect resources should: file.[ext](MimeType)", "source url:", url)
return nil, false
}
if m.enableCache {
if s, ok := m.sourceCache[path]; ok {
return s, true
} else {
s = &source{path: path, fileExt: ext, mimeType: m.getMimeType(ext), resourceType: rt}
m.sourceCache[path] = s
return s, true
}
}
return &source{path: path, fileExt: ext, mimeType: m.getMimeType(ext), resourceType: rt}, true
}
return nil, false
}
func (m *source) readFile() bool {
if localLoadResource.fileRoot != "" {
if localLoadResource.scheme == LocalCSFile {
data, err := ioutil.ReadFile(filepath.Join(localLoadResource.fileRoot, m.path))
if err == nil {
m.bytes = data
return true
}
logger.Error("ReadFile:", err.Error())
} else if localLoadResource.scheme == LocalCSFS {
data, err := localLoadResource.fs.ReadFile(localLoadResource.fileRoot + m.path)
if err == nil {
m.bytes = data
return true
}
logger.Error("ReadFile:", err.Error())
}
}
return false
}
func (m *source) open(request *ICefRequest, callback *ICefCallback) (handleRequest, result bool) {
m.readPosition = 0
m.status = 404
m.statusText = "Not Found"
if localLoadResource.enableCache {
if m.bytes == nil {
if !m.readFile() {
return true, true
}
}
} else {
if !m.readFile() {
return true, true
}
}
if m.resourceType == consts.RT_MEDIA {
m.status = 206
} else {
m.status = 200
m.statusText = "OK"
}
logger.Debug("open success", m.path)
return true, true
}
func (m *source) response(response *ICefResponse) (responseLength int64, redirectUrl string) {
fmt.Println("response")
response.SetStatus(m.status)
response.SetStatusText(m.statusText)
response.SetMimeType(m.mimeType)
responseLength = int64(len(m.bytes))
return
}
func (m *source) read(dataOut uintptr, bytesToRead int32, callback *ICefResourceReadCallback) (bytesRead int32, result bool) {
fmt.Println("read", dataOut, bytesToRead)
if m.bytes != nil && len(m.bytes) > 0 {
var i int32 = 0 // 默认 0
for i < bytesToRead && m.readPosition < len(m.bytes) {
*(*byte)(unsafe.Pointer(dataOut + uintptr(i))) = m.bytes[m.readPosition]
m.readPosition++
i++
}
return i, i > 0
}
return
}

View File

@ -76,7 +76,7 @@ func (m *ICefResourceRequestHandler) IsOtherEvent() bool {
return m.ct == consts.CtOther
}
func (m *ICefResourceRequestHandler) SetGetCookieAccessFilter(fn getCookieAccessFilter) {
func (m *ICefResourceRequestHandler) SetGetCookieAccessFilter(fn onGetCookieAccessFilter) {
if !m.IsValid() || m.IsOtherEvent() {
return
}
@ -90,7 +90,7 @@ func (m *ICefResourceRequestHandler) SetOnBeforeResourceLoad(fn onBeforeResource
imports.Proc(def.ResourceRequestHandler_OnBeforeResourceLoad).Call(m.Instance(), api.MakeEventDataPtr(fn))
}
func (m *ICefResourceRequestHandler) SetGetResourceHandler(fn getResourceHandler) {
func (m *ICefResourceRequestHandler) SetGetResourceHandler(fn onGetResourceHandler) {
if !m.IsValid() || m.IsOtherEvent() {
return
}
@ -111,7 +111,7 @@ func (m *ICefResourceRequestHandler) SetOnResourceResponse(fn onResourceResponse
imports.Proc(def.ResourceRequestHandler_OnResourceResponse).Call(m.Instance(), api.MakeEventDataPtr(fn))
}
func (m *ICefResourceRequestHandler) SetGetResourceResponseFilter(fn getResourceResponseFilter) {
func (m *ICefResourceRequestHandler) SetGetResourceResponseFilter(fn onGetResourceResponseFilter) {
if !m.IsValid() || m.IsOtherEvent() {
return
}
@ -134,12 +134,12 @@ func (m *ICefResourceRequestHandler) SetOnProtocolExecution(fn onProtocolExecuti
// ************************** events ************************** //
type getCookieAccessFilter func(browser *ICefBrowser, frame *ICefFrame, request *ICefRequest) (filter *ICefCookieAccessFilter)
type onGetCookieAccessFilter func(browser *ICefBrowser, frame *ICefFrame, request *ICefRequest) (filter *ICefCookieAccessFilter)
type onBeforeResourceLoad func(browser *ICefBrowser, frame *ICefFrame, request *ICefRequest, callback *ICefCallback) consts.TCefReturnValue
type getResourceHandler func(browser *ICefBrowser, frame *ICefFrame, request *ICefRequest) (resourceHandler *ICefResourceHandler)
type onGetResourceHandler func(browser *ICefBrowser, frame *ICefFrame, request *ICefRequest) (resourceHandler *ICefResourceHandler)
type onResourceRedirect func(browser *ICefBrowser, frame *ICefFrame, request *ICefRequest, response *ICefResponse) (newUrl string)
type onResourceResponse func(browser *ICefBrowser, frame *ICefFrame, request *ICefRequest, response *ICefResponse) bool
type getResourceResponseFilter func(browser *ICefBrowser, frame *ICefFrame, request *ICefRequest, response *ICefResponse) (responseFilter *ICefResponseFilter)
type onGetResourceResponseFilter func(browser *ICefBrowser, frame *ICefFrame, request *ICefRequest, response *ICefResponse) (responseFilter *ICefResponseFilter)
type onResourceLoadComplete func(browser *ICefBrowser, frame *ICefFrame, request *ICefRequest, status consts.TCefUrlRequestStatus, receivedContentLength int64)
type onProtocolExecution func(browser *ICefBrowser, frame *ICefFrame, request *ICefRequest) (allowOsExecution bool)
@ -149,12 +149,12 @@ func init() {
return unsafe.Pointer(getVal(i))
}
switch fn.(type) {
case getCookieAccessFilter:
case onGetCookieAccessFilter:
browse := &ICefBrowser{instance: getPtr(0)}
frame := &ICefFrame{instance: getPtr(1)}
request := &ICefRequest{instance: getPtr(2)}
filterPtr := (*uintptr)(getPtr(3))
filter := fn.(getCookieAccessFilter)(browse, frame, request)
filter := fn.(onGetCookieAccessFilter)(browse, frame, request)
if filter != nil && filter.IsValid() {
*filterPtr = filter.Instance()
}
@ -166,12 +166,12 @@ func init() {
returnValuePtr := (*consts.TCefReturnValue)(getPtr(4))
returnValue := fn.(onBeforeResourceLoad)(browse, frame, request, callback)
*returnValuePtr = returnValue
case getResourceHandler:
case onGetResourceHandler:
browse := &ICefBrowser{instance: getPtr(0)}
frame := &ICefFrame{instance: getPtr(1)}
request := &ICefRequest{instance: getPtr(2)}
resourceHandlerPtr := (*uintptr)(getPtr(3))
resourceHandler := fn.(getResourceHandler)(browse, frame, request)
resourceHandler := fn.(onGetResourceHandler)(browse, frame, request)
if resourceHandler != nil && resourceHandler.IsValid() {
*resourceHandlerPtr = resourceHandler.Instance()
} else {
@ -192,13 +192,13 @@ func init() {
response := &ICefResponse{instance: getPtr(3)}
result := (*bool)(getPtr(4))
*result = fn.(onResourceResponse)(browse, frame, request, response)
case getResourceResponseFilter:
case onGetResourceResponseFilter:
browse := &ICefBrowser{instance: getPtr(0)}
frame := &ICefFrame{instance: getPtr(1)}
request := &ICefRequest{instance: getPtr(2)}
response := &ICefResponse{instance: getPtr(3)}
responseFilterPtr := (*uintptr)(getPtr(4))
responseFilter := fn.(getResourceResponseFilter)(browse, frame, request, response)
responseFilter := fn.(onGetResourceResponseFilter)(browse, frame, request, response)
if responseFilter != nil && responseFilter.IsValid() {
*responseFilterPtr = responseFilter.Instance()
} else {