JavaScript

Este guia descreve como acessar o JavaScript na página Web carregada, executar código JavaScript, injetar objetos .NET para chamar o .NET a partir do JavaScript e outras funcionalidades.

Executando JavaScript

O DotNetBrowser permite acessar e executar código JavaScript na página Web carregada.

Para acessar o JavaScript, certifique-se de que a página Web está completamente carregada e que o JavaScript está ativo.

Para executar código JavaScript, utilize o método IFrame.ExecuteJavaScript(string). Este método devolve uma Task que pode ser utilizada para obter o resultado da execução. Obter a propriedade Task.Result ou chamar o método Task.Wait() bloqueia a execução da thread atual até que a execução do JavaScript esteja concluída.

Por predefinição, o método devolve uma Task<object> que representa o resultado da execução. O resultado é null se o JavaScript devolver null ou undefined. A sobrecarga genérica deste método efetua uma conversão direta e lança uma ClassCastException se houver uma incompatibilidade de tipos. Se você precisar evitar isto, utilize a sobrecarga não genérica em combinação com o cast seguro, nomeadamente o operador as.

O exemplo abaixo executa o código JavaScript que devolve um título do documento:

string title = frame.ExecuteJavaScript<string>("document.title").Result;
Dim title As String = frame.ExecuteJavaScript(Of String)("document.title").Result

Você pode executar qualquer código JavaScript:

double number = frame.ExecuteJavaScript<double>("123").Result;
bool boolean = frame.ExecuteJavaScript<bool>("true").Result;
string str = frame.ExecuteJavaScript<string>("'Olá'").Result;
IJsObject window = frame.ExecuteJavaScript<IJsObject>("window").Result;
IElement body = frame.ExecuteJavaScript<IElement>("document.body").Result;
Dim number As Double = frame.ExecuteJavaScript(Of Double)("123").Result
Dim [boolean] As Boolean = frame.ExecuteJavaScript(Of Boolean)("true").Result
Dim str As String = frame.ExecuteJavaScript(Of String)("'Hello'").Result
Dim window As IJsObject = frame.ExecuteJavaScript(Of IJsObject)("window").Result
Dim body As IElement = frame.ExecuteJavaScript(Of IElement)("document.body").Result

Injetando JavaScript

É possível injetar um código JavaScript personalizado na página Web utilizando a propriedade IBrowser.InjectJsHandler. Este manipulador destina-se a ser injetado:

  • um objeto .NET no código JavaScript;
  • um código JavaScript personalizado para execução posterior antes de quaisquer scripts serem executados no frame específico.

Veja o exemplo de código abaixo:

browser.InjectJsHandler = new Handler<InjectJsParameters>(args =>
{
    IJsObject window = args.Frame.ExecuteJavaScript<IJsObject>("window").Result;
    window.Properties["myObject"] = new MyObject();
});
browser.InjectJsHandler = New Handler(Of InjectJsParameters)(Sub(args)
    Dim window As IJsObject = args.Frame.ExecuteJavaScript(Of IJsObject)("window").Result
    window.Properties("myObject") = New MyObject()
End Sub)

A classe MyObject pode ter o seguinte aspecto:

public class MyObject
{
    public string SayHelloTo(string firstName) => "Hello " + firstName + "!";
}
Public Class MyObject
    Public Function SayHelloTo(ByVal firstName As String) As String
        Return "Hello " & firstName & "!"
    End Function
End Class

Quando a propriedade é definida, é possível chamar métodos do objeto .NET injetado a partir do JavaScript:

window.myObject.SayHelloTo('John');

Este manipulador pode ser invocado várias vezes para a mesma moldura.

Evite executar um código JavaScript que modifique a árvore DOM da página Web que está sendo carregada. Não se deve utilizar a API DOM do DotNetBrowser para remover o frame para a qual este handler é invocado, caso contrário o processo de renderização é bloqueado.

Conversão de tipo

O JavaScript e o .NET funcionam com tipos primitivos diferentes. O DotNetBrowser implementa uma conversão automática de tipos de JavaScript para tipos .NET e vice-versa.

JavaScript para .NET

