Nearly two years ago I first wrote about the Cordova Browser platform (Browser as a platform for your PhoneGap/Cordova apps). After seeing an update on the Apache Cordova blog (Cordova Browser 4.1.0), I thought it might be nice to take another look at it and see how far along it has come.
First and foremost, what is the point of the browser platform? Speaking for myself, and I'll clarify what that means in a second, I think the browser platform is a way to test your Cordova applications in the browser. You have other ways of doing this. For example, cordova serve
and ionic serve
. However, the "Browser platform" acts a bit differently. It attempts to actually make your application work on the browser. What do I mean by that? If you simply serve up your www assets, then you won't have a deviceready event (technically, Ionic will fire one for you) and you won't have support for things like the camera or other "device only" features.
On the flip side, the Browser platform actually supports the Camera. I'll be demonstrating how it does so a bit later, but you can use the same APIs you use on your device within the browser and actually get things done.
Now - here is where my opinion and those of the Cordova group (which technically I'm part of too ;) differ a bit. I think the intention of the browser platform is to actually support going to production with your app. To me, that just seems a bit too much. The camera does work, but it is more of a workaround then what I think a typical end user may expect in a production app. Again, this is just my opinion, but to me, the Browser platform is great for testing, but not necessarily something I'd go live with.
Working with the Browser Platform
Before you begin working with the Browser platform, there are a few things you should know. First, you have to actually add the platform. That means running cordova platform add browser
. And if you want the latest, then you need to specify cordova platform add browser@4.1.0
. Soon that version will be the default, and if you're reading this in the future you should probably just take the default, it could 6.0.0 by now.
And obviously, if you are only using the Browser platform to test and you find it isn't working out for you, then be sure to go ahead and remove it: cordova platform rm browser
.
To start testing the platform, simply cordova run browser
, or cordova run
if you only have one platform. When you do, a new instance of Chrome will pop up. I use Chrome as my default browser, but a new instance was launched as opposed to a new tab opening in my current browser. That's good imo but just keep that in mind. You'll also get a running log of requests in Terminal. What you see will be based on what plugins and other assets you use. Here is an example:
This is a live listing and will update as your app requests new resources. Can you configure how the browser is loaded? Yes. If you look in platforms/browser/cordova/
, you'll see a run
file whichi is used by the Cordova CLI. It's also a quick way to see the help. The run
script supports two arguments: browser
and port
. browser
defaults to Chrome. port
defaults to 8000. Here is how I'd switch the port and browser:
cordova run browser -- --port=8001 --target=Firefox
As you edit files in www
, remember that Cordova runs your code out of the platforms
folder. For my testing, I opened a new tab and ran cordova prepare browser
whenever I edited. That got old pretty quickly. I knew I could set upa Grunt task to automate that, but it seemed over the top. Then I found a cool utility called filewatcher. This is a command line tool that lets you watch files and run an arbitrary command when something changes. This is what I used:
filewatcher "www/**/*.*" "cordova prepare browser"
Working with Plugins
Ok, so how do you use plugins? Exactly the same way you do for any other platform. Find the plugin you want, install it, and then follow their API. At the Plugin Repository you'll notice a button to filter by browser support:

Notice that there is a bug with the registry in that you don't get a pretty little icon indicating that the Browser platform is supported. This is a known bug. (As in a known bug as of a few hours ago.)
Of course, the devil is in the details. For each supported plugin, you need to carefully read to see exactly how the platform will be supported. The barcode plugin, for example, doesn't actually let you scan a barcode. Instead, it simply prompts you to manually type in a value for the 'scanned' barcode. I used this recently in a production app though and it was great... for testing.
I decided to take a quick "tour" through the official plugins for Cordova and see which supported the Browser platform and how well they did. Below you'll see a report detailing what I found. All of the code used in this test is up on my GitHub repo here: https://github.com/cfjedimaster/Cordova-Examples/tree/master/browser1
Battery
The first plugin I tested was the Battery plugin. First off, this plugin does not follow the Battery API. If you've used that before, just keep it in mind. I did two tests:
window.addEventListener("batterystatus", function(info) {
console.log("[batterystatus event] Level: " + info.level + " isPlugged: " + info.isPlugged);
}, false);
window.addEventListener("batterylow", function(info) {
console.log("[batterylow event] Level: " + info.level);
}, false);
I never got around to seeing if batterylow
fired, but my batterystatus
handler did correctly report my battery level and plugin status:
[batterystatus event] Level: 100 isPlugged: true
Camera
First off, be sure to note that the 'Quirks' information for the Camera plugin clearly points out that only Base64 image URIs are returned by the plugin. Normally Cordova devs recommend against that, but if you want to test on the Browser platform you'll need to use it.
What's cool - is that you have support for both new images and existing images. How does it work? I added two buttons, one for each use case, and then used the following code.
var renderPic = function(data) {
var image = document.getElementById('myImage');
image.src = "data:image/jpeg;base64," + data;
};
var cameraError = function(err) {
console.log('[camera error]',err);
};
document.querySelector('#testCameraExisting').addEventListener('click', function() {
navigator.camera.getPicture(renderPic, cameraError, {
sourceType:Camera.PictureSourceType.PHOTOLIBRARY,
destinationType:Camera.DestinationType.DATA_URL
});
});
document.querySelector('#testCameraNew').addEventListener('click', function() {
navigator.camera.getPicture(renderPic, cameraError, {
sourceType:Camera.PictureSourceType.CAMERA,
destinationType:Camera.DestinationType.DATA_URL
});
});
In general, this is completely vanilla Camera code. So what does it do? For the source type of CAMERA, it actually fires up your webcam using getUserMedia.

The live video is added to the bottom of the DOM along with a capture button. When you click it, then your success handler will fire. Here is the result rendered in the DOM as my code specified.

Very Important Warning!! The live video stream also broadcasts your audio. That means if you're rocking out to some music, you'll get some really gross feedback. Be sure you mute beforehand.
Another Important Warning!! If you get this error:
Refused to load media from 'blob:http%3A//localhost%3A8000/d62e77b1-8354-4f25-abd2-f0634de5a4f5' because it violates the following Content Security Policy directive: "media-src *".
You will need to edit the CSP definition in index.html to specifically allow for blob URLs under media-src
. Here is mine:
<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap:
https://ssl.gstatic.com http://www.raymondcamden.com 'unsafe-eval'; style-src 'self' 'unsafe-inline';
media-src * blob:">
If you select PHOTOLIBRARY as your source, then you get a simple file chooser. As before, it gets dropped at the end of the DOM. (I won't add a screenshot of that since we all know what an HTML file chooser looks like.) You select your file then it is used as the result.

Device
So yeah, device just works. I added a button to my demo to dump out the device
object. Here is what it shows:

Device Motion
According to the docs, you can test the device motion plugin in the Browser platform and will fire off random values. In my experience, things were a bit flakey. First, here is the code I used. I had one button to get the current motion of the device, and then two more to set up a watch and clear it.
var watchId;
var renderAcc = function(data) {
console.log('[acceleration]' + JSON.stringify(data));
};
var accError = function(err) {
console.log('[acceleration error]',err);
};
document.querySelector('#testAcc').addEventListener('click', function() {
navigator.accelerometer.getCurrentAcceleration(renderAcc,accError);
});
document.querySelector('#testAccWatch').addEventListener('click', function() {
console.log('[acceleration] begin watch');
watchId = navigator.accelerometer.watchAcceleration(renderAcc,accError,
{frequency:1000});
});
document.querySelector('#testAccStop').addEventListener('click', function() {
console.log('[acceleration] clear watch');
navigator.accelerometer.clearWatch(watchId);
});
When I tested the code to get the current motion, the error handler was thrown first and then the success handler:

I filed a bug report for that.
Next - when I did a watch test, the value was random, but only once. I got the same result over every iteration of watch success handler. I filed a bug report for that as well.
Finally, the clear command also threw the error handler, but did properly stop the plugin from reporting values.
Device Orientation
Unlike device motion, device orientation worked perfectly. It is random as well, and if you do a watch, you get random values for every request.

File and FileTransfer
Yeah, no, I'm not touching these. For Firefox and IE, the Browser platform will use IndexedDB for File support, which is kinda cool I guess. Chrome has a file system feature that is deprecated but still works. For FileTransfer, it just uses XHR and FormData. If you aren't aware, a few years ago support was added for Ajax based file uploads. All modern browsers should support this well now.
Globalization
For the most part, this plugin kinda halfway-works on the Browser platform. It uses the excellent Moment.js for most of it's work, but coverage is spotty. So for example, you can't do currency formatting. I noticed this odd line in the quirks:
"The array of months contains 12 elements."
Which seemed an odd quirk, but then I saw this under Windows Phone 8:
"The array of months contains 13 elements."
Um.... ok. I whipped up a simple example where I can get locale information and do a simple percentage format. Here is the code.
var globError = function(err) {
console.log('[globalization error]',err);
};
document.querySelector('#testGlob').addEventListener('click', function() {
navigator.globalization.getPreferredLanguage(function(lang) {
console.log('[globalization] preferredLanguage: '+JSON.stringify(lang));
});
navigator.globalization.getLocaleName(function(locale) {
console.log('[globalization] localeName: '+JSON.stringify(locale));
});
navigator.globalization.getDateNames(function(names) {
console.log('[globalization] getDateNames:months: '+JSON.stringify(names));
},globError, {type:'wide', item:'months'});
navigator.globalization.getDateNames(function(names) {
console.log('[globalization] getDateNames:days: '+JSON.stringify(names));
},globError, {type:'wide', item:'days'});
});
document.querySelector('#testGlobInput').addEventListener('click', function() {
var input = document.querySelector('#numberGlob').value;
console.log('[globalization] initial input to format: '+input);
navigator.globalization.numberToString(
Number(input),
function (number) {
console.log('[globalization] formatted number: '+number.value);
},
globError,
{type:'percent'}
);
});
And here is a sample in the browser.

InAppBrowser
I had assumed this would just do a window.open, but surprisingly, it actually uses an iframe. Here is the code I used to test:
document.querySelector('#testIAB').addEventListener('click', function() {
iabRef = cordova.InAppBrowser.open('http://www.raymondcamden.com','_blank','location=yes');
});
And here is how it rendered:

As with the camera plugin, it is rendered at the bottom of the dom. Note the gray border and basic controls.
Media
For media, you can only do audio playback, not recordings. It works pretty much as expected:
/*
mp3 source:
http://www.gutenberg.org/ebooks/10246
Sensation Jazz: One-Step by All-Star Trio
*/
var mp3 = './10246-m-001.mp3';
var media;
document.querySelector('#testMedia').addEventListener('click', function() {
media = new Media(mp3, function() {
console.log('[media] Success');
}, function(err) {
console.log('[media error]', err);
}, function(s) {
/*
Media.MEDIA_NONE = 0;
Media.MEDIA_STARTING = 1;
Media.MEDIA_RUNNING = 2;
Media.MEDIA_PAUSED = 3;
Media.MEDIA_STOPPED = 4;
*/
console.log('[media status]', s);
});
setTimeout(function() {
console.log('[media] Duration is '+media.getDuration());
},100);
media.play();
});
document.querySelector('#testMediaStop').addEventListener('click', function() {
media.stop();
});
Note the setTimeout for duration. I noticed it took a little bit before you could get an accurate reading of the duration.
Network
Unfortunately, this is pretty poor. From the quirks: "Browser can't detect the type of network connection. navigator.connection.type is always set to Connection.UNKNOWN when online." My tests verified that.
SplashScreen
Yes! This works in the Browser platform. As crazy as it sounds, you can actually fire up a splash screen in front of your main application. You want to copy the code from the "Browser Quirks" section of the docs and then setup your image. Once you do, it should just work, although I noticed SplashScreenWidth
and SplashScreenHeight
did not seem to do anything. Here is an example:

Wrap Up
So yeah, over all, I think things are pretty solid, especially if you approach this as a way of testing. As a plugin developer, you can easily create a mock interface for your particular feature to allow you to use the browser and more rapidly develop your code. A colleague of mine, Nic Raboy, wrote an interesting article a few weeks back about the pitfalls of this type of development: Why You Should Not Use Ionic Serve For Hybrid Apps. As I said in the comments there, I do not agree. I believe as long as you have a solid understanding of what your limitations are, testing your mobile apps in the browser (or I should say, developing, not 'final testing') is both useful and practical. I hope this look at the Browser platform was helpful, and let me know if you have any questions by posting a comment below.
Archived Comments
Nice post. Hopefully the people who have these typical problems see this post
Thanks for this post Raymond. FYI: I logged a JIRA bug report on the subject of the Cordova documentation makes NO mention of "Browser" platform support a few days ago. I got a "thanks for telling us - we have never documented this" but no sense that it would be addressed at all.
Wow, I just noticed that myself - it isn't listed in the nav at all. What's the JIRA #?
Oops, this reply was meant for Rob's comment, not yours Nic.
CB-10872
Raymond Camden
Mod
Thank you so much! I just started to work with Phonegap, your articles saving a lot of time, all got clear now :)
You are welcome.
Rather off topic: I have an older existing web application for which I want to write mobile friendly screens. I could write some HTML/CSS/js screens (putting them into a mobile app using PG), using jQuery to call the existing server functions, or, write mobile specific screens (in addition to the existing old-school screens) and just keep everything on the IIS web server.
For several reasons I am leaning towards the latter approach. However, I would like a web app (essentially a welcome/log-in page) so that it could be part of the Google and Apple application world. That is, the app logs you in but you then are experiencing the web site.
Do you have any articles that cover this? (sorry for the long post).
Thank you
I do. This one is a bit old, but discusses it: http://www.raymondcamden.co...
Thank you so much!
Ok, I believe I have the gist. Your mobile pages are part of your codebase that is PG'd into an app. The js requests data, and the server side code was modified to serve back data in JSON for this purpose.
My quest would be to not write client side pages, but rather write the new pages on the server, albeit in a \Mobile directory. These new pages would make the same calls that the non-Mobile friendly pages currently do. When a mobile device hits the website everything is beautiful as they are directed to the \Mobile directory.
However, I want this as an app so that I can put it into an app store. So my thinking was to have a 1 pager PG'd into an app, but when the user went past the app greeting screen they were actually web browsing the site. Am I over-complicating things? I could simply load my site up with one of the PG browser components once thru the front door of the app?
Thank you
In general - most folks don't do "if your from a mobile browser, go to this subdirectory" anymore. You can, but I don't think I'd recommend that. If you want to use "just regular web pages", I'd look at a responsive design so your main dot com works well in multiple form factors.
As to your second idea - again - you *can* make a PG app hit a URL and browse there, but I would not recommend that either. It "works", but is clunky and not really intended use of the app platform. (Apple may even reject it.)
Thank you sir.
There are some issues with using the browser platforms as a way to test the apps though - first, it does not have the same cordova.js that say android and iOS apps will have. This could put off frameworks like Ionic that rely on cordova.js to get the platform that the app is running on, to display the UI.
Also, the plugin support on browsers is also not 100% uniform. If you are using it as a way to test your apps, would you rather have a better, working Ripple platform - with things like live reload, hot module replacement, debuggability, plugin simulation, etc ?
Plugin simulate could be particularly interesting - instead of giving you real data, the UI could have ways for you to "mock" the return values. The mocks could double up as objects in a test code.
These are definitely valid concerns, and I think I brought them up - except for cordova.js differences. To be fair, that's how *all* platforms are - the cordova.js for Android is not the same as iOS is not the same as Windows - etc.
If I were doing Ionic only, I'd probably just use ionic serve, but honestly, I don't care if the UI breaks typically. Normally I'm more worried about some other aspect, so if the UI defaults to the wrong platform, I'm ok with that.
Ripple: I like Ripple, I'm happy there is some life in it, but right now, the browser platform seems a bit more stable. I'm not sure why you say debugging is Ripple only, you can definitely debug w/ the Browser platform, and as for "platform simulation", thats kind of what is going on here too anyway.
For example, you say "the UI could have ways for you to "mock" the return values", but that's *exactly* what I said it does in some regards - like the barcode plugin.
For the mock part, some plugins allow mocking, others don't. I think the confusion here is - is browser a testing platform, or a real deployment platform - and all plugins need to be consistent with that.
If it is indeed a testing platform, do we need a "platform" specifically for testing ? Testing on code as real as possible is the best option, and maybe mocking iOS and Android builds should be the way to go ?
So to your first thing - I can't answer that. I can only speak to how I think it should be used and where I think it may be appropriate for users.
"Testing on code as real as possible is the best option, and maybe mocking iOS and Android builds should be the way to go ?"
I agree with you, which is why I tried to make it clear, multiple times, that what I'm talking about here is useful in development, but doesn't replace testing on a real device. If that point wasn't clear, then please reread what I said above. If you do not agree, that's fine too, we can agree to disagree. ;)
I was just probing a little to see if an experiment I am working on is something people may want. :)
I am working on an idea called "taco-simulate" - a replacement for Ripple. Clearly, looks like plugin simulation is not a top priority - this is consistent with what others have also told me.
This is great insights, and thanks for the blog - I was just trying really hard to see if plugin mocks would appeal to someone.
I think a lot depends on how you build, what you build, etc. For me, a lot of my experience has been with doing "Cordova + X", for example, a camera demo showing some other library. In my case, I know the camera works. I don't care if I'm faking it with a input[type=file]. So for me, the Browser platform could be really useful.
Did you know Ripple was - kinda - relaunching? You may want to check with them before replacing it.
Who is "they" who are relaunching Ripple ? I thought it was us, folks who run the github ripple org.
Ugh sorry - I totally forgot you were in those discussions. No offense! But yeah - if you know it moved to GitHub - why not continue work there?
Then again, TACO is really spearheading some awesome stuff (that I need to give more attention to on my blog), so I can see doing something there.
Taco-simulate what we started with as an idea (before Ripple retired).
We will, at some point, merge that effort back into Ripple, but that was the reason I was asking what Ripple developers would expect.
I could show you what we have with taco-simulate if you are interested. I was planning to write to the list sometime to ask the features of taco-simulate we should merge back into Ripple, and hte ones we should discard - https://github.com/Microsof...
Nice post, thanks I had no idea about the alternative browser options etc. I wrote a blog post on extending a plugin to support the Browser platform that may be useful to those reading this. It's here: https://medium.com/modus-cr...
Very nice - thank you for sharing this.
Have you tested the Google Analytics plugin? I'm not entirely sure it works for the browser platform.
Nope. If you find an issue with it, the best thing to do is file a bug report.
Hey Ray - the browser platform doesn't seem to have a 'check_reqs' module, so it produces an annoying fail message when running:
> cordova requirements
Is there any way to suppress this error during the checks or acquire an appropriate check_reqs module for the platform?
Hmm.... I honestly don't know. I'd suggest filing a bug against the platform. That won't magically fix it, but will at least bring it to the right people. :)
That's true - thanks anyway, I'll get in touch with them.
This is incredibly cheeky as well, but while I have your attention do you have any knowledge as to the solution to this?
http://stackoverflow.com/qu...
TL:DR; CORS requests cause a problem (when testing via a browser) when the app is launched through the CLI (at localhost:8000) but not the desktop app (at 192.168.0.2:3002)... strange?
Um.... yes. :) No idea on that one. I tend to use a Chrome extension that forces CORS to be allowed.
I noticed that PhoneGap says plugins are being removed from the PhoneGap Build repository Nov 16, 2016. A screenshot of the ones in my app is attached. Does this mean I can continue to use the ones where Source = 'npm', but the one Source = 'pgb' I must remove?
Thank you https://uploads.disquscdn.c...
I'm sorry - but this does not have anything to do with this blog post. Please use the PhoneGap Build support link (http://docs.phonegap.com/ph... to ask.
Thank you!
Hi. The plugin for Google Analytics now supports Browser ;-).
Cool.
which one
Just search the extension gallery for CORS.
I have tried a few but none worked. Would be gr8 if you point one to me
I used one for a while but didn't like that it auto-enabled on Chrome reloading. I'd suggest just trying the one with the highest rating.
Hi. I'm implementing the cordova-plugin-vibration and would like to know could I test if vibration is happening from a browser, or eventually an Android Virtual Device. Thanks!
I haven't tested it with the browser platform, but in Chrome Dev Tools, when you emulate a device, there is a little 3d box representing the device you can turn around and stuff. That _may_ work.
Hi, thank you! But no, it didn't simulate the vibration. Anyway navigator.vibrate is returning "true" so I assume it's doing what it should. AVD emulator neither seem to even mention vibrating. Strange that I haven't find in any website people trying to test that feature, since there's a module for it.
Oh I'm sorry - vibration. I totally misread that. Since vibration 'shakes' the device, why do you need to test it on browser? Just run it, if available, and ignore it not. Right?
Hey, I also tried out the browser platform.... Everything works fine but just the first time.
If I change my source files, then i execute the run browser command, the old source is loaded. No changes take effect. If I edit the index.html, it takes effect.
Do you know this problem?
So wait, you are editing stuff and it doesn't work, but editing index.html works. So what did you edit?
Sorry if I'm a little late to the party but Im curious if I wanted to deploy from www such that cordova plugins works like they do when ionic run browser, how would I do that?
After each time you make changes / build your js code, you need to run `cordova build browser` and then refresh the browser to load the new stuff.
Please add votes to the idea to add Browser platform into Visual Studio to help the process of integrating this platform.
Raymond Camden Are you really using PhoneGap instead of an IDE like Visual Studio to compile apps?
Yes, I am. I don't find the CLI hard to use. In times when it bothered me having to use it I just used Gulp to automate the process. Also, I'm not a Visual Studio user. I do use Visual Studio Code though.
The browser platform is not supported by cordova-plugin-mfp. No operation will be performed for the specified platform.
I am getting this error when I try to run cordova run browser.
Can someone comment on this error
Thanks! :) helpful article
I had no idea you could build a Cordova app natively to run in the browser!! This is going to save me so much time!
One quick note about the 'filewatcher' utility. I found that the command you used did not watch the root of the www folder -- it only watched the subdirectories underneath the www folder. So when I changed my index.html file, it would not automatically catch that and run the "cordova prepare browser" command.
So I changed the filewatcher command to the following:
filewatcher -s -l "www/*.* www/**/*.*" "cordova prepare browser"
This watches the root of the www folder in addition to the subdirectories inside the www folder. The -l option (that's a lower case L) makes filewatcher list out each file it's watching (which could be a long list, so feel free to leave that one out if you want to) and the -s option makes it display "Watching" with a spinny asterisk-looking thing next to it, instead of a blank line, so you know it's actually still running and hasn't locked up. That's literally just a cosmetic thing, so feel free to leave off the -s parameter if you don't want that.
Thanks for this post! It's been very helpful!
Glad it helped!