Merge the last PGO-green inbound changeset to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Sun, 26 Aug 2012 22:22:56 -0400
changeset 105535 0a9e931cdcf3ffc4f34bcd440b1f2fe087afa44a
parent 105517 e08a67884b9b0af40492adcf7acd81a409f4e23d (current diff)
parent 105534 74666a1eb791ef2b4447c442556017cc86e59544 (diff)
child 105536 a5ffaa0753dd052d540b69855646fa5a76fd9b4d
child 105552 64980ac7c48b3fc1fe72c5830cdb42e4f819a2fc
push id55
push usershu@rfrn.org
push dateThu, 30 Aug 2012 01:33:09 +0000
milestone17.0a1
Merge the last PGO-green inbound changeset to m-c.
--- a/browser/devtools/webconsole/WebConsoleUtils.jsm
+++ b/browser/devtools/webconsole/WebConsoleUtils.jsm
@@ -157,41 +157,16 @@ var WebConsoleUtils = {
                                    getInterface(Ci.nsIDOMWindowUtils);
       content = windowUtils.getOuterWindowWithId(aOuterId);
     }
 
     return content;
   },
 
   /**
-   * Gets the window that has the given inner ID.
-   *
-   * @param integer aInnerId
-   * @param nsIDOMWindow [aHintWindow]
-   *        Optional, the window object used to QueryInterface to
-   *        nsIDOMWindowUtils. If this is not given,
-   *        Services.wm.getMostRecentWindow() is used.
-   * @return nsIDOMWindow|null
-   *         The window object with the given inner ID.
-   */
-  getWindowByInnerId: function WCU_getWindowByInnerId(aInnerId, aHintWindow)
-  {
-    let someWindow = aHintWindow || Services.wm.getMostRecentWindow(null);
-    let content = null;
-
-    if (someWindow) {
-      let windowUtils = someWindow.QueryInterface(Ci.nsIInterfaceRequestor).
-                                   getInterface(Ci.nsIDOMWindowUtils);
-      content = windowUtils.getInnerWindowWithId(aInnerId);
-    }
-
-    return content;
-  },
-
-  /**
    * Abbreviates the given source URL so that it can be displayed flush-right
    * without being too distracting.
    *
    * @param string aSourceURL
    *        The source URL to shorten.
    * @return string
    *         The abbreviated form of the source URL.
    */
--- 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.3.452
+Current extension version is: 0.4.11
 
--- a/browser/extensions/pdfjs/components/PdfStreamConverter.js
+++ b/browser/extensions/pdfjs/components/PdfStreamConverter.js
@@ -9,16 +9,17 @@ const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 const Cu = Components.utils;
 // True only if this is the version of pdf.js that is included with firefox.
 const MOZ_CENTRAL = true;
 const PDFJS_EVENT_ID = 'pdf.js.message';
 const PDF_CONTENT_TYPE = 'application/pdf';
 const PREF_PREFIX = 'pdfjs';
+const PDF_VIEWER_WEB_PAGE = 'resource://pdf.js/web/viewer.html';
 const MAX_DATABASE_LENGTH = 4096;
 const FIREFOX_ID = '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}';
 const SEAMONKEY_ID = '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}';
 
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.import('resource://gre/modules/Services.jsm');
 Cu.import('resource://gre/modules/NetUtil.jsm');
 
@@ -117,19 +118,77 @@ function getLocalizedStrings(path) {
 }
 function getLocalizedString(strings, id, property) {
   property = property || 'textContent';
   if (id in strings)
     return strings[id][property];
   return id;
 }
 
+// PDF data storage
+function PdfDataListener(length) {
+  this.length = length; // less than 0, if length is unknown
+  this.data = new Uint8Array(length >= 0 ? length : 0x10000);
+  this.loaded = 0;
+}
+
+PdfDataListener.prototype = {
+  append: function PdfDataListener_append(chunk) {
+    var willBeLoaded = this.loaded + chunk.length;
+    if (this.length >= 0 && this.length < willBeLoaded) {
+      this.length = -1; // reset the length, server is giving incorrect one
+    }
+    if (this.length < 0 && this.data.length < willBeLoaded) {
+      // data length is unknown and new chunk will not fit in the existing
+      // buffer, resizing the buffer by doubling the its last length
+      var newLength = this.data.length;
+      for (; newLength < willBeLoaded; newLength *= 2) {}
+      var newData = new Uint8Array(newLength);
+      newData.set(this.data);
+      this.data = newData;
+    }
+    this.data.set(chunk, this.loaded);
+    this.loaded = willBeLoaded;
+    this.onprogress(this.loaded, this.length >= 0 ? this.length : void(0));
+  },
+  getData: function PdfDataListener_getData() {
+    var data = this.data;
+    if (this.loaded != data.length)
+      data = data.subarray(0, this.loaded);
+    delete this.data; // releasing temporary storage
+    return data;
+  },
+  finish: function PdfDataListener_finish() {
+    this.isDataReady = true;
+    if (this.oncompleteCallback) {
+      this.oncompleteCallback(this.getData());
+    }
+  },
+  error: function PdfDataListener_error(errorCode) {
+    this.errorCode = errorCode;
+    if (this.oncompleteCallback) {
+      this.oncompleteCallback(null, errorCode);
+    }
+  },
+  onprogress: function() {},
+  set oncomplete(value) {
+    this.oncompleteCallback = value;
+    if (this.isDataReady) {
+      value(this.getData());
+    }
+    if (this.errorCode) {
+      value(null, this.errorCode);
+    }
+  }
+};
+
 // All the priviledged actions.
