WebAssembly in Action

Author of the book "WebAssembly in Action"
Save 40% with the code: ggallantbl
The book's original source code can be downloaded from the Manning website and GitHub. The GitHub repository includes an updated-code branch that has been adjusted to work with the latest version of Emscripten (currently version 3.1.44).
Showing posts with label JavaScript. Show all posts
Showing posts with label JavaScript. Show all posts

Monday, January 30, 2023

The State of WebAssembly - 2022 and 2023

The State of WebAssembly – 2022 and 2023. For the third year in a row, I've had the privilege of writing an article on the state of WebAssembly. In this article, I started by revisiting developments during 2022 to see if any of my predictions came true and if there were any surprises. Then I tried to predict where I think things will go in 2023.
For the third year in a row, I've had the privilege of writing an article on the state of WebAssembly. In this article, I started by revisiting developments during 2022 to see if any of my predictions came true and if there were any surprises. Then I tried to predict where I think things will go in 2023.

2022 didn't really feel like it had a lot of movement as far as features being released go. However, it did feel like there were a lot of things coming into place for what's to come. I think 2023 is going to be really exciting for WebAssembly and even for JavaScript.

The article can be found here: The State of WebAssembly - 2022 and 2023

Thursday, December 21, 2017

WebAssembly – Caching to HTML5 IndexedDB


Update: September 1, 2018

Originally, the specification for WebAssembly called for explicit caching of a compiled WebAssembly module to HTML5 IndexedDB.

Firefox and Edge added support for serialization to IndexedDB but, after some discussion between the WebAssembly Community Group, browser makers, and others, it has been decided that it will be best for browsers to implicitly cache WebAssembly modules instead.

As a result of this decision, Firefox 63, which will ship on October 23rd, 2018 will no longer allow WebAssembly modules to be cached to IndexedDB (https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/63)

The content of this article should not be used and any explicit caching already in place in your code should be removed.

This article is a continuation of a series exploring how we can build and work with WebAssembly modules using Emscripten. The previous articles are not required reading to understand what we're going to cover today but, if you're curious, you can find them here:
Today we're going to continue using a bare-bones WebAssembly module (no Emscripten built-in helper methods) just to keep things as clear as possible as we dig into an important topic.

One of the main reasons why we would want to use a WebAssembly module in the first place is for the performance improvements that it brings but we haven't yet made use of one key performance item.

Typically, when you go to a webpage that has a JavaScript file, the browser will cache it so that it doesn't have to download it again. The next time you go to that same webpage, if the file is in the browser's cache, it will load that rather than pull it from the server which saves time.

If you've been watching your network traffic, while working with the WebAssembly examples so far, you may have noticed that the wasm file is requested every time your page is loaded which isn't desired if the module hasn't changed.


HTML5 IndexedDB

WebAssembly modules were created with the ability to cache the compiled module in mind but the trick is that the caching is something that needs to be done explicitly by us.

In JavaScript, modules are cached to IndexedDB.

Up until this point, with all of my WebAssembly testing, I've simply been using the file system and double clicking on the html file to test things in the browser.

For security reasons, however, browsers will not allow websites to access IndexedDB from a local file which means we need to set up a server of some sort. I'm a developer on Windows so I'm going to use IIS.

IIS has a whitelist of file extensions that it will allow a website to provide and .wasm was not in my list which resulted in a 404 error when the page tried to fetch the wasm file.

Adding .wasm to the list in IIS is fairly simple:
  • Open up Internet Information Services (IIS) Manager (found in Control Panel, Administrative Tools)
  • You can set this at the root, Default Web Site, or at the individual Application level
  • Double click on the MIME Types link
  • Click the Add... link on the Actions pane to the right
  • Enter wasm for the file name extension
  • Enter application/wasm for the MIME type
  • Click OK


Caching

So far, we've been working with the module's instance but the result object from the WebAssembly.instantiate call also returns a module object which is the compiled module that we can cache in an IndexedDB database:

// Request the wasm file from the server and compile it... fetch(sWasmURI).then(response =>
response.arrayBuffer()
).then(bytes =>
WebAssembly.instantiate(bytes, g_importObject)
).then(results => {
// We've been working with the .instance object so far
objModuleInstance = results.instance;

// The results object also holds a .module object which is
// what we can cache:
// results.module
});

When we retrieve the compiled module from the cache, we will need to pass it to WebAssembly.instantiate but there are a couple of differences compared to when we download the file.

The first difference is that we don't need to do a fetch or set up an arrayBuffer.

The second difference is that the return object from the instantiate object is the instance itself.

WebAssembly.instantiate(objModule, g_importObject).then(instance =>
g_objModuleInstance = instance
);


The following is some example code that shows how you can work with IndexedDB to cache and load WebAssembly modules:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<input type="button" value="Test" onclick="OnClickTest();" />

<script src="IndexedDB.js"></script>
<script type="text/javascript">
var g_importObject = {
'env': {
'memoryBase': 0,
'tableBase': 0,
'memory': new WebAssembly.Memory({ initial: 256 }),
'table': new WebAssembly.Table({ initial: 0, element: 'anyfunc' })
}
};

