Language:

Categories

Getting Started

General Use

JavaScript Integration

WPF

Windows Forms

MonoMac

Unity

Changelogs

Important Changes in 1.7.5

.NET Language:

This article presents important changes in version 1.7.5 that require refactoring your code.

For a full list of new features and changes in version 1.7.5, read: What’s New in 1.7.5.

Breaking Changes

A list of changes in version 1.7.5 that require refactoring your code:

1. JSObject.Bind(...,JavascriptMethodEventHandler) is obsolete.

The obsolete JSObject.Bind was used to create a custom JavaScript method and bind a managed handler to it. The same method was used to create both synchronous and asynchronous methods that were bound to a JavascriptMethodEventHandler.

Version 1.7.5 introduces new, more efficient methods to create and handle custom JavaScript methods using either the regular Awesomium.NET CLR API or the Dynamic Language Runtime (DLR).

Together with the old JSObject.Bind, the following types and members are also obsolete:

The new regular API includes four (4) methods to create synchronous JavaScript methods and another four (4) to create asynchronous methods:

Create Synchronous JavaScript Methods:

  • JSObject.Bind(JavascriptMethodHandler)

    The new JavascriptMethodHandler returns a JSValue directly from the handler. You no longer need to check JavascriptMethodEventArgs.MustReturnValue since a JavascriptMethodHandler can only handle a synchronounous JavaScript method and you cannot use JavascriptMethodEventArgs.Result (which is also obsolete) since the result is returned directly from the JavascriptMethodHandler method.

    In this overload of JSObject.Bind, the name of the new custom JavaScript method added to the JavaScript Object, is determined by the name of the managed handler. Therefore, you cannot pass a method with a special name (such as an anonymous method or a lambda expression) to this overload (see documentation).

  • JSObject.Bind(JSFunctionHandler)

    JSFunctionHandler is a new type of method for handling synchronous JavaScript methods. Like JavascriptMethodHandler it also returns a result directly from the handler but unlike JavascriptMethodHandler, JSFunctionHandler only accepts an array of JSValue as arguments. This array is equivalent to JavascriptMethodEventArgs.Arguments passed to a JavascriptMethodHandler. This new method signature simplifies code and follows the JavaScript pattern.

    This overload of JSObject.Bind, also determines the name of the new custom JavaScript method, from the name of the managed handler (see above).

  • JSObject.Bind(String,JavascriptMethodHandler)

    This is similar to JSObject.Bind(JavascriptMethodHandler), but users can define the name of the new custom JavaScript method added to the JavaScript Object. This overload allows you to pass methods with a special name (such as an anonymous method or a lambda expression).

  • JSObject.Bind(String,JSFunctionHandler)

    This is similar to JSObject.Bind(JSFunctionHandler), but users can define the name of the new custom JavaScript method added to the JavaScript Object. This overload allows you to pass methods with a special name (such as an anonymous method or a lambda expression).

Create Asynchronous JavaScript Methods:

  • JSObject.BindAsync(JavascriptAsyncMethodHandler)

    The new JavascriptAsyncMethodHandler does not return a value (returns void). You no longer need to check JavascriptMethodEventArgs.MustReturnValue since a JavascriptAsyncMethodHandler can only handle an asynchronounous JavaScript method.

    In this overload of JSObject.BindAsync, the name of the new custom JavaScript method added to the JavaScript Object, is determined by the name of the managed handler. Therefore, you cannot pass a method with a special name (such as an anonymous method or a lambda expression) to this overload.

  • JSObject.BindAsync(JSFunctionAsyncHandler)

    JSFunctionAsyncHandler is a new type of method for handling asynchronous JavaScript methods. Like JavascriptAsyncMethodHandler it does not return a result but unlike JavascriptAsyncMethodHandler, JSFunctionAsyncHandler only accepts an array of JSValue as arguments. This array is equivalent to JavascriptMethodEventArgs.Arguments passed to a JavascriptAsyncMethodHandler. This new method signature simplifies code and follows the JavaScript pattern.

    This overload of JSObject.BindAsync, also determines the name of the new custom JavaScript method, from the name of the managed handler (see above).

  • JSObject.BindAsync(String,JavascriptAsyncMethodHandler)

    This is similar to JSObject.BindAsync(JavascriptAsyncMethodHandler), but users can define the name of the new custom JavaScript method added to the JavaScript Object. This overload allows you to pass methods with a special name (such as an anonymous method or a lambda expression).

  • JSObject.BindAsync(String,JSFunctionAsyncHandler)

    This is similar to JSObject.BindAsync(JSFunctionAsyncHandler), but users can define the name of the new custom JavaScript method added to the JavaScript Object. This overload allows you to pass methods with a special name (such as an anonymous method or a lambda expression).

