Recientemente me surgió la necesidad de controlar remotamente un explorador web en Windows. En el sistema operativo de Microsoft, gracias a los objetos COM, es posible «instanciar» un objeto InternetExplorer y controlarlo desde código fuente. Gracias a .NET y la Interoperabilidad con los objetos COM, la tarea inicialmente no parece ser para nada complicada, aunque como la compañía de Redmond nos tiene acostumbrados, lidiar con los detalles será una tarea que nos llevará bastante tiempo.
En OSX por su parte este objetivo también esta cubierto, y como pasa habitualmente, de una forma bastante más elegante :D.
La idea es poder controlar, y crear instancias de una aplicación que esta ejecutandose, o que vamos a iniciar. Para ello, como requisito imprescindible, la aplicación debe estar preparada para ser manejada.
El componente clave para que todo esto se pueda realizar son los denominados «Apple Event», que son paquetes de información que se envían en forma de mensaje entre las diferente aplicaciones que se están ejecutando en el Sistema Operativo.
Por ejemplo, si queremos que el Safari en ejecución navegue a una URL, y eso lo queremos hacer desde nuestra aplicación, deberemos enviar un Apple Event que contenga los datos necesarios para llevar a cabo esa acción.
Originalmente, solo era posible enviar eventos usando AppleScript, de hecho, el lenguaje se creo alrededor de dichos eventos. Pero hoy en día se puede utilizar multitud de lenguajes para enviar «Apple Events» como Python, C#, Ruby o Javascript.
Lo que hace posible esta interoperabilidad de lenguajes es lo que se conoce como el Scripting Bridge, como ya hablamos en el blog cuando queríamos usar Python para controlar el iTunes.
A diferencia de la entrada anterior, en esta entrada, vamos a usar Objecitve-C para controlar el Navegador Web, dado que ObjC es un lenguaje compilado, necesitamos tener una referencia de lo que podemos hacer con el «objeto» Safari, de tal manera que podamos compilar nuestro código.
Para ello usamos el comando sdp, que generará un fichero .h y que usaremos para compilar nuestro código. La entrada del comando sdp son los ficheros .sdef, que contienen, en forma de XML, una descripción sobre lo que se puede hacer con el objeto en cuestión.
A nivel de controlar el Navegador, una de las cosas mas utiles es que podemos ejecutar código javascript, con lo que tenemos acceso a todo el documento web que estamos visualizando.
A continuación os dejo un fragmento de código que navega a google, realiza una busqueda usando javascript y muestra el numero de resultados encontrados.
SafariApplication* safari = [SBApplication applicationWithBundleIdentifier:@"com.apple.Safari"]; SBElementArray* windows = [safari windows]; SafariTab* currentTab = [[windows objectAtIndex: 0] currentTab]; [currentTab setURL:@"http://www.google.com"]; [safari doJavaScript: @"document.getElementsByName('q')[0].value = 'Safari'" in:currentTab]; [safari doJavaScript: @"document.forms[0].submit()" in: currentTab]; id result = [safari doJavaScript:@"document.getElementById('resultStats').firstChild.data" in:currentTab]; NSLog(@"Results: %@", (NSString*)result);
Un uso en el que esta técnica es útil es cuando queremos hacer pruebas automatizadas de alguna web que estemos realizando. Aunque para estas tareas existe selenium, que si utilizamos firefox, seguramente cubra nuestras necesidades.