Mozilla

XMLHttpRequest Articles

Sort by:

View:

  1. Aurora 6 is here

    What’s new in Aurora 6?

    The most notable addition to this new Aurora are the <progress> element, window.matchMedia API, better APIs for binary data, Server-Sent Events as well as the return of WebSockets.

    Aurora 6 has been published last week and can be downloaded from firefox.com/channel.

    The <progress> element


    screenshot of progress bars as seen on windows
    This element can be used to give a visual cue of something in progress in the page. System progress bars are being used, which means that users of MacOS and Linux will see something different than what is pictured here.

    matchMedia

    window.matchMedia() is the javascript equivalent of CSS Media Queries.

    Binary data APIs improvements

    • XHR2 responseType and response attributes allow getting the response from an XHR in the form of efficient Blob or ArrayBuffer.
    • FileReader.readAsArrayBuffer() allow reading files and get the response as an ArrayBuffer.
    • BlobBuilder allow concatenating multiple blobs as well as text and ArrayBuffer into a single Blob.

    Expect to see even more improvements in this area in Aurora 7.

    Server Sent Events

    Server Sent Events are a mean for a server-side script to generate client-side events accompanied with data.


    Messages generated on the server-side with a text/event-stream mime-type and consist of a list of events data.

    data: data generated by the server
    
    data: this line will generate a second event

    WebSockets are back!

    WebSockets can be used to create an interactive communication channel between a browser and a server. They are already used to build “HTML5” chats, multiplayer games, and much much more.
    Note that this API will be temporarily namespaced in prevision of upcoming changes to the specification.

    Other Interesting Additions

    Learn about what’s new in Aurora 6′s user interface on blog.mozilla.com/futurereleases and let us know what you think.

  2. The shortest image uploader – ever!

    A couple of line of JavaScript. That’s all you need.

    This is a very short Image Uploader, based on imgur.com API. If you want to do more complex stuff (like resize, crop, drawing, colors, …) see my previous post.

    Back-story. I’ve been talking to Imgur.com‘s owner (Hi Alan!). He recently added Drag’n Drop support to his image sharing website. But also, Alan allows Cross-Domain XMLHttpRequest (thank you!). So basically, you can use his API to upload pictures to his website, from your HTML page, with no server side code involved – at all.

    And here is an example of what you can do:

    (see the full working code on github – live version there )

    (also, you’ll need to understand FormData, see here)

    function upload(file) {
     
      // file is from a <input> tag or from Drag'n Drop
      // Is the file an image?
     
      if (!file || !file.type.match(/image.*/)) return;
     
      // It is!
      // Let's build a FormData object
     
      var fd = new FormData();
      fd.append("image", file); // Append the file
      fd.append("key", "6528448c258cff474ca9701c5bab6927");
      // Get your own key: http://api.imgur.com/
     
      // Create the XHR (Cross-Domain XHR FTW!!!)
      var xhr = new XMLHttpRequest();
      xhr.open("POST", "http://api.imgur.com/2/upload.json"); // Boooom!
      xhr.onload = function() {
        // Big win!
        // The URL of the image is:
        JSON.parse(xhr.responseText).upload.links.imgur_page;
       }
       // Ok, I don't handle the errors. An exercice for the reader.
       // And now, we send the formdata
       xhr.send(fd);
     }

    That’s all :)

    Works on Chrome and Firefox 4 (Edit:) and Safari.

  3. How to develop a HTML5 Image Uploader

    HTML5 comes with a set of really awesome APIs. If you combine these APIs with the <canvas> element, you could create a super/modern/awesome Image Uploader. This article shows you how.

    All these tips work well in Firefox 4. I also describe some alternative ways to make sure it works on Webkit-based browsers. Most of these APIs don’t work in IE, but it’s quite easy to use a normal form as a fallback.

    Please let us know if you use one of these technologies in your project!

    Retrieve the images

    Drag and drop

    To upload files, you’ll need an <input type=”file”> element. But you should also allow the user to drag and drop images from the desktop directly to your web page.

    I’ve written a detailed article about implementing drag-and-drop support for your web pages.

    Also, take a look at the Mozilla tutorial on drag-and-drop.

    Multiple input

    Allow the user the select several files to upload at the same time from the File Picker:

    <input type="file" multiple>

    Again, here is an article I’ve written about multiple file selection.

    Pre-process the files

    Use the File API

    (See the File API documentation for details.)

    From drag-and-drop or from the <input> element, you have a list a files ready to be used:

    // from an input element
    var filesToUpload = input.files;
    // from drag-and-drop
    function onDrop(e) {
      filesToUpload = e.dataTransfer.files;
    }

    Make sure these files are actually images:

    if (!file.type.match(/image.*/)) {
      // this file is not an image.
    };

    Show a thumbnail/preview

    There are two options here. You can either use a FileReader (from the File API) or use the new createObjectURL() method.

    createObjectURL()

    var img = document.createElement("img");
    img.src = window.URL.createObjectURL(file);

    FileReader

    var img = document.createElement("img");
    var reader = new FileReader();  
    reader.onload = function(e) {img.src = e.target.result}
    reader.readAsDataURL(file);

    Use a canvas

    Once you have the image preview in an <img> element, you can draw this image in a <canvas> element to pre-process the file.

    var ctx = canvas.getContext("2d");
    ctx.drawImage(img, 0, 0);

    Resize the image

    People are used to uploading images straight from their camera. This gives high resolution and extremely heavy (several megabyte) files. Depending on the usage, you may want to resize such images. A super easy trick is to simply have a small canvas (800×600 for example) and to draw the image tag into this canvas. Of course, you’ll have to update the canvas dimensions to keep the ratio of the image.

    var MAX_WIDTH = 800;
    var MAX_HEIGHT = 600;
    var width = img.width;
    var height = img.height;
     
    if (width > height) {
      if (width > MAX_WIDTH) {
        height *= MAX_WIDTH / width;
        width = MAX_WIDTH;
      }
    } else {
      if (height > MAX_HEIGHT) {
        width *= MAX_HEIGHT / height;
        height = MAX_HEIGHT;
      }
    }
    canvas.width = width;
    canvas.height = height;
    var ctx = canvas.getContext("2d");
    ctx.drawImage(img, 0, 0, width, height);

    Edit the image

    Now, you have your image in a canvas. Basically, the possibilities are infinite. Let’s say you want to apply a sepia filter:

    var imgData = ctx.createImageData(width, height);
    var data = imgData.data;
    var pixels = ctx.getImageData(0, 0, width, height);
    for (var i = 0, ii = pixels.data.length; i < ii; i += 4) {
        var r = pixels.data[i + 0];
        var g =pixels.data[i + 1];
        var b = this.pixels.data[i + 2];
        data[i + 0] = (r * .393) + (g *.769) + (b * .189);
        data[i + 1] = (r * .349) + (g *.686) + (b * .168)
        data[i + 2] = (r * .272) + (g *.534) + (b * .131)
        data[i + 3] = 255;
    }
    ctx.putImageData(imgData, 0, 0);

    Upload with XMLHttpRequest

    Now that you have loaded the images on the client, eventually you want to send them to the server.

    How to send a canvas

    Again, you have two options. You can convert the canvas to a data URL or (in Firefox) create a file from the canvas.

    canvas.toDataURL()

    var dataurl = canvas.toDataURL("image/png");

    Create a file from the canvas

    var file = canvas.mozGetAsFile("foo.png");

    Atomic upload

    Allow the user to upload just one file or all the files at the same time.

    Show progress of the upload

    Use the upload events to create a progress bar:

    xhr.upload.addEventListener("progress", function(e) {
      if (e.lengthComputable) {
        var percentage = Math.round((e.loaded * 100) / e.total);
        // do something
    }, false);

    Use FormData

    You probably don’t want to just upload the file (which could be easily done via: xhr.send(file)) but add side information (like a key and a name).

    In that case, you’ll need to create a multipart/form-data request via a FormData object. (See Firefox 4: easier JS form handling with FormData.)

    var fd = new FormData();
    fd.append("name", "paul");
    fd.append("image", canvas.mozGetAsFile("foo.png"));
    fd.append("key", "××××××××××××");
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "http://your.api.com/upload.json");
    xhr.send(fd);

    Open your API

    Maybe you want to allow other websites to use your service.

    Allow cross-domain requests

    By default, your API is only reachable from a request created from your own domain. If you want to allow people use your API, allow Cross-XHR in your HTTP header:

    Access-Control-Allow-Origin: *
    

    You can also allow just a pre-defined list of domains.

    Read about Cross-Origin Resource Sharing.

    postMessage

    (Thanks to Daniel Goodwin for this tip.)

    Also, listen to messages sent from postMessage. You could allow people to use your API through postMessage:

    document.addEventListener("message", function(e){
        // retrieve parameters from e.data
        var key = e.data.key;
        var name = e.data.name;
        var dataurl = e.data.dataurl;
        // Upload
    }
    // Once the upload is done, you can send a postMessage to the original window, with URL

    That’s all. If you have any other tips to share, feel free to drop a comment.

    Enjoy ;)

  4. HTML5 adoption stories: box.net and html5 drag and drop

    This is a guest post from Tomas Barreto, a developer who works at box.net. They recently adopted HTML5 drag and drop as a way to share files with other people using new features in Firefox. The included video is a pitch for the feature and service, but shows how easy it is to do simple HTML5-based upload progress even with multiple files. Tomas gives an overview of the relatively simple JavaScript required to do this, and how improvements in Firefox 4 will make things even easier. Also have a quick look at the bottom of the post for links to additional docs and resources.

    At Box.net, we’re always exploring new ways to help users get content quickly and securely onto our cloud content management platform. So when asked, “What feature would make you use Box more?” during the Box Hack Olympics in April, my colleague CJ and I decided to tackle the most intuitive way to upload files: simply dragging them from the desktop into Box.

    We considered technologies ranging from Gears to Firefox plugins, but only HTML5 had sufficient adoption. By using some of the JavaScript APIs defined in the HTML5 standard, CJ and I could create a seamless drag and drop experience for our users on supporting browsers. Furthermore, using an HTML5-based upload feature would allow us to enable users to select multiple files at once, and also display progress on the client without polling. And with HTML5 adoption across the latest versions of three of the top four browsers, we felt confident about building an upload method based on this new technology without the trade-offs of using a third-party plug-in.

    We rolled out the first rev of our drag and drop feature a few weeks ago, and we’re impressed with how quickly it has been adopted. It’s already one of the most popular ways to get files onto Box, and in its first week it surpassed our old AJAX upload method. You can check out our demo video to get a feel for the feature:

    To build this feature, we referenced a handful of online examples that explained how to use Firefox 3 FileReader object and the drag and drop file event support. Our first implementation used this object to load the file into memory and then took advantage of the latest XMLHttpRequest events to track progress on the client.

    var files = event.originalEvent.dataTransfer.files; // drop event
    var reader = new FileReader();
     
    reader.onload = function(event) {
      var file_contents = event.target.result;
      var request = new XMLHttpRequest();
     
      ... // attach event listeners to monitor progress and detect errors
     
      var post_body = '';
     
      .. // build post body
     
      post_body += file_contents;
     
      .. // finish post body
     
      var url = 'http://example.com/file_upload';
     
      var request = new XMLHttpRequest();
     
      request.open("POST",  url, true); // open asynchronous post request
      request.setRequestHeader('content-type', 'multipart/form-data; boundary=""'); // make sure to set a boundary
      request.send(post_body);
    }
     
    reader.readAsBinaryString(files[0]);

    This approach worked well because we could use the same server processing code that we previously used for uploads. The main disadvantage here is that the FileReader object reads the entire file into memory, which is not optimal for a general upload use case. Our current HTML5 implementation uses this logic and has forced us to restrict drag and drop uploads to just 25mb. However, thanks to recommendations from the Mozilla team, we’ll be taking an alternative approach for V2 of drag and drop, where the file is read chunks as needed by the request. Here’s how we’re going to do it:

    var files = event.originalEvent.dataTransfer.files; // drop event
    var url = 'http://example.com/file_upload';
     
    var request = new XMLHttpRequest();
    request.open("POST",  url, true); // open asynchronous post request
    request.send(files[0]);

    Since this approach is not formatted as a multipart form-data, it will require some adjustments on our back-end to support receiving file uploads in this way. However, it’s definitely worth the trade-off since we’ll get all the benefits of the previous method and we don’t need special file size restrictions. In the future, we’ll consider using yet another way to efficiently upload files that is supported in Firefox 4 and uses the traditional multi-part form:

    var files = event.originalEvent.dataTransfer.files; // drop event
    var url = 'http://example.com/file_upload';
     
    var request = new XMLHttpRequest();
     
    var fd = new FormData;
    fd.append("myFile", files[0]);
     
    request.open("POST",  url, true); // open asynchronous post request
    request.send(fd);

    We’re already exploring more ways to enrich the Box experience using HTML5. With HTML5, we can build faster, richer and more interactive features with native browser support, and bridge the traditional gap between desktop software and web applications. Here are just a few cool new upload-related features on our roadmap:

    • Pause/Resume uploads using the Blob slice API to split files into chunks (this will be a huge robustness boost, especially for large uploads)
    • Allowing uploads to resume even after the browser closes by caching the file using IndexedDB support (possibly in Firefox 4)

    We’d also like to begin a discussion about supporting the reverse drag and drop use case: dragging files from the browser to the desktop. Based on our users’ enthusiasm around the drag and drop upload feature, we think the reverse functionality would well received. If you are interested in contributing to a specification for this feature, please let us know (html5 [-at$] box.net)!

    Resources:

  5. Firefox 4: easier JS form handling with FormData

    This feature has landed in Mozilla Central (trunk) and only available with a Firefox Nightly Build for the time being.

    XMLHttpRequest Level 2 (editor’s draft) adds support for the new FormData interface. FormData objects provide a way to easily construct a set of key/value pairs representing form fields and their values, which can then be easily sent using the XMLHttpRequest send() method in “multipart/form-data” format.

    Why FormData?

    When you want to send complex data to a server from a web page (files, non-ASCII content), you must use the multipart/form-data content type. To set the content type in a <form>, you write:

    <form method="post" enctype="multipart/form-data" action="http://foo.bar/upload.php">
    <input type="file" name="media"/>
    <input name="nickname"/>
    <input name="website"/>
    <input type="submit" value="upload"/>
    </form>

    This is what you usually do to upload a file.

    Starting with Firefox 3.6, you can manipulate files with JavaScript (see File API), and maybe you want to send files using XMLHttpRequest. But if, for example, you want to reproduce this form, it’s really hard because you’ll have to create the multipart/form-data content yourself in JavaScript (see, for example, this code I wrote a while ago implementing a multipart/form-data: ugly and slow).

    This is where FormData is useful: to reproduce the <form> submission mechanism in JavaScript

    The FormData object

    The FormData object lets you compile a set of key/value pairs to send using XMLHttpRequest. This object has only one method:

    append(key, value);

    where key is the name of your value, and where value can be a string or a file.

    You can create a FormData object, append values and then send it through XMLHttpRequest. If you want to simulate the previous form, you write:

    // aFile could be from an input type="file" or from a Dragged'n Dropped file
    var formdata = new FormData();
    formdata.append("nickname", "Foooobar"); 
    formdata.append("website", "http://hacks.mozilla.org");
    formdata.append("media", aFile);
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "http://foo.bar/upload.php");  
    xhr.send(formdata);

    FormData and the <form> element

    Firefox extends the HTML form element slightly, adding a getFormData() method that lets you fetch a form’s data as a FormData object. This is not yet part of the HTML standard, but is expected to be added to the specification at some point in the future (although possibly with a different name):

    var formElement = document.getElementById("myFormElement");
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "submitform.php");
    xhr.send(formElement.getFormData());

    You can also add data to the FormData object between retrieving it from a form and sending it, like this:

    var formElement = document.getElementById("myFormElement");
    formData = formElement.getFormData();
    formData.append("serialnumber", serialNumber++);
    xhr.send(formData);

    This lets you augment the form’s data before sending it along, to include additional information that’s not necessarily user editable on the form.

    Resources

  6. WebSockets in Firefox

    Here’s the pitch for WebSockets: a low-complexity, low-latency, bi-directional communication system that has a pretty simple API for web developers. Let’s break that down, and then talk about if and when we’re going to include it in Firefox:

    Low-complexity

    The WebSocket protocol, which is started via an HTTP-like handshake, has a relatively simple model for sending text packets back and forth. The complexity of the protocol is quite low. There’s no multiplexing, no support for binary data, and once the data connection is set up, the actual transport is quite cheap.

    Note that there are people who feel – possibly correctly – that support for multiplexing and binary data should be added. That would obviously increase the complexity but in some cases might be worth the cost. But more on that later.

    Bi-directional Communication

    One of the key aspects of WebSocket is its support for simple bi-directional communication. Servers can easily send updates to clients and clients can send updates to servers. Many of the comet-style applications that people have built will be much simpler and faster with this model because the protocol and API support it directly.

    While allowing for bi-directional communication it also is bound by the HTTP same-origin model. That is, it’s doesn’t give the browser the ability to connect to any site on any port it wants to.

    So WebSocket is really TCP with the HTTP security model.

    Low-latency

    This is actually the primary value in WebSockets. The key thing is that the cost of sending a small amount of data is also very small. It’s possible to do bi-directional communication today with comet-style applications, but small amounts of data often require a huge amount of overhead to do so.

    A second-hand story to give you a sense of scale: Google Wave, which tries to do real-time communication with keystrokes has a several-kilobyte overhead for just about every keystroke because of the TCP startup, teardown and HTTP message headers involved with what should be only a few bytes sent over the wire.

    I haven’t tried, but I’m going to guess that if you built Quake on top of HTTP comet that the interactive experience would be poor. So this is where WebSockets really shine.

    Simple API

    The actual API that a developer sees for WebSocket is relatively simple. You can send messages, you can get messages, you get events when sockets open, close or there’s an error. Additional complexity, which I’m sure others will develop, will happen in JavaScript and backend libraries.

    (While this might seem like punting on the issue of complexity, this is actually the model for success we’ve seen for other browser standards: build something relatively simple that others can experiment with. When we understand what’s slow or what’s preventing progress, iterate and improve.)

    When will it be in Firefox?

    We really really want to support WebSockets in the next version of Firefox. And a lot of other people want us to include support too.

    The first set of test patches, written by the wonderful Wellington Fernando de Macedo (one of our most excellent contributors!) was first submitted in April of 2009. Since then we’ve been iterating both on the patches themselves and following the spec as it’s changed.

    Unfortunately, the spec itself is still under revision. WebSockets did ship in Chrome with version 4 and I’m told by Chrome developers that it’s going to be included in Chrome 5, without changes. Unfortunately, the version that Google included in Chrome doesn’t reflect the current draft. The handshake for how to start a WebSocket connection has changed, largely to improve security on servers. There’s also a lot of ongoing discussion on the role of integrated compression, direct support for binary data (instead of encoding binary data as strings), whether or not the protocol should support multiplexing and a number of other issues.

    Since WebSocket isn’t used widely yet, and that Chrome has an uptake rate that’s similar to Firefox, the hope is that once a more recent draft makes it through the process that both Chrome and Firefox can include a more recent version at around the same time.

    So in short: we want to ship it because the promise of WebSockets is great, but we’ll have to see if it’s stable and safe enough to do so. Code isn’t the issue – we’ve had that for a while. It’s whether or not there’s consensus on if the protocol is “done enough” to ship it to a few hundred million people.

  7. an HTML5 offline image editor and uploader application

    Many web applications use image uploaders: image hosting websites, blog publishing applications, social networks, among many others. Such uploaders have limitations: you can’t upload more than one file at a time and you can’t edit the image before sending it. A plugin is the usual workaround for uploading more than one image, and image modifications are usually done on the server side, which can make the editing process more cumbersome.

    Firefox 3.6 offers many new Open Web features to web developers, including even more HTML5 support. This post describes how to create a sophisticated image editor and uploader built using Open Web technologies.

    See below for a video of the demo with some background.

    Hosted on hacks, publishes to twitpic

    Our web application uploads pictures to twitpic, an image hosting service for Twitter.

    Note that code for this application is actually hosted on the hacks web server but can still upload to Twitpic. Uploading to Twitpic is possible because they opened up their API to Cross Domain XMLHttpRequests for applications like this. (Thank you twitpic!).

    Web Features

    The demo uses the following features from HTML5 included in Firefox 3.6:

    • HTML5 Drag and Drop: You can drag and drop items inside of the web page and drag images from your desktop directly into the browser.
    • HTML5 localStorage: to store the image data across browser restarts
    • HTML5 Application Cache: This allows you to create applications that can be used when the browser isn’t connected to the Internet. It stores the entire app in the browser and also gives you access to online and offline events so you know when you need to re-sync data with a server.
    • HTML5 Canvas: The HTML5 Canvas element is used through this demo to edit and render images.
    • Cross-Origin Resource Sharing to host an application at one site and publish data to another.

    What’s in the demo?

    See the live demo to try the image uploader for yourself. You will need Firefox 3.6 and a twitter account.

    Here’s a full list of the things that this application can do:

    • You can drag images from your desktop or the web into the application.
    • You can see a preview of each image you want to upload.
    • You can drag previews to the trash to delete an image.
    • Images are automatically made smaller if they are bigger than 600px wide.
    • You can edit any of the images before uploading. This includes being able to rotate, flip, crop or turn an image black and white.
    • If you edit an image it’s saved locally so you can still edit when you’re offline. If you close the tab, restart Firefox or your computer they will be there when you load the page again so you can upload when you’re re-connected.
    • It will upload several files at once and provide feedback as the upload progresses.
    • The HTML5 Offline Application Cache makes the application load very quickly since it’s all stored offline.

    Under the Hood

    Let’s quickly go over all of the technology that we’re using in this application.

    Cross-XMLHttpRequest

    Twitpic was nice enough to open their API to allow XMLHttpRequests from any other domain. This means that you can now use their API from your own website and offer your own image uploader.

    If you’re running a web site with an API you want people to use from other web sites, you can do that with Cross-site HTTP requests. In order for you to support it you will need to add an HTTP header to responses from your web server that says which domains you allow. As an example, here’s how twitpic allows access from all domains:

    Access-Control-Allow-Origin: *
    

    It’s important to note that opening your API does have security implications so you should be careful to understand those issues before blindly opening an API. For more details, see MDC documentation on CORS.

    Drag and Drop

    Drag and Drop is a mechanism with two important features:

    • Dragging files from your Desktop to your web page.
    • Native Drag and Drop inside your web page (not just changing the coordinates of your elements).

    The image uploader uses Drag and Drop to allow the user the add files from the Desktop, to remove files (drag them to the trash) and to insert a new image into a current image.

    For more on Drag and Drop, see previous hacks articles, in particular how to use Drag and Drop in your application.

    Canvas to Edit Images

    Once images have been dragged and dropped into the web page, the image uploader lets you edit them before uploading. This is possible because images are actually copied to canvas elements via the File API.

    In this case, the editing process is really basic: rotate, flip, add text, black and white, crop. However, you can imagine offering many other features in your version of the editor (see Pixastic for example, or this inlay feature here).

    Using canvas and the File API also let you resize the image before sending it. Here, every image is converted to a new image (canvas) that is less than 600px.

    localStorage: Save Local Data

    It’s possible to store local data persistently in a web page using localStorage, up to 5Mb of data per domain.

    In the image uploader, localStorage is used to store images and credentials. Since images are actually canvas, you can store them as data URLs:

    var url = canvas.getContext("2d").toDataURL("image/png");
    localStorage.setItem("image1", url);

    LocalStorage support means that you can edit an image, close Firefox, switch off your computer, and the edited image will still be there when you restart Firefox.

    Offline

    If you add a manifest file listing all remote files needed to display your web application it will work even when you aren’t connected to the Internet. A nice side effect is that it will also make your application load much faster.

    Here, the html element refers to a manifest file:

    <html manifest="offline.manifest">

    And the manifest file looks like:

    CACHE MANIFEST
    
    # v2.4
    index.xhtml
    fonts/MarketingScript.ttf
    css/desktop.css
    css/fonts.css
    css/mobile.css
    [...]
    

    You can also catch offline and online events to know if the connection status changes.
    For more information see our last article about offline.

    Conclusion

    Firefox 3.6 allows millions of people to take advantage of modern standards, including HTML5.
    The image uploader described here shows how a web page should really be considered as an application since it interacts with your Desktop and works offline.

    Here are a few tips for writing your next application using Open Web technologies:

    Allow Cross-XMLHttpRequest:
    If it makes sense for your service, allow people to access your API from a different domain, you’ll be amazed at the apps people will come up with.

    Allow multiple input:
    Let people Drag files to your application and use <input type="file" multiple=""> so they can select several files at once. In this demo, we use a multiple input which is visible only in the mobile version, but for accessibility consideration, don’t forget to use it to propose an alternative to Drag’n Drop.

    Use native Drag and Drop:
    Drag and Drop mechanisms are usually simulated (updating coordinates on the mousemove event.) When you can, use the native mechanism.

    Use the File API
    To pre-process a file before even talking to a server.

    Support offline
    Store data and use a manifest to make your application data persistent while offline.

    Use Canvas
    Canvas is the most widely implemented HTML5 element. It works everywhere (even if it has to be simulated), use it!

    Think “Client Side”: HTML5, CSS3 and the new powerful JavaScript engines let you create amazing applications, take advantage of them!

    We look forward to seeing the great new applications you’ll come up with using Open Web technologies!

  8. interactive file uploads with Drag and Drop, FileAPI and XMLHttpRequest

    In previous posts, we showed how to access a file through the input tag or through the Drag and Drop mechanism. In both cases, you can use XMLHttpRequest to upload the files and follow the upload progress.

    Demo

    If you’re running the latest beta of Firefox 3.6, check out our file upload demo.

    Uploading

    XMLHttpRequest will send a given file to the server as a binary array, so you first need to read the content of the file as a binary string, using the File API. Because both Drag and Drop and the input tag allow you to handle multiple files at once, you’ll need to create as many requests as there are files.

    var reader = new FileReader();
    reader.onload = function(e) {
      var bin = e.target.result;
      // bin is the binaryString
    };
    reader.readAsBinaryString(file);

    Once the file is read, send it to the server with XMLHttpRequest:

    var xhr = new XMLHttpRequest();
    xhr.open("POST", "upload.php");
    xhr.overrideMimeType('text/plain; charset=x-user-defined-binary');
    xhr.sendAsBinary(bin);

    You can choose to be notified when specific events, such as error, success, or abort, occur during the request (see the MDC documentation for more details).

    Following the Upload Progress

    The progress event provides the size of the uploaded portion of the binary content. This allows you to easily compute how much of the file has been uploaded.

    Here’s an example showing the percentage of the file that has been uploaded so far:

    xhr.upload.addEventListener("progress", function(e) {
      if (e.lengthComputable) {
        var percentage = Math.round((e.loaded * 100) / e.total);
        // do something
    }, false);
  9. cross-site xmlhttprequest with CORS

    XMLHttpRequest is used within many Ajax libraries, but till the release of browsers such as Firefox 3.5 and Safari 4 has only been usable within the framework of the same-origin policy for JavaScript. This meant that a web application using XMLHttpRequest could only make HTTP requests to the domain it was loaded from, and not to other domains. Developers expressed the desire to safely evolve capabilities such as XMLHttpRequest to make cross-site requests, for better, safer mash-ups within web applications. The Cross-Origin Resource Sharing (CORS) specification consists of a simple header exchange between client-and-server, and is used by IE8′s proprietary XDomainRequest object as well as by XMLHttpRequest in browsers such as Firefox 3.5 and Safari 4 to make cross-site requests. These browsers make it possible to make asynchronous HTTP calls within script to other domains, provided the resources being retrieved are returned with the appropriate CORS headers.

    A Quick Overview of CORS

    Firefox 3.5 and Safari 4 implement the CORS specification, using XMLHttpRequest as an “API container” that sends and receives the appropriate headers on behalf of the web developer, thus allowing cross-site requests. IE8 implements part of the CORS specification, using XDomainRequest as a similar “API container” for CORS, enabling simple cross-site GET and POST requests. Notably, these browsers send the ORIGIN header, which provides the scheme (http:// or https://) and the domain of the page that is making the cross-site request. Server developers have to ensure that they send the right headers back, notably the Access-Control-Allow-Origin header for the ORIGIN in question (or ” * ” for all domains, if the resource is public) .

    The CORS standard works by adding new HTTP headers that allow servers to serve resources to permitted origin domains. Browsers support these headers and enforce the restrictions they establish. Additionally, for HTTP request methods that can cause side-effects on user data (in particular, for HTTP methods other than GET, or for POST usage with certain MIME types), the specification mandates that browsers “preflight” the request, soliciting supported methods from the server with an HTTP OPTIONS request header, and then, upon “approval” from the server, sending the actual request with the actual HTTP request method. Servers can also notify clients whether “credentials” (including Cookies and HTTP Authentication data) should be sent with requests.

    Capability Detection

    XMLHttpRequest can make cross-site requests in Firefox 3.5 and in Safari 4; cross-site requests in previous versions of these browsers will fail. It is always possible to try to initiate the cross-site request first, and if it fails, to conclude that the browser in question cannot handle cross-site requests from XMLHttpRequest (based on handling failure conditions or exceptions, e.g. not getting a 200 status code back). In Firefox 3.5 and Safari 4, a cross-site XMLHttpRequest will not successfully obtain the resource if the server doesn’t provide the appropriate CORS headers (notably the Access-Control-Allow-Origin header) back with the resource, although the request will go through. And in older browsers, an attempt to make a cross-site XMLHttpRequest will simply fail (a request won’t be sent at all).

    Both Safari 4 and Firefox 3.5 provide the withCredentials property on XMLHttpRequest in keeping with the emerging XMLHttpRequest Level 2 specification, and this can be used to detect an XMLHttpRequest object that implements CORS (and thus allows cross-site requests). This allows for a convenient “object detection” mechanism:

    if (XMLHttpRequest)
    {
        var request = new XMLHttpRequest();
        if (request.withCredentials !== undefined)
        {
          // make cross-site requests
        }
    }

    Alternatively, you can also use the “in” operator:

    if("withCredentials" in request)
    {
      // make cross-site requests
    }

    Thus, the withCredentials property can be used in the context of capability detection. We’ll discuss the use of “withCredentials” as a means to send Cookies and HTTP-Auth data to sites later on in this article.

    “Simple” Requests using GET or POST

    IE8, Safari 4, and Firefox 3.5 allow simple GET and POST cross-site requests. “Simple” requests don’t set custom headers, and the request body only uses plain text (namely, the text/plain Content-Type).

    Let us assume the following code snippet is served from a page on site http://foo.example and is making a call to http://bar.other:

     
    var url = "http://bar.other/publicNotaries/"
    if(XMLHttpRequest)
    {
      var request = new XMLHttpRequest();
      if("withCredentials" in request)
      {
       // Firefox 3.5 and Safari 4
       request.open('GET', url, true);
       request.onreadystatechange = handler;
       request.send();
      }
      else if (XDomainRequest)
      {
       // IE8
       var xdr = new XDomainRequest();
       xdr.open("get", url);
       xdr.send();
     
       // handle XDR responses -- not shown here :-)
      }
     
     // This version of XHR does not support CORS  
     // Handle accordingly
    }

    Firefox 3.5, IE8, and Safari 4 take care of sending and receiving the right headers. Here is the Simple Request example. It is also instructive to look at the headers sent back by the server. Notably, amongst the other request headers, the browser would send the following in order to enable the simple request above:

    GET /publicNotaries/ HTTP/1.1
    Referer: http://foo.example/notary-mashup/
    Origin: http://foo.example

    Note the use of the “Origin” HTTP header that is part of the CORS specification.

    And, amongst the other response headers, the server at http://bar.other would include:

    Access-Control-Allow-Origin: http://foo.example
    Content-Type: application/xml
    ......

    A more complete treatment of CORS and XMLHttpRequest can be found here, on the Mozilla Developer Wiki.

    “Preflighted” Request

    The CORS specification mandates that requests that use methods other than POST or GET, or that use custom headers, or request bodies other than text/plain, are preflighted. A preflighted request first sends the OPTIONS header to the resource on the other domain, to check and see if the actual request is safe to send. This capability is currently not supported by IE8′s XDomainRequest object, but is supported by Firefox 3.5 and Safari 4 with XMLHttpRequest. The web developer does not need to worry about the mechanics of preflighting, since the implementation handles that.

    The code snippet below shows code from a web page on http://foo.example calling a resource on http://bar.other. For simplicity, we leave out the section on object and capability detection, since we’ve covered that already:

    var invocation = new XMLHttpRequest();
    var url = 'http://bar.other/resources/post-here/';
    var body = '
    Arun';
    function callOtherDomain(){
    if(invocation)
    {
        invocation.open('POST', url, true);
        invocation.setRequestHeader('X-PINGOTHER', 'pingpong');
        invocation.setRequestHeader('Content-Type', 'application/xml');
        invocation.onreadystatechange = handler;
        invocation.send(body);
    }

    You can see this example in action here. Looking at the header exchange between client and server is really instructive. A more detailed treatment of this can be found on the Mozilla Developer Wiki.

    In this case, before Firefox 3.5 sends the request, it first uses the OPTIONS header:

    OPTIONS /resources/post-here/ HTTP/1.1
    Origin: http://foo.example
    Access-Control-Request-Method: POST
    Access-Control-Request-Headers: X-PINGOTHER

    Then, amongst the other response headers, the server responds with:

    HTTP/1.1 200 OK
    Access-Control-Allow-Origin: http://arunranga.com
    Access-Control-Allow-Methods: POST, GET, OPTIONS
    Access-Control-Allow-Headers: X-PINGOTHER
    Access-Control-Max-Age: 1728000

    At which point, the actual response is sent:

    POST /resources/post-here/ HTTP/1.1
    ...
    Content-Type: application/xml; charset=UTF-8
    X-PINGOTHER: pingpong
    ...

    Credentialed Requests

    By default, “credentials” such as Cookies and HTTP Auth information are not sent in cross-site requests using XMLHttpRequest. In order to send them, you have to set the withCredentials property of the XMLHttpRequest object. This is a new property introduced in Firefox 3.5 and Safari 4. IE8′s XDomainRequest object does not have this capability.

    Again, let us assume some JavaScript on a page on http://foo.example wishes to call a resource on http://bar.other and send Cookies with the request, such that the response is cognizant of Cookies the user may have acquired.

    var request = new XMLHttpRequest();
    var url = 'http://bar.other/resources/credentialed-content/';
    function callOtherDomain(){
      if(request)
      {
       request.open('GET', url, true);
       request.withCredentials = "true";
       request.onreadystatechange = handler;
       request.send();
      }

    Note that withCredentials is false (and NOT set) by default. The header exchange is similar to the case of of a simple GET request, with the exception that now an HTTP Cookie header is sent with the request header. You can see this sample in action here.

    A Note on Security

    In general, data requested from a remote site should be treated as untrusted. Executing JavaScript code retrieved from a third-party site without first determining its validity is NOT recommended. Server administrators should be careful about leaking private data, and should judiciously determine that resources can be called in a cross-site manner.

    References