mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-09-15 07:19:20 -04:00
178 lines
7.2 KiB
C
178 lines
7.2 KiB
C
/* Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
|
*
|
|
* Francis Bouvier <francis@lightpanda.io>
|
|
* Pierre Tachoire <pierre@lightpanda.io>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/* Lightpanda embedded as a C library. Implemented by src/c_api.zig, which
|
|
* must stay in sync with this header.
|
|
*
|
|
* Threading contract (v1):
|
|
* - lp_init may be called ONCE per process. After lp_shutdown the library
|
|
* cannot be initialized again (V8's platform is not re-initializable).
|
|
* - Every call on the lp_browser handle and on all of its sessions must
|
|
* come from the thread that called lp_init. Sessions are cheap and
|
|
* isolated; parallelism means multiple processes.
|
|
* - The library is not fork-safe after lp_init.
|
|
*
|
|
* Logging goes to stderr (level: warnings and errors in release builds).
|
|
*
|
|
* Linking: `make lib` builds liblightpanda.so (zig-out/lib) and
|
|
* installs this header (zig-out/include) plus a pkg-config file:
|
|
* cc app.c $(PKG_CONFIG_PATH=zig-out/lib/pkgconfig pkg-config --cflags --libs lightpanda)
|
|
* or by hand:
|
|
* cc app.c -Izig-out/include -Lzig-out/lib -llightpanda
|
|
* The library resolves its dependencies internally and exports only lp_*
|
|
* symbols (safe next to a host's own OpenSSL/curl/sqlite), and it is
|
|
* dlopen-able for FFI (Python ctypes etc.).
|
|
*/
|
|
|
|
#ifndef LIGHTPANDA_H
|
|
#define LIGHTPANDA_H
|
|
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
typedef struct lp_browser lp_browser;
|
|
typedef struct lp_session lp_session;
|
|
|
|
typedef enum lp_status {
|
|
LP_OK = 0,
|
|
LP_ERR_INVALID_PARAMS = 1,
|
|
/* No page loaded yet; call goto (or pass a url) first. */
|
|
LP_ERR_FRAME_NOT_LOADED = 2,
|
|
LP_ERR_NODE_NOT_FOUND = 3,
|
|
LP_ERR_NAVIGATION_FAILED = 4,
|
|
/* The cancel hook (lp_session_set_cancel_hook) returned true. */
|
|
LP_ERR_CANCELLED = 5,
|
|
LP_ERR_TIMEOUT = 6,
|
|
LP_ERR_OUT_OF_MEMORY = 7,
|
|
LP_ERR_INTERNAL = 8,
|
|
/* API misuse: double init, use after shutdown, NULL handle. */
|
|
LP_ERR_MISUSE = 9
|
|
} lp_status;
|
|
|
|
/* Output of lp_fetch and lp_call. text is NUL-terminated (len excludes the
|
|
* NUL), read-only, and owned by the library: it stays valid until the next
|
|
* lp_call on the same session (for lp_fetch: the next lp_fetch on the same
|
|
* browser), or until that session/browser is torn down. Copy it out to keep
|
|
* it longer — including before handing it to another thread. lp_session_pump
|
|
* does not invalidate it. is_error signals an in-band page-level failure
|
|
* (e.g. a JS throw inside evaluate/extract) whose message is in text; the
|
|
* call itself still returns LP_OK. */
|
|
typedef struct lp_result {
|
|
const char *text;
|
|
size_t len;
|
|
bool is_error;
|
|
} lp_result;
|
|
|
|
/* Zero-initialize for defaults: no proxy, default user agent, no HTTP
|
|
* cache, 5s HTTP timeout, 30s JS watchdog, telemetry off. */
|
|
typedef struct lp_options {
|
|
const char *user_agent; /* NULL: default ("Lightpanda/1.0") */
|
|
const char *http_proxy; /* NULL: none */
|
|
const char *http_cache_dir; /* NULL: no persistent HTTP cache */
|
|
uint32_t http_timeout_ms; /* 0: default (5000) */
|
|
int32_t watchdog_ms; /* 0: default (30000), <0: disabled */
|
|
bool enable_telemetry; /* false: no telemetry */
|
|
} lp_options;
|
|
|
|
typedef enum lp_format {
|
|
LP_FORMAT_HTML = 0,
|
|
LP_FORMAT_MARKDOWN = 1,
|
|
LP_FORMAT_TREE_JSON = 2, /* semantic (accessibility-style) tree, JSON */
|
|
LP_FORMAT_TREE_TEXT = 3 /* semantic tree, indented text */
|
|
} lp_format;
|
|
|
|
typedef enum lp_wait_until {
|
|
LP_WAIT_DEFAULT = 0, /* page fully settled ("done"), or the selector */
|
|
LP_WAIT_LOAD = 1,
|
|
LP_WAIT_DOMCONTENTLOADED = 2,
|
|
LP_WAIT_NETWORKALMOSTIDLE = 3,
|
|
LP_WAIT_NETWORKIDLE = 4,
|
|
LP_WAIT_DONE = 5
|
|
} lp_wait_until;
|
|
|
|
/* Zero-initialize for defaults: HTML after the page settles, 5s budget. */
|
|
typedef struct lp_fetch_opts {
|
|
int format; /* lp_format */
|
|
uint32_t wait_ms; /* 0: default (5000) */
|
|
int wait_until; /* lp_wait_until */
|
|
const char *wait_selector; /* NULL: none; else wait for this CSS selector */
|
|
} lp_fetch_opts;
|
|
|
|
/* Initialize the library. opts may be NULL (all defaults). On LP_OK,
|
|
* *out is the process-wide browser handle. */
|
|
lp_status lp_init(const lp_options *opts, lp_browser **out);
|
|
|
|
/* Tear down the handle, closing any remaining sessions. Terminal — see the
|
|
* threading contract above. */
|
|
void lp_shutdown(lp_browser *browser);
|
|
|
|
/* Load url in a throwaway session, run its JavaScript, and return the page
|
|
* serialized per opts (NULL: all defaults). "curl that runs JavaScript".
|
|
* Every call gets a fresh session (cookies, storage, pages); the underlying
|
|
* browser is created on first use and reused, so looping lp_fetch is cheap. */
|
|
lp_status lp_fetch(lp_browser *browser, const char *url,
|
|
const lp_fetch_opts *opts, lp_result *out);
|
|
|
|
/* Create an isolated browsing session: its own page, cookies, JS heap. */
|
|
lp_status lp_session_new(lp_browser *browser, lp_session **out);
|
|
|
|
/* Close a session. The pointer is invalid afterwards. Sessions still open
|
|
* at lp_shutdown are closed then. */
|
|
void lp_session_close(lp_session *session);
|
|
|
|
/* Run one browser tool (goto, markdown, html, extract, tree, click, fill,
|
|
* waitForSelector, evaluate, ...) against the session. args_json is the
|
|
* tool's argument object as a JSON string (NULL: no arguments); the tool
|
|
* names and their JSON schemas are enumerated by lp_tools_json. Tools that
|
|
* read the page accept a "url" argument to navigate first, so
|
|
* lp_call(s, "markdown", "{\"url\":\"https://example.com\"}", &r)
|
|
* is a complete one-call scrape. */
|
|
lp_status lp_call(lp_session *session, const char *tool,
|
|
const char *args_json, lp_result *out);
|
|
|
|
/* Pump background work (timers, in-flight fetches) once; returns how many
|
|
* milliseconds the caller may sleep before pumping again. Only needed when
|
|
* idling between calls — every call waits for its own completion. */
|
|
uint32_t lp_session_pump(lp_session *session);
|
|
|
|
/* Install (cb != NULL) or clear (cb == NULL) a cancellation probe, polled
|
|
* on the session's thread during blocking waits; returning true fails the
|
|
* in-flight call with LP_ERR_CANCELLED. The probe may read state set from
|
|
* other threads (e.g. an atomic flag set by a signal handler). */
|
|
void lp_session_set_cancel_hook(lp_session *session,
|
|
bool (*cb)(void *), void *ctx);
|
|
|
|
/* JSON array of every tool lp_call accepts:
|
|
* [{"name", "description", "inputSchema"}, ...]. Static — do not free. */
|
|
const char *lp_tools_json(void);
|
|
|
|
/* Library version string. Static — do not free. */
|
|
const char *lp_version(void);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* LIGHTPANDA_H */
|