// The WebAssembly module instance that we'll be working with
var g_objModuleInstance = null;

// If we need to change the structure of the database, we can
// increment the DB_VERSION value to trigger the
// onupgradeneeded event when opening the database
var DB_VERSION = 1;
var DB_NAME = "WasmCache";
var DB_OBJSTORE_MODULES = "Modules";

// We've set things up in such a way so that each wasm file can
// have a version and we only clear the items from the cache if
// the version doesn't match
var g_sTestWasmURI = "test.wasm";
var g_sTestWasmVersion = "1.0.0";

// Check to see if the module is cached and, if so, use that.
// Otherwise, download the module and cache it.
GetCompiledModuleFromIndexedDB(g_sTestWasmURI, g_sTestWasmVersion);


function GetCompiledModuleFromIndexedDB(sWasmURI, sWasmVersion) {

// If we successfully opened the database then...
OpenDB(DB_NAME, DB_VERSION, HandleUpgradeDB).then(dbConnection => {

// If we successfully obtained the requested record then...
GetRecordFromObjectStore(dbConnection, DB_OBJSTORE_MODULES, sWasmURI).then(objRecord => {

// If the version stored for this module doesn't match the
// version we need then the module cached is out of date...
if (objRecord.WasmVersion !== sWasmVersion) {

// Have the record deleted and then fetch the proper file
DeleteRecordFromObjectStore(dbConnection, DB_OBJSTORE_MODULES, sWasmURI).then(result => {
LoadWebAssemblyFromFile(dbConnection, sWasmURI, sWasmVersion);
});

}
else { // The cached record is the version we need...

// Have the module instantiated.
//
// NOTE: Unlike when we pass in the bytes to instantiate
// in the LoadWebAssemblyFromFile method below, we
// don't have a separate 'instance' and 'modules' object
// returned in this case since we started out with the
// module object. We're only passed back the instance in
// this case.
WebAssembly.instantiate(objRecord.WasmModule, g_importObject).then(instance =>
g_objModuleInstance = instance // Hold onto the module's instance so that we can reuse it
);

}

}, sErrorMsg => { // Error in GetRecordFromObjectStore...

// We weren't able to pull the module from cache (most
// likely because it doesn't exist yet - hasn't been cached
// yet). Log the error and then fetch the file.
console.log(sErrorMsg);
LoadWebAssemblyFromFile(dbConnection, sWasmURI, sWasmVersion);

});

}, sErrorMsg => { // Error in OpenDB...

// Log the error and then fetch the file (won't be able to
// cache it in this case because we don't have a database
// connection to work with)
console.log(sErrorMsg);
LoadWebAssemblyFromFile(null, sWasmURI, sWasmVersion);

});

}

// Called by indexeddb if the database was just created or if the
// database version was changed
function HandleUpgradeDB(evt) {
// Create the object store which will hold 3 properties:
// • WasmURI - (primary key) e.g. 'test.wasm'
// • WasmVersion - e.g. '1.0.1'
// • WasmModule - the compiled module
var dbConnection = evt.target.result;
dbConnection.createObjectStore(DB_OBJSTORE_MODULES, { keyPath: "WasmURI" });
}

function LoadWebAssemblyFromFile(dbConnection, sWasmURI, sWasmVersion) {
// Request the wasm file from the server and compile it...
fetch(sWasmURI).then(response =>
response.arrayBuffer()
).then(bytes =>
WebAssembly.instantiate(bytes, g_importObject)
).then(results => {
// Hold onto the module's instance so that we can reuse it
g_objModuleInstance = results.instance;

// Only do the following if we have a database connection
// object (this method will be passed a null if we failed to load
// the module from cache due to an error when trying to open
// the database)
if (dbConnection !== null) {
// WARNING: Not all browsers that support WebAssembly
// also support the ability to store the module in IndexedDB
// (seems to work fine in Edge 16 and in Firefox but it
// doesn't work for me in Chrome 63)
try {
// Create the object we're about to store
var objRecord = { "WasmURI": sWasmURI, "WasmVersion": sWasmVersion, "WasmModule": results.module };

// Cache the compiled module so that we don't have to
// pull the file from the server again unless we change
// the module's version number.
SaveRecordToObjectStore(dbConnection, DB_OBJSTORE_MODULES, objRecord);
}
catch (ex) {
console.log(`Unable to save the WebAssembly module to IndexedDB: ${ex.message}`);
}
}
});
}

function OnClickTest() {
// Call the module's add method and display the results
var iResult = g_objModuleInstance.exports._add(1, 2);
alert(iResult.toString());
}
</script>
</body>
</html>

The following is the content of our IndexDB.js file:

// Helper methods to work with an IndexedDB database
//
// Note: IndexedDB methods are asynchronous. To make things a bit
// easier to work with for the calling code, I've added Promises.

