qira/web/client/vtimeline.js

382 lines
12 KiB
JavaScript

stream = io.connect(STREAM_URL);
// *** the analysis overlay ***
var overlays = {};
function update_picture(forknum) {
var vt = $('#vtimeline'+forknum);
if (vt.length == 0) return;
vt.css('background-image', "");
if (overlays[forknum] === undefined) return;
var cview = Session.get("cview");
if (cview === undefined) return;
var maxclnum = Session.get("max_clnum");
if (maxclnum === undefined) return;
var max = maxclnum[forknum];
var loading_scale = ((overlays[forknum][1]-overlays[forknum][0])*1.0)/(max[1]-max[0]);
vt.css('background-image', "url('"+overlays[forknum][2]+"')");
// so it looks like size is applied before position, hence we divide position by cscale
var cscale = get_cscale();
vt.css('background-size', "100% " + (max[1] / cscale) * loading_scale + "px")
vt.css('background-position-y', -1*((Math.max(max[0],cview[0])-max[0])/cscale) + "px");
vt.css('background-repeat', "no-repeat");
}
function on_setpicture(msg) { DS("setpicture");
//p(msg);
forknum = msg['forknum'];
overlays[forknum] = [msg['minclnum'], msg['maxclnum'], msg['data']];
update_picture(forknum);
} stream.on('setpicture', on_setpicture);
// *** functions for dealing with the zoom function ***
$(document).ready(function() {
$("#vtimelinebox")[0].addEventListener("mousewheel", function(e) {
var max = abs_maxclnum(); if (max === undefined) return;
var cview = Session.get("cview"); if (cview === undefined) return;
var cscale = get_cscale(); if (cscale === undefined) return;
var move = Math.round(cscale * 50.0); // 50 pixels
// clamping
if (e.wheelDelta > 0) {
if (cview[0] - move < 0) move = cview[0];
Session.set("cview", [cview[0] - move, cview[1] - move]);
} else {
if (cview[1] + move > max) move = max-cview[1];
Session.set("cview", [cview[0] + move, cview[1] + move]);
}
});
register_drag_zoom();
});
function register_drag_zoom() {
function get_clnum(e) {
if ($(".vtimeline").index(e.target) == -1 &&
e.target !== $("#vtimelinebox")[0]) return undefined;
var max = abs_maxclnum(); if (max === undefined) return;
var cview = Session.get("cview"); if (cview === undefined) return;
var cscale = get_cscale();
if (cscale === undefined) return undefined;
// fix for non full zoom
var clnum = (e.pageY * cscale) + cview[0];
var clret = Math.round(clnum);
if (clret > max) clret = max;
return clret;
}
function get_forknum(e) {
var fn = e.target.id.split("vtimeline")[1];
if (fn == "box") return -1;
return fdec(fn);
}
var down = -1;
var downforknum = -1;
$("#vtimelinebox").mousedown(function(e) {
if (e.button == 1) { zoom_out_max(); }
if (e.button != 0) return;
var clnum = get_clnum(e);
if (clnum === undefined) return;
down = clnum;
downforknum = get_forknum(e);
return false;
});
$("#vtimelinebox").contextmenu(function(e) {
// right click to delete forks
forknum = get_forknum(e);
if (forknum != -1) {
stream.emit("deletefork", forknum);
redraw_flags();
}
return false;
});
$("#vtimelinebox").mouseup(function(e) {
//p("mouseup");
if (e.button != 0) return;
var up = get_clnum(e);
if (up === undefined) return;
var forknum = get_forknum(e);
if (down != -1) {
// should check absolute length of drag, not clnums
//p("drag "+down+"-"+up);
if (down == up) {
if (forknum != -1) {
Session.set("clnum", down);
Session.set("forknum", forknum);
push_history("click vtimeline");
}
} else if (down < up) {
Session.set("cview", [down, up]);
push_history("zoom cview");
} else if (down > up) {
Session.set("cview", [up, down]);
push_history("zoom cview");
}
}
return false;
});
}
// *** functions for flags ***
var flags = {};
function get_cscale() {
var cview = Session.get("cview");
if (cview === undefined) return;
var range = cview[1] - cview[0];
var box = $("#vtimelinebox");
if (box.length == 0) return undefined;
var working_height = box[0].offsetHeight - 100;
//var scale = Math.ceil(range/working_height);
var scale = range/working_height;
/*var real_height = Math.ceil(range/scale);
var vtimelines = $('.vtimeline');
for (var i = 0; i < vtimelines.length; i++) {
vtimelines[i].style.height = real_height + "px";
}*/
//p("working height is "+working_height+" scale is "+scale);
return scale;
}
function redraw_vtimelines(scale) {
var cview = Session.get("cview");
if (cview === undefined) return;
var maxclnum = Session.get("max_clnum");
if (maxclnum === undefined) return;
// delete old ones that don't have a maxclnum anymore
$(".vtimeline").each(function(e) {
var forknum = $(this)[0].id.split("vtimeline")[1];
if (maxclnum[forknum] === undefined) {
$(this).remove();
}
});
remove_flags("zoom");
for (forknum in maxclnum) {
var vt = $('#vtimeline'+forknum);
var max = maxclnum[forknum];
if (max[0] < cview[0] && cview[0] < max[1]) { add_flag("zoom", forknum, cview[0]); }
if (max[0] < cview[1] && cview[1] < max[1]) { add_flag("zoom", forknum, cview[1]); }
if (vt.length == 0) {
$("#vtimelinebox").append($('<div class="vtimeline" id="vtimeline'+forknum+'"></div>'))
vt = $('#vtimeline'+forknum);
}
update_picture(forknum);
var range = Math.min(max[1], cview[1]) - Math.max(max[0], cview[0]);
range = Math.max(0, range);
var topp = 0;
if (maxclnum[forknum][0] > cview[0]) {
topp = Math.ceil((maxclnum[forknum][0] - cview[0])/scale);
}
var real_height = Math.ceil(range/scale);
vt[0].style.height = real_height + "px";
vt[0].style.top = topp + "px";
}
}
delete_all_forks = function() {
for (forknum in Session.get('max_clnum')) {
stream.emit("deletefork", fdec(forknum));
}
redraw_flags();
};
function redraw_flags() {
var cview = Session.get("cview");
if (cview === undefined) return undefined;
var maxclnum = Session.get("max_clnum");
if (maxclnum === undefined) return;
var cscale = get_cscale();
if (cscale === undefined) return;
$(".flag").remove(); // required if a vtimeline is off screen and not redrawn
redraw_vtimelines(cscale);
var colors = {
"bounds": "green",
"change": "blue",
"ciaddr": "#AA0000", // keep it alphabetical
"daddrr": "#888800",
"daddrw": "yellow",
"slice": "#000088",
"zoom": "gray"
};
var flags_out = {};
//var flag_count = 0;
for (arr in flags) {
if (flags[arr].length == 0) continue;
var classes = "flag";
var forknum = fdec(arr.split(",")[0]);
if (maxclnum[forknum] === undefined) continue;
var clnum = fdec(arr.split(",")[1]);
if (clnum < cview[0] || clnum > cview[1]) continue;
var flagpos = ((clnum-Math.max(maxclnum[forknum][0], cview[0]))/cscale);
sty = "";
if (flags[arr].length == 1) {
var col = colors[flags[arr][0]];
sty = "background-color:"+col+"; color:"+col;
}
else {
var haschange = false;
sty = "background: linear-gradient(to right"
var cols = flags[arr].sort()
for (var i = 0; i < cols.length; i++) {
if (cols[i]) haschange = true;
sty += ","+colors[cols[i]];
}
sty += ")";
// always put the current change in front
if (haschange) sty += ";z-index:2";
}
if (flags_out[forknum] === undefined) flags_out[forknum] = "";
flags_out[forknum] += '<div id="flag'+clnum+'" class="flag" style="'+sty+'; margin-top: '+flagpos+"px"+'">'+clnum+'</div>'
//flag_count += 1;
}
//p("drew "+flag_count+" flags");
for (forknum in flags_out) {
$('#vtimeline'+forknum).html(flags_out[forknum]);
}
}
function add_flag(type, forknum, clnum) {
var t = [forknum, clnum];
if (flags[t] !== undefined) flags[t].push(type);
else flags[t] = [type];
}
function remove_flags(type, forknum) {
for (arr in flags) {
var tforknum = fdec(arr.split(",")[0]);
if (forknum !== undefined && forknum != tforknum) continue;
var index = flags[arr].indexOf(type);
while (index != -1) {
flags[arr].splice(index, 1);
index = flags[arr].indexOf(type);
}
if (flags[arr].length == 0) delete flags[arr];
}
}
// these are public functions, no var
go_to_flag = function (next, data) {
var gclnum = Session.get("clnum");
var gforknum = Session.get("forknum");
//var idx = flags.indexOf((forknum, clnum));
var cls = [gclnum];
for (arr in flags) {
var forknum = fdec(arr.split(",")[0]);
var clnum = fdec(arr.split(",")[1]);
if (clnum == gclnum) continue;
if (data) {
if (flags[arr].indexOf("daddrr") != -1 ||
flags[arr].indexOf("daddrw") != -1) {
if (forknum == gforknum) cls.push(clnum);
}
} else {
if (flags[arr].indexOf("ciaddr") != -1) {
if (forknum == gforknum) cls.push(clnum);
}
}
}
cls.sort(function(a, b){return a-b});
var idx = cls.indexOf(gclnum);
if (idx == -1) return;
if (next) {
if (idx+1 < cls.length) {
Session.set("clnum", cls[idx+1])
}
} else {
if (idx-1 >= 0) {
Session.set("clnum", cls[idx-1])
}
}
push_history("go to flag");
};
Deps.autorun(function() { DA("updating bounds flags");
var maxclnum = Session.get("max_clnum");
if (maxclnum === undefined) return;
remove_flags("bounds");
for (forknum in maxclnum) {
forknum = fdec(forknum);
add_flag("bounds", forknum, maxclnum[forknum][0]);
add_flag("bounds", forknum, maxclnum[forknum][1]);
}
redraw_flags();
});
Deps.autorun(function() { DA("adding current change flag");
var forknum = Session.get("forknum");
var clnum = Session.get("clnum");
remove_flags("change");
add_flag("change", forknum, clnum);
redraw_flags();
});
Deps.autorun(function() { DA("emit getchanges for iaddr change");
var maxclnum = Session.get('max_clnum');
var cview = Session.get('cview');
var iaddr = Session.get('iaddr');
var clnum = Session.get('clnum');
var cscale = get_cscale();
stream.emit('getchanges', -1, iaddr, 'I', cview, cscale, clnum)
});
Deps.autorun(function() { DA("emit getchanges for daddr change");
var maxclnum = Session.get('max_clnum');
var cview = Session.get('cview');
var daddr = Session.get('daddr');
var clnum = Session.get('clnum');
var cscale = get_cscale();
stream.emit('getchanges', -1, daddr, 'L', cview, cscale, clnum)
stream.emit('getchanges', -1, daddr, 'S', cview, cscale, clnum)
});
function on_changes(msg) { DS("changes");
var types = {'I': 'ciaddr', 'L': 'daddrr', 'S': 'daddrw'};
var forknum = Session.get("forknum");
var clnums = msg['clnums'][forknum];
var type = types[msg['type']];
var clnum = Session.get('clnum');
// this should probably only be for the IDA plugin
if (msg['type'] == 'I' && clnums !== undefined && clnums.indexOf(clnum) == -1 && Session.get('dirtyiaddr') == true) {
var closest = undefined;
var diff = 0;
// if these are instructions and the current clnum isn't in the list
for (var i = 0; i < clnums.length; i++) {
var ldiff = Math.abs(clnums[i] - clnum);
if (closest == undefined || diff > ldiff) {
closest = clnums[i];
diff = ldiff;
}
}
//p("nearest change is "+closest);
if (closest !== undefined && closest !== clnum) {
Session.set("clnum", closest);
}
Session.set("dirtyiaddr", false);
}
remove_flags(type);
for (forknum in msg['clnums']) {
clnums = msg['clnums'][forknum];
for (var i = 0; i < clnums.length; i++) {
add_flag(type, forknum, clnums[i]);
}
}
redraw_flags();
} stream.on('changes', on_changes);
// fix the resize problem
window.onresize = redraw_flags;