Merge mozilla-inbound in mozilla-central.
authorMounir Lamouri <mounir.lamouri@gmail.com>
Wed, 26 Sep 2012 11:57:25 +0100
changeset 108103 df69d95f636c
parent 108033 e64a78df7258 (current diff)
parent 108102 f0a48031d4d1 (diff)
child 108104 1ae2d42ad234
child 108127 be28055643f5
push id23533
push usermlamouri@mozilla.com
push dateWed, 26 Sep 2012 10:57:37 +0000
treeherdermozilla-central@df69d95f636c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone18.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-inbound in mozilla-central.
browser/themes/pinstripe/places/starPage.png
gfx/2d/DrawEventRecorder.h.rej
ipc/socket/Makefile.in
ipc/socket/Socket.cpp
ipc/socket/Socket.h
toolkit/crashreporter/google-breakpad/android/google_breakpad/Android.mk
toolkit/crashreporter/google-breakpad/android/sample_app/jni/Android.mk
--- a/accessible/tests/mochitest/relations/test_tabbrowser.xul
+++ b/accessible/tests/mochitest/relations/test_tabbrowser.xul
@@ -24,18 +24,18 @@
 
   <script type="application/javascript">
   <![CDATA[
     ////////////////////////////////////////////////////////////////////////////
     // Invoker
     function testTabRelations()
     {
       this.eventSeq = [
-        new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, tabDocumentAt, 0),
-        new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, tabDocumentAt, 1)
+        new asyncInvokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, tabDocumentAt, 0),
+        new asyncInvokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, tabDocumentAt, 1)
      ];
 
       this.invoke = function testTabRelations_invoke()
       {
         var docURIs = ["about:", "about:mozilla"];
         tabBrowser().loadTabs(docURIs, false, true);
       }
 
@@ -56,18 +56,20 @@
       this.getID = function testTabRelations_getID()
       {
         return "relations of tabs";
       }
     }
     
     ////////////////////////////////////////////////////////////////////////////
     // Test
+
+    //gA11yEventDumpToConsole = true; // debug stuff
+
     var gQueue = null;
-    gA11yEventDumpToConsole = true;
     function doTest()
     {
       // Load documents into tabs and wait for DocLoadComplete events caused by
       // these documents load before we start the test.
 
       gQueue = new eventQueue();
 
       gQueue.push(new testTabRelations());
--- a/accessible/tests/mochitest/tree/test_tabbrowser.xul
+++ b/accessible/tests/mochitest/tree/test_tabbrowser.xul
@@ -23,18 +23,18 @@
 
   <script type="application/javascript">
   <![CDATA[
     ////////////////////////////////////////////////////////////////////////////
     // invoker
     function testTabHierarchy()
     {
       this.eventSeq = [
-        new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, tabDocumentAt, 0),
-        new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, tabDocumentAt, 1)
+        new asyncInvokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, tabDocumentAt, 0),
+        new asyncInvokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, tabDocumentAt, 1)
       ];
 
       this.invoke = function testTabHierarchy_invoke()
       {
         var docURIs = ["about:", "about:mozilla"];
         tabBrowser().loadTabs(docURIs, false, true);
       }
 
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -349,16 +349,20 @@ pref("browser.dom.window.dump.enabled", 
 
 
 
 // Temporarily relax file:// origin checks so that we can use <img>s
 // from other dirs as webgl textures and more.  Remove me when we have
 // installable apps or wifi support.
 pref("security.fileuri.strict_origin_policy", false);
 
+// Default Content Security Policy to apply to privileged and certified apps
+pref("security.apps.privileged.CSP.default", "default-src *; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'");
+pref("security.apps.certified.CSP.default", "options inline-script eval-script; default-src *; script-src 'self'; object-src 'none'; style-src 'self'");
+
 // Temporarily force-enable GL compositing.  This is default-disabled
 // deep within the bowels of the widgetry system.  Remove me when GL
 // compositing isn't default disabled in widget/android.
 pref("layers.acceleration.force-enabled", true);
 
 // handle links targeting new windows
 // 1=current window/tab, 2=new window, 3=new tab in most recent window
 pref("browser.link.open_newwindow", 3);
--- a/b2g/config/panda/config.json
+++ b/b2g/config/panda/config.json
@@ -1,5 +1,5 @@
 {
     "tooltool_manifest": "releng-pandaboard.tt",
     "mock_target": "mozilla-centos6-i386",
-    "mock_packages": ["ccache", "make", "bison", "flex", "gcc", "g++", "mpfr", "zlib-devel", "ncurses-devel", "zip", "autoconf213", "glibc-static", "java-1.6.0-openjdk-devel"]
+    "mock_packages": ["ccache", "make", "bison", "flex", "gcc", "g++", "mpfr", "zlib-devel", "ncurses-devel", "zip", "autoconf213", "glibc-static", "perl-Digest-SHA"]
 }
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/experiment-page.js
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/experiment-page.js
@@ -68,110 +68,113 @@ var stringBundle;
     let task = TestPilotSetup.getTaskById(eid);
     task.dataStore.wipeAllData();
     // reload the URL after wiping all data.
     window.location = "chrome://testpilot/content/status.html?eid=" + eid;
   }
 
   function saveCanvas(canvas) {
     const nsIFilePicker = Components.interfaces.nsIFilePicker;
-    let filePicker = Components.classes["@mozilla.org/filepicker;1"].
-      createInstance(nsIFilePicker);
-    filePicker.init(window, null, nsIFilePicker.modeSave);
-    filePicker.appendFilters(
-	nsIFilePicker.filterImages | nsIFilePicker.filterAll);
-    filePicker.defaultString = "canvas.png";
+    let fp = Components.classes["@mozilla.org/filepicker;1"].
+             createInstance(nsIFilePicker);
+    let fpCallback = function fpCallback_done(aResult) {
+      if (aResult == nsIFilePicker.returnOK ||
+          aResult == nsIFilePicker.returnReplace) {
+        const nsIWebBrowserPersist =
+          Components.interfaces.nsIWebBrowserPersist;
+        let file = fp.file;
 
-    let response = filePicker.show();
-    if (response == nsIFilePicker.returnOK ||
-	response == nsIFilePicker.returnReplace) {
-      const nsIWebBrowserPersist = Components.interfaces.nsIWebBrowserPersist;
-      let file = filePicker.file;
+        // create a data url from the canvas and then create URIs of the
+        // source and targets
+        let io = Components.classes["@mozilla.org/network/io-service;1"].
+                 getService(Components.interfaces.nsIIOService);
+        let source = io.newURI(canvas.toDataURL("image/png"), "UTF8", null);
+        let target = io.newFileURI(file);
 
-      // create a data url from the canvas and then create URIs of the source
-      // and targets
-      let io = Components.classes["@mozilla.org/network/io-service;1"].
-	getService(Components.interfaces.nsIIOService);
-      let source = io.newURI(canvas.toDataURL("image/png"), "UTF8", null);
-      let target = io.newFileURI(file);
+        // prepare to save the canvas data
+        let persist = Components.classes[
+          "@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].
+          createInstance(nsIWebBrowserPersist);
+        persist.persistFlags = nsIWebBrowserPersist.
+          PERSIST_FLAGS_REPLACE_EXISTING_FILES;
+        persist.persistFlags |= nsIWebBrowserPersist.
+          PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
 
-      // prepare to save the canvas data
-      let persist = Components.classes[
-	"@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].
-	  createInstance(nsIWebBrowserPersist);
-      persist.persistFlags = nsIWebBrowserPersist.
-	PERSIST_FLAGS_REPLACE_EXISTING_FILES;
-      persist.persistFlags |= nsIWebBrowserPersist.
-        PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
+        // displays a download dialog (remove these 3 lines for silent download)
+        let xfer = Components.classes["@mozilla.org/transfer;1"].
+                   createInstance(Components.interfaces.nsITransfer);
+        xfer.init(source, target, "", null, null, null, persist);
+        persist.progressListener = xfer;
 
-      // displays a download dialog (remove these 3 lines for silent download)
-      let xfer = Components.classes["@mozilla.org/transfer;1"].
-	createInstance(Components.interfaces.nsITransfer);
-      xfer.init(source, target, "", null, null, null, persist);
-      persist.progressListener = xfer;
+        // save the canvas data to the file
+        persist.saveURI(source, null, null, null, null, file);
+      }
+    };
 
-      // save the canvas data to the file
-      persist.saveURI(source, null, null, null, null, file);
-    }
+    fp.init(window, null, nsIFilePicker.modeSave);
+    fp.appendFilters(nsIFilePicker.filterImages | nsIFilePicker.filterAll);
+    fp.defaultString = "canvas.png";
+    fp.open(fpCallback);
   }
 
   function exportData() {
     const nsIFilePicker = Components.interfaces.nsIFilePicker;
-    let filePicker = Components.classes["@mozilla.org/filepicker;1"].
-      createInstance(nsIFilePicker);
     let eid = getUrlParam("eid");
     let task = TestPilotSetup.getTaskById(eid);
-
-    filePicker.init(window, null, nsIFilePicker.modeSave);
-    filePicker.appendFilters(
-	nsIFilePicker.filterImages | nsIFilePicker.filterAll);
-    filePicker.defaultString = task.title + ".csv";
+    let fp = Components.classes["@mozilla.org/filepicker;1"].
+             createInstance(nsIFilePicker);
+    let fpCallback = function fpCallback_done(aResult) {
+      if (aResult == nsIFilePicker.returnOK ||
+          aResult == nsIFilePicker.returnReplace) {
+        const nsIWebBrowserPersist =
+          Components.interfaces.nsIWebBrowserPersist;
+        let foStream =
+          Components.classes["@mozilla.org/network/file-output-stream;1"].
+          createInstance(Components.interfaces.nsIFileOutputStream);
+        let converter =
+          Components.classes["@mozilla.org/intl/converter-output-stream;1"].
+          createInstance(Components.interfaces.nsIConverterOutputStream);
+        let file = fp.file;
+        let dataStore = task.dataStore;
+        let columnNames = dataStore.getHumanReadableColumnNames();
+        let propertyNames = dataStore.getPropertyNames();
+        let csvString = "";
 
-    let response = filePicker.show();
-    if (response == nsIFilePicker.returnOK ||
-	response == nsIFilePicker.returnReplace) {
-      const nsIWebBrowserPersist = Components.interfaces.nsIWebBrowserPersist;
-      let foStream =
-        Components.classes["@mozilla.org/network/file-output-stream;1"].
-	  createInstance(Components.interfaces.nsIFileOutputStream);
-      let converter =
-        Components.classes["@mozilla.org/intl/converter-output-stream;1"].
-	  createInstance(Components.interfaces.nsIConverterOutputStream);
-      let file = filePicker.file;
-      let dataStore = task.dataStore;
-      let columnNames = dataStore.getHumanReadableColumnNames();
-      let propertyNames = dataStore.getPropertyNames();
-      let csvString = "";
-
-      // titles
-      for (let i = 0; i < columnNames.length; i++) {
-	csvString += "\"" + columnNames[i] + "\",";
-      }
-      if (csvString.length > 0) {
-	csvString = csvString.substring(0, (csvString.length - 1));
-        csvString += "\n";
-      }
-
-      dataStore.getAllDataAsJSON(true, function(rawData) {
-        // data
-        for (let i = 0; i < rawData.length; i++) {
-          for (let j = 0; j < columnNames.length; j++) {
-	    csvString += "\"" + rawData[i][propertyNames[j]] + "\",";
-          }
-	  csvString = csvString.substring(0, (csvString.length - 1));
+        // titles
+        for (let i = 0; i < columnNames.length; i++) {
+          csvString += "\"" + columnNames[i] + "\",";
+        }
+        if (csvString.length > 0) {
+          csvString = csvString.substring(0, (csvString.length - 1));
           csvString += "\n";
         }
 
-        // write, create, truncate
-        foStream.init(file, 0x02 | 0x08 | 0x20, 0664, 0);
-        converter.init(foStream, "UTF-8", 0, 0);
-        converter.writeString(csvString);
-        converter.close();
-      });
-    }
+        dataStore.getAllDataAsJSON(true, function(rawData) {
+          // data
+          for (let i = 0; i < rawData.length; i++) {
+            for (let j = 0; j < columnNames.length; j++) {
+              csvString += "\"" + rawData[i][propertyNames[j]] + "\",";
+            }
+            csvString = csvString.substring(0, (csvString.length - 1));
+            csvString += "\n";
+          }
+
+          // write, create, truncate
+          foStream.init(file, 0x02 | 0x08 | 0x20, 0664, 0);
+          converter.init(foStream, "UTF-8", 0, 0);
+          converter.writeString(csvString);
+          converter.close();
+        });
+      }
+    };
+
+    fp.init(window, null, nsIFilePicker.modeSave);
+    fp.appendFilters(nsIFilePicker.filterImages | nsIFilePicker.filterAll);
+    fp.defaultString = task.title + ".csv";
+    fp.open(fpCallback);
   }
 
   function openLink(url) {
     // open the link in the chromeless window
     let wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
                        .getService(Components.interfaces.nsIWindowMediator);
     let recentWindow = wm.getMostRecentWindow("navigator:browser");
 
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -186,31 +186,53 @@ let SocialChatBar = {
   update: function() {
     if (!this.canShow)
       this.chatbar.removeAll();
   }
 }
 
 function sizeSocialPanelToContent(iframe) {
   // FIXME: bug 764787: Maybe we can use nsIDOMWindowUtils.getRootBounds() here?
-  // Need to handle dynamic sizing
   let doc = iframe.contentDocument;
   if (!doc) {
     return;
   }
   // "notif" is an implementation detail that we should get rid of
   // eventually
   let body = doc.getElementById("notif") || doc.body;
-  if (!body || !body.firstChild) {
+  if (!body) {
     return;
   }
+  // XXX - do we want a max for width and height here?
+  // The 300 and 330 defaults also seem arbitrary, so should be revisited.
+  // BUT - for at least one provider, the scrollWidth/offsetWidth/css width
+  // isn't set appropriately, so the 330 is "fixed" for now...
+  iframe.style.width = "330px";
+  // offsetHeight doesn't include margins, so account for that.
+  let cs = doc.defaultView.getComputedStyle(body);
+  let computedHeight = parseInt(cs.marginTop) + body.offsetHeight + parseInt(cs.marginBottom);
+  let height = computedHeight || 300;
+  iframe.style.height = height + "px";
+}
 
-  let [height, width] = [body.firstChild.offsetHeight || 300, 330];
-  iframe.style.width = width + "px";
-  iframe.style.height = height + "px";
+function setupDynamicPanelResizer(iframe) {
+  let doc = iframe.contentDocument;
+  let mo = new iframe.contentWindow.MutationObserver(function(mutations) {
+    sizeSocialPanelToContent(iframe);
+  });
+  // Observe anything that causes the size to change.
+  let config = {attributes: true, characterData: true, childList: true, subtree: true};
+  mo.observe(doc, config);
+  doc.addEventListener("unload", function() {
+    if (mo) {
+      mo.disconnect();
+      mo = null;
+    }
+  }, false);
+  sizeSocialPanelToContent(iframe);
 }
 
 let SocialFlyout = {
   get panel() {
     return document.getElementById("social-flyout-panel");
   },
 
   dispatchPanelEvent: function(name) {
@@ -230,16 +252,17 @@ let SocialFlyout = {
     iframe.setAttribute("class", "social-panel-frame");
     iframe.setAttribute("flex", "1");
     iframe.setAttribute("origin", Social.provider.origin);
     panel.appendChild(iframe);
   },
 
   unload: function() {
     let panel = this.panel;
+    panel.hidePopup();
     if (!panel.firstChild)
       return
     panel.removeChild(panel.firstChild);
   },
 
   onShown: function(aEvent) {
     let iframe = this.panel.firstChild;
     iframe.docShell.isActive = true;
@@ -270,17 +293,17 @@ let SocialFlyout = {
       this._createFrame();
     panel.hidden = false;
     let iframe = panel.firstChild;
 
     let src = iframe.getAttribute("src");
     if (src != aURL) {
       iframe.addEventListener("load", function documentLoaded() {
         iframe.removeEventListener("load", documentLoaded, true);
-        sizeSocialPanelToContent(iframe);
+        setupDynamicPanelResizer(iframe);
         if (aCallback) {
           try {
             aCallback(iframe.contentWindow);
           } catch(e) {
             Cu.reportError(e);
           }
         }
       }, true);
@@ -327,17 +350,17 @@ let SocialShareButton = {
   updateProfileInfo: function SSB_updateProfileInfo() {
     let profileRow = document.getElementById("unsharePopupHeader");
     let profile = Social.provider.profile;
     this.promptImages = null;
     this.promptMessages = null;
     if (profile && profile.displayName) {
       profileRow.hidden = false;
       let portrait = document.getElementById("socialUserPortrait");
-      portrait.setAttribute("src", profile.portrait || "chrome://browser/skin/social/social.png");
+      portrait.setAttribute("src", profile.portrait || "chrome://global/skin/icons/information-32.png");
       let displayName = document.getElementById("socialUserDisplayName");
       displayName.setAttribute("label", profile.displayName);
     } else {
       profileRow.hidden = true;
       this.updateButtonHiddenState();
       return;
     }
     // XXX - this shouldn't be done as part of updateProfileInfo, but instead
@@ -535,17 +558,17 @@ var SocialToolbar = {
     }
   },
 
   updateProfile: function SocialToolbar_updateProfile() {
     // Profile may not have been initialized yet, since it depends on a worker
     // response. In that case we'll be called again when it's available, via
     // social:profile-changed
     let profile = Social.provider.profile || {};
-    let userPortrait = profile.portrait || "chrome://browser/skin/social/social.png";
+    let userPortrait = profile.portrait || "chrome://global/skin/icons/information-32.png";
     document.getElementById("social-statusarea-user-portrait").setAttribute("src", userPortrait);
 
     let notLoggedInLabel = document.getElementById("social-statusarea-notloggedin");
     let userNameBtn = document.getElementById("social-statusarea-username");
     if (profile.userName) {
       notLoggedInLabel.hidden = true;
       userNameBtn.hidden = false;
       userNameBtn.label = profile.userName;
@@ -645,23 +668,23 @@ var SocialToolbar = {
     });
 
     panel.addEventListener("popupshown", function onpopupshown() {
       panel.removeEventListener("popupshown", onpopupshown);
       SocialToolbar.button.setAttribute("open", "true");
       notificationFrame.docShell.isActive = true;
       notificationFrame.docShell.isAppTab = true;
       if (notificationFrame.contentDocument.readyState == "complete") {
-        sizeSocialPanelToContent(notificationFrame);
+        setupDynamicPanelResizer(notificationFrame);
         dispatchPanelEvent("socialFrameShow");
       } else {
         // first time load, wait for load and dispatch after load
         notificationFrame.addEventListener("load", function panelBrowserOnload(e) {
           notificationFrame.removeEventListener("load", panelBrowserOnload, true);
-          sizeSocialPanelToContent(notificationFrame);
+          setupDynamicPanelResizer(notificationFrame);
           setTimeout(function() {
             dispatchPanelEvent("socialFrameShow");
           }, 0);
         }, true);
       }
     });
 
     panel.openPopup(iconImage, "bottomcenter topleft", 0, 0, false, false);
--- a/browser/base/content/socialchat.xml
+++ b/browser/base/content/socialchat.xml
@@ -3,17 +3,17 @@
 <bindings id="socialChatBindings"
     xmlns="http://www.mozilla.org/xbl"
     xmlns:xbl="http://www.mozilla.org/xbl"
     xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <binding id="chatbox">
     <content orient="vertical" mousethrough="never">
       <xul:hbox class="chat-titlebar" xbl:inherits="minimized,selected"
-                onclick="document.getBindingParent(this).toggle();">
+                onclick="document.getBindingParent(this).toggle();" align="baseline">
         <xul:image class="chat-status-icon" xbl:inherits="src=image"/>
         <xul:label class="chat-title" flex="1" xbl:inherits="value=label,crop"/>
         <xul:toolbarbutton class="chat-close-button chat-toolbarbutton"
                            oncommand="document.getBindingParent(this).close();"/>
       </xul:hbox>
       <xul:iframe anonid="iframe" class="chat-frame" flex="1"
                   xbl:inherits="src,origin,collapsed=minimized" type="content"/>
     </content>
--- a/browser/base/content/test/browser_social_chatwindow.js
+++ b/browser/base/content/test/browser_social_chatwindow.js
@@ -110,10 +110,29 @@ var tests = {
           ok(!chats.selectedChat, "chats are all closed");
           port.close();
           ensureSocialUrlNotRemembered(chatUrl);
           next();
           break;
       }
     }
     port.postMessage({topic: "test-worker-chat", data: chatUrl});
+  },
+  testCloseOnLogout: function(next) {
+    const chatUrl = "https://example.com/browser/browser/base/content/test/social_chat.html";
+    let port = Social.provider.getWorkerPort();
+    ok(port, "provider has a port");
+    port.postMessage({topic: "test-init"});
+    port.onmessage = function (e) {
+      let topic = e.data.topic;
+      switch (topic) {
+        case "got-chatbox-message":
+          ok(true, "got a chat window opened");
+          port.postMessage({topic: "test-logout"});
+          waitForCondition(function() document.getElementById("pinnedchats").firstChild == null,
+                           next,
+                           "chat windows didn't close");
+          break;
+      }
+    }
+    port.postMessage({topic: "test-worker-chat", data: chatUrl});
   }
 }
--- a/browser/base/content/test/browser_social_flyout.js
+++ b/browser/base/content/test/browser_social_flyout.js
@@ -26,24 +26,54 @@ var tests = {
       let topic = e.data.topic;
       switch (topic) {
         case "got-sidebar-message":
           port.postMessage({topic: "test-flyout-open"});
           break;
         case "got-flyout-visibility":
           if (e.data.result == "hidden") {
             ok(true, "flyout visibility is 'hidden'");
+            is(panel.state, "closed", "panel really is closed");
             port.close();
             next();
           } else if (e.data.result == "shown") {
             ok(true, "flyout visibility is 'shown");
-            panel.hidePopup();
+            port.postMessage({topic: "test-flyout-close"});
           }
           break;
         case "got-flyout-message":
           ok(e.data.result == "ok", "got flyout message");
           break;
       }
     }
     port.postMessage({topic: "test-init"});
+  },
+
+  testResizeFlyout: function(next) {
+    let panel = document.getElementById("social-flyout-panel");
+    let port = Social.provider.getWorkerPort();
+    ok(port, "provider has a port");
+    port.onmessage = function (e) {
+      let topic = e.data.topic;
+      switch (topic) {
+        case "test-init-done":
+          port.postMessage({topic: "test-flyout-open"});
+          break;
+        case "got-flyout-visibility":
+          // The width of the flyout should be 250px
+          let iframe = panel.firstChild;
+          let cs = iframe.contentWindow.getComputedStyle(iframe.contentDocument.body);
+          is(cs.width, "250px", "should be 250px wide");
+          iframe.contentDocument.addEventListener("SocialTest-DoneMakeWider", function _doneHandler() {
+            iframe.contentDocument.removeEventListener("SocialTest-DoneMakeWider", _doneHandler, false);
+            cs = iframe.contentWindow.getComputedStyle(iframe.contentDocument.body);
+            is(cs.width, "500px", "should now be 500px wide");
+            panel.hidePopup();
+            next();
+          }, false);
+          SocialFlyout.dispatchPanelEvent("socialTest-MakeWider");
+          break;
+      }
+    }
+    port.postMessage({topic: "test-init"});
   }
 }
 
--- a/browser/base/content/test/browser_social_toolbar.js
+++ b/browser/base/content/test/browser_social_toolbar.js
@@ -51,18 +51,16 @@ var tests = {
     Social.provider.setAmbientNotification(ambience);
     ok(statusIcons.firstChild.lastChild.collapsed, "status value is not visible");
     is(statusIcons.firstChild.lastChild.textContent, "", "status value is correct");
     next();
   },
   testProfileUnset: function(next) {
     Social.provider.updateUserProfile({});
     // check dom values
-    let portrait = document.getElementById("social-statusarea-user-portrait").getAttribute("src");
-    is(portrait, "chrome://browser/skin/social/social.png", "portrait is generic");
     let userButton = document.getElementById("social-statusarea-username");
     ok(userButton.hidden, "username is not visible");
     let ambience = document.getElementById("social-status-iconbox").firstChild;
     while (ambience) {
       ok(ambience.collapsed, "ambient icon (" + ambience.id + ") is collapsed");
       ambience = ambience.nextSibling;
     }
     
--- a/browser/base/content/test/social_flyout.html
+++ b/browser/base/content/test/social_flyout.html
@@ -9,14 +9,21 @@
       window.addEventListener("socialFrameShow", function(e) {
         var port = navigator.mozSocial.getWorker().port;
         port.postMessage({topic: "flyout-visibility", result: "shown"});
       }, false);
       window.addEventListener("socialFrameHide", function(e) {
         var port = navigator.mozSocial.getWorker().port;
         port.postMessage({topic: "flyout-visibility", result: "hidden"});
       }, false);
+      window.addEventListener("socialTest-MakeWider", function(e) {
+        document.body.setAttribute("style", "width: 500px;");
+        document.body.offsetWidth; // force a layout flush
+        var evt = document.createEvent("CustomEvent");
+        evt.initCustomEvent("SocialTest-DoneMakeWider", true, true, {});
+        document.documentElement.dispatchEvent(evt);
+      }, false);
     </script>
   </head>
-  <body onload="pingWorker();">
+  <body style="max-width: 250px;" onload="pingWorker();">
     <p>This is a test social flyout panel.</p>
   </body>
 </html>
--- a/browser/base/content/test/social_sidebar.html
+++ b/browser/base/content/test/social_sidebar.html
@@ -6,16 +6,19 @@
       function pingWorker() {
         var port = navigator.mozSocial.getWorker().port;
         port.onmessage = function(e) {
           var topic = e.data.topic;
           switch (topic) {
             case "test-flyout-open":
               navigator.mozSocial.openPanel("social_flyout.html");
               break;
+            case "test-flyout-close":
+              navigator.mozSocial.closePanel();
+              break;
             case "test-chatbox-open":
               var url = "social_chat.html";
               var data = e.data.data;
               if (data && data.id) {
                 url = url + "?id="+data.id;
               }
               navigator.mozSocial.openChatWindow(url, function(chatwin) {
                 port.postMessage({topic: "chatbox-opened",
--- a/browser/base/content/test/social_worker.js
+++ b/browser/base/content/test/social_worker.js
@@ -8,16 +8,19 @@ onconnect = function(e) {
   let port = e.ports[0];
   port.onmessage = function onMessage(event) {
     let topic = event.data.topic;
     switch (topic) {
       case "test-init":
         testPort = port;
         port.postMessage({topic: "test-init-done"});
         break;
+      case "test-logout":
+        apiPort.postMessage({topic: "social.user-profile", data: {}});
+        break;
       case "sidebar-message":
         sidebarPort = port;
         if (testPort && event.data.result == "ok")
           testPort.postMessage({topic:"got-sidebar-message"});
         break;
       case "service-window-message":
         testPort.postMessage({topic:"got-service-window-message",
                               location: event.data.location});
@@ -59,16 +62,19 @@ onconnect = function(e) {
         sidebarPort.postMessage({topic:"test-flyout-open"});
         break;
       case "flyout-message":
         testPort.postMessage({topic:"got-flyout-message", result: event.data.result});
         break;
       case "flyout-visibility":
         testPort.postMessage({topic:"got-flyout-visibility", result: event.data.result});
         break;
+      case "test-flyout-close":
+        sidebarPort.postMessage({topic:"test-flyout-close"});
+        break;
       case "test-worker-chat":
         apiPort.postMessage({topic: "social.request-chat", data: event.data.data });
         break;
       case "social.initialize":
         // This is the workerAPI port, respond and set up a notification icon.
         apiPort = port;
         let profile = {
           portrait: "https://example.com/portrait.jpg",
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,4 +1,4 @@
 This is the pdf.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 0.5.22
+Current extension version is: 0.5.184
 
--- a/browser/extensions/pdfjs/components/PdfStreamConverter.js
+++ b/browser/extensions/pdfjs/components/PdfStreamConverter.js
@@ -534,18 +534,18 @@ PdfStreamConverter.prototype = {
     channel.asyncOpen(proxy, aContext);
     if (useFetchByChrome) {
       // We can use resource principal when data is fetched by the chrome
       // e.g. useful for NoScript
       var securityManager = Cc['@mozilla.org/scriptsecuritymanager;1']
                             .getService(Ci.nsIScriptSecurityManager);
       var uri = ioService.newURI(PDF_VIEWER_WEB_PAGE, null, null);
       // FF16 and below had getCodebasePrincipal (bug 774585)
-      var resourcePrincipal = 'getSimpleCodebasePrincipal' in securityManager ?
-                              securityManager.getSimpleCodebasePrincipal(uri) :
+      var resourcePrincipal = 'getNoAppCodebasePrincipal' in securityManager ?
+                              securityManager.getNoAppCodebasePrincipal(uri) :
                               securityManager.getCodebasePrincipal(uri);
       channel.owner = resourcePrincipal;
     }
   },
 
   // nsIRequestObserver::onStopRequest
   onStopRequest: function(aRequest, aContext, aStatusCode) {
     if (!this.dataListener) {
--- a/browser/extensions/pdfjs/content/web/viewer.css
+++ b/browser/extensions/pdfjs/content/web/viewer.css
@@ -116,17 +116,17 @@ html[dir='rtl'] .innerCenter {
   -moz-transition-duration: 200ms;
   -moz-transition-timing-function: ease;
   -ms-transition-duration: 200ms;
   -ms-transition-timing-function: ease;
   -o-transition-duration: 200ms;
   -o-transition-timing-function: ease;
   transition-duration: 200ms;
   transition-timing-function: ease;
-  
+
 }
 html[dir='ltr'] #sidebarContainer {
   -webkit-transition-property: left;
   -moz-transition-property: left;
   -ms-transition-property: left;
   -o-transition-property: left;
   transition-property: left;
   left: -200px;
@@ -624,42 +624,42 @@ html[dir='rtl'] .toolbarButton:first-chi
   -moz-box-flex: 1;
   min-width: 30px;
 }
 
 .toolbarButton#sidebarToggle::before {
   display: inline-block;
   content: url(images/toolbarButton-sidebarToggle.png);
 }
-    
+
 html[dir='ltr'] .toolbarButton.pageUp::before {
   display: inline-block;
   content: url(images/toolbarButton-pageUp.png);
 }
 
 html[dir='rtl'] .toolbarButton.pageUp::before {
   display: inline-block;
   content: url(images/toolbarButton-pageUp-rtl.png);
 }
-    
+
 html[dir='ltr'] .toolbarButton.pageDown::before {
   display: inline-block;
   content: url(images/toolbarButton-pageDown.png);
 }
 
 html[dir='rtl'] .toolbarButton.pageDown::before {
   display: inline-block;
   content: url(images/toolbarButton-pageDown-rtl.png);
 }
 
 .toolbarButton.zoomOut::before {
   display: inline-block;
   content: url(images/toolbarButton-zoomOut.png);
 }
-    
+
 .toolbarButton.zoomIn::before {
   display: inline-block;
   content: url(images/toolbarButton-zoomIn.png);
 }
 
 .toolbarButton.fullscreen::before {
   display: inline-block;
   content: url(images/toolbarButton-fullscreen.png);
@@ -686,22 +686,22 @@ html[dir='rtl'] .toolbarButton.pageDown:
   box-sizing: border-box;
   margin-top: 3px;
   padding-top: 4px;
 }
 
 .toolbarButton.bookmark::before {
   content: url(images/toolbarButton-bookmark.png);
 }
-    
+
 #viewThumbnail.toolbarButton::before {
   display: inline-block;
   content: url(images/toolbarButton-viewThumbnail.png);
 }
-    
+
 #viewOutline.toolbarButton::before {
   display: inline-block;
   content: url(images/toolbarButton-viewOutline.png);
 }
 
 #viewSearch.toolbarButton::before {
   display: inline-block;
   content: url(images/toolbarButton-search.png);
@@ -792,17 +792,17 @@ html[dir='rtl'] .toolbarButton.pageDown:
   z-index: 99;
 }
 
 .thumbnailSelectionRing {
   border-radius: 2px;
   padding: 7px;
   -moz-transition-duration: 150ms;
 }
-    
+
 a:focus > .thumbnail > .thumbnailSelectionRing > .thumbnailImage,
 .thumbnail:hover > .thumbnailSelectionRing > .thumbnailImage {
   opacity: .9;
 }
 
 a:focus > .thumbnail > .thumbnailSelectionRing,
 .thumbnail:hover > .thumbnailSelectionRing {
   background-color: hsla(0,0%,100%,.15);
@@ -1011,17 +1011,17 @@ canvas {
   float: left;
 
   background: #666;
   background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b2b2b2), color-stop(100%,#898989));
   background: -webkit-linear-gradient(top, #b2b2b2 0%,#898989 100%);
   background: -moz-linear-gradient(top, #b2b2b2 0%,#898989 100%);
   background: -ms-linear-gradient(top, #b2b2b2 0%,#898989 100%);
   background: -o-linear-gradient(top, #b2b2b2 0%,#898989 100%);
-  background: linear-gradient(top, #b2b2b2 0%,#898989 100%);    
+  background: linear-gradient(top, #b2b2b2 0%,#898989 100%);
 
   border-top-left-radius: 2px;
   border-bottom-left-radius: 2px;
 
   width: 0%;
   height: 100%;
 }
 
@@ -1061,16 +1061,17 @@ canvas {
   color: #000;
   font-family: sans-serif;
 }
 
 .textLayer > div {
   color: transparent;
   position: absolute;
   line-height:1.3;
+  white-space:pre;
 }
 
 /* TODO: file FF bug to support ::-moz-selection:window-inactive
    so we can override the opaque grey background when the window is inactive;
    see https://bugzilla.mozilla.org/show_bug.cgi?id=706209 */
 ::selection { background:rgba(0,0,255,0.3); }
 ::-moz-selection { background:rgba(0,0,255,0.3); }
 
@@ -1197,17 +1198,17 @@ canvas {
 
 #viewer.textLayer-shadow .textLayer > div {
   background-color: rgba(255,255,255, .6);
   color: black;
 }
 
 @page {
   margin: 0;
-} 
+}
 
 #printContainer {
   display: none;
 }
 
 @media print {
   /* Rules for browsers that don't support mozPrintCallback. */
   #sidebarContainer, .toolbar, #loadingBox, #errorWrapper, .textLayer {
--- a/browser/extensions/pdfjs/content/web/viewer.html
+++ b/browser/extensions/pdfjs/content/web/viewer.html
@@ -46,17 +46,17 @@ limitations under the License.
 
 var PDFJS = {};
 
 (function pdfjsWrapper() {
   // Use strict in our context only - users might not want it
   'use strict';
 
   PDFJS.build =
-'3120edc';
+'e98eba1';
 
 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
 /* Copyright 2012 Mozilla Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
@@ -282,17 +282,17 @@ var Page = (function PageClosure() {
           this.stats.timeEnd('Font Loading');
 
           callback.call(this);
         }.bind(this)
       );
     },
     getLinks: function Page_getLinks() {
       var links = [];
-      var annotations = pageGetAnnotations();
+      var annotations = this.getAnnotations();
       var i, n = annotations.length;
       for (i = 0; i < n; ++i) {
         if (annotations[i].type != 'Link')
           continue;
         links.push(annotations[i]);
       }
       return links;
     },
@@ -630,22 +630,26 @@ var PDFDocument = (function PDFDocumentC
 
   return PDFDocument;
 })();
 
 
 
 // Use only for debugging purposes. This should not be used in any code that is
 // in mozilla master.
-function log(msg) {
-  if (console && console.log)
-    console.log(msg);
-  else if (print)
-    print(msg);
-}
+var log = (function() {
+  if ('console' in globalScope && 'log' in globalScope['console']) {
+    return globalScope['console']['log'].bind(globalScope['console']);
+  } else if ('print' in globalScope) {
+    return globalScope['print'].bind(globalScope);
+  } else {
+    return function nop() {
+    };
+  }
+})();
 
 // A notice for devs that will not trigger the fallback UI.  These are good
 // for things that are helpful to devs, such as warning that Workers were
 // disabled, which is important to devs but not end users.
 function info(msg) {
   if (verbosity >= INFOS) {
     log('Info: ' + msg);
     PDFJS.LogManager.notify('info', msg);
@@ -658,17 +662,26 @@ function warn(msg) {
     log('Warning: ' + msg);
     PDFJS.LogManager.notify('warn', msg);
   }
 }
 
 // Fatal errors that should trigger the fallback UI and halt execution by
 // throwing an exception.
 function error(msg) {
-  log('Error: ' + msg);
+  // If multiple arguments were passed, pass them all to the log function.
+  if (arguments.length > 1) {
+    var logArguments = ['Error:'];
+    logArguments.push.apply(logArguments, arguments);
+    log.apply(null, logArguments);
+    // Join the arguments into a single string for the lines below.
+    msg = [].join.call(arguments, ' ');
+  } else {
+    log('Error: ' + msg);
+  }
   log(backtrace());
   PDFJS.LogManager.notify('error', msg);
   throw new Error(msg);
 }
 
 // Missing features that should trigger the fallback UI.
 function TODO(what) {
   warn('TODO: ' + what);
@@ -1261,16 +1274,25 @@ var StatTimer = (function StatTimerClosu
         out += rpad(span['name'], ' ', longest) + ' ' + duration + 'ms\n';
       }
       return out;
     }
   };
   return StatTimer;
 })();
 
+PDFJS.createBlob = function createBlob(data, contentType) {
+  if (typeof Blob === 'function')
+    return new Blob([data], { type: contentType });
+  // Blob builder is deprecated in FF14 and removed in FF18.
+  var bb = new MozBlobBuilder();
+  bb.append(data);
+  return bb.getBlob(contentType);
+};
+
 
 /**
  * This is the main entry point for loading a PDF and interacting with it.
  * NOTE: If a URL is used to fetch the PDF data a standard XMLHttpRequest(XHR)
  * is used, which means it must follow the same origin rules that any XHR does
  * e.g. No cross domain requests without CORS.
  *
  * @param {string|TypedAray|object} source Can be an url to where a PDF is
@@ -1606,18 +1628,19 @@ var PDFPageProxy = (function PDFPageProx
       stats.time('Rendering');
 
       gfx.beginDrawing(viewport);
 
       var startIdx = 0;
       var length = this.operatorList.fnArray.length;
       var operatorList = this.operatorList;
       var stepper = null;
-      if (PDFJS.pdfBug && StepperManager.enabled) {
-        stepper = StepperManager.create(this.pageNumber - 1);
+      if (PDFJS.pdfBug && 'StepperManager' in globalScope &&
+          globalScope['StepperManager'].enabled) {
+        stepper = globalScope['StepperManager'].create(this.pageNumber - 1);
         stepper.init(operatorList);
         stepper.nextBreakPoint = stepper.getNextBreakPoint();
       }
 
       var continueWrapper;
       if (continueCallback)
         continueWrapper = function() { continueCallback(next); }
       else
@@ -1699,19 +1722,19 @@ var WorkerTransport = (function WorkerTr
       if (typeof workerSrc === 'undefined') {
         error('No PDFJS.workerSrc specified');
       }
 
       try {
         var worker;
         // The firefox extension can't load the worker from the resource://
         // url so we have to inline the script and then use the blob loader.
-        var bb = new MozBlobBuilder();
-        bb.append(document.querySelector('#PDFJS_SCRIPT_TAG').textContent);
-        var blobUrl = window.URL.createObjectURL(bb.getBlob());
+        var script = document.querySelector('#PDFJS_SCRIPT_TAG');
+        var blob = PDFJS.createBlob(script.textContent, script.type);
+        var blobUrl = window.URL.createObjectURL(blob);
         worker = new Worker(blobUrl);
         var messageHandler = new MessageHandler('main', worker);
         this.messageHandler = messageHandler;
 
         messageHandler.on('test', function transportTest(supportTypedArray) {
           if (supportTypedArray) {
             this.worker = worker;
             this.setupMessageHandler(messageHandler);
@@ -1741,16 +1764,17 @@ var WorkerTransport = (function WorkerTr
     destroy: function WorkerTransport_destroy() {
       if (this.worker)
         this.worker.terminate();
 
       this.pageCache = [];
       this.pagePromises = [];
     },
     setupFakeWorker: function WorkerTransport_setupFakeWorker() {
+      warn('Setting up fake worker.');
       // If we don't use a worker, just post/sendMessage to the main thread.
       var fakeWorker = {
         postMessage: function WorkerTransport_postMessage(obj) {
           fakeWorker.onmessage({data: obj});
         },
         terminate: function WorkerTransport_terminate() {}
       };
 
@@ -1814,34 +1838,25 @@ var WorkerTransport = (function WorkerTr
             var imageData = data[2];
             loadJpegStream(id, imageData, this.objs);
             break;
           case 'Image':
             var imageData = data[2];
             this.objs.resolve(id, imageData);
             break;
           case 'Font':
-            var name = data[2];
-            var file = data[3];
-            var properties = data[4];
-
-            if (file) {
-              // Rewrap the ArrayBuffer in a stream.
-              var fontFileDict = new Dict();
-              file = new Stream(file, 0, file.length, fontFileDict);
-            }
+            var exportedData = data[2];
 
             // At this point, only the font object is created but the font is
             // not yet attached to the DOM. This is done in `FontLoader.bind`.
             var font;
-            try {
-              font = new Font(name, file, properties);
-            } catch (e) {
-              font = new ErrorFont(e);
-            }
+            if ('error' in exportedData)
+              font = new ErrorFont(exportedData.error);
+            else
+              font = new Font(exportedData);
             this.objs.resolve(id, font);
             break;
           default:
             error('Got unkown object type ' + type);
         }
       }, this);
 
       messageHandler.on('DocProgress', function transportDocProgress(data) {
@@ -2503,18 +2518,17 @@ var CanvasGraphics = (function CanvasGra
       if (fontObj.coded)
         return; // we don't need ctx.font for Type3 fonts
 
       var name = fontObj.loadedName || 'sans-serif';
       var bold = fontObj.black ? (fontObj.bold ? 'bolder' : 'bold') :
                                  (fontObj.bold ? 'bold' : 'normal');
 
       var italic = fontObj.italic ? 'italic' : 'normal';
-      var serif = fontObj.isSerifFont ? 'serif' : 'sans-serif';
-      var typeface = '"' + name + '", ' + serif;
+      var typeface = '"' + name + '", ' + fontObj.fallbackName;
 
       // Some font backends cannot handle fonts below certain size.
       // Keeping the font at minimal size and using the fontSizeScale to change
       // the current transformation matrix before the fillText/strokeText.
       // See https://bugzilla.mozilla.org/show_bug.cgi?id=726227
       var browserFontSize = size >= MIN_FONT_SIZE ? size : MIN_FONT_SIZE;
       this.current.fontSizeScale = browserFontSize != MIN_FONT_SIZE ? 1.0 :
                                    size / MIN_FONT_SIZE;
@@ -2585,74 +2599,74 @@ var CanvasGraphics = (function CanvasGra
       var fontSizeScale = current.fontSizeScale;
       var charSpacing = current.charSpacing;
       var wordSpacing = current.wordSpacing;
       var textHScale = current.textHScale;
       var fontMatrix = current.fontMatrix || IDENTITY_MATRIX;
       var textHScale2 = textHScale * fontMatrix[0];
       var glyphsLength = glyphs.length;
       var textLayer = this.textLayer;
-      var text = {str: '', length: 0, canvasWidth: 0, geom: {}};
+      var geom;
       var textSelection = textLayer && !skipTextSelection ? true : false;
       var textRenderingMode = current.textRenderingMode;
+      var canvasWidth = 0.0;
 
       // Type3 fonts - each glyph is a "mini-PDF"
       if (font.coded) {
         ctx.save();
         ctx.transform.apply(ctx, current.textMatrix);
         ctx.translate(current.x, current.y);
 
         ctx.scale(textHScale, 1);
 
         if (textSelection) {
           this.save();
           ctx.scale(1, -1);
-          text.geom = this.getTextGeometry();
+          geom = this.getTextGeometry();
           this.restore();
         }
         for (var i = 0; i < glyphsLength; ++i) {
 
           var glyph = glyphs[i];
           if (glyph === null) {
             // word break
             this.ctx.translate(wordSpacing, 0);
+            current.x += wordSpacing * textHScale;
             continue;
           }
 
           this.save();
           ctx.scale(fontSize, fontSize);
           ctx.transform.apply(ctx, fontMatrix);
           this.executeOperatorList(glyph.operatorList);
           this.restore();
 
           var transformed = Util.applyTransform([glyph.width, 0], fontMatrix);
           var width = transformed[0] * fontSize +
               Util.sign(current.fontMatrix[0]) * charSpacing;
 
           ctx.translate(width, 0);
           current.x += width * textHScale;
 
-          text.str += glyph.unicode;
-          text.length++;
-          text.canvasWidth += width;
+          canvasWidth += width;
         }
         ctx.restore();
       } else {
         ctx.save();
         this.applyTextTransforms();
 
         var lineWidth = current.lineWidth;
         var scale = Math.abs(current.textMatrix[0] * fontMatrix[0]);
         if (scale == 0 || lineWidth == 0)
           lineWidth = this.getSinglePixelWidth();
         else
           lineWidth /= scale;
 
         if (textSelection)
-          text.geom = this.getTextGeometry();
+          geom = this.getTextGeometry();
 
         if (fontSizeScale != 1.0) {
           ctx.scale(fontSizeScale, fontSizeScale);
           lineWidth /= fontSizeScale;
         }
 
         ctx.lineWidth = lineWidth;
 
@@ -2689,99 +2703,82 @@ var CanvasGraphics = (function CanvasGra
               case TextRenderingMode.INVISIBLE:
                 break;
             }
           }
 
           x += charWidth;
 
           var glyphUnicode = glyph.unicode === ' ' ? '\u00A0' : glyph.unicode;
-          var glyphUnicodeLength = glyphUnicode.length;
-          //reverse an arabic ligature
-          if (glyphUnicodeLength > 1 &&
-              isRTLRangeFor(glyphUnicode.charCodeAt(0))) {
-            for (var ii = glyphUnicodeLength - 1; ii >= 0; ii--)
-              text.str += glyphUnicode[ii];
-          } else
-            text.str += glyphUnicode;
-          text.length += glyphUnicodeLength;
-          text.canvasWidth += charWidth;
+          if (glyphUnicode in NormalizedUnicodes)
+            glyphUnicode = NormalizedUnicodes[glyphUnicode];
+
+          canvasWidth += charWidth;
         }
         current.x += x * textHScale2;
         ctx.restore();
       }
 
-      if (textSelection)
-        this.textLayer.appendText(text, font.loadedName, fontSize);
-
-      return text;
+      if (textSelection) {
+        geom.canvasWidth = canvasWidth;
+        this.textLayer.appendText(font.fallbackName, fontSize, geom);
+      }
+
+      return canvasWidth;
     },
     showSpacedText: function CanvasGraphics_showSpacedText(arr) {
       var ctx = this.ctx;
       var current = this.current;
       var font = current.font;
       var fontSize = current.fontSize;
       var textHScale = current.textHScale;
       if (!font.coded)
         textHScale *= (current.fontMatrix || IDENTITY_MATRIX)[0];
       var arrLength = arr.length;
       var textLayer = this.textLayer;
-      var text = {str: '', length: 0, canvasWidth: 0, geom: {}};
+      var geom;
+      var canvasWidth = 0.0;
       var textSelection = textLayer ? true : false;
 
       if (textSelection) {
         ctx.save();
         // Type3 fonts - each glyph is a "mini-PDF" (see also showText)
         if (font.coded) {
           ctx.transform.apply(ctx, current.textMatrix);
           ctx.scale(1, -1);
           ctx.translate(current.x, -1 * current.y);
           ctx.scale(textHScale, 1);
         } else
           this.applyTextTransforms();
-        text.geom = this.getTextGeometry();
+        geom = this.getTextGeometry();
         ctx.restore();
       }
 
       for (var i = 0; i < arrLength; ++i) {
         var e = arr[i];
         if (isNum(e)) {
           var spacingLength = -e * 0.001 * fontSize * textHScale;
           current.x += spacingLength;
 
-          if (textSelection) {
-            // Emulate precise spacing via HTML spaces
-            text.canvasWidth += spacingLength;
-            if (e < 0 && text.geom.spaceWidth > 0) { // avoid div by zero
-              var numFakeSpaces = Math.round(-e / text.geom.spaceWidth);
-              if (numFakeSpaces > 0) {
-                text.str += '\u00A0';
-                text.length++;
-              }
-            }
-          }
+          if (textSelection)
+            canvasWidth += spacingLength;
         } else if (isString(e)) {
-          var shownText = this.showText(e, true);
-
-          if (textSelection) {
-            if (shownText.str === ' ') {
-              text.str += '\u00A0';
-            } else {
-              text.str += shownText.str;
-            }
-            text.canvasWidth += shownText.canvasWidth;
-            text.length += shownText.length;
-          }
+          var shownCanvasWidth = this.showText(e, true);
+
+          if (textSelection)
+            canvasWidth += shownCanvasWidth;
         } else {
           error('TJ array element ' + e + ' is not string or num');
         }
       }
 
-      if (textSelection)
-        this.textLayer.appendText(text, font.loadedName, fontSize);
+      if (textSelection) {
+        geom.canvasWidth = canvasWidth;
+        this.textLayer.appendText(font.fallbackName, fontSize, geom);
+      }
     },
     nextLineShowText: function CanvasGraphics_nextLineShowText(text) {
       this.nextLine();
       this.showText(text);
     },
     nextLineSetSpacingShowText:
       function CanvasGraphics_nextLineSetSpacingShowText(wordSpacing,
                                                          charSpacing,
@@ -4200,17 +4197,17 @@ var PDFFunction = (function PDFFunctionC
         var decode = IR[4];
         var samples = IR[5];
         var size = IR[6];
         var n = IR[7];
         var mask = IR[8];
         var range = IR[9];
 
         if (m != args.length)
-          error('Incorrect number of arguments: ' + inputSize + ' != ' +
+          error('Incorrect number of arguments: ' + m + ' != ' +
                 args.length);
 
         var x = args;
 
         // Building the cube vertices: its part and sample index
         // http://rjwagner49.com/Mathematics/Interpolation.pdf
         var cubeVertices = 1 << m;
         var cubeN = new Float64Array(cubeVertices);
@@ -12133,17 +12130,17 @@ var ColorSpace = (function ColorSpaceClo
    * @param {Array} decode Decode map (usually from an image).
    * @param {Number} n Number of components the color space has.
    */
   ColorSpace.isDefaultDecode = function ColorSpace_isDefaultDecode(decode, n) {
     if (!decode)
       return true;
 
     if (n * 2 !== decode.length) {
-      warning('The decode map is not the correct length');
+      warn('The decode map is not the correct length');
       return true;
     }
     for (var i = 0, ii = decode.length; i < ii; i += 2) {
       if (decode[i] != 0 || decode[i + 1] != 1)
         return false;
     }
     return true;
   };
@@ -13149,16 +13146,17 @@ var PartialEvaluator = (function Partial
   function PartialEvaluator(xref, handler, uniquePrefix) {
     this.state = new EvalState();
     this.stateStack = [];
 
     this.xref = xref;
     this.handler = handler;
     this.uniquePrefix = uniquePrefix;
     this.objIdCounter = 0;
+    this.fontIdCounter = 0;
   }
 
   var OP_MAP = {
     // Graphics state
     w: 'setLineWidth',
     J: 'setLineCap',
     j: 'setLineJoin',
     M: 'setMiterLimit',
@@ -13261,16 +13259,60 @@ var PartialEvaluator = (function Partial
     fals: null,
     'false': null,
     nu: null,
     nul: null,
     'null': null
   };
 
   PartialEvaluator.prototype = {
+    loadFont: function PartialEvaluator_loadFont(fontName, font, xref,
+                                                 resources, dependency) {
+      var fontRes = resources.get('Font');
+
+      assert(fontRes, 'fontRes not available');
+
+      font = xref.fetchIfRef(font) || fontRes.get(fontName);
+      assertWellFormed(isDict(font));
+
+      ++this.fontIdCounter;
+      var loadedName = font.loadedName;
+      if (!loadedName) {
+        // keep track of each font we translated so the caller can
+        // load them asynchronously before calling display on a page
+        loadedName = 'font_' + this.uniquePrefix + this.fontIdCounter;
+        font.loadedName = loadedName;
+
+        var translated;
+        try {
+          translated = this.translateFont(font, xref, resources,
+                                          dependency);
+        } catch (e) {
+          translated = { error: e };
+        }
+        font.translated = translated;
+
+        var data = translated;
+        if (data.loadCharProcs) {
+          delete data.loadCharProcs;
+
+          var charProcs = font.get('CharProcs').getAll();
+          var fontResources = font.get('Resources') || resources;
+          var charProcOperatorList = {};
+          for (var key in charProcs) {
+            var glyphStream = charProcs[key];
+            charProcOperatorList[key] =
+              this.getOperatorList(glyphStream, fontResources, dependency);
+          }
+          data.charProcOperatorList = charProcOperatorList;
+        }
+      }
+      return font;
+    },
+
     getOperatorList: function PartialEvaluator_getOperatorList(stream,
                                                                resources,
                                                                dependency,
                                                                queue) {
 
       var self = this;
       var xref = this.xref;
       var handler = this.handler;
@@ -13283,56 +13325,32 @@ var PartialEvaluator = (function Partial
           var dep = depList[i];
           if (dependency.indexOf(dep) == -1) {
             dependency.push(depList[i]);
           }
         }
       }
 
       function handleSetFont(fontName, font) {
-        var loadedName = null;
-
-        var fontRes = resources.get('Font');
-
-        assert(fontRes, 'fontRes not available');
-
-        font = xref.fetchIfRef(font) || fontRes.get(fontName);
-        assertWellFormed(isDict(font));
-
-        ++self.objIdCounter;
-        if (!font.loadedName) {
-          font.translated = self.translateFont(font, xref, resources,
-                                               dependency);
-          if (font.translated) {
-            // keep track of each font we translated so the caller can
-            // load them asynchronously before calling display on a page
-            loadedName = 'font_' + uniquePrefix + self.objIdCounter;
-            font.translated.properties.loadedName = loadedName;
-            font.loadedName = loadedName;
-
-            var translated = font.translated;
-            // Convert the file to an ArrayBuffer which will be turned back into
-            // a Stream in the main thread.
-            if (translated.file)
-              translated.file = translated.file.getBytes();
-            if (translated.properties.file) {
-              translated.properties.file =
-                  translated.properties.file.getBytes();
-            }
-
-            handler.send('obj', [
-                loadedName,
-                'Font',
-                translated.name,
-                translated.file,
-                translated.properties
-            ]);
-          }
-        }
-        loadedName = loadedName || font.loadedName;
+        font = self.loadFont(fontName, font, xref, resources, dependency);
+
+        var loadedName = font.loadedName;
+        if (!font.sent) {
+          var data = font.translated;
+
+          if (data instanceof Font)
+            data = data.exportData();
+
+          handler.send('obj', [
+              loadedName,
+              'Font',
+              data
+          ]);
+          font.sent = true;
+        }
 
         // Ensure the font is ready before the font is set
         // and later on used for drawing.
         // OPTIMIZE: This should get insert to the operatorList only once per
         // page.
         insertDependency([loadedName]);
         return loadedName;
       }
@@ -13609,89 +13627,145 @@ var PartialEvaluator = (function Partial
           assertWellFormed(args.length <= 33, 'Too many arguments');
           args.push(obj instanceof Dict ? obj.getAll() : obj);
         }
       }
 
       return queue;
     },
 
-    getTextContent: function partialEvaluatorGetIRQueue(stream, resources) {
+    getTextContent: function partialEvaluatorGetIRQueue(
+                                                    stream, resources, state) {
+      var bidiTexts;
+
+      if (!state) {
+        bidiTexts = [];
+        state = {
+          bidiTexts: bidiTexts
+        };
+      } else {
+        bidiTexts = state.bidiTexts;
+      }
 
       var self = this;
       var xref = this.xref;
 
       function handleSetFont(fontName, fontRef) {
-        var fontRes = resources.get('Font');
-
-        // TODO: TOASK: Is it possible to get here? If so, what does
-        // args[0].name should be like???
-        assert(fontRes, 'fontRes not available');
-
-        fontRes = xref.fetchIfRef(fontRes);
-        fontRef = fontRef || fontRes.get(fontName);
-        var font = xref.fetchIfRef(fontRef), tra;
-        assertWellFormed(isDict(font));
-        if (!font.translated) {
-          font.translated = self.translateFont(font, xref, resources);
-        }
-        return font;
+        return self.loadFont(fontName, fontRef, xref, resources, null);
       }
 
       resources = xref.fetchIfRef(resources) || new Dict();
+      // The xobj is parsed iff it's needed, e.g. if there is a `DO` cmd.
+      var xobjs = null;
 
       var parser = new Parser(new Lexer(stream), false);
       var res = resources;
       var args = [], obj;
 
-      var text = '';
       var chunk = '';
       var font = null;
       while (!isEOF(obj = parser.getObj())) {
         if (isCmd(obj)) {
           var cmd = obj.cmd;
           switch (cmd) {
+            // TODO: Add support for SAVE/RESTORE and XFORM here.
             case 'Tf':
-              font = handleSetFont(args[0].name);
+              font = handleSetFont(args[0].name).translated;
               break;
             case 'TJ':
               var items = args[0];
               for (var j = 0, jj = items.length; j < jj; j++) {
                 if (typeof items[j] === 'string') {
-                  chunk += items[j];
-                } else if (items[j] < 0) {
-                  // making all negative offsets a space - better to have
-                  // a space in incorrect place than not have them at all
-                  chunk += ' ';
+                  chunk += fontCharsToUnicode(items[j], font);
+                } else if (items[j] < 0 && font.spaceWidth > 0) {
+                  var numFakeSpaces = Math.round(-items[j] / font.spaceWidth);
+                  if (numFakeSpaces > 0) {
+                    chunk += ' ';
+                  }
                 }
               }
               break;
             case 'Tj':
-              chunk += args[0];
+              chunk += fontCharsToUnicode(args[0], font);
               break;
             case "'":
-              chunk += args[0] + ' ';
+              // For search, adding a extra white space for line breaks would be
+              // better here, but that causes too much spaces in the
+              // text-selection divs.
+              chunk += fontCharsToUnicode(args[0], font);
               break;
             case '"':
-              chunk += args[2] + ' ';
+              // Note comment in "'"
+              chunk += fontCharsToUnicode(args[2], font);
+              break;
+            case 'Do':
+              // Set the chunk such that the following if won't add something
+              // to the state.
+              chunk = '';
+
+              if (args[0].code) {
+                break;
+              }
+
+              if (!xobjs) {
+                xobjs = resources.get('XObject') || new Dict();
+              }
+
+              var name = args[0].name;
+              var xobj = xobjs.get(name);
+              if (!xobj)
+                break;
+              assertWellFormed(isStream(xobj), 'XObject should be a stream');
+
+              var type = xobj.dict.get('Subtype');
+              assertWellFormed(
+                isName(type),
+                'XObject should have a Name subtype'
+              );
+
+              if ('Form' !== type.name)
+                break;
+
+              state = this.getTextContent(
+                xobj,
+                xobj.dict.get('Resources') || resources,
+                state
+              );
+              break;
+            case 'gs':
+              var dictName = args[0];
+              var extGState = resources.get('ExtGState');
+
+              if (!isDict(extGState) || !extGState.has(dictName.name))
+                break;
+
+              var gsState = extGState.get(dictName.name);
+
+              for (var i = 0; i < gsState.length; i++) {
+                if (gsState[i] === 'Font') {
+                  font = handleSetFont(args[0].name).translated;
+                }
+              }
               break;
           } // switch
+
           if (chunk !== '') {
-            text += fontCharsToUnicode(chunk, font.translated.properties);
+            bidiTexts.push(PDFJS.bidi(chunk, -1));
+
             chunk = '';
           }
 
           args = [];
         } else if (obj != null) {
           assertWellFormed(args.length <= 33, 'Too many arguments');
           args.push(obj);
         }
-      }
-
-      return text;
+      } // while
+
+      return state;
     },
 
     extractDataStructures: function
       partialEvaluatorExtractDataStructures(dict, baseDict,
                                             xref, properties) {
       // 9.10.2
       var toUnicode = dict.get('ToUnicode') ||
         baseDict.get('ToUnicode');
@@ -13942,31 +14016,51 @@ var PartialEvaluator = (function Partial
             var metrics = this.getBaseFontMetrics(baseFontName.name);
 
             glyphsWidths = metrics.widths;
             defaultWidth = metrics.defaultWidth;
           }
         }
       }
 
+      // Heuristic: detection of monospace font by checking all non-zero widths
+      var isMonospace = true, firstWidth = defaultWidth;
+      for (var glyph in glyphsWidths) {
+        var glyphWidth = glyphsWidths[glyph];
+        if (!glyphWidth)
+          continue;
+        if (!firstWidth) {
+          firstWidth = glyphWidth;
+          continue;
+        }
+        if (firstWidth != glyphWidth) {
+          isMonospace = false;
+          break;
+        }
+      }
+      if (isMonospace)
+        properties.flags |= FontFlags.FixedPitch;
+
       properties.defaultWidth = defaultWidth;
       properties.widths = glyphsWidths;
     },
 
     getBaseFontMetrics: function PartialEvaluator_getBaseFontMetrics(name) {
-      var defaultWidth = 0, widths = [];
+      var defaultWidth = 0, widths = [], monospace = false;
       var glyphWidths = Metrics[stdFontMap[name] || name];
       if (isNum(glyphWidths)) {
         defaultWidth = glyphWidths;
+        monospace = true;
       } else {
         widths = glyphWidths;
       }
 
       return {
         defaultWidth: defaultWidth,
+        monospace: monospace,
         widths: widths
       };
     },
 
     translateFont: function PartialEvaluator_translateFont(dict,
                                                            xref,
                                                            resources,
                                                            dependency) {
@@ -13977,17 +14071,17 @@ var PartialEvaluator = (function Partial
       var composite = false;
       if (type.name == 'Type0') {
         // If font is a composite
         //  - get the descendant font
         //  - set the type according to the descendant font
         //  - get the FontDescriptor from the descendant font
         var df = dict.get('DescendantFonts');
         if (!df)
-          return null;
+          error('Descendant fonts are not specified');
 
         dict = isArray(df) ? xref.fetchIfRef(df[0]) : df;
 
         type = dict.get('Subtype');
         assertWellFormed(isName(type), 'invalid font Subtype');
         composite = true;
       }
       var maxCharIndex = composite ? 0xFFFF : 0xFF;
@@ -14000,44 +14094,41 @@ var PartialEvaluator = (function Partial
           descriptor = new Dict();
           descriptor.set('FontName', new Name(type.name));
         } else {
           // Before PDF 1.5 if the font was one of the base 14 fonts, having a
           // FontDescriptor was not required.
           // This case is here for compatibility.
           var baseFontName = dict.get('BaseFont');
           if (!isName(baseFontName))
-            return null;
+            error('Base font is not specified');
 
           // Using base font name as a font name.
           baseFontName = baseFontName.name.replace(/[,_]/g, '-');
           var metrics = this.getBaseFontMetrics(baseFontName);
 
           // Simulating descriptor flags attribute
           var fontNameWoStyle = baseFontName.split('-')[0];
           var flags = (serifFonts[fontNameWoStyle] ||
             (fontNameWoStyle.search(/serif/gi) != -1) ? FontFlags.Serif : 0) |
+            (metrics.monospace ? FontFlags.FixedPitch : 0) |
             (symbolsFonts[fontNameWoStyle] ? FontFlags.Symbolic :
             FontFlags.Nonsymbolic);
 
           var properties = {
             type: type.name,
             widths: metrics.widths,
             defaultWidth: metrics.defaultWidth,
             flags: flags,
             firstChar: 0,
             lastChar: maxCharIndex
           };
           this.extractDataStructures(dict, dict, xref, properties);
 
-          return {
-            name: baseFontName,
-            dict: baseDict,
-            properties: properties
-          };
+          return new Font(baseFontName, null, properties);
         }
       }
 
       // According to the spec if 'FontDescriptor' is declared, 'FirstChar',
       // 'LastChar' and 'Widths' should exist too, but some PDF encoders seem
       // to ignore this rule when a variant of a standart font is used.
       // TODO Fill the width array depending on which of the base font this is
       // a variant.
@@ -14063,16 +14154,17 @@ var PartialEvaluator = (function Partial
       }
 
       var properties = {
         type: type.name,
         subtype: subtype,
         file: fontFile,
         length1: length1,
         length2: length2,
+        loadedName: baseDict.loadedName,
         composite: composite,
         wideChars: composite,
         fixedPitch: false,
         fontMatrix: dict.get('FontMatrix') || IDENTITY_MATRIX,
         firstChar: firstChar || 0,
         lastChar: lastChar || maxCharIndex,
         bbox: descriptor.get('FontBBox'),
         ascent: descriptor.get('Ascent'),
@@ -14083,32 +14175,19 @@ var PartialEvaluator = (function Partial
         italicAngle: descriptor.get('ItalicAngle'),
         coded: false
       };
       this.extractWidths(dict, xref, descriptor, properties);
       this.extractDataStructures(dict, baseDict, xref, properties);
 
       if (type.name === 'Type3') {
         properties.coded = true;
-        var charProcs = dict.get('CharProcs').getAll();
-        var fontResources = dict.get('Resources') || resources;
-        properties.charProcOperatorList = {};
-        for (var key in charProcs) {
-          var glyphStream = charProcs[key];
-          properties.charProcOperatorList[key] =
-            this.getOperatorList(glyphStream, fontResources, dependency);
-        }
-      }
-
-      return {
-        name: fontName.name,
-        dict: baseDict,
-        file: fontFile,
-        properties: properties
-      };
+      }
+
+      return new Font(fontName.name, fontFile, properties);
     }
   };
 
   return PartialEvaluator;
 })();
 
 var EvalState = (function EvalStateClosure() {
   function EvalState() {
@@ -14531,192 +14610,29 @@ function mapPrivateUseChars(code) {
     case 0xF6D9: // copyrightserif
       return 0x00A9; // copyright
     default:
       return code;
   }
 }
 
 var FontLoader = {
-  loadingContext: {
-    requests: [],
-    nextRequestId: 0
-  },
-
   bind: function fontLoaderBind(fonts, callback) {
     assert(!isWorker, 'bind() shall be called from main thread');
-
-    var rules = [], fontsToLoad = [];
+  
     for (var i = 0, ii = fonts.length; i < ii; i++) {
       var font = fonts[i];
-
-      // Add the font to the DOM only once or skip if the font
-      // is already loaded.
-      if (font.attached || font.loading == false) {
+      if (font.attached)
         continue;
-      }
+  
       font.attached = true;
-
-      var str = '';
-      var data = font.data;
-      if (data) {
-        var length = data.length;
-        for (var j = 0; j < length; j++)
-          str += String.fromCharCode(data[j]);
-
-        var rule = font.bindDOM(str);
-        if (rule) {
-          rules.push(rule);
-          fontsToLoad.push(font);
-        }
-      }
-    }
-
-    var request = FontLoader.queueLoadingCallback(callback);
-    if (rules.length > 0) {
-      FontLoader.prepareFontLoadEvent(rules, fontsToLoad, request);
-    } else {
-      request.complete();
-    }
-  },
-
-  queueLoadingCallback: function FontLoader_queueLoadingCallback(callback) {
-    function LoadLoader_completeRequest() {
-      assert(!request.end, 'completeRequest() cannot be called twice');
-      request.end = Date.now();
-
-      // sending all completed requests in order how they were queued
-      while (context.requests.length > 0 && context.requests[0].end) {
-        var otherRequest = context.requests.shift();
-        setTimeout(otherRequest.callback, 0);
-      }
-    }
-
-    var context = FontLoader.loadingContext;
-    var requestId = 'pdfjs-font-loading-' + (context.nextRequestId++);
-    var request = {
-      id: requestId,
-      complete: LoadLoader_completeRequest,
-      callback: callback,
-      started: Date.now()
-    };
-    context.requests.push(request);
-    return request;
-  },
-
-  // Set things up so that at least one pdfjsFontLoad event is
-  // dispatched when all the @font-face |rules| for |fonts| have been
-  // loaded in a subdocument.  It's expected that the load of |rules|
-  // has already started in this (outer) document, so that they should
-  // be ordered before the load in the subdocument.
-  prepareFontLoadEvent: function fontLoaderPrepareFontLoadEvent(rules,
-                                                                fonts,
-                                                                request) {
-      /** Hack begin */
-      // There's no event when a font has finished downloading so the
-      // following code is a dirty hack to 'guess' when a font is
-      // ready.  This code will be obsoleted by Mozilla bug 471915.
-      //
-      // The only reliable way to know if a font is loaded in Gecko
-      // (at the moment) is document.onload in a document with
-      // a @font-face rule defined in a "static" stylesheet.  We use a
-      // subdocument in an <iframe>, set up properly, to know when
-      // our @font-face rule was loaded.  However, the subdocument and
-      // outer document can't share CSS rules, so the inner document
-      // is only part of the puzzle.  The second piece is an invisible
-      // div created in order to force loading of the @font-face in
-      // the *outer* document.  (The font still needs to be loaded for
-      // its metrics, for reflow).  We create the div first for the
-      // outer document, then create the iframe.  Unless something
-      // goes really wonkily, we expect the @font-face for the outer
-      // document to be processed before the inner.  That's still
-      // fragile, but seems to work in practice.
-      //
-      // The postMessage() hackery was added to work around chrome bug
-      // 82402.
-
-      var requestId = request.id;
-      // Validate the requestId parameter -- the value used to construct HTML.
-      if (!/^[\w\-]+$/.test(requestId)) {
-        error('Invalid request id: ' + requestId);
-
-        // Normally the error-function throws. But if a malicious code
-        // intercepts the function call then the return is needed.
-        return;
-      }
-
-      var names = [];
-      for (var i = 0, ii = fonts.length; i < ii; i++)
-        names.push(fonts[i].loadedName);
-
-      // Validate the names parameter -- the values can used to construct HTML.
-      if (!/^\w+$/.test(names.join(''))) {
-        error('Invalid font name(s): ' + names.join());
-
-        // Normally the error-function throws. But if a malicious code
-        // intercepts the function call then the return is needed.
-        return;
-      }
-
-      var div = document.createElement('div');
-      div.setAttribute('style',
-                       'visibility: hidden;' +
-                       'width: 10px; height: 10px;' +
-                       'position: absolute; top: 0px; left: 0px;');
-      var html = '';
-      for (var i = 0, ii = names.length; i < ii; ++i) {
-        html += '<span style="font-family:' + names[i] + '">Hi</span>';
-      }
-      div.innerHTML = html;
-      document.body.appendChild(div);
-
-      window.addEventListener(
-        'message',
-        function fontLoaderMessage(e) {
-          if (e.data !== requestId)
-            return;
-          for (var i = 0, ii = fonts.length; i < ii; ++i) {
-            var font = fonts[i];
-            font.loading = false;
-          }
-          request.complete();
-          // cleanup
-          document.body.removeChild(frame);
-          window.removeEventListener('message', fontLoaderMessage, false);
-        },
-        false);
-
-      // XXX we should have a time-out here too, and maybe fire
-      // pdfjsFontLoadFailed?
-      var src = '<!DOCTYPE HTML><html><head><meta charset="utf-8">';
-      src += '<style type="text/css">';
-      for (var i = 0, ii = rules.length; i < ii; ++i) {
-        src += rules[i];
-      }
-      src += '</style>';
-      src += '<script type="application/javascript">';
-      src += '  window.onload = function fontLoaderOnload() {\n';
-      src += '    parent.postMessage("' + requestId + '", "*");\n';
-      // Chrome stuck on loading (see chrome issue 145227) - resetting url
-      src += '    window.location = "about:blank";\n';
-      src += '  }';
-      // Hack so the end script tag isn't counted if this is inline JS.
-      src += '</scr' + 'ipt></head><body>';
-      for (var i = 0, ii = names.length; i < ii; ++i) {
-        src += '<p style="font-family:\'' + names[i] + '\'">Hi</p>';
-      }
-      src += '</body></html>';
-      var frame = document.createElement('iframe');
-      frame.src = 'data:text/html,' + src;
-      frame.setAttribute('style',
-                         'visibility: hidden;' +
-                         'width: 10px; height: 10px;' +
-                         'position: absolute; top: 0px; left: 0px;');
-      document.body.appendChild(frame);
-      /** Hack end */
+      font.bindDOM()
+    }
+  
+    setTimeout(callback);
   }
 };
 
 var UnicodeRanges = [
   { 'begin': 0x0000, 'end': 0x007F }, // Basic Latin
   { 'begin': 0x0080, 'end': 0x00FF }, // Latin-1 Supplement
   { 'begin': 0x0100, 'end': 0x017F }, // Latin Extended-A
   { 'begin': 0x0180, 'end': 0x024F }, // Latin Extended-B
@@ -15568,108 +15484,794 @@ var NormalizedUnicodes = {
   '\uFB05': '\u017F\u0074',
   '\uFB06': '\u0073\u0074',
   '\uFB13': '\u0574\u0576',
   '\uFB14': '\u0574\u0565',
   '\uFB15': '\u0574\u056B',
   '\uFB16': '\u057E\u0576',
   '\uFB17': '\u0574\u056D',
   '\uFB4F': '\u05D0\u05DC',
+  '\uFB50': '\u0671',
+  '\uFB51': '\u0671',
+  '\uFB52': '\u067B',
+  '\uFB53': '\u067B',
+  '\uFB54': '\u067B',
+  '\uFB55': '\u067B',
+  '\uFB56': '\u067E',
+  '\uFB57': '\u067E',
+  '\uFB58': '\u067E',
+  '\uFB59': '\u067E',
+  '\uFB5A': '\u0680',
+  '\uFB5B': '\u0680',
+  '\uFB5C': '\u0680',
+  '\uFB5D': '\u0680',
+  '\uFB5E': '\u067A',
+  '\uFB5F': '\u067A',
+  '\uFB60': '\u067A',
+  '\uFB61': '\u067A',
+  '\uFB62': '\u067F',
+  '\uFB63': '\u067F',
+  '\uFB64': '\u067F',
+  '\uFB65': '\u067F',
+  '\uFB66': '\u0679',
+  '\uFB67': '\u0679',
+  '\uFB68': '\u0679',
+  '\uFB69': '\u0679',
+  '\uFB6A': '\u06A4',
+  '\uFB6B': '\u06A4',
+  '\uFB6C': '\u06A4',
+  '\uFB6D': '\u06A4',
+  '\uFB6E': '\u06A6',
+  '\uFB6F': '\u06A6',
+  '\uFB70': '\u06A6',
+  '\uFB71': '\u06A6',
+  '\uFB72': '\u0684',
+  '\uFB73': '\u0684',
+  '\uFB74': '\u0684',
+  '\uFB75': '\u0684',
+  '\uFB76': '\u0683',
+  '\uFB77': '\u0683',
+  '\uFB78': '\u0683',
+  '\uFB79': '\u0683',
+  '\uFB7A': '\u0686',
+  '\uFB7B': '\u0686',
+  '\uFB7C': '\u0686',
+  '\uFB7D': '\u0686',
+  '\uFB7E': '\u0687',
+  '\uFB7F': '\u0687',
+  '\uFB80': '\u0687',
+  '\uFB81': '\u0687',
+  '\uFB82': '\u068D',
+  '\uFB83': '\u068D',
+  '\uFB84': '\u068C',
+  '\uFB85': '\u068C',
+  '\uFB86': '\u068E',
+  '\uFB87': '\u068E',
+  '\uFB88': '\u0688',
+  '\uFB89': '\u0688',
+  '\uFB8A': '\u0698',
+  '\uFB8B': '\u0698',
+  '\uFB8C': '\u0691',
+  '\uFB8D': '\u0691',
+  '\uFB8E': '\u06A9',
+  '\uFB8F': '\u06A9',
+  '\uFB90': '\u06A9',
+  '\uFB91': '\u06A9',
+  '\uFB92': '\u06AF',
+  '\uFB93': '\u06AF',
+  '\uFB94': '\u06AF',
+  '\uFB95': '\u06AF',
+  '\uFB96': '\u06B3',
+  '\uFB97': '\u06B3',
+  '\uFB98': '\u06B3',
+  '\uFB99': '\u06B3',
+  '\uFB9A': '\u06B1',
+  '\uFB9B': '\u06B1',
+  '\uFB9C': '\u06B1',
+  '\uFB9D': '\u06B1',
+  '\uFB9E': '\u06BA',
+  '\uFB9F': '\u06BA',
+  '\uFBA0': '\u06BB',
+  '\uFBA1': '\u06BB',
+  '\uFBA2': '\u06BB',
+  '\uFBA3': '\u06BB',
+  '\uFBA4': '\u06C0',
+  '\uFBA5': '\u06C0',
+  '\uFBA6': '\u06C1',
+  '\uFBA7': '\u06C1',
+  '\uFBA8': '\u06C1',
+  '\uFBA9': '\u06C1',
+  '\uFBAA': '\u06BE',
+  '\uFBAB': '\u06BE',
+  '\uFBAC': '\u06BE',
+  '\uFBAD': '\u06BE',
+  '\uFBAE': '\u06D2',
+  '\uFBAF': '\u06D2',
+  '\uFBB0': '\u06D3',
+  '\uFBB1': '\u06D3',
+  '\uFBD3': '\u06AD',
+  '\uFBD4': '\u06AD',
+  '\uFBD5': '\u06AD',
+  '\uFBD6': '\u06AD',
+  '\uFBD7': '\u06C7',
+  '\uFBD8': '\u06C7',
+  '\uFBD9': '\u06C6',
+  '\uFBDA': '\u06C6',
+  '\uFBDB': '\u06C8',
+  '\uFBDC': '\u06C8',
+  '\uFBDD': '\u0677',
+  '\uFBDE': '\u06CB',
+  '\uFBDF': '\u06CB',
+  '\uFBE0': '\u06C5',
+  '\uFBE1': '\u06C5',
+  '\uFBE2': '\u06C9',
+  '\uFBE3': '\u06C9',
+  '\uFBE4': '\u06D0',
+  '\uFBE5': '\u06D0',
+  '\uFBE6': '\u06D0',
+  '\uFBE7': '\u06D0',
+  '\uFBE8': '\u0649',
+  '\uFBE9': '\u0649',
+  '\uFBEA': '\u0626\u0627',
+  '\uFBEB': '\u0626\u0627',
+  '\uFBEC': '\u0626\u06D5',
+  '\uFBED': '\u0626\u06D5',
+  '\uFBEE': '\u0626\u0648',
+  '\uFBEF': '\u0626\u0648',
+  '\uFBF0': '\u0626\u06C7',
+  '\uFBF1': '\u0626\u06C7',
+  '\uFBF2': '\u0626\u06C6',
+  '\uFBF3': '\u0626\u06C6',
+  '\uFBF4': '\u0626\u06C8',
+  '\uFBF5': '\u0626\u06C8',
+  '\uFBF6': '\u0626\u06D0',
+  '\uFBF7': '\u0626\u06D0',
+  '\uFBF8': '\u0626\u06D0',
+  '\uFBF9': '\u0626\u0649',
+  '\uFBFA': '\u0626\u0649',
+  '\uFBFB': '\u0626\u0649',
+  '\uFBFC': '\u06CC',
+  '\uFBFD': '\u06CC',
+  '\uFBFE': '\u06CC',
+  '\uFBFF': '\u06CC',
+  '\uFC00': '\u0626\u062C',
+  '\uFC01': '\u0626\u062D',
+  '\uFC02': '\u0626\u0645',
+  '\uFC03': '\u0626\u0649',
+  '\uFC04': '\u0626\u064A',
+  '\uFC05': '\u0628\u062C',
+  '\uFC06': '\u0628\u062D',
+  '\uFC07': '\u0628\u062E',
+  '\uFC08': '\u0628\u0645',
+  '\uFC09': '\u0628\u0649',
+  '\uFC0A': '\u0628\u064A',
+  '\uFC0B': '\u062A\u062C',
+  '\uFC0C': '\u062A\u062D',
+  '\uFC0D': '\u062A\u062E',
+  '\uFC0E': '\u062A\u0645',
+  '\uFC0F': '\u062A\u0649',
+  '\uFC10': '\u062A\u064A',
+  '\uFC11': '\u062B\u062C',
+  '\uFC12': '\u062B\u0645',
+  '\uFC13': '\u062B\u0649',
+  '\uFC14': '\u062B\u064A',
+  '\uFC15': '\u062C\u062D',
+  '\uFC16': '\u062C\u0645',
+  '\uFC17': '\u062D\u062C',
+  '\uFC18': '\u062D\u0645',
+  '\uFC19': '\u062E\u062C',
+  '\uFC1A': '\u062E\u062D',
+  '\uFC1B': '\u062E\u0645',
+  '\uFC1C': '\u0633\u062C',
+  '\uFC1D': '\u0633\u062D',
+  '\uFC1E': '\u0633\u062E',
+  '\uFC1F': '\u0633\u0645',
+  '\uFC20': '\u0635\u062D',
+  '\uFC21': '\u0635\u0645',
+  '\uFC22': '\u0636\u062C',
+  '\uFC23': '\u0636\u062D',
+  '\uFC24': '\u0636\u062E',
+  '\uFC25': '\u0636\u0645',
+  '\uFC26': '\u0637\u062D',
+  '\uFC27': '\u0637\u0645',
+  '\uFC28': '\u0638\u0645',
+  '\uFC29': '\u0639\u062C',
+  '\uFC2A': '\u0639\u0645',
+  '\uFC2B': '\u063A\u062C',
+  '\uFC2C': '\u063A\u0645',
+  '\uFC2D': '\u0641\u062C',
+  '\uFC2E': '\u0641\u062D',
+  '\uFC2F': '\u0641\u062E',
+  '\uFC30': '\u0641\u0645',
+  '\uFC31': '\u0641\u0649',
+  '\uFC32': '\u0641\u064A',
+  '\uFC33': '\u0642\u062D',
+  '\uFC34': '\u0642\u0645',
+  '\uFC35': '\u0642\u0649',
+  '\uFC36': '\u0642\u064A',
+  '\uFC37': '\u0643\u0627',
+  '\uFC38': '\u0643\u062C',
+  '\uFC39': '\u0643\u062D',
+  '\uFC3A': '\u0643\u062E',
+  '\uFC3B': '\u0643\u0644',
+  '\uFC3C': '\u0643\u0645',
+  '\uFC3D': '\u0643\u0649',
+  '\uFC3E': '\u0643\u064A',
+  '\uFC3F': '\u0644\u062C',
+  '\uFC40': '\u0644\u062D',
+  '\uFC41': '\u0644\u062E',
+  '\uFC42': '\u0644\u0645',
+  '\uFC43': '\u0644\u0649',
+  '\uFC44': '\u0644\u064A',
+  '\uFC45': '\u0645\u062C',
+  '\uFC46': '\u0645\u062D',
+  '\uFC47': '\u0645\u062E',
+  '\uFC48': '\u0645\u0645',
+  '\uFC49': '\u0645\u0649',
+  '\uFC4A': '\u0645\u064A',
+  '\uFC4B': '\u0646\u062C',
+  '\uFC4C': '\u0646\u062D',
+  '\uFC4D': '\u0646\u062E',
+  '\uFC4E': '\u0646\u0645',
+  '\uFC4F': '\u0646\u0649',
+  '\uFC50': '\u0646\u064A',
+  '\uFC51': '\u0647\u062C',
+  '\uFC52': '\u0647\u0645',
+  '\uFC53': '\u0647\u0649',
+  '\uFC54': '\u0647\u064A',
+  '\uFC55': '\u064A\u062C',
+  '\uFC56': '\u064A\u062D',
+  '\uFC57': '\u064A\u062E',
+  '\uFC58': '\u064A\u0645',
+  '\uFC59': '\u064A\u0649',
+  '\uFC5A': '\u064A\u064A',
+  '\uFC5B': '\u0630\u0670',
+  '\uFC5C': '\u0631\u0670',
+  '\uFC5D': '\u0649\u0670',
+  '\uFC5E': '\u0020\u064C\u0651',
+  '\uFC5F': '\u0020\u064D\u0651',
+  '\uFC60': '\u0020\u064E\u0651',
+  '\uFC61': '\u0020\u064F\u0651',
+  '\uFC62': '\u0020\u0650\u0651',
+  '\uFC63': '\u0020\u0651\u0670',
+  '\uFC64': '\u0626\u0631',
+  '\uFC65': '\u0626\u0632',
+  '\uFC66': '\u0626\u0645',
+  '\uFC67': '\u0626\u0646',
+  '\uFC68': '\u0626\u0649',
+  '\uFC69': '\u0626\u064A',
+  '\uFC6A': '\u0628\u0631',
+  '\uFC6B': '\u0628\u0632',
+  '\uFC6C': '\u0628\u0645',
+  '\uFC6D': '\u0628\u0646',
+  '\uFC6E': '\u0628\u0649',
+  '\uFC6F': '\u0628\u064A',
+  '\uFC70': '\u062A\u0631',
+  '\uFC71': '\u062A\u0632',
+  '\uFC72': '\u062A\u0645',
+  '\uFC73': '\u062A\u0646',
+  '\uFC74': '\u062A\u0649',
+  '\uFC75': '\u062A\u064A',
+  '\uFC76': '\u062B\u0631',
+  '\uFC77': '\u062B\u0632',
+  '\uFC78': '\u062B\u0645',
+  '\uFC79': '\u062B\u0646',
+  '\uFC7A': '\u062B\u0649',
+  '\uFC7B': '\u062B\u064A',
+  '\uFC7C': '\u0641\u0649',
+  '\uFC7D': '\u0641\u064A',
+  '\uFC7E': '\u0642\u0649',
+  '\uFC7F': '\u0642\u064A',
+  '\uFC80': '\u0643\u0627',
+  '\uFC81': '\u0643\u0644',
+  '\uFC82': '\u0643\u0645',
+  '\uFC83': '\u0643\u0649',
+  '\uFC84': '\u0643\u064A',
+  '\uFC85': '\u0644\u0645',
+  '\uFC86': '\u0644\u0649',
+  '\uFC87': '\u0644\u064A',
+  '\uFC88': '\u0645\u0627',
+  '\uFC89': '\u0645\u0645',
+  '\uFC8A': '\u0646\u0631',
+  '\uFC8B': '\u0646\u0632',
+  '\uFC8C': '\u0646\u0645',
+  '\uFC8D': '\u0646\u0646',
+  '\uFC8E': '\u0646\u0649',
+  '\uFC8F': '\u0646\u064A',
+  '\uFC90': '\u0649\u0670',
+  '\uFC91': '\u064A\u0631',
+  '\uFC92': '\u064A\u0632',
+  '\uFC93': '\u064A\u0645',
+  '\uFC94': '\u064A\u0646',
+  '\uFC95': '\u064A\u0649',
+  '\uFC96': '\u064A\u064A',
+  '\uFC97': '\u0626\u062C',
+  '\uFC98': '\u0626\u062D',
+  '\uFC99': '\u0626\u062E',
+  '\uFC9A': '\u0626\u0645',
+  '\uFC9B': '\u0626\u0647',
+  '\uFC9C': '\u0628\u062C',
+  '\uFC9D': '\u0628\u062D',
+  '\uFC9E': '\u0628\u062E',
+  '\uFC9F': '\u0628\u0645',
+  '\uFCA0': '\u0628\u0647',
+  '\uFCA1': '\u062A\u062C',
+  '\uFCA2': '\u062A\u062D',
+  '\uFCA3': '\u062A\u062E',
+  '\uFCA4': '\u062A\u0645',
+  '\uFCA5': '\u062A\u0647',
+  '\uFCA6': '\u062B\u0645',
+  '\uFCA7': '\u062C\u062D',
+  '\uFCA8': '\u062C\u0645',
+  '\uFCA9': '\u062D\u062C',
+  '\uFCAA': '\u062D\u0645',
+  '\uFCAB': '\u062E\u062C',
+  '\uFCAC': '\u062E\u0645',
+  '\uFCAD': '\u0633\u062C',
+  '\uFCAE': '\u0633\u062D',
+  '\uFCAF': '\u0633\u062E',
+  '\uFCB0': '\u0633\u0645',
+  '\uFCB1': '\u0635\u062D',
+  '\uFCB2': '\u0635\u062E',
+  '\uFCB3': '\u0635\u0645',
+  '\uFCB4': '\u0636\u062C',
+  '\uFCB5': '\u0636\u062D',
+  '\uFCB6': '\u0636\u062E',
+  '\uFCB7': '\u0636\u0645',
+  '\uFCB8': '\u0637\u062D',
+  '\uFCB9': '\u0638\u0645',
+  '\uFCBA': '\u0639\u062C',
+  '\uFCBB': '\u0639\u0645',
+  '\uFCBC': '\u063A\u062C',
+  '\uFCBD': '\u063A\u0645',
+  '\uFCBE': '\u0641\u062C',
+  '\uFCBF': '\u0641\u062D',
+  '\uFCC0': '\u0641\u062E',
+  '\uFCC1': '\u0641\u0645',
+  '\uFCC2': '\u0642\u062D',
+  '\uFCC3': '\u0642\u0645',
+  '\uFCC4': '\u0643\u062C',
+  '\uFCC5': '\u0643\u062D',
+  '\uFCC6': '\u0643\u062E',
+  '\uFCC7': '\u0643\u0644',
+  '\uFCC8': '\u0643\u0645',
+  '\uFCC9': '\u0644\u062C',
+  '\uFCCA': '\u0644\u062D',
+  '\uFCCB': '\u0644\u062E',
+  '\uFCCC': '\u0644\u0645',
+  '\uFCCD': '\u0644\u0647',
+  '\uFCCE': '\u0645\u062C',
+  '\uFCCF': '\u0645\u062D',
+  '\uFCD0': '\u0645\u062E',
+  '\uFCD1': '\u0645\u0645',
+  '\uFCD2': '\u0646\u062C',
+  '\uFCD3': '\u0646\u062D',
+  '\uFCD4': '\u0646\u062E',
+  '\uFCD5': '\u0646\u0645',
+  '\uFCD6': '\u0646\u0647',
+  '\uFCD7': '\u0647\u062C',
+  '\uFCD8': '\u0647\u0645',
+  '\uFCD9': '\u0647\u0670',
+  '\uFCDA': '\u064A\u062C',
+  '\uFCDB': '\u064A\u062D',
+  '\uFCDC': '\u064A\u062E',
+  '\uFCDD': '\u064A\u0645',
+  '\uFCDE': '\u064A\u0647',
+  '\uFCDF': '\u0626\u0645',
+  '\uFCE0': '\u0626\u0647',
+  '\uFCE1': '\u0628\u0645',
+  '\uFCE2': '\u0628\u0647',
+  '\uFCE3': '\u062A\u0645',
+  '\uFCE4': '\u062A\u0647',
+  '\uFCE5': '\u062B\u0645',
+  '\uFCE6': '\u062B\u0647',
+  '\uFCE7': '\u0633\u0645',
+  '\uFCE8': '\u0633\u0647',
+  '\uFCE9': '\u0634\u0645',
+  '\uFCEA': '\u0634\u0647',
+  '\uFCEB': '\u0643\u0644',
+  '\uFCEC': '\u0643\u0645',
+  '\uFCED': '\u0644\u0645',
+  '\uFCEE': '\u0646\u0645',
+  '\uFCEF': '\u0646\u0647',
+  '\uFCF0': '\u064A\u0645',
+  '\uFCF1': '\u064A\u0647',
+  '\uFCF2': '\u0640\u064E\u0651',
+  '\uFCF3': '\u0640\u064F\u0651',
+  '\uFCF4': '\u0640\u0650\u0651',
+  '\uFCF5': '\u0637\u0649',
+  '\uFCF6': '\u0637\u064A',
+  '\uFCF7': '\u0639\u0649',
+  '\uFCF8': '\u0639\u064A',
+  '\uFCF9': '\u063A\u0649',
+  '\uFCFA': '\u063A\u064A',
+  '\uFCFB': '\u0633\u0649',
+  '\uFCFC': '\u0633\u064A',
+  '\uFCFD': '\u0634\u0649',
+  '\uFCFE': '\u0634\u064A',
+  '\uFCFF': '\u062D\u0649',
+  '\uFD00': '\u062D\u064A',
+  '\uFD01': '\u062C\u0649',
+  '\uFD02': '\u062C\u064A',
+  '\uFD03': '\u062E\u0649',
+  '\uFD04': '\u062E\u064A',
+  '\uFD05': '\u0635\u0649',
+  '\uFD06': '\u0635\u064A',
+  '\uFD07': '\u0636\u0649',
+  '\uFD08': '\u0636\u064A',
+  '\uFD09': '\u0634\u062C',
+  '\uFD0A': '\u0634\u062D',
+  '\uFD0B': '\u0634\u062E',
+  '\uFD0C': '\u0634\u0645',
+  '\uFD0D': '\u0634\u0631',
+  '\uFD0E': '\u0633\u0631',
+  '\uFD0F': '\u0635\u0631',
+  '\uFD10': '\u0636\u0631',
+  '\uFD11': '\u0637\u0649',
+  '\uFD12': '\u0637\u064A',
+  '\uFD13': '\u0639\u0649',
+  '\uFD14': '\u0639\u064A',
+  '\uFD15': '\u063A\u0649',
+  '\uFD16': '\u063A\u064A',
+  '\uFD17': '\u0633\u0649',
+  '\uFD18': '\u0633\u064A',
+  '\uFD19': '\u0634\u0649',
+  '\uFD1A': '\u0634\u064A',
+  '\uFD1B': '\u062D\u0649',
+  '\uFD1C': '\u062D\u064A',
+  '\uFD1D': '\u062C\u0649',
+  '\uFD1E': '\u062C\u064A',
+  '\uFD1F': '\u062E\u0649',
+  '\uFD20': '\u062E\u064A',
+  '\uFD21': '\u0635\u0649',
+  '\uFD22': '\u0635\u064A',
+  '\uFD23': '\u0636\u0649',
+  '\uFD24': '\u0636\u064A',
+  '\uFD25': '\u0634\u062C',
+  '\uFD26': '\u0634\u062D',
+  '\uFD27': '\u0634\u062E',
+  '\uFD28': '\u0634\u0645',
+  '\uFD29': '\u0634\u0631',
+  '\uFD2A': '\u0633\u0631',
+  '\uFD2B': '\u0635\u0631',
+  '\uFD2C': '\u0636\u0631',
+  '\uFD2D': '\u0634\u062C',
+  '\uFD2E': '\u0634\u062D',
+  '\uFD2F': '\u0634\u062E',
+  '\uFD30': '\u0634\u0645',
+  '\uFD31': '\u0633\u0647',
+  '\uFD32': '\u0634\u0647',
+  '\uFD33': '\u0637\u0645',
+  '\uFD34': '\u0633\u062C',
+  '\uFD35': '\u0633\u062D',
+  '\uFD36': '\u0633\u062E',
+  '\uFD37': '\u0634\u062C',
+  '\uFD38': '\u0634\u062D',
+  '\uFD39': '\u0634\u062E',
+  '\uFD3A': '\u0637\u0645',
+  '\uFD3B': '\u0638\u0645',
+  '\uFD3C': '\u0627\u064B',
+  '\uFD3D': '\u0627\u064B',
+  '\uFD50': '\u062A\u062C\u0645',
+  '\uFD51': '\u062A\u062D\u062C',
+  '\uFD52': '\u062A\u062D\u062C',
+  '\uFD53': '\u062A\u062D\u0645',
+  '\uFD54': '\u062A\u062E\u0645',
+  '\uFD55': '\u062A\u0645\u062C',
+  '\uFD56': '\u062A\u0645\u062D',
+  '\uFD57': '\u062A\u0645\u062E',
+  '\uFD58': '\u062C\u0645\u062D',
+  '\uFD59': '\u062C\u0645\u062D',
+  '\uFD5A': '\u062D\u0645\u064A',
+  '\uFD5B': '\u062D\u0645\u0649',
+  '\uFD5C': '\u0633\u062D\u062C',
+  '\uFD5D': '\u0633\u062C\u062D',
+  '\uFD5E': '\u0633\u062C\u0649',
+  '\uFD5F': '\u0633\u0645\u062D',
+  '\uFD60': '\u0633\u0645\u062D',
+  '\uFD61': '\u0633\u0645\u062C',
+  '\uFD62': '\u0633\u0645\u0645',
+  '\uFD63': '\u0633\u0645\u0645',
+  '\uFD64': '\u0635\u062D\u062D',
+  '\uFD65': '\u0635\u062D\u062D',
+  '\uFD66': '\u0635\u0645\u0645',
+  '\uFD67': '\u0634\u062D\u0645',
+  '\uFD68': '\u0634\u062D\u0645',
+  '\uFD69': '\u0634\u062C\u064A',
+  '\uFD6A': '\u0634\u0645\u062E',
+  '\uFD6B': '\u0634\u0645\u062E',
+  '\uFD6C': '\u0634\u0645\u0645',
+  '\uFD6D': '\u0634\u0645\u0645',
+  '\uFD6E': '\u0636\u062D\u0649',
+  '\uFD6F': '\u0636\u062E\u0645',
+  '\uFD70': '\u0636\u062E\u0645',
+  '\uFD71': '\u0637\u0645\u062D',
+  '\uFD72': '\u0637\u0645\u062D',
+  '\uFD73': '\u0637\u0645\u0645',
+  '\uFD74': '\u0637\u0645\u064A',
+  '\uFD75': '\u0639\u062C\u0645',
+  '\uFD76': '\u0639\u0645\u0645',
+  '\uFD77': '\u0639\u0645\u0645',
+  '\uFD78': '\u0639\u0645\u0649',
+  '\uFD79': '\u063A\u0645\u0645',
+  '\uFD7A': '\u063A\u0645\u064A',
+  '\uFD7B': '\u063A\u0645\u0649',
+  '\uFD7C': '\u0641\u062E\u0645',
+  '\uFD7D': '\u0641\u062E\u0645',
+  '\uFD7E': '\u0642\u0645\u062D',
+  '\uFD7F': '\u0642\u0645\u0645',
+  '\uFD80': '\u0644\u062D\u0645',
+  '\uFD81': '\u0644\u062D\u064A',
+  '\uFD82': '\u0644\u062D\u0649',
+  '\uFD83': '\u0644\u062C\u062C',
+  '\uFD84': '\u0644\u062C\u062C',
+  '\uFD85': '\u0644\u062E\u0645',
+  '\uFD86': '\u0644\u062E\u0645',
+  '\uFD87': '\u0644\u0645\u062D',
+  '\uFD88': '\u0644\u0645\u062D',
+  '\uFD89': '\u0645\u062D\u062C',
+  '\uFD8A': '\u0645\u062D\u0645',
+  '\uFD8B': '\u0645\u062D\u064A',
+  '\uFD8C': '\u0645\u062C\u062D',
+  '\uFD8D': '\u0645\u062C\u0645',
+  '\uFD8E': '\u0645\u062E\u062C',
+  '\uFD8F': '\u0645\u062E\u0645',
+  '\uFD92': '\u0645\u062C\u062E',
+  '\uFD93': '\u0647\u0645\u062C',
+  '\uFD94': '\u0647\u0645\u0645',
+  '\uFD95': '\u0646\u062D\u0645',
+  '\uFD96': '\u0646\u062D\u0649',
+  '\uFD97': '\u0646\u062C\u0645',
+  '\uFD98': '\u0646\u062C\u0645',
+  '\uFD99': '\u0646\u062C\u0649',
+  '\uFD9A': '\u0646\u0645\u064A',
+  '\uFD9B': '\u0646\u0645\u0649',
+  '\uFD9C': '\u064A\u0645\u0645',
+  '\uFD9D': '\u064A\u0645\u0645',
+  '\uFD9E': '\u0628\u062E\u064A',
+  '\uFD9F': '\u062A\u062C\u064A',
+  '\uFDA0': '\u062A\u062C\u0649',
+  '\uFDA1': '\u062A\u062E\u064A',
+  '\uFDA2': '\u062A\u062E\u0649',
+  '\uFDA3': '\u062A\u0645\u064A',
+  '\uFDA4': '\u062A\u0645\u0649',
+  '\uFDA5': '\u062C\u0645\u064A',
+  '\uFDA6': '\u062C\u062D\u0649',
+  '\uFDA7': '\u062C\u0645\u0649',
+  '\uFDA8': '\u0633\u062E\u0649',
+  '\uFDA9': '\u0635\u062D\u064A',
+  '\uFDAA': '\u0634\u062D\u064A',
+  '\uFDAB': '\u0636\u062D\u064A',
+  '\uFDAC': '\u0644\u062C\u064A',
+  '\uFDAD': '\u0644\u0645\u064A',
+  '\uFDAE': '\u064A\u062D\u064A',
+  '\uFDAF': '\u064A\u062C\u064A',
+  '\uFDB0': '\u064A\u0645\u064A',
+  '\uFDB1': '\u0645\u0645\u064A',
+  '\uFDB2': '\u0642\u0645\u064A',
+  '\uFDB3': '\u0646\u062D\u064A',
+  '\uFDB4': '\u0642\u0645\u062D',
+  '\uFDB5': '\u0644\u062D\u0645',
+  '\uFDB6': '\u0639\u0645\u064A',
+  '\uFDB7': '\u0643\u0645\u064A',
+  '\uFDB8': '\u0646\u062C\u062D',
+  '\uFDB9': '\u0645\u062E\u064A',
+  '\uFDBA': '\u0644\u062C\u0645',
+  '\uFDBB': '\u0643\u0645\u0645',
+  '\uFDBC': '\u0644\u062C\u0645',
+  '\uFDBD': '\u0646\u062C\u062D',
+  '\uFDBE': '\u062C\u062D\u064A',
+  '\uFDBF': '\u062D\u062C\u064A',
+  '\uFDC0': '\u0645\u062C\u064A',
+  '\uFDC1': '\u0641\u0645\u064A',
+  '\uFDC2': '\u0628\u062D\u064A',
+  '\uFDC3': '\u0643\u0645\u0645',
+  '\uFDC4': '\u0639\u062C\u0645',
+  '\uFDC5': '\u0635\u0645\u0645',
+  '\uFDC6': '\u0633\u062E\u064A',
+  '\uFDC7': '\u0646\u062C\u064A',
   '\uFE49': '\u203E',
   '\uFE4A': '\u203E',
   '\uFE4B': '\u203E',
   '\uFE4C': '\u203E',
   '\uFE4D': '\u005F',
   '\uFE4E': '\u005F',
-  '\uFE4F': '\u005F'
+  '\uFE4F': '\u005F',
+  '\uFE80': '\u0621',
+  '\uFE81': '\u0622',
+  '\uFE82': '\u0622',
+  '\uFE83': '\u0623',
+  '\uFE84': '\u0623',
+  '\uFE85': '\u0624',
+  '\uFE86': '\u0624',
+  '\uFE87': '\u0625',
+  '\uFE88': '\u0625',
+  '\uFE89': '\u0626',
+  '\uFE8A': '\u0626',
+  '\uFE8B': '\u0626',
+  '\uFE8C': '\u0626',
+  '\uFE8D': '\u0627',
+  '\uFE8E': '\u0627',
+  '\uFE8F': '\u0628',
+  '\uFE90': '\u0628',
+  '\uFE91': '\u0628',
+  '\uFE92': '\u0628',
+  '\uFE93': '\u0629',
+  '\uFE94': '\u0629',
+  '\uFE95': '\u062A',
+  '\uFE96': '\u062A',
+  '\uFE97': '\u062A',
+  '\uFE98': '\u062A',
+  '\uFE99': '\u062B',
+  '\uFE9A': '\u062B',
+  '\uFE9B': '\u062B',
+  '\uFE9C': '\u062B',
+  '\uFE9D': '\u062C',
+  '\uFE9E': '\u062C',
+  '\uFE9F': '\u062C',
+  '\uFEA0': '\u062C',
+  '\uFEA1': '\u062D',
+  '\uFEA2': '\u062D',
+  '\uFEA3': '\u062D',
+  '\uFEA4': '\u062D',
+  '\uFEA5': '\u062E',
+  '\uFEA6': '\u062E',
+  '\uFEA7': '\u062E',
+  '\uFEA8': '\u062E',
+  '\uFEA9': '\u062F',
+  '\uFEAA': '\u062F',
+  '\uFEAB': '\u0630',
+  '\uFEAC': '\u0630',
+  '\uFEAD': '\u0631',
+  '\uFEAE': '\u0631',
+  '\uFEAF': '\u0632',
+  '\uFEB0': '\u0632',
+  '\uFEB1': '\u0633',
+  '\uFEB2': '\u0633',
+  '\uFEB3': '\u0633',
+  '\uFEB4': '\u0633',
+  '\uFEB5': '\u0634',
+  '\uFEB6': '\u0634',
+  '\uFEB7': '\u0634',
+  '\uFEB8': '\u0634',
+  '\uFEB9': '\u0635',
+  '\uFEBA': '\u0635',
+  '\uFEBB': '\u0635',
+  '\uFEBC': '\u0635',
+  '\uFEBD': '\u0636',
+  '\uFEBE': '\u0636',
+  '\uFEBF': '\u0636',
+  '\uFEC0': '\u0636',
+  '\uFEC1': '\u0637',
+  '\uFEC2': '\u0637',
+  '\uFEC3': '\u0637',
+  '\uFEC4': '\u0637',
+  '\uFEC5': '\u0638',
+  '\uFEC6': '\u0638',
+  '\uFEC7': '\u0638',
+  '\uFEC8': '\u0638',
+  '\uFEC9': '\u0639',
+  '\uFECA': '\u0639',
+  '\uFECB': '\u0639',
+  '\uFECC': '\u0639',
+  '\uFECD': '\u063A',
+  '\uFECE': '\u063A',
+  '\uFECF': '\u063A',
+  '\uFED0': '\u063A',
+  '\uFED1': '\u0641',
+  '\uFED2': '\u0641',
+  '\uFED3': '\u0641',
+  '\uFED4': '\u0641',
+  '\uFED5': '\u0642',
+  '\uFED6': '\u0642',
+  '\uFED7': '\u0642',
+  '\uFED8': '\u0642',
+  '\uFED9': '\u0643',
+  '\uFEDA': '\u0643',
+  '\uFEDB': '\u0643',
+  '\uFEDC': '\u0643',
+  '\uFEDD': '\u0644',
+  '\uFEDE': '\u0644',
+  '\uFEDF': '\u0644',
+  '\uFEE0': '\u0644',
+  '\uFEE1': '\u0645',
+  '\uFEE2': '\u0645',
+  '\uFEE3': '\u0645',
+  '\uFEE4': '\u0645',
+  '\uFEE5': '\u0646',
+  '\uFEE6': '\u0646',
+  '\uFEE7': '\u0646',
+  '\uFEE8': '\u0646',
+  '\uFEE9': '\u0647',
+  '\uFEEA': '\u0647',
+  '\uFEEB': '\u0647',
+  '\uFEEC': '\u0647',
+  '\uFEED': '\u0648',
+  '\uFEEE': '\u0648',
+  '\uFEEF': '\u0649',
+  '\uFEF0': '\u0649',
+  '\uFEF1': '\u064A',
+  '\uFEF2': '\u064A',
+  '\uFEF3': '\u064A',
+  '\uFEF4': '\u064A',
+  '\uFEF5': '\u0644\u0622',
+  '\uFEF6': '\u0644\u0622',
+  '\uFEF7': '\u0644\u0623',
+  '\uFEF8': '\u0644\u0623',
+  '\uFEF9': '\u0644\u0625',
+  '\uFEFA': '\u0644\u0625',
+  '\uFEFB': '\u0644\u0627',
+  '\uFEFC': '\u0644\u0627'
 };
 
-function fontCharsToUnicode(charCodes, fontProperties) {
-  var toUnicode = fontProperties.toUnicode;
-  var composite = fontProperties.composite;
-  var encoding, differences, cidToUnicode;
+function reverseIfRtl(chars) {
+  var charsLength = chars.length;
+  //reverse an arabic ligature
+  if (charsLength <= 1 || !isRTLRangeFor(chars.charCodeAt(0)))
+    return chars;
+
+  var s = '';
+  for (var ii = charsLength - 1; ii >= 0; ii--)
+    s += chars[ii];
+  return s;
+}
+
+function fontCharsToUnicode(charCodes, font) {
+  var glyphs = font.charsToGlyphs(charCodes);
   var result = '';
-  if (composite) {
-    cidToUnicode = fontProperties.cidToUnicode;
-    for (var i = 0, ii = charCodes.length; i < ii; i += 2) {
-      var charCode = (charCodes.charCodeAt(i) << 8) |
-        charCodes.charCodeAt(i + 1);
-      if (toUnicode && charCode in toUnicode) {
-        var unicode = toUnicode[charCode];
-        result += typeof unicode !== 'number' ? unicode :
-          String.fromCharCode(unicode);
-        continue;
-      }
-      result += String.fromCharCode(!cidToUnicode ? charCode :
-        cidToUnicode[charCode] || charCode);
-    }
-  } else {
-    differences = fontProperties.differences;
-    encoding = fontProperties.baseEncoding;
-    for (var i = 0, ii = charCodes.length; i < ii; i++) {
-      var charCode = charCodes.charCodeAt(i);
-      var unicode;
-      if (toUnicode && charCode in toUnicode) {
-        var unicode = toUnicode[charCode];
-        result += typeof unicode !== 'number' ? unicode :
-          String.fromCharCode(unicode);
-        continue;
-      }
-
-      var glyphName = charCode in differences ? differences[charCode] :
-        encoding[charCode];
-      if (glyphName in GlyphsUnicode) {
-        result += String.fromCharCode(GlyphsUnicode[glyphName]);
-        continue;
-      }
-      result += String.fromCharCode(charCode);
-    }
-  }
-  // normalizing the unicode characters
-  for (var i = 0, ii = result.length; i < ii; i++) {
-    if (!(result[i] in NormalizedUnicodes))
+  for (var i = 0, ii = glyphs.length; i < ii; i++) {
+    var glyph = glyphs[i];
+    if (!glyph)
       continue;
-    result = result.substring(0, i) + NormalizedUnicodes[result[i]] +
-      result.substring(i + 1);
-    ii = result.length;
+
+    var glyphUnicode = glyph.unicode;
+    if (glyphUnicode in NormalizedUnicodes)
+      glyphUnicode = NormalizedUnicodes[glyphUnicode];
+    result += reverseIfRtl(glyphUnicode);
   }
   return result;
 }
 
 /**
  * 'Font' is the class the outside world should use, it encapsulate all the font
  * decoding logics whatever type it is (assuming the font type is supported).
  *
  * For example to read a Type1 font and to attach it to the document:
  *   var type1Font = new Font("MyFontName", binaryFile, propertiesObject);
  *   type1Font.bind();
  */
 var Font = (function FontClosure() {
   function Font(name, file, properties) {
+    if (arguments.length === 1) {
+      // importing translated data
+      var data = arguments[0];
+      for (var i in data) {
+        this[i] = data[i];
+      }
+      return;
+    }
+
     this.name = name;
+    this.loadedName = properties.loadedName;
     this.coded = properties.coded;
-    this.charProcOperatorList = properties.charProcOperatorList;
+    this.loadCharProcs = properties.coded;
     this.sizes = [];
 
     var names = name.split('+');
     names = names.length > 1 ? names[1] : names[0];
     names = names.split(/[-,_]/g)[0];
     this.isSerifFont = !!(properties.flags & FontFlags.Serif);
     this.isSymbolicFont = !!(properties.flags & FontFlags.Symbolic);
+    this.isMonospace = !!(properties.flags & FontFlags.FixedPitch);
 
     var type = properties.type;
     this.type = type;
 
-    // If the font is to be ignored, register it like an already loaded font
-    // to avoid the cost of waiting for it be be loaded by the platform.
-    if (properties.ignore) {
-      this.loadedName = this.isSerifFont ? 'serif' : 'sans-serif';
-      this.loading = false;
-      return;
-    }
+    this.fallbackName = this.isMonospace ? 'monospace' :
+                        this.isSerifFont ? 'serif' : 'sans-serif';
 
     this.differences = properties.differences;
     this.widths = properties.widths;
     this.defaultWidth = properties.defaultWidth;
     this.composite = properties.composite;
     this.wideChars = properties.wideChars;
     this.hasEncoding = properties.hasEncoding;
 
@@ -15745,17 +16347,16 @@ var Font = (function FontClosure() {
         break;
     }
 
     this.data = data;
     this.fontMatrix = properties.fontMatrix;
     this.widthMultiplier = !properties.fontMatrix ? 1.0 :
       1.0 / properties.fontMatrix[0];
     this.encoding = properties.baseEncoding;
-    this.loadedName = properties.loadedName;
     this.loading = true;
   };
 
   var numFonts = 0;
   function getUniqueName() {
     return 'pdfFont' + numFonts++;
   }
 
@@ -16149,16 +16750,25 @@ var Font = (function FontClosure() {
   }
 
   Font.prototype = {
     name: null,
     font: null,
     mimetype: null,
     encoding: null,
 
+    exportData: function Font_exportData() {
+      var data = {};
+      for (var i in this) {
+        if (this.hasOwnProperty(i))
+          data[i] = this[i];
+      }
+      return data;
+    },
+
     checkAndRepair: function Font_checkAndRepair(name, font, properties) {
       function readTableEntry(file) {
         var tag = file.getBytes(4);
         tag = String.fromCharCode(tag[0]) +
               String.fromCharCode(tag[1]) +
               String.fromCharCode(tag[2]) +
               String.fromCharCode(tag[3]);
 
@@ -17251,17 +17861,21 @@ var Font = (function FontClosure() {
           cidToUnicodeMap[cid] = unicode;
           unicodeToCIDMap[unicode] = cid;
           cid++;
         } else
           cid++;
       }
     },
 
-    bindDOM: function Font_bindDOM(data) {
+    bindDOM: function Font_bindDOM() {
+      if (!this.data)
+        return null;
+
+      var data = bytesToString(this.data);
       var fontName = this.loadedName;
 
       // Add the font-face rule to the document
       var url = ('url(data:' + this.mimetype + ';base64,' +
                  window.btoa(data) + ');');
       var rule = "@font-face { font-family:'" + fontName + "';src:" + url + '}';
 
       var styleElement = document.getElementById('PDFJS_FONT_STYLE_TAG');
@@ -17270,23 +17884,28 @@ var Font = (function FontClosure() {
           styleElement.id = 'PDFJS_FONT_STYLE_TAG';
           document.documentElement.getElementsByTagName('head')[0].appendChild(
             styleElement);
       }
 
       var styleSheet = styleElement.sheet;
       styleSheet.insertRule(rule, styleSheet.cssRules.length);
 
-      if (PDFJS.pdfBug && FontInspector.enabled)
-        FontInspector.fontAdded(this, url);
+      if (PDFJS.pdfBug && 'FontInspector' in globalScope &&
+          globalScope['FontInspector'].enabled)
+        globalScope['FontInspector'].fontAdded(this, url);
 
       return rule;
     },
 
     get spaceWidth() {
+      if ('_shadowWidth' in this) {
+        return this._shadowWidth;
+      }
+
       // trying to estimate space character width
       var possibleSpaceReplacements = ['space', 'minus', 'one', 'i'];
       var width;
       for (var i = 0, ii = possibleSpaceReplacements.length; i < ii; i++) {
         var glyphName = possibleSpaceReplacements[i];
         // if possible, getting width by glyph name
         if (glyphName in this.widths) {
           width = this.widths[glyphName];
@@ -17304,17 +17923,20 @@ var Font = (function FontClosure() {
         if (!(charcode > 0))
           charcode = glyphUnicode;
         // trying to get width via charcode
         width = this.widths[charcode];
         if (width)
           break; // the non-zero width found
       }
       width = (width || this.defaultWidth) * this.widthMultiplier;
-      return shadow(this, 'spaceWidth', width);
+      // Do not shadow the property here. See discussion:
+      // https://github.com/mozilla/pdf.js/pull/2127#discussion_r1662280
+      this._shadowWidth = width;
+      return width;
     },
 
     charToGlyph: function Font_charToGlyph(charcode) {
       var fontCharCode, width, operatorList, disabled;
 
       var width = this.widths[charcode];
 
       switch (this.type) {
@@ -18800,17 +19422,17 @@ var CFFParser = (function CFFParserClosu
       // the font sanitizer don't. As a workaround the sequence (12, 0)
       // is replaced by a useless (0, hmoveto).
       var count = charStrings.count;
       for (var i = 0; i < count; i++) {
         var charstring = charStrings.get(i);
 
         var data = charstring;
         var length = data.length;
-        for (var j = 0; j <= length; j) {
+        for (var j = 0; j <= length;) {
           var value = data[j++];
           if (value == 12 && data[j++] == 0) {
               data[j - 2] = 139;
               data[j - 1] = 22;
           } else if (value === 28) {
             j += 2;
           } else if (value >= 247 && value <= 254) {
             j++;
@@ -27964,25 +28586,30 @@ var Pattern = (function PatternClosure()
         return new Shadings.Dummy();
     }
   };
   return Pattern;
 })();
 
 var Shadings = {};
 
+// A small number to offset the first/last color stops so we can insert ones to
+// support extend.  Number.MIN_VALUE appears to be too small and breaks the
+// extend. 1e-7 works in FF but chrome seems to use an even smaller sized number
+// internally so we have to go bigger.
+Shadings.SMALL_NUMBER = 1e-2;
+
 // Radial and axial shading have very similar implementations
 // If needed, the implementations can be broken into two classes
 Shadings.RadialAxial = (function RadialAxialClosure() {
   function RadialAxial(dict, matrix, xref, res, ctx) {
     this.matrix = matrix;
     this.coordsArr = dict.get('Coords');
     this.shadingType = dict.get('ShadingType');
     this.type = 'Pattern';
-
     this.ctx = ctx;
     var cs = dict.get('ColorSpace', 'CS');
     cs = ColorSpace.parse(cs, xref, res);
     this.cs = cs;
 
     var t0 = 0.0, t1 = 1.0;
     if (dict.has('Domain')) {
       var domainArr = dict.get('Domain');
@@ -27990,66 +28617,98 @@ Shadings.RadialAxial = (function RadialA
       t1 = domainArr[1];
     }
 
     var extendStart = false, extendEnd = false;
     if (dict.has('Extend')) {
       var extendArr = dict.get('Extend');
       extendStart = extendArr[0];
       extendEnd = extendArr[1];
-      TODO('Support extend');
+    }
+
+    if (this.shadingType === PatternType.RADIAL &&
+       (!extendStart || !extendEnd)) {
+      // Radial gradient only currently works if either circle is fully within
+      // the other circle.
+      var x1 = this.coordsArr[0];
+      var y1 = this.coordsArr[1];
+      var r1 = this.coordsArr[2];
+      var x2 = this.coordsArr[3];
+      var y2 = this.coordsArr[4];
+      var r2 = this.coordsArr[5];
+      var distance = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
+      if (r1 <= r2 + distance &&
+          r2 <= r1 + distance) {
+        warn('Unsupported radial gradient.');
+      }
     }
 
     this.extendStart = extendStart;
     this.extendEnd = extendEnd;
 
     var fnObj = dict.get('Function');
     if (isArray(fnObj))
       error('No support for array of functions');
     if (!isPDFFunction(fnObj))
       error('Invalid function');
     var fn = PDFFunction.parse(xref, fnObj);
 
     // 10 samples seems good enough for now, but probably won't work
     // if there are sharp color changes. Ideally, we would implement
     // the spec faithfully and add lossless optimizations.
-    var step = (t1 - t0) / 10;
     var diff = t1 - t0;
-
-    var colorStops = [];
+    var step = diff / 10;
+
+    var colorStops = this.colorStops = [];
+
+    // Protect against bad domains so we don't end up in an infinte loop below.
+    if (t0 >= t1 || step <= 0) {
+      // Acrobat doesn't seem to handle these cases so we'll ignore for
+      // now.
+      info('Bad shading domain.');
+      return;
+    }
+
     for (var i = t0; i <= t1; i += step) {
       var rgbColor = cs.getRgb(fn([i]));
       var cssColor = Util.makeCssRgb(rgbColor[0], rgbColor[1], rgbColor[2]);
       colorStops.push([(i - t0) / diff, cssColor]);
     }
 
+    var background = 'transparent';
+    if (dict.has('Background')) {
+      var rgbColor = cs.getRgb(dict.get('Background'));
+      background = Util.makeCssRgb(rgbColor[0], rgbColor[1], rgbColor[2]);
+    }
+
+    if (!extendStart) {
+      // Insert a color stop at the front and offset the first real color stop
+      // so it doesn't conflict with the one we insert.
+      colorStops.unshift([0, background]);
+      colorStops[1][0] += Shadings.SMALL_NUMBER;
+    }
+    if (!extendEnd) {
+      // Same idea as above in extendStart but for the end.
+      colorStops[colorStops.length - 1][0] -= Shadings.SMALL_NUMBER;
+      colorStops.push([1, background]);
+    }
+
     this.colorStops = colorStops;
   }
 
   RadialAxial.fromIR = function RadialAxial_fromIR(raw) {
     var type = raw[1];
     var colorStops = raw[2];
     var p0 = raw[3];
     var p1 = raw[4];
     var r0 = raw[5];
     var r1 = raw[6];
     return {
       type: 'Pattern',
       getPattern: function RadialAxial_getPattern(ctx) {
-        var curMatrix = ctx.mozCurrentTransform;
-        if (curMatrix) {
-          var userMatrix = ctx.mozCurrentTransformInverse;
-
-          p0 = Util.applyTransform(p0, curMatrix);
-          p0 = Util.applyTransform(p0, userMatrix);
-
-          p1 = Util.applyTransform(p1, curMatrix);
-          p1 = Util.applyTransform(p1, userMatrix);
-        }
-
         var grad;
         if (type == PatternType.AXIAL)
           grad = ctx.createLinearGradient(p0[0], p0[1], p1[0], p1[1]);
         else if (type == PatternType.RADIAL)
           grad = ctx.createRadialGradient(p0[0], p0[1], r0, p1[0], p1[1], r1);
 
         for (var i = 0, ii = colorStops.length; i < ii; ++i) {
           var c = colorStops[i];
@@ -28127,17 +28786,16 @@ var TilingPattern = (function TilingPatt
     var xstep = IR[5];
     var ystep = IR[6];
     var paintType = IR[7];
     var tilingType = IR[8];
 
     TODO('TilingType: ' + tilingType);
 
     this.curMatrix = ctx.mozCurrentTransform;
-    this.invMatrix = ctx.mozCurrentTransformInverse;
     this.ctx = ctx;
     this.type = 'Pattern';
 
     var x0 = bbox[0], y0 = bbox[1], x1 = bbox[2], y1 = bbox[3];
 
     var topLeft = [x0, y0];
     // we want the canvas to be as large as the step size
     var botRight = [x0 + xstep, y0 + ystep];
@@ -30572,21 +31230,28 @@ var LZWStream = (function LZWStreamClosu
 function MessageHandler(name, comObj) {
   this.name = name;
   this.comObj = comObj;
   this.callbackIndex = 1;
   var callbacks = this.callbacks = {};
   var ah = this.actionHandler = {};
 
   ah['console_log'] = [function ahConsoleLog(data) {
-    console.log.apply(console, data);
+    log.apply(null, data);
   }];
-  ah['console_error'] = [function ahConsoleError(data) {
-    console.error.apply(console, data);
-  }];
+  // If there's no console available, console_error in the
+  // action handler will do nothing.
+  if ('console' in globalScope) {
+    ah['console_error'] = [function ahConsoleError(data) {
+      globalScope['console'].error.apply(null, data);
+    }];
+  } else {
+    ah['console_error'] = [function ahConsoleError(data) {
+    }];
+  }
   ah['_warn'] = [function ah_Warn(data) {
     warn(data);
   }];
 
   comObj.onmessage = function messageHandlerComObjOnMessage(event) {
     var data = event.data;
     if (data.isReply) {
       var callbackId = data.callbackId;
@@ -30781,17 +31446,17 @@ var WorkerMessageHandler = {
 
         handler.send('PageError', {
           pageNum: pageNum,
           error: e
         });
         return;
       }
 
-      console.log('page=%d - getOperatorList: time=%dms, len=%d', pageNum,
+      log('page=%d - getOperatorList: time=%dms, len=%d', pageNum,
                               Date.now() - start, operatorList.fnArray.length);
 
       // Filter the dependecies for fonts.
       var fonts = {};
       for (var i = 0, ii = dependency.length; i < ii; i++) {
         var dep = dependency[i];
         if (dep.indexOf('font_') == 0) {
           fonts[dep] = true;
@@ -30813,36 +31478,36 @@ var WorkerMessageHandler = {
         var page = pdfModel.getPage(pageNum);
         textContent = page.extractTextContent();
         promise.resolve(textContent);
       } catch (e) {
         // Skip errored pages
         promise.reject(e);
       }
 
-      console.log('text indexing: page=%d - time=%dms',
+      log('text indexing: page=%d - time=%dms',
                       pageNum, Date.now() - start);
     });
   }
 };
 
 var consoleTimer = {};
 
 var workerConsole = {
   log: function log() {
     var args = Array.prototype.slice.call(arguments);
-    postMessage({
+    globalScope.postMessage({
       action: 'console_log',
       data: args
     });
   },
 
   error: function error() {
     var args = Array.prototype.slice.call(arguments);
-    postMessage({
+    globalScope.postMessage({
       action: 'console_error',
       data: args
     });
     throw 'pdf.js execution error';
   },
 
   time: function time(name) {
     consoleTimer[name] = Date.now();
@@ -30860,17 +31525,17 @@ var workerConsole = {
 // Worker thread?
 if (typeof window === 'undefined') {
   globalScope.console = workerConsole;
 
   // Add a logger so we can pass warnings on to the main thread, errors will
   // throw an exception which will be forwarded on automatically.
   PDFJS.LogManager.addLogger({
     warn: function(msg) {
-      postMessage({
+      globalScope.postMessage({
         action: '_warn',
         data: msg
       });
     }
   });
 
   var handler = new MessageHandler('worker_processor', this);
   WorkerMessageHandler.setup(handler);
@@ -31125,17 +31790,18 @@ var JpxImage = (function JpxImageClosure
                 cod.precinctsSizes = precinctsSizes;
               }
 
               if (cod.sopMarkerUsed || cod.ephMarkerUsed ||
                   cod.selectiveArithmeticCodingBypass ||
                   cod.resetContextProbabilities ||
                   cod.terminationOnEachCodingPass ||
                   cod.verticalyStripe || cod.predictableTermination)
-                throw 'Unsupported COD options: ' + uneval(cod);
+                throw 'Unsupported COD options: ' +
+                  globalScope.JSON.stringify(cod);
 
               if (context.mainHeader)
                 context.COD = cod;
               else {
                 context.currentTile.COD = cod;
                 context.currentTile.COC = [];
               }
               break;
@@ -33923,21 +34589,26 @@ var bidi = PDFJS.bidi = (function bidiCl
         return '\u00BB';
       case '\u00BB':
         return '\u00AB';
       default:
         return c;
     }
   }
 
-  function bidi(text, startLevel) {
-    var str = text.str;
+  function BidiResult(str, isLTR) {
+    this.str = str;
+    this.ltr = isLTR;
+  }
+
+  function bidi(str, startLevel) {
+    var isLTR = true;
     var strLength = str.length;
     if (strLength == 0)
-      return str;
+      return new BidiResult(str, ltr);
 
     // get types, fill arrays
 
     var chars = [];
     var types = [];
     var oldtypes = [];
     var numBidi = 0;
 
@@ -33961,26 +34632,26 @@ var bidi = PDFJS.bidi = (function bidiCl
       oldtypes[i] = types[i] = charType;
     }
 
     // detect the bidi method
     //  if there are no rtl characters then no bidi needed
     //  if less than 30% chars are rtl then string is primarily ltr
     //  if more than 30% chars are rtl then string is primarily rtl
     if (numBidi == 0) {
-      text.direction = 'ltr';
-      return str;
+      isLTR = true;
+      return new BidiResult(str, isLTR);
     }
 
     if (startLevel == -1) {
       if ((strLength / numBidi) < 0.3) {
-        text.direction = 'ltr';
+        isLTR = true;
         startLevel = 0;
       } else {
-        text.direction = 'rtl';
+        isLTR = false;
         startLevel = 1;
       }
     }
 
     var levels = [];
 
     for (var i = 0; i < strLength; ++i) {
       levels[i] = startLevel;
@@ -34223,17 +34894,18 @@ var bidi = PDFJS.bidi = (function bidiCl
     // Finally, return string
 
     var result = '';
     for (var i = 0, ii = chars.length; i < ii; ++i) {
       var ch = chars[i];
       if (ch != '<' && ch != '>')
         result += ch;
     }
-    return result;
+
+    return new BidiResult(result, isLTR);
   }
 
   return bidi;
 })();
 
 
 
 var Metadata = PDFJS.Metadata = (function MetadataClosure() {
@@ -34280,33 +34952,29 @@ var Metadata = PDFJS.Metadata = (functio
         while (rdf && rdf.nodeName.toLowerCase() !== 'rdf:rdf')
           rdf = rdf.nextSibling;
       }
 
       var nodeName = (rdf) ? rdf.nodeName.toLowerCase() : null;
       if (!rdf || nodeName !== 'rdf:rdf' || !rdf.hasChildNodes())
         return;
 
-      var childNodes = rdf.childNodes, desc, namespace, entries, entry;
-
-      for (var i = 0, length = childNodes.length; i < length; i++) {
-        desc = childNodes[i];
+      var children = rdf.childNodes, desc, entry, name, i, ii, length, iLength;
+
+      for (i = 0, length = children.length; i < length; i++) {
+        desc = children[i];
         if (desc.nodeName.toLowerCase() !== 'rdf:description')
           continue;
 
-        entries = [];
-        for (var ii = 0, iLength = desc.childNodes.length; ii < iLength; ii++) {
-          if (desc.childNodes[ii].nodeName.toLowerCase() !== '#text')
-            entries.push(desc.childNodes[ii]);
-        }
-
-        for (ii = 0, iLength = entries.length; ii < iLength; ii++) {
-          var entry = entries[ii];
-          var name = entry.nodeName.toLowerCase();
-          this.metadata[name] = entry.textContent.trim();
+        for (ii = 0, iLength = desc.childNodes.length; ii < iLength; ii++) {
+          if (desc.childNodes[ii].nodeName.toLowerCase() !== '#text') {
+            entry = desc.childNodes[ii];
+            name = entry.nodeName.toLowerCase();
+            this.metadata[name] = entry.textContent.trim();
+          }
         }
       }
     },
 
     get: function Metadata_get(name) {
       return this.metadata[name] || null;
     },
 
@@ -34830,16 +35498,20 @@ var JpegImage = (function jpegImage() {
           for (i = 0; i < 8; i++)
             line[sample + i] = r[offset++];
         }
       }
     }
     return lines;
   }
 
+  function clampTo8bit(a) {
+    return a < 0 ? 0 : a > 255 ? 255 : a;
+  }
+
   constructor.prototype = {
     load: function load(path) {
       var xhr = new XMLHttpRequest();
       xhr.open("GET", path, true);
       xhr.responseType = "arraybuffer";
       xhr.onload = (function() {
         // TODO catch parse error
         var data = new Uint8Array(xhr.response || xhr.mozResponseArrayBuffer);
@@ -35080,19 +35752,16 @@ var JpegImage = (function jpegImage() {
         this.components.push({
           lines: buildComponentData(frame, component),
           scaleX: component.h / frame.maxH,
           scaleY: component.v / frame.maxV
         });
       }
     },
     getData: function getData(width, height) {
-      function clampTo8bit(a) {
-        return a < 0 ? 0 : a > 255 ? 255 : a;
-      }
       var scaleX = this.width / width, scaleY = this.height / height;
 
       var component1, component2, component3, component4;
       var component1Line, component2Line, component3Line, component4Line;
       var x, y;
       var offset = 0;
       var Y, Cb, Cr, K, C, M, Ye, R, G, B;
       var colorTransform;
@@ -35251,16 +35920,17 @@ var JpegImage = (function jpegImage() {
           throw 'Unsupported color mode';
       }
     }
   };
 
   return constructor;
 })();
 
+
 }).call((typeof window === 'undefined') ? this : window);
 
 
 -->
 </script>
 <script type="text/javascript">
   // This specifies the location of the pdf.js file.
   PDFJS.isFirefoxExtension = true;
@@ -35332,18 +36002,18 @@ var JpegImage = (function jpegImage() {
                 <input type="number" id="pageNumber" class="toolbarField pageNumber" value="1" size="4" min="1" tabindex="7">
                 </input>
                 <span id="numPages" class="toolbarLabel"></span>
               </div>
               <div id="toolbarViewerRight">
                 <input id="fileInput" class="fileInput" type="file" oncontextmenu="return false;" style="visibility: hidden; position: fixed; right: 0; top: 0" />
 
 
-                <button id="fullscreen" class="toolbarButton fullscreen" title="Fullscreen" tabindex="11" data-l10n-id="fullscreen">
-                  <span data-l10n-id="fullscreen_label">Fullscreen</span>
+                <button id="fullscreen" class="toolbarButton fullscreen" title="Switch to Presentation Mode" tabindex="11" data-l10n-id="presentation_mode">
+                  <span data-l10n-id="presentation_mode_label">Presentation Mode</span>
                 </button>
 
                 <button id="openFile" class="toolbarButton openFile" title="Open File" tabindex="12" data-l10n-id="open_file">
                    <span data-l10n-id="open_file_label">Open</span>
                 </button>
 
                 <button id="print" class="toolbarButton print" title="Print" tabindex="13" data-l10n-id="print">
                   <span data-l10n-id="print_label">Print</span>
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -30,17 +30,16 @@ var kImageDirectory = './images/';
 var kSettingsMemory = 20;
 var RenderingStates = {
   INITIAL: 0,
   RUNNING: 1,
   PAUSED: 2,
   FINISHED: 3
 };
 
-PDFJS.workerSrc = '../build/pdf.js';
 
 var mozL10n = document.mozL10n || document.webL10n;
 
 function getFileName(url) {
   var anchor = url.indexOf('#');
   var query = url.indexOf('?');
   var end = Math.min(
     anchor > 0 ? anchor : url.length,
@@ -281,30 +280,35 @@ var PDFView = {
   fellback: false,
   pdfDocument: null,
   sidebarOpen: false,
   pageViewScroll: null,
   thumbnailViewScroll: null,
   isFullscreen: false,
   previousScale: null,
   pageRotation: 0,
+  lastScroll: 0,
 
   // called once when the document is loaded
   initialize: function pdfViewInitialize() {
+    var self = this;
     var container = this.container = document.getElementById('viewerContainer');
     this.pageViewScroll = {};
     this.watchScroll(container, this.pageViewScroll, updateViewarea);
 
     var thumbnailContainer = this.thumbnailContainer =
                              document.getElementById('thumbnailView');
     this.thumbnailViewScroll = {};
     this.watchScroll(thumbnailContainer, this.thumbnailViewScroll,
                      this.renderHighestPriority.bind(this));
 
     this.initialized = true;
+    container.addEventListener('scroll', function() {
+      self.lastScroll = Date.now();
+    }, false);
   },
 
   // Helper function to keep track whether a div was scrolled up or down and
   // then call a callback.
   watchScroll: function pdfViewWatchScroll(viewAreaElement, state, callback) {
     state.down = true;
     state.lastY = viewAreaElement.scrollTop;
     viewAreaElement.addEventListener('scroll', function webViewerScroll(evt) {
@@ -432,17 +436,17 @@ var PDFView = {
                                                       enumerable: true,
                                                       configurable: true,
                                                       writable: false });
     return value;
   },
 
   get supportsFullscreen() {
     var doc = document.documentElement;
-    var support = doc.requestFullScreen || doc.mozRequestFullScreen ||
+    var support = doc.requestFullscreen || doc.mozRequestFullScreen ||
                   doc.webkitRequestFullScreen;
     Object.defineProperty(this, 'supportsFullScreen', { value: support,
                                                         enumerable: true,
                                                         configurable: true,
                                                         writable: false });
     return support;
   },
 
@@ -532,29 +536,26 @@ var PDFView = {
       }
     );
   },
 
   download: function pdfViewDownload() {
     function noData() {
       FirefoxCom.request('download', { originalUrl: url });
     }
-
     var url = this.url.split('#')[0];
     // Document isn't ready just try to download with the url.
     if (!this.pdfDocument) {
       noData();
       return;
     }
     this.pdfDocument.getData().then(
       function getDataSuccess(data) {
-        var bb = new MozBlobBuilder();
-        bb.append(data.buffer);
-        var blobUrl = window.URL.createObjectURL(
-                        bb.getBlob('application/pdf'));
+        var blob = PDFJS.createBlob(data.buffer, 'application/pdf');
+        var blobUrl = window.URL.createObjectURL(blob);
   
         FirefoxCom.request('download', { blobUrl: blobUrl, originalUrl: url },
           function response(err) {
             if (err) {
               // This error won't really be helpful because it's likely the
               // fallback won't work either (or is already open).
               PDFView.error('PDF failed to download.');
             }
@@ -1047,17 +1048,17 @@ var PDFView = {
   extractText: function() {
     if (this.startedTextExtraction)
       return;
     this.startedTextExtraction = true;
     var self = this;
     function extractPageText(pageIndex) {
       self.pages[pageIndex].pdfPage.getTextContent().then(
         function textContentResolved(textContent) {
-          self.pageText[pageIndex] = textContent;
+          self.pageText[pageIndex] = textContent.join('');
           self.search();
           if ((pageIndex + 1) < self.pages.length)
             extractPageText(pageIndex + 1);
         }
       );
     }
     extractPageText(0);
   },
@@ -1159,26 +1160,26 @@ var PDFView = {
 
   afterPrint: function pdfViewSetupAfterPrint() {
     var div = document.getElementById('printContainer');
     while (div.hasChildNodes())
       div.removeChild(div.lastChild);
   },
 
   fullscreen: function pdfViewFullscreen() {
-    var isFullscreen = document.fullscreen || document.mozFullScreen ||
+    var isFullscreen = document.fullscreenElement || document.mozFullScreen ||
         document.webkitIsFullScreen;
 
     if (isFullscreen) {
       return false;
     }
 
     var wrapper = document.getElementById('viewerContainer');
-    if (document.documentElement.requestFullScreen) {
-      wrapper.requestFullScreen();
+    if (document.documentElement.requestFullscreen) {
+      wrapper.requestFullscreen();
     } else if (document.documentElement.mozRequestFullScreen) {
       wrapper.mozRequestFullScreen();
     } else if (document.documentElement.webkitRequestFullScreen) {
       wrapper.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
     } else {
       return false;
     }
 
@@ -1212,19 +1213,17 @@ var PDFView = {
 
     for (var i = 0, l = this.thumbnails.length; i < l; i++) {
       var thumb = this.thumbnails[i];
       thumb.updateRotation(this.pageRotation);
     }
 
     var currentPage = this.pages[this.page - 1];
 
-    if (this.isFullscreen) {
-      this.parseScale('page-fit', true);
-    }
+    this.parseScale(this.currentScaleValue, true);
 
     this.renderHighestPriority();
 
     // Wait for fullscreen to take effect
     setTimeout(function() {
       currentPage.scrollIntoView();
     }, 0);
   }
@@ -1237,16 +1236,18 @@ var PageView = function pageView(contain
 
   this.rotation = 0;
   this.scale = scale || 1.0;
   this.viewport = this.pdfPage.getViewport(this.scale, this.pdfPage.rotate);
 
   this.renderingState = RenderingStates.INITIAL;
   this.resume = null;
 
+  this.textContent = null;
+
   var anchor = document.createElement('a');
   anchor.name = '' + this.id;
 
   var div = this.el = document.createElement('div');
   div.id = 'pageContainer' + this.id;
   div.className = 'page';
   div.style.width = this.viewport.width + 'px';
   div.style.height = this.viewport.height + 'px';
@@ -1457,16 +1458,23 @@ var PageView = function pageView(contain
         var y = Math.min(boundingRect[0][1], boundingRect[1][1]);
         var width = Math.abs(boundingRect[0][0] - boundingRect[1][0]);
         var height = Math.abs(boundingRect[0][1] - boundingRect[1][1]);
 
         scrollIntoView(div, {left: x, top: y, width: width, height: height});
       }, 0);
   };
 
+  this.getTextContent = function pageviewGetTextContent() {
+    if (!this.textContent) {
+      this.textContent = this.pdfPage.getTextContent();
+    }
+    return this.textContent;
+  };
+
   this.draw = function pageviewDraw(callback) {
     if (this.renderingState !== RenderingStates.INITIAL)
       error('Must be in new state before drawing');
 
     this.renderingState = RenderingStates.RUNNING;
 
     var canvas = document.createElement('canvas');
     canvas.id = 'page' + this.id;
@@ -1537,16 +1545,24 @@ var PageView = function pageView(contain
       function pdfPageRenderCallback() {
         pageViewDrawCallback(null);
       },
       function pdfPageRenderError(error) {
         pageViewDrawCallback(error);
       }
     );
 
+    if (textLayer) {
+      this.getTextContent().then(
+        function textContentResolved(textContent) {
+          textLayer.setTextContent(textContent);
+        }
+      );
+    }
+
     setupAnnotations(this.pdfPage, this.viewport);
     div.setAttribute('data-loaded', true);
   };
 
   this.beforePrint = function pageViewBeforePrint() {
     var pdfPage = this.pdfPage;
     var viewport = pdfPage.getViewport(1);
 
@@ -1827,102 +1843,125 @@ var CustomStyle = (function CustomStyleC
     if (prop != 'undefined')
       element.style[prop] = str;
   };
 
   return CustomStyle;
 })();
 
 var TextLayerBuilder = function textLayerBuilder(textLayerDiv) {
+  var textLayerFrag = document.createDocumentFragment();
   this.textLayerDiv = textLayerDiv;
+  this.layoutDone = false;
+  this.divContentDone = false;
 
   this.beginLayout = function textLayerBuilderBeginLayout() {
     this.textDivs = [];
     this.textLayerQueue = [];
   };
 
   this.endLayout = function textLayerBuilderEndLayout() {
+    this.layoutDone = true;
+    this.insertDivContent();
+  },
+
+  this.renderLayer = function textLayerBuilderRenderLayer() {
     var self = this;
     var textDivs = this.textDivs;
     var textLayerDiv = this.textLayerDiv;
-    var renderTimer = null;
-    var renderingDone = false;
-    var renderInterval = 0;
-    var resumeInterval = 500; // in ms
-
     var canvas = document.createElement('canvas');
     var ctx = canvas.getContext('2d');
 
-    // Render the text layer, one div at a time
-    function renderTextLayer() {
-      if (textDivs.length === 0) {
-        clearInterval(renderTimer);
-        renderingDone = true;
-        self.textLayerDiv = textLayerDiv = canvas = ctx = null;
-        return;
-      }
+    // No point in rendering so many divs as it'd make the browser unusable
+    // even after the divs are rendered
+    if (textDivs.length > 100000)
+      return;
+
+    while (textDivs.length > 0) {
       var textDiv = textDivs.shift();
-      if (textDiv.dataset.textLength > 0) {
-        textLayerDiv.appendChild(textDiv);
+      textLayerFrag.appendChild(textDiv);
 
-        if (textDiv.dataset.textLength > 1) { // avoid div by zero
-          // Adjust div width to match canvas text
-
-          ctx.font = textDiv.style.fontSize + ' sans-serif';
-          var width = ctx.measureText(textDiv.textContent).width;
+      ctx.font = textDiv.style.fontSize + ' ' + textDiv.style.fontFamily;
+      var width = ctx.measureText(textDiv.textContent).width;
 
-          var textScale = textDiv.dataset.canvasWidth / width;
+      if (width > 0) {
+        var textScale = textDiv.dataset.canvasWidth / width;
 
-          CustomStyle.setProp('transform' , textDiv,
-            'scale(' + textScale + ', 1)');
-          CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%');
-        }
-      } // textLength > 0
+        CustomStyle.setProp('transform' , textDiv,
+          'scale(' + textScale + ', 1)');
+        CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%');
+      }
     }
-    renderTimer = setInterval(renderTextLayer, renderInterval);
 
-    // Stop rendering when user scrolls. Resume after XXX milliseconds
-    // of no scroll events
-    var scrollTimer = null;
-    function textLayerOnScroll() {
-      if (renderingDone) {
-        window.removeEventListener('scroll', textLayerOnScroll, false);
-        return;
-      }
+    textLayerDiv.appendChild(textLayerFrag);
+  };
 
-      // Immediately pause rendering
-      clearInterval(renderTimer);
+  this.setupRenderLayoutTimer = function textLayerSetupRenderLayoutTimer() {
+    // Schedule renderLayout() if user has been scrolling, otherwise
+    // run it right away
+    var kRenderDelay = 200; // in ms
+    var self = this;
+    if (Date.now() - PDFView.lastScroll > kRenderDelay) {
+      // Render right away
+      this.renderLayer();
+    } else {
+      // Schedule
+      if (this.renderTimer)
+        clearTimeout(this.renderTimer);
+      this.renderTimer = setTimeout(function() {
+        self.setupRenderLayoutTimer();
+      }, kRenderDelay);
+    }
+  };
 
-      clearTimeout(scrollTimer);
-      scrollTimer = setTimeout(function textLayerScrollTimer() {
-        // Resume rendering
-        renderTimer = setInterval(renderTextLayer, renderInterval);
-      }, resumeInterval);
-    } // textLayerOnScroll
-
-    window.addEventListener('scroll', textLayerOnScroll, false);
-  }; // endLayout
-
-  this.appendText = function textLayerBuilderAppendText(text,
-                                                        fontName, fontSize) {
+  this.appendText = function textLayerBuilderAppendText(fontName, fontSize,
+                                                        geom) {
     var textDiv = document.createElement('div');
 
     // vScale and hScale already contain the scaling to pixel units
-    var fontHeight = fontSize * text.geom.vScale;
-    textDiv.dataset.canvasWidth = text.canvasWidth * text.geom.hScale;
+    var fontHeight = fontSize * geom.vScale;
+    textDiv.dataset.canvasWidth = geom.canvasWidth * geom.hScale;
     textDiv.dataset.fontName = fontName;
 
     textDiv.style.fontSize = fontHeight + 'px';
-    textDiv.style.left = text.geom.x + 'px';
-    textDiv.style.top = (text.geom.y - fontHeight) + 'px';
-    textDiv.textContent = PDFJS.bidi(text, -1);
-    textDiv.dir = text.direction;
-    textDiv.dataset.textLength = text.length;
+    textDiv.style.fontFamily = fontName;
+    textDiv.style.left = geom.x + 'px';
+    textDiv.style.top = (geom.y - fontHeight) + 'px';
+
+    // The content of the div is set in the `setTextContent` function.
+
     this.textDivs.push(textDiv);
   };
+
+  this.insertDivContent = function textLayerUpdateTextContent() {
+    // Only set the content of the divs once layout has finished, the content
+    // for the divs is available and content is not yet set on the divs.
+    if (!this.layoutDone || this.divContentDone || !this.textContent)
+      return;
+
+    this.divContentDone = true;
+
+    var textDivs = this.textDivs;
+    var bidiTexts = this.textContent.bidiTexts;
+
+    for (var i = 0; i < bidiTexts.length; i++) {
+      var bidiText = bidiTexts[i];
+      var textDiv = textDivs[i];
+
+      textDiv.textContent = bidiText.str;
+      textDiv.dir = bidiText.ltr ? 'ltr' : 'rtl';
+    }
+
+    this.setupRenderLayoutTimer();
+  };
+
+  this.setTextContent = function textLayerBuilderSetTextContent(textContent) {
+    this.textContent = textContent;
+    this.insertDivContent();
+  };
 };
 
 document.addEventListener('DOMContentLoaded', function webViewerLoad(evt) {
   PDFView.initialize();
   var params = PDFView.parseQueryString(document.location.search.substring(1));
 
   var file = window.location.toString()
 
@@ -2054,17 +2093,17 @@ document.addEventListener('DOMContentLoa
     });
 
   document.getElementById('download').addEventListener('click',
     function() {
       PDFView.download();
     });
 
   document.getElementById('searchTermsInput').addEventListener('keydown',
-    function() {
+    function(event) {
       if (event.keyCode == 13) {
         PDFView.search();
       }
     });
 
   document.getElementById('pageNumber').addEventListener('change',
     function() {
       PDFView.page = this.value;
@@ -2277,16 +2316,17 @@ window.addEventListener('keydown', funct
   if (cmd == 1 || cmd == 8) { // either CTRL or META key.
     switch (evt.keyCode) {
       case 61: // FF/Mac '='
       case 107: // FF '+' and '='
       case 187: // Chrome '+'
         PDFView.zoomIn();
         handled = true;
         break;
+      case 173: // FF/Mac '-'
       case 109: // FF '-'
       case 189: // Chrome '-'
         PDFView.zoomOut();
         handled = true;
         break;
       case 48: // '0'
         PDFView.parseScale(kDefaultScale, true);
         handled = true;
@@ -2357,17 +2397,17 @@ window.addEventListener('beforeprint', f
 });
 
 window.addEventListener('afterprint', function afterPrint(evt) {
   PDFView.afterPrint();
 });
 
 (function fullscreenClosure() {
   function fullscreenChange(e) {
-    var isFullscreen = document.fullscreen || document.mozFullScreen ||
+    var isFullscreen = document.fullscreenElement || document.mozFullScreen ||
         document.webkitIsFullScreen;
 
     if (!isFullscreen) {
       PDFView.exitFullscreen();
     }
   }
 
   window.addEventListener('fullscreenchange', fullscreenChange, false);
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -393,22 +393,23 @@ social.error.tryAgain.accesskey=T
 social.error.ok.label=OK
 social.error.ok.accesskey=O
 social.error.closeSidebar.label=Close This Sidebar
 social.error.closeSidebar.accesskey=C
 
 # Identity notifications popups
 identity.termsOfService = Terms of Service
 identity.privacyPolicy = Privacy Policy
+# LOCALIZATION NOTE (identity.chooseIdentity.description): %S is the website origin (e.g. https://www.mozilla.org) shown in popup notifications.
 identity.chooseIdentity.description = Sign in to %S
 identity.chooseIdentity.label = Use an existing email
 identity.newIdentity.label = Use a different email
 identity.newIdentity.accessKey = e
 identity.newIdentity.email.placeholder = Email
-# LOCALIZATION NOTE (identity.newIdentity.description, identity.chooseIdentity.description): %S is the website origin (ie. https://www.mozilla.org) shown in popup notifications.
+# LOCALIZATION NOTE (identity.newIdentity.description): %S is the website origin (e.g. https://www.mozilla.org) shown in popup notifications.
 identity.newIdentity.description = Enter your email address to sign in to %S
 identity.next.label = Next
 identity.next.accessKey = n
 # LOCALIZATION NOTE: shown in the popup notification when a user successfully logs into a website
-# LOCALIZATION NOTE (identity.loggedIn.description): %S is the website origin (ie. https://www.mozilla.org)
+# LOCALIZATION NOTE (identity.loggedIn.description): %S is the user's identity (e.g. user@example.com)
 identity.loggedIn.description = Signed in as: %S
 identity.loggedIn.signOut.label = Sign Out
 identity.loggedIn.signOut.accessKey = O
--- a/browser/locales/en-US/pdfviewer/viewer.properties
+++ b/browser/locales/en-US/pdfviewer/viewer.properties
@@ -13,18 +13,18 @@ page_of=of {{pageCount}}
 
 zoom_out.title=Zoom Out
 zoom_out_label=Zoom Out
 zoom_in.title=Zoom In
 zoom_in_label=Zoom In
 zoom.title=Zoom
 print.title=Print
 print_label=Print
-fullscreen.title=Fullscreen
-fullscreen_label=Fullscreen
+presentation_mode.title=Switch to Presentation Mode
+presentation_mode_label=Presentation Mode
 open_file.title=Open File
 open_file_label=Open
 download.title=Download
 download_label=Download
 bookmark.title=Current view (copy or open in new window)
 bookmark_label=Current View
 
 # Tooltips and alt text for side panel toolbar buttons
@@ -46,18 +46,18 @@ no_outline=No Outline Available
 # LOCALIZATION NOTE (thumb_page_title): "{{page}}" will be replaced by the page
 # number.
 thumb_page_title=Page {{page}}
 # LOCALIZATION NOTE (thumb_page_canvas): "{{page}}" will be replaced by the page
 # number.
 thumb_page_canvas=Thumbnail of Page {{page}}
 
 # Context menu
-page_rotate_cw=Rotate Clockwise
-page_rotate_ccw=Rotate Counter-Clockwise
+page_rotate_cw.label=Rotate Clockwise
+page_rotate_ccw.label=Rotate Counter-Clockwise
 
 # Search panel button title and messages
 search=Find
 search_terms_not_found=(Not found)
 
 # Error panel labels
 error_more_info=More Information
 error_less_info=Less Information
--- a/browser/themes/gnomestripe/browser.css
+++ b/browser/themes/gnomestripe/browser.css
@@ -2697,27 +2697,27 @@ html|*#gcli-output-frame {
   font-size: 12px;
 }
 
 #social-statusarea-user-portrait {
   width: 32px;
   height: 32px;
   border-radius: 2px;
   margin: 10px;
-  list-style-image: url("chrome://browser/skin/social/social.png");
 }
 
 #social-statusarea-username {
   -moz-appearance: none;
   background: transparent;
   border: none;
   color: -moz-nativehyperlinktext;
   cursor: pointer;
   min-width: 0;
   margin: 0 6px;
+  list-style-image: none;
 }
 #social-statusarea-username:hover {
   text-decoration: underline;
 }
 
 .social-panel > .panel-arrowcontainer > .panel-arrowcontent {
   padding: 0;
 }
@@ -2761,16 +2761,17 @@ html|*#gcli-output-frame {
 
 .chat-titlebar {
   background-color: #d9d9d9;
   height: 20px;
   min-height: 20px;
   width: 100%;
   margin: 0;
   padding: 2px;
+  -moz-padding-start: 6px;
   border: none;
   border-bottom: 1px solid gray;
   cursor: pointer;
 }
 
 .chat-titlebar[minimized="true"] {
   border-bottom: none;
 }
--- a/browser/themes/pinstripe/browser.css
+++ b/browser/themes/pinstripe/browser.css
@@ -3377,25 +3377,25 @@ html|*#gcli-output-frame {
   font-family: "lucida grande",tahoma,verdana,arial,sans-serif;
   font-size: 12px;
 }
 
 #social-statusarea-user-portrait {
   width: 32px;
   height: 32px;
   margin: 10px;
-  list-style-image: url("chrome://browser/skin/social/social.png");
 }
 
 #social-statusarea-username {
   -moz-appearance: none;
   color: -moz-nativehyperlinktext;
   cursor: pointer;
   min-width: 0;
   margin: 0 6px;
+  list-style-image: none;
 }
 
 #social-statusarea-username:hover {
   text-decoration: underline;
 }
 
 .social-panel > .panel-arrowcontainer > .panel-arrowcontent {
   padding: 0;
@@ -3447,16 +3447,17 @@ html|*#gcli-output-frame {
 
 .chat-titlebar {
   background-color: #d9d9d9;
   height: 20px;
   min-height: 20px;
   width: 100%;
   margin: 0;
   padding: 2px;
+  -moz-padding-start: 6px;
   border: none;
   border-bottom: 1px solid #404040;
   cursor: pointer;
 }
 
 .chat-titlebar[minimized="true"] {
   border-bottom: none;
 }
--- a/browser/themes/pinstripe/jar.mn
+++ b/browser/themes/pinstripe/jar.mn
@@ -78,17 +78,16 @@ browser.jar:
   skin/classic/browser/places/toolbar.png                   (places/toolbar.png)
   skin/classic/browser/places/toolbarDropMarker.png         (places/toolbarDropMarker.png)
   skin/classic/browser/places/folderDropArrow.png           (places/folderDropArrow.png)
   skin/classic/browser/places/editBookmarkOverlay.css       (places/editBookmarkOverlay.css)
   skin/classic/browser/places/minus.png                     (places/minus.png)
   skin/classic/browser/places/minus-active.png              (places/minus-active.png)
   skin/classic/browser/places/plus.png                      (places/plus.png)
   skin/classic/browser/places/plus-active.png               (places/plus-active.png)
-  skin/classic/browser/places/starPage.png                  (places/starPage.png)
   skin/classic/browser/places/starred48.png                 (places/starred48.png)
   skin/classic/browser/places/unstarred48.png               (places/unstarred48.png)
   skin/classic/browser/places/unfiledBookmarks.png          (places/unfiledBookmarks.png)
   skin/classic/browser/places/twisty-open.gif               (places/twisty-open.gif)
   skin/classic/browser/places/twisty-closed.gif             (places/twisty-closed.gif)
   skin/classic/browser/places/tag.png                       (places/tag.png)
   skin/classic/browser/places/downloads.png                 (places/downloads.png)
   skin/classic/browser/places/expander-closed-active.png    (places/expander-closed-active.png)
deleted file mode 100644
index 569dd45cf0c72d53c7fdb8626681965898b35098..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/winstripe/browser.css
+++ b/browser/themes/winstripe/browser.css
@@ -3396,27 +3396,27 @@ html|*#gcli-output-frame {
   font-size: 12px;
 }
 
 #social-statusarea-user-portrait {
   width: 32px;
   height: 32px;
   border-radius: 2px;
   margin: 10px;
-  list-style-image: url("chrome://browser/skin/social/social.png");
 }
 
 #social-statusarea-username {
   -moz-appearance: none;
   background: transparent;
   border: none;
   color: -moz-nativehyperlinktext;
   cursor: pointer;
   min-width: 0;
   margin: 0 6px;
+  list-style-image: none;
 }
 #social-statusarea-username:hover {
   text-decoration: underline;
 }
 
 .social-panel > .panel-arrowcontainer > .panel-arrowcontent {
   padding: 0;
 }
@@ -3465,16 +3465,17 @@ html|*#gcli-output-frame {
 
 .chat-titlebar {
   background-color: #c4cfde;
   height: 20px;
   min-height: 20px;
   width: 100%;
   margin: 0;
   padding: 2px;
+  -moz-padding-start: 6px;
   border: none;
   border-bottom: 1px solid gray;
   cursor: pointer;
 }
 
 .chat-titlebar[minimized="true"] {
   border-bottom: none;
 }
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -24,16 +24,22 @@ SCRIPT_DIR = os.path.abspath(os.path.rea
 sys.path.insert(0, SCRIPT_DIR)
 import automationutils
 
 _DEFAULT_WEB_SERVER = "127.0.0.1"
 _DEFAULT_HTTP_PORT = 8888
 _DEFAULT_SSL_PORT = 4443
 _DEFAULT_WEBSOCKET_PORT = 9988
 
+# from nsIPrincipal.idl
+_APP_STATUS_NOT_INSTALLED = 0
+_APP_STATUS_INSTALLED     = 1
+_APP_STATUS_PRIVILEGED    = 2
+_APP_STATUS_CERTIFIED     = 3
+
 #expand _DIST_BIN = __XPC_BIN_PATH__
 #expand _IS_WIN32 = len("__WIN32__") != 0
 #expand _IS_MAC = __IS_MAC__ != 0
 #expand _IS_LINUX = __IS_LINUX__ != 0
 #ifdef IS_CYGWIN
 #expand _IS_CYGWIN = __IS_CYGWIN__ == 1
 #else
 _IS_CYGWIN = False
@@ -292,17 +298,18 @@ class Automation(object):
 
   def setupTestApps(self, profileDir, apps):
     webappJSONTemplate = Template(""""$name": {
 "origin": "$origin",
 "installOrigin": "$origin",
 "receipt": null,
 "installTime": 132333986000,
 "manifestURL": "$manifestURL",
-"localId": $localId
+"localId": $localId,
+"appStatus": $appStatus
 }""")
 
     manifestTemplate = Template("""{
   "name": "$name",
   "description": "$description",
   "launch_path": "/",
   "developer": {
     "name": "Mozilla",
@@ -515,41 +522,60 @@ user_pref("camino.use_system_proxy_setti
     prefsFile.write("".join(prefs))
     prefsFile.close()
 
     apps = [
       {
         'name': 'http_example_org',
         'origin': 'http://example.org',
         'manifestURL': 'http://example.org/manifest.webapp',
-        'description': 'http://example.org App'
+        'description': 'http://example.org App',
+        'appStatus': _APP_STATUS_INSTALLED
       },
       {
         'name': 'https_example_com',
         'origin': 'https://example.com',
         'manifestURL': 'https://example.com/manifest.webapp',
-        'description': 'https://example.com App'
+        'description': 'https://example.com App',
+        'appStatus': _APP_STATUS_INSTALLED
       },
       {
         'name': 'http_test1_example_org',
         'origin': 'http://test1.example.org',
         'manifestURL': 'http://test1.example.org/manifest.webapp',
-        'description': 'http://test1.example.org App'
+        'description': 'http://test1.example.org App',
+        'appStatus': _APP_STATUS_INSTALLED
       },
       {
         'name': 'http_test1_example_org_8000',
         'origin': 'http://test1.example.org:8000',
         'manifestURL': 'http://test1.example.org:8000/manifest.webapp',
-        'description': 'http://test1.example.org:8000 App'
+        'description': 'http://test1.example.org:8000 App',
+        'appStatus': _APP_STATUS_INSTALLED
       },
       {
         'name': 'http_sub1_test1_example_org',
         'origin': 'http://sub1.test1.example.org',
         'manifestURL': 'http://sub1.test1.example.org/manifest.webapp',
-        'description': 'http://sub1.test1.example.org App'
+        'description': 'http://sub1.test1.example.org App',
+        'appStatus': _APP_STATUS_INSTALLED
+      },
+      {
+        'name': 'https_example_com_privileged',
+        'origin': 'https://example.com',
+        'manifestURL': 'https://example.com/manifest_priv.webapp',
+        'description': 'https://example.com Privileged App',
+        'appStatus': _APP_STATUS_PRIVILEGED
+      },
+      {
+        'name': 'https_example_com_certified',
+        'origin': 'https://example.com',
+        'manifestURL': 'https://example.com/manifest_cert.webapp',
+        'description': 'https://example.com Certified App',
+        'appStatus': _APP_STATUS_CERTIFIED
       },
     ];
     self.setupTestApps(profileDir, apps)
 
   def addCommonOptions(self, parser):
     "Adds command-line options which are common to mochitest and reftest."
 
     parser.add_option("--setpref",
--- a/build/leaktest.py.in
+++ b/build/leaktest.py.in
@@ -41,12 +41,10 @@ if __name__ == '__main__':
     automation.initializeProfile(PROFILE_DIRECTORY)
     browserEnv = automation.environment()
 
     if not "XPCOM_DEBUG_BREAK" in browserEnv:
         browserEnv["XPCOM_DEBUG_BREAK"] = "stack"
     url = "http://localhost:%d/bloatcycle.html" % PORT
     appPath = os.path.join(SCRIPT_DIR, automation.DEFAULT_APP)
     status = automation.runApp(url, browserEnv, appPath, PROFILE_DIRECTORY,
-                               # leaktest builds are slow, give up and
-                               # don't use a timeout.
-                               extraArgs, timeout=None)
+                               extraArgs, timeout=1800)
     sys.exit(status)
--- a/caps/idl/nsIPrincipal.idl
+++ b/caps/idl/nsIPrincipal.idl
@@ -16,17 +16,17 @@ struct JSPrincipals;
 
 interface nsIURI;
 interface nsIContentSecurityPolicy;
 
 [ptr] native JSContext(JSContext);
 [ptr] native JSPrincipals(JSPrincipals);
 [ptr] native PrincipalArray(nsTArray<nsCOMPtr<nsIPrincipal> >);
 
-[scriptable, uuid(825ffce8-962d-11e1-aef3-8f2b6188709b)]
+[scriptable, uuid(115d1081-373e-4837-8d12-d0f4874f3467)]
 interface nsIPrincipal : nsISerializable
 {
     /**
      * Values of capabilities for each principal. Order is
      * significant: if an operation is performed on a set
      * of capabilities, the minimum is computed.
      */
     const short ENABLE_DENIED                = 1;
@@ -268,16 +268,23 @@ interface nsIPrincipal : nsISerializable
      * app.
      */
     readonly attribute unsigned long appId;
 
     /**
      * Returns true iif the principal is inside a browser element.
      */
     readonly attribute boolean isInBrowserElement;
+
+    /**
+     * Returns true if this principal has an unknown appId. This shouldn't
+     * generally be used. We only expose it due to not providing the correct
+     * appId everywhere where we construct principals.
+     */
+    readonly attribute boolean unknownAppId;
 };
 
 /**
  * If nsSystemPrincipal is too risky to use, but we want a principal to access 
  * more than one origin, nsExpandedPrincipals letting us define an array of 
  * principals it subsumes. So script with an nsExpandedPrincipals will gain
  * same origin access when at least one of its principals it contains gained 
  * sameorigin acccess. An nsExpandedPrincipal will be subsumed by the system
--- a/caps/include/nsPrincipal.h
+++ b/caps/include/nsPrincipal.h
@@ -126,16 +126,17 @@ public:
   NS_IMETHOD GetOrigin(char** aOrigin);
   NS_IMETHOD Subsumes(nsIPrincipal* other, bool* _retval);
   NS_IMETHOD SubsumesIgnoringDomain(nsIPrincipal* other, bool* _retval);
   NS_IMETHOD CheckMayLoad(nsIURI* uri, bool report, bool allowIfInheritsPrincipal);
   NS_IMETHOD GetExtendedOrigin(nsACString& aExtendedOrigin);
   NS_IMETHOD GetAppStatus(uint16_t* aAppStatus);
   NS_IMETHOD GetAppId(uint32_t* aAppStatus);
   NS_IMETHOD GetIsInBrowserElement(bool* aIsInBrowserElement);
+  NS_IMETHOD GetUnknownAppId(bool* aUnknownAppId);
 #ifdef DEBUG
   virtual void dumpImpl();
 #endif
 
   nsPrincipal();
 
   // Either Init() or InitFromPersistent() must be called before
   // the principal is in a usable state.
@@ -222,16 +223,17 @@ public:
   NS_IMETHOD GetOrigin(char** aOrigin);
   NS_IMETHOD Subsumes(nsIPrincipal* other, bool* _retval);
   NS_IMETHOD SubsumesIgnoringDomain(nsIPrincipal* other, bool* _retval);
   NS_IMETHOD CheckMayLoad(nsIURI* uri, bool report, bool allowIfInheritsPrincipal);
   NS_IMETHOD GetExtendedOrigin(nsACString& aExtendedOrigin);
   NS_IMETHOD GetAppStatus(uint16_t* aAppStatus);
   NS_IMETHOD GetAppId(uint32_t* aAppStatus);
   NS_IMETHOD GetIsInBrowserElement(bool* aIsInBrowserElement);
+  NS_IMETHOD GetUnknownAppId(bool* aUnknownAppId);
 #ifdef DEBUG
   virtual void dumpImpl();
 #endif
   
   virtual void GetScriptLocation(nsACString &aStr) MOZ_OVERRIDE;
 
 private:
   nsTArray< nsCOMPtr<nsIPrincipal> > mPrincipals;
--- a/caps/src/nsNullPrincipal.cpp
+++ b/caps/src/nsNullPrincipal.cpp
@@ -355,16 +355,23 @@ nsNullPrincipal::GetAppId(uint32_t* aApp
 
 NS_IMETHODIMP
 nsNullPrincipal::GetIsInBrowserElement(bool* aIsInBrowserElement)
 {
   *aIsInBrowserElement = false;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsNullPrincipal::GetUnknownAppId(bool* aUnknownAppId)
+{
+  *aUnknownAppId = false;
+  return NS_OK;
+}
+
 /**
  * nsISerializable implementation
  */
 NS_IMETHODIMP
 nsNullPrincipal::Read(nsIObjectInputStream* aStream)
 {
   // no-op: CID is sufficient to create a useful nsNullPrincipal, since the URI
   // is not really relevant.
--- a/caps/src/nsPrincipal.cpp
+++ b/caps/src/nsPrincipal.cpp
@@ -1107,16 +1107,23 @@ nsPrincipal::GetAppId(uint32_t* aAppId)
 NS_IMETHODIMP
 nsPrincipal::GetIsInBrowserElement(bool* aIsInBrowserElement)
 {
   *aIsInBrowserElement = mInMozBrowser;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsPrincipal::GetUnknownAppId(bool* aUnknownAppId)
+{
+  *aUnknownAppId = mAppId == nsIScriptSecurityManager::UNKNOWN_APP_ID;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsPrincipal::Read(nsIObjectInputStream* aStream)
 {
   bool hasCapabilities;
   nsresult rv = aStream->ReadBoolean(&hasCapabilities);
   if (NS_SUCCEEDED(rv) && hasCapabilities) {
     mCapabilities = new nsHashtable(aStream, ReadAnnotationEntry,
                                     FreeAnnotationEntry, &rv);
     NS_ENSURE_TRUE(mCapabilities, NS_ERROR_OUT_OF_MEMORY);
@@ -1530,16 +1537,23 @@ nsExpandedPrincipal::GetAppId(uint32_t* 
 }
 
 NS_IMETHODIMP
 nsExpandedPrincipal::GetIsInBrowserElement(bool* aIsInBrowserElement)
 {
   return NS_ERROR_NOT_AVAILABLE;
 }
 
+NS_IMETHODIMP
+nsExpandedPrincipal::GetUnknownAppId(bool* aUnknownAppId)
+{
+  *aUnknownAppId = false;
+  return NS_OK;
+}
+
 void
 nsExpandedPrincipal::GetScriptLocation(nsACString& aStr)
 {
   if (mCert) {
     aStr.Assign(mCert->fingerprint);
   } else {
     // Is that a good idea to list it's principals?
     aStr.Assign(EXPANDED_PRINCIPAL_SPEC);
--- a/caps/src/nsScriptSecurityManager.cpp
+++ b/caps/src/nsScriptSecurityManager.cpp
@@ -56,16 +56,20 @@
 #include "nsIChromeRegistry.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/StandardInteger.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/StaticPtr.h"
+#include "nsContentUtils.h"
+
+// This should be probably defined on some other place... but I couldn't find it
+#define WEBAPPS_PERM_NAME "webapps-manage"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
 
 nsIIOService    *nsScriptSecurityManager::sIOService = nullptr;
 nsIXPConnect    *nsScriptSecurityManager::sXPConnect = nullptr;
@@ -1398,17 +1402,35 @@ nsScriptSecurityManager::CheckLoadURIWit
         if (sourceURI == aTargetURI) {
             return NS_OK;
         }
     }
     else if (targetScheme.Equals(sourceScheme,
                                  nsCaseInsensitiveCStringComparator()))
     {
         // every scheme can access another URI from the same scheme,
-        // as long as they don't represent null principals.
+        // as long as they don't represent null principals...
+        // Or they don't require an special permission to do so
+        // See bug#773886
+
+        bool hasFlags;
+        rv = NS_URIChainHasFlags(targetBaseURI,
+                                 nsIProtocolHandler::URI_CROSS_ORIGIN_NEEDS_WEBAPPS_PERM,
+                                 &hasFlags);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        if (hasFlags) {
+            // In this case, we allow opening only if the source and target URIS
+            // are on the same domain, or the opening URI has the webapps
+            // permision granted
+            if (!SecurityCompareURIs(sourceBaseURI,targetBaseURI) &&
+                !nsContentUtils::IsExactSitePermAllow(aPrincipal,WEBAPPS_PERM_NAME)){
+                return NS_ERROR_DOM_BAD_URI;
+            }
+        }
         return NS_OK;
     }
 
     // If the schemes don't match, the policy is specified by the protocol
     // flags on the target URI.  Note that the order of policy checks here is
     // very important!  We start from most restrictive and work our way down.
     // Note that since we're working with the innermost URI, we can just use
     // the methods that work on chains of nested URIs and they will only look
--- a/caps/src/nsSystemPrincipal.cpp
+++ b/caps/src/nsSystemPrincipal.cpp
@@ -260,16 +260,23 @@ nsSystemPrincipal::GetAppId(uint32_t* aA
 
 NS_IMETHODIMP
 nsSystemPrincipal::GetIsInBrowserElement(bool* aIsInBrowserElement)
 {
   *aIsInBrowserElement = false;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsSystemPrincipal::GetUnknownAppId(bool* aUnknownAppId)
+{
+  *aUnknownAppId = false;
+  return NS_OK;
+}
+
 //////////////////////////////////////////
 // Methods implementing nsISerializable //
 //////////////////////////////////////////
 
 NS_IMETHODIMP
 nsSystemPrincipal::Read(nsIObjectInputStream* aStream)
 {
     // no-op: CID is sufficient to identify the mSystemPrincipal singleton
--- a/configure.in
+++ b/configure.in
@@ -2293,17 +2293,16 @@ ia64*-hpux*)
             CFLAGS="$CFLAGS -mstackrealign -fno-keep-inline-dllexport"
             CXXFLAGS="$CXXFLAGS -mstackrealign -fno-keep-inline-dllexport"
             LDFLAGS="$LDFLAGS -Wl,--enable-stdcall-fixup"
         else
             AC_DEFINE(HAVE_STDCALL)
             DSO_LDOPTS="$DSO_LDOPTS -MACHINE:X86"
         fi
 
-        MOZ_CHECK_HEADERS(mmintrin.h)
     	AC_DEFINE(_X86_)
 	;;
     x86_64-*)
         if test -n "$_WIN32_MSVC"; then
             DSO_LDOPTS="$DSO_LDOPTS -MACHINE:X64"
         fi
         AC_DEFINE(_AMD64_)
         ;;
@@ -2972,21 +2971,16 @@ MOZ_CHECK_COMMON_HEADERS
 
 dnl These are all the places some variant of statfs can be hiding.
 MOZ_CHECK_HEADERS(sys/statvfs.h sys/statfs.h sys/vfs.h sys/mount.h)
 
 dnl Quota support
 MOZ_CHECK_HEADERS(sys/quota.h sys/sysmacros.h)
 MOZ_CHECK_HEADERS(linux/quota.h)
 
-dnl Try for MMX support
-dnl NB - later gcc versions require -mmmx for this header to be successfully
-dnl included (or another option which implies it, such as -march=pentium-mmx)
-MOZ_CHECK_HEADERS(mmintrin.h)
-
 MOZ_CHECK_HEADERS(sys/types.h netinet/in.h byteswap.h)
 
 dnl Check whether the compiler supports the new-style C++ standard
 dnl library headers (i.e. <new>) or needs the old "new.h"
 AC_LANG_CPLUSPLUS
 NEW_H=new.h
 MOZ_CHECK_HEADER(new, [NEW_H=new])
 AC_DEFINE_UNQUOTED(NEW_H, <$NEW_H>)
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -565,16 +565,32 @@ public:
   static bool IsSitePermAllow(nsIPrincipal* aPrincipal, const char* aType);
 
   // Get a permission-manager setting for the given principal and type.
   // If the pref doesn't exist or if it isn't DENY_ACTION, false is
   // returned, otherwise true is returned. Always returns false for the
   // system principal, and true for a null principal.
   static bool IsSitePermDeny(nsIPrincipal* aPrincipal, const char* aType);
 
+  // Get a permission-manager setting for the given principal and type.
+  // If the pref doesn't exist or if it isn't ALLOW_ACTION, false is
+  // returned, otherwise true is returned. Always returns true for the
+  // system principal, and false for a null principal.
+  // This version checks the permission for an exact host match on
+  // the principal
+  static bool IsExactSitePermAllow(nsIPrincipal* aPrincipal, const char* aType);
+
+  // Get a permission-manager setting for the given principal and type.
+  // If the pref doesn't exist or if it isn't DENY_ACTION, false is
+  // returned, otherwise true is returned. Always returns false for the
+  // system principal, and true for a null principal.
+  // This version checks the permission for an exact host match on
+  // the principal
+  static bool IsExactSitePermDeny(nsIPrincipal* aPrincipal, const char* aType);
+
   // Returns true if aDoc1 and aDoc2 have equal NodePrincipal()s.
   static bool HaveEqualPrincipals(nsIDocument* aDoc1, nsIDocument* aDoc2);
 
   static nsILineBreaker* LineBreaker()
   {
     return sLineBreaker;
   }
 
--- a/content/base/src/nsContentSink.cpp
+++ b/content/base/src/nsContentSink.cpp
@@ -881,22 +881,18 @@ nsContentSink::SelectDocAppCache(nsIAppl
   *aAction = CACHE_SELECTION_NONE;
 
   nsCOMPtr<nsIApplicationCacheContainer> applicationCacheDocument =
     do_QueryInterface(mDocument);
   NS_ASSERTION(applicationCacheDocument,
                "mDocument must implement nsIApplicationCacheContainer.");
 
   if (aLoadApplicationCache) {
-    nsAutoCString groupID;
-    rv = aLoadApplicationCache->GetGroupID(groupID);
-    NS_ENSURE_SUCCESS(rv, rv);
-
     nsCOMPtr<nsIURI> groupURI;
-    rv = NS_NewURI(getter_AddRefs(groupURI), groupID);
+    rv = aLoadApplicationCache->GetManifestURI(getter_AddRefs(groupURI));
     NS_ENSURE_SUCCESS(rv, rv);
 
     bool equal = false;
     rv = groupURI->Equals(aManifestURI, &equal);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (!equal) {
       // This is a foreign entry, force a reload to avoid loading the foreign
@@ -970,21 +966,17 @@ nsContentSink::SelectDocAppCacheNoManife
         ("Selection, no manifest: assigning app cache %s to document %s", clientID.get(), docURISpec.get()));
 #endif
 
     rv = applicationCacheDocument->SetApplicationCache(aLoadApplicationCache);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Return the uri and invoke the update process for the selected
     // application cache.
-    nsAutoCString groupID;
-    rv = aLoadApplicationCache->GetGroupID(groupID);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = NS_NewURI(aManifestURI, groupID);
+    rv = aLoadApplicationCache->GetManifestURI(aManifestURI);
     NS_ENSURE_SUCCESS(rv, rv);
 
     *aAction = CACHE_SELECTION_UPDATE;
   }
 
   return NS_OK;
 }
 
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -2872,45 +2872,62 @@ nsContentUtils::IsDraggableImage(nsICont
 // static
 bool
 nsContentUtils::IsDraggableLink(const nsIContent* aContent) {
   nsCOMPtr<nsIURI> absURI;
   return aContent->IsLink(getter_AddRefs(absURI));
 }
 
 static bool
-TestSitePerm(nsIPrincipal* aPrincipal, const char* aType, uint32_t aPerm)
+TestSitePerm(nsIPrincipal* aPrincipal, const char* aType, uint32_t aPerm, bool aExactHostMatch)
 {
   if (!aPrincipal) {
     // We always deny (i.e. don't allow) the permission if we don't have a
     // principal.
     return aPerm != nsIPermissionManager::ALLOW_ACTION;
   }
 
   nsCOMPtr<nsIPermissionManager> permMgr =
     do_GetService("@mozilla.org/permissionmanager;1");
   NS_ENSURE_TRUE(permMgr, false);
 
   uint32_t perm;
-  nsresult rv = permMgr->TestPermissionFromPrincipal(aPrincipal, aType, &perm);
+  nsresult rv;
+  if (aExactHostMatch) {
+    rv = permMgr->TestExactPermissionFromPrincipal(aPrincipal, aType, &perm);
+  } else {
+    rv = permMgr->TestPermissionFromPrincipal(aPrincipal, aType, &perm);
+  }
   NS_ENSURE_SUCCESS(rv, false);
 
   return perm == aPerm;
 }
 
 bool
 nsContentUtils::IsSitePermAllow(nsIPrincipal* aPrincipal, const char* aType)
 {
-  return TestSitePerm(aPrincipal, aType, nsIPermissionManager::ALLOW_ACTION);
+  return TestSitePerm(aPrincipal, aType, nsIPermissionManager::ALLOW_ACTION, false);
 }
 
 bool
 nsContentUtils::IsSitePermDeny(nsIPrincipal* aPrincipal, const char* aType)
 {
-  return TestSitePerm(aPrincipal, aType, nsIPermissionManager::DENY_ACTION);
+  return TestSitePerm(aPrincipal, aType, nsIPermissionManager::DENY_ACTION, false);
+}
+
+bool
+nsContentUtils::IsExactSitePermAllow(nsIPrincipal* aPrincipal, const char* aType)
+{
+  return TestSitePerm(aPrincipal, aType, nsIPermissionManager::ALLOW_ACTION, true);
+}
+
+bool
+nsContentUtils::IsExactSitePermDeny(nsIPrincipal* aPrincipal, const char* aType)
+{
+  return TestSitePerm(aPrincipal, aType, nsIPermissionManager::DENY_ACTION, true);
 }
 
 static const char *gEventNames[] = {"event"};
 static const char *gSVGEventNames[] = {"evt"};
 // for b/w compat, the first name to onerror is still 'event', even though it
 // is actually the error message.  (pre this code, the other 2 were not avail.)
 // XXXmarkh - a quick lxr shows no affected code - should we correct this?
 static const char *gOnErrorNames[] = {"event", "source", "lineno"};
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -2389,19 +2389,16 @@ nsDocument::StartDocumentLoad(const char
   } else if (nsCRT::strcmp("external-resource", aCommand) == 0) {
     // Allow CSS, but not scripts
     ScriptLoader()->SetEnabled(false);
   }
 
   mMayStartLayout = false;
 
   mHaveInputEncoding = true;
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  nsresult rv = InitCSP(aChannel, getter_AddRefs(csp));
-  NS_ENSURE_SUCCESS(rv, rv);
 
   if (aReset) {
     Reset(aChannel, aLoadGroup);
   }
 
   nsAutoCString contentType;
   if (NS_SUCCEEDED(aChannel->GetContentType(contentType))) {
     // XXX this is only necessary for viewsource:
@@ -2421,150 +2418,202 @@ nsDocument::StartDocumentLoad(const char
   // to the document. These are immutable after being set here.
   nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(aContainer);
 
   if (docShell) {
     nsresult rv = docShell->GetSandboxFlags(&mSandboxFlags);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  if (csp) {
-    // Copy into principal
-    nsIPrincipal* principal = GetPrincipal();
-    principal->SetCsp(csp);
-#ifdef PR_LOGGING
-    PR_LOG(gCspPRLog, PR_LOG_DEBUG,
-           ("Inserted CSP into principal %p", principal));
-#endif
-  }
+  nsresult rv = InitCSP(aChannel);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
-nsDocument::InitCSP(nsIChannel* aChannel, nsIContentSecurityPolicy **aCSP)
-{
-  *aCSP = nullptr;
-  if (CSPService::sCSPEnabled) {
-    nsAutoCString tCspHeaderValue, tCspROHeaderValue;
-    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
-    if (!httpChannel) {
-      // no CSP for non http channels
-      return NS_OK;
-    }
+nsDocument::InitCSP(nsIChannel* aChannel)
+{
+  nsCOMPtr<nsIContentSecurityPolicy> csp;
+  if (!CSPService::sCSPEnabled) {
+#ifdef PR_LOGGING
+    PR_LOG(gCspPRLog, PR_LOG_DEBUG,
+           ("CSP is disabled, skipping CSP init for document %p", this));
+#endif
+    return NS_OK;
+  }
+
+  nsAutoCString tCspHeaderValue, tCspROHeaderValue;
+  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
+  if (httpChannel) {
     httpChannel->GetResponseHeader(
         NS_LITERAL_CSTRING("x-content-security-policy"),
         tCspHeaderValue);
 
     httpChannel->GetResponseHeader(
         NS_LITERAL_CSTRING("x-content-security-policy-report-only"),
         tCspROHeaderValue);
-    NS_ConvertASCIItoUTF16 cspHeaderValue(tCspHeaderValue);
-    NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue);
-
-    if (cspHeaderValue.IsEmpty() && cspROHeaderValue.IsEmpty()) {
-      // no CSP header present, stop processing
-      return NS_OK;
-    }
+  }
+  NS_ConvertASCIItoUTF16 cspHeaderValue(tCspHeaderValue);
+  NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue);
+
+  // ----- Figure out if we need to apply an app default CSP
+  bool applyAppDefaultCSP = false;
+  nsIPrincipal* principal = NodePrincipal();
+  PRUint16 appStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
+  bool unknownAppId;
+  if (NS_SUCCEEDED(principal->GetUnknownAppId(&unknownAppId)) &&
+      !unknownAppId &&
+      NS_SUCCEEDED(principal->GetAppStatus(&appStatus))) {
+    applyAppDefaultCSP = ( appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED ||
+                           appStatus == nsIPrincipal::APP_STATUS_CERTIFIED);
+  }
+#ifdef PR_LOGGING
+  else
+    PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("Failed to get app status from principal"));
+#endif
+
+  // If there's no CSP to apply go ahead and return early
+  if (!applyAppDefaultCSP &&
+      cspHeaderValue.IsEmpty() &&
+      cspROHeaderValue.IsEmpty()) {
+#ifdef PR_LOGGING
+    nsCOMPtr<nsIURI> chanURI;
+    aChannel->GetURI(getter_AddRefs(chanURI));
+    nsAutoCString aspec;
+    chanURI->GetAsciiSpec(aspec);
+    PR_LOG(gCspPRLog, PR_LOG_DEBUG,
+           ("no CSP for document, %s, %s",
+            aspec.get(),
+            applyAppDefaultCSP ? "is app" : "not an app"));
+#endif
+    return NS_OK;
+  }
 
 #ifdef PR_LOGGING
-    PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("CSP header specified for document %p", this));
+  PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("Document is an app or CSP header specified %p", this));
 #endif
 
-    nsresult rv;
-    nsCOMPtr<nsIContentSecurityPolicy> csp;
-    csp = do_CreateInstance("@mozilla.org/contentsecuritypolicy;1", &rv);
-
-    if (NS_FAILED(rv)) {
+  nsresult rv;
+  csp = do_CreateInstance("@mozilla.org/contentsecuritypolicy;1", &rv);
+
+  if (NS_FAILED(rv)) {
 #ifdef PR_LOGGING
-      PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("Failed to create CSP object: %x", rv));
+    PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("Failed to create CSP object: %x", rv));
 #endif
-      return rv;
-    }
-
-    // Store the request context for violation reports
-    csp->ScanRequestData(httpChannel);
-
-    // Start parsing the policy
-    nsCOMPtr<nsIURI> chanURI;
-    aChannel->GetURI(getter_AddRefs(chanURI));
-
+    return rv;
+  }
+
+  // used as a "self" identifier for the CSP.
+  nsCOMPtr<nsIURI> chanURI;
+  aChannel->GetURI(getter_AddRefs(chanURI));
+
+  // Store the request context for violation reports
+  csp->ScanRequestData(httpChannel);
+
+  // ----- process the app default policy, if necessary
+  if (applyAppDefaultCSP) {
+    nsAdoptingString appCSP;
+    if (appStatus ==  nsIPrincipal::APP_STATUS_PRIVILEGED) {
+      appCSP = Preferences::GetString("security.apps.privileged.CSP.default");
+      NS_ASSERTION(appCSP, "App, but no default CSP in security.apps.privileged.CSP.default");
+    } else if (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED) {
+      appCSP = Preferences::GetString("security.apps.certified.CSP.default");
+      NS_ASSERTION(appCSP, "App, but no default CSP in security.apps.certified.CSP.default");
+    }
+
+    if (appCSP)
+      csp->RefinePolicy(appCSP, chanURI);
+  }
+
+  // ----- if there's a full-strength CSP header, apply it.
+  if (!cspHeaderValue.IsEmpty()) {
+    // Need to tokenize the header value since multiple headers could be
+    // concatenated into one comma-separated list of policies.
+    // See RFC2616 section 4.2 (last paragraph)
+    nsCharSeparatedTokenizer tokenizer(cspHeaderValue, ',');
+    while (tokenizer.hasMoreTokens()) {
+        const nsSubstring& policy = tokenizer.nextToken();
+        csp->RefinePolicy(policy, chanURI);
 #ifdef PR_LOGGING
-    PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("CSP Loaded"));
+        {
+          PR_LOG(gCspPRLog, PR_LOG_DEBUG,
+                  ("CSP refined with policy: \"%s\"",
+                    NS_ConvertUTF16toUTF8(policy).get()));
+        }
 #endif
-
-    // ReportOnly mode is enabled *only* if there are no regular-strength CSP
-    // headers present.  If there are, then we ignore the ReportOnly mode and
-    // toss a warning into the error console, proceeding with enforcing the
-    // regular-strength CSP.
-    if (cspHeaderValue.IsEmpty()) {
+    }
+  }
+
+  // ----- if there's a report-only CSP header, apply it
+  if (!cspROHeaderValue.IsEmpty()) {
+    // post a warning and skip report-only CSP when both read only and regular
+    // CSP policies are present since CSP only allows one policy and it can't
+    // be partially report-only.
+    if (applyAppDefaultCSP || !cspHeaderValue.IsEmpty()) {
+      nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
+                                      "CSP", this,
+                                      nsContentUtils::eDOM_PROPERTIES,
+                                      "ReportOnlyCSPIgnored");
+#ifdef PR_LOGGING
+      PR_LOG(gCspPRLog, PR_LOG_DEBUG,
+              ("Skipped report-only CSP init for document %p because another, enforced policy is set", this));
+#endif
+    } else {
+      // we can apply the report-only policy because there's no other CSP
+      // applied.
       csp->SetReportOnlyMode(true);
 
       // Need to tokenize the header value since multiple headers could be
       // concatenated into one comma-separated list of policies.
       // See RFC2616 section 4.2 (last paragraph)
       nsCharSeparatedTokenizer tokenizer(cspROHeaderValue, ',');
       while (tokenizer.hasMoreTokens()) {
         const nsSubstring& policy = tokenizer.nextToken();
         csp->RefinePolicy(policy, chanURI);
 #ifdef PR_LOGGING
         {
           PR_LOG(gCspPRLog, PR_LOG_DEBUG,
-                  ("CSP (report only) refined with policy: \"%s\"",
+                  ("CSP (report-only) refined with policy: \"%s\"",
                     NS_ConvertUTF16toUTF8(policy).get()));
         }
 #endif
       }
-    } else {
-      //XXX(sstamm): maybe we should post a warning when both read only and regular
-      // CSP headers are present.
-
-      // Need to tokenize the header value since multiple headers could be
-      // concatenated into one comma-separated list of policies.
-      // See RFC2616 section 4.2 (last paragraph)
-      nsCharSeparatedTokenizer tokenizer(cspHeaderValue, ',');
-      while (tokenizer.hasMoreTokens()) {
-        const nsSubstring& policy = tokenizer.nextToken();
-        csp->RefinePolicy(policy, chanURI);
+    }
+  }
+
+  // ----- Enforce frame-ancestor policy on any applied policies
+  nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(mDocumentContainer);
+  if (docShell) {
+    bool safeAncestry = false;
+
+    // PermitsAncestry sends violation reports when necessary
+    rv = csp->PermitsAncestry(docShell, &safeAncestry);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (!safeAncestry) {
 #ifdef PR_LOGGING
-        {
-          PR_LOG(gCspPRLog, PR_LOG_DEBUG,
-                ("CSP refined with policy: \"%s\"",
-                  NS_ConvertUTF16toUTF8(policy).get()));
-        }
+      PR_LOG(gCspPRLog, PR_LOG_DEBUG,
+              ("CSP doesn't like frame's ancestry, not loading."));
 #endif
-      }
-    }
-
-    // Check for frame-ancestor violation
-    nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(mDocumentContainer);
-    if (docShell) {
-        bool safeAncestry = false;
-
-        // PermitsAncestry sends violation reports when necessary
-        rv = csp->PermitsAncestry(docShell, &safeAncestry);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        if (!safeAncestry) {
+      // stop!  ERROR page!
+      aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
+    }
+  }
+
+  if (csp) {
+    // Copy into principal
+    nsIPrincipal* principal = GetPrincipal();
+    principal->SetCsp(csp);
 #ifdef PR_LOGGING
-            PR_LOG(gCspPRLog, PR_LOG_DEBUG,
-                   ("CSP doesn't like frame's ancestry, not loading."));
+    PR_LOG(gCspPRLog, PR_LOG_DEBUG,
+           ("Inserted CSP into principal %p", principal));
 #endif
-            // stop!  ERROR page!
-            aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
-        }
-    }
-    csp.forget(aCSP);
-  }
-#ifdef PR_LOGGING
-  else { //CSP was not enabled!
-    PR_LOG(gCspPRLog, PR_LOG_DEBUG,
-           ("CSP is disabled, skipping CSP init for document %p", this));
-  }
-#endif
+  }
+
   return NS_OK;
 }
 
 void
 nsDocument::StopDocumentLoad()
 {
   if (mParser) {
     mParserAborted = true;
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -1269,17 +1269,17 @@ private:
   };
   // Recomputes the visibility state but doesn't set the new value.
   VisibilityState GetVisibilityState() const;
 
   void PostUnblockOnloadEvent();
   void DoUnblockOnload();
 
   nsresult CheckFrameOptions();
-  nsresult InitCSP(nsIChannel* aChannel, nsIContentSecurityPolicy **aCSP);
+  nsresult InitCSP(nsIChannel* aChannel);
 
   // Sets aElement to be the pending pointer lock element. Once this document's
   // node principal's URI is granted the "fullscreen" permission, the pointer
   // lock request will be processed. At any one time there can be only one
   // pending pointer lock request; calling this clears the previous pending
   // request.
   static nsresult SetPendingPointerLockRequest(Element* aElement);
 
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -653,16 +653,17 @@ GK_ATOM(oncompositionend, "oncomposition
 GK_ATOM(oncompositionstart, "oncompositionstart")
 GK_ATOM(oncompositionupdate, "oncompositionupdate")
 GK_ATOM(onconnected, "onconnected")
 GK_ATOM(onconnecting, "onconnecting")
 GK_ATOM(oncontextmenu, "oncontextmenu")
 GK_ATOM(oncopy, "oncopy")
 GK_ATOM(oncut, "oncut")
 GK_ATOM(ondatachange, "ondatachange")
+GK_ATOM(ondataerror, "ondataerror")
 GK_ATOM(ondblclick, "ondblclick")
 GK_ATOM(ondelivered, "ondelivered")
 GK_ATOM(ondevicecreated, "ondevicecreated")
 GK_ATOM(ondevicedisappeared, "ondevicedisappeared")
 GK_ATOM(ondevicefound, "ondevicefound")
 GK_ATOM(ondialing, "ondialing")
 GK_ATOM(ondisabled, "ondisabled")
 GK_ATOM(ondischargingtimechange, "ondischargingtimechange")
--- a/content/base/src/nsMixedContentBlocker.cpp
+++ b/content/base/src/nsMixedContentBlocker.cpp
@@ -190,12 +190,21 @@ nsMixedContentBlocker::ShouldProcess(PRU
                                      nsIURI* aContentLocation,
                                      nsIURI* aRequestingLocation,
                                      nsISupports* aRequestingContext,
                                      const nsACString& aMimeGuess,
                                      nsISupports* aExtra,
                                      nsIPrincipal* aRequestPrincipal,
                                      PRInt16* aDecision)
 {
+  if(!aContentLocation) {
+    // aContentLocation may be null when a plugin is loading without an associated URI resource
+    if(aContentType == TYPE_OBJECT) {
+       return NS_OK;
+    } else {
+       return NS_ERROR_FAILURE;
+    }
+  }
+
   return ShouldLoad(aContentType, aContentLocation, aRequestingLocation,
                     aRequestingContext, aMimeGuess, aExtra, aRequestPrincipal,
                     aDecision);
 }
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -362,16 +362,18 @@ MOCHITEST_FILES_B = \
 		file_CSP_frameancestors_main.js \
 		test_CSP_inlinescript.html \
 		file_CSP_inlinescript_main.html \
 		file_CSP_inlinescript_main.html^headers^ \
 		test_CSP_evalscript.html \
 		file_CSP_evalscript_main.html \
 		file_CSP_evalscript_main.html^headers^ \
 		file_CSP_evalscript_main.js \
+		file_csp_bug768029.html \
+		file_csp_bug768029.sjs \
 		test_bug540854.html \
 		bug540854.sjs \
 		test_bug548463.html \
 		test_bug545644.html \
 		test_bug545644.xhtml \
 		test_bug553896.xhtml \
 		test_bug515401.html \
 		test_bug541937.html \
--- a/content/base/test/chrome/Makefile.in
+++ b/content/base/test/chrome/Makefile.in
@@ -41,11 +41,12 @@ MOCHITEST_CHROME_FILES = \
     test_bug650776.html \
     test_bug650784.html \
     test_bug750096.html \
     test_bug752226-3.xul \
     test_bug752226-4.xul \
     test_bug682305.html \
     test_bug780199.xul \
     test_bug780529.xul \
+    test_csp_bug768029.html \
     $(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/content/base/test/chrome/test_csp_bug768029.html
@@ -0,0 +1,221 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+  https://bugzilla.mozilla.org/show_bug.cgi?id=768029
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for CSP on trusted/certified apps -- bug 768029</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=768029">Mozilla Bug 768029</a>
+<p id="display"></p>
+<div id="content">
+
+</div>
+<pre id="test">
+<script type="application/javascript;version=1.7">
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+/** Test for Bug 768029 **/
+
+// Note: we don't have to inspect all the different operations of CSP,
+// we're just looking for specific differences in behavior that indicate
+// a default CSP got applied.
+const DEFAULT_CSP_PRIV = "default-src *; script-src 'self'; style-src 'self' 'unsafe-inline'; object-src 'none'";
+const DEFAULT_CSP_CERT = "default-src *; script-src 'self'; style-src 'self'; object-src 'none'";
+
+SimpleTest.waitForExplicitFinish();
+
+var gData = [
+  {
+    app: "https://example.com/manifest.webapp",
+    appStatus: Components.interfaces.nsIPrincipal.APP_STATUS_INSTALLED,
+    origin: "https://example.com",
+    uri: "https://example.com/tests/content/base/test/file_csp_bug768029.html",
+    statusString: "installed app",
+    expectedTestResults: {
+      max_tests: 7, /* number of bools below plus one for the status check */
+      cross_origin: { img: true,  script: true,  style: true },
+      same_origin:  { img: true,  script: true,  style: true  },
+    },
+  },
+
+  {
+    app: "https://example.com/manifest_priv.webapp",
+    appStatus: Components.interfaces.nsIPrincipal.APP_STATUS_PRIVILEGED,
+    origin: "https://example.com",
+    uri: "https://example.com/tests/content/base/test/file_csp_bug768029.html",
+    statusString: "privileged app",
+    expectedTestResults: {
+      max_tests: 7, /* number of bools below plus one for the status check */
+      cross_origin: { img: true,  script: false, style: false },
+      same_origin:  { img: true,  script: true,  style: true  },
+    },
+  },
+
+  {
+    app: "https://example.com/manifest_cert.webapp",
+    appStatus: Components.interfaces.nsIPrincipal.APP_STATUS_CERTIFIED,
+    origin: "https://example.com",
+    uri: "https://example.com/tests/content/base/test/file_csp_bug768029.html",
+    statusString: "certified app",
+    expectedTestResults: {
+      max_tests: 7, /* number of bools below plus one for the status check */
+      cross_origin: { img: true,  script: false, style: false },
+      same_origin:  { img: true,  script: true,  style: true  },
+    },
+  },
+];
+
+// Observer for watching allowed loads and blocked attempts
+function ThingyListener(app, iframe) {
+  Services.obs.addObserver(this, "csp-on-violate-policy", false);
+  Services.obs.addObserver(this, "http-on-modify-request", false);
+  dump("added observers\n");
+  // keep track of which app ID this test is monitoring.
+  this._testData = app;
+  this._expectedResults = app.expectedTestResults;
+  this._resultsRecorded = { cross_origin: {}, same_origin: {}};
+  this._iframe = iframe;
+  this._countedTests = 0;
+}
+ThingyListener.prototype = {
+
+  observe: function(subject, topic, data) {
+    // make sure to only observe app-generated calls to the helper for this test.
+    var testpat = new RegExp("file_csp_bug768029\\.sjs");
+
+    // used to extract which kind of load this is (img, script, etc).
+    var typepat = new RegExp("type=([\\_a-z0-9]+)");
+
+    // used to identify whether it's cross-origin or same-origin loads
+    // (the applied CSP allows same-origin loads).
+    var originpat = new RegExp("origin=([\\_a-z0-9]+)");
+
+    if (topic === "http-on-modify-request") {
+      // Matching requests on this topic were allowed by the csp
+      var chan = subject.QueryInterface(Components.interfaces.nsIHttpChannel);
+      var uri = chan.URI;
+      // ignore irrelevent URIs
+      if (!testpat.test(uri.asciiSpec)) return;
+
+      var loadType = typepat.exec(uri.asciiSpec)[1];
+      var originType = originpat.exec(uri.asciiSpec)[1];
+
+      // skip duplicate hits to this topic (potentially document loads
+      // may generate duplicate loads.
+      if (this._resultsRecorded[originType] &&
+          this._resultsRecorded[originType][loadType]) {
+        return;
+      }
+      var message = originType + " : " + loadType + " should be " +
+                    (this._expectedResults[originType][loadType] ? "allowed" : "blocked");
+      ok(this._expectedResults[originType][loadType] == true, message);
+      this._resultsRecorded[originType][loadType] = true;
+      this._countedTests++;
+    }
+    else if (topic === "csp-on-violate-policy") {
+      // Matching hits on this topic were blocked by the csp
+      var uri = subject.QueryInterface(Components.interfaces.nsIURI);
+      // ignore irrelevent URIs
+      if (!testpat.test(uri.asciiSpec)) return;
+
+      var loadType = typepat.exec(uri.asciiSpec)[1];
+      var originType = originpat.exec(uri.asciiSpec)[1];
+
+      // skip duplicate hits to this topic (potentially document loads
+      // may generate duplicate loads.
+      if (this._resultsRecorded[originType] &&
+          this._resultsRecorded[originType][loadType]) {
+        return;
+      }
+
+      var message = originType + " : " + loadType + " should be " +
+                    (this._expectedResults[originType][loadType] ? "allowed" : "blocked");
+      ok(this._expectedResults[originType][loadType] == false, message);
+      this._resultsRecorded[originType][loadType] = true;
+      this._countedTests++;
+    }
+    else {
+      // wrong topic!  Nothing to do.
+      return;
+    }
+
+    this._checkForFinish();
+  },
+
+  _checkForFinish: function() {
+    // check to see if there are load tests still pending.
+    // (All requests triggered by the app should hit one of the
+    // two observer topics.)
+    if (this._countedTests == this._expectedResults.max_tests) {
+      Services.obs.removeObserver(this, "csp-on-violate-policy");
+      Services.obs.removeObserver(this, "http-on-modify-request");
+      dump("removed observers\n");
+      checkedCount++;
+      if (checkedCount == checksTodo) {
+        SpecialPowers.removePermission("browser", "https://example.com");
+        SimpleTest.finish();
+      } else {
+        gTestRunner.next();
+      }
+    }
+  },
+
+  // verify the status of the app
+  checkAppStatus: function() {
+    var principal = this._iframe.contentDocument.nodePrincipal;
+    if (this._testData.app) {
+      is(principal.appStatus, this._testData.appStatus,
+         "iframe principal's app status doesn't match the expected app status.");
+      this._countedTests++;
+      this._checkForFinish();
+    }
+  }
+}
+
+var content = document.getElementById('content');
+var checkedCount = 0; // number of apps checked
+var checksTodo = gData.length;
+
+// quick check to make sure we can test apps:
+is('appStatus' in document.nodePrincipal, true,
+   'appStatus should be present in nsIPrincipal, if not the rest of this test will fail');
+
+function runTest() {
+  for (var i = 0; i < gData.length; i++) {
+    let data = gData[i];
+    var iframe = document.createElement('iframe');
+
+    // watch for successes and failures
+    var examiner = new ThingyListener(data, iframe);
+
+    iframe.setAttribute('mozapp', data.app);
+    iframe.setAttribute('mozbrowser', 'true');
+    iframe.addEventListener('load', examiner.checkAppStatus.bind(examiner));
+    iframe.src = data.uri;
+
+    content.appendChild(iframe);
+
+    yield;
+  }
+}
+
+var gTestRunner = runTest();
+
+// load the default CSP and pref it on
+SpecialPowers.addPermission("browser", true, "https://example.com");
+SpecialPowers.pushPrefEnv({'set': [["dom.mozBrowserFramesEnabled", true],
+                                   ["security.apps.privileged.CSP.default", DEFAULT_CSP_PRIV],
+                                   ["security.apps.certified.CSP.default", DEFAULT_CSP_CERT]]},
+                          function() {  gTestRunner.next(); });
+
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_csp_bug768029.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+  https://bugzilla.mozilla.org/show_bug.cgi?id=768029
+-->
+<head>
+  <meta charset="utf-8">
+    <title>This is an app for testing</title>
+
+    <link rel="stylesheet" type="text/css"
+          href="file_csp_bug768029.sjs?type=style&origin=same_origin" />
+    <link rel="stylesheet" type="text/css"
+          href="http://example.com/tests/content/base/test/file_csp_bug768029.sjs?type=style&origin=cross_origin" />
+  </head>
+  <body>
+
+    <script src="file_csp_bug768029.sjs?type=script&origin=same_origin"></script>
+    <script src="http://example.com/tests/content/base/test/file_csp_bug768029.sjs?type=script&origin=cross_origin"></script>
+    <img src="file_csp_bug768029.sjs?type=img&origin=same_origin" />
+    <img src="http://example.com/tests/content/base/test/file_csp_bug768029.sjs?type=img&origin=cross_origin" />
+
+    Test for CSP applied to (simulated) app.
+
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_csp_bug768029.sjs
@@ -0,0 +1,29 @@
+function handleRequest(request, response) {
+
+  var query = {};
+
+  request.queryString.split('&').forEach(function(val) {
+    var [name, value] = val.split('=');
+    query[name] = unescape(value);
+  });
+  response.setHeader("Cache-Control", "no-cache", false);
+
+  if ("type" in query) {
+    switch (query.type) {
+      case "script":
+        response.setHeader("Content-Type", "application/javascript");
+        response.write("\n\ndocument.write('<pre>script loaded\\n</pre>');\n\n");
+        return;
+      case "style":
+        response.setHeader("Content-Type", "text/css");
+        response.write("\n\n.cspfoo { color:red; }\n\n");
+        return;
+      case "img":
+        response.setHeader("Content-Type", "image/png");
+        return;
+    }
+  }
+
+  response.setHeader("Content-Type", "text/plain");
+  response.write("ohnoes!");
+}
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/AudioBuffer.cpp
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "AudioBuffer.h"
+#include "mozilla/dom/AudioBufferBinding.h"
+#include "nsContentUtils.h"
+#include "AudioContext.h"
+#include "jsfriendapi.h"
+#include "mozilla/ErrorResult.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(AudioBuffer)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioBuffer)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mContext)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mChannels)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+  NS_DROP_JS_OBJECTS(tmp, AudioBuffer);
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AudioBuffer)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContext)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(AudioBuffer)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
+  for (uint32_t i = 0; i < tmp->mChannels.Length(); ++i) {
+    NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mChannels[i])
+  }
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(AudioBuffer)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(AudioBuffer)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AudioBuffer)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+AudioBuffer::AudioBuffer(AudioContext* aContext, uint32_t aLength,
+                         uint32_t aSampleRate)
+  : mContext(aContext),
+    mLength(aLength),
+    mSampleRate(aSampleRate)
+{
+  SetIsDOMBinding();
+}
+
+AudioBuffer::~AudioBuffer()
+{
+  // Drop the JS object reference if we're still holding to the channels
+  if (mChannels.Length()) {
+    NS_DROP_JS_OBJECTS(this, AudioBuffer);
+  }
+}
+
+bool
+AudioBuffer::InitializeBuffers(uint32_t aNumberOfChannels, JSContext* aJSContext)
+{
+  if (!mChannels.SetCapacity(aNumberOfChannels)) {
+    return false;
+  }
+  for (uint32_t i = 0; i < aNumberOfChannels; ++i) {
+    JSObject* array = JS_NewFloat32Array(aJSContext, mLength);
+    if (!array) {
+      return false;
+    }
+    mChannels.AppendElement(array);
+  }
+
+  NS_HOLD_JS_OBJECTS(this, AudioBuffer);
+
+  return true;
+}
+
+JSObject*
+AudioBuffer::WrapObject(JSContext* aCx, JSObject* aScope,
+                        bool* aTriedToWrap)
+{
+  return AudioBufferBinding::Wrap(aCx, aScope, this, aTriedToWrap);
+}
+
+JSObject*
+AudioBuffer::GetChannelData(JSContext* aJSContext, uint32_t aChannel,
+                            ErrorResult& aRv) const
+{
+  if (aChannel >= mChannels.Length()) {
+    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+    return nullptr;
+  }
+  return mChannels[aChannel];
+}
+
+}
+}
+
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/AudioBuffer.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#pragma once
+
+#include "nsWrapperCache.h"
+#include "nsCycleCollectionParticipant.h"
+#include "mozilla/Attributes.h"
+#include "EnableWebAudioCheck.h"
+#include "nsAutoPtr.h"
+#include "nsTArray.h"
+#include "AudioContext.h"
+
+struct JSContext;
+struct JSObject;
+
+namespace mozilla {
+
+class ErrorResult;
+
+namespace dom {
+
+class AudioBuffer MOZ_FINAL : public nsISupports,
+                              public nsWrapperCache,
+                              public EnableWebAudioCheck
+{
+public:
+  AudioBuffer(AudioContext* aContext, uint32_t aLength,
+              uint32_t aSampleRate);
+  ~AudioBuffer();
+
+  // This function needs to be called in order to allocate
+  // all of the channels.  It is fallible!
+  bool InitializeBuffers(uint32_t aNumberOfChannels,
+                         JSContext* aJSContext);
+
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AudioBuffer)
+
+  AudioContext* GetParentObject() const
+  {
+    return mContext;
+  }
+
+  virtual JSObject* WrapObject(JSContext* aCx, JSObject* aScope,
+                               bool* aTriedToWrap);
+
+  float SampleRate() const
+  {
+    return mSampleRate;
+  }
+
+  uint32_t Length() const
+  {
+    return mLength;
+  }
+
+  float Duration() const
+  {
+    return mLength / mSampleRate;
+  }
+
+  uint32_t NumberOfChannels() const
+  {
+    return mChannels.Length();
+  }
+
+  JSObject* GetChannelData(JSContext* aJSContext, uint32_t aChannel,
+                           ErrorResult& aRv) const;
+
+private:
+  nsRefPtr<AudioContext> mContext;
+  FallibleTArray<JSObject*> mChannels;
+  uint32_t mLength;
+  float mSampleRate;
+};
+
+}
+}
+
--- a/content/media/webaudio/AudioContext.cpp
+++ b/content/media/webaudio/AudioContext.cpp
@@ -6,16 +6,17 @@
 
 #include "AudioContext.h"
 #include "nsContentUtils.h"
 #include "nsIDOMWindow.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/AudioContextBinding.h"
 #include "AudioDestinationNode.h"
 #include "AudioBufferSourceNode.h"
+#include "AudioBuffer.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_2(AudioContext, mWindow, mDestination)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(AudioContext)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(AudioContext)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AudioContext)
@@ -58,11 +59,24 @@ AudioContext::Constructor(nsISupports* a
 already_AddRefed<AudioBufferSourceNode>
 AudioContext::CreateBufferSource()
 {
   nsRefPtr<AudioBufferSourceNode> bufferNode =
     new AudioBufferSourceNode(this);
   return bufferNode.forget();
 }
 
+already_AddRefed<AudioBuffer>
+AudioContext::CreateBuffer(JSContext* aJSContext, uint32_t aNumberOfChannels,
+                           uint32_t aLength, float aSampleRate,
+                           ErrorResult& aRv)
+{
+  nsRefPtr<AudioBuffer> buffer = new AudioBuffer(this, aLength, aSampleRate);
+  if (!buffer->InitializeBuffers(aNumberOfChannels, aJSContext)) {
+    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+    return nullptr;
+  }
+  return buffer.forget();
+}
+
 }
 }
 
--- a/content/media/webaudio/AudioContext.h
+++ b/content/media/webaudio/AudioContext.h
@@ -19,16 +19,17 @@ class nsIDOMWindow;
 namespace mozilla {
 
 class ErrorResult;
 
 namespace dom {
 
 class AudioDestinationNode;
 class AudioBufferSourceNode;
+class AudioBuffer;
 
 class AudioContext MOZ_FINAL : public nsISupports,
                                public nsWrapperCache,
                                public EnableWebAudioCheck
 {
   explicit AudioContext(nsIDOMWindow* aParentWindow);
 
 public:
@@ -50,16 +51,21 @@ public:
 
   AudioDestinationNode* Destination() const
   {
     return mDestination;
   }
 
   already_AddRefed<AudioBufferSourceNode> CreateBufferSource();
 
+  already_AddRefed<AudioBuffer>
+  CreateBuffer(JSContext* aJSContext, uint32_t aNumberOfChannels,
+               uint32_t aLength, float aSampleRate,
+               ErrorResult& aRv);
+
 private:
   nsCOMPtr<nsIDOMWindow> mWindow;
   nsRefPtr<AudioDestinationNode> mDestination;
 };
 
 }
 }
 
--- a/content/media/webaudio/Makefile.in
+++ b/content/media/webaudio/Makefile.in
@@ -10,26 +10,28 @@ FAIL_ON_WARNINGS := 1
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE         := content
 LIBRARY_NAME   := gkconwebaudio_s
 LIBXUL_LIBRARY := 1
 
 CPPSRCS := \
+  AudioBuffer.cpp \
   AudioBufferSourceNode.cpp \
   AudioContext.cpp \
   AudioDestinationNode.cpp \
   AudioNode.cpp \
   AudioSourceNode.cpp \
   EnableWebAudioCheck.cpp \
   $(NULL)
 
 EXPORTS_NAMESPACES := mozilla/dom
 EXPORTS_mozilla/dom := \
+  AudioBuffer.h \
   AudioBufferSourceNode.h \
   AudioDestinationNode.h \
   AudioNode.h \
   AudioSourceNode.h \
   $(NULL)
 
 PARALLEL_DIRS := test
 
--- a/content/media/webaudio/test/Makefile.in
+++ b/content/media/webaudio/test/Makefile.in
@@ -6,12 +6,13 @@ DEPTH          := @DEPTH@
 topsrcdir      := @top_srcdir@
 srcdir         := @srcdir@
 VPATH          := @srcdir@
 relativesrcdir := @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MOCHITEST_FILES := \
+  test_AudioBuffer.html \
   test_AudioContext.html \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/test/test_AudioBuffer.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test whether we can create an AudioContext interface</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+  SpecialPowers.setBoolPref("media.webaudio.enabled", true);
+  var ac = new mozAudioContext();
+  var buffer = ac.createBuffer(2, 2048, 44100);
+  SpecialPowers.gc(); // Make sure that our channels are accessible after GC
+  ok(buffer, "Buffer was allocated successfully");
+  is(buffer.sampleRate, 44100, "Correct sample rate");
+  is(buffer.length, 2048, "Correct length");
+  ok(Math.abs(buffer.duration - 2048 / 44100) < 0.0001, "Correct duration");
+  is(buffer.numberOfChannels, 2, "Correct number of channels");
+  for (var i = 0; i < buffer.numberOfChannels; ++i) {
+    var buf = buffer.getChannelData(i);
+    ok(buf, "Buffer index " + i + " exists");
+    ok(buf instanceof Float32Array, "Result is a typed array");
+    is(buf.length, buffer.length, "Correct length");
+    var foundNonZero = false;
+    for (var j = 0; j < buf.length; ++j) {
+      if (buf[j] != 0) {
+        foundNonZero = true;
+        break;
+      }
+    }
+    ok(!foundNonZero, "Buffer " + i + " should be initialized to 0");
+  }
+  SpecialPowers.clearUserPref("media.webaudio.enabled");
+  SimpleTest.finish();
+});
+
+</script>
+</pre>
+</body>
+</html>
--- a/docshell/test/chrome/mozFrameType_window.xul
+++ b/docshell/test/chrome/mozFrameType_window.xul
@@ -27,29 +27,28 @@
       var normalFrame = document.getElementById("normalFrame");
       var typeContentFrame = document.getElementById("typeContentFrame");
 
       SimpleTest.is(getDocShellType(normalFrame), Ci.nsIDocShellTreeItem.typeChrome,
                     "normal iframe in chrome document is typeChrome");
       SimpleTest.is(getDocShellType(typeContentFrame), Ci.nsIDocShellTreeItem.typeContent,
                     "iframe with mozFrameType='content' in chrome document is typeContent");
 
-      // avoid closing the window from within the onload event handler to see
-      // whether that fixes the intermittent orange in bug 772823
-      SimpleTest.executeSoon(function () {
-        // Wait for the window to be closed before finishing the test
-        let ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
-                     .getService(Components.interfaces.nsIWindowWatcher);
-        ww.registerNotification(function windowObs(subject, topic, data) {
-          if (topic == "domwindowclosed") {
-            ww.unregisterNotification(windowObs);
+      // Wait for the window to be closed before finishing the test
+      let ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
+                   .getService(Components.interfaces.nsIWindowWatcher);
+      ww.registerNotification(function windowObs(subject, topic, data) {
+        if (topic == "domwindowclosed") {
+          ww.unregisterNotification(windowObs);
+
+          SimpleTest.executeSoon(function () {
             SimpleTest.waitForFocus(function() {
               SimpleTest.finish();
             }, opener);
-          }
-        });
+          });
+        }
+      });
 
-        window.close();
-      });
+      window.close();
     }
   ]]></script>
 </window>
 
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -500,16 +500,17 @@ using mozilla::dom::indexedDB::IDBWrappe
 #include "nsIDOMSmsRequest.h"
 #include "nsIDOMSmsFilter.h"
 #include "nsIDOMSmsCursor.h"
 #include "nsIDOMConnection.h"
 #ifdef MOZ_B2G_RIL
 #include "nsIDOMMobileConnection.h"
 #endif
 #include "USSDReceivedEvent.h"
+#include "DataErrorEvent.h"
 #include "mozilla/dom/network/Utils.h"
 
 #ifdef MOZ_B2G_RIL
 #include "Telephony.h"
 #include "TelephonyCall.h"
 #include "CallEvent.h"
 #include "nsIDOMVoicemail.h"
 #include "nsIDOMVoicemailEvent.h"
@@ -1502,16 +1503,19 @@ static nsDOMClassInfoData sClassInfoData
 #ifdef MOZ_B2G_RIL
   NS_DEFINE_CLASSINFO_DATA(MozMobileConnection, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 #endif
 
   NS_DEFINE_CLASSINFO_DATA(USSDReceivedEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
+  NS_DEFINE_CLASSINFO_DATA(DataErrorEvent, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
+
   NS_DEFINE_CLASSINFO_DATA(CSSFontFaceRule, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(CSSFontFaceStyleDecl, nsCSSStyleDeclSH,
                            ARRAY_SCRIPTABLE_FLAGS)
 
 #if defined(MOZ_MEDIA)
   NS_DEFINE_CLASSINFO_DATA(HTMLVideoElement, nsElementSH,
                            ELEMENT_SCRIPTABLE_FLAGS)
@@ -4153,16 +4157,21 @@ nsDOMClassInfo::Init()
   DOM_CLASSINFO_MAP_END
 #endif
 
   DOM_CLASSINFO_MAP_BEGIN(USSDReceivedEvent, nsIDOMUSSDReceivedEvent)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMUSSDReceivedEvent)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEvent)
   DOM_CLASSINFO_MAP_END
  
+  DOM_CLASSINFO_MAP_BEGIN(DataErrorEvent, nsIDOMDataErrorEvent)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMDataErrorEvent)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMEvent)
+  DOM_CLASSINFO_MAP_END
+
   DOM_CLASSINFO_MAP_BEGIN(CSSFontFaceRule, nsIDOMCSSFontFaceRule)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSFontFaceRule)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(CSSFontFaceStyleDecl,
                                       nsIDOMCSSStyleDeclaration)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSStyleDeclaration)
   DOM_CLASSINFO_MAP_END
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -408,16 +408,18 @@ DOMCI_CLASS(MozSmsCursor)
 
 DOMCI_CLASS(MozConnection)
 #ifdef MOZ_B2G_RIL
 DOMCI_CLASS(MozMobileConnection)
 #endif
 
 DOMCI_CLASS(USSDReceivedEvent)
 
+DOMCI_CLASS(DataErrorEvent)
+
 // @font-face in CSS
 DOMCI_CLASS(CSSFontFaceRule)
 DOMCI_CLASS(CSSFontFaceStyleDecl)
 
 #if defined(MOZ_MEDIA)
 // WhatWG Video Element
 DOMCI_CLASS(HTMLVideoElement)
 DOMCI_CLASS(HTMLSourceElement)
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -61,18 +61,22 @@
 #
 #   * implicitJSContext - attributes and methods specified in the .webidl file
 #                         that require a JSContext as the first argument
 #   * resultNotAddRefed - attributes and methods specified in the .webidl file
 #                         that do not AddRef the return value
 
 DOMInterfaces = {
 
+'AudioBuffer' : {
+},
+
 'mozAudioContext': {
     'nativeType': 'AudioContext',
+    'implicitJSContext': [ 'createBuffer' ],
 },
 
 'AudioNode' : {
     'concrete': False,
 },
 
 'AudioSourceNode': {
     'concrete': False,
--- a/dom/bindings/stubgenerator/Skeleton.cpp
+++ b/dom/bindings/stubgenerator/Skeleton.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Skeleton.h"
 #include "mozilla/dom/SkeletonBinding.h"
+#include "nsContentUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(Skeleton)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(Skeleton)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(Skeleton)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Skeleton)
--- a/dom/bluetooth/BluetoothDevice.cpp
+++ b/dom/bluetooth/BluetoothDevice.cpp
@@ -56,16 +56,27 @@ BluetoothDevice::BluetoothDevice(nsPIDOM
   mAdapterPath(aAdapterPath),
   mIsRooted(false)
 {
   BindToOwner(aOwner);
   const InfallibleTArray<BluetoothNamedValue>& values =
     aValue.get_ArrayOfBluetoothNamedValue();
   for (uint32_t i = 0; i < values.Length(); ++i) {
     SetPropertyByValue(values[i]);
+    if (values[i].name().EqualsLiteral("Path")) {
+      // Since this is our signal handler string, set it as we set the property
+      // in the object. Odd place to do it, but makes more sense than in
+      // SetPropertyByValue.
+      BluetoothService* bs = BluetoothService::Get();
+      if (!bs) {
+        NS_WARNING("BluetoothService not available!");
+      } else {
+        bs->RegisterBluetoothSignalHandler(mPath, this);
+      }
+    }
   }
 }
 
 BluetoothDevice::~BluetoothDevice()
 {
   BluetoothService* bs = BluetoothService::Get();
   // bs can be null on shutdown, where destruction might happen.
   if (bs) {
@@ -95,24 +106,18 @@ BluetoothDevice::Unroot()
 void
 BluetoothDevice::SetPropertyByValue(const BluetoothNamedValue& aValue)
 {
   const nsString& name = aValue.name();
   const BluetoothValue& value = aValue.value();
   if (name.EqualsLiteral("Name")) {
     mName = value.get_nsString();
   } else if (name.EqualsLiteral("Path")) {
+    MOZ_ASSERT(value.get_nsString().Length() > 0);
     mPath = value.get_nsString();
-    NS_WARNING(NS_ConvertUTF16toUTF8(mPath).get());
-    BluetoothService* bs = BluetoothService::Get();
-    if (!bs) {
-      NS_WARNING("BluetoothService not available!");
-    } else {
-      bs->RegisterBluetoothSignalHandler(mPath, this);
-    }
   } else if (name.EqualsLiteral("Address")) {
     mAddress = value.get_nsString();
   } else if (name.EqualsLiteral("Class")) {
     mClass = value.get_uint32_t();
   } else if (name.EqualsLiteral("Icon")) {
     mIcon = value.get_nsString();
   } else if (name.EqualsLiteral("Connected")) {
 #ifdef MOZ_WIDGET_GONK
--- a/dom/bluetooth/BluetoothReplyRunnable.cpp
+++ b/dom/bluetooth/BluetoothReplyRunnable.cpp
@@ -60,21 +60,21 @@ BluetoothReplyRunnable::FireErrorString(
   
   return rs->FireError(mDOMRequest, mErrorString);
 }
 
 NS_IMETHODIMP
 BluetoothReplyRunnable::Run()
 {
   MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mDOMRequest);
+  MOZ_ASSERT(mReply);
 
   nsresult rv;
 
-  MOZ_ASSERT(mDOMRequest);
-
   if (mReply->type() != BluetoothReply::TBluetoothReplySuccess) {
     rv = FireReply(JSVAL_VOID);
   } else {
     jsval v; 
     if (!ParseSuccessfulReply(&v)) {
       rv = FireErrorString();
     } else {
       rv = FireReply(v);
--- a/dom/bluetooth/BluetoothService.cpp
+++ b/dom/bluetooth/BluetoothService.cpp
@@ -15,16 +15,17 @@
 
 #include "jsapi.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/unused.h"
 #include "mozilla/Util.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
+#include "mozilla/ipc/UnixSocket.h"
 #include "nsContentUtils.h"
 #include "nsIDOMDOMRequest.h"
 #include "nsIObserverService.h"
 #include "nsISettingsService.h"
 #include "nsISystemMessagesInternal.h"
 #include "nsITimer.h"
 #include "nsThreadUtils.h"
 #include "nsXPCOM.h"
@@ -237,20 +238,19 @@ BluetoothService::Create()
     return BluetoothServiceChildProcess::Create();
   }
 #endif
 
 #if defined(MOZ_BLUETOOTH_GONK)
   return new BluetoothGonkService();
 #elif defined(MOZ_BLUETOOTH_DBUS)
   return new BluetoothDBusService();
-#else
+#endif
   NS_WARNING("No platform support for bluetooth!");
   return nullptr;
-#endif
 }
 
 bool
 BluetoothService::Init()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
@@ -264,18 +264,16 @@ BluetoothService::Init()
 
   // Only the main process should observe bluetooth settings changes.
   if (IsMainProcess() &&
       NS_FAILED(obs->AddObserver(this, MOZSETTINGS_CHANGED_ID, false))) {
     NS_WARNING("Failed to add settings change observer!");
     return false;
   }
 
-  RegisterBluetoothSignalHandler(NS_LITERAL_STRING(LOCAL_AGENT_PATH), this);
-  RegisterBluetoothSignalHandler(NS_LITERAL_STRING(REMOTE_AGENT_PATH), this);
   mRegisteredForLocalAgent = true;
 
   return true;
 }
 
 void
 BluetoothService::Cleanup()
 {
@@ -355,26 +353,16 @@ BluetoothService::DistributeSignal(const
   ol->Broadcast(aSignal);
 }
 
 nsresult
 BluetoothService::StartStopBluetooth(bool aStart)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-#ifdef DEBUG
-  if (aStart && mLastRequestedEnable) {
-    MOZ_ASSERT(false, "Calling Start twice in a row!");
-  }
-  else if (!aStart && !mLastRequestedEnable) {
-    MOZ_ASSERT(false, "Calling Stop twice in a row!");
-  }
-  mLastRequestedEnable = aStart;
-#endif
-
   if (gInShutdown) {
     if (aStart) {
       // Don't try to start if we're already shutting down.
       MOZ_ASSERT(false, "Start called while in shutdown!");
       return NS_ERROR_FAILURE;
     }
 
     if (!mBluetoothCommandThread) {
@@ -389,16 +377,26 @@ BluetoothService::StartStopBluetooth(boo
   if (!mBluetoothCommandThread) {
     MOZ_ASSERT(!gInShutdown);
 
     rv = NS_NewNamedThread("BluetoothCmd",
                            getter_AddRefs(mBluetoothCommandThread));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
+  if (aStart) {
+    RegisterBluetoothSignalHandler(NS_LITERAL_STRING(LOCAL_AGENT_PATH), this);
+    RegisterBluetoothSignalHandler(NS_LITERAL_STRING(REMOTE_AGENT_PATH), this);
+
+    BluetoothManagerList::ForwardIterator iter(mLiveManagers);
+    while (iter.HasMore()) {
+      RegisterBluetoothSignalHandler(NS_LITERAL_STRING("/"), (BluetoothSignalObserver*)iter.GetNext());
+    }
+  }
+
   nsCOMPtr<nsIRunnable> runnable = new ToggleBtTask(aStart);
   rv = mBluetoothCommandThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 void
@@ -692,17 +690,17 @@ BluetoothService::Observe(nsISupports* a
   return NS_ERROR_UNEXPECTED;
 }
 
 bool
 SetJsObject(JSContext* aContext,
             JSObject* aObj,
             const InfallibleTArray<BluetoothNamedValue>& aData)
 {
-  for (int i = 0; i < aData.Length(); i++) {
+  for (uint32_t i = 0; i < aData.Length(); i++) {
     jsval v;
     if (aData[i].value().type() == BluetoothValue::TnsString) {
       nsString data = aData[i].value().get_nsString();
       JSString* JsData = JS_NewStringCopyN(aContext,
                                            NS_ConvertUTF16toUTF8(data).get(),
                                            data.Length());
       NS_ENSURE_TRUE(JsData, NS_ERROR_OUT_OF_MEMORY);
       v = STRING_TO_JSVAL(JsData);
--- a/dom/bluetooth/BluetoothService.h
+++ b/dom/bluetooth/BluetoothService.h
@@ -9,16 +9,22 @@
 
 #include "BluetoothCommon.h"
 #include "nsAutoPtr.h"
 #include "nsClassHashtable.h"
 #include "nsIObserver.h"
 #include "nsIThread.h"
 #include "nsTObserverArray.h"
 
+namespace mozilla {
+namespace ipc {
+class UnixSocketConsumer;
+}
+}
+
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothManager;
 class BluetoothNamedValue;
 class BluetoothReplyRunnable;
 class BluetoothSignal;
 
 typedef mozilla::ObserverList<BluetoothSignal> BluetoothSignalObserverList;
@@ -221,25 +227,23 @@ public:
   virtual nsresult
   RemoveDeviceInternal(const nsAString& aAdapterPath,
                        const nsAString& aObjectPath,
                        BluetoothReplyRunnable* aRunnable) = 0;
 
   virtual nsresult
   GetSocketViaService(const nsAString& aObjectPath,
                       const nsAString& aService,
-                      int aType,
+                      BluetoothSocketType aType,
                       bool aAuth,
                       bool aEncrypt,
+                      mozilla::ipc::UnixSocketConsumer* aSocketConsumer,
                       BluetoothReplyRunnable* aRunnable) = 0;
 
   virtual bool
-  CloseSocket(int aFd, BluetoothReplyRunnable* aRunnable) = 0;
-
-  virtual bool
   SetPinCodeInternal(const nsAString& aDeviceAddress, const nsAString& aPinCode,
                      BluetoothReplyRunnable* aRunnable) = 0;
 
   virtual bool
   SetPasskeyInternal(const nsAString& aDeviceAddress, uint32_t aPasskey,
                      BluetoothReplyRunnable* aRunnable) = 0;
 
   virtual bool
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth/BluetoothUnixSocketConnector.cpp
@@ -0,0 +1,175 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * NOTE: Due to being based on the dbus compatibility layer for
+ * android's bluetooth implementation, this file is licensed under the
+ * apache license instead of MPL.
+ *
+ */
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <sys/socket.h>
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sco.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/l2cap.h>
+
+#include "BluetoothUnixSocketConnector.h"
+#include "nsThreadUtils.h"
+
+USING_BLUETOOTH_NAMESPACE
+
+static const int RFCOMM_SO_SNDBUF = 70 * 1024; // 70 KB send buffer
+
+static
+int get_bdaddr(const char *str, bdaddr_t *ba)
+{
+  char *d = ((char*)ba) + 5, *endp;
+  for (int i = 0; i < 6; i++) {
+    *d-- = strtol(str, &endp, 16);
+    MOZ_ASSERT(!(*endp != ':' && i != 5));
+    str = endp + 1;
+  }
+  return 0;
+}
+
+BluetoothUnixSocketConnector::BluetoothUnixSocketConnector(
+  BluetoothSocketType aType,
+  int aChannel,
+  bool aAuth,
+  bool aEncrypt) : mType(aType)
+                 , mChannel(aChannel)
+                 , mAuth(aAuth)
+                 , mEncrypt(aEncrypt)
+{
+}
+
+int
+BluetoothUnixSocketConnector::Create()
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  int lm = 0;
+  int fd = -1;
+  int sndbuf;
+
+  switch (mType) {
+  case BluetoothSocketType::RFCOMM:
+    fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+    break;
+  case BluetoothSocketType::SCO:
+    fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+    break;
+  case BluetoothSocketType::L2CAP:
+    fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+    break;
+  default:
+    return -1;
+  }
+
+  if (fd < 0) {
+    NS_WARNING("Could not open bluetooth socket!");
+    return -1;
+  }
+
+  /* kernel does not yet support LM for SCO */
+  switch (mType) {
+  case BluetoothSocketType::RFCOMM:
+    lm |= mAuth ? RFCOMM_LM_AUTH : 0;
+    lm |= mEncrypt ? RFCOMM_LM_ENCRYPT : 0;
+    lm |= (mAuth && mEncrypt) ? RFCOMM_LM_SECURE : 0;
+    break;
+  case BluetoothSocketType::L2CAP:
+    lm |= mAuth ? L2CAP_LM_AUTH : 0;
+    lm |= mEncrypt ? L2CAP_LM_ENCRYPT : 0;
+    lm |= (mAuth && mEncrypt) ? L2CAP_LM_SECURE : 0;
+    break;
+  }
+
+  if (lm) {
+    if (setsockopt(fd, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm))) {
+      NS_WARNING("setsockopt(RFCOMM_LM) failed, throwing");
+      return -1;
+    }
+  }
+
+  if (mType == BluetoothSocketType::RFCOMM) {
+    sndbuf = RFCOMM_SO_SNDBUF;
+    if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf))) {
+      NS_WARNING("setsockopt(SO_SNDBUF) failed, throwing");
+      return -1;
+    }
+  }
+
+  return fd;
+}
+
+bool
+BluetoothUnixSocketConnector::ConnectInternal(int aFd, const char* aAddress)
+{
+  int n = 1;
+  setsockopt(aFd, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
+  
+  socklen_t addr_sz;
+  struct sockaddr *addr;
+  bdaddr_t bd_address_obj;
+
+  if (get_bdaddr(aAddress, &bd_address_obj)) {
+    NS_WARNING("Can't get bluetooth address!");
+    return false;
+  }
+
+  switch (mType) {
+  case BluetoothSocketType::RFCOMM:
+    struct sockaddr_rc addr_rc;
+    addr = (struct sockaddr *)&addr_rc;
+    addr_sz = sizeof(addr_rc);
+
+    memset(addr, 0, addr_sz);
+    addr_rc.rc_family = AF_BLUETOOTH;
+    addr_rc.rc_channel = mChannel;
+    memcpy(&addr_rc.rc_bdaddr, &bd_address_obj, sizeof(bdaddr_t));
+    break;
+  case BluetoothSocketType::SCO:
+    struct sockaddr_sco addr_sco;
+    addr = (struct sockaddr *)&addr_sco;
+    addr_sz = sizeof(addr_sco);
+
+    memset(addr, 0, addr_sz);
+    addr_sco.sco_family = AF_BLUETOOTH;
+    memcpy(&addr_sco.sco_bdaddr, &bd_address_obj, sizeof(bdaddr_t));
+    break;
+  default:
+    NS_WARNING("Socket type unknown!");
+    return false;
+  }
+
+  int ret = connect(aFd, addr, addr_sz);
+
+  if (ret) {
+#if DEBUG
+    //LOG("Socket connect errno=%d\n", errno);
+#endif
+    NS_WARNING("Socket connect error!");
+    return false;
+  }
+
+  return true;
+}
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth/BluetoothUnixSocketConnector.h
@@ -0,0 +1,34 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_bluetooth_BluetoothUnixSocketConnector_h
+#define mozilla_dom_bluetooth_BluetoothUnixSocketConnector_h
+
+#include "BluetoothCommon.h"
+#include <mozilla/ipc/UnixSocket.h>
+
+BEGIN_BLUETOOTH_NAMESPACE
+
+class BluetoothUnixSocketConnector : public mozilla::ipc::UnixSocketConnector
+{
+public:
+  BluetoothUnixSocketConnector(BluetoothSocketType aType, int aChannel,
+                               bool aAuth, bool aEncrypt);
+  virtual ~BluetoothUnixSocketConnector()
+  {}
+  virtual int Create() MOZ_OVERRIDE;
+  virtual bool ConnectInternal(int aFd, const char* aAddress) MOZ_OVERRIDE;
+
+private:
+  BluetoothSocketType mType;
+  int mChannel;
+  bool mAuth;
+  bool mEncrypt;
+};
+
+END_BLUETOOTH_NAMESPACE
+
+#endif
--- a/dom/bluetooth/Makefile.in
+++ b/dom/bluetooth/Makefile.in
@@ -46,16 +46,17 @@ CPPSRCS += \
   BluetoothDevice.cpp \
   BluetoothPropertyEvent.cpp \
   BluetoothReplyRunnable.cpp \
   BluetoothPropertyContainer.cpp \
   BluetoothUtils.cpp \
   BluetoothChild.cpp \
   BluetoothParent.cpp \
   BluetoothServiceChildProcess.cpp \
+  BluetoothUnixSocketConnector.cpp \
   $(NULL)
 
 XPIDLSRCS = \
   nsIDOMNavigatorBluetooth.idl \
   nsIDOMBluetoothManager.idl \
   nsIDOMBluetoothAdapter.idl \
   nsIDOMBluetoothDevice.idl \
   nsIDOMBluetoothDeviceEvent.idl \
--- a/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp
+++ b/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp
@@ -198,33 +198,26 @@ BluetoothServiceChildProcess::RemoveDevi
 {
   SendRequest(aRunnable,
               UnpairRequest(nsString(aAdapterPath), nsString(aObjectPath)));
   return NS_OK;
 }
 
 nsresult
 BluetoothServiceChildProcess::GetSocketViaService(
-                                              const nsAString& aObjectPath,
-                                              const nsAString& aService,
-                                              int aType,
-                                              bool aAuth,
-                                              bool aEncrypt,
-                                              BluetoothReplyRunnable* aRunnable)
+                                       const nsAString& aObjectPath,
+                                       const nsAString& aService,
+                                       BluetoothSocketType aType,
+                                       bool aAuth,
+                                       bool aEncrypt,
+                                       mozilla::ipc::UnixSocketConsumer* aConsumer,
+                                       BluetoothReplyRunnable* aRunnable)
 {
-  MOZ_NOT_REACHED("Implement me!");
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-bool
-BluetoothServiceChildProcess::CloseSocket(int aFd,
-                                          BluetoothReplyRunnable* aRunnable)
-{
-  MOZ_NOT_REACHED("Implement me!");
-  return false;
+  MOZ_NOT_REACHED("This should never be called!");
+  return NS_ERROR_FAILURE;
 }
 
 bool
 BluetoothServiceChildProcess::SetPinCodeInternal(
                                                 const nsAString& aDeviceAddress,
                                                 const nsAString& aPinCode,
                                                 BluetoothReplyRunnable* aRunnable)
 {
--- a/dom/bluetooth/ipc/BluetoothServiceChildProcess.h
+++ b/dom/bluetooth/ipc/BluetoothServiceChildProcess.h
@@ -5,16 +5,19 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_bluetooth_ipc_bluetoothservicechildprocess_h__
 #define mozilla_dom_bluetooth_ipc_bluetoothservicechildprocess_h__
 
 #include "BluetoothService.h"
 
 namespace mozilla {
+namespace ipc {
+class UnixSocketConsumer;
+}
 namespace dom {
 namespace bluetooth {
 
 class BluetoothChild;
 
 } // namespace bluetooth
 } // namespace dom
 } // namespace mozilla
@@ -81,39 +84,37 @@ public:
   virtual nsresult
   RemoveDeviceInternal(const nsAString& aAdapterPath,
                        const nsAString& aObjectPath,
                        BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
 
   virtual nsresult
   GetSocketViaService(const nsAString& aObjectPath,
                       const nsAString& aService,
-                      int aType,
+                      BluetoothSocketType aType,
                       bool aAuth,
                       bool aEncrypt,
+                      mozilla::ipc::UnixSocketConsumer* aConsumer,
                       BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
 
   virtual bool
-  CloseSocket(int aFd, BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
-
-  virtual bool
   SetPinCodeInternal(const nsAString& aDeviceAddress,
                      const nsAString& aPinCode,
                      BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
 
   virtual bool
   SetPasskeyInternal(const nsAString& aDeviceAddress,
                      uint32_t aPasskey,
                      BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
 
   virtual bool
   SetPairingConfirmationInternal(const nsAString& aDeviceAddress,
                                  bool aConfirm,
                                  BluetoothReplyRunnable* aRunnable)
-    MOZ_OVERRIDE;
+                                 MOZ_OVERRIDE;
 
   virtual bool
   SetAuthorizationInternal(const nsAString& aDeviceAddress,
                            bool aAllow,
                            BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
 
 protected:
   BluetoothServiceChildProcess();
--- a/dom/bluetooth/linux/BluetoothDBusService.cpp
+++ b/dom/bluetooth/linux/BluetoothDBusService.cpp
@@ -15,26 +15,27 @@
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 */
 
 #include "base/basictypes.h"
 #include "BluetoothDBusService.h"
 #include "BluetoothServiceUuid.h"
 #include "BluetoothReplyRunnable.h"
+#include "BluetoothUnixSocketConnector.h"
 
 #include <cstdio>
 #include <dbus/dbus.h>
 
 #include "nsIDOMDOMRequest.h"
 #include "nsAutoPtr.h"
 #include "nsThreadUtils.h"
 #include "nsDebug.h"
 #include "nsDataHashtable.h"
-#include "mozilla/ipc/Socket.h"
+#include "mozilla/ipc/UnixSocket.h"
 #include "mozilla/ipc/DBusThread.h"
 #include "mozilla/ipc/DBusUtils.h"
 #include "mozilla/ipc/RawDBusConnection.h"
 #include "mozilla/Util.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 
 /**
  * Some rules for dealing with memory in DBus:
@@ -1151,19 +1152,29 @@ EventFilter(DBusConnection* aConn, DBusM
                       ArrayLength(sDeviceProperties));
       if (v.type() == BluetoothValue::TArrayOfBluetoothNamedValue)
       {
         // The DBus DeviceFound message actually passes back a key value object
         // with the address as the key and the rest of the device properties as
         // a dict value. After we parse out the properties, we need to go back
         // and add the address to the ipdl dict we've created to make sure we
         // have all of the information to correctly build the device.
+        nsString addrstr = NS_ConvertUTF8toUTF16(addr);
+        nsString path = GetObjectPathFromAddress(signalPath, addrstr);
+
         v.get_ArrayOfBluetoothNamedValue()
           .AppendElement(BluetoothNamedValue(NS_LITERAL_STRING("Address"),
-                                             NS_ConvertUTF8toUTF16(addr)));
+                                             addrstr));
+
+        // We also need to create a path for the device, to make sure we know
+        // where to access it later.
+        v.get_ArrayOfBluetoothNamedValue()
+          .AppendElement(BluetoothNamedValue(NS_LITERAL_STRING("Path"),
+                                             path));
+
       }
     } else {
       errorStr.AssignLiteral("DBus device found message structure not as expected!");
     }
   } else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, "DeviceDisappeared")) {
     const char* str;
     if (!dbus_message_get_args(aMsg, &err,
                                DBUS_TYPE_STRING, &str,
@@ -1566,18 +1577,25 @@ public:
     }
 
     InfallibleTArray<BluetoothNamedValue> properties = prop.get_ArrayOfBluetoothNamedValue();
     if (v.type() == BluetoothValue::TArrayOfBluetoothNamedValue) {
       // Return original dbus message parameters and also device name
       // for agent events "RequestConfirmation", "RequestPinCode", and "RequestPasskey"
       InfallibleTArray<BluetoothNamedValue> parameters = v.get_ArrayOfBluetoothNamedValue();
 
+      // For consistency, append path
+      nsString path = parameters[0].value();
+      BluetoothNamedValue pathprop;
+      pathprop.name().AssignLiteral("Path");
+      pathprop.value() = path;
+      parameters.AppendElement(pathprop);
+
       // Replace object path with device address
-      nsString address = GetAddressFromObjectPath(parameters[0].value());
+      nsString address = GetAddressFromObjectPath(path);
       parameters[0].value() = address;
 
       uint8_t i;
       for (i = 0; i < properties.Length(); i++) {
         // Append device name
         if (properties[i].name().EqualsLiteral("Name")) {
           properties[i].name().AssignLiteral("name");
           parameters.AppendElement(properties[i]);
@@ -1828,28 +1846,36 @@ BluetoothDBusService::GetDevicePath(cons
 int
 GetDeviceServiceChannel(const nsAString& aObjectPath,
                         const nsAString& aPattern,
                         int aAttributeId)
 {
   // This is a blocking call, should not be run on main thread.
   MOZ_ASSERT(!NS_IsMainThread());
 
+#ifdef MOZ_WIDGET_GONK
+  // GetServiceAttributeValue only exists in android's bluez dbus binding
+  // implementation
   nsCString tempPattern = NS_ConvertUTF16toUTF8(aPattern);
   const char* pattern = tempPattern.get();
 
   DBusMessage *reply =
     dbus_func_args(gThreadConnection->GetConnection(),
                    NS_ConvertUTF16toUTF8(aObjectPath).get(),
                    DBUS_DEVICE_IFACE, "GetServiceAttributeValue",
                    DBUS_TYPE_STRING, &pattern,
                    DBUS_TYPE_UINT16, &aAttributeId,
                    DBUS_TYPE_INVALID);
 
   return reply ? dbus_returns_int32(reply) : -1;
+#else
+  // FIXME/Bug 793977 qdot: Just return something for desktop, until we have a
+  // parser for the GetServiceAttributes xml block
+  return 1;
+#endif
 }
 
 // static
 bool
 BluetoothDBusService::RemoveReservedServicesInternal(const nsAString& aAdapterPath,
                                                      const nsTArray<uint32_t>& aServiceHandles)
 {
   MOZ_ASSERT(!NS_IsMainThread());
@@ -2141,133 +2167,87 @@ BluetoothDBusService::PrepareAdapterInte
 
   return NS_OK;
 }
 
 class CreateBluetoothSocketRunnable : public nsRunnable
 {
 public:
   CreateBluetoothSocketRunnable(BluetoothReplyRunnable* aRunnable,
+                                UnixSocketConsumer* aConsumer,
                                 const nsAString& aObjectPath,
                                 const nsAString& aServiceUUID,
-                                int aType,
+                                BluetoothSocketType aType,
                                 bool aAuth,
                                 bool aEncrypt)
     : mRunnable(dont_AddRef(aRunnable)),
+      mConsumer(aConsumer),
       mObjectPath(aObjectPath),
       mServiceUUID(aServiceUUID),
       mType(aType),
       mAuth(aAuth),
       mEncrypt(aEncrypt)
   {
   }
 
   nsresult
   Run()
   {
     NS_WARNING("Running create socket!\n");
     MOZ_ASSERT(!NS_IsMainThread());
 
     nsString address = GetAddressFromObjectPath(mObjectPath);
     int channel = GetDeviceServiceChannel(mObjectPath, mServiceUUID, 0x0004);
-    int fd = mozilla::ipc::GetNewSocket(mType, NS_ConvertUTF16toUTF8(address).get(),
-                                        channel, mAuth, mEncrypt);
     BluetoothValue v;
     nsString replyError;
-    if (fd < 0) {
+    BluetoothUnixSocketConnector c(mType, channel, mAuth, mEncrypt);
+    if (!mConsumer->ConnectSocket(c, NS_ConvertUTF16toUTF8(address).get())) {
       replyError.AssignLiteral("SocketConnectionError");
       DispatchBluetoothReply(mRunnable, v, replyError);
       return NS_ERROR_FAILURE;
     }
-
-    v = (uint32_t)fd;
-
+    // Bluetooth value needs to be set to something to succeed.
+    v = true;
     DispatchBluetoothReply(mRunnable, v, replyError);
 
     return NS_OK;
   }
 
 private:
   nsRefPtr<BluetoothReplyRunnable> mRunnable;
+  nsRefPtr<UnixSocketConsumer> mConsumer;
   nsString mObjectPath;
   nsString mServiceUUID;
-  int mType;
+  BluetoothSocketType mType;
   bool mAuth;
   bool mEncrypt;
 };
 
 nsresult
 BluetoothDBusService::GetSocketViaService(const nsAString& aObjectPath,
                                           const nsAString& aService,
-                                          int aType,
+                                          BluetoothSocketType aType,
                                           bool aAuth,
                                           bool aEncrypt,
+                                          mozilla::ipc::UnixSocketConsumer* aConsumer,
                                           BluetoothReplyRunnable* aRunnable)
 {
   NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!");
   if (!mConnection || !gThreadConnection) {
     NS_ERROR("Bluetooth service not started yet!");
     return NS_ERROR_FAILURE;
   }
   nsRefPtr<BluetoothReplyRunnable> runnable = aRunnable;
 
-  nsRefPtr<nsRunnable> func(new CreateBluetoothSocketRunnable(runnable, aObjectPath,
+  nsRefPtr<nsRunnable> func(new CreateBluetoothSocketRunnable(runnable,
+                                                              aConsumer,
+                                                              aObjectPath,
                                                               aService, aType,
                                                               aAuth, aEncrypt));
   if (NS_FAILED(mBluetoothCommandThread->Dispatch(func, NS_DISPATCH_NORMAL))) {
     NS_WARNING("Cannot dispatch firmware loading task!");
     return NS_ERROR_FAILURE;
   }
 
   runnable.forget();
   return NS_OK;
 }
 
-class CloseBluetoothSocketRunnable : public nsRunnable
-{
-public:
-  CloseBluetoothSocketRunnable(BluetoothReplyRunnable* aRunnable,
-                               int aFd)
-    : mRunnable(dont_AddRef(aRunnable)),
-      mFd(aFd)
-  {
-  }
-
-  nsresult
-  Run()
-  {
-    BluetoothValue v;
-    nsString replyError;
-    if (mozilla::ipc::CloseSocket(mFd) != 0) {
-      replyError.AssignLiteral("SocketConnectionError");
-      DispatchBluetoothReply(mRunnable, v, replyError);
-      return NS_ERROR_FAILURE;
-    }
-
-    DispatchBluetoothReply(mRunnable, v, replyError);
-
-    return NS_OK;
-  }
-
-private:
-  nsRefPtr<BluetoothReplyRunnable> mRunnable;
-  int mFd;
-};
-
-bool
-BluetoothDBusService::CloseSocket(int aFd, BluetoothReplyRunnable* aRunnable)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!");
-  if (!mConnection || !gThreadConnection) {
-    NS_ERROR("Bluetooth service not started yet!");
-    return false;
-  }
-  nsRefPtr<BluetoothReplyRunnable> runnable = aRunnable;
-
-  nsRefPtr<nsRunnable> func(new CloseBluetoothSocketRunnable(runnable, aFd));
-  if (NS_FAILED(mBluetoothCommandThread->Dispatch(func, NS_DISPATCH_NORMAL))) {
-    NS_WARNING("Cannot dispatch firmware loading task!");
-    return false;
-  }
-
-  runnable.forget();
-  return true;
-}
--- a/dom/bluetooth/linux/BluetoothDBusService.h
+++ b/dom/bluetooth/linux/BluetoothDBusService.h
@@ -65,23 +65,22 @@ public:
 
   static bool
   RemoveReservedServicesInternal(const nsAString& aAdapterPath,
                                  const nsTArray<uint32_t>& aServiceHandles);
 
   virtual nsresult
   GetSocketViaService(const nsAString& aObjectPath,
                       const nsAString& aService,
-                      int aType,
+                      BluetoothSocketType aType,
                       bool aAuth,
                       bool aEncrypt,
+                      mozilla::ipc::UnixSocketConsumer* aConsumer,
                       BluetoothReplyRunnable* aRunnable);
 
-  virtual bool CloseSocket(int aFd, BluetoothReplyRunnable* aRunnable);
-
   virtual nsresult
   CreatePairedDeviceInternal(const nsAString& aAdapterPath,
                              const nsAString& aDeviceAddress,
                              int aTimeout,
                              BluetoothReplyRunnable* aRunnable);
 
   virtual nsresult
   RemoveDeviceInternal(const nsAString& aAdapterPath,
--- a/dom/ipc/AppProcessPermissions.cpp
+++ b/dom/ipc/AppProcessPermissions.cpp
@@ -14,17 +14,17 @@
 
 using namespace mozilla::dom;
 using namespace mozilla::hal_sandbox;
 using namespace mozilla::services;
 
 namespace mozilla {
 
 bool
-AppProcessHasPermission(PBrowserParent* aActor, const char* aPermission)
+AssertAppProcessPermission(PBrowserParent* aActor, const char* aPermission)
 {
   if (!aActor) {
     NS_WARNING("Testing permissions for null actor");
     return false;
   }
 
   TabParent* tab = static_cast<TabParent*>(aActor);
   nsCOMPtr<mozIApplication> app = tab->GetApp();
@@ -41,27 +41,27 @@ AppProcessHasPermission(PBrowserParent* 
     printf_stderr("Security problem: App process does not have `%s' permission.  It will be killed.", aPermission);
     ContentParent* process = static_cast<ContentParent*>(aActor->Manager());
     process->KillHard();
   }
   return hasPermission;
 }
 
 bool
-AppProcessHasPermission(PContentParent* aActor, const char* aPermission)
+AssertAppProcessPermission(PContentParent* aActor, const char* aPermission)
 {
   const InfallibleTArray<PBrowserParent*>& browsers =
     aActor->ManagedPBrowserParent();
   for (uint32_t i = 0; i < browsers.Length(); ++i) {
-    if (AppProcessHasPermission(browsers[i], aPermission)) {
+    if (AssertAppProcessPermission(browsers[i], aPermission)) {
       return true;
     }
   }
   return false;
 }
 
 bool
-AppProcessHasPermission(PHalParent* aActor, const char* aPermission)
+AssertAppProcessPermission(PHalParent* aActor, const char* aPermission)
 {
-  return AppProcessHasPermission(aActor->Manager(), aPermission);
+  return AssertAppProcessPermission(aActor->Manager(), aPermission);
 }
 
 } // namespace mozilla
--- a/dom/ipc/AppProcessPermissions.h
+++ b/dom/ipc/AppProcessPermissions.h
@@ -1,51 +1,54 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: sw=2 ts=8 et :
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_Capabilities_h
-#define mozilla_Capabilities_h
+#ifndef mozilla_AppProcessPermissions_h
+#define mozilla_AppProcessPermissions_h
 
 namespace mozilla {
 
 namespace dom {
 class PBrowserParent;
 class PContentParent;
 }
 
 namespace hal_sandbox {
 class PHalParent;
 }
 
 /**
  * Return true iff the specified browser has the specified capability.
+ * If this returns false, the browser didn't have the permission and
+ * will be killed.
  */
 bool
-AppProcessHasPermissions(mozilla::dom::PBrowserParent* aActor,
-                         const char* aPermission);
+AssertAppProcessPermission(mozilla::dom::PBrowserParent* aActor,
+                           const char* aPermission);
 
 /**
  * Return true iff any of the PBrowsers loaded in this content process
- * has the specified capability.
+ * has the specified capability.  If this returns false, the process
+ * didn't have the permission and will be killed.
  */
 bool
-AppProcessHasPermission(mozilla::dom::PContentParent* aActor,
-                        const char* aPermission);
+AssertAppProcessPermission(mozilla::dom::PContentParent* aActor,
+                           const char* aPermission);
 
 bool
-AppProcessHasPermission(mozilla::hal_sandbox::PHalParent* aActor,
-                        const char* aPermission);
+AssertAppProcessPermission(mozilla::hal_sandbox::PHalParent* aActor,
+                           const char* aPermission);
 
 // NB: when adding capability checks for other IPDL actors, please add
 // them to this file and have them delegate to the two functions above
 // as appropriate.  For example,
 //
 //   bool AppProcessHasCapability(PNeckoParent* aActor) {
-//     return AppProcessHasCapability(aActor->Manager());
+//     return AssertAppProcessPermission(aActor->Manager());
 //   }
 
 } // namespace mozilla
 
-#endif // mozilla_Capabilities_h
+#endif // mozilla_AppProcessPermissions_h
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1445,17 +1445,17 @@ ContentParent::DeallocPExternalHelperApp
     ExternalHelperAppParent *parent = static_cast<ExternalHelperAppParent *>(aService);
     parent->Release();
     return true;
 }
 
 PSmsParent*
 ContentParent::AllocPSms()
 {
-    if (!AppProcessHasPermission(this, "sms")) {
+    if (!AssertAppProcessPermission(this, "sms")) {
         return nullptr;
     }
     return new SmsParent();
 }
 
 bool
 ContentParent::DeallocPSms(PSmsParent* aSms)
 {
@@ -1475,17 +1475,17 @@ ContentParent::DeallocPStorage(PStorageP
     delete aActor;
     return true;
 }
 
 PBluetoothParent*
 ContentParent::AllocPBluetooth()
 {
 #ifdef MOZ_B2G_BT
-    if (!AppProcessHasPermission(this, "bluetooth")) {
+    if (!AssertAppProcessPermission(this, "bluetooth")) {
         return nullptr;
     }
     return new mozilla::dom::bluetooth::BluetoothParent();
 #else
     MOZ_NOT_REACHED("No support for bluetooth on this platform!");
     return nullptr;
 #endif
 }
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -225,17 +225,18 @@ parent:
      *   before load and refers a manifest and this manifest itself has not 
      *   been changed since the last fetch, we will not do the application 
      *   cache group update. But we must cache the document (identified by the
      *   documentURI). This argument will ensure that a previously uncached 
      *   document will get cached and that we don't re-cache a document that 
      *   has already been cached (stickDocument=false).
      */
     POfflineCacheUpdate(URIParams manifestURI, URIParams documentURI,
-                        nsCString clientID, bool stickDocument);
+                        bool isInBrowserElement, uint32_t appId,
+                        bool stickDocument);
 
     sync PIndexedDB(nsCString asciiOrigin)
         returns (bool allowed);
 
     /**
      * window.open from inside <iframe mozbrowser> is special.  When the child
      * process calls window.open, it creates a new PBrowser (in its own
      * process), then calls BrowserFrameOpenWindow on it.
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1172,19 +1172,20 @@ TabChild::RecvActivateFrameEvent(const n
   nsRefPtr<ContentListener> listener = new ContentListener(this);
   NS_ENSURE_TRUE(listener, true);
   chromeHandler->AddEventListener(aType, listener, capture);
   return true;
 }
 
 POfflineCacheUpdateChild*
 TabChild::AllocPOfflineCacheUpdate(const URIParams& manifestURI,
-            const URIParams& documentURI,
-            const nsCString& clientID,
-            const bool& stickDocument)
+                                   const URIParams& documentURI,
+                                   const bool& isInBrowserElement,
+                                   const uint32_t& appId,
+                                   const bool& stickDocument)
 {
   NS_RUNTIMEABORT("unused");
   return nullptr;
 }
 
 bool
 TabChild::DeallocPOfflineCacheUpdate(POfflineCacheUpdateChild* actor)
 {
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -248,19 +248,21 @@ public:
       child->mIPCOpen = true;
       return request;
     }
 #endif /* DEBUG */
 
     virtual PContentPermissionRequestChild* AllocPContentPermissionRequest(const nsCString& aType, const IPC::Principal& aPrincipal);
     virtual bool DeallocPContentPermissionRequest(PContentPermissionRequestChild* actor);
 
-    virtual POfflineCacheUpdateChild* AllocPOfflineCacheUpdate(const URIParams& manifestURI,
+    virtual POfflineCacheUpdateChild* AllocPOfflineCacheUpdate(
+            const URIParams& manifestURI,
             const URIParams& documentURI,
-            const nsCString& clientID,
+            const bool& isInBrowserElement,
+            const uint32_t& appId,
             const bool& stickDocument);
     virtual bool DeallocPOfflineCacheUpdate(POfflineCacheUpdateChild* offlineCacheUpdate);
 
     nsIWebNavigation* WebNavigation() { return mWebNav; }
 
     JSContext* GetJSContext() { return mCx; }
 
     nsIPrincipal* GetPrincipal() { return mPrincipal; }
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -1023,24 +1023,25 @@ TabParent::DeallocPRenderFrame(PRenderFr
 {
   delete aFrame;
   return true;
 }
 
 mozilla::docshell::POfflineCacheUpdateParent*
 TabParent::AllocPOfflineCacheUpdate(const URIParams& aManifestURI,
                                     const URIParams& aDocumentURI,
-                                    const nsCString& aClientID,
+                                    const bool& isInBrowserElement,
+                                    const uint32_t& appId,
                                     const bool& stickDocument)
 {
   nsRefPtr<mozilla::docshell::OfflineCacheUpdateParent> update =
     new mozilla::docshell::OfflineCacheUpdateParent();
 
-  nsresult rv = update->Schedule(aManifestURI, aDocumentURI, aClientID,
-                                 stickDocument);
+  nsresult rv = update->Schedule(aManifestURI, aDocumentURI,
+                                 isInBrowserElement, appId, stickDocument);
   if (NS_FAILED(rv))
     return nullptr;
 
   POfflineCacheUpdateParent* result = update.get();
   update.forget();
   return result;
 }
 
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -176,17 +176,18 @@ public:
 
     virtual PContentPermissionRequestParent*
     AllocPContentPermissionRequest(const nsCString& aType, const IPC::Principal& aPrincipal);
     virtual bool DeallocPContentPermissionRequest(PContentPermissionRequestParent* actor);
 
     virtual POfflineCacheUpdateParent* AllocPOfflineCacheUpdate(
             const URIParams& aManifestURI,
             const URIParams& aDocumentURI,
-            const nsCString& aClientID,
+            const bool& isInBrowserElement,
+            const uint32_t& appId,
             const bool& stickDocument);
     virtual bool DeallocPOfflineCacheUpdate(POfflineCacheUpdateParent* actor);
 
     JSBool GetGlobalJSObject(JSContext* cx, JSObject** globalp);
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSIAUTHPROMPTPROVIDER
     NS_DECL_NSISECUREBROWSERUI
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -100,16 +100,17 @@ FullScreenDeniedLostWindow=Request for f
 FullScreenDeniedSubDocFullScreen=Request for full-screen was denied because a subdocument of the document requesting full-screen is already full-screen.
 FullScreenDeniedNotDescendant=Request for full-screen was denied because requesting element is not a descendant of the current full-screen element.
 FullScreenDeniedNotFocusedTab=Request for full-screen was denied because requesting element is not in the currently focused tab.
 RemovedFullScreenElement=Exited full-screen because full-screen element was removed from document.
 FocusedWindowedPluginWhileFullScreen=Exited full-screen because windowed plugin was focused.
 HTMLMultipartXHRWarning=HTML parsing in XMLHttpRequest is not supported for multipart responses.
 HTMLSyncXHRWarning=HTML parsing in XMLHttpRequest is not supported in the synchronous mode.
 InvalidRedirectChannelWarning=Unable to redirect to %S because the channel doesn't implement nsIWritablePropertyBag2.
+ReportOnlyCSPIgnored=Report-only CSP policy will be ignored because there are other non-report-only CSP policies applied.
 ResponseTypeSyncXHRWarning=Use of XMLHttpRequest's responseType attribute is no longer supported in the synchronous mode in window context.
 WithCredentialsSyncXHRWarning=Use of XMLHttpRequest's withCredentials attribute is no longer supported in the synchronous mode in window context.
 TimeoutSyncXHRWarning=Use of XMLHttpRequest's timeout attribute is not supported in the synchronous mode in window context.
 JSONCharsetWarning=An attempt was made to declare a non-UTF-8 encoding for JSON retrieved using XMLHttpRequest. Only UTF-8 is supported for decoding JSON.
 MediaLoadExhaustedCandidates=All candidate resources failed to load. Media load paused.
 MediaLoadSourceMissingSrc=<source> element has no "src" attribute. Media resource load failed.
 # LOCALIZATION NOTE: %1$S is the Http error code the server returned (e.g. 404, 500, etc), %2$S is the URL of the media resource which failed to load.
 MediaLoadHttpError=HTTP load failed with status %1$S. Load of media resource %2$S failed.
--- a/dom/network/interfaces/Makefile.in
+++ b/dom/network/interfaces/Makefile.in
@@ -13,16 +13,17 @@ XPIDL_MODULE = dom_network
 
 include $(topsrcdir)/dom/dom-config.mk
 
 XPIDLSRCS = \
   nsIDOMNavigatorNetwork.idl \
   nsIDOMConnection.idl \
   nsIDOMUSSDReceivedEvent.idl \
   nsIDOMTCPSocket.idl \
+  nsIDOMDataErrorEvent.idl \
   $(NULL)
 
 ifdef MOZ_B2G_RIL
 XPIDLSRCS += \
   nsIDOMMobileConnection.idl \
   nsIMobileConnectionProvider.idl \
   nsINavigatorMobileConnection.idl \
   $(NULL)
new file mode 100644
--- /dev/null
+++ b/dom/network/interfaces/nsIDOMDataErrorEvent.idl
@@ -0,0 +1,11 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIDOMEvent.idl"
+
+[scriptable, builtinclass, uuid(1cfc45ba-c5d4-11e1-b4c3-00265511db39)]
+interface nsIDOMDataErrorEvent : nsIDOMEvent
+{
+  readonly attribute DOMString message;
+};
--- a/dom/network/interfaces/nsIDOMMobileConnection.idl
+++ b/dom/network/interfaces/nsIDOMMobileConnection.idl
@@ -7,17 +7,17 @@
 interface nsIDOMEventListener;
 interface nsIDOMDOMRequest;
 interface nsIDOMMozMobileICCInfo;
 interface nsIDOMMozMobileConnectionInfo;
 interface nsIDOMMozMobileNetworkInfo;
 interface nsIDOMMozMobileCellInfo;
 interface nsIDOMMozIccManager;
 
-[scriptable, builtinclass, uuid(d9009d90-a4b3-44fd-a592-42b09f330fe5)]
+[scriptable, builtinclass, uuid(c07309ee-a424-11e1-a75c-00265511db39)]
 interface nsIDOMMozMobileConnection : nsIDOMEventTarget
 {
   /**
    * Indicates the state of the device's ICC card.
    *
    * Possible values: null, 'absent', 'pinRequired', 'pukRequired',
    * 'networkLocked', 'ready'.
    */
@@ -234,16 +234,22 @@ interface nsIDOMMozMobileConnection : ns
    */
   [implicit_jscontext] attribute jsval ondatachange;
 
   /**
    * The 'ussdreceived' event is notified whenever a new USSD message is
    * received.
    */
   [implicit_jscontext] attribute jsval onussdreceived;
+
+  /**
+   * The 'dataerror' event is notified whenever the data connection object
+   * receives an error from the RIL
+   */
+  [implicit_jscontext] attribute jsval ondataerror;
 };
 
 [scriptable, uuid(5ea0e4a9-4684-40da-9930-8ebb61d187f3)]
 interface nsIDOMMozMobileConnectionInfo : nsISupports
 {
   /**
    * State of the connection.
    *
new file mode 100644
--- /dev/null
+++ b/dom/network/src/DataErrorEvent.cpp
@@ -0,0 +1,43 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DataErrorEvent.h"
+#include "nsIDOMClassInfo.h"
+
+DOMCI_DATA(DataErrorEvent, mozilla::dom::network::DataErrorEvent)
+
+namespace mozilla {
+namespace dom {
+namespace network {
+
+already_AddRefed<DataErrorEvent>
+DataErrorEvent::Create(nsAString& aMessage)
+{
+  NS_ASSERTION(!aMessage.IsEmpty(), "Empty message!");
+
+  nsRefPtr<DataErrorEvent> event = new DataErrorEvent();
+
+  event->mMessage = aMessage;
+
+  return event.forget();
+}
+
+NS_IMPL_ADDREF_INHERITED(DataErrorEvent, nsDOMEvent)
+NS_IMPL_RELEASE_INHERITED(DataErrorEvent, nsDOMEvent)
+
+NS_INTERFACE_MAP_BEGIN(DataErrorEvent)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMDataErrorEvent)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(DataErrorEvent)
+NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
+
+NS_IMETHODIMP
+DataErrorEvent::GetMessage(nsAString& aMessage)
+{
+  aMessage.Assign(mMessage);
+  return NS_OK;
+}
+
+}
+}
+}
new file mode 100644
--- /dev/null
+++ b/dom/network/src/DataErrorEvent.h
@@ -0,0 +1,63 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_network_dataerrorevent_h
+#define mozilla_dom_network_dataerrorevent_h
+
+#include "nsIDOMDataErrorEvent.h"
+#include "nsDOMEvent.h"
+
+namespace mozilla {
+namespace dom {
+namespace network {
+
+class DataErrorEvent : public nsDOMEvent,
+                       public nsIDOMDataErrorEvent
+{
+  nsString mMessage;
+
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_FORWARD_TO_NSDOMEVENT
+  NS_DECL_NSIDOMDATAERROREVENT
+
+  static already_AddRefed<DataErrorEvent>
+  Create(nsAString& aMessage);
+
+  nsresult
+  Dispatch(nsIDOMEventTarget* aTarget, const nsAString& aEventType)
+  {
+    NS_ASSERTION(aTarget, "Null pointer!");
+    NS_ASSERTION(!aEventType.IsEmpty(), "Empty event type!");
+
+    nsresult rv = InitEvent(aEventType, false, false);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = SetTrusted(true);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsIDOMEvent* thisEvent =
+      static_cast<nsDOMEvent*>(const_cast<DataErrorEvent*>(this));
+
+    bool dummy;
+    rv = aTarget->DispatchEvent(thisEvent, &dummy);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
+  }
+
+private:
+  DataErrorEvent()
+  : nsDOMEvent(nullptr, nullptr)
+  { }
+
+  ~DataErrorEvent()
+  { }
+};
+
+}
+}
+}
+
+#endif // mozilla_dom_network_dataerrorevent_h
--- a/dom/network/src/Makefile.in
+++ b/dom/network/src/Makefile.in
@@ -28,16 +28,17 @@ EXPORTS_mozilla/dom/network = \
   Types.h \
   Constants.h \
   $(NULL)
 
 CPPSRCS = \
   Connection.cpp \
   Utils.cpp \
   USSDReceivedEvent.cpp \
+  DataErrorEvent.cpp \
   $(NULL)
 
 ifdef MOZ_B2G_RIL
 CPPSRCS += \
   MobileConnection.cpp \
   $(NULL)
 endif
 
--- a/dom/network/src/MobileConnection.cpp
+++ b/dom/network/src/MobileConnection.cpp
@@ -3,38 +3,41 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MobileConnection.h"
 #include "nsIDOMDOMRequest.h"
 #include "nsIDOMClassInfo.h"
 #include "nsDOMEvent.h"
 #include "nsIObserverService.h"
 #include "USSDReceivedEvent.h"
+#include "DataErrorEvent.h"
 #include "mozilla/Services.h"
 #include "IccManager.h"
 
 #define NS_RILCONTENTHELPER_CONTRACTID "@mozilla.org/ril/content-helper;1"
 
 #define VOICECHANGE_EVENTNAME      NS_LITERAL_STRING("voicechange")
 #define DATACHANGE_EVENTNAME       NS_LITERAL_STRING("datachange")
 #define CARDSTATECHANGE_EVENTNAME  NS_LITERAL_STRING("cardstatechange")
 #define ICCINFOCHANGE_EVENTNAME    NS_LITERAL_STRING("iccinfochange")
 #define USSDRECEIVED_EVENTNAME     NS_LITERAL_STRING("ussdreceived")
+#define DATAERROR_EVENTNAME        NS_LITERAL_STRING("dataerror")
 
 DOMCI_DATA(MozMobileConnection, mozilla::dom::network::MobileConnection)
 
 namespace mozilla {
 namespace dom {
 namespace network {
 
 const char* kVoiceChangedTopic     = "mobile-connection-voice-changed";
 const char* kDataChangedTopic      = "mobile-connection-data-changed";
 const char* kCardStateChangedTopic = "mobile-connection-cardstate-changed";
 const char* kIccInfoChangedTopic   = "mobile-connection-iccinfo-changed";
 const char* kUssdReceivedTopic     = "mobile-connection-ussd-received";
+const char* kDataErrorTopic        = "mobile-connection-data-error";
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(MobileConnection)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MobileConnection,
                                                   nsDOMEventTargetHelper)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MobileConnection,
@@ -52,16 +55,17 @@ NS_INTERFACE_MAP_END_INHERITING(nsDOMEve
 NS_IMPL_ADDREF_INHERITED(MobileConnection, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(MobileConnection, nsDOMEventTargetHelper)
 
 NS_IMPL_EVENT_HANDLER(MobileConnection, cardstatechange)
 NS_IMPL_EVENT_HANDLER(MobileConnection, iccinfochange)
 NS_IMPL_EVENT_HANDLER(MobileConnection, voicechange)
 NS_IMPL_EVENT_HANDLER(MobileConnection, datachange)
 NS_IMPL_EVENT_HANDLER(MobileConnection, ussdreceived)
+NS_IMPL_EVENT_HANDLER(MobileConnection, dataerror)
 
 MobileConnection::MobileConnection()
 {
   mProvider = do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
 
   // Not being able to acquire the provider isn't fatal since we check
   // for it explicitly below.
   if (!mProvider) {
@@ -80,16 +84,17 @@ MobileConnection::Init(nsPIDOMWindow* aW
     return;
   }
 
   obs->AddObserver(this, kVoiceChangedTopic, false);
   obs->AddObserver(this, kDataChangedTopic, false);
   obs->AddObserver(this, kCardStateChangedTopic, false);
   obs->AddObserver(this, kIccInfoChangedTopic, false);
   obs->AddObserver(this, kUssdReceivedTopic, false);
+  obs->AddObserver(this, kDataErrorTopic, false);
 
   mIccManager = new icc::IccManager();
   mIccManager->Init(aWindow);
 }
 
 void
 MobileConnection::Shutdown()
 {
@@ -99,16 +104,17 @@ MobileConnection::Shutdown()
     return;
   }
 
   obs->RemoveObserver(this, kVoiceChangedTopic);
   obs->RemoveObserver(this, kDataChangedTopic);
   obs->RemoveObserver(this, kCardStateChangedTopic);
   obs->RemoveObserver(this, kIccInfoChangedTopic);
   obs->RemoveObserver(this, kUssdReceivedTopic);
+  obs->RemoveObserver(this, kDataErrorTopic);
 
   if (mIccManager) {
     mIccManager->Shutdown();
     mIccManager = nullptr;
   }
 }
 
 // nsIObserver
@@ -142,17 +148,28 @@ MobileConnection::Observe(nsISupports* a
     nsString ussd;
     ussd.Assign(aData);
     nsRefPtr<USSDReceivedEvent> event = USSDReceivedEvent::Create(ussd);
     NS_ASSERTION(event, "This should never fail!");
 
     nsresult rv =
       event->Dispatch(ToIDOMEventTarget(), USSDRECEIVED_EVENTNAME);
     NS_ENSURE_SUCCESS(rv, rv);
+    return NS_OK;
+  }
 
+  if (!strcmp(aTopic, kDataErrorTopic)) {
+    nsString dataerror;
+    dataerror.Assign(aData);
+    nsRefPtr<DataErrorEvent> event = DataErrorEvent::Create(dataerror);
+    NS_ASSERTION(event, "This should never fail!");
+
+    nsresult rv =
+      event->Dispatch(ToIDOMEventTarget(), DATAERROR_EVENTNAME);
+    NS_ENSURE_SUCCESS(rv, rv);
     return NS_OK;
   }
 
   MOZ_NOT_REACHED("Unknown observer topic!");
   return NS_OK;
 }
 
 // nsIDOMMozMobileConnection
--- a/dom/src/offline/nsDOMOfflineResourceList.cpp
+++ b/dom/src/offline/nsDOMOfflineResourceList.cpp
@@ -768,18 +768,26 @@ nsresult
 nsDOMOfflineResourceList::CacheKeys()
 {
   if (IS_CHILD_PROCESS()) 
     return NS_ERROR_NOT_IMPLEMENTED;
 
   if (mCachedKeys)
     return NS_OK;
 
+  nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(GetOwner());
+  nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(window);
+  nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav);
+
+  nsAutoCString groupID;
+  mApplicationCacheService->BuildGroupID(
+      mManifestURI, loadContext, groupID);
+
   nsCOMPtr<nsIApplicationCache> appCache;
-  mApplicationCacheService->GetActiveCache(mManifestSpec,
+  mApplicationCacheService->GetActiveCache(groupID,
                                            getter_AddRefs(appCache));
 
   if (!appCache) {
     return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
   return appCache->GatherEntries(nsIApplicationCache::ITEM_DYNAMIC,
                                  &mCachedKeysCount, &mCachedKeys);
--- a/dom/src/storage/PStorage.ipdl
+++ b/dom/src/storage/PStorage.ipdl
@@ -31,18 +31,17 @@ union StorageItem
 sync protocol PStorage
 {
   manager PContent;
 
 parent:
   __delete__();
 
   Init(bool useDB, bool sessionOnly, bool isPrivate,
-       nsCString domain, nsCString scopeDBKey,
-       nsCString quotaDBKey, uint32_t storageType);
+       nsCString scopeDBKey, nsCString quotaDBKey, uint32_t storageType);
   
   sync GetKeys(bool callerSecure)
       returns (nsString[] keys);
   sync GetLength(bool callerSecure, bool sessionOnly)
       returns (uint32_t length, nsresult rv);
   sync GetKey(bool callerSecure, bool sessionOnly, uint32_t index)
       returns (nsString key, nsresult rv);
   sync GetValue(bool callerSecure, bool sessionOnly, nsString key)
--- a/dom/src/storage/StorageChild.cpp
+++ b/dom/src/storage/StorageChild.cpp
@@ -78,31 +78,31 @@ StorageChild::CacheStoragePermissions()
 }
 
 void
 StorageChild::InitRemote()
 {
   ContentChild* child = ContentChild::GetSingleton();
   AddIPDLReference();
   child->SendPStorageConstructor(this, null_t());
-  SendInit(mUseDB, mSessionOnly, mInPrivateBrowsing, mDomain, mScopeDBKey,
+  SendInit(mUseDB, mSessionOnly, mInPrivateBrowsing, mScopeDBKey,
            mQuotaDBKey, mStorageType);
 }
 
 void
-StorageChild::InitAsSessionStorage(nsIURI* aDomainURI, bool aPrivate)
+StorageChild::InitAsSessionStorage(nsIPrincipal* aPrincipal, bool aPrivate)
 {
-  DOMStorageBase::InitAsSessionStorage(aDomainURI, aPrivate);
+  DOMStorageBase::InitAsSessionStorage(aPrincipal, aPrivate);
   InitRemote();
 }
 
 void
-StorageChild::InitAsLocalStorage(nsIURI* aDomainURI, bool aPrivate)
+StorageChild::InitAsLocalStorage(nsIPrincipal* aPrincipal, bool aPrivate)
 {
-  DOMStorageBase::InitAsLocalStorage(aDomainURI, aPrivate);
+  DOMStorageBase::InitAsLocalStorage(aPrincipal, aPrivate);
   InitRemote();
 }
 
 nsTArray<nsString>*
 StorageChild::GetKeys(bool aCallerSecure)
 {
   InfallibleTArray<nsString> remoteKeys;
   SendGetKeys(aCallerSecure, &remoteKeys);
@@ -228,17 +228,17 @@ StorageChild::SetSecure(const nsAString&
 nsresult
 StorageChild::CloneFrom(bool aCallerSecure, DOMStorageBase* aThat)
 {
   StorageChild* other = static_cast<StorageChild*>(aThat);
   ContentChild* child = ContentChild::GetSingleton();
   StorageClone clone(nullptr, other, aCallerSecure);
   AddIPDLReference();
   child->SendPStorageConstructor(this, clone);
-  SendInit(mUseDB, mSessionOnly, mInPrivateBrowsing, mDomain,
+  SendInit(mUseDB, mSessionOnly, mInPrivateBrowsing,
            mScopeDBKey, mQuotaDBKey, mStorageType);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 StorageChild::PrivateModeChanged(bool enabled)
 {
   mInPrivateBrowsing = enabled;
--- a/dom/src/storage/StorageChild.h
+++ b/dom/src/storage/StorageChild.h
@@ -21,18 +21,18 @@ class StorageChild : public PStorageChil
 public:
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(StorageChild, nsIPrivacyTransitionObserver)
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSIPRIVACYTRANSITIONOBSERVER
   
   StorageChild(nsDOMStorage* aOwner);
   StorageChild(nsDOMStorage* aOwner, StorageChild& aOther);
 
-  virtual void InitAsSessionStorage(nsIURI* aDomainURI, bool aPrivate);
-  virtual void InitAsLocalStorage(nsIURI* aDomainURI, bool aPrivate);
+  virtual void InitAsSessionStorage(nsIPrincipal* aPrincipal, bool aPrivate);
+  virtual void InitAsLocalStorage(nsIPrincipal* aPrincipal, bool aPrivate);
 
   virtual bool CacheStoragePermissions();
   
   virtual nsTArray<nsString>* GetKeys(bool aCallerSecure);
   virtual nsresult GetLength(bool aCallerSecure, uint32_t* aLength);
   virtual nsresult GetKey(bool aCallerSecure, uint32_t aIndex, nsAString& aKey);
   virtual nsIDOMStorageItem* GetValue(bool aCallerSecure, const nsAString& aKey,
                                       nsresult* rv);
--- a/dom/src/storage/StorageParent.cpp
+++ b/dom/src/storage/StorageParent.cpp
@@ -25,22 +25,21 @@ StorageParent::StorageParent(const Stora
     mStorage->CloneFrom(clone.callerSecure(), other->mStorage);
   }
 }
 
 bool
 StorageParent::RecvInit(const bool& aUseDB,
                         const bool& aSessionOnly,
                         const bool& aPrivate,
-                        const nsCString& aDomain,
                         const nsCString& aScopeDBKey,
                         const nsCString& aQuotaDBKey,
                         const uint32_t& aStorageType)
 {
-  mStorage->InitFromChild(aUseDB, aSessionOnly, aPrivate, aDomain,
+  mStorage->InitFromChild(aUseDB, aSessionOnly, aPrivate,
                           aScopeDBKey, aQuotaDBKey,
                           aStorageType);
   return true;
 }
 
 bool
 StorageParent::RecvUpdatePrivateState(const bool& aEnabled)
 {
--- a/dom/src/storage/StorageParent.h
+++ b/dom/src/storage/StorageParent.h
@@ -40,17 +40,16 @@ private:
                       nsresult* rv);
   bool RecvSetDBValue(const nsString& aKey, const nsString& aValue,
                       const bool& aSecure, nsresult* rv);
   bool RecvSetSecure(const nsString& aKey, const bool& aSecure, nsresult* rv);
 
   bool RecvInit(const bool& aUseDB,
                 const bool& aSessionOnly,
                 const bool& aPrivate,
-                const nsCString& aDomain,
                 const nsCString& aScopeDBKey,
                 const nsCString& aQuotaDBKey,
                 const uint32_t& aStorageType);
 
   bool RecvUpdatePrivateState(const bool& aEnabled);
 
   nsRefPtr<DOMStorageImpl> mStorage;
 };
--- a/dom/src/storage/nsDOMStorage.cpp
+++ b/dom/src/storage/nsDOMStorage.cpp
@@ -54,48 +54,16 @@ static const uint32_t BEHAVIOR_REJECT = 
 #define NS_DOMSTORAGE_MAXIMUM_TEMPTABLE_INACTIVITY_TIME (5)
 #define NS_DOMSTORAGE_MAXIMUM_TEMPTABLE_AGE (30)
 
 static const char kPermissionType[] = "cookie";
 static const char kStorageEnabled[] = "dom.storage.enabled";
 static const char kCookiesBehavior[] = "network.cookie.cookieBehavior";
 static const char kCookiesLifetimePolicy[] = "network.cookie.lifetimePolicy";
 
-// The URI returned is the innermost URI that should be used for
-// security-check-like stuff.  aHost is its hostname, correctly canonicalized.
-static nsresult
-GetPrincipalURIAndHost(nsIPrincipal* aPrincipal, nsIURI** aURI, nsCString& aHost)
-{
-  nsresult rv = aPrincipal->GetDomain(aURI);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (!*aURI) {
-    rv = aPrincipal->GetURI(aURI);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  if (!*aURI) {
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(*aURI);
-  if (!innerURI) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  rv = innerURI->GetAsciiHost(aHost);
-  if (NS_FAILED(rv)) {
-    return NS_ERROR_DOM_SECURITY_ERR;
-  }
-  
-  innerURI.swap(*aURI);
-
-  return NS_OK;
-}
-
 //
 // Helper that tells us whether the caller is secure or not.
 //
 
 static bool
 IsCallerSecure()
 {
   nsCOMPtr<nsIPrincipal> subjectPrincipal;
@@ -454,60 +422,44 @@ DOMStorageBase::DOMStorageBase()
   , mInPrivateBrowsing(false)
 {
 }
 
 DOMStorageBase::DOMStorageBase(DOMStorageBase& aThat)
   : mStorageType(aThat.mStorageType)
   , mUseDB(false) // Clones don't use the DB
   , mSessionOnly(true)
-  , mDomain(aThat.mDomain)
   , mScopeDBKey(aThat.mScopeDBKey)
   , mQuotaDBKey(aThat.mQuotaDBKey)
   , mInPrivateBrowsing(aThat.mInPrivateBrowsing)
 {
 }
 
 void
-DOMStorageBase::InitAsSessionStorage(nsIURI* aDomainURI, bool aPrivate)
+DOMStorageBase::InitAsSessionStorage(nsIPrincipal* aPrincipal, bool aPrivate)
 {
-  // No need to check for a return value. If this would fail we would not get
-  // here as we call GetPrincipalURIAndHost (nsDOMStorage.cpp:88) from
-  // nsDOMStorage::CanUseStorage before we query the storage manager for a new
-  // sessionStorage. It calls GetAsciiHost on innermost URI. If it fails, we
-  // won't get to InitAsSessionStorage.
-  aDomainURI->GetAsciiHost(mDomain);
-
+  MOZ_ASSERT(mQuotaDBKey.IsEmpty());
   mUseDB = false;
   mScopeDBKey.Truncate();
   mStorageType = nsPIDOMStorage::SessionStorage;
   mInPrivateBrowsing = aPrivate;
 }
 
 void
-DOMStorageBase::InitAsLocalStorage(nsIURI* aDomainURI,
-                                   bool aPrivate)
+DOMStorageBase::InitAsLocalStorage(nsIPrincipal* aPrincipal, bool aPrivate)
 {
-  // No need to check for a return value. If this would fail we would not get
-  // here as we call GetPrincipalURIAndHost (nsDOMStorage.cpp:88) from
-  // nsDOMStorage::CanUseStorage before we query the storage manager for a new
-  // localStorage. It calls GetAsciiHost on innermost URI. If it fails, we won't
-  // get to InitAsLocalStorage. Actually, mDomain will get replaced with
-  // mPrincipal in bug 455070. It is not even used for localStorage.
-  aDomainURI->GetAsciiHost(mDomain);
-
-  nsDOMStorageDBWrapper::CreateScopeDBKey(aDomainURI, mScopeDBKey);
+  nsDOMStorageDBWrapper::CreateScopeDBKey(aPrincipal, mScopeDBKey);
 
   // XXX Bug 357323, we have to solve the issue how to define
   // origin for file URLs. In that case CreateOriginScopeDBKey
   // fails (the result is empty) and we must avoid database use
   // in that case because it produces broken entries w/o owner.
   mUseDB = !mScopeDBKey.IsEmpty();
 
-  nsDOMStorageDBWrapper::CreateQuotaDBKey(mDomain, mQuotaDBKey);
+  nsDOMStorageDBWrapper::CreateQuotaDBKey(aPrincipal, mQuotaDBKey);
   mStorageType = nsPIDOMStorage::LocalStorage;
   mInPrivateBrowsing = aPrivate;
 }
 
 PLDHashOperator
 SessionStorageTraverser(nsSessionStorageEntry* aEntry, void* userArg) {
   nsCycleCollectionTraversalCallback *cb = 
       static_cast<nsCycleCollectionTraversalCallback*>(userArg);
@@ -584,49 +536,34 @@ DOMStorageImpl::InitDB()
   }
 
   return NS_OK;
 }
 
 void
 DOMStorageImpl::InitFromChild(bool aUseDB,
                               bool aSessionOnly, bool aPrivate,
-                              const nsACString& aDomain,
                               const nsACString& aScopeDBKey,
                               const nsACString& aQuotaDBKey,
                               uint32_t aStorageType)
 {
   mUseDB = aUseDB;
   mSessionOnly = aSessionOnly;
   mInPrivateBrowsing = aPrivate;
-  mDomain = aDomain;
   mScopeDBKey = aScopeDBKey;
   mQuotaDBKey = aQuotaDBKey;
   mStorageType = static_cast<nsPIDOMStorage::nsDOMStorageType>(aStorageType);
 }
 
 void
 DOMStorageImpl::SetSessionOnly(bool aSessionOnly)
 {
   mSessionOnly = aSessionOnly;
 }
 
-void
-DOMStorageImpl::InitAsSessionStorage(nsIURI* aDomainURI, bool aPrivate)
-{
-  DOMStorageBase::InitAsSessionStorage(aDomainURI, aPrivate);
-}
-
-void
-DOMStorageImpl::InitAsLocalStorage(nsIURI* aDomainURI,
-                                   bool aPrivate)
-{
-  DOMStorageBase::InitAsLocalStorage(aDomainURI, aPrivate);
-}
-
 bool
 DOMStorageImpl::CacheStoragePermissions()
 {
   // If this is a cross-process situation, we don't have a real storage owner.
   // All the correct checks have been done on the child, so we just need to
   // make sure that our session-only status is correctly updated.
   if (!mOwner)
     return CanUseStorage();
@@ -1117,76 +1054,55 @@ nsDOMStorage::nsDOMStorage(nsDOMStorage&
     mStorageImpl = new DOMStorageImpl(this, *other);
   }
 }
 
 nsDOMStorage::~nsDOMStorage()
 {
 }
 
-static
-nsresult
-GetDomainURI(nsIPrincipal *aPrincipal, bool aIncludeDomain, nsIURI **_domain)
-{
-  nsCOMPtr<nsIURI> uri;
-
-  if (aIncludeDomain) {
-    nsresult rv = aPrincipal->GetDomain(getter_AddRefs(uri));
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  if (!uri) {
-    nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  // Check if we really got any URI. System principal doesn't return a URI
-  // instance and we would crash in NS_GetInnermostURI below.
-  if (!uri)
-    return NS_ERROR_NOT_AVAILABLE;
-
-  nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(uri);
-  if (!innerURI)
-    return NS_ERROR_UNEXPECTED;
-  innerURI.forget(_domain);
-
-  return NS_OK;
-}
-
 nsresult
 nsDOMStorage::InitAsSessionStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI,
                                    bool aPrivate)
 {
-  nsCOMPtr<nsIURI> domainURI;
-  nsresult rv = GetDomainURI(aPrincipal, true, getter_AddRefs(domainURI));
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
   NS_ENSURE_SUCCESS(rv, rv);
 
+  if (!uri) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   mDocumentURI = aDocumentURI;
   mPrincipal = aPrincipal;
 
   mStorageType = SessionStorage;
 
-  mStorageImpl->InitAsSessionStorage(domainURI, aPrivate);
+  mStorageImpl->InitAsSessionStorage(mPrincipal, aPrivate);
   return NS_OK;
 }
 
 nsresult
 nsDOMStorage::InitAsLocalStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI,
                                  bool aPrivate)
 {
-  nsCOMPtr<nsIURI> domainURI;
-  nsresult rv = GetDomainURI(aPrincipal, false, getter_AddRefs(domainURI));
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
   NS_ENSURE_SUCCESS(rv, rv);
 
+  if (!uri) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   mDocumentURI = aDocumentURI;
   mPrincipal = aPrincipal;
 
   mStorageType = LocalStorage;
 
-  mStorageImpl->InitAsLocalStorage(domainURI, aPrivate);
+  mStorageImpl->InitAsLocalStorage(aPrincipal, aPrivate);
   return NS_OK;
 }
 
 bool
 DOMStorageBase::CanUseStorage()
 {
   return nsDOMStorage::CanUseStorage(this);
 }
@@ -1212,31 +1128,24 @@ nsDOMStorage::CanUseStorage(DOMStorageBa
   nsCOMPtr<nsIPrincipal> subjectPrincipal;
   nsresult rv = nsContentUtils::GetSecurityManager()->
                   GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
   NS_ENSURE_SUCCESS(rv, false);
 
   // if subjectPrincipal were null we'd have returned after
   // IsCallerChrome().
 
-  nsCOMPtr<nsIURI> subjectURI;
-  nsAutoCString unused;
-  if (NS_FAILED(GetPrincipalURIAndHost(subjectPrincipal,
-                                       getter_AddRefs(subjectURI),
-                                       unused))) {
-    return false;
-  }
-
   nsCOMPtr<nsIPermissionManager> permissionManager =
     do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
   if (!permissionManager)
     return false;
 
   uint32_t perm;
-  permissionManager->TestPermission(subjectURI, kPermissionType, &perm);
+  permissionManager->TestPermissionFromPrincipal(subjectPrincipal,
+                                                 kPermissionType, &perm);
 
   if (perm == nsIPermissionManager::DENY_ACTION)
     return false;
 
   // In private browsing mode we ougth to behave as in session-only cookies
   // mode to prevent detection of being in private browsing mode and ensuring
   // that there will be no traces left.
   if (perm == nsICookiePermission::ACCESS_SESSION ||
--- a/dom/src/storage/nsDOMStorage.h
+++ b/dom/src/storage/nsDOMStorage.h
@@ -104,18 +104,18 @@ protected:
 };
 
 class DOMStorageBase : public nsIPrivacyTransitionObserver
 {
 public:
   DOMStorageBase();
   DOMStorageBase(DOMStorageBase&);
 
-  virtual void InitAsSessionStorage(nsIURI* aDomainURI, bool aPrivate);
-  virtual void InitAsLocalStorage(nsIURI* aDomainURI, bool aPrivate);
+  virtual void InitAsSessionStorage(nsIPrincipal* aPrincipal, bool aPrivate);
+  virtual void InitAsLocalStorage(nsIPrincipal* aPrincipal, bool aPrivate);
 
   virtual nsTArray<nsString>* GetKeys(bool aCallerSecure) = 0;
   virtual nsresult GetLength(bool aCallerSecure, uint32_t* aLength) = 0;
   virtual nsresult GetKey(bool aCallerSecure, uint32_t aIndex, nsAString& aKey) = 0;
   virtual nsIDOMStorageItem* GetValue(bool aCallerSecure, const nsAString& aKey,
                                       nsresult* rv) = 0;
   virtual nsresult SetValue(bool aCallerSecure, const nsAString& aKey,
                             const nsAString& aData, nsAString& aOldValue) = 0;
@@ -185,19 +185,16 @@ protected:
 
   // true if the preferences indicates that this storage should be
   // session only. This member is updated by
   // CacheStoragePermissions(), using the current principal.
   // CacheStoragePermissions() must be called at each entry point to
   // make sure this stays up to date.
   bool mSessionOnly;
 
-  // domain this store is associated with
-  nsCString mDomain;
-
   // keys are used for database queries.
   // see comments of the getters bellow.
   nsCString mScopeDBKey;
   nsCString mQuotaDBKey;
 
   bool mInPrivateBrowsing;
 };
 
@@ -208,19 +205,16 @@ public:
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(DOMStorageImpl, nsIPrivacyTransitionObserver)
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSIPRIVACYTRANSITIONOBSERVER
 
   DOMStorageImpl(nsDOMStorage*);
   DOMStorageImpl(nsDOMStorage*, DOMStorageImpl&);
   ~DOMStorageImpl();
 
-  virtual void InitAsSessionStorage(nsIURI* aDomainURI, bool aPrivate);
-  virtual void InitAsLocalStorage(nsIURI* aDomainURI, bool aPrivate);
-
   bool SessionOnly() {
     return mSessionOnly;
   }
 
   virtual nsTArray<nsString>* GetKeys(bool aCallerSecure);
   virtual nsresult GetLength(bool aCallerSecure, uint32_t* aLength);
   virtual nsresult GetKey(bool aCallerSecure, uint32_t aIndex, nsAString& aKey);
   virtual nsIDOMStorageItem* GetValue(bool aCallerSecure, const nsAString& aKey,
@@ -276,17 +270,17 @@ private:
   friend class nsDOMStoragePersistentDB;
   friend class StorageParent;
 
   void Init(nsDOMStorage*);
 
   // Cross-process storage implementations never have InitAs(Session|Local|Global)Storage
   // called, so the appropriate initialization needs to happen from the child.
   void InitFromChild(bool aUseDB, bool aSessionOnly,
-                     bool aPrivate, const nsACString& aDomain,
+                     bool aPrivate,
                      const nsACString& aScopeDBKey,
                      const nsACString& aQuotaDBKey,
                      uint32_t aStorageType);
   void SetSessionOnly(bool aSessionOnly);
 
   static nsresult InitDB();
 
   // 0 initially or a positive data version number assigned by gStorageDB
--- a/dom/src/storage/nsDOMStorageDBWrapper.cpp
+++ b/dom/src/storage/nsDOMStorageDBWrapper.cpp
@@ -6,24 +6,26 @@
 #include "nsCOMPtr.h"
 #include "nsError.h"
 #include "nsDOMStorage.h"
 #include "nsDOMStorageDBWrapper.h"
 #include "nsIFile.h"
 #include "nsIURL.h"
 #include "nsIVariant.h"
 #include "nsIEffectiveTLDService.h"
+#include "nsIScriptSecurityManager.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "mozStorageCID.h"
 #include "mozStorageHelper.h"
 #include "mozIStorageService.h"
 #include "mozIStorageValueArray.h"
 #include "mozIStorageFunction.h"
 #include "nsPrintfCString.h"
 #include "nsNetUtil.h"
+#include "nsIPrincipal.h"
 
 void ReverseString(const nsCSubstring& source, nsCSubstring& result)
 {
   nsACString::const_iterator sourceBegin, sourceEnd;
   source.BeginReading(sourceBegin);
   source.EndReading(sourceEnd);
 
   result.SetLength(source.Length());
@@ -221,73 +223,84 @@ nsDOMStorageDBWrapper::GetUsage(const ns
   if (NS_SUECEEDED(rv))
     return rv;
 #endif
 
   return mPersistentDB.GetUsage(aDomain, aUsage);
 }
 
 nsresult
-nsDOMStorageDBWrapper::CreateScopeDBKey(nsIURI* aUri, nsACString& aKey)
+nsDOMStorageDBWrapper::CreateScopeDBKey(nsIPrincipal* aPrincipal,
+                                        nsACString& aKey)
 {
-  nsresult rv;
-
-  rv = CreateReversedDomain(aUri, aKey);
-  if (NS_FAILED(rv))
-    return rv;
-
-  nsAutoCString scheme;
-  rv = aUri->GetScheme(scheme);
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
   NS_ENSURE_SUCCESS(rv, rv);
-
-  aKey.AppendLiteral(":");
-  aKey.Append(scheme);
-
-  int32_t port = NS_GetRealPort(aUri);
-  if (port != -1) {
-    aKey.AppendLiteral(":");
-    aKey.Append(nsPrintfCString("%d", port));
-  }
-
-  return NS_OK;
-}
-
-nsresult
-nsDOMStorageDBWrapper::CreateReversedDomain(nsIURI* aUri, nsACString& aKey)
-{
-  nsresult rv;
+  NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
 
   nsAutoCString domainScope;
-  rv = aUri->GetAsciiHost(domainScope);
+  rv = uri->GetAsciiHost(domainScope);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (domainScope.IsEmpty()) {
     // About pages have an empty host but a valid path.  Since they are handled
     // internally by our own redirector, we can trust them and use path as key.
     // if file:/// protocol, let's make the exact directory the domain
     bool isScheme = false;
-    if ((NS_SUCCEEDED(aUri->SchemeIs("about", &isScheme)) && isScheme) ||
-        (NS_SUCCEEDED(aUri->SchemeIs("moz-safe-about", &isScheme)) && isScheme)) {
-      rv = aUri->GetPath(domainScope);
+    if ((NS_SUCCEEDED(uri->SchemeIs("about", &isScheme)) && isScheme) ||
+        (NS_SUCCEEDED(uri->SchemeIs("moz-safe-about", &isScheme)) && isScheme)) {
+      rv = uri->GetPath(domainScope);
       NS_ENSURE_SUCCESS(rv, rv);
       // While the host is always canonicalized to lowercase, the path is not,
       // thus need to force the casing.
       ToLowerCase(domainScope);
     }
-    else if (NS_SUCCEEDED(aUri->SchemeIs("file", &isScheme)) && isScheme) {
-      nsCOMPtr<nsIURL> url = do_QueryInterface(aUri, &rv);
+    else if (NS_SUCCEEDED(uri->SchemeIs("file", &isScheme)) && isScheme) {
+      nsCOMPtr<nsIURL> url = do_QueryInterface(uri, &rv);
       NS_ENSURE_SUCCESS(rv, rv);
       rv = url->GetDirectory(domainScope);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
-  rv = CreateReversedDomain(domainScope, aKey);
+  nsAutoCString key;
+
+  rv = CreateReversedDomain(domainScope, key);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoCString scheme;
+  rv = uri->GetScheme(scheme);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  key.Append(NS_LITERAL_CSTRING(":") + scheme);
+
+  int32_t port = NS_GetRealPort(uri);
+  if (port != -1) {
+    key.Append(nsPrintfCString(":%d", port));
+  }
+
+  uint32_t appId;
+  rv = aPrincipal->GetAppId(&appId);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool isInBrowserElement;
+  rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (appId == nsIScriptSecurityManager::NO_APP_ID && !isInBrowserElement) {
+    aKey.Assign(key);
+    return NS_OK;
+  }
+
+  aKey.Truncate();
+  aKey.AppendInt(appId);
+  aKey.Append(NS_LITERAL_CSTRING(":") + (isInBrowserElement ?
+              NS_LITERAL_CSTRING("t") : NS_LITERAL_CSTRING("f")) +
+              NS_LITERAL_CSTRING(":") + key);
+
   return NS_OK;
 }
 
 nsresult
 nsDOMStorageDBWrapper::CreateReversedDomain(const nsACString& aAsciiDomain,
                                             nsACString& aKey)
 {
   if (aAsciiDomain.IsEmpty())
@@ -295,54 +308,60 @@ nsDOMStorageDBWrapper::CreateReversedDom
 
   ReverseString(aAsciiDomain, aKey);
 
   aKey.AppendLiteral(".");
   return NS_OK;
 }
 
 nsresult
-nsDOMStorageDBWrapper::CreateQuotaDBKey(const nsACString& aAsciiDomain,
+nsDOMStorageDBWrapper::CreateQuotaDBKey(nsIPrincipal* aPrincipal,
                                         nsACString& aKey)
 {
   nsresult rv;
 
   nsAutoCString subdomainsDBKey;
   nsCOMPtr<nsIEffectiveTLDService> eTLDService(do_GetService(
     NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIURI> uri;
-  rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("http://") + aAsciiDomain);
+  rv = aPrincipal->GetURI(getter_AddRefs(uri));
   NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
 
   nsAutoCString eTLDplusOne;
   rv = eTLDService->GetBaseDomain(uri, 0, eTLDplusOne);
   if (NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS == rv) {
     // XXX bug 357323 - what to do for localhost/file exactly?
-    eTLDplusOne = aAsciiDomain;
-    rv = NS_OK;
+    rv = uri->GetAsciiHost(eTLDplusOne);
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   CreateReversedDomain(eTLDplusOne, subdomainsDBKey);
 
-  aKey.Assign(subdomainsDBKey);
-  return NS_OK;
-}
+  uint32_t appId;
+  rv = aPrincipal->GetAppId(&appId);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool isInBrowserElement;
+  rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
+  NS_ENSURE_SUCCESS(rv, rv);
 
-nsresult
-nsDOMStorageDBWrapper::GetDomainFromScopeKey(const nsACString& aScope,
-                                             nsACString& aDomain)
-{
-  nsAutoCString reverseDomain, scope;
-  scope = aScope;
-  scope.Left(reverseDomain, scope.FindChar(':')-1);
+  if (appId == nsIScriptSecurityManager::NO_APP_ID && !isInBrowserElement) {
+    aKey.Assign(subdomainsDBKey);
+    return NS_OK;
+  }
 
-  ReverseString(reverseDomain, aDomain);
+  aKey.Truncate();
+  aKey.AppendInt(appId);
+  aKey.Append(NS_LITERAL_CSTRING(":") + (isInBrowserElement ?
+              NS_LITERAL_CSTRING("t") : NS_LITERAL_CSTRING("f")) +
+              NS_LITERAL_CSTRING(":") + subdomainsDBKey);
+
   return NS_OK;
 }
 
 void
 nsDOMStorageDBWrapper::EnsureTempTableFlushTimer()
 {
   if (!mTempTableFlushTimer) {
     nsresult rv;
--- a/dom/src/storage/nsDOMStorageDBWrapper.h
+++ b/dom/src/storage/nsDOMStorageDBWrapper.h
@@ -168,35 +168,43 @@ public:
   bool
   IsScopeDirty(DOMStorageImpl* aStorage);
 
   /**
     * Turns "http://foo.bar.com:80" to "moc.rab.oof.:http:80",
     * i.e. reverses the host, appends a dot, appends the schema
     * and a port number.
     */
-  static nsresult CreateScopeDBKey(nsIURI* aUri, nsACString& aKey);
+  static nsresult CreateScopeDBKey(nsIPrincipal* aPrincipal, nsACString& aKey);
 
   /**
     * Turns "http://foo.bar.com" to "moc.rab.oof.",
     * i.e. reverses the host and appends a dot.
     */
   static nsresult CreateReversedDomain(nsIURI* aUri, nsACString& aKey);
   static nsresult CreateReversedDomain(const nsACString& aAsciiDomain, nsACString& aKey);
 
   /**
     * Turns "foo.bar.com" to "moc.rab.",
     * i.e. extracts eTLD+1 from the host, reverses the result
     * and appends a dot.
     */
-  static nsresult CreateQuotaDBKey(const nsACString& aAsciiDomain,
+  static nsresult CreateQuotaDBKey(nsIPrincipal* aPrincipal,
                                    nsACString& aKey);
 
-  static nsresult GetDomainFromScopeKey(const nsACString& aScope,
-                                        nsACString& aDomain);
+  /**
+    * Turns "foo.bar.com" to "moc.rab.",
+    * i.e. extracts eTLD+1 from the host, reverses the result
+    * and appends a dot.
+    */
+  static nsresult CreateQuotaDBKey(const nsACString& aDomain,
+                                   nsACString& aKey)
+  {
+    return CreateReversedDomain(aDomain, aKey);
+  }
 
   /**
    * Ensures the temp table flush timer is running. This is called when we add
    * data that will need to be flushed.
    */
   void EnsureTempTableFlushTimer();
 
   /**
--- a/dom/system/gonk/RILContentHelper.js
+++ b/dom/system/gonk/RILContentHelper.js
@@ -56,26 +56,28 @@ const RIL_IPC_MSG_NAMES = [
   "RIL:CallError",
   "RIL:CardLockResult",
   "RIL:UssdReceived",
   "RIL:SendUssd:Return:OK",
   "RIL:SendUssd:Return:KO",
   "RIL:CancelUssd:Return:OK",
   "RIL:CancelUssd:Return:KO",
   "RIL:StkCommand",
-  "RIL:StkSessionEnd"
+  "RIL:StkSessionEnd",
+  "RIL:DataError"
 ];
 
 const kVoiceChangedTopic     = "mobile-connection-voice-changed";
 const kDataChangedTopic      = "mobile-connection-data-changed";
 const kCardStateChangedTopic = "mobile-connection-cardstate-changed";
 const kIccInfoChangedTopic   = "mobile-connection-iccinfo-changed";
 const kUssdReceivedTopic     = "mobile-connection-ussd-received";
 const kStkCommandTopic       = "icc-manager-stk-command";
 const kStkSessionEndTopic    = "icc-manager-stk-session-end";
+const kDataErrorTopic        = "mobile-connection-data-error";
 
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
                                    "nsISyncMessageSender");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gUUIDGenerator",
                                    "@mozilla.org/uuid-generator;1",
                                    "nsIUUIDGenerator");
@@ -709,17 +711,21 @@ RILContentHelper.prototype = {
         }
         break;
       case "RIL:StkCommand":
         let jsonString = JSON.stringify(msg.json);
         Services.obs.notifyObservers(null, kStkCommandTopic, jsonString);
         break;
       case "RIL:StkSessionEnd":
         Services.obs.notifyObservers(null, kStkSessionEndTopic, null);
-      break;
+        break;
+      case "RIL:DataError":
+        this.updateConnectionInfo(msg.json, this.dataConnectionInfo);
+        Services.obs.notifyObservers(null, kDataErrorTopic, msg.json.error);
+        break;
     }
   },
 
   handleEnumerateCalls: function handleEnumerateCalls(calls) {
     debug("handleEnumerateCalls: " + JSON.stringify(calls));
     let callback = this._enumerationTelephonyCallbacks.shift();
     for (let i in calls) {
       let call = calls[i];
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -376,20 +376,17 @@ RadioInterfaceLayer.prototype = {
         break;
       case "voiceregistrationstatechange":
         this.updateVoiceConnection(message);
         break;
       case "dataregistrationstatechange":
         this.updateDataConnection(message);
         break;
       case "datacallerror":
-        // 3G Network revoked the data connection, possible unavailable APN
-        debug("Received data registration error message. Failed APN " +
-              this.dataCallSettings["apn"]);
-        RILNetworkInterface.reset();
+        this.handleDataCallError(message);
         break;
       case "signalstrengthchange":
         this.handleSignalStrengthChange(message);
         break;
       case "operatorchange":
         this.handleOperatorChange(message);
         break;
       case "radiostatechange":
@@ -640,16 +637,30 @@ RadioInterfaceLayer.prototype = {
     }
 
     if (!newInfo.batch) {
       ppmm.broadcastAsyncMessage("RIL:DataInfoChanged", dataInfo);
     }
     this.updateRILNetworkInterface();
   },
 
+  /**
+   * Handle data errors
+   */
+  handleDataCallError: function handleDataCallError(message) {
+    if (message.apn != this.dataCallSettings["apn"]) {
+      return;
+    }
+
+    // 3G Network revoked the data connection, possible unavailable APN
+    RILNetworkInterface.reset();
+    // Notify datacall error
+    ppmm.broadcastAsyncMessage("RIL:DataError", message);
+  },
+
   handleSignalStrengthChange: function handleSignalStrengthChange(message) {
     let voiceInfo = this.rilContext.voice;
     // TODO CDMA, EVDO, LTE, etc. (see bug 726098)
     if (voiceInfo.signalStrength != message.gsmDBM ||
         voiceInfo.relSignalStrength != message.gsmRelative) {
       voiceInfo.signalStrength = message.gsmDBM;
       voiceInfo.relSignalStrength = message.gsmRelative;
       ppmm.broadcastAsyncMessage("RIL:VoiceInfoChanged", voiceInfo);
--- a/dom/system/gonk/ril_consts.js
+++ b/dom/system/gonk/ril_consts.js
@@ -1714,16 +1714,63 @@ RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[C
 RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_CONGESTION]          = GECKO_CALL_ERROR_CONGESTION;
 RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_ACM_LIMIT_EXCEEDED]  = GECKO_CALL_ERROR_INCOMING_CALL_EXCEEDED;
 RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_CALL_BARRED]         = GECKO_CALL_ERROR_BARRED;
 RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_FDN_BLOCKED]         = GECKO_CALL_ERROR_FDN_BLOCKED;
 RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_IMSI_UNKNOWN_IN_VLR] = GECKO_CALL_ERROR_SUBSCRIBER_UNKNOWN;
 RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_IMEI_NOT_ACCEPTED]   = GECKO_CALL_ERROR_DEVICE_NOT_ACCEPTED;
 RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_ERROR_UNSPECIFIED]   = GECKO_CALL_ERROR_UNSPECIFIED;
 
+const GECKO_DATACALL_ERROR_OPERATOR_BARRED               = "OperatorBarredError";
+const GECKO_DATACALL_ERROR_INSUFFICIENT_RESOURCES        = "InsufficientResourcesError";
+const GECKO_DATACALL_ERROR_MISSING_UKNOWN_APN            = "MissingUnknownAPNError";
+const GECKO_DATACALL_ERROR_UNKNOWN_PDP_ADDRESS_TYPE      = "UnknownPDPAddressTypeError";
+const GECKO_DATACALL_ERROR_USER_AUTHENTICATION           = "UserAuthenticationError";
+const GECKO_DATACALL_ERROR_ACTIVATION_REJECT_GGSN        = "ActivationRejectGGSNError";
+const GECKO_DATACALL_ERROR_ACTIVATION_REJECT_UNSPECIFIED = "ActivationRejectUnspecifiedError";
+const GECKO_DATACALL_ERROR_SERVICE_OPTION_NOT_SUPPORTED  = "ServiceOptionNotSupportedError";
+const GECKO_DATACALL_ERROR_SERVICE_OPTION_NOT_SUBSCRIBED = "ServiceOptionNotSubscribedError";
+const GECKO_DATACALL_ERROR_SERVICE_OPTION_OUT_OF_ORDER   = "ServiceOptionOutOfOrderError";
+const GECKO_DATACALL_ERROR_NSAPI_IN_USE                  = "NSAPIInUseError";
+const GECKO_DATACALL_ERROR_ONLY_IPV4_ALLOWED             = "OnlyIPv4Error";
+const GECKO_DATACALL_ERROR_ONLY_IPV6_ALLOWED             = "OnlyIPv6Error";
+const GECKO_DATACALL_ERROR_ONLY_SINGLE_BEARER_ALLOWED    = "OnlySingleBearerAllowedError";
+const GECKO_DATACALL_ERROR_PROTOCOL_ERRORS               = "ProtocolErrorsError";
+const GECKO_DATACALL_ERROR_VOICE_REGISTRATION_FAIL       = "VoiceRegistrationFailError";
+const GECKO_DATACALL_ERROR_DATA_REGISTRATION_FAIL        = "DataRegistrationFailError";
+const GECKO_DATACALL_ERROR_SIGNAL_LOST                   = "SignalLostError";
+const GECKO_DATACALL_ERROR_PREF_RADIO_TECH_CHANGED       = "PrefRadioTechChangedError";
+const GECKO_DATACALL_ERROR_RADIO_POWER_OFF               = "RadioPowerOffError";
+const GECKO_DATACALL_ERROR_TETHERED_CALL_ACTIVE          = "TetheredCallActiveError";
+const GECKO_DATACALL_ERROR_UNSPECIFIED                   = "UnspecifiedError";
+
+const RIL_DATACALL_FAILCAUSE_TO_GECKO_DATACALL_ERROR = {};
+RIL_DATACALL_FAILCAUSE_TO_GECKO_DATACALL_ERROR[DATACALL_FAIL_OPERATOR_BARRED]               = GECKO_DATACALL_ERROR_OPERATOR_BARRED;
+RIL_DATACALL_FAILCAUSE_TO_GECKO_DATACALL_ERROR[DATACALL_FAIL_INSUFFICIENT_RESOURCES]        = GECKO_DATACALL_ERROR_INSUFFICIENT_RESOURCES;
+RIL_DATACALL_FAILCAUSE_TO_GECKO_DATACALL_ERROR[DATACALL_FAIL_MISSING_UKNOWN_APN]            = GECKO_DATACALL_ERROR_MISSING_UKNOWN_APN;
+RIL_DATACALL_FAILCAUSE_TO_GECKO_DATACALL_ERROR[DATACALL_FAIL_UNKNOWN_PDP_ADDRESS_TYPE]      = GECKO_DATACALL_ERROR_UNKNOWN_PDP_ADDRESS_TYPE;
+RIL_DATACALL_FAILCAUSE_TO_GECKO_DATACALL_ERROR[DATACALL_FAIL_USER_AUTHENTICATION]           = GECKO_DATACALL_ERROR_USER_AUTHENTICATION;
+RIL_DATACALL_FAILCAUSE_TO_GECKO_DATACALL_ERROR[DATACALL_FAIL_ACTIVATION_REJECT_GGSN]        = GECKO_DATACALL_ERROR_ACTIVATION_REJECT_GGSN;
+RIL_DATACALL_FAILCAUSE_TO_GECKO_DATACALL_ERROR[DATACALL_FAIL_ACTIVATION_REJECT_UNSPECIFIED] = GECKO_DATACALL_ERROR_ACTIVATION_REJECT_UNSPECIFIED;
+RIL_DATACALL_FAILCAUSE_TO_GECKO_DATACALL_ERROR[DATACALL_FAIL_SERVICE_OPTION_NOT_SUPPORTED]  = GECKO_DATACALL_ERROR_SERVICE_OPTION_NOT_SUPPORTED;
+RIL_DATACALL_FAILCAUSE_TO_GECKO_DATACALL_ERROR[DATACALL_FAIL_SERVICE_OPTION_NOT_SUBSCRIBED] = GECKO_DATACALL_ERROR_SERVICE_OPTION_NOT_SUBSCRIBED;
+RIL_DATACALL_FAILCAUSE_TO_GECKO_DATACALL_ERROR[DATACALL_FAIL_SERVICE_OPTION_OUT_OF_ORDER]   = GECKO_DATACALL_ERROR_SERVICE_OPTION_OUT_OF_ORDER;
+RIL_DATACALL_FAILCAUSE_TO_GECKO_DATACALL_ERROR[DATACALL_FAIL_NSAPI_IN_USE]                  = GECKO_DATACALL_ERROR_NSAPI_IN_USE;
+RIL_DATACALL_FAILCAUSE_TO_GECKO_DATACALL_ERROR[DATACALL_FAIL_ONLY_IPV4_ALLOWED]             = GECKO_DATACALL_ERROR_ONLY_IPV4_ALLOWED;
+RIL_DATACALL_FAILCAUSE_TO_GECKO_DATACALL_ERROR[DATACALL_FAIL_ONLY_IPV6_ALLOWED]             = GECKO_DATACALL_ERROR_ONLY_IPV6_ALLOWED;
+RIL_DATACALL_FAILCAUSE_TO_GECKO_DATACALL_ERROR[DATACALL_FAIL_ONLY_SINGLE_BEARER_ALLOWED]    = GECKO_DATACALL_ERROR_ONLY_SINGLE_BEARER_ALLOWED;
+RIL_DATACALL_FAILCAUSE_TO_GECKO_DATACALL_ERROR[DATACALL_FAIL_PROTOCOL_ERRORS]               = GECKO_DATACALL_ERROR_PROTOCOL_ERRORS;
+RIL_DATACALL_FAILCAUSE_TO_GECKO_DATACALL_ERROR[DATACALL_FAIL_VOICE_REGISTRATION_FAIL]       = GECKO_DATACALL_ERROR_VOICE_REGISTRATION_FAIL;
+RIL_DATACALL_FAILCAUSE_TO_GECKO_DATACALL_ERROR[DATACALL_FAIL_DATA_REGISTRATION_FAIL]        = GECKO_DATACALL_ERROR_DATA_REGISTRATION_FAIL;
+RIL_DATACALL_FAILCAUSE_TO_GECKO_DATACALL_ERROR[DATACALL_FAIL_SIGNAL_LOST]                   = GECKO_DATACALL_ERROR_SIGNAL_LOST;
+RIL_DATACALL_FAILCAUSE_TO_GECKO_DATACALL_ERROR[DATACALL_FAIL_PREF_RADIO_TECH_CHANGED]       = GECKO_DATACALL_ERROR_PREF_RADIO_TECH_CHANGED;
+RIL_DATACALL_FAILCAUSE_TO_GECKO_DATACALL_ERROR[DATACALL_FAIL_RADIO_POWER_OFF]               = GECKO_DATACALL_ERROR_RADIO_POWER_OFF;
+RIL_DATACALL_FAILCAUSE_TO_GECKO_DATACALL_ERROR[DATACALL_FAIL_TETHERED_CALL_ACTIVE]          = GECKO_DATACALL_ERROR_TETHERED_CALL_ACTIVE;
+RIL_DATACALL_FAILCAUSE_TO_GECKO_DATACALL_ERROR[DATACALL_FAIL_ERROR_UNSPECIFIED]             = GECKO_DATACALL_ERROR_UNSPECIFIED;
+
 const GECKO_RADIO_TECH = [
   null,
   "gprs",
   "edge",
   "umts",
   "is95a",
   "is95b",
   "1xrtt",
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -2895,17 +2895,36 @@ let RIL = {
         return true;
       case CALL_STATE_HOLDING:
       case CALL_STATE_INCOMING:
       case CALL_STATE_WAITING:
         return false;
     }
   },
 
+  _sendDataCallError: function _sendDataCallError(message, errorCode) {
+    message.rilMessageType = "datacallerror";
+    if (errorCode == ERROR_GENERIC_FAILURE) {
+      message.error = RIL_ERROR_TO_GECKO_ERROR[errorCode];
+    } else {
+      message.error = RIL_DATACALL_FAILCAUSE_TO_GECKO_DATACALL_ERROR[errorCode];
+    }
+    this.sendDOMMessage(message);
+  },
+
   _processDataCallList: function _processDataCallList(datacalls, newDataCallOptions) {
+    // Check for possible PDP errors: We check earlier because the datacall
+    // can be removed if is the same as the current one.
+    for each (let newDataCall in datacalls) {
+      if (newDataCall.status != DATACALL_FAIL_NONE) {
+        newDataCall.apn = newDataCallOptions.apn;
+        this._sendDataCallError(newDataCall, newDataCall.status);
+      }
+    }
+
     for each (let currentDataCall in this.currentDataCalls) {
       let updatedDataCall;
       if (datacalls) {
         updatedDataCall = datacalls[currentDataCall.cid];
         delete datacalls[currentDataCall.cid];
       }
 
       if (!updatedDataCall) {
@@ -3700,18 +3719,18 @@ RIL.readSetupDataCall_v5 = function read
   options.gw = gw;
   options.active = DATACALL_ACTIVE_UNKNOWN;
   options.state = GECKO_NETWORK_STATE_CONNECTING;
   return options;
 };
 
 RIL[REQUEST_SETUP_DATA_CALL] = function REQUEST_SETUP_DATA_CALL(length, options) {
   if (options.rilRequestError) {
-    options.rilMessageType = "datacallerror";
-    this.sendDOMMessage(options);
+    // On Data Call generic errors, we shall notify caller
+    this._sendDataCallError(options, options.rilRequestError);
     return;
   }
 
   if (RILQUIRKS_V5_LEGACY) {
     // Populate the `options` object with the data call information. That way
     // we retain the APN and other info about how the data call was set up.
     this.readSetupDataCall_v5(options);
     this.currentDataCalls[options.cid] = options;
--- a/dom/tests/mochitest/ajax/offline/foreign2.html
+++ b/dom/tests/mochitest/ajax/offline/foreign2.html
@@ -9,45 +9,45 @@
 <script class="testbody" type="text/javascript">
 
 function manifestUpdated()
 {
   var appCacheService = SpecialPowers.Components.classes["@mozilla.org/network/application-cache-service;1"]
     .getService(SpecialPowers.Ci.nsIApplicationCacheService);
 
   var foreign2cache = appCacheService.chooseApplicationCache(
-    "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign2.html");
+    "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign2.html", OfflineTest.loadContext());
 
   OfflineTest.ok(foreign2cache, "Foreign 2 cache present, chosen for foreign2.html");
-  OfflineTest.is(foreign2cache.groupID, "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign2.cacheManifest")
+  OfflineTest.is(foreign2cache.manifestURI.asciiSpec, "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign2.cacheManifest")
 
-  var foreign1cache = appCacheService.getActiveCache(
+  var foreign1cache = OfflineTest.getActiveCache(
     "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign1.cacheManifest");
   OfflineTest.ok(foreign1cache, "Foreign 1 cache loaded");
   foreign1cache.discard();
 
   OfflineTest.teardown();
   OfflineTest.finish();
 }
 
 function onLoaded()
 {
   var appCacheService = SpecialPowers.Components.classes["@mozilla.org/network/application-cache-service;1"]
     .getService(SpecialPowers.Ci.nsIApplicationCacheService);
 
-  var foreign1cache = appCacheService.getActiveCache(
+  var foreign1cache = OfflineTest.getActiveCache(
     "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign1.cacheManifest");
   OfflineTest.ok(foreign1cache, "Foreign 1 cache loaded");
 
-  var foreign2cache = appCacheService.getActiveCache(
+  var foreign2cache = OfflineTest.getActiveCache(
     "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign2.cacheManifest");
   OfflineTest.ok(!foreign2cache, "Foreign 2 cache not present");
 
   foreign1cache = appCacheService.chooseApplicationCache(
-    "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign2.html");
+    "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign2.html", OfflineTest.loadContext());
   OfflineTest.ok(!foreign1cache, "foreign2.html not chosen from foreign1 cache");
 
   try
   {
     OfflineTest.ok(applicationCache.status == SpecialPowers.Ci.nsIDOMOfflineResourceList.UNCACHED,
         "there is no associated application cache");
   }
   catch (ex)
--- a/dom/tests/mochitest/ajax/offline/offlineTests.js
+++ b/dom/tests/mochitest/ajax/offline/offlineTests.js
@@ -230,28 +230,43 @@ waitForAdd: function(url, onFinished) {
     cacheSession.asyncOpenCacheEntry(url,
                                      Ci.nsICache.ACCESS_READ,
                                      waitForAddListener);
   }
 
   setTimeout(this.priv(waitFunc), 500);
 },
 
-getManifestUrl: function()
+manifestURL: function(overload)
 {
-  return window.top.document.documentElement.getAttribute("manifest");
+  var manifestURLspec = overload || window.top.document.documentElement.getAttribute("manifest");
+
+  var ios = Cc["@mozilla.org/network/io-service;1"]
+            .getService(Ci.nsIIOService)
+
+  var baseURI = ios.newURI(window.location.href, null, null);
+  return ios.newURI(manifestURLspec, null, baseURI);
 },
 
-getActiveCache: function()
+loadContext: function()
+{
+  return SpecialPowers.wrap(window).QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+                                   .getInterface(Components.interfaces.nsIWebNavigation)
+                                   .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+                                   .getInterface(Components.interfaces.nsILoadContext);
+},
+
+getActiveCache: function(overload)
 {
   // Note that this is the current active cache in the cache stack, not the
   // one associated with this window.
   var serv = Cc["@mozilla.org/network/application-cache-service;1"]
              .getService(Ci.nsIApplicationCacheService);
-  return serv.getActiveCache(this.getManifestUrl());
+  var groupID = serv.buildGroupID(this.manifestURL(overload), this.loadContext());
+  return serv.getActiveCache(groupID);
 },
 
 getActiveSession: function()
 {
   var cache = this.getActiveCache();
   if (!cache)
     return null;
 
@@ -266,33 +281,16 @@ priv: function(func)
 {
   var self = this;
   return function() {
     netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
     func(arguments);
   }
 },
 
-checkCustomCache: function(group, url, expectEntry, callback)
-{
-  var serv = Cc["@mozilla.org/network/application-cache-service;1"]
-             .getService(Ci.nsIApplicationCacheService);
-  var cache = serv.getActiveCache(group);
-  var cacheSession = null;
-  if (cache) {
-    var cacheService = Cc["@mozilla.org/network/cache-service;1"]
-                       .getService(Ci.nsICacheService);
-    cacheSession = cacheService.createSession(cache.clientID,
-                                      Ci.nsICache.STORE_OFFLINE,
-                                      true);
-  }
-
-  this._checkCache(cacheSession, url, expectEntry, callback);
-},
-
 checkCacheEntries: function(entries, callback)
 {
   var checkNextEntry = function() {
     if (entries.length == 0) {
       setTimeout(OfflineTest.priv(callback), 0);
     } else {
       OfflineTest.checkCache(entries[0][0], entries[0][1], checkNextEntry);
       entries.shift();
--- a/dom/tests/mochitest/ajax/offline/test_bug460353.html
+++ b/dom/tests/mochitest/ajax/offline/test_bug460353.html
@@ -6,16 +6,17 @@
   This test checks that each iframe creates its own
   scope. Actually, we just check that it loads and updates
   its associated cache. There is no check that the cache is the
   expected one, there is no API to gain that information.
 -->
 
 <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+<script type="text/javascript" src="/tests/dom/tests/mochitest/ajax/offline/offlineTests.js"></script>
 
 <script class="testbody" type="text/javascript">
 
 var result = new Array();
 var expectedUpdates = 2;
 
 init();
 
@@ -91,22 +92,17 @@ function finish()
 
   SimpleTest.finish();
 }
 
 function cleanCache(manifestURL)
 {
   netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
 
-  var Cc = Components.classes;
-  var Ci = Components.interfaces;
-
-  var serv = Cc["@mozilla.org/network/application-cache-service;1"]
-             .getService(Ci.nsIApplicationCacheService);
-  var cache = serv.getActiveCache(manifestURL);
+  var cache = OfflineTest.getActiveCache(manifestURL);
   if (cache)
     cache.discard();
 }
 
 SimpleTest.waitForExplicitFinish();
 
 </script>
 
--- a/dom/tests/mochitest/ajax/offline/test_fallback.html
+++ b/dom/tests/mochitest/ajax/offline/test_fallback.html
@@ -121,11 +121,11 @@ if (OfflineTest.setup()) {
   applicationCache.oncached = OfflineTest.priv(manifestUpdated);
 }
 
 </script>
 
 </head>
 
 <body>
-<iframe name="fallbackFrame" src="http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/namespace1/non-existing.html"></iframe>
+<iframe name="fallbackFrame" src=""></iframe>
 </body>
 </html>
--- a/dom/tests/mochitest/ajax/offline/test_foreign.html
+++ b/dom/tests/mochitest/ajax/offline/test_foreign.html
@@ -20,20 +20,20 @@
  */
 
 function manifestUpdated()
 {
   var appCacheService = SpecialPowers.Components.classes["@mozilla.org/network/application-cache-service;1"]
     .getService(SpecialPowers.Ci.nsIApplicationCacheService);
 
   foreign1cache = appCacheService.chooseApplicationCache(
-    "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign2.html");
+    "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign2.html", OfflineTest.loadContext());
 
   OfflineTest.ok(foreign1cache, "foreign2.html chosen from foreign1 cache");
-  OfflineTest.is(foreign1cache.groupID, "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign1.cacheManifest")
+  OfflineTest.is(foreign1cache.manifestURI.asciiSpec, "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign1.cacheManifest")
 
   window.location = "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/foreign2.html";
 }
 
 SimpleTest.waitForExplicitFinish();
 
 if (OfflineTest.setup()) {
   applicationCache.onerror = OfflineTest.failEvent;
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -531,17 +531,18 @@ var interfaceNamesInGlobalScope =
     "CameraCapabilities",
     "CameraManager",
     "CSSSupportsRule",
     "MozMobileCellInfo",
     "MozCanvasPrintState",
     "TCPSocket",
     "MozTimeManager",
     "MozNavigatorTime",
-    "PermissionSettings"
+    "PermissionSettings",
+    "DataErrorEvent"
   ]
 
 for (var i in SpecialPowers.Components.interfaces) {
   var s = i.toString();
   var name = null;
   if (s.indexOf("nsIDOM") == 0) {
     name = s.substring("nsIDOM".length);
   } else if (s.indexOf("nsI") == 0) {
--- a/dom/tests/mochitest/localstorage/Makefile.in
+++ b/dom/tests/mochitest/localstorage/Makefile.in
@@ -7,31 +7,33 @@ DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir	= @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MOCHITEST_FILES	= \
+    frameAppIsolation.html \
     frameBug624047.html \
     frameChromeSlave.html \
     frameKeySync.html \
     frameMasterEqual.html \
     frameMasterNotEqual.html \
     frameSlaveEqual.html \
     frameSlaveNotEqual.html \
     frameReplace.html \
     frameQuota.html \
     frameQuotaSessionOnly.html \
     frameOrder.html \
     interOriginFrame.js \
     interOriginTest.js \
     interOriginTest2.js \
     pbSwitch.js \
+    test_appIsolation.html \
     test_brokenUTF-16.html \
     test_bug624047.html \
     test_bug746272-1.html \
     test_bug746272-2.html \
     test_cookieBlock.html \
     test_cookieSession-phase1.html \
     test_cookieSession-phase2.html \
     test_embededNulls.html \
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/localstorage/frameAppIsolation.html
@@ -0,0 +1,35 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <title>slave for app isolation test</title>
+  <script type="text/javascript">
+  var success = false;
+
+  var action = window.location.search.substring(1);
+
+  switch (action) {
+    case "read-no":
+      if (localStorage.getItem("0") == null) {
+        success = true;
+      }
+      break;
+    case "write":
+      localStorage.setItem("0", "foo");
+      success = true;
+      break;
+    case "read-yes":
+      if (localStorage.getItem("0") == "foo") {
+        success = true;
+      }
+      break;
+    case "clear":
+      localStorage.clear();
+      success = true;
+      break;
+  }
+  success ? alert("success") : alert("failure");
+  </script>
+</head>
+
+<body>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/localstorage/test_appIsolation.html
@@ -0,0 +1,149 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>localStorage app isolation</title>
+
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<script type="application/javascript;version=1.7">
+
+SimpleTest.waitForExplicitFinish();
+
+var fileTestOnCurrentOrigin = (location.protocol + "//" + location.host + location.pathname)
+                              .replace("test_a", "frameA");
+
+var previousPrefs = {
+  mozBrowserFramesEnabled: undefined,
+};
+
+try {
+  previousPrefs.mozBrowserFramesEnabled = SpecialPowers.getBoolPref('dom.mozBrowserFramesEnabled');
+} catch(e)
+{
+}
+
+SpecialPowers.setBoolPref('dom.mozBrowserFramesEnabled', true);
+
+SpecialPowers.addPermission("browser", true, window.document);
+
+var gData = [
+  // APP 1
+  {
+    app: 'http://example.org/manifest.webapp',
+    action: 'read-no',
+  },
+  {
+    app: 'http://example.org/manifest.webapp',
+    action: 'write',
+  },
+  {
+    app: 'http://example.org/manifest.webapp',
+    action: 'read-yes',
+  },
+  // APP 2
+  {
+    app: 'https://example.com/manifest.webapp',
+    action: 'read-no',
+  },
+  {
+    app: 'https://example.com/manifest.webapp',
+    action: 'write',
+  },
+  {
+    app: 'https://example.com/manifest.webapp',
+    action: 'read-yes',
+  },
+  // Browser
+  {
+    browser: true,
+    action: 'read-no',
+  },
+  {
+    browser: true,
+    action: 'write',
+  },
+  {
+    browser: true,
+    action: 'read-yes',
+  },
+  // Clear APP 1
+  {
+    app: 'http://example.org/manifest.webapp',
+    action: 'clear',
+  },
+  // Clear APP 2
+  {
+    app: 'https://example.com/manifest.webapp',
+    action: 'clear',
+  },
+  // Clear Browser
+  {
+    browser: true,
+    action: 'clear',
+  },
+];
+
+function runTest()
+{
+  for (var i in gData) {
+    var iframe = document.createElement('iframe');
+    var data = gData[i];
+
+    if (data.app) {
+      iframe.setAttribute('mozbrowser', '');
+      iframe.setAttribute('mozapp', data.app);
+    } else if (data.browser) {
+      iframe.setAttribute('mozbrowser', '');
+    }
+
+    if (data.app || data.browser) {
+      iframe.addEventListener('mozbrowsershowmodalprompt', function(e) {
+        is(e.detail.message, "success", "test number " + i);
+
+        document.body.removeChild(iframe);
+
+        i++;
+        if (i >= gData.length) {
+          localStorage.clear();
+
+          SpecialPowers.removePermission("browser", window.document);
+
+          if (previousPrefs.mozBrowserFramesEnabled !== undefined) {
+            SpecialPowers.setBoolPref('dom.mozBrowserFramesEnabled', previousPrefs.mozBrowserFramesEnabled);
+          }
+
+          SimpleTest.finish();
+        } else {
+          gTestRunner.next();
+        }
+      });
+    }
+
+    iframe.src = fileTestOnCurrentOrigin + "?" + data.action;
+
+    document.body.appendChild(iframe);
+
+    yield;
+  }
+}
+
+var gTestRunner = runTest();
+
+function startTest()
+{
+  is(localStorage.getItem("0"), null, "no data");
+  localStorage.setItem("0", "foo");
+  is(localStorage.getItem("0"), "foo", "data have been written");
+
+  gTestRunner.next();
+}
+
+addLoadEvent(startTest);
+
+</script>
+
+</head>
+
+<body>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/webidl/AudioBuffer.webidl
@@ -0,0 +1,28 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html
+ *
+ * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+
+[PrefControlled]
+interface AudioBuffer {
+
+    readonly attribute float sampleRate;
+    readonly attribute long length;
+
+    // in seconds 
+    readonly attribute float duration;
+
+    readonly attribute long numberOfChannels;
+
+    [Throws]
+    Float32Array getChannelData(unsigned long channel);
+
+};
+
--- a/dom/webidl/AudioContext.webidl
+++ b/dom/webidl/AudioContext.webidl
@@ -10,15 +10,21 @@
  * liability, trademark and document use rules apply.
  */
 
 [Constructor, PrefControlled]
 interface mozAudioContext {
 
     readonly attribute AudioDestinationNode destination;
 
+    [Creator, Throws]
+    AudioBuffer createBuffer(unsigned long numberOfChannels, unsigned long length, float sampleRate);
+
+    // [Creator, Throws]
+    // AudioBuffer createBuffer(ArrayBuffer buffer, boolean mixToMono);
+
     // AudioNode creation 
     AudioBufferSourceNode createBufferSource();
 
 };
 
 typedef mozAudioContext AudioContext;
 
--- a/dom/webidl/WebIDL.mk
+++ b/dom/webidl/WebIDL.mk
@@ -4,16 +4,17 @@
 
 webidl_base = $(topsrcdir)/dom/webidl
 
 generated_webidl_files = \
   CSS2Properties.webidl \
   $(NULL)
 
 webidl_files = \
+  AudioBuffer.webidl \
   AudioBufferSourceNode.webidl \
   AudioContext.webidl \
   AudioDestinationNode.webidl \
   AudioNode.webidl \
   AudioSourceNode.webidl \
   Blob.webidl \
   CanvasRenderingContext2D.webidl \
   ClientRectList.webidl \
--- a/embedding/android/GeckoAppShell.java
+++ b/embedding/android/GeckoAppShell.java
@@ -1523,30 +1523,16 @@ public class GeckoAppShell
         return info;
     }
 
     public static Class getSurfaceInfoClass() {
         Log.i("GeckoAppShell", "class name: " + SurfaceInfo.class.getName());
         return SurfaceInfo.class;
     }
 
-    static native void executeNextRunnable();
-
-    static class GeckoRunnableCallback implements Runnable {
-        public void run() {
-            Log.i("GeckoShell", "run GeckoRunnableCallback");
-            GeckoAppShell.executeNextRunnable();
-        }
-    }
-
-    public static void postToJavaThread(boolean mainThread) {
-        Log.i("GeckoShell", "post to " + (mainThread ? "main " : "") + "java thread");
-        getMainHandler().post(new GeckoRunnableCallback());
-    }
-
     public static android.hardware.Camera sCamera = null;
     
     static native void cameraCallbackBridge(byte[] data);
 
     static int kPreferedFps = 25;
     static byte[] sCameraBuffer = null;
 
     static int[] initCamera(String aContentType, int aCamera, int aWidth, int aHeight) {
deleted file mode 100644
--- a/gfx/2d/DrawEventRecorder.h.rej
+++ /dev/null
@@ -1,61 +0,0 @@
---- DrawEventRecorder.h
-+++ DrawEventRecorder.h
-@@ -6,34 +6,20 @@
- #ifndef MOZILLA_GFX_DRAWEVENTRECORDER_H_
- #define MOZILLA_GFX_DRAWEVENTRECORDER_H_
- 
- #include "2D.h"
- #include "RecordedEvent.h"
- #include <ostream>
- #include <fstream>
- 
--#if defined(_MSC_VER) || defined(MOZ_WIDGET_ANDROID)
-+#if defined(_MSC_VER)
- #include <hash_set>
- #else
--#include <ext/hash_set>
--#endif
--
--#ifndef _MSC_VER
--#ifndef MOZ_WIDGET_ANDROID
--namespace __gnu_cxx {
--#endif
--template<>
--struct hash<void *> {
--  size_t operator()(const void * __x) const {
--    return reinterpret_cast<size_t>(__x); }
--};
--#ifndef MOZ_WIDGET_ANDROID
--}
--#endif
-+#include <unordered_set>
- #endif
- 
- namespace mozilla {
- namespace gfx {
- 
- class PathRecording;
- 
- class DrawEventRecorderPrivate : public DrawEventRecorder
-@@ -62,20 +48,18 @@ public:
- 
- protected:
-   std::ostream *mOutputStream;
- 
-   virtual void Flush() = 0;
- 
- #if defined(_MSC_VER)
-   typedef stdext::hash_set<const void*> ObjectSet;
--#elif defined(MOZ_WIDGET_ANDROID)
--  typedef hash_set<const void*> ObjectSet;
- #else
--  typedef __gnu_cxx::hash_set<const void*, __gnu_cxx::hash<void*> > ObjectSet;
-+  typedef std::unordered_set<const void*> ObjectSet;
- #endif
- 
-   ObjectSet mStoredPaths;
-   ObjectSet mStoredScaledFonts;
- };
- 
- class DrawEventRecorderFile : public DrawEventRecorderPrivate
- {
--- a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp
+++ b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp
@@ -41,16 +41,19 @@
 
 #include "cairo-win32.h"
 #include "cairo-analysis-surface-private.h"
 #include "cairo-error-private.h"
 
 // Required for using placement new.
 #include <new>
 
+// HACK WARNING - Workaround for Windows 8 since we don't have the windows 8 SDK.
+#include "moz-d2d1-1.h"
+
 #define CAIRO_INT_STATUS_SUCCESS (cairo_int_status_t)CAIRO_STATUS_SUCCESS
 
 struct Vertex
 {
     float position[2];
 };
 
 // This factory is not device dependent, we can store it. But will clear it
@@ -895,29 +898,48 @@ push_clip (cairo_d2d_surface_t *d2dsurf,
 	RefPtr<ID2D1PathGeometry> geom = _cairo_d2d_create_path_geometry_for_path (&clip_path->path,
 							clip_path->fill_rule,
 							D2D1_FIGURE_BEGIN_FILLED);
 	RefPtr<ID2D1Layer> layer;
 
 	hr = d2dsurf->rt->CreateLayer (&layer);
 
 	D2D1_LAYER_OPTIONS options = D2D1_LAYER_OPTIONS_NONE;
+	D2D1_LAYER_OPTIONS1 options1 =  D2D1_LAYER_OPTIONS1_NONE;
+
 	if (d2dsurf->base.content == CAIRO_CONTENT_COLOR) {
 	    options = D2D1_LAYER_OPTIONS_INITIALIZE_FOR_CLEARTYPE;
+	    options1 = D2D1_LAYER_OPTIONS1_IGNORE_ALPHA;
+	    options1 = D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND;
 	}
 
-	d2dsurf->rt->PushLayer(D2D1::LayerParameters(
-		    D2D1::InfiniteRect(),
-		    geom,
-		    D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
-		    D2D1::IdentityMatrix(),
-		    1.0,
-		    0,
-		    options),
-		layer);
+	RefPtr<ID2D1DeviceContext> dc;
+	hr = d2dsurf->rt->QueryInterface(IID_ID2D1DeviceContext, (void**)&dc);
+
+	if (FAILED(hr)) {
+	    d2dsurf->rt->PushLayer(D2D1::LayerParameters(
+				       D2D1::InfiniteRect(),
+				       geom,
+				       D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
+				       D2D1::IdentityMatrix(),
+				       1.0,
+				       0,
+				       options),
+				   layer);
+	} else {
+	    dc->PushLayer(D2D1::LayerParameters1(
+			      D2D1::InfiniteRect(),
+			      geom,
+			      D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
+			      D2D1::IdentityMatrix(),
+			      1.0,
+			      0,
+			      options1),
+			  layer);
+	}
 
 	d2dsurf->d2d_clip = new d2d_clip_t(d2dsurf->d2d_clip, d2d_clip_t::LAYER);
    }
     if (!d2dsurf->d2d_clip)
 	return _cairo_error(CAIRO_STATUS_NO_MEMORY);
     return CAIRO_STATUS_SUCCESS;
 }
 
--- a/gfx/cairo/cairo/src/cairo-win32-surface.c
+++ b/gfx/cairo/cairo/src/cairo-win32-surface.c
@@ -889,19 +889,19 @@ static cairo_int_status_t
     return CAIRO_STATUS_SUCCESS;
 }
 
 /* makes the alpha channel in a RGB24 surface 0xff */
 static void
 make_opaque (cairo_image_surface_t *image, cairo_rectangle_int_t src_r)
 {
     int x; int y;
-    for (y = src_r.y; y < src_r.height; y++) {
-        for (x = src_r.x; x < src_r.width; x++) {
-            image->data[y * image->stride + x*4 + 3] = 0xff;
+    for (y = 0; y < src_r.height; y++) {
+        for (x = 0; x < src_r.width; x++) {
+            image->data[(src_r.y + y) * image->stride + (src_r.x + x)*4 + 3] = 0xff;
         }
     }
 }
 
 static cairo_int_status_t
 _cairo_win32_surface_composite_inner (cairo_win32_surface_t *src,
 				      cairo_image_surface_t *src_image,
 				      cairo_win32_surface_t *dst,
new file mode 100644
--- /dev/null
+++ b/gfx/cairo/cairo/src/moz-d2d1-1.h
@@ -0,0 +1,367 @@
+//---------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation.  All rights reserved.
+//
+// This file is automatically generated.  Please do not edit it directly.
+//
+// File name: D2D1_1.h
+//---------------------------------------------------------------------------
+#pragma once
+
+#ifndef _D2D1_1_H_
+#ifndef _D2D1_H_
+#include <d2d1.h>
+#endif // #ifndef _D2D1_H_
+
+//+-----------------------------------------------------------------------------
+//
+//  Flag:
+//      D2D1_LAYER_OPTIONS1
+//
+//  Synopsis:
+//      Specifies how the layer contents should be prepared.
+//
+//------------------------------------------------------------------------------
+typedef enum D2D1_LAYER_OPTIONS1
+{
+        D2D1_LAYER_OPTIONS1_NONE = 0,
+        D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND = 1,
+        D2D1_LAYER_OPTIONS1_IGNORE_ALPHA = 2,
+        D2D1_LAYER_OPTIONS1_FORCE_DWORD = 0xffffffff
+
+} D2D1_LAYER_OPTIONS1;
+
+//+-----------------------------------------------------------------------------
+//
+//  Struct:
+//      D2D1_LAYER_PARAMETERS1
+//
+//  Synopsis:
+//      All parameters related to pushing a layer.
+//
+//------------------------------------------------------------------------------
+typedef struct D2D1_LAYER_PARAMETERS1
+{
+    D2D1_RECT_F contentBounds;
+    ID2D1Geometry *geometricMask;
+    D2D1_ANTIALIAS_MODE maskAntialiasMode;
+    D2D1_MATRIX_3X2_F maskTransform;
+    FLOAT opacity;
+    ID2D1Brush *opacityBrush;
+    D2D1_LAYER_OPTIONS1 layerOptions;
+
+} D2D1_LAYER_PARAMETERS1;
+
+DEFINE_ENUM_FLAG_OPERATORS(D2D1_LAYER_OPTIONS1);
+
+#ifndef DX_DECLARE_INTERFACE
+#define DX_DECLARE_INTERFACE(x) DECLSPEC_UUID(x) DECLSPEC_NOVTABLE
+#endif
+
+namespace D2D1
+{
+    D2D1FORCEINLINE
+    D2D1_LAYER_PARAMETERS1
+    LayerParameters1(
+        CONST D2D1_RECT_F &contentBounds = D2D1::InfiniteRect(),
+        ID2D1Geometry *geometricMask = NULL,
+        D2D1_ANTIALIAS_MODE maskAntialiasMode = D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
+        D2D1_MATRIX_3X2_F maskTransform = D2D1::IdentityMatrix(),
+        FLOAT opacity = 1.0,
+        ID2D1Brush *opacityBrush = NULL,
+        D2D1_LAYER_OPTIONS1 layerOptions = D2D1_LAYER_OPTIONS1_NONE
+        )
+    {
+        D2D1_LAYER_PARAMETERS1 layerParameters = { 0 };
+
+        layerParameters.contentBounds = contentBounds;
+        layerParameters.geometricMask = geometricMask;
+        layerParameters.maskAntialiasMode = maskAntialiasMode;
+        layerParameters.maskTransform = maskTransform;
+        layerParameters.opacity = opacity;
+        layerParameters.opacityBrush = opacityBrush;
+        layerParameters.layerOptions = layerOptions;
+
+        return layerParameters;
+    }
+}
+
+DEFINE_GUID(IID_ID2D1DeviceContext, 0xe8f7fe7a, 0x191c, 0x466d, 0xad, 0x95, 0x97, 0x56, 0x78, 0xbd, 0xa9, 0x98);
+
+//+-----------------------------------------------------------------------------
+//
+//  Interface:
+//      ID2D1DeviceContext
+//
+//  Synopsis:
+//      The device context represents a set of state and a command buffer that is used
+//      to render to a target bitmap.
+//
+//------------------------------------------------------------------------------
+interface DX_DECLARE_INTERFACE("e8f7fe7a-191c-466d-ad95-975678bda998") ID2D1DeviceContext  : public ID2D1RenderTarget
+{
+    
+    
+    //
+    // Creates a bitmap with extended bitmap properties, potentially from a block of
+    // memory.
+    //
+    STDMETHOD(CreateBitmap)() PURE;
+    
+    //
+    // Create a D2D bitmap by copying a WIC bitmap.
+    //
+    STDMETHOD(CreateBitmapFromWicBitmap)() PURE;    
+    
+    //
+    // Creates a color context from a color space.  If the space is Custom, the context
+    // is initialized from the profile/profileSize arguments.  Otherwise the context is
+    // initialized with the profile bytes associated with the space and
+    // profile/profileSize are ignored.
+    //
+    STDMETHOD(CreateColorContext)() PURE;
+    
+    STDMETHOD(CreateColorContextFromFilename)() PURE;
+    
+    STDMETHOD(CreateColorContextFromWicColorContext)() PURE;
+    
+    
+    //
+    // Creates a bitmap from a DXGI surface with a set of extended properties.
+    //
+    STDMETHOD(CreateBitmapFromDxgiSurface)() PURE;
+    
+    
+    //
+    // Create a new effect, the effect must either be built in or previously registered
+    // through ID2D1Factory1::RegisterEffectFromStream or
+    // ID2D1Factory1::RegisterEffectFromString.
+    //
+    STDMETHOD(CreateEffect)() PURE;
+    
+    
+    //
+    // A gradient stop collection represents a set of stops in an ideal unit length.
+    // This is the source resource for a linear gradient and radial gradient brush.
+    //
+    STDMETHOD(CreateGradientStopCollection)() PURE;
+    
+    //
+    // Creates an image brush, the input image can be any type of image, including a
+    // bitmap, effect and a command list.
+    //
+    STDMETHOD(CreateImageBrush)() PURE;
+    
+    STDMETHOD(CreateBitmapBrush)() PURE;
+
+    //
+    // Creates a new command list.
+    //
+    STDMETHOD(CreateCommandList)() PURE;
+    
+    
+    //
+    // Indicates whether the format is supported by D2D.
+    //
+    STDMETHOD_(BOOL, IsDxgiFormatSupported)() CONST PURE;
+    
+    
+    //
+    // Indicates whether the buffer precision is supported by D2D.
+    //
+    STDMETHOD_(BOOL, IsBufferPrecisionSupported)() CONST PURE;
+    
+    
+    //
+    // This retrieves the local-space bounds in DIPs of the current image using the
+    // device context DPI.
+    //
+    STDMETHOD(GetImageLocalBounds)() CONST PURE;
+    
+    
+    //
+    // This retrieves the world-space bounds in DIPs of the current image using the
+    // device context DPI.
+    //
+    STDMETHOD(GetImageWorldBounds)() CONST PURE;
+    
+    
+    //
+    // Retrieves the world-space bounds in DIPs of the glyph run using the device
+    // context DPI.
+    //
+    STDMETHOD(GetGlyphRunWorldBounds)() CONST PURE;
+    
+    
+    //
+    // Retrieves the device associated with this device context.
+    //
+    STDMETHOD_(void, GetDevice)() CONST PURE;
+    
+    
+    //
+    // Sets the target for this device context to point to the given image. The image
+    // can be a command list or a bitmap created with the D2D1_BITMAP_OPTIONS_TARGET
+    // flag.
+    //
+    STDMETHOD_(void, SetTarget)() PURE;
+    
+    
+    //
+    // Gets the target that this device context is currently pointing to.
+    //
+    STDMETHOD_(void, GetTarget)() CONST PURE;
+    
+    
+    //
+    // Sets tuning parameters for internal rendering inside the device context.
+    //
+    STDMETHOD_(void, SetRenderingControls)() PURE;
+    
+    
+    //
+    // This retrieves the rendering controls currently selected into the device
+    // context.
+    //
+    STDMETHOD_(void, GetRenderingControls)() CONST PURE;
+    
+    
+    //
+    // Changes the primitive blending mode for all of the rendering operations.
+    //
+    STDMETHOD_(void, SetPrimitiveBlend)() PURE;
+    
+    
+    //
+    // Returns the primitive blend currently selected into the device context.
+    //
+    STDMETHOD_(void, GetPrimitiveBlend)(
+        ) CONST PURE;
+    
+    
+    //
+    // Changes the units used for all of the rendering operations.
+    //
+    STDMETHOD_(void, SetUnitMode)() PURE;
+    
+    
+    //
+    // Returns the unit mode currently set on the device context.
+    //
+    STDMETHOD_(void, GetUnitMode)(
+        ) CONST PURE;
+    
+    
+    //
+    // Draws the glyph run with an extended description to describe the glyphs.
+    //
+    STDMETHOD_(void, DrawGlyphRun)() PURE;
+    
+    //
+    // Draw an image to the device context. The image represents either a concrete
+    // bitmap or the output of an effect graph.
+    //
+    STDMETHOD_(void, DrawImage)() PURE;
+    
+    
+    //
+    // Draw a metafile to the device context.
+    //
+    STDMETHOD_(void, DrawGdiMetafile)() PURE;
+    
+    STDMETHOD_(void, DrawBitmap)() PURE;
+    
+    
+    //
+    // Push a layer on the device context.
+    //
+    STDMETHOD_(void, PushLayer)(
+        _In_ CONST D2D1_LAYER_PARAMETERS1 *layerParameters,
+        _In_opt_ ID2D1Layer *layer 
+        ) PURE;
+    
+    using ID2D1RenderTarget::PushLayer;
+    
+    
+    //
+    // This indicates that a portion of an effect's input is invalid. This method can
+    // be called many times.
+    //
+    STDMETHOD(InvalidateEffectInputRectangle)() PURE;
+    
+    
+    //
+    // Gets the number of invalid ouptut rectangles that have accumulated at the
+    // effect.
+    //
+    STDMETHOD(GetEffectInvalidRectangleCount)() PURE;
+    
+    
+    //
+    // Gets the invalid rectangles that are at the output of the effect.
+    //
+    STDMETHOD(GetEffectInvalidRectangles)() PURE;
+    
+    
+    //
+    // Gets the maximum region of each specified input which would be used during a
+    // subsequent rendering operation
+    //
+    STDMETHOD(GetEffectRequiredInputRectangles)() PURE;
+    
+    
+    //
+    // Fill using the alpha channel of the supplied opacity mask bitmap. The brush
+    // opacity will be modulated by the mask. The render target antialiasing mode must
+    // be set to aliased.
+    //
+    STDMETHOD_(void, FillOpacityMask)() PURE;
+    
+ 
+    HRESULT CreateBitmap1() { return S_OK; }
+    HRESULT CreateBitmap2() { return S_OK; }
+    HRESULT CreateBitmap3() { return S_OK; }
+    HRESULT CreateBitmap4() { return S_OK; }
+    
+    HRESULT CreateImageBrush1() { return S_OK; }
+    HRESULT CreateImageBrush2() { return S_OK; }
+    
+    HRESULT CreateBitmapBrush1() { return S_OK; }
+    HRESULT CreateBitmapBrush2() { return S_OK; }
+    HRESULT CreateBitmapBrush3() { return S_OK; }
+
+    //
+    // Draws the output of the effect as an image.
+    //
+    void DrawImage1() {}
+    void DrawImage2() {}
+    void DrawImage3() {}
+    void DrawImage4() {}
+    void DrawImage5() {}
+    void DrawImage6() {}
+    void DrawImage7() {}
+    
+    void
+    PushLayer(
+        CONST D2D1_LAYER_PARAMETERS1 &layerParameters,
+        _In_opt_ ID2D1Layer *layer 
+        )  
+    {
+        PushLayer(&layerParameters, layer);
+    }
+    
+    void DrawGdiMetafile1() {}
+    
+    void DrawBitmap1() {}
+    void DrawBitmap2() {}
+    void DrawBitmap3() {}
+    
+    void FillOpacityMask1() {}
+    void FillOpacityMask2() {}   
+    
+    //
+    // Sets tuning parameters for internal rendering inside the device context.
+    //
+    void SetRenderingControls1() {}
+}; // interface ID2D1DeviceContext
+
+#endif // #ifndef _D2D1_1_H_
--- a/gfx/layers/basic/BasicThebesLayer.cpp
+++ b/gfx/layers/basic/BasicThebesLayer.cpp
@@ -276,16 +276,27 @@ struct NS_STACK_CLASS AutoBufferTracker 
   nsAutoTArray<Maybe<AutoOpenSurface>, 3> mNewBuffers;
   BasicShadowableThebesLayer* mLayer;
 
 private:
   AutoBufferTracker(const AutoBufferTracker&) MOZ_DELETE;
   AutoBufferTracker& operator=(const AutoBufferTracker&) MOZ_DELETE;
 };
 
+BasicShadowableThebesLayer::~BasicShadowableThebesLayer()
+{
+  // Finish any use of mROFrontBuffer since the last ForwardTransaction(),
+  // before the Shadow frees the surface.
+  if (OptionalThebesBuffer::Tnull_t != mROFrontBuffer.type()) {
+    ShadowLayerForwarder::PlatformSyncBeforeUpdate();
+  }
+  DestroyBackBuffer();
+  MOZ_COUNT_DTOR(BasicShadowableThebesLayer);
+}
+
 void
 BasicShadowableThebesLayer::PaintThebes(gfxContext* aContext,
                                         Layer* aMaskLayer,
                                         LayerManager::DrawThebesLayerCallback aCallback,
                                         void* aCallbackData,
                                         ReadbackProcessor* aReadback)
 {
   if (!HasShadow()) {
--- a/gfx/layers/basic/BasicThebesLayer.h
+++ b/gfx/layers/basic/BasicThebesLayer.h
@@ -116,21 +116,17 @@ public:
   BasicShadowableThebesLayer(BasicShadowLayerManager* aManager)
     : BasicThebesLayer(aManager)
     , mBufferTracker(nullptr)
     , mIsNewBuffer(false)
     , mFrontAndBackBufferDiffer(false)
   {
     MOZ_COUNT_CTOR(BasicShadowableThebesLayer);
   }
-  virtual ~BasicShadowableThebesLayer()
-  {
-    DestroyBackBuffer();
-    MOZ_COUNT_DTOR(BasicShadowableThebesLayer);
-  }
+  virtual ~BasicShadowableThebesLayer();
 
   virtual void PaintThebes(gfxContext* aContext,
                            Layer* aMaskLayer,
                            LayerManager::DrawThebesLayerCallback aCallback,
                            void* aCallbackData,
                            ReadbackProcessor* aReadback);
 
   virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
--- a/gfx/layers/basic/BasicTiledThebesLayer.cpp
+++ b/gfx/layers/basic/BasicTiledThebesLayer.cpp
@@ -235,17 +235,26 @@ BasicTiledThebesLayer::PaintThebes(gfxCo
     mValidRegion = nsIntRegion();
   }
 
   nsIntRegion regionToPaint = mVisibleRegion;
   regionToPaint.Sub(regionToPaint, mValidRegion);
   if (regionToPaint.IsEmpty())
     return;
 
-  if (gfxPlatform::UseProgressiveTilePainting()) {
+  gfxSize resolution(1, 1);
+  for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) {
+    const FrameMetrics& metrics = parent->GetFrameMetrics();
+    resolution.width *= metrics.mResolution.width;
+    resolution.height *= metrics.mResolution.height;
+  }
+
+  // Force immediate tile painting when the layer has changed resolution.
+  if (gfxPlatform::UseProgressiveTilePainting() &&
+      mTiledBuffer.GetResolution() == resolution) {
     nsIntRegionRectIterator it(regionToPaint);
     const nsIntRect* rect = it.Next();
     if (!rect)
       return;
 
     // Currently we start painting from the first rect of the invalid
     // region and convert that into a tile.
     // TODO: Use a smart tile prioritization such as:
@@ -260,48 +269,30 @@ BasicTiledThebesLayer::PaintThebes(gfxCo
       nsIntRect(paintTileStartX, paintTileStartY,
                 mTiledBuffer.GetTileLength(), mTiledBuffer.GetTileLength()));
 
     if (!maxPaint.Contains(regionToPaint)) {
       // The region needed to paint is larger then our progressive chunk size
       // therefore update what we want to paint and ask for a new paint transaction.
       regionToPaint.And(regionToPaint, maxPaint);
       BasicManager()->SetRepeatTransaction();
-    }
 
-    // We want to continue to retain invalidated tiles that we're about to paint soon
-    // to prevent them from disapearing while doing progressive paint. However we only
-    // want to this if they were painted at the same resolution.
-    gfxSize resolution(1, 1);
-    for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) {
-      const FrameMetrics& metrics = parent->GetFrameMetrics();
-      resolution.width *= metrics.mResolution.width;
-      resolution.height *= metrics.mResolution.height;
+      // Make sure that tiles that fall outside of the visible region are discarded.
+      mValidRegion.And(mValidRegion, mVisibleRegion);
     }
 
-    nsIntRegion regionToRetain(mTiledBuffer.GetValidRegion());
-    if (false && mTiledBuffer.GetResolution() == resolution) {
-      // Retain stale tiles but keep them marked as invalid in mValidRegion
-      // so that they will be eventually repainted.
-      regionToRetain.And(regionToRetain, mVisibleRegion);
-      regionToRetain.Or(regionToRetain, regionToPaint);
-    } else {
-      regionToRetain = mValidRegion;
-      regionToRetain.Or(regionToRetain, regionToPaint);
-      mTiledBuffer.SetResolution(resolution);
-    }
-
-    // Paint and keep track of what we refreshed
-    mTiledBuffer.PaintThebes(this, regionToRetain, regionToPaint, aCallback, aCallbackData);
+    // Keep track of what we're about to refresh.
     mValidRegion.Or(mValidRegion, regionToPaint);
   } else {
-    mTiledBuffer.PaintThebes(this, mVisibleRegion, regionToPaint, aCallback, aCallbackData);
+    mTiledBuffer.SetResolution(resolution);
     mValidRegion = mVisibleRegion;
   }
 
+  mTiledBuffer.PaintThebes(this, mVisibleRegion, regionToPaint, aCallback, aCallbackData);
+
   mTiledBuffer.ReadLock();
   if (aMaskLayer) {
     static_cast<BasicImplData*>(aMaskLayer->ImplData())
       ->Paint(aContext, nullptr);
   }
 
   // Create a heap copy owned and released by the compositor. This is needed
   // since we're sending this over an async message and content needs to be
--- a/gfx/layers/ipc/ShadowLayerUtilsX11.cpp
+++ b/gfx/layers/ipc/ShadowLayerUtilsX11.cpp
@@ -172,17 +172,17 @@ ShadowLayerForwarder::PlatformDestroySha
 /*static*/ void
 ShadowLayerForwarder::PlatformSyncBeforeUpdate()
 {
   if (UsingXCompositing()) {
     // If we're using X surfaces, then we need to finish all pending
     // operations on the back buffers before handing them to the
     // parent, otherwise the surface might be used by the parent's
     // Display in between two operations queued by our Display.
-    XSync(DefaultXDisplay(), False);
+    FinishX(DefaultXDisplay());
   }
 }
 
 /*static*/ void
 ShadowLayerManager::PlatformSyncBeforeReplyUpdate()
 {
   if (UsingXCompositing()) {
     // If we're using X surfaces, we need to finish all pending
--- a/gfx/layers/ipc/ShadowLayers.h
+++ b/gfx/layers/ipc/ShadowLayers.h
@@ -314,16 +314,18 @@ public:
   /**
    * Flag the next paint as the first for a document.
    */
   void SetIsFirstPaint() { mIsFirstPaint = true; }
 
   virtual int32_t GetMaxTextureSize() const { return mMaxTextureSize; }
   void SetMaxTextureSize(int32_t aMaxTextureSize) { mMaxTextureSize = aMaxTextureSize; }
 
+  static void PlatformSyncBeforeUpdate();
+
 protected:
   ShadowLayerForwarder();
 
   PLayersChild* mShadowManager;
 
 private:
   bool AllocBuffer(const gfxIntSize& aSize,
                    gfxASurface::gfxContentType aContent,
@@ -374,18 +376,16 @@ private:
   static void
   CloseDescriptor(const SurfaceDescriptor& aDescriptor);
 
   static bool
   PlatformCloseDescriptor(const SurfaceDescriptor& aDescriptor);
 
   bool PlatformDestroySharedSurface(SurfaceDescriptor* aSurface);
 
-  static void PlatformSyncBeforeUpdate();
-
   Transaction* mTxn;
   int32_t mMaxTextureSize;
   LayersBackend mParentBackend;
 
   bool mIsFirstPaint;
 };
 
 class ShadowLayerManager : public LayerManager
--- a/hal/sandbox/SandboxHal.cpp
+++ b/hal/sandbox/SandboxHal.cpp
@@ -505,121 +505,121 @@ public:
 
   void Notify(const ScreenConfiguration& aScreenConfiguration) {
     unused << SendNotifyScreenConfigurationChange(aScreenConfiguration);
   }
 
   virtual bool
   RecvGetScreenEnabled(bool *enabled) MOZ_OVERRIDE
   {
-    if (!AppProcessHasPermission(this, "power")) {
+    if (!AssertAppProcessPermission(this, "power")) {
       return false;
     }
     *enabled = hal::GetScreenEnabled();
     return true;
   }
 
   virtual bool
   RecvSetScreenEnabled(const bool &enabled) MOZ_OVERRIDE
   {
-    if (!AppProcessHasPermission(this, "power")) {
+    if (!AssertAppProcessPermission(this, "power")) {
       return false;
     }
     hal::SetScreenEnabled(enabled);
     return true;
   }
 
   virtual bool
   RecvGetCpuSleepAllowed(bool *allowed) MOZ_OVERRIDE
   {
-    if (!AppProcessHasPermission(this, "power")) {
+    if (!AssertAppProcessPermission(this, "power")) {
       return false;
     }
     *allowed = hal::GetCpuSleepAllowed();
     return true;
   }
 
   virtual bool
   RecvSetCpuSleepAllowed(const bool &allowed) MOZ_OVERRIDE
   {
-    if (!AppProcessHasPermission(this, "power")) {
+    if (!AssertAppProcessPermission(this, "power")) {
       return false;
     }
     hal::SetCpuSleepAllowed(allowed);
     return true;
   }
 
   virtual bool
   RecvGetScreenBrightness(double *brightness) MOZ_OVERRIDE
   {
-    if (!AppProcessHasPermission(this, "power")) {
+    if (!AssertAppProcessPermission(this, "power")) {
       return false;
     }
     *brightness = hal::GetScreenBrightness();
     return true;
   }
 
   virtual bool
   RecvSetScreenBrightness(const double &brightness) MOZ_OVERRIDE
   {
-    if (!AppProcessHasPermission(this, "power")) {
+    if (!AssertAppProcessPermission(this, "power")) {
       return false;
     }
     hal::SetScreenBrightness(brightness);
     return true;
   }
 
   virtual bool
   RecvSetLight(const LightType& aLight,  const hal::LightConfiguration& aConfig, bool *status) MOZ_OVERRIDE
   {
     // XXX currently, the hardware key light and screen backlight are
     // controlled as a unit.  Those are set through the power API, and
     // there's no other way to poke lights currently, so we require
     // "power" privileges here.
-    if (!AppProcessHasPermission(this, "power")) {
+    if (!AssertAppProcessPermission(this, "power")) {
       return false;
     }
     *status = hal::SetLight(aLight, aConfig);
     return true;
   }
 
   virtual bool
   RecvGetLight(const LightType& aLight, LightConfiguration* aConfig, bool* status) MOZ_OVERRIDE
   {
-    if (!AppProcessHasPermission(this, "power")) {
+    if (!AssertAppProcessPermission(this, "power")) {
       return false;
     }
     *status = hal::GetLight(aLight, aConfig);
     return true;
   }
 
   virtual bool
   RecvAdjustSystemClock(const int32_t &aDeltaMilliseconds) MOZ_OVERRIDE
   {
-    if (!AppProcessHasPermission(this, "time")) {
+    if (!AssertAppProcessPermission(this, "time")) {
       return false;
     }
     hal::AdjustSystemClock(aDeltaMilliseconds);
     return true;
   }
 
   virtual bool 
   RecvSetTimezone(const nsCString& aTimezoneSpec) MOZ_OVERRIDE
   {
-    if (!AppProcessHasPermission(this, "time")) {
+    if (!AssertAppProcessPermission(this, "time")) {
       return false;
     }
     hal::SetTimezone(aTimezoneSpec);
     return true;  
   }
 
   virtual bool
   RecvGetTimezone(nsCString *aTimezoneSpec) MOZ_OVERRIDE
   {
-    if (!AppProcessHasPermission(this, "time")) {
+    if (!AssertAppProcessPermission(this, "time")) {
       return false;
     }
     *aTimezoneSpec = hal::GetTimezone();
     return true;
   }
 
   virtual bool
   RecvEnableSystemTimeChangeNotifications() MOZ_OVERRIDE
@@ -633,27 +633,27 @@ public:
   {
     hal::UnregisterSystemTimeChangeObserver(this);
     return true;
   }
 
   virtual bool
   RecvReboot() MOZ_OVERRIDE
   {
-    if (!AppProcessHasPermission(this, "power")) {
+    if (!AssertAppProcessPermission(this, "power")) {
       return false;
     }
     hal::Reboot();
     return true;
   }
 
   virtual bool
   RecvPowerOff() MOZ_OVERRIDE
   {
-    if (!AppProcessHasPermission(this, "power")) {
+    if (!AssertAppProcessPermission(this, "power")) {
       return false;
     }
     hal::PowerOff();
     return true;
   }
 
   virtual bool
   RecvEnableSensorNotifications(const SensorType &aSensor) MOZ_OVERRIDE {
@@ -696,17 +696,17 @@ public:
   {
     hal::UnregisterWakeLockObserver(this);
     return true;
   }
 
   virtual bool
   RecvGetWakeLockInfo(const nsString &aTopic, WakeLockInformation *aWakeLockInfo) MOZ_OVERRIDE
   {
-    if (!AppProcessHasPermission(this, "power")) {
+    if (!AssertAppProcessPermission(this, "power")) {
       return false;
     }
     hal::GetWakeLockInfo(aTopic, aWakeLockInfo);
     return true;
   }
   
   void Notify(const WakeLockInformation& aWakeLockInfo)
   {
--- a/ipc/Makefile.in
+++ b/ipc/Makefile.in
@@ -11,18 +11,22 @@ include $(DEPTH)/config/autoconf.mk
 
 DIRS += chromium glue ipdl testshell
 
 ifdef MOZ_B2G_RIL #{
 DIRS += ril
 endif #}
 
 ifdef MOZ_B2G_BT #{
-DIRS += dbus socket
+DIRS += dbus
 endif #}
 
 ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
-DIRS += netd
+DIRS += netd unixsocket
+endif
+
+ifeq ($(OS_ARCH),Linux)
+DIRS += unixsocket
 endif
 
 TOOL_DIRS = app
 
 include $(topsrcdir)/config/rules.mk
rename from ipc/socket/Makefile.in
rename to ipc/unixsocket/Makefile.in
--- a/ipc/socket/Makefile.in
+++ b/ipc/unixsocket/Makefile.in
@@ -5,27 +5,23 @@
 DEPTH = ../..
 topsrcdir = @top_srcdir@
 srcdir = @srcdir@
 VPATH = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = ipc
-LIBRARY_NAME = mozipcsocket_s
+LIBRARY_NAME = mozipcunixsocket_s
 FORCE_STATIC_LIB = 1
 LIBXUL_LIBRARY = 1
 EXPORT_LIBRARY = 1
 
 EXPORTS_NAMESPACES = mozilla/ipc
 
-EXPORTS_mozilla/ipc = \
-  Socket.h \
-  $(NULL)
+EXPORTS_mozilla/ipc = UnixSocket.h
 
-CPPSRCS += \
-  Socket.cpp \
-  $(NULL)
+CPPSRCS += UnixSocket.cpp
 
 include $(topsrcdir)/config/config.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
 
 include $(topsrcdir)/config/rules.mk
rename from ipc/socket/Socket.cpp
rename to ipc/unixsocket/UnixSocket.cpp
--- a/ipc/socket/Socket.cpp
+++ b/ipc/unixsocket/UnixSocket.cpp
@@ -1,180 +1,395 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "Socket.h"
+#include "UnixSocket.h"
 
+#include <fcntl.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <errno.h>
 
 #include <sys/socket.h>
 
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sco.h>
-#include <bluetooth/rfcomm.h>
-#include <bluetooth/l2cap.h>
-
-#include "nsThreadUtils.h"
+#include "base/eintr_wrapper.h"
+#include "base/message_loop.h"
 
-#undef LOG
-#if defined(MOZ_WIDGET_GONK)
-#include <android/log.h>
-#define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "Gonk", args)
-#else
-#define LOG(args...)  printf(args);
-#endif
-#define TYPE_AS_STR(t)                                                  \
-  ((t) == TYPE_RFCOMM ? "RFCOMM" : ((t) == TYPE_SCO ? "SCO" : "L2CAP"))
+#include "mozilla/Monitor.h"
+#include "mozilla/Util.h"
+#include "mozilla/FileUtils.h"
+#include "nsString.h"
+#include "nsThreadUtils.h"
+#include "nsTArray.h"
+#include "nsXULAppAPI.h"
 
 namespace mozilla {
 namespace ipc {
-static const int RFCOMM_SO_SNDBUF = 70 * 1024; // 70 KB send buffer
 
-static const int TYPE_RFCOMM = 1;
-static const int TYPE_SCO = 2;
-static const int TYPE_L2CAP = 3;
-
-static int get_bdaddr(const char *str, bdaddr_t *ba)
+class UnixSocketImpl : public MessageLoopForIO::Watcher
 {
-  char *d = ((char*)ba) + 5, *endp;
-  for (int i = 0; i < 6; i++) {
-    *d-- = strtol(str, &endp, 16);
-    MOZ_ASSERT(*endp != ':' && i != 5);
-    str = endp + 1;
+public:
+  UnixSocketImpl(UnixSocketConsumer* aConsumer, int aFd)
+    : mConsumer(aConsumer)
+    , mIOLoop(nullptr)
+    , mFd(aFd)
+  {
+  }
+
+  ~UnixSocketImpl()
+  {
+    mReadWatcher.StopWatchingFileDescriptor();
+    mWriteWatcher.StopWatchingFileDescriptor();
+  }
+
+  void QueueWriteData(UnixSocketRawData* aData)
+  {
+    mOutgoingQ.AppendElement(aData);
+    OnFileCanWriteWithoutBlocking(mFd);
+  }
+
+  bool isFdValid()
+  {
+    return mFd > 0;
+  }
+
+  void SetUpIO()
+  {
+    MOZ_ASSERT(!mIOLoop);
+    mIOLoop = MessageLoopForIO::current();
+    mIOLoop->WatchFileDescriptor(mFd,
+                                 true,
+                                 MessageLoopForIO::WATCH_READ,
+                                 &mReadWatcher,
+                                 this);
+  }
+
+  void PrepareRemoval()
+  {
+    mConsumer.forget();
   }
-  return 0;
+
+  /**
+   * Consumer pointer. Non-thread safe RefPtr, so should only be manipulated
+   * directly from main thread. All non-main-thread accesses should happen with
+   * mImpl as container.
+   */
+  RefPtr<UnixSocketConsumer> mConsumer;
+
+private:
+  /**
+   * libevent triggered functions that reads data from socket when available and
+   * guarenteed non-blocking. Only to be called on IO thread.
+   *
+   * @param aFd File descriptor to read from
+   */
+  virtual void OnFileCanReadWithoutBlocking(int aFd);
+
+  /**
+   * libevent or developer triggered functions that writes data to socket when
+   * available and guarenteed non-blocking. Only to be called on IO thread.
+   *
+   * @param aFd File descriptor to read from
+   */
+  virtual void OnFileCanWriteWithoutBlocking(int aFd);
+
+  /**
+   * IO Loop pointer. Must be initalized and called from IO thread only.
+   */
+  MessageLoopForIO* mIOLoop;
+
+  /**
+   * Raw data queue. Must be pushed/popped from IO thread only.
+   */
+  typedef nsTArray<UnixSocketRawData* > UnixSocketRawDataQueue;
+  UnixSocketRawDataQueue mOutgoingQ;
+
+  /**
+   * File descriptor to read from/write to. Connection happens on user provided
+   * thread. Read/write/close happens on IO thread.
+   */
+  ScopedClose mFd;
+
+  /**
+   * Incoming packet. Only to be accessed on IO Thread.
+   */
+  nsAutoPtr<UnixSocketRawData> mIncoming;
+
+  /**
+   * Read watcher for libevent. Only to be accessed on IO Thread.
+   */
+  MessageLoopForIO::FileDescriptorWatcher mReadWatcher;
+
+  /**
+   * Write watcher for libevent. Only to be accessed on IO Thread.
+   */
+  MessageLoopForIO::FileDescriptorWatcher mWriteWatcher;
+};
+
+static void
+DestroyImpl(UnixSocketImpl* impl)
+{
+  delete impl;
 }
 
-int
-OpenSocket(int type, const char* aAddress, int channel, bool auth, bool encrypt)
+class SocketReceiveTask : public nsRunnable
 {
-  MOZ_ASSERT(!NS_IsMainThread());
-  int lm = 0;
-  int fd = -1;
-  int sndbuf;
+public:
+  SocketReceiveTask(UnixSocketImpl* aImpl, UnixSocketRawData* aData) :
+    mImpl(aImpl),
+    mRawData(aData)
+  {
+    MOZ_ASSERT(aImpl);
+    MOZ_ASSERT(aData);
+  }
 
-  switch (type) {
-  case TYPE_RFCOMM:
-    fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
-    break;
-  case TYPE_SCO:
-    fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
-    break;
-  case TYPE_L2CAP:
-    fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
-    break;
-  default:
-    return -1;
+  NS_IMETHOD Run()
+  {
+    if(!mImpl->mConsumer) {
+      NS_WARNING("mConsumer is null, aborting receive!");
+      // Since we've already explicitly closed and the close happened before
+      // this, this isn't really an error. Since we've warned, return OK.
+      return NS_OK;
+    }
+    mImpl->mConsumer->ReceiveSocketData(mRawData);
+    return NS_OK;
+  }
+private:
+  UnixSocketImpl* mImpl;
+  nsAutoPtr<UnixSocketRawData> mRawData;
+};
+
+class SocketSendTask : public Task
+{
+public:
+  SocketSendTask(UnixSocketConsumer* aConsumer, UnixSocketImpl* aImpl,
+                 UnixSocketRawData* aData)
+    : mConsumer(aConsumer),
+      mImpl(aImpl),
+      mData(aData)
+  {
+    MOZ_ASSERT(aConsumer);
+    MOZ_ASSERT(aImpl);
+    MOZ_ASSERT(aData);
   }
 
-  if (fd < 0) {
-    NS_WARNING("Could not open bluetooth socket!");
-    return -1;
+  void Run()
+  {
+    mImpl->QueueWriteData(mData);
+  }
+
+private:
+  nsRefPtr<UnixSocketConsumer> mConsumer;
+  UnixSocketImpl* mImpl;
+  UnixSocketRawData* mData;
+};
+
+class StartImplReadingTask : public Task
+{
+public:
+  StartImplReadingTask(UnixSocketImpl* aImpl)
+    : mImpl(aImpl)
+  {
+  }
+
+  void Run()
+  {
+    mImpl->SetUpIO();
+  }
+private:
+  UnixSocketImpl* mImpl;
+};
+
+bool
+UnixSocketConnector::Connect(int aFd, const char* aAddress)
+{
+  if (!ConnectInternal(aFd, aAddress))
+  {
+    return false;
+  }
+
+  // Set close-on-exec bit.
+  int flags = fcntl(aFd, F_GETFD);
+  if (-1 == flags) {
+    return false;
+  }
+
+  flags |= FD_CLOEXEC;
+  if (-1 == fcntl(aFd, F_SETFD, flags)) {
+    return false;
+  }
+
+  // Select non-blocking IO.
+  if (-1 == fcntl(aFd, F_SETFL, O_NONBLOCK)) {
+    return false;
   }
 
-  /* kernel does not yet support LM for SCO */
-  switch (type) {
-  case TYPE_RFCOMM:
-    lm |= auth ? RFCOMM_LM_AUTH : 0;
-    lm |= encrypt ? RFCOMM_LM_ENCRYPT : 0;
-    lm |= (auth && encrypt) ? RFCOMM_LM_SECURE : 0;
-    break;
-  case TYPE_L2CAP:
-    lm |= auth ? L2CAP_LM_AUTH : 0;
-    lm |= encrypt ? L2CAP_LM_ENCRYPT : 0;
-    lm |= (auth && encrypt) ? L2CAP_LM_SECURE : 0;
-    break;
+  return true;
+}
+
+UnixSocketConsumer::~UnixSocketConsumer()
+{
+}
+
+bool
+UnixSocketConsumer::SendSocketData(UnixSocketRawData* aData)
+{
+  if (!mImpl) {
+    return false;
+  }
+  XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
+                                   new SocketSendTask(this, mImpl, aData));
+  return true;
+}
+
+bool
+UnixSocketConsumer::SendSocketData(const nsACString& aStr)
+{
+  if (!mImpl) {
+    return false;
+  }
+  if (aStr.Length() > UnixSocketRawData::MAX_DATA_SIZE) {
+    return false;
+  }
+  nsCString str(aStr);
+  UnixSocketRawData* d = new UnixSocketRawData(aStr.Length());
+  memcpy(d->mData, str.get(), aStr.Length());
+  XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
+                                   new SocketSendTask(this, mImpl, d));
+  return true;
+}
+
+void
+UnixSocketConsumer::CloseSocket()
+{
+  if (!mImpl) {
+    return;
   }
+  // To make sure the owner doesn't die on the IOThread, remove pointer here
+  mImpl->PrepareRemoval();
+  // Line it up to be destructed on the IO Thread
+  // Kill our pointer to it
+  XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
+                                   NewRunnableFunction(DestroyImpl,
+                                                       mImpl.forget()));
+}
 
-  if (lm) {
-    if (setsockopt(fd, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm))) {
-      LOG("setsockopt(RFCOMM_LM) failed, throwing");
-      return -1;
-    }
-  }
-
-  if (type == TYPE_RFCOMM) {
-    sndbuf = RFCOMM_SO_SNDBUF;
-    if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf))) {
-      LOG("setsockopt(SO_SNDBUF) failed, throwing");
-      return -1;
+void
+UnixSocketImpl::OnFileCanReadWithoutBlocking(int aFd)
+{
+  // Keep reading data until either
+  //
+  //   - mIncoming is completely read
+  //     If so, sConsumer->MessageReceived(mIncoming.forget())
+  //
+  //   - mIncoming isn't completely read, but there's no more
+  //     data available on the socket
+  //     If so, break;
+  while (true) {
+    if (!mIncoming) {
+      mIncoming = new UnixSocketRawData();
+      ssize_t ret = read(aFd, mIncoming->mData, UnixSocketRawData::MAX_DATA_SIZE);
+      if (ret <= 0) {
+        if (ret == -1) {
+          if (errno == EINTR) {
+            continue; // retry system call when interrupted
+          }
+          else if (errno == EAGAIN || errno == EWOULDBLOCK) {
+            mIncoming.forget();
+            return; // no data available: return and re-poll
+          }
+          // else fall through to error handling on other errno's
+        }
+#ifdef DEBUG
+        nsAutoString str;
+        str.AssignLiteral("Cannot read from network, error ");
+        str += (int)ret;
+        NS_WARNING(NS_ConvertUTF16toUTF8(str).get());
+#endif
+        // At this point, assume that we can't actually access
+        // the socket anymore
+        mIncoming.forget();
+        mReadWatcher.StopWatchingFileDescriptor();
+        mWriteWatcher.StopWatchingFileDescriptor();
+        mConsumer->CloseSocket();
+        return;
+      }
+      mIncoming->mData[ret] = 0;
+      mIncoming->mSize = ret;
+      nsRefPtr<SocketReceiveTask> t =
+        new SocketReceiveTask(this, mIncoming.forget());
+      NS_DispatchToMainThread(t);
+      if (ret < ssize_t(UnixSocketRawData::MAX_DATA_SIZE)) {
+        return;
+      }
     }
   }
-
-  int n = 1;
-  setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
-  
-  socklen_t addr_sz;
-  struct sockaddr *addr;
-  bdaddr_t bd_address_obj;
-
-  int mPort = channel;
-
-  char mAddress[18];
-  mAddress[17] = '\0';
-  strncpy(&mAddress[0], aAddress, 17);
-
-  if (get_bdaddr(aAddress, &bd_address_obj)) {
-    NS_WARNING("Can't get bluetooth address!");
-    return -1;
-  }
-
-  switch (type) {
-  case TYPE_RFCOMM:
-    struct sockaddr_rc addr_rc;
-    addr = (struct sockaddr *)&addr_rc;
-    addr_sz = sizeof(addr_rc);
-
-    memset(addr, 0, addr_sz);
-    addr_rc.rc_family = AF_BLUETOOTH;
-    addr_rc.rc_channel = mPort;
-    memcpy(&addr_rc.rc_bdaddr, &bd_address_obj, sizeof(bdaddr_t));
-    break;
-  case TYPE_SCO:
-    struct sockaddr_sco addr_sco;
-    addr = (struct sockaddr *)&addr_sco;
-    addr_sz = sizeof(addr_sco);
-
-    memset(addr, 0, addr_sz);
-    addr_sco.sco_family = AF_BLUETOOTH;
-    memcpy(&addr_sco.sco_bdaddr, &bd_address_obj, sizeof(bdaddr_t));
-    break;
-  default:
-    NS_WARNING("Socket type unknown!");
-    return -1;
-  }
-
-  int ret = connect(fd, addr, addr_sz);
-
-  if (ret) {
-#if DEBUG
-    LOG("Socket connect errno=%d\n", errno);
-#endif
-    NS_WARNING("Socket connect error!");
-    return -1;
-  }
-
-  // Match android_bluetooth_HeadsetBase.cpp line 384
-  // Skip many lines
-  return fd;
 }
 
-int
-GetNewSocket(int type, const char* aAddress, int channel, bool auth, bool encrypt)
+void
+UnixSocketImpl::OnFileCanWriteWithoutBlocking(int aFd)
 {
-  return OpenSocket(type, aAddress, channel, auth, encrypt);
+  // Try to write the bytes of mCurrentRilRawData.  If all were written, continue.
+  //
+  // Otherwise, save the byte position of the next byte to write
+  // within mCurrentRilRawData, and request another write when the
+  // system won't block.
+  //
+  while (true) {
+    UnixSocketRawData* data;
+    if (mOutgoingQ.IsEmpty()) {
+      return;
+    }
+    data = mOutgoingQ.ElementAt(0);
+    const uint8_t *toWrite;
+    toWrite = data->mData;
+
+    while (data->mCurrentWriteOffset < data->mSize) {
+      ssize_t write_amount = data->mSize - data->mCurrentWriteOffset;
+      ssize_t written;
+      written = write (aFd, toWrite + data->mCurrentWriteOffset,
+                       write_amount);
+      if (written > 0) {
+        data->mCurrentWriteOffset += written;
+      }
+      if (written != write_amount) {
+        break;
+      }
+    }
+
+    if (data->mCurrentWriteOffset != data->mSize) {
+      MessageLoopForIO::current()->WatchFileDescriptor(
+        aFd,
+        false,
+        MessageLoopForIO::WATCH_WRITE,
+        &mWriteWatcher,
+        this);
+      return;
+    }
+    mOutgoingQ.RemoveElementAt(0);
+    delete data;
+  }
 }
 
-int
-CloseSocket(int aFd)
+bool
+UnixSocketConsumer::ConnectSocket(UnixSocketConnector& aConnector,
+                                  const char* aAddress)
 {
-  // This can block since we aren't opening sockets O_NONBLOCK
   MOZ_ASSERT(!NS_IsMainThread());
-  return close(aFd);
+  MOZ_ASSERT(!mImpl);
+  ScopedClose fd(aConnector.Create());
+  if (fd < 0) {
+    return false;
+  }
+  if (!aConnector.Connect(fd, aAddress)) {
+    return false;
+  }
+  mImpl = new UnixSocketImpl(this, fd.forget());
+  XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
+                                   new StartImplReadingTask(mImpl));
+  return true;
 }
 
 } // namespace ipc
 } // namespace mozilla
rename from ipc/socket/Socket.h
rename to ipc/unixsocket/UnixSocket.h
--- a/ipc/socket/Socket.h
+++ b/ipc/unixsocket/UnixSocket.h
@@ -1,22 +1,170 @@
<