function OpenDB(sDatabaseName, sDatabaseVersion, fncUpgradeDB) {
return new Promise(function (fncResolve, fncReject) {

// Make a request for the database to be opened
var dbRequest = indexedDB.open(sDatabaseName, sDatabaseVersion);

dbRequest.onerror = function (evt) { fncReject(`Error in OpenDB: ${evt.target.error}`); }

// Pass the database connection object to the resolve method of the
// promise
dbRequest.onsuccess = function (evt) { fncResolve(evt.target.result); }

// This event handler will only be called if we're creating the database
// for the first time or if we're upgrading the database to a new
// version (this will be triggered before the onsuccess event handler
// above if it does get called). Let the calling code handle upgrading
// the database if needed to keep this file as generic as possible.
dbRequest.onupgradeneeded = fncUpgradeDB;

});
}


// Helper method to simplify the code some
function GetObjectStore(dbConnection, sObjectStoreName, sTransactionMode) {
// Create a transation and, from the transaction, get the object store
// object
return dbConnection.transaction([sObjectStoreName], sTransactionMode).objectStore(sObjectStoreName);
}


function GetRecordFromObjectStore(dbConnection, sObjectStoreName, sRecordID) {
return new Promise(function (fncResolve, fncReject) {

// Request the record specified
var dbGetRequest = GetObjectStore(dbConnection, sObjectStoreName, "readonly").get(sRecordID);

dbGetRequest.onerror = function (evt) { fncReject(`Error in GetRecordFromObjectStore: ${evt.target.error}`); }

dbGetRequest.onsuccess = function (evt) {
// If we have a record then...(we have to check because there
// won't be a record if the database was just created)
var objRecord = evt.target.result;
if (objRecord) { fncResolve(objRecord); }
else { fncReject(`The record '${sRecordID}' was not found in the object store '${sObjectStoreName}'`); }
}

});
}


function DeleteRecordFromObjectStore(dbConnection, sObjectStoreName, sRecordID) {
return new Promise(function (fncResolve, fncReject) {

// Request the delete of the record specified
var dbDeleteRequest = GetObjectStore(dbConnection, sObjectStoreName, "readwrite").delete(sRecordID);

dbDeleteRequest.onerror = function (evt) { fncReject(`Error in DeleteRecordFromObjectStore: ${evt.target.error}`); }

dbDeleteRequest.onsuccess = function (evt) { fncResolve(); }

});
}


function SaveRecordToObjectStore(dbConnection, sObjectStoreName, objRecord) {

// Request the put of our record (if it doesn't already exist, it gets added. otherwise, it gets updated)
var dbPutRequest = GetObjectStore(dbConnection, sObjectStoreName, "readwrite").put(objRecord);

dbPutRequest.onerror = function (evt) { console.log(`Error in SaveToIndexedDB: ${evt.target.error}`); }

dbPutRequest.onsuccess = function (evt) { console.log(`Successfully stored the record`); }

}


The following is the C code and command line needed to turn the C code into a WebAssembly module for today's article:

int add(int x, int y) { return x + y; }

emcc test.c -s WASM=1 -s SIDE_MODULE=1 -O1 -o test.wasm


Summary

Even though we tried to keep the code as clean as possible, this article was a bit more involved because of everything that is involved when working with IndexedDB databases.

Because this article was focused around WebAssembly caching, we only dug into IndexedDB as deep as was needed.

Fortunately, I had the privilege of writing a DZone Refcard on HTML5 IndexedDB a little while ago that you're more than welcome to check out if you would like more information on the technology.


Edit made on Monday, January 1st, 2018: Changed the MIME Type from application/octet-stream to application/wasm after discovering the WebAssembly meeting notes from October 23rd, 2017 proposing the MIME Type be submitted to IANA to make it official.

Saturday, December 16, 2017

WebAssembly – Calling into JavaScript from bare bones C code


In my previous blog post, we explored the idea of creating a bare bones WebAssembly module with no Emscripten plumbing. In that article, we were able to call into the module from JavaScript but we didn't get into the reverse:

How does one call into JavaScript from a module?


Calling into JavaScript from a WebAssembly module

In this article we're going to try and get a WebAssembly module to call a method that's defined in our JavaScript.

In order to compile C code, when the method being used isn't in the source code, you need to define the method signature using the extern keyword as shown in the following example:

// Define the JavaScript method's signature that we're going to be calling.
extern void CallJS(int iVal);

// A method that the JavaScript will call into to trigger our code
// that will in turn call a JavaScript method passing along the value
// received.
void Test(int iVal){ CallJS(iVal); }

You can run the following command line to build the wasm file:

emcc test.c -s WASM=1 -s SIDE_MODULE=1 -O1 -o test.wasm

To define the method in our JavaScript, we just need to add it to the env object that is part of the main object that we pass as the 2nd parameter to the WebAssembly.instantiate method:

var importObject = {
'env': {
// ... (other properties/methods of this object)

'_CallJS': function(iVal){ alert("value received: " + iVal.toString()); }
}
};

Just like how we needed to add an underscore character before the method name when calling into the module from JavaScript, we also need to include an underscore before the method name here.