As seguintes regras são utilizadas para converter JavaScript em tipos .NET:

  • Os números JavaScript são convertidos para System.Double
  • JavaScript string - para System.String
  • JavaScript boolean - para System.Boolean
  • JavaScript null e undefined - para null
  • Os objetos JavaScript são envolvidos como IJsObject
  • Os objetos JavaScript DOM Node são agrupados como IJsObject e IEventTarget.

No exemplo de código acima, sabemos que o document.title é uma cadeia de caracteres, pelo que utilizamos a sobrecarga do método genérico com o parâmetro de tipo string .

.NET para JavaScript

As seguintes regras são utilizadas para converter tipos .NET em JavaScript:

  • System.Double - é convertido para JavaScript Number;
  • System.String - para JavaScript string;
  • System.Boolean - para JavaScript boolean;
  • .NET null - para JavaScript null;
  • IJsObject - para um objeto JavaScript adequado;
  • IEventTarget - para um objeto JavaScript DOM Node adequado;
  • System.Object será envolvido num objeto JavaScript.

DOM wrappers

De acordo com as regras da conversão automática de tipos, os objetos DOM do JavaScript são agrupados como IJsObject e IEventTarget. É possível trabalhar com os objetos DOM do JavaScript utilizando a API DOM do DotNetBrowser.

No exemplo de código abaixo, devolvemos o documento que representa um objeto DOM JavaScript. Neste caso, o tipo de valor de retorno pode ser definido como IJsObject ou IDocument:

IDoc document = frame.ExecuteJavaScript<IDocument>("document").Result;
Dim document As IDocument = frame.ExecuteJavaScript(Of IDocument)("document").Result
IJsObject document = frame.ExecuteJavaScript<IJsObject>("document").Result;
Dim document As IJsObject = frame.ExecuteJavaScript(Of IJsObject)("document").Result

Trabalhando com JsObject

Para trabalhar com objetos JavaScript a partir do código .NET, utilize a interface IJsObject . Esta interface permite trabalhar com as propriedades do objeto e chamar as suas funções.

Propriedades

Para obter nomes de propriedades de um objeto JavaScript, incluindo propriedades dos objetos protótipo, utilize a propriedade IJsObjectPropertyCollection.Names:

IEnumerable<string> propertyNames = jsObject.Properties.Names;
Dim propertyNames As IEnumerable(Of String) = jsObject.Properties.Names

Para verificar se o objeto JavaScript tem uma propriedade especificada, utilize o método IJsObjectPropertyCollection.Contains(string) :

bool has = jsObject.Properties.Contains("<property-name>");
Dim has As Boolean = jsObject.Properties.Contains("<property-name>")

Para obter um valor da propriedade do objeto JavaScript pelo seu nome, utilize Properties[string]. Ver o exemplo de código abaixo:

IJsObject document = frame.ExecuteJavaScript<IJsObject>("document").Result;
object title = document.Properties["title"];
Dim document As IJsObject = frame.ExecuteJavaScript(Of IJsObject)("document").Result
Dim title As Object = document.Properties("title")

As Properties[string] também podem ser utilizadas para definir as propriedades do objeto JavaScript. Por exemplo:

document.Properties["title"] = "Novo Título";
document.Properties("title") = "Novo Título"

O valor de retorno é System.Object que pode ser definido para o tipo pretendido. Para mais informações, consulte a seção Conversão de tipos.

É possível remover uma propriedade utilizando a seguinte abordagem:

bool success = jsObject.Properties.Remove("<property-name>");
Dim success As Boolean = jsObject.Properties.Remove("<property-name>")

Funções

Para chamar uma função com o nome e os argumentos necessários, utilize o método Invoke(string methodName, object... args) . O exemplo de código abaixo demonstra como chamar a função JavaScript document.getElementById() :

IJsObject element = document.Invoke("getElementById", "demo");
Dim element As IJsObject = document.Invoke("getElementById", "demo")

…o que é equivalente ao seguinte código em JavaScript:

var element = document.getElementById("demo");

O método lança JsException se ocorrer um erro durante a execução da função.

Tipo dinâmico

É possível efetuar chamadas para objetos JavaScript como se mostra abaixo:

dynamic document = Browser.MainFrame.ExecuteJavaScript("document").Result;
document.write("Olá mundo!");
Dim document As Object = Browser.MainFrame.ExecuteJavaScript("document").Result
document.write("Olá mundo!")

