mirror of
https://github.com/netsurf-browser/netsurf
synced 2024-12-22 12:12:35 +03:00
24ec30359b
When we close the JS compartment we try and cancel all callbacks so that they do not fire after the compartment is closed. However if we have in-train callbacks, they can gum up the closedown and so we need to check and if we've done all we can, we break out of the callback removal loop. Signed-off-by: Daniel Silverstone <dsilvers@digital-scurf.org>
721 lines
19 KiB
Plaintext
721 lines
19 KiB
Plaintext
/* Window 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 Window {
|
|
private struct browser_window * win;
|
|
private struct html_content * htmlc;
|
|
private struct window_schedule_s * schedule_ring;
|
|
private bool closed_down;
|
|
prologue %{
|
|
#include "utils/corestrings.h"
|
|
#include "utils/nsurl.h"
|
|
#include "netsurf/browser_window.h"
|
|
#include "content/hlcache.h"
|
|
#include "html/html.h"
|
|
#include "html/html_internal.h"
|
|
#include "desktop/gui_internal.h"
|
|
#include "netsurf/misc.h"
|
|
#include "utils/ring.h"
|
|
#include "netsurf/inttypes.h"
|
|
|
|
#define WINDOW_CALLBACKS MAGIC(WindowCallbacks)
|
|
#define HANDLER_MAGIC MAGIC(HANDLER_MAP)
|
|
|
|
static size_t next_handle = 0;
|
|
|
|
typedef struct window_schedule_s {
|
|
window_private_t *owner;
|
|
duk_context *ctx;
|
|
struct window_schedule_s *r_next;
|
|
struct window_schedule_s *r_prev;
|
|
size_t handle;
|
|
int repeat_timeout;
|
|
bool running;
|
|
} window_schedule_t;
|
|
|
|
static void window_remove_callback_bits(duk_context *ctx, size_t handle) {
|
|
/* stack is ... */
|
|
duk_push_global_object(ctx);
|
|
duk_get_prop_string(ctx, -1, WINDOW_CALLBACKS);
|
|
/* stack is ..., win, cbt */
|
|
duk_push_int(ctx, (duk_int_t)handle);
|
|
/* ..., win, cbt, handle */
|
|
duk_del_prop(ctx, -2);
|
|
/* ..., win, cbt */
|
|
duk_pop_2(ctx);
|
|
/* ... */
|
|
}
|
|
|
|
static void
|
|
window_call_callback(duk_context *ctx, size_t handle, bool clear_entry)
|
|
{
|
|
NSLOG(dukky, DEEPDEBUG, "ctx=%p, handle=%"PRIsizet, ctx, handle);
|
|
/* Stack is ... */
|
|
duk_push_global_object(ctx);
|
|
/* ..., win */
|
|
duk_get_prop_string(ctx, -1, WINDOW_CALLBACKS);
|
|
/* ..., win, cbt */
|
|
duk_push_int(ctx, (duk_int_t)handle);
|
|
/* ..., win, cbt, handle */
|
|
duk_get_prop(ctx, -2);
|
|
/* ..., win, cbt, cbo */
|
|
//dukky_log_stack_frame(ctx, "On entry to callback");
|
|
/* ..., win, cbt, cbo */
|
|
/* What we want to do is call cbo.func passing all of cbo.args */
|
|
duk_get_prop_string(ctx, -1, "func");
|
|
duk_get_prop_string(ctx, -2, "args");
|
|
/* ..., win, cbt, cbo, func, argarr */
|
|
duk_size_t arrlen = duk_get_length(ctx, -1);
|
|
for (duk_size_t i = 0; i < arrlen; ++i) {
|
|
duk_push_int(ctx, (duk_int_t)i);
|
|
duk_get_prop(ctx, -(2+i));
|
|
}
|
|
/* ..., win, cbt, cbo, func, argarr, args... */
|
|
duk_remove(ctx, -(arrlen+1));
|
|
/* ..., win, cbt, cbo, func, args... */
|
|
//dukky_log_stack_frame(ctx, "Just before call");
|
|
(void) dukky_pcall(ctx, arrlen, true);
|
|
/* ..., win, cbt, cbo, retval */
|
|
if (clear_entry) {
|
|
NSLOG(dukky, DEEPDEBUG, "Not recurring callback, removing from cbt");
|
|
duk_pop_n(ctx, 2);
|
|
/* ..., win, cbt */
|
|
duk_push_int(ctx, (duk_int_t)handle);
|
|
/* ..., win, cbt, handle */
|
|
duk_del_prop(ctx, -2);
|
|
/* ..., win, cbt */
|
|
duk_pop_n(ctx, 2);
|
|
} else {
|
|
duk_pop_n(ctx, 4);
|
|
}
|
|
/* ... */
|
|
//dukky_log_stack_frame(ctx, "On leaving callback");
|
|
}
|
|
|
|
|
|
static void
|
|
window_schedule_callback(void *p)
|
|
{
|
|
window_schedule_t *priv = (window_schedule_t *)p;
|
|
|
|
NSLOG(dukky, DEEPDEBUG,
|
|
"Entered window scheduler callback: %"PRIsizet, priv->handle);
|
|
|
|
priv->running = true;
|
|
window_call_callback(priv->ctx,
|
|
priv->handle,
|
|
priv->repeat_timeout == 0);
|
|
priv->running = false;
|
|
|
|
if (priv->repeat_timeout > 0) {
|
|
/* Reschedule */
|
|
NSLOG(dukky, DEEPDEBUG,
|
|
"Rescheduling repeating callback %"PRIsizet,
|
|
priv->handle);
|
|
guit->misc->schedule(priv->repeat_timeout,
|
|
window_schedule_callback,
|
|
priv);
|
|
} else {
|
|
NSLOG(dukky, DEEPDEBUG,
|
|
"Removing completed callback %"PRIsizet, priv->handle);
|
|
/* Remove this from the ring */
|
|
RING_REMOVE(priv->owner->schedule_ring, priv);
|
|
window_remove_callback_bits(priv->ctx, priv->handle);
|
|
free(priv);
|
|
}
|
|
}
|
|
|
|
static size_t
|
|
window_alloc_new_callback(duk_context *ctx,
|
|
window_private_t *window,
|
|
bool repeating,
|
|
int timeout)
|
|
{
|
|
size_t new_handle = next_handle++;
|
|
window_schedule_t *sched = calloc(sizeof *sched, 1);
|
|
if (sched == NULL) {
|
|
return new_handle;
|
|
}
|
|
sched->owner = window;
|
|
sched->ctx = ctx;
|
|
sched->handle = new_handle;
|
|
sched->repeat_timeout = repeating ? timeout : 0;
|
|
sched->running = false;
|
|
|
|
RING_INSERT(window->schedule_ring, sched);
|
|
|
|
/* Next, the duktape stack looks like: func, timeout, ...
|
|
* In order to proceed, we want to put into the WINDOW_CALLBACKS
|
|
* keyed by the handle, an object containing the call to make and
|
|
* the array of arguments to call the function with
|
|
*/
|
|
duk_idx_t nargs = duk_get_top(ctx) - 2;
|
|
duk_push_global_object(ctx);
|
|
duk_get_prop_string(ctx, -1, WINDOW_CALLBACKS);
|
|
duk_push_int(ctx, (duk_int_t)new_handle);
|
|
duk_push_object(ctx);
|
|
/* stack is: func, timeout, ..., win, cbt, handle, cbo */
|
|
|
|
/* put the function into the cbo */
|
|
duk_dup(ctx, 0);
|
|
duk_put_prop_string(ctx, -2, "func");
|
|
|
|
/* Now the arguments */
|
|
duk_push_array(ctx);
|
|
for (duk_idx_t i = 0; i < nargs; ++i) {
|
|
duk_dup(ctx, 2 + i); /* Dup the arg */
|
|
duk_put_prop_index(ctx, -2, i); /* arr[i] = arg[i] */
|
|
}
|
|
duk_put_prop_string(ctx, -2, "args");
|
|
/* stack is: func, timeout, ..., win, cbt, handle, cbo */
|
|
duk_put_prop(ctx, -3);
|
|
/* stack is: func, timeout, ..., win, cbt */
|
|
duk_pop_2(ctx);
|
|
/* And we're back to func, timeout, ... */
|
|
|
|
guit->misc->schedule(timeout, window_schedule_callback, sched);
|
|
NSLOG(dukky, DEEPDEBUG, "Scheduled callback %"PRIsizet" for %d ms from now", new_handle, timeout);
|
|
|
|
return new_handle;
|
|
}
|
|
|
|
static void
|
|
window_remove_callback_by_handle(duk_context *ctx,
|
|
window_private_t *window,
|
|
size_t handle)
|
|
{
|
|
int res;
|
|
|
|
RING_ITERATE_START(window_schedule_t, window->schedule_ring, sched) {
|
|
if (sched->handle == handle) {
|
|
if (sched->running) {
|
|
NSLOG(dukky, DEEPDEBUG,
|
|
"Cancelling in-train callback %"PRIsizet,
|
|
sched->handle);
|
|
sched->repeat_timeout = 0;
|
|
} else {
|
|
NSLOG(dukky, DEEPDEBUG,
|
|
"Cancelled callback %"PRIsizet,
|
|
sched->handle);
|
|
res = guit->misc->schedule(-1,
|
|
window_schedule_callback,
|
|
sched);
|
|
assert(res == NSERROR_OK);
|
|
RING_REMOVE(window->schedule_ring, sched);
|
|
window_remove_callback_bits(ctx, sched->handle);
|
|
free(sched);
|
|
}
|
|
RING_ITERATE_STOP(window->schedule_ring, sched);
|
|
}
|
|
} RING_ITERATE_END(window->schedule_ring, sched);
|
|
}
|
|
|
|
/* This is the dodgy thread closedown method */
|
|
static duk_ret_t dukky_window_closedown_thread(duk_context *ctx)
|
|
{
|
|
window_private_t *priv = NULL;
|
|
|
|
duk_push_global_object(ctx);
|
|
duk_get_prop_string(ctx, -1, dukky_magic_string_private);
|
|
priv = duk_get_pointer(ctx, -1);
|
|
duk_pop_2(ctx);
|
|
|
|
if (priv == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
priv->closed_down = true;
|
|
|
|
NSLOG(dukky, DEEPDEBUG, "Closing down thread");
|
|
while (priv->schedule_ring != NULL) {
|
|
window_schedule_t *to_remove = NULL;
|
|
// Find a schedule item to remove
|
|
RING_ITERATE_START(window_schedule_t, priv->schedule_ring, sched) {
|
|
if (sched->running == false) {
|
|
// This one is not running, we can remove it
|
|
to_remove = sched;
|
|
RING_ITERATE_STOP(window->schedule_ring, sched);
|
|
} else if (sched->repeat_timeout != 0) {
|
|
// This one is running and has yet to be
|
|
// cancelled, so prevent it rescheduling itself
|
|
NSLOG(dukky, DEEPDEBUG,
|
|
"Cancelling in-train callback %"PRIsizet,
|
|
sched->handle);
|
|
sched->repeat_timeout = 0;
|
|
}
|
|
} RING_ITERATE_END(priv->schedule_ring, sched);
|
|
|
|
if (to_remove == NULL) {
|
|
// We didn't find any non-running callbacks
|
|
// so let's log that and break out of the closedown
|
|
// loop so we can continue and hopefully close down
|
|
NSLOG(dukky, DEEPDEBUG,
|
|
"Leaving in-train callbacks to unwind");
|
|
break;
|
|
}
|
|
|
|
// Remove the handle we found, this will reduce the callback
|
|
// scheduler ring by one and perhaps leave it empty so we can
|
|
// finish the closedown.
|
|
window_remove_callback_by_handle(ctx,
|
|
priv,
|
|
to_remove->handle);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
%};
|
|
};
|
|
|
|
init Window(struct browser_window *win, struct html_content *htmlc)
|
|
%{
|
|
/* element window */
|
|
priv->win = win;
|
|
priv->htmlc = htmlc;
|
|
priv->schedule_ring = NULL;
|
|
priv->closed_down = false;
|
|
NSLOG(netsurf, DEEPDEBUG, "win=%p htmlc=%p", priv->win, priv->htmlc);
|
|
|
|
NSLOG(netsurf, DEEPDEBUG,
|
|
"URL is %s", nsurl_access(browser_window_access_url(priv->win)));
|
|
duk_push_object(ctx);
|
|
duk_put_prop_string(ctx, 0, WINDOW_CALLBACKS);
|
|
%}
|
|
|
|
fini Window()
|
|
%{
|
|
NSLOG(dukky, DEEPDEBUG, "Shutting down Window %p", priv->win);
|
|
/* Cheaply iterate the schedule ring, cancelling any pending callbacks */
|
|
while (priv->schedule_ring != NULL) {
|
|
window_remove_callback_by_handle(ctx, priv, priv->schedule_ring->handle);
|
|
}
|
|
%}
|
|
|
|
prototype Window()
|
|
%{
|
|
#define EXPOSE(v) \
|
|
duk_get_global_string(ctx, #v); \
|
|
duk_put_prop_string(ctx, 0, #v)
|
|
/* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects */
|
|
/* ** Value properties */
|
|
EXPOSE(Infinity);
|
|
EXPOSE(NaN);
|
|
EXPOSE(undefined);
|
|
EXPOSE(null);
|
|
EXPOSE(globalThis);
|
|
|
|
/* ** Function properties */
|
|
EXPOSE(eval);
|
|
/* EXPOSE(uneval); */ /* Not standard, maybe not available */
|
|
EXPOSE(isFinite);
|
|
EXPOSE(isNaN);
|
|
EXPOSE(parseFloat);
|
|
EXPOSE(parseInt);
|
|
EXPOSE(decodeURI);
|
|
EXPOSE(decodeURIComponent);
|
|
EXPOSE(encodeURI);
|
|
EXPOSE(encodeURIComponent);
|
|
EXPOSE(escape);
|
|
EXPOSE(unescape);
|
|
|
|
/* ** Fundamental Objects */
|
|
EXPOSE(Object);
|
|
EXPOSE(Function);
|
|
EXPOSE(Boolean);
|
|
EXPOSE(Symbol);
|
|
EXPOSE(Error);
|
|
EXPOSE(EvalError);
|
|
EXPOSE(InternalError);
|
|
EXPOSE(RangeError);
|
|
EXPOSE(ReferenceError);
|
|
EXPOSE(SyntaxError);
|
|
EXPOSE(TypeError);
|
|
EXPOSE(URIError);
|
|
|
|
/* ** Numbers and Dates */
|
|
EXPOSE(Number);
|
|
EXPOSE(BigInt);
|
|
EXPOSE(Math);
|
|
EXPOSE(Date);
|
|
|
|
/* ** Text Processing */
|
|
EXPOSE(String);
|
|
EXPOSE(RegExp);
|
|
|
|
/* ** Indexed Collections */
|
|
EXPOSE(Array);
|
|
EXPOSE(Int8Array);
|
|
EXPOSE(Uint8Array);
|
|
EXPOSE(Uint8ClampedArray);
|
|
EXPOSE(Int16Array);
|
|
EXPOSE(Uint16Array);
|
|
EXPOSE(Int32Array);
|
|
EXPOSE(Uint32Array);
|
|
EXPOSE(Float32Array);
|
|
EXPOSE(Float64Array);
|
|
/* EXPOSE(BigInt64Array); */ /* Duktape lacks this - nonstandard API */
|
|
/* EXPOSE(BigUint64Array); */ /* Duktape lacks this - nonstandard API */
|
|
|
|
/* ** Keyed Collections */
|
|
/* EXPOSE(Map); */ /* Duktape lacks this - ES6 */
|
|
/* EXPOSE(Set); */ /* Duktape lacks this - ES6 */
|
|
/* EXPOSE(WeakMap); */ /* Duktape lacks this - ES6 */
|
|
/* EXPOSE(WeakSet); */ /* Duktape lacks this - ES6 */
|
|
|
|
/* Structured Data */
|
|
EXPOSE(ArrayBuffer);
|
|
/* EXPOSE(SharedArrayBuffer); */ /* Duktape lacks this - experimental API */
|
|
/* EXPOSE(Atomics); */ /* Duktape lacks this - experimental API */
|
|
EXPOSE(DataView);
|
|
EXPOSE(JSON);
|
|
|
|
/* ** Control abstraction properties */
|
|
/* EXPOSE(Promise); */ /* Probably ought to be one of ours? Also ES6 */
|
|
/* EXPOSE(Generator); */ /* Duktape and async? ES6 */
|
|
/* EXPOSE(GeneratorFunction); */ /* Duktape and async? ES6 */
|
|
/* EXPOSE(AsyncFunction); */ /* Duktape lacks this - experimental API */
|
|
|
|
/* Reflection */
|
|
EXPOSE(Reflect);
|
|
EXPOSE(Proxy);
|
|
|
|
/* ** Internationalisation */
|
|
/* Duktape lacks Intl - Maybe polyfill it? */
|
|
/* There is suggestion that cdn.polyfill.io exists for it */
|
|
|
|
/* ** WebAssembly */
|
|
/* As yet, Duktape lacks WA */
|
|
#undef EXPOSE
|
|
/* Add s3kr1t method to close the JS thread (browsing context) */
|
|
duk_dup(ctx, 0);
|
|
duk_push_string(ctx, MAGIC(closedownThread));
|
|
duk_push_c_function(ctx, dukky_window_closedown_thread, DUK_VARARGS);
|
|
duk_def_prop(ctx, -3,
|
|
DUK_DEFPROP_HAVE_VALUE |
|
|
DUK_DEFPROP_HAVE_WRITABLE |
|
|
DUK_DEFPROP_HAVE_ENUMERABLE |
|
|
DUK_DEFPROP_ENUMERABLE |
|
|
DUK_DEFPROP_HAVE_CONFIGURABLE);
|
|
duk_pop(ctx);
|
|
%}
|
|
|
|
getter Window::document()
|
|
%{
|
|
NSLOG(netsurf, DEBUG, "priv=%p", priv);
|
|
dom_document *doc = priv->htmlc->document;
|
|
dukky_push_node(ctx, (struct dom_node *)doc);
|
|
return 1;
|
|
%}
|
|
|
|
getter Window::window()
|
|
%{
|
|
duk_push_this(ctx);
|
|
return 1;
|
|
%}
|
|
|
|
getter Window::console()
|
|
%{
|
|
duk_push_this(ctx);
|
|
duk_get_prop_string(ctx, -1, MAGIC(Console));
|
|
if (duk_is_undefined(ctx, -1)) {
|
|
duk_pop(ctx);
|
|
if (dukky_create_object(ctx, PROTO_NAME(CONSOLE), 0) != DUK_EXEC_SUCCESS) {
|
|
return duk_error(ctx, DUK_ERR_ERROR, "Unable to create console object");
|
|
}
|
|
duk_dup(ctx, -1);
|
|
duk_put_prop_string(ctx, -3, MAGIC(Console));
|
|
}
|
|
return 1;
|
|
%}
|
|
|
|
getter Window::location()
|
|
%{
|
|
/* obtain location object for this window (if it exists) */
|
|
duk_push_this(ctx);
|
|
duk_get_prop_string(ctx, -1, MAGIC(Location));
|
|
if (duk_is_undefined(ctx, -1)) {
|
|
/* location object did not previously exist so create it */
|
|
duk_pop(ctx);
|
|
|
|
duk_push_pointer(ctx, llcache_handle_get_url(priv->htmlc->base.llcache));
|
|
|
|
if (dukky_create_object(ctx, PROTO_NAME(LOCATION), 1) != DUK_EXEC_SUCCESS) {
|
|
return duk_error(ctx, DUK_ERR_ERROR, "Unable to create location object");
|
|
}
|
|
duk_dup(ctx, -1);
|
|
duk_put_prop_string(ctx, -3, MAGIC(Location));
|
|
}
|
|
return 1;
|
|
%}
|
|
|
|
getter Window::navigator()
|
|
%{
|
|
duk_push_this(ctx);
|
|
duk_get_prop_string(ctx, -1, MAGIC(Navigator));
|
|
if (duk_is_undefined(ctx, -1)) {
|
|
duk_pop(ctx);
|
|
|
|
if (dukky_create_object(ctx,
|
|
PROTO_NAME(NAVIGATOR),
|
|
0) != DUK_EXEC_SUCCESS) {
|
|
return duk_error(ctx,
|
|
DUK_ERR_ERROR,
|
|
"Unable to create navigator object");
|
|
}
|
|
duk_dup(ctx, -1);
|
|
duk_put_prop_string(ctx, -3, MAGIC(Navigator));
|
|
}
|
|
return 1;
|
|
%}
|
|
|
|
getter Window::name()
|
|
%{
|
|
const char *name;
|
|
browser_window_get_name(priv->win, &name);
|
|
duk_push_string(ctx, name);
|
|
return 1;
|
|
%}
|
|
|
|
setter Window::name()
|
|
%{
|
|
const char *name;
|
|
name = duk_to_string(ctx, -1);
|
|
browser_window_set_name(priv->win, name);
|
|
return 0;
|
|
%}
|
|
|
|
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;
|
|
%}
|
|
|
|
method Window::setTimeout()
|
|
%{
|
|
duk_idx_t argc = duk_get_top(ctx);
|
|
duk_int_t timeout = 10;
|
|
|
|
if (priv->closed_down == true) {
|
|
return 0; /* coerced to undefined */
|
|
}
|
|
|
|
if (argc >= 2) {
|
|
timeout = duk_get_int(ctx, 1);
|
|
}
|
|
/* func, [timeout, args...] */
|
|
if (timeout < 10) { timeout = 10; }
|
|
size_t handle = window_alloc_new_callback(ctx, priv, false, (int)timeout);
|
|
|
|
duk_push_int(ctx, (duk_int_t)handle);
|
|
return 1;
|
|
%}
|
|
|
|
method Window::setInterval()
|
|
%{
|
|
duk_idx_t argc = duk_get_top(ctx);
|
|
duk_int_t timeout = 10;
|
|
|
|
if (priv->closed_down == true) {
|
|
return 0; /* coerced to undefined */
|
|
}
|
|
|
|
if (argc >= 2) {
|
|
timeout = duk_get_int(ctx, 1);
|
|
}
|
|
/* func, [timeout, args...] */
|
|
if (timeout < 10) { timeout = 10; }
|
|
size_t handle = window_alloc_new_callback(ctx, priv, true, (int)timeout);
|
|
|
|
duk_push_int(ctx, (duk_int_t)handle);
|
|
return 1;
|
|
%}
|
|
|
|
method Window::clearTimeout()
|
|
%{
|
|
duk_int_t handle = duk_get_int(ctx, 0);
|
|
window_remove_callback_by_handle(ctx, priv, (size_t) handle);
|
|
|
|
return 0;
|
|
%}
|
|
|
|
method Window::clearInterval()
|
|
%{
|
|
duk_int_t handle = duk_get_int(ctx, 0);
|
|
window_remove_callback_by_handle(ctx, priv, (size_t) handle);
|
|
|
|
return 0;
|
|
%}
|
|
|
|
getter Window::onabort();
|
|
setter Window::onabort();
|
|
getter Window::onafterprint();
|
|
setter Window::onafterprint();
|
|
getter Window::onautocompleteerror();
|
|
setter Window::onautocompleteerror();
|
|
getter Window::onautocomplete();
|
|
setter Window::onautocomplete();
|
|
getter Window::onbeforeprint();
|
|
setter Window::onbeforeprint();
|
|
getter Window::onbeforeunload();
|
|
setter Window::onbeforeunload();
|
|
getter Window::onblur();
|
|
setter Window::onblur();
|
|
getter Window::oncancel();
|
|
setter Window::oncancel();
|
|
getter Window::oncanplaythrough();
|
|
setter Window::oncanplaythrough();
|
|
getter Window::oncanplay();
|
|
setter Window::oncanplay();
|
|
getter Window::onchange();
|
|
setter Window::onchange();
|
|
getter Window::onclick();
|
|
setter Window::onclick();
|
|
getter Window::onclose();
|
|
setter Window::onclose();
|
|
getter Window::oncontextmenu();
|
|
setter Window::oncontextmenu();
|
|
getter Window::oncuechange();
|
|
setter Window::oncuechange();
|
|
getter Window::ondblclick();
|
|
setter Window::ondblclick();
|
|
getter Window::ondragend();
|
|
setter Window::ondragend();
|
|
getter Window::ondragenter();
|
|
setter Window::ondragenter();
|
|
getter Window::ondragexit();
|
|
setter Window::ondragexit();
|
|
getter Window::ondragleave();
|
|
setter Window::ondragleave();
|
|
getter Window::ondragover();
|
|
setter Window::ondragover();
|
|
getter Window::ondragstart();
|
|
setter Window::ondragstart();
|
|
getter Window::ondrag();
|
|
setter Window::ondrag();
|
|
getter Window::ondrop();
|
|
setter Window::ondrop();
|
|
getter Window::ondurationchange();
|
|
setter Window::ondurationchange();
|
|
getter Window::onemptied();
|
|
setter Window::onemptied();
|
|
getter Window::onended();
|
|
setter Window::onended();
|
|
getter Window::onerror();
|
|
setter Window::onerror();
|
|
getter Window::onfocus();
|
|
setter Window::onfocus();
|
|
getter Window::onhashchange();
|
|
setter Window::onhashchange();
|
|
getter Window::oninput();
|
|
setter Window::oninput();
|
|
getter Window::oninvalid();
|
|
setter Window::oninvalid();
|
|
getter Window::onkeydown();
|
|
setter Window::onkeydown();
|
|
getter Window::onkeypress();
|
|
setter Window::onkeypress();
|
|
getter Window::onkeyup();
|
|
setter Window::onkeyup();
|
|
getter Window::onlanguagechange();
|
|
setter Window::onlanguagechange();
|
|
getter Window::onloadeddata();
|
|
setter Window::onloadeddata();
|
|
getter Window::onloadedmetadata();
|
|
setter Window::onloadedmetadata();
|
|
getter Window::onloadstart();
|
|
setter Window::onloadstart();
|
|
getter Window::onload();
|
|
setter Window::onload();
|
|
getter Window::onmessage();
|
|
setter Window::onmessage();
|
|
getter Window::onmousedown();
|
|
setter Window::onmousedown();
|
|
getter Window::onmouseenter();
|
|
setter Window::onmouseenter();
|
|
getter Window::onmouseleave();
|
|
setter Window::onmouseleave();
|
|
getter Window::onmousemove();
|
|
setter Window::onmousemove();
|
|
getter Window::onmouseout();
|
|
setter Window::onmouseout();
|
|
getter Window::onmouseover();
|
|
setter Window::onmouseover();
|
|
getter Window::onmouseup();
|
|
setter Window::onmouseup();
|
|
getter Window::onoffline();
|
|
setter Window::onoffline();
|
|
getter Window::ononline();
|
|
setter Window::ononline();
|
|
getter Window::onpagehide();
|
|
setter Window::onpagehide();
|
|
getter Window::onpageshow();
|
|
setter Window::onpageshow();
|
|
getter Window::onpause();
|
|
setter Window::onpause();
|
|
getter Window::onplaying();
|
|
setter Window::onplaying();
|
|
getter Window::onplay();
|
|
setter Window::onplay();
|
|
getter Window::onpopstate();
|
|
setter Window::onpopstate();
|
|
getter Window::onprogress();
|
|
setter Window::onprogress();
|
|
getter Window::onratechange();
|
|
setter Window::onratechange();
|
|
getter Window::onreset();
|
|
setter Window::onreset();
|
|
getter Window::onresize();
|
|
setter Window::onresize();
|
|
getter Window::onscroll();
|
|
setter Window::onscroll();
|
|
getter Window::onseeked();
|
|
setter Window::onseeked();
|
|
getter Window::onseeking();
|
|
setter Window::onseeking();
|
|
getter Window::onselect();
|
|
setter Window::onselect();
|
|
getter Window::onshow();
|
|
setter Window::onshow();
|
|
getter Window::onsort();
|
|
setter Window::onsort();
|
|
getter Window::onstalled();
|
|
setter Window::onstalled();
|
|
getter Window::onstorage();
|
|
setter Window::onstorage();
|
|
getter Window::onsubmit();
|
|
setter Window::onsubmit();
|
|
getter Window::onsuspend();
|
|
setter Window::onsuspend();
|
|
getter Window::ontimeupdate();
|
|
setter Window::ontimeupdate();
|
|
getter Window::ontoggle();
|
|
setter Window::ontoggle();
|
|
getter Window::onunload();
|
|
setter Window::onunload();
|
|
getter Window::onvolumechange();
|
|
setter Window::onvolumechange();
|
|
getter Window::onwaiting();
|
|
setter Window::onwaiting();
|
|
getter Window::onwheel();
|
|
setter Window::onwheel();
|