NetSurf
script.c
Go to the documentation of this file.
1/*
2 * Copyright 2012 Vincent Sanders <vince@netsurf-browser.org>
3 *
4 * This file is part of NetSurf, http://www.netsurf-browser.org/
5 *
6 * NetSurf is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * NetSurf is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19/**
20 * \file
21 * implementation of content handling for text/html scripts.
22 */
23
24#include <assert.h>
25#include <ctype.h>
26#include <stdint.h>
27#include <stdbool.h>
28#include <string.h>
29#include <strings.h>
30#include <stdlib.h>
31
32#include "utils/config.h"
33#include "utils/corestrings.h"
34#include "utils/log.h"
35#include "utils/messages.h"
36#include "netsurf/content.h"
37#include "javascript/js.h"
40#include "content/fetch.h"
41#include "content/hlcache.h"
42
43#include "html/html.h"
44#include "html/private.h"
45
46typedef bool (script_handler_t)(struct jsthread *jsthread, const uint8_t *data, size_t size, const char *name);
47
48
50{
51 if (ctype == CONTENT_JS) {
52 return js_exec;
53 }
54 return NULL;
55}
56
57
58/* exported internal interface documented in html/html_internal.h */
60{
61 unsigned int i;
62 struct html_script *s;
63 script_handler_t *script_handler;
64 bool have_run_something = false;
65
66 if (c->jsthread == NULL) {
68 }
69
70 for (i = 0, s = c->scripts; i != c->scripts_count; i++, s++) {
71 if (s->already_started) {
72 continue;
73 }
74
75 if ((s->type == HTML_SCRIPT_ASYNC) ||
76 (allow_defer && (s->type == HTML_SCRIPT_DEFER))) {
77 /* ensure script content is present */
78 if (s->data.handle == NULL)
79 continue;
80
81 /* ensure script content fetch status is not an error */
84 continue;
85
86 /* ensure script handler for content type */
87 script_handler = select_script_handler(
89 if (script_handler == NULL)
90 continue; /* unsupported type */
91
94 /* external script is now available */
95 const uint8_t *data;
96 size_t size;
98 s->data.handle, &size );
99 script_handler(c->jsthread, data, size,
101 have_run_something = true;
102 /* We have to re-acquire this here since the
103 * c->scripts array may have been reallocated
104 * as a result of executing this script.
105 */
106 s = &(c->scripts[i]);
107
108 s->already_started = true;
109
110 }
111 }
112 }
113
114 if (have_run_something) {
115 return html_proceed_to_done(c);
116 }
117
118 return NSERROR_OK;
119}
120
121/* create new html script entry */
122static struct html_script *
124 dom_string *mimetype,
126{
127 struct html_script *nscript;
128 /* add space for new script entry */
129 nscript = realloc(c->scripts,
130 sizeof(struct html_script) * (c->scripts_count + 1));
131 if (nscript == NULL) {
132 return NULL;
133 }
134
135 c->scripts = nscript;
136
137 /* increment script entry count */
138 nscript = &c->scripts[c->scripts_count];
139 c->scripts_count++;
140
141 nscript->already_started = false;
142 nscript->parser_inserted = false;
143 nscript->force_async = true;
144 nscript->ready_exec = false;
145 nscript->async = false;
146 nscript->defer = false;
147
148 nscript->type = type;
149
150 nscript->mimetype = dom_string_ref(mimetype); /* reference mimetype */
151
152 return nscript;
153}
154
155/**
156 * Callback for asyncronous scripts
157 */
158static nserror
160 const hlcache_event *event,
161 void *pw)
162{
163 html_content *parent = pw;
164 unsigned int i;
165 struct html_script *s;
166
167 /* Find script */
168 for (i = 0, s = parent->scripts; i != parent->scripts_count; i++, s++) {
169 if (s->type == HTML_SCRIPT_ASYNC && s->data.handle == script)
170 break;
171 }
172
173 assert(i != parent->scripts_count);
174
175 switch (event->type) {
177 break;
178
180 break;
181
182 case CONTENT_MSG_DONE:
183 NSLOG(netsurf, INFO, "script %d done '%s'", i,
185 parent->base.active--;
186 NSLOG(netsurf, INFO, "%d fetches active", parent->base.active);
187
188 break;
189
191 NSLOG(netsurf, INFO, "script %s failed: %s",
193 event->data.errordata.errormsg);
194
196 s->data.handle = NULL;
197 parent->base.active--;
198 NSLOG(netsurf, INFO, "%d fetches active", parent->base.active);
199
200 break;
201
202 default:
203 break;
204 }
205
206 /* if there are no active fetches remaining begin post parse
207 * conversion
208 */
211 }
212
213 /* if we have already started converting though, then we can handle the
214 * scripts as they come in.
215 */
216 else if (parent->conversion_begun) {
217 return html_script_exec(parent, false);
218 }
219
220 return NSERROR_OK;
221}
222
223/**
224 * Callback for defer scripts
225 */
226static nserror
228 const hlcache_event *event,
229 void *pw)
230{
231 html_content *parent = pw;
232 unsigned int i;
233 struct html_script *s;
234
235 /* Find script */
236 for (i = 0, s = parent->scripts; i != parent->scripts_count; i++, s++) {
237 if (s->type == HTML_SCRIPT_DEFER && s->data.handle == script)
238 break;
239 }
240
241 assert(i != parent->scripts_count);
242
243 switch (event->type) {
244
245 case CONTENT_MSG_DONE:
246 NSLOG(netsurf, INFO, "script %d done '%s'", i,
248 parent->base.active--;
249 NSLOG(netsurf, INFO, "%d fetches active", parent->base.active);
250
251 break;
252
254 NSLOG(netsurf, INFO, "script %s failed: %s",
256 event->data.errordata.errormsg);
257
259 s->data.handle = NULL;
260 parent->base.active--;
261 NSLOG(netsurf, INFO, "%d fetches active", parent->base.active);
262
263 break;
264
265 default:
266 break;
267 }
268
269 /* if there are no active fetches remaining begin post parse
270 * conversion
271 */
274 }
275
276 return NSERROR_OK;
277}
278
279/**
280 * Callback for syncronous scripts
281 */
282static nserror
284 const hlcache_event *event,
285 void *pw)
286{
287 html_content *parent = pw;
288 unsigned int i;
289 struct html_script *s;
290 script_handler_t *script_handler;
291 dom_hubbub_error err;
292 unsigned int active_sync_scripts = 0;
293
294 /* Count sync scripts which have yet to complete (other than us) */
295 for (i = 0, s = parent->scripts; i != parent->scripts_count; i++, s++) {
296 if (s->type == HTML_SCRIPT_SYNC &&
297 s->data.handle != script && s->already_started == false) {
298 active_sync_scripts++;
299 }
300 }
301
302 /* Find script */
303 for (i = 0, s = parent->scripts; i != parent->scripts_count; i++, s++) {
304 if (s->type == HTML_SCRIPT_SYNC && s->data.handle == script)
305 break;
306 }
307
308 assert(i != parent->scripts_count);
309
310 switch (event->type) {
311 case CONTENT_MSG_DONE:
312 NSLOG(netsurf, INFO, "script %d done '%s'", i,
314 parent->base.active--;
315 NSLOG(netsurf, INFO, "%d fetches active", parent->base.active);
316
317 s->already_started = true;
318
319 /* attempt to execute script */
321 if (script_handler != NULL && parent->jsthread != NULL) {
322 /* script has a handler */
323 const uint8_t *data;
324 size_t size;
326 script_handler(parent->jsthread, data, size,
328 }
329
330 /* continue parse */
331 if (parent->parser != NULL && active_sync_scripts == 0) {
332 err = dom_hubbub_parser_pause(parent->parser, false);
333 if (err != DOM_HUBBUB_OK) {
334 NSLOG(netsurf, INFO, "unpause returned 0x%x", err);
335 }
336 }
337
338 break;
339
341 NSLOG(netsurf, INFO, "script %s failed: %s",
343 event->data.errordata.errormsg);
344
346 s->data.handle = NULL;
347 parent->base.active--;
348
349 NSLOG(netsurf, INFO, "%d fetches active", parent->base.active);
350
351 s->already_started = true;
352
353 /* continue parse */
354 if (parent->parser != NULL && active_sync_scripts == 0) {
355 err = dom_hubbub_parser_pause(parent->parser, false);
356 if (err != DOM_HUBBUB_OK) {
357 NSLOG(netsurf, INFO, "unpause returned 0x%x", err);
358 }
359 }
360
361 break;
362
363 default:
364 break;
365 }
366
367 /* if there are no active fetches remaining begin post parse
368 * conversion
369 */
372 }
373
374 return NSERROR_OK;
375}
376
377/**
378 * process a script with a src tag
379 */
380static dom_hubbub_error
382 dom_node *node,
383 dom_string *mimetype,
384 dom_string *src)
385{
386 nserror ns_error;
387 nsurl *joined;
389 struct html_script *nscript;
390 bool async;
391 bool defer;
392 enum html_script_type script_type;
393 hlcache_handle_callback script_cb;
394 dom_hubbub_error ret = DOM_HUBBUB_OK;
395 dom_exception exc; /* returned by libdom functions */
396
397 /* src url */
398 ns_error = nsurl_join(c->base_url, dom_string_data(src), &joined);
399 if (ns_error != NSERROR_OK) {
401 return DOM_HUBBUB_NOMEM;
402 }
403
404 NSLOG(netsurf, INFO, "script %i '%s'", c->scripts_count,
405 nsurl_access(joined));
406
407 /* there are three ways to process the script tag at this point:
408 *
409 * Syncronously pause the parent parse and continue after
410 * the script has downloaded and executed. (default)
411 * Async Start the script downloading and execute it when it
412 * becomes available.
413 * Defered Start the script downloading and execute it when
414 * the page has completed parsing, may be set along
415 * with async where it is ignored.
416 */
417
418 /* we interpret the presence of the async and defer attribute
419 * as true and ignore its value, technically only the empty
420 * value or the attribute name itself are valid. However
421 * various browsers interpret this in various ways the most
422 * compatible approach is to be liberal and accept any
423 * value. Note setting the values to "false" still makes them true!
424 */
425 exc = dom_element_has_attribute(node, corestring_dom_async, &async);
426 if (exc != DOM_NO_ERR) {
427 return DOM_HUBBUB_OK; /* dom error */
428 }
429
430 if (c->parse_completed) {
431 /* After parse completed, all scripts are essentially async */
432 async = true;
433 defer = false;
434 }
435
436 if (async) {
437 /* asyncronous script */
438 script_type = HTML_SCRIPT_ASYNC;
439 script_cb = convert_script_async_cb;
440
441 } else {
442 exc = dom_element_has_attribute(node,
443 corestring_dom_defer, &defer);
444 if (exc != DOM_NO_ERR) {
445 return DOM_HUBBUB_OK; /* dom error */
446 }
447
448 if (defer) {
449 /* defered script */
450 script_type = HTML_SCRIPT_DEFER;
451 script_cb = convert_script_defer_cb;
452 } else {
453 /* syncronous script */
454 script_type = HTML_SCRIPT_SYNC;
455 script_cb = convert_script_sync_cb;
456 }
457 }
458
459 nscript = html_process_new_script(c, mimetype, script_type);
460 if (nscript == NULL) {
461 nsurl_unref(joined);
463 return DOM_HUBBUB_NOMEM;
464 }
465
466 /* set up child fetch encoding and quirks */
467 child.charset = c->encoding;
468 child.quirks = c->base.quirks;
469
470 ns_error = hlcache_handle_retrieve(joined,
471 0,
473 NULL,
474 script_cb,
475 c,
476 &child,
478 &nscript->data.handle);
479
480
481 nsurl_unref(joined);
482
483 if (ns_error != NSERROR_OK) {
484 /* @todo Deal with fetch error better. currently assume
485 * fetch never became active
486 */
487 /* mark duff script fetch as already started */
488 nscript->already_started = true;
489 NSLOG(netsurf, INFO, "Fetch failed with error %d", ns_error);
490 } else {
491 /* update base content active fetch count */
492 c->base.active++;
493 NSLOG(netsurf, INFO, "%d fetches active", c->base.active);
494
495 switch (script_type) {
496 case HTML_SCRIPT_SYNC:
497 ret = DOM_HUBBUB_HUBBUB_ERR | HUBBUB_PAUSED;
498 break;
499
501 break;
502
504 break;
505
506 default:
507 assert(0);
508 }
509 }
510
511 return ret;
512}
513
514static dom_hubbub_error
515exec_inline_script(html_content *c, dom_node *node, dom_string *mimetype)
516{
517 dom_string *script;
518 dom_exception exc; /* returned by libdom functions */
519 struct lwc_string_s *lwcmimetype;
520 script_handler_t *script_handler;
521 struct html_script *nscript;
522
523 /* does not appear to be a src so script is inline content */
524 exc = dom_node_get_text_content(node, &script);
525 if ((exc != DOM_NO_ERR) || (script == NULL)) {
526 return DOM_HUBBUB_OK; /* no contents, skip */
527 }
528
530 if (nscript == NULL) {
531 dom_string_unref(script);
532
534 return DOM_HUBBUB_NOMEM;
535
536 }
537
538 nscript->data.string = script;
539 nscript->already_started = true;
540
541 /* ensure script handler for content type */
542 exc = dom_string_intern(mimetype, &lwcmimetype);
543 if (exc != DOM_NO_ERR) {
544 return DOM_HUBBUB_DOM;
545 }
546
547 script_handler = select_script_handler(content_factory_type_from_mime_type(lwcmimetype));
548 lwc_string_unref(lwcmimetype);
549
550 if (script_handler != NULL) {
551 script_handler(c->jsthread,
552 (const uint8_t *)dom_string_data(script),
553 dom_string_byte_length(script),
554 "?inline script?");
555 }
556 return DOM_HUBBUB_OK;
557}
558
559
560/**
561 * process script node parser callback
562 *
563 *
564 */
565dom_hubbub_error
566html_process_script(void *ctx, dom_node *node)
567{
568 html_content *c = (html_content *)ctx;
569 dom_exception exc; /* returned by libdom functions */
570 dom_string *src, *mimetype;
571 dom_hubbub_error err = DOM_HUBBUB_OK;
572
573 /* ensure javascript context is available */
574 /* We should only ever be here if scripting was enabled for this
575 * content so it's correct to make a javascript context if there
576 * isn't one already. */
577 if (c->jsthread == NULL) {
578 union content_msg_data msg_data;
579
580 msg_data.jsthread = &c->jsthread;
582 NSLOG(netsurf, INFO, "javascript context %p ", c->jsthread);
583 if (c->jsthread == NULL) {
584 /* no context and it could not be created, abort */
585 return DOM_HUBBUB_OK;
586 }
587 }
588
589 NSLOG(netsurf, INFO, "content %p parser %p node %p", c, c->parser,
590 node);
591
592 exc = dom_element_get_attribute(node, corestring_dom_type, &mimetype);
593 if (exc != DOM_NO_ERR || mimetype == NULL) {
594 mimetype = dom_string_ref(corestring_dom_text_javascript);
595 }
596
597 exc = dom_element_get_attribute(node, corestring_dom_src, &src);
598 if (exc != DOM_NO_ERR || src == NULL) {
599 err = exec_inline_script(c, node, mimetype);
600 } else {
601 err = exec_src_script(c, node, mimetype, src);
602 dom_string_unref(src);
603 }
604
605 dom_string_unref(mimetype);
606
607 return err;
608}
609
610/* exported internal interface documented in html/html_internal.h */
612{
613 struct html_script *s;
614 unsigned int i;
615
616 for (i = 0, s = htmlc->scripts; i != htmlc->scripts_count; i++, s++) {
617 if (s->type == HTML_SCRIPT_INLINE) {
618 /* Inline scripts are no less secure than their
619 * containing HTML content
620 */
621 continue;
622 }
623 if (s->data.handle == NULL) {
624 /* We've not begun loading this? */
625 continue;
626 }
628 return true;
629 }
630 }
631
632 return false;
633}
634
635/* exported internal interface documented in html/html_internal.h */
637{
638 unsigned int i;
639
640 for (i = 0; i != html->scripts_count; i++) {
641 if (html->scripts[i].mimetype != NULL) {
642 dom_string_unref(html->scripts[i].mimetype);
643 }
644
645 switch (html->scripts[i].type) {
647 if (html->scripts[i].data.string != NULL) {
648 dom_string_unref(html->scripts[i].data.string);
649 }
650 break;
651 case HTML_SCRIPT_SYNC:
652 /* fallthrough */
654 /* fallthrough */
656 if (html->scripts[i].data.handle != NULL) {
658 }
659 break;
660 }
661 }
662 free(html->scripts);
663
664 return NSERROR_OK;
665}
Fetching of data from a URL (interface).
void content_broadcast(struct content *c, content_msg msg, const union content_msg_data *data)
Send a message to all users.
Definition: content.c:752
bool content_saw_insecure_objects(struct hlcache_handle *h)
Determine if the content referred to any insecure objects.
Definition: content.c:500
nsurl * content_get_url(struct content *c)
Retrieve URL associated with content.
Definition: content.c:1051
content_status content_get_status(hlcache_handle *h)
Retrieve status of content.
Definition: content.c:1124
void content_broadcast_error(struct content *c, nserror errorcode, const char *msg)
Send an error message to all users.
Definition: content.c:769
content_type content_factory_type_from_mime_type(lwc_string *mime_type)
Compute the generic content type for a MIME type.
Protected interface to Content handling.
@ CONTENT_STATUS_DONE
Content has completed all processing.
Definition: content_type.h:95
@ CONTENT_STATUS_ERROR
Error occurred, content will be destroyed imminently.
Definition: content_type.h:98
content_type
The type of a content.
Definition: content_type.h:53
@ CONTENT_JS
Javascript.
Definition: content_type.h:76
@ CONTENT_SCRIPT
All script types.
Definition: content_type.h:79
@ CONTENT_MSG_DONE
content has finished processing
Definition: content_type.h:119
@ CONTENT_MSG_LOADING
fetching or converting
Definition: content_type.h:113
@ CONTENT_MSG_ERROR
error occurred
Definition: content_type.h:122
@ CONTENT_MSG_GETTHREAD
Javascript thread.
Definition: content_type.h:146
@ CONTENT_MSG_READY
may be displayed
Definition: content_type.h:116
Useful interned string pointers (interface).
wimp_w parent
Definition: dialog.c:88
nserror
Enumeration of error codes.
Definition: errors.h:29
@ NSERROR_BAD_PARAMETER
Bad Parameter.
Definition: errors.h:48
@ NSERROR_NOMEM
Memory exhaustion.
Definition: errors.h:32
@ NSERROR_OK
No error.
Definition: errors.h:30
const char * type
Definition: filetype.cpp:44
nserror hlcache_handle_release(hlcache_handle *handle)
Release a high-level cache handle.
Definition: hlcache.c:740
nserror hlcache_handle_retrieve(nsurl *url, uint32_t flags, nsurl *referer, llcache_post_data *post, hlcache_handle_callback cb, void *pw, hlcache_child_context *child, content_type accepted_types, hlcache_handle **result)
Retrieve a high-level cache handle for an object.
Definition: hlcache.c:679
High-level resource cache interface.
nserror(* hlcache_handle_callback)(hlcache_handle *handle, const hlcache_event *event, void *pw)
Client callback for high-level cache events.
Definition: hlcache.h:63
bool html_begin_conversion(html_content *htmlc)
Begin conversion of an HTML document.
Definition: html.c:833
nserror html_proceed_to_done(html_content *html)
Complete the HTML content state machine iff all scripts are finished.
Definition: html.c:285
bool html_can_begin_conversion(html_content *htmlc)
Test if an HTML content conversion can begin.
Definition: html.c:814
Interface to text/html content handler.
Public content interface.
struct nsurl * hlcache_handle_get_url(const struct hlcache_handle *handle)
Retrieve the URL associated with a high level cache handle.
const uint8_t * content_get_source_data(struct hlcache_handle *h, size_t *size)
Retrieve source of content.
Definition: content.c:1209
content_type content_get_type(struct hlcache_handle *h)
Retrieve computed type of content.
Definition: content.c:1061
Interface to javascript engine functions.
struct jsthread jsthread
JavaScript interpreter thread.
Definition: js.h:53
bool js_exec(jsthread *thread, const uint8_t *txt, size_t txtlen, const char *name)
execute some javascript in a context
Definition: dukky.c:922
#define NSLOG(catname, level, logmsg, args...)
Definition: log.h:116
Localised message support (interface).
void nsurl_unref(nsurl *url)
Drop a reference to a NetSurf URL object.
const char * nsurl_access(const nsurl *url)
Access a NetSurf URL object as a string.
nserror nsurl_join(const nsurl *base, const char *rel, nsurl **joined)
Join a base url to a relative link part, creating a new NetSurf URL object.
struct nsurl nsurl
NetSurf URL object.
Definition: nsurl.h:31
Private data for text/html content.
dom_hubbub_error html_process_script(void *ctx, dom_node *node)
process script node parser callback
Definition: script.c:566
static script_handler_t * select_script_handler(content_type ctype)
Definition: script.c:49
bool html_saw_insecure_scripts(html_content *htmlc)
Check if any of the scripts loaded were insecure.
Definition: script.c:611
static dom_hubbub_error exec_src_script(html_content *c, dom_node *node, dom_string *mimetype, dom_string *src)
process a script with a src tag
Definition: script.c:381
static struct html_script * html_process_new_script(html_content *c, dom_string *mimetype, enum html_script_type type)
Definition: script.c:123
static dom_hubbub_error exec_inline_script(html_content *c, dom_node *node, dom_string *mimetype)
Definition: script.c:515
static nserror convert_script_sync_cb(hlcache_handle *script, const hlcache_event *event, void *pw)
Callback for syncronous scripts.
Definition: script.c:283
nserror html_script_free(html_content *html)
Free all script resources and references for a html content.
Definition: script.c:636
nserror html_script_exec(html_content *c, bool allow_defer)
Attempt script execution for defer and async scripts.
Definition: script.c:59
bool() script_handler_t(struct jsthread *jsthread, const uint8_t *data, size_t size, const char *name)
Definition: script.c:46
static nserror convert_script_defer_cb(hlcache_handle *script, const hlcache_event *event, void *pw)
Callback for defer scripts.
Definition: script.c:227
static nserror convert_script_async_cb(hlcache_handle *script, const hlcache_event *event, void *pw)
Callback for asyncronous scripts.
Definition: script.c:159
Interface to utility string handling.
bool quirks
Content is in quirks mode.
unsigned int active
Number of child fetches or conversions currently in progress.
Context for retrieving a child object.
Definition: hlcache.h:37
bool quirks
Whether parent is quirky.
Definition: hlcache.h:39
const char * charset
Charset of parent.
Definition: hlcache.h:38
High-level cache event.
Definition: hlcache.h:43
content_msg type
Event type.
Definition: hlcache.h:44
union content_msg_data data
Event data.
Definition: hlcache.h:45
High-level cache handle.
Definition: hlcache.c:66
Data specific to CONTENT_HTML.
Definition: private.h:93
dom_hubbub_parser * parser
Parser object handle.
Definition: private.h:96
char * encoding
Encoding of source, NULL if unknown.
Definition: private.h:106
struct nsurl * base_url
Base URL (may be a copy of content->url).
Definition: private.h:111
struct jsthread * jsthread
javascript thread in use
Definition: private.h:152
bool parse_completed
Whether the parse has been completed.
Definition: private.h:97
unsigned int scripts_count
Number of entries in scripts.
Definition: private.h:148
struct content base
Definition: private.h:94
struct html_script * scripts
Scripts.
Definition: private.h:150
Container for scripts used by an HTML document.
Definition: html.h:69
struct dom_string * mimetype
Definition: html.h:79
union html_script::@136 data
Script data.
enum html_script::html_script_type type
bool defer
Definition: html.h:86
bool async
Definition: html.h:85
bool ready_exec
Definition: html.h:84
html_script_type
Type of script.
Definition: html.h:71
@ HTML_SCRIPT_DEFER
Definition: html.h:73
@ HTML_SCRIPT_INLINE
Definition: html.h:71
@ HTML_SCRIPT_SYNC
Definition: html.h:72
@ HTML_SCRIPT_ASYNC
Definition: html.h:74
bool force_async
Definition: html.h:83
bool parser_inserted
Definition: html.h:82
struct dom_string * string
Definition: html.h:77
bool already_started
Definition: html.h:81
struct hlcache_handle * handle
Definition: html.h:76
dukky javascript thread
Definition: dukky.c:70
Extra data for some content_msg messages.
Definition: content.h:60
struct content_msg_data::@99 errordata
CONTENT_MSG_ERROR - Error from content or underlying fetch.
const char * errormsg
The message.
Definition: content.h:95
struct jsthread ** jsthread
CONTENT_MSG_GETTHREAD - Javascript context (thread)
Definition: content.h:143
browser_window_console_source src
The source of the logging.
Definition: content.h:66