summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/build.yaml104
-rw-r--r--.github/workflows/monkey-test.yaml62
-rw-r--r--.github/workflows/static-analysis.yaml58
-rw-r--r--Makefile2
-rw-r--r--content/fetchers/curl.c32
-rw-r--r--content/handlers/css/dump.c6
-rw-r--r--content/handlers/css/utils.h25
-rw-r--r--content/handlers/html/Makefile1
-rw-r--r--content/handlers/html/box.h4
-rw-r--r--content/handlers/html/box_construct.c108
-rw-r--r--content/handlers/html/box_inspect.c10
-rw-r--r--content/handlers/html/box_inspect.h12
-rw-r--r--content/handlers/html/box_normalise.c202
-rw-r--r--content/handlers/html/box_normalise.h9
-rw-r--r--content/handlers/html/box_special.c37
-rw-r--r--content/handlers/html/layout.c762
-rw-r--r--content/handlers/html/layout_flex.c898
-rw-r--r--content/handlers/html/layout_internal.h711
-rw-r--r--content/handlers/image/Makefile2
-rw-r--r--content/handlers/image/gif.c9
-rw-r--r--content/handlers/image/image_cache.c14
-rw-r--r--content/handlers/image/rsvg246.c265
-rw-r--r--content/handlers/javascript/duktape/CSSRule.bnd22
-rw-r--r--content/handlers/javascript/duktape/CSSStyleSheet.bnd22
-rw-r--r--content/handlers/javascript/duktape/Console.bnd2
-rw-r--r--content/handlers/javascript/duktape/DOMSettableTokenList.bnd44
-rw-r--r--content/handlers/javascript/duktape/DOMTokenList.bnd163
-rw-r--r--content/handlers/javascript/duktape/Element.bnd31
-rw-r--r--content/handlers/javascript/duktape/ImageData.bnd2
-rw-r--r--content/handlers/javascript/duktape/netsurf.bnd6
-rw-r--r--content/handlers/javascript/duktape/polyfill.js18
-rw-r--r--content/llcache.c2
-rw-r--r--content/urldb.c5
-rw-r--r--docs/UnimplementedJavascript.md17
-rw-r--r--docs/env.sh156
-rw-r--r--frontends/amiga/Makefile2
-rwxr-xr-xfrontends/amiga/gui_options.c2
-rw-r--r--frontends/atari/Makefile2
-rw-r--r--frontends/beos/Makefile2
-rw-r--r--frontends/gtk/Makefile2
-rw-r--r--frontends/gtk/gdk.c2
-rw-r--r--frontends/gtk/toolbar.c9
-rw-r--r--frontends/riscos/Makefile8
-rw-r--r--frontends/riscos/Makefile.tools42
-rw-r--r--frontends/riscos/gui/url_bar.c5
-rw-r--r--frontends/riscos/image.c39
-rw-r--r--frontends/riscos/scripts/Run4
-rw-r--r--frontends/riscos/window.c1
-rw-r--r--frontends/windows/Makefile2
-rw-r--r--test/data/Choices-all1
-rw-r--r--test/js/class-list.html29
-rw-r--r--test/js/index.html1
-rw-r--r--utils/config.h9
-rw-r--r--utils/file.c57
-rw-r--r--utils/filename.c88
-rw-r--r--utils/log.c1
-rw-r--r--utils/log.h1
-rw-r--r--utils/ssl_certs.c35
-rw-r--r--utils/utf8.c203
59 files changed, 3386 insertions, 984 deletions
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
new file mode 100644
index 000000000..13b59c38b
--- /dev/null
+++ b/.github/workflows/build.yaml
@@ -0,0 +1,104 @@
+name: "Linux Build"
+
+on: [push]
+
+jobs:
+ linux:
+ name: '${{ matrix.os }}: ${{ matrix.compiler.vendor }}'
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os:
+ - ubuntu-22.04
+ - ubuntu-20.04
+ compiler:
+ # The NetSurf build system can't find LLVM AR (it looks for it
+ # in /usr/lib instead of /usr/bin:
+ # `make: /usr/lib/llvm-ar: No such file or directory`).
+ # So we need to make it explicit for llvm.
+ - { vendor: gnu, CC: gcc }
+ - { vendor: llvm, CC: clang, AR: llvm-ar }
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 1
+
+ - name: apt-get install packages
+ run: sudo apt-get update -qq &&
+ sudo apt-get install --no-install-recommends -y
+ bison
+ build-essential
+ check
+ clang
+ flex
+ git
+ gperf
+ libcurl4-openssl-dev
+ libgtk-3-dev
+ libhtml-parser-perl
+ libjpeg-dev
+ libpng-dev
+ librsvg2-dev
+ llvm
+ pkg-config
+ wbritish # Needed for `/usr/share/dict/words`, used by test
+
+ - name: Build and install project libs
+ env:
+ CC: ${{ matrix.compiler.CC }}
+ CXX: ${{ matrix.compiler.CXX }}
+ run: |
+ export TARGET_WORKSPACE="$(pwd)/projects"
+ source docs/env.sh
+ ns-clone -d -s
+ ns-make-tools install
+ ns-make-libs install
+
+ - name: Disable -Werror
+ # We can remove this step if we ever move to GitHub properly.
+ run: |
+ find . -type f -name Makefile | xargs sed -i 's/-Werror//'
+
+ - name: Unit Tests
+ # Fails when built with clang:
+ # test/corestrings.c:58:F:corestrings:corestrings_test:486:
+ # Assertion 'ires == NSERROR_NOMEM' failed: ires == 0, NSERROR_NOMEM == 2
+ # Looks like `malloc_limit()` not working.
+ if: ${{ matrix.compiler.vendor != 'llvm' }}
+ env:
+ CC: ${{ matrix.compiler.CC }}
+ CXX: ${{ matrix.compiler.CXX }}
+ run: |
+ export TARGET_WORKSPACE="$(pwd)/projects"
+ source docs/env.sh
+ make test
+
+ - name: Build NetSurf GTK
+ env:
+ CC: ${{ matrix.compiler.CC }}
+ CXX: ${{ matrix.compiler.CXX }}
+ run: |
+ export TARGET_WORKSPACE="$(pwd)/projects"
+ source docs/env.sh
+ make -j"$(nproc)" TARGET=gtk
+
+ - name: Build NetSurf Monkey
+ env:
+ CC: ${{ matrix.compiler.CC }}
+ CXX: ${{ matrix.compiler.CXX }}
+ run: |
+ export TARGET_WORKSPACE="$(pwd)/projects"
+ source docs/env.sh
+ make -j"$(nproc)" TARGET=monkey
+
+ - name: Build NetSurf Framebuffer
+ env:
+ CC: ${{ matrix.compiler.CC }}
+ CXX: ${{ matrix.compiler.CXX }}
+ run: |
+ export TARGET_WORKSPACE="$(pwd)/projects"
+ source docs/env.sh
+ make -j"$(nproc)" TARGET=framebuffer
diff --git a/.github/workflows/monkey-test.yaml b/.github/workflows/monkey-test.yaml
new file mode 100644
index 000000000..421bf3b50
--- /dev/null
+++ b/.github/workflows/monkey-test.yaml
@@ -0,0 +1,62 @@
+name: "Monkey Test"
+
+on: [pull_request]
+
+jobs:
+ test:
+ name: '${{ matrix.test }}'
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ test:
+ - short-internet
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 1
+
+ - name: apt-get install packages
+ run: sudo apt-get update -qq &&
+ sudo apt-get install --no-install-recommends -y
+ bison
+ build-essential
+ check
+ clang
+ flex
+ git
+ gperf
+ libcurl4-openssl-dev
+ libgtk-3-dev
+ libhtml-parser-perl
+ libjpeg-dev
+ libpng-dev
+ librsvg2-dev
+ llvm
+ pkg-config
+ wbritish # Needed for `/usr/share/dict/words`, used by test
+
+ - name: Build and install project libs
+ run: |
+ export TARGET_WORKSPACE="$(pwd)/projects"
+ source docs/env.sh
+ ns-clone -d -s
+ ns-make-tools install
+ ns-make-libs install
+
+ - name: Disable -Werror
+ # We can remove this step if we ever move to GitHub properly.
+ run: |
+ find . -type f -name Makefile | xargs sed -i 's/-Werror//'
+
+ - name: Build NetSurf Monkey
+ run: |
+ export TARGET_WORKSPACE="$(pwd)/projects"
+ source docs/env.sh
+ make -j"$(nproc)" TARGET=monkey
+
+ - name: Run Monkey Test
+ run: |
+ LC_ALL=C.UTF-8 test/monkey-see-monkey-do -v -d ${{ matrix.test }}
diff --git a/.github/workflows/static-analysis.yaml b/.github/workflows/static-analysis.yaml
new file mode 100644
index 000000000..5027d0510
--- /dev/null
+++ b/.github/workflows/static-analysis.yaml
@@ -0,0 +1,58 @@
+name: "Static Analysis"
+
+on: [push]
+
+jobs:
+ codeql:
+ name: codeql
+ runs-on: ubuntu-22.04
+
+ strategy:
+ fail-fast: false
+ matrix:
+ language: ['cpp']
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v2
+ with:
+ fetch-depth: 1
+
+ - name: apt-get install packages
+ run: sudo apt-get update -qq &&
+ sudo apt-get install --no-install-recommends -y
+ bison
+ build-essential
+ flex
+ git
+ gperf
+ libcurl4-openssl-dev
+ libgtk-3-dev
+ libhtml-parser-perl
+ libjpeg-dev
+ libpng-dev
+ librsvg2-dev
+ pkg-config
+
+ - name: Build and install project libs
+ run: |
+ export TARGET_WORKSPACE="$(pwd)/projects"
+ source docs/env.sh
+ ns-clone -d -s
+ ns-make-tools install
+ ns-make-libs install
+
+ # Initializes the CodeQL tools for scanning.
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v2
+ with:
+ languages: ${{ matrix.language }}
+
+ - name: Build NetSurf
+ run: |
+ export TARGET_WORKSPACE="$(pwd)/projects"
+ source docs/env.sh
+ make -j"$(nproc)"
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v2
diff --git a/Makefile b/Makefile
index f66f79649..828f83bf0 100644
--- a/Makefile
+++ b/Makefile
@@ -333,7 +333,7 @@ ifneq ($(TARGET),riscos)
$(Q)$(CC) -o $(EXETARGET) $(OBJECTS) $(LDFLAGS) > $(DEPROOT)/link-raw.d
else
@# RISC OS targets are a bit special: we need to convert ELF -> AIF
- ifeq ($(SUBTARGET),riscos-aof)
+ ifeq ($(SUBTARGET),-aof)
$(Q)$(CC) -o $(EXETARGET) $(OBJECTS) $(LDFLAGS) > $(DEPROOT)/link-raw.d
else
$(Q)$(CXX) -o $(EXETARGET:,ff8=,e1f) $(OBJECTS) $(LDFLAGS) > $(DEPROOT)/link-raw.d
diff --git a/content/fetchers/curl.c b/content/fetchers/curl.c
index d36f44c09..536898310 100644
--- a/content/fetchers/curl.c
+++ b/content/fetchers/curl.c
@@ -67,7 +67,15 @@
#define UPDATES_PER_SECOND 2
/**
- * The ciphersuites the browser is prepared to use
+ * The ciphersuites the browser is prepared to use for TLS1.3
+ */
+#define CIPHER_SUITES \
+ "TLS_AES_256_GCM_SHA384:" \
+ "TLS_CHACHA20_POLY1305_SHA256:" \
+ "TLS_AES_128_GCM_SHA256"
+
+/**
+ * The ciphersuites the browser is prepared to use for TLS<1.3
*/
#define CIPHER_LIST \
/* disable everything */ \
@@ -78,8 +86,6 @@
"EECDH+AESGCM:EDH+AESGCM:" \
/* Enable PFS AES CBC suites */ \
"EECDH+AES:EDH+AES:" \
- /* Enable non-PFS fallback suite */ \
- "AES128-SHA:" \
/* Remove any PFS suites using weak DSA key exchange */ \
"-DSS"
@@ -225,7 +231,7 @@ struct curl_fetch_info {
bool abort; /**< Abort requested. */
bool stopped; /**< Download stopped on purpose. */
bool only_2xx; /**< Only HTTP 2xx responses acceptable. */
- bool downgrade_tls; /**< Downgrade to TLS <= 1.0 */
+ bool downgrade_tls; /**< Downgrade to TLS 1.2 */
nsurl *url; /**< URL of this fetch. */
lwc_string *host; /**< The hostname of this fetch. */
struct curl_slist *headers; /**< List of request headers. */
@@ -805,7 +811,8 @@ fetch_curl_sslctxfun(CURL *curl_handle, void *_sslctx, void *parm)
{
struct curl_fetch_info *f = (struct curl_fetch_info *) parm;
SSL_CTX *sslctx = _sslctx;
- long options = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
+ long options = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
+ SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1;
/* set verify callback for each certificate in chain */
SSL_CTX_set_verify(sslctx, SSL_VERIFY_PEER, fetch_curl_verify_callback);
@@ -816,19 +823,14 @@ fetch_curl_sslctxfun(CURL *curl_handle, void *_sslctx, void *parm)
parm);
if (f->downgrade_tls) {
- /* Disable TLS 1.1/1.2 if the server can't cope with them */
-#ifdef SSL_OP_NO_TLSv1_1
- options |= SSL_OP_NO_TLSv1_1;
-#endif
-#ifdef SSL_OP_NO_TLSv1_2
- options |= SSL_OP_NO_TLSv1_2;
+ /* Disable TLS 1.3 if the server can't cope with it */
+#ifdef SSL_OP_NO_TLSv1_3
+ options |= SSL_OP_NO_TLSv1_3;
#endif
#ifdef SSL_MODE_SEND_FALLBACK_SCSV
/* Ensure server rejects the connection if downgraded too far */
SSL_CTX_set_mode(sslctx, SSL_MODE_SEND_FALLBACK_SCSV);
#endif
- /* Disable TLS1.2 ciphersuites */
- SSL_CTX_set_cipher_list(sslctx, CIPHER_LIST ":-TLSv1.2");
}
SSL_CTX_set_options(sslctx, options);
@@ -1785,6 +1787,10 @@ nserror fetch_curl_register(void)
/* only set the cipher list with openssl otherwise the
* fetch fails with "Unknown cipher in list"
*/
+#if LIBCURL_VERSION_NUM >= 0x073d00
+ /* Need libcurl 7.61.0 or later */
+ SETOPT(CURLOPT_TLS13_CIPHERS, CIPHER_SUITES);
+#endif
SETOPT(CURLOPT_SSL_CIPHER_LIST, CIPHER_LIST);
}
diff --git a/content/handlers/css/dump.c b/content/handlers/css/dump.c
index 1e448e0d4..4138f9343 100644
--- a/content/handlers/css/dump.c
+++ b/content/handlers/css/dump.c
@@ -864,6 +864,12 @@ void nscss_dump_computed_style(FILE *stream, const css_computed_style *style)
case CSS_DISPLAY_NONE:
fprintf(stream, "display: none ");
break;
+ case CSS_DISPLAY_FLEX:
+ fprintf(stream, "display: flex ");
+ break;
+ case CSS_DISPLAY_INLINE_FLEX:
+ fprintf(stream, "display: inline-flex ");
+ break;
default:
break;
}
diff --git a/content/handlers/css/utils.h b/content/handlers/css/utils.h
index 541677a3a..ee241e2cc 100644
--- a/content/handlers/css/utils.h
+++ b/content/handlers/css/utils.h
@@ -28,41 +28,50 @@ extern css_fixed nscss_screen_dpi;
/**
* Temporary helper wrappers for for libcss computed style getter, while
- * we don't support flexbox related property values.
+ * we don't support all values of display.
*/
-
static inline uint8_t ns_computed_display(
const css_computed_style *style, bool root)
{
uint8_t value = css_computed_display(style, root);
- if (value == CSS_DISPLAY_FLEX) {
+ switch (value) {
+ case CSS_DISPLAY_GRID:
return CSS_DISPLAY_BLOCK;
- } else if (value == CSS_DISPLAY_INLINE_FLEX) {
+ case CSS_DISPLAY_INLINE_GRID:
return CSS_DISPLAY_INLINE_BLOCK;
+
+ default:
+ break;
}
return value;
}
-
+/**
+ * Temporary helper wrappers for for libcss computed style getter, while
+ * we don't support all values of display.
+ */
static inline uint8_t ns_computed_display_static(
const css_computed_style *style)
{
uint8_t value = css_computed_display_static(style);
- if (value == CSS_DISPLAY_FLEX) {
+ switch (value) {
+ case CSS_DISPLAY_GRID:
return CSS_DISPLAY_BLOCK;
- } else if (value == CSS_DISPLAY_INLINE_FLEX) {
+ case CSS_DISPLAY_INLINE_GRID:
return CSS_DISPLAY_INLINE_BLOCK;
+
+ default:
+ break;
}
return value;
}
-
static inline uint8_t ns_computed_min_height(
const css_computed_style *style,
css_fixed *length, css_unit *unit)
diff --git a/content/handlers/html/Makefile b/content/handlers/html/Makefile
index 8bb329b76..e41cc1d22 100644
--- a/content/handlers/html/Makefile
+++ b/content/handlers/html/Makefile
@@ -16,6 +16,7 @@ S_HTML := box_construct.c \
imagemap.c \
interaction.c \
layout.c \
+ layout_flex.c \
object.c \
redraw.c \
redraw_border.c \
diff --git a/content/handlers/html/box.h b/content/handlers/html/box.h
index 1059556e6..df2b99d87 100644
--- a/content/handlers/html/box.h
+++ b/content/handlers/html/box.h
@@ -66,7 +66,9 @@ typedef enum {
BOX_BR,
BOX_TEXT,
BOX_INLINE_END,
- BOX_NONE
+ BOX_NONE,
+ BOX_FLEX,
+ BOX_INLINE_FLEX,
} box_type;
diff --git a/content/handlers/html/box_construct.c b/content/handlers/html/box_construct.c
index 12d9df87c..eeadb8453 100644
--- a/content/handlers/html/box_construct.c
+++ b/content/handlers/html/box_construct.c
@@ -86,28 +86,30 @@ struct box_construct_props {
static const content_type image_types = CONTENT_IMAGE;
-/**
- * mapping from CSS display to box type this table must be in sync
- * with libcss' css_display enum
- */
+/* mapping from CSS display to box type
+ * this table must be in sync with libcss' css_display enum */
static const box_type box_map[] = {
- 0, /* CSS_DISPLAY_INHERIT, */
- BOX_INLINE, /* CSS_DISPLAY_INLINE, */
- BOX_BLOCK, /* CSS_DISPLAY_BLOCK, */
- BOX_BLOCK, /* CSS_DISPLAY_LIST_ITEM, */
- BOX_INLINE, /* CSS_DISPLAY_RUN_IN, */
- BOX_INLINE_BLOCK, /* CSS_DISPLAY_INLINE_BLOCK, */
- BOX_TABLE, /* CSS_DISPLAY_TABLE, */
- BOX_TABLE, /* CSS_DISPLAY_INLINE_TABLE, */
- BOX_TABLE_ROW_GROUP, /* CSS_DISPLAY_TABLE_ROW_GROUP, */
- BOX_TABLE_ROW_GROUP, /* CSS_DISPLAY_TABLE_HEADER_GROUP, */
- BOX_TABLE_ROW_GROUP, /* CSS_DISPLAY_TABLE_FOOTER_GROUP, */
- BOX_TABLE_ROW, /* CSS_DISPLAY_TABLE_ROW, */
- BOX_NONE, /* CSS_DISPLAY_TABLE_COLUMN_GROUP, */
- BOX_NONE, /* CSS_DISPLAY_TABLE_COLUMN, */
- BOX_TABLE_CELL, /* CSS_DISPLAY_TABLE_CELL, */
- BOX_INLINE, /* CSS_DISPLAY_TABLE_CAPTION, */
- BOX_NONE /* CSS_DISPLAY_NONE */
+ BOX_BLOCK, /* CSS_DISPLAY_INHERIT */
+ BOX_INLINE, /* CSS_DISPLAY_INLINE */
+ BOX_BLOCK, /* CSS_DISPLAY_BLOCK */
+ BOX_BLOCK, /* CSS_DISPLAY_LIST_ITEM */
+ BOX_INLINE, /* CSS_DISPLAY_RUN_IN */
+ BOX_INLINE_BLOCK, /* CSS_DISPLAY_INLINE_BLOCK */
+ BOX_TABLE, /* CSS_DISPLAY_TABLE */
+ BOX_TABLE, /* CSS_DISPLAY_INLINE_TABLE */
+ BOX_TABLE_ROW_GROUP, /* CSS_DISPLAY_TABLE_ROW_GROUP */
+ BOX_TABLE_ROW_GROUP, /* CSS_DISPLAY_TABLE_HEADER_GROUP */
+ BOX_TABLE_ROW_GROUP, /* CSS_DISPLAY_TABLE_FOOTER_GROUP */
+ BOX_TABLE_ROW, /* CSS_DISPLAY_TABLE_ROW */
+ BOX_NONE, /* CSS_DISPLAY_TABLE_COLUMN_GROUP */
+ BOX_NONE, /* CSS_DISPLAY_TABLE_COLUMN */
+ BOX_TABLE_CELL, /* CSS_DISPLAY_TABLE_CELL */
+ BOX_INLINE, /* CSS_DISPLAY_TABLE_CAPTION */
+ BOX_NONE, /* CSS_DISPLAY_NONE */
+ BOX_FLEX, /* CSS_DISPLAY_FLEX */
+ BOX_INLINE_FLEX, /* CSS_DISPLAY_INLINE_FLEX */
+ BOX_BLOCK, /* CSS_DISPLAY_GRID */
+ BOX_INLINE_BLOCK, /* CSS_DISPLAY_INLINE_GRID */
};
@@ -142,7 +144,6 @@ static inline bool box_is_root(dom_node *n)
return true;
}
-
/**
* Extract transient construction properties
*
@@ -438,6 +439,23 @@ box_construct_marker(struct box *box,
return true;
}
+static inline bool box__style_is_float(const struct box *box)
+{
+ return css_computed_float(box->style) == CSS_FLOAT_LEFT ||
+ css_computed_float(box->style) == CSS_FLOAT_RIGHT;
+}
+
+static inline bool box__is_flex(const struct box *box)
+{
+ return box->type == BOX_FLEX || box->type == BOX_INLINE_FLEX;
+}
+
+static inline bool box__containing_block_is_flex(
+ const struct box_construct_props *props)
+{
+ return props->containing_block != NULL &&
+ box__is_flex(props->containing_block);
+}
/**
* Construct the box tree for an XML element.
@@ -451,6 +469,7 @@ box_construct_element(struct box_construct_ctx *ctx, bool *convert_children)
{
dom_string *title0, *s;
lwc_string *id = NULL;
+ enum css_display_e css_display;
struct box *box = NULL, *old_box;
css_select_results *styles = NULL;
lwc_string *bgimage_uri;
@@ -549,16 +568,15 @@ box_construct_element(struct box_construct_ctx *ctx, bool *convert_children)
dom_string_unref(s);
}
+ css_display = ns_computed_display_static(box->style);
+
/* Set box type from computed display */
if ((css_computed_position(box->style) == CSS_POSITION_ABSOLUTE ||
- css_computed_position(box->style) ==
- CSS_POSITION_FIXED) &&
- (ns_computed_display_static(box->style) ==
- CSS_DISPLAY_INLINE ||
- ns_computed_display_static(box->style) ==
- CSS_DISPLAY_INLINE_BLOCK ||
- ns_computed_display_static(box->style) ==
- CSS_DISPLAY_INLINE_TABLE)) {
+ css_computed_position(box->style) == CSS_POSITION_FIXED) &&
+ (css_display == CSS_DISPLAY_INLINE ||
+ css_display == CSS_DISPLAY_INLINE_BLOCK ||
+ css_display == CSS_DISPLAY_INLINE_TABLE ||
+ css_display == CSS_DISPLAY_INLINE_FLEX)) {
/* Special case for absolute positioning: make absolute inlines
* into inline block so that the boxes are constructed in an
* inline container as if they were not absolutely positioned.
@@ -572,6 +590,21 @@ box_construct_element(struct box_construct_ctx *ctx, bool *convert_children)
/* Normal mapping */
box->type = box_map[ns_computed_display(box->style,
props.node_is_root)];
+
+ if (props.containing_block->type == BOX_FLEX ||
+ props.containing_block->type == BOX_INLINE_FLEX) {
+ /* Blockification */
+ switch (box->type) {
+ case BOX_INLINE_FLEX:
+ box->type = BOX_FLEX;
+ break;
+ case BOX_INLINE_BLOCK:
+ box->type = BOX_BLOCK;
+ break;
+ default:
+ break;
+ }
+ }
}
if (convert_special_elements(ctx->n,
@@ -587,10 +620,9 @@ box_construct_element(struct box_construct_ctx *ctx, bool *convert_children)
box->styles->styles[CSS_PSEUDO_ELEMENT_BEFORE]);
}
- if (box->type == BOX_NONE ||
- (ns_computed_display(box->style,
- props.node_is_root) == CSS_DISPLAY_NONE &&
- props.node_is_root == false)) {
+ if (box->type == BOX_NONE || (ns_computed_display(box->style,
+ props.node_is_root) == CSS_DISPLAY_NONE &&
+ props.node_is_root == false)) {
css_select_results_destroy(styles);
box->styles = NULL;
box->style = NULL;
@@ -625,8 +657,9 @@ box_construct_element(struct box_construct_ctx *ctx, bool *convert_children)
(box->type == BOX_INLINE ||
box->type == BOX_BR ||
box->type == BOX_INLINE_BLOCK ||
- css_computed_float(box->style) == CSS_FLOAT_LEFT ||
- css_computed_float(box->style) == CSS_FLOAT_RIGHT) &&
+ box->type == BOX_INLINE_FLEX ||
+ (box__style_is_float(box) &&
+ !box__containing_block_is_flex(&props))) &&
props.node_is_root == false) {
/* Found an inline child of a block without a current container
* (i.e. this box is the first child of its parent, or was
@@ -674,6 +707,7 @@ box_construct_element(struct box_construct_ctx *ctx, bool *convert_children)
box->flags |= CONVERT_CHILDREN;
if (box->type == BOX_INLINE || box->type == BOX_BR ||
+ box->type == BOX_INLINE_FLEX ||
box->type == BOX_INLINE_BLOCK) {
/* Inline container must exist, as we'll have
* created it above if it didn't */
@@ -690,6 +724,7 @@ box_construct_element(struct box_construct_ctx *ctx, bool *convert_children)
}
if (props.node_is_root == false &&
+ box__containing_block_is_flex(&props) == false &&
(css_computed_float(box->style) ==
CSS_FLOAT_LEFT ||
css_computed_float(box->style) ==
@@ -1342,7 +1377,6 @@ struct box *box_for_node(dom_node *n)
return box;
}
-
/* exported function documented in html/box_construct.h */
bool
box_extract_link(const html_content *content,
diff --git a/content/handlers/html/box_inspect.c b/content/handlers/html/box_inspect.c
index b4b13940d..181f58cf8 100644
--- a/content/handlers/html/box_inspect.c
+++ b/content/handlers/html/box_inspect.c
@@ -660,7 +660,7 @@ void box_dump(FILE *stream, struct box *box, unsigned int depth, bool style)
if (box->max_width != UNKNOWN_MAX_WIDTH) {
fprintf(stream, "min%i max%i ", box->min_width, box->max_width);
}
- fprintf(stream, "(%i %i %i %i) ",
+ fprintf(stream, "desc(%i %i %i %i) ",
box->descendant_x0, box->descendant_y0,
box->descendant_x1, box->descendant_y1);
@@ -724,6 +724,14 @@ void box_dump(FILE *stream, struct box *box, unsigned int depth, bool style)
fprintf(stream, "TEXT ");
break;
+ case BOX_FLEX:
+ fprintf(stream, "FLEX ");
+ break;
+
+ case BOX_INLINE_FLEX:
+ fprintf(stream, "INLINE_FLEX ");
+ break;
+
default:
fprintf(stream, "Unknown box type ");
}
diff --git a/content/handlers/html/box_inspect.h b/content/handlers/html/box_inspect.h
index b9161f148..a218326d8 100644
--- a/content/handlers/html/box_inspect.h
+++ b/content/handlers/html/box_inspect.h
@@ -139,5 +139,17 @@ static inline bool box_is_first_child(struct box *b)
return (b->parent == NULL || b == b->parent->children);
}
+static inline unsigned box_count_children(const struct box *b)
+{
+ const struct box *c = b->children;
+ unsigned count = 0;
+
+ while (c != NULL) {
+ count++;
+ c = c->next;
+ }
+
+ return count;
+}
#endif
diff --git a/content/handlers/html/box_normalise.c b/content/handlers/html/box_normalise.c
index 1b6a345d5..8f25b031f 100644
--- a/content/handlers/html/box_normalise.c
+++ b/content/handlers/html/box_normalise.c
@@ -177,6 +177,7 @@ box_normalise_table_row(struct box *row,
return false;
cell = child;
break;
+ case BOX_FLEX:
case BOX_BLOCK:
case BOX_INLINE_CONTAINER:
case BOX_TABLE:
@@ -211,6 +212,7 @@ box_normalise_table_row(struct box *row,
cell->prev = child->prev;
while (child != NULL && (
+ child->type == BOX_FLEX ||
child->type == BOX_BLOCK ||
child->type == BOX_INLINE_CONTAINER ||
child->type == BOX_TABLE ||
@@ -238,6 +240,7 @@ box_normalise_table_row(struct box *row,
break;
case BOX_INLINE:
case BOX_INLINE_END:
+ case BOX_INLINE_FLEX:
case BOX_INLINE_BLOCK:
case BOX_FLOAT_LEFT:
case BOX_FLOAT_RIGHT:
@@ -314,6 +317,7 @@ box_normalise_table_row_group(struct box *row_group,
c) == false)
return false;
break;
+ case BOX_FLEX:
case BOX_BLOCK:
case BOX_INLINE_CONTAINER:
case BOX_TABLE:
@@ -348,6 +352,7 @@ box_normalise_table_row_group(struct box *row_group,
row->prev = child->prev;
while (child != NULL && (
+ child->type == BOX_FLEX ||
child->type == BOX_BLOCK ||
child->type == BOX_INLINE_CONTAINER ||
child->type == BOX_TABLE ||
@@ -377,6 +382,7 @@ box_normalise_table_row_group(struct box *row_group,
break;
case BOX_INLINE:
case BOX_INLINE_END:
+ case BOX_INLINE_FLEX:
case BOX_INLINE_BLOCK:
case BOX_FLOAT_LEFT:
case BOX_FLOAT_RIGHT:
@@ -648,6 +654,7 @@ box_normalise_table(struct box *table, const struct box *root, html_content * c)
return false;
}
break;
+ case BOX_FLEX:
case BOX_BLOCK:
case BOX_INLINE_CONTAINER:
case BOX_TABLE:
@@ -686,6 +693,7 @@ box_normalise_table(struct box *table, const struct box *root, html_content * c)
row_group->prev = child->prev;
while (child != NULL && (
+ child->type == BOX_FLEX ||
child->type == BOX_BLOCK ||
child->type == BOX_INLINE_CONTAINER ||
child->type == BOX_TABLE ||
@@ -716,6 +724,7 @@ box_normalise_table(struct box *table, const struct box *root, html_content * c)
break;
case BOX_INLINE:
case BOX_INLINE_END:
+ case BOX_INLINE_FLEX:
case BOX_INLINE_BLOCK:
case BOX_FLOAT_LEFT:
case BOX_FLOAT_RIGHT:
@@ -806,6 +815,181 @@ box_normalise_table(struct box *table, const struct box *root, html_content * c)
return true;
}
+static bool box_normalise_flex(
+ struct box *flex_container,
+ const struct box *root,
+ html_content *c)
+{
+ struct box *child;
+ struct box *next_child;
+ struct box *implied_flex_item;
+ css_computed_style *style;
+ nscss_select_ctx ctx;
+
+ assert(flex_container != NULL);
+ assert(root != NULL);
+
+ ctx.root_style = root->style;
+
+#ifdef BOX_NORMALISE_DEBUG
+ NSLOG(netsurf, INFO, "flex_container %p, flex_container->type %u",
+ flex_container, flex_container->type);
+#endif
+
+ assert(flex_container->type == BOX_FLEX ||
+ flex_container->type == BOX_INLINE_FLEX);
+
+ for (child = flex_container->children; child != NULL; child = next_child) {
+#ifdef BOX_NORMALISE_DEBUG
+ NSLOG(netsurf, INFO, "child %p, child->type = %d",
+ child, child->type);
+#endif
+
+ next_child = child->next; /* child may be destroyed */
+
+ switch (child->type) {
+ case BOX_FLEX:
+ /* ok */
+ if (box_normalise_flex(child, root, c) == false)
+ return false;
+ break;
+ case BOX_BLOCK:
+ /* ok */
+ if (box_normalise_block(child, root, c) == false)
+ return false;
+ break;
+ case BOX_INLINE_CONTAINER:
+ /* insert implied flex item */
+ assert(flex_container->style != NULL);
+
+ ctx.ctx = c->select_ctx;
+ ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL);
+ ctx.base_url = c->base_url;
+ ctx.universal = c->universal;
+
+ style = nscss_get_blank_style(&ctx, &c->unit_len_ctx,
+ flex_container->style);
+ if (style == NULL)
+ return false;
+
+ implied_flex_item = box_create(NULL, style, true,
+ flex_container->href,
+ flex_container->target,
+ NULL, NULL, c->bctx);
+ if (implied_flex_item == NULL) {
+ css_computed_style_destroy(style);
+ return false;
+ }
+ implied_flex_item->type = BOX_BLOCK;
+
+ if (child->prev == NULL)
+ flex_container->children = implied_flex_item;
+ else
+ child->prev->next = implied_flex_item;
+
+ implied_flex_item->prev = child->prev;
+
+ while (child != NULL &&
+ child->type == BOX_INLINE_CONTAINER) {
+ box_add_child(implied_flex_item, child);
+
+ next_child = child->next;
+ child->next = NULL;
+ child = next_child;
+ }
+
+ implied_flex_item->last->next = NULL;
+ implied_flex_item->next = next_child = child;
+ if (implied_flex_item->next != NULL)
+ implied_flex_item->next->prev = implied_flex_item;
+ else
+ flex_container->last = implied_flex_item;
+ implied_flex_item->parent = flex_container;
+
+ if (box_normalise_block(implied_flex_item,
+ root, c) == false)
+ return false;
+ break;
+
+ case BOX_TABLE:
+ if (box_normalise_table(child, root, c) == false)
+ return false;
+ break;
+ case BOX_INLINE:
+ case BOX_INLINE_END:
+ case BOX_INLINE_FLEX:
+ case BOX_INLINE_BLOCK:
+ case BOX_FLOAT_LEFT:
+ case BOX_FLOAT_RIGHT:
+ case BOX_BR:
+ case BOX_TEXT:
+ /* should have been wrapped in inline
+ container by convert_xml_to_box() */
+ assert(0);
+ break;
+ case BOX_TABLE_ROW_GROUP:
+ case BOX_TABLE_ROW:
+ case BOX_TABLE_CELL:
+ /* insert implied table */
+ assert(flex_container->style != NULL);
+
+ ctx.ctx = c->select_ctx;
+ ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL);
+ ctx.base_url = c->base_url;
+ ctx.universal = c->universal;
+
+ style = nscss_get_blank_style(&ctx, &c->unit_len_ctx,
+ flex_container->style);
+ if (style == NULL)
+ return false;
+
+ implied_flex_item = box_create(NULL, style, true,
+ flex_container->href,
+ flex_container->target,
+ NULL, NULL, c->bctx);
+ if (implied_flex_item == NULL) {
+ css_computed_style_destroy(style);
+ return false;
+ }
+ implied_flex_item->type = BOX_TABLE;
+
+ if (child->prev == NULL)
+ flex_container->children = implied_flex_item;
+ else
+ child->prev->next = implied_flex_item;
+
+ implied_flex_item->prev = child->prev;
+
+ while (child != NULL && (
+ child->type == BOX_TABLE_ROW_GROUP ||
+ child->type == BOX_TABLE_ROW ||
+ child->type == BOX_TABLE_CELL)) {
+ box_add_child(implied_flex_item, child);
+
+ next_child = child->next;
+ child->next = NULL;
+ child = next_child;
+ }
+
+ implied_flex_item->last->next = NULL;
+ implied_flex_item->next = next_child = child;
+ if (implied_flex_item->next != NULL)
+ implied_flex_item->next->prev = implied_flex_item;
+ else
+ flex_container->last = implied_flex_item;
+ implied_flex_item->parent = flex_container;
+
+ if (box_normalise_table(implied_flex_item,
+ root, c) == false)
+ return false;
+ break;
+ default:
+ assert(0);
+ }
+ }
+
+ return true;
+}
static bool
box_normalise_inline_container(struct box *cont,
@@ -836,6 +1020,11 @@ box_normalise_inline_container(struct box *cont,
if (box_normalise_block(child, root, c) == false)
return false;
break;
+ case BOX_INLINE_FLEX:
+ /* ok */
+ if (box_normalise_flex(child, root, c) == false)
+ return false;
+ break;
case BOX_FLOAT_LEFT:
case BOX_FLOAT_RIGHT:
/* ok */
@@ -852,6 +1041,11 @@ box_normalise_inline_container(struct box *cont,
c) == false)
return false;
break;
+ case BOX_FLEX:
+ if (box_normalise_flex(child->children, root,
+ c) == false)
+ return false;
+ break;
default:
assert(0);
}
@@ -870,6 +1064,7 @@ box_normalise_inline_container(struct box *cont,
box_free(child);
}
break;
+ case BOX_FLEX:
case BOX_BLOCK:
case BOX_INLINE_CONTAINER:
case BOX_TABLE:
@@ -888,7 +1083,6 @@ box_normalise_inline_container(struct box *cont,
return true;
}
-
/* Exported function documented in html/box_normalise.h */
bool
box_normalise_block(struct box *block, const struct box *root, html_content *c)
@@ -920,6 +1114,11 @@ box_normalise_block(struct box *block, const struct box *root, html_content *c)
next_child = child->next; /* child may be destroyed */
switch (child->type) {
+ case BOX_FLEX:
+ /* ok */
+ if (box_normalise_flex(child, root, c) == false)
+ return false;
+ break;
case BOX_BLOCK:
/* ok */
if (box_normalise_block(child, root, c) == false)
@@ -935,6 +1134,7 @@ box_normalise_block(struct box *block, const struct box *root, html_content *c)
break;
case BOX_INLINE:
case BOX_INLINE_END:
+ case BOX_INLINE_FLEX:
case BOX_INLINE_BLOCK:
case BOX_FLOAT_LEFT:
case BOX_FLOAT_RIGHT:
diff --git a/content/handlers/html/box_normalise.h b/content/handlers/html/box_normalise.h
index 591feab8d..377cd9019 100644
--- a/content/handlers/html/box_normalise.h
+++ b/content/handlers/html/box_normalise.h
@@ -50,14 +50,15 @@
* The tree is modified to satisfy the following:
* \code
* parent permitted child nodes
- * BLOCK, INLINE_BLOCK BLOCK, INLINE_CONTAINER, TABLE
- * INLINE_CONTAINER INLINE, INLINE_BLOCK, FLOAT_LEFT, FLOAT_RIGHT, BR, TEXT
+ * BLOCK, INLINE_BLOCK BLOCK, INLINE_CONTAINER, TABLE, FLEX
+ * FLEX, INLINE_FLEX BLOCK, INLINE_CONTAINER, TABLE, FLEX
+ * INLINE_CONTAINER INLINE, INLINE_BLOCK, FLOAT_LEFT, FLOAT_RIGHT, BR, TEXT, INLINE_FLEX
* INLINE, TEXT none
* TABLE at least 1 TABLE_ROW_GROUP
* TABLE_ROW_GROUP at least 1 TABLE_ROW
* TABLE_ROW at least 1 TABLE_CELL
- * TABLE_CELL BLOCK, INLINE_CONTAINER, TABLE (same as BLOCK)
- * FLOAT_(LEFT|RIGHT) exactly 1 BLOCK or TABLE
+ * TABLE_CELL BLOCK, INLINE_CONTAINER, TABLE, FLEX (same as BLOCK)
+ * FLOAT_(LEFT|RIGHT) exactly 1 BLOCK, TABLE or FLEX
* \endcode
*/
bool box_normalise_block(struct box *block, const struct box *root, struct html_content *c);
diff --git a/content/handlers/html/box_special.c b/content/handlers/html/box_special.c
index f761557e0..db3c4126d 100644
--- a/content/handlers/html/box_special.c
+++ b/content/handlers/html/box_special.c
@@ -560,8 +560,18 @@ static bool
box_input_text(html_content *html, struct box *box, struct dom_node *node)
{
struct box *inline_container, *inline_box;
+ uint8_t display = css_computed_display_static(box->style);
- box->type = BOX_INLINE_BLOCK;
+ switch (display) {
+ case CSS_DISPLAY_GRID:
+ case CSS_DISPLAY_FLEX:
+ case CSS_DISPLAY_BLOCK:
+ box->type = BOX_BLOCK;
+ break;
+ default:
+ box->type = BOX_INLINE_BLOCK;
+ break;
+ }
inline_container = box_create(NULL, 0, false, 0, 0, 0, 0, html->bctx);
if (!inline_container)
@@ -825,8 +835,8 @@ box_canvas(dom_node *n,
}
*convert_children = false;
- if (box->style &&
- ns_computed_display(box->style, box_is_root(n)) == CSS_DISPLAY_NONE)
+ if (box->style && ns_computed_display(box->style,
+ box_is_root(n)) == CSS_DISPLAY_NONE)
return true;
/* This is replaced content */
@@ -854,8 +864,8 @@ box_embed(dom_node *n,
dom_string *src;
dom_exception err;
- if (box->style &&
- ns_computed_display(box->style, box_is_root(n)) == CSS_DISPLAY_NONE)
+ if (box->style && ns_computed_display(box->style,
+ box_is_root(n)) == CSS_DISPLAY_NONE)
return true;
params = talloc(content->bctx, struct object_params);
@@ -1025,8 +1035,8 @@ box_iframe(dom_node *n,
struct content_html_iframe *iframe;
int i;
- if (box->style &&
- ns_computed_display(box->style, box_is_root(n)) == CSS_DISPLAY_NONE)
+ if (box->style && ns_computed_display(box->style,
+ box_is_root(n)) == CSS_DISPLAY_NONE)
return true;
if (box->style &&
@@ -1154,8 +1164,8 @@ box_image(dom_node *n,
css_unit wunit = CSS_UNIT_PX;
css_unit hunit = CSS_UNIT_PX;
- if (box->style &&
- ns_computed_display(box->style, box_is_root(n)) == CSS_DISPLAY_NONE)
+ if (box->style && ns_computed_display(box->style,
+ box_is_root(n)) == CSS_DISPLAY_NONE)
return true;
/* handle alt text */
@@ -1322,10 +1332,9 @@ box_input(dom_node *n,
corestring_lwc_image)) {
gadget->type = GADGET_IMAGE;
- if (box->style &&
- ns_computed_display(box->style,
+ if (box->style && ns_computed_display(box->style,
box_is_root(n)) != CSS_DISPLAY_NONE &&
- nsoption_bool(foreground_images) == true) {
+ nsoption_bool(foreground_images) == true) {
dom_string *s;
err = dom_element_get_attribute(n, corestring_dom_src, &s);
@@ -1405,8 +1414,8 @@ box_object(dom_node *n,
dom_node *c;
dom_exception err;
- if (box->style &&
- ns_computed_display(box->style, box_is_root(n)) == CSS_DISPLAY_NONE)
+ if (box->style && ns_computed_display(box->style,
+ box_is_root(n)) == CSS_DISPLAY_NONE)
return true;
if (box_get_attribute(n, "usemap", content->bctx, &box->usemap) ==
diff --git a/content/handlers/html/layout.c b/content/handlers/html/layout.c
index c06fdf690..d9de14b37 100644
--- a/content/handlers/html/layout.c
+++ b/content/handlers/html/layout.c
@@ -67,24 +67,11 @@
#include "html/font.h"
#include "html/form_internal.h"
#include "html/layout.h"
+#include "html/layout_internal.h"
#include "html/table.h"
-#define AUTO INT_MIN
-
-/* Fixed point percentage (a) of an integer (b), to an integer */
-#define FPCT_OF_INT_TOINT(a, b) (FIXTOINT(FDIV((a * b), F_100)))
-
-typedef uint8_t (*css_len_func)(
- const css_computed_style *style,
- css_fixed *length, css_unit *unit);
-typedef uint8_t (*css_border_style_func)(
- const css_computed_style *style);
-typedef uint8_t (*css_border_color_func)(
- const css_computed_style *style,
- css_color *color);
-
/** Array of per-side access functions for computed style margins. */
-static const css_len_func margin_funcs[4] = {
+const css_len_func margin_funcs[4] = {
[TOP] = css_computed_margin_top,
[RIGHT] = css_computed_margin_right,
[BOTTOM] = css_computed_margin_bottom,
@@ -92,7 +79,7 @@ static const css_len_func margin_funcs[4] = {
};
/** Array of per-side access functions for computed style paddings. */
-static const css_len_func padding_funcs[4] = {
+const css_len_func padding_funcs[4] = {
[TOP] = css_computed_padding_top,
[RIGHT] = css_computed_padding_right,
[BOTTOM] = css_computed_padding_bottom,
@@ -100,7 +87,7 @@ static const css_len_func padding_funcs[4] = {
};
/** Array of per-side access functions for computed style border_widths. */
-static const css_len_func border_width_funcs[4] = {
+const css_len_func border_width_funcs[4] = {
[TOP] = css_computed_border_top_width,
[RIGHT] = css_computed_border_right_width,
[BOTTOM] = css_computed_border_bottom_width,
@@ -108,7 +95,7 @@ static const css_len_func border_width_funcs[4] = {
};
/** Array of per-side access functions for computed style border styles. */
-static const css_border_style_func border_style_funcs[4] = {
+const css_border_style_func border_style_funcs[4] = {
[TOP] = css_computed_border_top_style,
[RIGHT] = css_computed_border_right_style,
[BOTTOM] = css_computed_border_bottom_style,
@@ -116,7 +103,7 @@ static const css_border_style_func border_style_funcs[4] = {
};
/** Array of per-side access functions for computed style border colors. */
-static const css_border_color_func border_color_funcs[4] = {
+const css_border_color_func border_color_funcs[4] = {
[TOP] = css_computed_border_top_color,
[RIGHT] = css_computed_border_right_color,
[BOTTOM] = css_computed_border_bottom_color,
@@ -124,10 +111,6 @@ static const css_border_color_func border_color_funcs[4] = {
};
/* forward declaration to break cycles */
-static bool layout_block_context(
- struct box *block,
- int viewport_height,
- html_content *content);
static void layout_minmax_block(
struct box *block,
const struct gui_layout_table *font_func,
@@ -263,75 +246,6 @@ static int layout_text_indent(
/**
- * Determine width of margin, borders, and padding on one side of a box.
- *
- * \param unit_len_ctx CSS length conversion context for document
- * \param style style to measure
- * \param side side of box to measure
- * \param margin whether margin width is required
- * \param border whether border width is required
- * \param padding whether padding width is required
- * \param fixed increased by sum of fixed margin, border, and padding
- * \param frac increased by sum of fractional margin and padding
- */
-static void
-calculate_mbp_width(const css_unit_ctx *unit_len_ctx,
- const css_computed_style *style,
- unsigned int side,
- bool margin,
- bool border,
- bool padding,
- int *fixed,
- float *frac)
-{
- css_fixed value = 0;
- css_unit unit = CSS_UNIT_PX;
-
- assert(style);
-
- /* margin */
- if (margin) {
- enum css_margin_e type;
-
- type = margin_funcs[side](style, &value, &unit);
- if (type == CSS_MARGIN_SET) {
- if (unit == CSS_UNIT_PCT) {
- *frac += FIXTOINT(FDIV(value, F_100));
- } else {
- *fixed += FIXTOINT(css_unit_len2device_px(
- style, unit_len_ctx,
- value, unit));
- }
- }
- }
-
- /* border */
- if (border) {
- if (border_style_funcs[side](style) !=
- CSS_BORDER_STYLE_NONE) {
- border_width_funcs[side](style, &value, &unit);
-
- *fixed += FIXTOINT(css_unit_len2device_px(
- style, unit_len_ctx,
- value, unit));
- }
- }
-
- /* padding */
- if (padding) {
- padding_funcs[side](style, &value, &unit);
- if (unit == CSS_UNIT_PCT) {
- *frac += FIXTOINT(FDIV(value, F_100));
- } else {
- *fixed += FIXTOINT(css_unit_len2device_px(
- style, unit_len_ctx,
- value, unit));
- }
- }
-}
-
-
-/**
* Calculate minimum and maximum width of a table.
*
* \param table box of type TABLE
@@ -360,7 +274,7 @@ static void layout_minmax_table(struct box *table,
return;
if (table_calculate_column_types(&content->unit_len_ctx, table) == false) {
- NSLOG(netsurf, WARNING,
+ NSLOG(netsurf, ERROR,
"Could not establish table column types.");
return;
}
@@ -588,11 +502,7 @@ layout_minmax_line(struct box *first,
css_fixed value = 0;
css_unit unit = CSS_UNIT_PX;
- assert(b->type == BOX_INLINE || b->type == BOX_INLINE_BLOCK ||
- b->type == BOX_FLOAT_LEFT ||
- b->type == BOX_FLOAT_RIGHT ||
- b->type == BOX_BR || b->type == BOX_TEXT ||
- b->type == BOX_INLINE_END);
+ assert(lh__box_is_inline_content(b));
NSLOG(layout, DEBUG, "%p: min %i, max %i", b, min, max);
@@ -601,13 +511,13 @@ layout_minmax_line(struct box *first,
break;
}
- if (b->type == BOX_FLOAT_LEFT || b->type == BOX_FLOAT_RIGHT) {
+ if (lh__box_is_float_box(b)) {
assert(b->children);
- if (b->children->type == BOX_BLOCK)
- layout_minmax_block(b->children, font_func,
+ if (b->children->type == BOX_TABLE)
+ layout_minmax_table(b->children, font_func,
content);
else
- layout_minmax_table(b->children, font_func,
+ layout_minmax_block(b->children, font_func,
content);
b->min_width = b->children->min_width;
b->max_width = b->children->max_width;
@@ -617,7 +527,7 @@ layout_minmax_line(struct box *first,
continue;
}
- if (b->type == BOX_INLINE_BLOCK) {
+ if (b->type == BOX_INLINE_BLOCK || b->type == BOX_INLINE_FLEX) {
layout_minmax_block(b, font_func, content);
if (min < b->min_width)
min = b->min_width;
@@ -668,8 +578,7 @@ layout_minmax_line(struct box *first,
continue;
}
- if (!b->object && !(b->flags & IFRAME) && !b->gadget &&
- !(b->flags & REPLACE_DIM)) {
+ if (lh__box_is_replace(b) == false) {
/* inline non-replaced, 10.3.1 and 10.6.1 */
bool no_wrap_box;
if (!b->text)
@@ -951,7 +860,6 @@ layout_minmax_inline_container(struct box *inline_container,
inline_container->max_width);
}
-
/**
* Calculate minimum and maximum width of a block.
*
@@ -977,9 +885,13 @@ static void layout_minmax_block(
css_fixed height = 0;
css_unit hunit = CSS_UNIT_PX;
enum css_box_sizing_e bs = CSS_BOX_SIZING_CONTENT_BOX;
+ bool using_min_border_box = false;
+ bool using_max_border_box = false;
bool child_has_height = false;
assert(block->type == BOX_BLOCK ||
+ block->type == BOX_FLEX ||
+ block->type == BOX_INLINE_FLEX ||
block->type == BOX_INLINE_BLOCK ||
block->type == BOX_TABLE_CELL);
@@ -994,9 +906,9 @@ static void layout_minmax_block(
}
/* set whether the minimum width is of any interest for this box */
- if (((block->parent && (block->parent->type == BOX_FLOAT_LEFT ||
- block->parent->type == BOX_FLOAT_RIGHT)) ||
- block->type == BOX_INLINE_BLOCK) &&
+ if (((block->parent && lh__box_is_float_box(block->parent)) ||
+ block->type == BOX_INLINE_BLOCK ||
+ block->type == BOX_INLINE_FLEX) &&
wtype != CSS_WIDTH_SET) {
/* box shrinks to fit; need minimum width */
block->flags |= NEED_MIN;
@@ -1007,6 +919,9 @@ static void layout_minmax_block(
wtype != CSS_WIDTH_SET) {
/* box inside shrink-to-fit context; need minimum width */
block->flags |= NEED_MIN;
+ } else if (block->parent && (block->parent->type == BOX_FLEX)) {
+ /* box is flex item */
+ block->flags |= NEED_MIN;
}
if (block->gadget && (block->gadget->type == GADGET_TEXTBOX ||
@@ -1056,6 +971,7 @@ static void layout_minmax_block(
/* recurse through children */
for (child = block->children; child; child = child->next) {
switch (child->type) {
+ case BOX_FLEX:
case BOX_BLOCK:
layout_minmax_block(child, font_func,
content);
@@ -1097,10 +1013,24 @@ static void layout_minmax_block(
continue;
}
- if (min < child->min_width)
- min = child->min_width;
- if (max < child->max_width)
- max = child->max_width;
+ if (lh__box_is_flex_container(block) &&
+ lh__flex_main_is_horizontal(block)) {
+ if (block->style != NULL &&
+ css_computed_flex_wrap(block->style) ==
+ CSS_FLEX_WRAP_NOWRAP) {
+ min += child->min_width;
+ } else {
+ if (min < child->min_width)
+ min = child->min_width;
+ }
+ max += child->max_width;
+
+ } else {
+ if (min < child->min_width)
+ min = child->min_width;
+ if (max < child->max_width)
+ max = child->max_width;
+ }
if (child_has_height)
block->flags |= HAS_HEIGHT;
@@ -1113,23 +1043,40 @@ static void layout_minmax_block(
}
/* fixed width takes priority */
- if (block->type != BOX_TABLE_CELL && wtype == CSS_WIDTH_SET &&
- wunit != CSS_UNIT_PCT) {
- min = max = FIXTOINT(css_unit_len2device_px(block->style,
- &content->unit_len_ctx, width, wunit));
- if (bs == CSS_BOX_SIZING_BORDER_BOX) {
- int border_box_fixed = 0;
- float border_box_frac = 0;
- calculate_mbp_width(&content->unit_len_ctx,
- block->style, LEFT,
- false, true, true,
- &border_box_fixed, &border_box_frac);
- calculate_mbp_width(&content->unit_len_ctx,
- block->style, RIGHT,
- false, true, true,
- &border_box_fixed, &border_box_frac);
- if (min < border_box_fixed) {
- min = max = border_box_fixed;
+ if (block->type != BOX_TABLE_CELL && !lh__box_is_flex_item(block)) {
+ bool border_box = bs == CSS_BOX_SIZING_BORDER_BOX;
+ enum css_max_width_e max_type;
+ enum css_min_width_e min_type;
+ css_unit unit = CSS_UNIT_PX;
+ css_fixed value = 0;
+
+ if (wtype == CSS_WIDTH_SET && wunit != CSS_UNIT_PCT) {
+ min = max = FIXTOINT(
+ css_unit_len2device_px(block->style,
+ &content->unit_len_ctx, width, wunit));
+ using_max_border_box = border_box;
+ using_min_border_box = border_box;
+ }
+
+ min_type = css_computed_min_width(block->style, &value, &unit);
+ if (min_type == CSS_MIN_WIDTH_SET && unit != CSS_UNIT_PCT) {
+ int val = FIXTOINT(css_unit_len2device_px(block->style,
+ &content->unit_len_ctx, value, unit));
+
+ if (min < val) {
+ min = val;
+ using_min_border_box = border_box;
+ }
+ }
+
+ max_type = css_computed_max_width(block->style, &value, &unit);
+ if (max_type == CSS_MAX_WIDTH_SET && unit != CSS_UNIT_PCT) {
+ int val = FIXTOINT(css_unit_len2device_px(block->style,
+ &content->unit_len_ctx, value, unit));
+
+ if (val >= 0 && max > val) {
+ max = val;
+ using_max_border_box = border_box;
}
}
}
@@ -1143,22 +1090,30 @@ static void layout_minmax_block(
/* add margins, border, padding to min, max widths */
/* Note: we don't know available width here so percentage margin
* and paddings are wrong. */
- if (bs == CSS_BOX_SIZING_BORDER_BOX && wtype == CSS_WIDTH_SET) {
- /* Border and padding included in width, so just get margin */
- calculate_mbp_width(&content->unit_len_ctx,
- block->style, LEFT, true, false, false,
- &extra_fixed, &extra_frac);
- calculate_mbp_width(&content->unit_len_ctx,
- block->style, RIGHT, true, false, false,
- &extra_fixed, &extra_frac);
- } else {
- calculate_mbp_width(&content->unit_len_ctx,
- block->style, LEFT, true, true, true,
- &extra_fixed, &extra_frac);
- calculate_mbp_width(&content->unit_len_ctx,
- block->style, RIGHT, true, true, true,
- &extra_fixed, &extra_frac);
+ calculate_mbp_width(&content->unit_len_ctx, block->style, LEFT,
+ false, true, true, &extra_fixed, &extra_frac);
+ calculate_mbp_width(&content->unit_len_ctx, block->style, RIGHT,
+ false, true, true, &extra_fixed, &extra_frac);
+
+ if (using_max_border_box) {
+ max -= extra_fixed;
+ max = max(max, 0);
+ }
+
+ if (using_min_border_box) {
+ min -= extra_fixed;
+ min = max(min, 0);
+ }
+
+ if (max < min) {
+ min = max;
}
+
+ calculate_mbp_width(&content->unit_len_ctx, block->style, LEFT,
+ true, false, false, &extra_fixed, &extra_frac);
+ calculate_mbp_width(&content->unit_len_ctx, block->style, RIGHT,
+ true, false, false, &extra_fixed, &extra_frac);
+
if (extra_fixed < 0)
extra_fixed = 0;
if (extra_frac < 0)
@@ -1177,376 +1132,8 @@ static void layout_minmax_block(
block->max_width = (max + extra_fixed) / (1.0 - extra_frac);
}
- assert(0 <= block->min_width && block->min_width <= block->max_width);
-}
-
-
-/**
- * Adjust a specified width or height for the box-sizing property.
- *
- * This turns the specified dimension into a content-box dimension.
- *
- * \param unit_len_ctx Length conversion context
- * \param box gadget to adjust dimensions of
- * \param available_width width of containing block
- * \param setwidth set true if the dimension to be tweaked is a width,
- * else set false for a height
- * \param dimension current value for given width/height dimension.
- * updated to new value after consideration of
- * gadget properties.
- */
-static void layout_handle_box_sizing(
- const css_unit_ctx *unit_len_ctx,
- struct box *box,
- int available_width,
- bool setwidth,
- int *dimension)
-{
- enum css_box_sizing_e bs;
-
- assert(box && box->style);
-
- bs = css_computed_box_sizing(box->style);
-
- if (bs == CSS_BOX_SIZING_BORDER_BOX) {
- int orig = *dimension;
- int fixed = 0;
- float frac = 0;
-
- calculate_mbp_width(unit_len_ctx, box->style,
- setwidth ? LEFT : TOP,
- false, true, true, &fixed, &frac);
- calculate_mbp_width(unit_len_ctx, box->style,
- setwidth ? RIGHT : BOTTOM,
- false, true, true, &fixed, &frac);
- orig -= frac * available_width + fixed;
- *dimension = orig > 0 ? orig : 0;
- }
-}
-
-
-/**
- * Calculate width, height, and thickness of margins, paddings, and borders.
- *
- * \param unit_len_ctx Length conversion context
- * \param available_width width of containing block
- * \param viewport_height height of viewport in pixels or -ve if unknown
- * \param box current box
- * \param style style giving width, height, margins, paddings,
- * and borders
- * \param width updated to width, may be NULL
- * \param height updated to height, may be NULL
- * \param max_width updated to max-width, may be NULL
- * \param min_width updated to min-width, may be NULL
- * \param max_height updated to max-height, may be NULL
- * \param min_height updated to min-height, may be NULL
- * \param margin filled with margins, may be NULL
- * \param padding filled with paddings, may be NULL
- * \param border filled with border widths, may be NULL
- */
-static void
-layout_find_dimensions(const css_unit_ctx *unit_len_ctx,
- int available_width,
- int viewport_height,
- struct box *box,
- const css_computed_style *style,
- int *width,
- int *height,
- int *max_width,
- int *min_width,
- int *max_height,
- int *min_height,
- int margin[4],
- int padding[4],
- struct box_border border[4])
-{
- struct box *containing_block = NULL;
- unsigned int i;
-
- if (width) {
- enum css_width_e wtype;
- css_fixed value = 0;
- css_unit unit = CSS_UNIT_PX;
-
- wtype = css_computed_width(style, &value, &unit);
-
- if (wtype == CSS_WIDTH_SET) {
- if (unit == CSS_UNIT_PCT) {
- *width = FPCT_OF_INT_TOINT(
- value, available_width);
- } else {
- *width = FIXTOINT(css_unit_len2device_px(
- style, unit_len_ctx,
- value, unit));
- }
- } else {
- *width = AUTO;
- }
-
- if (*width != AUTO) {
- layout_handle_box_sizing(unit_len_ctx, box, available_width,
- true, width);
- }
- }
-
- if (height) {
- enum css_height_e htype;
- css_fixed value = 0;
- css_unit unit = CSS_UNIT_PX;
-
- htype = css_computed_height(style, &value, &unit);
-
- if (htype == CSS_HEIGHT_SET) {
- if (unit == CSS_UNIT_PCT) {
- enum css_height_e cbhtype;
-
- if (css_computed_position(box->style) ==
- CSS_POSITION_ABSOLUTE &&
- box->parent) {
- /* Box is absolutely positioned */
- assert(box->float_container);
- containing_block = box->float_container;
- } else if (box->float_container &&
- css_computed_position(box->style) !=
- CSS_POSITION_ABSOLUTE &&
- (css_computed_float(box->style) ==
- CSS_FLOAT_LEFT ||
- css_computed_float(box->style) ==
- CSS_FLOAT_RIGHT)) {
- /* Box is a float */
- assert(box->parent &&
- box->parent->parent &&
- box->parent->parent->parent);
-
- containing_block =
- box->parent->parent->parent;
- } else if (box->parent && box->parent->type !=
- BOX_INLINE_CONTAINER) {
- /* Box is a block level element */
- containing_block = box->parent;
- } else if (box->parent && box->parent->type ==
- BOX_INLINE_CONTAINER) {
- /* Box is an inline block */
- assert(box->parent->parent);
- containing_block = box->parent->parent;
- }
-
- if (containing_block) {
- css_fixed f = 0;
- css_unit u = CSS_UNIT_PX;
-
- cbhtype = css_computed_height(
- containing_block->style,
- &f, &u);
- }
-
- if (containing_block &&
- containing_block->height != AUTO &&
- (css_computed_position(box->style) ==
- CSS_POSITION_ABSOLUTE ||
- cbhtype == CSS_HEIGHT_SET)) {
- /* Box is absolutely positioned or its
- * containing block has a valid
- * specified height.
- * (CSS 2.1 Section 10.5) */
- *height = FPCT_OF_INT_TOINT(value,
- containing_block->height);
- } else if ((!box->parent ||
- !box->parent->parent) &&
- viewport_height >= 0) {
- /* If root element or it's child
- * (HTML or BODY) */
- *height = FPCT_OF_INT_TOINT(value,
- viewport_height);
- } else {
- /* precentage height not permissible
- * treat height as auto */
- *height = AUTO;
- }
- } else {
- *height = FIXTOINT(css_unit_len2device_px(
- style, unit_len_ctx,
- value, unit));
- }
- } else {
- *height = AUTO;
- }
-
- if (*height != AUTO) {
- layout_handle_box_sizing(unit_len_ctx, box, available_width,
- false, height);
- }
- }
-
- if (max_width) {
- enum css_max_width_e type;
- css_fixed value = 0;
- css_unit unit = CSS_UNIT_PX;
-
- type = css_computed_max_width(style, &value, &unit);
-
- if (type == CSS_MAX_WIDTH_SET) {
- if (unit == CSS_UNIT_PCT) {
- *max_width = FPCT_OF_INT_TOINT(value,
- available_width);
- } else {
- *max_width = FIXTOINT(css_unit_len2device_px(
- style, unit_len_ctx,
- value, unit));
- }
- } else {
- /* Inadmissible */
- *max_width = -1;
- }
-
- if (*max_width != -1) {
- layout_handle_box_sizing(unit_len_ctx, box, available_width,
- true, max_width);
- }
- }
-
- if (min_width) {
- enum css_min_width_e type;
- css_fixed value = 0;
- css_unit unit = CSS_UNIT_PX;
-
- type = ns_computed_min_width(style, &value, &unit);
-
- if (type == CSS_MIN_WIDTH_SET) {
- if (unit == CSS_UNIT_PCT) {
- *min_width = FPCT_OF_INT_TOINT(value,
- available_width);
- } else {
- *min_width = FIXTOINT(css_unit_len2device_px(
- style, unit_len_ctx,
- value, unit));
- }
- } else {
- /* Inadmissible */
- *min_width = 0;
- }
-
- if (*min_width != 0) {
- layout_handle_box_sizing(unit_len_ctx, box, available_width,
- true, min_width);
- }
- }
-
- if (max_height) {
- enum css_max_height_e type;
- css_fixed value = 0;
- css_unit unit = CSS_UNIT_PX;
-
- type = css_computed_max_height(style, &value, &unit);
-
- if (type == CSS_MAX_HEIGHT_SET) {
- if (unit == CSS_UNIT_PCT) {
- /* TODO: handle percentage */
- *max_height = -1;
- } else {
- *max_height = FIXTOINT(css_unit_len2device_px(
- style, unit_len_ctx,
- value, unit));
- }
- } else {
- /* Inadmissible */
- *max_height = -1;
- }
- }
-
- if (min_height) {
- enum css_min_height_e type;
- css_fixed value = 0;
- css_unit unit = CSS_UNIT_PX;
-
- type = ns_computed_min_height(style, &value, &unit);
-
- if (type == CSS_MIN_HEIGHT_SET) {
- if (unit == CSS_UNIT_PCT) {
- /* TODO: handle percentage */
- *min_height = 0;
- } else {
- *min_height = FIXTOINT(css_unit_len2device_px(
- style, unit_len_ctx,
- value, unit));
- }
- } else {
- /* Inadmissible */
- *min_height = 0;
- }
- }
-
- for (i = 0; i != 4; i++) {
- if (margin) {
- enum css_margin_e type = CSS_MARGIN_AUTO;
- css_fixed value = 0;
- css_unit unit = CSS_UNIT_PX;
-
- type = margin_funcs[i](style, &value, &unit);
-
- if (type == CSS_MARGIN_SET) {
- if (unit == CSS_UNIT_PCT) {
- margin[i] = FPCT_OF_INT_TOINT(value,
- available_width);
- } else {
- margin[i] = FIXTOINT(css_unit_len2device_px(
- style, unit_len_ctx,
- value, unit));
- }
- } else {
- margin[i] = AUTO;
- }
- }
-
- if (padding) {
- css_fixed value = 0;
- css_unit unit = CSS_UNIT_PX;
-
- padding_funcs[i](style, &value, &unit);
-
- if (unit == CSS_UNIT_PCT) {
- padding[i] = FPCT_OF_INT_TOINT(value,
- available_width);
- } else {
- padding[i] = FIXTOINT(css_unit_len2device_px(
- style, unit_len_ctx,
- value, unit));
- }
- }
-
- /* Table cell borders are populated in table.c */
- if (border && box->type != BOX_TABLE_CELL) {
- enum css_border_style_e bstyle = CSS_BORDER_STYLE_NONE;
- css_color color = 0;
- css_fixed value = 0;
- css_unit unit = CSS_UNIT_PX;
-
- border_width_funcs[i](style, &value, &unit);
- bstyle = border_style_funcs[i](style);
- border_color_funcs[i](style, &color);
-
- border[i].style = bstyle;
- border[i].c = color;
-
- if (bstyle == CSS_BORDER_STYLE_HIDDEN ||
- bstyle == CSS_BORDER_STYLE_NONE)
- /* spec unclear: following Mozilla */
- border[i].width = 0;
- else
- border[i].width = FIXTOINT(css_unit_len2device_px(
- style, unit_len_ctx,
- value, unit));
-
- /* Special case for border-collapse: make all borders
- * on table/table-row-group/table-row zero width. */
- if (css_computed_border_collapse(style) ==
- CSS_BORDER_COLLAPSE_COLLAPSE &&
- (box->type == BOX_TABLE ||
- box->type == BOX_TABLE_ROW_GROUP ||
- box->type == BOX_TABLE_ROW))
- border[i].width = 0;
- }
- }
+ assert(0 <= block->min_width);
+ assert(block->min_width <= block->max_width);
}
@@ -2013,15 +1600,10 @@ static void layout_move_children(struct box *box, int x, int y)
}
-/**
- * Layout a table.
- *
- * \param table table to layout
- * \param available_width width of containing block
- * \param content memory pool for any new boxes
- * \return true on success, false on memory exhaustion
- */
-static bool layout_table(struct box *table, int available_width,
+/* Documented in layout_internal.h */
+bool layout_table(
+ struct box *table,
+ int available_width,
html_content *content)
{
unsigned int columns = table->columns; /* total columns */
@@ -2713,7 +2295,9 @@ static bool layout_block_object(struct box *block)
{
assert(block);
assert(block->type == BOX_BLOCK ||
+ block->type == BOX_FLEX ||
block->type == BOX_INLINE_BLOCK ||
+ block->type == BOX_INLINE_FLEX ||
block->type == BOX_TABLE ||
block->type == BOX_TABLE_CELL);
assert(block->object);
@@ -3007,12 +2591,20 @@ layout_float_find_dimensions(
*/
static bool layout_float(struct box *b, int width, html_content *content)
{
- assert(b->type == BOX_TABLE || b->type == BOX_BLOCK ||
- b->type == BOX_INLINE_BLOCK);
+ assert(b->type == BOX_TABLE ||
+ b->type == BOX_BLOCK ||
+ b->type == BOX_INLINE_BLOCK ||
+ b->type == BOX_FLEX ||
+ b->type == BOX_INLINE_FLEX);
layout_float_find_dimensions(&content->unit_len_ctx, width, b->style, b);
- if (b->type == BOX_TABLE) {
- if (!layout_table(b, width, content))
- return false;
+ if (b->type == BOX_TABLE || b->type == BOX_INLINE_FLEX) {
+ if (b->type == BOX_TABLE) {
+ if (!layout_table(b, width, content))
+ return false;
+ } else {
+ if (!layout_flex(b, width, content))
+ return false;
+ }
if (b->margin[LEFT] == AUTO)
b->margin[LEFT] = 0;
if (b->margin[RIGHT] == AUTO)
@@ -3021,8 +2613,9 @@ static bool layout_float(struct box *b, int width, html_content *content)
b->margin[TOP] = 0;
if (b->margin[BOTTOM] == AUTO)
b->margin[BOTTOM] = 0;
- } else
+ } else {
return layout_block_context(b, -1, content);
+ }
return true;
}
@@ -3209,20 +2802,14 @@ layout_line(struct box *first,
for (x = 0, b = first; x <= x1 - x0 && b != 0; b = b->next) {
int min_width, max_width, min_height, max_height;
- assert(b->type == BOX_INLINE || b->type == BOX_INLINE_BLOCK ||
- b->type == BOX_FLOAT_LEFT ||
- b->type == BOX_FLOAT_RIGHT ||
- b->type == BOX_BR || b->type == BOX_TEXT ||
- b->type == BOX_INLINE_END);
-
+ assert(lh__box_is_inline_content(b));
NSLOG(layout, DEBUG, "pass 1: b %p, x %i", b, x);
-
if (b->type == BOX_BR)
break;
- if (b->type == BOX_FLOAT_LEFT || b->type == BOX_FLOAT_RIGHT)
+ if (lh__box_is_float_box(b))
continue;
if (b->type == BOX_INLINE_BLOCK &&
(css_computed_position(b->style) ==
@@ -3236,7 +2823,8 @@ layout_line(struct box *first,
x += space_after;
- if (b->type == BOX_INLINE_BLOCK) {
+ if (b->type == BOX_INLINE_BLOCK ||
+ b->type == BOX_INLINE_FLEX) {
if (b->max_width != UNKNOWN_WIDTH)
if (!layout_float(b, *width, content))
return false;
@@ -3288,8 +2876,7 @@ layout_line(struct box *first,
continue;
}
- if (!b->object && !(b->flags & IFRAME) && !b->gadget &&
- !(b->flags & REPLACE_DIM)) {
+ if (lh__box_is_replace(b) == false) {
/* inline non-replaced, 10.3.1 and 10.6.1 */
b->height = line_height(&content->unit_len_ctx,
b->style ? b->style :
@@ -3449,10 +3036,7 @@ layout_line(struct box *first,
CSS_POSITION_FIXED)) {
b->x = x + space_after;
- } else if (b->type == BOX_INLINE ||
- b->type == BOX_INLINE_BLOCK ||
- b->type == BOX_TEXT ||
- b->type == BOX_INLINE_END) {
+ } else if (lh__box_is_inline_flow(b)) {
assert(b->width != UNKNOWN_WIDTH);
x_previous = x;
@@ -3460,7 +3044,8 @@ layout_line(struct box *first,
b->x = x;
if ((b->type == BOX_INLINE && !b->inline_end) ||
- b->type == BOX_INLINE_BLOCK) {
+ b->type == BOX_INLINE_BLOCK ||
+ b->type == BOX_INLINE_FLEX) {
b->x += b->margin[LEFT] + b->border[LEFT].width;
x = b->x + b->padding[LEFT] + b->width +
b->padding[RIGHT] +
@@ -3791,9 +3376,7 @@ layout_line(struct box *first,
d->y = *y;
continue;
} else if ((d->type == BOX_INLINE &&
- ((d->object || d->gadget) == false) &&
- !(d->flags & IFRAME) &&
- !(d->flags & REPLACE_DIM)) ||
+ lh__box_is_replace(d) == false) ||
d->type == BOX_BR ||
d->type == BOX_TEXT ||
d->type == BOX_INLINE_END) {
@@ -3914,8 +3497,7 @@ static bool layout_inline_container(struct box *inline_container, int width,
whitespace == CSS_WHITE_SPACE_PRE_WRAP);
}
- if ((!c->object && !(c->flags & REPLACE_DIM) &&
- !(c->flags & IFRAME) &&
+ if ((lh__box_is_object(c) == false &&
c->text && (c->length || is_pre)) ||
c->type == BOX_BR)
has_text_children = true;
@@ -3945,21 +3527,11 @@ static bool layout_inline_container(struct box *inline_container, int width,
}
-/**
- * Layout a block formatting context.
- *
- * \param block BLOCK, INLINE_BLOCK, or TABLE_CELL to layout
- * \param viewport_height Height of viewport in pixels or -ve if unknown
- * \param content Memory pool for any new boxes
- * \return true on success, false on memory exhaustion
- *
- * This function carries out layout of a block and its children, as described
- * in CSS 2.1 9.4.1.
- */
-static bool
-layout_block_context(struct box *block,
- int viewport_height,
- html_content *content)
+/* Documented in layout_intertnal.h */
+bool layout_block_context(
+ struct box *block,
+ int viewport_height,
+ html_content *content)
{
struct box *box;
int cx, cy; /**< current coordinates */
@@ -3974,7 +3546,9 @@ layout_block_context(struct box *block,
assert(block->type == BOX_BLOCK ||
block->type == BOX_INLINE_BLOCK ||
- block->type == BOX_TABLE_CELL);
+ block->type == BOX_TABLE_CELL ||
+ block->type == BOX_FLEX ||
+ block->type == BOX_INLINE_FLEX);
assert(block->width != UNKNOWN_WIDTH);
assert(block->width != AUTO);
@@ -4043,7 +3617,9 @@ layout_block_context(struct box *block,
enum css_overflow_e overflow_x = CSS_OVERFLOW_VISIBLE;
enum css_overflow_e overflow_y = CSS_OVERFLOW_VISIBLE;
- assert(box->type == BOX_BLOCK || box->type == BOX_TABLE ||
+ assert(box->type == BOX_BLOCK ||
+ box->type == BOX_FLEX ||
+ box->type == BOX_TABLE ||
box->type == BOX_INLINE_CONTAINER);
/* Tables are laid out before being positioned, because the
@@ -4094,9 +3670,10 @@ layout_block_context(struct box *block,
* left and right margins to avoid any floats. */
lm = rm = 0;
- if (box->type == BOX_BLOCK || box->flags & IFRAME) {
- if (!box->object && !(box->flags & IFRAME) &&
- !(box->flags & REPLACE_DIM) &&
+ if (box->type == BOX_FLEX ||
+ box->type == BOX_BLOCK ||
+ box->flags & IFRAME) {
+ if (lh__box_is_object(box) == false &&
box->style &&
(overflow_x != CSS_OVERFLOW_VISIBLE ||
overflow_y != CSS_OVERFLOW_VISIBLE)) {
@@ -4181,6 +3758,7 @@ layout_block_context(struct box *block,
/* Vertical margin */
if (((box->type == BOX_BLOCK && (box->flags & HAS_HEIGHT)) ||
+ box->type == BOX_FLEX ||
box->type == BOX_TABLE ||
(box->type == BOX_INLINE_CONTAINER &&
!box_is_first_child(box)) ||
@@ -4205,11 +3783,19 @@ layout_block_context(struct box *block,
/* Unless the box has an overflow style of visible, the box
* establishes a new block context. */
- if (box->type == BOX_BLOCK && box->style &&
- (overflow_x != CSS_OVERFLOW_VISIBLE ||
- overflow_y != CSS_OVERFLOW_VISIBLE)) {
+ if (box->type == BOX_FLEX ||
+ (box->type == BOX_BLOCK && box->style &&
+ (overflow_x != CSS_OVERFLOW_VISIBLE ||
+ overflow_y != CSS_OVERFLOW_VISIBLE))) {
- layout_block_context(box, viewport_height, content);
+ if (box->type == BOX_FLEX) {
+ if (!layout_flex(box, box->width, content)) {
+ return false;
+ }
+ } else {
+ layout_block_context(box,
+ viewport_height, content);
+ }
cy += box->padding[TOP];
@@ -4230,7 +3816,8 @@ layout_block_context(struct box *block,
goto advance_to_next_box;
}
- NSLOG(layout, DEBUG, "box %p, cx %i, cy %i", box, cx, cy);
+ NSLOG(layout, DEBUG, "box %p, cx %i, cy %i, width %i",
+ box, cx, cy, box->width);
/* Layout (except tables). */
if (box->object) {
@@ -4914,9 +4501,9 @@ layout_compute_offsets(const css_unit_ctx *unit_len_ctx,
css_fixed value = 0;
css_unit unit = CSS_UNIT_PX;
- assert(containing_block->width != UNKNOWN_WIDTH &&
- containing_block->width != AUTO &&
- containing_block->height != AUTO);
+ assert(containing_block->width != UNKNOWN_WIDTH);
+ assert(containing_block->width != AUTO);
+ assert(containing_block->height != AUTO);
/* left */
type = css_computed_left(box->style, &value, &unit);
@@ -5006,7 +4593,9 @@ layout_absolute(struct box *box,
int space;
assert(box->type == BOX_BLOCK || box->type == BOX_TABLE ||
- box->type == BOX_INLINE_BLOCK);
+ box->type == BOX_INLINE_BLOCK ||
+ box->type == BOX_FLEX ||
+ box->type == BOX_INLINE_FLEX);
/* The static position is where the box would be if it was not
* absolutely positioned. The x and y are filled in by
@@ -5024,8 +4613,6 @@ layout_absolute(struct box *box,
containing_block->padding[RIGHT];
containing_block->height += containing_block->padding[TOP] +
containing_block->padding[BOTTOM];
- } else {
- /** \todo inline containers */
}
layout_compute_offsets(&content->unit_len_ctx, box, containing_block,
@@ -5243,6 +4830,13 @@ layout_absolute(struct box *box,
box->float_container = NULL;
layout_solve_width(box, box->parent->width, box->width, 0, 0,
-1, -1);
+ } else if (box->type == BOX_FLEX || box->type == BOX_INLINE_FLEX) {
+ /* layout_table also expects the containing block to be
+ * stored in the float_container field */
+ box->float_container = containing_block;
+ if (!layout_flex(box, width, content))
+ return false;
+ box->float_container = NULL;
}
/* 10.6.4 */
@@ -5379,7 +4973,9 @@ layout_position_absolute(struct box *box,
for (c = box->children; c; c = c->next) {
if ((c->type == BOX_BLOCK || c->type == BOX_TABLE ||
- c->type == BOX_INLINE_BLOCK) &&
+ c->type == BOX_INLINE_BLOCK ||
+ c->type == BOX_FLEX ||
+ c->type == BOX_INLINE_FLEX) &&
(css_computed_position(c->style) ==
CSS_POSITION_ABSOLUTE ||
css_computed_position(c->style) ==
diff --git a/content/handlers/html/layout_flex.c b/content/handlers/html/layout_flex.c
new file mode 100644
index 000000000..feaa4b346
--- /dev/null
+++ b/content/handlers/html/layout_flex.c
@@ -0,0 +1,898 @@
+/*
+ * Copyright 2022 Michael Drake <tlsa@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file
+ * HTML layout implementation: display: flex.
+ *
+ * Layout is carried out in two stages:
+ *
+ * 1. + calculation of minimum / maximum box widths, and
+ * + determination of whether block level boxes will have >zero height
+ *
+ * 2. + layout (position and dimensions)
+ *
+ * In most cases the functions for the two stages are a corresponding pair
+ * layout_minmax_X() and layout_X().
+ */
+
+#include <string.h>
+
+#include "utils/log.h"
+#include "utils/utils.h"
+
+#include "html/box.h"
+#include "html/html.h"
+#include "html/private.h"
+#include "html/box_inspect.h"
+#include "html/layout_internal.h"
+
+struct flex_item_data {
+ enum css_flex_basis_e basis;
+ css_fixed basis_length;
+ css_unit basis_unit;
+ struct box *box;
+
+ css_fixed shrink;
+ css_fixed grow;
+
+ int min_main;
+ int max_main;
+ int min_cross;
+ int max_cross;
+
+ int target_main_size;
+ int base_size;
+ int main_size;
+ size_t line;
+
+ bool freeze;
+ bool min_violation;
+ bool max_violation;
+};
+
+struct flex_line_data {
+ int main_size;
+ int cross_size;
+
+ int pos;
+
+ size_t first;
+ size_t count;
+ size_t frozen;
+};
+
+struct flex_ctx {
+ html_content *content;
+ const struct box *flex;
+ const css_unit_ctx *unit_len_ctx;
+
+ int main_size;
+ int cross_size;
+
+ int available_main;
+ int available_cross;
+
+ bool horizontal;
+ enum css_flex_wrap_e wrap;
+
+ struct flex_items {
+ size_t count;
+ struct flex_item_data *data;
+ } item;
+
+ struct flex_lines {
+ size_t count;
+ size_t alloc;
+ struct flex_line_data *data;
+ } line;
+};
+
+static void layout_flex_ctx__destroy(struct flex_ctx *ctx)
+{
+ if (ctx != NULL) {
+ free(ctx->item.data);
+ free(ctx->line.data);
+ free(ctx);
+ }
+}
+
+static struct flex_ctx *layout_flex_ctx__create(
+ html_content *content,
+ const struct box *flex)
+{
+ struct flex_ctx *ctx;
+
+ ctx = calloc(1, sizeof(*ctx));
+ if (ctx == NULL) {
+ return NULL;
+ }
+ ctx->line.alloc = 1;
+
+ ctx->item.count = box_count_children(flex);
+ ctx->item.data = calloc(ctx->item.count, sizeof(*ctx->item.data));
+ if (ctx->item.data == NULL) {
+ layout_flex_ctx__destroy(ctx);
+ return NULL;
+ }
+
+ ctx->line.alloc = 1;
+ ctx->line.data = calloc(ctx->line.alloc, sizeof(*ctx->line.data));
+ if (ctx->line.data == NULL) {
+ layout_flex_ctx__destroy(ctx);
+ return NULL;
+ }
+
+ ctx->flex = flex;
+ ctx->content = content;
+ ctx->unit_len_ctx = &content->unit_len_ctx;
+
+ ctx->wrap = css_computed_flex_wrap(flex->style);
+ ctx->horizontal = lh__flex_main_is_horizontal(flex);
+
+ return ctx;
+}
+
+static bool layout_flex_item(
+ const struct flex_ctx *ctx,
+ const struct flex_item_data *item,
+ int available_width)
+{
+ bool success;
+ struct box *b = item->box;
+
+ switch (b->type) {
+ case BOX_BLOCK:
+ success = layout_block_context(b, -1,
+ ctx->content);
+ break;
+ case BOX_TABLE:
+ b->float_container = b->parent;
+ success = layout_table(b, available_width,
+ ctx->content);
+ b->float_container = NULL;
+ break;
+ case BOX_FLEX:
+ b->float_container = b->parent;
+ success = layout_flex(b, available_width,
+ ctx->content);
+ b->float_container = NULL;
+ break;
+ default:
+ assert(0 && "Bad flex item back type");
+ success = false;
+ break;
+ }
+
+ if (!success) {
+ NSLOG(flex, ERROR, "box %p: layout failed", b);
+ }
+
+ return success;
+}
+
+static inline bool layout_flex__base_and_main_sizes(
+ const struct flex_ctx *ctx,
+ struct flex_item_data *item,
+ int available_width)
+{
+ struct box *b = item->box;
+ int content_min_width = b->min_width;
+ int content_max_width = b->max_width;
+ int delta_outer_main = lh__delta_outer_main(ctx->flex, b);
+
+ NSLOG(flex, DEEPDEBUG, "box %p: delta_outer_main: %i",
+ b, delta_outer_main);
+
+ if (item->basis == CSS_FLEX_BASIS_SET) {
+ if (item->basis_unit == CSS_UNIT_PCT) {
+ item->base_size = FPCT_OF_INT_TOINT(
+ item->basis_length,
+ available_width);
+ } else {
+ item->base_size = FIXTOINT(css_unit_len2device_px(
+ b->style, ctx->unit_len_ctx,
+ item->basis_length,
+ item->basis_unit));
+ }
+
+ } else if (item->basis == CSS_FLEX_BASIS_AUTO) {
+ item->base_size = ctx->horizontal ? b->width : b->height;
+ } else {
+ item->base_size = AUTO;
+ }
+
+ if (ctx->horizontal == false) {
+ if (b->width == AUTO) {
+ b->width = min(max(content_min_width, available_width),
+ content_max_width);
+ b->width -= lh__delta_outer_width(b);
+ }
+
+ if (!layout_flex_item(ctx, item, b->width)) {
+ return false;
+ }
+ }
+
+ if (item->base_size == AUTO) {
+ if (ctx->horizontal == false) {
+ item->base_size = b->height;
+ } else {
+ item->base_size = content_max_width - delta_outer_main;
+ }
+ }
+
+ item->base_size += delta_outer_main;
+
+ if (ctx->horizontal) {
+ item->base_size = min(item->base_size, available_width);
+ item->base_size = max(item->base_size, content_min_width);
+ }
+
+ item->target_main_size = item->base_size;
+ item->main_size = item->base_size;
+
+ if (item->max_main > 0 &&
+ item->main_size > item->max_main + delta_outer_main) {
+ item->main_size = item->max_main + delta_outer_main;
+ }
+
+ if (item->main_size < item->min_main + delta_outer_main) {
+ item->main_size = item->min_main + delta_outer_main;
+ }
+
+ NSLOG(flex, DEEPDEBUG, "flex-item box: %p: base_size: %i, main_size %i",
+ b, item->base_size, item->main_size);
+
+ return true;
+}
+
+static void layout_flex_ctx__populate_item_data(
+ const struct flex_ctx *ctx,
+ const struct box *flex,
+ int available_width)
+{
+ size_t i = 0;
+ bool horizontal = ctx->horizontal;
+
+ for (struct box *b = flex->children; b != NULL; b = b->next) {
+ struct flex_item_data *item = &ctx->item.data[i++];
+
+ b->float_container = b->parent;
+ layout_find_dimensions(ctx->unit_len_ctx, available_width, -1,
+ b, b->style, &b->width, &b->height,
+ horizontal ? &item->max_main : &item->max_cross,
+ horizontal ? &item->min_main : &item->min_cross,
+ horizontal ? &item->max_cross : &item->max_main,
+ horizontal ? &item->min_cross : &item->min_main,
+ b->margin, b->padding, b->border);
+ b->float_container = NULL;
+
+ NSLOG(flex, DEEPDEBUG, "flex-item box: %p: width: %i",
+ b, b->width);
+
+ item->box = b;
+ item->basis = css_computed_flex_basis(b->style,
+ &item->basis_length, &item->basis_unit);
+
+ css_computed_flex_shrink(b->style, &item->shrink);
+ css_computed_flex_grow(b->style, &item->grow);
+
+ layout_flex__base_and_main_sizes(ctx, item, available_width);
+ }
+}
+
+static bool layout_flex_ctx__ensure_line(struct flex_ctx *ctx)
+{
+ struct flex_line_data *temp;
+ size_t line_alloc = ctx->line.alloc * 2;
+
+ if (ctx->line.alloc > ctx->line.count) {
+ return true;
+ }
+
+ temp = realloc(ctx->line.data, sizeof(*ctx->line.data) * line_alloc);
+ if (temp == NULL) {
+ return false;
+ }
+ ctx->line.data = temp;
+
+ memset(ctx->line.data + ctx->line.alloc, 0,
+ sizeof(*ctx->line.data) * (line_alloc - ctx->line.alloc));
+ ctx->line.alloc = line_alloc;
+
+ return true;
+}
+
+static struct flex_line_data *layout_flex__build_line(struct flex_ctx *ctx,
+ size_t item_index)
+{
+ struct flex_line_data *line;
+ int used_main = 0;
+
+ if (!layout_flex_ctx__ensure_line(ctx)) {
+ return 0;
+ }
+
+ line = &ctx->line.data[ctx->line.count];
+ line->first = item_index;
+
+ NSLOG(flex, DEEPDEBUG, "flex container %p: available main: %i",
+ ctx->flex, ctx->available_main);
+
+ while (item_index < ctx->item.count) {
+ struct flex_item_data *item = &ctx->item.data[item_index];
+ struct box *b = item->box;
+ int pos_main;
+
+ pos_main = ctx->horizontal ?
+ item->main_size :
+ b->height + lh__delta_outer_main(ctx->flex, b);
+
+ if (ctx->wrap == CSS_FLEX_WRAP_NOWRAP ||
+ pos_main + used_main <= ctx->available_main ||
+ lh__box_is_absolute(item->box) ||
+ ctx->available_main == AUTO ||
+ line->count == 0 ||
+ pos_main == 0) {
+ if (lh__box_is_absolute(item->box) == false) {
+ line->main_size += item->main_size;
+ used_main += pos_main;
+ }
+ item->line = ctx->line.count;
+ line->count++;
+ item_index++;
+ } else {
+ break;
+ }
+ }
+
+ if (line->count > 0) {
+ ctx->line.count++;
+ } else {
+ NSLOG(layout, ERROR, "Failed to fit any flex items");
+ }
+
+ return line;
+}
+
+static inline void layout_flex__item_freeze(
+ struct flex_line_data *line,
+ struct flex_item_data *item)
+{
+ item->freeze = true;
+ line->frozen++;
+
+ NSLOG(flex, DEEPDEBUG, "flex-item box: %p: "
+ "Frozen at target_main_size: %i",
+ item->box, item->target_main_size);
+}
+
+static inline int layout_flex__remaining_free_main(
+ struct flex_ctx *ctx,
+ struct flex_line_data *line,
+ css_fixed *unfrozen_factor_sum,
+ int initial_free_main,
+ int available_main,
+ bool grow)
+{
+ int remaining_free_main = available_main;
+ size_t item_count = line->first + line->count;
+
+ *unfrozen_factor_sum = 0;
+
+ for (size_t i = line->first; i < item_count; i++) {
+ struct flex_item_data *item = &ctx->item.data[i];
+
+ if (item->freeze) {
+ remaining_free_main -= item->target_main_size;
+ } else {
+ remaining_free_main -= item->base_size;
+
+ *unfrozen_factor_sum += grow ?
+ item->grow : item->shrink;
+ }
+ }
+
+ if (*unfrozen_factor_sum < F_1) {
+ int free_space = FIXTOINT(FMUL(INTTOFIX(initial_free_main),
+ *unfrozen_factor_sum));
+
+ if (free_space < remaining_free_main) {
+ remaining_free_main = free_space;
+ }
+ }
+
+ NSLOG(flex, DEEPDEBUG, "Remaining free space: %i",
+ remaining_free_main);
+
+ return remaining_free_main;
+}
+
+static inline int layout_flex__get_min_max_violations(
+ struct flex_ctx *ctx,
+ struct flex_line_data *line)
+{
+
+ int total_violation = 0;
+ size_t item_count = line->first + line->count;
+
+ for (size_t i = line->first; i < item_count; i++) {
+ struct flex_item_data *item = &ctx->item.data[i];
+ int target_main_size = item->target_main_size;
+
+ NSLOG(flex, DEEPDEBUG, "item %p: target_main_size: %i",
+ item->box, target_main_size);
+
+ if (item->freeze) {
+ continue;
+ }
+
+ if (item->max_main > 0 &&
+ target_main_size > item->max_main) {
+ target_main_size = item->max_main;
+ item->max_violation = true;
+ NSLOG(flex, DEEPDEBUG, "Violation: max_main: %i",
+ item->max_main);
+ }
+
+ if (target_main_size < item->min_main) {
+ target_main_size = item->min_main;
+ item->min_violation = true;
+ NSLOG(flex, DEEPDEBUG, "Violation: min_main: %i",
+ item->min_main);
+ }
+
+ if (target_main_size < item->box->min_width) {
+ target_main_size = item->box->min_width;
+ item->min_violation = true;
+ NSLOG(flex, DEEPDEBUG, "Violation: box min_width: %i",
+ item->box->min_width);
+ }
+
+ if (target_main_size < 0) {
+ target_main_size = 0;
+ item->min_violation = true;
+ NSLOG(flex, DEEPDEBUG, "Violation: less than 0");
+ }
+
+ total_violation += target_main_size - item->target_main_size;
+ item->target_main_size = target_main_size;
+ }
+
+ NSLOG(flex, DEEPDEBUG, "Total violation: %i", total_violation);
+
+ return total_violation;
+}
+
+static inline void layout_flex__distribute_free_main(
+ struct flex_ctx *ctx,
+ struct flex_line_data *line,
+ css_fixed unfrozen_factor_sum,
+ int remaining_free_main,
+ bool grow)
+{
+ size_t item_count = line->first + line->count;
+
+ if (grow) {
+ for (size_t i = line->first; i < item_count; i++) {
+ struct flex_item_data *item = &ctx->item.data[i];
+ css_fixed ratio;
+
+ if (item->freeze) {
+ continue;
+ }
+
+ ratio = FDIV(item->grow, unfrozen_factor_sum);
+
+ item->target_main_size = item->base_size +
+ FIXTOINT(FMUL(
+ INTTOFIX(remaining_free_main),
+ ratio));
+ }
+ } else {
+ css_fixed scaled_shrink_factor_sum = 0;
+
+ for (size_t i = line->first; i < item_count; i++) {
+ struct flex_item_data *item = &ctx->item.data[i];
+ css_fixed scaled_shrink_factor;
+
+ if (item->freeze) {
+ continue;
+ }
+
+ scaled_shrink_factor = FMUL(
+ item->shrink,
+ INTTOFIX(item->base_size));
+ scaled_shrink_factor_sum += scaled_shrink_factor;
+ }
+
+ for (size_t i = line->first; i < item_count; i++) {
+ struct flex_item_data *item = &ctx->item.data[i];
+ css_fixed scaled_shrink_factor;
+ css_fixed ratio;
+
+ if (item->freeze) {
+ continue;
+ } else if (scaled_shrink_factor_sum == 0) {
+ item->target_main_size = item->main_size;
+ layout_flex__item_freeze(line, item);
+ continue;
+ }
+
+ scaled_shrink_factor = FMUL(
+ item->shrink,
+ INTTOFIX(item->base_size));
+ ratio = FDIV(scaled_shrink_factor,
+ scaled_shrink_factor_sum);
+
+ item->target_main_size = item->base_size -
+ FIXTOINT(FMUL(
+ INTTOFIX(abs(remaining_free_main)),
+ ratio));
+ }
+ }
+}
+
+static bool layout_flex__place_line_items_main(
+ struct flex_ctx *ctx,
+ struct flex_line_data *line)
+{
+ enum box_side main_start = ctx->horizontal ? LEFT : TOP;
+ size_t item_count = line->first + line->count;
+ int main_pos = ctx->flex->padding[main_start];
+
+ for (size_t i = line->first; i < item_count; i++) {
+ struct flex_item_data *item = &ctx->item.data[i];
+ struct box *b = item->box;
+ int *box_pos_main;
+
+ if (ctx->horizontal) {
+ b->width = item->target_main_size -
+ lh__delta_outer_width(b);
+
+ if (!layout_flex_item(ctx, item, b->width)) {
+ return false;
+ }
+ }
+
+ box_pos_main = ctx->horizontal ? &b->x : &b->y;
+ *box_pos_main = main_pos + lh__non_auto_margin(b, main_start) +
+ b->border[main_start].width;
+
+ if (!lh__box_is_absolute(b)) {
+ int cross_size;
+ int *box_size_main;
+ int *box_size_cross;
+
+ box_size_main = lh__box_size_main(ctx->horizontal, b);
+ box_size_cross = lh__box_size_cross(ctx->horizontal, b);
+
+ main_pos += *box_size_main + lh__delta_outer_main(
+ ctx->flex, b);
+
+ cross_size = *box_size_cross + lh__delta_outer_cross(
+ ctx->flex, b);
+ if (line->cross_size < cross_size) {
+ line->cross_size = cross_size;
+ }
+ }
+ }
+
+ return true;
+}
+
+/** 9.7. Resolving Flexible Lengths */
+static bool layout_flex__resolve_line(
+ struct flex_ctx *ctx,
+ struct flex_line_data *line)
+{
+ size_t item_count = line->first + line->count;
+ int available_main = ctx->available_main;
+ int initial_free_main;
+ bool grow;
+
+ if (available_main == AUTO) {
+ available_main = INT_MAX;
+ }
+
+ grow = (line->main_size < available_main);
+ initial_free_main = available_main;
+
+ NSLOG(flex, DEEPDEBUG, "box %p: line %zu: first: %zu, count: %zu",
+ ctx->flex, line - ctx->line.data,
+ line->first, line->count);
+ NSLOG(flex, DEEPDEBUG, "Line main_size: %i, available_main: %i",
+ line->main_size, available_main);
+
+ for (size_t i = line->first; i < item_count; i++) {
+ struct flex_item_data *item = &ctx->item.data[i];
+
+ /* 3. Size inflexible items */
+ if (grow) {
+ if (item->grow == 0 ||
+ item->base_size > item->main_size) {
+ item->target_main_size = item->main_size;
+ layout_flex__item_freeze(line, item);
+ }
+ } else {
+ if (item->shrink == 0 ||
+ item->base_size < item->main_size) {
+ item->target_main_size = item->main_size;
+ layout_flex__item_freeze(line, item);
+ }
+ }
+
+ /* 4. Calculate initial free space */
+ if (item->freeze) {
+ initial_free_main -= item->target_main_size;
+ } else {
+ initial_free_main -= item->base_size;
+ }
+ }
+
+ /* 5. Loop */
+ while (line->frozen < line->count) {
+ css_fixed unfrozen_factor_sum;
+ int remaining_free_main;
+ int total_violation;
+
+ NSLOG(flex, DEEPDEBUG, "flex-container: %p: Resolver pass",
+ ctx->flex);
+
+ /* b */
+ remaining_free_main = layout_flex__remaining_free_main(ctx,
+ line, &unfrozen_factor_sum, initial_free_main,
+ available_main, grow);
+
+ /* c */
+ if (remaining_free_main != 0) {
+ layout_flex__distribute_free_main(ctx,
+ line, unfrozen_factor_sum,
+ remaining_free_main, grow);
+ }
+
+ /* d */
+ total_violation = layout_flex__get_min_max_violations(
+ ctx, line);
+
+ /* e */
+ for (size_t i = line->first; i < item_count; i++) {
+ struct flex_item_data *item = &ctx->item.data[i];
+
+ if (total_violation == 0 ||
+ (total_violation > 0 && item->min_violation) ||
+ (total_violation < 0 && item->max_violation)) {
+ layout_flex__item_freeze(line, item);
+ }
+ }
+ }
+
+ if (!layout_flex__place_line_items_main(ctx, line)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool layout_flex__collect_items_into_lines(
+ struct flex_ctx *ctx)
+{
+ size_t pos = 0;
+
+ while (pos < ctx->item.count) {
+ struct flex_line_data *line;
+
+ line = layout_flex__build_line(ctx, pos);
+ if (line == NULL) {
+ return false;
+ }
+
+ pos += line->count;
+
+ NSLOG(flex, DEEPDEBUG, "flex-container: %p: "
+ "fitted: %zu (total: %zu/%zu)",
+ ctx->flex, line->count,
+ pos, ctx->item.count);
+
+ if (!layout_flex__resolve_line(ctx, line)) {
+ return false;
+ }
+
+ ctx->cross_size += line->cross_size;
+ if (ctx->main_size < line->main_size) {
+ ctx->main_size = line->main_size;
+ }
+ }
+
+ return true;
+}
+
+static void layout_flex__place_line_items_cross(struct flex_ctx *ctx,
+ struct flex_line_data *line, int extra)
+{
+ enum box_side cross_start = ctx->horizontal ? TOP : LEFT;
+ size_t item_count = line->first + line->count;
+
+ for (size_t i = line->first; i < item_count; i++) {
+ struct flex_item_data *item = &ctx->item.data[i];
+ struct box *b = item->box;
+ int cross_free_space;
+ int *box_size_cross;
+ int *box_pos_cross;
+
+ box_pos_cross = ctx->horizontal ? &b->y : &b->x;
+ box_size_cross = lh__box_size_cross(ctx->horizontal, b);
+
+ cross_free_space = line->cross_size + extra - *box_size_cross -
+ lh__delta_outer_cross(ctx->flex, b);
+
+ switch (lh__box_align_self(ctx->flex, b)) {
+ default:
+ /* Fall through. */
+ case CSS_ALIGN_SELF_STRETCH:
+ if (lh__box_size_cross_is_auto(ctx->horizontal, b)) {
+ *box_size_cross += cross_free_space;
+
+ /* Relayout children for stretch. */
+ if (!layout_flex_item(ctx, item, b->width)) {
+ return;
+ }
+ }
+ /* Fall through. */
+ case CSS_ALIGN_SELF_FLEX_START:
+ *box_pos_cross = ctx->flex->padding[cross_start] +
+ line->pos +
+ lh__non_auto_margin(b, cross_start) +
+ b->border[cross_start].width;
+ break;
+
+ case CSS_ALIGN_SELF_FLEX_END:
+ *box_pos_cross = ctx->flex->padding[cross_start] +
+ line->pos + cross_free_space +
+ lh__non_auto_margin(b, cross_start) +
+ b->border[cross_start].width;
+ break;
+
+ case CSS_ALIGN_SELF_BASELINE:
+ /* Fall through. */
+ case CSS_ALIGN_SELF_CENTER:
+ *box_pos_cross = ctx->flex->padding[cross_start] +
+ line->pos + cross_free_space / 2 +
+ lh__non_auto_margin(b, cross_start) +
+ b->border[cross_start].width;
+ break;
+ }
+ }
+}
+
+static void layout_flex__place_lines(struct flex_ctx *ctx)
+{
+ bool reversed = ctx->wrap == CSS_FLEX_WRAP_WRAP_REVERSE;
+ int line_pos = reversed ? ctx->cross_size : 0;
+ int post_multiplier = reversed ? 0 : 1;
+ int pre_multiplier = reversed ? -1 : 0;
+ int extra_remainder = 0;
+ int extra = 0;
+
+ if (ctx->available_cross != AUTO &&
+ ctx->available_cross != ctx->cross_size &&
+ ctx->line.count > 0) {
+ extra = ctx->available_cross - ctx->cross_size;
+
+ extra_remainder = extra % ctx->line.count;
+ extra /= ctx->line.count;
+ }
+
+ for (size_t i = 0; i < ctx->line.count; i++) {
+ struct flex_line_data *line = &ctx->line.data[i];
+
+ line_pos += pre_multiplier * line->cross_size;
+ line->pos = line_pos;
+ line_pos += post_multiplier * line->cross_size +
+ extra + extra_remainder;
+
+ layout_flex__place_line_items_cross(ctx, line,
+ extra + extra_remainder);
+
+ if (extra_remainder > 0) {
+ extra_remainder--;
+ }
+ }
+}
+
+/**
+ * Layout a flex container.
+ *
+ * \param[in] flex table to layout
+ * \param[in] available_width width of containing block
+ * \param[in] content memory pool for any new boxes
+ * \return true on success, false on memory exhaustion
+ */
+bool layout_flex(struct box *flex, int available_width,
+ html_content *content)
+{
+ int max_height, min_height;
+ struct flex_ctx *ctx;
+ bool success = false;
+
+ ctx = layout_flex_ctx__create(content, flex);
+ if (ctx == NULL) {
+ return false;
+ }
+
+ NSLOG(flex, DEEPDEBUG, "box %p: %s, available_width %i, width: %i",
+ flex, ctx->horizontal ? "horizontal" : "vertical",
+ available_width, flex->width);
+
+ layout_find_dimensions(
+ ctx->unit_len_ctx, available_width, -1,
+ flex, flex->style, NULL, &flex->height,
+ NULL, NULL, &max_height, &min_height,
+ flex->margin, flex->padding, flex->border);
+
+ available_width = min(available_width, flex->width);
+
+ if (ctx->horizontal) {
+ ctx->available_main = available_width;
+ ctx->available_cross = ctx->flex->height;
+ } else {
+ ctx->available_main = ctx->flex->height;
+ ctx->available_cross = available_width;
+ }
+
+ NSLOG(flex, DEEPDEBUG, "box %p: available_main: %i",
+ flex, ctx->available_main);
+ NSLOG(flex, DEEPDEBUG, "box %p: available_cross: %i",
+ flex, ctx->available_cross);
+
+ layout_flex_ctx__populate_item_data(ctx, flex, available_width);
+
+ /* Place items onto lines. */
+ success = layout_flex__collect_items_into_lines(ctx);
+ if (!success) {
+ goto cleanup;
+ }
+
+ layout_flex__place_lines(ctx);
+
+ if (flex->height == AUTO) {
+ flex->height = ctx->horizontal ?
+ ctx->cross_size :
+ ctx->main_size;
+ }
+
+ if (flex->height != AUTO) {
+ if (max_height >= 0 && flex->height > max_height) {
+ flex->height = max_height;
+ }
+ if (min_height > 0 && flex->height < min_height) {
+ flex->height = min_height;
+ }
+ }
+
+ success = true;
+
+cleanup:
+ layout_flex_ctx__destroy(ctx);
+
+ NSLOG(flex, DEEPDEBUG, "box %p: %s: w: %i, h: %i", flex,
+ success ? "success" : "failure",
+ flex->width, flex->height);
+ return success;
+}
diff --git a/content/handlers/html/layout_internal.h b/content/handlers/html/layout_internal.h
new file mode 100644
index 000000000..974f49348
--- /dev/null
+++ b/content/handlers/html/layout_internal.h
@@ -0,0 +1,711 @@
+/*
+ * Copyright 2003 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file
+ * HTML layout private interface.
+ */
+
+#ifndef NETSURF_HTML_LAYOUT_INTERNAL_H
+#define NETSURF_HTML_LAYOUT_INTERNAL_H
+
+#define AUTO INT_MIN
+
+/* Fixed point percentage (a) of an integer (b), to an integer */
+#define FPCT_OF_INT_TOINT(a, b) (FIXTOINT(FDIV((a * b), F_100)))
+
+/**
+ * Layout a block formatting context.
+ *
+ * \param block BLOCK, INLINE_BLOCK, or TABLE_CELL to layout
+ * \param viewport_height Height of viewport in pixels or -ve if unknown
+ * \param content Memory pool for any new boxes
+ * \return true on success, false on memory exhaustion
+ *
+ * This function carries out layout of a block and its children, as described
+ * in CSS 2.1 9.4.1.
+ */
+bool layout_block_context(
+ struct box *block,
+ int viewport_height,
+ html_content *content);
+
+/**
+ * Layout a table.
+ *
+ * \param table table to layout
+ * \param available_width width of containing block
+ * \param content memory pool for any new boxes
+ * \return true on success, false on memory exhaustion
+ */
+bool layout_table(
+ struct box *table,
+ int available_width,
+ html_content *content);
+
+/**
+ * Layout a flex container.
+ *
+ * \param[in] flex table to layout
+ * \param[in] available_width width of containing block
+ * \param[in] content memory pool for any new boxes
+ * \return true on success, false on memory exhaustion
+ */
+bool layout_flex(
+ struct box *flex,
+ int available_width,
+ html_content *content);
+
+typedef uint8_t (*css_len_func)(
+ const css_computed_style *style,
+ css_fixed *length, css_unit *unit);
+typedef uint8_t (*css_border_style_func)(
+ const css_computed_style *style);
+typedef uint8_t (*css_border_color_func)(
+ const css_computed_style *style,
+ css_color *color);
+
+/** Array of per-side access functions for computed style margins. */
+extern const css_len_func margin_funcs[4];
+
+/** Array of per-side access functions for computed style paddings. */
+extern const css_len_func padding_funcs[4];
+
+/** Array of per-side access functions for computed style border_widths. */
+extern const css_len_func border_width_funcs[4];
+
+/** Array of per-side access functions for computed style border styles. */
+extern const css_border_style_func border_style_funcs[4];
+
+/** Array of per-side access functions for computed style border colors. */
+extern const css_border_color_func border_color_funcs[4];
+
+/** Layout helper: Check whether box is a float. */
+static inline bool lh__box_is_float_box(const struct box *b)
+{
+ return b->type == BOX_FLOAT_LEFT ||
+ b->type == BOX_FLOAT_RIGHT;
+}
+
+/** Layout helper: Check whether box takes part in inline flow. */
+static inline bool lh__box_is_inline_flow(const struct box *b)
+{
+ return b->type == BOX_INLINE ||
+ b->type == BOX_INLINE_FLEX ||
+ b->type == BOX_INLINE_BLOCK ||
+ b->type == BOX_TEXT ||
+ b->type == BOX_INLINE_END;
+}
+
+/** Layout helper: Check whether box takes part in inline flow. */
+static inline bool lh__box_is_flex_container(const struct box *b)
+{
+ return b->type == BOX_FLEX ||
+ b->type == BOX_INLINE_FLEX;
+}
+
+/** Layout helper: Check whether box takes part in inline flow. */
+static inline bool lh__box_is_flex_item(const struct box *b)
+{
+ return (b->parent != NULL) && lh__box_is_flex_container(b->parent);
+}
+
+/** Layout helper: Check whether box is inline level. (Includes BR.) */
+static inline bool lh__box_is_inline_level(const struct box *b)
+{
+ return lh__box_is_inline_flow(b) ||
+ b->type == BOX_BR;
+}
+
+/** Layout helper: Check whether box is inline level. (Includes BR, floats.) */
+static inline bool lh__box_is_inline_content(const struct box *b)
+{
+ return lh__box_is_float_box(b) ||
+ lh__box_is_inline_level(b);
+}
+
+/** Layout helper: Check whether box is an object. */
+static inline bool lh__box_is_object(const struct box *b)
+{
+ return b->object ||
+ (b->flags & (IFRAME | REPLACE_DIM));
+}
+
+/** Layout helper: Check whether box is replaced. */
+static inline bool lh__box_is_replace(const struct box *b)
+{
+ return b->gadget ||
+ lh__box_is_object(b);
+}
+
+/** Layout helper: Check for CSS border on given side. */
+static inline bool lh__have_border(
+ enum box_side side,
+ const css_computed_style *style)
+{
+ return border_style_funcs[side](style) != CSS_BORDER_STYLE_NONE;
+}
+
+static inline bool lh__box_is_absolute(const struct box *b)
+{
+ return css_computed_position(b->style) == CSS_POSITION_ABSOLUTE ||
+ css_computed_position(b->style) == CSS_POSITION_FIXED;
+}
+
+static inline bool lh__flex_main_is_horizontal(const struct box *flex)
+{
+ const css_computed_style *style = flex->style;
+
+ assert(style != NULL);
+
+ switch (css_computed_flex_direction(style)) {
+ default: /* Fallthrough. */
+ case CSS_FLEX_DIRECTION_ROW: /* Fallthrough. */
+ case CSS_FLEX_DIRECTION_ROW_REVERSE:
+ return true;
+ case CSS_FLEX_DIRECTION_COLUMN: /* Fallthrough. */
+ case CSS_FLEX_DIRECTION_COLUMN_REVERSE:
+ return false;
+ }
+}
+
+static inline int lh__non_auto_margin(const struct box *b, enum box_side side)
+{
+ return (b->margin[side] == AUTO) ? 0 : b->margin[side];
+}
+
+static inline int lh__delta_outer_height(const struct box *b)
+{
+ return b->padding[TOP] +
+ b->padding[BOTTOM] +
+ b->border[TOP].width +
+ b->border[BOTTOM].width +
+ lh__non_auto_margin(b, TOP) +
+ lh__non_auto_margin(b, BOTTOM);
+}
+
+static inline int lh__delta_outer_width(const struct box *b)
+{
+ return b->padding[LEFT] +
+ b->padding[RIGHT] +
+ b->border[LEFT].width +
+ b->border[RIGHT].width +
+ lh__non_auto_margin(b, LEFT) +
+ lh__non_auto_margin(b, RIGHT);
+}
+
+static inline int lh__delta_outer_main(
+ const struct box *flex,
+ const struct box *b)
+{
+ if (lh__flex_main_is_horizontal(flex)) {
+ return lh__delta_outer_width(b);
+ } else {
+ return lh__delta_outer_height(b);
+ }
+}
+
+static inline int lh__delta_outer_cross(
+ const struct box *flex,
+ const struct box *b)
+{
+ if (lh__flex_main_is_horizontal(flex) == false) {
+ return lh__delta_outer_width(b);
+ } else {
+ return lh__delta_outer_height(b);
+ }
+}
+
+static inline int *lh__box_size_main(
+ bool horizontal,
+ struct box *b)
+{
+ return horizontal ? &b->width : &b->height;
+}
+
+static inline int *lh__box_size_cross(
+ bool horizontal,
+ struct box *b)
+{
+ return horizontal ? &b->height : &b->width;
+}
+
+static inline bool lh__box_size_cross_is_auto(
+ bool horizontal,
+ struct box *b)
+{
+ css_fixed length;
+ css_unit unit;
+
+ if (horizontal) {
+ return css_computed_height(b->style,
+ &length, &unit) == CSS_HEIGHT_AUTO;
+ } else {
+ return css_computed_width(b->style,
+ &length, &unit) == CSS_WIDTH_AUTO;
+ }
+}
+
+static inline enum css_align_self_e lh__box_align_self(
+ const struct box *flex,
+ const struct box *item)
+{
+ enum css_align_self_e align_self = css_computed_align_self(item->style);
+
+ if (align_self == CSS_ALIGN_SELF_AUTO) {
+ align_self = css_computed_align_items(flex->style);
+ }
+
+ return align_self;
+}
+
+/**
+ * Determine width of margin, borders, and padding on one side of a box.
+ *
+ * \param unit_len_ctx CSS length conversion context for document
+ * \param style style to measure
+ * \param side side of box to measure
+ * \param margin whether margin width is required
+ * \param border whether border width is required
+ * \param padding whether padding width is required
+ * \param fixed increased by sum of fixed margin, border, and padding
+ * \param frac increased by sum of fractional margin and padding
+ */
+static inline void calculate_mbp_width(
+ const css_unit_ctx *unit_len_ctx,
+ const css_computed_style *style,
+ unsigned int side,
+ bool margin,
+ bool border,
+ bool padding,
+ int *fixed,
+ float *frac)
+{
+ css_fixed value = 0;
+ css_unit unit = CSS_UNIT_PX;
+
+ assert(style);
+
+ /* margin */
+ if (margin) {
+ enum css_margin_e type;
+
+ type = margin_funcs[side](style, &value, &unit);
+ if (type == CSS_MARGIN_SET) {
+ if (unit == CSS_UNIT_PCT) {
+ *frac += FIXTOFLT(FDIV(value, F_100));
+ } else {
+ *fixed += FIXTOINT(css_unit_len2device_px(
+ style, unit_len_ctx,
+ value, unit));
+ }
+ }
+ }
+
+ /* border */
+ if (border) {
+ if (lh__have_border(side, style)) {
+ border_width_funcs[side](style, &value, &unit);
+
+ *fixed += FIXTOINT(css_unit_len2device_px(
+ style, unit_len_ctx,
+ value, unit));
+ }
+ }
+
+ /* padding */
+ if (padding) {
+ padding_funcs[side](style, &value, &unit);
+ if (unit == CSS_UNIT_PCT) {
+ *frac += FIXTOFLT(FDIV(value, F_100));
+ } else {
+ *fixed += FIXTOINT(css_unit_len2device_px(
+ style, unit_len_ctx,
+ value, unit));
+ }
+ }
+}
+
+/**
+ * Adjust a specified width or height for the box-sizing property.
+ *
+ * This turns the specified dimension into a content-box dimension.
+ *
+ * \param unit_len_ctx Length conversion context
+ * \param box gadget to adjust dimensions of
+ * \param available_width width of containing block
+ * \param setwidth set true if the dimension to be tweaked is a width,
+ * else set false for a height
+ * \param dimension current value for given width/height dimension.
+ * updated to new value after consideration of
+ * gadget properties.
+ */
+static inline void layout_handle_box_sizing(
+ const css_unit_ctx *unit_len_ctx,
+ const struct box *box,
+ int available_width,
+ bool setwidth,
+ int *dimension)
+{
+ enum css_box_sizing_e bs;
+
+ assert(box && box->style);
+
+ bs = css_computed_box_sizing(box->style);
+
+ if (bs == CSS_BOX_SIZING_BORDER_BOX) {
+ int orig = *dimension;
+ int fixed = 0;
+ float frac = 0;
+
+ calculate_mbp_width(unit_len_ctx, box->style,
+ setwidth ? LEFT : TOP,
+ false, true, true, &fixed, &frac);
+ calculate_mbp_width(unit_len_ctx, box->style,
+ setwidth ? RIGHT : BOTTOM,
+ false, true, true, &fixed, &frac);
+ orig -= frac * available_width + fixed;
+ *dimension = orig > 0 ? orig : 0;
+ }
+}
+
+/**
+ * Calculate width, height, and thickness of margins, paddings, and borders.
+ *
+ * \param unit_len_ctx Length conversion context
+ * \param available_width width of containing block
+ * \param viewport_height height of viewport in pixels or -ve if unknown
+ * \param box current box
+ * \param style style giving width, height, margins, paddings,
+ * and borders
+ * \param width updated to width, may be NULL
+ * \param height updated to height, may be NULL
+ * \param max_width updated to max-width, may be NULL
+ * \param min_width updated to min-width, may be NULL
+ * \param max_height updated to max-height, may be NULL
+ * \param min_height updated to min-height, may be NULL
+ * \param margin filled with margins, may be NULL
+ * \param padding filled with paddings, may be NULL
+ * \param border filled with border widths, may be NULL
+ */
+static inline void layout_find_dimensions(
+ const css_unit_ctx *unit_len_ctx,
+ int available_width,
+ int viewport_height,
+ const struct box *box,
+ const css_computed_style *style,
+ int *width,
+ int *height,
+ int *max_width,
+ int *min_width,
+ int *max_height,
+ int *min_height,
+ int margin[4],
+ int padding[4],
+ struct box_border border[4])
+{
+ struct box *containing_block = NULL;
+ unsigned int i;
+
+ if (width) {
+ enum css_width_e wtype;
+ css_fixed value = 0;
+ css_unit unit = CSS_UNIT_PX;
+
+ wtype = css_computed_width(style, &value, &unit);
+
+ if (wtype == CSS_WIDTH_SET) {
+ if (unit == CSS_UNIT_PCT) {
+ *width = FPCT_OF_INT_TOINT(
+ value, available_width);
+ } else {
+ *width = FIXTOINT(css_unit_len2device_px(
+ style, unit_len_ctx,
+ value, unit));
+ }
+ } else {
+ *width = AUTO;
+ }
+
+ if (*width != AUTO) {
+ layout_handle_box_sizing(unit_len_ctx, box,
+ available_width, true, width);
+ }
+ }
+
+ if (height) {
+ enum css_height_e htype;
+ css_fixed value = 0;
+ css_unit unit = CSS_UNIT_PX;
+
+ htype = css_computed_height(style, &value, &unit);
+
+ if (htype == CSS_HEIGHT_SET) {
+ if (unit == CSS_UNIT_PCT) {
+ enum css_height_e cbhtype;
+
+ if (css_computed_position(box->style) ==
+ CSS_POSITION_ABSOLUTE &&
+ box->parent) {
+ /* Box is absolutely positioned */
+ assert(box->float_container);
+ containing_block = box->float_container;
+ } else if (box->float_container &&
+ css_computed_position(box->style) !=
+ CSS_POSITION_ABSOLUTE &&
+ (css_computed_float(box->style) ==
+ CSS_FLOAT_LEFT ||
+ css_computed_float(box->style) ==
+ CSS_FLOAT_RIGHT)) {
+ /* Box is a float */
+ assert(box->parent &&
+ box->parent->parent &&
+ box->parent->parent->parent);
+
+ containing_block =
+ box->parent->parent->parent;
+ } else if (box->parent && box->parent->type !=
+ BOX_INLINE_CONTAINER) {
+ /* Box is a block level element */
+ containing_block = box->parent;
+ } else if (box->parent && box->parent->type ==
+ BOX_INLINE_CONTAINER) {
+ /* Box is an inline block */
+ assert(box->parent->parent);
+ containing_block = box->parent->parent;
+ }
+
+ if (containing_block) {
+ css_fixed f = 0;
+ css_unit u = CSS_UNIT_PX;
+
+ cbhtype = css_computed_height(
+ containing_block->style,
+ &f, &u);
+ }
+
+ if (containing_block &&
+ containing_block->height != AUTO &&
+ (css_computed_position(box->style) ==
+ CSS_POSITION_ABSOLUTE ||
+ cbhtype == CSS_HEIGHT_SET)) {
+ /* Box is absolutely positioned or its
+ * containing block has a valid
+ * specified height.
+ * (CSS 2.1 Section 10.5) */
+ *height = FPCT_OF_INT_TOINT(value,
+ containing_block->height);
+ } else if ((!box->parent ||
+ !box->parent->parent) &&
+ viewport_height >= 0) {
+ /* If root element or it's child
+ * (HTML or BODY) */
+ *height = FPCT_OF_INT_TOINT(value,
+ viewport_height);
+ } else {
+ /* precentage height not permissible
+ * treat height as auto */
+ *height = AUTO;
+ }
+ } else {
+ *height = FIXTOINT(css_unit_len2device_px(
+ style, unit_len_ctx,
+ value, unit));
+ }
+ } else {
+ *height = AUTO;
+ }
+
+ if (*height != AUTO) {
+ layout_handle_box_sizing(unit_len_ctx, box,
+ available_width, false, height);
+ }
+ }
+
+ if (max_width) {
+ enum css_max_width_e type;
+ css_fixed value = 0;
+ css_unit unit = CSS_UNIT_PX;
+
+ type = css_computed_max_width(style, &value, &unit);
+
+ if (type == CSS_MAX_WIDTH_SET) {
+ if (unit == CSS_UNIT_PCT) {
+ *max_width = FPCT_OF_INT_TOINT(value,
+ available_width);
+ } else {
+ *max_width = FIXTOINT(css_unit_len2device_px(
+ style, unit_len_ctx,
+ value, unit));
+ }
+ } else {
+ /* Inadmissible */
+ *max_width = -1;
+ }
+
+ if (*max_width != -1) {
+ layout_handle_box_sizing(unit_len_ctx, box,
+ available_width, true, max_width);
+ }
+ }
+
+ if (min_width) {
+ enum css_min_width_e type;
+ css_fixed value = 0;
+ css_unit unit = CSS_UNIT_PX;
+
+ type = ns_computed_min_width(style, &value, &unit);
+
+ if (type == CSS_MIN_WIDTH_SET) {
+ if (unit == CSS_UNIT_PCT) {
+ *min_width = FPCT_OF_INT_TOINT(value,
+ available_width);
+ } else {
+ *min_width = FIXTOINT(css_unit_len2device_px(
+ style, unit_len_ctx,
+ value, unit));
+ }
+ } else {
+ /* Inadmissible */
+ *min_width = 0;
+ }
+
+ if (*min_width != 0) {
+ layout_handle_box_sizing(unit_len_ctx, box,
+ available_width, true, min_width);
+ }
+ }
+
+ if (max_height) {
+ enum css_max_height_e type;
+ css_fixed value = 0;
+ css_unit unit = CSS_UNIT_PX;
+
+ type = css_computed_max_height(style, &value, &unit);
+
+ if (type == CSS_MAX_HEIGHT_SET) {
+ if (unit == CSS_UNIT_PCT) {
+ /* TODO: handle percentage */
+ *max_height = -1;
+ } else {
+ *max_height = FIXTOINT(css_unit_len2device_px(
+ style, unit_len_ctx,
+ value, unit));
+ }
+ } else {
+ /* Inadmissible */
+ *max_height = -1;
+ }
+ }
+
+ if (min_height) {
+ enum css_min_height_e type;
+ css_fixed value = 0;
+ css_unit unit = CSS_UNIT_PX;
+
+ type = ns_computed_min_height(style, &value, &unit);
+
+ if (type == CSS_MIN_HEIGHT_SET) {
+ if (unit == CSS_UNIT_PCT) {
+ /* TODO: handle percentage */
+ *min_height = 0;
+ } else {
+ *min_height = FIXTOINT(css_unit_len2device_px(
+ style, unit_len_ctx,
+ value, unit));
+ }
+ } else {
+ /* Inadmissible */
+ *min_height = 0;
+ }
+ }
+
+ for (i = 0; i != 4; i++) {
+ if (margin) {
+ enum css_margin_e type = CSS_MARGIN_AUTO;
+ css_fixed value = 0;
+ css_unit unit = CSS_UNIT_PX;
+
+ type = margin_funcs[i](style, &value, &unit);
+
+ if (type == CSS_MARGIN_SET) {
+ if (unit == CSS_UNIT_PCT) {
+ margin[i] = FPCT_OF_INT_TOINT(value,
+ available_width);
+ } else {
+ margin[i] = FIXTOINT(css_unit_len2device_px(
+ style, unit_len_ctx,
+ value, unit));
+ }
+ } else {
+ margin[i] = AUTO;
+ }
+ }
+
+ if (padding) {
+ css_fixed value = 0;
+ css_unit unit = CSS_UNIT_PX;
+
+ padding_funcs[i](style, &value, &unit);
+
+ if (unit == CSS_UNIT_PCT) {
+ padding[i] = FPCT_OF_INT_TOINT(value,
+ available_width);
+ } else {
+ padding[i] = FIXTOINT(css_unit_len2device_px(
+ style, unit_len_ctx,
+ value, unit));
+ }
+ }
+
+ /* Table cell borders are populated in table.c */
+ if (border && box->type != BOX_TABLE_CELL) {
+ enum css_border_style_e bstyle = CSS_BORDER_STYLE_NONE;
+ css_color color = 0;
+ css_fixed value = 0;
+ css_unit unit = CSS_UNIT_PX;
+
+ border_width_funcs[i](style, &value, &unit);
+ bstyle = border_style_funcs[i](style);
+ border_color_funcs[i](style, &color);
+
+ border[i].style = bstyle;
+ border[i].c = color;
+
+ if (bstyle == CSS_BORDER_STYLE_HIDDEN ||
+ bstyle == CSS_BORDER_STYLE_NONE)
+ /* spec unclear: following Mozilla */
+ border[i].width = 0;
+ else
+ border[i].width = FIXTOINT(css_unit_len2device_px(
+ style, unit_len_ctx,
+ value, unit));
+
+ /* Special case for border-collapse: make all borders
+ * on table/table-row-group/table-row zero width. */
+ if (css_computed_border_collapse(style) ==
+ CSS_BORDER_COLLAPSE_COLLAPSE &&
+ (box->type == BOX_TABLE ||
+ box->type == BOX_TABLE_ROW_GROUP ||
+ box->type == BOX_TABLE_ROW))
+ border[i].width = 0;
+ }
+ }
+}
+
+#endif
diff --git a/content/handlers/image/Makefile b/content/handlers/image/Makefile
index 1c27f74d7..afc90f407 100644
--- a/content/handlers/image/Makefile
+++ b/content/handlers/image/Makefile
@@ -10,7 +10,7 @@ S_IMAGE_$(NETSURF_USE_JPEG) += jpeg.c
S_IMAGE_$(NETSURF_USE_ROSPRITE) += nssprite.c
S_IMAGE_$(NETSURF_USE_PNG) += png.c
S_IMAGE_$(NETSURF_USE_NSSVG) += svg.c
-S_IMAGE_$(NETSURF_USE_RSVG) += rsvg.c
+S_IMAGE_$(NETSURF_USE_RSVG) += rsvg$(RSVG_API).c
S_IMAGE_$(NETSURF_USE_VIDEO) += video.c
S_IMAGE_$(NETSURF_USE_WEBP) += webp.c
diff --git a/content/handlers/image/gif.c b/content/handlers/image/gif.c
index deabd0adc..4671d1df5 100644
--- a/content/handlers/image/gif.c
+++ b/content/handlers/image/gif.c
@@ -68,9 +68,6 @@ static inline nserror gif__nsgif_error_to_ns(nsgif_error gif_res)
nserror err;
switch (gif_res) {
- case NSGIF_OK:
- err = NSERROR_OK;
- break;
case NSGIF_ERR_OOM:
err = NSERROR_NOMEM;
break;
@@ -233,10 +230,12 @@ static bool gif_convert(struct content *c)
/* Initialise the GIF */
gif_err = nsgif_data_scan(gif->gif, size, data);
if (gif_err != NSGIF_OK) {
- NSLOG(netsurf, DEBUG, "%s", nsgif_strerror(gif_err));
- /* Not fatal unless er have no frames. */
+ NSLOG(netsurf, INFO, "nsgif scan: %s", nsgif_strerror(gif_err));
+ /* Not fatal unless we have no frames. */
}
+ nsgif_data_complete(gif->gif);
+
gif_info = nsgif_get_info(gif->gif);
assert(gif_info != NULL);
diff --git a/content/handlers/image/image_cache.c b/content/handlers/image/image_cache.c
index 6ce6b5234..8f7ff89ff 100644
--- a/content/handlers/image/image_cache.c
+++ b/content/handlers/image/image_cache.c
@@ -438,6 +438,7 @@ image_cache_init(const struct image_cache_parameters *image_cache_parameters)
nserror image_cache_fini(void)
{
unsigned int op_count;
+ uint64_t op_size;
guit->misc->schedule(-1, image_cache__background_update, image_cache);
@@ -452,6 +453,10 @@ nserror image_cache_fini(void)
image_cache->miss_count +
image_cache->fail_count;
+ op_size = image_cache->hit_size +
+ image_cache->miss_size +
+ image_cache->fail_size;
+
NSLOG(netsurf, INFO, "Age %ds", image_cache->current_age / 1000);
NSLOG(netsurf, INFO, "Peak size %"PRIsizet" (in %d)",
image_cache->max_bitmap_size,
@@ -460,12 +465,7 @@ nserror image_cache_fini(void)
image_cache->max_bitmap_count,
image_cache->max_bitmap_count_size);
- if (op_count > 0) {
- uint64_t op_size;
-
- op_size = image_cache->hit_size +
- image_cache->miss_size +
- image_cache->fail_size;
+ if ((op_count > 0) && (op_size >0)) {
NSLOG(netsurf, INFO,
"Cache total/hit/miss/fail (counts) %d/%d/%d/%d (100%%/%d%%/%d%%/%d%%)",
@@ -528,7 +528,7 @@ nserror image_cache_add(struct content *content,
image_cache__link(centry);
centry->content = content;
- centry->bitmap_size = content->width * content->height * 4;
+ centry->bitmap_size = content->width * content->height * 4llu;
}
NSLOG(netsurf, INFO, "centry %p, content %p, bitmap %p", centry,
diff --git a/content/handlers/image/rsvg246.c b/content/handlers/image/rsvg246.c
new file mode 100644
index 000000000..1769c505a
--- /dev/null
+++ b/content/handlers/image/rsvg246.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2022 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file
+ * implementation of content handler for image/svg using librsvg 2.46 API.
+ *
+ * SVG files are rendered to a NetSurf bitmap by creating a Cairo rendering
+ * surface (content_rsvg_data.cs) over the bitmap's data, creating a Cairo
+ * drawing context using that surface, and then passing that drawing context
+ * to librsvg which then uses Cairo calls to plot the graphic to the bitmap.
+ * We store this in content->bitmap, and then use the usual bitmap plotter
+ * function to render it for redraw requests.
+ */
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <librsvg/rsvg.h>
+
+#include <nsutils/endian.h>
+
+#include "utils/log.h"
+#include "utils/utils.h"
+#include "utils/messages.h"
+#include "netsurf/plotters.h"
+#include "netsurf/bitmap.h"
+#include "netsurf/content.h"
+#include "content/llcache.h"
+#include "content/content_protected.h"
+#include "content/content_factory.h"
+#include "desktop/gui_internal.h"
+#include "desktop/bitmap.h"
+
+#include "image/image_cache.h"
+
+#include "image/rsvg.h"
+
+
+typedef struct rsvg_content {
+ struct content base;
+
+ RsvgHandle *rsvgh; /**< Context handle for RSVG renderer */
+} rsvg_content;
+
+
+static nserror
+rsvg_create(const content_handler *handler,
+ lwc_string *imime_type,
+ const struct http_parameter *params,
+ llcache_handle *llcache,
+ const char *fallback_charset,
+ bool quirks,
+ struct content **c)
+{
+ rsvg_content *svg;
+ nserror error;
+
+ svg = calloc(1, sizeof(rsvg_content));
+ if (svg == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__init(&svg->base, handler, imime_type, params,
+ llcache, fallback_charset, quirks);
+ if (error != NSERROR_OK) {
+ free(svg);
+ return error;
+ }
+
+ *c = (struct content *)svg;
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * create a bitmap from jpeg content for the image cache.
+ */
+static struct bitmap *
+rsvg_cache_convert(struct content *c)
+{
+ rsvg_content *svgc = (rsvg_content *)c;
+ struct bitmap *bitmap;
+ cairo_surface_t *cs;
+ cairo_t *cr;
+ RsvgRectangle viewport;
+ gboolean renderres;
+
+ if ((bitmap = guit->bitmap->create(c->width, c->height, BITMAP_NONE)) == NULL) {
+ NSLOG(netsurf, INFO, "Failed to create bitmap for rsvg render.");
+ return NULL;
+ }
+
+ if ((cs = cairo_image_surface_create_for_data(
+ (unsigned char *)guit->bitmap->get_buffer(bitmap),
+ CAIRO_FORMAT_ARGB32,
+ c->width, c->height,
+ guit->bitmap->get_rowstride(bitmap))) == NULL) {
+ NSLOG(netsurf, INFO, "Failed to create Cairo image surface for rsvg render.");
+ guit->bitmap->destroy(bitmap);
+ return NULL;
+ }
+ if ((cr = cairo_create(cs)) == NULL) {
+ NSLOG(netsurf, INFO,
+ "Failed to create Cairo drawing context for rsvg render.");
+ cairo_surface_destroy(cs);
+ guit->bitmap->destroy(bitmap);
+ return NULL;
+ }
+
+ viewport.x = 0;
+ viewport.y = 0;
+ viewport.width = c->width;
+ viewport.height = c->height;
+ renderres = rsvg_handle_render_document(svgc->rsvgh, cr, &viewport, NULL);
+ NSLOG(netsurf, DEBUG, "rsvg render:%d, width:%d, height %d", renderres, c->width, c->height);
+
+ bitmap_format_to_client(bitmap, &(bitmap_fmt_t) {
+ .layout = BITMAP_LAYOUT_ARGB8888,
+ });
+ guit->bitmap->modified(bitmap);
+
+ cairo_destroy(cr);
+ cairo_surface_destroy(cs);
+
+ return bitmap;
+}
+
+
+static bool rsvg_convert(struct content *c)
+{
+ rsvg_content *svgc = (rsvg_content *)c;
+ const uint8_t *data; /* content data */
+ size_t size; /* content data size */
+ GInputStream * istream;
+ GError *gerror = NULL;
+ gdouble rwidth, rheight;
+ gboolean gotsize;
+ /* check image header is valid and get width/height */
+
+ data = content__get_source_data(c, &size);
+
+ istream = g_memory_input_stream_new_from_data(data, size, NULL);
+ svgc->rsvgh = rsvg_handle_new_from_stream_sync(istream,
+ NULL,
+ RSVG_HANDLE_FLAGS_NONE,
+ NULL,
+ &gerror);
+ g_object_unref(istream);
+ if (svgc->rsvgh == NULL) {
+ NSLOG(netsurf, INFO, "Failed to create rsvg handle for content.");
+ return false;
+ }
+
+ gotsize = rsvg_handle_get_intrinsic_size_in_pixels(svgc->rsvgh,
+ &rwidth,
+ &rheight);
+ if (gotsize == TRUE) {
+ c->width = rwidth;
+ c->height = rheight;
+ } else {
+ RsvgRectangle ink_rect;
+ RsvgRectangle logical_rect;
+ rsvg_handle_get_geometry_for_element(svgc->rsvgh,
+ NULL,
+ &ink_rect,
+ &logical_rect,
+ NULL);
+ c->width = ink_rect.width;
+ c->height = ink_rect.height;
+ }
+
+ NSLOG(netsurf, DEBUG, "rsvg width:%d height:%d.",c->width, c->height);
+
+ c->size = c->width * c->height * 4;
+
+ image_cache_add(c, NULL, rsvg_cache_convert);
+
+ content_set_ready(c);
+ content_set_done(c);
+ content_set_status(c, ""); /* Done: update status bar */
+
+ return true;
+}
+
+
+static nserror rsvg_clone(const struct content *old, struct content **newc)
+{
+ rsvg_content *svg;
+ nserror error;
+
+ svg = calloc(1, sizeof(rsvg_content));
+ if (svg == NULL)
+ return NSERROR_NOMEM;
+
+ error = content__clone(old, &svg->base);
+ if (error != NSERROR_OK) {
+ content_destroy(&svg->base);
+ return error;
+ }
+
+ /* re-convert if the content is ready */
+ if ((old->status == CONTENT_STATUS_READY) ||
+ (old->status == CONTENT_STATUS_DONE)) {
+ if (rsvg_convert(&svg->base) == false) {
+ content_destroy(&svg->base);
+ return NSERROR_CLONE_FAILED;
+ }
+ }
+
+ *newc = (struct content *)svg;
+
+ return NSERROR_OK;
+}
+
+
+static void rsvg_destroy(struct content *c)
+{
+ rsvg_content *d = (rsvg_content *) c;
+
+ if (d->rsvgh != NULL) {
+ g_object_unref(d->rsvgh);
+ d->rsvgh = NULL;
+ }
+
+ return image_cache_destroy(c);
+}
+
+static const content_handler rsvg_content_handler = {
+ .create = rsvg_create,
+ .data_complete = rsvg_convert,
+ .destroy = rsvg_destroy,
+ .redraw = image_cache_redraw,
+ .clone = rsvg_clone,
+ .get_internal = image_cache_get_internal,
+ .type = image_cache_content_type,
+ .is_opaque = image_cache_is_opaque,
+ .no_share = false,
+};
+
+static const char *rsvg_types[] = {
+ "image/svg",
+ "image/svg+xml"
+};
+
+CONTENT_FACTORY_REGISTER_TYPES(nsrsvg, rsvg_types, rsvg_content_handler);
+
diff --git a/content/handlers/javascript/duktape/CSSRule.bnd b/content/handlers/javascript/duktape/CSSRule.bnd
new file mode 100644
index 000000000..555023c85
--- /dev/null
+++ b/content/handlers/javascript/duktape/CSSRule.bnd
@@ -0,0 +1,22 @@
+/* CSS Rule binding for NetSurf using duktape and libcss/libdom
+ *
+ * Copyright 2022 Daniel Silverstone <dsilvers@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * Released under the terms of the MIT License,
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+/* Note, for now this exists purely to block warnings, eventually
+ * rules will have to come from stylesheets etc.
+ */
+
+class CSSRule {
+ private bool unused;
+};
+
+init CSSRule()
+%{
+ priv->unused = true;
+%}
diff --git a/content/handlers/javascript/duktape/CSSStyleSheet.bnd b/content/handlers/javascript/duktape/CSSStyleSheet.bnd
new file mode 100644
index 000000000..9167b8afa
--- /dev/null
+++ b/content/handlers/javascript/duktape/CSSStyleSheet.bnd
@@ -0,0 +1,22 @@
+/* CSS Stylesheet binding for NetSurf using duktape and libcss/libdom
+ *
+ * Copyright 2022 Daniel Silverstone <dsilvers@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * Released under the terms of the MIT License,
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+/* Note, for now this exists purely to block warnings, eventually
+ * stylesheets will have to come from documents etc.
+ */
+
+class CSSStyleSheet {
+ private bool unused;
+};
+
+init CSSStyleSheet()
+%{
+ priv->unused = true;
+%}
diff --git a/content/handlers/javascript/duktape/Console.bnd b/content/handlers/javascript/duktape/Console.bnd
index 74601d6d4..89d13cfad 100644
--- a/content/handlers/javascript/duktape/Console.bnd
+++ b/content/handlers/javascript/duktape/Console.bnd
@@ -182,7 +182,7 @@ method Console::timeEnd()
duk_del_prop(ctx, 0);
duk_push_string(ctx, "Timer elapsed: ");
duk_insert(ctx, 0);
- duk_push_sprintf(ctx, "%lu ms", (duk_uint_t)(time_ms - old_time_ms));
+ duk_push_sprintf(ctx, "%u ms", (duk_uint_t)(time_ms - old_time_ms));
write_log_entry(ctx, priv->group, BW_CS_FLAG_LEVEL_INFO);
return 0;
%}
diff --git a/content/handlers/javascript/duktape/DOMSettableTokenList.bnd b/content/handlers/javascript/duktape/DOMSettableTokenList.bnd
new file mode 100644
index 000000000..ac5c7062a
--- /dev/null
+++ b/content/handlers/javascript/duktape/DOMSettableTokenList.bnd
@@ -0,0 +1,44 @@
+/* DOMTokenList binding for browser using duktape and libdom
+ *
+ * Copyright 2015 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * Released under the terms of the MIT License,
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+class DOMSettableTokenList {
+};
+
+init DOMSettableTokenList(struct dom_tokenlist *tokens::tokens);
+
+getter DOMSettableTokenList::value()
+%{
+ dom_exception exc;
+ dom_string *value;
+
+ exc = dom_tokenlist_get_value(priv->parent.tokens, &value);
+ if (exc != DOM_NO_ERR) return 0; /* coerced to undefined */
+
+ duk_push_lstring(ctx, dom_string_data(value), dom_string_length(value));
+ dom_string_unref(value);
+
+ return 1;
+%}
+
+setter DOMSettableTokenList::value()
+%{
+ dom_exception exc;
+ dom_string *value;
+ duk_size_t slen;
+ const char *s = duk_require_lstring(ctx, 0, &slen);
+
+ exc = dom_string_create_interned((const uint8_t *)s, slen, &value);
+ if (exc != DOM_NO_ERR) return 0;
+
+ exc = dom_tokenlist_set_value(priv->parent.tokens, value);
+ dom_string_unref(value);
+
+ return 0;
+%} \ No newline at end of file
diff --git a/content/handlers/javascript/duktape/DOMTokenList.bnd b/content/handlers/javascript/duktape/DOMTokenList.bnd
new file mode 100644
index 000000000..928d9ef35
--- /dev/null
+++ b/content/handlers/javascript/duktape/DOMTokenList.bnd
@@ -0,0 +1,163 @@
+/* DOMTokenList binding for browser using duktape and libdom
+ *
+ * Copyright 2015 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * Released under the terms of the MIT License,
+ * http://www.opensource.org/licenses/mit-license
+ */
+
+class DOMTokenList {
+ private struct dom_tokenlist *tokens;
+};
+
+init DOMTokenList(struct dom_tokenlist *tokens)
+%{
+ priv->tokens = tokens;
+ dom_tokenlist_ref(tokens);
+%}
+
+fini DOMTokenList()
+%{
+ dom_tokenlist_unref(priv->tokens);
+%}
+
+getter DOMTokenList::length()
+%{
+ dom_exception err;
+ uint32_t len;
+
+ err = dom_tokenlist_get_length(priv->tokens, &len);
+
+ if (err != DOM_NO_ERR) return 0; /* coerced to undefined */
+
+ duk_push_uint(ctx, (duk_uint_t)len);
+
+ return 1;
+%}
+
+method DOMTokenList::item()
+%{
+ unsigned long i = duk_to_uint(ctx, 0);
+ dom_exception err;
+ dom_string *value;
+
+ err = dom_tokenlist_item(priv->tokens, i, &value);
+
+ if (err != DOM_NO_ERR) return 0; /* coerced to undefined */
+
+ if (value == NULL) {
+ duk_push_null(ctx);
+ return 1;
+ }
+
+ duk_push_lstring(ctx, dom_string_data(value), dom_string_length(value));
+ dom_string_unref(value);
+
+ return 1;
+%}
+
+method DOMTokenList::add()
+%{
+ dom_exception exc;
+ dom_string *value;
+ duk_size_t slen;
+ const char *s;
+ duk_idx_t spos;
+
+ for (spos = 0; spos < duk_get_top(ctx); ++spos) {
+ s = duk_safe_to_lstring(ctx, spos, &slen);
+
+ duk_safe_to_lstring(ctx, 0, &slen);
+
+ exc = dom_string_create_interned((const uint8_t *)s, slen, &value);
+ if (exc != DOM_NO_ERR) return 0;
+
+ exc = dom_tokenlist_add(priv->tokens, value);
+ dom_string_unref(value);
+ }
+
+ return 0;
+%}
+
+method DOMTokenList::remove()
+%{
+ dom_exception exc;
+ dom_string *value;
+ duk_size_t slen;
+ const char *s;
+ duk_idx_t spos;
+
+ for (spos = 0; spos < duk_get_top(ctx); ++spos) {
+ s = duk_safe_to_lstring(ctx, spos, &slen);
+
+ duk_safe_to_lstring(ctx, 0, &slen);
+
+ exc = dom_string_create_interned((const uint8_t *)s, slen, &value);
+ if (exc != DOM_NO_ERR) return 0;
+
+ exc = dom_tokenlist_remove(priv->tokens, value);
+ dom_string_unref(value);
+ }
+
+ return 0;
+%}
+
+method DOMTokenList::contains()
+%{
+ dom_exception exc;
+ dom_string *value;
+ duk_size_t slen;
+ const char *s = duk_safe_to_lstring(ctx, 0, &slen);
+ bool present = false;
+
+ exc = dom_string_create_interned((const uint8_t *)s, slen, &value);
+ if (exc != DOM_NO_ERR) return 0;
+
+ exc = dom_tokenlist_contains(priv->tokens, value, &present);
+ dom_string_unref(value);
+ if (exc != DOM_NO_ERR) return 0;
+
+ duk_push_boolean(ctx, present);
+
+ return 1;
+%}
+
+method DOMTokenList::toggle()
+%{
+ dom_exception exc;
+ dom_string *value;
+ duk_size_t slen;
+ const char *s = duk_require_lstring(ctx, 0, &slen);
+ bool toggle_set = duk_get_top(ctx) > 1;
+ bool toggle = duk_opt_boolean(ctx, 1, 0);
+ bool present;
+
+ exc = dom_string_create_interned((const uint8_t *)s, slen, &value);
+ if (exc != DOM_NO_ERR) return 0;
+
+ exc = dom_tokenlist_contains(priv->tokens, value, &present);
+ if (exc != DOM_NO_ERR) {
+ dom_string_unref(value);
+ return 0;
+ }
+
+ /* Decision matrix is based on present, toggle_set, and toggle */
+ if (toggle_set) {
+ if (toggle) {
+ exc = dom_tokenlist_add(priv->tokens, value);
+ } else {
+ exc = dom_tokenlist_remove(priv->tokens, value);
+ }
+ } else {
+ if (present) {
+ exc = dom_tokenlist_add(priv->tokens, value);
+ } else {
+ exc = dom_tokenlist_remove(priv->tokens, value);
+ }
+ }
+ dom_string_unref(value);
+
+ return 0;
+%} \ No newline at end of file
diff --git a/content/handlers/javascript/duktape/Element.bnd b/content/handlers/javascript/duktape/Element.bnd
index a965a6ceb..ff0686fd9 100644
--- a/content/handlers/javascript/duktape/Element.bnd
+++ b/content/handlers/javascript/duktape/Element.bnd
@@ -392,6 +392,37 @@ setter Element::className ()
return 0;
%}
+getter Element::classList ()
+%{
+ dom_exception exc;
+ dom_tokenlist *tokens;
+
+ duk_set_top(ctx, 0);
+ duk_push_this(ctx);
+ duk_get_prop_string(ctx, 0, MAGIC(classList));
+
+ if (duk_is_undefined(ctx, -1)) {
+ duk_pop(ctx);
+ exc = dom_tokenlist_create((dom_element *)priv->parent.node, corestring_dom_class, &tokens);
+ if (exc != DOM_NO_ERR) return 0; /* Coerced to undefined */
+
+ /* Create a settable tokenlist - While the IDL says this isn't settable, all browsers
+ * seem to make it settable, so we'll permit it too
+ */
+ duk_push_pointer(ctx, tokens);
+ if (dukky_create_object(ctx, PROTO_NAME(DOMSETTABLETOKENLIST), 1) != DUK_EXEC_SUCCESS) {
+ dom_tokenlist_unref(tokens);
+ NSLOG(dukky, DEBUG, "Unable to create DOMSettableTokenList object");
+ return 0; /* Coerced to undefined */
+ }
+ dom_tokenlist_unref(tokens);
+ duk_dup(ctx, -1);
+ duk_put_prop_string(ctx, 0, MAGIC(classList));
+ }
+
+ return 1;
+%}
+
getter Element::innerHTML()
%{
duk_push_lstring(ctx, "", 0);
diff --git a/content/handlers/javascript/duktape/ImageData.bnd b/content/handlers/javascript/duktape/ImageData.bnd
index 17673d92a..be28cbd77 100644
--- a/content/handlers/javascript/duktape/ImageData.bnd
+++ b/content/handlers/javascript/duktape/ImageData.bnd
@@ -39,6 +39,6 @@ getter ImageData::data()
%{
duk_push_this(ctx);
duk_get_prop_string(ctx, -1, MAGIC(DATA));
- duk_push_buffer_object(ctx, -1, 0, priv->width * priv->height * 4, DUK_BUFOBJ_UINT8CLAMPEDARRAY);
+ duk_push_buffer_object(ctx, -1, 0, priv->width * priv->height * 4llu, DUK_BUFOBJ_UINT8CLAMPEDARRAY);
return 1;
%}
diff --git a/content/handlers/javascript/duktape/netsurf.bnd b/content/handlers/javascript/duktape/netsurf.bnd
index e47f07d2b..483f3471f 100644
--- a/content/handlers/javascript/duktape/netsurf.bnd
+++ b/content/handlers/javascript/duktape/netsurf.bnd
@@ -60,6 +60,8 @@ struct dom_html_br_element;
#include "Document.bnd"
#include "Node.bnd"
#include "NodeList.bnd"
+#include "DOMTokenList.bnd"
+#include "DOMSettableTokenList.bnd"
#include "NamedNodeMap.bnd"
#include "Element.bnd"
#include "HTMLCollection.bnd"
@@ -205,3 +207,7 @@ init HTMLPropertiesCollection(struct dom_html_collection *coll);
#include "CanvasRenderingContext2D.bnd"
#include "ImageData.bnd"
+/* CSS Object model */
+
+#include "CSSRule.bnd"
+#include "CSSStyleSheet.bnd"
diff --git a/content/handlers/javascript/duktape/polyfill.js b/content/handlers/javascript/duktape/polyfill.js
index 5f0215618..6c85ce1a0 100644
--- a/content/handlers/javascript/duktape/polyfill.js
+++ b/content/handlers/javascript/duktape/polyfill.js
@@ -83,3 +83,21 @@ if (!Array.from) {
};
}());
}
+
+// DOMTokenList formatter, in theory we can remove this if we do the stringifier IDL support
+
+DOMTokenList.prototype.toString = function () {
+ if (this.length == 0) {
+ return "";
+ }
+
+ var ret = this.item(0);
+ for (var index = 1; index < this.length; index++) {
+ ret = ret + " " + this.item(index);
+ }
+
+ return ret;
+}
+
+// Inherit the same toString for settable lists
+DOMSettableTokenList.prototype.toString = DOMTokenList.prototype.toString; \ No newline at end of file
diff --git a/content/llcache.c b/content/llcache.c
index 7db59de0a..f86ae0d42 100644
--- a/content/llcache.c
+++ b/content/llcache.c
@@ -116,7 +116,7 @@ typedef struct {
bool tried_with_auth; /**< Whether we've tried with auth */
- bool tried_with_tls_downgrade; /**< Whether we've tried TLS <= 1.0 */
+ bool tried_with_tls_downgrade; /**< Whether we've tried TLS 1.2 */
bool tainted_tls; /**< Whether the TLS transport is tainted */
} llcache_fetch_ctx;
diff --git a/content/urldb.c b/content/urldb.c
index 74d0b7db2..7352d67c3 100644
--- a/content/urldb.c
+++ b/content/urldb.c
@@ -1526,7 +1526,10 @@ static void urldb_dump_search(struct search_node *parent, int depth)
for (h = parent->data; h; h = h->parent) {
if (h->part) {
r = snprintf(&s[i], sl - i, "%s", h->part);
- if ((i + r) > sl) {
+ if (r < 0) {
+ break;
+ }
+ if ((i + r) >= sl) {
break;
}
i += r;
diff --git a/docs/UnimplementedJavascript.md b/docs/UnimplementedJavascript.md
index 4a2f02e3f..0d21dc852 100644
--- a/docs/UnimplementedJavascript.md
+++ b/docs/UnimplementedJavascript.md
@@ -170,7 +170,6 @@ getter | Element::namespaceURI(string);
getter | Element::prefix(string);
getter | Element::localName(string);
getter | Element::tagName(string);
-getter | Element::classList(user);
getter | Element::outerHTML(string);
setter | Element::outerHTML(string);
getter | Element::children(user);
@@ -597,10 +596,6 @@ method | CanvasRenderingContext2D::arcTo();
method | CanvasRenderingContext2D::rect();
method | CanvasRenderingContext2D::arc();
method | CanvasRenderingContext2D::ellipse();
-getter | CanvasRenderingContext2D::width(unsigned long);
-setter | CanvasRenderingContext2D::width(unsigned long);
-getter | CanvasRenderingContext2D::height(unsigned long);
-setter | CanvasRenderingContext2D::height(unsigned long);
getter | CanvasRenderingContext2D::currentTransform(user);
setter | CanvasRenderingContext2D::currentTransform(user);
getter | CanvasRenderingContext2D::globalAlpha(double);
@@ -647,8 +642,6 @@ method | HTMLCanvasElement::setContext();
method | HTMLCanvasElement::transferControlToProxy();
method | HTMLCanvasElement::toDataURL();
method | HTMLCanvasElement::toBlob();
-setter | HTMLCanvasElement::width(unsigned long);
-setter | HTMLCanvasElement::height(unsigned long);
getter | HTMLTemplateElement::content(user);
getter | HTMLScriptElement::async(boolean);
setter | HTMLScriptElement::async(boolean);
@@ -1344,14 +1337,6 @@ method | HTMLAllCollection::namedItem();
getter | HTMLAllCollection::length(unsigned long);
method | XMLSerializer::serializeToString();
method | DOMParser::parseFromString();
-method | DOMTokenList::item();
-method | DOMTokenList::contains();
-method | DOMTokenList::add();
-method | DOMTokenList::remove();
-method | DOMTokenList::toggle();
-getter | DOMTokenList::length(unsigned long);
-getter | DOMSettableTokenList::value(string);
-setter | DOMSettableTokenList::value(string);
method | NodeFilter::acceptNode();
method | TreeWalker::parentNode();
method | TreeWalker::firstChild();
@@ -1560,5 +1545,5 @@ method | EventListener::handleEvent();
method | CustomEvent::initCustomEvent();
getter | CustomEvent::detail(any);
- 1554 unimplemented bindings
+ 1539 unimplemented bindings
diff --git a/docs/env.sh b/docs/env.sh
index 80292a40c..4cb056ec1 100644
--- a/docs/env.sh
+++ b/docs/env.sh
@@ -263,64 +263,81 @@ NS_GIT="git://git.netsurf-browser.org"
# Buildsystem: everything depends on this
NS_BUILDSYSTEM="buildsystem"
-# internal libraries all frontends require (order is important)
-NS_INTERNAL_LIBS="libwapcaplet libparserutils libhubbub libdom libcss libnsgif libnsbmp libutf8proc libnsutils libnspsl libnslog"
+NS_TOOLS=""
+NS_FRONTEND_LIBS=""
-# The browser itself
-NS_BROWSER="netsurf"
+BUILD_TARGET="${TARGET:-netsurf}"
-
-# add target specific libraries
-case "${HOST}" in
- i586-pc-haiku)
- # tools required to build the browser for haiku (beos)
- NS_TOOLS="nsgenbind"
- # libraries required for the haiku target abi
- NS_FRONTEND_LIBS="libsvgtiny"
- ;;
- *arwin*)
- # tools required to build the browser for OS X
- NS_TOOLS=""
- # libraries required for the Darwin target abi
- NS_FRONTEND_LIBS="libsvgtiny libnsfb"
- ;;
- arm-unknown-riscos|arm-riscos-gnueabihf)
- # tools required to build the browser for RISC OS
- NS_TOOLS="nsgenbind"
- # libraries required for the risc os target abi
- NS_FRONTEND_LIBS="libsvgtiny librufl libpencil librosprite"
+case "$BUILD_TARGET" in
+ libhubbub)
+ NS_INTERNAL_LIBS="libparserutils"
;;
- *-atari-mint)
- # tools required to build the browser for atari
- NS_TOOLS=""
- # libraries required for the atari frontend
- NS_FRONTEND_LIBS=""
- ;;
- ppc-amigaos)
- # default tools required to build the browser
- NS_TOOLS="nsgenbind"
- # default additional internal libraries
- NS_FRONTEND_LIBS="libsvgtiny"
- ;;
- m68k-unknown-amigaos)
- # default tools required to build the browser
- NS_TOOLS="nsgenbind"
- # default additional internal libraries
- NS_FRONTEND_LIBS="libsvgtiny"
+
+ libdom)
+ NS_INTERNAL_LIBS="libwapcaplet libparserutils libhubbub"
;;
- *-unknown-freebsd*)
- # tools required to build the browser for freebsd
- NS_TOOLS=""
- # libraries required for the freebsd frontend
- NS_FRONTEND_LIBS=""
- # select gnu make
- MAKE=gmake
+
+ libcss)
+ NS_INTERNAL_LIBS="libwapcaplet libparserutils"
;;
- *)
- # default tools required to build the browser
- NS_TOOLS="nsgenbind"
- # default additional internal libraries
- NS_FRONTEND_LIBS="libsvgtiny libnsfb"
+
+ netsurf)
+ # internal libraries all frontends require (order is important)
+ NS_INTERNAL_LIBS="libwapcaplet libparserutils libhubbub libdom libcss libnsgif libnsbmp libutf8proc libnsutils libnspsl libnslog"
+
+ # add target specific libraries
+ case "${HOST}" in
+ i586-pc-haiku)
+ # tools required to build the browser for haiku (beos)
+ NS_TOOLS="nsgenbind"
+ # libraries required for the haiku target abi
+ NS_FRONTEND_LIBS="libsvgtiny"
+ ;;
+ *arwin*)
+ # tools required to build the browser for OS X
+ NS_TOOLS=""
+ # libraries required for the Darwin target abi
+ NS_FRONTEND_LIBS="libsvgtiny libnsfb"
+ ;;
+ arm-unknown-riscos|arm-riscos-gnueabi*)
+ # tools required to build the browser for RISC OS
+ NS_TOOLS="nsgenbind"
+ # libraries required for the risc os target abi
+ NS_FRONTEND_LIBS="libsvgtiny librufl libpencil librosprite"
+ ;;
+ *-atari-mint)
+ # tools required to build the browser for atari
+ NS_TOOLS=""
+ # libraries required for the atari frontend
+ NS_FRONTEND_LIBS=""
+ ;;
+ ppc-amigaos)
+ # default tools required to build the browser
+ NS_TOOLS="nsgenbind"
+ # default additional internal libraries
+ NS_FRONTEND_LIBS="libsvgtiny"
+ ;;
+ m68k-unknown-amigaos)
+ # default tools required to build the browser
+ NS_TOOLS="nsgenbind"
+ # default additional internal libraries
+ NS_FRONTEND_LIBS="libsvgtiny"
+ ;;
+ *-unknown-freebsd*)
+ # tools required to build the browser for freebsd
+ NS_TOOLS=""
+ # libraries required for the freebsd frontend
+ NS_FRONTEND_LIBS=""
+ # select gnu make
+ MAKE=gmake
+ ;;
+ *)
+ # default tools required to build the browser
+ NS_TOOLS="nsgenbind"
+ # default additional internal libraries
+ NS_FRONTEND_LIBS="libsvgtiny libnsfb"
+ ;;
+ esac
;;
esac
@@ -331,7 +348,7 @@ export MAKE
# git pull in all repos parameters are passed to git pull
ns-pull()
{
- for REPO in $(echo ${NS_BUILDSYSTEM} ${NS_INTERNAL_LIBS} ${NS_FRONTEND_LIBS} ${NS_TOOLS} ${NS_BROWSER}) ; do
+ for REPO in $(echo ${NS_BUILDSYSTEM} ${NS_INTERNAL_LIBS} ${NS_FRONTEND_LIBS} ${NS_TOOLS} ${BUILD_TARGET}) ; do
echo -n " GIT: Pulling ${REPO}: "
if [ -f "${TARGET_WORKSPACE}/${REPO}/.git/config" ]; then
(cd ${TARGET_WORKSPACE}/${REPO} && git pull $*; )
@@ -344,19 +361,42 @@ ns-pull()
# clone all repositories
ns-clone()
{
+ SHALLOW=""
+ SKIP=""
+ while [ $# -gt 0 ]
+ do
+ case "$1" in
+ -d | --deps-only) SKIP="${BUILD_TARGET}"
+ shift
+ ;;
+ -s | --shallow) SHALLOW="--depth 1"
+ shift
+ ;;
+ -*) echo "Error: Unknown option: $1" >&2
+ exit 1
+ ;;
+ *) # No more options
+ break
+ ;;
+ esac
+ done
+
mkdir -p ${TARGET_WORKSPACE}
- for REPO in $(echo ${NS_BUILDSYSTEM} ${NS_INTERNAL_LIBS} ${NS_FRONTEND_LIBS} ${NS_RISCOS_LIBS} ${NS_TOOLS} ${NS_BROWSER}) ; do
+ for REPO in $(echo ${NS_BUILDSYSTEM} ${NS_INTERNAL_LIBS} ${NS_FRONTEND_LIBS} ${NS_RISCOS_LIBS} ${NS_TOOLS} ${BUILD_TARGET}) ; do
+ [ "x${REPO}" != "x${SKIP}" ] || continue
echo -n " GIT: Cloning ${REPO}: "
if [ -f ${TARGET_WORKSPACE}/${REPO}/.git/config ]; then
echo "Repository already present"
else
- (cd ${TARGET_WORKSPACE} && git clone ${NS_GIT}/${REPO}.git; )
+ (cd ${TARGET_WORKSPACE} && git clone ${SHALLOW} ${NS_GIT}/${REPO}.git; )
fi
done
# put current env.sh in place in workspace
- if [ ! -f "${TARGET_WORKSPACE}/env.sh" -a -f ${TARGET_WORKSPACE}/${NS_BROWSER}/docs/env.sh ]; then
- cp ${TARGET_WORKSPACE}/${NS_BROWSER}/docs/env.sh ${TARGET_WORKSPACE}/env.sh
+ if [ "x$NS_BROWSER" = "x" ]; then
+ if [ ! -f "${TARGET_WORKSPACE}/env.sh" -a -f ${TARGET_WORKSPACE}/${NS_BROWSER}/docs/env.sh ]; then
+ cp ${TARGET_WORKSPACE}/${NS_BROWSER}/docs/env.sh ${TARGET_WORKSPACE}/env.sh
+ fi
fi
}
diff --git a/frontends/amiga/Makefile b/frontends/amiga/Makefile
index 99745c90e..f537a1163 100644
--- a/frontends/amiga/Makefile
+++ b/frontends/amiga/Makefile
@@ -80,7 +80,7 @@ AMIGA_DISTRIBUTION_FILES := $(FRONTEND_SOURCE_DIR)/dist/*
AMIGA_PKG_DIR := $(FRONTEND_SOURCE_DIR)/pkg
AMIGA_INSTALL_TARGET_DIR := NetSurf_Amiga
-netsurf.lha: $(EXETARGET)
+netsurf.lha: $(EXETARGET) $(POSTEXES)
$(VQ)echo Creating netsurf.lha
$(Q)rm -rf $(AMIGA_INSTALL_TARGET_DIR)
$(Q)$(MKDIR) -p $(AMIGA_INSTALL_TARGET_DIR)/NetSurf
diff --git a/frontends/amiga/gui_options.c b/frontends/amiga/gui_options.c
index 79af71c5d..395e5b48e 100755
--- a/frontends/amiga/gui_options.c
+++ b/frontends/amiga/gui_options.c
@@ -886,7 +886,7 @@ void ami_gui_opts_open(void)
#else
CHOOSER_Labels, &gow->pagethemeoptslist,
#endif
- CHOOSER_Selected, nsoption_bool(os_mouse_pointers),
+ CHOOSER_Selected, nsoption_bool(prefer_dark_mode),
ChooserEnd,
CHILD_Label, LabelObj,
LABEL_Text, gadlab[GID_OPTS_THEMEPAGE],
diff --git a/frontends/atari/Makefile b/frontends/atari/Makefile
index 6de15b62e..2f6d7118b 100644
--- a/frontends/atari/Makefile
+++ b/frontends/atari/Makefile
@@ -161,7 +161,7 @@ $(ATARI_FONT_TMP_DIR)$(ATARI_FONT_NAME):
package-atari: $(ATARI_FONT_TMP_DIR)$(ATARI_FONT_NAME) $(PKGNAME)
$(VQ)echo Creating $(PKGNAME)
-$(PKGNAME): $(EXETARGET)
+$(PKGNAME): $(EXETARGET) $(POSTEXES)
ifneq ($(strip $(STRIP)),)
$(Q)echo Stripping symbols from $(EXETARGET) with $(STRIP)
$(Q)$(STRIP) $(EXETARGET)
diff --git a/frontends/beos/Makefile b/frontends/beos/Makefile
index 46f12ec85..81c9326ee 100644
--- a/frontends/beos/Makefile
+++ b/frontends/beos/Makefile
@@ -151,7 +151,7 @@ install-beos:
package-beos: $(PKGNAME)
$(VQ)echo Creating $(PKGNAME)
-$(PKGNAME): $(EXETARGET)
+$(PKGNAME): $(EXETARGET) $(POSTEXES)
$(Q)rm -rf $(HAIKU_TARGET_DIR)
$(Q)rm -rf $(PKGNAME)
$(Q)$(MKDIR) $(HAIKU_TARGET_DIR)
diff --git a/frontends/gtk/Makefile b/frontends/gtk/Makefile
index 4f3fd5f52..79f03b370 100644
--- a/frontends/gtk/Makefile
+++ b/frontends/gtk/Makefile
@@ -11,6 +11,8 @@
NETSURF_FEATURE_RSVG_CFLAGS := -DWITH_RSVG
NETSURF_FEATURE_VIDEO_CFLAGS := -DWITH_VIDEO
+# determine if the rsvg library API version
+RSVG_API := $(shell $(PKG_CONFIG) --atleast-version=2.46 librsvg-2.0 && echo 246)
$(eval $(call pkg_config_find_and_add_enabled,RSVG,librsvg-2.0,SVG))
$(eval $(call pkg_config_find_and_add_enabled,VIDEO,gstreamer-0.10,Video))
diff --git a/frontends/gtk/gdk.c b/frontends/gtk/gdk.c
index 2912862da..fe9a0791c 100644
--- a/frontends/gtk/gdk.c
+++ b/frontends/gtk/gdk.c
@@ -73,7 +73,7 @@ nsgdk_pixbuf_get_from_surface(cairo_surface_t *surface, int scwidth, int scheigh
memset(gdk_pixbuf_get_pixels(pixbuf),
0xff,
- gdk_pixbuf_get_rowstride(pixbuf) * scheight);
+ gdk_pixbuf_get_rowstride(pixbuf) * (size_t)scheight);
/* scale cairo surface into new surface the target size */
cairo_surface_flush(surface); /* ensure source surface is ready */
diff --git a/frontends/gtk/toolbar.c b/frontends/gtk/toolbar.c
index be676e210..6ec41cc1d 100644
--- a/frontends/gtk/toolbar.c
+++ b/frontends/gtk/toolbar.c
@@ -673,15 +673,22 @@ nsgtk_toolbar_customisation_save(struct nsgtk_toolbar *tb)
for (location = BACK_BUTTON;
location < PLACEHOLDER_BUTTON;
location++) {
+ int written;
itemid = itemid_from_location(tb, location);
if (itemid == PLACEHOLDER_BUTTON) {
/* no more filled locations */
break;
}
- start += snprintf(start,
+ written = snprintf(start,
orderlen - (start - order),
"%s/",
tb->items[itemid].name);
+ if ((written < 0) ||
+ (written >= orderlen - (start - order))) {
+ free(order);
+ return NSERROR_UNKNOWN;
+ }
+ start += written;
if ((start - order) >= orderlen) {
break;
diff --git a/frontends/riscos/Makefile b/frontends/riscos/Makefile
index 79e6fc493..2b595916a 100644
--- a/frontends/riscos/Makefile
+++ b/frontends/riscos/Makefile
@@ -22,6 +22,9 @@ RESOURCES = $(TPD_RISCOS)
CFLAGS += -Driscos -std=c99 -D_BSD_SOURCE -D_POSIX_C_SOURCE \
-mpoke-function-name -fno-strict-aliasing
+ifeq ($(findstring -elfeabi,$(SUBTARGET)),-elfeabi)
+ CFLAGS += -funwind-tables
+endif
CFLAGS += -I$(GCCSDK_INSTALL_ENV)/include
ifeq ($(HOST),riscos)
@@ -111,6 +114,9 @@ MESSAGES_FILTER=ro
$(FRONTEND_SOURCE_DIR)/appdir/!Run$(RUNEXT): $(FRONTEND_SOURCE_DIR)/scripts/Run $(EXETARGET)
$(VQ)echo " MAKERUN: $@"
$(Q)$(MAKERUN) $(EXETARGET) $< $@
+ifeq ($(findstring -elfeabi,$(SUBTARGET)),-elfeabi)
+ $(Q)$(SED) -i -e 's/^|\(RMEnsure ARMEABISupport\)/\1/' $@
+endif
$(FRONTEND_SOURCE_DIR)/appdir/!Help$(RUNEXT): $(FRONTEND_SOURCE_DIR)/scripts/Help
$(VQ)echo " CP: $@"
@@ -174,7 +180,7 @@ install-riscos:
package-riscos: netsurf.zip
-netsurf.zip: $(EXETARGET)
+netsurf.zip: $(EXETARGET) $(POSTEXES)
$(eval $@_TMPDIR := $(shell mktemp -d))
$(Q) $(RM) $@
$(Q) cp -rLvp $(FRONTEND_SOURCE_DIR)/appdir $($@_TMPDIR)/!NetSurf
diff --git a/frontends/riscos/Makefile.tools b/frontends/riscos/Makefile.tools
index 19a2e7768..a5b5cca1e 100644
--- a/frontends/riscos/Makefile.tools
+++ b/frontends/riscos/Makefile.tools
@@ -16,27 +16,33 @@ ifeq ($(HOST),riscos)
EXEEXT :=
PKG_CONFIG :=
ZIP := zip
+ SED :=
else
# Cross-build for RO
- # Three options are available:
- # a. GCCSDK 3.4.6 - AOF (machine: arm-unknown-riscos)
- # b. GCCSDK 4 - ELF (machine: arm-unknown-riscos)
- # c. GCCSDK 8+ - ELF, using EABI (machine: arm-riscos-gnueabihf)
+ # Four options are available:
+ # a. GCCSDK 3.4.6 - AOF (machine: arm-unknown-riscos)
+ # b. GCCSDK 4 - ELF (machine: arm-unknown-riscos)
+ # c. GCCSDK 8+ - ELF, using soft-float EABI (machine: arm-riscos-gnueabi)
+ # d. GCCSDK 8+ - ELF, using hard-float EABI (machine: arm-riscos-gnueabihf)
# GCCSDK 3.4.6 and 4 are distinguished by GCCSDK 3.4.6 binary names
# not having the machine prefix (e.g. gcc), whereas GCCSDK 4 binaries
# do (e.g. arm-unknown-riscos-gcc).
# Search for the toolchain install locations if we haven't been told
- # The search order prefers GCCSDK 3.4.6/4 over 8+.
+ # The search order prefers GCCSDK 3.4.6/4 over 8+ and soft-float over hard.
ifeq ($(origin GCCSDK_INSTALL_ENV),undefined)
ifneq ($(realpath /opt/netsurf/arm-unknown-riscos/env),)
GCCSDK_INSTALL_ENV := /opt/netsurf/arm-unknown-riscos/env
else
- ifneq ($(realpath /opt/netsurf/arm-riscos-gnueabihf/env),)
- GCCSDK_INSTALL_ENV := /opt/netsurf/arm-riscos-gnueabihf/env
+ ifneq ($(realpath /opt/netsurf/arm-riscos-gnueabi/env),)
+ GCCSDK_INSTALL_ENV := /opt/netsurf/arm-riscos-gnueabi/env
else
- # No NetSurf-specific toolchain found: try the "normal" GCCSDK path
- GCCSDK_INSTALL_ENV := /home/riscos/env
+ ifneq ($(realpath /opt/netsurf/arm-riscos-gnueabihf/env),)
+ GCCSDK_INSTALL_ENV := /opt/netsurf/arm-riscos-gnueabihf/env
+ else
+ # No NetSurf-specific toolchain found: try the "normal" GCCSDK path
+ GCCSDK_INSTALL_ENV := /home/riscos/env
+ endif
endif
endif
endif
@@ -45,11 +51,15 @@ else
ifneq ($(realpath /opt/netsurf/arm-unknown-riscos/cross/bin),)
GCCSDK_INSTALL_CROSSBIN := /opt/netsurf/arm-unknown-riscos/cross/bin
else
- ifneq ($(realpath /opt/netsurf/arm-riscos-gnueabihf/cross/bin),)
- GCCSDK_INSTALL_CROSSBIN := /opt/netsurf/arm-riscos-gnueabihf/cross/bin
+ ifneq ($(realpath /opt/netsurf/arm-riscos-gnueabi/cross/bin),)
+ GCCSDK_INSTALL_CROSSBIN := /opt/netsurf/arm-riscos-gnueabi/cross/bin
else
- # No NetSurf-specific toolchain found: try the "normal" GCCSDK path
- GCCSDK_INSTALL_CROSSBIN := /home/riscos/cross/bin
+ ifneq ($(realpath /opt/netsurf/arm-riscos-gnueabihf/cross/bin),)
+ GCCSDK_INSTALL_CROSSBIN := /opt/netsurf/arm-riscos-gnueabihf/cross/bin
+ else
+ # No NetSurf-specific toolchain found: try the "normal" GCCSDK path
+ GCCSDK_INSTALL_CROSSBIN := /home/riscos/cross/bin
+ endif
endif
endif
endif
@@ -68,9 +78,12 @@ else
EXEEXT := ,e1f
ELF2AIF := $(GCCSDK_INSTALL_CROSSBIN)/elf2aif
else
- ifneq (,$(findstring arm-riscos-gnueabihf-gcc,$(CC)))
+ ifneq (,$(findstring arm-riscos-gnueabi,$(CC)))
# GCCSDK 8+
SUBTARGET := -elfeabi
+ ifneq (,$(findstring gnueabihf,$(CC)))
+ SUBTARGET := -elfeabihf
+ endif
EXEEXT := ,e1f
ELF2AIF := $(GCCSDK_INSTALL_CROSSBIN)/elf2aif -e
else
@@ -83,4 +96,5 @@ else
CXX := $(wildcard $(GCCSDK_INSTALL_CROSSBIN)/*g++)
PKG_CONFIG = PKG_CONFIG_LIBDIR="$(PREFIX)/lib/pkgconfig:$(GCCSDK_INSTALL_ENV)/lib/pkgconfig:$(GCCSDK_INSTALL_ENV)/share/pkgconfig" pkg-config
ZIP := $(GCCSDK_INSTALL_CROSSBIN)/zip
+ SED := sed
endif
diff --git a/frontends/riscos/gui/url_bar.c b/frontends/riscos/gui/url_bar.c
index 8802db731..99a90f58a 100644
--- a/frontends/riscos/gui/url_bar.c
+++ b/frontends/riscos/gui/url_bar.c
@@ -351,9 +351,12 @@ static bool ro_gui_url_bar_icon_update(struct url_bar *url_bar)
if (url_bar->display) {
icon.icon.flags |= (wimp_BUTTON_NEVER <<
wimp_ICON_BUTTON_TYPE_SHIFT);
- } else {
+ } else if (!ns_wimp_has_text_selection()) {
icon.icon.flags |= (wimp_BUTTON_WRITE_CLICK_DRAG <<
wimp_ICON_BUTTON_TYPE_SHIFT);
+ } else {
+ icon.icon.flags |= (wimp_BUTTON_WRITABLE <<
+ wimp_ICON_BUTTON_TYPE_SHIFT);
}
error = xwimp_create_icon(&icon, &url_bar->text.icon);
if (error) {
diff --git a/frontends/riscos/image.c b/frontends/riscos/image.c
index 0bad94810..5c90b05ba 100644
--- a/frontends/riscos/image.c
+++ b/frontends/riscos/image.c
@@ -27,6 +27,7 @@
#include "riscos/image.h"
#include "riscos/gui.h"
+#include "riscos/wimp.h"
#include "riscos/tinct.h"
/**
@@ -65,11 +66,11 @@ static bool image_redraw_tinct(osspriteop_id header, int x, int y,
if (alpha) {
error = _swix(Tinct_PlotScaledAlpha, _INR(2,7),
- header, x, y - req_height,
+ header, x, y,
req_width, req_height, tinct_options);
} else {
error = _swix(Tinct_PlotScaled, _INR(2,7),
- header, x, y - req_height,
+ header, x, y,
req_width, req_height, tinct_options);
}
@@ -144,13 +145,11 @@ static bool image_redraw_os(osspriteop_id header, int x, int y, int req_width,
if (tile) {
error = xosspriteop_plot_tiled_sprite(osspriteop_PTR,
- osspriteop_UNSPECIFIED, header,
- x, (int)(y - req_height),
+ osspriteop_UNSPECIFIED, header, x, y,
osspriteop_USE_MASK, &f, table);
} else {
error = xosspriteop_put_sprite_scaled(osspriteop_PTR,
- osspriteop_UNSPECIFIED, header,
- x, (int)(y - req_height),
+ osspriteop_UNSPECIFIED, header, x, y,
osspriteop_USE_MASK, &f, table);
}
if (error) {
@@ -173,17 +172,19 @@ static bool image_redraw_os(osspriteop_id header, int x, int y, int req_width,
* Only replaces mode if existing mode matches \ref old.
*
* \param[in] area The sprite area containing the sprite.
+ * \param[in] type Requested plot mode.
* \param[in] old Existing sprite mode to check for.
* \param[in] new Sprite mode to set if existing mode is expected.
*/
static inline void image__override_sprite_mode(
osspriteop_area *area,
+ image_type type,
os_mode old,
os_mode new)
{
osspriteop_header *sprite = (osspriteop_header *)(area + 1);
- if (sprite->mode == old) {
+ if (sprite->mode == old && type == IMAGE_PLOT_TINCT_ALPHA) {
sprite->mode = new;
}
}
@@ -210,6 +211,7 @@ bool image_redraw(osspriteop_area *area, int x, int y, int req_width,
colour background_colour,
bool repeatx, bool repeaty, bool background, image_type type)
{
+ image_type used_type = type;
unsigned int tinct_options;
bool tinct_avoid = false;
bool res = false;
@@ -220,10 +222,13 @@ bool image_redraw(osspriteop_area *area, int x, int y, int req_width,
osspriteop_id header = (osspriteop_id)
((char*) area + area->first);
+
req_width *= 2;
req_height *= 2;
width *= 2;
height *= 2;
+ y -= req_height;
+
tinct_options = background ? nsoption_int(plot_bg_quality) :
nsoption_int(plot_fg_quality);
@@ -234,12 +239,24 @@ bool image_redraw(osspriteop_area *area, int x, int y, int req_width,
* is that we lose the optimisation for tiling tiny bitmaps.
*/
if (tinct_options & tinct_USE_OS_SPRITE_OP) {
- type = IMAGE_PLOT_OS;
+ used_type = IMAGE_PLOT_OS;
tinct_avoid = true;
}
}
- switch (type) {
+ if (tinct_avoid) {
+ int xeig;
+ int yeig;
+
+ if (ro_gui_wimp_read_eig_factors(os_CURRENT_MODE,
+ &xeig, &yeig)) {
+
+ req_width = (req_width / 2) * (4 >> xeig);
+ req_height = (req_height / 2) * (4 >> yeig);
+ }
+ }
+
+ switch (used_type) {
case IMAGE_PLOT_TINCT_ALPHA:
res = image_redraw_tinct(header, x, y,
req_width, req_height,
@@ -260,7 +277,7 @@ bool image_redraw(osspriteop_area *area, int x, int y, int req_width,
case IMAGE_PLOT_OS:
if (tinct_avoid) {
- image__override_sprite_mode(area,
+ image__override_sprite_mode(area, type,
tinct_SPRITE_MODE,
alpha_SPRITE_MODE);
}
@@ -268,7 +285,7 @@ bool image_redraw(osspriteop_area *area, int x, int y, int req_width,
req_height, width, height,
repeatx | repeaty);
if (tinct_avoid) {
- image__override_sprite_mode(area,
+ image__override_sprite_mode(area, type,
alpha_SPRITE_MODE,
tinct_SPRITE_MODE);
}
diff --git a/frontends/riscos/scripts/Run b/frontends/riscos/scripts/Run
index 94aa933b0..cb78e907e 100644
--- a/frontends/riscos/scripts/Run
+++ b/frontends/riscos/scripts/Run
@@ -68,6 +68,10 @@ RMEnsure DrawFile 1.30 Error NetSurf requires the DrawFile module. This can be d
RMEnsure SharedUnixLibrary 1.07 NetSurfRMLoad System:Modules.SharedULib
RMEnsure SharedUnixLibrary 1.07 Error NetSurf requires SharedUnixLibrary 1.07 or later. Please use the RISC OS Configure app to update the computer's !System directory from the NetSurf archive.
+| Ensure ARMEABISupport is installed
+|RMEnsure ARMEABISupport 1.04 NetSurfRMLoad System:Modules.ARMEABISupport
+|RMEnsure ARMEABISupport 1.04 Error NetSurf requires ARMEABISupport 1.04 or later. Please use the RISC OS Configure app to update the computer's !System directory from the NetSurf archive.
+
| Load AcornURI if it isn't already
Unset NetSurf$Start_URI_Handler
RMEnsure AcornURI 0.12 Set NetSurf$Start_URI_Handler 1
diff --git a/frontends/riscos/window.c b/frontends/riscos/window.c
index 2b51c2fb9..4dcdcd734 100644
--- a/frontends/riscos/window.c
+++ b/frontends/riscos/window.c
@@ -833,6 +833,7 @@ ro_gui_window_toolbar_click(void *data,
if (action_type == TOOLBAR_ACTION_URL) {
switch (action.url) {
case TOOLBAR_URL_DRAG_URL:
+ case TOOLBAR_URL_DRAG_FAVICON:
{
gui_save_type save_type;
nserror err;
diff --git a/frontends/windows/Makefile b/frontends/windows/Makefile
index 4e74edd7a..524f716fe 100644
--- a/frontends/windows/Makefile
+++ b/frontends/windows/Makefile
@@ -90,7 +90,7 @@ $(OBJROOT)/messages-en: $(MESSAGES_TARGET)/en/Messages
$(VQ)echo " MSGCP: $< $@"
$(Q)$(INSTALL) -m 644 -T $< $@
-netsurf-installer.exe: $(EXETARGET) $(WIN_RES_INS_OBJ)
+netsurf-installer.exe: $(EXETARGET) $(POSTEXES) $(WIN_RES_INS_OBJ)
$(VQ)echo "MAKENSIS: $@"
$(Q)makensis -V$(NSIS_VERBOSE) -NOCD -DOBJROOT=$(OBJROOT) -DRESDIR=$(FRONTEND_RESOURCES_DIR) -DVERSIONMAJOR=$(VERSION_MAJ) -DVERSIONMINOR=$(VERSION_MIN) -DOUTFNAME=$@ $(FRONTEND_RESOURCES_DIR)/installer.nsi
diff --git a/test/data/Choices-all b/test/data/Choices-all
index baae231a2..829a270e0 100644
--- a/test/data/Choices-all
+++ b/test/data/Choices-all
@@ -65,6 +65,7 @@ remove_backgrounds:0
enable_loosening:1
enable_PDF_compression:1
enable_PDF_password:0
+prefer_dark_mode:0
sys_colour_ActiveBorder:d3d3d3
sys_colour_ActiveCaption:f1f1f1
sys_colour_AppWorkspace:f1f1f1
diff --git a/test/js/class-list.html b/test/js/class-list.html
new file mode 100644
index 000000000..4c73283e5
--- /dev/null
+++ b/test/js/class-list.html
@@ -0,0 +1,29 @@
+<html>
+ <head>
+ <title>Class List (and other token lists?)</title>
+ <style>
+ .bad { background-color: red; }
+ .ok { background-color: green; }
+ </style>
+ </head>
+ <body>
+ <h1>This is a set of demonstrators for the token list Element.classList</h1>
+ <h2>This first is taken from the MDN for DOMTokenList</h2>
+ <span id="demo1" class=" d d e f bad"></span>
+ <script>
+ var span = document.getElementById("demo1");
+ var classes = span.classList;
+ classes.add("x", "d", "g");
+ classes.remove("e", "g");
+ classes.toggle("d"); // Toggles d off
+ classes.toggle("q", false); // Forces q off (won't be present)
+ classes.toggle("d"); // Toggles d on
+ classes.toggle("d", true); // Forces d on (won't toggle it off again)
+ if (classes.contains("d")) {
+ classes.add("ok")
+ classes.remove("bad")
+ span.textContent = "span classList is \"" + classes + '"';
+ }
+ </script>
+ </body>
+</html>
diff --git a/test/js/index.html b/test/js/index.html
index 032946726..6d2c6541e 100644
--- a/test/js/index.html
+++ b/test/js/index.html
@@ -104,6 +104,7 @@
<li><a href="assorted-log-doc-write.html">console.log and document.write</a></li>
<li><a href="wikipedia-lcm.html">Example from wikipedia</a></li>
<li><a href="verify-instanceofness.html">Check instanceof behaviour</a></li>
+<li><a href="class-list.html">Class list (and other token lists?)</a></li>
<li><a href="mandelbrot.html">Canvas/ImageData Mandelbrot ploter</a></li>
<li><a href="life.html">Game of Life</a></li>
</ul>
diff --git a/utils/config.h b/utils/config.h
index 0227f4177..f54326dba 100644
--- a/utils/config.h
+++ b/utils/config.h
@@ -146,6 +146,15 @@ char *realpath(const char *path, char *resolved_path);
#undef HAVE_SCANDIR
#endif
+#define HAVE_DIRFD
+#define HAVE_UNLINKAT
+#define HAVE_FSTATAT
+#if (defined(_WIN32) || defined(__riscos__) || defined(__HAIKU__) || defined(__BEOS__) || defined(__amigaos4__) || defined(__AMIGA__) || defined(__MINT__))
+#undef HAVE_DIRFD
+#undef HAVE_UNLINKAT
+#undef HAVE_FSTATAT
+#endif
+
#define HAVE_REGEX
#if (defined(__serenity__))
#undef HAVE_REGEX
diff --git a/utils/file.c b/utils/file.c
index c460e49e9..75a8a1c03 100644
--- a/utils/file.c
+++ b/utils/file.c
@@ -26,6 +26,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
+#include <fcntl.h>
#include <errno.h>
#include "desktop/gui_internal.h"
@@ -318,16 +319,13 @@ nserror netsurf_mkdir_all(const char *fname)
nserror
netsurf_recursive_rm(const char *path)
{
- struct dirent **listing = NULL; /* directory entry listing */
- int nentries, ent;
+ DIR *parent;
+ struct dirent *entry;
nserror ret = NSERROR_OK;
struct stat ent_stat; /* stat result of leaf entry */
- char *leafpath = NULL;
- const char *leafname;
-
- nentries = scandir(path, &listing, 0, alphasort);
- if (nentries < 0) {
+ parent = opendir(path);
+ if (parent == NULL) {
switch (errno) {
case ENOENT:
return NSERROR_NOT_FOUND;
@@ -336,26 +334,44 @@ netsurf_recursive_rm(const char *path)
}
}
- for (ent = 0; ent < nentries; ent++) {
- leafname = listing[ent]->d_name;
- if (strcmp(leafname, ".") == 0 ||
- strcmp(leafname, "..") == 0)
+ while ((entry = readdir(parent))) {
+ char *leafpath = NULL;
+
+ if (strcmp(entry->d_name, ".") == 0 ||
+ strcmp(entry->d_name, "..") == 0)
continue;
- ret = netsurf_mkpath(&leafpath, NULL, 2, path, leafname);
- if (ret != NSERROR_OK) goto out;
+
+ ret = netsurf_mkpath(&leafpath, NULL, 2, path, entry->d_name);
+ if (ret != NSERROR_OK)
+ goto out;
+
+#if (defined(HAVE_DIRFD) && defined(HAVE_FSTATAT))
+ if (fstatat(dirfd(parent), entry->d_name, &ent_stat,
+ AT_SYMLINK_NOFOLLOW) != 0) {
+#else
if (stat(leafpath, &ent_stat) != 0) {
+#endif
+ free(leafpath);
goto out_via_errno;
}
if (S_ISDIR(ent_stat.st_mode)) {
ret = netsurf_recursive_rm(leafpath);
- if (ret != NSERROR_OK) goto out;
+ if (ret != NSERROR_OK) {
+ free(leafpath);
+ goto out;
+ }
} else {
+#if (defined(HAVE_DIRFD) && defined(HAVE_UNLINKAT))
+ if (unlinkat(dirfd(parent), entry->d_name, 0) != 0) {
+#else
if (unlink(leafpath) != 0) {
+#endif
+ free(leafpath);
goto out_via_errno;
}
}
+
free(leafpath);
- leafpath = NULL;
}
if (rmdir(path) != 0) {
@@ -373,16 +389,7 @@ out_via_errno:
ret = NSERROR_UNKNOWN;
}
out:
- if (listing != NULL) {
- for (ent = 0; ent < nentries; ent++) {
- free(listing[ent]);
- }
- free(listing);
- }
-
- if (leafpath != NULL) {
- free(leafpath);
- }
+ closedir(parent);
return ret;
}
diff --git a/utils/filename.c b/utils/filename.c
index 00ade7409..346fa85cc 100644
--- a/utils/filename.c
+++ b/utils/filename.c
@@ -29,10 +29,13 @@
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
+#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include "utils/dirent.h"
+#include "utils/errors.h"
+#include "utils/file.h"
#include "utils/filename.h"
#include "utils/log.h"
#include "utils/utils.h"
@@ -55,7 +58,6 @@ static char filename_directory[256];
static struct directory *filename_create_directory(const char *prefix);
static bool filename_flush_directory(const char *folder, int depth);
-static bool filename_delete_recursive(char *folder);
/**
* Request a new, unique, filename.
@@ -272,6 +274,8 @@ bool filename_flush_directory(const char *folder, int depth)
}
parent = opendir(folder);
+ if (parent == NULL)
+ return false;
while ((entry = readdir(parent))) {
int written;
@@ -288,7 +292,12 @@ bool filename_flush_directory(const char *folder, int depth)
child[sizeof(child) - 1] = '\0';
}
+#if (defined(HAVE_DIRFD) && defined(HAVE_FSTATAT))
+ if (fstatat(dirfd(parent), entry->d_name, &statbuf,
+ AT_SYMLINK_NOFOLLOW) == -1) {
+#else
if (stat(child, &statbuf) == -1) {
+#endif
NSLOG(netsurf, INFO, "Unable to stat %s: %s", child,
strerror(errno));
continue;
@@ -354,14 +363,20 @@ bool filename_flush_directory(const char *folder, int depth)
/* delete or recurse */
if (del) {
- if (S_ISDIR(statbuf.st_mode))
- filename_delete_recursive(child);
-
- if (remove(child))
- NSLOG(netsurf, INFO, "Failed to remove '%s'",
- child);
- else
- changed = true;
+ if (S_ISDIR(statbuf.st_mode)) {
+ changed = (netsurf_recursive_rm(child) ==
+ NSERROR_OK);
+ } else {
+#if (defined(HAVE_DIRFD) && defined(HAVE_UNLINKAT))
+ if (unlinkat(dirfd(parent), entry->d_name, 0)) {
+#else
+ if (unlink(child)) {
+#endif
+ NSLOG(netsurf, INFO,
+ "Failed to remove '%s'", child);
+ } else
+ changed = true;
+ }
} else {
while (filename_flush_directory(child, depth + 1));
}
@@ -374,61 +389,6 @@ bool filename_flush_directory(const char *folder, int depth)
/**
- * Recursively deletes the contents of a directory
- *
- * \param folder the directory to delete
- * \return true on success, false otherwise
- */
-bool filename_delete_recursive(char *folder)
-{
- DIR *parent;
- struct dirent *entry;
- char child[256];
- struct stat statbuf;
-
- parent = opendir(folder);
-
- while ((entry = readdir(parent))) {
- int written;
-
- /* Ignore '.' and '..' */
- if (strcmp(entry->d_name, ".") == 0 ||
- strcmp(entry->d_name, "..") == 0)
- continue;
-
- written = snprintf(child, sizeof(child), "%s/%s",
- folder, entry->d_name);
- if (written == sizeof(child)) {
- child[sizeof(child) - 1] = '\0';
- }
-
- if (stat(child, &statbuf) == -1) {
- NSLOG(netsurf, INFO, "Unable to stat %s: %s", child,
- strerror(errno));
- continue;
- }
-
- if (S_ISDIR(statbuf.st_mode)) {
- if (!filename_delete_recursive(child)) {
- closedir(parent);
- return false;
- }
- }
-
- if (remove(child)) {
- NSLOG(netsurf, INFO, "Failed to remove '%s'", child);
- closedir(parent);
- return false;
- }
- }
-
- closedir(parent);
-
- return true;
-}
-
-
-/**
* Creates a new directory.
*
* \param prefix the prefix to use, or NULL to allocate a new one
diff --git a/utils/log.c b/utils/log.c
index 68b188e0f..8cc42a35f 100644
--- a/utils/log.c
+++ b/utils/log.c
@@ -99,6 +99,7 @@ NSLOG_DEFINE_CATEGORY(plot, "Rendering system");
NSLOG_DEFINE_CATEGORY(schedule, "Scheduler");
NSLOG_DEFINE_CATEGORY(fbtk, "Framebuffer toolkit");
NSLOG_DEFINE_CATEGORY(layout, "Layout");
+NSLOG_DEFINE_CATEGORY(flex, "Flex");
NSLOG_DEFINE_CATEGORY(dukky, "Duktape JavaScript Binding");
NSLOG_DEFINE_CATEGORY(jserrors, "JavaScript error messages");
diff --git a/utils/log.h b/utils/log.h
index 02a886c01..b7aa83358 100644
--- a/utils/log.h
+++ b/utils/log.h
@@ -84,6 +84,7 @@ NSLOG_DECLARE_CATEGORY(plot);
NSLOG_DECLARE_CATEGORY(schedule);
NSLOG_DECLARE_CATEGORY(fbtk);
NSLOG_DECLARE_CATEGORY(layout);
+NSLOG_DECLARE_CATEGORY(flex);
NSLOG_DECLARE_CATEGORY(dukky);
NSLOG_DECLARE_CATEGORY(jserrors);
diff --git a/utils/ssl_certs.c b/utils/ssl_certs.c
index d0f2a6c18..8546165ac 100644
--- a/utils/ssl_certs.c
+++ b/utils/ssl_certs.c
@@ -248,12 +248,23 @@ nserror cert_chain_to_query(struct cert_chain *chain, struct nsurl **url_out )
urlstrlen = snprintf((char *)urlstr, allocsize, "about:certificate");
for (depth = 0; depth < chain->depth; depth++) {
+ int written;
nsuerror nsures;
size_t output_length;
- urlstrlen += snprintf((char *)urlstr + urlstrlen,
- allocsize - urlstrlen,
- "&cert=");
+ written = snprintf((char *)urlstr + urlstrlen,
+ allocsize - urlstrlen,
+ "&cert=");
+ if (written < 0) {
+ free(urlstr);
+ return NSERROR_UNKNOWN;
+ }
+ if ((size_t)written >= allocsize - urlstrlen) {
+ free(urlstr);
+ return NSERROR_UNKNOWN;
+ }
+
+ urlstrlen += (size_t)written;
output_length = allocsize - urlstrlen;
nsures = nsu_base64_encode_url(
@@ -268,10 +279,20 @@ nserror cert_chain_to_query(struct cert_chain *chain, struct nsurl **url_out )
urlstrlen += output_length;
if (chain->certs[depth].err != SSL_CERT_ERR_OK) {
- urlstrlen += snprintf((char *)urlstr + urlstrlen,
- allocsize - urlstrlen,
- "&certerr=%d",
- chain->certs[depth].err);
+ written = snprintf((char *)urlstr + urlstrlen,
+ allocsize - urlstrlen,
+ "&certerr=%d",
+ chain->certs[depth].err);
+ if (written < 0) {
+ free(urlstr);
+ return NSERROR_UNKNOWN;
+ }
+ if ((size_t)written >= allocsize - urlstrlen) {
+ free(urlstr);
+ return NSERROR_UNKNOWN;
+ }
+
+ urlstrlen += (size_t)written;
}
}
diff --git a/utils/utf8.c b/utils/utf8.c
index f0ac0c9b2..7091ad405 100644
--- a/utils/utf8.c
+++ b/utils/utf8.c
@@ -44,7 +44,7 @@ uint32_t utf8_to_ucs4(const char *s_in, size_t l)
parserutils_error perror;
perror = parserutils_charset_utf8_to_ucs4((const uint8_t *) s_in, l,
- &ucs4, &len);
+ &ucs4, &len);
if (perror != PARSERUTILS_OK)
ucs4 = 0xfffd;
@@ -106,7 +106,7 @@ size_t utf8_char_byte_length(const char *s)
parserutils_error perror;
perror = parserutils_charset_utf8_char_byte_length((const uint8_t *) s,
- &len);
+ &len);
assert(perror == PARSERUTILS_OK);
return len;
@@ -131,7 +131,7 @@ size_t utf8_next(const char *s, size_t l, size_t o)
parserutils_error perror;
perror = parserutils_charset_utf8_next((const uint8_t *) s, l, o,
- &next);
+ &next);
assert(perror == PARSERUTILS_OK);
return next;
@@ -151,6 +151,47 @@ static inline void utf8_clear_cd_cache(void)
last_cd.cd = 0;
}
+/**
+ * obtain a cached conversion descriptor
+ *
+ * either return the cached conversion descriptor or create one if required
+ */
+static nserror
+get_cached_cd(const char *enc_from, const char *enc_to, iconv_t *cd_out)
+{
+ iconv_t cd;
+ /* we cache the last used conversion descriptor,
+ * so check if we're trying to use it here */
+ if (strncasecmp(last_cd.from, enc_from, sizeof(last_cd.from)) == 0 &&
+ strncasecmp(last_cd.to, enc_to, sizeof(last_cd.to)) == 0 &&
+ last_cd.cd != 0) {
+ *cd_out = last_cd.cd;
+ return NSERROR_OK;
+ }
+
+ /* no match, so create a new cd */
+ cd = iconv_open(enc_to, enc_from);
+ if (cd == (iconv_t) -1) {
+ if (errno == EINVAL) {
+ return NSERROR_BAD_ENCODING;
+ }
+ /* default to no memory */
+ return NSERROR_NOMEM;
+ }
+
+ /* close the last cd - we don't care if this fails */
+ if (last_cd.cd) {
+ iconv_close(last_cd.cd);
+ }
+
+ /* and safely copy the to/from/cd data into last_cd */
+ snprintf(last_cd.from, sizeof(last_cd.from), "%s", enc_from);
+ snprintf(last_cd.to, sizeof(last_cd.to), "%s", enc_to);
+ *cd_out = last_cd.cd = cd;
+
+ return NSERROR_OK;
+}
+
/* exported interface documented in utils/utf8.h */
nserror utf8_finalise(void)
{
@@ -168,95 +209,72 @@ nserror utf8_finalise(void)
* Convert a string from one encoding to another
*
* \param string The NULL-terminated string to convert
- * \param len Length of input string to consider (in bytes), or 0
+ * \param slen Length of input string to consider (in bytes), or 0
* \param from The encoding name to convert from
* \param to The encoding name to convert to
- * \param result Pointer to location in which to store result.
- * \param result_len Pointer to location in which to store result length.
+ * \param result_out Pointer to location in which to store result.
+ * \param result_len_out Pointer to location in which to store result length.
* \return NSERROR_OK for no error, NSERROR_NOMEM on allocation error,
* NSERROR_BAD_ENCODING for a bad character encoding
*/
static nserror
utf8_convert(const char *string,
- size_t len,
+ size_t slen,
const char *from,
const char *to,
- char **result,
- size_t *result_len)
+ char **result_out,
+ size_t *result_len_out)
{
iconv_t cd;
- char *temp, *out, *in;
- size_t slen, rlen;
-
- assert(string && from && to && result);
-
- if (string[0] == '\0') {
- /* On AmigaOS, iconv() returns an error if we pass an
- * empty string. This prevents iconv() being called as
- * there is no conversion necessary anyway. */
- *result = strdup("");
- if (!(*result)) {
- *result = NULL;
- return NSERROR_NOMEM;
- }
+ char *temp, *out, *in, *result;
+ size_t result_len;
+ nserror res;
- return NSERROR_OK;
+ assert(string && from && to && result_out);
+
+ /* calculate the source length if not given */
+ if (slen==0) {
+ slen = strlen(string);
}
- if (strcasecmp(from, to) == 0) {
- /* conversion from an encoding to itself == strdup */
- slen = len ? len : strlen(string);
- *(result) = strndup(string, slen);
- if (!(*result)) {
- *(result) = NULL;
+ /* process the empty string separately avoiding any conversion
+ * check for the source and destination encoding being the same
+ *
+ * This optimisation is necessary on AmigaOS as iconv()
+ * returns an error if an empty string is passed.
+ */
+ if ((slen == 0) || (strcasecmp(from, to) == 0)) {
+ *result_out = strndup(string, slen);
+ if (*result_out == NULL) {
return NSERROR_NOMEM;
}
+ if (result_len_out != NULL) {
+ *result_len_out = slen;
+ }
return NSERROR_OK;
}
in = (char *)string;
- /* we cache the last used conversion descriptor,
- * so check if we're trying to use it here */
- if (strncasecmp(last_cd.from, from, sizeof(last_cd.from)) == 0 &&
- strncasecmp(last_cd.to, to, sizeof(last_cd.to)) == 0) {
- cd = last_cd.cd;
- }
- else {
- /* no match, so create a new cd */
- cd = iconv_open(to, from);
- if (cd == (iconv_t)-1) {
- if (errno == EINVAL)
- return NSERROR_BAD_ENCODING;
- /* default to no memory */
- return NSERROR_NOMEM;
- }
-
- /* close the last cd - we don't care if this fails */
- if (last_cd.cd)
- iconv_close(last_cd.cd);
-
- /* and copy the to/from/cd data into last_cd */
- snprintf(last_cd.from, sizeof(last_cd.from), "%s", from);
- snprintf(last_cd.to, sizeof(last_cd.to), "%s", to);
- last_cd.cd = cd;
+ res = get_cached_cd(from, to, &cd);
+ if (res != NSERROR_OK) {
+ return res;
}
- slen = len ? len : strlen(string);
/* Worst case = ASCII -> UCS4, so allocate an output buffer
* 4 times larger than the input buffer, and add 4 bytes at
* the end for the NULL terminator
*/
- rlen = slen * 4 + 4;
+ result_len = slen * 4 + 4;
- temp = out = malloc(rlen);
+ temp = out = malloc(result_len);
if (!out) {
return NSERROR_NOMEM;
}
/* perform conversion */
- if (iconv(cd, (void *) &in, &slen, &out, &rlen) == (size_t)-1) {
+ if (iconv(cd, (void *) &in, &slen, &out, &result_len) == (size_t)-1) {
free(temp);
/* clear the cached conversion descriptor as it's invalid */
if (last_cd.cd)
@@ -270,19 +288,22 @@ utf8_convert(const char *string,
return NSERROR_NOMEM;
}
- *(result) = realloc(temp, out - temp + 4);
- if (!(*result)) {
+ result_len = out - temp;
+
+ /* resize buffer allowing for null termination */
+ result = realloc(temp, result_len + 4);
+ if (result == NULL) {
free(temp);
- *(result) = NULL; /* for sanity's sake */
return NSERROR_NOMEM;
}
/* NULL terminate - needs 4 characters as we may have
* converted to UTF-32 */
- memset((*result) + (out - temp), 0, 4);
+ memset(result + result_len, 0, 4);
- if (result_len != NULL) {
- *result_len = (out - temp);
+ *result_out = result;
+ if (result_len_out != NULL) {
+ *result_len_out = result_len;
}
return NSERROR_OK;
@@ -290,14 +311,14 @@ utf8_convert(const char *string,
/* exported interface documented in utils/utf8.h */
nserror utf8_to_enc(const char *string, const char *encname,
- size_t len, char **result)
+ size_t len, char **result)
{
return utf8_convert(string, len, "UTF-8", encname, result, NULL);
}
/* exported interface documented in utils/utf8.h */
nserror utf8_from_enc(const char *string, const char *encname,
- size_t len, char **result, size_t *result_len)
+ size_t len, char **result, size_t *result_len)
{
return utf8_convert(string, len, encname, "UTF-8", result, result_len);
}
@@ -328,7 +349,7 @@ utf8_convert_html_chunk(iconv_t cd,
esclen = snprintf(escape, sizeof(escape), "&#x%06x;", ucs4);
pescape = escape;
ret = iconv(cd, (void *) &pescape, &esclen,
- (void *) out, outlen);
+ (void *) out, outlen);
if (ret == (size_t) -1)
return NSERROR_NOMEM;
@@ -340,45 +361,26 @@ utf8_convert_html_chunk(iconv_t cd,
return NSERROR_OK;
}
+
+
/* exported interface documented in utils/utf8.h */
nserror
-utf8_to_html(const char *string, const char *encname, size_t len, char **result)
+utf8_to_html(const char *string, const char *encname, size_t len, char **result_out)
{
iconv_t cd;
const char *in;
- char *out, *origout;
+ char *out, *origout, *result;
size_t off, prev_off, inlen, outlen, origoutlen, esclen;
nserror ret;
char *pescape, escape[11];
+ nserror res;
if (len == 0)
len = strlen(string);
- /* we cache the last used conversion descriptor,
- * so check if we're trying to use it here */
- if (strncasecmp(last_cd.from, "UTF-8", sizeof(last_cd.from)) == 0 &&
- strncasecmp(last_cd.to, encname,
- sizeof(last_cd.to)) == 0 &&
- last_cd.cd != 0) {
- cd = last_cd.cd;
- } else {
- /* no match, so create a new cd */
- cd = iconv_open(encname, "UTF-8");
- if (cd == (iconv_t) -1) {
- if (errno == EINVAL)
- return NSERROR_BAD_ENCODING;
- /* default to no memory */
- return NSERROR_NOMEM;
- }
-
- /* close the last cd - we don't care if this fails */
- if (last_cd.cd)
- iconv_close(last_cd.cd);
-
- /* and safely copy the to/from/cd data into last_cd */
- snprintf(last_cd.from, sizeof(last_cd.from), "UTF-8");
- snprintf(last_cd.to, sizeof(last_cd.to), "%s", encname);
- last_cd.cd = cd;
+ res = get_cached_cd("UTF-8", encname, &cd);
+ if (res != NSERROR_OK) {
+ return res;
}
/* Worst case is ASCII -> UCS4, with all characters escaped:
@@ -398,13 +400,13 @@ utf8_to_html(const char *string, const char *encname, size_t len, char **result)
while (off < len) {
/* Must escape '&', '<', and '>' */
if (string[off] == '&' || string[off] == '<' ||
- string[off] == '>') {
+ string[off] == '>') {
if (off - prev_off > 0) {
/* Emit chunk */
in = string + prev_off;
inlen = off - prev_off;
ret = utf8_convert_html_chunk(cd, in, inlen,
- &out, &outlen);
+ &out, &outlen);
if (ret != NSERROR_OK) {
free(origout);
iconv_close(cd);
@@ -415,10 +417,10 @@ utf8_to_html(const char *string, const char *encname, size_t len, char **result)
/* Emit mandatory escape */
esclen = snprintf(escape, sizeof(escape),
- "&#x%06x;", string[off]);
+ "&#x%06x;", string[off]);
pescape = escape;
ret = utf8_convert_html_chunk(cd, pescape, esclen,
- &out, &outlen);
+ &out, &outlen);
if (ret != NSERROR_OK) {
free(origout);
iconv_close(cd);
@@ -450,11 +452,12 @@ utf8_to_html(const char *string, const char *encname, size_t len, char **result)
outlen -= 4;
/* Shrink-wrap */
- *result = realloc(origout, origoutlen - outlen);
- if (*result == NULL) {
+ result = realloc(origout, origoutlen - outlen);
+ if (result == NULL) {
free(origout);
return NSERROR_NOMEM;
}
+ *result_out = result;
return NSERROR_OK;
}