-function ChromeActions(domWindow) {
+function ChromeActions(domWindow, dataListener) {
   this.domWindow = domWindow;
+  this.dataListener = dataListener;
 }
 
 ChromeActions.prototype = {
   download: function(data, sendResponse) {
     var originalUrl = data.originalUrl;
     // The data may not be downloaded so we need just retry getting the pdf with
     // the original url.
     var originalUri = NetUtil.newURI(data.originalUrl);
@@ -189,16 +248,48 @@ ChromeActions.prototype = {
   getDatabase: function() {
     if (inPrivateBrowsing)
       return '{}';
     return getStringPref(PREF_PREFIX + '.database', '{}');
   },
   getLocale: function() {
     return getStringPref('general.useragent.locale', 'en-US');
   },
+  getLoadingType: function() {
+    return this.dataListener ? 'passive' : 'active';
+  },
+  initPassiveLoading: function() {
+    if (!this.dataListener)
+      return false;
+
+    var domWindow = this.domWindow;
+    this.dataListener.onprogress =
+      function ChromeActions_dataListenerProgress(loaded, total) {
+
+      domWindow.postMessage({
+        pdfjsLoadAction: 'progress',
+        loaded: loaded,
+        total: total
+      }, '*');
+    };
+
+    this.dataListener.oncomplete =
+      function ChromeActions_dataListenerComplete(data, errorCode) {
+
+      domWindow.postMessage({
+        pdfjsLoadAction: 'complete',
+        data: data,
+        errorCode: errorCode
+      }, '*');
+
+      delete this.dataListener;
+    };
+
+    return true;
+  },
   getStrings: function(data) {
     try {
       // Lazy initialization of localizedStrings
       if (!('localizedStrings' in this))
         this.localizedStrings = getLocalizedStrings('viewer.properties');
 
       var result = this.localizedStrings[data];
       return JSON.stringify(result || null);
@@ -214,19 +305,36 @@ ChromeActions.prototype = {
     return getBoolPref(PREF_PREFIX + '.searchEnabled', false);
   },
   fallback: function(url, sendResponse) {
     var self = this;
     var domWindow = this.domWindow;
     var strings = getLocalizedStrings('chrome.properties');
     var message = getLocalizedString(strings, 'unsupported_feature');
 
-    var win = Services.wm.getMostRecentWindow('navigator:browser');
-    var browser = win.gBrowser.getBrowserForDocument(domWindow.top.document);
-    var notificationBox = win.gBrowser.getNotificationBox(browser);
+    var notificationBox = null;
+    // Multiple browser windows can be opened, finding one for notification box
+    var windowsEnum = Services.wm
+                      .getZOrderDOMWindowEnumerator('navigator:browser', true);
+    while (windowsEnum.hasMoreElements()) {
+      var win = windowsEnum.getNext();
+      if (win.closed)
+        continue;
+      var browser = win.gBrowser.getBrowserForDocument(domWindow.top.document);
+      if (browser) {
+        // right window/browser is found, getting the notification box
+        notificationBox = win.gBrowser.getNotificationBox(browser);
+        break;
+      }
+    }
+    if (!notificationBox) {
+      log('Unable to get a notification box for the fallback message');
+      return;
+    }
+
     // Flag so we don't call the response callback twice, since if the user
     // clicks open with different viewer both the button callback and
     // eventCallback will be called.
     var sentResponse = false;
     var buttons = [{
       label: getLocalizedString(strings, 'open_with_different_viewer'),
       accessKey: getLocalizedString(strings, 'open_with_different_viewer',
                                     'accessKey'),
@@ -319,79 +427,123 @@ PdfStreamConverter.prototype = {
   convert: function(aFromStream, aFromType, aToType, aCtxt) {
     throw Cr.NS_ERROR_NOT_IMPLEMENTED;
   },
 
   // nsIStreamConverter::asyncConvertData
   asyncConvertData: function(aFromType, aToType, aListener, aCtxt) {
     if (!isEnabled())
       throw Cr.NS_ERROR_NOT_IMPLEMENTED;
-    // Ignoring HTTP POST requests -- pdf.js has to repeat the request.
-    var skipConversion = false;
-    try {
-      var request = aCtxt;
-      request.QueryInterface(Ci.nsIHttpChannel);
-      skipConversion = (request.requestMethod !== 'GET');
-    } catch (e) {
-      // Non-HTTP request... continue normally.
+
+    var useFetchByChrome = getBoolPref(PREF_PREFIX + '.fetchByChrome', true);
+    if (!useFetchByChrome) {
+      // Ignoring HTTP POST requests -- pdf.js has to repeat the request.
+      var skipConversion = false;
+      try {
+        var request = aCtxt;
+        request.QueryInterface(Ci.nsIHttpChannel);
+        skipConversion = (request.requestMethod !== 'GET');
+      } catch (e) {
+        // Non-HTTP request... continue normally.
+      }
+      if (skipConversion)
+        throw Cr.NS_ERROR_NOT_IMPLEMENTED;
     }
-    if (skipConversion)
-      throw Cr.NS_ERROR_NOT_IMPLEMENTED;
 
     // Store the listener passed to us
     this.listener = aListener;
   },
 
   // nsIStreamListener::onDataAvailable
   onDataAvailable: function(aRequest, aContext, aInputStream, aOffset, aCount) {
-    // Do nothing since all the data loading is handled by the viewer.
-    log('SANITY CHECK: onDataAvailable SHOULD NOT BE CALLED!');
+    if (!this.dataListener) {
+      // Do nothing since all the data loading is handled by the viewer.
+      return;
+    }
+
+    var binaryStream = this.binaryStream;
+    binaryStream.setInputStream(aInputStream);
+    this.dataListener.append(binaryStream.readByteArray(aCount));
   },
 
   // nsIRequestObserver::onStartRequest
   onStartRequest: function(aRequest, aContext) {
 
     // Setup the request so we can use it below.
     aRequest.QueryInterface(Ci.nsIChannel);
-    // Cancel the request so the viewer can handle it.
-    aRequest.cancel(Cr.NS_BINDING_ABORTED);
+    var useFetchByChrome = getBoolPref(PREF_PREFIX + '.fetchByChrome', true);
+    var dataListener;
+    if (useFetchByChrome) {
+      // Creating storage for PDF data
+      var contentLength = aRequest.contentLength;
+      dataListener = new PdfDataListener(contentLength);
+      this.dataListener = dataListener;
+      this.binaryStream = Cc['@mozilla.org/binaryinputstream;1']
+                          .createInstance(Ci.nsIBinaryInputStream);
+    } else {
+      // Cancel the request so the viewer can handle it.
+      aRequest.cancel(Cr.NS_BINDING_ABORTED);
+    }
 
     // Create a new channel that is viewer loaded as a resource.
     var ioService = Services.io;
     var channel = ioService.newChannel(
-                    'resource://pdf.js/web/viewer.html', null, null);
+                    PDF_VIEWER_WEB_PAGE, null, null);
 
     var listener = this.listener;
+    var self = this;
     // Proxy all the request observer calls, when it gets to onStopRequest
     // we can get the dom window.
     var proxy = {
       onStartRequest: function() {
         listener.onStartRequest.apply(listener, arguments);
       },
       onDataAvailable: function() {
         listener.onDataAvailable.apply(listener, arguments);
       },
       onStopRequest: function() {
         var domWindow = getDOMWindow(channel);
         // Double check the url is still the correct one.
         if (domWindow.document.documentURIObject.equals(aRequest.URI)) {
-          let requestListener = new RequestListener(
-                                      new ChromeActions(domWindow));
+          let actions = new ChromeActions(domWindow, dataListener);
+          let requestListener = new RequestListener(actions);
           domWindow.addEventListener(PDFJS_EVENT_ID, function(event) {
             requestListener.receive(event);
           }, false, true);
         }
         listener.onStopRequest.apply(listener, arguments);
       }
     };
 
     // Keep the URL the same so the browser sees it as the same.
     channel.originalURI = aRequest.URI;
     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) :
+                              securityManager.getCodebasePrincipal(uri);
+      channel.owner = resourcePrincipal;
+    }
   },
 
   // nsIRequestObserver::onStopRequest
   onStopRequest: function(aRequest, aContext, aStatusCode) {
-    // Do nothing.
+    if (!this.dataListener) {
+      // Do nothing
+      return;
+    }
+
+    if (Components.isSuccessCode(aStatusCode))
+      this.dataListener.finish();
+    else
+      this.dataListener.error(aStatusCode);
+    delete this.dataListener;
+    delete this.binaryStream;
   }
 };
 
 var NSGetFactory = XPCOMUtils.generateNSGetFactory([PdfStreamConverter]);
--- a/browser/extensions/pdfjs/content/web/debugger.js
+++ b/browser/extensions/pdfjs/content/web/debugger.js
@@ -39,17 +39,17 @@ var FontInspector = (function FontInspec
       var select = selects[i];
       if (select.dataset.fontName != fontName) continue;
       select.checked = !select.checked;
       selectFont(fontName, select.checked);
       select.scrollIntoView();
     }
   }
   return {
-    // Poperties/functions needed by PDFBug.
+    // Properties/functions needed by PDFBug.
     id: 'FontInspector',
     name: 'Font Inspector',
     panel: null,
     manager: null,
     init: function init() {
       var panel = this.panel;
       panel.setAttribute('style', 'padding: 5px;');
       var tmp = document.createElement('button');
@@ -135,17 +135,17 @@ var FontInspector = (function FontInspec
 // Manages all the page steppers.
 var StepperManager = (function StepperManagerClosure() {
   var steppers = [];
   var stepperDiv = null;
   var stepperControls = null;
   var stepperChooser = null;
   var breakPoints = {};
   return {
-    // Poperties/functions needed by PDFBug.
+    // Properties/functions needed by PDFBug.
     id: 'Stepper',
     name: 'Stepper',
     panel: null,
     manager: null,
     init: function init() {
       var self = this;
       this.panel.setAttribute('style', 'padding: 5px;');
       stepperControls = document.createElement('div');
@@ -202,17 +202,17 @@ var StepperManager = (function StepperMa
     }
   };
 })();
 
 // The stepper for each page's IRQueue.
 var Stepper = (function StepperClosure() {
   function Stepper(panel, pageIndex, initialBreakPoints) {
     this.panel = panel;
-    this.len;
+    this.len = 0;
     this.breakPoint = 0;
     this.nextBreakPoint = null;
     this.pageIndex = pageIndex;
     this.breakPoints = initialBreakPoints;
     this.currentIdx = -1;
   }
   Stepper.prototype = {
     init: function init(IRQueue) {
@@ -231,30 +231,30 @@ var Stepper = (function StepperClosure()
       table.cellSpacing = 0;
       var headerRow = c('tr');
       table.appendChild(headerRow);
       headerRow.appendChild(c('th', 'Break'));
       headerRow.appendChild(c('th', 'Idx'));
       headerRow.appendChild(c('th', 'fn'));
       headerRow.appendChild(c('th', 'args'));
 
+      var self = this;
       for (var i = 0; i < IRQueue.fnArray.length; i++) {
         var line = c('tr');
         line.className = 'line';
         line.dataset.idx = i;
         table.appendChild(line);
         var checked = this.breakPoints.indexOf(i) != -1;
         var args = IRQueue.argsArray[i] ? IRQueue.argsArray[i] : [];
 
         var breakCell = c('td');
         var cbox = c('input');
         cbox.type = 'checkbox';
         cbox.className = 'points';
         cbox.checked = checked;
-        var self = this;
         cbox.onclick = (function(x) {
           return function() {
             if (this.checked)
               self.breakPoints.push(x);
             else
               self.breakPoints.splice(self.breakPoints.indexOf(x), 1);
             StepperManager.saveBreakPoints(self.pageIndex, self.breakPoints);
           }
@@ -293,17 +293,17 @@ var Stepper = (function StepperClosure()
           case 67: // continue
             dom.removeEventListener('keydown', listener, false);
             var breakPoint = self.getNextBreakPoint();
             self.nextBreakPoint = breakPoint;
             self.goTo(-1);
             callback();
             break;
         }
-      }
+      };
       dom.addEventListener('keydown', listener, false);
       self.goTo(idx);
     },
     goTo: function goTo(idx) {
       var allRows = this.panel.getElementsByClassName('line');
       for (var x = 0, xx = allRows.length; x < xx; ++x) {
         var row = allRows[x];
         if (row.dataset.idx == idx) {
@@ -326,17 +326,17 @@ var Stats = (function Stats() {
   }
   function getStatIndex(pageNumber) {
     for (var i = 0, ii = stats.length; i < ii; ++i)
       if (stats[i].pageNumber === pageNumber)
         return i;
     return false;
   }
   return {
-    // Poperties/functions needed by PDFBug.
+    // Properties/functions needed by PDFBug.
     id: 'Stats',
     name: 'Stats',
     panel: null,
     manager: null,
     init: function init() {
       this.panel.setAttribute('style', 'padding: 5px;');
       PDFJS.enableStats = true;
     },
@@ -424,22 +424,22 @@ var PDFBug = (function PDFBugClosure() {
       ui.appendChild(panels);
 
       var container = document.getElementById('viewerContainer');
       container.appendChild(ui);
       container.style.right = panelWidth + 'px';
 
       // Initialize all the debugging tools.
       var tools = this.tools;
+      var self = this;
       for (var i = 0; i < tools.length; ++i) {
         var tool = tools[i];
         var panel = document.createElement('div');
         var panelButton = document.createElement('button');
         panelButton.textContent = tool.name;
-        var self = this;
         panelButton.addEventListener('click', (function(selected) {
           return function(event) {
             event.preventDefault();
             self.selectPanel(selected);
           };
         })(i));
         controls.appendChild(panelButton);
         panels.appendChild(panel);
--- a/browser/extensions/pdfjs/content/web/viewer.css
+++ b/browser/extensions/pdfjs/content/web/viewer.css
@@ -22,16 +22,60 @@ select {
 
 .hidden {
   display: none;
 }
 [hidden] {
   display: none !important;
 }
 
+#viewerContainer:-webkit-full-screen {
+  top: 0px;
+  padding-top: 6px;
+  padding-bottom: 24px;
+  background-color: #404040;
+  background-image: url(images/texture.png);
+  width: 100%;
+  height: 100%;
+  overflow: auto;
+}
+
+:-webkit-full-screen #viewer {
+  margin: 0pt;
+  padding: 0pt;
+  height: 100%;
+  width: 100%;
+  overflow: hidden;
+}
+
+:-webkit-full-screen .page {
+  margin: 0px auto;
+  margin-bottom: 10px;
+}
+
+#viewerContainer:-moz-full-screen {
+  background-color: #404040;
+  background-image: url(images/texture.png);
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+}
+
+:-moz-full-screen .page:last-child {
+  margin-bottom: 40px;
+}
+
+#viewerContainer:full-screen {
+  top: 0px;
+  background-color: #404040;
+  background-image: url(images/texture.png);
+  width: 100%;
+  height: 100%;
+}
+
 /* outer/inner center provides horizontal center */
 html[dir='ltr'] .outerCenter {
   float: right;
   position: relative;
   right: 50%;
 }
 html[dir='rtl'] .outerCenter {
   float: left;
@@ -529,17 +573,17 @@ html[dir='rtl'] .dropdownToolbarButton {
   -webkit-appearance: none;
   -moz-appearance: none; /* in the future this might matter, see bugzilla bug #649849 */
   min-width: 140px;
   font-size: 12px;
   color: hsl(0,0%,95%);
   margin:0;
   padding:0;
   border:none;
-  background: transparent;
+  background: rgba(0,0,0,0); /* Opera does not support 'transparent' <select> background */
 }
 
 .dropdownToolbarButton > select > option {
   background: hsl(0,0%,24%);
 }
 
 #customScaleOption {
   display: none;
@@ -604,16 +648,21 @@ html[dir='rtl'] .toolbarButton.pageDown:
   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);
+}
+
 .toolbarButton.print::before {
   display: inline-block;
   content: url(images/toolbarButton-print.png);
 }
 
 .toolbarButton.openFile::before {
   display: inline-block;
   content: url(images/toolbarButton-openFile.png);
@@ -971,16 +1020,38 @@ canvas {
   height: 100%;
 }
 
 #loadingBar .progress.full {
   border-top-right-radius: 2px;
   border-bottom-right-radius: 2px;
 }
 
+#loadingBar .progress.indeterminate {
+  width: 100%;
+  height: 25px;
+  background-image: -moz-linear-gradient( 30deg, #404040, #404040 15%, #898989, #404040 85%, #404040);
+  background-image: -webkit-linear-gradient( 30deg, #404040, #404040 15%, #898989, #404040 85%, #404040);
+  background-image: -ms-linear-gradient( 30deg, #404040, #404040 15%, #898989, #404040 85%, #404040);
+  background-image: -o-linear-gradient( 30deg, #404040, #404040 15%, #898989, #404040 85%, #404040);
+  background-size: 75px 25px;
+  -moz-animation: progressIndeterminate 1s linear infinite;
+  -webkit-animation: progressIndeterminate 1s linear infinite;
+}
+
+@-moz-keyframes progressIndeterminate {
+  from { background-position: 0px 0px; }
+  to { background-position: 75px 0px; }
+}
+
+@-webkit-keyframes progressIndeterminate {
+  from { background-position: 0px 0px; }
+  to { background-position: 75px 0px; }
+}
+
 .textLayer {
   position: absolute;
   left: 0;
   top: 0;
   right: 0;
   bottom: 0;
   color: #000;
   font-family: sans-serif;
@@ -1108,16 +1179,27 @@ canvas {
 }
 #PDFBug .stats .title {
     font-weight: bold;
 }
 #PDFBug table {
   font-size: 10px;
 }
 
+#viewer.textLayer-visible .textLayer > div,
+#viewer.textLayer-hover .textLayer > div:hover {
+  background-color: white;
+  color: black;
+}
+
+#viewer.textLayer-shadow .textLayer > div {
+  background-color: rgba(255,255,255, .6);
+  color: black;
+}
+
 @page {
   margin: 0;
 } 
 
 #printContainer {
   display: none;
 }
 
--- a/browser/extensions/pdfjs/content/web/viewer.html
+++ b/browser/extensions/pdfjs/content/web/viewer.html
@@ -1,36 +1,34 @@
 <!DOCTYPE html>
 <html dir="ltr">
   <head>
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
     <title>PDF.js viewer</title>
+
 <!-- This snippet is used in firefox extension, see Makefile -->
 <base href="resource://pdf.js/web/" />
-<script type="application/l10n">
-<!-- PDFJSSCRIPT_LOCALE_DATA -->
-</script>
 <script type="text/javascript" src="l10n.js"></script>
 <script type="text/javascript" id="PDFJS_SCRIPT_TAG">
 <!--
 // pdf.js is inlined here because resource:// urls won't work
 // for loading workers.
 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
 
 var PDFJS = {};
 
 (function pdfjsWrapper() {
   // Use strict in our context only - users might not want it
   'use strict';
 
-  PDFJS.build = 'c757eed';
-
-  // Files are inserted below - see Makefile
+  PDFJS.build =
+'cb05144';
+
 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
 
 'use strict';
 
 var globalScope = (typeof window === 'undefined') ? this : window;
 
 var isWorker = (typeof window == 'undefined');
@@ -52,49 +50,56 @@ if (!globalScope.PDFJS) {
 //                 url:String ,
 //                 [,progress:Function, error:Function]
 //               },
 //               callback)
 function getPdf(arg, callback) {
   var params = arg;
   if (typeof arg === 'string')
     params = { url: arg };
-
   var xhr = new XMLHttpRequest();
-
   xhr.open('GET', params.url);
 
   var headers = params.headers;
   if (headers) {
     for (var property in headers) {
       if (typeof headers[property] === 'undefined')
         continue;
 
       xhr.setRequestHeader(property, params.headers[property]);
     }
   }
 
   xhr.mozResponseType = xhr.responseType = 'arraybuffer';
-  var protocol = params.url.indexOf(':') < 0 ? window.location.protocol :
-    params.url.substring(0, params.url.indexOf(':') + 1);
+
+  var protocol = params.url.substring(0, params.url.indexOf(':') + 1);
   xhr.expected = (protocol === 'http:' || protocol === 'https:') ? 200 : 0;
 
   if ('progress' in params)
     xhr.onprogress = params.progress || undefined;
 
-  if ('error' in params)
-    xhr.onerror = params.error || undefined;
+  var calledErrorBack = false;
+
+  if ('error' in params) {
+    xhr.onerror = function errorBack() {
+      if (!calledErrorBack) {
+        calledErrorBack = true;
+        params.error();
+      }
+    }
+  }
 
   xhr.onreadystatechange = function getPdfOnreadystatechange(e) {
     if (xhr.readyState === 4) {
       if (xhr.status === xhr.expected) {
         var data = (xhr.mozResponseArrayBuffer || xhr.mozResponse ||
                     xhr.responseArrayBuffer || xhr.response);
         callback(data);
-      } else if (params.error) {
+      } else if (params.error && !calledErrorBack) {
+        calledErrorBack = true;
         params.error(e);
       }
     }
   };
   xhr.send(null);
 }
 globalScope.PDFJS.getPdf = getPdf;
 globalScope.PDFJS.pdfBug = false;
@@ -417,24 +422,47 @@ var PDFDocument = (function PDFDocumentC
     stream.pos = pos;
     var index = backwards ? str.lastIndexOf(needle) : str.indexOf(needle);
     if (index == -1)
       return false; /* not found */
     stream.pos += index;
     return true; /* found */
   }
 
+  var DocumentInfoValidators = {
+    get entries() {
+      // Lazily build this since all the validation functions below are not
+      // defined until after this file loads.
+      return shadow(this, 'entries', {
+        Title: isString,
+        Author: isString,
+        Subject: isString,
+        Keywords: isString,
+        Creator: isString,
+        Producer: isString,
+        CreationDate: isString,
+        ModDate: isString,
+        Trapped: isName
+      });
+    }
+  };
+
   PDFDocument.prototype = {
     get linearization() {
       var length = this.stream.length;
       var linearization = false;
       if (length) {
-        linearization = new Linearization(this.stream);
-        if (linearization.length != length)
-          linearization = false;
+        try {
+          linearization = new Linearization(this.stream);
+          if (linearization.length != length)
+            linearization = false;
+        } catch (err) {
+          warn('The linearization data is not available ' +
+               'or unreadable pdf data is found');
+        }
       }
       // shadow the prototype getter with a data property
       return shadow(this, 'linearization', linearization);
     },
     get startXRef() {
       var stream = this.stream;
       var startXRef = 0;
       var linearization = this.linearization;
@@ -504,28 +532,37 @@ var PDFDocument = (function PDFDocumentC
     },
     get numPages() {
       var linearization = this.linearization;
       var num = linearization ? linearization.numPages : this.catalog.numPages;
       // shadow the prototype getter
       return shadow(this, 'numPages', num);
     },
     getDocumentInfo: function PDFDocument_getDocumentInfo() {
-      var info;
+      var docInfo;
       if (this.xref.trailer.has('Info')) {
         var infoDict = this.xref.trailer.get('Info');
 
-        info = {};
-        infoDict.forEach(function(key, value) {
-          info[key] = typeof value !== 'string' ? value :
-            stringToPDFString(value);
-        });
-      }
-
-      return shadow(this, 'getDocumentInfo', info);
+        docInfo = {};
+        var validEntries = DocumentInfoValidators.entries;
+        // Only fill the document info with valid entries from the spec.
+        for (var key in validEntries) {
+          if (infoDict.has(key)) {
+            var value = infoDict.get(key);
+            // Make sure the value conforms to the spec.
+            if (validEntries[key](value)) {
+              docInfo[key] = typeof value !== 'string' ? value :
+                             stringToPDFString(value);
+            } else {
+              info('Bad value in document info for "' + key + '"');
+            }
+          }
+        }
+      }
+      return shadow(this, 'getDocumentInfo', docInfo);
     },
     getFingerprint: function PDFDocument_getFingerprint() {
       var xref = this.xref, fileID;
       if (xref.trailer.has('ID')) {
         fileID = '';
         var id = xref.trailer.get('ID')[0];
         id.split('').forEach(function(el) {
           fileID += Number(el.charCodeAt(0)).toString(16);
@@ -606,16 +643,38 @@ function backtrace() {
   }
 }
 
 function assert(cond, msg) {
   if (!cond)
     error(msg);
 }
 
+// Combines two URLs. The baseUrl shall be absolute URL. If the url is an
+// absolute URL, it will be returned as is.
+function combineUrl(baseUrl, url) {
+  if (url.indexOf(':') >= 0)
+    return url;
+  if (url.charAt(0) == '/') {
+    // absolute path
+    var i = baseUrl.indexOf('://');
+    i = baseUrl.indexOf('/', i + 3);
+    return baseUrl.substring(0, i) + url;
+  } else {
+    // relative path
+    var pathLength = baseUrl.length, i;
+    i = baseUrl.lastIndexOf('#');
+    pathLength = i >= 0 ? i : pathLength;
+    i = baseUrl.lastIndexOf('?', pathLength);
+    pathLength = i >= 0 ? i : pathLength;
+    var prefixLength = baseUrl.lastIndexOf('/', pathLength);
+    return baseUrl.substring(0, prefixLength + 1) + url;
+  }
+}
+
 // In a well-formed PDF, |cond| holds.  If it doesn't, subsequent
 // behavior is undefined.
 function assertWellFormed(cond, msg) {
   if (!cond)
     error(msg);
 }
 
 var LogManager = PDFJS.LogManager = (function LogManagerClosure() {
@@ -1183,73 +1242,46 @@ var StatTimer = (function StatTimerClosu
  *  - url   - The URL of the PDF.
  *  - data  - A typed array with PDF data.
  *  - httpHeaders - Basic authentication headers.
  *  - password - For decrypting password-protected PDFs.
  *
  * @return {Promise} A promise that is resolved with {PDFDocumentProxy} object.
  */
 PDFJS.getDocument = function getDocument(source) {
-  var url, data, headers, password, parameters = {}, workerInitializedPromise,
-    workerReadyPromise, transport;
+  var workerInitializedPromise, workerReadyPromise, transport;
 
   if (typeof source === 'string') {
-    url = source;
+    source = { url: source };
   } else if (isArrayBuffer(source)) {
-    data = source;
-  } else if (typeof source === 'object') {
-    url = source.url;
-    data = source.data;
-    headers = source.httpHeaders;
-    password = source.password;
-    parameters.password = password || null;
-
-    if (!url && !data)
-      error('Invalid parameter array, need either .data or .url');
-  } else {
+    source = { data: source };
+  } else if (typeof source !== 'object') {
     error('Invalid parameter in getDocument, need either Uint8Array, ' +
           'string or a parameter object');
   }
 
+  if (!source.url && !source.data)
+    error('Invalid parameter array, need either .data or .url');
+
+  // copy/use all keys as is except 'url' -- full path is required
+  var params = {};
+  for (var key in source) {
+    if (key === 'url' && typeof window !== 'undefined') {
+      params[key] = combineUrl(window.location.href, source[key]);
+      continue;
+    }
+    params[key] = source[key];
+  }
+
   workerInitializedPromise = new PDFJS.Promise();
   workerReadyPromise = new PDFJS.Promise();
   transport = new WorkerTransport(workerInitializedPromise, workerReadyPromise);
-  if (data) {
-    // assuming the data is array, instantiating directly from it
-    transport.sendData(data, parameters);
-  } else if (url) {
-    // fetch url
-    PDFJS.getPdf(
-      {
-        url: url,
-        progress: function getPDFProgress(evt) {
-          if (evt.lengthComputable) {
-            workerReadyPromise.progress({
-              loaded: evt.loaded,
-              total: evt.total
-            });
-          }
-        },
-        error: function getPDFError(e) {
-          workerReadyPromise.reject('Unexpected server response of ' +
-            e.target.status + '.');
-        },
-        headers: headers
-      },
-      function getPDFLoad(data) {
-        // sometimes the pdf has finished downloading before the web worker-test
-        // has finished. In that case the rendering of the final pdf would cause
-        // errors. We have to wait for the WorkerTransport to finalize worker-
-        // support detection
-        workerInitializedPromise.then(function workerInitialized() {
-          transport.sendData(data, parameters);
-        });
-      });
-  }
-
+  workerInitializedPromise.then(function transportInitialized() {
+    transport.fetchDocument(params);
+  });
   return workerReadyPromise;
 };
 
 /**
  * Proxy to a PDFDocument in the worker thread. Also, contains commonly used
  * properties that can be read synchronously.
  */
 var PDFDocumentProxy = (function PDFDocumentProxyClosure() {
@@ -1622,29 +1654,22 @@ var WorkerTransport = (function WorkerTr
     if (!globalScope.PDFJS.disableWorker && typeof Worker !== 'undefined') {
       var workerSrc = PDFJS.workerSrc;
       if (typeof workerSrc === 'undefined') {
         error('No PDFJS.workerSrc specified');
       }
 
       try {
         var worker;
-        if (PDFJS.isFirefoxExtension) {
-          // 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());
-          worker = new Worker(blobUrl);
-        } else {
-          // Some versions of FF can't create a worker on localhost, see:
-          // https://bugzilla.mozilla.org/show_bug.cgi?id=683280
-          worker = new Worker(workerSrc);
-        }
-
+        // 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());
+        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);
           } else {
@@ -1766,16 +1791,27 @@ var WorkerTransport = (function WorkerTr
             var font = new Font(name, file, properties);
             this.objs.resolve(id, font);
             break;
           default:
             error('Got unkown object type ' + type);
         }
       }, this);
 
+      messageHandler.on('DocProgress', function transportDocProgress(data) {
+        this.workerReadyPromise.progress({
+          loaded: data.loaded,
+          total: data.total
+        });
+      }, this);
+
+      messageHandler.on('DocError', function transportDocError(data) {
+        this.workerReadyPromise.reject(data);
+      }, this);
+
       messageHandler.on('PageError', function transportError(data) {
         var page = this.pageCache[data.pageNum - 1];
         if (page.displayReadyPromise)
           page.displayReadyPromise.reject(data.error);
         else
           error(data.error);
       }, this);
 
@@ -1810,21 +1846,21 @@ var WorkerTransport = (function WorkerTr
           }
           promise.resolve({ data: buf, width: width, height: height});
         }).bind(this);
         var src = 'data:image/jpeg;base64,' + window.btoa(imageData);
         img.src = src;
       });
     },
 
-    sendData: function WorkerTransport_sendData(data, params) {
-      this.messageHandler.send('GetDocRequest', {data: data, params: params});
-    },
-
-    getData: function WorkerTransport_sendData(promise) {
+    fetchDocument: function WorkerTransport_fetchDocument(source) {
+      this.messageHandler.send('GetDocRequest', {source: source});
+    },
+
+    getData: function WorkerTransport_getData(promise) {
       this.messageHandler.send('GetData', null, function(data) {
         promise.resolve(data);
       });
     },
 
     getPage: function WorkerTransport_getPage(pageNumber, promise) {
       var pageIndex = pageNumber - 1;
       if (pageIndex in this.pagePromises)
@@ -1998,16 +2034,17 @@ var CanvasExtraState = (function CanvasE
     // Start of text line (in text coordinates)
     this.lineX = 0;
     this.lineY = 0;
     // Character and word spacing
     this.charSpacing = 0;
     this.wordSpacing = 0;
     this.textHScale = 1;
     this.textRenderingMode = TextRenderingMode.FILL;
+    this.textRise = 0;
     // Color spaces
     this.fillColorSpace = new DeviceGrayCS();
     this.fillColorSpaceObj = null;
     this.strokeColorSpace = new DeviceGrayCS();
     this.strokeColorSpaceObj = null;
     this.fillColorObj = null;
     this.strokeColorObj = null;
     // Default fore and background colors
@@ -2442,17 +2479,17 @@ var CanvasGraphics = (function CanvasGra
       this.ctx.font = rule;
     },
     setTextRenderingMode: function CanvasGraphics_setTextRenderingMode(mode) {
       if (mode >= TextRenderingMode.FILL_ADD_TO_PATH)
         TODO('unsupported text rendering mode: ' + mode);
       this.current.textRenderingMode = mode;
     },
     setTextRise: function CanvasGraphics_setTextRise(rise) {
-      TODO('text rise: ' + rise);
+      this.current.textRise = rise;
     },
     moveText: function CanvasGraphics_moveText(x, y) {
       this.current.x = this.current.lineX += x;
       this.current.y = this.current.lineY += y;
     },
     setLeadingMoveText: function CanvasGraphics_setLeadingMoveText(x, y) {
       this.setLeading(-y);
       this.moveText(x, y);
@@ -2469,17 +2506,17 @@ var CanvasGraphics = (function CanvasGra
     applyTextTransforms: function CanvasGraphics_applyTextTransforms() {
       var ctx = this.ctx;
       var current = this.current;
       var textHScale = current.textHScale;
       var fontMatrix = current.fontMatrix || IDENTITY_MATRIX;
 
       ctx.transform.apply(ctx, current.textMatrix);
       ctx.scale(1, -1);
-      ctx.translate(current.x, -1 * current.y);
+      ctx.translate(current.x, -current.y - current.textRise);
       ctx.transform.apply(ctx, fontMatrix);
       ctx.scale(textHScale, 1);
     },
     getTextGeometry: function CanvasGraphics_getTextGeometry() {
       var geometry = {};
       var ctx = this.ctx;
       var font = this.current.font;
       var ctxMatrix = ctx.mozCurrentTransform;
@@ -3037,30 +3074,30 @@ var CanvasGraphics = (function CanvasGra
 
     putBinaryImageData: function CanvasGraphics_putBinaryImageData() {
       //
     },
 
     // Marked content
 
     markPoint: function CanvasGraphics_markPoint(tag) {
-      TODO('Marked content');
+      // TODO Marked content.
     },
     markPointProps: function CanvasGraphics_markPointProps(tag, properties) {
-      TODO('Marked content');
+      // TODO Marked content.
     },
     beginMarkedContent: function CanvasGraphics_beginMarkedContent(tag) {
-      TODO('Marked content');
+      // TODO Marked content.
     },
     beginMarkedContentProps: function CanvasGraphics_beginMarkedContentProps(
                                         tag, properties) {
-      TODO('Marked content');
+      // TODO Marked content.
     },
     endMarkedContent: function CanvasGraphics_endMarkedContent() {
-      TODO('Marked content');
+      // TODO Marked content.
     },
 
     // Compatibility
 
     beginCompat: function CanvasGraphics_beginCompat() {
       TODO('ignore undefined operators (should we do that anyway?)');
     },
     endCompat: function CanvasGraphics_endCompat() {
@@ -3098,17 +3135,17 @@ var CanvasGraphics = (function CanvasGra
       var inverse = this.ctx.mozCurrentTransformInverse;
       return Math.abs(inverse[0] + inverse[2]);
     }
   };
 
   return CanvasGraphics;
 })();
 
-if (!isWorker) {
+function checkPutBinaryImageDataCompatibility() {
   // Feature detection if the browser can use an Uint8Array directly as imgData.
   var canvas = document.createElement('canvas');
   canvas.width = 1;
   canvas.height = 1;
   var ctx = canvas.getContext('2d');
 
   try {
     ctx.putImageData({
@@ -3119,30 +3156,36 @@ if (!isWorker) {
 
     CanvasGraphics.prototype.putBinaryImageData =
       function CanvasGraphicsPutBinaryImageDataNative(ctx, imgData) {
         ctx.putImageData(imgData, 0, 0);
       };
   } catch (e) {
     CanvasGraphics.prototype.putBinaryImageData =
       function CanvasGraphicsPutBinaryImageDataShim(ctx, imgData, w, h) {
-        var tmpImgData = ctx.getImageData(0, 0, w, h);
-
-        // Copy over the imageData pixel by pixel.
+        var tmpImgData = 'createImageData' in ctx ? ctx.createImageData(w, h) :
+          ctx.getImageData(0, 0, w, h);
+
         var tmpImgDataPixels = tmpImgData.data;
-        var len = tmpImgDataPixels.length;
-
-        while (len--) {
-          tmpImgDataPixels[len] = imgData.data[len];
+        var data = imgData.data;
+        if ('set' in tmpImgDataPixels)
+          tmpImgDataPixels.set(data);
+        else {
+          // Copy over the imageData pixel by pixel.
+          for (var i = 0, ii = tmpImgDataPixels.length; i < ii; i++)
+            tmpImgDataPixels[i] = data[i];
         }
 
         ctx.putImageData(tmpImgData, 0, 0);
       };
   }
 }
+if (!isWorker) {
+  checkPutBinaryImageDataCompatibility();
+}
 
 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
 
 'use strict';
 
 var Name = (function NameClosure() {
   function Name(name) {
@@ -3760,17 +3803,17 @@ var XRef = (function XRefClosure() {
 
       warn('Indexing all PDF objects');
       return this.indexObjects();
     },
     getEntry: function XRef_getEntry(i) {
       var e = this.entries[i];
       if (e === null)
         return null;
-      return e.free ? null : e; // returns null is the entry is free
+      return e.free || !e.offset ? null : e; // returns null if entry is free
     },
     fetchIfRef: function XRef_fetchIfRef(obj) {
       if (!isRef(obj))
         return obj;
       return this.fetch(obj);
     },
     fetch: function XRef_fetch(ref, suppressEncryption) {
       assertWellFormed(isRef(ref), 'ref object is not a reference');
@@ -4071,21 +4114,22 @@ var PDFFunction = (function PDFFunctionC
       var inputSize = domain.length / 2;
       var outputSize = range.length / 2;
 
       domain = toMultiArray(domain);
       range = toMultiArray(range);
 
       var size = dict.get('Size');
       var bps = dict.get('BitsPerSample');
-      var order = dict.get('Order');
-      if (!order)
-        order = 1;
-      if (order !== 1)
-        error('No support for cubic spline interpolation: ' + order);
+      var order = dict.get('Order') || 1;
+      if (order !== 1) {
+        // No description how cubic spline interpolation works in PDF32000:2008
+        // As in poppler, ignoring order, linear interpolation may work as good
+        TODO('No support for cubic spline interpolation: ' + order);
+      }
 
       var encode = dict.get('Encode');
       if (!encode) {
         encode = [];
         for (var i = 0; i < inputSize; ++i) {
           encode.push(0);
           encode.push(size[i] - 1);
         }
@@ -12025,16 +12069,19 @@ var ColorSpace = (function ColorSpaceClo
           if (basePatternCS)
             basePatternCS = ColorSpace.parseToIR(basePatternCS, xref, res);
           return ['PatternCS', basePatternCS];
         case 'Indexed':
         case 'I':
           var baseIndexedCS = ColorSpace.parseToIR(cs[1], xref, res);
           var hiVal = cs[2] + 1;
           var lookup = xref.fetchIfRef(cs[3]);
+          if (isStream(lookup)) {
+            lookup = lookup.getBytes();
+          }
           return ['IndexedCS', baseIndexedCS, hiVal, lookup];
         case 'Separation':
         case 'DeviceN':
           var name = cs[1];
           var numComps = 1;
           if (isName(name))
             numComps = 1;
           else if (isArray(name))
@@ -12146,24 +12193,28 @@ var IndexedCS = (function IndexedCSClosu
     this.name = 'Indexed';
     this.numComps = 1;
     this.defaultColor = [0];
     this.base = base;
     this.highVal = highVal;
 
     var baseNumComps = base.numComps;
     var length = baseNumComps * highVal;
-    var lookupArray = new Uint8Array(length);
+    var lookupArray;
 
     if (isStream(lookup)) {
+      lookupArray = new Uint8Array(length);
       var bytes = lookup.getBytes(length);
       lookupArray.set(bytes);
     } else if (isString(lookup)) {
+      lookupArray = new Uint8Array(length);
       for (var i = 0; i < length; ++i)
         lookupArray[i] = lookup.charCodeAt(i);
+    } else if (lookup instanceof Uint8Array) {
+      lookupArray = lookup;
     } else {
       error('Unrecognized lookup table: ' + lookup);
     }
     this.lookup = lookupArray;
   }
 
   IndexedCS.prototype = {
     getRgb: function IndexedCS_getRgb(color) {
@@ -13835,17 +13886,17 @@ var PartialEvaluator = (function Partial
       var defaultWidth = 0;
       if (properties.composite) {
         defaultWidth = dict.get('DW') || 1000;
 
         var widths = dict.get('W');
         if (widths) {
           var start = 0, end = 0;
           for (var i = 0, ii = widths.length; i < ii; i++) {
-            var code = widths[i];
+            var code = xref.fetchIfRef(widths[i]);
             if (isArray(code)) {
               for (var j = 0, jj = code.length; j < jj; j++)
                 glyphsWidths[start++] = code[j];
               start = 0;
             } else if (start) {
               var width = widths[++i];
               for (var j = start; j <= code; j++)
                 glyphsWidths[j] = width;
@@ -14477,17 +14528,18 @@ var FontLoader = {
         if (fontObj.loading) {
           return false;
         }
       }
 
       document.documentElement.removeEventListener(
         'pdfjsFontLoad', checkFontsLoaded, false);
 
-      callback();
+      // Use timeout to fix chrome intermittent failures on font loading.
+      setTimeout(callback, 0);
       return true;
     }
 
     var rules = [], names = [], fontsToLoad = [];
     var fontCreateTimer = 0;
 
     for (var i = 0, ii = fonts.length; i < ii; i++) {
       var font = fonts[i];
@@ -17351,16 +17403,24 @@ var Font = (function FontClosure() {
       // Enter the translated string into the cache
       return (charsCache[chars] = glyphs);
     }
   };
 
   return Font;
 })();
 
+var CallothersubrCmd = (function CallothersubrCmdClosure() {
+  function CallothersubrCmd(index) {
+    this.index = index;
+  }
+
+  return CallothersubrCmd;
+})();
+
 /*
  * Type1Parser encapsulate the needed code for parsing a Type1 font
  * program. Some of its logic depends on the Type2 charstrings
  * structure.
  */
 var Type1Parser = function type1Parser() {
   /*
    * Decrypt a Sequence of Ciphertext Bytes to Produce the Original Sequence
@@ -17450,18 +17510,18 @@ var Type1Parser = function type1Parser()
       '0': null, // dotsection
 
       // [vh]stem3 are Type1 only and Type2 supports [vh]stem with multiple
       // parameters, so instead of returning [vh]stem3 take a shortcut and
       // return [vhstem] instead.
       '1': 'vstem',
       '2': 'hstem',
 
+      '6': 'endchar', // seac
       // Type1 only command with command not (yet) built-in ,throw an error
-      '6': -1, // seac
       '7': -1, // sbw
 
       '11': 'sub',
       '12': 'div',
 
       // callothersubr is a mechanism to make calls on the postscript
       // interpreter, this is not supported by Type2 charstring but hopefully
       // most of the default commands can be ignored safely.
@@ -17481,16 +17541,53 @@ var Type1Parser = function type1Parser()
     '21': 'rmoveto',
     '22': 'hmoveto',
     '30': 'vhcurveto',
     '31': 'hvcurveto'
   };
 
   var kEscapeCommand = 12;
 
+  // Breaks up the stack by arguments and also calculates the value.
+  function breakUpArgs(stack, numArgs) {
+    var args = [];
+    var index = stack.length - 1;
+    for (var i = 0; i < numArgs; i++) {
+      if (index < 0) {
+        args.unshift({ arg: [0],
+                       value: 0,
+                       offset: 0 });
+        warn('Malformed charstring stack: not enough values on stack.');
+        continue;
+      }
+      var token = stack[index];
+      if (token === 'div') {
+        var a = stack[index - 2];
+        var b = stack[index - 1];
+        if (!isInt(a) || !isInt(b)) {
+          warn('Malformed charsting stack: expected ints on stack for div.');
+          a = 0;
+          b = 1;
+        }
+        args.unshift({ arg: [a, b, 'div'],
+                       value: a / b,
+                       offset: index - 2 });
+        index -= 3;
+      } else if (isInt(token)) {
+        args.unshift({ arg: stack.slice(index, index + 1),
+                       value: token,
+                       offset: index });
+        index--;
+      } else {
+        warn('Malformed charsting stack: found bad token ' + token + '.');
+      }
+    }
+    return args;
+  }
+
   function decodeCharString(array) {
     var charstring = [];
     var lsb = 0;
     var width = 0;
     var flexState = 0;
 
     var value = '';
     var count = array.length;
@@ -17519,46 +17616,48 @@ var Type1Parser = function type1Parser()
             // This is the same things about hint replacement, if it is not used
             // entry 3 can be replaced by {3}
             // TODO support hint replacment
             if (index == 3) {
               charstring.push(3);
               i++;
               continue;
             }
+
+            assert(argc == 0, 'callothersubr with arguments is not supported');
+            charstring.push(new CallothersubrCmd(index));
+            continue;
           } else if (escape == 17 || escape == 33) {
             // pop or setcurrentpoint commands can be ignored
             // since we are not doing callothersubr
             continue;
+          } else if (escape == 6) {
+            // seac is like type 2's special endchar but it doesn't use the
+            // first argument asb, so remove it.
+            var args = breakUpArgs(charstring, 5);
+            var arg0 = args[0];
+            charstring.splice(arg0.offset, arg0.arg.length);
           } else if (!kHintingEnabled && (escape == 1 || escape == 2)) {
             charstring.push('drop', 'drop', 'drop', 'drop', 'drop', 'drop');
             continue;
           }
 
           command = charStringDictionary['12'][escape];
         } else {
-          // TODO Clean this code
           if (value == 13) { // hsbw
-            if (charstring.length == 2) {
-              lsb = charstring[0];
-              width = charstring[1];
-              charstring.splice(0, 1);
-            } else if (charstring.length == 4 && charstring[3] == 'div') {
-              lsb = charstring[0];
-              width = charstring[1] / charstring[2];
-              charstring.splice(0, 1);
-            } else if (charstring.length == 4 && charstring[2] == 'div') {
-              lsb = charstring[0] / charstring[1];
-              width = charstring[3];
-              charstring.splice(0, 3);
-            } else {
-              error('Unsupported hsbw format: ' + charstring);
-            }
-
-            charstring.push(lsb, 'hmoveto');
+            var args = breakUpArgs(charstring, 2);
+            var arg0 = args[0];
+            var arg1 = args[1];
+            lsb = arg0.value;
+            width = arg1.value;
+            // To convert to type2 we have to move the width value to the first
+            // part of the charstring and then use hmoveto with lsb.
+            charstring = arg1.arg;
+            charstring = charstring.concat(arg0.arg);
+            charstring.push('hmoveto');
             continue;
           } else if (value == 10) { // callsubr
             if (charstring[charstring.length - 1] < 3) { // subr #0..2
               var subrNumber = charstring.pop();
               switch (subrNumber) {
                 case 1:
                   flexState = 1; // prepare for flex coordinates
                   break;
@@ -17758,16 +17857,20 @@ var Type1Parser = function type1Parser()
                   getToken(); // read in 'put'
                 program.subrs[index] = str.charstring;
               }
               break;
             case '/BlueValues':
             case '/OtherBlues':
             case '/FamilyBlues':
             case '/FamilyOtherBlues':
+              var blueArray = readNumberArray(eexecStr, i + 1);
+              if (blueArray.length > 0 && (blueArray.length % 2) == 0)
+                program.properties.privateData[token.substring(1)] = blueArray;
+              break;
             case '/StemSnapH':
             case '/StemSnapV':
               program.properties.privateData[token.substring(1)] =
                 readNumberArray(eexecStr, i + 1);
               break;
             case '/StdHW':
             case '/StdVW':
               program.properties.privateData[token.substring(1)] =
@@ -18040,48 +18143,44 @@ Type1Font.prototype = {
 
     charstrings.sort(function charstrings_sort(a, b) {
       return a.unicode - b.unicode;
     });
     return charstrings;
   },
 
   getType2Charstrings: function Type1Font_getType2Charstrings(
-                                  type1Charstrings) {
+                                  type1Subrs) {
     var type2Charstrings = [];
-    var count = type1Charstrings.length;
-    for (var i = 0; i < count; i++) {
-      var charstring = type1Charstrings[i].charstring;
-      type2Charstrings.push(this.flattenCharstring(charstring.slice(),
-                                                   this.commandsMap));
-    }
+    var count = type1Subrs.length;
+    var type1Charstrings = [];
+    for (var i = 0; i < count; i++)
+      type1Charstrings.push(type1Subrs[i].charstring.slice());
+    for (var i = 0; i < count; i++)
+      type2Charstrings.push(this.flattenCharstring(type1Charstrings, i));
     return type2Charstrings;
   },
 
   getType2Subrs: function Type1Font_getType2Subrs(type1Subrs) {
     var bias = 0;
     var count = type1Subrs.length;
-    if (count < 1240)
+    if (count < 1133)
       bias = 107;
-    else if (count < 33900)
+    else if (count < 33769)
       bias = 1131;
     else
       bias = 32768;
 
     // Add a bunch of empty subrs to deal with the Type2 bias
     var type2Subrs = [];
     for (var i = 0; i < bias; i++)
       type2Subrs.push([0x0B]);
 
     for (var i = 0; i < count; i++) {
-      var subr = type1Subrs[i];
-      if (!subr)
-        subr = [0x0B];
-
-      type2Subrs.push(this.flattenCharstring(subr, this.commandsMap));
+      type2Subrs.push(this.flattenCharstring(type1Subrs, i));
     }
 
     return type2Subrs;
   },
 
   /*
    * Flatten the commands by interpreting the postscript code and replacing
    * every 'callsubr', 'callothersubr' by the real commands.
@@ -18103,28 +18202,43 @@ Type1Font.prototype = {
     'drop' : [12, 18],
     'endchar': 14,
     'rmoveto': 21,
     'hmoveto': 22,
     'vhcurveto': 30,
     'hvcurveto': 31
   },
 
-  flattenCharstring: function Type1Font_flattenCharstring(charstring, map) {
+  flattenCharstring: function Type1Font_flattenCharstring(charstrings, index) {
+    var charstring = charstrings[index];
+    if (!charstring)
+      return [0x0B];
+    var map = this.commandsMap;
     // charstring changes size - can't cache .length in loop
     for (var i = 0; i < charstring.length; i++) {
       var command = charstring[i];
-      if (command.charAt) {
+      if (typeof command === 'string') {
         var cmd = map[command];
         assert(cmd, 'Unknow command: ' + command);
 
         if (isArray(cmd))
           charstring.splice(i++, 1, cmd[0], cmd[1]);
         else
           charstring[i] = cmd;
+      } else if (command instanceof CallothersubrCmd) {
+        var otherSubrCharstring = charstrings[command.index];
+        if (otherSubrCharstring) {
+          var lastCommand = otherSubrCharstring.indexOf('return');
+          if (lastCommand >= 0)
+            otherSubrCharstring = otherSubrCharstring.slice(0, lastCommand);
+          charstring.splice.apply(charstring,
+                                  [i, 1].concat(otherSubrCharstring));
+        } else
+          charstring.splice(i, 1); // ignoring empty subr call
+        i--;
       } else {
         // Type1 charstring use a division for number above 32000
         if (command > 32000) {
           var divisor = charstring[i + 1];
           command /= divisor;
           charstring.splice(i, 3, 28, command >> 8, command & 0xff);
         } else {
           charstring.splice(i, 1, 28, command >> 8, command & 0xff);
@@ -24057,25 +24171,29 @@ var PDFImage = (function PDFImageClosure
       var originalWidth = this.width;
       var originalHeight = this.height;
       var bpc = this.bpc;
 
       // rows start at byte boundary;
       var rowBytes = (originalWidth * numComps * bpc + 7) >> 3;
       var imgArray = this.getImageBytes(originalHeight * rowBytes);
 
+      // imgArray can be incomplete (e.g. after CCITT fax encoding)
+      var actualHeight = 0 | (imgArray.length / rowBytes *
+                         height / originalHeight);
+
       var comps = this.colorSpace.getRgbBuffer(
         this.getComponents(imgArray), bpc);
       if (originalWidth != width || originalHeight != height)
         comps = PDFImage.resize(comps, this.bpc, 3, originalWidth,
                                 originalHeight, width, height);
       var compsPos = 0;
       var opacity = this.getOpacity(width, height);
       var opacityPos = 0;
-      var length = width * height * 4;
+      var length = width * actualHeight * 4;
 
       for (var i = 0; i < length; i += 4) {
         buffer[i] = comps[compsPos++];
         buffer[i + 1] = comps[compsPos++];
         buffer[i + 2] = comps[compsPos++];
         buffer[i + 3] = opacity[opacityPos++];
       }
     },
@@ -27757,16 +27875,17 @@ var Pattern = (function PatternClosure()
     var type = dict.get('ShadingType');
 
     switch (type) {
       case PatternType.AXIAL:
       case PatternType.RADIAL:
         // Both radial and axial shadings are handled by RadialAxial shading.
         return new Shadings.RadialAxial(dict, matrix, xref, res);
       default:
+        TODO('Unsupported shading type: ' + type);
         return new Shadings.Dummy();
     }
   };
   return Pattern;
 })();
 
 var Shadings = {};
 
@@ -27893,17 +28012,22 @@ Shadings.RadialAxial = (function RadialA
 })();
 
 Shadings.Dummy = (function DummyClosure() {
   function Dummy() {
     this.type = 'Pattern';
   }
 
   Dummy.fromIR = function Dummy_fromIR() {
-    return 'hotpink';
+    return {
+      type: 'Pattern',
+      getPattern: function Dummy_fromIR_getPattern() {
+        return 'hotpink';
+      }
+    };
   };
 
   Dummy.prototype = {
     getIR: function Dummy_getIR() {
       return ['Dummy'];
     }
   };
   return Dummy;
@@ -28875,32 +28999,32 @@ var JpegStream = (function JpegStreamClo
     error('internal error: getChar is not valid on JpegStream');
   };
   /**
    * Checks if the image can be decoded and displayed by the browser without any
    * further processing such as color space conversions.
    */
   JpegStream.prototype.isNativelySupported =
     function JpegStream_isNativelySupported(xref, res) {
-    var cs = ColorSpace.parse(this.dict.get('ColorSpace'), xref, res);
+    var cs = ColorSpace.parse(this.dict.get('ColorSpace', 'CS'), xref, res);
     // when bug 674619 lands, let's check if browser can do
     // normal cmyk and then we won't need to decode in JS
     if (cs.name === 'DeviceGray' || cs.name === 'DeviceRGB')
       return true;
     if (cs.name === 'DeviceCMYK' && !this.isAdobeImage &&
         this.colorTransform < 1)
       return true;
     return false;
   };
   /**
    * Checks if the image can be decoded by the browser.
    */
   JpegStream.prototype.isNativelyDecodable =
     function JpegStream_isNativelyDecodable(xref, res) {
-    var cs = ColorSpace.parse(this.dict.get('ColorSpace'), xref, res);
+    var cs = ColorSpace.parse(this.dict.get('ColorSpace', 'CS'), xref, res);
     var numComps = cs.numComps;
     if (numComps == 1 || numComps == 3)
       return true;
 
     return false;
   };
 
   return JpegStream;
@@ -30446,25 +30570,20 @@ MessageHandler.prototype = {
     this.comObj.postMessage(message);
   }
 };
 
 var WorkerMessageHandler = {
   setup: function wphSetup(handler) {
     var pdfModel = null;
 
-    handler.on('test', function wphSetupTest(data) {
-      handler.send('test', data instanceof Uint8Array);
-    });
-
-    handler.on('GetDocRequest', function wphSetupDoc(data) {
+    function loadDocument(pdfData, pdfModelSource) {
       // Create only the model of the PDFDoc, which is enough for
       // processing the content of the pdf.
-      var pdfData = data.data;
-      var pdfPassword = data.params.password;
+      var pdfPassword = pdfModelSource.password;
       try {
         pdfModel = new PDFDocument(new Stream(pdfData), pdfPassword);
       } catch (e) {
         if (e instanceof PasswordException) {
           if (e.code === 'needpassword') {
             handler.send('NeedPassword', {
               exception: e
             });
@@ -30484,16 +30603,48 @@ var WorkerMessageHandler = {
         fingerprint: pdfModel.getFingerprint(),
         destinations: pdfModel.catalog.destinations,
         outline: pdfModel.catalog.documentOutline,
         info: pdfModel.getDocumentInfo(),
         metadata: pdfModel.catalog.metadata,
         encrypted: !!pdfModel.xref.encrypt
       };
       handler.send('GetDoc', {pdfInfo: doc});
+    }
+
+    handler.on('test', function wphSetupTest(data) {
+      handler.send('test', data instanceof Uint8Array);
+    });
+
+    handler.on('GetDocRequest', function wphSetupDoc(data) {
+      var source = data.source;
+      if (source.data) {
+        // the data is array, instantiating directly from it
+        loadDocument(source.data, source);
+        return;
+      }
+
+      PDFJS.getPdf(
+        {
+          url: source.url,
+          progress: function getPDFProgress(evt) {
+            handler.send('DocProgress', {
+              loaded: evt.loaded,
+              total: evt.lengthComputable ? evt.total : void(0)
+            });
+          },
+          error: function getPDFError(e) {
+            handler.send('DocError', 'Unexpected server response of ' +
+                         e.target.status + '.');
+          },
+          headers: source.httpHeaders
+        },
+        function getPDFLoad(data) {
+          loadDocument(data, source);
+        });
     });
 
     handler.on('GetPageRequest', function wphSetupGetPage(data) {
       var pageNumber = data.pageIndex + 1;
       var pdfPage = pdfModel.getPage(pageNumber);
       var page = {
         pageIndex: data.pageIndex,
         rotate: pdfPage.rotate,
@@ -35018,29 +35169,36 @@ var Metadata = PDFJS.Metadata = (functio
     has: function Metadata_has(name) {
       return typeof this.metadata[name] !== 'undefined';
     }
   };
 
   return Metadata;
 })();
 
+
 }).call((typeof window === 'undefined') ? this : window);
+
+
 -->
 </script>
 <script type="text/javascript">
   // This specifies the location of the pdf.js file.
   PDFJS.isFirefoxExtension = true;
   PDFJS.workerSrc = 'none';
 </script>
 
+
     <link rel="stylesheet" href="viewer.css"/>
 
 
-    <!-- PDFJSSCRIPT_INCLUDE_BUILD -->
+
+
+
+
     <script type="text/javascript" src="debugger.js"></script>
     <script type="text/javascript" src="viewer.js"></script>
   </head>
 
   <body>
     <div id="outerContainer">
 
       <div id="sidebarContainer">
@@ -35091,29 +35249,35 @@ var Metadata = PDFJS.Metadata = (functio
                 </div>
                 <label id="pageNumberLabel" class="toolbarLabel" for="pageNumber" data-l10n-id="page_label">Page: </label>
                 <input type="number" id="pageNumber" class="toolbarField pageNumber" onchange="PDFView.page = this.value;" 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="openFile" class="toolbarButton openFile" title="Open File" tabindex="11" data-l10n-id="open_file" onclick="document.getElementById('fileInput').click()">
+
+
+                <button id="fullscreen" class="toolbarButton fullscreen" title="Fullscreen" tabindex="11" data-l10n-id="fullscreen" onclick="PDFView.fullscreen();">
+                  <span data-l10n-id="fullscreen_label">Fullscreen</span>
+                </button>
+
+                <button id="openFile" class="toolbarButton openFile" title="Open File" tabindex="12" data-l10n-id="open_file" onclick="document.getElementById('fileInput').click()">
                    <span data-l10n-id="open_file_label">Open</span>
                 </button>
 
-                <button id="print" class="toolbarButton print" title="Print" tabindex="11" data-l10n-id="print" onclick="window.print()">
+                <button id="print" class="toolbarButton print" title="Print" tabindex="13" data-l10n-id="print" onclick="window.print()">
                   <span data-l10n-id="print_label">Print</span>
                 </button>
 
-                <button id="download" class="toolbarButton download" title="Download" onclick="PDFView.download();" tabindex="12" data-l10n-id="download">
+                <button id="download" class="toolbarButton download" title="Download" onclick="PDFView.download();" tabindex="14" data-l10n-id="download">
                   <span data-l10n-id="download_label">Download</span>
                 </button>
                 <!-- <div class="toolbarButtonSpacer"></div> -->
-                <a href="#" id="viewBookmark" class="toolbarButton bookmark" title="Current view (copy or open in new window)" tabindex="13" data-l10n-id="bookmark"><span data-l10n-id="bookmark_label">Current View</span></a>
+                <a href="#" id="viewBookmark" class="toolbarButton bookmark" title="Current view (copy or open in new window)" tabindex="15" data-l10n-id="bookmark"><span data-l10n-id="bookmark_label">Current View</span></a>
               </div>
               <div class="outerCenter">
                 <div class="innerCenter" id="toolbarViewerMiddle">
                   <div class="splitToolbarButton">
                     <button class="toolbarButton zoomOut" title="Zoom Out" onclick="PDFView.zoomOut();" tabindex="8" data-l10n-id="zoom_out">
                       <span data-l10n-id="zoom_out_label">Zoom Out</span>
                     </button>
                     <div class="splitToolbarButtonSeparator"></div>
@@ -35170,8 +35334,9 @@ var Metadata = PDFJS.Metadata = (functio
           <textarea id="errorMoreInfo" hidden='true' readonly="readonly"></textarea>
         </div>
       </div> <!-- mainContainer -->
 
     </div> <!-- outerContainer -->
     <div id="printContainer"></div>
   </body>
 </html>
+
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -55,38 +55,46 @@ var ProgressBar = (function ProgressBarC
 
     // Fetch the sub-elements for later
     this.div = document.querySelector(id + ' .progress');
 
     // Get options, with sensible defaults
     this.height = opts.height || 100;
     this.width = opts.width || 100;
     this.units = opts.units || '%';
-    this.percent = opts.percent || 0;
 
     // Initialize heights
     this.div.style.height = this.height + this.units;
   }
 
   ProgressBar.prototype = {
 
     updateBar: function ProgressBar_updateBar() {
+      if (this._indeterminate) {
+        this.div.classList.add('indeterminate');
+        return;
+      }
+
       var progressSize = this.width * this._percent / 100;
 
       if (this._percent > 95)
         this.div.classList.add('full');
+      else
+        this.div.classList.remove('full');
+      this.div.classList.remove('indeterminate');
 
       this.div.style.width = progressSize + this.units;
     },
 
     get percent() {
       return this._percent;
     },
 
     set percent(val) {
+      this._indeterminate = isNaN(val);
       this._percent = clamp(val, 0, 100);
       this.updateBar();
     }
   };
 
   return ProgressBar;
 })();
 
@@ -146,43 +154,37 @@ var FirefoxCom = (function FirefoxComClo
 
       var sender = document.createEvent('HTMLEvents');
       sender.initEvent('pdf.js.message', true, false);
       return request.dispatchEvent(sender);
     }
   };
 })();
 
+
 // Settings Manager - This is a utility for saving settings
 // First we see if localStorage is available
 // If not, we use FUEL in FF
 var Settings = (function SettingsClosure() {
   var isLocalStorageEnabled = (function localStorageEnabledTest() {
     // Feature test as per http://diveintohtml5.info/storage.html
     // The additional localStorage call is to get around a FF quirk, see
     // bug #495747 in bugzilla
     try {
       return 'localStorage' in window && window['localStorage'] !== null &&
           localStorage;
     } catch (e) {
       return false;
     }
   })();
 
-  var isFirefoxExtension = PDFJS.isFirefoxExtension;
-
   function Settings(fingerprint) {
     var database = null;
     var index;
-    if (isFirefoxExtension)
-      database = FirefoxCom.requestSync('getDatabase', null) || '{}';
-    else if (isLocalStorageEnabled)
-      database = localStorage.getItem('database') || '{}';
-    else
-      return false;
+    database = FirefoxCom.requestSync('getDatabase', null) || '{}';
 
     database = JSON.parse(database);
     if (!('files' in database))
       database.files = [];
     if (database.files.length >= kSettingsMemory)
       database.files.shift();
     for (var i = 0, length = database.files.length; i < length; i++) {
       var branch = database.files[i];
@@ -200,20 +202,17 @@ var Settings = (function SettingsClosure
   Settings.prototype = {
     set: function settingsSet(name, val) {
       if (!('file' in this))
         return false;
 
       var file = this.file;
       file[name] = val;
       var database = JSON.stringify(this.database);
-      if (isFirefoxExtension)
-        FirefoxCom.requestSync('setDatabase', database);
-      else if (isLocalStorageEnabled)
-        localStorage.setItem('database', database);
+      FirefoxCom.requestSync('setDatabase', database);
     },
 
     get: function settingsGet(name, defaultValue) {
       if (!('file' in this))
         return defaultValue;
 
       return this.file[name] || defaultValue;
     }
@@ -236,16 +235,18 @@ var PDFView = {
   container: null,
   thumbnailContainer: null,
   initialized: false,
   fellback: false,
   pdfDocument: null,
   sidebarOpen: false,
   pageViewScroll: null,
   thumbnailViewScroll: null,
+  isFullscreen: false,
+  previousScale: null,
 
   // called once when the document is loaded
   initialize: function pdfViewInitialize() {
     var container = this.container = document.getElementById('viewerContainer');
     this.pageViewScroll = {};
     this.watchScroll(container, this.pageViewScroll, updateViewarea);
 
     var thumbnailContainer = this.thumbnailContainer =
@@ -302,16 +303,17 @@ var PDFView = {
     this.currentScaleValue = value;
     if (scale) {
       this.setScale(scale, true, noScroll);
       return;
     }
 
     var container = this.container;
     var currentPage = this.pages[this.page - 1];
+
     var pageWidthScale = (container.clientWidth - kScrollbarPadding) /
                           currentPage.width * currentPage.scale / kCssUnits;
     var pageHeightScale = (container.clientHeight - kScrollbarPadding) /
                            currentPage.height * currentPage.scale / kCssUnits;
     switch (value) {
       case 'page-actual':
         scale = 1;
         break;
@@ -343,28 +345,27 @@ var PDFView = {
     var newScale = (this.currentScale / kDefaultScaleDelta).toFixed(2);
     newScale = Math.max(kMinScale, newScale);
     this.parseScale(newScale, true);
   },
 
   set page(val) {
     var pages = this.pages;
     var input = document.getElementById('pageNumber');
+    var event = document.createEvent('UIEvents');
+    event.initUIEvent('pagechange', false, false, window, 0);
+
     if (!(0 < val && val <= pages.length)) {
-      var event = document.createEvent('UIEvents');
-      event.initUIEvent('pagechange', false, false, window, 0);
       event.pageNumber = this.page;
       window.dispatchEvent(event);
       return;
     }
 
     pages[val - 1].updateStats();
     currentPageNumber = val;
-    var event = document.createEvent('UIEvents');
-    event.initUIEvent('pagechange', false, false, window, 0);
     event.pageNumber = val;
     window.dispatchEvent(event);
 
     // checking if the this.page was called from the updateViewarea function:
     // avoiding the creation of two "set page" method (internal and public)
     if (updateViewarea.inProgress)
       return;
 
@@ -385,21 +386,63 @@ var PDFView = {
     // shadow
     Object.defineProperty(this, 'supportsPrinting', { value: value,
                                                       enumerable: true,
                                                       configurable: true,
                                                       writable: false });
     return value;
   },
 
+  get supportsFullscreen() {
+    var doc = document.documentElement;
+    var support = doc.requestFullScreen || doc.mozRequestFullScreen ||
+                  doc.webkitRequestFullScreen;
+    Object.defineProperty(this, 'supportsFullScreen', { value: support,
+                                                        enumerable: true,
+                                                        configurable: true,
+                                                        writable: false });
+    return support;
+  },
+
+  initPassiveLoading: function pdfViewInitPassiveLoading() {
+    if (!PDFView.loadingBar) {
+      PDFView.loadingBar = new ProgressBar('#loadingBar', {});
+    }
+
+    window.addEventListener('message', function window_message(e) {
+      var args = e.data;
+
+      if (typeof args !== 'object' || !('pdfjsLoadAction' in args))
+        return;
+      switch (args.pdfjsLoadAction) {
+        case 'progress':
+          PDFView.progress(args.loaded / args.total);
+          break;
+        case 'complete':
+          if (!args.data) {
+            PDFView.error(mozL10n.get('loading_error', null,
+                          'An error occurred while loading the PDF.'), e);
+            break;
+          }
+          PDFView.open(args.data, 0);
+          break;
+      }
+    });
+    FirefoxCom.requestSync('initPassiveLoading', null);
+  },
+
+  setTitleUsingUrl: function pdfViewSetTitleUsingUrl(url) {
+    this.url = url;
+    document.title = decodeURIComponent(getFileName(url)) || url;
+  },
+
   open: function pdfViewOpen(url, scale, password) {
     var parameters = {password: password};
     if (typeof url === 'string') { // URL
-      this.url = url;
-      document.title = decodeURIComponent(getFileName(url)) || url;
+      this.setTitleUsingUrl(url);
       parameters.url = url;
     } else if (url && 'byteLength' in url) { // ArrayBuffer
       parameters.data = url;
     }
 
     if (!PDFView.loadingBar) {
       PDFView.loadingBar = new ProgressBar('#loadingBar', {});
     }
@@ -441,51 +484,44 @@ var PDFView = {
   },
 
   download: function pdfViewDownload() {
     function noData() {
       FirefoxCom.request('download', { originalUrl: url });
     }
 
     var url = this.url.split('#')[0];
-    if (PDFJS.isFirefoxExtension) {
-      // 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'));
-
-          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.');
-              }
-              window.URL.revokeObjectURL(blobUrl);
+    // 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'));
+  
+        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.');
             }
-          );
-        },
-        noData // Error ocurred try downloading with just the url.
-      );
-    } else {
-      url += '#pdfjs.action=download', '_parent';
-      window.open(url, '_parent');
-    }
+            window.URL.revokeObjectURL(blobUrl);
+          }
+        );
+      },
+      noData // Error occurred try downloading with just the url.
+    );
   },
 
   fallback: function pdfViewFallback() {
-    if (!PDFJS.isFirefoxExtension)
-      return;
     // Only trigger the fallback once so we don't spam the user with messages
     // for one PDF.
     if (this.fellback)
       return;
     this.fellback = true;
     var url = this.url.split('#')[0];
     FirefoxCom.request('fallback', url, function response(download) {
       if (!download)
@@ -538,19 +574,17 @@ var PDFView = {
   },
 
   /**
    * For the firefox extension we prefix the full url on anchor links so they
    * don't come up as resource:// urls and so open in new tab/window works.
    * @param {String} anchor The anchor hash include the #.
    */
   getAnchorUrl: function getAnchorUrl(anchor) {
-    if (PDFJS.isFirefoxExtension)
-      return this.url.split('#')[0] + anchor;
-    return anchor;
+    return this.url.split('#')[0] + anchor;
   },
 
   /**
    * Show the error box.
    * @param {String} message A message that is human readable.
    * @param {Object} moreInfo (optional) Further information about the error
    *                            that is more technical.  Should have a 'message'
    *                            and optionally a 'stack' property.
@@ -574,50 +608,22 @@ var PDFView = {
         }
         if (moreInfo.lineNumber) {
           moreInfoText += '\n' +
             mozL10n.get('error_line', {line: moreInfo.lineNumber},
             'Line: {{line}}');
         }
       }
     }
-    if (PDFJS.isFirefoxExtension) {
-      console.error(message + '\n' + moreInfoText);
-      this.fallback();
-      return;
-    }
-    var errorWrapper = document.getElementById('errorWrapper');
-    errorWrapper.removeAttribute('hidden');
-
-    var errorMessage = document.getElementById('errorMessage');
-    errorMessage.textContent = message;
-
-    var closeButton = document.getElementById('errorClose');
-    closeButton.onclick = function() {
-      errorWrapper.setAttribute('hidden', 'true');
-    };
 
-    var errorMoreInfo = document.getElementById('errorMoreInfo');
-    var moreInfoButton = document.getElementById('errorShowMore');
-    var lessInfoButton = document.getElementById('errorShowLess');
-    moreInfoButton.onclick = function() {
-      errorMoreInfo.removeAttribute('hidden');
-      moreInfoButton.setAttribute('hidden', 'true');
-      lessInfoButton.removeAttribute('hidden');
-    };
-    lessInfoButton.onclick = function() {
-      errorMoreInfo.setAttribute('hidden', 'true');
-      moreInfoButton.removeAttribute('hidden');
-      lessInfoButton.setAttribute('hidden', 'true');
-    };
-    moreInfoButton.removeAttribute('hidden');
-    lessInfoButton.setAttribute('hidden', 'true');
-    errorMoreInfo.value = moreInfoText;
+    var loadingBox = document.getElementById('loadingBox');
+    loadingBox.setAttribute('hidden', 'true');
 
-    errorMoreInfo.rows = moreInfoText.split('\n').length - 1;
+    console.error(message + '\n' + moreInfoText);
+    this.fallback();
   },
 
   progress: function pdfViewProgress(level) {
     var percent = Math.round(level * 100);
     PDFView.loadingBar.percent = percent;
   },
 
   load: function pdfViewLoad(pdfDocument, scale) {
@@ -766,53 +772,53 @@ var PDFView = {
       var thumbView = this.getHighestPriority(visibleThumbs,
                                               this.thumbnails,
                                               this.thumbnailViewScroll.down);
       if (thumbView)
         this.renderView(thumbView, 'thumbnail');
     }
   },
 
-  getHighestPriority: function pdfViewGetHighestPriority(visibleViews, views,
+  getHighestPriority: function pdfViewGetHighestPriority(visible, views,
                                                          scrolledDown) {
     // The state has changed figure out which page has the highest priority to
     // render next (if any).
     // Priority:
     // 1 visible pages
     // 2 if last scrolled down page after the visible pages
     // 2 if last scrolled up page before the visible pages
+    var visibleViews = visible.views;
+
     var numVisible = visibleViews.length;
     if (numVisible === 0) {
-      info('No visible views.');
       return false;
     }
     for (var i = 0; i < numVisible; ++i) {
       var view = visibleViews[i].view;
-      if (!this.isViewFinshed(view))
+      if (!this.isViewFinished(view))
         return view;
     }
 
     // All the visible views have rendered, try to render next/previous pages.
     if (scrolledDown) {
-      var lastVisible = visibleViews[visibleViews.length - 1];
-      var nextPageIndex = lastVisible.id;
+      var nextPageIndex = visible.last.id;
       // ID's start at 1 so no need to add 1.
-      if (views[nextPageIndex] && !this.isViewFinshed(views[nextPageIndex]))
+      if (views[nextPageIndex] && !this.isViewFinished(views[nextPageIndex]))
         return views[nextPageIndex];
     } else {
-      var previousPageIndex = visibleViews[0].id - 2;
+      var previousPageIndex = visible.first.id - 2;
       if (views[previousPageIndex] &&
-          !this.isViewFinshed(views[previousPageIndex]))
+          !this.isViewFinished(views[previousPageIndex]))
         return views[previousPageIndex];
     }
     // Everything that needs to be rendered has been.
     return false;
   },
 
-  isViewFinshed: function pdfViewNeedsRendering(view) {
+  isViewFinished: function pdfViewNeedsRendering(view) {
     return view.renderingState === RenderingStates.FINISHED;
   },
 
   // Render a page or thumbnail view. This calls the appropriate function based
   // on the views state. If the view is already rendered it will return false.
   renderView: function pdfViewRender(view, type) {
     var state = view.renderingState;
     switch (state) {
@@ -831,24 +837,24 @@ var PDFView = {
         break;
     }
     return true;
   },
 
   search: function pdfViewStartSearch() {
     // Limit this function to run every <SEARCH_TIMEOUT>ms.
     var SEARCH_TIMEOUT = 250;
-    var lastSeach = this.lastSearch;
+    var lastSearch = this.lastSearch;
     var now = Date.now();
-    if (lastSeach && (now - lastSeach) < SEARCH_TIMEOUT) {
+    if (lastSearch && (now - lastSearch) < SEARCH_TIMEOUT) {
       if (!this.searchTimer) {
         this.searchTimer = setTimeout(function resumeSearch() {
             PDFView.search();
           },
-          SEARCH_TIMEOUT - (now - lastSeach)
+          SEARCH_TIMEOUT - (now - lastSearch)
         );
       }
       return;
     }
     this.searchTimer = null;
     this.lastSearch = now;
 
     function bindLink(link, pageNumber) {
@@ -907,35 +913,34 @@ var PDFView = {
       var params = PDFView.parseQueryString(hash);
       // borrowing syntax from "Parameters for Opening PDF Files"
       if ('nameddest' in params) {
         PDFView.navigateTo(params.nameddest);
         return;
       }
       if ('page' in params) {
         var pageNumber = (params.page | 0) || 1;
-        this.page = pageNumber;
         if ('zoom' in params) {
           var zoomArgs = params.zoom.split(','); // scale,left,top
           // building destination array
 
           // If the zoom value, it has to get divided by 100. If it is a string,
           // it should stay as it is.
           var zoomArg = zoomArgs[0];
           var zoomArgNumber = parseFloat(zoomArg);
           if (zoomArgNumber)
             zoomArg = zoomArgNumber / 100;
 
           var dest = [null, {name: 'XYZ'}, (zoomArgs[1] | 0),
             (zoomArgs[2] | 0), zoomArg];
           var currentPage = this.pages[pageNumber - 1];
           currentPage.scrollIntoView(dest);
-        } else
-          this.page = params.page; // simple page
-        return;
+        } else {
+          this.page = pageNumber; // simple page
+        }
       }
     } else if (/^\d+$/.test(hash)) // page number
       this.page = hash;
     else // named destination
       PDFView.navigateTo(unescape(hash));
   },
 
   switchSidebarView: function pdfViewSwitchSidebarView(view) {
@@ -996,54 +1001,86 @@ var PDFView = {
       self.pages[pageIndex].pdfPage.getTextContent().then(
         function textContentResolved(textContent) {
           self.pageText[pageIndex] = textContent;
           self.search();
           if ((pageIndex + 1) < self.pages.length)
             extractPageText(pageIndex + 1);
         }
       );
-    };
+    }
     extractPageText(0);
   },
 
   getVisiblePages: function pdfViewGetVisiblePages() {
     return this.getVisibleElements(this.container,
-                                   this.pages);
+                                   this.pages, true);
   },
 
   getVisibleThumbs: function pdfViewGetVisibleThumbs() {
     return this.getVisibleElements(this.thumbnailContainer,
                                    this.thumbnails);
   },
 
   // Generic helper to find out what elements are visible within a scroll pane.
-  getVisibleElements: function pdfViewGetVisibleElements(scrollEl, views) {
+  getVisibleElements: function pdfViewGetVisibleElements(
+      scrollEl, views, sortByVisibility) {
     var currentHeight = 0, view;
     var top = scrollEl.scrollTop;
 
-    for (var i = 1; i <= views.length; ++i) {
+    for (var i = 1, ii = views.length; i <= ii; ++i) {
       view = views[i - 1];
       currentHeight = view.el.offsetTop;
       if (currentHeight + view.el.clientHeight > top)
         break;
       currentHeight += view.el.clientHeight;
     }
 
     var visible = [];
-    var bottom = top + scrollEl.clientHeight;
-    for (; i <= views.length && currentHeight < bottom; ++i) {
-      view = views[i - 1];
-      currentHeight = view.el.offsetTop;
-      visible.push({ id: view.id, y: currentHeight,
-                     view: view });
-      currentHeight += view.el.clientHeight;
+
+    // Algorithm broken in fullscreen mode
+    if (this.isFullscreen) {
+      var currentPage = this.pages[this.page - 1];
+      visible.push({
+        id: currentPage.id,
+        view: currentPage
+      });
+
+      return { first: currentPage, last: currentPage, views: visible};
     }
 
-    return visible;
+    var bottom = top + scrollEl.clientHeight;
+    var nextHeight, hidden, percent, viewHeight;
+    for (; i <= ii && currentHeight < bottom; ++i) {
+      view = views[i - 1];
+      viewHeight = view.el.clientHeight;
+      currentHeight = view.el.offsetTop;
+      nextHeight = currentHeight + viewHeight;
+      hidden = Math.max(0, top - currentHeight) +
+               Math.max(0, nextHeight - bottom);
+      percent = Math.floor((viewHeight - hidden) * 100.0 / viewHeight);
+      visible.push({ id: view.id, y: currentHeight,
+                     view: view, percent: percent });
+      currentHeight = nextHeight;
+    }
+
+    var first = visible[0];
+    var last = visible[visible.length - 1];
+
+    if (sortByVisibility) {
+      visible.sort(function(a, b) {
+        var pc = a.percent - b.percent;
+        if (Math.abs(pc) > 0.001)
+          return -pc;
+
+        return a.id - b.id; // ensure stability
+      });
+    }
+
+    return {first: first, last: last, views: visible};
   },
 
   // Helper function to parse query string (e.g. ?param1=value&parm2=...).
   parseQueryString: function pdfViewParseQueryString(query) {
     var parts = query.split('&');
     var params = {};
     for (var i = 0, ii = parts.length; i < parts.length; ++i) {
       var param = parts[i].split('=');
@@ -1067,16 +1104,54 @@ var PDFView = {
       this.pages[i].beforePrint();
     }
   },
 
   afterPrint: function pdfViewSetupAfterPrint() {
     var div = document.getElementById('printContainer');
     while (div.hasChildNodes())
       div.removeChild(div.lastChild);
+  },
+
+  fullscreen: function pdfViewFullscreen() {
+    var isFullscreen = document.fullscreen || document.mozFullScreen ||
+        document.webkitIsFullScreen;
+
+    if (isFullscreen) {
+      return false;
+    }
+
+    var wrapper = document.getElementById('viewerContainer');
+    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;
+    }
+
+    this.isFullscreen = true;
+    var currentPage = this.pages[this.page - 1];
+    this.previousScale = this.currentScaleValue;
+    this.parseScale('page-fit', true);
+
+    // Wait for fullscreen to take effect
+    setTimeout(function() {
+      currentPage.scrollIntoView();
+    }, 0);
+
+    return true;
+  },
+
+  exitFullscreen: function pdfViewExitFullscreen() {
+    this.isFullscreen = false;
+    this.parseScale(this.previousScale);
+    this.page = this.page;
   }
 };
 
 var PageView = function pageView(container, pdfPage, id, scale,
                                  stats, navigateTo) {
   this.id = id;
   this.pdfPage = pdfPage;
 
@@ -1632,23 +1707,23 @@ var CustomStyle = (function CustomStyleC
       prefixed = prefixes[i] + uPropName;
       if (typeof style[prefixed] == 'string') {
         return (_cache[propName] = prefixed);
       }
     }
 
     //if all fails then set to undefined
     return (_cache[propName] = 'undefined');
-  }
+  };
 
   CustomStyle.setProp = function set(propName, element, str) {
     var prop = this.getProp(propName);
     if (prop != 'undefined')
       element.style[prop] = str;
-  }
+  };
 
   return CustomStyle;
 })();
 
 var TextLayerBuilder = function textLayerBuilder(textLayerDiv) {
   this.textLayerDiv = textLayerDiv;
 
   this.beginLayout = function textLayerBuilderBeginLayout() {
@@ -1660,32 +1735,39 @@ var TextLayerBuilder = function textLaye
     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;
       }
       var textDiv = textDivs.shift();
       if (textDiv.dataset.textLength > 0) {
         textLayerDiv.appendChild(textDiv);
 
         if (textDiv.dataset.textLength > 1) { // avoid div by zero
           // Adjust div width to match canvas text
-          // Due to the .offsetWidth calls, this is slow
-          // This needs to come after appending to the DOM
-          var textScale = textDiv.dataset.canvasWidth / textDiv.offsetWidth;
+
+          ctx.font = textDiv.style.fontSize + ' sans-serif';
+          var width = ctx.measureText(textDiv.textContent).width;
+
+          var textScale = textDiv.dataset.canvasWidth / width;
+
           CustomStyle.setProp('transform' , textDiv,
             'scale(' + textScale + ', 1)');
           CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%');
         }
       } // textLength > 0
     }
     renderTimer = setInterval(renderTextLayer, renderInterval);
 
@@ -1701,17 +1783,17 @@ var TextLayerBuilder = function textLaye
       // Immediately pause rendering
       clearInterval(renderTimer);
 
       clearTimeout(scrollTimer);
       scrollTimer = setTimeout(function textLayerScrollTimer() {
         // Resume rendering
         renderTimer = setInterval(renderTextLayer, renderInterval);
       }, resumeInterval);
-    }; // textLayerOnScroll
+    } // textLayerOnScroll
 
     window.addEventListener('scroll', textLayerOnScroll, false);
   }; // endLayout
 
   this.appendText = function textLayerBuilderAppendText(text,
                                                         fontName, fontSize) {
     var textDiv = document.createElement('div');
 
@@ -1725,65 +1807,66 @@ var TextLayerBuilder = function textLaye
     textDiv.style.top = (text.geom.y - fontHeight) + 'px';
     textDiv.textContent = PDFJS.bidi(text, -1);
     textDiv.dir = text.direction;
     textDiv.dataset.textLength = text.length;
     this.textDivs.push(textDiv);
   };
 };
 
-window.addEventListener('load', function webViewerLoad(evt) {
+document.addEventListener('DOMContentLoaded', function webViewerLoad(evt) {
   PDFView.initialize();
   var params = PDFView.parseQueryString(document.location.search.substring(1));
 
-  var file = PDFJS.isFirefoxExtension ?
-              window.location.toString() : params.file || kDefaultURL;
+  var file = window.location.toString()
 
-  if (PDFJS.isFirefoxExtension || !window.File || !window.FileReader ||
-      !window.FileList || !window.Blob) {
-    document.getElementById('openFile').setAttribute('hidden', 'true');
-  } else {
-    document.getElementById('fileInput').value = null;
-  }
+  document.getElementById('openFile').setAttribute('hidden', 'true');
 
   // Special debugging flags in the hash section of the URL.
   var hash = document.location.hash.substring(1);
   var hashParams = PDFView.parseQueryString(hash);
 
   if ('disableWorker' in hashParams)
     PDFJS.disableWorker = (hashParams['disableWorker'] === 'true');
 
-  if (!PDFJS.isFirefoxExtension) {
-    var locale = navigator.language;
-    if ('locale' in hashParams)
-      locale = hashParams['locale'];
-    mozL10n.language.code = locale;
+
+  if ('textLayer' in hashParams) {
+    switch (hashParams['textLayer']) {
+      case 'off':
+        PDFJS.disableTextLayer = true;
+        break;
+      case 'visible':
+      case 'shadow':
+      case 'hover':
+        var viewer = document.getElementById('viewer');
+        viewer.classList.add('textLayer-' + hashParams['textLayer']);
+        break;
+    }
   }
 
-  if ('disableTextLayer' in hashParams)
-    PDFJS.disableTextLayer = (hashParams['disableTextLayer'] === 'true');
-
-  if ('pdfBug' in hashParams &&
-      (!PDFJS.isFirefoxExtension || FirefoxCom.requestSync('pdfBugEnabled'))) {
+  if ('pdfBug' in hashParams && FirefoxCom.requestSync('pdfBugEnabled')) {
     PDFJS.pdfBug = true;
     var pdfBug = hashParams['pdfBug'];
     var enabled = pdfBug.split(',');
     PDFBug.enable(enabled);
     PDFBug.init();
   }
 
-  if (!PDFJS.isFirefoxExtension ||
-    (PDFJS.isFirefoxExtension && FirefoxCom.requestSync('searchEnabled'))) {
+  if (FirefoxCom.requestSync('searchEnabled')) {
     document.querySelector('#viewSearch').classList.remove('hidden');
   }
 
   if (!PDFView.supportsPrinting) {
     document.getElementById('print').classList.add('hidden');
   }
 
+  if (!PDFView.supportsFullscreen) {
+    document.getElementById('fullscreen').classList.add('hidden');
+  }
+
   // Listen for warnings to trigger the fallback UI.  Errors should be caught
   // and call PDFView.error() so we don't need to listen for those.
   PDFJS.LogManager.addLogger({
     warn: function() {
       PDFView.fallback();
     }
   });
 
@@ -1802,31 +1885,59 @@ window.addEventListener('load', function
     function() {
       this.classList.toggle('toggled');
       outerContainer.classList.add('sidebarMoving');
       outerContainer.classList.toggle('sidebarOpen');
       PDFView.sidebarOpen = outerContainer.classList.contains('sidebarOpen');
       PDFView.renderHighestPriority();
     });
 
+  if (FirefoxCom.requestSync('getLoadingType') == 'passive') {
+    PDFView.setTitleUsingUrl(file);
+    PDFView.initPassiveLoading();
+    return;
+  }
+
   PDFView.open(file, 0);
 }, true);
 
 function updateViewarea() {
+
   if (!PDFView.initialized)
     return;
-  var visiblePages = PDFView.getVisiblePages();
+  var visible = PDFView.getVisiblePages();
+  var visiblePages = visible.views;
 
   PDFView.renderHighestPriority();
 
-  updateViewarea.inProgress = true; // used in "set page"
   var currentId = PDFView.page;
-  var firstPage = visiblePages[0];
-  PDFView.page = firstPage.id;
-  updateViewarea.inProgress = false;
+  var firstPage = visible.first;
+
+  for (var i = 0, ii = visiblePages.length, stillFullyVisible = false;
+       i < ii; ++i) {
+    var page = visiblePages[i];
+
+    if (page.percent < 100)
+      break;
+
+    if (page.id === PDFView.page) {
+      stillFullyVisible = true;
+      break;
+    }
+  }
+
+  if (!stillFullyVisible) {
+    currentId = visiblePages[0].id;
+  }
+
+  if (!PDFView.isFullscreen) {
+    updateViewarea.inProgress = true; // used in "set page"
+    PDFView.page = currentId;
+    updateViewarea.inProgress = false;
+  }
 
   var currentScale = PDFView.currentScale;
   var currentScaleValue = PDFView.currentScaleValue;
   var normalizedScaleValue = currentScaleValue == currentScale ?
     currentScale * 100 : currentScaleValue;
 
   var pageNumber = firstPage.id;
   var pdfOpenParams = '#page=' + pageNumber;
@@ -1862,31 +1973,24 @@ window.addEventListener('hashchange', fu
 window.addEventListener('change', function webViewerChange(evt) {
   var files = evt.target.files;
   if (!files || files.length == 0)
     return;
 
   // Read the local file into a Uint8Array.
   var fileReader = new FileReader();
   fileReader.onload = function webViewerChangeFileReaderOnload(evt) {
-    var data = evt.target.result;
-    var buffer = new ArrayBuffer(data.length);
+    var buffer = evt.target.result;
     var uint8Array = new Uint8Array(buffer);
-
-    for (var i = 0; i < data.length; i++)
-      uint8Array[i] = data.charCodeAt(i);
-
     PDFView.open(uint8Array, 0);
   };
 
-  // Read as a binary string since "readAsArrayBuffer" is not yet
-  // implemented in Firefox.
   var file = files[0];
-  fileReader.readAsBinaryString(file);
-  document.title = file.name;
+  fileReader.readAsArrayBuffer(file);
+  PDFView.setTitleUsingUrl(file.name);
 
   // URL does not reflect proper document location - hiding some icons.
   document.getElementById('viewBookmark').setAttribute('hidden', 'true');
   document.getElementById('download').setAttribute('hidden', 'true');
 }, true);
 
 function selectScaleOption(value) {
   var options = document.getElementById('scaleSelect').options;
@@ -1933,23 +2037,23 @@ window.addEventListener('pagechange', fu
   if (document.getElementById('pageNumber').value != page) {
     document.getElementById('pageNumber').value = page;
     var selected = document.querySelector('.thumbnail.selected');
     if (selected)
       selected.classList.remove('selected');
     var thumbnail = document.getElementById('thumbnailContainer' + page);
     thumbnail.classList.add('selected');
     var visibleThumbs = PDFView.getVisibleThumbs();
-    var numVisibleThumbs = visibleThumbs.length;
+    var numVisibleThumbs = visibleThumbs.views.length;
     // If the thumbnail isn't currently visible scroll it into view.
     if (numVisibleThumbs > 0) {
-      var first = visibleThumbs[0].id;
+      var first = visibleThumbs.first.id;
       // Account for only one thumbnail being visible.
       var last = numVisibleThumbs > 1 ?
-                  visibleThumbs[numVisibleThumbs - 1].id : first;
+                  visibleThumbs.last.id : first;
       if (page <= first || page >= last)
         thumbnail.scrollIntoView();
     }
 
   }
   document.getElementById('previous').disabled = (page <= 1);
   document.getElementById('next').disabled = (page >= PDFView.pages.length);
 }, true);
@@ -2002,17 +2106,17 @@ window.addEventListener('keydown', funct
 
   // Some shortcuts should not get handled if a control/input element
   // is selected.
   var curElement = document.activeElement;
   if (curElement && curElement.tagName == 'INPUT')
     return;
   var controlsElement = document.getElementById('controls');
   while (curElement) {
-    if (curElement === controlsElement)
+    if (curElement === controlsElement && !PDFView.isFullscreen)
       return; // ignoring if the 'controls' element is focused
     curElement = curElement.parentNode;
   }
 
   if (cmd == 0) { // no control key pressed at all.
     switch (evt.keyCode) {
       case 37: // left arrow
       case 75: // 'k'
@@ -2021,23 +2125,47 @@ window.addEventListener('keydown', funct
         handled = true;
         break;
       case 39: // right arrow
       case 74: // 'j'
       case 78: // 'n'
         PDFView.page++;
         handled = true;
         break;
+
+      case 32: // spacebar
+        if (PDFView.isFullscreen) {
+          PDFView.page++;
+          handled = true;
+        }
+        break;
     }
   }
 
   if (handled) {
     evt.preventDefault();
   }
 });
 
 window.addEventListener('beforeprint', function beforePrint(evt) {
   PDFView.beforePrint();
 });
 
 window.addEventListener('afterprint', function afterPrint(evt) {
   PDFView.afterPrint();
 });
+
+(function fullscreenClosure() {
+  function fullscreenChange(e) {
+    var isFullscreen = document.fullscreen || document.mozFullScreen ||
+        document.webkitIsFullScreen;
+
+    if (!isFullscreen) {
+      PDFView.exitFullscreen();
+    }
+  }
+
+  window.addEventListener('fullscreenchange', fullscreenChange, false);
+  window.addEventListener('mozfullscreenchange', fullscreenChange, false);
+  window.addEventListener('webkitfullscreenchange', fullscreenChange, false);
+})();
+
+
--- a/browser/extensions/pdfjs/extension-files
+++ b/browser/extensions/pdfjs/extension-files
@@ -4,16 +4,17 @@ content/PdfJs.jsm
 content/web/debugger.js
 content/web/images/annotation-check.svg
 content/web/images/annotation-comment.svg
 content/web/images/annotation-text.svg
 content/web/images/loading-icon.gif
 content/web/images/texture.png
 content/web/images/toolbarButton-bookmark.png
 content/web/images/toolbarButton-download.png
+content/web/images/toolbarButton-fullscreen.png
 content/web/images/toolbarButton-menuArrows.png
 content/web/images/toolbarButton-openFile.png
 content/web/images/toolbarButton-pageDown-rtl.png
 content/web/images/toolbarButton-pageDown.png
 content/web/images/toolbarButton-pageUp-rtl.png
 content/web/images/toolbarButton-pageUp.png
 content/web/images/toolbarButton-print.png
 content/web/images/toolbarButton-search.png
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -259,31 +259,36 @@ class Automation(object):
       raise SyntaxError(lineno + 1, "missing primary location")
 
     return locations
 
   def setupPermissionsDatabase(self, profileDir, permissions):
     # Open database and create table
     permDB = sqlite3.connect(os.path.join(profileDir, "permissions.sqlite"))
     cursor = permDB.cursor();
+
+    cursor.execute("PRAGMA user_version=3");
+
     # SQL copied from nsPermissionManager.cpp
     cursor.execute("""CREATE TABLE moz_hosts (
        id INTEGER PRIMARY KEY,
        host TEXT,
        type TEXT,
        permission INTEGER,
        expireType INTEGER,
-       expireTime INTEGER)""")
+       expireTime INTEGER,
+       appId INTEGER,
+       isInBrowserElement INTEGER)""")
 
     # Insert desired permissions
     c = 0
     for perm in permissions.keys():
       for host,allow in permissions[perm]:
         c += 1
-        cursor.execute("INSERT INTO moz_hosts values(?, ?, ?, ?, 0, 0)",
+        cursor.execute("INSERT INTO moz_hosts values(?, ?, ?, ?, 0, 0, 0, 0)",
                        (c, host, perm, 1 if allow else 2))
 
     # Commit and close
     permDB.commit()
     cursor.close()
 
   def setupTestApps(self, profileDir, apps):
     webappJSONTemplate = Template(""""$name": {
--- a/caps/tests/mochitest/test_principal_extendedorigin_appid_appstatus.html
+++ b/caps/tests/mochitest/test_principal_extendedorigin_appid_appstatus.html
@@ -384,16 +384,18 @@ function runTest() {
   for (var i=0; i<gData.length; ++i) {
     let data = gData[i];
 
     var iframe = document.createElement('iframe');
     iframe.check = function() {
       checkIFrame(this, data);
     };
     iframe.addChild = function() {
+      SpecialPowers.addPermission("browser", true, iframe.contentDocument);
+
       var childFrame = document.createElement('iframe');
 
       if (data.child.app) {
         childFrame.setAttribute('mozapp', data.child.app)
         childFrame.setAttribute('mozbrowser', '');
       } else if (data.child.browser) {
         childFrame.setAttribute('mozbrowser', '');
       }
@@ -424,16 +426,15 @@ function runTest() {
     content.appendChild(iframe);
 
     yield;
   }
 }
 
 var gTestRunner = runTest();
 
-SpecialPowers.addPermission("browser", true, "http://example.org");
 SpecialPowers.pushPrefEnv({'set':[["dom.mozBrowserFramesEnabled", true]]},
                            function() { gTestRunner.next(); });
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -1766,18 +1766,18 @@ protected:
   nsIDocument* mParentDocument;
 
   // A reference to the element last returned from GetRootElement().
   mozilla::dom::Element* mCachedRootElement;
 
   // We hold a strong reference to mNodeInfoManager through mNodeInfo
   nsNodeInfoManager* mNodeInfoManager; // [STRONG]
   nsRefPtr<mozilla::css::Loader> mCSSLoader;
-  mozilla::css::ImageLoader* mStyleImageLoader; // [STRONG]
-  nsHTMLStyleSheet* mAttrStyleSheet;
+  nsRefPtr<mozilla::css::ImageLoader> mStyleImageLoader;
+  nsRefPtr<nsHTMLStyleSheet> mAttrStyleSheet;
 
   // The set of all object, embed, applet, video and audio elements for
   // which this is the owner document. (They might not be in the document.)
   // These are non-owning pointers, the elements are responsible for removing
   // themselves when they go away.
   nsAutoPtr<nsTHashtable<nsPtrHashKey<nsIContent> > > mFreezableElements;
 
   // The set of all links that need their status resolved.  Links must add themselves
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1617,17 +1617,16 @@ nsDocument::~nsDocument()
     mStyleSheets[indx]->SetOwningDocument(nullptr);
   }
   indx = mCatalogSheets.Count();
   while (--indx >= 0) {
     mCatalogSheets[indx]->SetOwningDocument(nullptr);
   }
   if (mAttrStyleSheet) {
     mAttrStyleSheet->SetOwningDocument(nullptr);
-    NS_RELEASE(mAttrStyleSheet);
   }
   if (mStyleAttrStyleSheet)
     mStyleAttrStyleSheet->SetOwningDocument(nullptr);
 
   if (mListenerManager) {
     mListenerManager->Disconnect();
     UnsetFlags(NODE_HAS_LISTENERMANAGER);
   }
@@ -1638,17 +1637,16 @@ nsDocument::~nsDocument()
 
   if (mCSSLoader) {
     // Could be null here if Init() failed
     mCSSLoader->DropDocumentReference();
   }
 
   if (mStyleImageLoader) {
     mStyleImageLoader->DropDocumentReference();
-    NS_RELEASE(mStyleImageLoader);
   }
 
   delete mHeaderData;
 
   if (mBoxObjectTable) {
     mBoxObjectTable->EnumerateRead(ClearAllBoxObjects, nullptr);
     delete mBoxObjectTable;
   }
@@ -1996,17 +1994,16 @@ nsDocument::Init()
 
 
   mOnloadBlocker = new nsOnloadBlocker();
   mCSSLoader = new mozilla::css::Loader(this);
   // Assume we're not quirky, until we know otherwise
   mCSSLoader->SetCompatibilityMode(eCompatibility_FullStandards);
 
   mStyleImageLoader = new mozilla::css::ImageLoader(this);
-  NS_ADDREF(mStyleImageLoader);
 
   mNodeInfoManager = new nsNodeInfoManager();
   nsresult rv = mNodeInfoManager->Init(this);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // mNodeInfo keeps NodeInfoManager alive!
   mNodeInfo = mNodeInfoManager->GetDocumentNodeInfo();
   NS_ENSURE_TRUE(mNodeInfo, NS_ERROR_OUT_OF_MEMORY);
@@ -2273,17 +2270,16 @@ nsDocument::ResetStylesheetsToURI(nsIURI
     nsCOMPtr<nsIPresShell> shell = GetShell();
     if (shell) {
       shell->StyleSet()->RemoveStyleSheet(nsStyleSet::ePresHintSheet,
                                           mAttrStyleSheet);
     }
     mAttrStyleSheet->Reset(aURI);
   } else {
     mAttrStyleSheet = new nsHTMLStyleSheet(aURI, this);
-    NS_ADDREF(mAttrStyleSheet);
   }
 
   // Don't use AddStyleSheet, since it'll put the sheet into style
   // sets in the document level, which is not desirable here.
   mAttrStyleSheet->SetOwningDocument(this);
   
   if (mStyleAttrStyleSheet) {
     // Remove this sheet from all style sets
--- a/dom/browser-element/mochitest/browserElement_SetVisibleFrames.js
+++ b/dom/browser-element/mochitest/browserElement_SetVisibleFrames.js
@@ -13,16 +13,21 @@
 SimpleTest.waitForExplicitFinish();
 
 var iframe;
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
+  var principal = SpecialPowers.wrap(SpecialPowers.getNodePrincipal(document));
+  SpecialPowers.addPermission("browser", true, { url: SpecialPowers.wrap(principal.URI).spec,
+                                                 appId: principal.appId,
+                                                 isInBrowserElement: true });
+
   iframe = document.createElement('iframe');
   iframe.mozbrowser = true;
 
   // Our test involves three <iframe mozbrowser>'s, parent, child1, and child2.
   // child1 and child2 are contained inside parent.  child1 is visibile, and
   // child2 is not.
   //
   // For the purposes of this test, we want there to be a process barrier
@@ -50,16 +55,22 @@ function test2() {
 
 function finish() {
   // We need to remove this listener because when this test finishes and the
   // iframe containing this document is navigated, we'll fire a
   // visibilitychange(false) event on all child iframes.  That's OK and
   // expected, but if we don't remove our listener, then we'll end up causing
   // the /next/ test to fail!
   iframe.removeEventListener('mozbrowsershowmodalprompt', checkMessage);
+
+  var principal = SpecialPowers.wrap(SpecialPowers.getNodePrincipal(document));
+  SpecialPowers.removePermission("browser", { url: SpecialPowers.wrap(principal.URI).spec,
+                                              appId: principal.appId,
+                                              isInBrowserElement: true });
+
   SimpleTest.finish();
 }
 
 var expectedMsg = null;
 var expectedMsgCallback = null;
 function expectMessage(msg, next) {
   expectedMsg = msg;
   expectedMsgCallback = next;
--- a/dom/browser-element/mochitest/browserElement_SetVisibleFrames2.js
+++ b/dom/browser-element/mochitest/browserElement_SetVisibleFrames2.js
@@ -7,16 +7,21 @@
 "use strict";
 
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
+  var principal = SpecialPowers.wrap(SpecialPowers.getNodePrincipal(document));
+  SpecialPowers.addPermission("browser", true, { url: SpecialPowers.wrap(principal.URI).spec,
+                                                 appId: principal.appId,
+                                                 isInBrowserElement: true });
+
   var iframe = document.createElement('iframe');
   iframe.mozbrowser = true;
 
   // We need remote = false here until bug 761935 is fixed; see
   // SetVisibleFrames.js for an explanation.
   iframe.remote = false;
 
   iframe.addEventListener('mozbrowserloadend', function loadEnd(e) {
@@ -30,24 +35,33 @@ function runTest() {
       ok(true, "Got parent:finish");
 
       // Give any extra events a chance to fire, then end the test.
       SimpleTest.executeSoon(function() {
         SimpleTest.executeSoon(function() {
           SimpleTest.executeSoon(function() {
             SimpleTest.executeSoon(function() {
               SimpleTest.executeSoon(function() {
-                SimpleTest.finish();
+                finish();
               });
             });
           });
         });
       });
     }
     else {
       ok(false, "Got unexpected message: " + e.detail.message);
     }
   });
 
   document.body.appendChild(iframe);
 }
 
+function finish() {
+  var principal = SpecialPowers.wrap(SpecialPowers.getNodePrincipal(document));
+  SpecialPowers.removePermission("browser", { url: SpecialPowers.wrap(principal.URI).spec,
+                                              appId: principal.appId,
+                                              isInBrowserElement: true });
+
+  SimpleTest.finish();
+}
+
 runTest();
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -90,16 +90,19 @@
 #include "mozilla/dom/indexedDB/PIndexedDBChild.h"
 #include "mozilla/dom/sms/SmsChild.h"
 #include "mozilla/dom/devicestorage/DeviceStorageRequestChild.h"
 
 #include "nsDOMFile.h"
 #include "nsIRemoteBlob.h"
 #include "StructuredCloneUtils.h"
 #include "URIUtils.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsContentUtils.h"
+#include "nsIPrincipal.h"
 
 using namespace mozilla::docshell;
 using namespace mozilla::dom::devicestorage;
 using namespace mozilla::dom::sms;
 using namespace mozilla::dom::indexedDB;
 using namespace mozilla::hal_sandbox;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
@@ -842,17 +845,30 @@ ContentChild::RecvAddPermission(const IP
 #if MOZ_PERMISSIONS
   nsCOMPtr<nsIPermissionManager> permissionManagerIface =
       do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
   nsPermissionManager* permissionManager =
       static_cast<nsPermissionManager*>(permissionManagerIface.get());
   NS_ABORT_IF_FALSE(permissionManager, 
                    "We have no permissionManager in the Content process !");
 
-  permissionManager->AddInternal(nsCString(permission.host),
+  nsCOMPtr<nsIURI> uri;
+  NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("http://") + nsCString(permission.host));
+  NS_ENSURE_TRUE(uri, true);
+
+  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
+  MOZ_ASSERT(secMan);
+
+  nsCOMPtr<nsIPrincipal> principal;
+  nsresult rv = secMan->GetAppCodebasePrincipal(uri, permission.appId,
+                                                permission.isInBrowserElement,
+                                                getter_AddRefs(principal));
+  NS_ENSURE_SUCCESS(rv, true);
+
+  permissionManager->AddInternal(principal,
                                  nsCString(permission.type),
                                  permission.capability,
                                  0,
                                  permission.expireType,
                                  permission.expireTime,
                                  nsPermissionManager::eNotify,
                                  nsPermissionManager::eNoDBOperation);
 #endif
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -779,27 +779,33 @@ ContentParent::RecvReadPermissions(Infal
             break;
 
         nsCOMPtr<nsISupports> supp;
         enumerator->GetNext(getter_AddRefs(supp));
         nsCOMPtr<nsIPermission> perm = do_QueryInterface(supp);
 
         nsCString host;
         perm->GetHost(host);
+        uint32_t appId;
+        perm->GetAppId(&appId);
+        bool isInBrowserElement;
+        perm->GetIsInBrowserElement(&isInBrowserElement);
         nsCString type;
         perm->GetType(type);
         uint32_t capability;
         perm->GetCapability(&capability);
         uint32_t expireType;
         perm->GetExpireType(&expireType);
         int64_t expireTime;
         perm->GetExpireTime(&expireTime);
 
-        aPermissions->AppendElement(IPC::Permission(host, type, capability,
-                                                    expireType, expireTime));
+        aPermissions->AppendElement(IPC::Permission(host, appId,
+                                                    isInBrowserElement, type,
+                                                    capability, expireType,
+                                                    expireTime));
     }
 
     // Ask for future changes
     mSendPermissionUpdates = true;
 #endif
 
     return true;
 }
--- a/extensions/cookie/nsPermission.cpp
+++ b/extensions/cookie/nsPermission.cpp
@@ -5,40 +5,54 @@
 
 #include "nsPermission.h"
 
 // nsPermission Implementation
 
 NS_IMPL_ISUPPORTS1(nsPermission, nsIPermission)
 
 nsPermission::nsPermission(const nsACString &aHost,
+                           uint32_t aAppId,
+                           bool aIsInBrowserElement,
                            const nsACString &aType,
                            uint32_t         aCapability,
                            uint32_t         aExpireType,
                            int64_t          aExpireTime)
  : mHost(aHost)
  , mType(aType)
  , mCapability(aCapability)
  , mExpireType(aExpireType)
  , mExpireTime(aExpireTime)
-{
-}
-
-nsPermission::~nsPermission()
+ , mAppId(aAppId)
+ , mIsInBrowserElement(aIsInBrowserElement)
 {
 }
 
 NS_IMETHODIMP
 nsPermission::GetHost(nsACString &aHost)
 {
   aHost = mHost;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsPermission::GetAppId(uint32_t* aAppId)
+{
+  *aAppId = mAppId;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPermission::GetIsInBrowserElement(bool* aIsInBrowserElement)
+{
+  *aIsInBrowserElement = mIsInBrowserElement;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsPermission::GetType(nsACString &aType)
 {
   aType = mType;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPermission::GetCapability(uint32_t *aCapability)
--- a/extensions/cookie/nsPermission.h
+++ b/extensions/cookie/nsPermission.h
@@ -14,24 +14,28 @@
 class nsPermission : public nsIPermission
 {
 public:
   // nsISupports
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPERMISSION
 
   nsPermission(const nsACString &aHost,
-               const nsACString &aType, 
+               uint32_t aAppId,
+               bool aIsInBrowserElement,
+               const nsACString &aType,
                uint32_t aCapability,
                uint32_t aExpireType,
                int64_t aExpireTime);
 
-  virtual ~nsPermission();
-  
+  virtual ~nsPermission() {};
+
 protected:
   nsCString mHost;
   nsCString mType;
   uint32_t  mCapability;
   uint32_t  mExpireType;
   int64_t   mExpireTime;
+  uint32_t  mAppId;
+  bool      mIsInBrowserElement;
 };
 
 #endif // nsPermission_h__
--- a/extensions/cookie/nsPermissionManager.cpp
+++ b/extensions/cookie/nsPermissionManager.cpp
@@ -18,16 +18,17 @@
 #include "nsIIDNService.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "prprf.h"
 #include "mozilla/storage.h"
 #include "mozilla/Attributes.h"
 #include "nsXULAppAPI.h"
 #include "nsIPrincipal.h"
 #include "nsContentUtils.h"
+#include "nsIScriptSecurityManager.h"
 
 static nsPermissionManager *gPermissionManager = nullptr;
 
 using mozilla::dom::ContentParent;
 using mozilla::dom::ContentChild;
 using mozilla::unused; // ha!
 
 static bool
@@ -65,52 +66,76 @@ ChildProcess()
 #define ENSURE_NOT_CHILD_PROCESS \
   ENSURE_NOT_CHILD_PROCESS_({ return NS_ERROR_NOT_AVAILABLE; })
 
 #define ENSURE_NOT_CHILD_PROCESS_NORET \
   ENSURE_NOT_CHILD_PROCESS_()
 
 ////////////////////////////////////////////////////////////////////////////////
 
-#define PL_ARENA_CONST_ALIGN_MASK 3
-#include "plarena.h"
+namespace {
 
-static PLArenaPool *gHostArena = nullptr;
-
-// making sHostArena 512b for nice allocation
-// growing is quite cheap
-#define HOST_ARENA_SIZE 512
+nsresult
+GetPrincipal(const nsACString& aHost, uint32_t aAppId, bool aIsInBrowserElement,
+             nsIPrincipal** aPrincipal)
+{
+  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
+  NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
 
-// equivalent to strdup() - does no error checking,
-// we're assuming we're only called with a valid pointer
-static char *
-ArenaStrDup(const char* str, PLArenaPool* aArena)
+  nsCOMPtr<nsIURI> uri;
+  // NOTE: we use "http://" as a protocal but we will just use the host so it
+  // doesn't really matter.
+  NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("http://") + aHost);
+
+  return secMan->GetAppCodebasePrincipal(uri, aAppId, aIsInBrowserElement, aPrincipal);
+}
+
+nsresult
+GetPrincipal(nsIURI* aURI, nsIPrincipal** aPrincipal)
 {
-  void* mem;
-  const uint32_t size = strlen(str) + 1;
-  PL_ARENA_ALLOCATE(mem, aArena, size);
-  if (mem)
-    memcpy(mem, str, size);
-  return static_cast<char*>(mem);
+  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
+  NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
+
+  return secMan->GetNoAppCodebasePrincipal(aURI, aPrincipal);
 }
 
-nsHostEntry::nsHostEntry(const char* aHost)
+nsresult
+GetPrincipal(const nsACString& aHost, nsIPrincipal** aPrincipal)
 {
-  mHost = ArenaStrDup(aHost, gHostArena);
+  return GetPrincipal(aHost, nsIScriptSecurityManager::NO_APP_ID, false, aPrincipal);
 }
 
-// XXX this can fail on OOM
-nsHostEntry::nsHostEntry(const nsHostEntry& toCopy)
- : mHost(toCopy.mHost)
- , mPermissions(toCopy.mPermissions)
+nsresult
+GetHostForPrincipal(nsIPrincipal* aPrincipal, nsACString& aHost)
 {
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  uri = NS_GetInnermostURI(uri);
+  NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
+
+  rv = uri->GetAsciiHost(aHost);
+  if (NS_FAILED(rv) || aHost.IsEmpty()) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  return NS_OK;
 }
 
+} // anonymous namespace
+
 ////////////////////////////////////////////////////////////////////////////////
 
+nsPermissionManager::PermissionKey::PermissionKey(nsIPrincipal* aPrincipal)
+{
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(GetHostForPrincipal(aPrincipal, mHost)));
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPrincipal->GetAppId(&mAppId)));
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPrincipal->GetIsInBrowserElement(&mIsInBrowserElement)));
+}
 
 /**
  * Simple callback used by |AsyncClose| to trigger a treatment once
  * the database is closed.
  *
  * Note: Beware that, if you hold onto a |CloseDatabaseListener| from a
  * |nsPermissionManager|, this will create a cycle.
  *
@@ -212,17 +237,17 @@ NS_IMETHODIMP DeleteFromMozHostListener:
 
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsPermissionManager Implementation
 
 static const char kPermissionsFileName[] = "permissions.sqlite";
-#define HOSTS_SCHEMA_VERSION 2
+#define HOSTS_SCHEMA_VERSION 3
 
 static const char kHostpermFileName[] = "hostperm.1";
 
 static const char kPermissionChangeNotification[] = PERM_CHANGE_NOTIFICATION;
 
 NS_IMPL_ISUPPORTS3(nsPermissionManager, nsIPermissionManager, nsIObserver, nsISupportsWeakReference)
 
 nsPermissionManager::nsPermissionManager()
@@ -263,32 +288,37 @@ nsPermissionManager::GetXPCOMSingleton()
   return gPermissionManager;
 }
 
 nsresult
 nsPermissionManager::Init()
 {
   nsresult rv;
 
-  mHostTable.Init();
+  mPermissionTable.Init();
 
   mObserverService = do_GetService("@mozilla.org/observer-service;1", &rv);
   if (NS_SUCCEEDED(rv)) {
     mObserverService->AddObserver(this, "profile-before-change", true);
     mObserverService->AddObserver(this, "profile-do-change", true);
   }
 
   if (IsChildProcess()) {
     // Get the permissions from the parent process
     InfallibleTArray<IPC::Permission> perms;
     ChildProcess()->SendReadPermissions(&perms);
 
     for (uint32_t i = 0; i < perms.Length(); i++) {
       const IPC::Permission &perm = perms[i];
-      AddInternal(perm.host, perm.type, perm.capability, 0, perm.expireType,
+
+      nsCOMPtr<nsIPrincipal> principal;
+      rv = GetPrincipal(perm.host, perm.appId, perm.isInBrowserElement, getter_AddRefs(principal));
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      AddInternal(principal, perm.type, perm.capability, 0, perm.expireType,
                   perm.expireTime, eNotify, eNoDBOperation);
     }
 
     // Stop here; we don't need the DB in the child process
     return NS_OK;
   }
 
   // ignore failure here, since it's non-fatal (we can run fine without
@@ -369,53 +399,55 @@ nsPermissionManager::InitDB(bool aRemove
         // expiration columns
         rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
               "ALTER TABLE moz_hosts ADD expireType INTEGER"));
         NS_ENSURE_SUCCESS(rv, rv);
 
         rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
               "ALTER TABLE moz_hosts ADD expireTime INTEGER"));
         NS_ENSURE_SUCCESS(rv, rv);
+      }
+
+      // fall through to the next upgrade
+
+    // TODO: we want to make default version as version 2 in order to fix bug 784875.
+    case 0:
+    case 2:
+      {
+        // Add appId/isInBrowserElement fields.
+        rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+              "ALTER TABLE moz_hosts ADD appId INTEGER"));
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+              "ALTER TABLE moz_hosts ADD isInBrowserElement INTEGER"));
+        NS_ENSURE_SUCCESS(rv, rv);
 
         rv = mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION);
         NS_ENSURE_SUCCESS(rv, rv);
       }
 
       // fall through to the next upgrade
-      
+
     // current version.
     case HOSTS_SCHEMA_VERSION:
       break;
 
-    case 0:
-      {
-        NS_WARNING("couldn't get schema version!");
-          
-        // the table may be usable; someone might've just clobbered the schema
-        // version. we can treat this case like a downgrade using the codepath
-        // below, by verifying the columns we care about are all there. for now,
-        // re-set the schema version in the db, in case the checks succeed (if
-        // they don't, we're dropping the table anyway).
-        rv = mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION);
-        NS_ENSURE_SUCCESS(rv, rv);
-      }
-      // fall through to downgrade check
-
     // downgrading.
     // if columns have been added to the table, we can still use the ones we
     // understand safely. if columns have been deleted or altered, just
     // blow away the table and start from scratch! if you change the way
     // a column is interpreted, make sure you also change its name so this
     // check will catch it.
     default:
       {
         // check if all the expected columns exist
         nsCOMPtr<mozIStorageStatement> stmt;
         rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
-          "SELECT host, type, permission, expireType, expireTime FROM moz_hosts"),
+          "SELECT host, type, permission, expireType, expireTime, appId, isInBrowserElement FROM moz_hosts"),
           getter_AddRefs(stmt));
         if (NS_SUCCEEDED(rv))
           break;
 
         // our columns aren't there - drop the table!
         rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DROP TABLE moz_hosts"));
         NS_ENSURE_SUCCESS(rv, rv);
 
@@ -427,18 +459,18 @@ nsPermissionManager::InitDB(bool aRemove
   }
 
   // make operations on the table asynchronous, for performance
   mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA synchronous = OFF"));
 
   // cache frequently used statements (for insertion, deletion, and updating)
   rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
     "INSERT INTO moz_hosts "
-    "(id, host, type, permission, expireType, expireTime) "
-    "VALUES (?1, ?2, ?3, ?4, ?5, ?6)"), getter_AddRefs(mStmtInsert));
+    "(id, host, type, permission, expireType, expireTime, appId, isInBrowserElement) "
+    "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)"), getter_AddRefs(mStmtInsert));
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
     "DELETE FROM moz_hosts "
     "WHERE id = ?1"), getter_AddRefs(mStmtDelete));
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
@@ -468,124 +500,126 @@ nsPermissionManager::CreateTable()
   return mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "CREATE TABLE moz_hosts ("
       " id INTEGER PRIMARY KEY"
       ",host TEXT"
       ",type TEXT"
       ",permission INTEGER"
       ",expireType INTEGER"
       ",expireTime INTEGER"
+      ",appId INTEGER"
+      ",isInBrowserElement INTEGER"
     ")"));
 }
 
 NS_IMETHODIMP
 nsPermissionManager::Add(nsIURI     *aURI,
                          const char *aType,
                          uint32_t    aPermission,
                          uint32_t    aExpireType,
                          int64_t     aExpireTime)
 {
-  ENSURE_NOT_CHILD_PROCESS;
-
   NS_ENSURE_ARG_POINTER(aURI);
-  NS_ENSURE_ARG_POINTER(aType);
-  NS_ENSURE_TRUE(aExpireType == nsIPermissionManager::EXPIRE_NEVER ||
-                 aExpireType == nsIPermissionManager::EXPIRE_TIME ||
-                 aExpireType == nsIPermissionManager::EXPIRE_SESSION,
-                 NS_ERROR_INVALID_ARG);
 
-  nsresult rv;
-
-  // Skip addition if the permission is already expired.
-  if (aExpireType == nsIPermissionManager::EXPIRE_TIME &&
-      aExpireTime <= PR_Now() / 1000)
-    return NS_OK;
-
-  nsCAutoString host;
-  rv = GetHost(aURI, host);
+  nsCOMPtr<nsIPrincipal> principal;
+  nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  return AddInternal(host, nsDependentCString(aType), aPermission, 0, 
-                     aExpireType, aExpireTime, eNotify, eWriteToDB);
+  return AddFromPrincipal(principal, aType, aPermission, aExpireType, aExpireTime);
 }
 
 NS_IMETHODIMP
 nsPermissionManager::AddFromPrincipal(nsIPrincipal* aPrincipal,
                                       const char* aType, uint32_t aPermission,
                                       uint32_t aExpireType, int64_t aExpireTime)
 {
+  ENSURE_NOT_CHILD_PROCESS;
   NS_ENSURE_ARG_POINTER(aPrincipal);
+  NS_ENSURE_ARG_POINTER(aType);
+  NS_ENSURE_TRUE(aExpireType == nsIPermissionManager::EXPIRE_NEVER ||
+                 aExpireType == nsIPermissionManager::EXPIRE_TIME ||
+                 aExpireType == nsIPermissionManager::EXPIRE_SESSION,
+                 NS_ERROR_INVALID_ARG);
+
+  // Skip addition if the permission is already expired.
+  if (aExpireType == nsIPermissionManager::EXPIRE_TIME &&
+      aExpireTime <= (PR_Now() / 1000)) {
+    return NS_OK;
+  }
 
   // We don't add the system principal because it actually has no URI and we
   // always allow action for them.
   if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
     return NS_OK;
   }
 
-  nsCOMPtr<nsIURI> uri;
-  aPrincipal->GetURI(getter_AddRefs(uri));
-
-  return Add(uri, aType, aPermission, aExpireType, aExpireTime);
+  return AddInternal(aPrincipal, nsDependentCString(aType), aPermission, 0,
+                     aExpireType, aExpireTime, eNotify, eWriteToDB);
 }
 
 nsresult
-nsPermissionManager::AddInternal(const nsAFlatCString &aHost,
+nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
                                  const nsAFlatCString &aType,
                                  uint32_t              aPermission,
                                  int64_t               aID,
                                  uint32_t              aExpireType,
                                  int64_t               aExpireTime,
                                  NotifyOperationType   aNotifyOperation,
                                  DBOperationType       aDBOperation)
 {
+  nsCAutoString host;
+  nsresult rv = GetHostForPrincipal(aPrincipal, host);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   if (!IsChildProcess()) {
-    IPC::Permission permission((aHost),
-                               (aType),
+    uint32_t appId;
+    rv = aPrincipal->GetAppId(&appId);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    bool isInBrowserElement;
+    rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    IPC::Permission permission(host, appId, isInBrowserElement, aType,
                                aPermission, aExpireType, aExpireTime);
 
     nsTArray<ContentParent*> cplist;
     ContentParent::GetAll(cplist);
     for (uint32_t i = 0; i < cplist.Length(); ++i) {
       ContentParent* cp = cplist[i];
       if (cp->NeedsPermissionsUpdate())
         unused << cp->SendAddPermission(permission);
     }
   }
 
-  if (!gHostArena) {
-    gHostArena = new PLArenaPool;
-    if (!gHostArena)
-      return NS_ERROR_OUT_OF_MEMORY;    
-    PL_INIT_ARENA_POOL(gHostArena, "PermissionHostArena", HOST_ARENA_SIZE);
-  }
-
   // look up the type index
   int32_t typeIndex = GetTypeIndex(aType.get(), true);
   NS_ENSURE_TRUE(typeIndex != -1, NS_ERROR_OUT_OF_MEMORY);
 
   // When an entry already exists, PutEntry will return that, instead
   // of adding a new one
-  nsHostEntry *entry = mHostTable.PutEntry(aHost.get());
+  nsRefPtr<PermissionKey> key = new PermissionKey(aPrincipal);
+  PermissionHashKey* entry = mPermissionTable.PutEntry(key);
   if (!entry) return NS_ERROR_FAILURE;
   if (!entry->GetKey()) {
-    mHostTable.RawRemoveEntry(entry);
+    mPermissionTable.RawRemoveEntry(entry);
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   // figure out the transaction type, and get any existing permission value
   OperationType op;
   int32_t index = entry->GetPermissionIndex(typeIndex);
   if (index == -1) {
     if (aPermission == nsIPermissionManager::UNKNOWN_ACTION)
       op = eOperationNone;
     else
       op = eOperationAdding;
 
   } else {
-    nsPermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
+    PermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
 
     // remove the permission if the permission is UNKNOWN, update the
     // permission if its value or expire type have changed OR if the time has
     // changed and the expire type is time, otherwise, don't modify.  There's
     // no need to modify a permission that doesn't expire with time when the
     // only thing changed is the expire time.
     if (aPermission == oldPermissionEntry.mPermission && 
         aExpireType == oldPermissionEntry.mExpireType &&
@@ -613,69 +647,89 @@ nsPermissionManager::AddInternal(const n
       if (aDBOperation == eWriteToDB) {
         // we'll be writing to the database - generate a known unique id
         id = ++mLargestID;
       } else {
         // we're reading from the database - use the id already assigned
         id = aID;
       }
 
-      entry->GetPermissions().AppendElement(nsPermissionEntry(typeIndex, aPermission, id, aExpireType, aExpireTime));
+      entry->GetPermissions().AppendElement(PermissionEntry(id, typeIndex, aPermission, aExpireType, aExpireTime));
+
+      if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION) {
+        uint32_t appId;
+        rv = aPrincipal->GetAppId(&appId);
+        NS_ENSURE_SUCCESS(rv, rv);
 
-      if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION)
-        UpdateDB(op, mStmtInsert, id, aHost, aType, aPermission, aExpireType, aExpireTime);
+        bool isInBrowserElement;
+        rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        UpdateDB(op, mStmtInsert, id, host, aType, aPermission, aExpireType, aExpireTime, appId, isInBrowserElement);
+      }
 
       if (aNotifyOperation == eNotify) {
-        NotifyObserversWithPermission(aHost,
+        NotifyObserversWithPermission(host,
+                                      entry->GetKey()->mAppId,
+                                      entry->GetKey()->mIsInBrowserElement,
                                       mTypeArray[typeIndex],
                                       aPermission,
                                       aExpireType,
                                       aExpireTime,
                                       NS_LITERAL_STRING("added").get());
       }
 
       break;
     }
 
   case eOperationRemoving:
     {
-      nsPermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
+      PermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
       id = oldPermissionEntry.mID;
       entry->GetPermissions().RemoveElementAt(index);
 
       // If no more types are present, remove the entry
       if (entry->GetPermissions().IsEmpty())
-        mHostTable.RawRemoveEntry(entry);
+        mPermissionTable.RawRemoveEntry(entry);
 
       if (aDBOperation == eWriteToDB)
-        UpdateDB(op, mStmtDelete, id, EmptyCString(), EmptyCString(), 0, 
-                 nsIPermissionManager::EXPIRE_NEVER, 0);
+        // We care only about the id here so we pass dummy values for all other
+        // parameters.
+        UpdateDB(op, mStmtDelete, id, EmptyCString(), EmptyCString(), 0,
+                 nsIPermissionManager::EXPIRE_NEVER, 0, 0, false);
 
       if (aNotifyOperation == eNotify) {
-        NotifyObserversWithPermission(aHost,
+        NotifyObserversWithPermission(host,
+                                      entry->GetKey()->mAppId,
+                                      entry->GetKey()->mIsInBrowserElement,
                                       mTypeArray[typeIndex],
                                       oldPermissionEntry.mPermission,
                                       oldPermissionEntry.mExpireType,
                                       oldPermissionEntry.mExpireTime,
                                       NS_LITERAL_STRING("deleted").get());
       }
 
       break;
     }
 
   case eOperationChanging:
     {
       id = entry->GetPermissions()[index].mID;
       entry->GetPermissions()[index].mPermission = aPermission;
 
       if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION)
-        UpdateDB(op, mStmtUpdate, id, EmptyCString(), EmptyCString(), aPermission, aExpireType, aExpireTime);
+        // We care only about the id, the permission and expireType/expireTime here.
+        // We pass dummy values for all other parameters.
+        UpdateDB(op, mStmtUpdate, id, EmptyCString(), EmptyCString(),
+                 aPermission, aExpireType, aExpireTime, 0, false);
 
       if (aNotifyOperation == eNotify) {
-        NotifyObserversWithPermission(aHost,
+        NotifyObserversWithPermission(host,
+                                      entry->GetKey()->mAppId,
+                                      entry->GetKey()->mIsInBrowserElement,
                                       mTypeArray[typeIndex],
                                       aPermission,
                                       aExpireType,
                                       aExpireTime,
                                       NS_LITERAL_STRING("changed").get());
       }
 
       break;
@@ -684,53 +738,48 @@ nsPermissionManager::AddInternal(const n
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPermissionManager::Remove(const nsACString &aHost,
                             const char       *aType)
 {
+  nsCOMPtr<nsIPrincipal> principal;
+  nsresult rv = GetPrincipal(aHost, getter_AddRefs(principal));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return RemoveFromPrincipal(principal, aType);
+}
+
+NS_IMETHODIMP
+nsPermissionManager::RemoveFromPrincipal(nsIPrincipal* aPrincipal,
+                                         const char* aType)
+{
   ENSURE_NOT_CHILD_PROCESS;
-
+  NS_ENSURE_ARG_POINTER(aPrincipal);
   NS_ENSURE_ARG_POINTER(aType);
 
+  // System principals are never added to the database, no need to remove them.
+  if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
+    return NS_OK;
+  }
+
   // AddInternal() handles removal, just let it do the work
-  return AddInternal(PromiseFlatCString(aHost),
+  return AddInternal(aPrincipal,
                      nsDependentCString(aType),
                      nsIPermissionManager::UNKNOWN_ACTION,
                      0,
                      nsIPermissionManager::EXPIRE_NEVER,
                      0,
                      eNotify,
                      eWriteToDB);
 }
 
 NS_IMETHODIMP
-nsPermissionManager::RemoveFromPrincipal(nsIPrincipal* aPrincipal,
-                                         const char* aType)
-{
-  NS_ENSURE_ARG_POINTER(aPrincipal);
-
-  // System principals are never added to the database, no need to remove them.
-  if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsIURI> uri;
-  aPrincipal->GetURI(getter_AddRefs(uri));
-  NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
-
-  nsCAutoString host;
-  uri->GetHost(host);
-
-  return Remove(host, aType);
-}
-
-NS_IMETHODIMP
 nsPermissionManager::RemoveAll()
 {
   ENSURE_NOT_CHILD_PROCESS;
   return RemoveAllInternal(true);
 }
 
 void
 nsPermissionManager::CloseDB(bool aRebuildOnSuccess)
@@ -781,132 +830,169 @@ nsPermissionManager::RemoveAllInternal(b
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPermissionManager::TestExactPermission(nsIURI     *aURI,
                                          const char *aType,
                                          uint32_t   *aPermission)
 {
-  return CommonTestPermission(aURI, aType, aPermission, true);
-}
-
-NS_IMETHODIMP
-nsPermissionManager::TestPermission(nsIURI     *aURI,
-                                    const char *aType,
-                                    uint32_t   *aPermission)
-{
-  return CommonTestPermission(aURI, aType, aPermission, false);
-}
+  nsCOMPtr<nsIPrincipal> principal;
+  nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
+  NS_ENSURE_SUCCESS(rv, rv);
 
-NS_IMETHODIMP
-nsPermissionManager::TestPermissionFromPrincipal(nsIPrincipal* aPrincipal,
-                                                 const char* aType,
-                                                 uint32_t* aPermission)
-{
-  NS_ENSURE_ARG_POINTER(aPrincipal);
-
-  // System principals do not have URI so we can't try to get
-  // retro-compatibility here.
-  if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
-    *aPermission = nsIPermissionManager::ALLOW_ACTION;
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsIURI> uri;
-  aPrincipal->GetURI(getter_AddRefs(uri));
-
-  return TestPermission(uri, aType, aPermission);
+  return TestExactPermissionFromPrincipal(principal, aType, aPermission);
 }
 
 NS_IMETHODIMP
 nsPermissionManager::TestExactPermissionFromPrincipal(nsIPrincipal* aPrincipal,
                                                       const char* aType,
                                                       uint32_t* aPermission)
 {
   NS_ENSURE_ARG_POINTER(aPrincipal);
 
   // System principals do not have URI so we can't try to get
   // retro-compatibility here.
   if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
     *aPermission = nsIPermissionManager::ALLOW_ACTION;
     return NS_OK;
   }
 
-  nsCOMPtr<nsIURI> uri;
-  aPrincipal->GetURI(getter_AddRefs(uri));
+  return CommonTestPermission(aPrincipal, aType, aPermission, true);
+}
+
+NS_IMETHODIMP
+nsPermissionManager::TestPermission(nsIURI     *aURI,
+                                    const char *aType,
+                                    uint32_t   *aPermission)
+{
+  nsCOMPtr<nsIPrincipal> principal;
+  nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return TestPermissionFromPrincipal(principal, aType, aPermission);
+}
 
-  return TestExactPermission(uri, aType, aPermission);
+NS_IMETHODIMP
+nsPermissionManager::TestPermissionFromPrincipal(nsIPrincipal* aPrincipal,
+                                                 const char* aType,
+                                                 uint32_t* aPermission)
+{
+  NS_ENSURE_ARG_POINTER(aPrincipal);
+
+  // System principals do not have URI so we can't try to get
+  // retro-compatibility here.
+  if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
+    *aPermission = nsIPermissionManager::ALLOW_ACTION;
+    return NS_OK;
+  }
+
+  return CommonTestPermission(aPrincipal, aType, aPermission, false);
 }
 
 nsresult
-nsPermissionManager::CommonTestPermission(nsIURI     *aURI,
+nsPermissionManager::CommonTestPermission(nsIPrincipal* aPrincipal,
                                           const char *aType,
                                           uint32_t   *aPermission,
                                           bool        aExactHostMatch)
 {
-  NS_ENSURE_ARG_POINTER(aURI);
+  NS_ENSURE_ARG_POINTER(aPrincipal);
   NS_ENSURE_ARG_POINTER(aType);
 
   // set the default
   *aPermission = nsIPermissionManager::UNKNOWN_ACTION;
 
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
+  NS_ENSURE_SUCCESS(rv, rv);
+
   nsCAutoString host;
-  nsresult rv = GetHost(aURI, host);
+  rv = GetHostForPrincipal(aPrincipal, host);
+
   // No host doesn't mean an error. Just return the default. Unless this is
   // a file uri. In that case use a magic host.
   if (NS_FAILED(rv)) {
     bool isFile;
-    rv = aURI->SchemeIs("file", &isFile);
+    rv = uri->SchemeIs("file", &isFile);
     NS_ENSURE_SUCCESS(rv, rv);
     if (isFile) {
       host.AssignLiteral("<file>");
     }
     else {
       return NS_OK;
     }
   }
-  
+
   int32_t typeIndex = GetTypeIndex(aType, false);
   // If type == -1, the type isn't known,
   // so just return NS_OK
   if (typeIndex == -1) return NS_OK;
 
-  nsHostEntry *entry = GetHostEntry(host, typeIndex, aExactHostMatch);
-  if (entry)
+  uint32_t appId;
+  rv = aPrincipal->GetAppId(&appId);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool isInBrowserElement;
+  rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PermissionHashKey* entry = GetPermissionHashKey(host, appId, isInBrowserElement,
+                                                  typeIndex, aExactHostMatch);
+  if (entry) {
     *aPermission = entry->GetPermission(typeIndex).mPermission;
+  }
 
   return NS_OK;
 }
 
-// Get hostentry for given host string and permission type.
-// walk up the domain if needed.
-// return null if nothing found.
+// Returns PermissionHashKey for a given { host, appId, isInBrowserElement } tuple.
+// This is not simply using PermissionKey because we will walk-up domains in
+// case of |host| contains sub-domains.
+// Returns null if nothing found.
 // Also accepts host on the format "<foo>". This will perform an exact match
 // lookup as the string doesn't contain any dots.
-nsHostEntry *
-nsPermissionManager::GetHostEntry(const nsAFlatCString &aHost,
-                                  uint32_t              aType,
-                                  bool                  aExactHostMatch)
+nsPermissionManager::PermissionHashKey*
+nsPermissionManager::GetPermissionHashKey(const nsACString& aHost,
+                                          uint32_t aAppId,
+                                          bool aIsInBrowserElement,
+                                          uint32_t aType,
+                                          bool aExactHostMatch)
 {
   uint32_t offset = 0;
-  nsHostEntry *entry;
+  PermissionHashKey* entry;
   int64_t now = PR_Now() / 1000;
 
   do {
-    entry = mHostTable.GetEntry(aHost.get() + offset);
+    nsRefPtr<PermissionKey> key = new PermissionKey(Substring(aHost, offset), aAppId, aIsInBrowserElement);
+    entry = mPermissionTable.GetEntry(key);
+
+    if (!entry) {
+      // This is a temporary fix to have Gaia working and allow a time frame to
+      // update profiles. With this hack, if a permission isn't found for an app
+      // the check will be done for the same host outside of any app.
+      // TODO: remove this with bug 785632.
+      key = new PermissionKey(Substring(aHost, offset), nsIScriptSecurityManager::NO_APP_ID, false);
+      entry = mPermissionTable.GetEntry(key);
+    }
+
     if (entry) {
-      nsPermissionEntry permEntry = entry->GetPermission(aType);
+      PermissionEntry permEntry = entry->GetPermission(aType);
 
       // if the entry is expired, remove and keep looking for others.
       if (permEntry.mExpireType == nsIPermissionManager::EXPIRE_TIME &&
-          permEntry.mExpireTime <= now)
-        Remove(aHost, mTypeArray[aType].get());
-      else if (permEntry.mPermission != nsIPermissionManager::UNKNOWN_ACTION)
+          permEntry.mExpireTime <= now) {
+        nsCOMPtr<nsIPrincipal> principal;
+        if (NS_FAILED(GetPrincipal(aHost, aAppId, aIsInBrowserElement, getter_AddRefs(principal)))) {
+          return nullptr;
+        }
+
+        RemoveFromPrincipal(principal, mTypeArray[aType].get());
+      } else if (permEntry.mPermission != nsIPermissionManager::UNKNOWN_ACTION) {
         break;
+      }
 
       // reset entry, to be able to return null on failure
       entry = nullptr;
     }
     if (aExactHostMatch)
       break; // do not try super domains
 
     offset = aHost.FindChar('.', offset) + 1;
@@ -924,24 +1010,26 @@ struct nsGetEnumeratorData
    : array(aArray)
    , types(aTypes) {}
 
   nsCOMArray<nsIPermission> *array;
   const nsTArray<nsCString> *types;
 };
 
 static PLDHashOperator
-AddPermissionsToList(nsHostEntry *entry, void *arg)
+AddPermissionsToList(nsPermissionManager::PermissionHashKey* entry, void *arg)
 {
   nsGetEnumeratorData *data = static_cast<nsGetEnumeratorData *>(arg);
 
   for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) {
-    nsPermissionEntry &permEntry = entry->GetPermissions()[i];
+    nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i];
 
-    nsPermission *perm = new nsPermission(entry->GetHost(), 
+    nsPermission *perm = new nsPermission(entry->GetKey()->mHost,
+                                          entry->GetKey()->mAppId,
+                                          entry->GetKey()->mIsInBrowserElement,
                                           data->types->ElementAt(permEntry.mType),
                                           permEntry.mPermission,
                                           permEntry.mExpireType,
                                           permEntry.mExpireTime);
 
     data->array->AppendObject(perm);
   }
 
@@ -949,17 +1037,17 @@ AddPermissionsToList(nsHostEntry *entry,
 }
 
 NS_IMETHODIMP nsPermissionManager::GetEnumerator(nsISimpleEnumerator **aEnum)
 {
   // roll an nsCOMArray of all our permissions, then hand out an enumerator
   nsCOMArray<nsIPermission> array;
   nsGetEnumeratorData data(&array, &mTypeArray);
 
-  mHostTable.EnumerateEntries(AddPermissionsToList, &data);
+  mPermissionTable.EnumerateEntries(AddPermissionsToList, &data);
 
   return NS_NewArrayEnumerator(aEnum, array);
 }
 
 NS_IMETHODIMP nsPermissionManager::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData)
 {
   ENSURE_NOT_CHILD_PROCESS;
 
@@ -987,22 +1075,18 @@ NS_IMETHODIMP nsPermissionManager::Obser
 //*** nsPermissionManager private methods
 //*****************************************************************************
 
 nsresult
 nsPermissionManager::RemoveAllFromMemory()
 {
   mLargestID = 0;
   mTypeArray.Clear();
-  mHostTable.Clear();
-  if (gHostArena) {
-    PL_FinishArenaPool(gHostArena);
-    delete gHostArena;
-  }
-  gHostArena = nullptr;
+  mPermissionTable.Clear();
+
   return NS_OK;
 }
 
 // Returns -1 on failure
 int32_t
 nsPermissionManager::GetTypeIndex(const char *aType,
                                   bool        aAdd)
 {
@@ -1024,24 +1108,27 @@ nsPermissionManager::GetTypeIndex(const 
   elem->Assign(aType);
   return mTypeArray.Length() - 1;
 }
 
 // wrapper function for mangling (host,type,perm,expireType,expireTime)
 // set into an nsIPermission.
 void
 nsPermissionManager::NotifyObserversWithPermission(const nsACString &aHost,
+                                                   uint32_t          aAppId,
+                                                   bool              aIsInBrowserElement,
                                                    const nsCString  &aType,
                                                    uint32_t          aPermission,
                                                    uint32_t          aExpireType,
                                                    int64_t           aExpireTime,
                                                    const PRUnichar  *aData)
 {
   nsCOMPtr<nsIPermission> permission =
-    new nsPermission(aHost, aType, aPermission, aExpireType, aExpireTime);
+    new nsPermission(aHost, aAppId, aIsInBrowserElement, aType, aPermission,
+                     aExpireType, aExpireTime);
   if (permission)
     NotifyObservers(permission, aData);
 }
 
 // notify observers that the permission list changed. there are four possible
 // values for aData:
 // "deleted" means a permission was deleted. aPermission is the deleted permission.
 // "added"   means a permission was added. aPermission is the added permission.
@@ -1081,25 +1168,27 @@ nsPermissionManager::Read()
 
     bool hasResult;
     rv = stmtDeleteExpired->ExecuteStep(&hasResult);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   nsCOMPtr<mozIStorageStatement> stmt;
   rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
-    "SELECT id, host, type, permission, expireType, expireTime "
+    "SELECT id, host, type, permission, expireType, expireTime, appId, isInBrowserElement "
     "FROM moz_hosts"), getter_AddRefs(stmt));
   NS_ENSURE_SUCCESS(rv, rv);
 
   int64_t id;
   nsCAutoString host, type;
   uint32_t permission;
   uint32_t expireType;
   int64_t expireTime;
+  uint32_t appId;
+  bool isInBrowserElement;
   bool hasResult;
   while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
     // explicitly set our entry id counter for use in AddInternal(),
     // and keep track of the largest id so we know where to pick up.
     id = stmt->AsInt64(0);
     if (id > mLargestID)
       mLargestID = id;
 
@@ -1110,17 +1199,25 @@ nsPermissionManager::Read()
     NS_ENSURE_SUCCESS(rv, rv);
 
     permission = stmt->AsInt32(3);
     expireType = stmt->AsInt32(4);
 
     // convert into int64_t value (milliseconds)
     expireTime = stmt->AsInt64(5);
 
-    rv = AddInternal(host, type, permission, id, expireType, expireTime,
+    MOZ_ASSERT(stmt->AsInt64(6) >= 0);
+    appId = static_cast<uint32_t>(stmt->AsInt64(6));
+    isInBrowserElement = static_cast<bool>(stmt->AsInt32(7));
+
+    nsCOMPtr<nsIPrincipal> principal;
+    nsresult rv = GetPrincipal(host, appId, isInBrowserElement, getter_AddRefs(principal));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = AddInternal(principal, type, permission, id, expireType, expireTime,
                      eDontNotify, eNoDBOperation);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 static const char kMatchTypeHost[] = "host";
@@ -1180,17 +1277,21 @@ nsPermissionManager::Import()
 
       // hosts might be encoded in UTF8; switch them to ACE to be consistent
       if (!IsASCII(lineArray[3])) {
         rv = NormalizeToACE(lineArray[3]);
         if (NS_FAILED(rv))
           continue;
       }
 
-      rv = AddInternal(lineArray[3], lineArray[1], permission, 0, 
+      nsCOMPtr<nsIPrincipal> principal;
+      nsresult rv = GetPrincipal(lineArray[3], getter_AddRefs(principal));
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      rv = AddInternal(principal, lineArray[1], permission, 0,
                        nsIPermissionManager::EXPIRE_NEVER, 0, eDontNotify, eWriteToDB);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
   // we're done importing - delete the old file
   permissionsFile->Remove(false);
 
@@ -1205,39 +1306,27 @@ nsPermissionManager::NormalizeToACE(nsCS
     nsresult rv;
     mIDNService = do_GetService(NS_IDNSERVICE_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return mIDNService->ConvertUTF8toACE(aHost, aHost);
 }
 
-nsresult
-nsPermissionManager::GetHost(nsIURI *aURI, nsACString &aResult)
-{
-  nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
-  if (!innerURI) return NS_ERROR_FAILURE;
-
-  nsresult rv = innerURI->GetAsciiHost(aResult);
-
-  if (NS_FAILED(rv) || aResult.IsEmpty())
-    return NS_ERROR_UNEXPECTED;
-
-  return NS_OK;
-}
-
 void
 nsPermissionManager::UpdateDB(OperationType         aOp,
                               mozIStorageStatement* aStmt,
                               int64_t               aID,
                               const nsACString     &aHost,
                               const nsACString     &aType,
                               uint32_t              aPermission,
                               uint32_t              aExpireType,
-                              int64_t               aExpireTime)
+                              int64_t               aExpireTime,
+                              uint32_t              aAppId,
+                              bool                  aIsInBrowserElement)
 {
   ENSURE_NOT_CHILD_PROCESS_NORET;
 
   nsresult rv;
 
   // no statement is ok - just means we don't have a profile
   if (!aStmt)
     return;
@@ -1256,16 +1345,22 @@ nsPermissionManager::UpdateDB(OperationT
 
       rv = aStmt->BindInt32ByIndex(3, aPermission);
       if (NS_FAILED(rv)) break;
 
       rv = aStmt->BindInt32ByIndex(4, aExpireType);
       if (NS_FAILED(rv)) break;
 
       rv = aStmt->BindInt64ByIndex(5, aExpireTime);
+      if (NS_FAILED(rv)) break;
+
+      rv = aStmt->BindInt64ByIndex(6, aAppId);
+      if (NS_FAILED(rv)) break;
+
+      rv = aStmt->BindInt64ByIndex(7, aIsInBrowserElement);
       break;
     }
 
   case eOperationRemoving:
     {
       rv = aStmt->BindInt64ByIndex(0, aID);
       break;
     }
--- a/extensions/cookie/nsPermissionManager.h
+++ b/extensions/cookie/nsPermissionManager.h
@@ -11,125 +11,152 @@
 #include "nsIObserverService.h"
 #include "nsWeakReference.h"
 #include "nsCOMPtr.h"
 #include "nsIFile.h"
 #include "nsTHashtable.h"
 #include "nsTArray.h"
 #include "nsString.h"
 #include "nsPermission.h"
+#include "nsHashKeys.h"
+#include "nsAutoPtr.h"
 
 class nsIPermission;
 class nsIIDNService;
 class mozIStorageConnection;
 class mozIStorageStatement;
 
 ////////////////////////////////////////////////////////////////////////////////
 
-class nsPermissionEntry
-{
-public:
-  nsPermissionEntry(uint32_t aType, uint32_t aPermission, int64_t aID, 
-                    uint32_t aExpireType, int64_t aExpireTime)
-   : mType(aType)
-   , mPermission(aPermission)
-   , mID(aID)
-   , mExpireType(aExpireType)
-   , mExpireTime(aExpireTime) {}
-
-  uint32_t mType;
-  uint32_t mPermission;
-  int64_t  mID;
-  uint32_t mExpireType;
-  int64_t  mExpireTime;
-};
-
-class nsHostEntry : public PLDHashEntryHdr
-{
-public:
-  // Hash methods
-  typedef const char* KeyType;
-  typedef const char* KeyTypePointer;
-
-  nsHostEntry(const char* aHost);
-  nsHostEntry(const nsHostEntry& toCopy);
-
-  ~nsHostEntry()
-  {
-  }
-
-  KeyType GetKey() const
-  {
-    return mHost;
-  }
-
-  bool KeyEquals(KeyTypePointer aKey) const
-  {
-    return !strcmp(mHost, aKey);
-  }
-
-  static KeyTypePointer KeyToPointer(KeyType aKey)
-  {
-    return aKey;
-  }
-
-  static PLDHashNumber HashKey(KeyTypePointer aKey)
-  {
-    // PL_DHashStringKey doesn't use the table parameter, so we can safely
-    // pass nullptr
-    return PL_DHashStringKey(nullptr, aKey);
-  }
-
-  // force the hashtable to use the copy constructor when shuffling entries
-  // around, otherwise the Auto part of our nsAutoTArray won't be happy!
-  enum { ALLOW_MEMMOVE = false };
-
-  // Permissions methods
-  inline const nsDependentCString GetHost() const
-  {
-    return nsDependentCString(mHost);
-  }
-
-  inline nsTArray<nsPermissionEntry> & GetPermissions()
-  {
-    return mPermissions;
-  }
-
-  inline int32_t GetPermissionIndex(uint32_t aType) const
-  {
-    for (uint32_t i = 0; i < mPermissions.Length(); ++i)
-      if (mPermissions[i].mType == aType)
-        return i;
-
-    return -1;
-  }
-
-  inline nsPermissionEntry GetPermission(uint32_t aType) const
-  {
-    for (uint32_t i = 0; i < mPermissions.Length(); ++i)
-      if (mPermissions[i].mType == aType)
-        return mPermissions[i];
-
-    // unknown permission... return relevant data 
-    nsPermissionEntry unk = nsPermissionEntry(aType, nsIPermissionManager::UNKNOWN_ACTION,
-                                              -1, nsIPermissionManager::EXPIRE_NEVER, 0);
-    return unk;
-  }
-
-private:
-  const char *mHost;
-  nsAutoTArray<nsPermissionEntry, 1> mPermissions;
-};
-
-
 class nsPermissionManager : public nsIPermissionManager,
                             public nsIObserver,
                             public nsSupportsWeakReference
 {
 public:
+  class PermissionEntry
+  {
+  public:
+    PermissionEntry(int64_t aID, uint32_t aType, uint32_t aPermission,
+                    uint32_t aExpireType, int64_t aExpireTime)
+     : mID(aID)
+     , mType(aType)
+     , mPermission(aPermission)
+     , mExpireType(aExpireType)
+     , mExpireTime(aExpireTime)
+    {}
+
+    int64_t  mID;
+    uint32_t mType;
+    uint32_t mPermission;
+    uint32_t mExpireType;
+    int64_t  mExpireTime;
+  };
+
+  /**
+   * PermissionKey is the key used by PermissionHashKey hash table.
+   *
+   * NOTE: It could be implementing nsIHashable but there is no reason to worry
+   * with XPCOM interfaces while we don't need to.
+   */
+  class PermissionKey
+  {
+  public:
+    PermissionKey(nsIPrincipal* aPrincipal);
+    PermissionKey(const nsACString& aHost,
+                  uint32_t aAppId,
+                  bool aIsInBrowserElement)
+      : mHost(aHost)
+      , mAppId(aAppId)
+      , mIsInBrowserElement(aIsInBrowserElement)
+    {
+    }
+
+    bool operator==(const PermissionKey& aKey) const {
+      return mHost.Equals(aKey.mHost) &&
+             mAppId == aKey.mAppId &&
+             mIsInBrowserElement == aKey.mIsInBrowserElement;
+    }
+
+    PLDHashNumber GetHashCode() const {
+      nsCAutoString str;
+      str.Assign(mHost);
+      str.AppendInt(mAppId);
+      str.AppendInt(static_cast<int32_t>(mIsInBrowserElement));
+
+      return mozilla::HashString(str);
+    }
+
+    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PermissionKey);
+
+    nsCString mHost;
+    uint32_t  mAppId;
+    bool      mIsInBrowserElement;
+
+  private:
+    // Default ctor shouldn't be used.
+    PermissionKey() MOZ_DELETE;
+
+    // Dtor shouldn't be used outside of the class.
+    ~PermissionKey() {};
+  };
+
+  class PermissionHashKey : public nsRefPtrHashKey<PermissionKey>
+  {
+  public:
+    PermissionHashKey(const PermissionKey* aPermissionKey)
+      : nsRefPtrHashKey<PermissionKey>(aPermissionKey)
+    {}
+
+    PermissionHashKey(const PermissionHashKey& toCopy)
+      : nsRefPtrHashKey<PermissionKey>(toCopy)
+      , mPermissions(toCopy.mPermissions)
+    {}
+
+    bool KeyEquals(const PermissionKey* aKey) const
+    {
+      return *aKey == *GetKey();
+    }
+
+    static PLDHashNumber HashKey(const PermissionKey* aKey)
+    {
+      return aKey->GetHashCode();
+    }
+
+    // Force the hashtable to use the copy constructor when shuffling entries
+    // around, otherwise the Auto part of our nsAutoTArray won't be happy!
+    enum { ALLOW_MEMMOVE = false };
+
+    inline nsTArray<PermissionEntry> & GetPermissions()
+    {
+      return mPermissions;
+    }
+
+    inline int32_t GetPermissionIndex(uint32_t aType) const
+    {
+      for (uint32_t i = 0; i < mPermissions.Length(); ++i)
+        if (mPermissions[i].mType == aType)
+          return i;
+
+      return -1;
+    }
+
+    inline PermissionEntry GetPermission(uint32_t aType) const
+    {
+      for (uint32_t i = 0; i < mPermissions.Length(); ++i)
+        if (mPermissions[i].mType == aType)
+          return mPermissions[i];
+
+      // unknown permission... return relevant data 
+      return PermissionEntry(-1, aType, nsIPermissionManager::UNKNOWN_ACTION,
+                             nsIPermissionManager::EXPIRE_NEVER, 0);
+    }
+
+  private:
+    nsAutoTArray<PermissionEntry, 1> mPermissions;
+  };
 
   // nsISupports
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPERMISSIONMANAGER
   NS_DECL_NSIOBSERVER
 
   nsPermissionManager();
   virtual ~nsPermissionManager();
@@ -149,77 +176,81 @@ public:
     eWriteToDB
   };
 
   enum NotifyOperationType {
     eDontNotify,
     eNotify
   };
 
-  nsresult AddInternal(const nsAFlatCString &aHost,
+  nsresult AddInternal(nsIPrincipal* aPrincipal,
                        const nsAFlatCString &aType,
                        uint32_t aPermission,
                        int64_t aID,
                        uint32_t aExpireType,
                        int64_t  aExpireTime,
                        NotifyOperationType aNotifyOperation,
                        DBOperationType aDBOperation);
 
 private:
-
   int32_t GetTypeIndex(const char *aTypeString,
                        bool        aAdd);
 
-  nsHostEntry *GetHostEntry(const nsAFlatCString &aHost,
-                            uint32_t              aType,
-                            bool                  aExactHostMatch);
+  PermissionHashKey* GetPermissionHashKey(const nsACString& aHost,
+                                          uint32_t aAppId,
+                                          bool aIsInBrowserElement,
+                                          uint32_t          aType,
+                                          bool              aExactHostMatch);
 
-  nsresult CommonTestPermission(nsIURI     *aURI,
+  nsresult CommonTestPermission(nsIPrincipal* aPrincipal,
                                 const char *aType,
                                 uint32_t   *aPermission,
                                 bool        aExactHostMatch);
 
   nsresult InitDB(bool aRemoveFile);
   nsresult CreateTable();
   nsresult Import();
   nsresult Read();
   void     NotifyObserversWithPermission(const nsACString &aHost,
+                                         uint32_t          aAppId,
+                                         bool              aIsInBrowserElement,
                                          const nsCString  &aType,
                                          uint32_t          aPermission,
                                          uint32_t          aExpireType,
                                          int64_t           aExpireTime,
                                          const PRUnichar  *aData);
   void     NotifyObservers(nsIPermission *aPermission, const PRUnichar *aData);
 
   // Finalize all statements, close the DB and null it.
   // if aRebuildOnSuccess, reinitialize database
   void     CloseDB(bool aRebuildOnSuccess = false);
 
   nsresult RemoveAllInternal(bool aNotifyObservers);
   nsresult RemoveAllFromMemory();
   nsresult NormalizeToACE(nsCString &aHost);
-  nsresult GetHost(nsIURI *aURI, nsACString &aResult);
   static void UpdateDB(OperationType         aOp,
                        mozIStorageStatement* aStmt,
                        int64_t               aID,
                        const nsACString     &aHost,
                        const nsACString     &aType,
                        uint32_t              aPermission,
                        uint32_t              aExpireType,
-                       int64_t               aExpireTime);
+                       int64_t               aExpireTime,
+                       uint32_t              aAppId,
+                       bool                  aIsInBrowserElement);
 
   nsCOMPtr<nsIObserverService> mObserverService;
   nsCOMPtr<nsIIDNService>      mIDNService;
 
   nsCOMPtr<mozIStorageConnection> mDBConn;
   nsCOMPtr<mozIStorageStatement> mStmtInsert;
   nsCOMPtr<mozIStorageStatement> mStmtDelete;
   nsCOMPtr<mozIStorageStatement> mStmtUpdate;
 
-  nsTHashtable<nsHostEntry>    mHostTable;
+  nsTHashtable<PermissionHashKey> mPermissionTable;
   // a unique, monotonically increasing id used to identify each database entry
   int64_t                      mLargestID;
 
   // An array to store the strings identifying the different types.
   nsTArray<nsCString>          mTypeArray;
 
   // Initially, |false|. Set to |true| once shutdown has started, to avoid
   // reopening the database.
--- a/extensions/cookie/test/Makefile.in
+++ b/extensions/cookie/test/Makefile.in
@@ -47,16 +47,20 @@ MOCHITEST_FILES = \
   test_same_base_domain_3.html \
   test_same_base_domain_4.html \
   file_localhost_inner.html \
   test_same_base_domain_5.html \
   test_same_base_domain_6.html \
   file_loopback_inner.html \
   $(NULL)
 
+MOCHITEST_CHROME_FILES = \
+  test_permissionmanager_app_isolation.html \
+  $(NULL)
+
 MOCHITEST_BROWSER_FILES = \
   browser_test_favicon.js \
   $(NULL)
 
 XPCSHELL_TESTS = unit
 
 # FIXME/bug 575918: out-of-process xpcshell is broken on OS X
 ifneq ($(OS_ARCH),Darwin)
new file mode 100644
--- /dev/null
+++ b/extensions/cookie/test/test_permissionmanager_app_isolation.html
@@ -0,0 +1,168 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=758258
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for nsIPrincipal extendedOrigin, appStatus and appId</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=758258">Mozilla Bug 758258</a>
+<p id="display"></p>
+<div id="content">
+  
+</div>
+<pre id="test">
+<script type="application/javascript;version=1.7">
+
+/** Test for Bug 758258 **/
+
+var Ci = Components.interfaces;
+var Cc = Components.classes;
+
+SimpleTest.waitForExplicitFinish();
+
+var permManager = Cc["@mozilla.org/permissionmanager;1"]
+                    .getService(Ci.nsIPermissionManager);
+
+const gPermName = 'foobar';
+
+var previousPrefs = {
+  mozBrowserFramesEnabled: undefined,
+};
+
+try {
+  previousPrefs.mozBrowserFramesEnabled = SpecialPowers.getBoolPref('dom.mozBrowserFramesEnabled');
+} catch(e)
+{
+}
+
+SpecialPowers.setBoolPref('dom.mozBrowserFramesEnabled', true);
+
+// We use http://test/ as url so all apps use the same url and app isolation is
+// more obvious.
+var gData = [
+  // APP 1
+  {
+    app: 'http://example.org/manifest.webapp',
+    action: 'read-no',
+    src: 'http://test/',
+  },
+  {
+    app: 'http://example.org/manifest.webapp',
+    action: 'write',
+    src: 'http://test/',
+  },
+  {
+    app: 'http://example.org/manifest.webapp',
+    action: 'read-yes',
+    src: 'http://test/',
+  },
+  // APP 2
+  {
+    app: 'https://example.com/manifest.webapp',
+    action: 'read-no',
+    src: 'http://test/',
+  },
+  {
+    app: 'https://example.com/manifest.webapp',
+    action: 'write',
+    src: 'http://test/',
+  },
+  {
+    app: 'https://example.com/manifest.webapp',
+    action: 'read-yes',
+    src: 'http://test/',
+  },
+  // Browser
+  {
+    browser: true,
+    action: 'read-no',
+    src: 'http://test/',
+  },
+  {
+    browser: true,
+    action: 'write',
+    src: 'http://test/',
+  },
+  {
+    browser: true,
+    action: 'read-yes',
+    src: 'http://test/',
+  },
+];
+
+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('load', function(e) {
+        var principal = iframe.contentDocument.nodePrincipal;
+
+        switch (data.action) {
+          case 'read-no':
+            is(permManager.testPermissionFromPrincipal(principal, gPermName),
+               Ci.nsIPermissionManager.UNKNOWN_ACTION,
+               "Permission should not be set yet");
+            is(permManager.testExactPermissionFromPrincipal(principal, gPermName),
+               Ci.nsIPermissionManager.UNKNOWN_ACTION,
+               "Permission should not be set yet");
+            break;
+          case 'write':
+            permManager.addFromPrincipal(principal, gPermName, Ci.nsIPermissionManager.ALLOW_ACTION);
+            break;
+          case 'read-yes':
+            is(permManager.testPermissionFromPrincipal(principal, gPermName),
+               Ci.nsIPermissionManager.ALLOW_ACTION,
+               "Permission should be set");
+            is(permManager.testExactPermissionFromPrincipal(principal, gPermName),
+               Ci.nsIPermissionManager.ALLOW_ACTION,
+               "Permission should be set");
+            break;
+          default:
+            ok(false, "shouldn't be there");
+        }
+
+        // Calling removeChild() produces an error that creates failures.
+        //document.getElementById('content').removeChild(iframe);
+
+        i++;
+        if (i >= gData.length) {
+          if (previousPrefs.mozBrowserFramesEnabled !== undefined) {
+            SpecialPowers.setBoolPref('dom.mozBrowserFramesEnabled', previousPrefs.mozBrowserFramesEnabled);
+          }
+
+          SimpleTest.finish();
+        } else {
+          gTestRunner.next();
+        }
+      });
+    }
+
+    iframe.src = data.src;
+
+    document.getElementById('content').appendChild(iframe);
+
+    yield;
+  }
+}
+
+var gTestRunner = runTest();
+gTestRunner.next();
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/lib/parallelarray-helpers.js
@@ -0,0 +1,52 @@
+/* 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/. */
+
+load(libdir + "eqArrayHelper.js");
+
+function assertEqParallelArray(a, b) {
+  assertEq(a instanceof ParallelArray, true);
+  assertEq(b instanceof ParallelArray, true);
+
+  var shape = a.shape;
+  assertEqArray(shape, b.shape);
+
+  function bump(indices) {
+    var d = indices.length - 1;
+    while (d >= 0) {
+      if (++indices[d] < shape[d])
+        break;
+      indices[d] = 0;
+      d--;
+    }
+    return d >= 0;
+  }
+
+  var iv = shape.map(function () { return 0; });
+  do {
+    var e1 = a.get(iv);
+    var e2 = b.get(iv);
+    if (e1 instanceof ParallelArray && e2 instanceof ParallelArray)
+      assertEqParallelArray(e1, e2);
+    else if (e1 instanceof Array && e2 instanceof Array)
+      assertEqArray(e1, e2);
+    else
+      assertEq(e1, e2);
+  } while (bump(iv));
+}
+
+function assertParallelArrayModesCommute(modes, pa, op) {
+  var args = Array.slice(arguments, 3);
+  var acc;
+  modes.forEach(function (mode) {
+    var result = op.apply(pa, args.concat([{ mode: mode, expect: "success" }]));
+    if (acc) {
+      if (acc instanceof ParallelArray)
+        assertEqParallelArray(acc, result);
+      else
+        assertEq(acc, result);
+    } else {
+      acc = result;
+    }
+  });
+}
--- a/js/src/jit-test/tests/parallelarray/comprehension-1.js
+++ b/js/src/jit-test/tests/parallelarray/comprehension-1.js
@@ -1,9 +1,10 @@
+load(libdir + "parallelarray-helpers.js")
 
 function buildComprehension() {
-  // 1D comprehension 
+  // 1D comprehension
   var p = new ParallelArray(10, function (idx) { return idx; });
-  var a = [0,1,2,3,4,5,6,7,8,9];
-  assertEq(p.toString(), "<" + a.join(",") + ">");
+  var a = new ParallelArray([0,1,2,3,4,5,6,7,8,9]);
+  assertEqParallelArray(p, a);
 }
 
 buildComprehension();
--- a/js/src/jit-test/tests/parallelarray/comprehension-2.js
+++ b/js/src/jit-test/tests/parallelarray/comprehension-2.js
@@ -1,9 +1,12 @@
+load(libdir + "parallelarray-helpers.js");
+load(libdir + "eqArrayHelper.js");
 
 function buildMultidim() {
   // 2D comprehension
   var p = new ParallelArray([2,2], function (i,j) { return i + j; });
-  assertEq(p.shape.toString(), [2,2].toString());
-  assertEq(p.toString(), "<<0,1>,<1,2>>");
+  var a = new ParallelArray([0,1,1,2]).partition(2);
+  assertEqArray(p.shape, [2,2]);
+  assertEqParallelArray(p, a);
 }
 
 buildMultidim();
--- a/js/src/jit-test/tests/parallelarray/comprehension-scale.js
+++ b/js/src/jit-test/tests/parallelarray/comprehension-scale.js
@@ -1,31 +1,21 @@
+load(libdir + "parallelarray-helpers.js");
 
 function buildComprehension() {
   var H = 96;
   var W = 96;
   var d = 4;
   // 3D 96x96x4 texture-like PA
   var p = new ParallelArray([H,W,d], function (i,j,k) { return i + j + k; });
-  var a = "<";
+  var a = [];
   for (var i = 0; i < H; i++) {
-    a += "<";
     for (var j = 0; j < W; j++) {
-      a += "<";
       for (var k = 0; k < d; k++) {
-        a += i+j+k;
-        if (k !== d - 1)
-          a += ",";
+        a.push(i+j+k);
       }
-      a += ">";
-      if (j !== W - 1)
-        a += ","
     }
-    a += ">";
-    if (i !== H - 1)
-      a += ","
   }
-  a += ">"
-
-  assertEq(p.toString(), a);
+  var p2 = new ParallelArray(a).partition(d).partition(H);
+  assertEqParallelArray(p, p2);
 }
 
 buildComprehension();
--- a/js/src/jit-test/tests/parallelarray/constructor-1.js
+++ b/js/src/jit-test/tests/parallelarray/constructor-1.js
@@ -1,16 +1,14 @@
-function bracket(s) {
-  return "<" + s + ">";
-}
+load(libdir + "eqArrayHelper.js");
 
 function buildSimple() {
   // Simple constructor
   var a = [1,2,3,4,5];
   var p = new ParallelArray(a);
-  var e = a.join(",");
-  assertEq(p.toString(), bracket(e));
+  assertEqArray(p, a);
+  var a2 = a.slice();
   a[0] = 9;
   // No sharing
-  assertEq(p.toString(), bracket(e));
+  assertEqArray(p, a2);
 }
 
 buildSimple();
--- a/js/src/jit-test/tests/parallelarray/constructor-4.js
+++ b/js/src/jit-test/tests/parallelarray/constructor-4.js
@@ -1,12 +1,13 @@
+load(libdir + "parallelarray-helpers.js");
 
 function buildPA() {
   // Construct copying from PA
   var p1 = new ParallelArray([1,2,3,4]);
   var p2 = new ParallelArray(p1);
-  assertEq(p1.toString(), p2.toString());
+  assertEqParallelArray(p1, p2);
   var p1d = new ParallelArray([2,2], function(i,j) { return i + j; });
   var p2d = new ParallelArray(p1d);
   assertEq(p1d.toString(), p2d.toString());
 }
 
 buildPA();
--- a/js/src/jit-test/tests/parallelarray/element-2.js
+++ b/js/src/jit-test/tests/parallelarray/element-2.js
@@ -1,11 +1,14 @@
+load(libdir + "parallelarray-helpers.js");
+
 function testElement() {
   // Test getting element from higher dimension
   var p = new ParallelArray([2,2,2], function () { return 0; });
-  assertEq(p[0].toString(), "<<0,0>,<0,0>>");
+  var p0 = new ParallelArray([2,2], function () { return 0; });
+  assertEqParallelArray(p[0], p0);
   // Should create new wrapper
   assertEq(p[0] !== p[0], true);
   // Test out of bounds
   assertEq(p[42], undefined);
 }
 
 testElement();
--- a/js/src/jit-test/tests/parallelarray/filter-1.js
+++ b/js/src/jit-test/tests/parallelarray/filter-1.js
@@ -1,13 +1,14 @@
+load(libdir + "parallelarray-helpers.js");
 
 function testFilterAll() {
   // Test filtering everything (leaving everything in)
   var p = new ParallelArray([0,1,2,3,4]);
   var all = p.map(function (i) { return true; });
   var r = p.filter(all);
-  assertEq(r.toString(), "<0,1,2,3,4>");
+  assertEqParallelArray(r, p);
   var p = new ParallelArray([5,2], function(i,j) { return i+j; });
   var r = p.filter(all);
-  assertEq(r.toString(), "<<0,1>,<1,2>,<2,3>,<3,4>,<4,5>>");
+  assertEqParallelArray(r, new ParallelArray(p));
 }
 
 testFilterAll();
--- a/js/src/jit-test/tests/parallelarray/filter-2.js
+++ b/js/src/jit-test/tests/parallelarray/filter-2.js
@@ -1,12 +1,14 @@
+load(libdir + "parallelarray-helpers.js");
+
 function testFilterNone() {
   // Test filtering (removing everything)
   var p = new ParallelArray([0,1,2,3,4]);
   var none = p.map(function () { return false; });
   var r = p.filter(none);
-  assertEq(r.toString(), "<>");
+  assertEqParallelArray(r, new ParallelArray);
   var p = new ParallelArray([5,2], function(i,j) { return i+j; });
   var r = p.filter(none);
-  assertEq(r.toString(), "<>");
+  assertEqParallelArray(r, new ParallelArray);
 }
 
 testFilterNone();
--- a/js/src/jit-test/tests/parallelarray/filter-3.js
+++ b/js/src/jit-test/tests/parallelarray/filter-3.js
@@ -1,16 +1,14 @@
-function bracket(s) {
-  return "<" + s + ">";
-}
+load(libdir + "parallelarray-helpers.js");
 
 function testFilterSome() {
   var p = new ParallelArray([0,1,2,3,4]);
   var evenBelowThree = p.map(function (i) { return ((i%2) === 0) && (i < 3); });
   var r = p.filter(evenBelowThree);
-  assertEq(r.toString(), bracket([0,2].join(",")));
+  assertEqParallelArray(r, new ParallelArray([0,2]));
   var p = new ParallelArray([5,2], function (i,j) { return i; });
   var evenBelowThree = p.map(function (i) { return ((i[0]%2) === 0) && (i[0] < 3); });
   var r = p.filter(evenBelowThree);
-  assertEq(r.toString(), bracket(["<0,0>","<2,2>"].join(",")));
+  assertEqParallelArray(r, new ParallelArray([p[0], p[2]]));
 }
 
 testFilterSome();
--- a/js/src/jit-test/tests/parallelarray/filter-4.js
+++ b/js/src/jit-test/tests/parallelarray/filter-4.js
@@ -1,18 +1,16 @@
-function bracket(s) {
-  return "<" + s + ">";
-}
+load(libdir + "parallelarray-helpers.js");
 
 function testFilterMisc() {
   var p = new ParallelArray([0,1,2]);
   // Test array
   var r = p.filter([true, false, true]);
-  assertEq(r.toString(), bracket([0,2].join(",")));
+  assertEqParallelArray(r, new ParallelArray([p[0], p[2]]));
   // Test array-like
   var r = p.filter({ 0: true, 1: false, 2: true, length: 3 });
-  assertEq(r.toString(), bracket([0,2].join(",")));
+  assertEqParallelArray(r, new ParallelArray([p[0], p[2]]));
   // Test truthy
   var r = p.filter([1, "", {}]);
-  assertEq(r.toString(), bracket([0,2].join(",")));
+  assertEqParallelArray(r, new ParallelArray([p[0], p[2]]));
 }
 
 testFilterMisc();
--- a/js/src/jit-test/tests/parallelarray/flatten-1.js
+++ b/js/src/jit-test/tests/parallelarray/flatten-1.js
@@ -1,11 +1,13 @@
+load(libdir + "eqArrayHelper.js");
+
 function testFlatten() {
   var shape = [5];
   for (var i = 0; i < 7; i++) {
     shape.push(i+1);
     var p = new ParallelArray(shape, function(i,j) { return i+j; });
     var flatShape = ([shape[0] * shape[1]]).concat(shape.slice(2));
-    assertEq(p.flatten().shape.toString(), flatShape.toString());
+    assertEqArray(p.flatten().shape, flatShape);
   }
 }
 
 testFlatten();
--- a/js/src/jit-test/tests/parallelarray/flatten-2.js
+++ b/js/src/jit-test/tests/parallelarray/flatten-2.js
@@ -1,7 +1,9 @@
+load(libdir + "parallelarray-helpers.js");
+
 function testFlatten() {
   var p = new ParallelArray([2,2], function(i,j) { return i+j; });
   var p2 = new ParallelArray([0,1,1,2]);
-  assertEq(p.flatten().toString(), p2.toString());
+  assertEqParallelArray(p.flatten(), p2);
 }
 
 testFlatten();
--- a/js/src/jit-test/tests/parallelarray/get-2.js
+++ b/js/src/jit-test/tests/parallelarray/get-2.js
@@ -1,10 +1,12 @@
+load(libdir + "parallelarray-helpers.js");
+
 function testGet() {
   var p = new ParallelArray([2,2,2], function(i,j,k) { return i+j+k; });
   assertEq(p.get([1,1,1]), 1+1+1);
   var p2 = new ParallelArray([2], function(i) { return 1+1+i; });
-  assertEq(p.get([1,1]).toString(), p2.toString());
+  assertEqParallelArray(p.get([1,1]), p2);
   var p3 = new ParallelArray([2,2], function(i,j) { return 1+i+j; });
-  assertEq(p.get([1]).toString(), p3.toString());
+  assertEqParallelArray(p.get([1]), p3);
 }
 
 testGet();
--- a/js/src/jit-test/tests/parallelarray/map-1.js
+++ b/js/src/jit-test/tests/parallelarray/map-1.js
@@ -1,11 +1,10 @@
-function bracket(s) {
-  return "<" + s + ">";
-}
+load(libdir + "parallelarray-helpers.js");
 
 function testMap() {
-    var p = new ParallelArray([0,1,2,3,4]);
-    var m = p.map(function (v) { return v+1; });
-    assertEq(m.toString(), bracket([1,2,3,4,5].join(",")));
+  var p = new ParallelArray([0,1,2,3,4]);
+  var m = p.map(function (v) { return v+1; });
+  var p2 = new ParallelArray([1,2,3,4,5]);
+  assertEqParallelArray(m, p2);
 }
 
 testMap();
--- a/js/src/jit-test/tests/parallelarray/map-3.js
+++ b/js/src/jit-test/tests/parallelarray/map-3.js
@@ -1,9 +1,12 @@
+load(libdir + "parallelarray-helpers.js");
+
 function testMap() {
   // Test mapping higher dimensional
   var p = new ParallelArray([2,2], function (i,j) { return i+j; });
-  var m = p.map(function(x) { return x.toString(); });
-  assertEq(m.toString(), "<<0,1>,<1,2>>");
+  var m = p.map(function(x) { return x; });
+  var p2 = new ParallelArray(p);
+  assertEqParallelArray(m, p2);
 }
 
 testMap();
 
--- a/js/src/jit-test/tests/parallelarray/partition-1.js
+++ b/js/src/jit-test/tests/parallelarray/partition-1.js
@@ -1,13 +1,15 @@
+load(libdir + "eqArrayHelper.js");
+
 function testPartition() {
   var p = new ParallelArray([1,2,3,4,5,6,7,8]);
   var pp = p.partition(2);
   var ppp = pp.partition(2);
   var ppShape = [p.shape[0] / 2, 2].concat(p.shape.slice(1));
   var pppShape = [pp.shape[0] / 2, 2].concat(pp.shape.slice(1));
-  assertEq(pp.shape.toString(), ppShape.toString())
+  assertEqArray(pp.shape, ppShape);
   assertEq(pp.toString(), "<<1,2>,<3,4>,<5,6>,<7,8>>");
-  assertEq(ppp.shape.toString(), pppShape.toString())
+  assertEqArray(ppp.shape, pppShape);
   assertEq(ppp.toString(), "<<<1,2>,<3,4>>,<<5,6>,<7,8>>>");
 }
 
 testPartition();
--- a/js/src/jit-test/tests/parallelarray/scan-3.js
+++ b/js/src/jit-test/tests/parallelarray/scan-3.js
@@ -5,14 +5,14 @@ function testScan() {
   function f(a, b) { return a; }
   var shape = [2];
   for (var i = 0; i < 7; i++) {
     shape.push(i+1);
     var p = new ParallelArray(shape, function () { return i+1; });
     var r = p.reduce(f);
     var s = p.scan(f)
     for (var j = 0; j < s.length; j++)
-      assertEq(s[0].shape.length, i + 1);
+      assertEq(s[j].shape.length, i + 1);
     assertEq(r.shape.length, i + 1);
   }
 }
 
 testScan();
--- a/js/src/jit-test/tests/parallelarray/scatter-2.js
+++ b/js/src/jit-test/tests/parallelarray/scatter-2.js
@@ -1,9 +1,10 @@
+load(libdir + "parallelarray-helpers.js");
 
 function testScatterIdentity() {
   var p = new ParallelArray([1,2,3,4,5]);
   var r = p.scatter([0,1,2,3,4]);
-  assertEq(p.toString(), r.toString());
+  assertEqParallelArray(p, r);
 }
 
 testScatterIdentity();
 
--- a/js/src/jit-test/tests/parallelarray/scatter-3.js
+++ b/js/src/jit-test/tests/parallelarray/scatter-3.js
@@ -1,10 +1,11 @@
+load(libdir + "parallelarray-helpers.js");
 
 function testScatter() {
   var p = new ParallelArray([1,2,3,4,5]);
   var r = p.scatter([1,0,3,2,4]);
   var p2 = new ParallelArray([2,1,4,3,5]);
-  assertEq(r.toString(), p2.toString());
+  assertEqParallelArray(r, p2);
 }
 
 testScatter();
 
--- a/js/src/jit-test/tests/parallelarray/scatter-4.js
+++ b/js/src/jit-test/tests/parallelarray/scatter-4.js
@@ -1,12 +1,10 @@
-function bracket(s) {
-  return "<" + s + ">";
-}
+load(libdir + "parallelarray-helpers.js");
 
 function testScatterDefault() {
   var p = new ParallelArray([1,2,3,4,5]);
   var r = p.scatter([0,2,4], 9);
-  assertEq(r.toString(), bracket([1,9,2,9,3].join(",")));
+  assertEqParallelArray(r, new ParallelArray([1,9,2,9,3]));
 }
 
 testScatterDefault();
 
--- a/js/src/jit-test/tests/parallelarray/scatter-5.js
+++ b/js/src/jit-test/tests/parallelarray/scatter-5.js
@@ -1,11 +1,9 @@
-function bracket(s) {
-  return "<" + s + ">";
-}
+load(libdir + "parallelarray-helpers.js");
 
 function testScatterConflict() {
-    var p = new ParallelArray([1,2,3,4,5]);
-    var r = p.scatter([0,1,0,3,4], 9, function (a,b) { return a+b; });
-    assertEq(r.toString(), bracket([4,2,9,4,5].join(",")));
+  var p = new ParallelArray([1,2,3,4,5]);
+  var r = p.scatter([0,1,0,3,4], 9, function (a,b) { return a+b; });
+  assertEqParallelArray(r, new ParallelArray([4,2,9,4,5]));
 }
 
 testScatterConflict();
--- a/js/src/jit-test/tests/parallelarray/scatter-7.js
+++ b/js/src/jit-test/tests/parallelarray/scatter-7.js
@@ -1,13 +1,15 @@
+load(libdir + "parallelarray-helpers.js");
 
 function testScatterIdentity() {
   var shape = [5];
   for (var i = 0; i < 7; i++) {
     shape.push(i+1);
     var p = new ParallelArray(shape, function(k) { return k; });
     var r = p.scatter([0,1,2,3,4]);
-    assertEq(p.toString(), r.toString());
+    var p2 = new ParallelArray([p[0], p[1], p[2], p[3], p[4]]);
+    assertEqParallelArray(p2, r);
   }
 }
 
 testScatterIdentity();
 
--- a/js/src/jit-test/tests/parallelarray/scatter-8.js
+++ b/js/src/jit-test/tests/parallelarray/scatter-8.js
@@ -1,14 +1,15 @@
+load(libdir + "parallelarray-helpers.js");
 
 function testScatter() {
   var shape = [5];
   for (var i = 0; i < 7; i++) {
     shape.push(i+1);
     var p = new ParallelArray(shape, function(k) { return k; });
     var r = p.scatter([1,0,3,2,4]);
     var p2 = new ParallelArray([p[1], p[0], p[3], p[2], p[4]]);
-    assertEq(p2.toString(), r.toString());
+    assertEqParallelArray(p2, r);
   }
 }
 
 testScatter();
 
--- a/js/src/jit-test/tests/parallelarray/scatter-9.js
+++ b/js/src/jit-test/tests/parallelarray/scatter-9.js
@@ -1,7 +1,9 @@
+load(libdir + "parallelarray-helpers.js");
+
 function testScatter() {
   // Ignore the rest of the scatter vector if longer than source
   var p = new ParallelArray([1,2,3,4,5]);
   var r = p.scatter([1,0,3,2,4,1,2,3]);
   var p2 = new ParallelArray([2,1,4,3,5]);
-  assertEq(r.toString(), p2.toString());
+  assertEqParallelArray(r, p2);
 }
--- a/js/src/jit-test/tests/parallelarray/shape-2.js
+++ b/js/src/jit-test/tests/parallelarray/shape-2.js
@@ -1,14 +1,16 @@
+load(libdir + "eqArrayHelper.js");
+
 function testShape() {
   // Test higher dimension shape up to 8D
   var shape = [];
   for (var i = 0; i < 8; i++) {
     shape.push(i+1);
     var p = new ParallelArray(shape, function () { return 0; });
     // Test shape identity and shape
     assertEq(p.shape, p.shape);
     assertEq(p.shape !== shape, true);
-    assertEq(p.shape.toString(), shape.toString());
+    assertEqArray(p.shape, shape);
   }
 }
 
 testShape();
--- a/netwerk/base/public/nsIPermission.idl
+++ b/netwerk/base/public/nsIPermission.idl
@@ -1,31 +1,41 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  *
  * 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 "nsISupports.idl"
 
-[scriptable, uuid(5036f0f6-f77b-4168-9d57-a1c0dd66cf02)]
+[scriptable, uuid(cfb08e46-193c-4be7-a467-d7775fb2a31e)]
 /**
  * This interface defines a "permission" object,
  * used to specify allowed/blocked objects from
  * user-specified sites (cookies, images etc).
  */
 
 interface nsIPermission : nsISupports
 {
     /**
      * The name of the host for which the permission is set
      */
     readonly attribute AUTF8String host;
 
     /**
+     * The id of the app for which the permission is set.
+     */
+    readonly attribute unsigned long appId;
+
+    /**
+     * Whether the permission has been set to a page inside a browser element.
+     */
+    readonly attribute boolean isInBrowserElement;
+
+    /**
      * a case-sensitive ASCII string, indicating the type of permission
      * (e.g., "cookie", "image", etc).
      * This string is specified by the consumer when adding a permission 
      * via nsIPermissionManager.
      * @see nsIPermissionManager
      */
     readonly attribute ACString type;
 
--- a/netwerk/ipc/NeckoMessageUtils.h
+++ b/netwerk/ipc/NeckoMessageUtils.h
@@ -15,53 +15,76 @@ namespace IPC {
 
 // nsIPermissionManager utilities
 
 struct Permission
 {
   nsCString host, type;
   uint32_t capability, expireType;
   int64_t expireTime;
+  uint32_t appId;
+  bool isInBrowserElement;
 
   Permission() { }
   Permission(const nsCString& aHost,
+             const uint32_t aAppId,
+             const bool aIsInBrowserElement,
              const nsCString& aType,
              const uint32_t aCapability,
              const uint32_t aExpireType,
              const int64_t aExpireTime) : host(aHost),
                                           type(aType),
                                           capability(aCapability),
                                           expireType(aExpireType),
-                                          expireTime(aExpireTime) { }
+                                          expireTime(aExpireTime),
+                                          appId(aAppId),
+                                          isInBrowserElement(aIsInBrowserElement)
+  {}
 };
 
 template<>
 struct ParamTraits<Permission>
 {
   static void Write(Message* aMsg, const Permission& aParam)
   {
     WriteParam(aMsg, aParam.host);
     WriteParam(aMsg, aParam.type);
     WriteParam(aMsg, aParam.capability);
     WriteParam(aMsg, aParam.expireType);
     WriteParam(aMsg, aParam.expireTime);
+    WriteParam(aMsg, aParam.appId);
+    WriteParam(aMsg, aParam.isInBrowserElement);
   }
 
   static bool Read(const Message* aMsg, void** aIter, Permission* aResult)
   {
     return ReadParam(aMsg, aIter, &aResult->host) &&
            ReadParam(aMsg, aIter, &aResult->type) &&
            ReadParam(aMsg, aIter, &aResult->capability) &&
            ReadParam(aMsg, aIter, &aResult->expireType) &&
-           ReadParam(aMsg, aIter, &aResult->expireTime);
+           ReadParam(aMsg, aIter, &aResult->expireTime) &&
+           ReadParam(aMsg, aIter, &aResult->appId) &&
+           ReadParam(aMsg, aIter, &aResult->isInBrowserElement);
   }
 
-  static void Log(const Permission& aParam, std::wstring* aLog)
+  static void Log(const Permission& p, std::wstring* l)
   {
-    aLog->append(StringPrintf(L"[%s]", aParam.host.get()));
+    l->append(L"(");
+    LogParam(p.host, l);
+    l->append(L", ");
+    LogParam(p.appId, l);
+    l->append(L", ");
+    LogParam(p.isInBrowserElement, l);
+    l->append(L", ");
+    LogParam(p.capability, l);
+    l->append(L", ");
+    LogParam(p.expireTime, l);
+    l->append(L", ");
+    LogParam(p.expireType, l);
+    l->append(L")");
   }
 };
 
 template<>
 struct ParamTraits<PRNetAddr>
 {
   static void Write(Message* aMsg, const PRNetAddr &aParam)
   {
--- a/testing/mozbase/mozprofile/mozprofile/permissions.py
+++ b/testing/mozbase/mozprofile/mozprofile/permissions.py
@@ -226,36 +226,41 @@ class Permissions(object):
                 self._locations.read(locations)
 
     def write_db(self, locations):
         """write permissions to the sqlite database"""
 
         # Open database and create table
         permDB = sqlite3.connect(os.path.join(self._profileDir, "permissions.sqlite"))
         cursor = permDB.cursor();
+
+        cursor.execute("PRAGMA user_version=3");
+
         # SQL copied from
         # http://mxr.mozilla.org/mozilla-central/source/extensions/cookie/nsPermissionManager.cpp
         cursor.execute("""CREATE TABLE IF NOT EXISTS moz_hosts (
            id INTEGER PRIMARY KEY,
            host TEXT,
            type TEXT,
            permission INTEGER,
            expireType INTEGER,
-           expireTime INTEGER)""")
+           expireTime INTEGER,
+           appId INTEGER,
+           isInBrowserElement INTEGER)""")
 
         for location in locations:
             # set the permissions
             permissions = { 'allowXULXBL': 'noxul' not in location.options }
             for perm, allow in permissions.iteritems():
                 self._num_permissions += 1
                 if allow:
                     permission_type = 1
                 else:
                     permission_type = 2
-                cursor.execute("INSERT INTO moz_hosts values(?, ?, ?, ?, 0, 0)",
+                cursor.execute("INSERT INTO moz_hosts values(?, ?, ?, ?, 0, 0, 0, 0)",
                                (self._num_permissions, location.host, perm,
                                 permission_type))
 
         # Commit and close
         permDB.commit()
         cursor.close()
 
     def network_prefs(self, proxy=False):
--- a/xpcom/reflect/xptcall/src/md/unix/Makefile.in
+++ b/xpcom/reflect/xptcall/src/md/unix/Makefile.in
@@ -160,17 +160,17 @@ endif
 ifeq ($(OS_ARCH),NetBSD)
 ifneq (,$(filter arm% sa110,$(OS_TEST)))
 CPPSRCS		:= xptcinvoke_arm_netbsd.cpp xptcstubs_arm_netbsd.cpp
 endif
 endif
 #
 # OpenBSD/ARM
 #
-ifneq (,$(filter OpenBSDarmish OpenBSDzaurus,$(OS_ARCH)$(OS_TEST)))
+ifeq ($(OS_ARCH)$(OS_TEST),OpenBSDarm)
 CPPSRCS		:= xptcinvoke_arm_openbsd.cpp xptcstubs_arm_openbsd.cpp
 endif
 
 ######################################################################
 # HPPA
 ######################################################################
 #
 # HP-UX/PA32