Refactoring Example (CLR)

Old Method:
using ( JSObject myGlobalObject = webView.CreateGlobalJavascriptObject( 
    "myGlobalObject" ) )
{
    myGlobalObject.Bind( "myMethod", true, OnMyMethod );
    myGlobalObject.Bind( "myAsyncMethod", false, OnMyMethod );
}

private void OnMyMethod( object sender, JavascriptMethodEventArgs e )
{
    if ( !e.MustReturnValue )
        return;

    e.Result = "My response";
}
Using myGlobalObject As JSObject = webView.CreateGlobalJavascriptObject(
    "myGlobalObject")
    myGlobalObject.Bind("myMethod", True, AddressOf OnMyMethod)
    myGlobalObject.Bind("myAsyncMethod", False, AddressOf OnMyMethod)
End Using

Private Sub OnMyMethod(sender As Object, e As JavascriptMethodEventArgs)
    If Not e.MustReturnValue Then Return

    e.Result = "My response"
End Sub
New Method:
using ( JSObject myGlobalObject = webView.CreateGlobalJavascriptObject(
    "myGlobalObject" ) )
{
    // The method name is determined from the passed handler's name.
    // Therefore we cannot pass a lambda expression to these overloads.
    myGlobalObject.Bind( myMethod );
    myGlobalObject.BindAsync( myAsyncMethod );
}

private JSValue myMethod( object sender, JavascriptMethodEventArgs e )
{
    // Return result directly from the handler.
    return "My response";
}

// Demonstrating JSFunctionAsyncHandler.
private void myAsyncMethod( JSValue[] arguments )
{
    // The handler is called asynchronously and
    // it does not return a result.
}
Using myGlobalObject As JSObject = webView.CreateGlobalJavascriptObject(
    "myGlobalObject")
    myGlobalObject.Bind(myMethod)
    myGlobalObject.BindAsync(myAsyncMethod)
End Using

Private Function myMethod(sender As Object,
                          e As JavascriptMethodEventArgs) As JSValue
    ' Return result directly from the handler.
    Return "My response"
End Function

' Demonstrating JSFunctionAsyncHandler.
Private Sub myAsyncMethod(arguments() As JSValue)
    ' The handler is called asynchronously and
    ' it does not return a result.
End Sub

Refactoring Example (DLR)

Old Method:
dynamic myGlobalObject = (JSObject)webView.ExecuteJavascriptWithResult(
    "myGlobalObject" );
 
// Make sure we have the object.
if ( myGlobalObject == null )
    return;
 
using ( myGlobalObject )
{
    myGlobalObject.myMethod = (JavascriptMethodEventHandler)myMethod;
    myGlobalObject.myAsyncMethod = (JavascriptAsynchMethodEventHandler)myMethod;
}
Option Explicit Off

[...] 

Dim myGlobalObject As Object = CType(webView.ExecuteJavascriptWithResult(
    "myGlobalObject"), JSObject)
 
' Make sure we have the object. 
If myGlobalObject Is Nothing Then 
    Return 
End If
 
Using myGlobalObject
    myGlobalObject.myMethod = 
        CType(AddressOf OnCustomJavascriptMethod, JavascriptMethodEventHandler)
    myGlobalObject.myAsyncMethod = 
        CType(AddressOf OnCustomJavascriptMethod, JavascriptAsynchMethodEventHandler)
