(part 2) when scratching an itch becomes a stupid timesink

This is part 2 of a post about how adding a keyboard short-cut for the equation editor in google docs turned in to a week long mission into foreign lands such as google closure, chrome extension API, XPCwrappers and even a bit of C++ 

So the task of adding a keystroke for the insert new equation widget came down to 2 sub-tasks.
  1. catch the keystroke event
  2. call the application insert equation function.
I was pretty confident about being able to find a solution to the first problem, as there were examples on the web of google docs keystroke scripts, and that I had the power of the browser extension API. (insert crazed maniacal laughter, you probably need to write a few greasemonkey scripts to know what I mean ;-)

  And what's more is that you have the element of surprise. The web app is not expecting to be fucked with, and if you do it right, then the web app shouldn't even know that its being fucked with.

So I started with number 2 of the sub-tasks, "to call the application insert equation Javascript function."

Immediately my freight train of enthusiasm and limited Javascript ability ploughed into a snow-bank of issues.

The first problem is that google have properly obfuscated (or "optimised") the Javascript that they deliver to the browser in google docs. This in itself was quite a surprise because google web apps are typically open, standardized and have easily accessible points to interact with.

However google provide the closure compiler tool for Javascript optimisation and once a javascript file has had the closure compiler treatment with all the optimisation options set, the code is pretty grim and all the identifiers are meaningless, and there are multiple levels of indirection between function calls and the intention of the action you are trying to replicate, for example here are a couple of functions;

D.yaa = function (a) {
    ( !! a.g && a.g.g()) != ( !! a.b && a.b.g()) && hVa(this, a.b)
};
function eu(a) {
    return a.g.BG()
}

However when you are writing userscripts, or browser extensions, you have the power of the debugger ie firebug or chrome developer tools, and you can insert or delete DOM nodes, and you can on-the-fly replace whole chunks of incoming Javascript before the browser has read the content. So all is not lost, there are many paths to try before giving the goal up as lost! onward....

I presumed that somewhere in the code there would be a "insert-new-equation()" Javascript function so I set about tracking this down using the various step debuggers, I was looking to discover the locations of these functions;
event(insert equation menu item) -> a(b) ->c(d) -> e(f) -> insert(equation into doc)

and add in something like;
event(keystroke) -> insert(equation into doc)

However it seems that the insert-new-equation() function might actually be an insert("new-equation") function, and its pretty obvious that it would be more difficult to use that type of function if the strings are all meaningless. In addition it would be dependent on the choice of string that the closure compiler used for the "insert equation" function call.

At some point about a day after this it became obvious in the value of de-minimising the Javascript code, even if the function names were still meaningless. This is where the value of the chrome developer tools comes in. Chrome has built in Javascript de-minimising which works well with its code stepper.

To do something similar on firefox requires the use of a firefox plugin which de-minimizes the Javascript code on the fly so the debugger stepper already has the correct line numbers.

After a few days of on-off messing about with this, I decided I wanted a tool to change the Javascript on-the-fly so I could add debugging lines, and this ended up being a separate project called unFuck which is a chrome plugin to match URL strings and replace their contents during load-time.

At some point while digging about with closure compiled code, google updated their code version for google docs, and script src changed. This indicated that any solution dependent on the particulars of the code was going to get broken with upgrades so I decided to abandon this strategy and look to programmatically send an event to the menu item, rather than try to call the end-function myself, so rather than look for this function;
event(insert equation menu item) -> a(b) ->c(d) -> e(f) -> insert(equation into doc)

try and send an event that causes this function to trigger;
event(insert equation menu item) -> a(b) ->c(d) -> e(f) -> insert(equation into doc)

So onto the part 3... working out the peculiarities of the google docs (and chrome and firefox) event handling.







No comments:

Post a Comment

Don't be nasty. Being rude is fine.