The following is the full HTML/JavaScript for our example module:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<input type="text" id="txtValue" />
<input type="button" value="Pass Value" onclick="PassValue();" />

<script type="text/javascript">
var gModule = null;

var importObject = {
'env': {
'memoryBase': 0,
'tableBase': 0,
'memory': new WebAssembly.Memory({initial: 256}),
'table': new WebAssembly.Table({initial: 0, element: 'anyfunc'}),

'_CallJS': function(iVal){ alert("value received: " + iVal.toString()); }
}
};

fetch('test.wasm').then(response =>
response.arrayBuffer()
).then(bytes =>
WebAssembly.instantiate(bytes, importObject)
).then(results => {
gModule = results.instance; // Hold onto the module's instance so that we can reuse it
});

function PassValue(){
// Get the value from the textbox (convert the value from a string to an int)
var iVal = parseInt(document.getElementById("txtValue").value,10);

// Call the method in the module
gModule.exports._Test(iVal);
}
</script>
</body>
</html>



A New Book

I’ve been honored with an opportunity to write a book about WebAssembly.

If you enjoyed this article and would like to know more about WebAssembly, I welcome you to check out my book: WebAssembly in Action

Friday, September 21, 2012

Windows 8: Windows Store apps - JavaScript passing data to the code containing the WebView control


Over the past few weeks, in my spare time, I've been working on building a Windows Store app that will wrap an HTML web app that I've built.

To use the web app, you enter a token in a textbox on the welcome page and then click the Verify button.

If the token is valid, a URI is given to the user which can then be used for the tracking of one's time using the start/stop timer view in the web app.


The issue that we're going to tackle in this blog post is the following:

When the user clicks the Verify button on the welcome page and a URI is generated, I would like the JavaScript to call the code that holds the WebView control to have the URI saved.

This will save the user the trouble of having to copy the URI, open up the settings flyout, and paste in the new URI.


I know that the JavaScript, of a page loaded in a WebBrowser control, can talk to the C# code when the ObjectForScripting option is turned on but do we have access to a similar feature in a Windows Store app when using a WebView control?


ScriptNotify and AllowedScriptNotifyUris

As it turns out, it is possible for the JavaScript of a page loaded into a WebView control to call into the C# code of the page if you set up a ScriptNotify event.