End Using
New Method:
// All JSValues can now be implicitly casted to JSObject, even if they do not
// represent a JavaScript object. See Breaking Change N.3 below.
dynamic myGlobalObject = (JSObject)webView.ExecuteJavascriptWithResult(
    "myGlobalObject" );
 
// Invalid JSObjects are converted to false.
if ( !myGlobalObject )
    return;
 
using ( myGlobalObject )
{
    myGlobalObject.myMethod = (JavascriptMethodHandler)myMethod;
    myGlobalObject.myAsyncMethod = (JSFunctionAsyncHandler)myAsyncMethod;
}
Option Explicit Off

[...] 

' All JSValues can now be implicitly casted to JSObject, even if they do not
' represent a JavaScript object. See Breaking Change N.3 below.
Dim myGlobalObject As Object = CType(webView.ExecuteJavascriptWithResult(
    "myGlobalObject"), JSObject)
 
' Invalid dynamic JSObjects are evaluated as False.
If Not myGlobalObject Then 
    Return
End If
 
Using myGlobalObject
    myGlobalObject.myMethod = 
        CType(AddressOf OnCustomJavascriptMethod, JavascriptMethodEventHandler)
    myGlobalObject.myAsyncMethod = 
        CType(AddressOf OnCustomJavascriptMethod, JSFunctionAsyncHandler)
End Using

Additional Resources:

2. JavascriptAsynchMethodEventHandler is obsolete.

The obsolete JavascriptAsynchMethodEventHandler was used with the Dynamic Language Runtime (DLR) to tell a JSObject to create an asynchronous custom JavaScript method and bind a managed handler to it.

Since there are now two (2) new method types for specifying asynchronous custom JavaScript method handlers, JavascriptAsynchMethodEventHandler is deprecated and cannot be used any more.

Additional Resources:

For examples of using the new available API, see the refactoring example above and also read:

3. JSValue is now a class.

Up until version 1.7.5, JSValue was a value type (struct). Starting with version 1.7.5, JSValue is now a reference type (class).

The following list explains some of the resons for this change:

  • Many new features have been added to JSValue that could not be implemented if JSValue remained an immutable value type. Some of them are the indexer that allows editing JavaScript arrays directly from JSValue and the unary and binary operators.
  • JSValue, even as it was before 1.7.5, did not qualify almost all of the requirements for remaining a structure. Beyond its size, the most important issue was that due to its extensive use with the Dynamic Language Runtime (DLR) supported by the Awesomium.NET Javascript Integration, JSValue was frequently being boxed/unboxed. Because boxes are objects that are allocated on the heap and are garbage-collected, too much boxing and unboxing can have a negative impact on the heap, the garbage collector, and ultimately the performance of the application. In contrast, no such boxing occurs as reference types are cast.
  • JSValue is the type that all JavaScript values are being converted to, when passed to and acquired from V8 or assigned to JSObject properties. Reference type assignments copy the reference, whereas value type assignments copy the entire value. Therefore, assignments of large reference types are cheaper than assignments of large value types, as JSValue was.

Refactoring:

This change should not affect user code in most scenrarios.

  • In the rare scenario that your code used CLR’s default parameterless constructror available with all value types, this code will have to be refactored since JSValue, as a reference type, does no contain a parameterless constructor:
