summaryrefslogtreecommitdiff
path: root/appl/charon/jscript.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/charon/jscript.b')
-rw-r--r--appl/charon/jscript.b3025
1 files changed, 3025 insertions, 0 deletions
diff --git a/appl/charon/jscript.b b/appl/charon/jscript.b
new file mode 100644
index 00000000..ad35f295
--- /dev/null
+++ b/appl/charon/jscript.b
@@ -0,0 +1,3025 @@
+implement JScript;
+
+include "common.m";
+include "ecmascript.m";
+
+ES: Ecmascript;
+ Exec, Obj, Call, Prop, Val, Ref, RefVal, Builtin, ReadOnly: import ES;
+me: ESHostobj;
+
+# local copies from CU
+sys: Sys;
+CU: CharonUtils;
+
+D: Draw;
+S: String;
+T: StringIntTab;
+C: Ctype;
+B: Build;
+ Item: import B;
+CH: Charon;
+L: Layout;
+ Frame, Control: import L;
+U: Url;
+ Parsedurl: import U;
+E: Events;
+ Event, ScriptEvent: import E;
+
+G : Gui;
+
+JScript: module
+{
+ # First, conform to Script interface
+ defaultStatus: string;
+ jevchan: chan of ref ScriptEvent;
+ versions: array of string;
+
+ init: fn(cu: CharonUtils): string;
+ frametreechanged: fn(top: ref Layout->Frame);
+ havenewdoc: fn(f: ref Layout->Frame);
+ evalscript: fn(f: ref Layout->Frame, s: string) : (string, string, string);
+ framedone: fn(f : ref Layout->Frame, hasscripts : int);
+
+ #
+ # implement the host object interface, too
+ #
+ get: fn(ex: ref Exec, o: ref Obj, property: string): ref Val;
+ put: fn(ex: ref Exec, o: ref Obj, property: string, val: ref Val);
+ canput: fn(ex: ref Exec, o: ref Obj, property: string): ref Val;
+ hasproperty: fn(ex: ref Exec, o: ref Obj, property: string): ref Val;
+ delete: fn(ex: ref Exec, o: ref Obj, property: string);
+ defaultval: fn(ex: ref Exec, o: ref Obj, tyhint: int): ref Val;
+ call: fn(ex: ref Exec, func, this: ref Obj, args: array of ref Val, eval: int): ref Ref;
+ construct: fn(ex: ref Exec, func: ref Obj, args: array of ref Val): ref Obj;
+};
+
+versions = array [] of {
+ "javascript",
+ "javascript1.0",
+ "javascript1.1",
+ "javascript1.2",
+ "javascript1.3",
+};
+
+# Call init() before calling anything else.
+# It makes a global object (a Window) for the browser's top level frame,
+# and also puts a navaigator object in it. The document can't be filled
+# in until the first document gets loaded.
+#
+# This module keeps track of the correspondence between the Script Window
+# objects and the corresponding Layout Frames, using the ScriptWin adt to
+# build a tree mirroring the structure. The root of the tree never changes
+# after first being set (but changing its document essentially resets all of the
+# other data structures). After charon has built its top-level window, it
+# should call frametreechanged(top).
+#
+# When a frame gets reset or gets some frame children added, call frametreechanged(f),
+# where f is the changed frame. This module will update its ScriptWin tree as needed.
+#
+# Whenever the document in a (Layout) Frame f changes, call havenewdoc(f)
+# after the frame's doc field is set. This causes this module to initialize the document
+# object in the corresponding window object.
+#
+# From within the build process, call evalscript(containing frame, script) to evaluate
+# global code fragments as needed. The return value is two strings: a possible error
+# description, and HTML that is the result of a document.write (so it should be spliced
+# in at the point where the <SCRIPT> element occurred). evalscript() also handles
+# the job of synching up the Docinfo data (on the Build side) with the document object
+# (on the Script side).
+#
+# For use by other external routines, the xfertoscriptobjs() and xferfromscriptobjs()
+# functions that do the just-described synch-up are available for external callers.
+
+# Adt for keeping track of correspondence between Image objects
+# and their corresponding Build items.
+ScriptImg: adt
+{
+ item: ref Build->Item.Iimage;
+ obj: ref Obj;
+};
+
+ScriptForm : adt {
+ form : ref Build->Form;
+ obj : ref Obj;
+ ix : int; # index in document.forms array
+ fields : list of (ref Build->Formfield, ref Obj);
+};
+
+# Adt for keeping track of correspondence between Window
+# objects and their corresponding Frames.
+
+ScriptWin: adt
+{
+ frame: ref Layout->Frame;
+ ex: ref Exec; # ex.global is frame's Window obj
+ locobj: ref Obj; # Location object for window
+ val : ref Val; # val of ex.global - used to side-effect entry in parent.frames[]
+
+ parent: ref ScriptWin;
+ forms: list of ref ScriptForm; # no guaranteed order
+ kids: cyclic list of ref ScriptWin;
+ imgs: list of ref ScriptImg;
+ newloc: string; # url to go to after script finishes executing
+ newloctarg: string; # target frame for newloc
+ docwriteout: string;
+ inbuild: int;
+ active: int; # frame or sub-frame has scripts
+ error: int;
+ imgrelocs: list of ref Obj;
+
+ new: fn(f: ref Layout->Frame, ex: ref Exec, loc: ref Obj, par: ref ScriptWin) : ref ScriptWin;
+ addkid: fn(sw: self ref ScriptWin, f: ref Layout->Frame);
+ dummy: fn(): ref ScriptWin;
+# findbyframe: fn(sw: self ref ScriptWin, f: ref Layout->Frame) : ref ScriptWin;
+ findbyframeid: fn(sw: self ref ScriptWin, fid: int) : ref ScriptWin;
+ findbydoc: fn(sw: self ref ScriptWin, d: ref Build->Docinfo) : ref ScriptWin;
+ findbyobj: fn(sw : self ref ScriptWin, obj : ref Obj) : ref ScriptWin;
+ findbyname: fn(sw : self ref ScriptWin, name : string) : ref ScriptWin;
+};
+
+opener: ref ScriptWin;
+winclose: int;
+
+# Helper adts for initializing objects.
+# Methods go in prototype, properties go in objects
+
+MethSpec: adt
+{
+ name: string;
+ args: array of string;
+};
+
+
+IVundef, IVnull, IVtrue, IVfalse, IVnullstr, IVzero, IVzerostr, IVarray: con iota;
+
+PropSpec: adt
+{
+ name: string;
+ attr: int;
+ initval: int; # one of IVnull, etc.
+};
+
+ObjSpec: adt
+{
+ name: string;
+ methods: array of MethSpec;
+ props: array of PropSpec;
+};
+
+MimeSpec: adt
+{
+ description: string;
+ suffixes: string;
+ ty: string;
+};
+
+# Javascript 1.1 (Netscape 3) client objects
+
+objspecs := array[] of {
+ ObjSpec("Anchor",
+ nil,
+ array[] of {PropSpec
+ ("name", ReadOnly, IVnullstr) }
+ ),
+ ObjSpec("Applet",
+ nil,
+ nil
+ ),
+ ObjSpec("document",
+ array[] of {MethSpec
+ ("close", nil),
+ ("open", array[] of { "mimetype", "replace" }),
+ ("write", array[] of { "string" }),
+ ("writeln", array[] of { "string" }) },
+ array[] of {PropSpec
+ ("alinkColor", 0, IVnullstr),
+ ("anchors", ReadOnly, IVarray),
+ ("applets", ReadOnly, IVarray),
+ ("bgColor", 0, IVnullstr),
+ ("cookie", 0, IVnullstr),
+ ("domain", 0, IVnullstr),
+ ("embeds", ReadOnly, IVarray),
+ ("fgColor", 0, IVnullstr),
+ ("forms", ReadOnly, IVarray),
+ ("images", ReadOnly, IVarray),
+ ("lastModified", ReadOnly, IVnullstr),
+ ("linkColor", 0, IVnullstr),
+ ("links", ReadOnly, IVarray),
+ ("location", 0, IVnullstr),
+ ("plugins", ReadOnly, IVarray),
+ ("referrer", ReadOnly, IVnullstr),
+ ("title", ReadOnly, IVnullstr),
+ ("URL", ReadOnly, IVnullstr),
+ ("vlinkColor", 0, IVnullstr) }
+ ),
+ ObjSpec("Form",
+ array[] of {MethSpec
+ ("reset", nil),
+ ("submit", nil) },
+ array[] of {PropSpec
+ ("action", 0, IVnullstr),
+ ("elements", ReadOnly, IVarray),
+ ("encoding", 0, IVnullstr),
+ ("length", ReadOnly, IVzero),
+ ("method", 0, IVnullstr),
+ ("name", 0, IVnullstr),
+ ("target", 0, IVnullstr) }
+ ),
+ # This is merge of Netscape objects (to save code & data space):
+ # Button, Checkbox, Hidden, Radio, Reset, Select, Text, and Textarea
+ ObjSpec("FormField",
+ array[] of {MethSpec
+ ("blur", nil),
+ ("click", nil),
+ ("focus", nil),
+ ("select", nil) },
+ array[] of {PropSpec
+ ("checked", 0, IVundef),
+ ("defaultChecked", 0, IVundef),
+ ("defaultValue", 0, IVundef),
+ ("form", ReadOnly, IVundef),
+ ("length", 0, IVundef),
+ ("name", 0, IVnullstr),
+ ("options", 0, IVundef),
+ ("type", ReadOnly, IVundef),
+ ("selectedIndex", 0, IVundef),
+ ("value", 0, IVnullstr) }
+ ),
+ ObjSpec("History",
+ array[] of {MethSpec
+ ("back", nil),
+ ("forward", nil),
+ ("go", array[] of { "location-or-delta" }) },
+ array[] of {PropSpec
+ ("current", ReadOnly, IVnullstr),
+ ("length", ReadOnly, IVzero),
+ ("next", ReadOnly, IVnullstr),
+ ("previous", ReadOnly, IVnullstr) }
+ ),
+ ObjSpec("Image",
+ nil,
+ array[] of {PropSpec
+ ("border", ReadOnly, IVzerostr),
+ ("complete", ReadOnly, IVfalse),
+ ("height", ReadOnly, IVzerostr),
+ ("hspace", ReadOnly, IVzerostr),
+ ("lowsrc", 0, IVnullstr),
+ ("name", ReadOnly, IVnullstr),
+ ("src", 0, IVnullstr),
+ ("vspace", ReadOnly, IVzerostr),
+ ("width", ReadOnly, IVzerostr) }
+ ),
+ ObjSpec("Link",
+ nil,
+ array[] of {PropSpec
+ ("hash", 0, IVnullstr),
+ ("host", 0, IVnullstr),
+ ("hostname", 0, IVnullstr),
+ ("href", 0, IVnullstr),
+ ("pathname", 0, IVnullstr),
+ ("port", 0, IVnullstr),
+ ("protocol", 0, IVnullstr),
+ ("search", 0, IVnullstr),
+ ("target", 0, IVnullstr) }
+ ),
+ ObjSpec("Location",
+ array[] of {MethSpec
+ ("reload", array[] of { "forceGet" }),
+ ("replace", array[] of { "URL" }) },
+ array[] of {PropSpec
+ ("hash", 0, IVnullstr),
+ ("host", 0, IVnullstr),
+ ("hostname", 0, IVnullstr),
+ ("href", 0, IVnullstr),
+ ("pathname", 0, IVnullstr),
+ ("port", 0, IVnullstr),
+ ("protocol", 0, IVnullstr),
+ ("search", 0, IVnullstr) }
+ ),
+ ObjSpec("MimeType",
+ nil,
+ array[] of {PropSpec
+ ("description", ReadOnly, IVnullstr),
+ ("enabledPlugin", ReadOnly, IVnull),
+ ("suffixes", ReadOnly, IVnullstr),
+ ("type", ReadOnly, IVnullstr) }
+ ),
+ ObjSpec("Option",
+ nil,
+ array[] of {PropSpec
+ ("defaultSelected", 0, IVfalse),
+ ("index", 0, IVundef),
+ ("selected", 0, IVfalse),
+ ("text", 0, IVnullstr),
+ ("value", 0, IVnullstr) }
+ ),
+ ObjSpec("navigator",
+ array[] of {MethSpec
+ ("javaEnabled", nil),
+ ("plugins.refresh", nil),
+ ("taintEnabled", nil) },
+ array[] of {PropSpec
+ ("appCodeName", ReadOnly, IVnullstr),
+ ("appName", ReadOnly, IVnullstr),
+ ("appVersion", ReadOnly, IVnullstr),
+ ("mimeTypes", ReadOnly, IVarray),
+ ("platform", ReadOnly, IVnullstr),
+ ("plugins", ReadOnly, IVarray),
+ ("userAgent", ReadOnly, IVnullstr) }
+ ),
+ ObjSpec("Plugin",
+ nil,
+ array[] of {PropSpec
+ ("description", 0, IVnullstr),
+ ("filename", 0, IVnullstr),
+ ("length", 0, IVzero),
+ ("name", 0, IVnullstr) }
+ ),
+ ObjSpec("Screen",
+ nil,
+ array[] of {PropSpec
+ ("availHeight", ReadOnly, IVzero),
+ ("availWidth", ReadOnly, IVzero),
+ ("availLeft", ReadOnly, IVzero),
+ ("availTop", ReadOnly, IVzero),
+ ("colorDepth", ReadOnly, IVzero),
+ ("pixelDepth", ReadOnly, IVzero),
+ ("height", ReadOnly, IVzero),
+ ("width", ReadOnly, IVzero) }
+ ),
+ ObjSpec("Window",
+ array[] of {MethSpec
+ ("alert", array[] of { "msg" }),
+ ("blur", nil),
+ ("clearInterval", array[] of { "intervalid" }),
+ ("clearTimeout", array[] of { "timeoutid" }),
+ ("close", nil),
+ ("confirm", array[] of { "msg" }),
+ ("focus", nil),
+ ("moveBy", array[] of { "dx", "dy" }),
+ ("moveTo", array[] of { "x", "y" }),
+ ("open", array[] of { "url", "winname", "winfeatures" }),
+ ("prompt", array[] of { "msg", "inputdflt" }),
+ ("resizeBy", array[] of { "dh", "dw" }),
+ ("resizeTo", array[] of { "width", "height" }),
+ ("scroll", array[] of { "x", "y" }),
+ ("scrollBy", array[] of { "dx", "dy" }),
+ ("scrollTo", array[] of { "x", "y" }),
+ ("setInterval", array[] of { "code", "msec" }),
+ ("setTimeout", array[] of { "expr", "msec" }) },
+ array[] of {PropSpec
+ ("closed", ReadOnly, IVfalse),
+ ("defaultStatus", 0, IVnullstr),
+ ("document", 0, IVnull),
+ ("frames", ReadOnly, IVnull), # array, really
+ ("history", 0, IVnull), # array, really
+ ("length", ReadOnly, IVzero),
+ ("location", 0, IVnullstr),
+# ("Math", ReadOnly, IVnull),
+ ("name", 0, IVnullstr),
+ ("navigator", ReadOnly, IVnull),
+ ("offscreenBuffering", 0, IVnullstr),
+ ("opener", 0, IVnull),
+ ("parent", ReadOnly, IVnull),
+ ("screen", 0, IVnull),
+ ("self", ReadOnly, IVnull),
+ ("status", 0, IVnullstr),
+ ("top", ReadOnly, IVnull),
+ ("window", ReadOnly, IVnull) }
+ )
+};
+
+# Currently supported charon mime types
+mimespecs := array[] of {
+ MimeSpec("HTML",
+ "htm,html",
+ "text/html"
+ ),
+ MimeSpec("Plain text",
+ "txt,text",
+ "text/plain"
+ ),
+ MimeSpec("Gif Image",
+ "gif",
+ "image/gif"
+ ),
+ MimeSpec("Jpeg Image",
+ "jpeg,jpg,jpe",
+ "image/jpeg"
+ ),
+ MimeSpec("X Bitmap Image",
+ "",
+ "image/x-xbitmap"
+ )
+};
+
+# charon's 's' debug flag:
+# 1: basic syntax and runtime errors
+# 2: 'event' logging and DOM actions
+# 3: print parsed code and ops as executed
+# 4: print value of expression statements and abort on runtime errors
+dbg := 0;
+dbgdom := 0;
+
+top: ref ScriptWin;
+createdimages : list of ref Obj;
+nullstrval: ref Val;
+zeroval: ref Val;
+zerostrval: ref Val;
+
+# Call this after charon's main (top) frame has been built
+init(cu: CharonUtils) : string
+{
+ CU = cu;
+ sys = load Sys Sys->PATH;
+ D = load Draw Draw->PATH;
+ S = load String String->PATH;
+ T = load StringIntTab StringIntTab->PATH;
+ U = load Url Url->PATH;
+ if (U != nil)
+ U->init();
+ C = cu->C;
+ B = cu->B;
+ L = cu->L;
+ E = cu->E;
+ CH = cu->CH;
+ G = cu->G;
+ dbg = int (CU->config).dbg['s'];
+ if (dbg > 1)
+ dbgdom = 1;
+ ES = load Ecmascript Ecmascript->PATH;
+ if(ES == nil)
+ return sys->sprint("could not load module %s: %r", Ecmascript->PATH);
+ err := ES->init();
+ if (err != nil)
+ return sys->sprint("ecmascript error: %s", err);
+
+ me = load ESHostobj SELF;
+ if(me == nil)
+ return sys->sprint("jscript: could not load self as a ESHostobj: %r");
+ if(dbg >= 3) {
+ ES->debug['p'] = 1; # print parsed code
+ ES->debug['e'] = 1; # prinv ops as they are executed
+ if(dbg >= 4) {
+ ES->debug['e'] = 2;
+ ES->debug['v'] = 1; # print value of expression statements
+ ES->debug['r'] = 1; # print and abort if runtime errors
+ }
+ }
+
+ # some constant values, for initialization
+ nullstrval = ES->strval("");
+ zeroval = ES->numval(0.);
+ zerostrval = ES->strval("0");
+ jevchan = chan of ref ScriptEvent;
+ spawn jevhandler();
+ return nil;
+}
+
+doneevent := ScriptEvent(-1,0,0,0,0,0,0,0,0,nil,nil,0);
+
+# Used to receive and act upon ScriptEvents from main charon thread.
+# Want to queue the events up, so that the main thread doesn't have
+# to wait, and spawn off a do_on, one at a time, so that they don't
+# interfere with each other.
+# When do_on is finished, it must send a copy of doneevent
+# so that jevhandler knows it can spawn another do_on.
+jevhandler()
+{
+ q := array[10] of ref ScriptEvent;
+ qhead := 0;
+ qtail := 0;
+ spawnok := 1;
+ for(;;) {
+ jev := <- jevchan;
+ if(jev.kind == -1)
+ spawnok = 1;
+ else
+ q[qtail++] = jev;
+ jev = nil;
+
+ # remove next event to process, if ok to and there is one
+ if(spawnok && qhead < qtail)
+ jev = q[qhead++];
+
+ # adjust queue to make sure there is room for next event
+ if(qhead == qtail) {
+ qhead = 0;
+ qtail = 0;
+ }
+ if(qtail == len q) {
+ if(qhead > 0) {
+ q[0:] = q[qhead:qtail];
+ qtail -= qhead;
+ qhead = 0;
+ }
+ else {
+ newq := array[len q + 10] of ref ScriptEvent;
+ newq[0:] = q;
+ q = newq;
+ }
+ }
+
+ # process next event, if any
+ if(jev != nil) {
+ spawnok = 0;
+ spawn do_on(jev);
+ }
+ }
+}
+
+# Create an execution context for the frame.
+# The global object of the frame is the frame's Window object.
+# Return the execution context and the Location object for the window.
+makeframeex(f : ref Layout->Frame) : (ref Exec, ref Obj)
+{
+ winobj := mkhostobj(nil, "Window");
+ ex := ES->mkexec(winobj);
+ winobj.prototype = ex.objproto;
+ winobj.prototype = mkprototype(ex, specindex("Window"));
+
+ navobj := mknavobj(ex);
+ reinitprop(winobj, "navigator", ES->objval(navobj));
+
+ histobj := mkhostobj(ex, "History");
+ (length, current, next, previous) := CH->histinfo();
+ reinitprop(histobj, "current", ES->strval(current));
+ reinitprop(histobj, "length", ES->numval(real length));
+ reinitprop(histobj, "next", ES->strval(next));
+ reinitprop(histobj, "previous", ES->strval(previous));
+ ES->put(ex, winobj, "history", ES->objval(histobj));
+
+ locobj := mkhostobj(ex, "Location");
+ src : ref U->Parsedurl;
+ di := f.doc;
+ if (di != nil && di.src != nil) {
+ src = di.src;
+ reinitprop(locobj, "hash", ES->strval("#" + src.frag));
+ reinitprop(locobj, "host", ES->strval(src.host + ":" + src.port));
+ reinitprop(locobj, "hostname", ES->strval(src.host));
+ reinitprop(locobj, "href", ES->strval(src.tostring()));
+ reinitprop(locobj, "pathname", ES->strval(src.path));
+ reinitprop(locobj, "port", ES->strval(src.port));
+ reinitprop(locobj, "protocol", ES->strval(src.scheme + ":"));
+ reinitprop(locobj, "search", ES->strval("?" + src.query));
+ }
+ ES->put(ex, winobj, "location", ES->objval(locobj));
+
+ scrobj := mkhostobj(ex, "Screen");
+ scr := (CU->G->display).image;
+ scrw := D->(scr.r.dx)();
+ scrh := D->(scr.r.dy)();
+ reinitprop(scrobj, "availHeight", ES->numval(real scrh));
+ reinitprop(scrobj, "availWidth", ES->numval(real scrw));
+ reinitprop(scrobj, "availLeft", ES->numval(real scr.r.min.x));
+ reinitprop(scrobj, "availTop", ES->numval(real scr.r.min.y));
+ reinitprop(scrobj, "colorDepth", ES->numval(real scr.depth));
+ reinitprop(scrobj, "pixelDepth", ES->numval(real scr.depth));
+ reinitprop(scrobj, "height", ES->numval(real scrh));
+ reinitprop(scrobj, "width", ES->numval(real scrw));
+ ES->put(ex, winobj, "screen", ES->objval(scrobj));
+
+ # make the non-core constructor objects
+# improto := mkprototype(ex, specindex("Image"));
+ o := ES->biinst(winobj, Builtin("Image", "Image", array[] of {"width", "height"}, 2),
+ ex.funcproto, me);
+ o.construct = o.call;
+
+ o = ES->biinst(winobj, Builtin("Option", "Option", array[] of {"text", "value", "defaultSelected", "selected"}, 4),
+ ex.funcproto, me);
+ o.construct = o.call;
+ defaultStatus = "";
+ return (ex, locobj);
+}
+
+mknavobj(ex: ref Exec) : ref Obj
+{
+ navobj := mkhostobj(ex, "navigator");
+ reinitprop(navobj, "appCodeName", ES->strval("Mozilla"));
+ reinitprop(navobj, "appName", ES->strval("Netscape"));
+# reinitprop(navobj, "appVersion", ES->strval("3.0 (Inferno, U)"));
+# reinitprop(navobj, "userAgent", ES->strval("Mozilla/3.0 (Inferno; U)"));
+ reinitprop(navobj, "appVersion", ES->strval("4.08 (Charon; Inferno)"));
+ reinitprop(navobj, "userAgent", ES->strval("Mozilla/4.08 (Charon; Inferno)"));
+
+ omty := getobj(ex, navobj, "mimeTypes");
+ for(i := 0; i < len mimespecs; i++) {
+ sp := mimespecs[i];
+ v := mkhostobj(ex, "MimeType");
+ reinitprop(v, "description", ES->strval(sp.description));
+ reinitprop(v, "suffixes", ES->strval(sp.suffixes));
+ reinitprop(v, "type", ES->strval(sp.ty));
+ arrayput(ex, omty, i, sp.ty, ES->objval(v));
+ }
+ return navobj;
+}
+
+# Something changed in charon's frame tree
+frametreechanged(t: ref Layout->Frame)
+{
+ rebuild : ref ScriptWin;
+ if (top == nil) {
+ (ex, loc) := makeframeex(t);
+ top = ScriptWin.new(t, ex, loc, nil);
+ rebuild = top;
+ } else {
+ rebuild = top.findbyframeid(t.id);
+ # t could be new frame - need to look for parent
+ while (rebuild == nil && t.parent != nil) {
+ t = t.parent;
+ rebuild = top.findbyframeid(t.id);
+ }
+ # if we haven't found it by now, it's not in the official Frame
+ # hierarchy, so ignore it
+ }
+ if (rebuild != nil)
+ wininstant(rebuild);
+}
+
+# Frame f has just been reset, then given a new doc field
+# (with initial values for src, base, refresh, chset).
+# We'll defer doing any actual building of the script objects
+# until an evalscript; that way, pages that don't use scripts
+# incur minimum penalties).
+havenewdoc(f: ref Layout->Frame)
+{
+ sw := top.findbyframeid(f.id);
+ if(sw != nil) {
+ sw.inbuild = 1;
+ sw.forms = nil;
+ (sw.ex, sw.locobj) = makeframeex(f);
+ if (sw.val != nil)
+ # global object is referenced via parent.frames array
+ sw.val.obj = sw.ex.global;
+ wininstant(sw);
+ }
+}
+
+# returns (error, output, value)
+# error: error message
+# output: result of any document.writes
+# value: value of last statement executed (used for handling "javascript:" URL scheme)
+#
+evalscript(f: ref Layout->Frame, s: string) : (string, string, string)
+{
+ if (top.error)
+ return("scripts disabled for this document", "", "");
+ sw := top.findbyframeid(f.id);
+ if (sw == nil)
+ return("cannot find script window", "", "");
+ if (sw.ex == nil)
+ return("script window has no execution context", "", "");
+ if(sw == nil || sw.ex == nil)
+ return ("", "", "");
+
+ ex := sw.ex;
+ sw.docwriteout = "";
+ expval := "";
+ createdimages = nil;
+ {
+ xfertoscriptobjs(f, 1);
+ if(s != "") {
+ ex.error = nil;
+ c := ES->eval(ex, s);
+ if (c.kind == ES->CThrow && dbg) {
+ sys->print("unhandled error:\n\tvalue:%s\n\treason:%s\n",
+ ES->toString(ex, c.val), ex.error);
+ sys->print("%s\n", s);
+ }
+ if (c.kind == ES->CNormal && c.val != nil) {
+ if (ES->isstr(c.val))
+ expval = c.val.str;
+ }
+ xferfromscriptobjs(f, 1);
+ checknewlocs(top);
+ checkopener();
+ }
+ w := sw.docwriteout;
+ sw.docwriteout = nil;
+ return("", w, expval);
+ }exception exc{
+ "*" =>
+ if(dbg) {
+ sys->print("fatal error %q executing evalscript: %s\nscript=", exc, ex.error);
+ sa := array of byte s;
+ sys->write(sys->fildes(1), sa, len sa);
+ sys->print("\n");
+ }
+ top.error = 1;
+ emsg := "Fatal error processing script\n\nScript processing suspended for this page";
+ G->alert(emsg);
+ w := sw.docwriteout;
+ sw.docwriteout = nil;
+ return (ex.error, w, "");
+ }
+}
+
+xfertoscriptobjs(f: ref Layout->Frame, inbuild: int)
+{
+ sw := top.findbyframeid(f.id);
+ if(sw == nil)
+ return;
+ ex := sw.ex;
+ ow := ex.global;
+ di := f.doc;
+
+ for(el := di.events; el != nil; el = tl el) {
+ e := hd el;
+ hname := "";
+ dhname := "";
+ case e.attid {
+ Lex->Aonblur =>
+ hname = "onblur";
+ di.evmask |= E->SEonblur;
+ Lex->Aonerror =>
+ hname = "onerror";
+ di.evmask |= E->SEonerror;
+ Lex->Aonfocus =>
+ hname = "onfocus";
+ di.evmask |= E->SEonfocus;
+ Lex->Aonload =>
+ hname = "onload";
+ di.evmask |= E->SEonload;
+ Lex->Aonresize =>
+ hname = "onresize";
+ di.evmask |= E->SEonresize;
+ Lex->Aonunload =>
+ hname = "onunload";
+ di.evmask |= E->SEonunload;
+ Lex->Aondblclick =>
+ dhname = "ondblclick";
+ di.evmask |= E->SEondblclick;
+ Lex->Aonkeydown =>
+ dhname = "onkeydown";
+ di.evmask |= E->SEonkeydown;
+ Lex->Aonkeypress =>
+ dhname = "onkeypress";
+ di.evmask |= E->SEonkeypress;
+ Lex->Aonkeyup =>
+ dhname = "onkeyup";
+ di.evmask |= E->SEonkeyup;
+ Lex->Aonmousedown =>
+ dhname = "onmousedown";
+ di.evmask |= E->SEonmousedown;
+ Lex->Aonmouseup =>
+ dhname = "onmouseup";
+ di.evmask |= E->SEonmouseup;
+ }
+ if(hname != "")
+ puthandler(ex, ow, hname, e.value);
+ if(dhname != ""){
+ od := getobj(ex, ow, "document");
+ if(od == nil) {
+ reinitprop(ow, "document", docinstant(ex, f));
+ od = getobj(ex, ow, "document");
+ }
+ puthandler(ex, od, dhname, e.value);
+ }
+ }
+ di.events = nil;
+
+ od := getobj(ex, ow, "document");
+ if(od == nil) {
+ reinitprop(ow, "document", docinstant(ex, f));
+ od = getobj(ex, ow, "document");
+ CU->assert(od != nil);
+ }
+ else if(inbuild) {
+ docfill(ex, od, f);
+ ES->put(ex, od, "location", ES->objval(sw.locobj));
+ }
+ for(frml := sw.forms; frml != nil; frml = tl frml) {
+ frm := hd frml;
+ for (fldl := frm.fields; fldl != nil; fldl = tl fldl) {
+ (fld, ofield) := hd fldl;
+ if (ofield == nil)
+ continue;
+ if(fld.ctlid >= 0 && fld.ctlid < len f.controls) {
+ pick c := f.controls[fld.ctlid] {
+ Centry =>
+ reinitprop(ofield, "value", ES->strval(c.s));
+ Ccheckbox =>
+ cv := ES->false;
+ if(c.flags&Layout->CFactive)
+ cv = ES->true;
+ reinitprop(ofield, "checked", cv);
+ Cselect =>
+ for(i := 0; i < len c.options; i++) {
+ if(c.options[i].selected) {
+ reinitprop(ofield, "selectedIndex", ES->numval(real i));
+ # hack for common mistake in scripts
+ # (implemented by other browsers)
+ opts := getobj(ex, ofield, "options");
+ if (opts != nil)
+ reinitprop(opts, "selectedIndex", ES->numval(real i));
+ }
+ }
+ }
+ }
+ }
+ }
+ for(sil := sw.imgs; sil != nil; sil = tl sil) {
+ si := hd sil;
+ if(si.item.ci.complete != 0)
+ reinitprop(si.obj, "complete", ES->true);
+ }
+}
+
+xferfromscriptobjs(f: ref Layout->Frame, inbuild: int)
+{
+ sw := top.findbyframeid(f.id);
+ if(sw == nil)
+ return;
+ ex := sw.ex;
+ ow := ex.global;
+ od := getobj(ex, ow, "document");
+ if(od != nil) {
+ if(inbuild) {
+ di := f.doc;
+ di.doctitle = strxfer(ex, od, "title", di.doctitle);
+ di.background.color = colorxfer(ex, od, "bgColor", di.background.color);
+ di.text = colorxfer(ex, od, "fgColor", di.text);
+ di.alink = colorxfer(ex, od, "alinkColor", di.alink);
+ di.link = colorxfer(ex, od, "linkColor", di.link);
+ di.vlink = colorxfer(ex, od, "vlinkColor", di.vlink);
+ if(createdimages != nil) {
+ for(oil := createdimages; oil != nil; oil = tl oil) {
+ oi := hd oil;
+ vsrc := ES->get(ex, oi, "src");
+ if(ES->isstr(vsrc)) {
+ u := U->parse(vsrc.str);
+ if(u.path != "") {
+ u = U->mkabs(u, di.base);
+ it := Item.newimage(di, u, nil, "", B->Anone,
+ 0, 0, 0, 0, 0, 0, 0, nil, nil, nil);
+ di.images = it :: di.images;
+ }
+ }
+ }
+ }
+ }
+ else {
+ for (ol := sw.imgrelocs; ol != nil; ol = tl ol) {
+ oi := hd ol;
+ vnewsrc := ES->get(ex, oi, "src");
+ if(ES->isstr(vnewsrc) && vnewsrc.str != nil) {
+ for(sil := sw.imgs; sil != nil; sil = tl sil) {
+ si := hd sil;
+ if(si.obj == oi) {
+ f.swapimage(si.item, vnewsrc.str);
+ break;
+ }
+ }
+ }
+ }
+ sw.imgrelocs = nil;
+ }
+ }
+}
+
+# Check ScriptWin tree for non-empty newlocs.
+# When found, generate a go event to the new place.
+# If found, don't recurse into children, because those
+# child frames are about to go away anyway.
+# Otherwise, recurse into all kids -- this might generate
+# multiple go events.
+# BUG: if multiple events are generated, later ones will
+# interrupt (STOP!) loading of pages specified by preceding
+# events. To fix, need to queue them up, probably in
+# main charon module.
+checknewlocs(sw: ref ScriptWin)
+{
+ if(sw.newloc != "") {
+ E->evchan <-= ref Event.Ego(sw.newloc, sw.newloctarg, 0, E->EGnormal);
+ sw.newloc = "";
+ }
+ else {
+ for(l := sw.kids; l != nil; l = tl l)
+ checknewlocs(hd l);
+ }
+}
+
+checkopener()
+{
+ if(opener != nil && opener.newloc != "") {
+ CH->sendopener(sys->sprint("L %s", opener.newloc)); # just location for now
+ opener.newloc = "";
+ }
+ if(winclose)
+ G->exitcharon();
+}
+
+# if e.anchorid >= 0 => target is Link
+# if e.fieldid > 0 => target is FormField (and e.formid > 0)
+# if e.formid > 0 => target is Form (e.fieldid == -1)
+# if e.imageid >= 0 => target is Image
+# otherwise => target is window
+do_on(e: ref ScriptEvent)
+{
+ if(dbgdom)
+ sys->print("do_on %d, frameid=%d, formid=%d, fieldid=%d, anchorid=%d, imageid=%d, x=%d, y=%d, which=%d\n",
+ e.kind, e.frameid, e.formid, e.fieldid, e.anchorid, e.imageid, e.x, e.y, e.which);
+ if (top.error) {
+ if (dbgdom)
+ sys->print("do_on() previous error prevents processing\n");
+ if (e.reply != nil)
+ e.reply <-= nil;
+ jevchan <-= ref doneevent;
+ return;
+ }
+ sw := top.findbyframeid(e.frameid);
+ # BUG FIX: Frame can be reset by Charon main thread
+ # between us getting its ref and using it
+ # WARNING - xferfromscriptobjs() will not update non-ref-type members of frame adt
+ # (currently not a problem) as updates will go to our copy
+ f : ref Frame;
+ if (sw != nil && !sw.inbuild) {
+ f = ref *sw.frame;
+ if (f.doc == nil)
+ f = nil;
+ }
+ if (f == nil) {
+ if(e.reply != nil)
+ e.reply <-= nil;
+ jevchan <-= ref doneevent;
+ if (dbgdom)
+ sys->print("do_on() failed to find frame %d\n", e.frameid);
+ return;
+ }
+ ex := sw.ex;
+ ow := ex.global;
+ od := getobj(ex, ow, "document");
+ sw.docwriteout = nil;
+
+{
+ # event target types
+ TAnchor, TForm, TFormField, TImage, TDocument, TWindow, Tnone: con iota;
+ ttype := Tnone;
+ target, oform: ref Obj;
+ if(e.anchorid >= 0) {
+ ttype = TAnchor;
+ target = getanchorobj(ex, e.frameid, e.anchorid);
+ } else if(e.formid > 0) {
+ oform = getformobj(ex, e.frameid, e.formid);
+ if(e.fieldid > 0) {
+ ttype = TFormField;
+ target = getformfieldobj(e.frameid, e.formid, e.fieldid);
+ } else {
+ ttype = TForm;
+ target = oform;
+ }
+ } else if(e.imageid >= 0) {
+ ttype = TImage;
+ target = getimageobj(ex, e.frameid, e.imageid);
+ } else if(e.kind == E->SEondblclick || e.kind == E->SEonkeydown ||
+ e.kind == E->SEonkeypress || e.kind == E->SEonkeyup ||
+ e.kind == E->SEonmousedown || e.kind == E->SEonmouseup){
+ ttype = TDocument;
+ target = od;
+ } else {
+ ttype = TWindow;
+ target = ow;
+ }
+ if(target != nil) {
+ oscript: ref Obj;
+ scrname := "";
+ case e.kind {
+ E->SEonabort =>
+ scrname = "onabort";
+ E->SEonblur =>
+ scrname = "onblur";
+ E->SEonchange =>
+ scrname = "onchange";
+ E->SEonclick =>
+ scrname = "onclick";
+ E->SEondblclick =>
+ scrname = "ondblclick";
+ E->SEonerror =>
+ scrname = "onerror";
+ E->SEonfocus =>
+ scrname = "onfocus";
+ E->SEonkeydown =>
+ scrname = "onkeydown";
+ E->SEonkeypress =>
+ scrname = "onkeypress";
+ E->SEonkeyup =>
+ scrname = "onkeyup";
+ E->SEonload =>
+ scrname = "onload";
+ E->SEonmousedown =>
+ scrname = "onmousedown";
+ E->SEonmouseout =>
+ scrname = "onmouseout";
+ E->SEonmouseover =>
+ scrname = "onmouseover";
+ E->SEonmouseup =>
+ scrname = "onmouseup";
+ E->SEonreset =>
+ scrname = "onreset";
+ E->SEonresize =>
+ scrname = "onresize";
+ E->SEonselect =>
+ scrname = "onselect";
+ E->SEonsubmit =>
+ scrname = "onsubmit";
+ E->SEonunload =>
+ scrname = "onunload";
+ E->SEtimeout or
+ E->SEinterval =>
+ oscript = dotimeout(ex, target, e);
+ E->SEscript =>
+ # TODO - handle document text from evalscript
+ # need to determine if document is 'open' or not.
+ (err, nil, val) := evalscript(f, e.script);
+ if (e.reply != nil)
+ e.reply <- = val;
+ e.reply = nil;
+ }
+ if(scrname != "")
+ oscript = getobj(ex, target, scrname);
+ if(oscript != nil) {
+ xfertoscriptobjs(f, 0);
+ if(dbgdom)
+ sys->print("calling script\n");
+ # establish scope chain per Rhino p. 287 (3rd edition)
+ oldsc := ex.scopechain;
+ sc := ow :: nil;
+ if(ttype != TWindow) {
+ sc = od :: sc;
+ if(ttype == TFormField)
+ sc = oform :: sc;
+ if(ttype != TDocument)
+ sc = target :: sc;
+ }
+ ex.scopechain = sc;
+ v := ES->call(ex, oscript, target, nil, 1).val;
+ # 'fix' for onsubmit
+ # JS references state that if the handler returns false
+ # then the action is cancelled.
+ # other browsers interpret this as "if and only if the handler
+ # returns false."
+ # When a function completes normally without returning a value
+ # its value is 'undefined', toBoolean(undefined) = false
+ if (v == ES->undefined)
+ v = ES->true;
+ else
+ v = ES->toBoolean(ex, v);
+ ex.scopechain = oldsc;
+ # onreset/onsubmit reply channel
+ if(e.reply != nil) {
+ ans : string;
+ if(v == ES->true)
+ ans = "true";
+ e.reply <-= ans;
+ e.reply = nil;
+ }
+ xferfromscriptobjs(f, 0);
+ checknewlocs(top);
+ checkopener();
+
+ if (ttype == TFormField && e.kind == E->SEonclick && v == ES->true)
+ E->evchan <-= ref Event.Eformfield(e.frameid, e.formid, e.fieldid, E->EFFclick);
+ if (ttype == TAnchor && e.kind == E->SEonclick && v == ES->true) {
+ gohref := getstr(ex, target, "href");
+ gotarget := getstr(ex, target, "target");
+ if (gotarget == "")
+ gotarget = "_self";
+ E->evchan <-= ref Event.Ego(gohref, gotarget, 0, E->EGnormal);
+ }
+ }
+ }
+ if(e.reply != nil)
+ e.reply <-= nil;
+ checkdocwrite(top);
+ jevchan <-= ref doneevent;
+}
+exception exc{
+ "*" =>
+ if (exc == "throw") {
+ # ignore ecmascript runtime errors
+ if(dbgdom)
+ sys->print("error executing 'on' handler: %s\n", ex.error);
+ } else {
+ # fatal error
+ top.error = 1;
+ emsg := "Fatal error in script ("+exc+"):\n" + ex.error + "\n";
+ G->alert(emsg);
+ }
+ if(e.reply != nil)
+ e.reply <-= nil;
+ jevchan <-= ref doneevent;
+ return;
+}
+}
+
+xferframeset(sw : ref ScriptWin)
+{
+ if (!sw.inbuild)
+ xfertoscriptobjs(sw.frame, 1);
+ for (k := sw.kids; k != nil; k = tl k)
+ xferframeset(hd k);
+}
+
+framedone(f : ref Frame, hasscripts : int)
+{
+ sw := top.findbyframeid(f.id);
+ if (sw != nil) {
+ if (!top.active && hasscripts) {
+ # need to transfer entire frame tree
+ # as one frame can reference objects in another
+ xferframeset(top);
+ }
+ sw.active |= hasscripts;
+ top.active |= hasscripts;
+ if (top.active)
+ xfertoscriptobjs(f, 1);
+ sw.inbuild = 0;
+ }
+}
+
+checkdocwrite(sw : ref ScriptWin) : int
+{
+ if (sw.inbuild)
+ return 0;
+
+ if (sw.docwriteout != nil) {
+ # The URL is bogus - not sure what the correct value should be
+ ev := ref Event.Esettext(sw.frame.id, sw.frame.src, sw.docwriteout);
+ sw.docwriteout = "";
+ E->evchan <- = ev;
+ return 1;
+ }
+ for (k := sw.kids; k != nil; k = tl k)
+ if (checkdocwrite(hd k))
+ break;
+ return 0;
+}
+
+#
+# interface for host objects
+#
+get(ex: ref Exec, o: ref Obj, property: string): ref Val
+{
+ if(o.class == "document" && property == "cookie") {
+ ans := "";
+ target := top.findbyobj(o);
+ if(target != nil) {
+ url := target.frame.doc.src;
+ ans = CU->getcookies(url.host, url.path, url.scheme == "https");
+ }
+ return ES->strval(ans);
+ }
+ if(o.class == "Window" && property == "opener"){
+ if(!CH->hasopener() || top.ex.global != o)
+ v := ES->undefined;
+ else{
+ if(opener == nil)
+ opener = ScriptWin.dummy();
+ v = ES->objval(opener.ex.global);
+ }
+ reinitprop(o, "opener", v);
+ }
+ return ES->get(ex, o, property);
+}
+
+
+put(ex: ref Exec, o: ref Obj, property: string, val: ref Val)
+{
+ if(dbgdom)
+ sys->print("put property %s in cobj of class %s\n", property, o.class);
+
+ url : ref Parsedurl;
+ target : ref ScriptWin;
+ str := ES->toString(ex, val);
+ ev := E->SEnone;
+
+ case o.class {
+ "Array" =>
+ # we 'host' the Formfield.options array so as we can
+ # track changes to the options list
+ vformfield := ES->get(ex, o, "@PRIVformfield");
+ if (!ES->isobj(vformfield))
+ # not one of our 'options' arrays
+ break;
+ ix := prop2index(property);
+ if (property != "length" && ix == -1)
+ # not a property that affects us
+ break;
+ oformfield := vformfield.obj;
+ oform := getobj(ex, oformfield, "form");
+ if (oform == nil)
+ break;
+ ES->put(ex, o, property, val);
+ if (ES->isobj(val) && val.obj.class == "Option") {
+ ES->put(ex, val.obj, "@PRIVformfield", vformfield);
+ ES->put(ex, val.obj, "form", ES->objval(oform));
+ reinitprop(val.obj, "index", ES->numval(real ix));
+ }
+ updateffopts(ex, oform, oformfield, ix);
+ return;
+ "Window" =>
+ case property {
+ "defaultStatus" or
+ "status" =>
+ if(ES->isstr(val)) {
+ if(property == "defaultStatus")
+ defaultStatus = val.str;
+ G->setstatus(val.str);
+ }
+ "location" =>
+ target = top.findbyobj(o);
+ if (target == nil)
+ break;
+ url = U->parse(str);
+ # TODO: be more defensive
+ url = U->mkabs(url, target.frame.doc.base);
+ "name" =>
+ sw := top.findbyobj(o);
+ if (sw == nil)
+ break;
+ name := ES->toString(ex, val);
+ if (sw.parent != nil) {
+ w := sw.parent.ex.global;
+ v := sw.val;
+ if (sw.frame.name != nil)
+ ES->delete(ex, w, sw.frame.name);
+ ES->varinstant(w, 0, name, ref RefVal(v));
+ }
+ # Window.name is used for determining TARGET of <A> etc.
+ # update Charon's Frame info so as new name gets used properly
+ sw.frame.name = name;
+ "offscreenBuffering" =>
+ if(ES->isstr(val) || val == ES->true || val == ES->false){
+ }
+ "onblur" =>
+ ev = E->SEonblur;
+ "onerror" =>
+ ev = E->SEonerror;
+ "onfocus" =>
+ ev = E->SEonfocus;
+ "onload" =>
+ ev = E->SEonload;
+ "onresize" =>
+ ev = E->SEonresize;
+ "onunload" =>
+ ev = E->SEonunload;
+ "opener" =>
+ ;
+ }
+ if(ev != E->SEnone) {
+ target = top.findbyobj(o);
+ if(target == nil)
+ break;
+ di := target.frame.doc;
+ if(!ES->isobj(val) || val.obj.call == nil)
+ di.evmask &= ~ev;
+ else
+ di.evmask |= ev;
+ }
+ "Link" =>
+ case property {
+ "onclick" =>
+ ev = E->SEonclick;
+ "ondblclick" =>
+ ev = E->SEondblclick;
+ "onkeydown" =>
+ ev = E->SEonkeydown;
+ "onkeypress" =>
+ ev = E->SEonkeypress;
+ "onkeyup" =>
+ ev = E->SEonkeyup;
+ "onmousedown" =>
+ ev = E->SEonmousedown;
+ "onmouseout" =>
+ ev = E->SEonmouseout;
+ "onmouseover" =>
+ ev = E->SEonmouseover;
+ "onmouseup" =>
+ ev = E->SEonmouseup;
+ }
+ if(ev != E->SEnone) {
+ vframeid := ES->get(ex, o, "@PRIVframeid");
+ vanchorid := ES->get(ex, o, "@PRIVanchorid");
+ if(!ES->isnum(vframeid) || !ES->isnum(vanchorid))
+ break;
+ frameid := ES->toInt32(ex, vframeid);
+ anchorid := ES->toInt32(ex, vanchorid);
+ target = top.findbyframeid(frameid);
+ if(target == nil)
+ break;
+ anchor: ref B->Anchor;
+ for(al := target.frame.doc.anchors; al != nil; al = tl al) {
+ a := hd al;
+ if(a.index == anchorid) {
+ anchor = a;
+ break;
+ }
+ }
+ if(anchor == nil)
+ break;
+ if(!ES->isobj(val) || val.obj.call == nil)
+ anchor.evmask &= ~ev;
+ else
+ anchor.evmask |= ev;
+ }
+ "Location" =>
+ target = top.findbyobj(o);
+ if (target == nil) {
+ break;
+ }
+ url = ref *target.frame.doc.src;
+ case property {
+ "hash" =>
+ if (str != nil && str[0] == '#')
+ str = str[1:];
+ url.frag = str;
+ "host" =>
+ # host:port
+ (h, p) := S->splitl(str, ":");
+ if (p != nil)
+ p = p[1:];
+ if (h != nil)
+ url.host = h;
+ if (p != nil)
+ url.port = p;
+ "hostname" =>
+ url.host = str;
+ "href" or
+ "pathname" =>
+ url = U->mkabs(U->parse(str), target.frame.doc.base);
+ "port" =>
+ url.port = str;
+ "protocol" =>
+ url.scheme = S->tolower(str);
+ "search" =>
+ url.query = str;
+ * =>
+ url = nil;
+ }
+ "Image" =>
+ case property {
+ "src" or
+ "lowsrc" =>
+ # making URLs absolute matches Netscape
+ target = top.findbyobj(o);
+ if(target == nil)
+ break;
+ url = U->mkabs(U->parse(str), target.frame.doc.base);
+ val = ES->strval(url.tostring());
+ target.imgrelocs = o :: target.imgrelocs;
+ url = nil;
+ "onabort" =>
+ ev = E->SEonabort;
+ "ondblclick" =>
+ ev = E->SEondblclick;
+ "onkeydown" =>
+ ev = E->SEonkeydown;
+ "onkeypress" =>
+ ev = E->SEonkeypress;
+ "onkeyup" =>
+ ev = E->SEonkeyup;
+ "onerror" =>
+ ev = E->SEonerror;
+ "onload" =>
+ ev = E->SEonload;
+ "onmousedown" =>
+ ev = E->SEonmousedown;
+ "onmouseout" =>
+ ev = E->SEonmouseout;
+ "onmouseover" =>
+ ev = E->SEonmouseover;
+ "onmouseup" =>
+ ev = E->SEonmouseup;
+ }
+ if(ev != E->SEnone) {
+ target = top.findbyobj(o);
+ if(target == nil)
+ break;
+ vimageid := ES->get(ex, o, "@PRIVimageid");
+ if(!ES->isnum(vimageid))
+ break;
+ imageid := ES->toInt32(ex, vimageid);
+ image: ref (Build->Item).Iimage;
+ forloop:
+ for(il := target.frame.doc.images; il != nil; il = tl il) {
+ pick im := hd il {
+ Iimage =>
+ if(im.imageid == imageid) {
+ image = im;
+ break forloop;
+ }
+ }
+ }
+ # BUG: if image has no genattr then the event handler update
+ # will not be done - can never set a handler for an image that
+ # didn't have a handler
+ if(image == nil || image.genattr == nil)
+ break;
+ if(!ES->isobj(val) || val.obj.call == nil)
+ image.genattr.evmask &= ~ev;
+ else
+ image.genattr.evmask |= ev;
+ }
+ "Form" =>
+ action := "";
+ case property {
+ "onreset" =>
+ ev = E->SEonreset;
+ "onsubmit" =>
+ ev = E->SEonsubmit;
+ "action" =>
+ action = str;
+ * =>
+ break;
+ }
+ vframeid := ES->get(ex, o, "@PRIVframeid");
+ vformid := ES->get(ex, o, "@PRIVformid");
+ if(!ES->isnum(vframeid) || !ES->isnum(vformid))
+ break;
+ frameid := ES->toInt32(ex, vframeid);
+ formid := ES->toInt32(ex, vformid);
+ target = top.findbyframeid(frameid);
+ if(target == nil)
+ break;
+ form: ref B->Form;
+ for(fl := target.frame.doc.forms; fl != nil; fl = tl fl) {
+ f := hd fl;
+ if(f.formid == formid) {
+ form = f;
+ break;
+ }
+ }
+ if(form == nil)
+ break;
+ if (ev != E->SEnone) {
+ if(!ES->isobj(val) || val.obj.call == nil)
+ form.evmask &= ~ev;
+ else
+ form.evmask |= ev;
+ break;
+ }
+ if (action != "")
+ form.action = U->mkabs(U->parse(action), target.frame.doc.base);
+ "FormField" =>
+ oform := getobj(ex, o, "form");
+ vframeid := ES->get(ex, oform, "@PRIVframeid");
+ vformid := ES->get(ex, oform, "@PRIVformid");
+ vfieldid := ES->get(ex, o, "@PRIVfieldid");
+ if(!ES->isnum(vframeid) || !ES->isnum(vformid) || !ES->isnum(vfieldid))
+ break;
+ frameid := ES->toInt32(ex, vframeid);
+ formid := ES->toInt32(ex, vformid);
+ fieldid := ES->toInt32(ex, vfieldid);
+ target = top.findbyframeid(frameid);
+ if(target == nil)
+ break;
+ form: ref B->Form;
+ for(fl := target.frame.doc.forms; fl != nil; fl = tl fl) {
+ f := hd fl;
+ if(f.formid == formid) {
+ form = f;
+ break;
+ }
+ }
+ if(form == nil)
+ break;
+ field: ref B->Formfield;
+ for(ffl := form.fields; ffl != nil; ffl = tl ffl) {
+ ff := hd ffl;
+ if(ff.fieldid == fieldid) {
+ field = ff;
+ break;
+ }
+ }
+ if(field == nil)
+ break;
+ case property {
+ "onblur" =>
+ ev = E->SEonblur;
+ "onchange" =>
+ ev = E->SEonchange;
+ "onclick" =>
+ ev = E->SEonclick;
+ "ondblclick" =>
+ ev = E->SEondblclick;
+ "onfocus" =>
+ ev = E->SEonfocus;
+ "onkeydown" =>
+ ev = E->SEonkeydown;
+ "onkeypress" =>
+ ev = E->SEonkeypress;
+ "onkeyup" =>
+ ev = E->SEonkeyup;
+ "onmousedown" =>
+ ev = E->SEonmousedown;
+ "onmouseup" =>
+ ev = E->SEonmouseup;
+ "onselect" =>
+ ev = E->SEonselect;
+ "value" =>
+ field.value = str;
+ if(target.frame.controls == nil ||
+ field.ctlid < 0 ||
+ field.ctlid > len target.frame.controls){
+ break;
+ }
+ c := target.frame.controls[field.ctlid];
+ pick ctl := c {
+ Centry =>
+ ctl.s = str;
+ ctl.sel = (0, 0);
+ E->evchan <-= ref Event.Eformfield(frameid, formid, fieldid, E->EFFredraw);
+ }
+ }
+
+ if(ev != E->SEnone) {
+ if(!ES->isobj(val) || val.obj.call == nil)
+ field.evmask &= ~ev;
+ else
+ field.evmask |= ev;
+ }
+ "document" =>
+ case property {
+ "location" =>
+ target = top.findbyobj(o);
+ if (target == nil)
+ break;
+ # TODO: be more defensive
+ url = U->mkabs(U->parse(str), target.frame.doc.base);
+ "cookie" =>
+ target = top.findbyobj(o);
+ if(target != nil && (CU->config).docookies > 0) {
+ url = target.frame.doc.src;
+ CU->setcookie(url.host, url.path, str);
+ }
+ return;
+ "ondblclick" =>
+ ev = E->SEondblclick;
+ "onkeydown" =>
+ ev = E->SEonkeydown;
+ "onkeypress" =>
+ ev = E->SEonkeypress;
+ "onkeyup" =>
+ ev = E->SEonkeyup;
+ "onmousedown" =>
+ ev = E->SEonmousedown;
+ "onmouseup" =>
+ ev = E->SEonmouseup;
+ }
+ if(ev != E->SEnone){
+ target = top.findbyobj(o);
+ if(target == nil)
+ break;
+ di := target.frame.doc;
+ if(!ES->isobj(val) || val.obj.call == nil)
+ di.evmask &= ~ev;
+ else
+ di.evmask |= ev;
+ }
+ "Option" =>
+ vformfield := ES->get(ex, o, "@PRIVformfield");
+ vindex := ES->get(ex, o, "index");
+ if (!ES->isobj(vformfield) || !ES->isnum(vindex))
+ # not one of our 'options' objects
+ break;
+ oformfield := vformfield.obj;
+ oform := getobj(ex, oformfield, "form");
+ if (oform == nil)
+ break;
+ ES->put(ex, o, property, val);
+ index := ES->toInt32(ex, vindex);
+ updateffopts(ex, oform, oformfield, index);
+ }
+ ES->put(ex, o, property, val);
+
+ if (url != nil && target != nil) {
+ if (!CU->urlequal(url, target.frame.doc.src)) {
+ target.newloc = url.tostring();
+ target.newloctarg = "_top";
+ if(target.frame != nil)
+ target.newloctarg = target.frame.name;
+ }
+ }
+}
+
+canput(ex: ref Exec, o: ref Obj, property: string): ref Val
+{
+ return ES->canput(ex, o, property);
+}
+
+hasproperty(ex: ref Exec, o: ref Obj, property: string): ref Val
+{
+ return ES->hasproperty(ex, o, property);
+}
+
+delete(ex: ref Exec, o: ref Obj, property: string)
+{
+ ES->delete(ex, o, property);
+}
+
+defaultval(ex: ref Exec, o: ref Obj, tyhint: int): ref Val
+{
+ return ES->defaultval(ex, o, tyhint);
+}
+
+call(ex: ref Exec, func, this: ref Obj, args: array of ref Val, nil: int): ref Ref
+{
+ if(dbgdom)
+ sys->print("call %x (class %s), val %s\n", func, func.class, func.val.str);
+ ans := ES->valref(ES->true);
+ case func.val.str{
+ "document.prototype.open" =>
+ sw := top.findbyobj(this);
+ if (sw != nil)
+ sw.docwriteout = "";
+ "document.prototype.close" =>
+ # ignore for now
+ ;
+ "document.prototype.write" =>
+ sw := top.findbyobj(this);
+ if (sw != nil) {
+ for (ai := 0; ai < len args; ai++)
+ sw.docwriteout += ES->toString(ex, ES->biarg(args, ai));
+ }
+ "document.prototype.writeln" =>
+ sw := top.findbyobj(this);
+ if (sw != nil) {
+ for (ai := 0; ai < len args; ai++)
+ sw.docwriteout += ES->toString(ex, ES->biarg(args, ai));
+ sw.docwriteout += "\n";
+ }
+ "navigator.prototype.javaEnabled" or
+ "navigator.prototype.taintEnabled" =>
+ ans = ES->valref(ES->false);
+ "Form.prototype.reset" or "Form.prototype.submit"=>
+ vframeid := ES->get(ex, this, "@PRIVframeid");
+ vformid := ES->get(ex, this, "@PRIVformid");
+ if(ES->isnum(vframeid) && ES->isnum(vformid)) {
+ frameid := ES->toInt32(ex, vframeid);
+ formid := ES->toInt32(ex, vformid);
+ ftype : int;
+ if(func.val.str == "Form.prototype.reset")
+ ftype = E->EFreset;
+ else
+ ftype = E->EFsubmit;
+ E->evchan <-= ref Event.Eform(frameid, formid, ftype);
+ }
+ "FormField.prototype.blur" or
+ "FormField.prototype.click" or
+ "FormField.prototype.focus" or
+ "FormField.prototype.select" =>
+ oform := getobj(ex, this, "form");
+ vformid := ES->get(ex, oform, "@PRIVformid");
+ vframeid := ES->get(ex, oform, "@PRIVframeid");
+ vfieldid := ES->get(ex, this, "@PRIVfieldid");
+ if(ES->isnum(vframeid) && ES->isnum(vformid) && ES->isnum(vfieldid)) {
+ frameid := ES->toInt32(ex, vframeid);
+ formid := ES->toInt32(ex, vformid);
+ fieldid := ES->toInt32(ex, vfieldid);
+ fftype : int;
+ case func.val.str{
+ "FormField.prototype.blur" =>
+ fftype = E->EFFblur;
+ "FormField.prototype.click" =>
+ fftype = E->EFFclick;
+ "FormField.prototype.focus" =>
+ fftype = E->EFFfocus;
+ "FormField.prototype.select" =>
+ fftype = E->EFFselect;
+ * =>
+ fftype = E->EFFnone;
+ }
+ E->evchan <-= ref Event.Eformfield(frameid, formid, fieldid, fftype);
+ }
+ "History.prototype.back" =>
+ E->evchan <-= ref Event.Ego("", "", 0, E->EGback);
+ "History.prototype.forward" =>
+ E->evchan <-= ref Event.Ego("", "", 0, E->EGforward);
+ "History.prototype.go" =>
+ ego : ref Event.Ego;
+ v := ES->biarg(args, 0);
+ if(ES->isstr(v))
+ ego = ref Event.Ego(v.str, "", 0, E->EGlocation);
+ else if(ES->isnum(v)) {
+ delta := ES->toInt32(ex, v);
+ case delta {
+ -1 =>
+ ego = ref Event.Ego("", "", 0, E->EGback);
+ 0 =>
+ ego = ref Event.Ego("", "", 0, E->EGreload);
+ 1 =>
+ ego = ref Event.Ego("", "", 0, E->EGforward);
+ * =>
+ ego = ref Event.Ego("", "", delta, E->EGdelta);
+ }
+ }
+ if(ego != nil)
+ E->evchan <-= ego;
+ "Location.prototype.reload" =>
+ # ignore 'force' argument for now
+ E->evchan <-= ref Event.Ego("", "", 0, E->EGreload);
+ "Location.prototype.replace" =>
+ v := ES->biarg(args, 0);
+ if(ES->isstr(v)) {
+ sw := top.findbyobj(this);
+ if(sw == nil)
+ fname := "_top";
+ else
+ fname = sw.frame.name;
+ if (v.str != nil) {
+ url := U->mkabs(U->parse(v.str), sw.frame.doc.base);
+ E->evchan <-= ref Event.Ego(url.tostring(), fname, 0, E->EGreplace);
+ }
+ }
+ "Window.prototype.alert" =>
+ G->alert(ES->toString(ex, ES->biarg(args, 0)));
+ "Window.prototype.blur" =>
+ ;
+# sw := top.findbyobj(this);
+# if (sw != nil)
+# E->evchan <-= ref Event.Eframefocus(sw.frame.id, 0);
+
+ "Window.prototype.clearTimeout" or
+ "Window.prototype.clearInterval" =>
+ v := ES->biarg(args, 0);
+ id := ES->toInt32(ex, v);
+ clrtimeout(ex, this, id);
+ "Window.prototype.close" =>
+ if(this == top.ex.global)
+ winclose = 1;
+ # no-op
+ ;
+ "Window.prototype.confirm" =>
+ code := G->confirm(ES->toString(ex, ES->biarg(args, 0)));
+ if(code != 1)
+ ans = ES->valref(ES->false);
+ "Window.prototype.focus" =>
+ ;
+# sw := top.findbyobj(this);
+# if (sw != nil)
+# E->evchan <-= ref Event.Eframefocus(sw.frame.id, 1);
+ "Window.prototype.moveBy" or
+ "Window.prototype.moveTo" =>
+ # no-op
+ ;
+ "Window.prototype.open" =>
+ if (dbgdom)
+ sys->print("window.open called\n");
+ u := ES->toString(ex, ES->biarg(args, 0));
+ n := ES->toString(ex, ES->biarg(args, 1));
+ sw : ref ScriptWin;
+ if (n != "")
+ sw = top.findbyname(n);
+ newch := 0;
+ if (sw == nil){
+ sw = top;
+ newch = 1;
+ }
+ if(u != "") {
+ # Want to replace window by navigation to u
+ sw.newloc = u;
+ if (sw.frame.name != "")
+ sw.newloctarg = sw.frame.name;
+ else
+ sw.newloctarg = "_top";
+ url : ref U->Parsedurl;
+ if (sw.frame.doc != nil && sw.frame.doc.base != nil)
+ url = U->mkabs(U->parse(u), sw.frame.doc.base);
+ else
+ url = CU->makeabsurl(u);
+ sw.newloc = url.tostring();
+ }
+ if(newch){
+ # create dummy window
+ dw := ScriptWin.dummy();
+ spawn newcharon(sw.newloc, sw.newloctarg, sw);
+ sw.newloc = "";
+ sw.newloctarg = "";
+ ans = ES->valref(dw.val);
+ }
+ else
+ ans = ES->valref(sw.val);
+ "Window.prototype.prompt" =>
+ msg := ES->toString(ex, ES->biarg(args, 0));
+ dflt := ES->toString(ex, ES->biarg(args, 1));
+ (code, input) := G->prompt(msg, dflt);
+ v := ES->null;
+ if(code == 1)
+ v = ES->strval(input);
+ ans = ES->valref(v);
+ "Window.prototype.resizeBy" or
+ "Window.prototype.resizeTo" =>
+ # no-op
+ ;
+ "Window.prototype.scroll" or
+ "Window.prototype.scrollTo" =>
+ # scroll is done via an event to avoid race in calls to
+ # Layout->fixframegeom() [made by scroll code]
+ sw := top.findbyobj(this);
+ if (sw != nil) {
+ (xv, yv) := (ES->biarg(args, 0), ES->biarg(args, 1));
+ pt := Draw->Point(ES->toInt32(ex, xv), ES->toInt32(ex, yv));
+ E->evchan <-= ref Event.Escroll(sw.frame.id, pt);
+ }
+ "Window.prototype.scrollBy" =>
+ sw := top.findbyobj(this);
+ if (sw != nil) {
+ (dxv, dyv) := (ES->biarg(args, 0), ES->biarg(args, 1));
+ pt := Draw->Point(ES->toInt32(ex, dxv), ES->toInt32(ex, dyv));
+ E->evchan <-= ref Event.Escrollr(sw.frame.id, pt);
+ }
+ "Window.prototype.setTimeout" =>
+ (v1, v2) := (ES->biarg(args, 0), ES->biarg(args, 1));
+ cmd := ES->toString(ex, v1);
+ ms := ES->toInt32(ex, v2);
+ id := addtimeout(ex, this, cmd, ms, E->SEtimeout);
+ ans = ES->valref(ES->numval(real id));
+ "Window.prototype.setInterval" =>
+ (v1, v2) := (ES->biarg(args, 0), ES->biarg(args, 1));
+ cmd := ES->toString(ex, v1);
+ ms := ES->toInt32(ex, v2);
+ id := addtimeout(ex, this, cmd, ms, E->SEinterval);
+ ans = ES->valref(ES->numval(real id));
+ * =>
+ ES->runtime(ex, nil, "unknown or unimplemented func "+func.val.str+" in host call");
+ return nil;
+ }
+ return ans;
+}
+
+construct(ex: ref Exec, func: ref Obj, args: array of ref Val): ref Obj
+{
+ if(dbgdom)
+ sys->print("construct %x (class %s), val %s\n", func, func.class, func.val.str);
+ params: array of string;
+ o: ref Obj;
+#sys->print("Construct(%s)\n", func.val.str);
+ case func.val.str {
+ "Image" =>
+ o = mkhostobj(ex, "Image");
+ params = array [] of {"width", "height"};
+ createdimages = o :: createdimages;
+ "Option" =>
+ o = mkhostobj(ex, "Option");
+ params = array [] of {"text", "value", "defaultSelected", "selected"};
+ * =>
+ return nil;
+ }
+ for (i := 0; i < len args && i < len params; i++)
+ reinitprop(o, params[i], args[i]);
+ return o;
+}
+
+updateffopts(ex: ref Exec, oform, oformfield: ref Obj, ix: int)
+{
+ vframeid := ES->get(ex, oform, "@PRIVframeid");
+ vformid := ES->get(ex, oform, "@PRIVformid");
+ vfieldid := ES->get(ex, oformfield, "@PRIVfieldid");
+ if(!ES->isnum(vframeid) || !ES->isnum(vformid) || !ES->isnum(vfieldid))
+ return;
+ frameid := ES->toInt32(ex, vframeid);
+ formid := ES->toInt32(ex, vformid);
+ fieldid := ES->toInt32(ex, vfieldid);
+
+ target := top.findbyframeid(frameid);
+ if(target == nil)
+ return;
+ form: ref B->Form;
+ for(fl := target.frame.doc.forms; fl != nil; fl = tl fl) {
+ f := hd fl;
+ if(f.formid == formid) {
+ form = f;
+ break;
+ }
+ }
+ if(form == nil)
+ return;
+ field: ref B->Formfield;
+ for(ffl := form.fields; ffl != nil; ffl = tl ffl) {
+ ff := hd ffl;
+ if(ff.fieldid == fieldid) {
+ field = ff;
+ break;
+ }
+ }
+ if(field == nil)
+ return;
+
+ selctl : ref Control.Cselect;
+ pick ctl := target.frame.controls[field.ctlid] {
+ Cselect =>
+ selctl = ctl;
+ * =>
+ return;
+ }
+
+ (opts, nopts) := getarraywithlen(ex, oformfield, "options");
+ if (opts == nil)
+ return;
+ optl: list of ref B->Option;
+ selobj, firstobj: ref Obj;
+ selopt, firstopt: ref B->Option;
+ noptl := 0;
+ for (i := 0; i < nopts; i++) {
+ vopt := ES->get(ex, opts, string i);
+ if (!ES->isobj(vopt) || vopt.obj.class != "Option")
+ continue;
+ oopt := vopt.obj;
+ sel := ES->get(ex, oopt, "selected") == ES->true;
+ val := ES->toString(ex, ES->get(ex, oopt, "value"));
+ text := ES->toString(ex, ES->get(ex, oopt, "text"));
+ option := ref B->Option(sel, val, text);
+ optl = option :: optl;
+ if (noptl++ == 0) {
+ firstobj = oopt;
+ firstopt = option;
+ }
+ if (sel && (selobj == nil || ix == i)) {
+ selobj = oopt;
+ selopt = option;
+ }
+ if (! int(field.flags & B->FFmultiple)) {
+ ES->put(ex, oopt, "selected", ES->false);
+ option.selected = 0;
+ }
+ }
+ if (selobj != nil)
+ ES->put(ex, selobj, "selected", ES->true);
+ else if (firstobj != nil)
+ ES->put(ex, firstobj, "selected", ES->true);
+ if (selopt != nil)
+ selopt.selected = 1;
+ else if (firstopt != nil)
+ firstopt.selected = 1;
+ opta := array [noptl] of B->Option;
+ for (i = noptl - 1; i >= 0; i--)
+ (opta[i], optl) = (*hd optl, tl optl);
+ # race here with charon.b:form_submit() and layout code
+ selctl.options = opta;
+ E->evchan <-= ref Event.Eformfield(frameid, formid, fieldid, E->EFFredraw);
+}
+
+timeout(e : ref ScriptEvent, ms : int)
+{
+ sys->sleep(ms);
+ jevchan <- = e;
+}
+
+# BUGS
+# cannot set a timeout for a window just created by window.open()
+# because it will not have an entry in the ScriptWin tree
+# (This is really a problem with the ScriptEvent adt only taking a frame id)
+#
+addtimeout(ex : ref Exec, win : ref Obj, cmd : string, ms : int, evk: int) : int
+{
+ sw := top.findbyobj(win);
+ if (sw == nil || cmd == nil || ms <= 0)
+ return -1;
+
+ # check for timeout handler array, create if doesn't exist
+ (toa, n) := getarraywithlen(ex, win, "@PRIVtoa");
+ if (toa == nil) {
+ toa = ES->mkobj(ex.arrayproto, "Array");
+ ES->varinstant(toa, ES->DontEnum|ES->DontDelete, "length", ref RefVal(ES->numval(0.)));
+ ES->varinstant(win, ES->DontEnum|ES->DontDelete, "@PRIVtoa", ref RefVal(ES->objval(toa)));
+ }
+ # find first free handler
+ for (ix := 0; ix < n; ix++) {
+ hv := ES->get(ex, toa, string ix);
+
+ if (hv == nil)
+ break;
+ # val == null Timeout has been cancelled, but timer still running
+ # val == undefined Timeout has expired
+ if (hv == ES->undefined)
+ break;
+ }
+
+ # construct a private handler for the timeout
+ # The code is always executed in the scope of the window object
+ # for which the timeout is being set.
+ oldsc := ex.scopechain;
+ ex.scopechain = win :: nil;
+ ES->eval(ex, "function PRIVhandler() {" + cmd + "}");
+ hobj := getobj(ex, win, "PRIVhandler");
+ ex.scopechain = oldsc;
+ if(hobj == nil)
+ return -1;
+ ES->put(ex, toa, string ix, ES->objval(hobj));
+ ev := ref ScriptEvent(evk, sw.frame.id, -1, -1, -1, -1, 0, 0, ix, nil, nil, ms);
+ spawn timeout(ev, ms);
+ return ix;
+}
+
+dotimeout(ex : ref Exec, win : ref Obj, e: ref ScriptEvent) : ref Ecmascript->Obj
+{
+ id := e.which;
+ if (id < 0)
+ return nil;
+
+ (toa, n) := getarraywithlen(ex, win, "@PRIVtoa");
+ if (toa == nil || id >= n)
+ return nil;
+
+ handler := getobj(ex, toa, string id);
+ if (handler == nil)
+ return nil;
+ if(e.kind == E->SEinterval){
+ ev := ref ScriptEvent;
+ *ev = *e;
+ spawn timeout(ev, e.ms);
+ return handler;
+ }
+ if (id == n-1)
+ ES->put(ex, toa, "length", ES->numval(real (n-1)));
+ else
+ ES->put(ex, toa, string id, ES->undefined);
+ return handler;
+}
+
+clrtimeout(ex : ref Exec, win : ref Obj, id : int)
+{
+ if (id < 0)
+ return;
+ (toa, n) := getarraywithlen(ex, win, "@PRIVtoa");
+ if (toa == nil || id >= n)
+ return;
+
+ ES->put(ex, toa, string id, ES->null);
+}
+
+# Make a host object with given class.
+# Get the prototype from the objspecs array
+# (if none yet, make one up and install the methods).
+# Put in required properties, with undefined values initially.
+# If mainex is nil (it will be for bootstrapping the initial object),
+# the prototype has to be filled in later.
+mkhostobj(ex : ref Exec, class: string) : ref Obj
+{
+ ci := specindex(class);
+ proto : ref Obj;
+ if(ex != nil)
+ proto = mkprototype(ex, ci);
+ ans := ES->mkobj(proto, class);
+ initprops(ex, ans, objspecs[ci].props);
+ ans.host = me;
+ return ans;
+}
+
+initprops(ex : ref Exec, o: ref Obj, props: array of PropSpec)
+{
+ if(props == nil)
+ return;
+ for(i := 0; i < len props; i++) {
+ v := ES->undefined;
+ case props[i].initval {
+ IVundef =>
+ v = ES->undefined;
+ IVnull =>
+ v = ES->null;
+ IVtrue =>
+ v = ES->true;
+ IVfalse =>
+ v = ES->false;
+ IVnullstr =>
+ v = nullstrval;
+ IVzero =>
+ v = zeroval;
+ IVzerostr =>
+ v = zerostrval;
+ IVarray =>
+ # need a separate one for each array,
+ # since we'll update these rather than replacing
+ ao := ES->mkobj(ex.arrayproto, "Array");
+ ES->varinstant(ao, ES->DontEnum|ES->DontDelete, "length", ref RefVal(ES->numval(0.)));
+ v = ES->objval(ao);
+ * =>
+ CU->assert(0);
+ }
+ ES->varinstant(o, props[i].attr | ES->DontDelete, props[i].name, ref RefVal(v));
+ }
+}
+
+# Return index into objspecs where class is specified
+specindex(class: string) : int
+{
+ for(i := 0; i < len objspecs; i++)
+ if(objspecs[i].name == class)
+ break;
+ if(i == len objspecs)
+ CU->raisex("EXInternal: couldn't find host object class " + class);
+ return i;
+}
+
+# Make a prototype for host object specified by objspecs[ci]
+mkprototype(ex : ref Exec, ci : int) : ref Obj
+{
+ CU->assert(ex != nil);
+ class := objspecs[ci].name;
+ prototype := ES->mkobj(ex.objproto, class);
+ meths := objspecs[ci].methods;
+ for(k := 0; k < len meths; k++) {
+ name := meths[k].name;
+ fullname := class + ".prototype." + name;
+ args := meths[k].args;
+ ES->biinst(prototype, Builtin(name, fullname, args, len args),
+ ex.funcproto, me);
+ }
+ return prototype;
+}
+
+
+getframeobj(frameid: int) : ref Obj
+{
+ sw := top.findbyframeid(frameid);
+ if(sw != nil)
+ return sw.ex.global;
+ return nil;
+}
+
+getdocobj(ex : ref Exec, frameid: int) : ref Obj
+{
+ return getobj(ex, getframeobj(frameid), "document");
+}
+
+getformobj(ex : ref Exec, frameid, formid: int) : ref Obj
+{
+ # frameids are 1-origin, document.forms is 0-origin
+ return getarrayelem(ex, getdocobj(ex, frameid), "forms", formid-1);
+}
+
+getformfieldobj(frameid, formid, fieldid: int) : ref Obj
+{
+ sw := top.findbyframeid(frameid);
+ if (sw == nil)
+ return nil;
+ flds : list of (ref Build->Formfield, ref Obj);
+ for (fl := sw.forms; fl != nil; fl = tl fl) {
+ sf := hd fl;
+ if (sf.form.formid == formid) {
+ flds = sf.fields;
+ break;
+ }
+ }
+ for (; flds != nil; flds = tl flds) {
+ (fld, obj) := hd flds;
+ if (fld.fieldid == fieldid)
+ return obj;
+ }
+ return nil;
+}
+
+getanchorobj(ex: ref Exec, frameid, anchorid: int) : ref Obj
+{
+ od := getdocobj(ex, frameid);
+ if(od != nil) {
+ (olinks, olinkslen) := getarraywithlen(ex, od, "links");
+ if(olinks != nil) {
+ for(i := 0; i < olinkslen; i++) {
+ ol := getobj(ex, olinks, string i);
+ if(ol != nil) {
+ v := ES->get(ex, ol, "@PRIVanchorid");
+ if(ES->isnum(v) && ES->toInt32(ex, v) == anchorid)
+ return ol;
+ }
+ }
+ }
+ }
+ return nil;
+}
+
+getimageobj(ex: ref Exec, frameid, imageid: int) : ref Obj
+{
+ od := getdocobj(ex, frameid);
+ if(od != nil) {
+ (oimages, oimageslen) := getarraywithlen(ex, od, "images");
+ if(oimages != nil) {
+ for(i := 0; i < oimageslen; i++) {
+ oi := getobj(ex, oimages, string i);
+ if(oi != nil) {
+ v := ES->get(ex, oi, "@PRIVimageid");
+ if(ES->isnum(v) && ES->toInt32(ex, v) == imageid)
+ return oi;
+ }
+ }
+ }
+ }
+ return nil;
+}
+
+# return nil if none such, or not an object
+getobj(ex : ref Exec, o: ref Obj, prop: string) : ref Obj
+{
+ if(o != nil) {
+ v := ES->get(ex, o, prop);
+ if(ES->isobj(v))
+ return ES->toObject(ex, v);
+ }
+ return nil;
+}
+
+# return nil if none such, or not an object
+getarrayelem(ex : ref Exec, o: ref Obj, arrayname: string, index: int) : ref Obj
+{
+ oarr := getobj(ex, o, arrayname);
+ if(oarr != nil) {
+ v := ES->get(ex, oarr, string index);
+ if(ES->isobj(v))
+ return ES->toObject(ex, v);
+ }
+ return nil;
+}
+
+# return "" if none such, or not a string
+getstr(ex : ref Exec, o: ref Obj, prop: string) : string
+{
+
+ if(o != nil) {
+ v := ES->get(ex, o, prop);
+ if(ES->isstr(v))
+ return ES->toString(ex, v);
+ }
+ return "";
+}
+
+# Property index, -1 if doesn't exist
+pind(o: ref Obj, prop: string) : int
+{
+ props := o.props;
+ for(i := 0; i < len props; i++){
+ if(props[i] != nil && props[i].name == prop)
+ return i;
+ }
+ return -1;
+}
+
+# Reinitialize property prop of object o to value v
+# (pay no attention to ReadOnly status, so can't use ES->put).
+# Assume the property exists already.
+reinitprop(o: ref Obj, prop: string, v: ref Val)
+{
+ i := pind(o, prop);
+ if(i < 0) {
+ # set up dummy ex for now - needs sorting out
+ ex := ref Exec;
+ ES->runtime(ex, nil, "missing property " + prop); # shouldn't happen
+ }
+ CU->assert(i >= 0);
+ o.props[i].val.val = v;
+}
+
+# Get the array object named aname from o, and also find its current
+# length value. If there is any problem, return (nil, 0).
+getarraywithlen(ex : ref Exec, o: ref Obj, aname: string) : (ref Obj, int)
+{
+ varray := ES->get(ex, o, aname);
+ if(ES->isobj(varray)) {
+ oarray := ES->toObject(ex, varray);
+ vlen := ES->get(ex, oarray, "length");
+ if(vlen != ES->undefined)
+ return (oarray, ES->toInt32(ex, vlen));
+ }
+ return (nil, 0);
+}
+
+# Put val v as property "index" of object oarray.
+# Also, if the name doesn't conflict with array properties, add the val as
+# a "name" property too
+arrayput(ex : ref Exec, oarray: ref Obj, index: int, name: string, v: ref Val)
+{
+ ES->put(ex, oarray, string index, v);
+ if (name != "length" && prop2index(name) == -1)
+ ES->put(ex, oarray, name, v);
+}
+
+prop2index(p: string): int
+{
+ if (p == nil)
+ return -1;
+ v := 0;
+ for (i := 0; i < len p; i++) {
+ c := p[i];
+ if (c < '0' || c > '9')
+ return -1;
+ v = 10 * v + c - '0';
+ }
+ return v;
+}
+
+# Instantiate window object.
+# mkhostobj has already put the property names and default initial values in;
+# we have to fill in the proper values.
+wininstant(sw: ref ScriptWin)
+{
+ ex := sw.ex;
+ w := ex.global;
+ f := sw.frame;
+
+ sw.error = 0;
+ prevkids := sw.kids;
+ sw.kids = nil;
+ sw.forms = nil;
+ sw.imgs = nil;
+ sw.active = 0;
+
+ # document to be init'd by xfertoscriptobjs - WRONG,
+ # has to be init'd up-front as one frame may refer
+ # to another's document object (esp. for document.write calls)
+ od := getobj(ex, w, "document");
+ if(od == nil) {
+ docv := ES->objval(mkhostobj(ex, "document"));
+ reinitprop(w, "document", docv);
+ od = getobj(ex, w, "document");
+ CU->assert(od != nil);
+ }
+
+ # frames[ ]
+ ao := ES->mkobj(ex.arrayproto, "Array");
+ ES->varinstant(ao, ES->DontEnum|ES->DontDelete, "length", ref RefVal(ES->numval(0.)));
+ reinitprop(w, "frames", ES->objval(ao));
+ for (kl := f.kids; kl != nil; kl = tl kl) {
+ klf := hd kl;
+ # look for original ScriptWin
+ for (oldkl := prevkids; oldkl != nil; oldkl = tl oldkl) {
+ oldksw := hd oldkl;
+ if (oldksw.frame == klf) {
+ wininstant(oldksw);
+ sw.kids = oldksw :: sw.kids;
+ break;
+ }
+ }
+ if (oldkl == nil)
+ sw.addkid(klf);
+ }
+ kn := 0;
+ for (swkl := sw.kids; swkl != nil; swkl = tl swkl) {
+ k := hd swkl;
+ # Yes, frame name should be defined as property of parent
+ arrayput(ex, ao, kn++, "", k.val);
+ if (k.frame != nil && k.frame.name != nil) {
+ ES->put(ex, ao, k.frame.name, k.val);
+ ES->varinstant(w, 0, k.frame.name, ref RefVal(k.val));
+ }
+ }
+
+ reinitprop(w, "length", ES->numval(real len f.kids));
+
+ v := ref Val;
+ if (sw.parent == nil)
+ v = ES->objval(w);
+ else
+ v = ES->objval(sw.parent.ex.global);
+ reinitprop(w, "parent", v);
+
+ if (f.name != nil)
+ reinitprop(w, "name", ES->strval(f.name));
+ reinitprop(w, "self", ES->objval(w));
+ reinitprop(w, "window", ES->objval(w));
+ reinitprop(w, "top", ES->objval(top.ex.global));
+ reinitprop(w, "Math", ES->get(ex, top.ex.global, "Math"));
+ reinitprop(w, "navigator", ES->get(ex, top.ex.global, "navigator"));
+}
+
+# Return initial document object value, based on d
+docinstant(ex: ref Exec, f: ref Layout->Frame) : ref Val
+{
+ od := mkhostobj(ex, "document");
+ docfill(ex, od, f);
+ return ES->objval(od);
+}
+
+# Fill in properties of doc object, based on d.
+# Can be called at various points during build.
+docfill(ex: ref Exec, od: ref Obj, f: ref Layout->Frame)
+{
+ sw := top.findbyframeid(f.id);
+ if(sw == nil)
+ return;
+ di := f.doc;
+ if(di.src != nil) {
+ reinitprop(od, "URL", ES->strval(di.src.tostring()));
+ reinitprop(od, "domain", ES->strval(di.src.host));
+ }
+ if(di.referrer != nil)
+ reinitprop(od, "referrer", ES->strval(di.referrer.tostring()));
+ if(di.doctitle != "")
+ reinitprop(od, "title", ES->strval(di.doctitle));
+ reinitprop(od, "lastModified", ES->strval(di.lastModified));
+ reinitprop(od, "bgColor", colorval(di.background.color));
+ reinitprop(od, "fgColor", colorval(di.text));
+ reinitprop(od, "alinkColor", colorval(di.alink));
+ reinitprop(od, "linkColor", colorval(di.link));
+ reinitprop(od, "vlinkColor", colorval(di.vlink));
+
+ # Forms in d.forms are in reverse order of appearance.
+ # Add any that aren't already in the document.forms object,
+ # assuming that the relative lengths will tell us what needs
+ # to be done.
+ if(di.forms != nil) {
+ newformslen := len di.forms;
+ oldformslen := len sw.forms;
+ oforms := getobj(ex, od, "forms");
+
+ # oforms should be non-nil, because the object is initialized
+ # to an empty array and is readonly. The following test
+ # is just defensive.
+ if(oforms != nil) {
+ # run through our existing list of forms, looking
+ # for any not marked as Transferred (happens as a result
+ # of a script being called in the body of a form, while it is
+ # still being parsed)
+ for (sfl := sw.forms; sfl != nil; sfl = tl sfl) {
+ sf := hd sfl;
+ form := sf.form;
+ if (form.state != B->FormTransferred) {
+# (sf.obj, sf.fields) = forminstant(ex, form, di.frameid);
+ (newobj, newfields) := forminstant(ex, form, di.frameid);
+ *sf.obj = *newobj;
+ sf.fields = newfields;
+ }
+ if (form.state == B->FormDone)
+ form.state = B->FormTransferred;
+ }
+
+ # process additional forms
+ fl := di.forms;
+ for(i := newformslen-1; i >= oldformslen; i--) {
+ form := hd fl;
+ fl = tl fl;
+ if (form.state != B->FormTransferred) {
+ sf := ref ScriptForm (form, nil, i, nil);
+ (sf.obj, sf.fields) = forminstant(ex, form, di.frameid);
+ arrayput(ex, oforms, i, form.name, ES->objval(sf.obj));
+ if(form.name != "")
+ ES->put(ex, od, form.name, ES->objval(sf.obj));
+ sw.forms = sf :: sw.forms;
+ }
+ if (form.state == B->FormDone)
+ form.state = B->FormTransferred;
+ }
+ }
+ }
+
+ # Charon calls "DestAnchor" what Netscape calls "Anchor".
+ # Use same method as for forms to discover new ones.
+ if(di.dests != nil) {
+ newdestslen := len di.dests;
+ (oanchors, oldanchorslen) := getarraywithlen(ex, od, "anchors");
+ if(oanchors != nil) {
+ dl := di.dests;
+ for(i := newdestslen-1; i >= oldanchorslen; i--) {
+ dest := hd dl;
+ dl = tl dl;
+ arrayput(ex, oanchors, i, dest.name, anchorinstant(ex, dest.name));
+ }
+ }
+ }
+
+ # Charon calls "Anchor" what Netscape calls "Link" (how confusing for us!).
+ # Use same method as for forms to discover new ones.
+ # BUG: Areas are supposed to be in this list too.
+ if(di.anchors != nil) {
+ newanchorslen := len di.anchors;
+ (olinks, oldlinkslen) := getarraywithlen(ex, od, "links");
+ if(olinks != nil) {
+ al := di.anchors;
+ for(i := newanchorslen-1; i >= oldlinkslen; i--) {
+ a := hd al;
+ al = tl al;
+ arrayput(ex, olinks, i, a.name, linkinstant(ex, a, f.id));
+ }
+ }
+ }
+
+ if(di.images != nil) {
+ newimageslen := len di.images;
+ (oimages, oldimageslen) := getarraywithlen(ex, od, "images");
+ if(oimages != nil) {
+ il := di.images;
+ for(i := newimageslen-1; i >= oldimageslen; i--) {
+ imit := hd il;
+ il = tl il;
+ pick ii := imit {
+ Iimage =>
+ vim := imageinstant(ex, ii);
+ arrayput(ex, oimages, i, ii.name, vim);
+ ES->put(ex, od, ii.name, vim);
+ if(ES->isobj(vim)) {
+ sw.imgs = ref ScriptImg(ii, vim.obj) :: sw.imgs;
+ }
+ }
+ }
+ }
+ }
+
+ # private variables
+ ES->varinstant(od, ES->DontEnum|ES->DontDelete, "@PRIVframeid",
+ ref RefVal(ES->numval(real di.frameid)));
+}
+
+forminstant(ex : ref Exec, form: ref Build->Form, frameid: int) : (ref Obj, list of (ref Build->Formfield, ref Obj))
+{
+ fields : list of (ref Build->Formfield, ref ES->Obj);
+ oform := mkhostobj(ex, "Form");
+ reinitprop(oform, "action", ES->strval(form.action.tostring()));
+ reinitprop(oform, "encoding", ES->strval("application/x-www-form-urlencoded"));
+ reinitprop(oform, "length", ES->numval(real form.nfields));
+ reinitprop(oform, "method", ES->strval(CU->hmeth[form.method]));
+ reinitprop(oform, "name", ES->strval(form.name));
+ reinitprop(oform, "target", ES->strval(form.target));
+ ffl := form.fields;
+ if(ffl != nil) {
+ velements := ES->get(ex, oform, "elements");
+ if(ES->isobj(velements)) {
+ oelements := ES->toObject(ex, velements);
+ for(i := 0; i < form.nfields; i++) {
+ field := hd ffl;
+ ffl = tl ffl;
+ vfield := fieldinstant(ex, field, oform);
+
+ # convert multiple fields of same name to an array
+ prev := ES->get(ex, oform, field.name);
+ if (prev != nil && ES->isobj(prev)) {
+ newix := 0;
+ ar : ref Obj;
+ if (ES->isarray(prev.obj)) {
+ ar = prev.obj;
+ vlen := ES->get(ex, ar, "length");
+ newix = ES->toInt32(ex, vlen);
+ } else {
+ # create a new array
+ ar = ES->mkobj(ex.arrayproto, "Array");
+ ES->varinstant(ar, ES->DontEnum|ES->DontDelete, "length", ref RefVal(ES->numval(real 2)));
+ ES->put(ex, oform, field.name, ES->objval(ar));
+ arrayput(ex, ar, 0, "", prev);
+ newix = 1;
+ }
+ arrayput(ex, ar, newix, "", vfield);
+ } else {
+ # first time we have seen a field of this name
+ ES->put(ex, oform, field.name, vfield);
+ }
+ # although it is incorrect to add field name to
+ # elements array (as well as being indexed)
+ # - gives rise to name clashes, e.g radio buttons
+ # do it because other browsers do and some fools use it!
+ arrayput(ex, oelements, i, field.name, vfield);
+ fields = (field, ES->toObject(ex, vfield)) :: fields;
+ }
+ }
+ }
+ for(el := form.events; el != nil; el = tl el) {
+ e := hd el;
+ hname := "";
+ case e.attid {
+ Lex->Aonreset =>
+ hname = "onreset";
+ form.evmask |= E->SEonreset;
+ Lex->Aonsubmit =>
+ hname = "onsubmit";
+ form.evmask |= E->SEonsubmit;
+ }
+ if(hname != "")
+ puthandler(ex, oform, hname, e.value);
+ }
+# form.events = nil;
+ # private variables
+ ES->varinstant(oform, ES->DontEnum|ES->DontDelete, "@PRIVformid",
+ ref RefVal(ES->numval(real form.formid)));
+ ES->varinstant(oform, ES->DontEnum|ES->DontDelete, "@PRIVframeid",
+ ref RefVal(ES->numval(real frameid)));
+ return (oform, fields);
+}
+
+fieldinstant(ex : ref Exec, field: ref Build->Formfield, oform: ref Obj) : ref Val
+{
+ ofield := mkhostobj(ex, "FormField");
+ reinitprop(ofield, "form", ES->objval(oform));
+ reinitprop(ofield, "name", ES->strval(field.name));
+ reinitprop(ofield, "value", ES->strval(field.value));
+ reinitprop(ofield, "defaultValue", ES->strval(field.value));
+ chkd := ES->false;
+ if((field.flags & Build->FFchecked) != byte 0)
+ chkd = ES->true;
+ reinitprop(ofield, "checked", chkd);
+ reinitprop(ofield, "defaultChecked", chkd);
+ nopts := len field.options;
+ reinitprop(ofield, "length", ES->numval(real nopts));
+ reinitprop(ofield, "selectedIndex", ES->numval(-1.0)); # BUG: search for selected option
+ ty : string;
+ case field.ftype {
+ Build->Ftext =>
+ ty = "text";
+ reinitprop(ofield, "value", ES->strval(field.value));
+ Build->Fpassword =>
+ ty = "password";
+ Build->Fcheckbox =>
+ ty = "checkbox";
+ Build->Fradio =>
+ ty = "radio";
+ Build->Fsubmit =>
+ ty = "submit";
+ Build->Fhidden =>
+ ty = "hidden";
+ Build->Fimage =>
+ ty = "image";
+ Build->Freset =>
+ ty = "reset";
+ Build->Ffile =>
+ ty = "fileupload";
+ Build->Fbutton =>
+ ty = "button";
+ Build->Fselect =>
+ ty = "select";
+ si := -1;
+ options := ES->mkobj(ex.arrayproto, "Array");
+ ES->varinstant(options, ES->DontEnum|ES->DontDelete, "length",
+ ref RefVal(ES->numval(real nopts)));
+ reinitprop(ofield, "options", ES->objval(options));
+ optl := field.options;
+ vfield := ES->objval(ofield);
+ for(i := 0; i < nopts; i++) {
+ opt := hd optl;
+ optl = tl optl;
+ oopt := mkhostobj(ex, "Option");
+ reinitprop(oopt, "index", ES->numval(real i));
+ reinitprop(oopt, "value", ES->strval(opt.value));
+ reinitprop(oopt, "text", ES->strval(opt.display));
+ # private variables
+ ES->put(ex, oopt, "@PRIVformfield", vfield);
+ if(opt.selected) {
+ si = i;
+ reinitprop(oopt, "selected", ES->true);
+ reinitprop(oopt, "defaultSelected", ES->true);
+ reinitprop(ofield, "selectedIndex", ES->numval(real i));
+ }
+ ES->put(ex, options, string i, ES->objval(oopt));
+ }
+ ES->put(ex, options, "selectedIndex", ES->numval(real si));
+ ES->put(ex, options, "@PRIVformfield", vfield);
+ options.host = me;
+ Build->Ftextarea =>
+ ty = "textarea";
+ }
+ reinitprop(ofield, "type", ES->strval(ty));
+ for(el := field.events; el != nil; el = tl el) {
+ e := hd el;
+ hname := "";
+ case e.attid {
+ Lex->Aonblur =>
+ hname = "onblur";
+ field.evmask |= E->SEonblur;
+ Lex->Aonchange =>
+ hname = "onchange";
+ field.evmask |= E->SEonchange;
+ Lex->Aonclick =>
+ hname = "onclick";
+ field.evmask |= E->SEonclick;
+ Lex->Aondblclick =>
+ hname = "ondblclick";
+ field.evmask |= E->SEondblclick;
+ Lex->Aonfocus =>
+ hname = "onfocus";
+ field.evmask |= E->SEonfocus;
+ Lex->Aonkeydown =>
+ hname = "onkeydown";
+ field.evmask |= E->SEonkeydown;
+ Lex->Aonkeypress =>
+ hname = "onkeypress";
+ field.evmask |= E->SEonkeypress;
+ Lex->Aonkeyup =>
+ hname = "onkeyup";
+ field.evmask |= E->SEonkeyup;
+ Lex->Aonmousedown =>
+ hname = "onmousedown";
+ field.evmask |= E->SEonmousedown;
+ Lex->Aonmouseup =>
+ hname = "onmouseup";
+ field.evmask |= E->SEonmouseup;
+ Lex->Aonselect =>
+ hname = "onselect";
+ field.evmask |= E->SEonselect;
+ }
+ if(hname != "")
+ puthandler(ex, ofield, hname, e.value);
+ }
+# field.events = nil;
+ # private variables
+ ES->varinstant(ofield, ES->DontEnum|ES->DontDelete, "@PRIVfieldid",
+ ref RefVal(ES->numval(real field.fieldid)));
+ return ES->objval(ofield);
+}
+
+# Make an event handler named hname in o, with given body.
+puthandler(ex: ref Exec, o: ref Obj, hname: string, hbody: string)
+{
+ c := ES->eval(ex, "function PRIVhandler() {" + hbody + "}");
+ hobj := getobj(ex, ex.global, "PRIVhandler");
+ if(hobj != nil) {
+ ES->put(ex, o, hname, ES->objval(hobj));
+ }
+}
+
+anchorinstant(ex : ref Exec, nm: string) : ref Val
+{
+ oanchor := mkhostobj(ex, "Anchor");
+ reinitprop(oanchor, "name", ES->strval(nm));
+ return ES->objval(oanchor);
+}
+
+# Build ensures that the anchor href has been made absolute
+linkinstant(ex: ref Exec, anchor: ref Build->Anchor, frameid: int) : ref Val
+{
+ olink := mkhostobj(ex, "Link");
+ u := anchor.href;
+ if(u != nil) {
+ if(u.frag != "")
+ reinitprop(olink, "hash", ES->strval("#" + u.frag));
+ host := u.host;
+ if(u.user != "" || u.passwd != "") {
+ host = u.user;
+ if(u.passwd != "")
+ host += ":" + u.passwd;
+ host += "@" + u.host;
+ }
+ reinitprop(olink, "host", ES->strval(host));
+ hostname := host;
+ if(u.port != "")
+ hostname += ":" + u.port;
+ reinitprop(olink, "hostname", ES->strval(hostname));
+ reinitprop(olink, "href", ES->strval(u.tostring()));
+ reinitprop(olink, "pathname", ES->strval(u.path));
+ if(u.port != "")
+ reinitprop(olink, "port", ES->strval(u.port));
+ reinitprop(olink, "protocol", ES->strval(u.scheme + ":"));
+ if(u.query != "")
+ reinitprop(olink, "search", ES->strval("?" + u.query));
+ }
+ if(anchor.target != "")
+ reinitprop(olink, "target", ES->strval(anchor.target));
+
+ for(el := anchor.events; el != nil; el = tl el) {
+ e := hd el;
+ hname := "";
+ case e.attid {
+ Lex->Aonclick =>
+ hname = "onclick";
+ anchor.evmask |= E->SEonclick;
+ Lex->Aondblclick =>
+ hname = "ondblclick";
+ anchor.evmask |= E->SEondblclick;
+ Lex->Aonkeydown =>
+ hname = "onkeydown";
+ anchor.evmask |= E->SEonkeydown;
+ Lex->Aonkeypress =>
+ hname = "onkeypress";
+ anchor.evmask |= E->SEonkeypress;
+ Lex->Aonkeyup =>
+ hname = "onkeyup";
+ anchor.evmask |= E->SEonkeyup;
+ Lex->Aonmousedown =>
+ hname = "onmousedown";
+ anchor.evmask |= E->SEonmousedown;
+ Lex->Aonmouseout =>
+ hname = "onmouseout";
+ anchor.evmask |= E->SEonmouseout;
+ Lex->Aonmouseover =>
+ hname = "onmouseover";
+ anchor.evmask |= E->SEonmouseover;
+ Lex->Aonmouseup =>
+ hname = "onmouseup";
+ anchor.evmask |= E->SEonmouseup;
+ }
+ if(hname != "")
+ puthandler(ex, olink, hname, e.value);
+ }
+ anchor.events = nil;
+ # private variable
+ ES->varinstant(olink, ES->DontEnum|ES->DontDelete, "@PRIVanchorid",
+ ref RefVal(ES->numval(real anchor.index)));
+ ES->varinstant(olink, ES->DontEnum|ES->DontDelete, "@PRIVframeid",
+ ref RefVal(ES->numval(real frameid)));
+
+ return ES->objval(olink);
+}
+
+imageinstant(ex: ref Exec, im: ref Build->Item.Iimage) : ref Val
+{
+ oim := mkhostobj(ex, "Image");
+ src := im.ci.src.tostring();
+ reinitprop(oim, "border", ES->numval(real im.border));
+ reinitprop(oim, "height", ES->numval(real im.imheight));
+ reinitprop(oim, "hspace", ES->numval(real im.hspace));
+ reinitprop(oim, "name", ES->strval(im.name));
+ reinitprop(oim, "src", ES->strval(src));
+ if(im.ci.lowsrc != nil)
+ reinitprop(oim, "lowsrc", ES->strval(im.ci.lowsrc.tostring()));
+ reinitprop(oim, "vspace", ES->numval(real im.vspace));
+ reinitprop(oim, "width", ES->numval(real im.imwidth));
+ if(im.ci.complete == 0)
+ done := ES->false;
+ else
+ done = ES->true;
+ reinitprop(oim, "complete", done);
+
+ el : list of Lex->Attr = nil;
+ if(im.genattr != nil)
+ el = im.genattr.events;
+ for(; el != nil; el = tl el) {
+ e := hd el;
+ hname := "";
+ case e.attid {
+ Lex->Aonabort =>
+ hname = "onabort";
+ im.genattr.evmask |= E->SEonabort;
+ Lex->Aondblclick =>
+ hname = "ondblclick";
+ im.genattr.evmask |= E->SEondblclick;
+ Lex->Aonerror =>
+ hname = "onerror";
+ im.genattr.evmask |= E->SEonerror;
+ Lex->Aonkeydown =>
+ hname = "onkeydown";
+ im.genattr.evmask |= E->SEonkeydown;
+ Lex->Aonkeypress =>
+ hname = "onkeypress";
+ im.genattr.evmask |= E->SEonkeypress;
+ Lex->Aonkeyup =>
+ hname = "onkeyup";
+ im.genattr.evmask |= E->SEonkeyup;
+ Lex->Aonload =>
+ hname = "onload";
+ im.genattr.evmask |= E->SEonload;
+ Lex->Aonmousedown =>
+ hname = "onmousedown";
+ im.genattr.evmask |= E->SEonmousedown;
+ Lex->Aonmouseout =>
+ hname = "onmouseout";
+ im.genattr.evmask |= E->SEonmouseout;
+ Lex->Aonmouseover =>
+ hname = "onmouseover";
+ im.genattr.evmask |= E->SEonmouseover;
+ Lex->Aonmouseup =>
+ hname = "onmouseup";
+ im.genattr.evmask |= E->SEonmouseup;
+ }
+ if(hname != "")
+ puthandler(ex, oim, hname, e.value);
+ }
+ if(im.genattr != nil)
+ im.genattr.events = nil;
+
+ # private variables
+ ES->varinstant(oim, ES->DontEnum|ES->DontDelete, "@PRIVimageid",
+ ref RefVal(ES->numval(real im.imageid)));
+ # to keep track of src as currently known in item
+# ES->varinstant(oim, ES->DontEnum|ES->DontDelete, "@PRIVsrc",
+# ref RefVal(ES->strval(src)));
+ return ES->objval(oim);
+}
+
+colorval(v: int) : ref Val
+{
+ return ES->strval(sys->sprint("%.6x", v));
+}
+
+# If the o.name is a recognizable color, return it, else dflt
+colorxfer(ex: ref Exec, o: ref Obj, name: string, dflt: int) : int
+{
+ v := ES->get(ex, o, name);
+ if(v == ES->undefined)
+ return dflt;
+ return CU->color(ES->toString(ex, v), dflt);
+}
+
+strxfer(ex : ref Exec, o: ref Obj, name: string, dflt: string) : string
+{
+ v := ES->get(ex, o, name);
+ if(v == ES->undefined)
+ return dflt;
+ return ES->toString(ex, v);
+}
+
+ScriptWin.new(f: ref Layout->Frame, ex: ref Exec, loc: ref Obj, par: ref ScriptWin) : ref ScriptWin
+{
+ return ref ScriptWin(f, ex, loc, ES->objval(ex.global), par, nil, nil, nil, "", "", "", 1, 0, 0, nil);
+}
+
+# Make a new ScriptWin with f as frame and new, empty
+# Window object as obj, to be a child window of sw's window.
+ScriptWin.addkid(sw: self ref ScriptWin, f: ref Layout->Frame)
+{
+ (cex, clocobj) := makeframeex(f);
+ csw := ScriptWin.new(f, cex, clocobj, sw);
+ wininstant(csw);
+ sw.kids = csw :: sw.kids;
+}
+
+ScriptWin.dummy(): ref ScriptWin
+{
+ f := ref Layout->Frame;
+ f.doc = ref Build->Docinfo;
+ f.doc.base = U->parse("");
+ f.doc.src = U->parse("");
+ (cex, clocobj) := makeframeex(f);
+ csw := ScriptWin.new(f, cex, clocobj, nil);
+ wininstant(csw);
+ return csw;
+}
+
+# Find the ScriptWin in the tree with sw as root that has
+# f as frame, returning nil if none.
+#ScriptWin.findbyframe(sw: self ref ScriptWin, f: ref Layout->Frame) : ref ScriptWin
+#{
+# if(sw.frame.id == f.id)
+# return sw;
+# for(l := sw.kids; l != nil; l = tl l) {
+# x := (hd l).findbyframe(f);
+# if(x != nil)
+# return x;
+# }
+# return nil;
+#}
+
+# Find the ScriptWin in the tree with sw as root that has
+# fid as frame id, returning nil if none.
+ScriptWin.findbyframeid(sw: self ref ScriptWin, fid: int) : ref ScriptWin
+{
+ if(sw.frame.id == fid)
+ return sw;
+ for(l := sw.kids; l != nil; l = tl l) {
+ x := (hd l).findbyframeid(fid);
+ if(x != nil)
+ return x;
+ }
+ return nil;
+}
+
+# Find the ScriptWin in the tree with sw as root that has
+# d as doc for the frame, returning nil if none.
+ScriptWin.findbydoc(sw: self ref ScriptWin, d: ref Build->Docinfo) : ref ScriptWin
+{
+ if(sw.frame.doc == d)
+ return sw;
+ for(l := sw.kids; l != nil; l = tl l) {
+ x := (hd l).findbydoc(d);
+ if(x != nil)
+ return x;
+ }
+ return nil;
+}
+
+# obj can either be the frame's Window obj, Location obj, or document obj,
+# or an Image object within the frame
+ScriptWin.findbyobj(sw : self ref ScriptWin, obj : ref Obj) : ref ScriptWin
+{
+ if (sw.locobj == obj || sw.ex.global == obj || obj == getdocobj(sw.ex, sw.frame.id))
+ return sw;
+ if(opener != nil && (opener.locobj == obj || opener.ex.global == obj))
+ return opener;
+ for(sil := sw.imgs; sil != nil; sil = tl sil) {
+ if((hd sil).obj == obj)
+ return sw;
+ }
+ for (l := sw.kids; l != nil; l = tl l) {
+ x := (hd l).findbyobj(obj);
+ if (x != nil)
+ return x;
+ }
+ return nil;
+}
+
+ScriptWin.findbyname(sw : self ref ScriptWin, name : string) : ref ScriptWin
+{
+ if (sw.frame != nil && sw.frame.name == name)
+ return sw;
+ for (l := sw.kids; l != nil; l = tl l) {
+ x := (hd l).findbyname(name);
+ if (x != nil)
+ return x;
+ }
+ return nil;
+}
+
+newcharon(url: string, nm: string, sw: ref ScriptWin)
+{
+ cs := chan of string;
+
+ spawn CH->startcharon(url, cs);
+ for(;;){
+ alt{
+ s := <- cs =>
+ if(s == "B")
+ continue;
+ if(s == "E")
+ exit;
+ (n, l) := sys->tokenize(s, " ");
+ case hd l{
+ "L" =>
+ sw.newloc = hd tl l;
+ sw.newloctarg = nm;
+ checknewlocs(sw);
+ }
+ }
+ }
+}