Eis um exemplo mais complexo que demonstra como trabalhar com XMLHttpRequest e enviar um pedido AJAX utilizando o tipo dinâmico : ligação tardia:

dynamic xhr = browser.MainFrame.ExecuteJavaScript("new XMLHttpRequest()").Result;
var url = "https://jsonplaceholder.typicode.com/todos/1";
xhr.open("GET", url, true);

// Executar função em readystatechange.
xhr.onreadystatechange = (Ação<dynamic>)((o) =>
{
    if (xhr.readyState == 4 && xhr.status == 200)
    {
        System.Console.WriteLine(xhr.responseText);
    }
});
// Enviar o nosso pedido.
xhr.send();

// Espera na thread principal para ver a resposta impressa.
Thread.Sleep(10000);
Dim xhr As Object
xhr = browser.MainFrame.ExecuteJavaScript("new XMLHttpRequest()").Result
Dim url = "https://jsonplaceholder.typicode.com/todos/1"
xhr.open("GET", url, true)

' Executar função em readystatechange.
xhr.onreadystatechange = Sub (o As Object)
    If xhr.readyState = 4 And xhr.status = 200
        System.Console.WriteLine(xhr.responseText.ToString())
    End If
End Sub
' Enviar o pedido.
xhr.send()

' Espera na thread principal para ver a resposta impressa.
Thread.Sleep(10000)

Trabalhar com funções JavaScript

Desde o DotNetBrowser 2.1, é possível trabalhar com as funções JavaScript diretamente a partir do código .NET e passar a referência a uma função do JavaScript para o .NET. Por exemplo:

IJsFunction alert = frame.ExecuteJavaScript<IJsFunction>("window.alert").Result;
alert?.Invoke(window, "Olá mundo!");
Dim alert As IJsFunction = frame.ExecuteJavaScript(Of IJsFunction)("window.alert").Result
alert?.Invoke(window, "Olá mundo!")

Trabalhando com promessas JavaScript

Desde o DotNetBrowser 2.7 você pode trabalhar diretamente com as promessas JavaScript sem necessidade de criar um wrapper sobre IJsObject. Por exemplo:

IJsPromise promise = browser.MainFrame
                            .ExecuteJavaScript<IJsPromise>("Promise.resolve(\"test\")")
                            .Result;

// Invoca algum código .NET quando a promessa é cumprida ou rejeitada.
promise.Then(o => Console.WriteLine("Promessa cumprida"),
             e => Console.WriteLine("Promessa rejeitada"));
Dim promise As IJsPromise =
    browser1.MainFrame.ExecuteJavaScript(
        Of IJsPromise)("Promise.resolve(""test"")").Result

' Invoca algum código .NET quando a promessa é cumprida ou rejeitada.
promise.Then(Sub(o) Console.WriteLine("Promessa cumprida"), 
             Sub(e) Console.WriteLine("Promessa rejeitada"))

Chamando o .NET a partir do JavaScript

Quando você passa um System.Object como um valor de propriedade ou um argumento ao chamar a função JavaScript, o objeto .NET será automaticamente convertido/envolvido num objeto JavaScript. Isso permite injetar objetos .NET em JavaScript e invocar os seus métodos públicos a partir do código JavaScript.

As amostras de código abaixo demonstram como passar um objeto .NET como um valor de propriedade e chamar o seu método público a partir do JavaScript:

public class MyObject {

    public string SayHelloTo(string firstName) {
        return "Hello " + firstName + "!";
    }
}
Public Class MyObject
    Public Function SayHelloTo(ByVal firstName As String) As String
        Return "Hello " & firstName & "!"
    End Function
End Class
IJsObject window = frame.ExecuteJavaScript<IJsObject>("window").Result;
window.Properties["MyObject"] = new MyObject();
Dim window As IJsObject = frame.ExecuteJavaScript(Of IJsObject)("window").Result
window.Properties("MyObject") = New MyObject()

Depois disso, você pode referir-se ao objeto e chamar o seu método a partir do código JavaScript:

consola.log(window.MyObject.SayHelloTo("John"));

A saída do console é o seguinte:

Olá João!

Conversão de tipo