Old Method:
// Create a local object.
JSObject myObject = new JSObject();
// Create a JavaScript value, using the parameterless constructor.
// This would create an uninitialized JSValue that Awesomium.NET
// would translate to JSValue.Undefined.
JSValue myValue = new JSValue();
// Would assign 'undefined' to 'myProperty'.
myObject[ "myProperty" ] = myValue;
' Create a local object.
Dim myObject As JSObject = New JSObject()
' Create a JavaScript value, using the parameterless constructor.
' This would create an uninitialized JSValue that Awesomium.NET
' would translate to JSValue.Undefined.
Dim myValue As JSValue = New JSValue()
' Would assign 'undefined' to 'myProperty'.
myObject("myProperty") = myValue
New Method:
// Create a local object.
JSObject myObject = new JSObject();
// There's no parameterless constructor to JSValue.
// You must explicitly specify the value. 
JSValue myValue = JSValue.Undefined;
// Will assign 'undefined' to 'myProperty'.
myObject[ "myProperty" ] = myValue;
' Create a local object.
Dim myObject As JSObject = New JSObject()
' There's no parameterless constructor to JSValue.
' You must explicitly specify the value. 
Dim myValue As JSValue = JSValue.Undefined
' Will assign 'undefined' to 'myProperty'.
myObject("myProperty") = myValue
  • As a reference type, JSValue is now nullable. Assigning or passing a null JSValue reference, will effectively assign or pass JSValue.Null:
// Create a local object.
JSObject myObject = new JSObject();
// Will assign 'null' to 'myProperty'.
myObject[ "myProperty" ] = null;
' Create a local object.
Dim myObject As JSObject = New JSObject()
' Will assign 'null' to 'myProperty'.
myObject("myProperty") = Nothing
  • A null JSValue reference can also be implicitly converted to Boolean:
JSValue myValue = null;

// Will be 'false'
if ( !myValue )
  return;
Dim myValue As JSValue = Nothing

' Will be 'false'
if Not CBool(myValue) Then Return

All members of the Awesomium.NET Javascript Integration API, are guaranteed to never return a null JSValue reference. In the worse case, they will return JSValue.Undefined.

Additional Resources:

4. All JSValue instances, can be implicitly converted to JSObject.

Due to changes in Awesomium.NET’s support of the Dynamic Language Runatime (DLR), all JSValue instances can now be implicitly converted to JSObject (or predefined subclasses of it such as JSFunction), even if they do not represent a JavaScript Object (or Function).

Until version 1.7.5, attempting to cast a JSValue that does not represent a JavaScript Object to JSObject, would return a null JSObject reference. Starting with version 1.7.5, this casting will return an invalid JSObject instance (JSObject.Type is Invalid) that is equivalent to JSValue.Undefined. This is mainly because new equality and conversion operators were added to both JSValue and JSObject that simplify code expecially when using the Dynamic Language Runtime (DLR). When such an invalid JSObject is checked against null, it will return true and when converted to Boolean, it will be converted to false:

The following example explains why this feature is added and how you can use the new operators:

// The returned value will be a JSValue of type Integer. However casting to
// JSObject will still succeed returning an invalid JSObject equivalent to
// JSValue.Undefined.
dynamic myObject = (JSObject)webView.ExecuteJavascriptWithResult( "0" );
// There's a new operator that converts JSObjects to Boolean. An invalid
// JSObject will be converted to false. Notice that the result above, is
// assigned to a weakly typed variable (dynamic). If casting to JSObject
// would assign 'null' to 'myObject' the CLR/DLR would not be able to know
// which type's operators to invoke to convert the value to Boolean and the
// following condition would fail with a message like:
// "Cannot convert null to Boolean".
if ( !myObject )
    return;
Option Explicit Off

[...]

' The returned value will be a JSValue of type Integer. However casting to
' JSObject will still succeed returning an invalid JSObject equivalent to
' JSValue.Undefined.
Dim myObject = CType(webView.ExecuteJavascriptWithResult("0"), JSObject)
' There's a new operator that converts JSObjects to Boolean. An invalid
' JSObject will be converted to false. Notice that the result above, is
' assigned to a weakly typed variable (dynamic). If casting to JSObject
' would assign 'null' to 'myObject' the CLR/DLR would not be able to know
' which type's operators to invoke to convert the value to Boolean and the
' following condition would fail with a message like:
' "Cannot convert Nothing to Boolean" or "Cannot find a Not unary operator
' for Nothing".
If Not myObject Then Return

Refactoring:

This change actually simpilfies code and it should not affect user code in most scenrarios. However, in the rare scenario that your code uses other methods than simple equality operations to check if a JSObject equals to null, these methods may produce unexpected results:

Here’s a summary of the new features and potentially needed refactoring:

