/*
* 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;
}