--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/webview.m Mon Nov 10 21:52:51 2025 +0100 @@ -0,0 +1,273 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2025 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#import "webview.h" +#import "Container.h" + +UIWIDGET ui_webview_create(UiObject *obj, UiWebviewArgs *args) { + UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_GENERIC); + + WKWebView *webview = [[WKWebView alloc]init]; + + UiLayout layout = UI_ARGS2LAYOUT(args); + ui_container_add(obj, webview, &layout); + + if(var) { + UiGeneric *value = var->value; + value->get = ui_webview_get; + value->get_type = ui_webview_get_type; + value->set = ui_webview_set; + value->destroy = ui_webview_destroy; + value->obj = (__bridge void*)webview; + if(value->value) { + ui_webview_set(value, value->value, UI_WEBVIEW_OBJECT_TYPE); + } else { + UiWebViewData *data = malloc(sizeof(UiWebViewData)); + memset(data, 0, sizeof(UiWebViewData)); + data->webview = (__bridge void*)webview; + data->javascript = TRUE; + data->zoom = 1; + value->value = value; + } + } + + return (__bridge void*)webview; +} + +UiWebViewData* ui_webview_data_clone(UiWebViewData *data) { + UiWebViewData *newdata = malloc(sizeof(UiWebViewData)); + memset(newdata, 0, sizeof(UiWebViewData)); + newdata->zoom = 1; + newdata->javascript = TRUE; + + if(data) { + newdata->uri = data->uri ? strdup(data->uri) : NULL; + newdata->mimetype = data->mimetype ? strdup(data->mimetype) : NULL; + newdata->encoding = data->encoding ? strdup(data->encoding) : NULL; + newdata->type = data->type; + newdata->javascript = data->javascript; + newdata->zoom = data->zoom; + if(data->content && data->contentlength > 0) { + newdata->content = malloc(data->contentlength + 1); + memcpy(newdata->content, data->content, data->contentlength); + newdata->content[data->contentlength] = 0; + newdata->contentlength = data->contentlength; + } + } + + return newdata; +} + +void ui_webview_data_free(UiWebViewData *data) { + if(!data) { + return; + } + free(data->uri); + free(data->mimetype); + free(data->encoding); + free(data->content); + free(data); +} + +void* ui_webview_get(UiGeneric *g) { + UiWebViewData *data = g->value; + WKWebView *webview = (__bridge WKWebView*)g->obj; + + if(data->type == WEBVIEW_CONTENT_URL) { + (void)ui_webview_get_uri(g); // this updates data->uri + } + data->zoom = webview.pageZoom; + + return ui_webview_data_clone(g->value); +} + +const char* ui_webview_get_type(UiGeneric *g) { + return UI_WEBVIEW_OBJECT_TYPE; +} + +int ui_webview_set(UiGeneric *g, void *data, const char *type) { + if(!data || !type) { + return 1; + } + if(strcmp(type, UI_WEBVIEW_OBJECT_TYPE)) { + return 1; + } + ui_webview_data_free(g->value); + g->value = ui_webview_data_clone(data); + + WKWebView *webview = (__bridge WKWebView*)g->obj; + UiWebViewData *webd = data; + if(webd->type == WEBVIEW_CONTENT_URL) { + const char *uri = webd->uri ? webd->uri : "about:blank"; + NSURL *url = [NSURL URLWithString:[[NSString alloc] initWithUTF8String:uri]]; + if(url) { + NSURLRequest *req = [NSURLRequest requestWithURL:url]; + if(req) { + [webview loadRequest:req]; + } + } + } else { + NSString *mimetype = [[NSString alloc]initWithUTF8String: webd->mimetype ? webd->mimetype : "text/plain"]; + NSString *encoding = [[NSString alloc]initWithUTF8String: webd->encoding ? webd->encoding : "UTF-8"]; + NSString *urlStr = [[NSString alloc]initWithUTF8String: webd->uri ? webd->uri : "file:///"]; + NSURL *url = [NSURL URLWithString:urlStr]; + NSData *content = [NSData dataWithBytes:webd->content length:webd->contentlength]; + if(!url) { + url = [NSURL URLWithString:@"about:blank"]; + } + [webview loadData:content MIMEType:mimetype characterEncodingName:encoding baseURL:url]; + } + + webview.pageZoom = webd->zoom; + + return 1; +} + +void ui_webview_destroy(UiGeneric *g) { + ui_webview_data_free(g->value); + g->value = NULL; +} + + +void ui_webview_load_url(UiGeneric *g, const char *url) { + WKWebView *webview = (__bridge WKWebView*)g->obj; + UiWebViewData *data = g->value; + data->type = WEBVIEW_CONTENT_URL; + + if(!url) { + url = "about:blank"; + } + + NSURL *nsurl = [NSURL URLWithString:[[NSString alloc] initWithUTF8String:url]]; + if(nsurl) { + NSURLRequest *req = [NSURLRequest requestWithURL:nsurl]; + if(req) { + [webview loadRequest:req]; + } + } +} + +void ui_webview_load_content( + UiGeneric *g, + const char *uri, + const char *content, + size_t contentlength, + const char *mimetype, + const char *encoding) +{ + UiWebViewData *data = g->value; + WKWebView *webview = (__bridge WKWebView*)g->obj; + + data->type = WEBVIEW_CONTENT_CONTENT; + + free(data->uri); + data->uri = NULL; + free(data->mimetype); + free(data->encoding); + free(data->content); + data->type = WEBVIEW_CONTENT_URL; + + data->content = malloc(contentlength+1); + memcpy(data->content, content, contentlength); + data->content[contentlength] = 0; + + if(!mimetype) { + mimetype = "text/plain"; + } + if(!encoding) { + encoding = "UTF-8"; + } + + data->mimetype = strdup(mimetype); + data->encoding = strdup(encoding); + + NSString *mtype = [[NSString alloc]initWithUTF8String:mimetype]; + NSString *enc = [[NSString alloc]initWithUTF8String:encoding]; + NSData *ct = [NSData dataWithBytes:content length:contentlength]; + NSURL *url; + if(uri) { + NSString *uriStr = [[NSString alloc]initWithUTF8String:uri]; + url = [NSURL URLWithString:uriStr]; + } else { + url = [NSURL URLWithString:@"file:///"]; + } + [webview loadData:ct MIMEType:mtype characterEncodingName:enc baseURL:url]; +} + + +void ui_webview_reload(UiGeneric *g) { + WKWebView *webview = (__bridge WKWebView*)g->obj; + [webview reload]; +} + +UiBool ui_webview_can_go_back(UiGeneric *g) { + return FALSE; +} + +UiBool ui_webview_can_go_forward(UiGeneric *g) { + return FALSE; +} + +void ui_webview_go_back(UiGeneric *g) { + +} + +void ui_webview_go_forward(UiGeneric *g) { + +} + +const char * ui_webview_get_uri(UiGeneric *g) { + UiWebViewData *data = g->value; + WKWebView *webview = (__bridge WKWebView*)g->obj; + + free(data->uri); + data->uri = NULL; + + NSURL *url = webview.URL; + if(url) { + NSString *s = [url absoluteString]; + if(s) { + data->uri = strdup(s.UTF8String); + } + } + return data->uri; +} + +void ui_webview_enable_javascript(UiGeneric *g, UiBool enable) { + // unsupported +} + +void ui_webview_set_zoom(UiGeneric *g, double zoom) { + WKWebView *webview = (__bridge WKWebView*)g->obj; + webview.pageZoom = zoom; +} + +double ui_webview_get_zoom(UiGeneric *g) { + WKWebView *webview = (__bridge WKWebView*)g->obj; + return webview.pageZoom; +}