JSObject myObject = null;
// A strongly typed (JSObject) variable that has been assigned a null reference,
// will be successfully converted to Boolean. The CLR knows to invoke the static
// operators of JSObject:
if ( myObject )
{
    // Will never reach here. 'myObject' is null so it's converted to 'false'.
    return;    
}

// The returned value will be a JSValue of type Integer. However casting to
// JSObject will still succeed returning an invalid JSObject equivalent to
// JSValue.Undefined.
myObject = webView.ExecuteJavascriptWithResult( "0" );

// When an invalid JSObject is checked against 'null', it will return 'true':
if ( myObject != null )
{
    // Will never reach here. 'myObject' is not actually a null reference
    // but it is invalid.
    return;
}

if ( !Object.ReferenceEquals( myObject, null ) )
{
    // HOWEVER THIS CODE WILL BE REACHED! Operator overloads are not 
    // called for methods such as 'Object.ReferenceEquals'. Although 
    // invalid, 'myObject' is not actually a null reference.
    System.Diagnostics.Debug.Print( "Reached Here" );
}
Dim myObject As JSObject = Nothing
' A strongly typed (JSObject) variable that has been assigned a null reference,
' will be successfully converted to Boolean. The CLR knows to invoke the static
' operators of JSObject:
If CBool(myObject) Then
    ' Will never reach here. 'myObject' is null so it's converted to 'False'.
    Return    
End If

' The returned value will be a JSValue of type Integer. However casting to
' JSObject will still succeed returning an invalid JSObject equivalent to
' JSValue.Undefined.
myObject = webView.ExecuteJavascriptWithResult("0")

' When an invalid JSObject is checked against 'null', it will return 'true':
If myObject IsNot Nothing Then
    ' Will never reach here. 'myObject' is not actually a null reference
    ' but it is invalid.
    Return
End If

If Not Object.ReferenceEquals(myObject, Nothing) Then
    ' HOWEVER THIS CODE WILL BE REACHED! Operator overloads are not 
    ' called for methods such as 'Object.ReferenceEquals'. Although 
    ' invalid, 'myObject' is not actually a null reference.
    System.Diagnostics.Debug.Print("Reached Here")
End If

Additional Resources:

Important Changes

The following changes do not require refactoring your code but your application will perform better and your code can be simplified by taking these changes into consideration.

1. The behavior and signature of the DocumentReady event, has changed.

  • Until version 1.7.5, a UrlEventHandler method was used to handle a DocumentReady event. Starting with version 1.7.5, the new DocumentReadyEventHandler method is used to handle a DocumentReady event.

    DocumentReadyEventHandler accepts a DocumentReadyEventArgs as event arguments that provide data for the event. DocumentReadyEventArgs still derives UrlEventArgs so old UrlEventHandler methods assigned as handlers of a DocumentReady event will keep on working but user code will not have access to many new features provided by the new event handler arguments.

  • Starting with version 1.7.5, the DocumentReady event may be fired more than once when a web-page is being loaded (or edited through JavaScript). DocumentReady is fired every time the ready-state of the loaded page changes and the new DocumentReadyEventArgs passed to your DocumentReadyEventHandler method, contains a ReadyState property that informs you if the DOM is still being loaded or if it is completely loaded.

    Until version 1.7.5, users used to handle the LoadingFrameComplete waiting for IsMainFrame to be true before executing JavaScript that required access to the page’s DOM. This was because as noted, DocumentReady was being fired too early (it was equivalent to DOMContentLoaded). This technique is now not suggested and it is actually discouraged for the following reasons:

    • DocumentReady is now fired more than once and it is guaranteed to fire when the DOM is completely loaded and ready (ReadyState will be Loaded).
    • LoadingFrameComplete is not fired in a Javascript Execution Context (JEC) since it is not a Javascript-related event. Features available to methods executed in a JEC, are not available to a LoadingFrameComplete event handler.
    • The main frame of a page (see: IsMainFrame) is usually the last to load when a page is being loaded but this is not guaranteed.
    • A page that has all its HTML content loaded, doesn’t necessarily have a completely loaded DOM. A view created with JavaScript window.open specifying no target URL, does not have a ready DOM until JavaScript code loads something into the new window or uses document.write, document.close.
  • Handlers of a DocumentReady event are now executed in an asynchronous Javascript Execution Context (JEC). This technology is new in version 1.7.5. Methods executed in a JEC, enjoy certain benefits such as:

    • JSObject instances created or acquired in a JEC, do not need to be explicitly disposed. They are automatically disposed upon exiting the method associated with the JEC.
    • Errors or exceptions that occur within a JEC, are silently handled and propagated to the JavaScript console.
    • Code executing in an asynchronous JEC, has immediate access to essential objects of the loaded page’s current JavaScript environment (such as window and document) through the new Global class, without any need to perform additional synchronous calls to the child-process to acquire these objects. In particular, the DocumentReadyEventArgs.Environment property provides an instance of Global when DocumentReadyEventArgs.ReadyState is Loaded.

