Merge branch 'master' of https://github.com/uareurapid/cordova-plugin-inappbrowser
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index e5fcfd6..5cb452c 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -20,6 +20,11 @@
 -->
 # Release Notes
 
+### 1.2.0 (Jan 15, 2016)
+* CB-8180: Changing methods of interception in `WebViewClient` class
+* CB-10009 Improve `InAppBrowser` toolbar look and feel on **Windows**
+* Open a new window on the **Browser** platform
+
 ### 1.1.1 (Dec 10, 2015)
 
 * CB-9445 Improves executeScript callbacks on iOS
diff --git a/package.json b/package.json
index dc1c422..55ca8d7 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "cordova-plugin-inappbrowser",
-  "version": "1.1.2-dev",
+  "version": "1.2.1-dev",
   "description": "Cordova InAppBrowser Plugin",
   "cordova": {
     "id": "cordova-plugin-inappbrowser",
diff --git a/plugin.xml b/plugin.xml
index 052bd13..359d660 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -20,7 +20,7 @@
 
 <plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
            id="cordova-plugin-inappbrowser"
-      version="1.1.2-dev">
+      version="1.2.1-dev">
 
     <name>InAppBrowser</name>
     <description>Cordova InAppBrowser Plugin</description>
diff --git a/src/android/InAppBrowser.java b/src/android/InAppBrowser.java
index 8487b64..6612a66 100644
--- a/src/android/InAppBrowser.java
+++ b/src/android/InAppBrowser.java
@@ -362,20 +362,22 @@
      * Closes the dialog
      */
     public void closeDialog() {
-        final WebView childView = this.inAppWebView;
-        // The JS protects against multiple calls, so this should happen only when
-        // closeDialog() is called by other native code.
-        if (childView == null) {
-            return;
-        }
         this.cordova.getActivity().runOnUiThread(new Runnable() {
             @Override
             public void run() {
+                final WebView childView = inAppWebView;
+                // The JS protects against multiple calls, so this should happen only when
+                // closeDialog() is called by other native code.
+                if (childView == null) {
+                    return;
+                }
+
                 childView.setWebViewClient(new WebViewClient() {
                     // NB: wait for about:blank before dismissing
                     public void onPageFinished(WebView view, String url) {
                         if (dialog != null) {
                             dialog.dismiss();
+                            dialog = null;
                         }
                     }
                 });
@@ -383,16 +385,16 @@
                 // other than your app's UI thread, it can cause unexpected results."
                 // http://developer.android.com/guide/webapps/migrating.html#Threads
                 childView.loadUrl("about:blank");
+
+                try {
+                    JSONObject obj = new JSONObject();
+                    obj.put("type", EXIT_EVENT);
+                    sendUpdate(obj, false);
+                } catch (JSONException ex) {
+                    Log.d(LOG_TAG, "Should never happen");
+                }
             }
         });
-
-        try {
-            JSONObject obj = new JSONObject();
-            obj.put("type", EXIT_EVENT);
-            sendUpdate(obj, false);
-        } catch (JSONException ex) {
-            Log.d(LOG_TAG, "Should never happen");
-        }
     }
 
     /**
@@ -519,6 +521,12 @@
 
             @SuppressLint("NewApi")
             public void run() {
+
+                // CB-6702 InAppBrowser hangs when opening more than one instance
+                if (dialog != null) {
+                    dialog.dismiss();
+                };
+
                 // Let's create the main dialog
                 dialog = new InAppBrowserDialog(cordova.getActivity(), android.R.style.Theme_NoTitleBar);
                 dialog.getWindow().getAttributes().windowAnimations = android.R.style.Animation_Dialog;
@@ -544,7 +552,7 @@
                 actionButtonContainer.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
                 actionButtonContainer.setHorizontalGravity(Gravity.LEFT);
                 actionButtonContainer.setVerticalGravity(Gravity.CENTER_VERTICAL);
-                actionButtonContainer.setId(1);
+                actionButtonContainer.setId(Integer.valueOf(1));
 
                 // Back button
                 Button back = new Button(cordova.getActivity());
@@ -552,7 +560,7 @@
                 backLayoutParams.addRule(RelativeLayout.ALIGN_LEFT);
                 back.setLayoutParams(backLayoutParams);
                 back.setContentDescription("Back Button");
-                back.setId(2);
+                back.setId(Integer.valueOf(2));
                 Resources activityRes = cordova.getActivity().getResources();
                 int backResId = activityRes.getIdentifier("ic_action_previous_item", "drawable", cordova.getActivity().getPackageName());
                 Drawable backIcon = activityRes.getDrawable(backResId);
@@ -576,7 +584,7 @@
                 forwardLayoutParams.addRule(RelativeLayout.RIGHT_OF, 2);
                 forward.setLayoutParams(forwardLayoutParams);
                 forward.setContentDescription("Forward Button");
-                forward.setId(3);
+                forward.setId(Integer.valueOf(3));
                 int fwdResId = activityRes.getIdentifier("ic_action_next_item", "drawable", cordova.getActivity().getPackageName());
                 Drawable fwdIcon = activityRes.getDrawable(fwdResId);
                 if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN)
@@ -599,7 +607,7 @@
                 textLayoutParams.addRule(RelativeLayout.RIGHT_OF, 1);
                 textLayoutParams.addRule(RelativeLayout.LEFT_OF, 5);
                 edittext.setLayoutParams(textLayoutParams);
-                edittext.setId(4);
+                edittext.setId(Integer.valueOf(4));
                 edittext.setSingleLine(true);
                 edittext.setText(url);
                 edittext.setInputType(InputType.TYPE_TEXT_VARIATION_URI);
@@ -622,7 +630,7 @@
                 closeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
                 close.setLayoutParams(closeLayoutParams);
                 forward.setContentDescription("Close Button");
-                close.setId(5);
+                close.setId(Integer.valueOf(5));
                 int closeResId = activityRes.getIdentifier("ic_action_remove", "drawable", cordova.getActivity().getPackageName());
                 Drawable closeIcon = activityRes.getDrawable(closeResId);
                 if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN)
@@ -642,6 +650,7 @@
                 // WebView
                 inAppWebView = new WebView(cordova.getActivity());
                 inAppWebView.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+                inAppWebView.setId(Integer.valueOf(6));
                 inAppWebView.setWebChromeClient(new InAppChromeClient(thatWebView));
                 WebViewClient client = new InAppBrowserClient(thatWebView, edittext);
                 inAppWebView.setWebViewClient(client);
@@ -668,7 +677,7 @@
                 }
 
                 inAppWebView.loadUrl(url);
-                inAppWebView.setId(6);
+                inAppWebView.setId(Integer.valueOf(6));
                 inAppWebView.getSettings().setLoadWithOverviewMode(true);
                 inAppWebView.getSettings().setUseWideViewPort(true);
                 inAppWebView.requestFocus();
@@ -756,34 +765,30 @@
         }
 
         /**
-         * Notify the host application that a page has started loading.
+         * Override the URL that should be loaded
          *
-         * @param view          The webview initiating the callback.
-         * @param url           The url of the page.
+         * This handles a small subset of all the URIs that would be encountered.
+         *
+         * @param webView
+         * @param url
          */
         @Override
-        public void onPageStarted(WebView view, String url,  Bitmap favicon) {
-            super.onPageStarted(view, url, favicon);
-            String newloc = "";
-            if (url.startsWith("http:") || url.startsWith("https:") || url.startsWith("file:")) {
-                newloc = url;
-            }
-            // If dialing phone (tel:5551212)
-            else if (url.startsWith(WebView.SCHEME_TEL)) {
+        public boolean shouldOverrideUrlLoading(WebView webView, String url) {
+            if (url.startsWith(WebView.SCHEME_TEL)) {
                 try {
                     Intent intent = new Intent(Intent.ACTION_DIAL);
                     intent.setData(Uri.parse(url));
                     cordova.getActivity().startActivity(intent);
+                    return true;
                 } catch (android.content.ActivityNotFoundException e) {
                     LOG.e(LOG_TAG, "Error dialing " + url + ": " + e.toString());
                 }
-            }
-
-            else if (url.startsWith("geo:") || url.startsWith(WebView.SCHEME_MAILTO) || url.startsWith("market:")) {
+            } else if (url.startsWith("geo:") || url.startsWith(WebView.SCHEME_MAILTO) || url.startsWith("market:")) {
                 try {
                     Intent intent = new Intent(Intent.ACTION_VIEW);
                     intent.setData(Uri.parse(url));
                     cordova.getActivity().startActivity(intent);
+                    return true;
                 } catch (android.content.ActivityNotFoundException e) {
                     LOG.e(LOG_TAG, "Error with " + url + ": " + e.toString());
                 }
@@ -798,8 +803,7 @@
                     int parmIndex = url.indexOf('?');
                     if (parmIndex == -1) {
                         address = url.substring(4);
-                    }
-                    else {
+                    } else {
                         address = url.substring(4, parmIndex);
 
                         // If body, then set sms body
@@ -815,29 +819,54 @@
                     intent.putExtra("address", address);
                     intent.setType("vnd.android-dir/mms-sms");
                     cordova.getActivity().startActivity(intent);
+                    return true;
                 } catch (android.content.ActivityNotFoundException e) {
                     LOG.e(LOG_TAG, "Error sending sms " + url + ":" + e.toString());
                 }
             }
-            else {
+            return false;
+        }
+
+
+        /*
+         * onPageStarted fires the LOAD_START_EVENT
+         *
+         * @param view
+         * @param url
+         * @param favicon
+         */
+        @Override
+        public void onPageStarted(WebView view, String url, Bitmap favicon) {
+            super.onPageStarted(view, url, favicon);
+            String newloc = "";
+            if (url.startsWith("http:") || url.startsWith("https:") || url.startsWith("file:")) {
+                newloc = url;
+            }
+            else
+            {
+                // Assume that everything is HTTP at this point, because if we don't specify,
+                // it really should be.  Complain loudly about this!!!
+                LOG.e(LOG_TAG, "Possible Uncaught/Unknown URI");
                 newloc = "http://" + url;
             }
 
+            // Update the UI if we haven't already
             if (!newloc.equals(edittext.getText().toString())) {
                 edittext.setText(newloc);
-            }
+             }
 
             try {
                 JSONObject obj = new JSONObject();
                 obj.put("type", LOAD_START_EVENT);
                 obj.put("url", newloc);
-
                 sendUpdate(obj, true);
             } catch (JSONException ex) {
-                Log.d(LOG_TAG, "Should never happen");
+                LOG.e(LOG_TAG, "URI passed in has caused a JSON error.");
             }
         }
 
+
+
         public void onPageFinished(WebView view, String url) {
             super.onPageFinished(view, url);
 
diff --git a/src/windows/InAppBrowserProxy.js b/src/windows/InAppBrowserProxy.js
index fc037bd..f6d1da6 100644
--- a/src/windows/InAppBrowserProxy.js
+++ b/src/windows/InAppBrowserProxy.js
@@ -25,7 +25,6 @@
 
 
 var cordova = require('cordova'),
-    channel = require('cordova/channel'),
     urlutil = require('cordova/urlutil');
 
 var browserWrap,
@@ -35,11 +34,12 @@
     backButton,
     forwardButton,
     closeButton,
-    bodyOverflowStyle;
+    bodyOverflowStyle,
+    navigationEventsCallback;
 
 // x-ms-webview is available starting from Windows 8.1 (platformId is 'windows')
 // http://msdn.microsoft.com/en-us/library/windows/apps/dn301831.aspx
-var isWebViewAvailable = cordova.platformId == 'windows';
+var isWebViewAvailable = cordova.platformId === 'windows';
 
 function attachNavigationEvents(element, callback) {
     if (isWebViewAvailable) {
@@ -48,19 +48,32 @@
         });
 
         element.addEventListener("MSWebViewNavigationCompleted", function (e) {
-            callback({ type: e.isSuccess ? "loadstop" : "loaderror", url: e.uri}, {keepCallback: true});
+            if (e.isSuccess) {
+                callback({ type: "loadstop", url: e.uri }, { keepCallback: true });
+            } else {
+                callback({ type: "loaderror", url: e.uri, code: e.webErrorStatus, message: "Navigation failed with error code " + e.webErrorStatus}, { keepCallback: true });
+            }
         });
 
         element.addEventListener("MSWebViewUnviewableContentIdentified", function (e) {
             // WebView found the content to be not HTML.
             // http://msdn.microsoft.com/en-us/library/windows/apps/dn609716.aspx
-            callback({ type: "loaderror", url: e.uri}, {keepCallback: true});
+            callback({ type: "loaderror", url: e.uri, code: e.webErrorStatus, message: "Navigation failed with error code " + e.webErrorStatus}, { keepCallback: true });
         });
 
         element.addEventListener("MSWebViewContentLoading", function (e) {
-            if (navigationButtonsDiv) {
-                backButton.disabled = !popup.canGoBack;
-                forwardButton.disabled = !popup.canGoForward;
+            if (navigationButtonsDiv && popup) {
+                if (popup.canGoBack) {
+                    backButton.removeAttribute("disabled");
+                } else {
+                    backButton.setAttribute("disabled", "true");
+                }
+
+                if (popup.canGoForward) {
+                    forwardButton.removeAttribute("disabled");
+                } else {
+                    forwardButton.setAttribute("disabled", "true");
+                }
             }
         });
     } else {
@@ -83,228 +96,228 @@
 
 var IAB = {
     close: function (win, lose) {
-        if (browserWrap) {
-            if (win) win({ type: "exit" });
+        setImmediate(function () {
+            if (browserWrap) {
+                if (navigationEventsCallback) {
+                    navigationEventsCallback({ type: "exit" });
+                }
 
-            browserWrap.parentNode.removeChild(browserWrap);
-            // Reset body overflow style to initial value
-            document.body.style.msOverflowStyle = bodyOverflowStyle;
-            browserWrap = null;
-            popup = null;
-        }
+                browserWrap.parentNode.removeChild(browserWrap);
+                // Reset body overflow style to initial value
+                document.body.style.msOverflowStyle = bodyOverflowStyle;
+                browserWrap = null;
+                popup = null;
+            }
+        });
     },
     show: function (win, lose) {
-        if (browserWrap) {
-            browserWrap.style.display = "block";
-        }
+        setImmediate(function () {
+            if (browserWrap) {
+                browserWrap.style.display = "block";
+            }
+        });
     },
     open: function (win, lose, args) {
-        var strUrl = args[0],
-            target = args[1],
-            features = args[2],
-            url;
+        // make function async so that we can add navigation events handlers before view is loaded and navigation occured
+        setImmediate(function () {
+            var strUrl = args[0],
+                target = args[1],
+                features = args[2],
+                url;
 
-        if (target === "_system") {
-            url = new Windows.Foundation.Uri(strUrl);
-            Windows.System.Launcher.launchUriAsync(url);
-        } else if (target === "_self" || !target) {
-            window.location = strUrl;
-        } else {
-            // "_blank" or anything else
-            if (!browserWrap) {
-                var browserWrapStyle = document.createElement('link');
-                browserWrapStyle.rel = "stylesheet";
-                browserWrapStyle.type = "text/css";
-                browserWrapStyle.href = urlutil.makeAbsolute("/www/css/inappbrowser.css");
+            navigationEventsCallback = win;
 
-                document.head.appendChild(browserWrapStyle);
-
-                browserWrap = document.createElement("div");
-                browserWrap.className = "inAppBrowserWrap";
-
-                if (features.indexOf("fullscreen=yes") > -1) {
-                    browserWrap.classList.add("inAppBrowserWrapFullscreen");
-                }
-
-                // Save body overflow style to be able to reset it back later
-                bodyOverflowStyle = document.body.style.msOverflowStyle;
-
-                browserWrap.onclick = function () {
-                    setTimeout(function () {
-                        IAB.close(win);
-                    }, 0);
-                };
-
-                document.body.appendChild(browserWrap);
-                // Hide scrollbars for the whole body while inappbrowser's window is open
-                document.body.style.msOverflowStyle = "none";
-            }
-
-            if (features.indexOf("hidden=yes") !== -1) {
-                browserWrap.style.display = "none";
-            }
-
-            popup = document.createElement(isWebViewAvailable ? "x-ms-webview" : "iframe");
-            if (popup instanceof HTMLIFrameElement) {
-                // For iframe we need to override bacground color of parent element here
-                // otherwise pages without background color set will have transparent background
-                popup.style.backgroundColor = "white";
-            }
-            popup.style.borderWidth = "0px";
-            popup.style.width = "100%";
-
-            browserWrap.appendChild(popup);
-
-            if (features.indexOf("location=yes") !== -1 || features.indexOf("location") === -1) {
-                popup.style.height = "calc(100% - 60px)";
-
-                navigationButtonsDiv = document.createElement("div");
-                navigationButtonsDiv.style.height = "60px";
-                navigationButtonsDiv.style.backgroundColor = "#404040";
-                navigationButtonsDiv.style.zIndex = "999";
-                navigationButtonsDiv.onclick = function (e) {
-                    e.cancelBubble = true;
-                };
-
-                navigationButtonsDivInner = document.createElement("div");
-                navigationButtonsDivInner.style.paddingTop = "10px";
-                navigationButtonsDivInner.style.height = "50px";
-                navigationButtonsDivInner.style.width = "160px";
-                navigationButtonsDivInner.style.margin = "0 auto";
-                navigationButtonsDivInner.style.backgroundColor = "#404040";
-                navigationButtonsDivInner.style.zIndex = "999";
-                navigationButtonsDivInner.onclick = function (e) {
-                    e.cancelBubble = true;
-                };
-
-
-                backButton = document.createElement("button");
-                backButton.style.width = "40px";
-                backButton.style.height = "40px";
-                backButton.style.borderRadius = "40px";
-
-                backButton.innerText = "<-";
-                backButton.addEventListener("click", function (e) {
-                    if (popup.canGoBack)
-                        popup.goBack();
-                });
-
-                forwardButton = document.createElement("button");
-                forwardButton.style.marginLeft = "20px";
-                forwardButton.style.width = "40px";
-                forwardButton.style.height = "40px";
-                forwardButton.style.borderRadius = "40px";
-
-                forwardButton.innerText = "->";
-                forwardButton.addEventListener("click", function (e) {
-                    if (popup.canGoForward)
-                        popup.goForward();
-                });
-
-                closeButton = document.createElement("button");
-                closeButton.style.marginLeft = "20px";
-                closeButton.style.width = "40px";
-                closeButton.style.height = "40px";
-                closeButton.style.borderRadius = "40px";
-
-                closeButton.innerText = "x";
-                closeButton.addEventListener("click", function (e) {
-                    setTimeout(function () {
-                        IAB.close(win);
-                    }, 0);
-                });
-
-                if (!isWebViewAvailable) {
-                    // iframe navigation is not yet supported
-                    backButton.disabled = true;
-                    forwardButton.disabled = true;
-                }
-
-                navigationButtonsDivInner.appendChild(backButton);
-                navigationButtonsDivInner.appendChild(forwardButton);
-                navigationButtonsDivInner.appendChild(closeButton);
-                navigationButtonsDiv.appendChild(navigationButtonsDivInner);
-
-                browserWrap.appendChild(navigationButtonsDiv);
+            if (target === "_system") {
+                url = new Windows.Foundation.Uri(strUrl);
+                Windows.System.Launcher.launchUriAsync(url);
+            } else if (target === "_self" || !target) {
+                window.location = strUrl;
             } else {
-                popup.style.height = "100%";
-            }
+                // "_blank" or anything else
+                if (!browserWrap) {
+                    var browserWrapStyle = document.createElement('link');
+                    browserWrapStyle.rel = "stylesheet";
+                    browserWrapStyle.type = "text/css";
+                    browserWrapStyle.href = urlutil.makeAbsolute("/www/css/inappbrowser.css");
 
-            // start listening for navigation events
-            attachNavigationEvents(popup, win);
+                    document.head.appendChild(browserWrapStyle);
 
-            if (isWebViewAvailable) {
-                strUrl = strUrl.replace("ms-appx://", "ms-appx-web://");
+                    browserWrap = document.createElement("div");
+                    browserWrap.className = "inAppBrowserWrap";
+
+                    if (features.indexOf("fullscreen=yes") > -1) {
+                        browserWrap.classList.add("inAppBrowserWrapFullscreen");
+                    }
+
+                    // Save body overflow style to be able to reset it back later
+                    bodyOverflowStyle = document.body.style.msOverflowStyle;
+
+                    browserWrap.onclick = function () {
+                        setTimeout(function () {
+                            IAB.close(navigationEventsCallback);
+                        }, 0);
+                    };
+
+                    document.body.appendChild(browserWrap);
+                    // Hide scrollbars for the whole body while inappbrowser's window is open
+                    document.body.style.msOverflowStyle = "none";
+                }
+
+                if (features.indexOf("hidden=yes") !== -1) {
+                    browserWrap.style.display = "none";
+                }
+
+                popup = document.createElement(isWebViewAvailable ? "x-ms-webview" : "iframe");
+                if (popup instanceof HTMLIFrameElement) {
+                    // For iframe we need to override bacground color of parent element here
+                    // otherwise pages without background color set will have transparent background
+                    popup.style.backgroundColor = "white";
+                }
+                popup.style.borderWidth = "0px";
+                popup.style.width = "100%";
+
+                browserWrap.appendChild(popup);
+
+                if (features.indexOf("location=yes") !== -1 || features.indexOf("location") === -1) {
+                    popup.style.height = "calc(100% - 70px)";
+
+                    navigationButtonsDiv = document.createElement("div");
+                    navigationButtonsDiv.className = "inappbrowser-app-bar";
+                    navigationButtonsDiv.onclick = function (e) {
+                        e.cancelBubble = true;
+                    };
+
+                    navigationButtonsDivInner = document.createElement("div");
+                    navigationButtonsDivInner.className = "inappbrowser-app-bar-inner"
+                    navigationButtonsDivInner.onclick = function (e) {
+                        e.cancelBubble = true;
+                    };
+
+                    backButton = document.createElement("div");
+                    backButton.innerText = "back";
+                    backButton.className = "app-bar-action action-back";
+                    backButton.addEventListener("click", function (e) {
+                        if (popup.canGoBack)
+                            popup.goBack();
+                    });
+
+                    forwardButton = document.createElement("div");
+                    forwardButton.innerText = "forward";
+                    forwardButton.className = "app-bar-action action-forward";
+                    forwardButton.addEventListener("click", function (e) {
+                        if (popup.canGoForward)
+                            popup.goForward();
+                    });
+
+                    closeButton = document.createElement("div");
+                    closeButton.innerText = "close";
+                    closeButton.className = "app-bar-action action-close";
+                    closeButton.addEventListener("click", function (e) {
+                        setTimeout(function () {
+                            IAB.close(navigationEventsCallback);
+                        }, 0);
+                    });
+
+                    if (!isWebViewAvailable) {
+                        // iframe navigation is not yet supported
+                        backButton.setAttribute("disabled", "true");
+                        forwardButton.setAttribute("disabled", "true");
+                    }
+
+                    navigationButtonsDivInner.appendChild(backButton);
+                    navigationButtonsDivInner.appendChild(forwardButton);
+                    navigationButtonsDivInner.appendChild(closeButton);
+                    navigationButtonsDiv.appendChild(navigationButtonsDivInner);
+
+                    browserWrap.appendChild(navigationButtonsDiv);
+                } else {
+                    popup.style.height = "100%";
+                }
+
+                // start listening for navigation events
+                attachNavigationEvents(popup, navigationEventsCallback);
+
+                if (isWebViewAvailable) {
+                    strUrl = strUrl.replace("ms-appx://", "ms-appx-web://");
+                }
+                popup.src = strUrl;
             }
-            popup.src = strUrl;
-        }
+        });
     },
 
     injectScriptCode: function (win, fail, args) {
-        var code = args[0],
-            hasCallback = args[1];
+        setImmediate(function () {
+            var code = args[0],
+                hasCallback = args[1];
 
-        if (isWebViewAvailable && browserWrap && popup) {
-            var op = popup.invokeScriptAsync("eval", code);
-            op.oncomplete = function (e) {
-                // return null if event target is unavailable by some reason
-                var result = (e && e.target) ? [e.target.result] : [null];
-                hasCallback && win(result);
-            };
-            op.onerror = function () { };
-            op.start();
-        }
+            if (isWebViewAvailable && browserWrap && popup) {
+                var op = popup.invokeScriptAsync("eval", code);
+                op.oncomplete = function (e) {
+                    // return null if event target is unavailable by some reason
+                    var result = (e && e.target) ? [e.target.result] : [null];
+                    hasCallback && win(result);
+                };
+                op.onerror = function () { };
+                op.start();
+            }
+        });
     },
 
     injectScriptFile: function (win, fail, args) {
-        var filePath = args[0],
-            hasCallback = args[1];
+        setImmediate(function () {
+            var filePath = args[0],
+                hasCallback = args[1];
 
-        if (!!filePath) {
-            filePath = urlutil.makeAbsolute(filePath);
-        }
+            if (!!filePath) {
+                filePath = urlutil.makeAbsolute(filePath);
+            }
 
-        if (isWebViewAvailable && browserWrap && popup) {
-            var uri = new Windows.Foundation.Uri(filePath);
-            Windows.Storage.StorageFile.getFileFromApplicationUriAsync(uri).done(function (file) {
-                Windows.Storage.FileIO.readTextAsync(file).done(function (code) {
-                    var op = popup.invokeScriptAsync("eval", code);
-                    op.oncomplete = function(e) {
-                        var result = [e.target.result];
-                        hasCallback && win(result);
-                    };
-                    op.onerror = function () { };
-                    op.start();
+            if (isWebViewAvailable && browserWrap && popup) {
+                var uri = new Windows.Foundation.Uri(filePath);
+                Windows.Storage.StorageFile.getFileFromApplicationUriAsync(uri).done(function (file) {
+                    Windows.Storage.FileIO.readTextAsync(file).done(function (code) {
+                        var op = popup.invokeScriptAsync("eval", code);
+                        op.oncomplete = function(e) {
+                            var result = [e.target.result];
+                            hasCallback && win(result);
+                        };
+                        op.onerror = function () { };
+                        op.start();
+                    });
                 });
-            });
-        }
+            }
+        });
     },
 
     injectStyleCode: function (win, fail, args) {
-        var code = args[0],
-            hasCallback = args[1];
+        setImmediate(function () {
+            var code = args[0],
+                hasCallback = args[1];
 
-        if (isWebViewAvailable && browserWrap && popup) {
-            injectCSS(popup, code, hasCallback && win);
-        }
+            if (isWebViewAvailable && browserWrap && popup) {
+                injectCSS(popup, code, hasCallback && win);
+            }
+        });
     },
 
     injectStyleFile: function (win, fail, args) {
-        var filePath = args[0],
-            hasCallback = args[1];
+        setImmediate(function () {
+            var filePath = args[0],
+                hasCallback = args[1];
 
-        filePath = filePath && urlutil.makeAbsolute(filePath);
+            filePath = filePath && urlutil.makeAbsolute(filePath);
 
-        if (isWebViewAvailable && browserWrap && popup) {
-            var uri = new Windows.Foundation.Uri(filePath);
-            Windows.Storage.StorageFile.getFileFromApplicationUriAsync(uri).then(function (file) {
-                return Windows.Storage.FileIO.readTextAsync(file);
-            }).done(function (code) {
-                injectCSS(popup, code, hasCallback && win);
-            }, function () {
-                // no-op, just catch an error
-            });
-        }
+            if (isWebViewAvailable && browserWrap && popup) {
+                var uri = new Windows.Foundation.Uri(filePath);
+                Windows.Storage.StorageFile.getFileFromApplicationUriAsync(uri).then(function (file) {
+                    return Windows.Storage.FileIO.readTextAsync(file);
+                }).done(function (code) {
+                    injectCSS(popup, code, hasCallback && win);
+                }, function () {
+                    // no-op, just catch an error
+                });
+            }
+        });
     }
 };
 
diff --git a/tests/plugin.xml b/tests/plugin.xml
index 5f1a8b0..8562fab 100644
--- a/tests/plugin.xml
+++ b/tests/plugin.xml
@@ -20,10 +20,12 @@
 
 <plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
     id="cordova-plugin-inappbrowser-tests"
-    version="1.1.2-dev">
+    version="1.2.1-dev">
     <name>Cordova InAppBrowser Plugin Tests</name>
     <license>Apache 2.0</license>
 
+    <dependency id="cordova-plugin-dialogs" />
+
     <js-module src="tests.js" name="tests">
     </js-module>
 
diff --git a/tests/tests.js b/tests/tests.js
index a827986..67dd4b6 100644
--- a/tests/tests.js
+++ b/tests/tests.js
@@ -24,6 +24,110 @@
 
 window.alert = window.alert || navigator.notification.alert;
 
+exports.defineAutoTests = function () {
+
+    describe('cordova.InAppBrowser', function () {
+
+        it("inappbrowser.spec.1 should exist", function () {
+            expect(cordova.InAppBrowser).toBeDefined();
+        });
+
+        it("inappbrowser.spec.2 should contain open function", function () {
+            expect(cordova.InAppBrowser.open).toBeDefined();
+            expect(cordova.InAppBrowser.open).toEqual(jasmine.any(Function));
+        });
+    });
+
+    describe('open method', function () {
+
+        var iabInstance;
+        var originalTimeout;
+        var url = 'http://apache.org/';
+        var badUrl = 'http://bad-uri/';
+
+        beforeEach(function () {
+            // increase timeout to ensure test url could be loaded within test time
+            originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
+            jasmine.DEFAULT_TIMEOUT_INTERVAL = 15000;
+
+            iabInstance = null;
+        });
+
+        afterEach(function (done) {
+            // restore original timeout
+            jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
+
+            if (iabInstance !== null && iabInstance.close) {
+                iabInstance.close();
+            }
+            iabInstance = null;
+            // add some extra time so that iab dialog is closed
+            setTimeout(done, 2000);
+        });
+
+        function verifyEvent(evt, type) {
+            expect(evt).toBeDefined();
+            expect(evt.type).toEqual(type);
+            if (type !== 'exit') { // `exit` event does not have url field
+                expect(evt.url).toEqual(url);
+            }
+        }
+
+        function verifyLoadErrorEvent(evt) {
+            expect(evt).toBeDefined();
+            expect(evt.type).toEqual('loaderror');
+            expect(evt.url).toEqual(badUrl);
+            expect(evt.code).toEqual(jasmine.any(Number));
+            expect(evt.message).toEqual(jasmine.any(String));
+        }
+
+        it("inappbrowser.spec.3 should retun InAppBrowser instance with required methods", function () {
+            iabInstance = cordova.InAppBrowser.open(url, '_blank');
+
+            expect(iabInstance).toBeDefined();
+
+            expect(iabInstance.addEventListener).toEqual(jasmine.any(Function));
+            expect(iabInstance.removeEventListener).toEqual(jasmine.any(Function));
+            expect(iabInstance.close).toEqual(jasmine.any(Function));
+            expect(iabInstance.show).toEqual(jasmine.any(Function));
+            expect(iabInstance.executeScript).toEqual(jasmine.any(Function));
+            expect(iabInstance.insertCSS).toEqual(jasmine.any(Function));
+        });
+
+        it("inappbrowser.spec.4 should support loadstart and loadstop events", function (done) {
+            var onLoadStart = jasmine.createSpy('loadstart event callback').and.callFake(function (evt) {
+                verifyEvent(evt, 'loadstart');
+            });
+
+            iabInstance = cordova.InAppBrowser.open(url, '_blank');
+            iabInstance.addEventListener('loadstart', onLoadStart);
+            iabInstance.addEventListener('loadstop', function (evt) {
+                verifyEvent(evt, 'loadstop');
+                expect(onLoadStart).toHaveBeenCalled();
+                done();
+            });
+        });
+
+        it("inappbrowser.spec.5 should support exit event", function (done) {
+            iabInstance = cordova.InAppBrowser.open(url, '_blank');
+            iabInstance.addEventListener('exit', function (evt) {
+                verifyEvent(evt, 'exit');
+                done();
+            });
+            iabInstance.close();
+            iabInstance = null;
+        });
+
+        it("inappbrowser.spec.6 should support loaderror event", function (done) {
+            iabInstance = cordova.InAppBrowser.open(badUrl, '_blank');
+            iabInstance.addEventListener('loaderror', function (evt) {
+                verifyLoadErrorEvent(evt);
+                done();
+            });
+        });
+    });
+};
+
 exports.defineManualTests = function (contentEl, createActionButton) {
 
     function doOpen(url, target, params, numExpectedRedirects, useWindowOpen) {
diff --git a/www/inappbrowser.css b/www/inappbrowser.css
index bd1526d..5762c74 100644
--- a/www/inappbrowser.css
+++ b/www/inappbrowser.css
@@ -38,3 +38,77 @@
 .inAppBrowserWrapFullscreen {
     border: 0;
 }
+
+.inappbrowser-app-bar {
+    height: 70px;
+    background-color: #404040;
+    z-index: 9999999;
+}
+
+.inappbrowser-app-bar-inner {
+    padding-top: 10px;
+    height: 60px;
+    width: 155px;
+    margin: 0 auto;
+    background-color: #404040;
+    z-index: 9999999;
+}
+
+.app-bar-action {
+    width: auto;
+    height: 40px;
+    margin-left: 20px;
+    font-family: "Segoe UI Symbol";
+    float: left;
+    color: white;
+    font-size: 12px;
+    text-transform: lowercase;
+    text-align: center;
+    cursor: default;
+}
+
+.app-bar-action[disabled] {
+    color: gray;
+    /*disable click*/
+    pointer-events: none;
+}
+
+.app-bar-action::before {
+    font-size: 28px;
+    display: block;
+    height: 36px;
+}
+
+/* Back */
+.action-back { 
+    margin-left: 0px;
+}
+
+.action-back::before {
+    content: "\E0BA";
+}
+
+.action-back:not([disabled]):hover::before {
+    content: "\E0B3";
+}
+
+/* Forward */
+.action-forward::before {
+    content: "\E0AC";
+}
+
+.action-forward:not([disabled]):hover::before {
+    content: "\E0AF";
+}
+
+/* Close */
+.action-close::before {
+    content: "\E0C7";
+    /* close icon is larger so we re-size it to fit other icons */
+    font-size: 20px;
+    line-height: 40px;
+}
+
+.action-close:not([disabled]):hover::before {
+    content: "\E0CA";
+}
diff --git a/www/inappbrowser.js b/www/inappbrowser.js
index 93eb420..25f6271 100644
--- a/www/inappbrowser.js
+++ b/www/inappbrowser.js
@@ -19,92 +19,93 @@
  *
 */
 
-// special patch to correctly work on Ripple emulator (CB-9760)
-if (window.parent && !!window.parent.ripple) { // https://gist.github.com/triceam/4658021
-    module.exports = window.open.bind(window); // fallback to default window.open behaviour
-    return;
-}
-
-var exec = require('cordova/exec');
-var channel = require('cordova/channel');
-var modulemapper = require('cordova/modulemapper');
-var urlutil = require('cordova/urlutil');
-
-function InAppBrowser() {
-   this.channels = {
-        'loadstart': channel.create('loadstart'),
-        'loadstop' : channel.create('loadstop'),
-        'loaderror' : channel.create('loaderror'),
-        'exit' : channel.create('exit')
-   };
-}
-
-InAppBrowser.prototype = {
-    _eventHandler: function (event) {
-        if (event && (event.type in this.channels)) {
-            this.channels[event.type].fire(event);
-        }
-    },
-    close: function (eventname) {
-        exec(null, null, "InAppBrowser", "close", []);
-    },
-    show: function (eventname) {
-      exec(null, null, "InAppBrowser", "show", []);
-    },
-    addEventListener: function (eventname,f) {
-        if (eventname in this.channels) {
-            this.channels[eventname].subscribe(f);
-        }
-    },
-    removeEventListener: function(eventname, f) {
-        if (eventname in this.channels) {
-            this.channels[eventname].unsubscribe(f);
-        }
-    },
-
-    executeScript: function(injectDetails, cb) {
-        if (injectDetails.code) {
-            exec(cb, null, "InAppBrowser", "injectScriptCode", [injectDetails.code, !!cb]);
-        } else if (injectDetails.file) {
-            exec(cb, null, "InAppBrowser", "injectScriptFile", [injectDetails.file, !!cb]);
-        } else {
-            throw new Error('executeScript requires exactly one of code or file to be specified');
-        }
-    },
-
-    insertCSS: function(injectDetails, cb) {
-        if (injectDetails.code) {
-            exec(cb, null, "InAppBrowser", "injectStyleCode", [injectDetails.code, !!cb]);
-        } else if (injectDetails.file) {
-            exec(cb, null, "InAppBrowser", "injectStyleFile", [injectDetails.file, !!cb]);
-        } else {
-            throw new Error('insertCSS requires exactly one of code or file to be specified');
-        }
-    }
-};
-
-module.exports = function(strUrl, strWindowName, strWindowFeatures, callbacks) {
-    // Don't catch calls that write to existing frames (e.g. named iframes).
-    if (window.frames && window.frames[strWindowName]) {
-        var origOpenFunc = modulemapper.getOriginalSymbol(window, 'open');
-        return origOpenFunc.apply(window, arguments);
+(function() {
+    // special patch to correctly work on Ripple emulator (CB-9760)
+    if (window.parent && !!window.parent.ripple) { // https://gist.github.com/triceam/4658021
+        module.exports = window.open.bind(window); // fallback to default window.open behaviour
+        return;
     }
 
-    strUrl = urlutil.makeAbsolute(strUrl);
-    var iab = new InAppBrowser();
+    var exec = require('cordova/exec');
+    var channel = require('cordova/channel');
+    var modulemapper = require('cordova/modulemapper');
+    var urlutil = require('cordova/urlutil');
 
-    callbacks = callbacks || {};
-    for (var callbackName in callbacks) {
-        iab.addEventListener(callbackName, callbacks[callbackName]);
+    function InAppBrowser() {
+       this.channels = {
+            'loadstart': channel.create('loadstart'),
+            'loadstop' : channel.create('loadstop'),
+            'loaderror' : channel.create('loaderror'),
+            'exit' : channel.create('exit')
+       };
     }
 
-    var cb = function(eventname) {
-       iab._eventHandler(eventname);
+    InAppBrowser.prototype = {
+        _eventHandler: function (event) {
+            if (event && (event.type in this.channels)) {
+                this.channels[event.type].fire(event);
+            }
+        },
+        close: function (eventname) {
+            exec(null, null, "InAppBrowser", "close", []);
+        },
+        show: function (eventname) {
+          exec(null, null, "InAppBrowser", "show", []);
+        },
+        addEventListener: function (eventname,f) {
+            if (eventname in this.channels) {
+                this.channels[eventname].subscribe(f);
+            }
+        },
+        removeEventListener: function(eventname, f) {
+            if (eventname in this.channels) {
+                this.channels[eventname].unsubscribe(f);
+            }
+        },
+
+        executeScript: function(injectDetails, cb) {
+            if (injectDetails.code) {
+                exec(cb, null, "InAppBrowser", "injectScriptCode", [injectDetails.code, !!cb]);
+            } else if (injectDetails.file) {
+                exec(cb, null, "InAppBrowser", "injectScriptFile", [injectDetails.file, !!cb]);
+            } else {
+                throw new Error('executeScript requires exactly one of code or file to be specified');
+            }
+        },
+
+        insertCSS: function(injectDetails, cb) {
+            if (injectDetails.code) {
+                exec(cb, null, "InAppBrowser", "injectStyleCode", [injectDetails.code, !!cb]);
+            } else if (injectDetails.file) {
+                exec(cb, null, "InAppBrowser", "injectStyleFile", [injectDetails.file, !!cb]);
+            } else {
+                throw new Error('insertCSS requires exactly one of code or file to be specified');
+            }
+        }
     };
 
-    strWindowFeatures = strWindowFeatures || "";
+    module.exports = function(strUrl, strWindowName, strWindowFeatures, callbacks) {
+        // Don't catch calls that write to existing frames (e.g. named iframes).
+        if (window.frames && window.frames[strWindowName]) {
+            var origOpenFunc = modulemapper.getOriginalSymbol(window, 'open');
+            return origOpenFunc.apply(window, arguments);
+        }
 
-    exec(cb, cb, "InAppBrowser", "open", [strUrl, strWindowName, strWindowFeatures]);
-    return iab;
-};
+        strUrl = urlutil.makeAbsolute(strUrl);
+        var iab = new InAppBrowser();
 
+        callbacks = callbacks || {};
+        for (var callbackName in callbacks) {
+            iab.addEventListener(callbackName, callbacks[callbackName]);
+        }
+
+        var cb = function(eventname) {
+           iab._eventHandler(eventname);
+        };
+
+        strWindowFeatures = strWindowFeatures || "";
+
+        exec(cb, cb, "InAppBrowser", "open", [strUrl, strWindowName, strWindowFeatures]);
+        return iab;
+    };
+})();