Skip to content

Commit

Permalink
Add .loadUrl, support for headers, support for userAgent (#75)
Browse files Browse the repository at this point in the history
* Add the ability to load a URL

* Add the ability to set a user agent

* Prepare 0.0.17 (0.1.14)
  • Loading branch information
zephraph authored Oct 3, 2024
1 parent 20643ba commit 676cbbd
Show file tree
Hide file tree
Showing 11 changed files with 209 additions and 16 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## 0.0.17 (binary 0.1.14) -- 2024-10-02

- Add `webview.loadUrl` to load a new URL after the webview is initialized
- Add the ability to specify headers when instantiating a webview with a URL
- Add `userAgent` to `WebViewOptions` to construct a new webview with a custom user agent.

## 0.0.16 (binary 0.1.13) -- 2024-09-29

- Add `initializationScript` to `WebViewOptions`. Allows providing JS that runs before `onLoad`.
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "deno-webview"
version = "0.1.13"
version = "0.1.14"
edition = "2021"

[profile.release]
Expand Down
2 changes: 1 addition & 1 deletion deno.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@justbe/webview",
"exports": "./src/lib.ts",
"version": "0.0.16",
"version": "0.0.17",
"tasks": {
"dev": "deno run --watch main.ts",
"gen": "deno task gen:rust && deno task gen:deno",
Expand Down
25 changes: 25 additions & 0 deletions examples/load-url.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { createWebView } from "../src/lib.ts";

using webview = await createWebView({
title: "Load Url Example",
url: "https://example.com",
headers: {
"Content-Type": "text/html",
},
userAgent: "curl/7.81.0",
devtools: true,
});

webview.on("started", async () => {
await webview.openDevTools();
await sleep(2000);
await webview.loadUrl("https://val.town/", {
"Content-Type": "text/html",
});
});

await webview.waitUntilClosed();

function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
18 changes: 18 additions & 0 deletions schemas/WebViewOptions.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 34 additions & 0 deletions schemas/WebViewRequest.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 31 additions & 10 deletions scripts/generate-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type NodeIR =
value: NodeIR;
}[];
}
| { type: "record"; valueType: string }
| { type: "boolean"; optional?: boolean }
| { type: "string"; optional?: boolean }
| { type: "literal"; value: string }
Expand Down Expand Up @@ -170,20 +171,34 @@ function jsonSchemaToIR(schema: JSONSchema): DocIR {
},
)
.with(
{ type: "object" },
() => ({
type: "object" as const,
properties: Object.entries(node.properties ?? {}).map((
[key, value],
) => {
return ({
{ type: P.union("object", P.when(isOptionalType("object"))) },
() => {
if (Object.keys(node.properties ?? {}).length === 0) {
if (
typeof node.additionalProperties === "object" &&
"type" in node.additionalProperties
) {
return {
type: "record" as const,
valueType: typeof node.additionalProperties.type === "string"
? node.additionalProperties.type
: "unknown",
};
}
return { type: "record" as const, valueType: "unknown" };
}
return ({
type: "object" as const,
properties: Object.entries(node.properties ?? {}).map((
[key, value],
) => ({
key,
required: node.required?.includes(key) ?? false,
description: (value as JSONSchema).description,
value: nodeToIR(value as JSONSchema),
});
}),
}),
})),
});
},
)
.otherwise(() => ({ type: "unknown" }));
};
Expand Down Expand Up @@ -215,6 +230,9 @@ function generateTypes(ir: DocIR) {
.with({ type: "boolean" }, () => w("boolean"))
.with({ type: "string" }, () => w("string"))
.with({ type: "literal" }, (node) => w(`"${node.value}"`))
.with({ type: "record" }, (node) => {
w(`Record<string, ${node.valueType}>`);
})
.with({ type: "object" }, (node) => {
wn("{");
for (const { key, required, description, value } of node.properties) {
Expand Down Expand Up @@ -300,6 +318,9 @@ function generateZodSchema(ir: DocIR) {
{ type: "literal" },
(node) => w(`z.literal("${node.value}")`),
)
.with({ type: "record" }, (node) => {
w(`z.record(z.string(), z.${node.valueType}())`);
})
.with({ type: "object" }, (node) => {
w("z.object({");
for (const { key, required, value } of node.properties) {
Expand Down
10 changes: 9 additions & 1 deletion src/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export type { WebViewOptions } from "./schemas.ts";

// Should match the cargo package version
/** The version of the webview binary that's expected */
export const BIN_VERSION = "0.1.13";
export const BIN_VERSION = "0.1.14";

type WebViewNotification = Extract<
WebViewMessage,
Expand Down Expand Up @@ -463,6 +463,14 @@ export class WebView implements Disposable {
return returnAck(result);
}

/**
* Loads a URL in the webview.
*/
async loadUrl(url: string, headers?: Record<string, string>): Promise<void> {
const result = await this.#send({ $type: "loadUrl", url, headers });
return returnAck(result);
}

/**
* Destroys the webview and cleans up resources.
*
Expand Down
60 changes: 59 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::HashMap;
use std::env;
use std::io::{self, BufRead, Write};
use std::str::FromStr;
use std::sync::mpsc;
use std::sync::Arc;

Expand All @@ -16,6 +18,7 @@ use tao::{
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
use wry::http::header::{HeaderName, HeaderValue};
use wry::http::Response as HttpResponse;
use wry::WebViewBuilder;

Expand Down Expand Up @@ -92,6 +95,9 @@ struct WebViewOptions {
#[serde(default)]
/// Run JavaScript code when loading new pages. When the webview loads a new page, this code will be executed. It is guaranteed that the code is executed before window.onload.
initialization_script: Option<String>,
/// Sets the user agent to use when loading pages.
#[serde(default)]
user_agent: Option<String>,
}

fn default_true() -> bool {
Expand All @@ -105,6 +111,8 @@ enum WebViewTarget {
Url {
/// Url to load in the webview. Note: Don't use data URLs here, as they are not supported. Use the `html` field instead.
url: String,
/// Optional headers to send with the request.
headers: Option<HashMap<String, String>>,
},
Html {
/// Html to load in the webview.
Expand Down Expand Up @@ -232,6 +240,14 @@ enum Request {
/// If not specified, the origin will be set to the value of the `origin` field when the webview was created.
origin: Option<String>,
},
LoadUrl {
/// The id of the request.
id: String,
/// URL to load in the webview.
url: String,
/// Optional headers to send with the request.
headers: Option<HashMap<String, String>>,
},
}

/// Responses from the webview to the client.
Expand Down Expand Up @@ -310,7 +326,22 @@ fn main() -> wry::Result<()> {

let html_cell_init = html_cell.clone();
let mut webview_builder = match webview_options.target {
WebViewTarget::Url { url } => WebViewBuilder::new(&window).with_url(url),
WebViewTarget::Url { url, headers } => {
let mut webview_builder = WebViewBuilder::new(&window).with_url(url);
if let Some(headers) = headers {
let headers = headers
.into_iter()
.map(|(k, v)| {
(
HeaderName::from_str(&k).unwrap(),
HeaderValue::from_str(&v).unwrap(),
)
})
.collect();
webview_builder = webview_builder.with_headers(headers);
}
webview_builder
}
WebViewTarget::Html { html, origin } => {
origin_cell.replace(origin.clone());
html_cell.replace(html);
Expand Down Expand Up @@ -346,6 +377,9 @@ fn main() -> wry::Result<()> {
webview_builder =
webview_builder.with_initialization_script(initialization_script.as_str());
}
if let Some(user_agent) = webview_options.user_agent {
webview_builder = webview_builder.with_user_agent(user_agent.as_str());
}
let webview = webview_builder.build()?;

let notify_tx = tx.clone();
Expand Down Expand Up @@ -527,6 +561,30 @@ fn main() -> wry::Result<()> {
.unwrap();
res(Response::Ack { id });
}
Request::LoadUrl { id, url, headers } => {
let resp = match headers {
Some(headers) => {
let headers = headers
.into_iter()
.map(|(k, v)| {
(
HeaderName::from_str(&k).unwrap(),
HeaderValue::from_str(&v).unwrap(),
)
})
.collect();
webview.load_url_with_headers(&url, headers)
}
None => webview.load_url(&url),
};
match resp {
Ok(_) => res(Response::Ack { id }),
Err(err) => res(Response::Err {
id,
message: err.to_string(),
}),
}
}
}
}
}
Expand Down
Loading

0 comments on commit 676cbbd

Please sign in to comment.