Refactoring:

It is strongly suggested that you revise your code taking advantage of the new features provided to the DocumentReady event. The following example shows how the DocumentReady can be handled:

webView.DocumentReady += OnDocumentReady;

[...]

private void OnDocumentReady( Object sender, DocumentReadyEventArgs e )
{
    // If your code needs to access and manipulate the DOM,
    // wait for it be fully loaded.
    if ( e.ReadyState == DocumentReadyState.Ready )
        return;

    // DOM is fully loaded. Access essential objects
    // through Global.
    var global = e.Environment;

    // Implicit casting to Boolean available.
    if ( !global )
        return;

    // Create a local object whose own properties specify data 
    // property descriptors to be added to a remote object, 
    // with the corresponding property names and values.
    var props = new JSObject();
    // Specify the property name, the data property descriptor and initial value.
    props[ "myProperty",
           new JSPropertyDescriptor() { Writable = false } ] = "Ready";
    // Create a remote object using generics of the global Object and
    // assign it to a global variable. Note that members of the Global
    // class, are exposed as 'dynamic'.
    global.window.myVar = global.Object.create( global.Object.prototype, props ); 

    // More examples.
    var myDiv = global.document.getElementById( "myDiv" );

    if ( !myDiv )
        return;

    myDiv.innerHTML = "Loaded";

    // Note that we do not not explcitly dispose any JSObject
    // (by wrapping code in 'using' statements for example).
    // All JSObjects created or acquired in a JEC, are
    // automatically disposed upon exiting the method.
}
Option Explicit Off

[...]

AddHandler webView.DocumentReady, AddressOf OnDocumentReady

[...]

Private Sub OnDocumentReady(sender As Object, e As DocumentReadyEventArgs)
    ' If your code needs to access and manipulate the DOM,
    ' wait for it be fully loaded.
    If e.ReadyState = DocumentReadyState.Ready Then Return

    ' DOM is fully loaded. Access essential objects
    ' through Global.
    var js = e.Environment;

    ' Implicit casting to Boolean available.
    If Not CBool(global) Then Return

    ' Create a local object whose own properties specify data 
    ' property descriptors to be added to a remote object, 
    ' with the corresponding property names and values.
    Dim props As New JSObject()
    ' Specify the property name, the data property descriptor and initial value.
    props("myProperty",
          New JSPropertyDescriptor() With { .Writable = false }) = "Ready"
    ' Create a remote object using generics of the global Object and
    ' assign it to a global variable. Note that members of the Global
    ' class, are exposed as 'dynamic'.
    global.window.myVar = global.Object.create(global.Object.prototype, props)

    ' More examples.
    Dim myDiv = global.document.getElementById("myDiv")

    If Not myDiv Then Return

    myDiv.innerHTML = "Loaded"

    ' Note that we do not not explcitly dispose any JSObject
    ' (by wrapping code in 'Using' statements for example).
    ' All JSObjects created or acquired in a JEC, are
    ' automatically disposed upon exiting the method.
End Sub

Additional Resources: