summaryrefslogtreecommitdiff
path: root/docs/jsbinding.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/jsbinding.md')
-rw-r--r--docs/jsbinding.md313
1 files changed, 313 insertions, 0 deletions
diff --git a/docs/jsbinding.md b/docs/jsbinding.md
new file mode 100644
index 000000000..8b24a80a5
--- /dev/null
+++ b/docs/jsbinding.md
@@ -0,0 +1,313 @@
+JavaScript bindings
+===================
+
+In order for JavaScript programs to to interact with the page contents
+it must use the Document Object Model (DOM) and Cascading Style Sheet
+Object Model (CSSOM) API.
+
+These interfaces are described using Web Interface Description
+Language (WebIDL) within the relevant specifications
+(e.g. https://dom.spec.whatwg.org/).
+
+Each interface described by the WebIDL must be bound (connected) to
+the browsers internal representation for the DOM or CSS, etc. These
+bindings descriptions are processed together with the WebIDL by the
+nsgenbind tool to generate source code.
+
+A list of [DOM and CSSOM methods](unimplemented.html) is available
+outlining the remaining unimplemented API bindings.
+
+WebIDL
+------
+
+The [WebIDL specification](http://www.w3.org/TR/WebIDL/) defines the
+interface description language used. The WebIDL is being updated and
+an [editors draft](https://heycam.github.io/webidl/) is available but
+use is inconsistent.
+
+These descriptions should be periodically updated to keep the browser
+interfaces current.
+
+There is a content/handlers/javascript/WebIDL/Makefile which attempts
+to automatically update the IDL files by scraping the web specs.
+
+This tool needs a great deal of hand holding, not least because many of the
+source documents list the IDL fragments multiple times, some even have
+appendices with the entire IDL repeated.
+
+The IDL uses some slightly different terms than other object orientated
+ systems.
+
+ WebIDL | JavaScript | Common OOP | Note
+ ------- | ---------- | ---------- | ----
+ interface | prototype | class | The data definition of the object
+ constants | read-only value property on the prototype | class variable | Belong to class, one copy
+ operation | method | method | functions that can be called
+ attribute | property | property | Variables set per instance
+
+JavaScript implementation
+-------------------------
+
+NetSurf consumes the Duktape JS engine in order to run the JS code which
+is used within the browser. Duktape is exceedingly well documented and
+its API docs at https://duktape.org/api.html are incredibly useful.
+
+It'll be worthwhile learning about how duktape stacks work in order to
+work on bindings in NetSurf
+
+Dukky
+-----
+
+Wrappering around and layering between duktape and the browser is a
+set of functionality we call `dukky`. This defines a variety of
+conventions and capabilities which are common to almost all bindings.
+The header `dukky.h` provides the interface to these functions.
+
+Normally these functions are used by automatically generated content,
+but if a binding needs to add DOM nodes back into the JavaScript
+environment (for example when returning them from a method
+implementation) `dukky_push_node()` should be used or when calling a
+function in a JS context `dukky_pcall()`
+
+Dukky automatically terminates any JS call which lasts for more than
+10 seconds. If you are calling a JS function from outside any of the
+"normal" means by which dukky might call code on your behalf
+(`js_exec()`, events, etc) then you should be sure to use
+`dukky_pcall()` and pass in `true` in `reset_timeout` otherwise your
+code may unexpectedly terminate early.
+
+Interface binding introduction
+------------------------------
+
+The binding files are processed by the nsgenbind tool to generate c
+source code which implements the interfaces within the JavaScript
+engine.
+
+The bindings are specific to a JavaScript engine, the DOM library, the
+CSS library and the browser. In this case that is the tuple of
+duktape, libdom, libcss and NetSurf.
+
+In principle other engines or libraries could be substituted
+(historically NetSurf unsuccessfully tried to use spidermonkey) but the
+engineering to do so is formidable.
+
+The bindings are kept the main [NetSurf source code
+repository](http://git.netsurf-browser.org/netsurf.git/) within the
+duktape JavaScript handler directory `content/handlers/javascript/duktape/`
+
+The root binding which contains all the interfaces introduced into
+the JavaScript programs initial execution context is nesurf.bnd this
+references all the WebIDL to be bound and includes all additional
+binding definitions to implement the interfaces.
+
+The bindings are a Domain Specific Language (DSL) which allows
+implementations to be added to each WebIDL method. nsgenbind
+documentation contains a [full description of the
+language](https://ci.netsurf-browser.org/jenkins/view/Categorized/job/docs-nsgenbind/doxygen/index.html).
+
+The main focus on creating binding is to implement the content within
+getter, setter and method stanzas. These correspond to implementations
+of the WebIDL operations on an interface.
+
+The binding implementations are in the form of C code fragments
+directly pasted into the generated code with generated setup code
+surrounding it.
+
+### Simple attribute example
+
+The Window interface (class) in the HTML specification has an
+attribute (property) called `name`.
+
+The full WebIDL for the Window interface is defined in the
+`content/handlers/javascript/WebIDL/html.idl` file but the
+fragment for our example is just:
+
+ interface Window : EventTarget {
+ attribute DOMString name;
+ };
+
+This indicates there is an attribute called `name` which is a string
+(technicaly a DOMString but the implementation does not differentiate
+between string types) which means it has both a setter and a getter.
+
+attributes can be marked readonly which would mean there is only a
+getter required for them. For example the Plugin interface has a
+'name' attribute defined as:
+
+ interface Plugin {
+ readonly attribute DOMString name;
+ };
+
+The getter and setter for the Window class attribute will be added to
+`Window.bnd`. The entries added will be of the form:
+
+ getter Window::name()
+ %{
+ %}
+
+ setter Window::name()
+ %{
+ %}
+
+The top level `netsurf.bnd` binding includes `Window.bnd` (using a
+`#include` directive) which contains the implementation of the Window
+class. This is purely to split the bindings up into logical units.
+
+The nsgenbind tool generates code that automatically allows acess to
+the classes private data structure elements through a variable called
+`priv` and the duktape stack in the variable `ctx`.
+
+The getter binding code must place the retrived value on the duktape
+stack and return 1 to indicate this or 0 if it failed.
+
+So for the name attribute case the complete getter binding is:
+
+ getter Window::name()
+ %{
+ const char *name;
+ browser_window_get_name(priv->win, &name);
+ duk_push_string(ctx, name);
+ return 1;
+ %}
+
+This uses the browser_window_get_name() interface to retrieve the name
+string for the window (identified using the private context) and then
+adds it to the duktape stack. The return value indicates the sucess of
+the operation.
+
+The setter must retrive the value to set from the duktap stack and
+update the internal private data structure with that value and return
+0 to indicate success.
+
+So for the name attribute case the complete setter binding is:
+
+ setter Window::name()
+ %{
+ const char *name;
+ name = duk_to_string(ctx, -1);
+ browser_window_set_name(priv->win, name);
+ return 0;
+ %}
+
+This uses browser_window_set_name() interface to set the name of the
+window. The return indicates the success of the operation.
+
+### Simple method example
+
+The Location interface (class) in the HTML specification has an
+operation (method) called `asign`. This method causes the browser to
+navigate to a new url.
+
+The full WebIDL for the Window interface is defined in the
+`content/handlers/javascript/WebIDL/html.idl` file but the
+fragment for our example is:
+
+ interface Location {
+ void assign(DOMString url);
+ }
+
+This shows there is an operation called assign in the Location
+interface which takes a single string parameter and returns nothing.
+
+The method binding will be added to `Location.bnd` similarly to how
+the attribute example used `Window.bnd`
+
+ method Location::assign()
+ %{
+ window_private_t *priv_win;
+ nsurl *joined;
+ duk_size_t slen;
+ const char *url;
+
+ /* retrieve the private data from the root object (window) */
+ duk_push_global_object(ctx);
+ duk_get_prop_string(ctx, -1, PRIVATE_MAGIC);
+ priv_win = duk_get_pointer(ctx, -1);
+ duk_pop(ctx);
+
+ if (priv_win == NULL || priv_win->win == NULL) {
+ NSLOG(netsurf, INFO, "failed to get browser context");
+ return 0;
+ }
+
+ url = duk_safe_to_lstring(ctx, 0, &slen);
+
+ nsurl_join(priv->url, url, &joined);
+ browser_window_navigate(priv_win->win,
+ joined,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(joined);
+ return 0;
+ %}
+
+The nsgenbind tool generates code that automatically allows acess to
+the classes private data structure elements through a variable called
+`priv` and the duktape stack in the variable `ctx`.
+
+In this case nsgenbind will generate code that will ensure there is at
+least one parameter and coerce it to a string on the duktape `ctx`
+stack returning a type error if it is unable to do so.
+
+The binding implementation shown here uses browser_window_navigate()
+to navigate to the new url. To do this it needs the browser window
+handle (pointer) which is obtained from the global object (the window)
+private structure.
+
+Note that the duk_safe_to_lstring() call used to obtain the url
+parameter needs no additional checking as nsgenbind emits this code
+automaticaly.
+
+### Overloaded method example
+
+The Window interface (class) in the HTML specification has an
+operation (method) called `alert`. This method is supposed to cause
+the user to be alerted about something on the page.
+
+The full WebIDL for the Window interface is defined in the
+`content/handlers/javascript/WebIDL/html.idl` file but the
+fragment for our example is:
+
+ interface Window : EventTarget {
+ void alert();
+ void alert(DOMString message);
+ };
+
+This indicates there is an operation called `alert`. It has two
+overloaded prototypes, one takes no parameters and one takes a string
+parameter (technicaly a DOMString but the implementation does not
+differentiate between string types)
+
+The method binding will be added to `Window.bnd` as the attribute example
+
+ method Window::alert()
+ %{
+ duk_idx_t dukky_argc = duk_get_top(ctx);
+ if (dukky_argc == 0) {
+ NSLOG(netsurf, INFO, "JS ALERT");
+ } else {
+ duk_size_t msg_len;
+ const char *msg;
+
+ if (!duk_is_string(ctx, 0)) {
+ duk_to_string(ctx, 0);
+ }
+ msg = duk_safe_to_lstring(ctx, 0, &msg_len);
+ NSLOG(netsurf, INFO, "JS ALERT: %*s", (int)msg_len, msg);
+ }
+ return 0;
+ %}
+
+The nsgenbind tool generates code that automatically allows acess to
+the classes private data structure elements through a variable called
+`priv` and the duktape stack in the variable `ctx`.
+
+For overloaded method calls nsgenbind does not emit code to do
+parameter verification and the binding code has to deal with all
+possible parameters itself.
+
+This binding checks the number of parameters and if one is present it
+coerces it to be a string and logs the result. \ No newline at end of file