According to the Microsoft documentation on the ScriptNotify event (http://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.controls.webview.scriptnotify), depending on which method you use to display the page in the WebView control, you may or may not have to specify URIs that are allowed to call the event handler.

If you use the WebView's Navigate method then you must specify the list of URIs that are allowed to call the ScriptNotify event handler.

If you use the WebView's NavigateToString method then the list of URIs is not necessary.


I've been simply setting the WebView's 'Source' property with the desired URI so the question is: Do I need to specify the list of URIs or not?

As it turns out, if I don't specify the list of URIs when using the WebView's Source property, the ScriptNotify event does not trigger.

You specify a list of allowed URIs in the following manner:
List<Uri> lstAllowedUris = new List<Uri>();
lstAllowedUris.Add(new Uri("http://apps.dovico.net"));
wvBrowser.AllowedScriptNotifyUris = lstAllowedUris;

Note: The property AllowedScriptNotifyUris almost suggests that you can say one page can make ScriptNotify event handler calls but none of the other pages on the site can.

In reality, based on my testing, even if you specify a URI to a particular page, any page within that domain can call the event handler.

The AllowedScriptNotifyUris property is really a list of allowed domains who's pages are allowed to call the ScriptNotify event handler.


The following is an example of how to wire up a ScriptNotify event handler:
public MainPage()
{
// Only allow the following domain's pages to call our ScriptNotify
// event handler
List<Uri> lstAllowedUris = new List<Uri>();
lstAllowedUris.Add(new Uri("http://apps.dovico.net"));
wvBrowser.AllowedScriptNotifyUris = lstAllowedUris;

// Attach our ScriptNotify event handler
wvBrowser.ScriptNotify += wvBrowser_ScriptNotify;
}

The following is an example of the event handler itself:
void wvBrowser_ScriptNotify(object sender, NotifyEventArgs e)
{
// The string received from the JavaScript code can be found
// in e.Value
}


On the JavaScript side of things, the code is similar to what you would write when calling the C# code of a WebBrowser control with the exception that the function you call is 'notify' where with a WebBrowser control you could define your own callback function name.

The following is an example of the JavaScript code that calls the C# code:
// We have a couple of checks to make sure the 'external'
// object and 'notify' method exist before we try using them
// because our project is a web application and might not be
// running in a WebView control (the checks prevent
// JavaScript errors if the method doesn't exist)
if ((typeof (window.external) !== "undefined") &&
(typeof (window.external.notify) !== "undefined"))
{
window.external.notify(sURI);
}



In Conclusion

Even though the JavaScript is only capable of passing a string to the ScriptNotify event handler, it is highly recommended that you verify the string is in the expected format before trying to use it even if the string came from a source you included in the list of allowed URIs.


Additional Resources

If you are interested in details on how to implement a Settings flyout, especially if you are using a WebView control, I have a previous article that might be of interest: Windows 8: Windows Store apps and the WebView control (we discussed how to display a Settings flyout and focused mostly around making the WebView control play nice with the flyout).

If you are interested in RoamingSettings and the DataChanged event, the following article might also be of interest: Windows 8: Windows Store apps - RoamingSettings and the DataChanged event


A download of the project (C# and built using Visual Studio Express 2012 for Windows 8) can be found in the following location:
https://github.com/downloads/dovicoapi/DOVICOTimerForWindowsStore/DOVICOTimerForWindowsStore.zip

Wednesday, March 2, 2011

Viewing JavaScript Errors in Internet Explorer 9 and 10

With Internet Explorer 10 (IE 10), it is not currently possible to view JavaScript errors when using the metro version of the browser.

The contents of this article still apply to IE 10 but only when using the desktop version.

To switch from the metro version of IE 10 to the desktop version, click on the Settings button in the browser and choose the 'View on the desktop' menu item.

JavaScript error reporting is a bit different in Internet Explorer 9 and 10 compared to previous Internet Explorer browsers since, by default, the Status bar is not visible and depending on your settings this might be the only place that would indicate if there was an error on the page.

You can turn on the Status bar either through the menu system by pressing Alt on your keyboard to show the menu bar and then navigating to the View, Toolbars, Status bar menu item.

Another approach to showing the Status bar is to right-click on title/tab area and choose the Status bar option from the context menu.


JavaScript Errors on the Status Bar

Getting errors to show via the status bar seems to be hit and miss because sometimes it works for me and sometimes it doesn't. It is my guess that Microsoft intends to remove the status bar error log functionality altogether and that it is simply an oversight (bug) that lets us view the error log in the first place.

In my testing if you start the browser normally the Status bar behavior I'm about to describe will not work.

If you launch IE from a pinned site, however, this Status bar behavior usually works.


To pin a site, drag the icon that is on the address bar to the Windows taskbar.

(click to view the image full size)


In your Advanced settings, if you have the 'Display a notification about every script error' option turned off then even if you have the status bar visible, you will not know there was an error.

The only hint of a JavaScript error would be if the page didn't respond properly (something didn't load for example).

The following is a screen shot of a website launched from a pinned site that has thrown an error (the status bar doesn't indicate that there was an error):

(click to view the image full size)

If you double-click anywhere on the status bar, if there was a JavaScript error on the page, the error log will pop-up.

(click to view the image full size)

If you have the 'Display a notification about every script error' option turned on then you will get a bit more feedback when a JavaScript error happens on the page.

Now, when a JavaScript error happens, the error log pops up showing us the error and, when we dismiss it, the status bar tells us there was an error on the page.

(click to view the image full size)


Whether or not the status bar was visible at the time of the JavaScript error doesn't matter. If you show the status bar and then double-click on it, you will see the error log.


Viewing JavaScript Errors from the Developer Tools

Since the status bar is no longer reliable for viewing if there was a JavaScript error on the page there is another approach available for viewing JavaScript errors.

Internet Explorer has a set of tools built in called Developer Tools and they can be displayed by pressing F12. The following is a screen shot of the Developer Tools window:

(click to view the image full size)

In the Developer Tools is a Console tab that shows you a log of all JavaScript errors on the page since the Developer Tools were opened.

The trick with the Console tab of the Developer Tools is that the error log only starts logging errors once the Developer Tools window is opened.

(click to view the image full size)

You will need to repeat the action that triggered the error in the first place to see the error in the Console tab.

The following is a screen shot of the Console tab with a JavaScript error logged:

(click to view the image full size)



In Conclusion

If you think there was a JavaScript error on the page, and the web page was launched from a pinned site, then double-clicking the Status bar usually displays the JavaScript error log if there were errors.

The Console tab of the Developer Tools (F12) will also give you a list of all JavaScript errors that happened on the page regardless of if the web page was launched from a pinned site or not.

The only issue with the Console tab's error logging is that it only starts logging errors once the Developer Tools window has been opened forcing one to repeat the steps to try and trigger the error again.

Friday, October 8, 2010

JSONP Overview and JSONP in HTML 5 Web Workers


If you are new to JSON (JavaScript Object Notation), my previous post 'JSON Overview' might be something you would like to read since this topic deals with JSON.


Same Origin Policy

Usually, a browser will prevent a web page from requesting data from an origin other than the one that served the page with what is known as the 'same origin policy'.

For example, if the current web page was served from Site1.com and you try to do an XMLHttpRequest to Site2.com, the request will usually fail. I say 'usually' because in some browsers the settings can be adjusted to allow for cross-domain requests.

Depending on your settings in Internet Explorer, doing a cross domain request could result in an 'Access is denied' error.

Firefox returns the error 'Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsIXMLHttpRequest.send]'

Chrome and Safari both return the error 'NETWORK_ERR: XMLHttpRequest Exception101'


Certain HTML tags within the browser do not enforce the same origin policy. Two of the tags are img and script.

There are advantages of having some HTML tags that can make cross-domain requests like the img tag being able to display pictures from other sites as well as the script tag allowing for the use of 3rd party files/libraries without having to host locally.


Security Concerns with 3rd Party Script Includes

Before I go any further, it's important to understand that there are some security concerns with importing JavaScript files from 3rd party sites.

When you include a script tag on an HTML page, the browser will download the file and then evaluate the code converting it from text into functions, variables, objects, etc.

The browser will also run any global code that is within the file as it's evaluated and this is where the security concern comes in.

If you are including code from a 3rd party site it could be running code to try and find sensitive information in cookies, web storage, etc and then pass that back to the 3rd party server. You might not even notice the information being leaked unless you were running HTTP traffic monitoring software.


JSONP

If you simply included a script tag that points to a 3rd party site and that site returns pure JSON (e.g. {"FirstName" : "Sam", "LastName" : "Smith"} ) the JavaScript will be evaluated but the JSON data will be unusable because even though the browser will automatically convert that JSON string into an object, the object is not assigned to a variable of any kind.

Where JSONP (JSON with padding) comes in is that it allows you to tell the server to wrap the JSON data in a function call to a function that you specify as part of the URI.

The following is an example of a JSONP call that indicates HandleRequest is the function to include with the response JSON:
<script type="text/javascript" src="http://SomeServer.com?jsonp=HandleRequest"></script>

The following is what the received JavaScript would look like:
HandleRequest({ "FirstName": "Sam", "LastName": "Smith" });



Script Injection

Now that we know what JSONP is and how it works, the issue that we now face is that we need to be able to make requests to the server and we likely don't know what those requests are when the page is first rendered.

One method is to change the src attribute of an existing script tag as in the following example:
Within your HTML page:

<script type="text/javascript" id="JSONPScriptTag" src=""></script>


Within your JavaScript:

// Grab a reference to the tag and then adjust the src property so that the
// proper data is returned
var ScriptTag = document.getElementById("JSONPScriptTag");
ScriptTag.src = "http://SomeServer.com?jsonp=HandleRequest";

// The callback function for the JSONP request results
function HandleRequest(objJSON) {
alert("Data returned from the server...FirstName: " + objJSON.FirstName + " LastName: " + objJSON.LastName);
}

If you have access to the html of the page and will only be making a JSONP request once in a while then this approach might be good for you.

If you do not have access to the html of the page or you need to make multiple JSONP requests, perhaps even at the same time, the other approach is to do what is known as 'Script Injection' where, through code you create a script tag and add it to the page.

The following is an example of script injection:
// Build up the Script tag dynamically
var ScriptTag = document.createElement("script");
ScriptTag.setAttribute("type", "text/javascript");
ScriptTag.setAttribute("src", "http://SomeServer.com?jsonp=HandleRequest");

// Grab the page's Head tag and add the script tag as a child
var hHead = document.getElementsByTagName("head")[0];
hHead.appendChild(ScriptTag);

// The callback function for the JSONP request results
function HandleRequest(objJSON) {
alert("Data returned from the server...FirstName: " + objJSON.FirstName + " LastName: " + objJSON.LastName);
}



HTML 5 Web Workers and JSONP

If you are unfamiliar with HTML 5 Web Workers, you can read up on them with my blog post 'An Introduction to HTML 5 Web Workers' as well as my post 'A Deeper Look at HTML 5 Web Workers'.

New: I have recently written an article for DZone.com which combines the Web Worker information presented in my blog posts and introduces some new information like transferable objects, inline workers, and includes a link to a sample project stored on github.com. The article can be found here: http://refcardz.dzone.com/refcardz/html5-web-workers


Web workers run in a separate thread from the UI thread of the browser itself. As a result, the web worker threads do not have access to the DOM or any other UI elements.

Traditionally with JSONP you use Script Injection by modifying the DOM. In the case of web workers that is not an option. So how do we accomplish JSONP from a Web Worker?

The answer was surprisingly simple.

In a web worker, you have access to an importScripts function that lets you pull in JavaScript files/libraries to aid in processing.

As it turns out, you can use the importScripts function to make JSONP requests as in the following example:
// Helper function to make the server requests
function MakeServerRequest()
{
importScripts("http://SomeServer.com?jsonp=HandleRequest");
}

// Callback function for the JSONP result
function HandleRequest(objJSON)
{
// Up to you what you do with the data received. In this case I pass
// it back to the UI layer so that an alert can be displayed to prove
// to me that the JSONP request worked.
postMessage("Data returned from the server...FirstName: " + objJSON.FirstName + " LastName: " + objJSON.LastName);
}

// Trigger the server request for the JSONP data
MakeServerRequest();



In Closing

JSONP is a fairly easy way to obtain JSON data from an origin other than the origin that served the page currently being viewed. JSONP is even available from within HTML 5 Web Workers.

The main thing to remember with JSONP is that there is a security concern since the browser automatically loads and evaluates the code. The global code could be executed before your data is received and you may not be aware of it without tools that monitor the HTTP traffic.

Friday, August 20, 2010

Creating JavaScript Namespaces

As with any programming language there are many ways to accomplish a task and JavaScript is no different. What I'm about to show you is one way to create namespaces in JavaScript but other approaches exist too.

Organizing functions and variables into classes helps cut down on spaghetti code and introduce some organization to the JavaScript. Namespaces allow you to take that organization a step further and prevent class naming conflicts with other JavaScript libraries.

You can use JSON to create your namespaces but I tend to avoid JSON when creating classes because it does not allow for private members or methods.

The following is an example of how you would create a global namespace object (RootNamespace), add an object to the root object (ServerSideCalls), and then add an even more specific object to the newly added object (Products):

// Our root namespace so that our code does
// not interfere with other libraries.

// Also, being a global object already
// initialized allows calling code to access
// it
without needing to create an instance
// of a class first.
var RootNamespace = new function(){};



// Create a ServerSideCalls object within our
// RootNamespace object

RootNamespace.ServerSideCalls = new function()
{
// Private function
var GetXmlHttpRequestObject = function()
{

if(window.XMLHttpRequest)
{ return new XMLHttpRequest(); }
else if(window.ActiveXObject)
{ return new ActiveXObject("Microsoft.XMLHTTP");
}

}


// Public function
this.SendHttpRequest = function(sURL, sHTTPMethod, sData)
{

// Call the private function to get our
// XmlHttpRequest object

var xhrXmlHttpRequest = GetXmlHttpRequestObject();

// Send the request and return the response
// to the calling function

xhrXmlHttpRequest.open(sHTTPMethod, sURL, false);
xhrXmlHttpRequest.send(sData);
return xhrXmlHttpRequest.responseText;

}
};


// Go a step further and create a Products
// object within our ServerSideCalls object

RootNamespace.ServerSideCalls.Products = new function()
{
this.GetProductList = function()
{

// Call out to our web site to get the
// list of products

var sResult = RootNamespace.ServerSideCalls.SendHttpRequest("ourwebsite.com/products", "GET", "");

// Parse the return data into an array
var arrResults = [];
//...parse code

// Return the array of products
return arrResults;

}
};


Since we initialized our root namespace object when we created it, any code that needs to make use of the contained functions can do so without needing to worry about creating an instance of an object first as in the following example:

function TestNamespace()
{
// Ask for the array of products from our
// web site

var arrProducts = RootNamespace.ServerSideCalls.Products.GetProductList();

// do something with the returned array
// of products

}


You can always create the child namespaces objects within the parent namespace when you create that object. I don’t tend to take that approach because I usually separate my functionality into separate JavaScript files and add the object to the namespace only for the pages that need it.

In this example, I declared the root namespace object as a global variable. I have seen cases where the objects were simply added to the window object and the window object acted like the root namespace. I'm not sure if there are any issues with that approach so I error on the side of caution just in case adding items to the window object is not permitted by a browser in the future.

Friday, August 13, 2010

JavaScript: Try-Catch-Finally and Throw

I ran into an issue a while ago where, on occasion, the window that opened a pop-up was refreshed while the pop-up window was still open.

When the pop-up window tried to call a function within the opener window a 'Permission denied' exception was thrown because the reference to the opener window was no longer valid.

After a bit of searching for a solution I ran across a feature of JavaScript that I didn’t realize was available. The solution was a try-catch statement.

At the time, I didn't realize that JavaScript supported anything more than a simple try-catch block so I didn't dig any further. It was only recently that I ran across an article that opened my eyes to what was possible in JavaScript when it comes to error handling.



The Try-Catch Statement

If an error can be avoided simply by checking to see if an object is null, for example, before using the object then best practices are to test for the null condition rather than use a try-catch statement.

A try-catch statement is used if you have code that might throw an exception and there is no way to test for the error condition. The statement gives you the opportunity to fail gracefully when unexpected errors happen.


The way a try-catch statement works is that you place the code that might throw an unexpected error within a try block. If an error does occur, execution is transferred from the try block to the matching catch block. The catch block can then display or log the error message.

The following is a simple example of a try-catch statement:
try
{

// Do some work that might produce an
// unexpected error
DoSomeWork();

}
catch (e)
{

// Depending on the error you may want
// to log it or display it to the user.
// In this case, display it to the user.
alert("The following error has occurred: " + e.message);

}

The catch block is optional but I recommend that it should only be omitted if you are nesting try-catch statements and only if there is a catch statement further up in the hierarchy.

If no catch statement exists in the hierarchy of nested try-catch statements, once the optional finally blocks finish executing, the execution is transferred to the page so that the error can be logged and/or displayed to the user by the browser. The JavaScript that would normally happen following the code that triggered the exception, will not happen as a result since the exception was not handled by your code.



The Finally Block

There is an optional 'finally' block that can be included with the try-catch blocks and is always the last block of the try-catch statement.

If included, the finally block's code gets executed regardless of if there was an error nor not.

The following is a simple example of a try-catch statement that includes the finally block:
try
{

// Do some work that might produce an
// unexpected error
DoSomeWork();

}
catch (e)
{

// Depending on the error you may want
// to log it or display it to the user.
// In this case, display it to the user.
alert("Error: " + e.message);

}
finally
{

// release resources that might have
// been allocated before the try block
// began


}


Nesting Try-Catch Statements

You can nest try-catch statements.

If an exception happens within an inner try-catch statement and the try block has no matching catch statement then, after the optional finally block finishes execution, the catch block of the wrapping try-catch statement will be transferred the execution of the code so that it can handle the exception.

Letting the exception bubble up the hierarchy of try-catch statements is up to you and can have its uses. One scenario could be that you just have a try-finally block with no catch so that resources are released and then allow the parent try-catch statement to handle the error logging.



The Error object

The standard error object that is usually passed to the catch statement has the following properties (some browsers have additional properties):
  • name - the class of the Error ('DOMException' or 'Error' for example)
  • message - the error message

Depending on who's throwing the error (the browser, a third party's JavaScript library, or even your own code), the error may or may not be the standard Error object and as a result it may or may not contain the properties mentioned above.


It is good practice to test if the desired property exists before trying to access it.



Handling Multiple Types of Exceptions

As you will see in the upcoming Throw section, a variety of items can be thrown.

Sometimes it's nice to be able to do specific error handling for specific types of errors.

With a single catch block this can be achieved by implementing an if/else-if statement, within the catch block, to test which type of error was received and then process the error accordingly as in the following example:

for (var iCounter = 0; iCounter < 3; iCounter++)
{
// Try/catch here in the loop so that if
// DoWork throws an exception, the loop
// will continue on
try
{

DoWork(iCounter);

}
catch (e)
{

// Test for our own specific error
// conditions
if (e == "ItemNotFoundException") { HandleItemNotFoundException(e); }

else if (e == "InvalidCredentialsException") { HandleInvalidCredentialsException(e); }

else if (e == "InvalidDateException") { HandleInvalidDateException(e); }

// We didn't hit one of our own exception
// conditions above so this one isn't
// ours. Handle the standard error...
else { LogSystemError(e); }

}
}

// Simply a function to throw a different
// exception string for each iCounter value
// passed in (simulate some errors)
function DoWork(iCounter)
{
if (iCounter == 0) { throw "ItemNotFoundException"; }

else if (iCounter == 1) { throw "InvalidCredentialsException"; }

else if (iCounter == 2) { throw "InvalidDateException"; }
}


Note: Firefox is the only major browser that supports multiple catch blocks and, as a result, I recommend against using them simply because multiple catch blocks are not cross-browser compatible.



The Throw Statement

Based on what you've read so far, you now know that there are at least two types of items that can be thrown: an Error object and a string.

So far we've caught Error objects but have only thrown strings. The following is an example of how you throw an Error object from your own code:
throw new Error("custom error message");

You can actually throw any expression including strings, numbers, objects, true, false, and null. Some expressions would be more valuable to an error handler within a catch statement than others.


You need to be careful if not passing an Error object as an exception because throwing a string, number, true, false, or null will pass the value to the catch statement 'as is'. The result is that you could end up with the Error object in some cases and your own values (strings, numbers, etc) in other cases.

If you are throwing error expressions that don't match the Error object's structure, you would want to do some form of type checking (typeof, instanceof) on the error object in your catch statements to handle the error correctly in all cases.



Rethrowing the Exception

There are times where you might want to respond to certain error conditions that are specific to your block of code but if a system error occurs, for example, maybe you just want to just let the exception continue up the chain of try-catch statements.

The following is an example that rethrows an exception:
try
{

DoWork(iCounter);


}

catch (e)
{

// Test for our own specific error
// conditions
if (e == "ItemNotFoundException") { HandleItemNotFoundException(e); }

else if (e == "InvalidCredentialsException") { HandleInvalidCredentialsException(e); }

else if (e == "InvalidDateException") { HandleInvalidDateException(e); }

// We didn't hit one of our own
// exception conditions above so this
// one isn't ours. Let one of the
// parent try-catch blocks handle the
// error
else { throw e; }

}


Custom Exceptions

Being able to throw a custom object can be of use in that you can differentiate your own types of errors (CustomException for example) from system errors (Error object).

Throwing a string would be more useful as a way to output a specific error message rather than doing custom error handling when a certain type of error occurs.

The following is an example of the creation and throwing of a custom exception object:

// Our CustomException class declaration
function CustomException(sErrorMessage)
{
this.name = "CustomException";
this.message = sErrorMessage;

this.toString = function () { return this.message; }
}


try
{
...

// We hit an issue. Throw our custom
// exception
throw new CustomException("Processing error...item has already been specified");
}
catch (e)
{
// If the exception is our custom
// exception...
if (e instanceof CustomException)
{

alert("Error: " + e.message);


}

else // Standard Error object
{

// Just rethrow and let the parent
// try/catch statements deal with it
throw e;

}
}



In Closing

When I started looking at this topic as a potential article, I didn't think there would be much to write about. As I started digging deeper, however, I realized that there is much more to the try-catch statement than I originally thought.

I hope this article was of use to you.