diff --git a/content/content.h b/content/content.h index f8f8d32f1..c2605a7f1 100644 --- a/content/content.h +++ b/content/content.h @@ -72,7 +72,7 @@ typedef enum { CONTENT_MSG_REFRESH, /**< wants refresh */ CONTENT_MSG_DOWNLOAD, /**< download, not for display */ CONTENT_MSG_LINK, /**< RFC5988 link */ - CONTENT_MSG_GETCTX, /**< Javascript context */ + CONTENT_MSG_GETTHREAD, /**< Javascript thread */ CONTENT_MSG_GETDIMS, /**< Get viewport dimensions. */ CONTENT_MSG_SCROLL, /**< Request to scroll content */ CONTENT_MSG_DRAGSAVE, /**< Allow drag saving of content */ @@ -180,9 +180,9 @@ union content_msg_data { struct content_rfc5988_link *rfc5988_link; /** - * CONTENT_MSG_GETCTX - Javascript context + * CONTENT_MSG_GETTHREAD - Javascript context (thread) */ - struct jscontext **jscontext; + struct jsthread **jsthread; /** * CONTENT_MSG_GETDIMS - Get the viewport dimensions diff --git a/content/handlers/html/html.c b/content/handlers/html/html.c index d1b810fbc..26be58d73 100644 --- a/content/handlers/html/html.c +++ b/content/handlers/html/html.c @@ -757,8 +757,8 @@ void html_finish_conversion(html_content *htmlc) * object, but with its target set to the Document object (and * the currentTarget set to the Window object) */ - if (htmlc->jscontext != NULL) { - js_fire_event(htmlc->jscontext, "load", htmlc->document, NULL); + if (htmlc->jsthread != NULL) { + js_fire_event(htmlc->jsthread, "load", htmlc->document, NULL); } /* convert dom tree to box tree */ @@ -896,20 +896,20 @@ dom_default_action_DOMNodeInserted_cb(struct dom_event *evt, void *pw) } if (htmlc->enable_scripting) { /* ensure javascript context is available */ - if (htmlc->jscontext == NULL) { + if (htmlc->jsthread == NULL) { union content_msg_data msg_data; - msg_data.jscontext = &htmlc->jscontext; + msg_data.jsthread = &htmlc->jsthread; content_broadcast(&htmlc->base, - CONTENT_MSG_GETCTX, + CONTENT_MSG_GETTHREAD, &msg_data); NSLOG(netsurf, INFO, "javascript context: %p (htmlc: %p)", - htmlc->jscontext, + htmlc->jsthread, htmlc); } - if (htmlc->jscontext != NULL) { - js_handle_new_element(htmlc->jscontext, + if (htmlc->jsthread != NULL) { + js_handle_new_element(htmlc->jsthread, (dom_element *) node); } } @@ -1015,8 +1015,8 @@ dom_default_action_finished_cb(struct dom_event *evt, void *pw) { html_content *htmlc = pw; - if (htmlc->jscontext != NULL) - js_event_cleanup(htmlc->jscontext, evt); + if (htmlc->jsthread != NULL) + js_event_cleanup(htmlc->jsthread, evt); } /* callback function selector @@ -1136,7 +1136,7 @@ html_create_html_data(html_content *c, const http_parameter *params) c->search_string = NULL; c->scripts_count = 0; c->scripts = NULL; - c->jscontext = NULL; + c->jsthread = NULL; c->enable_scripting = nsoption_bool(enable_javascript); c->base.active = 1; /* The html content itself is active */ diff --git a/content/handlers/html/html_internal.h b/content/handlers/html/html_internal.h index 9b363dc8b..a64078143 100644 --- a/content/handlers/html/html_internal.h +++ b/content/handlers/html/html_internal.h @@ -151,8 +151,8 @@ typedef struct html_content { unsigned int scripts_count; /** Scripts */ struct html_script *scripts; - /** javascript context */ - struct jscontext *jscontext; + /** javascript thread in use */ + struct jsthread *jsthread; /** Number of entries in stylesheet_content. */ unsigned int stylesheet_count; diff --git a/content/handlers/html/html_object.c b/content/handlers/html/html_object.c index 223f5516d..3a60c47f1 100644 --- a/content/handlers/html/html_object.c +++ b/content/handlers/html/html_object.c @@ -340,8 +340,9 @@ html_object_callback(hlcache_handle *object, /* Don't care about favicons that aren't on top level content */ break; - case CONTENT_MSG_GETCTX: - *(event->data.jscontext) = NULL; + case CONTENT_MSG_GETTHREAD: + /* Objects don't have JS threads */ + *(event->data.jsthread) = NULL; break; case CONTENT_MSG_GETDIMS: diff --git a/content/handlers/html/html_script.c b/content/handlers/html/html_script.c index 4df5f3384..301acadd6 100644 --- a/content/handlers/html/html_script.c +++ b/content/handlers/html/html_script.c @@ -42,7 +42,7 @@ #include "html/html.h" #include "html/html_internal.h" -typedef bool (script_handler_t)(struct jscontext *jscontext, const uint8_t *data, size_t size, const char *name); +typedef bool (script_handler_t)(struct jsthread *jsthread, const uint8_t *data, size_t size, const char *name); static script_handler_t *select_script_handler(content_type ctype) @@ -62,7 +62,7 @@ nserror html_script_exec(html_content *c, bool allow_defer) script_handler_t *script_handler; bool have_run_something = false; - if (c->jscontext == NULL) { + if (c->jsthread == NULL) { return NSERROR_BAD_PARAMETER; } @@ -95,7 +95,7 @@ nserror html_script_exec(html_content *c, bool allow_defer) size_t size; data = content_get_source_data( s->data.handle, &size ); - script_handler(c->jscontext, data, size, + script_handler(c->jsthread, data, size, nsurl_access(hlcache_handle_get_url(s->data.handle))); have_run_something = true; /* We have to re-acquire this here since the @@ -319,12 +319,12 @@ convert_script_sync_cb(hlcache_handle *script, /* attempt to execute script */ script_handler = select_script_handler(content_get_type(s->data.handle)); - if (script_handler != NULL && parent->jscontext != NULL) { + if (script_handler != NULL && parent->jsthread != NULL) { /* script has a handler */ const uint8_t *data; size_t size; data = content_get_source_data(s->data.handle, &size ); - script_handler(parent->jscontext, data, size, + script_handler(parent->jsthread, data, size, nsurl_access(hlcache_handle_get_url(s->data.handle))); } @@ -549,7 +549,7 @@ exec_inline_script(html_content *c, dom_node *node, dom_string *mimetype) lwc_string_unref(lwcmimetype); if (script_handler != NULL) { - script_handler(c->jscontext, + script_handler(c->jsthread, (const uint8_t *)dom_string_data(script), dom_string_byte_length(script), "?inline script?"); @@ -575,13 +575,13 @@ html_process_script(void *ctx, dom_node *node) /* We should only ever be here if scripting was enabled for this * content so it's correct to make a javascript context if there * isn't one already. */ - if (c->jscontext == NULL) { + if (c->jsthread == NULL) { union content_msg_data msg_data; - msg_data.jscontext = &c->jscontext; - content_broadcast(&c->base, CONTENT_MSG_GETCTX, &msg_data); - NSLOG(netsurf, INFO, "javascript context %p ", c->jscontext); - if (c->jscontext == NULL) { + msg_data.jsthread = &c->jsthread; + content_broadcast(&c->base, CONTENT_MSG_GETTHREAD, &msg_data); + NSLOG(netsurf, INFO, "javascript context %p ", c->jsthread); + if (c->jsthread == NULL) { /* no context and it could not be created, abort */ return DOM_HUBBUB_OK; } @@ -668,6 +668,6 @@ nserror html_script_free(html_content *html) /* exported internal interface documented in html/html_internal.h */ nserror html_script_invalidate_ctx(html_content *htmlc) { - htmlc->jscontext = NULL; + htmlc->jsthread = NULL; return NSERROR_OK; } diff --git a/content/handlers/javascript/js.h b/content/handlers/javascript/js.h index 522dd9879..126cb7cfb 100644 --- a/content/handlers/javascript/js.h +++ b/content/handlers/javascript/js.h @@ -25,15 +25,33 @@ #include "utils/errors.h" -typedef struct jscontext jscontext; -typedef struct jsobject jsobject; - struct dom_event; struct dom_document; struct dom_node; struct dom_element; struct dom_string; +/** + * JavaScript interpreter heap + * + * In order to try and be moderately performant, we create a heap + * per browser window. This heap is shared by all browsing contexts + * we end up creating in that window. + */ +typedef struct jsheap jsheap; + +/** + * JavaScript interpreter thread + * + * When we create a browsing context itself (window+content) we have + * to create a JS thread to attach to the browsing context. + * + * JS threads are associated with heaps and will be destroyed when + * the heap is destroyed. They can be shut down manually though + * and should be for object lifetime safety reasons. + */ +typedef struct jsthread jsthread; + /** * Initialise javascript interpreter */ @@ -45,41 +63,51 @@ void js_initialise(void); void js_finalise(void); /** - * Create a new javascript context. + * Create a new javascript heap. * - * There is usually one context per browsing context (browser window) + * There is usually one heap per browser window. * * \param timeout elapsed wallclock time (in seconds) before \a callback is called - * \param jsctx Updated to the created JS context + * \param heap Updated to the created JS heap * \return NSERROR_OK on success, appropriate error otherwise. */ -nserror js_newcontext(int timeout, jscontext **jsctx); +nserror js_newheap(int timeout, jsheap **heap); /** - * Destroy a previously created context + * Destroy a previously created heap. + * + * \param heap The heap to destroy */ -void js_destroycontext(jscontext *ctx); +void js_destroyheap(jsheap *heap); /** - * Create a new javascript compartment + * Create a new javascript thread * * This is called once for a page with javascript script tags on - * it. It constructs a fresh global window object. + * it. It constructs a fresh global window object and prepares the JS + * browsing context. It's important that threads are shut down cleanly + * when the browsing context is going to be cleaned up. + * + * \param heap The heap to create the thread within + * \param win_priv The value to give to the Window constructor as the window + * \param doc_priv The value to give to the Document constructor as the document + * \param thread Updated to the created thread + * \return NSERROR_OK on success, appropriate error otherwise */ -jsobject *js_newcompartment(jscontext *ctx, void *win_priv, void *doc_priv); +nserror js_newthread(jsheap *heap, void *win_priv, void *doc_priv, jsthread **thread); /** * execute some javascript in a context */ -bool js_exec(jscontext *ctx, const uint8_t *txt, size_t txtlen, const char *name); +bool js_exec(jsthread *thread, const uint8_t *txt, size_t txtlen, const char *name); /** * fire an event at a dom node */ -bool js_fire_event(jscontext *ctx, const char *type, struct dom_document *doc, struct dom_node *target); +bool js_fire_event(jsthread *thread, const char *type, struct dom_document *doc, struct dom_node *target); bool -js_dom_event_add_listener(jscontext *ctx, +js_dom_event_add_listener(jsthread *thread, struct dom_document *document, struct dom_node *node, struct dom_string *event_type_dom, @@ -94,7 +122,7 @@ js_dom_event_add_listener(jscontext *ctx, * by the context provided. The JS implementation must then scan the element * for on* attributes and register appropriate listeners for those handlers. */ -void js_handle_new_element(jscontext *ctx, struct dom_element *node); +void js_handle_new_element(jsthread *thread, struct dom_element *node); /** * Handle an event propagation finished callback. @@ -104,6 +132,6 @@ void js_handle_new_element(jscontext *ctx, struct dom_element *node); * it may need to perform before the DOM finishes and the event may end up * freed. */ -void js_event_cleanup(jscontext *ctx, struct dom_event *evt); +void js_event_cleanup(jsthread *thread, struct dom_event *evt); #endif /* NETSURF_JAVASCRIPT_JS_H_ */ diff --git a/content/handlers/javascript/none/none.c b/content/handlers/javascript/none/none.c index 26b9b5300..b79d3242f 100644 --- a/content/handlers/javascript/none/none.c +++ b/content/handlers/javascript/none/none.c @@ -35,35 +35,36 @@ void js_finalise(void) { } -nserror js_newcontext(int timeout, jscontext **jsctx) +nserror js_newheap(int timeout, jsheap **heap) { - *jsctx = NULL; + *heap = NULL; return NSERROR_OK; } -void js_destroycontext(jscontext *ctx) +void js_destroyheap(jsheap *heap) { } -jsobject *js_newcompartment(jscontext *ctx, void *win_priv, void *doc_priv) +nserror js_newthread(jsheap *heap, void *win_priv, void *doc_priv, jsthread **thread) { - return NULL; + *thread = NULL; + return NSERROR_NOT_IMPLEMENTED; } -bool js_exec(jscontext *ctx, const uint8_t *txt, size_t txtlen, const char *name) +bool js_exec(jsthread *thread, const uint8_t *txt, size_t txtlen, const char *name) { return true; } -bool js_fire_event(jscontext *ctx, const char *type, struct dom_document *doc, struct dom_node *target) +bool js_fire_event(jsthread *thread, const char *type, struct dom_document *doc, struct dom_node *target) { return true; } -void js_handle_new_element(jscontext *ctx, struct dom_element *node) +void js_handle_new_element(jsthread *thread, struct dom_element *node) { } -void js_event_cleanup(jscontext *ctx, struct dom_event *evt) +void js_event_cleanup(jsthread *thread, struct dom_event *evt) { } diff --git a/desktop/browser_private.h b/desktop/browser_private.h index c9f9bbcf6..562abf131 100644 --- a/desktop/browser_private.h +++ b/desktop/browser_private.h @@ -263,7 +263,8 @@ struct browser_window { bool can_edit; /** current javascript context */ - struct jscontext *jsctx; + struct jsheap *jsheap; + struct jsthread *jsthread; /** cache of the currently displayed status text. */ struct { diff --git a/desktop/browser_window.c b/desktop/browser_window.c index 3d1a19f60..bd446a072 100644 --- a/desktop/browser_window.c +++ b/desktop/browser_window.c @@ -1485,15 +1485,19 @@ browser_window_callback(hlcache_handle *c, const hlcache_event *event, void *pw) } break; - case CONTENT_MSG_GETCTX: - /* only the content object created by the browser - * window requires a new global compartment object - */ - assert(bw->loading_content == c); - if (js_newcompartment(bw->jsctx, - bw, - hlcache_handle_get_content(c)) != NULL) { - *(event->data.jscontext) = bw->jsctx; + case CONTENT_MSG_GETTHREAD: + { + /* only the content object created by the browser + * window requires a new global compartment object + */ + jsthread *thread; + assert(bw->loading_content == c); + if (js_newthread(bw->jsheap, + bw, + hlcache_handle_get_content(c), + &thread) == NSERROR_OK) { + *(event->data.jsthread) = thread; + } } break; @@ -1760,8 +1764,8 @@ static void browser_window_destroy_internal(struct browser_window *bw) bw->box = NULL; } - if (bw->jsctx != NULL) { - js_destroycontext(bw->jsctx); + if (bw->jsheap != NULL) { + js_destroyheap(bw->jsheap); } /* These simply free memory, so are safe here */ @@ -3085,7 +3089,7 @@ browser_window_initialise_common(enum browser_window_create_flags flags, assert(bw); /* new javascript context for each window/(i)frame */ - err = js_newcontext(nsoption_int(script_timeout), &bw->jsctx); + err = js_newheap(nsoption_int(script_timeout), &bw->jsheap); if (err != NSERROR_OK) return err;