A ponte JavaScript-.NET fornece a funcionalidade de conversão automática de tipos quando chama um método público do objeto .NET injetado a partir do JavaScript.

A biblioteca converte automaticamente o Number JavaScript para o tipo .NET necessário se for possível. Se detectarmos que o número fornecido não pode ser convertido, por exemplo, para um byte .NET sem perda de dados, a biblioteca lança uma exceção e notifica o JavaScript de que não existe nenhum método .NET adequado. Se o valor fornecido puder ser convertido sem perda de dados, a biblioteca converte-o e invoca o método .NET adequado.

Por exemplo, se você injetar o seguinte objeto .NET no JavaScript:

public sealed class MyObject {

    public int Method(int intValue) {
        return intValue;
    }
}
Public NotInheritable Class MyObject
    Public Function Method(ByVal intValue As Integer) As Integer
        Return intValue
    End Function
End Class

Depois disso, você pode chamá-lo a partir do JavaScript e passar o JavaScript Number valor que pode ser convertido para e Integer sem perda de dados:

window.MyObject.method(123);

No entanto, se você passar um valor Double que não possa ser convertido para um Integer sem perda de dados, você vai obter o seguinte erro:

window.MyObject.method(3.14); // <- erro

Injetar delegates

A outra maneira de chamar o .NET a partir do JavaScript é injetando delegates.

A ponte JavaScript-.NET permite que você associe um delegate a uma propriedade JavaScript. Esse delegate será tratado como uma função que pode ser invocada no código JavaScript.

Por exemplo, você pode registrar uma função JavaScript associada ao delegate utilizando o seguinte código:

IJsObject window = frame.ExecuteJavaScript<IJsObject>("window").Result;
if (window != null) {
    window.Properties["sayHello"] = (Func<string, string>) ((args) => "Hello, " + args[0]);
}
Dim window As IJsObject = frame.ExecuteJavaScript(Of IJsObject)("window").Result
If window IsNot Nothing Then
    window.Properties("sayHello") = CType(Function(args) "Hello, " & args(0), 
        Func(Of String, String))
End If

Agora, você pode invocar esta função em JavaScript da seguinte forma:

window.sayHello('John');

As regras de conversão de tipos não se aplicam aos delegates. O JavaScript Number é sempre interpretado como Double neste caso.

Mensagens do console

O DotNetBrowser permite receber todas as mensagens de saída enviadas para a Consola utilizando a função JavaScript console.log() . Você pode ouvir as mensagens com os seguintes níveis:

  • DEBUG
  • LOG
  • WARNING
  • ERROR

Para obter uma notificação sobre a receção de uma mensagem pelo Console, utilize o evento ConsoleMessageReceived. Veja o exemplo de código abaixo:

browser.ConsoleMessageReceived += (s, e) => {
    string consoleMessage = e.Message;
    ConsoleMessageReceivedEventArgs.MessageLevel level = e.Level;
};
AddHandler browser.ConsoleMessageReceived, Sub(s, e)
    Dim consoleMessage As String = e.Message
    Dim level As ConsoleMessageReceivedEventArgs.MessageLevel = e.Level
End Sub

Trabalhando com JSON

O DotNetBrowser JS - .NET Bridge API permite converter uma string que representa JSON num objeto/objetos JavaScript. O exemplo de código abaixo demonstra como criar um objeto JavaScript a partir de um string JSON:

string jsonString = "[123, \"Olá\"]";
IJsObject jsObject = browser.MainFrame.ParseJsonString<IJsObject>(jsonString);
Dim jsonString As String = "[123, ""Olá""]"
Dim jsObject As IJsObject = browser.MainFrame.ParseJsonString(Of IJsObject)(jsonString)

Se você tentar analisar o valor da string que não representa um JSON válido, é lançada uma JsException.

No exemplo de código acima, a cadeia de caracteres "[123, \"Olá\"]" a string JSON é convertida num objeto JavaScript apropriado. Neste caso, será convertido numa array.

É possível converter IJsObject numa string JSON utilizando o método de extensão ToJsonString(). Ele devolve uma string equivalente ao método JavaScript JSON.stringify():

string jsonRepresentation = jsObject.ToJsonString();
Dim jsonRepresentation As String = jsObject.ToJsonString()
Go Top