fix: ports - netsurf renders pages (mmap, resources, error loop)
Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NQRTGoYgnZQsh7uCh6Jsxm
This commit is contained in:
@@ -86,6 +86,12 @@ NETSURF_ARGS := \
|
|||||||
# it references.
|
# it references.
|
||||||
COMPAT_LIB := $(CURDIR)/compat/libnscompat.a
|
COMPAT_LIB := $(CURDIR)/compat/libnscompat.a
|
||||||
|
|
||||||
|
# NetSurf formats floats (page load time in the status bar, plot coordinates).
|
||||||
|
# The Montauk libc keeps %e/%f/%g in a separate archive member reached by a
|
||||||
|
# WEAK reference, and a weak undefined ref does not extract a member -- without
|
||||||
|
# this the status bar reads "Done (%fs)". See programs/lib/libc/printf_float.c.
|
||||||
|
NS_LDFLAGS := $(COMPAT_LIB) -Wl,-u,_pf_putfloat
|
||||||
|
|
||||||
$(COMPAT_LIB): compat/iconv.c compat/iconv.h
|
$(COMPAT_LIB): compat/iconv.c compat/iconv.h
|
||||||
$(CC) $(NETSURF_CFLAGS) -std=gnu99 -c compat/iconv.c -o compat/iconv.o
|
$(CC) $(NETSURF_CFLAGS) -std=gnu99 -c compat/iconv.c -o compat/iconv.o
|
||||||
$(AR) rcs $@ compat/iconv.o
|
$(AR) rcs $@ compat/iconv.o
|
||||||
@@ -96,7 +102,7 @@ all: netsurf-build
|
|||||||
|
|
||||||
netsurf-build: $(COMPAT_LIB)
|
netsurf-build: $(COMPAT_LIB)
|
||||||
@$(call ns_patch,$(UPSTREAM))
|
@$(call ns_patch,$(UPSTREAM))
|
||||||
CFLAGS="$(NETSURF_CFLAGS)" LDFLAGS="$(COMPAT_LIB)" $(NS_ENV) \
|
CFLAGS="$(NETSURF_CFLAGS)" LDFLAGS="$(NS_LDFLAGS)" $(NS_ENV) \
|
||||||
$(NS_MAKE) -C $(UPSTREAM) $(NETSURF_ARGS)
|
$(NS_MAKE) -C $(UPSTREAM) $(NETSURF_ARGS)
|
||||||
@test -f $(NS_BUILD_OUT) || { echo "netsurf: expected $(NS_BUILD_OUT)" >&2; exit 1; }
|
@test -f $(NS_BUILD_OUT) || { echo "netsurf: expected $(NS_BUILD_OUT)" >&2; exit 1; }
|
||||||
@echo "Built: $(NS_BUILD_OUT) ($$(stat -c%s $(NS_BUILD_OUT)) bytes)"
|
@echo "Built: $(NS_BUILD_OUT) ($$(stat -c%s $(NS_BUILD_OUT)) bytes)"
|
||||||
@@ -108,7 +114,12 @@ netsurf-build: $(COMPAT_LIB)
|
|||||||
install: netsurf-build
|
install: netsurf-build
|
||||||
mkdir -p $(NS_INSTALL)/netsurf
|
mkdir -p $(NS_INSTALL)/netsurf
|
||||||
cp $(NS_BUILD_OUT) $(NS_INSTALL)/nsmonkey.elf
|
cp $(NS_BUILD_OUT) $(NS_INSTALL)/nsmonkey.elf
|
||||||
cp -r $(UPSTREAM)/frontends/monkey/res/. $(NS_INSTALL)/netsurf/
|
# -L, not -r alone: nearly everything in a NetSurf frontend res/ dir is a
|
||||||
|
# symlink into ../../../resources/. Copied as links they dangle outside
|
||||||
|
# the installed tree, and the ramdisk turns a symlink into a ZERO-LENGTH
|
||||||
|
# FILE (Fs/Ramdisk.cpp only handles typeflag '5'), so default.css and
|
||||||
|
# welcome.html arrive empty and every page lays out to nothing.
|
||||||
|
cp -rL $(UPSTREAM)/frontends/monkey/res/. $(NS_INSTALL)/netsurf/
|
||||||
@echo "installed: 0:/os/nsmonkey.elf + 0:/os/netsurf/ resources"
|
@echo "installed: 0:/os/nsmonkey.elf + 0:/os/netsurf/ resources"
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
|
|||||||
@@ -11,19 +11,20 @@
|
|||||||
* NETSURF_USE_CURL=NO no fetcher registers a descriptor, so the only fd that
|
* NETSURF_USE_CURL=NO no fetcher registers a descriptor, so the only fd that
|
||||||
* ever appears is the monkey frontend's stdin (fd 0).
|
* ever appears is the monkey frontend's stdin (fd 0).
|
||||||
*
|
*
|
||||||
* That makes a useful shim possible:
|
* That makes a useful shim possible: fd 0 is answered from the libc's
|
||||||
|
* montauk_stdin_ready(), which reports whether a COMPLETE line is buffered.
|
||||||
|
* That is the readiness question monkey is really asking -- it goes on to call
|
||||||
|
* fgets(), so reporting "readable" on a half-typed line would park the whole
|
||||||
|
* browser inside stdio until Enter. With no line pending we sleep out the
|
||||||
|
* caller's timeout and return 0, which lets the core's scheduled callbacks run
|
||||||
|
* to completion: a fetch takes many main-loop iterations, and under the old
|
||||||
|
* always-ready shim it got exactly one per line typed.
|
||||||
*
|
*
|
||||||
* - if any read descriptor is requested, report them ready immediately and
|
* LIMITATION: only fd 0 has a readiness source. Any other read descriptor is
|
||||||
* let the caller block in read(). monkey is a line-driven REPL, so
|
* still reported ready unconditionally, and write/except sets are always
|
||||||
* blocking on stdin is what it wants anyway.
|
* empty. With NETSURF_USE_CURL=NO no fetcher registers a descriptor, so fd 0
|
||||||
* - otherwise sleep for the timeout so an idle loop does not spin.
|
* is the only one that ever appears. When fetch_montauk lands it will register
|
||||||
*
|
* descriptors here and this needs a genuine waitset-backed implementation.
|
||||||
* LIMITATION: because a requested read fd is always reported ready, scheduled
|
|
||||||
* callbacks do not fire while the process sits idle waiting for a command.
|
|
||||||
* That is fine for driving monkey by hand or by script, and it is wrong for
|
|
||||||
* anything with real network activity. When fetch_montauk lands it will
|
|
||||||
* register descriptors here and this needs replacing with a genuine
|
|
||||||
* waitset-backed implementation.
|
|
||||||
*/
|
*/
|
||||||
#ifndef _MONTAUK_COMPAT_SYS_SELECT_H_
|
#ifndef _MONTAUK_COMPAT_SYS_SELECT_H_
|
||||||
#define _MONTAUK_COMPAT_SYS_SELECT_H_
|
#define _MONTAUK_COMPAT_SYS_SELECT_H_
|
||||||
@@ -31,6 +32,7 @@
|
|||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#define FD_SETSIZE 64
|
#define FD_SETSIZE 64
|
||||||
|
|
||||||
@@ -80,6 +82,7 @@ static inline int montauk_fdset_count(const fd_set *s, int nfds)
|
|||||||
static inline int select(int nfds, fd_set *readfds, fd_set *writefds,
|
static inline int select(int nfds, fd_set *readfds, fd_set *writefds,
|
||||||
fd_set *exceptfds, struct timeval *timeout)
|
fd_set *exceptfds, struct timeval *timeout)
|
||||||
{
|
{
|
||||||
|
int stdin_wanted;
|
||||||
int ready;
|
int ready;
|
||||||
|
|
||||||
if (writefds != NULL)
|
if (writefds != NULL)
|
||||||
@@ -87,21 +90,57 @@ static inline int select(int nfds, fd_set *readfds, fd_set *writefds,
|
|||||||
if (exceptfds != NULL)
|
if (exceptfds != NULL)
|
||||||
FD_ZERO(exceptfds);
|
FD_ZERO(exceptfds);
|
||||||
|
|
||||||
if (montauk_fdset_empty(readfds) == 0) {
|
stdin_wanted = (readfds != NULL) && (nfds > 0) && FD_ISSET(0, readfds);
|
||||||
/* leave readfds as-is: every requested fd is "ready" */
|
if (stdin_wanted) {
|
||||||
return montauk_fdset_count(readfds, nfds);
|
FD_CLR(0, readfds);
|
||||||
|
if (montauk_stdin_ready()) {
|
||||||
|
FD_SET(0, readfds);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ready = 0;
|
/*
|
||||||
|
* Descriptors other than stdin have no readiness source here, so they
|
||||||
|
* keep the old unconditional-ready answer.
|
||||||
|
*/
|
||||||
|
ready = montauk_fdset_count(readfds, nfds);
|
||||||
|
if (ready > 0)
|
||||||
|
return ready;
|
||||||
|
|
||||||
if (timeout != NULL) {
|
if (timeout == NULL) {
|
||||||
|
/*
|
||||||
|
* The core has nothing scheduled ("POLL BLOCKING"), so blocking
|
||||||
|
* for a command is right -- and returning 0 here instead would
|
||||||
|
* spin the CPU at 100%.
|
||||||
|
*/
|
||||||
|
if (stdin_wanted && montauk_stdin_wait()) {
|
||||||
|
FD_SET(0, readfds);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
unsigned long usec = (unsigned long) timeout->tv_sec * 1000000UL +
|
unsigned long usec = (unsigned long) timeout->tv_sec * 1000000UL +
|
||||||
(unsigned long) timeout->tv_usec;
|
(unsigned long) timeout->tv_usec;
|
||||||
if (usec != 0)
|
|
||||||
usleep(usec);
|
/*
|
||||||
|
* Sleep in slices rather than one long usleep: a scheduled
|
||||||
|
* timeout can be seconds long, and typing must not wait it out.
|
||||||
|
*/
|
||||||
|
while (usec > 0) {
|
||||||
|
unsigned long slice = (usec > 5000UL) ? 5000UL : usec;
|
||||||
|
|
||||||
|
usleep(slice);
|
||||||
|
usec -= slice;
|
||||||
|
|
||||||
|
if (stdin_wanted && montauk_stdin_ready()) {
|
||||||
|
FD_SET(0, readfds);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ready;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
diff --git a/utils/config.h b/utils/config.h
|
diff --git a/utils/config.h b/utils/config.h
|
||||||
index 3914771fd..09f2659d6 100644
|
index 3914771fd..578280321 100644
|
||||||
--- a/utils/config.h
|
--- a/utils/config.h
|
||||||
+++ b/utils/config.h
|
+++ b/utils/config.h
|
||||||
@@ -119,7 +119,7 @@ char *strchrnul(const char *s, int c);
|
@@ -119,7 +119,7 @@ char *strchrnul(const char *s, int c);
|
||||||
@@ -11,7 +11,22 @@ index 3914771fd..09f2659d6 100644
|
|||||||
#undef HAVE_UTSNAME
|
#undef HAVE_UTSNAME
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -158,14 +158,14 @@ char *realpath(const char *path, char *resolved_path);
|
@@ -144,8 +144,13 @@ char *realpath(const char *path, char *resolved_path);
|
||||||
|
#undef HAVE_STDOUT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
+/* MontaukOS: the libc mmap() is anonymous-only (it is a thin wrapper over
|
||||||
|
+ * SYS_ALLOC and rejects any fd != -1), so the file fetcher's
|
||||||
|
+ * mmap(..., MAP_SHARED, fd, 0) fails and every file: URL dies with "Unable to
|
||||||
|
+ * map memory for file data buffer". The fread() fallback in the same function
|
||||||
|
+ * is what we want. */
|
||||||
|
#define HAVE_MMAP
|
||||||
|
-#if (defined(_WIN32) || defined(__riscos__) || defined(__HAIKU__) || defined(__BEOS__) || defined(__amigaos4__) || defined(__AMIGA__) || defined(__MINT__))
|
||||||
|
+#if (defined(_WIN32) || defined(__riscos__) || defined(__HAIKU__) || defined(__BEOS__) || defined(__amigaos4__) || defined(__AMIGA__) || defined(__MINT__) || defined(__montauk__))
|
||||||
|
#undef HAVE_MMAP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
@@ -158,14 +163,14 @@ char *realpath(const char *path, char *resolved_path);
|
||||||
#define HAVE_DIRFD
|
#define HAVE_DIRFD
|
||||||
#define HAVE_UNLINKAT
|
#define HAVE_UNLINKAT
|
||||||
#define HAVE_FSTATAT
|
#define HAVE_FSTATAT
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
diff --git a/utils/file.c b/utils/file.c
|
||||||
|
index 75a8a1c03..a8ab70f67 100644
|
||||||
|
--- a/utils/file.c
|
||||||
|
+++ b/utils/file.c
|
||||||
|
@@ -152,6 +152,30 @@ static nserror posix_nsurl_to_path(struct nsurl *url, char **path_out)
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
+#if defined(__montauk__)
|
||||||
|
+ /*
|
||||||
|
+ * MontaukOS absolute paths are drive-qualified ("0:/dir/file"). The
|
||||||
|
+ * file: URL syntax requires a leading slash before the path, so the
|
||||||
|
+ * round trip through path_to_nsurl() yields "/0%3A/dir/file", which
|
||||||
|
+ * unescapes to "/0:/dir/file". Resolving that treats the leading slash
|
||||||
|
+ * as "root of the current drive" and leaves the drive prefix as a
|
||||||
|
+ * directory name, producing "0:/0:/dir/file".
|
||||||
|
+ *
|
||||||
|
+ * Drop the slash when what follows is a drive prefix, restoring the
|
||||||
|
+ * original path.
|
||||||
|
+ */
|
||||||
|
+ if (path[0] == '/') {
|
||||||
|
+ const char *p = path + 1;
|
||||||
|
+
|
||||||
|
+ while (*p >= '0' && *p <= '9') {
|
||||||
|
+ p++;
|
||||||
|
+ }
|
||||||
|
+ if ((p > path + 1) && (*p == ':')) {
|
||||||
|
+ memmove(path, path + 1, strlen(path));
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
*path_out = path;
|
||||||
|
|
||||||
|
return NSERROR_OK;
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
diff --git a/frontends/monkey/browser.c b/frontends/monkey/browser.c
|
||||||
|
index 958375486..6b406f552 100644
|
||||||
|
--- a/frontends/monkey/browser.c
|
||||||
|
+++ b/frontends/monkey/browser.c
|
||||||
|
@@ -27,10 +27,13 @@
|
||||||
|
#include "utils/log.h"
|
||||||
|
#include "utils/messages.h"
|
||||||
|
#include "utils/nsurl.h"
|
||||||
|
+#include "utils/nsoption.h"
|
||||||
|
#include "netsurf/mouse.h"
|
||||||
|
#include "netsurf/window.h"
|
||||||
|
#include "netsurf/browser_window.h"
|
||||||
|
+#include "netsurf/content_type.h"
|
||||||
|
#include "netsurf/plotters.h"
|
||||||
|
+#include "css/utils.h"
|
||||||
|
|
||||||
|
#include "monkey/output.h"
|
||||||
|
#include "monkey/browser.h"
|
||||||
|
@@ -599,6 +602,69 @@ monkey_window_handle_redraw(int argc, char **argv)
|
||||||
|
moutf(MOUT_WINDOW, "REDRAW WIN %d STOP", atoi(argv[2]));
|
||||||
|
}
|
||||||
|
|
||||||
|
+/**
|
||||||
|
+ * WINDOW DUMP <win> [path]
|
||||||
|
+ *
|
||||||
|
+ * Port-local diagnostic. The layout dump goes to a file because it is
|
||||||
|
+ * hundreds of lines, then the head of it is echoed so the box geometry is
|
||||||
|
+ * visible without any way to get a file off the ramdisk. The LENGTHS line
|
||||||
|
+ * carries the inputs every CSS length conversion depends on -- if the DPI or
|
||||||
|
+ * font sizes come out wrong, every box is wrong and nothing plots.
|
||||||
|
+ */
|
||||||
|
+static void
|
||||||
|
+monkey_window_handle_dump(int argc, char **argv)
|
||||||
|
+{
|
||||||
|
+ const char *path = (argc > 3) ? argv[3] : "0:/users/admin/nsbox.txt";
|
||||||
|
+ struct gui_window *gw;
|
||||||
|
+ char line[512];
|
||||||
|
+ int lineno;
|
||||||
|
+ FILE *f;
|
||||||
|
+
|
||||||
|
+ if (argc != 3 && argc != 4) {
|
||||||
|
+ moutf(MOUT_ERROR, "WINDOW DUMP ARGS BAD");
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ gw = monkey_find_window_by_num(atoi(argv[2]));
|
||||||
|
+ if (gw == NULL) {
|
||||||
|
+ moutf(MOUT_ERROR, "WINDOW NUM BAD");
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* Raw css_fixed (22.10), not FIXTOINT: a value that is merely small
|
||||||
|
+ * rather than zero has to stay visible. */
|
||||||
|
+ moutf(MOUT_WINDOW,
|
||||||
|
+ "DUMP WIN %s LENGTHS DPI %d FONT_SIZE %d FONT_MIN %d SCALE %d",
|
||||||
|
+ argv[2], (int) nscss_screen_dpi, nsoption_int(font_size),
|
||||||
|
+ nsoption_int(font_min_size), nsoption_int(scale));
|
||||||
|
+
|
||||||
|
+ f = fopen(path, "w");
|
||||||
|
+ if (f == NULL) {
|
||||||
|
+ moutf(MOUT_ERROR, "WINDOW DUMP OPEN FAILED %s", path);
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ browser_window_debug_dump(gw->bw, f, CONTENT_DEBUG_RENDER);
|
||||||
|
+ fclose(f);
|
||||||
|
+
|
||||||
|
+ f = fopen(path, "r");
|
||||||
|
+ if (f == NULL) {
|
||||||
|
+ moutf(MOUT_ERROR, "WINDOW DUMP REOPEN FAILED %s", path);
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ for (lineno = 0; lineno < 40; lineno++) {
|
||||||
|
+ if (fgets(line, sizeof(line), f) == NULL) {
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ line[strcspn(line, "\n")] = '\0';
|
||||||
|
+ moutf(MOUT_WINDOW, "DUMP WIN %s BOX %s", argv[2], line);
|
||||||
|
+ }
|
||||||
|
+ fclose(f);
|
||||||
|
+
|
||||||
|
+ moutf(MOUT_WINDOW, "DUMP WIN %s END LINES %d", argv[2], lineno);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static void
|
||||||
|
monkey_window_handle_reload(int argc, char **argv)
|
||||||
|
{
|
||||||
|
@@ -721,6 +787,8 @@ monkey_window_handle_command(int argc, char **argv)
|
||||||
|
monkey_window_handle_stop(argc, argv);
|
||||||
|
} else if (strcmp(argv[1], "REDRAW") == 0) {
|
||||||
|
monkey_window_handle_redraw(argc, argv);
|
||||||
|
+ } else if (strcmp(argv[1], "DUMP") == 0) {
|
||||||
|
+ monkey_window_handle_dump(argc, argv);
|
||||||
|
} else if (strcmp(argv[1], "RELOAD") == 0) {
|
||||||
|
monkey_window_handle_reload(argc, argv);
|
||||||
|
} else if (strcmp(argv[1], "EXEC") == 0) {
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
diff --git a/desktop/browser_window.c b/desktop/browser_window.c
|
||||||
|
index 226741be5..57da7d56e 100644
|
||||||
|
--- a/desktop/browser_window.c
|
||||||
|
+++ b/desktop/browser_window.c
|
||||||
|
@@ -1338,6 +1338,24 @@ browser_window__handle_fetcherror(struct browser_window *bw,
|
||||||
|
|
||||||
|
memset(¶ms, 0, sizeof(params));
|
||||||
|
|
||||||
|
+ /* If it is the error page itself that failed, navigating to the error
|
||||||
|
+ * page again lands straight back here. Nothing bounds that: the pair
|
||||||
|
+ * recurses, allocating a content per turn, until the heap is gone and
|
||||||
|
+ * the real failure is buried under hundreds of retries. Report the
|
||||||
|
+ * reason and stop instead. */
|
||||||
|
+ if (nsurl_compare(url, corestring_nsurl_about_query_fetcherror,
|
||||||
|
+ NSURL_COMPLETE)) {
|
||||||
|
+ NSLOG(netsurf, WARNING,
|
||||||
|
+ "error page itself failed: %s -- not retrying", reason);
|
||||||
|
+ fprintf(stderr,
|
||||||
|
+ "netsurf: error page failed (%s); giving up\n", reason);
|
||||||
|
+ browser_window_set_status(bw, reason);
|
||||||
|
+ return browser_window_stop_throbber(bw);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ fprintf(stderr, "netsurf: fetch of '%s' failed: %s\n",
|
||||||
|
+ nsurl_access(url), reason);
|
||||||
|
+
|
||||||
|
params.url = nsurl_ref(corestring_nsurl_about_query_fetcherror);
|
||||||
|
params.referrer = nsurl_ref(url);
|
||||||
|
params.flags = BW_NAVIGATE_HISTORY | BW_NAVIGATE_NO_TERMINAL_HISTORY_UPDATE | BW_NAVIGATE_INTERNAL;
|
||||||
Reference in New Issue
Block a user