Merge mozilla-inbound to mozilla-central
authorEd Morley <emorley@mozilla.com>
Wed, 06 Feb 2013 14:26:36 +0000
changeset 130865 04e13fc9dbff9391f9a42ab2f084a1f4f13e0729
parent 130816 bc108d2ce8d17a560e2fc2affa6867ca9418448d (current diff)
parent 130864 6e9df8c0e7907836667b31bea392ef25a1601d24 (diff)
child 130866 beca57e612fd8a3da02b1e54509c192e63b24451
child 130901 c66945e9956d6d23fd846fcf14ddf8eeed6e10fe
child 131022 d8fd1560108a12ce35616653568985a0c9590be9
push id2323
push userbbajaj@mozilla.com
push dateMon, 01 Apr 2013 19:47:02 +0000
treeherdermozilla-beta@7712be144d91 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone21.0a1
first release with
nightly linux32
04e13fc9dbff / 21.0a1 / 20130207030936 / files
nightly linux64
04e13fc9dbff / 21.0a1 / 20130207030936 / files
nightly mac
04e13fc9dbff / 21.0a1 / 20130207030936 / files
nightly win32
04e13fc9dbff / 21.0a1 / 20130207030936 / files
nightly win64
04e13fc9dbff / 21.0a1 / 20130207030936 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-inbound to mozilla-central
b2g/chrome/content/UAO_child.js
content/media/MediaStreamGraph.cpp
dom/telephony/Makefile.in
dom/telephony/nsIDOMNavigatorTelephony.idl
js/src/jsval.h
mobile/android/chrome/content/sanitize.js
testing/mozbase/README
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -640,8 +640,10 @@ pref("webgl.can-lose-context-in-foregrou
 // Allow nsMemoryInfoDumper to create a fifo in the temp directory.  We use
 // this fifo to trigger about:memory dumps, among other things.
 pref("memory_info_dumper.watch_fifo.enabled", true);
 pref("memory_info_dumper.watch_fifo.directory", "/data/local");
 
 // <input type='file'> implementation is not complete. We have to disable the
 // type to web content to help them do feature detection.
 pref("dom.disable_input_file", true);
+
+pref("general.useragent.enable_overrides", true);
deleted file mode 100644
--- a/b2g/chrome/content/UAO_child.js
+++ /dev/null
@@ -1,2 +0,0 @@
-Components.utils.import('resource://gre/modules/UserAgentOverrides.jsm');
-UserAgentOverrides.init();
--- a/b2g/chrome/jar.mn
+++ b/b2g/chrome/jar.mn
@@ -20,18 +20,16 @@ chrome.jar:
   content/runapp.js                     (content/runapp.js)
 #endif
   content/content.css                   (content/content.css)
   content/touchcontrols.css             (content/touchcontrols.css)
 
   content/payment.js                    (content/payment.js)
   content/identity.js                   (content/identity.js)
 
-  content/UAO_child.js                 (content/UAO_child.js)
-
 % override chrome://global/content/netError.xhtml chrome://browser/content/netError.xhtml
 % override chrome://global/skin/netError.css chrome://browser/content/netError.css
 % override chrome://global/skin/media/videocontrols.css chrome://browser/content/touchcontrols.css
 
   content/netError.xhtml                (content/netError.xhtml)
   content/netError.css                  (content/netError.css)
   content/images/errorpage-larry-black.png (content/images/errorpage-larry-black.png)
   content/images/errorpage-larry-white.png (content/images/errorpage-larry-white.png)
--- a/b2g/components/ProcessGlobal.js
+++ b/b2g/components/ProcessGlobal.js
@@ -52,24 +52,13 @@ ProcessGlobal.prototype = {
       let message = subject.wrappedJSObject;
       let prefix = ('Content JS ' + message.level.toUpperCase() +
                     ' at ' + message.filename + ':' + message.lineNumber +
                     ' in ' + (message.functionName || 'anonymous') + ': ');
       Services.console.logStringMessage(prefix + Array.join(message.arguments,
                                                             ' '));
       break;
     }
-    case 'remote-browser-frame-shown': {
-      let frameLoader = subject.QueryInterface(Ci.nsIFrameLoader);
-      let mm = frameLoader.messageManager;
-
-      const kFrameScript = "chrome://browser/content/UAO_child.js";
-      try {
-        mm.loadFrameScript(kFrameScript, true);
-      } catch (e) {
-        dump('Error loading ' + kFrameScript + ' as frame script: ' + e + '\n');
-      }
-    }
     }
   },
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ProcessGlobal]);
--- a/b2g/config/panda/releng-pandaboard.tt
+++ b/b2g/config/panda/releng-pandaboard.tt
@@ -1,8 +1,14 @@
 [
 {
 "size": 678628568,
 "digest": "e0b98a75831313171781d91ab4c3b8ae66e3af7fcec433a33d75fbc647cb3bba311f790ccb9cd9c8be8c5549210b759d6b85afe7395ada74c2c319a29556fd8e",
 "algorithm": "sha512",
 "filename": "gonk.tar.xz"
+},
+{
+"size": 2116507,
+"digest": "be67a012963a5c162834f9fcb989bcebd2d047dcb4e17ee23031b694dcf7cdfd6d7a6545d7a1f5e7293b6d24415403972f4ea1ab8c6c78fefcabfaf3f6875214",
+"algorithm": "sha512",
+"filename": "download-panda.tar.bz2"
 }
 ]
--- a/browser/base/Makefile.in
+++ b/browser/base/Makefile.in
@@ -31,15 +31,15 @@ DEFINES += \
 ifneq (,$(filter windows gtk2 cocoa, $(MOZ_WIDGET_TOOLKIT)))
 DEFINES += -DHAVE_SHELL_SERVICE=1
 endif
 
 ifneq (,$(filter windows cocoa gtk2, $(MOZ_WIDGET_TOOLKIT)))
 DEFINES += -DCONTEXT_COPY_IMAGE_CONTENTS=1
 endif
 
-ifneq (,$(filter windows, $(MOZ_WIDGET_TOOLKIT)))
+ifneq (,$(filter windows cocoa, $(MOZ_WIDGET_TOOLKIT)))
 DEFINES += -DCAN_DRAW_IN_TITLEBAR=1
 endif
 
 ifneq (,$(filter windows gtk2, $(MOZ_WIDGET_TOOLKIT)))
 DEFINES += -DMENUBAR_CAN_AUTOHIDE=1
 endif
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -484,24 +484,26 @@
       </popupnotificationcontent>
     </popupnotification>
 
   </popupset>
 
 #ifdef CAN_DRAW_IN_TITLEBAR
 <vbox id="titlebar">
   <hbox id="titlebar-content">
+#ifdef MENUBAR_CAN_AUTOHIDE
     <hbox id="appmenu-button-container">
       <button id="appmenu-button"
               type="menu"
               label="&brandShortName;"
               style="-moz-user-focus: ignore;">
 #include browser-appmenu.inc
       </button>
     </hbox>
+#endif
     <spacer id="titlebar-spacer" flex="1"/>
     <hbox id="titlebar-buttonbox-container" align="start">
       <hbox id="titlebar-buttonbox">
         <toolbarbutton class="titlebar-button" id="titlebar-min" oncommand="window.minimize();"/>
         <toolbarbutton class="titlebar-button" id="titlebar-max" oncommand="onTitlebarMaxClick();"/>
         <toolbarbutton class="titlebar-button" id="titlebar-close" command="cmd_closeWindow"/>
       </hbox>
     </hbox>
--- a/browser/base/content/test/social/browser_social_chatwindow.js
+++ b/browser/base/content/test/social/browser_social_chatwindow.js
@@ -451,28 +451,27 @@ var tests = {
       let url = chatUrl + "?" + (chatId++);
       port.postMessage({topic: "test-worker-chat", data: url});
     }
 
     // open a chat (it will open in the main window)
     ok(!window.SocialChatBar.hasChats, "first window should start with no chats");
     openChat(function() {
       ok(window.SocialChatBar.hasChats, "first window has the chat");
-      // create a second window - although this will be the "most recent",
-      // the fact the first window has a chat open means the first will be targetted.
+      // create a second window - this will be the "most recent" and will
+      // therefore be the window that hosts the new chat (see bug 835111)
       let secondWindow = OpenBrowserWindow();
       secondWindow.addEventListener("load", function loadListener() {
         secondWindow.removeEventListener("load", loadListener);
         ok(!secondWindow.SocialChatBar.hasChats, "second window has no chats");
         openChat(function() {
-          ok(!secondWindow.SocialChatBar.hasChats, "second window still has no chats");
-          is(window.SocialChatBar.chatbar.childElementCount, 2, "first window now has 2 chats");
+          ok(secondWindow.SocialChatBar.hasChats, "second window now has chats");
+          is(window.SocialChatBar.chatbar.childElementCount, 1, "first window still has 1 chat");
           window.SocialChatBar.chatbar.removeAll();
-          // now open another chat - it should open in the second window (as
-          // it is the "most recent" and no other windows have chats)
+          // now open another chat - it should still open in the second.
           openChat(function() {
             ok(!window.SocialChatBar.hasChats, "first window has no chats");
             ok(secondWindow.SocialChatBar.hasChats, "second window has a chat");
             secondWindow.close();
             next();
           });
         });
       })
--- a/browser/components/downloads/content/allDownloadsViewOverlay.js
+++ b/browser/components/downloads/content/allDownloadsViewOverlay.js
@@ -1032,18 +1032,18 @@ DownloadsPlacesView.prototype = {
       this._removeElement(shell.element);
       shells.delete(shell);
       if (shells.size == 0)
         this._downloadElementsShellsForURI.delete(aDataItem.uri);
     }
     else {
       shell.dataItem = null;
       // Move it below the session-download items;
-      if (this._lastSessionDownloadElement == shell.dataItem) {
-        this._lastSessionDownloadElement = shell.dataItem.previousSibling;
+      if (this._lastSessionDownloadElement == shell.element) {
+        this._lastSessionDownloadElement = shell.element.previousSibling;
       }
       else {
         let before = this._lastSessionDownloadElement ?
           this._lastSessionDownloadElement.nextSibling : this._richlistbox.firstChild;
         this._richlistbox.insertBefore(shell.element, before);
       }
     }
   },
--- a/browser/config/mozconfigs/linux32/debug-asan
+++ b/browser/config/mozconfigs/linux32/debug-asan
@@ -9,9 +9,12 @@ ac_add_options --enable-valgrind
 . $topsrcdir/build/unix/mozconfig.asan
 
 # Avoid dependency on libstdc++ 4.5
 ac_add_options --enable-stdcxx-compat
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
+# Need this to prevent name conflicts with the normal nightly build packages
+export MOZ_PKG_SPECIAL=asan
+
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux32/nightly-asan
+++ b/browser/config/mozconfigs/linux32/nightly-asan
@@ -11,9 +11,12 @@ ac_add_options --enable-codesighs
 . $topsrcdir/build/unix/mozconfig.asan
 
 # Avoid dependency on libstdc++ 4.5
 ac_add_options --enable-stdcxx-compat
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
+# Need this to prevent name conflicts with the normal nightly build packages
+export MOZ_PKG_SPECIAL=asan
+
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux64/debug-asan
+++ b/browser/config/mozconfigs/linux64/debug-asan
@@ -9,9 +9,12 @@ ac_add_options --enable-valgrind
 . $topsrcdir/build/unix/mozconfig.asan
 
 # Avoid dependency on libstdc++ 4.5
 ac_add_options --enable-stdcxx-compat
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
+# Need this to prevent name conflicts with the normal nightly build packages
+export MOZ_PKG_SPECIAL=asan
+
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux64/nightly-asan
+++ b/browser/config/mozconfigs/linux64/nightly-asan
@@ -11,9 +11,12 @@ ac_add_options --enable-codesighs
 . $topsrcdir/build/unix/mozconfig.asan
 
 # Avoid dependency on libstdc++ 4.5
 ac_add_options --enable-stdcxx-compat
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
+# Need this to prevent name conflicts with the normal nightly build packages
+export MOZ_PKG_SPECIAL=asan
+
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/macosx64/debug-asan
+++ b/browser/config/mozconfigs/macosx64/debug-asan
@@ -5,9 +5,12 @@ ac_add_options --enable-debug
 ac_add_options --enable-optimize="-O1"
 ac_add_options --enable-accessibility
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 ac_add_options --with-macbundlename-prefix=Firefox
 
+# Need this to prevent name conflicts with the normal nightly build packages
+export MOZ_PKG_SPECIAL=asan
+
 . "$topsrcdir/build/mozconfig.common.override"
--- 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.7.82
+Current extension version is: 0.7.180
 
--- a/browser/extensions/pdfjs/components/PdfStreamConverter.js
+++ b/browser/extensions/pdfjs/components/PdfStreamConverter.js
@@ -196,19 +196,20 @@ PdfDataListener.prototype = {
     }
     if (this.errorCode) {
       value(null, this.errorCode);
     }
   }
 };
 
 // All the priviledged actions.
-function ChromeActions(domWindow, dataListener) {
+function ChromeActions(domWindow, dataListener, contentDispositionFilename) {
   this.domWindow = domWindow;
   this.dataListener = dataListener;
+  this.contentDispositionFilename = contentDispositionFilename;
 }
 
 ChromeActions.prototype = {
   isInPrivateBrowsing: function() {
     let docIsPrivate;
     try {
       docIsPrivate = PrivateBrowsingUtils.isWindowPrivate(this.domWindow);
     } catch (x) {
@@ -227,16 +228,17 @@ ChromeActions.prototype = {
     }
     // caching the result
     this.isInPrivateBrowsing = function isInPrivateBrowsingCached() {
       return docIsPrivate;
     };
     return docIsPrivate;
   },
   download: function(data, sendResponse) {
+    var self = this;
     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);
     var blobUri = data.blobUrl ? NetUtil.newURI(data.blobUrl) : originalUri;
     var extHelperAppSvc =
           Cc['@mozilla.org/uriloader/external-helper-app-service;1'].
              getService(Ci.nsIExternalHelperAppService);
@@ -254,19 +256,23 @@ ChromeActions.prototype = {
         if (sendResponse)
           sendResponse(true);
         return;
       }
       // Create a nsIInputStreamChannel so we can set the url on the channel
       // so the filename will be correct.
       let channel = Cc['@mozilla.org/network/input-stream-channel;1'].
                        createInstance(Ci.nsIInputStreamChannel);
+      channel.QueryInterface(Ci.nsIChannel);
+      channel.contentDisposition = Ci.nsIChannel.DISPOSITION_ATTACHMENT;
+      if (self.contentDispositionFilename) {
+        channel.contentDispositionFilename = self.contentDispositionFilename;
+      }
       channel.setURI(originalUri);
       channel.contentStream = aInputStream;
-      channel.QueryInterface(Ci.nsIChannel);
       if ('nsIPrivateBrowsingChannel' in Ci &&
           channel instanceof Ci.nsIPrivateBrowsingChannel) {
         channel.setPrivate(docIsPrivate);
       }
 
       var listener = {
         extListener: null,
         onStartRequest: function(aRequest, aContext) {
@@ -578,16 +584,20 @@ PdfStreamConverter.prototype = {
 
   // nsIRequestObserver::onStartRequest
   onStartRequest: function(aRequest, aContext) {
     // Setup the request so we can use it below.
     aRequest.QueryInterface(Ci.nsIChannel);
     // Creating storage for PDF data
     var contentLength = aRequest.contentLength;
     var dataListener = new PdfDataListener(contentLength);
+    var contentDispositionFilename;
+    try {
+      contentDispositionFilename = aRequest.contentDispositionFilename;
+    } catch (e) {}
     this.dataListener = dataListener;
     this.binaryStream = Cc['@mozilla.org/binaryinputstream;1']
                         .createInstance(Ci.nsIBinaryInputStream);
 
     // Change the content type so we don't get stuck in a loop.
     aRequest.contentType = 'text/html';
 
     // Create a new channel that is viewer loaded as a resource.
@@ -608,17 +618,18 @@ PdfStreamConverter.prototype = {
         listener.onDataAvailable(aRequest, context, inputStream, offset, count);
       },
       onStopRequest: function(request, context, statusCode) {
         // We get the DOM window here instead of before the request since it
         // may have changed during a redirect.
         var domWindow = getDOMWindow(channel);
         // Double check the url is still the correct one.
         if (domWindow.document.documentURIObject.equals(aRequest.URI)) {
-          let actions = new ChromeActions(domWindow, dataListener);
+          let actions = new ChromeActions(domWindow, dataListener,
+                                          contentDispositionFilename);
           let requestListener = new RequestListener(actions);
           domWindow.addEventListener(PDFJS_EVENT_ID, function(event) {
             requestListener.receive(event);
           }, false, true);
           if (actions.supportsIntegratedFind()) {
             var chromeWindow = getChromeWindow(domWindow);
             var findEventManager = new FindEventManager(chromeWindow.gFindBar,
                                                         domWindow,
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -11,18 +11,18 @@
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 var PDFJS = {};
-PDFJS.version = '0.7.82';
-PDFJS.build = 'd467790';
+PDFJS.version = '0.7.180';
+PDFJS.build = '3699c31';
 
 (function pdfjsWrapper() {
   // Use strict in our context only - users might not want it
   'use strict';
 
 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
 /* Copyright 2012 Mozilla Foundation
@@ -34,16 +34,20 @@ PDFJS.build = 'd467790';
  *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+/* globals assertWellFormed, calculateMD5, Catalog, error, info, isArray,
+           isArrayBuffer, isDict, isName, isStream, isString, Lexer,
+           Linearization, NullStream, PartialEvaluator, shadow, Stream,
+           StreamsSequenceStream, stringToPDFString, TODO, Util, warn, XRef */
 
 'use strict';
 
 var globalScope = (typeof window === 'undefined') ? this : window;
 
 var isWorker = (typeof window == 'undefined');
 
 var ERRORS = 0, WARNINGS = 1, INFOS = 5;
@@ -92,17 +96,17 @@ function getPdf(arg, callback) {
   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);
@@ -172,17 +176,17 @@ var Page = (function PageClosure() {
       return shadow(this, 'view', cropBox);
     },
     get annotations() {
       return shadow(this, 'annotations', this.inheritPageProp('Annots'));
     },
     get rotate() {
       var rotate = this.inheritPageProp('Rotate') || 0;
       // Normalize rotation so it's a multiple of 90 and between 0 and 270
-      if (rotate % 90 != 0) {
+      if (rotate % 90 !== 0) {
         rotate = 0;
       } else if (rotate >= 360) {
         rotate = rotate % 360;
       } else if (rotate < 0) {
         // The spec doesn't cover negatives, assume its counterclockwise
         // rotation. The following is the other implementation of modulo.
         rotate = ((rotate % 360) + 360) % 360;
       }
@@ -551,17 +555,18 @@ 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 docInfo = {
-        PDFFormatVersion: this.pdfFormatVersion
+        PDFFormatVersion: this.pdfFormatVersion,
+        IsAcroFormPresent: !!this.acroForm
       };
       if (this.xref.trailer.has('Info')) {
         var infoDict = this.xref.trailer.get('Info');
 
         var validEntries = DocumentInfoValidators.entries;
         // Only fill the document info with valid entries from the spec.
         for (var key in validEntries) {
           if (infoDict.has(key)) {
@@ -762,16 +767,28 @@ var InvalidPDFException = (function Inva
   }
 
   InvalidPDFException.prototype = new Error();
   InvalidPDFException.constructor = InvalidPDFException;
 
   return InvalidPDFException;
 })();
 
+var MissingPDFException = (function MissingPDFExceptionClosure() {
+  function MissingPDFException(msg) {
+    this.name = 'MissingPDFException';
+    this.message = msg;
+  }
+
+  MissingPDFException.prototype = new Error();
+  MissingPDFException.constructor = MissingPDFException;
+
+  return MissingPDFException;
+})();
+
 function bytesToString(bytes) {
   var str = '';
   var length = bytes.length;
   for (var n = 0; n < length; ++n)
     str += String.fromCharCode(bytes[n]);
   return str;
 }
 
@@ -828,42 +845,42 @@ var Util = PDFJS.Util = (function UtilCl
   // M is assumed to be serialized as [a,b,c,d,e,f,g,h,i],
   // with v as [X,Y,Z]
   Util.apply3dTransform = function Util_apply3dTransform(m, v) {
     return [
       m[0] * v[0] + m[1] * v[1] + m[2] * v[2],
       m[3] * v[0] + m[4] * v[1] + m[5] * v[2],
       m[6] * v[0] + m[7] * v[1] + m[8] * v[2]
     ];
-  }
+  };
 
   // Normalize rectangle rect=[x1, y1, x2, y2] so that (x1,y1) < (x2,y2)
   // For coordinate systems whose origin lies in the bottom-left, this
   // means normalization to (BL,TR) ordering. For systems with origin in the
   // top-left, this means (TL,BR) ordering.
   Util.normalizeRect = function Util_normalizeRect(rect) {
     var r = rect.slice(0); // clone rect
     if (rect[0] > rect[2]) {
       r[0] = rect[2];
       r[2] = rect[0];
     }
     if (rect[1] > rect[3]) {
       r[1] = rect[3];
       r[3] = rect[1];
     }
     return r;
-  }
+  };
 
   // Returns a rectangle [x1, y1, x2, y2] corresponding to the
   // intersection of rect1 and rect2. If no intersection, returns 'false'
   // The rectangle coordinates of rect1, rect2 should be [x1, y1, x2, y2]
   Util.intersect = function Util_intersect(rect1, rect2) {
     function compare(a, b) {
       return a - b;
-    };
+    }
 
     // Order points along the axes
     var orderedX = [rect1[0], rect1[2], rect2[0], rect2[2]].sort(compare),
         orderedY = [rect1[1], rect1[3], rect2[1], rect2[3]].sort(compare),
         result = [];
 
     rect1 = Util.normalizeRect(rect1);
     rect2 = Util.normalizeRect(rect2);
@@ -913,25 +930,25 @@ var PageViewport = PDFJS.PageViewport = 
       case -270:
       case 90:
         rotateA = 0; rotateB = 1; rotateC = 1; rotateD = 0;
         break;
       case -90:
       case 270:
         rotateA = 0; rotateB = -1; rotateC = -1; rotateD = 0;
         break;
-      case 360:
-      case 0:
+      //case 360:
+      //case 0:
       default:
         rotateA = 1; rotateB = 0; rotateC = 0; rotateD = -1;
         break;
     }
     var offsetCanvasX, offsetCanvasY;
     var width, height;
-    if (rotateA == 0) {
+    if (rotateA === 0) {
       offsetCanvasX = Math.abs(centerY - viewBox[1]) * scale + offsetX;
       offsetCanvasY = Math.abs(centerX - viewBox[0]) * scale + offsetY;
       width = Math.abs(viewBox[3] - viewBox[1]) * scale;
       height = Math.abs(viewBox[2] - viewBox[0]) * scale;
     } else {
       offsetCanvasX = Math.abs(centerX - viewBox[0]) * scale + offsetX;
       offsetCanvasY = Math.abs(centerY - viewBox[1]) * scale + offsetY;
       width = Math.abs(viewBox[2] - viewBox[0]) * scale;
@@ -1043,21 +1060,23 @@ function isDict(v, type) {
   return isName(dictType) && dictType.name == type;
 }
 
 function isArray(v) {
   return v instanceof Array;
 }
 
 function isStream(v) {
-  return typeof v == 'object' && v != null && ('getChar' in v);
+  return typeof v == 'object' && v !== null && v !== undefined &&
+         ('getChar' in v);
 }
 
 function isArrayBuffer(v) {
-  return typeof v == 'object' && v != null && ('byteLength' in v);
+  return typeof v == 'object' && v !== null && v !== undefined &&
+         ('byteLength' in v);
 }
 
 function isRef(v) {
   return v instanceof Ref;
 }
 
 function isPDFFunction(v) {
   var fnDict;
@@ -1090,29 +1109,30 @@ var Promise = PDFJS.Promise = (function 
   /**
    * If `data` is passed in this constructor, the promise is created resolved.
    * If there isn't data, it isn't resolved at the beginning.
    */
   function Promise(name, data) {
     this.name = name;
     this.isRejected = false;
     this.error = null;
+    this.exception = null;
     // If you build a promise and pass in some data it's already resolved.
-    if (data != null) {
+    if (data !== null && data !== undefined) {
       this.isResolved = true;
       this._data = data;
       this.hasData = true;
     } else {
       this.isResolved = false;
       this._data = EMPTY_PROMISE;
     }
     this.callbacks = [];
     this.errbacks = [];
     this.progressbacks = [];
-  };
+  }
   /**
    * Builds a promise that is resolved when all the passed in promises are
    * resolved.
    * @param {Promise[]} promises Array of promises to wait for.
    * @return {Promise} New dependant promise.
    */
   Promise.all = function Promise_all(promises) {
     var deferred = new Promise();
@@ -1198,16 +1218,17 @@ var Promise = PDFJS.Promise = (function 
         error('A Promise can be rejected only once ' + this.name);
       }
       if (this.isResolved) {
         error('The Promise was already resolved ' + this.name);
       }
 
       this.isRejected = true;
       this.error = reason || null;
+      this.exception = exception || null;
       var errbacks = this.errbacks;
 
       for (var i = 0, ii = errbacks.length; i < ii; i++) {
         errbacks[i].call(null, reason, exception);
       }
     },
 
     then: function Promise_then(callback, errback, progressback) {
@@ -1216,17 +1237,18 @@ var Promise = PDFJS.Promise = (function 
       }
 
       // If the promise is already resolved, call the callback directly.
       if (this.isResolved) {
         var data = this.data;
         callback.call(null, data);
       } else if (this.isRejected && errback) {
         var error = this.error;
-        errback.call(null, error);
+        var exception = this.exception;
+        errback.call(null, error, exception);
       } else {
         this.callbacks.push(callback);
         if (errback)
           this.errbacks.push(errback);
       }
 
       if (progressback)
         this.progressbacks.push(progressback);
@@ -1559,29 +1581,29 @@ var PDFPageProxy = (function PDFPageProx
           delete self.operatorList;
           self.objs.clear();
         }
 
         if (error)
           promise.reject(error);
         else
           promise.resolve();
-      };
+      }
       var continueCallback = params.continueCallback;
 
       // Once the operatorList and fonts are loaded, do the actual rendering.
       this.displayReadyPromise.then(
         function pageDisplayReadyPromise() {
           if (self.destroyed) {
             complete();
             return;
           }
 
           var gfx = new CanvasGraphics(params.canvasContext, this.commonObjs,
-            this.objs, params.textLayer);
+            this.objs, !this.pageInfo.disableTextLayer && params.textLayer);
           try {
             this.display(gfx, params.viewport, complete, continueCallback);
           } catch (e) {
             complete(e);
           }
         }.bind(this),
         function pageDisplayReadPromiseError(reason) {
           complete(reason);
@@ -1660,17 +1682,17 @@ var PDFPageProxy = (function PDFPageProx
           globalScope['StepperManager'].enabled) {
         stepper = globalScope['StepperManager'].create(this.pageNumber - 1);
         stepper.init(operatorList);
         stepper.nextBreakPoint = stepper.getNextBreakPoint();
       }
 
       var continueWrapper;
       if (continueCallback)
-        continueWrapper = function() { continueCallback(next); }
+        continueWrapper = function() { continueCallback(next); };
       else
         continueWrapper = next;
 
       var self = this;
       function next() {
         startIdx = gfx.executeOperatorList(operatorList, startIdx,
                                            continueWrapper, stepper);
         if (startIdx == length) {
@@ -1824,16 +1846,20 @@ var WorkerTransport = (function WorkerTr
       messageHandler.on('IncorrectPassword', function transportBadPass(data) {
         this.workerReadyPromise.reject(data.exception.message, data.exception);
       }, this);
 
       messageHandler.on('InvalidPDF', function transportInvalidPDF(data) {
         this.workerReadyPromise.reject(data.exception.name, data.exception);
       }, this);
 
+      messageHandler.on('MissingPDF', function transportMissingPDF(data) {
+        this.workerReadyPromise.reject(data.exception.message, data.exception);
+      }, this);
+
       messageHandler.on('UnknownError', function transportUnknownError(data) {
         this.workerReadyPromise.reject(data.exception.message, data.exception);
       }, this);
 
       messageHandler.on('GetPage', function transportPage(data) {
         var pageInfo = data.pageInfo;
         var page = new PDFPageProxy(pageInfo, this);
         this.pageCache[pageInfo.pageIndex] = page;
@@ -2017,24 +2043,26 @@ function createScratchCanvas(width, heig
   canvas.height = height;
   return canvas;
 }
 
 function addContextCurrentTransform(ctx) {
   // If the context doesn't expose a `mozCurrentTransform`, add a JS based on.
   if (!ctx.mozCurrentTransform) {
     // Store the original context
+    ctx._scaleX = ctx._scaleX || 1.0;
+    ctx._scaleY = ctx._scaleY || 1.0;
     ctx._originalSave = ctx.save;
     ctx._originalRestore = ctx.restore;
     ctx._originalRotate = ctx.rotate;
     ctx._originalScale = ctx.scale;
     ctx._originalTranslate = ctx.translate;
     ctx._originalTransform = ctx.transform;
 
-    ctx._transformMatrix = [1, 0, 0, 1, 0, 0];
+    ctx._transformMatrix = [ctx._scaleX, 0, 0, ctx._scaleY, 0, 0];
     ctx._transformStack = [];
 
     Object.defineProperty(ctx, 'mozCurrentTransform', {
       get: function getCurrentTransform() {
         return this._transformMatrix;
       }
     });
 
@@ -2207,17 +2235,17 @@ var CanvasGraphics = (function CanvasGra
     var bufferPos = 3; // alpha component offset
     for (i = 0; i < height; i++) {
       mask = 0;
       for (j = 0; j < width; j++) {
         if (!mask) {
           buf = imgArray[imgArrayPos++];
           mask = 128;
         }
-        if (!(buf & mask) == inverseDecode) {
+        if (!(buf & mask) === inverseDecode) {
           buffer[bufferPos] = 0;
         }
         bufferPos += 4;
         mask >>= 1;
       }
     }
   }
 
@@ -2901,17 +2929,17 @@ var CanvasGraphics = (function CanvasGra
         ctx.restore();
       } else {
         ctx.save();
         this.applyTextTransforms();
 
         var lineWidth = current.lineWidth;
         var a1 = current.textMatrix[0], b1 = current.textMatrix[1];
         var scale = Math.sqrt(a1 * a1 + b1 * b1);
-        if (scale == 0 || lineWidth == 0)
+        if (scale === 0 || lineWidth === 0)
           lineWidth = this.getSinglePixelWidth();
         else
           lineWidth /= scale;
 
         if (textSelection)
           geom = this.createTextGeometry();
 
         if (fontSizeScale != 1.0) {
@@ -3516,17 +3544,17 @@ var Dict = (function DictClosure() {
       return key in map;
     };
 
     this.forEach = function Dict_forEach(callback) {
       for (var key in map) {
         callback(key, this.get(key));
       }
     };
-  };
+  }
 
   return Dict;
 })();
 
 var Ref = (function RefClosure() {
   function Ref(num, gen) {
     this.num = num;
     this.gen = gen;
@@ -3858,17 +3886,17 @@ var XRef = (function XRefClosure() {
             !isInt(generationFieldWidth)) {
           error('Invalid XRef entry fields length: ' + first + ', ' + n);
         }
         for (i = 0; i < n; ++i) {
           var type = 0, offset = 0, generation = 0;
           for (j = 0; j < typeFieldWidth; ++j)
             type = (type << 8) | stream.getByte();
           // if type field is absent, its default value = 1
-          if (typeFieldWidth == 0)
+          if (typeFieldWidth === 0)
             type = 1;
           for (j = 0; j < offsetFieldWidth; ++j)
             offset = (offset << 8) | stream.getByte();
           for (j = 0; j < generationFieldWidth; ++j)
             generation = (generation << 8) | stream.getByte();
           var entry = {};
           entry.offset = offset;
           entry.gen = generation;
@@ -4094,17 +4122,17 @@ var XRef = (function XRefClosure() {
         var obj3 = parser.getObj();
         if (!isInt(obj1) || obj1 != num ||
             !isInt(obj2) || obj2 != gen ||
             !isCmd(obj3)) {
           error('bad XRef entry');
         }
         if (!isCmd(obj3, 'obj')) {
           // some bad pdfs use "obj1234" and really mean 1234
-          if (obj3.cmd.indexOf('obj') == 0) {
+          if (obj3.cmd.indexOf('obj') === 0) {
             num = parseInt(obj3.cmd.substring(3), 10);
             if (!isNaN(num))
               return num;
           }
           error('bad XRef entry');
         }
         if (this.encrypt && !suppressEncryption) {
           try {
@@ -4348,17 +4376,17 @@ var PDFFunction = (function PDFFunctionC
       var type = IR[0];
       switch (type) {
         case CONSTRUCT_SAMPLED:
           return this.constructSampledFromIR(IR);
         case CONSTRUCT_INTERPOLATED:
           return this.constructInterpolatedFromIR(IR);
         case CONSTRUCT_STICHED:
           return this.constructStichedFromIR(IR);
-        case CONSTRUCT_POSTSCRIPT:
+        //case CONSTRUCT_POSTSCRIPT:
         default:
           return this.constructPostScriptFromIR(IR);
       }
     },
 
     parse: function PDFFunction_parse(xref, fn) {
       var IR = this.getIR(xref, fn);
       return this.fromIR(IR);
@@ -4501,17 +4529,17 @@ var PDFFunction = (function PDFFunctionC
           //                    Decode_2j, Decode_2j+1)
           rj = interpolate(rj, 0, 1, decode[j][0], decode[j][1]);
 
           // y_j = min(max(r_j, range_2j), range_2j+1)
           y[j] = Math.min(Math.max(rj, range[j][0]), range[j][1]);
         }
 
         return y;
-      }
+      };
     },
 
     constructInterpolated: function PDFFunction_constructInterpolated(str,
                                                                       dict) {
       var c0 = dict.get('C0') || [0];
       var c1 = dict.get('C1') || [1];
       var n = dict.get('N');
 
@@ -4538,17 +4566,17 @@ var PDFFunction = (function PDFFunctionC
         var x = n == 1 ? args[0] : Math.pow(args[0], n);
 
         var out = [];
         for (var j = 0; j < length; ++j)
           out.push(c0[j] + (x * diff[j]));
 
         return out;
 
-      }
+      };
     },
 
     constructStiched: function PDFFunction_constructStiched(fn, dict, xref) {
       var domain = dict.get('Domain');
 
       if (!domain)
         error('No domain');
 
@@ -5273,17 +5301,18 @@ var CIDToUnicodeMaps = {
     [700, 8217], 92, [699, 8216], 124, [126, 8764], {f: 3, c: 161}, 8260, 402,
     0, 164, 8220, 171, {f: 2, c: 8249}, {f: 2, c: 64257}, [8210, 8211], 0, 0,
     [183, 8729], 0, 8226, 8218, 8222, 8221, 187, 0, 0, 191, {f: 2, c: 769},
     [175, 772], {f: 3, c: 774}, 778, [184, 807], 779, 808, 780, [822, 8212],
     198, 170, 321, 216, 338, 186, 230, 305, 322, 248, 339, 223, 173, 169, 172,
     174, 0, 0, {f: 2, c: 178}, 181, 185, {f: 3, c: 188}, {f: 6, c: 192},
     {f: 16, c: 199}, 0, {f: 6, c: 217}, {f: 6, c: 224}, {f: 16, c: 231}, 0,
     {f: 7, c: 249}, 352, 376, 381, [773, 8254], 353, 8482, 382, 0, 8194,
-    {s: 91}, 65512, {s: 3}, {f: 63, c: 65377}, {s: 243}, [8195, 12288],
+    {f: 59, c: 33}, 165, {f: 31, c: 93}, 65512, {f: 2, c: 125}, 0,
+    {f: 63, c: 65377}, {s: 243}, [8195, 12288],
     {f: 2, c: 12289}, 65292, 65294, 12539, {f: 2, c: 65306}, 65311, 65281,
     {f: 2, c: 12443}, 180, 65344, 168, 65342, 65507, 65343, {f: 2, c: 12541},
     {f: 2, c: 12445}, 12291, 20189, {f: 3, c: 12293}, 12540, 8213, 8208, 65295,
     65340, [12316, 65374], 8214, 65372, 8230, 8229, {s: 4}, {f: 2, c: 65288},
     {f: 2, c: 12308}, 65339, 65341, 65371, 65373, {f: 10, c: 12296}, 65291,
     [8722, 65293], 177, 215, 247, 65309, 8800, 65308, 65310, {f: 2, c: 8806},
     8734, 8756, 9794, 9792, 176, {f: 2, c: 8242}, 8451, 65509, 65284,
     {f: 2, c: 65504}, 65285, 65283, 65286, 65290, 65312, 167, 9734, 9733, 9675,
@@ -6025,19 +6054,19 @@ var CIDToUnicodeMaps = {
     64035, 36559, 0, 64037, 36967, 37086, 64038, 37141, 37159, 37338, 37335,
     37342, {f: 2, c: 37357}, {f: 2, c: 37348}, 37382, 37392, 37386, 37434,
     37440, 37436, 37454, 37465, 37457, 37433, 37479, 37543, {f: 2, c: 37495},
     37607, 37591, 37593, 37584, 64039, 37589, 37600, 37587, 37669, 37665,
     37627, 64040, 37662, 37631, 37661, 37634, 37744, 37719, 37796, 37830,
     37854, 37880, 37937, 37957, 37960, 38290, 0, 64041, 38557, 38575, 38707,
     38715, 38723, 38733, 38735, [12205, 38737], 0, 38999, 39013,
     {f: 2, c: 64042}, 39207, 64044, 39326, 39502, 39641, 39644, 39797, 39794,
-    39823, 39857, 39867, 39936, 40304, 40299, 64045, 40473, 40657, {s: 636},
-    8364, 8486, 0, 0, 64256, {f: 2, c: 64259}, 257, 299, 363, 275, 333, 256,
-    298, 362, 274, 332, {f: 4, c: 8539}, {f: 2, c: 8531}, 8304,
+    39823, 39857, 39867, 39936, 40304, 40299, 64045, 40473, 40657, 0, 92,
+    {s: 634}, 8364, 8486, 0, 0, 64256, {f: 2, c: 64259}, 257, 299, 363, 275,
+    333, 256, 298, 362, 274, 332, {f: 4, c: 8539}, {f: 2, c: 8531}, 8304,
     {f: 6, c: 8308}, {f: 10, c: 8320}, 461, 282, 0, 7868, 463, 0, 296, 465, 0,
     467, 366, 360, 462, 283, 0, 7869, 464, 0, 297, 466, 0, 468, 367, 361, 593,
     8049, 8048, 509, 0, 596, 0, 0, 601, 0, 0, 602, 0, 0, 603, 8051, 8050, 0,
     331, 629, 652, 0, 0, 658, 643, 720, {s: 682}, {f: 10, c: 12832}, {s: 108},
     {f: 4, c: 12892}, {f: 15, c: 12977}, {s: 50}, {f: 26, c: 9424},
     {f: 26, c: 9398}, {s: 48}, {f: 47, c: 13008}, 0, {f: 10, c: 12928}, 12944,
     {f: 6, c: 12938}, 0, 12959, {s: 6}, {f: 2, c: 12960}, 12955, 12954, 12963,
     12962, 12951, 0, 12956, 12949, {s: 6}, 9676, {s: 11}, 10111,
@@ -12415,17 +12444,17 @@ var ColorSpace = (function ColorSpaceClo
     if (!decode)
       return true;
 
     if (n * 2 !== decode.length) {
       warn('The decode map is not the correct length');
       return true;
     }
     for (var i = 0, ii = decode.length; i < ii; i += 2) {
-      if (decode[i] != 0 || decode[i + 1] != 1)
+      if (decode[i] !== 0 || decode[i + 1] != 1)
         return false;
     }
     return true;
   };
 
   return ColorSpace;
 })();
 
@@ -13463,17 +13492,17 @@ var LabCS = (function LabCSClosure() {
 
     if (this.amin > this.amax || this.bmin > this.bmax) {
       info('Invalid Range, falling back to defaults');
       this.amin = -100;
       this.amax = 100;
       this.bmin = -100;
       this.bmax = 100;
     }
-  };
+  }
 
   // Function g(x) from spec
   function fn_g(x) {
     if (x >= 6 / 29)
       return x * x * x;
     else
       return (108 / 841) * (x - 4 / 29);
   }
@@ -13897,17 +13926,17 @@ var AES128Cipher = (function AES128Ciphe
       result.push(plain);
       buffer = new Uint8Array(16);
       bufferLength = 0;
     }
     // saving incomplete buffer
     this.buffer = buffer;
     this.bufferLength = bufferLength;
     this.iv = iv;
-    if (result.length == 0)
+    if (result.length === 0)
       return new Uint8Array([]);
     if (result.length == 1)
       return result[0];
     // combining plain text blocks into one
     var output = new Uint8Array(16 * result.length);
     for (i = 0, j = 0, ii = result.length; i < ii; ++i, j += 16)
       output.set(result[i], j);
     return output;
@@ -14077,22 +14106,23 @@ var CipherTransformFactory = (function C
     this.dict = dict;
     var algorithm = dict.get('V');
     if (!isInt(algorithm) ||
       (algorithm != 1 && algorithm != 2 && algorithm != 4))
       error('unsupported encryption algorithm');
     this.algorithm = algorithm;
     var keyLength = dict.get('Length') || 40;
     if (!isInt(keyLength) ||
-      keyLength < 40 || (keyLength % 8) != 0)
+      keyLength < 40 || (keyLength % 8) !== 0)
       error('invalid key length');
     // prepare keys
     var ownerPassword = stringToBytes(dict.get('O'));
     var userPassword = stringToBytes(dict.get('U'));
     var flags = dict.get('P');
+    this.disableTextLayer = !(flags & 16);
     var revision = dict.get('R');
     var encryptMetadata = algorithm == 4 &&  // meaningful when V is 4
       dict.get('EncryptMetadata') !== false; // makes true as default value
     this.encryptMetadata = encryptMetadata;
 
     var fileIdBytes = stringToBytes(fileId);
     var passwordBytes;
     if (password)
@@ -14142,17 +14172,17 @@ var CipherTransformFactory = (function C
     }
     var hash = calculateMD5(key, 0, i);
     return hash.subarray(0, Math.min(encryptionKey.length + 5, 16));
   }
 
   function buildCipherConstructor(cf, name, num, gen, key) {
     var cryptFilter = cf.get(name.name);
     var cfm;
-    if (cryptFilter != null)
+    if (cryptFilter !== null && cryptFilter !== undefined)
       cfm = cryptFilter.get('CFM');
     if (!cfm || cfm.name == 'None') {
       return function cipherTransformFactoryBuildCipherConstructorNone() {
         return new NullCipher();
       };
     }
     if ('V2' == cfm.name) {
       return function cipherTransformFactoryBuildCipherConstructorV2() {
@@ -14713,17 +14743,17 @@ var PartialEvaluator = (function Partial
               );
               args = [gsStateObj];
               break;
           } // switch
 
           fnArray.push(fn);
           argsArray.push(args);
           args = [];
-        } else if (obj != null) {
+        } else if (obj !== null && obj !== undefined) {
           args.push(obj instanceof Dict ? obj.getAll() : obj);
           assertWellFormed(args.length <= 33, 'Too many arguments');
         }
       }
 
       return queue;
     },
 
@@ -14899,17 +14929,17 @@ var PartialEvaluator = (function Partial
                     chunk += ' ';
                   }
                 }
               }
               break;
             case 'Tj':
               chunk += fontCharsToUnicode(args[0], font);
               break;
-            case "'":
+            case '\'':
               // For search, adding a extra white space for line breaks would be
               // better here, but that causes too much spaces in the
               // text-selection divs.
               chunk += fontCharsToUnicode(args[0], font);
               break;
             case '"':
               // Note comment in "'"
               chunk += fontCharsToUnicode(args[2], font);
@@ -14967,17 +14997,17 @@ var PartialEvaluator = (function Partial
 
           if (chunk !== '') {
             bidiTexts.push(PDFJS.bidi(chunk, -1));
 
             chunk = '';
           }
 
           args = [];
-        } else if (obj != null) {
+        } else if (obj !== null && obj !== undefined) {
           assertWellFormed(args.length <= 33, 'Too many arguments');
           args.push(obj);
         }
       } // while
 
       return state;
     },
 
@@ -15181,17 +15211,17 @@ var PartialEvaluator = (function Partial
     readCidToGidMap: function PartialEvaluator_readCidToGidMap(cidToGidStream) {
       // Extract the encoding from the CIDToGIDMap
       var glyphsData = cidToGidStream.getBytes();
 
       // Set encoding 0 to later verify the font has an encoding
       var result = [];
       for (var j = 0, jj = glyphsData.length; j < jj; j++) {
         var glyphID = (glyphsData[j++] << 8) | glyphsData[j];
-        if (glyphID == 0)
+        if (glyphID === 0)
           continue;
 
         var code = j >> 1;
         result[code] = glyphID;
       }
       return result;
     },
 
@@ -15201,30 +15231,26 @@ var PartialEvaluator = (function Partial
                                                    properties) {
       var glyphsWidths = [];
       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 start = 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) {
+            } else {
               var width = widths[++i];
               for (var j = start; j <= code; j++)
                 glyphsWidths[j] = width;
-              start = 0;
-            } else {
-              start = code;
             }
           }
         }
       } else {
         var firstChar = properties.firstChar;
         var widths = dict.get('Widths');
         if (widths) {
           var j = firstChar;
@@ -15384,16 +15410,23 @@ var PartialEvaluator = (function Partial
       if (isString(fontName)) {
         fontName = new Name(fontName);
       }
       if (isString(baseFont)) {
         baseFont = new Name(baseFont);
       }
 
       var fontNameStr = fontName && fontName.name;
+      // 9.7.6.1
+      if (type.name == 'CIDFontType0') {
+        var cidEncoding = baseDict.get('Encoding');
+        if (isName(cidEncoding)) {
+          fontNameStr = fontNameStr + '-' + cidEncoding.name;
+        }
+      }
       var baseFontStr = baseFont && baseFont.name;
       if (fontNameStr !== baseFontStr) {
         warn('The FontDescriptor\'s FontName is "' + fontNameStr +
             '" but should be the same as the Font\'s BaseFont "' +
             baseFontStr + '"');
       }
       fontName = fontName || baseFont;
 
@@ -15477,17 +15510,18 @@ var EvalState = (function EvalStateClosu
 var CMAP_GLYPH_OFFSET = 0xE000;
 var GLYPH_AREA_SIZE = 0x1900;
 var SYMBOLIC_FONT_GLYPH_OFFSET = 0xF000;
 
 // PDF Glyph Space Units are one Thousandth of a TextSpace Unit
 // except for Type 3 fonts
 var PDF_GLYPH_SPACE_UNITS = 1000;
 
-// Until hinting is fully supported this constant can be used
+// Hinting is currently disabled due to unknown problems on windows
+// in tracemonkey and various other pdfs with type1 fonts.
 var HINTING_ENABLED = false;
 
 var FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0];
 
 var FontFlags = {
   FixedPitch: 1,
   Serif: 2,
   Symbolic: 4,
@@ -15860,21 +15894,37 @@ var symbolsFonts = {
 var CMapConverterList = {
   'H': jis7ToUnicode,
   'V': jis7ToUnicode,
   'EUC-H': eucjpToUnicode,
   'EUC-V': eucjpToUnicode,
   '90ms-RKSJ-H': sjisToUnicode,
   '90ms-RKSJ-V': sjisToUnicode,
   '90msp-RKSJ-H': sjisToUnicode,
-  '90msp-RKSJ-V': sjisToUnicode
+  '90msp-RKSJ-V': sjisToUnicode,
+  'GBK-EUC-H': gbkToUnicode
+};
+
+// CMaps using Hankaku (Halfwidth) Latin glyphs instead of proportional one.
+// We need to distinguish them to get correct widths from CIDFont dicts.
+var HalfwidthCMaps = {
+  'H': true,
+  'V': true,
+  'EUC-H': true,
+  'EUC-V': true,
+  '90ms-RKSJ-H': true,
+  '90ms-RKSJ-V': true,
+  'UniJIS-UCS2-HW-H': true,
+  'UniJIS-UCS2-HW-V': true
 };
 
 var decodeBytes;
 if (typeof TextDecoder !== 'undefined') {
+  // The encodings supported by TextDecoder can be found at:
+  // http://encoding.spec.whatwg.org/#concept-encoding-get
   decodeBytes = function(bytes, encoding) {
     return new TextDecoder(encoding).decode(bytes);
   };
 } else if (typeof FileReaderSync !== 'undefined') {
   decodeBytes = function(bytes, encoding) {
     return new FileReaderSync().readAsText(new Blob([bytes]), encoding);
   };
 } else {
@@ -15894,16 +15944,20 @@ function jis7ToUnicode(str) {
 function eucjpToUnicode(str) {
   return decodeBytes(stringToBytes(str), 'euc-jp');
 }
 
 function sjisToUnicode(str) {
   return decodeBytes(stringToBytes(str), 'shift_jis');
 }
 
+function gbkToUnicode(str) {
+  return decodeBytes(stringToBytes(str), 'gbk');
+}
+
 // Some characters, e.g. copyrightserif, mapped to the private use area and
 // might not be displayed using standard fonts. Mapping/hacking well-known chars
 // to the similar equivalents in the normal characters range.
 function mapPrivateUseChars(code) {
   switch (code) {
     case 0xF8E9: // copyrightsans
     case 0xF6D9: // copyrightserif
       return 0x00A9; // copyright
@@ -17669,80 +17723,80 @@ var Font = (function FontClosure() {
 
     // Transfer some properties again that could change during font conversion
     this.fontMatrix = properties.fontMatrix;
     this.widths = properties.widths;
     this.defaultWidth = properties.defaultWidth;
     this.encoding = properties.baseEncoding;
 
     this.loading = true;
-  };
+  }
 
   var numFonts = 0;
   function getUniqueName() {
     return 'pdfFont' + numFonts++;
   }
 
   function stringToArray(str) {
     var array = [];
     for (var i = 0, ii = str.length; i < ii; ++i)
       array[i] = str.charCodeAt(i);
 
     return array;
-  };
+  }
 
   function arrayToString(arr) {
     var str = '';
     for (var i = 0, ii = arr.length; i < ii; ++i)
       str += String.fromCharCode(arr[i]);
 
     return str;
-  };
+  }
 
   function int16(bytes) {
     return (bytes[0] << 8) + (bytes[1] & 0xff);
-  };
+  }
 
   function int32(bytes) {
     return (bytes[0] << 24) + (bytes[1] << 16) +
            (bytes[2] << 8) + (bytes[3] & 0xff);
-  };
+  }
 
   function getMaxPower2(number) {
     var maxPower = 0;
     var value = number;
     while (value >= 2) {
       value /= 2;
       maxPower++;
     }
 
     value = 2;
     for (var i = 1; i < maxPower; i++)
       value *= 2;
     return value;
-  };
+  }
 
   function string16(value) {
     return String.fromCharCode((value >> 8) & 0xff) +
            String.fromCharCode(value & 0xff);
-  };
+  }
 
   function safeString16(value) {
     // clamp value to the 16-bit int range
     value = value > 0x7FFF ? 0x7FFF : value < -0x8000 ? -0x8000 : value;
     return String.fromCharCode((value >> 8) & 0xff) +
            String.fromCharCode(value & 0xff);
-  };
+  }
 
   function string32(value) {
     return String.fromCharCode((value >> 24) & 0xff) +
            String.fromCharCode((value >> 16) & 0xff) +
            String.fromCharCode((value >> 8) & 0xff) +
            String.fromCharCode(value & 0xff);
-  };
+  }
 
   function createOpenTypeHeader(sfnt, file, numTables) {
     // Windows hates the Mac TrueType sfnt version number
     if (sfnt == 'true')
       sfnt = string32(0x00010000);
 
     // sfnt version (4 bytes)
     var header = sfnt;
@@ -17758,17 +17812,17 @@ var Font = (function FontClosure() {
     // entrySelector (2 bytes)
     header += string16(Math.log(tablesMaxPower2) / Math.log(2));
 
     // rangeShift (2 bytes)
     header += string16(numTables * 16 - searchRange);
 
     file.file += header;
     file.virtualOffset += header.length;
-  };
+  }
 
   function createTableEntry(file, tag, data) {
     // offset
     var offset = file.virtualOffset;
 
     // length
     var length = data.length;
 
@@ -17784,17 +17838,17 @@ var Font = (function FontClosure() {
     for (var i = 0; i < n; i += 4)
       checksum = (checksum + int32([data[i], data[i + 1], data[i + 2],
                                     data[i + 3]])) | 0;
 
     var tableEntry = (tag + string32(checksum) +
                       string32(offset) + string32(length));
     file.file += tableEntry;
     file.virtualOffset += data.length;
-  };
+  }
 
   function getRanges(glyphs) {
     // Array.sort() sorts by characters, not numerically, so convert to an
     // array of characters.
     var codes = [];
     var length = glyphs.length;
     for (var n = 0; n < length; ++n)
       codes.push({ unicode: glyphs[n].unicode, code: n });
@@ -17813,19 +17867,19 @@ var Font = (function FontClosure() {
         codeIndices.push(codes[n].code);
         ++end;
         ++n;
       }
       ranges.push([start, end, codeIndices]);
     }
 
     return ranges;
-  };
-
-  function createCMapTable(glyphs, deltas) {
+  }
+
+  function createCmapTable(glyphs, deltas) {
     var ranges = getRanges(glyphs);
 
     var numTables = 1;
     var cmap = '\x00\x00' + // version
                string16(numTables) +  // numTables
                '\x00\x03' + // platformID
                '\x00\x01' + // encodingID
                string32(4 + numTables * 8); // start of the table record
@@ -17890,17 +17944,17 @@ var Font = (function FontClosure() {
                     string16(rangeShift) +
                     endCount + '\x00\x00' + startCount +
                     idDeltas + idRangeOffsets + glyphsIds;
 
     return stringToArray(cmap +
                          '\x00\x04' + // format
                          string16(format314.length + 4) + // length
                          format314);
-  };
+  }
 
   function validateOS2Table(os2) {
     var stream = new Stream(os2.data);
     var version = int16(stream.getBytes(2));
     // TODO verify all OS/2 tables fields, but currently we validate only those
     // that give us issues
     stream.getBytes(60); // skipping type, misc sizes, panose, unicode ranges
     var selection = int16(stream.getBytes(2));
@@ -18013,30 +18067,30 @@ var Font = (function FontClosure() {
            string16(winDescent) + // usWinDescent
            '\x00\x00\x00\x00' + // ulCodePageRange1 (Bits 0-31)
            '\x00\x00\x00\x00' + // ulCodePageRange2 (Bits 32-63)
            string16(properties.xHeight) + // sxHeight
            string16(properties.capHeight) + // sCapHeight
            string16(0) + // usDefaultChar
            string16(firstCharIndex || properties.firstChar) + // usBreakChar
            '\x00\x03';  // usMaxContext
-  };
+  }
 
   function createPostTable(properties) {
     var angle = Math.floor(properties.italicAngle * (Math.pow(2, 16)));
     return '\x00\x03\x00\x00' + // Version number
            string32(angle) + // italicAngle
            '\x00\x00' + // underlinePosition
            '\x00\x00' + // underlineThickness
            string32(properties.fixedPitch) + // isFixedPitch
            '\x00\x00\x00\x00' + // minMemType42
            '\x00\x00\x00\x00' + // maxMemType42
            '\x00\x00\x00\x00' + // minMemType1
            '\x00\x00\x00\x00';  // maxMemType1
-  };
+  }
 
   function createNameTable(name, proto) {
     if (!proto) {
       proto = [[], []]; // no strings and unicode strings
     }
 
     var strings = [
       proto[0][0] || 'Original licence',  // 0.Copyright
@@ -18091,16 +18145,47 @@ var Font = (function FontClosure() {
         strOffset += str.length;
       }
     }
 
     nameTable += strings.join('') + stringsUnicode.join('');
     return nameTable;
   }
 
+  // Normalize the charcodes in the cmap table into unicode values
+  // that will work with the (3, 1) cmap table we will write out.
+  function cmapCharcodeToUnicode(charcode, symbolic, platformId, encodingId) {
+    var unicode;
+    if (symbolic) {
+      // These codes will be shifted into the range
+      // SYMBOLIC_FONT_GLYPH_OFFSET to (SYMBOLIC_FONT_GLYPH_OFFSET + 0xFF)
+      // so that they are not in the control character range that could
+      // be displayed as spaces by browsers.
+      if (platformId === 3 && encodingId === 0 ||
+          platformId === 1 && encodingId === 0) {
+        unicode = SYMBOLIC_FONT_GLYPH_OFFSET | (charcode & 0xFF);
+      }
+    } else {
+      if (platformId === 3 && encodingId === 1) {
+        // A (3, 1) table is alredy unicode (Microsoft Unicode format)
+        unicode = charcode;
+      } else if (platformId === 1 && encodingId === 0) {
+        // TODO(mack): Should apply the changes to convert the
+        // MacRomanEncoding to Mac OS Roman encoding in 9.6.6.4
+        // table 115 of the pdf spec
+        var glyphName = Encodings.MacRomanEncoding[charcode];
+        if (glyphName) {
+          unicode = GlyphsUnicode[glyphName];
+        }
+      }
+    }
+    return unicode;
+  }
+
+
   Font.prototype = {
     name: null,
     font: null,
     mimetype: null,
     encoding: null,
 
     exportData: function Font_exportData() {
       var data = {};
@@ -18138,27 +18223,27 @@ var Font = (function FontClosure() {
 
         return {
           tag: tag,
           checksum: checksum,
           length: length,
           offset: offset,
           data: data
         };
-      };
+      }
 
       function readOpenTypeHeader(ttf) {
         return {
           version: arrayToString(ttf.getBytes(4)),
           numTables: int16(ttf.getBytes(2)),
           searchRange: int16(ttf.getBytes(2)),
           entrySelector: int16(ttf.getBytes(2)),
           rangeShift: int16(ttf.getBytes(2))
         };
-      };
+      }
 
       function createGlyphNameMap(glyphs, ids, properties) {
         var glyphNames = properties.glyphNames;
         if (!glyphNames) {
           properties.glyphNameMap = {};
           return;
         }
         var glyphsLength = glyphs.length;
@@ -18173,213 +18258,264 @@ var Font = (function FontClosure() {
           var code = glyphs[i].code;
           encoding[code] = glyphName;
         }
         properties.glyphNameMap = glyphNameMap;
         if (!properties.hasEncoding)
           properties.baseEncoding = encoding;
       }
 
-      function readCMapTable(cmap, font) {
+      /**
+       * Read the appropriate subtable from the cmap according to 9.6.6.4 from
+       * PDF spec
+       */
+      function readCmapTable(cmap, font, hasEncoding, isSymbolicFont) {
         var start = (font.start ? font.start : 0) + cmap.offset;
         font.pos = start;
 
         var version = int16(font.getBytes(2));
-        var numRecords = int16(font.getBytes(2));
-
-        var records = [];
-        for (var i = 0; i < numRecords; i++) {
-          records.push({
-            platformID: int16(font.getBytes(2)),
-            encodingID: int16(font.getBytes(2)),
-            offset: int32(font.getBytes(4))
-          });
-        }
-
-        // Check that table are sorted by platformID then encodingID,
-        records.sort(function fontReadCMapTableSort(a, b) {
-          return ((a.platformID << 16) + a.encodingID) -
-                 ((b.platformID << 16) + b.encodingID);
-        });
-
-        var tables = [records[0]];
-        for (var i = 1; i < numRecords; i++) {
-          // The sanitizer will drop the font if 2 tables have the same
-          // platformID and the same encodingID, this will be correct for
-          // most cases but if the font has been made for Mac it could
-          // exist a few platformID: 1, encodingID: 0 but with a different
-          // language field and that's correct. But the sanitizer does not
-          // seem to support this case.
-          var current = records[i];
-          var previous = records[i - 1];
-          if (((current.platformID << 16) + current.encodingID) <=
-             ((previous.platformID << 16) + previous.encodingID))
-                continue;
-          tables.push(current);
-        }
-
-        var missing = numRecords - tables.length;
-        if (missing) {
-          numRecords = tables.length;
-          var data = string16(version) + string16(numRecords);
-
-          for (var i = 0; i < numRecords; i++) {
-            var table = tables[i];
-            data += string16(table.platformID) +
-                    string16(table.encodingID) +
-                    string32(table.offset);
-          }
-
-          for (var i = 0, ii = data.length; i < ii; i++)
-            cmap.data[i] = data.charCodeAt(i);
-        }
-
-        for (var i = 0; i < numRecords; i++) {
-          var table = tables[i];
-          font.pos = start + table.offset;
-
-          var format = int16(font.getBytes(2));
-          var length = int16(font.getBytes(2));
-          var language = int16(font.getBytes(2));
-
-          if (format == 0) {
-            // Characters below 0x20 are controls characters that are hardcoded
-            // into the platform so if some characters in the font are assigned
-            // under this limit they will not be displayed so let's rewrite the
-            // CMap.
-            var glyphs = [];
-            var ids = [];
-            for (var j = 0; j < 256; j++) {
-              var index = font.getByte();
-              if (index) {
-                glyphs.push({ unicode: j, code: j });
-                ids.push(index);
-              }
-            }
-            return {
-              glyphs: glyphs,
-              ids: ids,
-              hasShortCmap: true
+        var numTables = int16(font.getBytes(2));
+
+        var potentialTable;
+        var foundPreferredTable;
+        // There's an order of preference in terms of which cmap subtable we
+        // want to use. So scan through them to find our preferred table.
+        for (var i = 0; i < numTables; i++) {
+          var platformId = int16(font.getBytes(2));
+          var encodingId = int16(font.getBytes(2));
+          var offset = int32(font.getBytes(4));
+          var useTable = false;
+          var canBreak = false;
+
+          // The following block implements the following from the spec:
+          //
+          //   When the font has no Encoding entry, or the font descriptor’s
+          //   Symbolic flag is set (in which case the Encoding entry
+          //   is ignored), this shall occur:
+          //      - If the font contains a (3, 0) subtable, the range of
+          //      - Otherwise, the (1, 0) subtable will be used.
+          //   Otherwise, if the font does have an encoding:
+          //      - Use the (3, 1) cmap subtable
+          //      - Otherwise, use the (1, 0) subtable if present
+          //
+          // The following diverges slightly from the above spec in order
+          // to handle the case that hasEncoding and isSymbolicFont are both
+          // true. In this, based on the ordering of the rules in the spec,
+          // my interpretation is that we should be acting as if the font is
+          // symbolic.
+          //
+          // However, in this case, the test pdf 'preistabelle.pdf'
+          // is interpreting this case as a non-symbolic font. In this case
+          // though, 'presitabelle.pdf' does contain a (3, 1) table and does
+          // not contain a (3, 0) table which indicates it is non-symbolic.
+          //
+          // Thus, I am using this heurisitic of looking at which table is
+          // found to truly determine whether or not the font is symbolic.
+          // That is, if the specific symbolic/non-symbolic font specific
+          // tables (3, 0) or (3, 1) is found, that information is used for
+          // deciding if the font is symbolic or not.
+          //
+          // TODO(mack): This section needs some more thought on whether the
+          // heuristic is good enough. For now, it passes all the regression
+          // tests.
+          if (isSymbolicFont && platformId === 3 && encodingId === 0) {
+            useTable = true;
+            canBreak = true;
+            foundPreferredTable = true;
+          } else if (hasEncoding && platformId === 3 && encodingId === 1) {
+            useTable = true;
+            canBreak = true;
+            foundPreferredTable = true;
+            // Update the isSymbolicFont based on this heuristic
+            isSymbolicFont = false;
+          } else if (platformId === 1 && encodingId === 0 &&
+              !foundPreferredTable) {
+            useTable = true;
+            foundPreferredTable = true;
+          } else if (!potentialTable) {
+            // We will use an arbitrary table if we cannot find a preferred
+            // table
+            useTable = true;
+          }
+
+          if (useTable) {
+            potentialTable = {
+              platformId: platformId,
+              encodingId: encodingId,
+              offset: offset,
+              isSymbolicFont: isSymbolicFont
             };
-          } else if (format == 4) {
-            // re-creating the table in format 4 since the encoding
-            // might be changed
-            var segCount = (int16(font.getBytes(2)) >> 1);
-            font.getBytes(6); // skipping range fields
-            var segIndex, segments = [];
-            for (segIndex = 0; segIndex < segCount; segIndex++) {
-              segments.push({ end: int16(font.getBytes(2)) });
-            }
-            font.getBytes(2);
-            for (segIndex = 0; segIndex < segCount; segIndex++) {
-              segments[segIndex].start = int16(font.getBytes(2));
-            }
-
-            for (segIndex = 0; segIndex < segCount; segIndex++) {
-              segments[segIndex].delta = int16(font.getBytes(2));
-            }
-
-            var offsetsCount = 0;
-            for (segIndex = 0; segIndex < segCount; segIndex++) {
-              var segment = segments[segIndex];
-              var rangeOffset = int16(font.getBytes(2));
-              if (!rangeOffset) {
-                segment.offsetIndex = -1;
+          }
+          if (canBreak) {
+            break;
+          }
+        }
+
+        if (!potentialTable) {
+          error('Could not find a cmap table');
+          return;
+        }
+
+        if (!foundPreferredTable) {
+          warn('Did not find a cmap of suitable format. Interpreting (' +
+               potentialTable.platformId + ', ' + potentialTable.encodingId +
+               ') as (3, 1) table');
+          potentialTable.platformId = 3;
+          potentialTable.encodingId = 1;
+        }
+
+        font.pos = start + potentialTable.offset;
+        var format = int16(font.getBytes(2));
+        var length = int16(font.getBytes(2));
+        var language = int16(font.getBytes(2));
+
+        var hasShortCmap = false;
+        var mappings = [];
+
+        // TODO(mack): refactor this cmap subtable reading logic out
+        if (format === 0) {
+          for (var j = 0; j < 256; j++) {
+            var index = font.getByte();
+            if (!index) {
+              continue;
+            }
+            mappings.push({
+              charcode: j,
+              glyphId: index
+            });
+          }
+          hasShortCmap = true;
+        } else if (format === 4) {
+          // re-creating the table in format 4 since the encoding
+          // might be changed
+          var segCount = (int16(font.getBytes(2)) >> 1);
+          font.getBytes(6); // skipping range fields
+          var segIndex, segments = [];
+          for (segIndex = 0; segIndex < segCount; segIndex++) {
+            segments.push({ end: int16(font.getBytes(2)) });
+          }
+          font.getBytes(2);
+          for (segIndex = 0; segIndex < segCount; segIndex++) {
+            segments[segIndex].start = int16(font.getBytes(2));
+          }
+
+          for (segIndex = 0; segIndex < segCount; segIndex++) {
+            segments[segIndex].delta = int16(font.getBytes(2));
+          }
+
+          var offsetsCount = 0;
+          for (segIndex = 0; segIndex < segCount; segIndex++) {
+            var segment = segments[segIndex];
+            var rangeOffset = int16(font.getBytes(2));
+            if (!rangeOffset) {
+              segment.offsetIndex = -1;
+              continue;
+            }
+
+            var offsetIndex = (rangeOffset >> 1) - (segCount - segIndex);
+            segment.offsetIndex = offsetIndex;
+            offsetsCount = Math.max(offsetsCount, offsetIndex +
+              segment.end - segment.start + 1);
+          }
+
+          var offsets = [];
+          for (var j = 0; j < offsetsCount; j++) {
+            offsets.push(int16(font.getBytes(2)));
+          }
+
+          for (segIndex = 0; segIndex < segCount; segIndex++) {
+            var segment = segments[segIndex];
+            var start = segment.start, end = segment.end;
+            var delta = segment.delta, offsetIndex = segment.offsetIndex;
+
+            for (var j = start; j <= end; j++) {
+              if (j == 0xFFFF) {
                 continue;
               }
 
-              var offsetIndex = (rangeOffset >> 1) - (segCount - segIndex);
-              segment.offsetIndex = offsetIndex;
-              offsetsCount = Math.max(offsetsCount, offsetIndex +
-                segment.end - segment.start + 1);
-            }
-
-            var offsets = [];
-            for (var j = 0; j < offsetsCount; j++)
-              offsets.push(int16(font.getBytes(2)));
-
-            var glyphs = [], ids = [];
-
-            for (segIndex = 0; segIndex < segCount; segIndex++) {
-              var segment = segments[segIndex];
-              var start = segment.start, end = segment.end;
-              var delta = segment.delta, offsetIndex = segment.offsetIndex;
-
-              for (var j = start; j <= end; j++) {
-                if (j == 0xFFFF)
-                  continue;
-
-                var glyphCode = offsetIndex < 0 ? j :
-                  offsets[offsetIndex + j - start];
-                glyphCode = (glyphCode + delta) & 0xFFFF;
-                if (glyphCode == 0)
-                  continue;
-
-                glyphs.push({ unicode: j, code: j });
-                ids.push(glyphCode);
-              }
-            }
-
-            return {
-              glyphs: glyphs,
-              ids: ids
-            };
-          } else if (format == 6) {
-            // Format 6 is a 2-bytes dense mapping, which means the font data
-            // lives glue together even if they are pretty far in the unicode
-            // table. (This looks weird, so I can have missed something), this
-            // works on Linux but seems to fails on Mac so let's rewrite the
-            // cmap table to a 3-1-4 style
-            var firstCode = int16(font.getBytes(2));
-            var entryCount = int16(font.getBytes(2));
-
-            var glyphs = [];
-            var ids = [];
-            for (var j = 0; j < entryCount; j++) {
-              var glyphCode = int16(font.getBytes(2));
-              var code = firstCode + j;
-
-              glyphs.push({ unicode: code, code: code });
-              ids.push(glyphCode);
-            }
-
-            return {
-              glyphs: glyphs,
-              ids: ids
-            };
-          }
-        }
-        error('Unsupported cmap table format');
-      };
+              var glyphId = offsetIndex < 0 ? j :
+                offsets[offsetIndex + j - start];
+              glyphId = (glyphId + delta) & 0xFFFF;
+              if (glyphId === 0) {
+                continue;
+              }
+              mappings.push({
+                charcode: j,
+                glyphId: glyphId
+              });
+            }
+          }
+        } else if (format == 6) {
+          // Format 6 is a 2-bytes dense mapping, which means the font data
+          // lives glue together even if they are pretty far in the unicode
+          // table. (This looks weird, so I can have missed something), this
+          // works on Linux but seems to fails on Mac so let's rewrite the
+          // cmap table to a 3-1-4 style
+          var firstCode = int16(font.getBytes(2));
+          var entryCount = int16(font.getBytes(2));
+
+          var glyphs = [];
+          var ids = [];
+          for (var j = 0; j < entryCount; j++) {
+            var glyphId = int16(font.getBytes(2));
+            var charcode = firstCode + j;
+
+            mappings.push({
+              charcode: charcode,
+              glyphId: glyphId
+            });
+          }
+        } else {
+          error('cmap table has unsupported format: ' + format);
+        }
+
+        return {
+          platformId: potentialTable.platformId,
+          encodingId: potentialTable.encodingId,
+          isSymbolicFont: potentialTable.isSymbolicFont,
+          mappings: mappings,
+          hasShortCmap: hasShortCmap
+        };
+      }
 
       function sanitizeMetrics(font, header, metrics, numGlyphs) {
         if (!header) {
           if (metrics) {
             metrics.data = null;
           }
           return;
         }
 
         font.pos = (font.start ? font.start : 0) + header.offset;
         font.pos += header.length - 2;
         var numOfMetrics = int16(font.getBytes(2));
 
+        if (numOfMetrics > numGlyphs) {
+          info('The numOfMetrics (' + numOfMetrics + ') should not be ' +
+               'greater than the numGlyphs (' + numGlyphs + ')');
+          // Reduce numOfMetrics if it is greater than numGlyphs
+          numOfMetrics = numGlyphs;
+          header.data[34] = (numOfMetrics & 0xff00) >> 8;
+          header.data[35] = numOfMetrics & 0x00ff;
+        }
+
         var numOfSidebearings = numGlyphs - numOfMetrics;
         var numMissing = numOfSidebearings -
-          ((hmtx.length - numOfMetrics * 4) >> 1);
+          ((metrics.length - numOfMetrics * 4) >> 1);
+
         if (numMissing > 0) {
           font.pos = (font.start ? font.start : 0) + metrics.offset;
           var entries = '';
-          for (var i = 0, ii = hmtx.length; i < ii; i++)
+          for (var i = 0, ii = metrics.length; i < ii; i++)
             entries += String.fromCharCode(font.getByte());
           for (var i = 0; i < numMissing; i++)
             entries += '\x00\x00';
           metrics.data = stringToArray(entries);
         }
-      };
+      }
 
       function sanitizeGlyph(source, sourceStart, sourceEnd, dest, destStart) {
         if (sourceEnd - sourceStart <= 12) {
           // glyph with data less than 12 is invalid one
           return 0;
         }
         var glyf = source.subarray(sourceStart, sourceEnd);
         var contoursCount = (glyf[0] << 8) | glyf[1];
@@ -18426,16 +18562,60 @@ var Font = (function FontClosure() {
           dest.set(glyf.subarray(0, glyphDataLength), destStart);
           return glyphDataLength;
         }
         // glyph data is fine
         dest.set(glyf, destStart);
         return glyf.length;
       }
 
+      function sanitizeHead(head, numGlyphs, locaLength) {
+        var data = head.data;
+
+        // Validate version:
+        // Should always be 0x00010000
+        var version = int32([data[0], data[1], data[2], data[3]]);
+        if (version >> 16 !== 1) {
+          info('Attempting to fix invalid version in head table: ' + version);
+          data[0] = 0;
+          data[1] = 1;
+          data[2] = 0;
+          data[3] = 0;
+        }
+
+        var indexToLocFormat = int16([data[50], data[51]]);
+        if (indexToLocFormat < 0 || indexToLocFormat > 1) {
+          info('Attempting to fix invalid indexToLocFormat in head table: ' +
+               indexToLocFormat);
+
+          // The value of indexToLocFormat should be 0 if the loca table
+          // consists of short offsets, and should be 1 if the loca table
+          // consists of long offsets.
+          //
+          // The number of entries in the loca table should be numGlyphs + 1.
+          //
+          // Using this information, we can work backwards to deduce if the
+          // size of each offset in the loca table, and thus figure out the
+          // appropriate value for indexToLocFormat.
+
+          var numGlyphsPlusOne = numGlyphs + 1;
+          if (locaLength === numGlyphsPlusOne << 1) {
+            // 0x0000 indicates the loca table consists of short offsets
+            data[50] = 0;
+            data[51] = 0;
+          } else if (locaLength === numGlyphsPlusOne << 2) {
+            // 0x0001 indicates the loca table consists of long offsets
+            data[50] = 0;
+            data[51] = 1;
+          } else {
+            warn('Could not fix indexToLocFormat: ' + indexToLocFormat);
+          }
+        }
+      }
+
       function sanitizeGlyphLocations(loca, glyf, numGlyphs,
                                       isGlyphLocationsLong) {
         var itemSize, itemDecode, itemEncode;
         if (isGlyphLocationsLong) {
           itemSize = 4;
           itemDecode = function fontItemDecodeLong(data, offset) {
             return (data[offset] << 24) | (data[offset + 1] << 16) |
                    (data[offset + 2] << 8) | data[offset + 3];
@@ -18475,17 +18655,17 @@ var Font = (function FontClosure() {
 
           var newLength = sanitizeGlyph(oldGlyfData, startOffset, endOffset,
                                         newGlyfData, writeOffset);
           writeOffset += newLength;
           itemEncode(locaData, j, writeOffset);
           startOffset = endOffset;
         }
 
-        if (writeOffset == 0) {
+        if (writeOffset === 0) {
           // glyf table cannot be empty -- redoing the glyf and loca tables
           // to have single glyph with one point
           var simpleGlyph = new Uint8Array(
             [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 0]);
           for (var i = 0, j = itemSize; i < numGlyphs; i++, j += itemSize)
             itemEncode(locaData, j, simpleGlyph.length);
           glyf.data = simpleGlyph;
           return;
@@ -18602,17 +18782,17 @@ var Font = (function FontClosure() {
             platform: int16(font.getBytes(2)),
             encoding: int16(font.getBytes(2)),
             language: int16(font.getBytes(2)),
             name: int16(font.getBytes(2)),
             length: int16(font.getBytes(2)),
             offset: int16(font.getBytes(2))
           };
           // using only Macintosh and Windows platform/encoding names
-          if ((r.platform == 1 && r.encoding == 0 && r.language == 0) ||
+          if ((r.platform == 1 && r.encoding === 0 && r.language === 0) ||
               (r.platform == 3 && r.encoding == 1 && r.language == 0x409)) {
             records.push(r);
           }
         }
         for (var i = 0, ii = records.length; i < ii; i++) {
           var record = records[i];
           var pos = start + stringsStart + record.offset;
           if (pos + record.length > end) {
@@ -18634,17 +18814,17 @@ var Font = (function FontClosure() {
         }
         return names;
       }
 
       function isOS2Valid(os2Table) {
         var data = os2Table.data;
         // usWinAscent == 0 makes font unreadable by windows
         var usWinAscent = (data[74] << 8) | data[75];
-        if (usWinAscent == 0)
+        if (usWinAscent === 0)
           return false;
 
         return true;
       }
 
       var TTOpsStackDeltas = [
         0, 0, 0, 0, 0, 0, 0, 0, -2, -2, -2, -2, 0, 0, -2, -5,
         -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, -1, 0, -1, -1, -1, -1,
@@ -18850,28 +19030,32 @@ var Font = (function FontClosure() {
       font.pos = (font.start || 0) + maxp.offset;
       var version = int16(font.getBytes(4));
       var numGlyphs = int16(font.getBytes(2));
 
       sanitizeMetrics(font, hhea, hmtx, numGlyphs);
 
       sanitizeTTPrograms(fpgm, prep);
 
+      if (head) {
+        sanitizeHead(head, numGlyphs, loca.length);
+      }
+
       var isGlyphLocationsLong = int16([head.data[50], head.data[51]]);
       if (head && loca && glyf) {
         sanitizeGlyphLocations(loca, glyf, numGlyphs, isGlyphLocationsLong);
       }
 
       var emptyGlyphIds = [];
       if (glyf)
         findEmptyGlyphs(loca, isGlyphLocationsLong, emptyGlyphIds);
 
       // Sanitizer reduces the glyph advanceWidth to the maxAdvanceWidth
       // Sometimes it's 0. That needs to be fixed
-      if (hhea.data[10] == 0 && hhea.data[11] == 0) {
+      if (hhea.data[10] === 0 && hhea.data[11] === 0) {
         hhea.data[10] = 0xFF;
         hhea.data[11] = 0xFF;
       }
 
       // The 'post' table has glyphs names.
       if (post) {
         var valid = readPostScriptTable(post, properties, numGlyphs);
         if (!valid) {
@@ -18943,198 +19127,154 @@ var Font = (function FontClosure() {
             break;
           var unicode = unusedUnicode++;
           this.toFontChar[cid] = unicode;
           usedUnicodes[unicode] = true;
           glyphs.push({ unicode: unicode, code: cid });
           ids.push(i);
         }
       } else {
-        var cmapTable = readCMapTable(cmap, font);
-
-        glyphs = cmapTable.glyphs;
-        ids = cmapTable.ids;
-
-        var hasShortCmap = !!cmapTable.hasShortCmap;
+        this.useToFontChar = true;
+        // Most of the following logic in this code branch is based on the
+        // 9.6.6.4 of the PDF spec.
+
+        // TODO(mack):
+        // We are using this.hasEncoding to mean that the encoding is either
+        // MacRomanEncoding or WinAnsiEncoding (following spec in 9.6.6.4),
+        // but this.hasEncoding is currently true for any encodings on the
+        // Encodings object (e.g. MacExpertEncoding). So should consider using
+        // better check for this.
+        var cmapTable = readCmapTable(cmap, font, this.hasEncoding,
+            this.isSymbolicFont);
+
+        // TODO(mack): If the (3, 0) cmap table used, then the font is
+        // symbolic. The range of charcodes in the cmap table should be
+        // one of the following:
+        //   -> 0x0000 - 0x00FF
+        //   -> 0xF000 - 0xF0FF
+        //   -> 0xF100 - 0xF1FF
+        //   -> 0xF200 - 0xF2FF
+        // If it is not, we should change not consider this a symbolic font
+        this.isSymbolicFont = cmapTable.isSymbolicFont;
+
+        var cmapPlatformId = cmapTable.platformId;
+        var cmapEncodingId = cmapTable.encodingId;
+        var cmapMappings = cmapTable.mappings;
+        var cmapMappingsLength = cmapMappings.length;
+        var glyphs = [];
+        var ids = [];
+        for (var i = 0; i < cmapMappingsLength; ++i) {
+          var cmapMapping = cmapMappings[i];
+          var charcode = cmapMapping.charcode;
+          var unicode = cmapCharcodeToUnicode(charcode, this.isSymbolicFont,
+              cmapPlatformId, cmapEncodingId);
+
+          if (!unicode) {
+            // TODO(mack): gotta check if skipping mappings where we cannot find
+            // a unicode is the correct behaviour
+            continue;
+          }
+          glyphs.push({
+            code: charcode,
+            unicode: unicode
+          });
+          ids.push(cmapMapping.glyphId);
+        }
+
+        var hasShortCmap = cmapTable.hasShortCmap;
         var toFontChar = this.toFontChar;
 
         if (hasShortCmap && ids.length == numGlyphs) {
           // Fixes the short cmap tables -- some generators use incorrect
           // glyph id.
-          for (var i = 0, ii = ids.length; i < ii; i++)
+          for (var i = 0, ii = ids.length; i < ii; i++) {
             ids[i] = i;
-        }
-
-        var unusedUnicode = CMAP_GLYPH_OFFSET;
-        var glyphNames = properties.glyphNames || [];
-        var encoding = properties.baseEncoding;
-        var differences = properties.differences;
-        if (toFontChar && toFontChar.length > 0) {
-          // checking if cmap is just identity map
-          var isIdentity = true;
-          for (var i = 0, ii = glyphs.length; i < ii; i++) {
-            if (glyphs[i].unicode != i + 1) {
-              isIdentity = false;
-              break;
-            }
-          }
-          // if it is, replacing with meaningful toUnicode values
-          if (isIdentity && !this.isSymbolicFont) {
-            var usedUnicodes = [], unassignedUnicodeItems = [];
-            for (var i = 0, ii = glyphs.length; i < ii; i++) {
-              var unicode = toFontChar[i + 1];
-              if (!unicode || typeof unicode !== 'number' ||
-                  unicode in usedUnicodes) {
-                unassignedUnicodeItems.push(i);
-                continue;
-              }
-              glyphs[i].unicode = unicode;
-              usedUnicodes[unicode] = true;
-            }
-            for (var j = 0, jj = unassignedUnicodeItems.length; j < jj; j++) {
-              var i = unassignedUnicodeItems[j];
-              while (unusedUnicode in usedUnicodes)
-                unusedUnicode++;
-              var cid = i + 1;
-              // override only if unicode mapping is not specified
-              if (!(cid in toFontChar))
-                toFontChar[cid] = unusedUnicode;
-              glyphs[i].unicode = unusedUnicode++;
-            }
-            this.useToFontChar = true;
-          }
-        }
-
-        // remove glyph references outside range of avaialable glyphs or empty
-        var glyphsRemoved = 0;
-        for (var i = ids.length - 1; i >= 0; i--) {
-          if (ids[i] < numGlyphs &&
-              (!emptyGlyphIds[ids[i]] || this.isSymbolicFont))
-            continue;
-          ids.splice(i, 1);
-          glyphs.splice(i, 1);
-          glyphsRemoved++;
-        }
-
-        // checking if it's a "true" symbolic font
-        if (this.isSymbolicFont) {
-          var minUnicode = 0xFFFF, maxUnicode = 0;
-          for (var i = 0, ii = glyphs.length; i < ii; i++) {
-            var unicode = glyphs[i].unicode;
-            minUnicode = Math.min(minUnicode, unicode);
-            maxUnicode = Math.max(maxUnicode, unicode);
-          }
-          // high byte must be the same for min and max unicodes
-          if ((maxUnicode & 0xFF00) != (minUnicode & 0xFF00))
-            this.isSymbolicFont = false;
-        }
-
-        // heuristics: if removed more than 5 glyphs encoding WinAnsiEncoding
-        // does not set properly (broken PDFs have about 100 removed glyphs)
-        if (glyphsRemoved > 5) {
-          warn('Switching TrueType encoding to MacRomanEncoding for ' +
-               this.name + ' font');
-          encoding = Encodings.MacRomanEncoding;
-        }
-
-        if (hasShortCmap && this.hasEncoding && !this.isSymbolicFont) {
-          // Re-encode short map encoding to unicode -- that simplifies the
-          // resolution of MacRoman encoded glyphs logic for TrueType fonts:
-          // copying all characters to private use area, all mapping all known
-          // glyphs to the unicodes. The glyphs and ids arrays will grow.
-          var usedUnicodes = [];
-          for (var i = 0, ii = glyphs.length; i < ii; i++) {
-            var code = glyphs[i].unicode;
-            var gid = ids[i];
-            glyphs[i].unicode += CMAP_GLYPH_OFFSET;
-            toFontChar[code] = glyphs[i].unicode;
-
-            var glyphName = glyphNames[gid] || encoding[code];
-            if (glyphName in GlyphsUnicode) {
-              var unicode = GlyphsUnicode[glyphName];
-              if (unicode in usedUnicodes)
-                continue;
-
-              usedUnicodes[unicode] = true;
-              glyphs.push({
-                unicode: unicode,
-                code: glyphs[i].code
-              });
-              ids.push(gid);
-              toFontChar[code] = unicode;
-            }
-          }
-          this.useToFontChar = true;
-        } else if (!this.isSymbolicFont && (this.hasEncoding ||
-                    properties.glyphNames || differences.length > 0)) {
-          // Re-encode cmap encoding to unicode, based on the 'post' table data
-          // diffrence array or base encoding
-          var reverseMap = [];
-          for (var i = 0, ii = glyphs.length; i < ii; i++)
-            reverseMap[glyphs[i].unicode] = i;
-
-          var newGlyphUnicodes = [];
-          for (var i = 0, ii = glyphs.length; i < ii; i++) {
-            var code = glyphs[i].unicode;
-            var changeCode = false;
-            var gid = ids[i];
-
-            var glyphName = glyphNames[gid];
-            if (!glyphName) {
-              glyphName = differences[code] || encoding[code];
-              changeCode = true;
-            }
-            if (glyphName in GlyphsUnicode) {
-              var unicode = GlyphsUnicode[glyphName];
-              if (!unicode || reverseMap[unicode] === i)
-                continue; // unknown glyph name or in its own place
-
-              newGlyphUnicodes[i] = unicode;
-              if (changeCode)
-                toFontChar[code] = unicode;
-              delete reverseMap[code];
-            }
-          }
-          for (var index in newGlyphUnicodes) {
-            if (newGlyphUnicodes.hasOwnProperty(index)) {
-              var unicode = newGlyphUnicodes[index];
-              if (reverseMap[unicode]) {
-                // avoiding assigning to the same unicode
-                glyphs[index].unicode = unusedUnicode++;
-                continue;
-              }
-              glyphs[index].unicode = unicode;
-              reverseMap[unicode] = index;
-            }
-          }
-          this.useToFontChar = true;
-        }
-
-        // Moving all symbolic font glyphs into 0xF000 - 0xF0FF range.
+          }
+        }
+
+        // Rewrite the whole toFontChar dictionary with a new one using the
+        // information from the mappings in the cmap table.
+        var newToFontChar = [];
         if (this.isSymbolicFont) {
           for (var i = 0, ii = glyphs.length; i < ii; i++) {
-            var code = glyphs[i].unicode & 0xFF;
-            var fontCharCode = SYMBOLIC_FONT_GLYPH_OFFSET | code;
-            glyphs[i].unicode = toFontChar[code] = fontCharCode;
-          }
-          this.useToFontChar = true;
-        }
+            var glyph = glyphs[i];
+            // For (3, 0) cmap tables:
+            // The charcode key being stored in toFontChar is the lower byte
+            // of the two-byte charcodes of the cmap table since according to
+            // the spec: 'each byte from the string shall be prepended with the
+            // high byte of the range [of charcodes in the cmap table], to form
+            // a two-byte character, which shall be used to select the
+            // associated glyph description from the subtable'.
+            //
+            // For (1, 0) cmap tables:
+            // 'single bytes from the string shall be used to look up the
+            // associated glyph descriptions from the subtable'. This means
+            // charcodes in the cmap will be single bytes, so no-op since
+            // glyph.code & 0xFF === glyph.code
+            newToFontChar[glyph.code & 0xFF] = glyph.unicode;
+          }
+        } else {
+
+          var encoding = properties.baseEncoding;
+          var differences = properties.differences;
+
+          // TODO(mack): check if it is necessary to shift control characters
+          // for non-symbolic fonts so that browsers dont't render them using
+          // space characters
+
+          var glyphCodeMapping = cmapTable.glyphCodeMapping;
+          for (var charcode = 0; charcode < encoding.length; ++charcode) {
+            if (!encoding.hasOwnProperty(charcode)) {
+              continue;
+            }
+
+            // Since the cmap table that we will be writing out is a (3, 1)
+            // unicode table, in this section we will rewrites the charcodes
+            // in the pdf into unicodes
+
+            var glyphName = encoding[charcode];
+            // A nonsymbolic font should not have a Differences array, but
+            // if it does have one, we should still use it
+            if (charcode in differences) {
+              glyphName = differences[charcode];
+            }
+
+            // Finally, any undefined entries in the table shall be filled
+            // using StandardEncoding
+            if (!glyphName) {
+              glyphName = Encodings.StandardEncoding[charcode];
+            }
+
+            // TODO(mack): Handle the case that the glyph name cannot be
+            // mapped as specified, in which case the glyph name shall be
+            // looked up in the font program's 'post' table (if one is
+            // present) and the associated glyph id shall be used.
+            //
+            // For now, we're just using the '.notdef' glyph name in this
+            // case.
+            glyphName = glyphName || '.notdef';
+
+            var unicode = GlyphsUnicode[glyphName];
+            newToFontChar[charcode] = unicode;
+          }
+        }
+        this.toFontChar = toFontChar = newToFontChar;
 
         createGlyphNameMap(glyphs, ids, properties);
         this.glyphNameMap = properties.glyphNameMap;
       }
 
       if (glyphs.length === 0) {
         // defines at least one glyph
         glyphs.push({ unicode: 0xF000, code: 0xF000, glyph: '.notdef' });
         ids.push(0);
       }
 
       // Converting glyphs and ids into font's cmap table
-      cmap.data = createCMapTable(glyphs, ids);
+      cmap.data = createCmapTable(glyphs, ids);
       var unicodeIsEnabled = [];
       for (var i = 0, ii = glyphs.length; i < ii; i++) {
         unicodeIsEnabled[glyphs[i].unicode] = true;
       }
       this.unicodeIsEnabled = unicodeIsEnabled;
 
       if (os2 && !validateOS2Table(os2)) {
         tables.splice(tables.indexOf(os2), 1);
@@ -19268,17 +19408,17 @@ var Font = (function FontClosure() {
       var fields = {
         // PostScript Font Program
         'CFF ': font.data,
 
         // OS/2 and Windows Specific metrics
         'OS/2': stringToArray(createOS2Table(properties, charstrings)),
 
         // Character to glyphs mapping
-        'cmap': createCMapTable(charstrings.slice(),
+        'cmap': createCmapTable(charstrings.slice(),
                                 ('glyphIds' in font) ? font.glyphIds : null),
 
         // Font header
         'head': (function fontFieldsHead() {
           return stringToArray(
               '\x00\x01\x00\x00' + // Version number
               '\x00\x00\x10\x00' + // fontRevision
               '\x00\x00\x00\x00' + // checksumAdjustement
@@ -19369,17 +19509,17 @@ var Font = (function FontClosure() {
       }
       return result;
     },
 
     rebuildToUnicode: function Font_rebuildToUnicode(properties) {
       var firstChar = properties.firstChar, lastChar = properties.lastChar;
       var map = [];
       if (properties.composite) {
-        var isIdentityMap = this.cidToUnicode.length == 0;
+        var isIdentityMap = this.cidToUnicode.length === 0;
         for (var i = firstChar, ii = lastChar; i <= ii; i++) {
           // TODO missing map the character according font's CMap
           var cid = i;
           map[i] = isIdentityMap ? cid : this.cidToUnicode[cid];
         }
       } else {
         for (var i = firstChar, ii = lastChar; i <= ii; i++) {
           var glyph = properties.differences[i];
@@ -19405,67 +19545,78 @@ var Font = (function FontClosure() {
       if (cidSystemInfo) {
         cidToUnicode = CIDToUnicodeMaps[
           cidSystemInfo.registry + '-' + cidSystemInfo.ordering];
       }
 
       if (!cidToUnicode)
         return; // identity encoding
 
+      var cidEncoding = properties.cidEncoding;
+      var overwrite = HalfwidthCMaps[cidEncoding];
       var cid = 1, i, j, k, ii;
       for (i = 0, ii = cidToUnicode.length; i < ii; ++i) {
         var unicode = cidToUnicode[i];
         if (isArray(unicode)) {
           var length = unicode.length;
           for (j = 0; j < length; j++) {
-            cidToUnicodeMap[cid] = unicode[j];
-            unicodeToCIDMap[unicode[j]] = cid;
+            cidToUnicodeMap[cid] = k = unicode[j];
+            if (!unicodeToCIDMap[k] || overwrite) {
+              unicodeToCIDMap[k] = cid;
+            }
           }
           cid++;
         } else if (typeof unicode === 'object') {
           var fillLength = unicode.f;
           if (fillLength) {
             k = unicode.c;
             for (j = 0; j < fillLength; ++j) {
               cidToUnicodeMap[cid] = k;
-              unicodeToCIDMap[k] = cid;
+              if (!unicodeToCIDMap[k] || overwrite) {
+                unicodeToCIDMap[k] = cid;
+              }
               cid++;
               k++;
             }
           } else
             cid += unicode.s;
         } else if (unicode) {
           cidToUnicodeMap[cid] = unicode;
-          unicodeToCIDMap[unicode] = cid;
+          if (!unicodeToCIDMap[unicode] || overwrite) {
+            unicodeToCIDMap[unicode] = cid;
+          }
           cid++;
         } else
           cid++;
       }
 
-      var cidEncoding = properties.cidEncoding;
-      if (cidEncoding && cidEncoding.indexOf('Identity-') !== 0) {
+      if (!cidEncoding) {
+        return;
+      }
+      if (cidEncoding.indexOf('Identity-') !== 0) {
         // input is already Unicode for non-Identity CMap encodings.
-        // However, Unicode-to-CID conversion is needed
-        // regardless of the CMap encoding. So we can't reset
-        // unicodeToCID.
         this.cidToUnicode = [];
+      } else {
+        // We don't have to do reverse conversions if the string is
+        // already CID.
+        this.unicodeToCID = [];
       }
     },
 
     bindDOM: function Font_bindDOM() {
       if (!this.data)
         return null;
 
       var data = bytesToString(this.data);
       var fontName = this.loadedName;
 
       // Add the font-face rule to the document
       var url = ('url(data:' + this.mimetype + ';base64,' +
                  window.btoa(data) + ');');
-      var rule = "@font-face { font-family:'" + fontName + "';src:" + url + '}';
+      var rule = '@font-face { font-family:"' + fontName + '";src:' + url + '}';
 
       var styleElement = document.getElementById('PDFJS_FONT_STYLE_TAG');
       if (!styleElement) {
           styleElement = document.createElement('style');
           styleElement.id = 'PDFJS_FONT_STYLE_TAG';
           document.documentElement.getElementsByTagName('head')[0].appendChild(
             styleElement);
       }
@@ -19499,17 +19650,17 @@ var Font = (function FontClosure() {
         // finding the charcode via unicodeToCID map
         var charcode = 0;
         if (this.composite)
           charcode = this.unicodeToCID[glyphUnicode];
         // ... via toUnicode map
         if (!charcode && 'toUnicode' in this)
           charcode = this.toUnicode.indexOf(glyphUnicode);
         // setting it to unicode if negative or undefined
-        if (!(charcode > 0))
+        if (charcode <= 0)
           charcode = glyphUnicode;
         // trying to get width via charcode
         width = this.widths[charcode];
         if (width)
           break; // the non-zero width found
       }
       width = width || this.defaultWidth;
       // Do not shadow the property here. See discussion:
@@ -19676,22 +19827,333 @@ var ErrorFont = (function ErrorFontClosu
     exportData: function ErrorFont_exportData() {
       return {error: this.error};
     }
   };
 
   return ErrorFont;
 })();
 
-var CallothersubrCmd = (function CallothersubrCmdClosure() {
-  function CallothersubrCmd(index) {
-    this.index = index;
-  }
-
-  return CallothersubrCmd;
+/*
+ * CharStrings are encoded following the the CharString Encoding sequence
+ * describe in Chapter 6 of the "Adobe Type1 Font Format" specification.
+ * The value in a byte indicates a command, a number, or subsequent bytes
+ * that are to be interpreted in a special way.
+ *
+ * CharString Number Encoding:
+ *  A CharString byte containing the values from 32 through 255 inclusive
+ *  indicate an integer. These values are decoded in four ranges.
+ *
+ * 1. A CharString byte containing a value, v, between 32 and 246 inclusive,
+ * indicate the integer v - 139. Thus, the integer values from -107 through
+ * 107 inclusive may be encoded in single byte.
+ *
+ * 2. A CharString byte containing a value, v, between 247 and 250 inclusive,
+ * indicates an integer involving the next byte, w, according to the formula:
+ * [(v - 247) x 256] + w + 108
+ *
+ * 3. A CharString byte containing a value, v, between 251 and 254 inclusive,
+ * indicates an integer involving the next byte, w, according to the formula:
+ * -[(v - 251) * 256] - w - 108
+ *
+ * 4. A CharString containing the value 255 indicates that the next 4 bytes
+ * are a two complement signed integer. The first of these bytes contains the
+ * highest order bits, the second byte contains the next higher order bits
+ * and the fourth byte contain the lowest order bits.
+ *
+ *
+ * CharString Command Encoding:
+ *  CharStrings commands are encoded in 1 or 2 bytes.
+ *
+ *  Single byte commands are encoded in 1 byte that contains a value between
+ *  0 and 31 inclusive.
+ *  If a command byte contains the value 12, then the value in the next byte
+ *  indicates a command. This "escape" mechanism allows many extra commands
+ * to be encoded and this encoding technique helps to minimize the length of
+ * the charStrings.
+ */
+var Type1CharString = (function Type1CharStringClosure() {
+  var COMMAND_MAP = {
+    'hstem': [1],
+    'vstem': [3],
+    'vmoveto': [4],
+    'rlineto': [5],
+    'hlineto': [6],
+    'vlineto': [7],
+    'rrcurveto': [8],
+    'callsubr': [10],
+    'flex': [12, 35],
+    'drop' : [12, 18],
+    'endchar': [14],
+    'rmoveto': [21],
+    'hmoveto': [22],
+    'vhcurveto': [30],
+    'hvcurveto': [31]
+  };
+
+  function Type1CharString() {
+    this.width = 0;
+    this.lsb = 0;
+    this.flexing = false;
+    this.output = [];
+    this.stack = [];
+  }
+
+  Type1CharString.prototype = {
+    convert: function Type1CharString_convert(encoded, subrs) {
+      var count = encoded.length;
+      var error = false;
+      for (var i = 0; i < count; i++) {
+        var value = encoded[i];
+        if (value < 32) {
+          if (value === 12) {
+            value = (value << 8) + encoded[++i];
+          }
+          switch (value) {
+            case 1: // hstem
+              if (!HINTING_ENABLED) {
+                this.stack = [];
+                break;
+              }
+              error = this.executeCommand(2, COMMAND_MAP.hstem);
+              break;
+            case 3: // vstem
+              if (!HINTING_ENABLED) {
+                this.stack = [];
+                break;
+              }
+              error = this.executeCommand(2, COMMAND_MAP.vstem);
+              break;
+            case 4: // vmoveto
+              if (this.flexing) {
+                if (this.stack.length < 1) {
+                  error = true;
+                  break;
+                }
+                // Add the dx for flex and but also swap the values so they are
+                // the right order.
+                var dy = this.stack.pop();
+                this.stack.push(0, dy);
+                break;
+              }
+              error = this.executeCommand(1, COMMAND_MAP.vmoveto);
+              break;
+            case 5: // rlineto
+              error = this.executeCommand(2, COMMAND_MAP.rlineto);
+              break;
+            case 6: // hlineto
+              error = this.executeCommand(1, COMMAND_MAP.hlineto);
+              break;
+            case 7: // vlineto
+              error = this.executeCommand(1, COMMAND_MAP.vlineto);
+              break;
+            case 8: // rrcurveto
+              error = this.executeCommand(6, COMMAND_MAP.rrcurveto);
+              break;
+            case 9: // closepath
+              // closepath is a Type1 command that does not take argument and is
+              // useless in Type2 and it can simply be ignored.
+              this.stack = [];
+              break;
+            case 10: // callsubr
+              if (this.stack.length < 1) {
+                error = true;
+                break;
+              }
+              var subrNumber = this.stack.pop();
+              error = this.convert(subrs[subrNumber], subrs);
+              break;
+            case 11: // return
+              return error;
+            case 13: // hsbw
+              if (this.stack.length < 2) {
+                error = true;
+                break;
+              }
+              // To convert to type2 we have to move the width value to the
+              // first part of the charstring and then use hmoveto with lsb.
+              var wx = this.stack.pop();
+              var sbx = this.stack.pop();
+              this.lsb = sbx;
+              this.width = wx;
+              this.stack.push(sbx);
+              error = this.executeCommand(1, COMMAND_MAP.hmoveto);
+              break;
+            case 14: // endchar
+              this.output.push(COMMAND_MAP.endchar[0]);
+              break;
+            case 21: // rmoveto
+              if (this.flexing) {
+                break;
+              }
+              error = this.executeCommand(2, COMMAND_MAP.rmoveto);
+              break;
+            case 22: // hmoveto
+              if (this.flexing) {
+                // Add the dy for flex.
+                this.stack.push(0);
+                break;
+              }
+              error = this.executeCommand(1, COMMAND_MAP.hmoveto);
+              break;
+            case 30: // vhcurveto
+              error = this.executeCommand(4, COMMAND_MAP.vhcurveto);
+              break;
+            case 31: // hvcurveto
+              error = this.executeCommand(4, COMMAND_MAP.hvcurveto);
+              break;
+            case (12 << 8) + 0: // dotsection
+              // dotsection is a Type1 command to specify some hinting feature
+              // for dots that do not take a parameter and it can safely be
+              // ignored for Type2.
+              this.stack = [];
+              break;
+            case (12 << 8) + 1: // vstem3
+              if (!HINTING_ENABLED) {
+                this.stack = [];
+                break;
+              }
+              // [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.
+              error = this.executeCommand(2, COMMAND_MAP.vstem);
+              break;
+            case (12 << 8) + 2: // hstem3
+              if (!HINTING_ENABLED) {
+                 this.stack = [];
+                break;
+              }
+              // See vstem3.
+              error = this.executeCommand(2, COMMAND_MAP.hstem);
+              break;
+            case (12 << 8) + 6: // seac
+              // seac is like type 2's special endchar but it doesn't use the
+              // first argument asb, so remove it.
+              error = this.executeCommand(4, COMMAND_MAP.endchar);
+              break;
+            case (12 << 8) + 7: // sbw
+              if (this.stack.length < 4) {
+                error = true;
+                break;
+              }
+              // To convert to type2 we have to move the width value to the
+              // first part of the charstring and then use rmoveto with
+              // (dx, dy). The height argument will not be used for vmtx and
+              // vhea tables reconstruction -- ignoring it.
+              var wy = this.stack.pop();
+              var wx = this.stack.pop();
+              var sby = this.stack.pop();
+              var sbx = this.stack.pop();
+              this.lsb = sbx;
+              this.width = wx;
+              this.stack.push(sbx, sby);
+              error = this.executeCommand(2, COMMAND_MAP.rmoveto);
+              break;
+            case (12 << 8) + 12: // div
+              if (this.stack.length < 2) {
+                error = true;
+                break;
+              }
+              var num2 = this.stack.pop();
+              var num1 = this.stack.pop();
+              this.stack.push(num1 / num2);
+              break;
+            case (12 << 8) + 16: // callothersubr
+              if (this.stack.length < 2) {
+                error = true;
+                break;
+              }
+              var subrNumber = this.stack.pop();
+              var numArgs = this.stack.pop();
+              if (subrNumber === 0 && numArgs === 3) {
+                var flexArgs = this.stack.splice(this.stack.length - 17, 17);
+                this.stack.push(
+                  flexArgs[2] + flexArgs[0], // bcp1x + rpx
+                  flexArgs[3] + flexArgs[1], // bcp1y + rpy
+                  flexArgs[4], // bcp2x
+                  flexArgs[5], // bcp2y
+                  flexArgs[6], // p2x
+                  flexArgs[7], // p2y
+                  flexArgs[8], // bcp3x
+                  flexArgs[9], // bcp3y
+                  flexArgs[10], // bcp4x
+                  flexArgs[11], // bcp4y
+                  flexArgs[12], // p3x
+                  flexArgs[13], // p3y
+                  flexArgs[14] // flexDepth
+                  // 15 = finalx unused by flex
+                  // 16 = finaly unused by flex
+                );
+                error = this.executeCommand(13, COMMAND_MAP.flex, true);
+                this.flexing = false;
+                this.stack.push(flexArgs[15], flexArgs[16]);
+              } else if (subrNumber === 1 && numArgs === 0) {
+                this.flexing = true;
+              }
+              break;
+            case (12 << 8) + 17: // pop
+              // Ignore this since it is only used with othersubr.
+              break;
+            case (12 << 8) + 33: // setcurrentpoint
+              // Ignore for now.
+              this.stack = [];
+              break;
+            default:
+              warn('Unknown type 1 charstring command of "' + value + '"');
+              break;
+          }
+          if (error) {
+            break;
+          }
+          continue;
+        } else if (value <= 246) {
+          value = value - 139;
+        } else if (value <= 250) {
+          value = ((value - 247) * 256) + encoded[++i] + 108;
+        } else if (value <= 254) {
+          value = -((value - 251) * 256) - encoded[++i] - 108;
+        } else {
+          value = (encoded[++i] & 0xff) << 24 | (encoded[++i] & 0xff) << 16 |
+                  (encoded[++i] & 0xff) << 8 | (encoded[++i] & 0xff) << 0;
+        }
+        this.stack.push(value);
+      }
+      return error;
+    },
+
+    executeCommand: function(howManyArgs, command, keepStack) {
+      var stackLength = this.stack.length;
+      if (howManyArgs > stackLength) {
+        return true;
+      }
+      var start = stackLength - howManyArgs;
+      for (var i = start; i < stackLength; i++) {
+        var value = this.stack[i];
+        if (value === (value | 0)) { // int
+          this.output.push(28, (value >> 8) & 0xff, value & 0xff);
+        } else { // fixed point
+          value = (65536 * value) | 0;
+          this.output.push(255,
+                           (value >> 24) & 0xFF,
+                           (value >> 16) & 0xFF,
+                           (value >> 8) & 0xFF,
+                           value & 0xFF);
+        }
+      }
+      this.output.push.apply(this.output, command);
+      if (keepStack) {
+        this.stack.splice(start, howManyArgs);
+      } else {
+        this.stack = [];
+      }
+      return false;
+    }
+  };
+
+  return Type1CharString;
 })();
 
 /*
  * 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() {
@@ -19713,348 +20175,16 @@ var Type1Parser = function type1Parser()
       value = stream[i];
       decryptedString[i] = value ^ (r >> 8);
       r = ((value + r) * c1 + c2) & ((1 << 16) - 1);
     }
     return decryptedString.slice(discardNumber);
   }
 
   /*
-   * CharStrings are encoded following the the CharString Encoding sequence
-   * describe in Chapter 6 of the "Adobe Type1 Font Format" specification.
-   * The value in a byte indicates a command, a number, or subsequent bytes
-   * that are to be interpreted in a special way.
-   *
-   * CharString Number Encoding:
-   *  A CharString byte containing the values from 32 through 255 inclusive
-   *  indicate an integer. These values are decoded in four ranges.
-   *
-   * 1. A CharString byte containing a value, v, between 32 and 246 inclusive,
-   * indicate the integer v - 139. Thus, the integer values from -107 through
-   * 107 inclusive may be encoded in single byte.
-   *
-   * 2. A CharString byte containing a value, v, between 247 and 250 inclusive,
-   * indicates an integer involving the next byte, w, according to the formula:
-   * [(v - 247) x 256] + w + 108
-   *
-   * 3. A CharString byte containing a value, v, between 251 and 254 inclusive,
-   * indicates an integer involving the next byte, w, according to the formula:
-   * -[(v - 251) * 256] - w - 108
-   *
-   * 4. A CharString containing the value 255 indicates that the next 4 bytes
-   * are a two complement signed integer. The first of these bytes contains the
-   * highest order bits, the second byte contains the next higher order bits
-   * and the fourth byte contain the lowest order bits.
-   *
-   *
-   * CharString Command Encoding:
-   *  CharStrings commands are encoded in 1 or 2 bytes.
-   *
-   *  Single byte commands are encoded in 1 byte that contains a value between
-   *  0 and 31 inclusive.
-   *  If a command byte contains the value 12, then the value in the next byte
-   *  indicates a command. This "escape" mechanism allows many extra commands
-   * to be encoded and this encoding technique helps to minimize the length of
-   * the charStrings.
-   */
-  var charStringDictionary = {
-    '1': 'hstem',
-    '3': 'vstem',
-    '4': 'vmoveto',
-    '5': 'rlineto',
-    '6': 'hlineto',
-    '7': 'vlineto',
-    '8': 'rrcurveto',
-
-    // closepath is a Type1 command that do not take argument and is useless
-    // in Type2 and it can simply be ignored.
-    '9': null, // closepath
-
-    '10': 'callsubr',
-
-    // return is normally used inside sub-routines to tells to the execution
-    // flow that it can be back to normal.
-    // During the translation process Type1 charstrings will be flattened and
-    // sub-routines will be embedded directly into the charstring directly, so
-    // this can be ignored safely.
-    '11': 'return',
-
-    '12': {
-      // dotsection is a Type1 command to specify some hinting feature for dots
-      // that do not take a parameter and it can safely be ignored for Type2.
-      '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
-      '7': -1, // sbw
-
-      '10': 'add',
-      '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.
-      '16': 'callothersubr',
-
-      '17': 'pop',
-
-      // setcurrentpoint sets the current point to x, y without performing a
-      // moveto (this is a one shot positionning command). This is used only
-      // with the return of an OtherSubrs call.
-      // TODO Implement the OtherSubrs charstring embedding and replace this
-      // call by a no-op, like 2 'pop' commands for example.
-      '33': null // setcurrentpoint
-    },
-    '13': 'hsbw',
-    '14': 'endchar',
-    '21': 'rmoveto',
-    '22': 'hmoveto',
-    '30': 'vhcurveto',
-    '31': 'hvcurveto'
-  };
-
-  var ESCAPE_CMD = 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 flexing = false;
-
-    var value = '';
-    var count = array.length;
-    for (var i = 0; i < count; i++) {
-      value = array[i];
-
-      if (value < 32) {
-        var command = null;
-        if (value == ESCAPE_CMD) {
-          var escape = array[++i];
-
-          // TODO Clean this code
-          if (escape == 16) {
-            var index = charstring.pop();
-            var argc = charstring.pop();
-            for (var j = 0; j < argc; j++)
-              charstring.push('drop');
-
-            // If the flex mechanism is not used in a font program, Adobe
-            // states that entries 0, 1 and 2 can simply be replaced by
-            // {}, which means that we can simply ignore them.
-            if (index < 3) {
-              continue;
-            }
-
-            // 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 == 7) { // sbw
-            var args = breakUpArgs(charstring, 4);
-            var arg0 = args[0];
-            var arg1 = args[1];
-            var arg2 = args[2];
-            lsb = arg0.value;
-            width = arg2.value;
-            // To convert to type2 we have to move the width value to the first
-            // part of the charstring and then use rmoveto with (dx, dy).
-            // The height argument will not be used for vmtx and vhea tables
-            // reconstruction -- ignoring it.
-            charstring = arg2.arg;
-            charstring = charstring.concat(arg0.arg, arg1.arg);
-            charstring.push('rmoveto');
-            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 (!HINTING_ENABLED && (escape == 1 || escape == 2)) {
-            charstring.push('drop', 'drop', 'drop', 'drop', 'drop', 'drop');
-            continue;
-          }
-
-          command = charStringDictionary['12'][escape];
-        } else {
-          if (value == 13) { // hsbw
-            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
-              // XXX: According to the spec if flex or hinting is not used then
-              // subroutines 0-3 can actually be anything defined by the font,
-              // so we really shouldn't be doing flex here but when
-              // callothersubr 0-2 is used. There hasn't been a real world
-              // example of this yet so we'll keep doing it here.
-              var subrNumber = charstring.pop();
-              switch (subrNumber) {
-                case 1:
-                  flexing = true; // prepare for flex coordinates
-                  break;
-                case 0:
-                  var flexArgs = breakUpArgs(charstring, 17);
-
-                  // removing all flex arguments from the stack
-                  charstring.splice(flexArgs[0].offset,
-                                    charstring.length - flexArgs[0].offset);
-
-                  charstring = charstring.concat(
-                    flexArgs[0].arg, // bcp1x +
-                    flexArgs[2].arg, // rpx
-                    ['add'],
-                    flexArgs[1].arg, // bcp1y +
-                    flexArgs[3].arg, // rpy
-                    ['add'],
-                    flexArgs[4].arg, // bcp2x
-                    flexArgs[5].arg, // bcp2y
-                    flexArgs[6].arg, // p2x
-                    flexArgs[7].arg, // p2y
-                    flexArgs[8].arg, // bcp3x
-                    flexArgs[9].arg, // bcp3y
-                    flexArgs[10].arg, // bcp4x
-                    flexArgs[11].arg, // bcp4y
-                    flexArgs[12].arg, // p3x
-                    flexArgs[13].arg, // p3y
-                    flexArgs[14].arg, // flexDepth
-                    // 15 = finalx unused by flex
-                    // 16 = finaly unused by flex
-                    ['flex']
-                  );
-
-                  flexing = false;
-                  break;
-              }
-              continue;
-            }
-          } else if (value == 21 && flexing) { // rmoveto
-            continue; // ignoring rmoveto
-          } else if (value == 22 && flexing) { // hmoveto
-            // Add the dy for flex.
-            charstring.push(0);
-            continue; // ignoring hmoveto
-          } else if (value == 4 && flexing) { // vmoveto
-            // Insert the dx for flex before dy.
-            var flexArgs = breakUpArgs(charstring, 1);
-            charstring.splice(flexArgs[0].offset, 0, 0);
-            continue; // ignoring vmoveto
-          } else if (!HINTING_ENABLED && (value == 1 || value == 3)) {
-            charstring.push('drop', 'drop');
-            continue;
-          }
-          command = charStringDictionary[value];
-        }
-
-        // Some charstring commands are meaningless in Type2 and will return
-        // a null, let's just ignored them
-        if (!command && i < count) {
-          continue;
-        } else if (!command) {
-          break;
-        } else if (command == -1) {
-          warn('Support for Type1 command ' + value +
-                ' (' + escape + ') is not implemented in charstring: ' +
-                charstring);
-          if (value == 12) {
-            // we know how to ignore only some the Type1 commands
-            switch (escape) {
-              case 7:
-                charstring.push('drop', 'drop', 'drop', 'drop');
-                continue;
-              case 8:
-                charstring.push('drop');
-                continue;
-            }
-          }
-        }
-
-        value = command;
-      } else if (value <= 246) {
-        value = value - 139;
-      } else if (value <= 250) {
-        value = ((value - 247) * 256) + array[++i] + 108;
-      } else if (value <= 254) {
-        value = -((value - 251) * 256) - array[++i] - 108;
-      } else {
-        value = (array[++i] & 0xff) << 24 | (array[++i] & 0xff) << 16 |
-                (array[++i] & 0xff) << 8 | (array[++i] & 0xff) << 0;
-      }
-
-      charstring.push(value);
-    }
-
-    return { charstring: charstring, width: width, lsb: lsb };
-  }
-
-  /*
    * Returns an object containing a Subrs array and a CharStrings
    * array extracted from and eexec encrypted block of data
    */
   function readNumberArray(str, index) {
     var start = index;
     while (str[index++] != '[')
       start++;
     start++;
@@ -20111,16 +20241,17 @@ var Type1Parser = function type1Parser()
 
   this.extractFontProgram = function Type1Parser_extractFontProgram(stream) {
     var eexec = decrypt(stream, EEXEC_ENCRYPT_KEY, 4);
     var eexecStr = '';
     for (var i = 0, ii = eexec.length; i < ii; i++)
       eexecStr += String.fromCharCode(eexec[i]);
 
     var glyphsSection = false, subrsSection = false;
+    var subrs = [], charstrings = [];
     var program = {
       subrs: [],
       charstrings: [],
       properties: {
         'privateData': {
           'lenIV': 4
         }
       }
@@ -20146,27 +20277,24 @@ var Type1Parser = function type1Parser()
       var c = eexecStr[i];
 
       if ((glyphsSection || subrsSection) &&
           (token == 'RD' || token == '-|')) {
         i++;
         var data = eexec.slice(i, i + length);
         var lenIV = program.properties.privateData['lenIV'];
         var encoded = decrypt(data, CHAR_STRS_ENCRYPT_KEY, lenIV);
-        var str = decodeCharString(encoded);
 
         if (glyphsSection) {
-          program.charstrings.push({
+          charstrings.push({
             glyph: glyph,
-            data: str.charstring,
-            lsb: str.lsb,
-            width: str.width
+            encoded: encoded
           });
         } else {
-          program.subrs.push(str.charstring);
+          subrs.push(encoded);
         }
         i += length;
         token = '';
       } else if (isSeparator(c)) {
         // Use '| 0' to prevent setting a double into length such as the double
         // does not flow into the loop variable.
         length = parseInt(token, 10) | 0;
         token = '';
@@ -20188,32 +20316,31 @@ var Type1Parser = function type1Parser()
                 var index = parseInt(getToken(), 10);
                 if (index > j)
                   j = index;
                 var length = parseInt(getToken(), 10);
                 getToken(); // read in 'RD'
                 var data = eexec.slice(i + 1, i + 1 + length);
                 var lenIV = program.properties.privateData['lenIV'];
                 var encoded = decrypt(data, CHAR_STRS_ENCRYPT_KEY, lenIV);
-                var str = decodeCharString(encoded);
                 i = i + 1 + length;
                 t = getToken(); // read in 'NP'
                 if (t == 'noaccess')
                   getToken(); // read in 'put'
-                program.subrs[index] = str.charstring;
+                subrs[index] = encoded;
               }
               break;
             case '/BlueValues':
             case '/OtherBlues':
             case '/FamilyBlues':
             case '/FamilyOtherBlues':
               var blueArray = readNumberArray(eexecStr, i + 1);
               // *Blue* values may contain invalid data: disables reading of
               // those values when hinting is disabled.
-              if (blueArray.length > 0 && (blueArray.length % 2) == 0 &&
+              if (blueArray.length > 0 && (blueArray.length % 2) === 0 &&
                   HINTING_ENABLED) {
                 program.properties.privateData[token.substring(1)] = blueArray;
               }
               break;
             case '/StemSnapH':
             case '/StemSnapV':
               program.properties.privateData[token.substring(1)] =
                 readNumberArray(eexecStr, i + 1);
@@ -20240,16 +20367,36 @@ var Type1Parser = function type1Parser()
         } else if (c == '/') {
           token = glyph = '';
           while ((c = eexecStr[++i]) != ' ')
             glyph += c;
         }
       }
     }
 
+    for (var i = 0; i < charstrings.length; i++) {
+      var glyph = charstrings[i].glyph;
+      var encoded = charstrings[i].encoded;
+      var charString = new Type1CharString();
+      var error = charString.convert(encoded, subrs);
+      var output = charString.output;
+      if (error) {
+        // It seems when FreeType encounters an error while evaluating a glyph
+        // that it completely ignores the glyph so we'll mimic that behaviour
+        // here and put an endchar to make the validator happy.
+        output = [14];
+      }
+      program.charstrings.push({
+        glyph: glyph,
+        data: output,
+        lsb: charString.lsb,
+        width: charString.width
+      });
+    }
+
     return program;
   };
 
   this.extractFontHeader = function Type1Parser_extractFontHeader(stream,
                                                                   properties) {
     var headerString = '';
     for (var i = 0, ii = stream.length; i < ii; i++)
       headerString += String.fromCharCode(stream[i]);
@@ -20431,24 +20578,21 @@ Type1Font.prototype = {
 
     charstrings.sort(function charstrings_sort(a, b) {
       return a.unicode - b.unicode;
     });
     return charstrings;
   },
 
   getType2Charstrings: function Type1Font_getType2Charstrings(
-                                  type1Subrs) {
+                                  type1Charstrings) {
     var type2Charstrings = [];
-    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));
+    for (var i = 0, ii = type1Charstrings.length; i < ii; i++) {
+      type2Charstrings.push(type1Charstrings[i].charstring);
+    }
     return type2Charstrings;
   },
 
   getType2Subrs: function Type1Font_getType2Subrs(type1Subrs) {
     var bias = 0;
     var count = type1Subrs.length;
     if (count < 1133)
       bias = 107;
@@ -20458,103 +20602,36 @@ Type1Font.prototype = {
       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++) {
-      type2Subrs.push(this.flattenCharstring(type1Subrs, i));
+      type2Subrs.push(type1Subrs[i]);
     }
 
     return type2Subrs;
   },
 
-  /*
-   * Flatten the commands by interpreting the postscript code and replacing
-   * every 'callsubr', 'callothersubr' by the real commands.
-   */
-  commandsMap: {
-    'hstem': 1,
-    'vstem': 3,
-    'vmoveto': 4,
-    'rlineto': 5,
-    'hlineto': 6,
-    'vlineto': 7,
-    'rrcurveto': 8,
-    'callsubr': 10,
-    'return': 11,
-    'add': [12, 10],
-    'sub': [12, 11],
-    'div': [12, 12],
-    'exch': [12, 28],
-    'flex': [12, 35],
-    'drop' : [12, 18],
-    'endchar': 14,
-    'rmoveto': 21,
-    'hmoveto': 22,
-    'vhcurveto': 30,
-    'hvcurveto': 31
-  },
-
-  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 (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) & 0xff, command & 0xff);
-        } else {
-          charstring.splice(i, 1, 28, (command >> 8) & 0xff, command & 0xff);
-        }
-        i += 2;
-      }
-    }
-    return charstring;
-  },
-
   wrap: function Type1Font_wrap(name, glyphs, charstrings, subrs, properties) {
     var cff = new CFF();
     cff.header = new CFFHeader(1, 0, 4, 4);
 
     cff.names = [name];
 
     var topDict = new CFFTopDict();
-    topDict.setByName('version', 0);
-    topDict.setByName('Notice', 1);
-    topDict.setByName('FullName', 2);
-    topDict.setByName('FamilyName', 3);
-    topDict.setByName('Weight', 4);
+    // CFF strings IDs 0...390 are predefined names, so refering
+    // to entries in our own String INDEX starts at SID 391.
+    topDict.setByName('version', 391);
+    topDict.setByName('Notice', 392);
+    topDict.setByName('FullName', 393);
+    topDict.setByName('FamilyName', 394);
+    topDict.setByName('Weight', 395);
     topDict.setByName('Encoding', null); // placeholder
     topDict.setByName('FontMatrix', properties.fontMatrix);
     topDict.setByName('FontBBox', properties.bbox);
     topDict.setByName('charset', null); // placeholder
     topDict.setByName('CharStrings', null); // placeholder
     topDict.setByName('Private', null); // placeholder
     cff.topDict = topDict;
 
@@ -20627,17 +20704,17 @@ Type1Font.prototype = {
   }
 };
 
 var CFFFont = (function CFFFontClosure() {
   function CFFFont(file, properties) {
     this.properties = properties;
 
     var parser = new CFFParser(file, properties);
-    var cff = parser.parse(true);
+    var cff = parser.parse();
     var compiler = new CFFCompiler(cff);
     this.readExtra(cff);
     try {
       this.data = compiler.compile();
     } catch (e) {
       warn('Failed to compile font ' + properties.loadedName);
       // There may have just been an issue with the compiler, set the data
       // anyway and hope the font loaded.
@@ -20714,55 +20791,55 @@ var CFFFont = (function CFFFontClosure()
   };
 
   return CFFFont;
 })();
 
 var CFFParser = (function CFFParserClosure() {
   var CharstringValidationData = [
     null,
-    { id: 'hstem', min: 2, resetStack: true },
+    { id: 'hstem', min: 2, resetStack: true, stem: true },
     null,
-    { id: 'vstem', min: 2, resetStack: true },
+    { id: 'vstem', min: 2, resetStack: true, stem: true },
     { id: 'vmoveto', min: 1, resetStack: true },
     { id: 'rlineto', min: 2, resetStack: true },
     { id: 'hlineto', min: 1, resetStack: true },
     { id: 'vlineto', min: 1, resetStack: true },
     { id: 'rrcurveto', min: 6, resetStack: true },
     null,
     { id: 'callsubr', min: 1, undefStack: true },
-    { id: 'return', min: 0, resetStack: true },
+    { id: 'return', min: 0, undefStack: true },
     null, // 12
     null,
     null, // endchar
     null,
     null,
     null,
-    { id: 'hstemhm', min: 2, resetStack: true },
+    { id: 'hstemhm', min: 2, resetStack: true, stem: true },
     null, // hintmask
     null, // cntrmask
     { id: 'rmoveto', min: 2, resetStack: true },
     { id: 'hmoveto', min: 1, resetStack: true },
-    { id: 'vstemhm', min: 2, resetStack: true },
+    { id: 'vstemhm', min: 2, resetStack: true, stem: true },
     { id: 'rcurveline', min: 8, resetStack: true },
     { id: 'rlinecurve', min: 8, resetStack: true },
     { id: 'vvcurveto', min: 4, resetStack: true },
     { id: 'hhcurveto', min: 4, resetStack: true },
     null, // shortint
     { id: 'callgsubr', min: 1, undefStack: true },
     { id: 'vhcurveto', min: 4, resetStack: true },
     { id: 'hvcurveto', min: 4, resetStack: true }
   ];
   var CharstringValidationData12 = [
     null,
     null,
     null,
     { id: 'and', min: 2, stackDelta: -1 },
     { id: 'or', min: 2, stackDelta: -1 },
-    { id: 'not', min: 2, stackDelta: -1 },
+    { id: 'not', min: 1, stackDelta: 0 },
     null,
     null,
     null,
     { id: 'abs', min: 1, stackDelta: 0 },
     { id: 'add', min: 2, stackDelta: -1 },
     { id: 'sub', min: 2, stackDelta: -1 },
     { id: 'div', min: 2, stackDelta: -1 },
     null,
@@ -20792,17 +20869,17 @@ var CFFParser = (function CFFParserClosu
     { id: 'flex1', min: 11, resetStack: true }
   ];
 
   function CFFParser(file, properties) {
     this.bytes = file.getBytes();
     this.properties = properties;
   }
   CFFParser.prototype = {
-    parse: function CFFParser_parse(normalizeCIDData) {
+    parse: function CFFParser_parse() {
       var properties = this.properties;
       var cff = new CFF();
       this.cff = cff;
 
       // The first five sections must be in order, all the others are reached
       // via offsets contained in one of the below.
       var header = this.parseHeader();
       var nameIndex = this.parseIndex(header.endPos);
@@ -20860,50 +20937,26 @@ var CFFParser = (function CFFParserClosu
                                      cff.charStrings.count, cff.strings, false);
         encoding = this.parseEncoding(topDict.getByName('Encoding'),
                                       properties,
                                       cff.strings, charset.charset);
       }
       cff.charset = charset;
       cff.encoding = encoding;
 
-      if (!cff.isCIDFont || !normalizeCIDData)
-        return cff;
-
-      // DirectWrite does not like CID fonts data. Trying to convert/flatten
-      // the font data and remove CID properties.
-      if (cff.fdArray.length !== 1) {
-        warn('Unable to normalize CID font in CFF data -- using font as is');
-        return cff;
-      }
-
-      var fontDict = cff.fdArray[0];
-      // Make the sanitizer happy and remove anything that is only for CID
-      // fonts.
-      fontDict.setByKey(17, topDict.getByName('CharStrings'));
-      fontDict.removeByName('CIDFontVersion');
-      fontDict.removeByName('CIDFontRevision');
-      fontDict.removeByName('CIDFontType');
-      fontDict.removeByName('CIDCount');
-      fontDict.removeByName('UIDBase');
-      cff.topDict = fontDict;
-      cff.isCIDFont = false;
-      delete cff.fdArray;
-      delete cff.fdSelect;
-
       return cff;
     },
     parseHeader: function CFFParser_parseHeader() {
       var bytes = this.bytes;
       var offset = 0;
 
       while (bytes[offset] != 1)
         ++offset;
 
-      if (offset != 0) {
+      if (offset !== 0) {
         info('cff data is shifted');
         bytes = bytes.subarray(offset);
         this.bytes = bytes;
       }
       var major = bytes[0];
       var minor = bytes[1];
       var hdrSize = bytes[2];
       var offSize = bytes[3];
@@ -20983,17 +21036,17 @@ var CFFParser = (function CFFParserClosu
     parseIndex: function CFFParser_parseIndex(pos) {
       var cffIndex = new CFFIndex();
       var bytes = this.bytes;
       var count = (bytes[pos++] << 8) | bytes[pos++];
       var offsets = [];
       var start = pos;
       var end = pos;
 
-      if (count != 0) {
+      if (count !== 0) {
         var offsetSize = bytes[pos++];
         // add 1 for offset to determine size of last object
         var startPos = pos + ((count + 1) * offsetSize) - 1;
 
         for (var i = 0, ii = count + 1; i < ii; ++i) {
           var offset = 0;
           for (var j = 0; j < offsetSize; ++j) {
             offset <<= 8;
@@ -21040,18 +21093,18 @@ var CFFParser = (function CFFParserClosu
     parseStringIndex: function CFFParser_parseStringIndex(index) {
       var strings = new CFFStrings();
       for (var i = 0, ii = index.count; i < ii; ++i) {
         var data = index.get(i);
         strings.add(String.fromCharCode.apply(null, data));
       }
       return strings;
     },
-    createDict: function CFFParser_createDict(type, dict, strings) {
-      var cffDict = new type(strings);
+    createDict: function CFFParser_createDict(Type, dict, strings) {
+      var cffDict = new Type(strings);
       var types = cffDict.types;
 
       for (var i = 0, ii = dict.length; i < ii; ++i) {
         var pair = dict[i];
         var key = pair[0];
         var value = pair[1];
         cffDict.setByKey(key, value);
       }
@@ -21069,17 +21122,17 @@ var CFFParser = (function CFFParserClosu
         var valid = true;
         var data = charstring;
         var length = data.length;
         for (var j = 0; j < length;) {
           var value = data[j++];
           var validationCommand = null;
           if (value == 12) {
             var q = data[j++];
-            if (q == 0) {
+            if (q === 0) {
               // The CFF specification state that the 'dotsection' command
               // (12, 0) is deprecated and treated as a no-op, but all Type2
               // charstrings processors should support them. Unfortunately
               // the font sanitizer don't. As a workaround the sequence (12, 0)
               // is replaced by a useless (0, hmoveto).
               data[j - 2] = 139;
               data[j - 1] = 22;
               stackSize = 0;
@@ -21096,27 +21149,27 @@ var CFFParser = (function CFFParserClosu
           } else if (value >= 32 && value <= 246) {  // number
             stackSize++;
           } else if (value >= 247 && value <= 254) {  // number (+1 bytes)
             j++;
             stackSize++;
           } else if (value == 255) {  // number (32 bit)
             j += 4;
             stackSize++;
-          } else if (value == 18 || value == 23) {
-            hints += stackSize >> 1;
-            validationCommand = CharstringValidationData[value];
           } else if (value == 19 || value == 20) {
             hints += stackSize >> 1;
             j += (hints + 7) >> 3; // skipping right amount of hints flag data
             stackSize = 0;
           } else {
             validationCommand = CharstringValidationData[value];
           }
           if (validationCommand) {
+            if (validationCommand.stem) {
+              hints += stackSize >> 1;
+            }
             if ('min' in validationCommand) {
               if (!undefStack && stackSize < validationCommand.min) {
                 warn('Not enough parameters for ' + validationCommand.id +
                      '; actual: ' + stackSize +
                      ', expected: ' + validationCommand.min);
                 valid = false;
                 break;
               }
@@ -21173,17 +21226,17 @@ var CFFParser = (function CFFParserClosu
       if (subrsOffset === 0 || relativeOffset >= this.bytes.length) {
         privateDict.removeByName('Subrs');
         return;
       }
       var subrsIndex = this.parseIndex(relativeOffset);
       privateDict.subrsIndex = subrsIndex.obj;
     },
     parseCharsets: function CFFParser_parseCharsets(pos, length, strings, cid) {
-      if (pos == 0) {
+      if (pos === 0) {
         return new CFFCharset(true, CFFCharsetPredefinedTypes.ISO_ADOBE,
                               ISOAdobeCharset);
       } else if (pos == 1) {
         return new CFFCharset(true, CFFCharsetPredefinedTypes.EXPERT,
                               ExpertCharset);
       } else if (pos == 2) {
         return new CFFCharset(true, CFFCharsetPredefinedTypes.EXPERT_SUBSET,
                               ExpertSubsetCharset);
@@ -21244,17 +21297,17 @@ var CFFParser = (function CFFParserClosu
         var supplementsCount = bytes[pos++];
         for (var i = 0; i < supplementsCount; i++) {
           var code = bytes[pos++];
           var sid = (bytes[pos++] << 8) + (bytes[pos++] & 0xff);
           encoding[code] = properties.differences.indexOf(strings.get(sid));
         }
       }
 
-      if (pos == 0 || pos == 1) {
+      if (pos === 0 || pos == 1) {
         predefined = true;
         format = pos;
         var baseEncoding = pos ? Encodings.ExpertEncoding :
                                  Encodings.StandardEncoding;
         for (var i = 0, ii = charset.length; i < ii; i++) {
           var index = baseEncoding.indexOf(charset[i]);
           if (index != -1) {
             encoding[index] = i;
@@ -21497,36 +21550,39 @@ var CFFTopDict = (function CFFTopDictClo
     [4, 'Weight', 'sid', null],
     [[12, 1], 'isFixedPitch', 'num', 0],
     [[12, 2], 'ItalicAngle', 'num', 0],
     [[12, 3], 'UnderlinePosition', 'num', -100],
     [[12, 4], 'UnderlineThickness', 'num', 50],
     [[12, 5], 'PaintType', 'num', 0],
     [[12, 6], 'CharstringType', 'num', 2],
     [[12, 7], 'FontMatrix', ['num', 'num', 'num', 'num', 'num', 'num'],
-                            [.001, 0, 0, .001, 0, 0]],
+                            [0.001, 0, 0, 0.001, 0, 0]],
     [13, 'UniqueID', 'num', null],
     [5, 'FontBBox', ['num', 'num', 'num', 'num'], [0, 0, 0, 0]],
     [[12, 8], 'StrokeWidth', 'num', 0],
     [14, 'XUID', 'array', null],
     [15, 'charset', 'offset', 0],
     [16, 'Encoding', 'offset', 0],
     [17, 'CharStrings', 'offset', 0],
     [18, 'Private', ['offset', 'offset'], null],
     [[12, 21], 'PostScript', 'sid', null],
     [[12, 22], 'BaseFontName', 'sid', null],
     [[12, 23], 'BaseFontBlend', 'delta', null],
     [[12, 31], 'CIDFontVersion', 'num', 0],
     [[12, 32], 'CIDFontRevision', 'num', 0],
     [[12, 33], 'CIDFontType', 'num', 0],
     [[12, 34], 'CIDCount', 'num', 8720],
     [[12, 35], 'UIDBase', 'num', null],
-    [[12, 36], 'FDArray', 'offset', null],
+    // XXX: CID Fonts on DirectWrite 6.1 only seem to work if FDSelect comes
+    // before FDArray.
     [[12, 37], 'FDSelect', 'offset', null],
-    [[12, 38], 'FontName', 'sid', null]];
+    [[12, 36], 'FDArray', 'offset', null],
+    [[12, 38], 'FontName', 'sid', null]
+  ];
   var tables = null;
   function CFFTopDict(strings) {
     if (tables === null)
       tables = CFFDict.createTables(layout);
     CFFDict.call(this, tables, strings);
     this.privateDict = null;
   }
   CFFTopDict.prototype = Object.create(CFFDict.prototype);
@@ -21664,17 +21720,17 @@ var CFFOffsetTracker = (function CFFOffs
 // Takes a CFF and converts it to the binary representation.
 var CFFCompiler = (function CFFCompilerClosure() {
   function stringToArray(str) {
     var array = [];
     for (var i = 0, ii = str.length; i < ii; ++i)
       array[i] = str.charCodeAt(i);
 
     return array;
-  };
+  }
   function CFFCompiler(cff) {
     this.cff = cff;
   }
   CFFCompiler.prototype = {
     compile: function CFFCompiler_compile() {
       var cff = this.cff;
       var output = {
         data: [],
@@ -21687,17 +21743,19 @@ var CFFCompiler = (function CFFCompilerC
 
       // Compile the five entries that must be in order.
       var header = this.compileHeader(cff.header);
       output.add(header);
 
       var nameIndex = this.compileNameIndex(cff.names);
       output.add(nameIndex);
 
-      var compiled = this.compileTopDicts([cff.topDict], output.length);
+      var compiled = this.compileTopDicts([cff.topDict],
+                                          output.length,
+                                          cff.isCIDFont);
       output.add(compiled.output);
       var topDictTracker = compiled.trackers[0];
 
       var stringIndex = this.compileStringIndex(cff.strings.strings);
       output.add(stringIndex);
 
       var globalSubrIndex = this.compileIndex(cff.globalSubrIndex);
       output.add(globalSubrIndex);
@@ -21730,31 +21788,32 @@ var CFFCompiler = (function CFFCompilerC
       output.add(charStrings);
 
       if (cff.isCIDFont) {
         // For some reason FDSelect must be in front of FDArray on windows. OSX
         // and linux don't seem to care.
         topDictTracker.setEntryLocation('FDSelect', [output.length], output);
         var fdSelect = this.compileFDSelect(cff.fdSelect.raw);
         output.add(fdSelect);
-
-        var compiled = this.compileTopDicts(cff.fdArray, output.length);
+        // It is unclear if the sub font dictionary can have CID related
+        // dictionary keys, but the sanitizer doesn't like them so remove them.
+        var compiled = this.compileTopDicts(cff.fdArray, output.length, true);
         topDictTracker.setEntryLocation('FDArray', [output.length], output);
         output.add(compiled.output);
         var fontDictTrackers = compiled.trackers;
 
         this.compilePrivateDicts(cff.fdArray, fontDictTrackers, output);
       }
 
       this.compilePrivateDicts([cff.topDict], [topDictTracker], output);
 
       return output.data;
     },
     encodeNumber: function CFFCompiler_encodeNumber(value) {
-      if (parseFloat(value) == parseInt(value) && !isNaN(value)) // isInt
+      if (parseFloat(value) == parseInt(value, 10) && !isNaN(value)) // isInt
         return this.encodeInteger(value);
       else
         return this.encodeFloat(value);
     },
     encodeFloat: function CFFCompiler_encodeFloat(num) {
       var value = num.toString();
       var nibbles = '';
       for (var i = 0, ii = value.length; i < ii; ++i) {
@@ -21806,21 +21865,30 @@ var CFFCompiler = (function CFFCompilerC
       ];
     },
     compileNameIndex: function CFFCompiler_compileNameIndex(names) {
       var nameIndex = new CFFIndex();
       for (var i = 0, ii = names.length; i < ii; ++i)
         nameIndex.add(stringToArray(names[i]));
       return this.compileIndex(nameIndex);
     },
-    compileTopDicts: function CFFCompiler_compileTopDicts(dicts, length) {
+    compileTopDicts: function CFFCompiler_compileTopDicts(dicts,
+                                                          length,
+                                                          removeCidKeys) {
       var fontDictTrackers = [];
       var fdArrayIndex = new CFFIndex();
       for (var i = 0, ii = dicts.length; i < ii; ++i) {
         var fontDict = dicts[i];
+        if (removeCidKeys) {
+          fontDict.removeByName('CIDFontVersion');
+          fontDict.removeByName('CIDFontRevision');
+          fontDict.removeByName('CIDFontType');
+          fontDict.removeByName('CIDCount');
+          fontDict.removeByName('UIDBase');
+        }
         var fontDictTracker = new CFFOffsetTracker();
         var fontDictData = this.compileDict(fontDict, fontDictTracker);
         fontDictTrackers.push(fontDictTracker);
         fdArrayIndex.add(fontDictData);
         fontDictTracker.offset(length);
       }
       fdArrayIndex = this.compileIndex(fdArrayIndex, fontDictTrackers);
       return {
@@ -21935,17 +22003,17 @@ var CFFCompiler = (function CFFCompilerC
     compileIndex: function CFFCompiler_compileIndex(index, trackers) {
       trackers = trackers || [];
       var objects = index.objects;
       // First 2 bytes contains the number of objects contained into this index
       var count = objects.length;
 
       // If there is no object, just create an index. This technically
       // should just be [0, 0] but OTS has an issue with that.
-      if (count == 0)
+      if (count === 0)
         return [0, 0, 0];
 
       var data = [(count >> 8) & 0xFF, count & 0xff];
 
       var lastOffset = 1;
       for (var i = 0; i < count; ++i)
         lastOffset += objects[i].length;
 
@@ -26456,17 +26524,17 @@ var PDFImage = (function PDFImageClosure
         if (decodeMap) {
           valueZero = decodeMap[0] ? 1 : 0;
           valueOne = decodeMap[1] ? 1 : 0;
         }
         var mask = 0;
         var buf = 0;
 
         for (var i = 0, ii = length; i < ii; ++i) {
-          if (i % rowComps == 0) {
+          if (i % rowComps === 0) {
             mask = 0;
             buf = 0;
           } else {
             mask >>= 1;
           }
 
           if (mask <= 0) {
             buf = buffer[bufferPos++];
@@ -26474,17 +26542,17 @@ var PDFImage = (function PDFImageClosure
           }
 
           output[i] = !(buf & mask) ? valueZero : valueOne;
         }
       } else {
         // The general case that handles all other bpc values.
         var bits = 0, buf = 0;
         for (var i = 0, ii = length; i < ii; ++i) {
-          if (i % rowComps == 0) {
+          if (i % rowComps === 0) {
             buf = 0;
             bits = 0;
           }
 
           while (bits < bpc) {
             buf = (buf << 8) | buffer[bufferPos++];
             bits += 8;
           }
@@ -26570,17 +26638,17 @@ var PDFImage = (function PDFImageClosure
       var bufferPos = 3; // alpha component offset
       for (i = 0; i < height; i++) {
         mask = 0;
         for (j = 0; j < width; j++) {
           if (!mask) {
             buf = imgArray[imgArrayPos++];
             mask = 128;
           }
-          if (!(buf & mask) == inverseDecode) {
+          if (!(buf & mask) === inverseDecode) {
             buffer[bufferPos] = 0;
           }
           bufferPos += 4;
           mask >>= 1;
         }
       }
     },
     fillRgbaBuffer: function PDFImage_fillRgbaBuffer(buffer, width, height) {
@@ -29719,17 +29787,18 @@ var Parser = (function ParserClosure() {
         dict.set(key, this.getObj(cipherTransform));
       }
 
       // parse image stream
       var startPos = stream.pos;
 
       // searching for the /EI\s/
       var state = 0, ch;
-      while (state != 4 && (ch = stream.getByte()) != null) {
+      while (state != 4 &&
+             (ch = stream.getByte()) !== null && ch !== undefined) {
         switch (ch) {
           case 0x20:
           case 0x0D:
           case 0x0A:
             state = state === 3 ? 4 : 0;
             break;
           case 0x45:
             state = 2;
@@ -29950,17 +30019,17 @@ var Lexer = (function LexerClosure() {
             warn('Unterminated string');
             done = true;
             break;
           case '(':
             ++numParen;
             str += ch;
             break;
           case ')':
-            if (--numParen == 0) {
+            if (--numParen === 0) {
               done = true;
             } else {
               str += ch;
             }
             break;
           case '\\':
             ch = stream.getChar();
             switch (ch) {
@@ -30062,23 +30131,23 @@ var Lexer = (function LexerClosure() {
         } else if (ch === '>') {
           break;
         } else if (specialChars[ch.charCodeAt(0)] === 1) {
           continue;
         } else {
           if (isFirstHex) {
             firstDigit = toHexDigit(ch);
             if (firstDigit === -1) {
-              warn("Ignoring invalid character '" + ch + "' in hex string");
+              warn('Ignoring invalid character "' + ch + '" in hex string');
               continue;
             }
           } else {
             secondDigit = toHexDigit(ch);
             if (secondDigit === -1) {
-              warn("Ignoring invalid character '" + ch + "' in hex string");
+              warn('Ignoring invalid character "' + ch + '" in hex string');
               continue;
             }
             str += String.fromCharCode((firstDigit << 4) | secondDigit);
           }
           isFirstHex = !isFirstHex;
         }
       }
       return str;
@@ -30126,16 +30195,17 @@ var Lexer = (function LexerClosure() {
           return this.getHexString(ch);
         // dict punctuation
         case '>':
           ch = stream.lookChar();
           if (ch == '>') {
             stream.skip();
             return Cmd.get('>>');
           }
+          return Cmd.get(ch);
         case '{':
         case '}':
           return Cmd.get(ch);
         // fall through
         case ')':
           error('Illegal character: ' + ch);
       }
 
@@ -30849,17 +30919,17 @@ var StreamsSequenceStream = (function St
   }
 
   StreamsSequenceStream.prototype = Object.create(DecodeStream.prototype);
 
   StreamsSequenceStream.prototype.readBlock =
     function streamSequenceStreamReadBlock() {
 
     var streams = this.streams;
-    if (streams.length == 0) {
+    if (streams.length === 0) {
       this.eof = true;
       return;
     }
     var stream = streams.shift();
     var chunk = stream.getBytes();
     var bufferLength = this.bufferLength;
     var newLength = bufferLength + chunk.length;
     var buffer = this.ensureBuffer(newLength);
@@ -30969,17 +31039,17 @@ var FlateStream = (function FlateStreamC
 
     this.dict = stream.dict;
     var cmf = bytes[bytesPos++];
     var flg = bytes[bytesPos++];
     if (cmf == -1 || flg == -1)
       error('Invalid header in flate stream: ' + cmf + ', ' + flg);
     if ((cmf & 0x0f) != 0x08)
       error('Unknown compression method in flate stream: ' + cmf + ', ' + flg);
-    if ((((cmf << 8) + flg) % 31) != 0)
+    if ((((cmf << 8) + flg) % 31) !== 0)
       error('Bad FCHECK in flate stream: ' + cmf + ', ' + flg);
     if (flg & 0x20)
       error('FDICT bit set in flate stream: ' + cmf + ', ' + flg);
 
     this.bytes = bytes;
     this.bytesPos = bytesPos;
 
     this.codeSize = 0;
@@ -31023,17 +31093,17 @@ var FlateStream = (function FlateStreamC
       if (typeof (b = bytes[bytesPos++]) == 'undefined')
         error('Bad encoding in flate stream');
       codeBuf |= (b << codeSize);
       codeSize += 8;
     }
     var code = codes[codeBuf & ((1 << maxLen) - 1)];
     var codeLen = code >> 16;
     var codeVal = code & 0xffff;
-    if (codeSize == 0 || codeSize < codeLen || codeLen == 0)
+    if (codeSize === 0 || codeSize < codeLen || codeLen === 0)
       error('Bad encoding in flate stream');
     this.codeBuf = (codeBuf >> codeLen);
     this.codeSize = (codeSize - codeLen);
     this.bytesPos = bytesPos;
     return codeVal;
   };
 
   FlateStream.prototype.generateHuffmanTable =
@@ -31077,17 +31147,17 @@ var FlateStream = (function FlateStreamC
 
   FlateStream.prototype.readBlock = function FlateStream_readBlock() {
     // read block header
     var hdr = this.getBits(3);
     if (hdr & 1)
       this.eof = true;
     hdr >>= 1;
 
-    if (hdr == 0) { // uncompressed block
+    if (hdr === 0) { // uncompressed block
       var bytes = this.bytes;
       var bytesPos = this.bytesPos;
       var b;
 
       if (typeof (b = bytes[bytesPos++]) == 'undefined')
         error('Bad block header in flate stream');
       var blockLen = b;
       if (typeof (b = bytes[bytesPos++]) == 'undefined')
@@ -31310,17 +31380,17 @@ var PredictorStream = (function Predicto
 
     var predictor = this.stream.getByte();
     var rawBytes = this.stream.getBytes(rowBytes);
 
     var bufferLength = this.bufferLength;
     var buffer = this.ensureBuffer(bufferLength + rowBytes);
 
     var prevRow = buffer.subarray(bufferLength - rowBytes, bufferLength);
-    if (prevRow.length == 0)
+    if (prevRow.length === 0)
       prevRow = new Uint8Array(rowBytes);
 
     var j = bufferLength;
     switch (predictor) {
       case 0:
         for (var i = 0; i < rowBytes; ++i)
           buffer[j++] = rawBytes[i];
         break;
@@ -31395,20 +31465,20 @@ var PredictorStream = (function Predicto
  * DecodeStreams.
  */
 var JpegStream = (function JpegStreamClosure() {
   function isAdobeImage(bytes) {
     var maxBytesScanned = Math.max(bytes.length - 16, 1024);
     // Looking for APP14, 'Adobe'
     for (var i = 0; i < maxBytesScanned; ++i) {
       if (bytes[i] == 0xFF && bytes[i + 1] == 0xEE &&
-          bytes[i + 2] == 0x00 && bytes[i + 3] == 0x0E &&
+          bytes[i + 2] === 0x00 && bytes[i + 3] == 0x0E &&
           bytes[i + 4] == 0x41 && bytes[i + 5] == 0x64 &&
           bytes[i + 6] == 0x6F && bytes[i + 7] == 0x62 &&
-          bytes[i + 8] == 0x65 && bytes[i + 9] == 0x00)
+          bytes[i + 8] == 0x65 && bytes[i + 9] === 0x00)
           return true;
       // scanning until frame tag
       if (bytes[i] == 0xFF && bytes[i + 1] == 0xC0)
         break;
     }
     return false;
   }
 
@@ -31655,17 +31725,17 @@ var DecryptStream = (function DecryptStr
   }
 
   var chunkSize = 512;
 
   DecryptStream.prototype = Object.create(DecodeStream.prototype);
 
   DecryptStream.prototype.readBlock = function DecryptStream_readBlock() {
     var chunk = this.str.getBytes(chunkSize);
-    if (!chunk || chunk.length == 0) {
+    if (!chunk || chunk.length === 0) {
       this.eof = true;
       return;
     }
     var decrypt = this.decrypt;
     chunk = decrypt(chunk);
 
     var bufferLength = this.bufferLength;
     var i, n = chunk.length;
@@ -32297,17 +32367,17 @@ var CCITTFaxStream = (function CCITTFaxS
     params = params || new Dict();
 
     this.encoding = params.get('K') || 0;
     this.eoline = params.get('EndOfLine') || false;
     this.byteAlign = params.get('EncodedByteAlign') || false;
     this.columns = params.get('Columns') || 1728;
     this.rows = params.get('Rows') || 0;
     var eoblock = params.get('EndOfBlock');
-    if (eoblock == null)
+    if (eoblock === null || eoblock === undefined)
       eoblock = true;
     this.eoblock = eoblock;
     this.black = params.get('BlackIs1') || false;
 
     this.codingLine = new Uint32Array(this.columns + 1);
     this.refLine = new Uint32Array(this.columns + 2);
 
     this.codingLine[0] = this.columns;
@@ -32316,17 +32386,17 @@ var CCITTFaxStream = (function CCITTFaxS
     this.row = 0;
     this.nextLine2D = this.encoding < 0;
     this.inputBits = 0;
     this.inputBuf = 0;
     this.outputBits = 0;
     this.buf = EOF;
 
     var code1;
-    while ((code1 = this.lookBits(12)) == 0) {
+    while ((code1 = this.lookBits(12)) === 0) {
       this.eatBits(1);
     }
     if (code1 == 1) {
       this.eatBits(12);
     }
     if (this.encoding > 0) {
       this.nextLine2D = !this.lookBits(1);
       this.eatBits(1);
@@ -32400,17 +32470,17 @@ var CCITTFaxStream = (function CCITTFaxS
       return this.buf;
 
     var refLine = this.refLine;
     var codingLine = this.codingLine;
     var columns = this.columns;
 
     var refPos, blackPixels, bits;
 
-    if (this.outputBits == 0) {
+    if (this.outputBits === 0) {
       if (this.eof)
         return null;
 
       this.err = false;
 
       var code1, code2, code3;
       if (this.nextLine2D) {
         for (var i = 0; codingLine[i] < columns; ++i)
@@ -32574,17 +32644,17 @@ var CCITTFaxStream = (function CCITTFaxS
         this.inputBits &= ~7;
 
       var gotEOL = false;
 
       if (!this.eoblock && this.row == this.rows - 1) {
         this.eof = true;
       } else {
         code1 = this.lookBits(12);
-        while (code1 == 0) {
+        while (code1 === 0) {
           this.eatBits(1);
           code1 = this.lookBits(12);
         }
         if (code1 == 1) {
           this.eatBits(12);
           gotEOL = true;
         } else if (code1 == EOF) {
           this.eof = true;
@@ -32642,17 +32712,17 @@ var CCITTFaxStream = (function CCITTFaxS
       else
         this.outputBits = codingLine[this.codingPos = 1];
       this.row++;
     }
 
     if (this.outputBits >= 8) {
       this.buf = (this.codingPos & 1) ? 0 : 0xFF;
       this.outputBits -= 8;
-      if (this.outputBits == 0 && codingLine[this.codingPos] < columns) {
+      if (this.outputBits === 0 && codingLine[this.codingPos] < columns) {
         this.codingPos++;
         this.outputBits = (codingLine[this.codingPos] -
                            codingLine[this.codingPos - 1]);
       }
     } else {
       var bits = 8;
       this.buf = 0;
       do {
@@ -32741,17 +32811,17 @@ var CCITTFaxStream = (function CCITTFaxS
     var code = 0;
     var p;
     var n;
     if (this.eoblock) {
       code = this.lookBits(12);
       if (code == EOF)
         return 1;
 
-      if ((code >> 5) == 0)
+      if ((code >> 5) === 0)
         p = whiteTable1[code];
       else
         p = whiteTable2[code >> 3];
 
       if (p[0] > 0) {
         this.eatBits(p[0]);
         return p[1];
       }
@@ -32772,19 +32842,19 @@ var CCITTFaxStream = (function CCITTFaxS
   CCITTFaxStream.prototype.getBlackCode =
     function ccittFaxStreamGetBlackCode() {
 
     var code, p;
     if (this.eoblock) {
       code = this.lookBits(13);
       if (code == EOF)
         return 1;
-      if ((code >> 7) == 0)
+      if ((code >> 7) === 0)
         p = blackTable1[code];
-      else if ((code >> 9) == 0 && (code >> 7) != 0)
+      else if ((code >> 9) === 0 && (code >> 7) !== 0)
         p = blackTable2[(code >> 1) - 64];
       else
         p = blackTable3[code >> 7];
 
       if (p[0] > 0) {
         this.eatBits(p[0]);
         return p[1];
       }
@@ -32804,18 +32874,18 @@ var CCITTFaxStream = (function CCITTFaxS
     info('bad black code');
     this.eatBits(1);
     return 1;
   };
 
   CCITTFaxStream.prototype.lookBits = function CCITTFaxStream_lookBits(n) {
     var c;
     while (this.inputBits < n) {
-      if ((c = this.str.getByte()) == null) {
-        if (this.inputBits == 0)
+      if ((c = this.str.getByte()) === null || c === undefined) {
+        if (this.inputBits === 0)
           return EOF;
         return ((this.inputBuf << (n - this.inputBits)) &
                 (0xFFFF >> (16 - n)));
       }
       this.inputBuf = (this.inputBuf << 8) + c;
       this.inputBits += 8;
     }
     return (this.inputBuf >> (this.inputBits - n)) & (0xFFFF >> (16 - n));
@@ -32858,17 +32928,17 @@ var LZWStream = (function LZWStreamClosu
 
   LZWStream.prototype = Object.create(DecodeStream.prototype);
 
   LZWStream.prototype.readBits = function LZWStream_readBits(n) {
     var bitsCached = this.bitsCached;
     var cachedData = this.cachedData;
     while (bitsCached < n) {
       var c = this.str.getByte();
-      if (c == null) {
+      if (c === null || c === undefined) {
         this.eof = true;
         return null;
       }
       cachedData = (cachedData << 8) | c;
       bitsCached += 8;
     }
     this.bitsCached = (bitsCached -= n);
     this.cachedData = cachedData;
@@ -33078,16 +33148,22 @@ var WorkerMessageHandler = {
 
           return;
         } else if (e instanceof InvalidPDFException) {
           handler.send('InvalidPDF', {
             exception: e
           });
 
           return;
+        } else if (e instanceof MissingPDFException) {
+          handler.send('MissingPDF', {
+            exception: e
+          });
+
+          return;
         } else {
           handler.send('UnknownError', {
             exception: new UnknownErrorException(e.message, e.toString())
           });
 
           return;
         }
       }
@@ -33132,34 +33208,43 @@ var WorkerMessageHandler = {
           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 + '.');
+            if (e.target.status == 404) {
+              handler.send('MissingPDF', {
+                exception: new MissingPDFException(
+                  'Missing PDF \"' + source.url + '\".')});
+            } else {
+              handler.send('DocError', 'Unexpected server response (' +
+                            e.target.status + ') while retrieving PDF \"' +
+                            source.url + '\".');
+            }
           },
           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 encrypt = pdfModel.xref.encrypt;
       var page = {
         pageIndex: data.pageIndex,
         rotate: pdfPage.rotate,
         ref: pdfPage.ref,
-        view: pdfPage.view
+        view: pdfPage.view,
+        disableTextLayer: encrypt ? encrypt.disableTextLayer : false
       };
       handler.send('GetPage', {pageInfo: page});
     });
 
     handler.on('GetData', function wphSetupGetData(data, promise) {
       promise.resolve(pdfModel.stream.bytes);
     });
 
@@ -33186,49 +33271,51 @@ var WorkerMessageHandler = {
       try {
         var page = pdfModel.getPage(pageNum);
         // Pre compile the pdf page and fetch the fonts/images.
         operatorList = page.getOperatorList(handler, dependency);
       } catch (e) {
         var minimumStackMessage =
             'worker.js: while trying to getPage() and getOperatorList()';
 
+        var wrappedException;
+
         // Turn the error into an obj that can be serialized
         if (typeof e === 'string') {
-          e = {
+          wrappedException = {
             message: e,
             stack: minimumStackMessage
           };
         } else if (typeof e === 'object') {
-          e = {
+          wrappedException = {
             message: e.message || e.toString(),
             stack: e.stack || minimumStackMessage
           };
         } else {
-          e = {
+          wrappedException = {
             message: 'Unknown exception type: ' + (typeof e),
             stack: minimumStackMessage
           };
         }
 
         handler.send('PageError', {
           pageNum: pageNum,
-          error: e
+          error: wrappedException
         });
         return;
       }
 
       log('page=%d - getOperatorList: time=%dms, len=%d', pageNum,
                               Date.now() - start, operatorList.fnArray.length);
 
       // Filter the dependecies for fonts.
       var fonts = {};
       for (var i = 0, ii = dependency.length; i < ii; i++) {
         var dep = dependency[i];
-        if (dep.indexOf('g_font_') == 0) {
+        if (dep.indexOf('g_font_') === 0) {
           fonts[dep] = true;
         }
       }
       handler.send('RenderPage', {
         pageIndex: data.pageIndex,
         operatorList: operatorList,
         depFonts: Object.keys(fonts)
       });
@@ -33275,17 +33362,17 @@ var workerConsole = {
   },
 
   time: function time(name) {
     consoleTimer[name] = Date.now();
   },
 
   timeEnd: function timeEnd(name) {
     var time = consoleTimer[name];
-    if (time == null) {
+    if (!time) {
       error('Unkown timer name ' + name);
     }
     this.log('Timer:', name, Date.now() - time);
   }
 };
 
 // Worker thread?
 if (typeof window === 'undefined') {
@@ -33328,34 +33415,34 @@ var JpxImage = (function JpxImageClosure
         var data = new Uint8Array(xhr.response || xhr.mozResponseArrayBuffer);
         this.parse(data);
         if (this.onload)
           this.onload();
       }).bind(this);
       xhr.send(null);
     },
     parse: function JpxImage_parse(data) {
-      function ReadUint(data, offset, bytes) {
+      function readUint(data, offset, bytes) {
         var n = 0;
         for (var i = 0; i < bytes; i++)
           n = n * 256 + (data[offset + i] & 0xFF);
         return n;
       }
       var position = 0, length = data.length;
       while (position < length) {
         var headerSize = 8;
-        var lbox = ReadUint(data, position, 4);
-        var tbox = ReadUint(data, position + 4, 4);
+        var lbox = readUint(data, position, 4);
+        var tbox = readUint(data, position + 4, 4);
         position += headerSize;
         if (lbox == 1) {
-          lbox = ReadUint(data, position, 8);
+          lbox = readUint(data, position, 8);
           position += 8;
           headerSize += 8;
         }
-        if (lbox == 0)
+        if (lbox === 0)
           lbox = length - position + headerSize;
         if (lbox < headerSize)
           error('JPX error: Invalid box field size');
         var dataLength = lbox - headerSize;
         var jumpDataLength = true;
         switch (tbox) {
           case 0x6A501A1A: // 'jP\032\032'
             // TODO
@@ -33574,28 +33661,28 @@ var JpxImage = (function JpxImageClosure
               var tile = {};
               tile.index = readUint16(data, position + 2);
               tile.length = readUint32(data, position + 4);
               tile.dataEnd = tile.length + position - 2;
               tile.partIndex = data[position + 8];
               tile.partsCount = data[position + 9];
 
               context.mainHeader = false;
-              if (tile.partIndex == 0) {
+              if (tile.partIndex === 0) {
                 // reset component specific settings
                 tile.COD = context.COD;
                 tile.COC = context.COC.slice(0); // clone of the global COC
                 tile.QCD = context.QCD;
                 tile.QCC = context.QCC.slice(0); // clone of the global COC
               }
               context.currentTile = tile;
               break;
             case 0xFF93: // Start of data (SOD)
               var tile = context.currentTile;
-              if (tile.partIndex == 0) {
+              if (tile.partIndex === 0) {
                 initializeTile(context, tile.index);
                 buildPackets(context);
               }
 
               // moving to the end of the data
               length = tile.dataEnd - position;
 
               parseTilePackets(context, data, position, length);
@@ -33916,17 +34003,17 @@ var JpxImage = (function JpxImageClosure
         resolution.trx0 = Math.ceil(component.tcx0 / scale);
         resolution.try0 = Math.ceil(component.tcy0 / scale);
         resolution.trx1 = Math.ceil(component.tcx1 / scale);
         resolution.try1 = Math.ceil(component.tcy1 / scale);
         buildPrecincts(context, resolution, blocksDimensions);
         resolutions.push(resolution);
 
         var subband;
-        if (r == 0) {
+        if (r === 0) {
           // one sub-band (LL) with last decomposition
           subband = {};
           subband.type = 'LL';
           subband.tbx0 = Math.ceil(component.tcx0 / scale);
           subband.tby0 = Math.ceil(component.tcy0 / scale);
           subband.tbx1 = Math.ceil(component.tcx1 / scale);
           subband.tby1 = Math.ceil(component.tcy1 / scale);
           subband.resolution = resolution;
@@ -34018,17 +34105,17 @@ var JpxImage = (function JpxImageClosure
       bufferSize = 0;
       if (skipNextBit) {
         position++;
         skipNextBit = false;
       }
     }
     function readCodingpasses() {
       var value = readBits(1);
-      if (value == 0)
+      if (value === 0)
         return 1;
       value = (value << 1) | readBits(1);
       if (value == 0x02)
         return 2;
       value = (value << 2) | readBits(2);
       if (value <= 0x0E)
         return (value & 0x03) + 3;
       value = (value << 5) | readBits(5);
@@ -34138,17 +34225,17 @@ var JpxImage = (function JpxImageClosure
   function copyCoefficients(coefficients, x0, y0, width, height,
                             delta, mb, codeblocks, transformation,
                             segmentationSymbolUsed) {
     var r = 0.5; // formula (E-6)
     for (var i = 0, ii = codeblocks.length; i < ii; ++i) {
       var codeblock = codeblocks[i];
       var blockWidth = codeblock.tbx1_ - codeblock.tbx0_;
       var blockHeight = codeblock.tby1_ - codeblock.tby0_;
-      if (blockWidth == 0 || blockHeight == 0)
+      if (blockWidth === 0 || blockHeight === 0)
         continue;
       if (!('data' in codeblock))
         continue;
 
       var bitModel, currentCodingpassType;
       bitModel = new BitModel(blockWidth, blockHeight, codeblock.subbandType,
         codeblock.zeroBitPlanes);
       currentCodingpassType = 2; // first bit plane starts from cleanup
@@ -34190,17 +34277,17 @@ var JpxImage = (function JpxImageClosure
 
       var offset = (codeblock.tbx0_ - x0) + (codeblock.tby0_ - y0) * width;
       var position = 0;
       for (var j = 0; j < blockHeight; j++) {
         for (var k = 0; k < blockWidth; k++) {
           var n = (bitModel.coefficentsSign[position] ? -1 : 1) *
             bitModel.coefficentsMagnitude[position];
           var nb = bitModel.bitsDecoded[position], correction;
-          if (transformation == 0 || mb > nb) {
+          if (transformation === 0 || mb > nb) {
             // use r only if transformation is irreversible or
             // not all bitplanes were decoded for reversible transformation
             n += n < 0 ? n - r : n > 0 ? n + r : 0;
             correction = 1 << (mb - nb);
           } else
             correction = 1;
           coefficients[offset++] = n * correction * delta;
           position++;
@@ -34259,17 +34346,17 @@ var JpxImage = (function JpxImageClosure
           items: coefficients
         });
 
         b++;
       }
     }
 
     var transformation = codingStyleParameters.transformation;
-    var transform = transformation == 0 ? new IrreversibleTransform() :
+    var transform = transformation === 0 ? new IrreversibleTransform() :
       new ReversibleTransform();
     var result = transform.calculate(subbandCoefficients,
       component.tcx0, component.tcy0);
     return {
       left: component.tcx0,
       top: component.tcy0,
       width: result.width,
       height: result.height,
@@ -34589,35 +34676,35 @@ var JpxImage = (function JpxImageClosure
         this.a -= qeIcx;
 
         if (this.chigh < qeIcx) {
           var d = this.exchangeLps(cx);
           this.renormD();
           return d;
         } else {
           this.chigh -= qeIcx;
-          if ((this.a & 0x8000) == 0) {
+          if ((this.a & 0x8000) === 0) {
             var d = this.exchangeMps(cx);
             this.renormD();
             return d;
           } else {
             return cx.mps;
           }
         }
       },
       renormD: function ArithmeticDecoder_renormD() {
         do {
-          if (this.ct == 0)
+          if (this.ct === 0)
             this.byteIn();
 
           this.a <<= 1;
           this.chigh = ((this.chigh << 1) & 0xFFFF) | ((this.clow >> 15) & 1);
           this.clow = (this.clow << 1) & 0xFFFF;
           this.ct--;
-        } while ((this.a & 0x8000) == 0);
+        } while ((this.a & 0x8000) === 0);
       },
       exchangeMps: function ArithmeticDecoder_exchangeMps(cx) {
         var d;
         var qeTableIcx = QeTable[cx.index];
         if (this.a < qeTableIcx.qe) {
           d = 1 - cx.mps;
 
           if (qeTableIcx.switchFlag == 1) {
@@ -34843,22 +34930,22 @@ var JpxImage = (function JpxImageClosure
             for (var i1 = 0; i1 < 4; i1++) {
               var i = i0 + i1;
               if (i >= height)
                 break;
               var index = i * width + j;
 
               // significant but not those that have just become
               if (!coefficentsMagnitude[index] ||
-                (processingFlags[index] & processedMask) != 0)
+                (processingFlags[index] & processedMask) !== 0)
                 continue;
 
               var contextLabel = 16;
               if ((processingFlags[index] &
-                firstMagnitudeBitMask) != 0) {
+                firstMagnitudeBitMask) !== 0) {
                 processingFlags[i * width + j] ^= firstMagnitudeBitMask;
                 // first refinement
                 var significance = neighborsSignificance[index];
                 var sumOfSignificance = (significance & 3) +
                   ((significance >> 2) & 3) + ((significance >> 4) & 7);
                 contextLabel = sumOfSignificance >= 1 ? 15 : 14;
               }
 
@@ -34889,24 +34976,24 @@ var JpxImage = (function JpxImageClosure
         var twoRowsDown = width * 2;
         var threeRowsDown = width * 3;
         for (var i0 = 0; i0 < height; i0 += 4) {
           for (var j = 0; j < width; j++) {
             var index0 = i0 * width + j;
             // using the property: labels[neighborsSignificance[index]] == 0
             // when neighborsSignificance[index] == 0
             var allEmpty = i0 + 3 < height &&
-              processingFlags[index0] == 0 &&
-              processingFlags[index0 + oneRowDown] == 0 &&
-              processingFlags[index0 + twoRowsDown] == 0 &&
-              processingFlags[index0 + threeRowsDown] == 0 &&
-              neighborsSignificance[index0] == 0 &&
-              neighborsSignificance[index0 + oneRowDown] == 0 &&
-              neighborsSignificance[index0 + twoRowsDown] == 0 &&
-              neighborsSignificance[index0 + threeRowsDown] == 0;
+              processingFlags[index0] === 0 &&
+              processingFlags[index0 + oneRowDown] === 0 &&
+              processingFlags[index0 + twoRowsDown] === 0 &&
+              processingFlags[index0 + threeRowsDown] === 0 &&
+              neighborsSignificance[index0] === 0 &&
+              neighborsSignificance[index0 + oneRowDown] === 0 &&
+              neighborsSignificance[index0 + twoRowsDown] === 0 &&
+              neighborsSignificance[index0 + threeRowsDown] === 0;
             var i1 = 0, index = index0;
             var cx, i;
             if (allEmpty) {
               cx = this.runLengthContext;
               var hasSignificantCoefficent = decoder.readBit(cx);
               if (!hasSignificantCoefficent) {
                 bitsDecoded[index0]++;
                 bitsDecoded[index0 + oneRowDown]++;
@@ -34932,17 +35019,17 @@ var JpxImage = (function JpxImageClosure
               i1++;
             }
             for (; i1 < 4; i1++, index += width) {
               i = i0 + i1;
               if (i >= height)
                 break;
 
               if (coefficentsMagnitude[index] ||
-                (processingFlags[index] & processedMask) != 0)
+                (processingFlags[index] & processedMask) !== 0)
                 continue;
 
               var contextLabel = labels[neighborsSignificance[index]];
               cx = contexts[contextLabel];
               var decision = decoder.readBit(cx);
               if (decision == 1) {
                 var sign = this.decodeSignBit(i, j);
                 coefficentsSign[index] = sign;
@@ -35018,17 +35105,17 @@ var JpxImage = (function JpxImageClosure
         2 * bufferPadding);
       var buffer = new Float32Array(bufferLength);
       var bufferOut = new Float32Array(bufferLength);
 
       // Section F.3.4 HOR_SR
       for (var v = 0; v < height; v++) {
         if (width == 1) {
           // if width = 1, when u0 even keep items as is, when odd divide by 2
-          if ((u0 % 1) != 0) {
+          if ((u0 % 1) !== 0) {
             items[v * width] /= 2;
           }
           continue;
         }
 
         var k = v * width;
         var l = bufferPadding;
         for (var u = 0; u < width; u++, k++, l++)
@@ -35053,17 +35140,17 @@ var JpxImage = (function JpxImageClosure
         for (var u = 0; u < width; u++, k++, l++)
           items[k] = bufferOut[l];
       }
 
       // Section F.3.5 VER_SR
       for (var u = 0; u < width; u++) {
         if (height == 1) {
           // if height = 1, when v0 even keep items as is, when odd divide by 2
-          if ((v0 % 1) != 0) {
+          if ((v0 % 1) !== 0) {
             items[u] /= 2;
           }
           continue;
         }
 
         var k = u;
         var l = bufferPadding;
         for (var v = 0; v < height; v++, k += width, l++)
@@ -35280,35 +35367,35 @@ var Jbig2Image = (function Jbig2ImageClo
         this.a -= qeIcx;
 
         if (this.chigh < qeIcx) {
           var d = this.exchangeLps(cx);
           this.renormD();
           return d;
         } else {
           this.chigh -= qeIcx;
-          if ((this.a & 0x8000) == 0) {
+          if ((this.a & 0x8000) === 0) {
             var d = this.exchangeMps(cx);
             this.renormD();
             return d;
           } else {
             return cx.mps;
           }
         }
       },
       renormD: function ArithmeticDecoder_renormD() {
         do {
-          if (this.ct == 0)
+          if (this.ct === 0)
             this.byteIn();
 
           this.a <<= 1;
           this.chigh = ((this.chigh << 1) & 0xFFFF) | ((this.clow >> 15) & 1);
           this.clow = (this.clow << 1) & 0xFFFF;
           this.ct--;
-        } while ((this.a & 0x8000) == 0);
+        } while ((this.a & 0x8000) === 0);
       },
       exchangeMps: function ArithmeticDecoder_exchangeMps(cx) {
         var d;
         var qeTableIcx = QeTable[cx.index];
         if (this.a < qeTableIcx.qe) {
           d = 1 - cx.mps;
 
           if (qeTableIcx.switchFlag == 1) {
@@ -35418,17 +35505,17 @@ var Jbig2Image = (function Jbig2ImageClo
         case 6:
           if (bit) break;
           state = 7;
           toRead = 12;
           offset = 340;
           break;
         default:
           v = v * 2 + bit;
-          if (--toRead == 0)
+          if (--toRead === 0)
             state = 0;
           continue;
       }
       state++;
     }
     v += offset;
     return !s ? v : v > 0 ? -v : null;
   }
@@ -35594,27 +35681,27 @@ var Jbig2Image = (function Jbig2ImageClo
     return bitmap;
   }
 
   // 6.3.2 Generic Refinement Region Decoding Procedure
   function decodeRefinement(width, height, templateIndex, referenceBitmap,
                             offsetX, offsetY, prediction, at,
                             decodingContext) {
     var codingTemplate = RefinementTemplates[templateIndex].coding;
-    if (templateIndex == 0)
+    if (templateIndex === 0)
       codingTemplate = codingTemplate.concat([at[0]]);
     var codingTemplateLength = codingTemplate.length;
     var codingTemplateX = new Int32Array(codingTemplateLength);
     var codingTemplateY = new Int32Array(codingTemplateLength);
     for (var k = 0; k < codingTemplateLength; k++) {
       codingTemplateX[k] = codingTemplate[k].x;
       codingTemplateY[k] = codingTemplate[k].y;
     }
     var referenceTemplate = RefinementTemplates[templateIndex].reference;
-    if (templateIndex == 0)
+    if (templateIndex === 0)
       referenceTemplate = referenceTemplate.concat([at[1]]);
     var referenceTemplateLength = referenceTemplate.length;
     var referenceTemplateX = new Int32Array(referenceTemplateLength);
     var referenceTemplateY = new Int32Array(referenceTemplateLength);
     for (var k = 0; k < referenceTemplateLength; k++) {
       referenceTemplateX[k] = referenceTemplate[k].x;
       referenceTemplateY[k] = referenceTemplate[k].y;
     }
@@ -35687,17 +35774,17 @@ var Jbig2Image = (function Jbig2ImageClo
 
     while (newSymbols.length < numberOfNewSymbols) {
       var deltaHeight = decodeInteger(contextCache, 'IADH', decoder); // 6.5.6
       currentHeight += deltaHeight;
       var currentWidth = 0;
       var totalWidth = 0;
       while (true) {
         var deltaWidth = decodeInteger(contextCache, 'IADW', decoder); // 6.5.7
-        if (deltaWidth == null)
+        if (deltaWidth === null)
           break; // OOB
         currentWidth += deltaWidth;
         totalWidth += currentWidth;
         var bitmap;
         if (refinement) {
           // 6.5.8.2 Refinement/aggregate-coded symbol bitmap
           var numberOfInstances = decodeInteger(contextCache, 'IAAI', decoder);
           if (numberOfInstances > 1)
@@ -35814,17 +35901,17 @@ var Jbig2Image = (function Jbig2ImageClo
                     ' is not supported');
           }
         }
 
         currentS += symbolWidth - 1;
         i++;
 
         var deltaS = decodeInteger(contextCache, 'IADS', decoder); // 6.4.8
-        if (deltaS == null)
+        if (deltaS === null)
           break; // OOB
         currentS += deltaS + dsOffset;
       } while (true);
     }
     return bitmap;
   }
 
   function readSegmentHeader(data, start) {
@@ -35936,17 +36023,17 @@ var Jbig2Image = (function Jbig2ImageClo
         dictionary.bitmapSizeSelector = (dictionaryFlags >> 6) & 1;
         dictionary.aggregationInstancesSelector = (dictionaryFlags >> 7) & 1;
         dictionary.bitmapCodingContextUsed = !!(dictionaryFlags & 256);
         dictionary.bitmapCodingContextRetained = !!(dictionaryFlags & 512);
         dictionary.template = (dictionaryFlags >> 10) & 3;
         dictionary.refinementTemplate = (dictionaryFlags >> 12) & 1;
         position += 2;
         if (!dictionary.huffman) {
-          var atLength = dictionary.template == 0 ? 4 : 1;
+          var atLength = dictionary.template === 0 ? 4 : 1;
           var at = [];
           for (var i = 0; i < atLength; i++) {
             at.push({
               x: readInt8(data, position),
               y: readInt8(data, position + 1)
             });
             position += 2;
           }
@@ -36022,17 +36109,17 @@ var Jbig2Image = (function Jbig2ImageClo
         var genericRegion = {};
         genericRegion.info = readRegionSegmentInformation(data, position);
         position += RegionSegmentInformationFieldLength;
         var genericRegionSegmentFlags = data[position++];
         genericRegion.mmr = !!(genericRegionSegmentFlags & 1);
         genericRegion.template = (genericRegionSegmentFlags >> 1) & 3;
         genericRegion.prediction = !!(genericRegionSegmentFlags & 8);
         if (!genericRegion.mmr) {
-          var atLength = genericRegion.template == 0 ? 4 : 1;
+          var atLength = genericRegion.template === 0 ? 4 : 1;
           var at = [];
           for (var i = 0; i < atLength; i++) {
             at.push({
               x: readInt8(data, position),
               y: readInt8(data, position + 1)
             });
             position += 2;
           }
@@ -36285,21 +36372,21 @@ var bidi = PDFJS.bidi = (function bidiCl
     'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
     'AL', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM',
     'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'ON', 'NSM',
     'NSM', 'NSM', 'NSM', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
     'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL'
   ];
 
   function isOdd(i) {
-    return (i & 1) != 0;
+    return (i & 1) !== 0;
   }
 
   function isEven(i) {
-    return (i & 1) == 0;
+    return (i & 1) === 0;
   }
 
   function findUnequal(arr, start, value) {
     var j;
     for (var j = start, jj = arr.length; j < jj; ++j) {
       if (arr[j] != value)
         return j;
     }
@@ -36363,18 +36450,18 @@ var bidi = PDFJS.bidi = (function bidiCl
   function BidiResult(str, isLTR) {
     this.str = str;
     this.ltr = isLTR;
   }
 
   function bidi(str, startLevel) {
     var isLTR = true;
     var strLength = str.length;
-    if (strLength == 0)
-      return new BidiResult(str, ltr);
+    if (strLength === 0)
+      return new BidiResult(str, isLTR);
 
     // get types, fill arrays
 
     var chars = [];
     var types = [];
     var oldtypes = [];
     var numBidi = 0;
 
@@ -36397,17 +36484,17 @@ var bidi = PDFJS.bidi = (function bidiCl
 
       oldtypes[i] = types[i] = charType;
     }
 
     // detect the bidi method
     //  if there are no rtl characters then no bidi needed
     //  if less than 30% chars are rtl then string is primarily ltr
     //  if more than 30% chars are rtl then string is primarily rtl
-    if (numBidi == 0) {
+    if (numBidi === 0) {
       isLTR = true;
       return new BidiResult(str, isLTR);
     }
 
     if (startLevel == -1) {
       if ((strLength / numBidi) < 0.3) {
         isLTR = true;
         startLevel = 0;
--- a/browser/extensions/pdfjs/content/web/l10n.js
+++ b/browser/extensions/pdfjs/content/web/l10n.js
@@ -89,26 +89,21 @@
     window.dispatchEvent(evtObject);
   });
 
   // Public API
   document.mozL10n = {
     // get a localized string
     get: translateString,
 
-    // get|set the document language and direction
-    get language() {
-      return {
-        // get|set the document language (ISO-639-1)
-        get code() { return gLanguage; },
+    // get the document language
+    getLanguage: function() { return gLanguage; },
 
-        // get the direction (ltr|rtl) of the current language
-        get direction() {
-          // http://www.w3.org/International/questions/qa-scripts
-          // Arabic, Hebrew, Farsi, Pashto, Urdu
-          var rtlList = ['ar', 'he', 'fa', 'ps', 'ur'];
-          return (rtlList.indexOf(gLanguage) >= 0) ? 'rtl' : 'ltr';
-        }
-      };
+    // get the direction (ltr|rtl) of the current language
+    getDirection: function() {
+      // http://www.w3.org/International/questions/qa-scripts
+      // Arabic, Hebrew, Farsi, Pashto, Urdu
+      var rtlList = ['ar', 'he', 'fa', 'ps', 'ur'];
+      return (rtlList.indexOf(gLanguage) >= 0) ? 'rtl' : 'ltr';
     }
   };
 })(this);
 
--- a/browser/extensions/pdfjs/content/web/viewer.css
+++ b/browser/extensions/pdfjs/content/web/viewer.css
@@ -660,17 +660,16 @@ html[dir='rtl'] .dropdownToolbarButton {
   background-color: hsla(0,0%,0%,.4);
   border-color: hsla(0,0%,0%,.4) hsla(0,0%,0%,.5) hsla(0,0%,0%,.55);
   box-shadow: 0 1px 1px hsla(0,0%,0%,.2) inset,
               0 0 1px hsla(0,0%,0%,.3) inset,
               0 1px 0 hsla(0,0%,100%,.05);
 }
 
 .dropdownToolbarButton {
-  min-width: 120px;
   max-width: 120px;
   padding: 3px 2px 2px;
   overflow: hidden;
   background: url(images/toolbarButton-menuArrows.png) no-repeat;
 }
 html[dir='ltr'] .dropdownToolbarButton {
   background-position: 95%;
 }
--- a/browser/extensions/pdfjs/content/web/viewer.html
+++ b/browser/extensions/pdfjs/content/web/viewer.html
@@ -9,17 +9,17 @@ You may obtain a copy of the License at
     http://www.apache.org/licenses/LICENSE-2.0
 
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 -->
-<html dir="ltr">
+<html dir="ltr" mozdisallowselectionprint>
   <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="text/javascript" src="l10n.js"></script>
@@ -76,18 +76,18 @@ limitations under the License.
           <input type="checkbox" id="findMatchCase" class="toolbarField">
           <label for="findMatchCase" class="toolbarLabel" tabindex="24" data-l10n-id="find_match_case_label">Match case</label>
           <span id="findMsg" class="toolbarLabel"></span>
         </div>
         <div class="toolbar">
           <div id="toolbarContainer">
             <div id="toolbarViewer">
               <div id="toolbarViewerLeft">
-                <button id="sidebarToggle" class="toolbarButton" title="Toggle Sidebar" tabindex="3" data-l10n-id="toggle_slider">
-                  <span data-l10n-id="toggle_slider_label">Toggle Sidebar</span>
+                <button id="sidebarToggle" class="toolbarButton" title="Toggle Sidebar" tabindex="3" data-l10n-id="toggle_sidebar">
+                  <span data-l10n-id="toggle_sidebar_label">Toggle Sidebar</span>
                 </button>
                 <div class="toolbarButtonSpacer"></div>
                 <button id="viewFind" class="toolbarButton group" title="Find in Document" tabindex="4" data-l10n-id="findbar">
                    <span data-l10n-id="findbar_label">Find</span>
                 </button>
                 <div class="splitToolbarButton">
                   <button class="toolbarButton pageUp" title="Previous Page" id="previous" tabindex="5" data-l10n-id="previous">
                     <span data-l10n-id="previous_label">Previous</span>
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -52,17 +52,23 @@ function getFileName(url) {
   var query = url.indexOf('?');
   var end = Math.min(
     anchor > 0 ? anchor : url.length,
     query > 0 ? query : url.length);
   return url.substring(url.lastIndexOf('/', end) + 1, end);
 }
 
 function scrollIntoView(element, spot) {
+  // Assuming offsetParent is available (it's not available when viewer is in
+  // hidden iframe or object). We have to scroll: if the offsetParent is not set
+  // producing the error. See also animationStartedClosure.
   var parent = element.offsetParent, offsetY = element.offsetTop;
+  if (!parent) {
+    error('offsetParent is not set -- cannot scroll');
+  }
   while (parent.clientHeight == parent.scrollHeight) {
     offsetY += parent.offsetTop;
     parent = parent.offsetParent;
     if (!parent)
       return; // no need to scroll
   }
   if (spot)
     offsetY += spot.top;
@@ -1033,16 +1039,23 @@ var PDFView = {
           'An error occurred while loading the PDF.');
 
         if (exception && exception.name === 'InvalidPDFException') {
           // change error message also for other builds
           var loadingErrorMessage = mozL10n.get('invalid_file_error', null,
                                         'Invalid or corrupted PDF file.');
         }
 
+        if (exception && exception.name === 'MissingPDFException') {
+          // special message for missing PDF's
+          var loadingErrorMessage = mozL10n.get('missing_file_error', null,
+                                        'Missing PDF file.');
+
+        }
+
         var loadingIndicator = document.getElementById('loading');
         loadingIndicator.textContent = mozL10n.get('loading_error_indicator',
           null, 'Error');
         var moreInfo = {
           message: message
         };
         self.error(loadingErrorMessage, moreInfo);
         self.loading = false;
@@ -1280,17 +1293,18 @@ var PDFView = {
     });
 
     var destinationsPromise = pdfDocument.getDestinations();
     destinationsPromise.then(function(destinations) {
       self.destinations = destinations;
     });
 
     // outline and initial view depends on destinations and pagesRefMap
-    var promises = [pagesPromise, destinationsPromise, storePromise];
+    var promises = [pagesPromise, destinationsPromise, storePromise,
+                    PDFView.animationStartedPromise];
     PDFJS.Promise.all(promises).then(function() {
       pdfDocument.getOutline().then(function(outline) {
         self.outline = new DocumentOutlineView(outline);
       });
 
       var storedHash = null;
       if (store.get('exists', false)) {
         var page = store.get('page', '1');
@@ -1321,16 +1335,21 @@ var PDFView = {
           pdfTitle = metadata.get('dc:title');
       }
 
       if (!pdfTitle && info && info['Title'])
         pdfTitle = info['Title'];
 
       if (pdfTitle)
         self.setTitle(pdfTitle + ' - ' + document.title);
+
+      if (info.IsAcroFormPresent) {
+        // AcroForm/XFA was found
+        PDFView.fallback();
+      }
     });
   },
 
   setInitialView: function pdfViewSetInitialView(storedHash, scale) {
     // Reset the current scale, as otherwise the page's scale might not get
     // updated if the zoom level stayed the same.
     this.currentScale = 0;
     this.currentScaleValue = null;
@@ -1931,20 +1950,16 @@ var PageView = function pageView(contain
               bindLink(link, ('dest' in item) ? item.dest : null);
             div.appendChild(link);
             break;
           case 'Text':
             var textAnnotation = createTextAnnotation(item);
             if (textAnnotation)
               div.appendChild(textAnnotation);
             break;
-          case 'Widget':
-            // TODO: support forms
-            PDFView.fallback();
-            break;
         }
       }
     });
   }
 
   this.getPagePoint = function pageViewGetPagePoint(x, y) {
     return this.viewport.convertToPdfPoint(x, y);
   };
@@ -2054,30 +2069,38 @@ var PageView = function pageView(contain
       CustomStyle.setProp('transformOrigin' , canvas, '0% 0%');
       if (textLayerDiv) {
         CustomStyle.setProp('transform' , textLayerDiv, cssScale);
         CustomStyle.setProp('transformOrigin' , textLayerDiv, '0% 0%');
       }
     }
 
     var ctx = canvas.getContext('2d');
+    // TODO(mack): use data attributes to store these
+    ctx._scaleX = outputScale.sx;
+    ctx._scaleY = outputScale.sy;
     ctx.save();
     ctx.fillStyle = 'rgb(255, 255, 255)';
     ctx.fillRect(0, 0, canvas.width, canvas.height);
     ctx.restore();
     if (outputScale.scaled) {
       ctx.scale(outputScale.sx, outputScale.sy);
     }
     // Checking if document fonts are used only once
     var checkIfDocumentFontsUsed = !PDFView.pdfDocument.embeddedFontsUsed;
 
     // Rendering area
 
     var self = this;
+    var renderingWasReset = false;
     function pageViewDrawCallback(error) {
+      if (renderingWasReset) {
+        return;
+      }
+
       self.renderingState = RenderingStates.FINISHED;
 
       if (self.loadingIconDiv) {
         div.removeChild(self.loadingIconDiv);
         delete self.loadingIconDiv;
       }
 
       if (checkIfDocumentFontsUsed && PDFView.pdfDocument.embeddedFontsUsed &&
@@ -2100,16 +2123,22 @@ var PageView = function pageView(contain
       callback();
     }
 
     var renderContext = {
       canvasContext: ctx,
       viewport: this.viewport,
       textLayer: textLayer,
       continueCallback: function pdfViewcContinueCallback(cont) {
+        if (self.renderingState === RenderingStates.INITIAL) {
+          // The page update() was called, we just need to abort any rendering.
+          renderingWasReset = true;
+          return;
+        }
+
         if (PDFView.highestPriorityPage !== 'page' + self.id) {
           self.renderingState = RenderingStates.PAUSED;
           self.resume = function resumeCallback() {
             self.renderingState = RenderingStates.RUNNING;
             cont();
           };
           return;
         }
@@ -2880,19 +2909,29 @@ document.addEventListener('DOMContentLoa
       window.print();
     });
 
   document.getElementById('download').addEventListener('click',
     function() {
       PDFView.download();
     });
 
+  document.getElementById('pageNumber').addEventListener('click',
+    function() {
+      this.select();
+    });
+
   document.getElementById('pageNumber').addEventListener('change',
     function() {
-      PDFView.page = this.value;
+      // Handle the user inputting a floating point number.
+      PDFView.page = (this.value | 0);
+
+      if (this.value !== (this.value | 0).toString()) {
+        this.value = PDFView.page;
+      }
     });
 
   document.getElementById('scaleSelect').addEventListener('change',
     function() {
       PDFView.parseScale(this.value);
     });
 
   document.getElementById('first_page').addEventListener('click',
@@ -3034,16 +3073,26 @@ function selectScaleOption(value) {
     option.selected = true;
     predefinedValueFound = true;
   }
   return predefinedValueFound;
 }
 
 window.addEventListener('localized', function localized(evt) {
   document.getElementsByTagName('html')[0].dir = mozL10n.getDirection();
+
+  // Adjust the width of the zoom box to fit the content.
+  var container = document.getElementById('scaleSelectContainer');
+  var select = document.getElementById('scaleSelect');
+
+  select.setAttribute('style', 'min-width: inherit;');
+  var width = select.clientWidth + 8;
+  container.setAttribute('style', 'min-width: ' + width + 'px; ' +
+                                  'max-width: ' + width + 'px;');
+  select.setAttribute('style', 'min-width: ' + (width + 20) + 'px;');
 }, true);
 
 window.addEventListener('scalechange', function scalechange(evt) {
   var customScaleOption = document.getElementById('customScaleOption');
   customScaleOption.selected = false;
 
   if (!evt.resetAutoSettings &&
        (document.getElementById('pageWidthOption').selected ||
@@ -3275,9 +3324,24 @@ window.addEventListener('afterprint', fu
     }
   }
 
   window.addEventListener('fullscreenchange', fullscreenChange, false);
   window.addEventListener('mozfullscreenchange', fullscreenChange, false);
   window.addEventListener('webkitfullscreenchange', fullscreenChange, false);
 })();
 
-
+(function animationStartedClosure() {
+  // The offsetParent is not set until the pdf.js iframe or object is visible.
+  // Waiting for first animation.
+  var requestAnimationFrame = window.requestAnimationFrame ||
+                              window.mozRequestAnimationFrame ||
+                              window.webkitRequestAnimationFrame ||
+                              window.oRequestAnimationFrame ||
+                              window.msRequestAnimationFrame ||
+                              function startAtOnce(callback) { callback(); };
+  PDFView.animationStartedPromise = new PDFJS.Promise();
+  requestAnimationFrame(function onAnimationFrame() {
+    PDFView.animationStartedPromise.resolve();
+  });
+})();
+
+
--- a/browser/locales/en-US/pdfviewer/viewer.properties
+++ b/browser/locales/en-US/pdfviewer/viewer.properties
@@ -39,18 +39,18 @@ open_file_label=Open
 download.title=Download
 download_label=Download
 bookmark.title=Current view (copy or open in new window)
 bookmark_label=Current View
 
 # Tooltips and alt text for side panel toolbar buttons
 # (the _label strings are alt text for the buttons, the .title strings are
 # tooltips)
-toggle_slider.title=Toggle Slider
-toggle_slider_label=Toggle Slider
+toggle_sidebar.title=Toggle Sidebar
+toggle_sidebar_label=Toggle Sidebar
 outline.title=Show Document Outline
 outline_label=Document Outline
 thumbs.title=Show Thumbnails
 thumbs_label=Thumbnails
 findbar.title=Find in Document
 findbar_label=Find
 
 # Document outline messages
@@ -106,16 +106,17 @@ page_scale_width=Page Width
 page_scale_fit=Page Fit
 page_scale_auto=Automatic Zoom
 page_scale_actual=Actual Size
 
 # Loading indicator messages
 loading_error_indicator=Error
 loading_error=An error occurred while loading the PDF.
 invalid_file_error=Invalid or corrupted PDF file.
+missing_file_error=Missing PDF file.
 
 # LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip.
 # "{{type}}" will be replaced with an annotation type from a list defined in
 # the PDF spec (32000-1:2008 Table 169 – Annotation types).
 # Some common types are e.g.: "Check", "Text", "Comment", "Note"
 text_annotation_type=[{{type}} Annotation]
 request_password=PDF is protected by a password:
 
--- a/browser/themes/gnomestripe/browser.css
+++ b/browser/themes/gnomestripe/browser.css
@@ -1284,16 +1284,23 @@ toolbar[iconsize="small"] #webrtc-status
 #plugins-notification-icon {
   list-style-image: url(chrome://mozapps/skin/plugins/pluginGeneric-16.png);
 }
 
 #blocked-plugins-notification-icon {
   list-style-image: url(chrome://mozapps/skin/plugins/notifyPluginBlocked.png);
 }
 
+#notification-popup-box[hidden] {
+  /* Override display:none to make the pluginBlockedNotification animation work
+     when showing the notification repeatedly. */
+  display: -moz-box;
+  visibility: collapse;
+}
+
 #blocked-plugins-notification-icon[showing] {
   animation: pluginBlockedNotification 500ms ease 0s 5 alternate both;
 }
 
 @keyframes pluginBlockedNotification {
   from {
     opacity: 0;
   }
--- a/browser/themes/pinstripe/browser.css
+++ b/browser/themes/pinstripe/browser.css
@@ -33,16 +33,25 @@
   border-color: transparent !important;
 }
 
 #main-window {
   -moz-appearance: none;
   background-color: #eeeeee;
 }
 
+#titlebar-buttonbox-container,
+#titlebar:not(:-moz-lwtheme) {
+  display: none;
+}
+
+#main-window[drawintitlebar="true"] > #titlebar {
+  height: 22px;
+}
+
 #main-window[chromehidden~="toolbar"][chromehidden~="location"][chromehidden~="directories"] {
   border-top: 1px solid rgba(0,0,0,0.65);
 }
 
 #navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar) {
   -moz-box-align: center;
   padding: 2px 4px;
 }
@@ -3071,16 +3080,23 @@ toolbarbutton.chevron > .toolbarbutton-m
 }
 
 @media (min-resolution: 2dppx) {
   #blocked-plugins-notification-icon {
     list-style-image: url(chrome://mozapps/skin/plugins/pluginBlocked.png);
   }
 }
 
+#notification-popup-box[hidden] {
+  /* Override display:none to make the pluginBlockedNotification animation work
+     when showing the notification repeatedly. */
+  display: -moz-box;
+  visibility: collapse;
+}
+
 #blocked-plugins-notification-icon[showing] {
   animation: pluginBlockedNotification 500ms ease 0s 5 alternate both;
 }
 
 @keyframes pluginBlockedNotification {
   from {
     opacity: 0;
   }
--- a/browser/themes/winstripe/browser.css
+++ b/browser/themes/winstripe/browser.css
@@ -2348,16 +2348,23 @@ toolbarbutton.bookmark-item[dragover="tr
 #plugins-notification-icon {
   list-style-image: url(chrome://mozapps/skin/plugins/pluginGeneric-16.png);
 }
 
 #blocked-plugins-notification-icon {
   list-style-image: url(chrome://mozapps/skin/plugins/notifyPluginBlocked.png);
 }
 
+#notification-popup-box[hidden] {
+  /* Override display:none to make the pluginBlockedNotification animation work
+     when showing the notification repeatedly. */
+  display: -moz-box;
+  visibility: collapse;
+}
+
 #blocked-plugins-notification-icon[showing] {
   animation: pluginBlockedNotification 500ms ease 0s 5 alternate both;
 }
 
 @keyframes pluginBlockedNotification {
   from {
     opacity: 0;
   }
--- a/content/base/src/nsDOMDataChannel.cpp
+++ b/content/base/src/nsDOMDataChannel.cpp
@@ -21,17 +21,17 @@ extern PRLogModuleInfo* GetDataChannelLo
 #include "nsDOMDataChannel.h"
 #include "nsIDOMFile.h"
 #include "nsIJSNativeInitializer.h"
 #include "nsIDOMDataChannel.h"
 #include "nsIDOMMessageEvent.h"
 #include "nsDOMClassInfo.h"
 #include "nsDOMEventTargetHelper.h"
 
-#include "jsval.h"
+#include "js/Value.h"
 
 #include "nsError.h"
 #include "nsAutoPtr.h"
 #include "nsContentUtils.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsJSUtils.h"
 #include "nsNetUtil.h"
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -41,17 +41,17 @@ static WebGLenum InternalFormatForFormat
 //
 //  WebGL API
 //
 
 
 void
 WebGLContext::ActiveTexture(WebGLenum texture)
 {
-    if (!IsContextStable())
+    if (!IsContextStable()) 
         return;
 
     if (texture < LOCAL_GL_TEXTURE0 ||
         texture >= LOCAL_GL_TEXTURE0 + uint32_t(mGLMaxTextureUnits))
     {
         return ErrorInvalidEnum(
             "ActiveTexture: texture unit %d out of range. "
             "Accepted values range from TEXTURE0 to TEXTURE0 + %d. "
@@ -4189,16 +4189,25 @@ WebGLContext::CompileShader(WebGLShader 
 
         compiler = ShConstructCompiler((ShShaderType) shader->ShaderType(),
                                        SH_WEBGL_SPEC,
                                        targetShaderSourceLanguage,
                                        &resources);
 
         int compileOptions = SH_ATTRIBUTES_UNIFORMS |
                              SH_ENFORCE_PACKING_RESTRICTIONS;
+
+        // we want to do this everywhere, but:
+#ifndef XP_WIN // to do this on Windows, we need ANGLE r1719, 1733, 1734.
+#ifndef XP_MACOSX // to do this on Mac, we need to do it only on Mac OSX > 10.6 as this
+                  // causes the shader compiler in 10.6 to crash
+        compileOptions |= SH_CLAMP_INDIRECT_ARRAY_BOUNDS;
+#endif
+#endif
+
         if (useShaderSourceTranslation) {
             compileOptions |= SH_OBJECT_CODE
                             | SH_MAP_LONG_VARIABLE_NAMES;
 #ifdef XP_MACOSX
             if (gl->WorkAroundDriverBugs()) {
                 // Work around bug 665578 and bug 769810
                 if (gl->Vendor() == gl::GLContext::VendorATI) {
                     compileOptions |= SH_EMULATE_BUILT_IN_FUNCTIONS;
--- a/content/events/src/TextComposition.cpp
+++ b/content/events/src/TextComposition.cpp
@@ -101,17 +101,17 @@ TextComposition::NotifyIME(widget::Notif
 TextComposition::CompositionEventDispatcher::CompositionEventDispatcher(
                                                nsPresContext* aPresContext,
                                                nsINode* aEventTarget,
                                                uint32_t aEventMessage,
                                                const nsAString& aData) :
   mPresContext(aPresContext), mEventTarget(aEventTarget),
   mEventMessage(aEventMessage), mData(aData)
 {
-  mWidget = mPresContext->GetNearestWidget();
+  mWidget = mPresContext->GetRootWidget();
 }
 
 NS_IMETHODIMP
 TextComposition::CompositionEventDispatcher::Run()
 {
   if (!mPresContext->GetPresShell() ||
       mPresContext->GetPresShell()->IsDestroying()) {
     return NS_OK; // cannot dispatch any events anymore
--- a/content/events/src/nsIMEStateManager.cpp
+++ b/content/events/src/nsIMEStateManager.cpp
@@ -122,17 +122,17 @@ nsIMEStateManager::OnDestroyPresContext(
     }
   }
 
   if (aPresContext != sPresContext)
     return NS_OK;
 
   DestroyTextStateManager();
 
-  nsCOMPtr<nsIWidget> widget = sPresContext->GetNearestWidget();
+  nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget();
   if (widget) {
     IMEState newState = GetNewIMEState(sPresContext, nullptr);
     InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
                               InputContextAction::LOST_FOCUS);
     SetIMEState(newState, nullptr, widget, action);
   }
   NS_IF_RELEASE(sContent);
   sPresContext = nullptr;
@@ -152,17 +152,17 @@ nsIMEStateManager::OnRemoveContent(nsPre
 
     if (compositionInContent) {
       // Store the composition before accessing the native IME.
       TextComposition storedComposition = *compositionInContent;
       // Try resetting the native IME state.  Be aware, typically, this method
       // is called during the content being removed.  Then, the native
       // composition events which are caused by following APIs are ignored due
       // to unsafe to run script (in PresShell::HandleEvent()).
-      nsCOMPtr<nsIWidget> widget = aPresContext->GetNearestWidget();
+      nsCOMPtr<nsIWidget> widget = aPresContext->GetRootWidget();
       if (widget) {
         nsresult rv =
           storedComposition.NotifyIME(REQUEST_TO_CANCEL_COMPOSITION);
         if (NS_FAILED(rv)) {
           storedComposition.NotifyIME(REQUEST_TO_COMMIT_COMPOSITION);
         }
         // By calling the APIs, the composition may have been finished normally.
         compositionInContent =
@@ -182,17 +182,17 @@ nsIMEStateManager::OnRemoveContent(nsPre
   if (!sPresContext || !sContent ||
       !nsContentUtils::ContentIsDescendantOf(sContent, aContent)) {
     return NS_OK;
   }
 
   DestroyTextStateManager();
 
   // Current IME transaction should commit
-  nsCOMPtr<nsIWidget> widget = sPresContext->GetNearestWidget();
+  nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget();
   if (widget) {
     IMEState newState = GetNewIMEState(sPresContext, nullptr);
     InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
                               InputContextAction::LOST_FOCUS);
     SetIMEState(newState, nullptr, widget, action);
   }
 
   NS_IF_RELEASE(sContent);
@@ -214,17 +214,17 @@ nsresult
 nsIMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext,
                                          nsIContent* aContent,
                                          InputContextAction aAction)
 {
   bool focusActuallyChanging =
     (sContent != aContent || sPresContext != aPresContext);
 
   nsCOMPtr<nsIWidget> oldWidget =
-    sPresContext ? sPresContext->GetNearestWidget() : nullptr;
+    sPresContext ? sPresContext->GetRootWidget() : nullptr;
   if (oldWidget && focusActuallyChanging) {
     // If we're deactivating, we shouldn't commit composition forcibly because
     // the user may want to continue the composition.
     if (aPresContext) {
       NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget);
     }
   }
 
@@ -234,17 +234,17 @@ nsIMEStateManager::OnChangeFocusInternal
   }
 
   if (!aPresContext) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIWidget> widget =
     (sPresContext == aPresContext) ? oldWidget.get() :
-                                     aPresContext->GetNearestWidget();
+                                     aPresContext->GetRootWidget();
   if (!widget) {
     return NS_OK;
   }
 
   // Handle secure input mode for password field input.
   bool contentIsPassword = false;
   if (aContent && aContent->GetNameSpaceID() == kNameSpaceID_XHTML) {
     if (aContent->Tag() == nsGkAtoms::input) {
@@ -321,17 +321,17 @@ void
 nsIMEStateManager::OnClickInEditor(nsPresContext* aPresContext,
                                    nsIContent* aContent,
                                    nsIDOMMouseEvent* aMouseEvent)
 {
   if (sPresContext != aPresContext || sContent != aContent) {
     return;
   }
 
-  nsCOMPtr<nsIWidget> widget = aPresContext->GetNearestWidget();
+  nsCOMPtr<nsIWidget> widget = aPresContext->GetRootWidget();
   NS_ENSURE_TRUE_VOID(widget);
 
   bool isTrusted;
   nsresult rv = aMouseEvent->GetIsTrusted(&isTrusted);
   NS_ENSURE_SUCCESS_VOID(rv);
   if (!isTrusted) {
     return; // ignore untrusted event.
   }
@@ -379,17 +379,17 @@ nsIMEStateManager::OnFocusInEditor(nsPre
 void
 nsIMEStateManager::UpdateIMEState(const IMEState &aNewIMEState,
                                   nsIContent* aContent)
 {
   if (!sPresContext) {
     NS_WARNING("ISM doesn't know which editor has focus");
     return;
   }
-  nsCOMPtr<nsIWidget> widget = sPresContext->GetNearestWidget();
+  nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget();
   if (!widget) {
     NS_WARNING("focused widget is not found");
     return;
   }
 
   // If the nsTextStateManager instance isn't managing the editor's current
   // editable root content, the editor frame might be reframed.  We should
   // recreate the instance at that time.
@@ -678,17 +678,17 @@ nsIMEStateManager::NotifyIME(Notificatio
 
 // static
 nsresult
 nsIMEStateManager::NotifyIME(NotificationToIME aNotification,
                              nsPresContext* aPresContext)
 {
   NS_ENSURE_TRUE(aPresContext, NS_ERROR_INVALID_ARG);
 
-  nsIWidget* widget = aPresContext->GetNearestWidget();
+  nsIWidget* widget = aPresContext->GetRootWidget();
   if (!widget) {
     return NS_ERROR_NOT_AVAILABLE;
   }
   return NotifyIME(aNotification, widget);
 }
 
 void
 nsTextStateManager::Init(nsIWidget* aWidget,
@@ -1088,17 +1088,17 @@ void
 nsIMEStateManager::CreateTextStateManager()
 {
   if (sTextStateObserver) {
     NS_WARNING("text state observer has been there already");
     MOZ_ASSERT(sTextStateObserver->IsManaging(sPresContext, sContent));
     return;
   }
 
-  nsCOMPtr<nsIWidget> widget = sPresContext->GetNearestWidget();
+  nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget();
   if (!widget) {
     return; // Sometimes, there are no widgets.
   }
 
   // If it's not text ediable, we don't need to create nsTextStateManager.
   if (!IsEditableIMEState(widget)) {
     return;
   }
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -1087,21 +1087,27 @@ MediaStreamGraphImpl::ForceShutDown()
     EnsureImmediateWakeUpLocked(lock);
   }
 }
 
 namespace {
 
 class MediaStreamGraphThreadRunnable : public nsRunnable {
 public:
+  explicit MediaStreamGraphThreadRunnable(MediaStreamGraphImpl* aGraph)
+    : mGraph(aGraph)
+  {
+  }
   NS_IMETHOD Run()
   {
-    gGraph->RunThread();
+    mGraph->RunThread();
     return NS_OK;
   }
+private:
+  MediaStreamGraphImpl* mGraph;
 };
 
 class MediaStreamGraphShutDownRunnable : public nsRunnable {
 public:
   MediaStreamGraphShutDownRunnable(MediaStreamGraphImpl* aGraph) : mGraph(aGraph) {}
   NS_IMETHOD Run()
   {
     NS_ASSERTION(mGraph->mDetectedNotRunning,
@@ -1120,23 +1126,29 @@ public:
     return NS_OK;
   }
 private:
   MediaStreamGraphImpl* mGraph;
 };
 
 class MediaStreamGraphStableStateRunnable : public nsRunnable {
 public:
+  explicit MediaStreamGraphStableStateRunnable(MediaStreamGraphImpl* aGraph)
+    : mGraph(aGraph)
+  {
+  }
   NS_IMETHOD Run()
   {
-    if (gGraph) {
-      gGraph->RunInStableState();
+    if (mGraph) {
+      mGraph->RunInStableState();
     }
     return NS_OK;
   }
+private:
+  MediaStreamGraphImpl* mGraph;
 };
 
 /*
  * Control messages forwarded from main thread to graph manager thread
  */
 class CreateMessage : public ControlMessage {
 public:
   CreateMessage(MediaStream* aStream) : ControlMessage(aStream) {}
@@ -1195,27 +1207,29 @@ MediaStreamGraphImpl::RunInStableState()
       NS_DispatchToMainThread(event);
     }
 
     if (mLifecycleState == LIFECYCLE_THREAD_NOT_STARTED) {
       mLifecycleState = LIFECYCLE_RUNNING;
       // Start the thread now. We couldn't start it earlier because
       // the graph might exit immediately on finding it has no streams. The
       // first message for a new graph must create a stream.
-      nsCOMPtr<nsIRunnable> event = new MediaStreamGraphThreadRunnable();
+      nsCOMPtr<nsIRunnable> event = new MediaStreamGraphThreadRunnable(this);
       NS_NewThread(getter_AddRefs(mThread), event);
     }
 
     if (mCurrentTaskMessageQueue.IsEmpty()) {
       if (mLifecycleState == LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP && IsEmpty()) {
-        NS_ASSERTION(gGraph == this, "Not current graph??");
         // Complete shutdown. First, ensure that this graph is no longer used.
         // A new graph graph will be created if one is needed.
-        LOG(PR_LOG_DEBUG, ("Disconnecting MediaStreamGraph %p", gGraph));
-        gGraph = nullptr;
+        LOG(PR_LOG_DEBUG, ("Disconnecting MediaStreamGraph %p", this));
+        if (this == gGraph) {
+          // null out gGraph if that's the graph being shut down
+          gGraph = nullptr;
+        }
         // Asynchronously clean up old graph. We don't want to do this
         // synchronously because it spins the event loop waiting for threads
         // to shut down, and we don't want to do that in a stable state handler.
         mLifecycleState = LIFECYCLE_WAITING_FOR_THREAD_SHUTDOWN;
         nsCOMPtr<nsIRunnable> event = new MediaStreamGraphShutDownRunnable(this);
         NS_DispatchToMainThread(event);
       }
     } else {
@@ -1227,17 +1241,17 @@ MediaStreamGraphImpl::RunInStableState()
         EnsureNextIterationLocked(lock);
       }
 
       if (mLifecycleState == LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP) {
         mLifecycleState = LIFECYCLE_RUNNING;
         // Revive the MediaStreamGraph since we have more messages going to it.
         // Note that we need to put messages into its queue before reviving it,
         // or it might exit immediately.
-        nsCOMPtr<nsIRunnable> event = new MediaStreamGraphThreadRunnable();
+        nsCOMPtr<nsIRunnable> event = new MediaStreamGraphThreadRunnable(this);
         mThread->Dispatch(event, 0);
       }
     }
 
     mDetectedNotRunning = mLifecycleState > LIFECYCLE_RUNNING;
   }
 
   // Make sure we get a new current time in the next event loop task
@@ -1256,34 +1270,34 @@ static NS_DEFINE_CID(kAppShellCID, NS_AP
 void
 MediaStreamGraphImpl::EnsureRunInStableState()
 {
   NS_ASSERTION(NS_IsMainThread(), "main thread only");
 
   if (mPostedRunInStableState)
     return;
   mPostedRunInStableState = true;
-  nsCOMPtr<nsIRunnable> event = new MediaStreamGraphStableStateRunnable();
+  nsCOMPtr<nsIRunnable> event = new MediaStreamGraphStableStateRunnable(this);
   nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
   if (appShell) {
     appShell->RunInStableState(event);
   } else {
     NS_ERROR("Appshell already destroyed?");
   }
 }
 
 void
 MediaStreamGraphImpl::EnsureStableStateEventPosted()
 {
   mMonitor.AssertCurrentThreadOwns();
 
   if (mPostedRunInStableStateEvent)
     return;
   mPostedRunInStableStateEvent = true;
-  nsCOMPtr<nsIRunnable> event = new MediaStreamGraphStableStateRunnable();
+  nsCOMPtr<nsIRunnable> event = new MediaStreamGraphStableStateRunnable(this);
   NS_DispatchToMainThread(event);
 }
 
 void
 MediaStreamGraphImpl::AppendMessage(ControlMessage* aMessage)
 {
   NS_ASSERTION(NS_IsMainThread(), "main thread only");
   NS_ASSERTION(!aMessage->GetStream() ||
@@ -1295,19 +1309,20 @@ MediaStreamGraphImpl::AppendMessage(Cont
     // The graph control loop is not running and main thread cleanup has
     // happened. From now on we can't append messages to mCurrentTaskMessageQueue,
     // because that will never be processed again, so just RunDuringShutdown
     // this message.
     // This should only happen during forced shutdown.
     aMessage->RunDuringShutdown();
     delete aMessage;
     if (IsEmpty()) {
-      NS_ASSERTION(gGraph == this, "Switched managers during forced shutdown?");
-      gGraph = nullptr;
-      delete this;
+      if (gGraph == this) {
+        gGraph = nullptr;
+        delete this;
+      }
     }
     return;
   }
 
   mCurrentTaskMessageQueue.AppendElement(aMessage);
   EnsureRunInStableState();
 }
 
@@ -1318,23 +1333,30 @@ MediaStream::Init()
   mBlocked.SetAtAndAfter(graph->mCurrentTime, true);
   mExplicitBlockerCount.SetAtAndAfter(graph->mCurrentTime, true);
   mExplicitBlockerCount.SetAtAndAfter(graph->mStateComputedTime, false);
 }
 
 MediaStreamGraphImpl*
 MediaStream::GraphImpl()
 {
-  return gGraph;
+  return mGraph;
 }
 
 MediaStreamGraph*
 MediaStream::Graph()
 {
-  return gGraph;
+  return mGraph;
+}
+
+void
+MediaStream::SetGraphImpl(MediaStreamGraphImpl* aGraph)
+{
+  MOZ_ASSERT(!mGraph, "Should only be called once");
+  mGraph = aGraph;
 }
 
 StreamTime
 MediaStream::GraphTimeToStreamTime(GraphTime aTime)
 {
   return GraphImpl()->GraphTimeToStreamTime(this, aTime);
 }
 
@@ -1782,23 +1804,30 @@ MediaInputPort::Destroy()
     MediaInputPort* mPort;
   };
   GraphImpl()->AppendMessage(new Message(this));
 }
 
 MediaStreamGraphImpl*
 MediaInputPort::GraphImpl()
 {
-  return gGraph;
+  return mGraph;
 }
 
 MediaStreamGraph*
 MediaInputPort::Graph()
 {
-  return gGraph;
+  return mGraph;
+}
+
+void
+MediaInputPort::SetGraphImpl(MediaStreamGraphImpl* aGraph)
+{
+  MOZ_ASSERT(!mGraph, "Should only be called once");
+  mGraph = aGraph;
 }
 
 already_AddRefed<MediaInputPort>
 ProcessedMediaStream::AllocateInputPort(MediaStream* aStream, uint32_t aFlags)
 {
   // This method creates two references to the MediaInputPort: one for
   // the main thread, and one for the MediaStreamGraph.
   class Message : public ControlMessage {
@@ -1810,16 +1839,17 @@ ProcessedMediaStream::AllocateInputPort(
     {
       mPort->Init();
       // The graph holds its reference implicitly
       mPort.forget();
     }
     nsRefPtr<MediaInputPort> mPort;
   };
   nsRefPtr<MediaInputPort> port = new MediaInputPort(aStream, this, aFlags);
+  port->SetGraphImpl(GraphImpl());
   GraphImpl()->AppendMessage(new Message(port));
   return port.forget();
 }
 
 void
 ProcessedMediaStream::Finish()
 {
   class Message : public ControlMessage {
@@ -1925,31 +1955,37 @@ MediaStreamGraph::GetInstance()
   return gGraph;
 }
 
 SourceMediaStream*
 MediaStreamGraph::CreateSourceStream(nsDOMMediaStream* aWrapper)
 {
   SourceMediaStream* stream = new SourceMediaStream(aWrapper);
   NS_ADDREF(stream);
-  static_cast<MediaStreamGraphImpl*>(this)->AppendMessage(new CreateMessage(stream));
+  MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(this);
+  stream->SetGraphImpl(graph);
+  graph->AppendMessage(new CreateMessage(stream));
   return stream;
 }
 
 ProcessedMediaStream*
 MediaStreamGraph::CreateTrackUnionStream(nsDOMMediaStream* aWrapper)
 {
   TrackUnionStream* stream = new TrackUnionStream(aWrapper);
   NS_ADDREF(stream);
-  static_cast<MediaStreamGraphImpl*>(this)->AppendMessage(new CreateMessage(stream));
+  MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(this);
+  stream->SetGraphImpl(graph);
+  graph->AppendMessage(new CreateMessage(stream));
   return stream;
 }
 
 AudioNodeStream*
 MediaStreamGraph::CreateAudioNodeStream(AudioNodeEngine* aEngine)
 {
   AudioNodeStream* stream = new AudioNodeStream(aEngine);
   NS_ADDREF(stream);
-  static_cast<MediaStreamGraphImpl*>(this)->AppendMessage(new CreateMessage(stream));
+  MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(this);
+  stream->SetGraphImpl(graph);
+  graph->AppendMessage(new CreateMessage(stream));
   return stream;
 }
 
 }
--- a/content/media/MediaStreamGraph.h
+++ b/content/media/MediaStreamGraph.h
@@ -267,30 +267,35 @@ public:
     , mGraphUpdateIndices(0)
     , mFinished(false)
     , mNotifiedFinished(false)
     , mNotifiedBlocked(false)
     , mWrapper(aWrapper)
     , mMainThreadCurrentTime(0)
     , mMainThreadFinished(false)
     , mMainThreadDestroyed(false)
+    , mGraph(nullptr)
   {
   }
   virtual ~MediaStream()
   {
     NS_ASSERTION(mMainThreadDestroyed, "Should have been destroyed already");
     NS_ASSERTION(mMainThreadListeners.IsEmpty(),
                  "All main thread listeners should have been removed");
   }
 
   /**
    * Returns the graph that owns this stream.
    */
   MediaStreamGraphImpl* GraphImpl();
   MediaStreamGraph* Graph();
+  /**
+   * Sets the graph that owns this stream.  Should only be called once.
+   */
+  void SetGraphImpl(MediaStreamGraphImpl* aGraph);
 
   // Control API.
   // Since a stream can be played multiple ways, we need to combine independent
   // volume settings. The aKey parameter is used to keep volume settings
   // separate. Since the stream is always playing the same contents, only
   // a single audio output stream is used; the volumes are combined.
   // Currently only the first enabled audio track is played.
   // XXX change this so all enabled audio tracks are mixed and played.
@@ -510,16 +515,19 @@ protected:
   bool mBlockInThisPhase;
 
   // This state is only used on the main thread.
   nsDOMMediaStream* mWrapper;
   // Main-thread views of state
   StreamTime mMainThreadCurrentTime;
   bool mMainThreadFinished;
   bool mMainThreadDestroyed;
+
+  // Our media stream graph
+  MediaStreamGraphImpl* mGraph;
 };
 
 /**
  * This is a stream into which a decoder can write audio and video.
  *
  * Audio and video can be written on any thread, but you probably want to
  * always write from the same thread to avoid unexpected interleavings.
  */
@@ -698,16 +706,17 @@ public:
     FLAG_BLOCK_OUTPUT = 0x02
   };
   // Do not call this constructor directly. Instead call aDest->AllocateInputPort.
   MediaInputPort(MediaStream* aSource, ProcessedMediaStream* aDest,
                  uint32_t aFlags)
     : mSource(aSource)
     , mDest(aDest)
     , mFlags(aFlags)
+    , mGraph(nullptr)
   {
     MOZ_COUNT_CTOR(MediaInputPort);
   }
   ~MediaInputPort()
   {
     MOZ_COUNT_DTOR(MediaInputPort);
   }
 
@@ -738,25 +747,32 @@ public:
   // mDest is not blocked and mSource's blocking status does not change.
   InputInterval GetNextInputInterval(GraphTime aTime);
 
   /**
    * Returns the graph that owns this port.
    */
   MediaStreamGraphImpl* GraphImpl();
   MediaStreamGraph* Graph();
+  /**
+   * Sets the graph that owns this stream.  Should only be called once.
+   */
+  void SetGraphImpl(MediaStreamGraphImpl* aGraph);
 
 protected:
   friend class MediaStreamGraphImpl;
   friend class MediaStream;
   friend class ProcessedMediaStream;
   // Never modified after Init()
   MediaStream* mSource;
   ProcessedMediaStream* mDest;
   uint32_t mFlags;
+
+  // Our media stream graph
+  MediaStreamGraphImpl* mGraph;
 };
 
 /**
  * This stream processes zero or more input streams in parallel to produce
  * its output. The details of how the output is produced are handled by
  * subclasses overriding the ProduceOutput method.
  */
 class ProcessedMediaStream : public MediaStream {
--- a/docshell/shistory/src/Makefile.in
+++ b/docshell/shistory/src/Makefile.in
@@ -9,16 +9,17 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= shistory
 LIBRARY_NAME	= shistory_s
 FORCE_STATIC_LIB = 1
 LIBXUL_LIBRARY	= 1
+FAIL_ON_WARNINGS = 1
 
 EXPORTS		= nsSHEntryShared.h \
 		  $(NULL)
 
 CPPSRCS		= nsSHEntry.cpp        \
             nsSHTransaction.cpp   \
             nsSHistory.cpp \
 	    nsSHEntryShared.cpp \
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -555,38 +555,39 @@ NS_IMETHODIMP
 nsDOMWindowUtils::SendMouseEvent(const nsAString& aType,
                                  float aX,
                                  float aY,
                                  int32_t aButton,
                                  int32_t aClickCount,
                                  int32_t aModifiers,
                                  bool aIgnoreRootScrollFrame,
                                  float aPressure,
-                                 unsigned short aInputSourceArg)
+                                 unsigned short aInputSourceArg,
+                                 bool *aPreventDefault)
 {
   return SendMouseEventCommon(aType, aX, aY, aButton, aClickCount, aModifiers,
                               aIgnoreRootScrollFrame, aPressure,
-                              aInputSourceArg, false);
+                              aInputSourceArg, false, aPreventDefault);
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::SendMouseEventToWindow(const nsAString& aType,
                                          float aX,
                                          float aY,
                                          int32_t aButton,
                                          int32_t aClickCount,
                                          int32_t aModifiers,
                                          bool aIgnoreRootScrollFrame,
                                          float aPressure,
                                          unsigned short aInputSourceArg)
 {
   SAMPLE_LABEL("nsDOMWindowUtils", "SendMouseEventToWindow");
   return SendMouseEventCommon(aType, aX, aY, aButton, aClickCount, aModifiers,
                               aIgnoreRootScrollFrame, aPressure,
-                              aInputSourceArg, true);
+                              aInputSourceArg, true, nullptr);
 }
 
 static nsIntPoint
 ToWidgetPoint(float aX, float aY, const nsPoint& aOffset,
               nsPresContext* aPresContext)
 {
   double appPerDev = aPresContext->AppUnitsPerDevPixel();
   nscoord appPerCSS = nsPresContext::AppUnitsPerCSSPixel();
@@ -599,17 +600,18 @@ nsDOMWindowUtils::SendMouseEventCommon(c
                                        float aX,
                                        float aY,
                                        int32_t aButton,
                                        int32_t aClickCount,
                                        int32_t aModifiers,
                                        bool aIgnoreRootScrollFrame,
                                        float aPressure,
                                        unsigned short aInputSourceArg,
-                                       bool aToWindow)
+                                       bool aToWindow,
+                                       bool *aPreventDefault)
 {
   if (!nsContentUtils::IsCallerChrome()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   // get the widget to send the event to
   nsPoint offset;
   nsCOMPtr<nsIWidget> widget = GetWidget(&offset);
@@ -626,17 +628,19 @@ nsDOMWindowUtils::SendMouseEventCommon(c
     msg = NS_MOUSE_MOVE;
   else if (aType.EqualsLiteral("mouseover"))
     msg = NS_MOUSE_ENTER;
   else if (aType.EqualsLiteral("mouseout"))
     msg = NS_MOUSE_EXIT;
   else if (aType.EqualsLiteral("contextmenu")) {
     msg = NS_CONTEXTMENU;
     contextMenuKey = (aButton == 0);
-  } else
+  } else if (aType.EqualsLiteral("MozMouseHittest"))
+    msg = NS_MOUSE_MOZHITTEST;
+  else
     return NS_ERROR_FAILURE;
 
   if (aInputSourceArg == nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN) {
     aInputSourceArg = nsIDOMMouseEvent::MOZ_SOURCE_MOUSE;
   }
 
   nsMouseEvent event(true, msg, widget, nsMouseEvent::eReal,
                      contextMenuKey ?
@@ -667,17 +671,20 @@ nsDOMWindowUtils::SendMouseEventCommon(c
       return NS_ERROR_FAILURE;
     nsView* view = viewManager->GetRootView();
     if (!view)
       return NS_ERROR_FAILURE;
 
     status = nsEventStatus_eIgnore;
     return presShell->HandleEvent(view->GetFrame(), &event, false, &status);
   }
-  return widget->DispatchEvent(&event, status);
+  nsresult rv = widget->DispatchEvent(&event, status);
+  *aPreventDefault = (status == nsEventStatus_eConsumeNoDefault);
+
+  return rv;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::SendWheelEvent(float aX,
                                  float aY,
                                  double aDeltaX,
                                  double aDeltaY,
                                  double aDeltaZ,
--- a/dom/base/nsDOMWindowUtils.h
+++ b/dom/base/nsDOMWindowUtils.h
@@ -42,14 +42,15 @@ protected:
                                   float aX,
                                   float aY,
                                   int32_t aButton,
                                   int32_t aClickCount,
                                   int32_t aModifiers,
                                   bool aIgnoreRootScrollFrame,
                                   float aPressure,
                                   unsigned short aInputSourceArg,
-                                  bool aToWindow);
+                                  bool aToWindow,
+                                  bool *aPreventDefault);
 
   static mozilla::widget::Modifiers GetWidgetModifiers(int32_t aModifiers);
 };
 
 #endif
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -521,16 +521,25 @@ DOMInterfaces = {
 'Location': {
     # NOTE: Before you turn on codegen for Location, make sure all the
     # Unforgeable stuff is dealt with.
     'nativeType': 'nsIDOMLocation',
     'skipGen': True,
     'register': False
 },
 
+'MediaStream': [{
+    'nativeType': 'nsIDOMMediaStream',
+},
+{
+    'nativeType': 'JSObject',
+    'workers': True,
+    'skipGen': True
+}],
+
 'MediaStreamList': {
     'headerFile': 'MediaStreamList.h',
     'wrapperCache': False,
     'nativeOwnership': 'owned',
     'resultNotAddRefed': [ '__indexedGetter' ],
     'binaryNames': { '__indexedGetter': 'IndexedGetter' }
 },
 
@@ -865,19 +874,23 @@ DOMInterfaces = {
 {
     'implicitJSContext': [ 'encode' ],
 },
 {
     'workers': True,
     'implicitJSContext': [ 'encode' ],
 }],
 
-'URL' : {
+'URL' : [{
     'concrete': False,
 },
+{
+    'implicitJSContext': [ 'createObjectURL', 'revokeObjectURL' ],
+    'workers': True,
+}],
 
 'WebGLActiveInfo': {
    'nativeType': 'mozilla::WebGLActiveInfo',
    'headerFile': 'WebGLContext.h',
    'wrapperCache': False
 },
 
 'WebGLBuffer': {
@@ -1227,17 +1240,16 @@ addExternalIface('DOMRequest')
 addExternalIface('DOMStringList')
 addExternalIface('File')
 addExternalIface('HitRegionOptions', nativeType='nsISupports')
 addExternalIface('HTMLHeadElement', nativeType='mozilla::dom::Element')
 addExternalIface('HTMLCanvasElement', nativeType='mozilla::dom::HTMLCanvasElement')
 addExternalIface('imgINotificationObserver', nativeType='imgINotificationObserver')
 addExternalIface('imgIRequest', nativeType='imgIRequest', notflattened=True)
 addExternalIface('LockedFile')
-addExternalIface('MediaStream')
 addExternalIface('MozBoxObject', nativeType='nsIBoxObject')
 addExternalIface('MozControllers', nativeType='nsIControllers')
 addExternalIface('MozFrameLoader', nativeType='nsIFrameLoader', notflattened=True)
 addExternalIface('MozRDFCompositeDataSource', nativeType='nsIRDFCompositeDataSource',
                  notflattened=True)
 addExternalIface('MozRDFResource', nativeType='nsIRDFResource', notflattened=True)
 addExternalIface('MozXULTemplateBuilder', nativeType='nsIXULTemplateBuilder')
 addExternalIface('NamedNodeMap')
--- a/dom/bluetooth/BluetoothService.cpp
+++ b/dom/bluetooth/BluetoothService.cpp
@@ -25,30 +25,36 @@
 #include "nsContentUtils.h"
 #include "nsIObserverService.h"
 #include "nsISettingsService.h"
 #include "nsISystemMessagesInternal.h"
 #include "nsITimer.h"
 #include "nsThreadUtils.h"
 #include "nsXPCOM.h"
 
+#if defined(MOZ_WIDGET_GONK)
+#include "cutils/properties.h"
+#endif
+
 #if defined(MOZ_B2G_BT)
 # if defined(MOZ_BLUETOOTH_GONK)
 #  include "BluetoothGonkService.h"
 # elif defined(MOZ_BLUETOOTH_DBUS)
 #  include "BluetoothDBusService.h"
 # else
 #  error No_suitable_backend_for_bluetooth!
 # endif
 #endif
 
 #define MOZSETTINGS_CHANGED_ID      "mozsettings-changed"
 #define BLUETOOTH_ENABLED_SETTING   "bluetooth.enabled"
 #define BLUETOOTH_DEBUGGING_SETTING "bluetooth.debugging.enabled"
 
+#define PROP_BLUETOOTH_ENABLED      "bluetooth.isEnabled"
+
 #define DEFAULT_SHUTDOWN_TIMER_MS 5000
 
 bool gBluetoothDebugFlag = false;
 
 using namespace mozilla;
 using namespace mozilla::dom;
 USING_BLUETOOTH_NAMESPACE
 
@@ -179,16 +185,29 @@ public:
       } else {
         if (NS_FAILED(gBluetoothService->StopInternal())) {
           NS_WARNING("Bluetooth service failed to stop!");
           mEnabled = !mEnabled;
         }
       }
     }
 
+    // This is requested in Bug 836516. With settings this property, WLAN
+    // firmware could be aware of Bluetooth has been turned on/off, so that the
+    // mecahnism of handling coexistence of WIFI and Bluetooth could be started.
+    //
+    // In the future, we may have our own way instead of setting a system
+    // property to let firmware developers be able to sense that Bluetooth has
+    // been toggled.
+#if defined(MOZ_WIDGET_GONK)
+    if (property_set(PROP_BLUETOOTH_ENABLED, mEnabled ? "true" : "false") != 0) {
+      NS_WARNING("Failed to set bluetooth enabled property");
+    }
+#endif
+
     nsCOMPtr<nsIRunnable> ackTask = new BluetoothService::ToggleBtAck(mEnabled);
     if (NS_FAILED(NS_DispatchToMainThread(ackTask))) {
       NS_WARNING("Failed to dispatch to main thread!");
     }
 
     return NS_OK;
   }
 
@@ -353,23 +372,32 @@ BluetoothService::UnregisterBluetoothSig
       mBluetoothSignalObserverTable.Remove(aNodeName);
     }
   }
   else {
     NS_WARNING("Node was never registered!");
   }
 }
 
+PLDHashOperator
+RemoveAllSignalHandlers(const nsAString& aKey,
+                        nsAutoPtr<BluetoothSignalObserverList>& aData,
+                        void* aUserArg)
+{
+  aData->RemoveObserver(static_cast<BluetoothSignalObserver*>(aUserArg));
+  return aData->Length() ? PL_DHASH_NEXT : PL_DHASH_REMOVE;
+}
+
 void
 BluetoothService::UnregisterAllSignalHandlers(BluetoothSignalObserver* aHandler)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aHandler);
 
-  mBluetoothSignalObserverTable.Clear();
+  mBluetoothSignalObserverTable.Enumerate(RemoveAllSignalHandlers, aHandler);
 }
 
 void
 BluetoothService::DistributeSignal(const BluetoothSignal& aSignal)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (aSignal.path().EqualsLiteral(LOCAL_AGENT_PATH)) {
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -36,17 +36,17 @@ interface nsIDOMWindow;
 interface nsIDOMBlob;
 interface nsIDOMFile;
 interface nsIFile;
 interface nsIDOMTouch;
 interface nsIDOMClientRect;
 interface nsIURI;
 interface nsIDOMEventTarget;
 
-[scriptable, uuid(458f5b08-4966-4a91-8617-258afb87070e)]
+[scriptable, uuid(020deb5a-cba6-41dd-8551-72a880d01970)]
 interface nsIDOMWindowUtils : nsISupports {
 
   /**
    * Image animation mode of the window. When this attribute's value
    * is changed, the implementation should set all images in the window
    * to the given value. That is, when set to kDontAnimMode, all images
    * will stop animating. The attribute's value must be one of the
    * animationMode values from imgIContainer.
@@ -208,17 +208,18 @@ interface nsIDOMWindowUtils : nsISupport
   const long MODIFIER_CAPSLOCK   = 0x0020;
   const long MODIFIER_FN         = 0x0040;
   const long MODIFIER_NUMLOCK    = 0x0080;
   const long MODIFIER_SCROLLLOCK = 0x0100;
   const long MODIFIER_SYMBOLLOCK = 0x0200;
   const long MODIFIER_OS         = 0x0400;
 
   /** Synthesize a mouse event. The event types supported are:
-   *    mousedown, mouseup, mousemove, mouseover, mouseout, contextmenu
+   *    mousedown, mouseup, mousemove, mouseover, mouseout, contextmenu,
+   *    MozMouseHitTest
    *
    * Events are sent in coordinates offset by aX and aY from the window.
    *
    * Note that additional events may be fired as a result of this call. For
    * instance, typically a click event will be fired as a result of a
    * mousedown and mouseup in sequence.
    *
    * Normally at this level of events, the mouseover and mouseout events are
@@ -239,26 +240,28 @@ interface nsIDOMWindowUtils : nsISupport
    * @param aButton button to synthesize
    * @param aClickCount number of clicks that have been performed
    * @param aModifiers modifiers pressed, using constants defined as MODIFIER_*
    * @param aIgnoreRootScrollFrame whether the event should ignore viewport bounds
    *                           during dispatch
    * @param aPressure touch input pressure: 0.0 -> 1.0
    * @param aInputSourceArg input source, see nsIDOMMouseEvent for values,
    *        defaults to mouse input.
+   *
+   * returns true if the page called prevent default on this event
    */
-  void sendMouseEvent(in AString aType,
-                      in float aX,
-                      in float aY,
-                      in long aButton,
-                      in long aClickCount,
-                      in long aModifiers,
-                      [optional] in boolean aIgnoreRootScrollFrame,
-                      [optional] in float aPressure,
-                      [optional] in unsigned short aInputSourceArg);
+  boolean sendMouseEvent(in AString aType,
+                         in float aX,
+                         in float aY,
+                         in long aButton,
+                         in long aClickCount,
+                         in long aModifiers,
+                         [optional] in boolean aIgnoreRootScrollFrame,
+                         [optional] in float aPressure,
+                         [optional] in unsigned short aInputSourceArg);
 
   /** Synthesize a touch event. The event types supported are:
    *    touchstart, touchend, touchmove, and touchcancel
    *
    * Events are sent in coordinates offset by aX and aY from the window.
    *
    * Cannot be accessed from unprivileged context (not content-accessible)
    * Will throw a DOM security error if called without chrome privileges.
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -128,18 +128,23 @@ TabChild::PreloadSlowThings()
 {
     MOZ_ASSERT(!sPreallocatedTab);
 
     nsRefPtr<TabChild> tab(new TabChild(TabContext(), /* chromeFlags */ 0));
     if (!NS_SUCCEEDED(tab->Init()) ||
         !tab->InitTabChildGlobal(DONT_LOAD_SCRIPTS)) {
         return;
     }
+    // Just load and compile these scripts, but don't run them.
     tab->TryCacheLoadAndCompileScript(BROWSER_ELEMENT_CHILD_SCRIPT);
-    tab->RecvLoadRemoteScript(NS_LITERAL_STRING("chrome://global/content/preload.js"));
+    tab->TryCacheLoadAndCompileScript(
+        NS_LITERAL_STRING("chrome://browser/content/forms.js"));
+    // Load, compile, and run these scripts.
+    tab->RecvLoadRemoteScript(
+        NS_LITERAL_STRING("chrome://global/content/preload.js"));
 
     nsCOMPtr<nsIDocShell> docShell = do_GetInterface(tab->mWebNav);
     nsCOMPtr<nsIPresShell> presShell;
     if (nsIPresShell* presShell = docShell->GetPresShell()) {
         // Initialize and do an initial reflow of the about:blank
         // PresShell to let it preload some things for us.
         presShell->Initialize(0, 0);
         nsIDocument* doc = presShell->GetDocument();
@@ -1354,18 +1359,19 @@ TabChild::RecvMouseEvent(const nsString&
                          const float&    aY,
                          const int32_t&  aButton,
                          const int32_t&  aClickCount,
                          const int32_t&  aModifiers,
                          const bool&     aIgnoreRootScrollFrame)
 {
   nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
   NS_ENSURE_TRUE(utils, true);
+  bool ignored = false;
   utils->SendMouseEvent(aType, aX, aY, aButton, aClickCount, aModifiers,
-                        aIgnoreRootScrollFrame, 0, 0);
+                        aIgnoreRootScrollFrame, 0, 0, &ignored);
   return true;
 }
 
 bool
 TabChild::RecvRealMouseEvent(const nsMouseEvent& event)
 {
   nsMouseEvent localEvent(event);
   DispatchWidgetEvent(localEvent);
--- a/dom/ipc/preload.js
+++ b/dom/ipc/preload.js
@@ -23,16 +23,20 @@
   Cu.import("resource://gre/modules/Geometry.jsm");
   Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
   Cu.import("resource://gre/modules/NetUtil.jsm");
   Cu.import("resource://gre/modules/ObjectWrapper.jsm");
   Cu.import("resource://gre/modules/Services.jsm");
   Cu.import("resource://gre/modules/SettingsDB.jsm");
   Cu.import("resource://gre/modules/SettingsQueue.jsm");
   Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+  if (Services.prefs.getBoolPref("general.useragent.enable_overrides")) {
+    Cu.import('resource://gre/modules/UserAgentOverrides.jsm');
+    UserAgentOverrides.init();
+  }
 
   Cc["@mozilla.org/appshell/appShellService;1"].getService(Ci["nsIAppShellService"]);
   Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci["nsIWindowMediator"]);
   Cc["@mozilla.org/AppsService;1"].getService(Ci["nsIAppsService"]);
   Cc["@mozilla.org/base/telemetry;1"].getService(Ci["nsITelemetry"]);
   Cc["@mozilla.org/categorymanager;1"].getService(Ci["nsICategoryManager"]);
   Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci["nsIMessageSender"]);
   Cc["@mozilla.org/consoleservice;1"].getService(Ci["nsIConsoleService"]);
@@ -65,12 +69,14 @@
   Cc["@mozilla.org/preferences-service;1"].getService(Ci["nsIPrefBranch"]);
   Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci["nsIScriptSecurityManager"]);
   Cc["@mozilla.org/storage/service;1"].getService(Ci["mozIStorageService"]);
   Cc["@mozilla.org/system-info;1"].getService(Ci["nsIPropertyBag2"]);
   Cc["@mozilla.org/thread-manager;1"].getService(Ci["nsIThreadManager"]);
   Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci["nsIAppStartup"]);
   Cc["@mozilla.org/uriloader;1"].getService(Ci["nsIURILoader"]);
 
+  Services.io.getProtocolHandler("app");
+
   docShell.isActive = false;
   docShell.createAboutBlankContentViewer(null);
 
 })();
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -2770,17 +2770,17 @@ function RILNetworkInterface(ril, type)
   this.type = type;
 }
 
 RILNetworkInterface.prototype = {
   classID:   RILNETWORKINTERFACE_CID,
   classInfo: XPCOMUtils.generateCI({classID: RILNETWORKINTERFACE_CID,
                                     classDescription: "RILNetworkInterface",
                                     interfaces: [Ci.nsINetworkInterface,
-                                                 Ci.nsIRIODataCallback]}),
+                                                 Ci.nsIRILDataCallback]}),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkInterface,
                                          Ci.nsIRILDataCallback]),
 
   // nsINetworkInterface
 
   NETWORK_STATE_UNKNOWN:       Ci.nsINetworkInterface.NETWORK_STATE_UNKNOWN,
   NETWORK_STATE_CONNECTING:    Ci.nsINetworkInterface.CONNECTING,
   NETWORK_STATE_CONNECTED:     Ci.nsINetworkInterface.CONNECTED,
--- a/dom/workers/Makefile.in
+++ b/dom/workers/Makefile.in
@@ -27,16 +27,17 @@ CPPSRCS = \
   File.cpp \
   FileReaderSync.cpp \
   ImageData.cpp \
   Location.cpp \
   Navigator.cpp \
   Principal.cpp \
   RuntimeService.cpp \
   ScriptLoader.cpp \
+  URL.cpp \
   TextDecoder.cpp \
   TextEncoder.cpp \
   Worker.cpp \
   WorkerPrivate.cpp \
   WorkerScope.cpp \
   XMLHttpRequestEventTarget.cpp \
   XMLHttpRequestUpload.cpp \
   XMLHttpRequest.cpp \
@@ -51,16 +52,17 @@ EXPORTS_NAMESPACES = \
 EXPORTS_mozilla/dom/workers = Workers.h
 
 # Stuff needed for the bindings, not really public though.
 EXPORTS_mozilla/dom/workers/bindings = \
   DOMBindingBase.h \
   EventListenerManager.h \
   EventTarget.h \
   FileReaderSync.h \
+  URL.h \
   TextDecoder.h \
   TextEncoder.h \
   WorkerFeature.h \
   XMLHttpRequestEventTarget.h \
   XMLHttpRequestUpload.h \
   XMLHttpRequest.h \
   $(NULL)
 
new file mode 100644
--- /dev/null
+++ b/dom/workers/URL.cpp
@@ -0,0 +1,233 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* 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 "URL.h"
+#include "File.h"
+
+#include "nsTraceRefcnt.h"
+
+#include "WorkerPrivate.h"
+#include "nsThreadUtils.h"
+
+#include "nsPIDOMWindow.h"
+#include "nsGlobalWindow.h"
+#include "nsHostObjectProtocolHandler.h"
+
+#include "nsIDOMFile.h"
+
+USING_WORKERS_NAMESPACE
+using mozilla::dom::WorkerGlobalObject;
+
+// Base class for the Revoke and Create runnable objects.
+class URLRunnable : public nsRunnable
+{
+protected:
+  WorkerPrivate* mWorkerPrivate;
+  uint32_t mSyncQueueKey;
+
+private:
+  class ResponseRunnable : public WorkerSyncRunnable
+  {
+    uint32_t mSyncQueueKey;
+
+  public:
+    ResponseRunnable(WorkerPrivate* aWorkerPrivate,
+                     uint32_t aSyncQueueKey)
+    : WorkerSyncRunnable(aWorkerPrivate, aSyncQueueKey, false),
+      mSyncQueueKey(aSyncQueueKey)
+    {
+      NS_ASSERTION(aWorkerPrivate, "Don't hand me a null WorkerPrivate!");
+    }
+
+    bool
+    WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
+    {
+      aWorkerPrivate->StopSyncLoop(mSyncQueueKey, true);
+      return true;
+    }
+
+    bool
+    PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
+    {
+      AssertIsOnMainThread();
+      return true;
+    }
+
+    void
+    PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
+                 bool aDispatchResult)
+    {
+      AssertIsOnMainThread();
+    }
+  };
+
+protected:
+  URLRunnable(WorkerPrivate* aWorkerPrivate)
+  : mWorkerPrivate(aWorkerPrivate)
+  {
+    mWorkerPrivate->AssertIsOnWorkerThread();
+  }
+
+public:
+  bool
+  Dispatch(JSContext* aCx)
+  {
+    mWorkerPrivate->AssertIsOnWorkerThread();
+    mSyncQueueKey = mWorkerPrivate->CreateNewSyncLoop();
+
+    if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
+      JS_ReportError(aCx, "Failed to dispatch to main thread!");
+      mWorkerPrivate->StopSyncLoop(mSyncQueueKey, false);
+      return false;
+    }
+
+    return mWorkerPrivate->RunSyncLoop(aCx, mSyncQueueKey);
+  }
+
+private:
+  NS_IMETHOD Run()
+  {
+    AssertIsOnMainThread();
+
+    MainThreadRun();
+
+    nsRefPtr<ResponseRunnable> response =
+      new ResponseRunnable(mWorkerPrivate, mSyncQueueKey);
+    if (!response->Dispatch(nullptr)) {
+      NS_WARNING("Failed to dispatch response!");
+    }
+
+    return NS_OK;
+  }
+
+protected:
+  virtual void
+  MainThreadRun() = 0;
+};
+
+// This class creates an URL from a DOM Blob on the main thread.
+class CreateURLRunnable : public URLRunnable
+{
+private:
+  nsIDOMBlob* mBlob;
+  nsString& mURL;
+
+public:
+  CreateURLRunnable(WorkerPrivate* aWorkerPrivate, nsIDOMBlob* aBlob,
+                    const mozilla::dom::objectURLOptionsWorkers& aOptions,
+                    nsString& aURL)
+  : URLRunnable(aWorkerPrivate),
+    mBlob(aBlob),
+    mURL(aURL)
+  {
+    MOZ_ASSERT(aBlob);
+  }
+
+  void
+  MainThreadRun()
+  {
+    AssertIsOnMainThread();
+
+    nsCOMPtr<nsPIDOMWindow> window = mWorkerPrivate->GetWindow();
+    nsIDocument* doc = window->GetExtantDoc();
+    if (!doc) {
+      SetDOMStringToNull(mURL);
+      return;
+    }
+
+    nsCString url;
+    nsresult rv = nsHostObjectProtocolHandler::AddDataEntry(
+        NS_LITERAL_CSTRING(BLOBURI_SCHEME),
+        mBlob, doc->NodePrincipal(), url);
+
+    if (NS_FAILED(rv)) {
+      NS_WARNING("Failed to add data entry for the blob!");
+      SetDOMStringToNull(mURL);
+      return;
+    }
+
+    doc->RegisterHostObjectUri(url);
+    mURL = NS_ConvertUTF8toUTF16(url);
+  }
+};
+
+// This class revokes an URL on the main thread.
+class RevokeURLRunnable : public URLRunnable
+{
+private:
+  const nsString mURL;
+
+public:
+  RevokeURLRunnable(WorkerPrivate* aWorkerPrivate,
+                    const nsAString& aURL)
+  : URLRunnable(aWorkerPrivate),
+    mURL(aURL)
+  {}
+
+  void
+  MainThreadRun()
+  {
+    AssertIsOnMainThread();
+
+    nsCOMPtr<nsPIDOMWindow> window = mWorkerPrivate->GetWindow();
+    nsIDocument* doc = window->GetExtantDoc();
+    if (!doc) {
+      return;
+    }
+
+    NS_ConvertUTF16toUTF8 url(mURL);
+
+    nsIPrincipal* principal =
+      nsHostObjectProtocolHandler::GetDataEntryPrincipal(url);
+
+    bool subsumes;
+    if (principal &&
+        NS_SUCCEEDED(doc->NodePrincipal()->Subsumes(principal, &subsumes)) &&
+        subsumes) {
+      doc->UnregisterHostObjectUri(url);
+      nsHostObjectProtocolHandler::RemoveDataEntry(url);
+    }
+  }
+};
+
+// static
+void
+URL::CreateObjectURL(const WorkerGlobalObject& aGlobal, JSObject* aBlob,
+                     const mozilla::dom::objectURLOptionsWorkers& aOptions,
+                     nsString& aResult, mozilla::ErrorResult& aRv)
+{
+  JSContext* cx = aGlobal.GetContext();
+  WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
+
+  nsCOMPtr<nsIDOMBlob> blob = file::GetDOMBlobFromJSObject(aBlob);
+  if (!blob) {
+    SetDOMStringToNull(aResult);
+    aRv.Throw(NS_ERROR_TYPE_ERR);
+    return;
+  }
+
+  nsRefPtr<CreateURLRunnable> runnable =
+    new CreateURLRunnable(workerPrivate, blob, aOptions, aResult);
+
+  if (!runnable->Dispatch(cx)) {
+    JS_ReportPendingException(cx);
+  }
+}
+
+// static
+void
+URL::RevokeObjectURL(const WorkerGlobalObject& aGlobal, const nsAString& aUrl)
+{
+  JSContext* cx = aGlobal.GetContext();
+  WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
+
+  nsRefPtr<RevokeURLRunnable> runnable =
+    new RevokeURLRunnable(workerPrivate, aUrl);
+
+  if (!runnable->Dispatch(cx)) {
+    JS_ReportPendingException(cx);
+  }
+}
+
new file mode 100644
--- /dev/null
+++ b/dom/workers/URL.h
@@ -0,0 +1,30 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * url, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_workers_url_h__
+#define mozilla_dom_workers_url_h__
+
+#include "mozilla/dom/URLBinding.h"
+
+#include "EventTarget.h"
+
+BEGIN_WORKERS_NAMESPACE
+
+class URL : public EventTarget
+{
+public: // Methods for WebIDL
+  static void
+  CreateObjectURL(const WorkerGlobalObject& aGlobal,
+                  JSObject* aArg, const objectURLOptionsWorkers& aOptions,
+                  nsString& aResult, ErrorResult& aRv);
+
+  static void
+  RevokeObjectURL(const WorkerGlobalObject& aGlobal, const nsAString& aUrl);
+};
+
+END_WORKERS_NAMESPACE
+
+#endif /* mozilla_dom_workers_url_h__ */
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -12,16 +12,17 @@
 #include "mozilla/dom/DOMJSClass.h"
 #include "mozilla/dom/EventTargetBinding.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/FileReaderSyncBinding.h"
 #include "mozilla/dom/TextDecoderBinding.h"
 #include "mozilla/dom/TextEncoderBinding.h"
 #include "mozilla/dom/XMLHttpRequestBinding.h"
 #include "mozilla/dom/XMLHttpRequestUploadBinding.h"
+#include "mozilla/dom/URLBinding.h"
 #include "mozilla/OSFileConstants.h"
 #include "nsTraceRefcnt.h"
 #include "xpcpublic.h"
 
 #ifdef ANDROID
 #include <android/log.h>
 #endif
 
@@ -981,17 +982,18 @@ CreateDedicatedWorkerGlobalScope(JSConte
     return NULL;
   }
 
   // Init other paris-bindings.
   if (!FileReaderSyncBinding_workers::GetConstructorObject(aCx, global) ||
       !TextDecoderBinding_workers::GetConstructorObject(aCx, global) ||
       !TextEncoderBinding_workers::GetConstructorObject(aCx, global) ||
       !XMLHttpRequestBinding_workers::GetConstructorObject(aCx, global) ||
-      !XMLHttpRequestUploadBinding_workers::GetConstructorObject(aCx, global)) {
+      !XMLHttpRequestUploadBinding_workers::GetConstructorObject(aCx, global) ||
+      !URLBinding_workers::GetConstructorObject(aCx, global)) {
     return NULL;
   }
 
   if (!JS_DefineProfilingFunctions(aCx, global)) {
     return NULL;
   }
 
   return global;
--- a/dom/workers/test/Makefile.in
+++ b/dom/workers/test/Makefile.in
@@ -103,16 +103,18 @@ MOCHITEST_FILES = \
   test_csp.html^headers^ \
   csp_worker.js \
   test_transferable.html \
   transferable_worker.js \
   test_errorwarning.html \
   errorwarning_worker.js \
   test_contentWorker.html \
   content_worker.js \
+  test_url.html \
+  url_worker.js \
   $(NULL)
 
 _SUBDIRMOCHITEST_FILES = \
   relativeLoad_sub_worker.js \
   relativeLoad_sub_worker2.js \
   relativeLoad_sub_import.js \
   $(NULL)
 
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_url.html
@@ -0,0 +1,53 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for URL object in workers</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+  var worker = new Worker("url_worker.js");
+
+  worker.onmessage = function(event) {
+    is(event.target, worker);
+
+    if (event.data.type == 'finish') {
+      SimpleTest.finish();
+    } else if (event.data.type == 'status') {
+      ok(event.data.status, event.data.msg);
+    } else if (event.data.type == 'url') {
+      var xhr = new XMLHttpRequest();
+      xhr.open('GET', event.data.url, false);
+      xhr.onreadystatechange = function() {
+        if (xhr.readyState == 4) {
+          ok(true, "Blob readable!");
+        }
+      }
+      xhr.send();
+    }
+  };
+
+  worker.onerror = function(event) {
+    is(event.target, worker);
+    ok(false, "Worker had an error: " + event.data);
+    SimpleTest.finish();
+  };
+
+  worker.postMessage(true);
+
+  SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/url_worker.js
@@ -0,0 +1,63 @@
+onmessage = function() {
+  status = false;
+  try {
+    if ((URL instanceof Object)) {
+      status = true;
+    }
+  } catch(e) {
+  }
+
+  postMessage({type: 'status', status: status, msg: 'URL object:' + URL});
+
+  status = false;
+  var blob = null;
+  try {
+    blob = new Blob([]);
+    status = true;
+  } catch(e) {
+  }
+
+  postMessage({type: 'status', status: status, msg: 'Blob:' + blob});
+
+  status = false;
+  var url = null;
+  try {
+    url = URL.createObjectURL(blob);
+    status = true;
+  } catch(e) {
+  }
+
+  postMessage({type: 'status', status: status, msg: 'Blob URL:' + url});
+
+  status = false;
+  try {
+    URL.revokeObjectURL(url);
+    status = true;
+  } catch(e) {
+  }
+
+  postMessage({type: 'status', status: status, msg: 'Blob Revoke URL'});
+
+  status = false;
+  var url = null;
+  try {
+    url = URL.createObjectURL(true);
+  } catch(e) {
+    status = true;
+  }
+
+  postMessage({type: 'status', status: status, msg: 'CreateObjectURL should fail if the arg is not a blob'});
+
+  status = false;
+  var url = null;
+  try {
+    url = URL.createObjectURL(blob);
+    status = true;
+  } catch(e) {
+  }
+
+  postMessage({type: 'status', status: status, msg: 'Blob URL2:' + url});
+  postMessage({type: 'url', url: url});
+
+  postMessage({type: 'finish' });
+}
--- a/gfx/angle/AUTHORS
+++ b/gfx/angle/AUTHORS
@@ -7,16 +7,17 @@
 #	Name or Organization
 # Email addresses for individuals are tracked elsewhere to avoid spam.
 
 Google Inc.
 TransGaming Inc.
 3DLabs Inc. Ltd.
 
 Adobe Systems Inc.
+Apple Inc.
 Autodesk, Inc.
 Cloud Party, Inc.
 Intel Corporation
 Mozilla Corporation
 Turbulenz
 Klarälvdalens Datakonsult AB
 
 Jacek Caban
--- a/gfx/angle/Makefile.in
+++ b/gfx/angle/Makefile.in
@@ -51,16 +51,17 @@ LOCAL_INCLUDES += \
   -I$(srcdir)/include/KHR \
   -I$(srcdir)/src
 
 DEFINES += -DCOMPILER_IMPLEMENTATION
 
 VPATH += $(srcdir)/src/compiler
 # src/compiler:
 CPPSRCS += \
+  ArrayBoundsClamper.cpp \
   BuiltInFunctionEmulator.cpp \
   Compiler.cpp \
   compiler_debug.cpp \
   DetectRecursion.cpp \
   Diagnostics.cpp \
   DirectiveHandler.cpp \
   ForLoopUnroll.cpp \
   glslang_lex.cpp \
--- a/gfx/angle/README.mozilla
+++ b/gfx/angle/README.mozilla
@@ -21,16 +21,20 @@ In this order:
   
   angle-long-ident-spooky-hash.patch:
     Use Spooky Hash for long identifier hashing. See bug 676071.
   
   angle-faceforward-emu.patch:
     Adds emulation for faceforward(float,float,float), which is needed to
     prevent crashing on Mac+Intel. See bug 771406.
 
+  angle-r1638.patch
+    Adds uniform array index clamping on non-Windows platforms.
+    Windows would require r1719, r1733, r1734.
+
 In addition to these patches, the Makefile.in files are ours, they're not present in
 upsteam ANGLE. Therefore, changes made to the Makefile.in files should not be stored
 in the local .patch files.
 
 
 == How to do a clean-slate upgrade ==
 1.  Backup our moz-specific files:
       README.mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/angle/angle-r1638.patch
@@ -0,0 +1,475 @@
+# HG changeset patch
+# Parent faf255e4400222ee23c29ddcc76fb3dce56145f4
+
+diff --git a/gfx/angle/AUTHORS b/gfx/angle/AUTHORS
+--- a/gfx/angle/AUTHORS
++++ b/gfx/angle/AUTHORS
+@@ -7,16 +7,17 @@
+ #	Name or Organization
+ # Email addresses for individuals are tracked elsewhere to avoid spam.
+ 
+ Google Inc.
+ TransGaming Inc.
+ 3DLabs Inc. Ltd.
+ 
+ Adobe Systems Inc.
++Apple Inc.
+ Autodesk, Inc.
+ Cloud Party, Inc.
+ Intel Corporation
+ Mozilla Corporation
+ Turbulenz
+ Klarälvdalens Datakonsult AB
+ 
+ Jacek Caban
+diff --git a/gfx/angle/Makefile.in b/gfx/angle/Makefile.in
+--- a/gfx/angle/Makefile.in
++++ b/gfx/angle/Makefile.in
+@@ -51,16 +51,17 @@ LOCAL_INCLUDES += \
+   -I$(srcdir)/include/KHR \
+   -I$(srcdir)/src
+ 
+ DEFINES += -DCOMPILER_IMPLEMENTATION
+ 
+ VPATH += $(srcdir)/src/compiler
+ # src/compiler:
+ CPPSRCS += \
++  ArrayBoundsClamper.cpp \
+   BuiltInFunctionEmulator.cpp \
+   Compiler.cpp \
+   compiler_debug.cpp \
+   DetectRecursion.cpp \
+   Diagnostics.cpp \
+   DirectiveHandler.cpp \
+   ForLoopUnroll.cpp \
+   glslang_lex.cpp \
+diff --git a/gfx/angle/README.mozilla b/gfx/angle/README.mozilla
+--- a/gfx/angle/README.mozilla
++++ b/gfx/angle/README.mozilla
+@@ -21,16 +21,20 @@ In this order:
+   
+   angle-long-ident-spooky-hash.patch:
+     Use Spooky Hash for long identifier hashing. See bug 676071.
+   
+   angle-faceforward-emu.patch:
+     Adds emulation for faceforward(float,float,float), which is needed to
+     prevent crashing on Mac+Intel. See bug 771406.
+ 
++  angle-r1638.patch
++    Adds uniform array index clamping on non-Windows platforms.
++    Windows would require r1719, r1733, r1734.
++
+ In addition to these patches, the Makefile.in files are ours, they're not present in
+ upsteam ANGLE. Therefore, changes made to the Makefile.in files should not be stored
+ in the local .patch files.
+ 
+ 
+ == How to do a clean-slate upgrade ==
+ 1.  Backup our moz-specific files:
+       README.mozilla
+diff --git a/gfx/angle/include/GLSLANG/ShaderLang.h b/gfx/angle/include/GLSLANG/ShaderLang.h
+--- a/gfx/angle/include/GLSLANG/ShaderLang.h
++++ b/gfx/angle/include/GLSLANG/ShaderLang.h
+@@ -145,17 +145,23 @@ typedef enum {
+   // restrictions on fragment shaders.
+   // This flag only has an effect if all of the following are true:
+   // - The shader spec is SH_WEBGL_SPEC.
+   // - The compile options contain the SH_TIMING_RESTRICTIONS flag.
+   // - The shader type is SH_FRAGMENT_SHADER.
+   SH_DEPENDENCY_GRAPH = 0x0400,
+ 
+   // Enforce the GLSL 1.017 Appendix A section 7 packing restrictions.
+-  SH_ENFORCE_PACKING_RESTRICTIONS = 0x0800
++  SH_ENFORCE_PACKING_RESTRICTIONS = 0x0800,
++
++  // This flag ensures all indirect (expression-based) array indexing
++  // is clamped to the bounds of the array. This ensures, for example,
++  // that you cannot read off the end of a uniform, whether an array
++  // vec234, or mat234 type.
++  SH_CLAMP_INDIRECT_ARRAY_BOUNDS = 0x1000
+ } ShCompileOptions;
+ 
+ //
+ // Driver must call this first, once, before doing any other
+ // compiler operations.
+ // If the function succeeds, the return value is nonzero, else zero.
+ //
+ COMPILER_EXPORT int ShInitialize();
+diff --git a/gfx/angle/src/build_angle.gypi b/gfx/angle/src/build_angle.gypi
+--- a/gfx/angle/src/build_angle.gypi
++++ b/gfx/angle/src/build_angle.gypi
+@@ -54,16 +54,18 @@
+       'include_dirs': [
+         '.',
+         '../include',
+       ],
+       'defines': [
+         'COMPILER_IMPLEMENTATION',
+       ],
+       'sources': [
++        'compiler/ArrayBoundsClamper.cpp',
++        'compiler/ArrayBoundsClamper.h',
+         'compiler/BaseTypes.h',
+         'compiler/BuiltInFunctionEmulator.cpp',
+         'compiler/BuiltInFunctionEmulator.h',
+         'compiler/Common.h',
+         'compiler/Compiler.cpp',
+         'compiler/ConstantUnion.h',
+         'compiler/debug.cpp',
+         'compiler/debug.h',
+diff --git a/gfx/angle/src/compiler/Compiler.cpp b/gfx/angle/src/compiler/Compiler.cpp
+--- a/gfx/angle/src/compiler/Compiler.cpp
++++ b/gfx/angle/src/compiler/Compiler.cpp
+@@ -1,14 +1,15 @@
+ //
+ // Copyright (c) 2002-2012 The ANGLE Project Authors. All rights reserved.
+ // Use of this source code is governed by a BSD-style license that can be
+ // found in the LICENSE file.
+ //
+ 
++#include "compiler/ArrayBoundsClamper.h"
+ #include "compiler/BuiltInFunctionEmulator.h"
+ #include "compiler/DetectRecursion.h"
+ #include "compiler/ForLoopUnroll.h"
+ #include "compiler/Initialize.h"
+ #include "compiler/InitializeParseContext.h"
+ #include "compiler/MapLongVariableNames.h"
+ #include "compiler/ParseHelper.h"
+ #include "compiler/RenameFunction.h"
+@@ -187,16 +188,20 @@ bool TCompiler::compile(const char* cons
+         // Unroll for-loop markup needs to happen after validateLimitations pass.
+         if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX))
+             ForLoopUnroll::MarkForLoopsWithIntegerIndicesForUnrolling(root);
+ 
+         // Built-in function emulation needs to happen after validateLimitations pass.
+         if (success && (compileOptions & SH_EMULATE_BUILT_IN_FUNCTIONS))
+             builtInFunctionEmulator.MarkBuiltInFunctionsForEmulation(root);
+ 
++        // Clamping uniform array bounds needs to happen after validateLimitations pass.
++        if (success && (compileOptions & SH_CLAMP_INDIRECT_ARRAY_BOUNDS))
++            arrayBoundsClamper.MarkIndirectArrayBoundsForClamping(root);
++
+         // Call mapLongVariableNames() before collectAttribsUniforms() so in
+         // collectAttribsUniforms() we already have the mapped symbol names and
+         // we could composite mapped and original variable names.
+         // Also, if we hash all the names, then no need to do this for long names.
+         if (success && (compileOptions & SH_MAP_LONG_VARIABLE_NAMES) && hashFunction == NULL)
+             mapLongVariableNames(root);
+ 
+         if (success && (compileOptions & SH_ATTRIBUTES_UNIFORMS)) {
+@@ -232,16 +237,17 @@ bool TCompiler::InitBuiltInSymbolTable(c
+ 
+     builtIns.initialize(shaderType, shaderSpec, resources);
+     return InitializeSymbolTable(builtIns.getBuiltInStrings(),
+         shaderType, shaderSpec, resources, infoSink, symbolTable);
+ }
+ 
+ void TCompiler::clearResults()
+ {
++    arrayBoundsClamper.Cleanup();
+     infoSink.info.erase();
+     infoSink.obj.erase();
+     infoSink.debug.erase();
+ 
+     attribs.clear();
+     uniforms.clear();
+ 
+     builtInFunctionEmulator.Cleanup();
+@@ -348,8 +354,14 @@ const TExtensionBehavior& TCompiler::get
+ {
+     return extensionBehavior;
+ }
+ 
+ const BuiltInFunctionEmulator& TCompiler::getBuiltInFunctionEmulator() const
+ {
+     return builtInFunctionEmulator;
+ }
++
++const ArrayBoundsClamper& TCompiler::getArrayBoundsClamper() const
++{
++    return arrayBoundsClamper;
++}
++
+diff --git a/gfx/angle/src/compiler/OutputGLSLBase.cpp b/gfx/angle/src/compiler/OutputGLSLBase.cpp
+--- a/gfx/angle/src/compiler/OutputGLSLBase.cpp
++++ b/gfx/angle/src/compiler/OutputGLSLBase.cpp
+@@ -207,18 +207,47 @@ bool TOutputGLSLBase::visitBinary(Visit 
+         case EOpVectorTimesMatrixAssign:
+         case EOpVectorTimesScalarAssign:
+         case EOpMatrixTimesScalarAssign:
+         case EOpMatrixTimesMatrixAssign:
+             writeTriplet(visit, "(", " *= ", ")");
+             break;
+ 
+         case EOpIndexDirect:
++            writeTriplet(visit, NULL, "[", "]");
++            break;
+         case EOpIndexIndirect:
+-            writeTriplet(visit, NULL, "[", "]");
++            if (node->getAddIndexClamp())
++            {
++                if (visit == InVisit)
++                {
++                    out << "[webgl_int_clamp(";
++                }
++                else if (visit == PostVisit)
++                {
++                    int maxSize;
++                    TIntermTyped *left = node->getLeft();
++                    TType leftType = left->getType();
++
++                    if (left->isArray())
++                    {
++                        // The shader will fail validation if the array length is not > 0.
++                        maxSize = leftType.getArraySize() - 1;
++                    }
++                    else
++                    {
++                        maxSize = leftType.getNominalSize() - 1;
++                    }
++                    out << ", 0, " << maxSize << ")]";
++                }
++            }
++            else
++            {
++                writeTriplet(visit, NULL, "[", "]");
++            }
+             break;
+         case EOpIndexDirectStruct:
+             if (visit == InVisit)
+             {
+                 out << ".";
+                 // TODO(alokp): ASSERT
+                 out << hashName(node->getType().getFieldName());
+                 visitChildren = false;
+diff --git a/gfx/angle/src/compiler/ShHandle.h b/gfx/angle/src/compiler/ShHandle.h
+--- a/gfx/angle/src/compiler/ShHandle.h
++++ b/gfx/angle/src/compiler/ShHandle.h
+@@ -11,16 +11,17 @@
+ // Machine independent part of the compiler private objects
+ // sent as ShHandle to the driver.
+ //
+ // This should not be included by driver code.
+ //
+ 
+ #include "GLSLANG/ShaderLang.h"
+ 
++#include "compiler/ArrayBoundsClamper.h"
+ #include "compiler/BuiltInFunctionEmulator.h"
+ #include "compiler/ExtensionBehavior.h"
+ #include "compiler/HashNames.h"
+ #include "compiler/InfoSink.h"
+ #include "compiler/SymbolTable.h"
+ #include "compiler/VariableInfo.h"
+ 
+ class LongNameMap;
+@@ -101,30 +102,32 @@ protected:
+     // Returns true if the shader does not use samplers.
+     bool enforceVertexShaderTimingRestrictions(TIntermNode* root);
+     // Returns true if the shader does not use sampler dependent values to affect control 
+     // flow or in operations whose time can depend on the input values.
+     bool enforceFragmentShaderTimingRestrictions(const TDependencyGraph& graph);
+     // Get built-in extensions with default behavior.
+     const TExtensionBehavior& getExtensionBehavior() const;
+ 
++    const ArrayBoundsClamper& getArrayBoundsClamper() const;
+     const BuiltInFunctionEmulator& getBuiltInFunctionEmulator() const;
+ 
+ private:
+     ShShaderType shaderType;
+     ShShaderSpec shaderSpec;
+ 
+     int maxUniformVectors;
+ 
+     // Built-in symbol table for the given language, spec, and resources.
+     // It is preserved from compile-to-compile.
+     TSymbolTable symbolTable;
+     // Built-in extensions with default behavior.
+     TExtensionBehavior extensionBehavior;
+ 
++    ArrayBoundsClamper arrayBoundsClamper;
+     BuiltInFunctionEmulator builtInFunctionEmulator;
+ 
+     // Results of compilation.
+     TInfoSink infoSink;  // Output sink.
+     TVariableInfoList attribs;  // Active attributes in the compiled shader.
+     TVariableInfoList uniforms;  // Active uniforms in the compiled shader.
+ 
+     // Cached copy of the ref-counted singleton.
+diff --git a/gfx/angle/src/compiler/TranslatorESSL.cpp b/gfx/angle/src/compiler/TranslatorESSL.cpp
+--- a/gfx/angle/src/compiler/TranslatorESSL.cpp
++++ b/gfx/angle/src/compiler/TranslatorESSL.cpp
+@@ -17,16 +17,19 @@ void TranslatorESSL::translate(TIntermNo
+ 
+     // Write built-in extension behaviors.
+     writeExtensionBehavior();
+ 
+     // Write emulated built-in functions if needed.
+     getBuiltInFunctionEmulator().OutputEmulatedFunctionDefinition(
+         sink, getShaderType() == SH_FRAGMENT_SHADER);
+ 
++    // Write array bounds clamping emulation if needed.
++    getArrayBoundsClamper().OutputClampingFunctionDefinition(sink);
++
+     // Write translated shader.
+     TOutputESSL outputESSL(sink, getHashFunction(), getNameMap(), getSymbolTable());
+     root->traverse(&outputESSL);
+ }
+ 
+ void TranslatorESSL::writeExtensionBehavior() {
+     TInfoSinkBase& sink = getInfoSink().obj;
+     const TExtensionBehavior& extensionBehavior = getExtensionBehavior();
+diff --git a/gfx/angle/src/compiler/TranslatorGLSL.cpp b/gfx/angle/src/compiler/TranslatorGLSL.cpp
+--- a/gfx/angle/src/compiler/TranslatorGLSL.cpp
++++ b/gfx/angle/src/compiler/TranslatorGLSL.cpp
+@@ -30,12 +30,15 @@ void TranslatorGLSL::translate(TIntermNo
+ 
+     // Write GLSL version.
+     writeVersion(getShaderType(), root, sink);
+ 
+     // Write emulated built-in functions if needed.
+     getBuiltInFunctionEmulator().OutputEmulatedFunctionDefinition(
+         sink, false);
+ 
++    // Write array bounds clamping emulation if needed.
++    getArrayBoundsClamper().OutputClampingFunctionDefinition(sink);
++
+     // Write translated shader.
+     TOutputGLSL outputGLSL(sink, getHashFunction(), getNameMap(), getSymbolTable());
+     root->traverse(&outputGLSL);
+ }
+diff --git a/gfx/angle/src/compiler/intermOut.cpp b/gfx/angle/src/compiler/intermOut.cpp
+--- a/gfx/angle/src/compiler/intermOut.cpp
++++ b/gfx/angle/src/compiler/intermOut.cpp
+@@ -37,17 +37,17 @@ protected:
+ 
+ TString TType::getCompleteString() const
+ {
+     TStringStream stream;
+ 
+     if (qualifier != EvqTemporary && qualifier != EvqGlobal)
+         stream << getQualifierString() << " " << getPrecisionString() << " ";
+     if (array)
+-        stream << "array of ";
++        stream << "array[" << getArraySize() << "] of ";
+     if (matrix)
+         stream << size << "X" << size << " matrix of ";
+     else if (size > 1)
+         stream << size << "-component vector of ";
+ 
+     stream << getBasicString();
+     return stream.str();
+ }
+diff --git a/gfx/angle/src/compiler/intermediate.h b/gfx/angle/src/compiler/intermediate.h
+--- a/gfx/angle/src/compiler/intermediate.h
++++ b/gfx/angle/src/compiler/intermediate.h
+@@ -386,30 +386,36 @@ protected:
+     TOperator op;
+ };
+ 
+ //
+ // Nodes for all the basic binary math operators.
+ //
+ class TIntermBinary : public TIntermOperator {
+ public:
+-    TIntermBinary(TOperator o) : TIntermOperator(o) {}
++    TIntermBinary(TOperator o) : TIntermOperator(o), addIndexClamp(false) {}
+ 
+     virtual TIntermBinary* getAsBinaryNode() { return this; }
+     virtual void traverse(TIntermTraverser*);
+ 
+     void setLeft(TIntermTyped* n) { left = n; }
+     void setRight(TIntermTyped* n) { right = n; }
+     TIntermTyped* getLeft() const { return left; }
+     TIntermTyped* getRight() const { return right; }
+     bool promote(TInfoSink&);
+ 
++    void setAddIndexClamp() { addIndexClamp = true; }
++    bool getAddIndexClamp() { return addIndexClamp; }
++
+ protected:
+     TIntermTyped* left;
+     TIntermTyped* right;
++
++    // If set to true, wrap any EOpIndexIndirect with a clamp to bounds.
++    bool addIndexClamp;
+ };
+ 
+ //
+ // Nodes for unary math operators.
+ //
+ class TIntermUnary : public TIntermOperator {
+ public:
+     TIntermUnary(TOperator o, TType& t) : TIntermOperator(o, t), operand(0), useEmulatedFunction(false) {}
+diff --git a/gfx/angle/src/compiler/translator_common.vcxproj b/gfx/angle/src/compiler/translator_common.vcxproj
+--- a/gfx/angle/src/compiler/translator_common.vcxproj
++++ b/gfx/angle/src/compiler/translator_common.vcxproj
+@@ -133,16 +133,17 @@
+       </PrecompiledHeader>
+       <WarningLevel>Level4</WarningLevel>
+       <TreatWarningAsError>true</TreatWarningAsError>
+       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+       <DisableSpecificWarnings>4100;4127;4189;4239;4244;4245;4267;4512;4702;4718;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+     </ClCompile>
+   </ItemDefinitionGroup>
+   <ItemGroup>
++    <ClCompile Include="ArrayBoundsClamper.cpp" />
+     <ClCompile Include="BuiltInFunctionEmulator.cpp" />
+     <ClCompile Include="Compiler.cpp" />
+     <ClCompile Include="debug.cpp" />
+     <ClCompile Include="DetectRecursion.cpp" />
+     <ClCompile Include="Diagnostics.cpp" />
+     <ClCompile Include="DirectiveHandler.cpp" />
+     <ClCompile Include="ForLoopUnroll.cpp" />
+     <ClCompile Include="InfoSink.cpp" />
+@@ -220,16 +221,17 @@
+       <Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+       </Message>
+       <Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+       </Command>
+       <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(Outputs)</Outputs>
+     </CustomBuild>
+   </ItemGroup>
+   <ItemGroup>
++    <ClInclude Include="ArrayBoundsClamper.h" />
+     <ClInclude Include="BaseTypes.h" />
+     <ClInclude Include="BuiltInFunctionEmulator.h" />
+     <ClInclude Include="Common.h" />
+     <ClInclude Include="ConstantUnion.h" />
+     <ClInclude Include="debug.h" />
+     <ClInclude Include="DetectRecursion.h" />
+     <ClInclude Include="Diagnostics.h" />
+     <ClInclude Include="DirectiveHandler.h" />
+@@ -274,9 +276,9 @@
+       <CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
+       <LinkLibraryDependencies>true</LinkLibraryDependencies>
+       <UseLibraryDependencyInputs>true</UseLibraryDependencyInputs>
+     </ProjectReference>
+   </ItemGroup>
+   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+   <ImportGroup Label="ExtensionTargets">
+   </ImportGroup>
+-</Project>
+\ No newline at end of file
++</Project>
+diff --git a/gfx/angle/src/libGLESv2/Makefile.in b/gfx/angle/src/libGLESv2/Makefile.in
+--- a/gfx/angle/src/libGLESv2/Makefile.in
++++ b/gfx/angle/src/libGLESv2/Makefile.in
+@@ -64,16 +64,17 @@ LOCAL_INCLUDES = \
+   -I$(srcdir)/../../include/KHR \
+   -I$(srcdir)/..
+ 
+ DEFINES += -DCOMPILER_IMPLEMENTATION
+ 
+ VPATH += $(srcdir)/../compiler
+ # src/compiler:
+ CPPSRCS += \
++  ArrayBoundsClamper.cpp \
+   BuiltInFunctionEmulator.cpp \
+   Compiler.cpp \
+   compiler_debug.cpp \
+   DetectRecursion.cpp \
+   Diagnostics.cpp \
+   DirectiveHandler.cpp \
+   ForLoopUnroll.cpp \
+   glslang_lex.cpp \
--- a/gfx/angle/include/GLSLANG/ShaderLang.h
+++ b/gfx/angle/include/GLSLANG/ShaderLang.h
@@ -145,17 +145,23 @@ typedef enum {
   // restrictions on fragment shaders.
   // This flag only has an effect if all of the following are true:
   // - The shader spec is SH_WEBGL_SPEC.
   // - The compile options contain the SH_TIMING_RESTRICTIONS flag.
   // - The shader type is SH_FRAGMENT_SHADER.
   SH_DEPENDENCY_GRAPH = 0x0400,
 
   // Enforce the GLSL 1.017 Appendix A section 7 packing restrictions.
-  SH_ENFORCE_PACKING_RESTRICTIONS = 0x0800
+  SH_ENFORCE_PACKING_RESTRICTIONS = 0x0800,
+
+  // This flag ensures all indirect (expression-based) array indexing
+  // is clamped to the bounds of the array. This ensures, for example,
+  // that you cannot read off the end of a uniform, whether an array
+  // vec234, or mat234 type.
+  SH_CLAMP_INDIRECT_ARRAY_BOUNDS = 0x1000
 } ShCompileOptions;
 
 //
 // Driver must call this first, once, before doing any other
 // compiler operations.
 // If the function succeeds, the return value is nonzero, else zero.
 //
 COMPILER_EXPORT int ShInitialize();
--- a/gfx/angle/src/build_angle.gypi
+++ b/gfx/angle/src/build_angle.gypi
@@ -54,16 +54,18 @@
       'include_dirs': [
         '.',
         '../include',
       ],
       'defines': [
         'COMPILER_IMPLEMENTATION',
       ],
       'sources': [
+        'compiler/ArrayBoundsClamper.cpp',
+        'compiler/ArrayBoundsClamper.h',
         'compiler/BaseTypes.h',
         'compiler/BuiltInFunctionEmulator.cpp',
         'compiler/BuiltInFunctionEmulator.h',
         'compiler/Common.h',
         'compiler/Compiler.cpp',
         'compiler/ConstantUnion.h',
         'compiler/debug.cpp',
         'compiler/debug.h',
new file mode 100644
--- /dev/null
+++ b/gfx/angle/src/compiler/ArrayBoundsClamper.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "compiler/ArrayBoundsClamper.h"
+
+const char* kIntClampBegin = "// BEGIN: Generated code for array bounds clamping\n\n";
+const char* kIntClampEnd = "// END: Generated code for array bounds clamping\n\n";
+const char* kIntClampDefinition = "int webgl_int_clamp(int value, int minValue, int maxValue) { return ((value < minValue) ? minValue : ((value > maxValue) ? maxValue : value)); }\n\n";
+
+namespace {
+
+class ArrayBoundsClamperMarker : public TIntermTraverser {
+public:
+    ArrayBoundsClamperMarker()
+        : mNeedsClamp(false)
+   {
+   }
+
+   virtual bool visitBinary(Visit visit, TIntermBinary* node)
+   {
+       if (node->getOp() == EOpIndexIndirect)
+       {
+           TIntermTyped* left = node->getLeft();
+           if (left->isArray() || left->isVector() || left->isMatrix())
+           {
+               node->setAddIndexClamp();
+               mNeedsClamp = true;
+           }
+       }
+       return true;
+   }
+
+    bool GetNeedsClamp() { return mNeedsClamp; }
+
+private:
+    bool mNeedsClamp;
+};
+
+}  // anonymous namespace
+
+ArrayBoundsClamper::ArrayBoundsClamper()
+    : mArrayBoundsClampDefinitionNeeded(false)
+{
+}
+
+void ArrayBoundsClamper::OutputClampingFunctionDefinition(TInfoSinkBase& out) const
+{
+    if (!mArrayBoundsClampDefinitionNeeded)
+    {
+        return;
+    }
+    out << kIntClampBegin << kIntClampDefinition << kIntClampEnd;
+}
+
+void ArrayBoundsClamper::MarkIndirectArrayBoundsForClamping(TIntermNode* root)
+{
+    ASSERT(root);
+
+    ArrayBoundsClamperMarker clamper;
+    root->traverse(&clamper);
+    if (clamper.GetNeedsClamp())
+    {
+        SetArrayBoundsClampDefinitionNeeded();
+    }
+}
+
new file mode 100644
--- /dev/null
+++ b/gfx/angle/src/compiler/ArrayBoundsClamper.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef COMPILER_ARRAY_BOUNDS_CLAMPER_H_
+#define COMPILER_ARRAY_BOUNDS_CLAMPER_H_
+
+#include "GLSLANG/ShaderLang.h"
+
+#include "compiler/InfoSink.h"
+#include "compiler/intermediate.h"
+
+class ArrayBoundsClamper {
+public:
+    ArrayBoundsClamper();
+
+    // Output array clamp function source into the shader source.
+    void OutputClampingFunctionDefinition(TInfoSinkBase& out) const;
+
+    // Marks nodes in the tree that index arrays indirectly as
+    // requiring clamping.
+    void MarkIndirectArrayBoundsForClamping(TIntermNode* root);
+
+    void Cleanup()
+    {
+        mArrayBoundsClampDefinitionNeeded = false;
+    }
+
+private:
+    bool GetArrayBoundsClampDefinitionNeeded() const { return mArrayBoundsClampDefinitionNeeded; }
+    void SetArrayBoundsClampDefinitionNeeded() { mArrayBoundsClampDefinitionNeeded = true; }
+    
+    bool mArrayBoundsClampDefinitionNeeded;
+};
+
+#endif // COMPILER_ARRAY_BOUNDS_CLAMPER_H_
--- a/gfx/angle/src/compiler/Compiler.cpp
+++ b/gfx/angle/src/compiler/Compiler.cpp
@@ -1,14 +1,15 @@
 //
 // Copyright (c) 2002-2012 The ANGLE Project Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
 
+#include "compiler/ArrayBoundsClamper.h"
 #include "compiler/BuiltInFunctionEmulator.h"
 #include "compiler/DetectRecursion.h"
 #include "compiler/ForLoopUnroll.h"
 #include "compiler/Initialize.h"
 #include "compiler/InitializeParseContext.h"
 #include "compiler/MapLongVariableNames.h"
 #include "compiler/ParseHelper.h"
 #include "compiler/RenameFunction.h"
@@ -187,16 +188,20 @@ bool TCompiler::compile(const char* cons
         // Unroll for-loop markup needs to happen after validateLimitations pass.
         if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX))
             ForLoopUnroll::MarkForLoopsWithIntegerIndicesForUnrolling(root);
 
         // Built-in function emulation needs to happen after validateLimitations pass.
         if (success && (compileOptions & SH_EMULATE_BUILT_IN_FUNCTIONS))
             builtInFunctionEmulator.MarkBuiltInFunctionsForEmulation(root);
 
+        // Clamping uniform array bounds needs to happen after validateLimitations pass.
+        if (success && (compileOptions & SH_CLAMP_INDIRECT_ARRAY_BOUNDS))
+            arrayBoundsClamper.MarkIndirectArrayBoundsForClamping(root);
+
         // Call mapLongVariableNames() before collectAttribsUniforms() so in
         // collectAttribsUniforms() we already have the mapped symbol names and
         // we could composite mapped and original variable names.
         // Also, if we hash all the names, then no need to do this for long names.
         if (success && (compileOptions & SH_MAP_LONG_VARIABLE_NAMES) && hashFunction == NULL)
             mapLongVariableNames(root);
 
         if (success && (compileOptions & SH_ATTRIBUTES_UNIFORMS)) {
@@ -232,16 +237,17 @@ bool TCompiler::InitBuiltInSymbolTable(c
 
     builtIns.initialize(shaderType, shaderSpec, resources);
     return InitializeSymbolTable(builtIns.getBuiltInStrings(),
         shaderType, shaderSpec, resources, infoSink, symbolTable);
 }
 
 void TCompiler::clearResults()
 {
+    arrayBoundsClamper.Cleanup();
     infoSink.info.erase();
     infoSink.obj.erase();
     infoSink.debug.erase();
 
     attribs.clear();
     uniforms.clear();
 
     builtInFunctionEmulator.Cleanup();
@@ -348,8 +354,14 @@ const TExtensionBehavior& TCompiler::get
 {
     return extensionBehavior;
 }
 
 const BuiltInFunctionEmulator& TCompiler::getBuiltInFunctionEmulator() const
 {
     return builtInFunctionEmulator;
 }
+
+const ArrayBoundsClamper& TCompiler::getArrayBoundsClamper() const
+{
+    return arrayBoundsClamper;
+}
+
--- a/gfx/angle/src/compiler/OutputGLSLBase.cpp
+++ b/gfx/angle/src/compiler/OutputGLSLBase.cpp
@@ -207,18 +207,47 @@ bool TOutputGLSLBase::visitBinary(Visit 
         case EOpVectorTimesMatrixAssign:
         case EOpVectorTimesScalarAssign:
         case EOpMatrixTimesScalarAssign:
         case EOpMatrixTimesMatrixAssign:
             writeTriplet(visit, "(", " *= ", ")");
             break;
 
         case EOpIndexDirect:
+            writeTriplet(visit, NULL, "[", "]");
+            break;
         case EOpIndexIndirect:
-            writeTriplet(visit, NULL, "[", "]");
+            if (node->getAddIndexClamp())
+            {
+                if (visit == InVisit)
+                {
+                    out << "[webgl_int_clamp(";
+                }
+                else if (visit == PostVisit)
+                {
+                    int maxSize;
+                    TIntermTyped *left = node->getLeft();
+                    TType leftType = left->getType();
+
+                    if (left->isArray())
+                    {
+                        // The shader will fail validation if the array length is not > 0.
+                        maxSize = leftType.getArraySize() - 1;
+                    }
+                    else
+                    {
+                        maxSize = leftType.getNominalSize() - 1;
+                    }
+                    out << ", 0, " << maxSize << ")]";
+                }
+            }
+            else
+            {
+                writeTriplet(visit, NULL, "[", "]");
+            }
             break;
         case EOpIndexDirectStruct:
             if (visit == InVisit)
             {
                 out << ".";
                 // TODO(alokp): ASSERT
                 out << hashName(node->getType().getFieldName());
                 visitChildren = false;
--- a/gfx/angle/src/compiler/ShHandle.h
+++ b/gfx/angle/src/compiler/ShHandle.h
@@ -11,16 +11,17 @@
 // Machine independent part of the compiler private objects
 // sent as ShHandle to the driver.
 //
 // This should not be included by driver code.
 //
 
 #include "GLSLANG/ShaderLang.h"
 
+#include "compiler/ArrayBoundsClamper.h"
 #include "compiler/BuiltInFunctionEmulator.h"
 #include "compiler/ExtensionBehavior.h"
 #include "compiler/HashNames.h"
 #include "compiler/InfoSink.h"
 #include "compiler/SymbolTable.h"
 #include "compiler/VariableInfo.h"
 
 class LongNameMap;
@@ -101,30 +102,32 @@ protected:
     // Returns true if the shader does not use samplers.
     bool enforceVertexShaderTimingRestrictions(TIntermNode* root);
     // Returns true if the shader does not use sampler dependent values to affect control 
     // flow or in operations whose time can depend on the input values.
     bool enforceFragmentShaderTimingRestrictions(const TDependencyGraph& graph);
     // Get built-in extensions with default behavior.
     const TExtensionBehavior& getExtensionBehavior() const;
 
+    const ArrayBoundsClamper& getArrayBoundsClamper() const;
     const BuiltInFunctionEmulator& getBuiltInFunctionEmulator() const;
 
 private:
     ShShaderType shaderType;
     ShShaderSpec shaderSpec;
 
     int maxUniformVectors;
 
     // Built-in symbol table for the given language, spec, and resources.
     // It is preserved from compile-to-compile.
     TSymbolTable symbolTable;
     // Built-in extensions with default behavior.
     TExtensionBehavior extensionBehavior;
 
+    ArrayBoundsClamper arrayBoundsClamper;
     BuiltInFunctionEmulator builtInFunctionEmulator;
 
     // Results of compilation.
     TInfoSink infoSink;  // Output sink.
     TVariableInfoList attribs;  // Active attributes in the compiled shader.
     TVariableInfoList uniforms;  // Active uniforms in the compiled shader.
 
     // Cached copy of the ref-counted singleton.
--- a/gfx/angle/src/compiler/TranslatorESSL.cpp
+++ b/gfx/angle/src/compiler/TranslatorESSL.cpp
@@ -17,16 +17,19 @@ void TranslatorESSL::translate(TIntermNo
 
     // Write built-in extension behaviors.
     writeExtensionBehavior();
 
     // Write emulated built-in functions if needed.
     getBuiltInFunctionEmulator().OutputEmulatedFunctionDefinition(
         sink, getShaderType() == SH_FRAGMENT_SHADER);
 
+    // Write array bounds clamping emulation if needed.
+    getArrayBoundsClamper().OutputClampingFunctionDefinition(sink);
+
     // Write translated shader.
     TOutputESSL outputESSL(sink, getHashFunction(), getNameMap(), getSymbolTable());
     root->traverse(&outputESSL);
 }
 
 void TranslatorESSL::writeExtensionBehavior() {
     TInfoSinkBase& sink = getInfoSink().obj;
     const TExtensionBehavior& extensionBehavior = getExtensionBehavior();
--- a/gfx/angle/src/compiler/TranslatorGLSL.cpp
+++ b/gfx/angle/src/compiler/TranslatorGLSL.cpp
@@ -30,12 +30,15 @@ void TranslatorGLSL::translate(TIntermNo
 
     // Write GLSL version.
     writeVersion(getShaderType(), root, sink);
 
     // Write emulated built-in functions if needed.
     getBuiltInFunctionEmulator().OutputEmulatedFunctionDefinition(
         sink, false);
 
+    // Write array bounds clamping emulation if needed.
+    getArrayBoundsClamper().OutputClampingFunctionDefinition(sink);
+
     // Write translated shader.
     TOutputGLSL outputGLSL(sink, getHashFunction(), getNameMap(), getSymbolTable());
     root->traverse(&outputGLSL);
 }
--- a/gfx/angle/src/compiler/intermOut.cpp
+++ b/gfx/angle/src/compiler/intermOut.cpp
@@ -37,17 +37,17 @@ protected:
 
 TString TType::getCompleteString() const
 {
     TStringStream stream;
 
     if (qualifier != EvqTemporary && qualifier != EvqGlobal)
         stream << getQualifierString() << " " << getPrecisionString() << " ";
     if (array)
-        stream << "array of ";
+        stream << "array[" << getArraySize() << "] of ";
     if (matrix)
         stream << size << "X" << size << " matrix of ";
     else if (size > 1)
         stream << size << "-component vector of ";
 
     stream << getBasicString();
     return stream.str();
 }
--- a/gfx/angle/src/compiler/intermediate.h
+++ b/gfx/angle/src/compiler/intermediate.h
@@ -386,30 +386,36 @@ protected:
     TOperator op;
 };
 
 //
 // Nodes for all the basic binary math operators.
 //
 class TIntermBinary : public TIntermOperator {
 public:
-    TIntermBinary(TOperator o) : TIntermOperator(o) {}
+    TIntermBinary(TOperator o) : TIntermOperator(o), addIndexClamp(false) {}
 
     virtual TIntermBinary* getAsBinaryNode() { return this; }
     virtual void traverse(TIntermTraverser*);
 
     void setLeft(TIntermTyped* n) { left = n; }
     void setRight(TIntermTyped* n) { right = n; }
     TIntermTyped* getLeft() const { return left; }
     TIntermTyped* getRight() const { return right; }
     bool promote(TInfoSink&);
 
+    void setAddIndexClamp() { addIndexClamp = true; }
+    bool getAddIndexClamp() { return addIndexClamp; }
+
 protected:
     TIntermTyped* left;
     TIntermTyped* right;
+
+    // If set to true, wrap any EOpIndexIndirect with a clamp to bounds.
+    bool addIndexClamp;
 };
 
 //
 // Nodes for unary math operators.
 //
 class TIntermUnary : public TIntermOperator {
 public:
     TIntermUnary(TOperator o, TType& t) : TIntermOperator(o, t), operand(0), useEmulatedFunction(false) {}
--- a/gfx/angle/src/compiler/translator_common.vcxproj
+++ b/gfx/angle/src/compiler/translator_common.vcxproj
@@ -133,16 +133,17 @@
       </PrecompiledHeader>
       <WarningLevel>Level4</WarningLevel>
       <TreatWarningAsError>true</TreatWarningAsError>
       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
       <DisableSpecificWarnings>4100;4127;4189;4239;4244;4245;4267;4512;4702;4718;%(DisableSpecificWarnings)</DisableSpecificWarnings>
     </ClCompile>
   </ItemDefinitionGroup>
   <ItemGroup>
+    <ClCompile Include="ArrayBoundsClamper.cpp" />
     <ClCompile Include="BuiltInFunctionEmulator.cpp" />
     <ClCompile Include="Compiler.cpp" />
     <ClCompile Include="debug.cpp" />
     <ClCompile Include="DetectRecursion.cpp" />
     <ClCompile Include="Diagnostics.cpp" />
     <ClCompile Include="DirectiveHandler.cpp" />
     <ClCompile Include="ForLoopUnroll.cpp" />
     <ClCompile Include="InfoSink.cpp" />
@@ -220,16 +221,17 @@
       <Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
       </Message>
       <Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
       </Command>
       <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(Outputs)</Outputs>
     </CustomBuild>
   </ItemGroup>
   <ItemGroup>
+    <ClInclude Include="ArrayBoundsClamper.h" />
     <ClInclude Include="BaseTypes.h" />
     <ClInclude Include="BuiltInFunctionEmulator.h" />
     <ClInclude Include="Common.h" />
     <ClInclude Include="ConstantUnion.h" />
     <ClInclude Include="debug.h" />
     <ClInclude Include="DetectRecursion.h" />
     <ClInclude Include="Diagnostics.h" />
     <ClInclude Include="DirectiveHandler.h" />
@@ -274,9 +276,9 @@
       <CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
       <LinkLibraryDependencies>true</LinkLibraryDependencies>
       <UseLibraryDependencyInputs>true</UseLibraryDependencyInputs>
     </ProjectReference>
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
\ No newline at end of file
+</Project>
--- a/gfx/angle/src/libGLESv2/Makefile.in
+++ b/gfx/angle/src/libGLESv2/Makefile.in
@@ -64,16 +64,17 @@ LOCAL_INCLUDES = \
   -I$(srcdir)/../../include/KHR \
   -I$(srcdir)/..
 
 DEFINES += -DCOMPILER_IMPLEMENTATION
 
 VPATH += $(srcdir)/../compiler
 # src/compiler:
 CPPSRCS += \
+  ArrayBoundsClamper.cpp \
   BuiltInFunctionEmulator.cpp \
   Compiler.cpp \
   compiler_debug.cpp \
   DetectRecursion.cpp \
   Diagnostics.cpp \
   DirectiveHandler.cpp \
   ForLoopUnroll.cpp \
   glslang_lex.cpp \
new file mode 100644
--- /dev/null
+++ b/js/public/Anchor.h
@@ -0,0 +1,163 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=4 sw=4 et tw=99 ft=cpp:
+ *
+ * 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/. */
+
+/* JS::Anchor implementation. */
+
+#ifndef js_Anchor_h___
+#define js_Anchor_h___
+
+#include "mozilla/Attributes.h"
+
+class JSFunction;
+class JSObject;
+class JSScript;
+class JSString;
+
+namespace JS { class Value; }
+
+namespace JS {
+
+/*
+ * Protecting non-Value, non-JSObject *, non-JSString * values from collection
+ *
+ * Most of the time, the garbage collector's conservative stack scanner works
+ * behind the scenes, finding all live values and protecting them from being
+ * collected. However, when JSAPI client code obtains a pointer to data the
+ * scanner does not know about, owned by an object the scanner does know about,
+ * Care Must Be Taken.
+ *
+ * The scanner recognizes only a select set of types: pointers to JSObjects and
+ * similar things (JSFunctions, and so on), pointers to JSStrings, and Values.
+ * So while the scanner finds all live |JSString| pointers, it does not notice
+ * |jschar| pointers.
+ *
+ * So suppose we have:
+ *
+ *   void f(JSString *str) {
+ *     const jschar *ch = JS_GetStringCharsZ(str);
+ *     ... do stuff with ch, but no uses of str ...;
+ *   }
+ *
+ * After the call to |JS_GetStringCharsZ|, there are no further uses of
+ * |str|, which means that the compiler is within its rights to not store
+ * it anywhere. But because the stack scanner will not notice |ch|, there
+ * is no longer any live value in this frame that would keep the string
+ * alive. If |str| is the last reference to that |JSString|, and the
+ * collector runs while we are using |ch|, the string's array of |jschar|s
+ * may be freed out from under us.
+ *
+ * Note that there is only an issue when 1) we extract a thing X the scanner
+ * doesn't recognize from 2) a thing Y the scanner does recognize, and 3) if Y
+ * gets garbage-collected, then X gets freed. If we have code like this:
+ *
+ *   void g(JSObject *obj) {
+ *     JS::Value x;
+ *     JS_GetProperty(obj, "x", &x);
+ *     ... do stuff with x ...
+ *   }
+ *
+ * there's no problem, because the value we've extracted, x, is a Value, a
+ * type that the conservative scanner recognizes.
+ *
+ * Conservative GC frees us from the obligation to explicitly root the types it
+ * knows about, but when we work with derived values like |ch|, we must root
+ * their owners, as the derived value alone won't keep them alive.
+ *
+ * A JS::Anchor is a kind of GC root that allows us to keep the owners of
+ * derived values like |ch| alive throughout the Anchor's lifetime. We could
+ * fix the above code as follows:
+ *
+ *   void f(JSString *str) {
+ *     JS::Anchor<JSString *> a_str(str);
+ *     const jschar *ch = JS_GetStringCharsZ(str);
+ *     ... do stuff with ch, but no uses of str ...;
+ *   }
+ *
+ * This simply ensures that |str| will be live until |a_str| goes out of scope.
+ * As long as we don't retain a pointer to the string's characters for longer
+ * than that, we have avoided all garbage collection hazards.
+ */
+template<typename T> class AnchorPermitted;
+template<> class AnchorPermitted<JSObject *> { };
+template<> class AnchorPermitted<const JSObject *> { };
+template<> class AnchorPermitted<JSFunction *> { };
+template<> class AnchorPermitted<const JSFunction *> { };
+template<> class AnchorPermitted<JSString *> { };
+template<> class AnchorPermitted<const JSString *> { };
+template<> class AnchorPermitted<Value> { };
+template<> class AnchorPermitted<const JSScript *> { };
+template<> class AnchorPermitted<JSScript *> { };
+
+template<typename T>
+class Anchor : AnchorPermitted<T>
+{
+  public:
+    Anchor() { }
+    explicit Anchor(T t) { hold = t; }
+    inline ~Anchor();
+    T &get() { return hold; }
+    const T &get() const { return hold; }
+    void set(const T &t) { hold = t; }
+    void operator=(const T &t) { hold = t; }
+    void clear() { hold = 0; }
+
+  private:
+    T hold;
+    Anchor(const Anchor &other) MOZ_DELETE;
+    void operator=(const Anchor &other) MOZ_DELETE;
+};
+
+template<typename T>
+inline Anchor<T>::~Anchor()
+{
+#ifdef __GNUC__
+    /*
+     * No code is generated for this. But because this is marked 'volatile', G++ will
+     * assume it has important side-effects, and won't delete it. (G++ never looks at
+     * the actual text and notices it's empty.) And because we have passed |hold| to
+     * it, GCC will keep |hold| alive until this point.
+     *
+     * The "memory" clobber operand ensures that G++ will not move prior memory
+     * accesses after the asm --- it's a barrier. Unfortunately, it also means that
+     * G++ will assume that all memory has changed after the asm, as it would for a
+     * call to an unknown function. I don't know of a way to avoid that consequence.
+     */
+    asm volatile("":: "g" (hold) : "memory");
+#else
+    /*
+     * An adequate portable substitute, for non-structure types.
+     *
+     * The compiler promises that, by the end of an expression statement, the
+     * last-stored value to a volatile object is the same as it would be in an
+     * unoptimized, direct implementation (the "abstract machine" whose behavior the
+     * language spec describes). However, the compiler is still free to reorder
+     * non-volatile accesses across this store --- which is what we must prevent. So
+     * assigning the held value to a volatile variable, as we do here, is not enough.
+     *
+     * In our case, however, garbage collection only occurs at function calls, so it
+     * is sufficient to ensure that the destructor's store isn't moved earlier across
+     * any function calls that could collect. It is hard to imagine the compiler
+     * analyzing the program so thoroughly that it could prove that such motion was
+     * safe. In practice, compilers treat calls to the collector as opaque operations
+     * --- in particular, as operations which could access volatile variables, across
+     * which this destructor must not be moved.
+     *
+     * ("Objection, your honor!  *Alleged* killer whale!")
+     *
+     * The disadvantage of this approach is that it does generate code for the store.
+     * We do need to use Anchors in some cases where cycles are tight.
+     *
+     * Note that there is a Anchor<Value>::~Anchor() specialization in Value.h.
+     */
+    volatile T sink;
+    sink = hold;
+#endif  /* defined(__GNUC__) */
+}
+
+} // namespace JS
+
+#endif /* js_Anchor_h___ */
new file mode 100644
--- /dev/null
+++ b/js/public/PropertyKey.h
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=4 sw=4 et tw=99 ft=cpp:
+ *
+ * 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/. */
+
+/* JS::PropertyKey implementation. */
+
+#ifndef js_PropertyKey_h___
+#define js_PropertyKey_h___
+
+#include "mozilla/Attributes.h"
+
+#include "js/Value.h"
+
+struct JSContext;
+
+namespace JS {
+
+class PropertyKey;
+
+namespace detail {
+
+extern JS_PUBLIC_API(bool)
+ToPropertyKeySlow(JSContext *cx, HandleValue v, PropertyKey *key);
+
+} // namespace detail
+
+/*
+ * A PropertyKey is a key used to access some property on an object.  It is a
+ * natural way to represent a property accessed using a JavaScript value.
+ *
+ * PropertyKey can represent indexes, named properties, and ES6 symbols.  The
+ * latter aren't implemented in SpiderMonkey yet, but PropertyKey carves out
+ * space for them.
+ */
+class PropertyKey
+{
+    Value v;
+    friend bool detail::ToPropertyKeySlow(JSContext *cx, HandleValue v, PropertyKey *key);
+
+  public:
+    explicit PropertyKey(uint32_t index) : v(PrivateUint32Value(index)) {}
+
+    /*
+     * An index is a string property name whose characters exactly spell out an
+     * unsigned 32-bit integer in decimal: "0", "1", "2", ...., "4294967294",
+     * "4294967295".
+     */
+    bool isIndex(uint32_t *index) {
+        // The implementation here assumes that private uint32_t are stored
+        // using the int32_t representation.  This is purely an implementation
+        // detail: embedders must not rely upon this!
+        if (!v.isInt32())
+            return false;
+        *index = v.toPrivateUint32();
+        return true;
+    }
+
+    /*
+     * A name is a string property name which is *not* an index.  Note that by
+     * the ECMAScript language grammar, any dotted property access |obj.prop|
+     * will access a named property.
+     */
+    bool isName(JSString **str) {
+        uint32_t dummy;
+        if (isIndex(&dummy))
+            return false;
+        *str = v.toString();
+        return true;
+    }
+
+    /*
+     * A symbol is a property name that's a Symbol, a particular kind of object
+     * in ES6.  It is the only kind of property name that's not a string.
+     *
+     * SpiderMonkey doesn't yet implement symbols, but we're carving out API
+     * space for them in advance.
+     */
+    bool isSymbol() {
+        return false;
+    }
+};
+
+inline bool
+ToPropertyKey(JSContext *cx, HandleValue v, PropertyKey *key)
+{
+    if (v.isInt32() && v.toInt32() >= 0) {
+        *key = PropertyKey(uint32_t(v.toInt32()));
+        return true;
+    }
+
+    return detail::ToPropertyKeySlow(cx, v, key);
+}
+
+} // namespace JS
+
+#endif /* js_PropertyKey_h___ */
rename from js/src/jsval.h
rename to js/public/Value.h
--- a/js/src/jsval.h
+++ b/js/public/Value.h
@@ -1,23 +1,36 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=4 sw=4 et tw=99 ft=cpp:
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef jsvalimpl_h__
-#define jsvalimpl_h__
+/* JS::Value implementation. */
+
+#ifndef js_Value_h___
+#define js_Value_h___
+
+#include "mozilla/Attributes.h"
+#include "mozilla/FloatingPoint.h"
+#include "mozilla/Likely.h"
+
+#include <limits> /* for std::numeric_limits */
 
-/*
- * Implementation details for js::Value in jsapi.h.
- */
+#include "gc/Root.h"
+#include "js/Anchor.h"
+#include "js/Utility.h"
 
-#include "js/Utility.h"
+namespace JS { class Value; }
+
+/* JS::Value can store a full int32_t. */
+#define JSVAL_INT_BITS          32
+#define JSVAL_INT_MIN           ((int32_t)0x80000000)
+#define JSVAL_INT_MAX           ((int32_t)0x7fffffff)
 
 /*
  * Try to get jsvals 64-bit aligned. We could almost assert that all values are
  * aligned, but MSVC and GCC occasionally break alignment.
  */
 #if defined(__GNUC__) || defined(__xlc__) || defined(__xlC__)
 # define JSVAL_ALIGNMENT        __attribute__((aligned (8)))
 #elif defined(_MSC_VER)
@@ -324,518 +337,1485 @@ JS_STATIC_ASSERT(sizeof(jsval_layout) ==
 #if JS_BITS_PER_WORD == 32
 
 /*
  * N.B. GCC, in some but not all cases, chooses to emit signed comparison of
  * JSValueTag even though its underlying type has been forced to be uint32_t.
  * Thus, all comparisons should explicitly cast operands to uint32_t.
  */
 
-static JS_ALWAYS_INLINE jsval_layout
+static MOZ_ALWAYS_INLINE jsval_layout
 BUILD_JSVAL(JSValueTag tag, uint32_t payload)
 {
     jsval_layout l;
     l.asBits = (((uint64_t)(uint32_t)tag) << 32) | payload;
     return l;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_DOUBLE_IMPL(jsval_layout l)
 {
     return (uint32_t)l.s.tag <= (uint32_t)JSVAL_TAG_CLEAR;
 }
 
-static JS_ALWAYS_INLINE jsval_layout
+static MOZ_ALWAYS_INLINE jsval_layout
 DOUBLE_TO_JSVAL_IMPL(double d)
 {
     jsval_layout l;
     l.asDouble = d;
-    JS_ASSERT(JSVAL_IS_DOUBLE_IMPL(l));
+    MOZ_ASSERT(JSVAL_IS_DOUBLE_IMPL(l));
     return l;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_INT32_IMPL(jsval_layout l)
 {
     return l.s.tag == JSVAL_TAG_INT32;
 }
 
-static JS_ALWAYS_INLINE int32_t
+static MOZ_ALWAYS_INLINE int32_t
 JSVAL_TO_INT32_IMPL(jsval_layout l)
 {
     return l.s.payload.i32;
 }
 
-static JS_ALWAYS_INLINE jsval_layout
+static MOZ_ALWAYS_INLINE jsval_layout
 INT32_TO_JSVAL_IMPL(int32_t i)
 {
     jsval_layout l;
     l.s.tag = JSVAL_TAG_INT32;
     l.s.payload.i32 = i;
     return l;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_NUMBER_IMPL(jsval_layout l)
 {
     JSValueTag tag = l.s.tag;
-    JS_ASSERT(tag != JSVAL_TAG_CLEAR);
+    MOZ_ASSERT(tag != JSVAL_TAG_CLEAR);
     return (uint32_t)tag <= (uint32_t)JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_UNDEFINED_IMPL(jsval_layout l)
 {
     return l.s.tag == JSVAL_TAG_UNDEFINED;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_STRING_IMPL(jsval_layout l)
 {
     return l.s.tag == JSVAL_TAG_STRING;
 }
 
-static JS_ALWAYS_INLINE jsval_layout
+static MOZ_ALWAYS_INLINE jsval_layout
 STRING_TO_JSVAL_IMPL(JSString *str)
 {
     jsval_layout l;
-    JS_ASSERT(str);
+    MOZ_ASSERT(str);
     l.s.tag = JSVAL_TAG_STRING;
     l.s.payload.str = str;
     return l;
 }
 
-static JS_ALWAYS_INLINE JSString *
+static MOZ_ALWAYS_INLINE JSString *
 JSVAL_TO_STRING_IMPL(jsval_layout l)
 {
     return l.s.payload.str;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_BOOLEAN_IMPL(jsval_layout l)
 {
     return l.s.tag == JSVAL_TAG_BOOLEAN;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_TO_BOOLEAN_IMPL(jsval_layout l)
 {
     return l.s.payload.boo;
 }
 
-static JS_ALWAYS_INLINE jsval_layout
+static MOZ_ALWAYS_INLINE jsval_layout
 BOOLEAN_TO_JSVAL_IMPL(JSBool b)
 {
     jsval_layout l;
-    JS_ASSERT(b == JS_TRUE || b == JS_FALSE);
+    MOZ_ASSERT(b == JS_TRUE || b == JS_FALSE);
     l.s.tag = JSVAL_TAG_BOOLEAN;
     l.s.payload.boo = b;
     return l;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_MAGIC_IMPL(jsval_layout l)
 {
     return l.s.tag == JSVAL_TAG_MAGIC;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_OBJECT_IMPL(jsval_layout l)
 {
     return l.s.tag == JSVAL_TAG_OBJECT;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_PRIMITIVE_IMPL(jsval_layout l)
 {
     return (uint32_t)l.s.tag < (uint32_t)JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_OBJECT_OR_NULL_IMPL(jsval_layout l)
 {
-    JS_ASSERT((uint32_t)l.s.tag <= (uint32_t)JSVAL_TAG_OBJECT);
+    MOZ_ASSERT((uint32_t)l.s.tag <= (uint32_t)JSVAL_TAG_OBJECT);
     return (uint32_t)l.s.tag >= (uint32_t)JSVAL_LOWER_INCL_TAG_OF_OBJ_OR_NULL_SET;
 }
 
-static JS_ALWAYS_INLINE JSObject *
+static MOZ_ALWAYS_INLINE JSObject *
 JSVAL_TO_OBJECT_IMPL(jsval_layout l)
 {
     return l.s.payload.obj;
 }
 
-static JS_ALWAYS_INLINE jsval_layout
+static MOZ_ALWAYS_INLINE jsval_layout
 OBJECT_TO_JSVAL_IMPL(JSObject *obj)
 {
     jsval_layout l;
-    JS_ASSERT(obj);
+    MOZ_ASSERT(obj);
     l.s.tag = JSVAL_TAG_OBJECT;
     l.s.payload.obj = obj;
     return l;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_NULL_IMPL(jsval_layout l)
 {
     return l.s.tag == JSVAL_TAG_NULL;
 }
 
-static JS_ALWAYS_INLINE jsval_layout
+static MOZ_ALWAYS_INLINE jsval_layout
 PRIVATE_PTR_TO_JSVAL_IMPL(void *ptr)
 {
     jsval_layout l;
-    JS_ASSERT(((uint32_t)ptr & 1) == 0);
+    MOZ_ASSERT(((uint32_t)ptr & 1) == 0);
     l.s.tag = (JSValueTag)0;
     l.s.payload.ptr = ptr;
-    JS_ASSERT(JSVAL_IS_DOUBLE_IMPL(l));
+    MOZ_ASSERT(JSVAL_IS_DOUBLE_IMPL(l));
     return l;
 }
 
-static JS_ALWAYS_INLINE void *
+static MOZ_ALWAYS_INLINE void *
 JSVAL_TO_PRIVATE_PTR_IMPL(jsval_layout l)
 {
     return l.s.payload.ptr;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_GCTHING_IMPL(jsval_layout l)
 {
     /* gcc sometimes generates signed < without explicit casts. */
     return (uint32_t)l.s.tag >= (uint32_t)JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET;
 }
 
-static JS_ALWAYS_INLINE void *
+static MOZ_ALWAYS_INLINE void *
 JSVAL_TO_GCTHING_IMPL(jsval_layout l)
 {
     return l.s.payload.ptr;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_TRACEABLE_IMPL(jsval_layout l)
 {
     return l.s.tag == JSVAL_TAG_STRING || l.s.tag == JSVAL_TAG_OBJECT;
 }
 
-static JS_ALWAYS_INLINE uint32_t
+static MOZ_ALWAYS_INLINE uint32_t
 JSVAL_TRACE_KIND_IMPL(jsval_layout l)
 {
     return (uint32_t)(JSBool)JSVAL_IS_STRING_IMPL(l);
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_SPECIFIC_INT32_IMPL(jsval_layout l, int32_t i32)
 {
     return l.s.tag == JSVAL_TAG_INT32 && l.s.payload.i32 == i32;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_SPECIFIC_BOOLEAN(jsval_layout l, JSBool b)
 {
     return (l.s.tag == JSVAL_TAG_BOOLEAN) && (l.s.payload.boo == b);
 }
 
-static JS_ALWAYS_INLINE jsval_layout
+static MOZ_ALWAYS_INLINE jsval_layout
 MAGIC_TO_JSVAL_IMPL(JSWhyMagic why)
 {
     jsval_layout l;
     l.s.tag = JSVAL_TAG_MAGIC;
     l.s.payload.why = why;
     return l;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_SAME_TYPE_IMPL(jsval_layout lhs, jsval_layout rhs)
 {
     JSValueTag ltag = lhs.s.tag, rtag = rhs.s.tag;
     return ltag == rtag || (ltag < JSVAL_TAG_CLEAR && rtag < JSVAL_TAG_CLEAR);
 }
 
-static JS_ALWAYS_INLINE jsval_layout
-PRIVATE_UINT32_TO_JSVAL_IMPL(uint32_t ui)
-{
-    jsval_layout l;
-    l.s.tag = (JSValueTag)0;
-    l.s.payload.u32 = ui;
-    JS_ASSERT(JSVAL_IS_DOUBLE_IMPL(l));
-    return l;
-}
-
-static JS_ALWAYS_INLINE uint32_t
-JSVAL_TO_PRIVATE_UINT32_IMPL(jsval_layout l)
-{
-    return l.s.payload.u32;
-}
-
-static JS_ALWAYS_INLINE JSValueType
+static MOZ_ALWAYS_INLINE JSValueType
 JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(jsval_layout l)
 {
     uint32_t type = l.s.tag & 0xF;
-    JS_ASSERT(type > JSVAL_TYPE_DOUBLE);
+    MOZ_ASSERT(type > JSVAL_TYPE_DOUBLE);
     return (JSValueType)type;
 }
 
 #elif JS_BITS_PER_WORD == 64
 
-static JS_ALWAYS_INLINE jsval_layout
+static MOZ_ALWAYS_INLINE jsval_layout
 BUILD_JSVAL(JSValueTag tag, uint64_t payload)
 {
     jsval_layout l;
     l.asBits = (((uint64_t)(uint32_t)tag) << JSVAL_TAG_SHIFT) | payload;
     return l;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_DOUBLE_IMPL(jsval_layout l)
 {
     return l.asBits <= JSVAL_SHIFTED_TAG_MAX_DOUBLE;
 }
 
-static JS_ALWAYS_INLINE jsval_layout
+static MOZ_ALWAYS_INLINE jsval_layout
 DOUBLE_TO_JSVAL_IMPL(double d)
 {
     jsval_layout l;
     l.asDouble = d;
-    JS_ASSERT(l.asBits <= JSVAL_SHIFTED_TAG_MAX_DOUBLE);
+    MOZ_ASSERT(l.asBits <= JSVAL_SHIFTED_TAG_MAX_DOUBLE);
     return l;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_INT32_IMPL(jsval_layout l)
 {
     return (uint32_t)(l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_INT32;
 }
 
-static JS_ALWAYS_INLINE int32_t
+static MOZ_ALWAYS_INLINE int32_t
 JSVAL_TO_INT32_IMPL(jsval_layout l)
 {
     return (int32_t)l.asBits;
 }
 
-static JS_ALWAYS_INLINE jsval_layout
+static MOZ_ALWAYS_INLINE jsval_layout
 INT32_TO_JSVAL_IMPL(int32_t i32)
 {
     jsval_layout l;
     l.asBits = ((uint64_t)(uint32_t)i32) | JSVAL_SHIFTED_TAG_INT32;
     return l;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_NUMBER_IMPL(jsval_layout l)
 {
     return l.asBits < JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_NUMBER_SET;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_UNDEFINED_IMPL(jsval_layout l)
 {
     return l.asBits == JSVAL_SHIFTED_TAG_UNDEFINED;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_STRING_IMPL(jsval_layout l)
 {
     return (uint32_t)(l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_STRING;
 }
 
-static JS_ALWAYS_INLINE jsval_layout
+static MOZ_ALWAYS_INLINE jsval_layout
 STRING_TO_JSVAL_IMPL(JSString *str)
 {
     jsval_layout l;
     uint64_t strBits = (uint64_t)str;
-    JS_ASSERT(str);
-    JS_ASSERT((strBits >> JSVAL_TAG_SHIFT) == 0);
+    MOZ_ASSERT(str);
+    MOZ_ASSERT((strBits >> JSVAL_TAG_SHIFT) == 0);
     l.asBits = strBits | JSVAL_SHIFTED_TAG_STRING;
     return l;
 }
 
-static JS_ALWAYS_INLINE JSString *
+static MOZ_ALWAYS_INLINE JSString *
 JSVAL_TO_STRING_IMPL(jsval_layout l)
 {
     return (JSString *)(l.asBits & JSVAL_PAYLOAD_MASK);
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_BOOLEAN_IMPL(jsval_layout l)
 {
     return (uint32_t)(l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_BOOLEAN;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_TO_BOOLEAN_IMPL(jsval_layout l)
 {
     return (JSBool)l.asBits;
 }
 
-static JS_ALWAYS_INLINE jsval_layout
+static MOZ_ALWAYS_INLINE jsval_layout
 BOOLEAN_TO_JSVAL_IMPL(JSBool b)
 {
     jsval_layout l;
-    JS_ASSERT(b == JS_TRUE || b == JS_FALSE);
+    MOZ_ASSERT(b == JS_TRUE || b == JS_FALSE);
     l.asBits = ((uint64_t)(uint32_t)b) | JSVAL_SHIFTED_TAG_BOOLEAN;
     return l;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_MAGIC_IMPL(jsval_layout l)
 {
     return (l.asBits >> JSVAL_TAG_SHIFT) == JSVAL_TAG_MAGIC;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_PRIMITIVE_IMPL(jsval_layout l)
 {
     return l.asBits < JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_PRIMITIVE_SET;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_OBJECT_IMPL(jsval_layout l)
 {
-    JS_ASSERT((l.asBits >> JSVAL_TAG_SHIFT) <= JSVAL_SHIFTED_TAG_OBJECT);
+    MOZ_ASSERT((l.asBits >> JSVAL_TAG_SHIFT) <= JSVAL_SHIFTED_TAG_OBJECT);
     return l.asBits >= JSVAL_SHIFTED_TAG_OBJECT;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_OBJECT_OR_NULL_IMPL(jsval_layout l)
 {
-    JS_ASSERT((l.asBits >> JSVAL_TAG_SHIFT) <= JSVAL_TAG_OBJECT);
+    MOZ_ASSERT((l.asBits >> JSVAL_TAG_SHIFT) <= JSVAL_TAG_OBJECT);
     return l.asBits >= JSVAL_LOWER_INCL_SHIFTED_TAG_OF_OBJ_OR_NULL_SET;
 }
 
-static JS_ALWAYS_INLINE JSObject *
+static MOZ_ALWAYS_INLINE JSObject *
 JSVAL_TO_OBJECT_IMPL(jsval_layout l)
 {
     uint64_t ptrBits = l.asBits & JSVAL_PAYLOAD_MASK;
-    JS_ASSERT((ptrBits & 0x7) == 0);
+    MOZ_ASSERT((ptrBits & 0x7) == 0);
     return (JSObject *)ptrBits;
 }
 
-static JS_ALWAYS_INLINE jsval_layout
+static MOZ_ALWAYS_INLINE jsval_layout
 OBJECT_TO_JSVAL_IMPL(JSObject *obj)
 {
     jsval_layout l;
     uint64_t objBits = (uint64_t)obj;
-    JS_ASSERT(obj);
-    JS_ASSERT((objBits >> JSVAL_TAG_SHIFT) == 0);
+    MOZ_ASSERT(obj);
+    MOZ_ASSERT((objBits >> JSVAL_TAG_SHIFT) == 0);
     l.asBits = objBits | JSVAL_SHIFTED_TAG_OBJECT;
     return l;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_NULL_IMPL(jsval_layout l)
 {
     return l.asBits == JSVAL_SHIFTED_TAG_NULL;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_GCTHING_IMPL(jsval_layout l)
 {
     return l.asBits >= JSVAL_LOWER_INCL_SHIFTED_TAG_OF_GCTHING_SET;
 }
 
-static JS_ALWAYS_INLINE void *
+static MOZ_ALWAYS_INLINE void *
 JSVAL_TO_GCTHING_IMPL(jsval_layout l)
 {
     uint64_t ptrBits = l.asBits & JSVAL_PAYLOAD_MASK;
-    JS_ASSERT((ptrBits & 0x7) == 0);
+    MOZ_ASSERT((ptrBits & 0x7) == 0);
     return (void *)ptrBits;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_TRACEABLE_IMPL(jsval_layout l)
 {
     return JSVAL_IS_GCTHING_IMPL(l) && !JSVAL_IS_NULL_IMPL(l);
 }
 
-static JS_ALWAYS_INLINE uint32_t
+static MOZ_ALWAYS_INLINE uint32_t
 JSVAL_TRACE_KIND_IMPL(jsval_layout l)
 {
     return (uint32_t)(JSBool)!(JSVAL_IS_OBJECT_IMPL(l));
 }
 
-static JS_ALWAYS_INLINE jsval_layout
+static MOZ_ALWAYS_INLINE jsval_layout
 PRIVATE_PTR_TO_JSVAL_IMPL(void *ptr)
 {
     jsval_layout l;
     uint64_t ptrBits = (uint64_t)ptr;
-    JS_ASSERT((ptrBits & 1) == 0);
+    MOZ_ASSERT((ptrBits & 1) == 0);
     l.asBits = ptrBits >> 1;
-    JS_ASSERT(JSVAL_IS_DOUBLE_IMPL(l));
+    MOZ_ASSERT(JSVAL_IS_DOUBLE_IMPL(l));
     return l;
 }
 
-static JS_ALWAYS_INLINE void *
+static MOZ_ALWAYS_INLINE void *
 JSVAL_TO_PRIVATE_PTR_IMPL(jsval_layout l)
 {
-    JS_ASSERT((l.asBits & 0x8000000000000000LL) == 0);
+    MOZ_ASSERT((l.asBits & 0x8000000000000000LL) == 0);
     return (void *)(l.asBits << 1);
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_SPECIFIC_INT32_IMPL(jsval_layout l, int32_t i32)
 {
     return l.asBits == (((uint64_t)(uint32_t)i32) | JSVAL_SHIFTED_TAG_INT32);
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_IS_SPECIFIC_BOOLEAN(jsval_layout l, JSBool b)
 {
     return l.asBits == (((uint64_t)(uint32_t)b) | JSVAL_SHIFTED_TAG_BOOLEAN);
 }
 
-static JS_ALWAYS_INLINE jsval_layout
+static MOZ_ALWAYS_INLINE jsval_layout
 MAGIC_TO_JSVAL_IMPL(JSWhyMagic why)
 {
     jsval_layout l;
     l.asBits = ((uint64_t)(uint32_t)why) | JSVAL_SHIFTED_TAG_MAGIC;
     return l;
 }
 
-static JS_ALWAYS_INLINE JSBool
+static MOZ_ALWAYS_INLINE JSBool
 JSVAL_SAME_TYPE_IMPL(jsval_layout lhs, jsval_layout rhs)
 {
     uint64_t lbits = lhs.asBits, rbits = rhs.asBits;
     return (lbits <= JSVAL_SHIFTED_TAG_MAX_DOUBLE && rbits <= JSVAL_SHIFTED_TAG_MAX_DOUBLE) ||
            (((lbits ^ rbits) & 0xFFFF800000000000LL) == 0);
 }
 
-static JS_ALWAYS_INLINE jsval_layout
-PRIVATE_UINT32_TO_JSVAL_IMPL(uint32_t ui)
-{
-    jsval_layout l;
-    l.asBits = (uint64_t)ui;
-    JS_ASSERT(JSVAL_IS_DOUBLE_IMPL(l));
-    return l;
-}
-
-static JS_ALWAYS_INLINE uint32_t
-JSVAL_TO_PRIVATE_UINT32_IMPL(jsval_layout l)
-{
-    JS_ASSERT((l.asBits >> 32) == 0);
-    return (uint32_t)l.asBits;
-}
-
-static JS_ALWAYS_INLINE JSValueType
+static MOZ_ALWAYS_INLINE JSValueType
 JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(jsval_layout l)
 {
    uint64_t type = (l.asBits >> JSVAL_TAG_SHIFT) & 0xF;
-   JS_ASSERT(type > JSVAL_TYPE_DOUBLE);
+   MOZ_ASSERT(type > JSVAL_TYPE_DOUBLE);
    return (JSValueType)type;
 }
 
 #endif  /* JS_BITS_PER_WORD */
 
-static JS_ALWAYS_INLINE double
+static MOZ_ALWAYS_INLINE double
 JS_CANONICALIZE_NAN(double d)
 {
-    if (JS_UNLIKELY(d != d)) {
+    if (MOZ_UNLIKELY(d != d)) {
         jsval_layout l;
         l.asBits = 0x7FF8000000000000LL;
         return l.asDouble;
     }
     return d;
 }
 
-#ifdef __cplusplus
-static jsval_layout JSVAL_TO_IMPL(JS::Value);
-static JS::Value IMPL_TO_JSVAL(jsval_layout);
+static MOZ_ALWAYS_INLINE jsval_layout JSVAL_TO_IMPL(JS::Value v);
+static MOZ_ALWAYS_INLINE JS::Value IMPL_TO_JSVAL(jsval_layout l);
+
+namespace JS {
+
+/*
+ * JS::Value is the interface for a single JavaScript Engine value.  A few
+ * general notes on JS::Value:
+ *
+ * - JS::Value has setX() and isX() members for X in
+ *
+ *     { Int32, Double, String, Boolean, Undefined, Null, Object, Magic }
+ *
+ *   JS::Value also contains toX() for each of the non-singleton types.
+ *
+ * - Magic is a singleton type whose payload contains a JSWhyMagic "reason" for
+ *   the magic value. By providing JSWhyMagic values when creating and checking
+ *   for magic values, it is possible to assert, at runtime, that only magic
+ *   values with the expected reason flow through a particular value. For
+ *   example, if cx->exception has a magic value, the reason must be
+ *   JS_GENERATOR_CLOSING.
+ *
+ * - The JS::Value operations are preferred.  The JSVAL_* operations remain for
+ *   compatibility; they may be removed at some point.  These operations mostly
+ *   provide similar functionality.  But there are a few key differences.  One
+ *   is that JS::Value gives null a separate type. Thus
+ *
+ *           JSVAL_IS_OBJECT(v) === v.isObjectOrNull()
+ *       !JSVAL_IS_PRIMITIVE(v) === v.isObject()
+ *
+ *   Also, to help prevent mistakenly boxing a nullable JSObject* as an object,
+ *   Value::setObject takes a JSObject&. (Conversely, Value::asObject returns a
+ *   JSObject&.)  A convenience member Value::setObjectOrNull is provided.
+ *
+ * - JSVAL_VOID is the same as the singleton value of the Undefined type.
+ *
+ * - Note that JS::Value is 8 bytes on 32 and 64-bit architectures. Thus, on
+ *   32-bit user code should avoid copying jsval/JS::Value as much as possible,
+ *   preferring to pass by const Value &.
+ */
+class Value
+{
+  public:
+    /*
+     * N.B. the default constructor leaves Value unitialized. Adding a default
+     * constructor prevents Value from being stored in a union.
+     */
+
+    /*** Mutators ***/
+
+    MOZ_ALWAYS_INLINE
+    void setNull() {
+        data.asBits = BUILD_JSVAL(JSVAL_TAG_NULL, 0).asBits;
+    }
+
+    MOZ_ALWAYS_INLINE
+    void setUndefined() {
+        data.asBits = BUILD_JSVAL(JSVAL_TAG_UNDEFINED, 0).asBits;
+    }
+
+    MOZ_ALWAYS_INLINE
+    void setInt32(int32_t i) {
+        data = INT32_TO_JSVAL_IMPL(i);
+    }
+
+    MOZ_ALWAYS_INLINE
+    int32_t &getInt32Ref() {
+        MOZ_ASSERT(isInt32());
+        return data.s.payload.i32;
+    }
+
+    MOZ_ALWAYS_INLINE
+    void setDouble(double d) {
+        data = DOUBLE_TO_JSVAL_IMPL(d);
+    }
+
+    MOZ_ALWAYS_INLINE
+    double &getDoubleRef() {
+        MOZ_ASSERT(isDouble());
+        return data.asDouble;
+    }
+
+    MOZ_ALWAYS_INLINE
+    void setString(JSString *str) {
+        MOZ_ASSERT(!IsPoisonedPtr(str));
+        data = STRING_TO_JSVAL_IMPL(str);
+    }
+
+    MOZ_ALWAYS_INLINE
+    void setString(const JS::Anchor<JSString *> &str) {
+        setString(str.get());
+    }
+
+    MOZ_ALWAYS_INLINE
+    void setObject(JSObject &obj) {
+        MOZ_ASSERT(!IsPoisonedPtr(&obj));
+        data = OBJECT_TO_JSVAL_IMPL(&obj);
+    }
+
+    MOZ_ALWAYS_INLINE
+    void setBoolean(bool b) {
+        data = BOOLEAN_TO_JSVAL_IMPL(b);
+    }
+
+    MOZ_ALWAYS_INLINE
+    void setMagic(JSWhyMagic why) {
+        data = MAGIC_TO_JSVAL_IMPL(why);
+    }
+
+    MOZ_ALWAYS_INLINE
+    bool setNumber(uint32_t ui) {
+        if (ui > JSVAL_INT_MAX) {
+            setDouble((double)ui);
+            return false;
+        } else {
+            setInt32((int32_t)ui);
+            return true;
+        }
+    }
+
+    MOZ_ALWAYS_INLINE
+    bool setNumber(double d) {
+        int32_t i;
+        if (MOZ_DOUBLE_IS_INT32(d, &i)) {
+            setInt32(i);
+            return true;
+        } else {
+            setDouble(d);
+            return false;
+        }
+    }
+
+    MOZ_ALWAYS_INLINE
+    void setObjectOrNull(JSObject *arg) {
+        if (arg)
+            setObject(*arg);
+        else
+            setNull();
+    }
+
+    MOZ_ALWAYS_INLINE
+    void swap(Value &rhs) {
+        uint64_t tmp = rhs.data.asBits;
+        rhs.data.asBits = data.asBits;
+        data.asBits = tmp;
+    }
+
+    /*** Value type queries ***/
+
+    MOZ_ALWAYS_INLINE
+    bool isUndefined() const {
+        return JSVAL_IS_UNDEFINED_IMPL(data);
+    }
+
+    MOZ_ALWAYS_INLINE
+    bool isNull() const {
+        return JSVAL_IS_NULL_IMPL(data);
+    }
+
+    MOZ_ALWAYS_INLINE
+    bool isNullOrUndefined() const {
+        return isNull() || isUndefined();
+    }
+
+    MOZ_ALWAYS_INLINE
+    bool isInt32() const {
+        return JSVAL_IS_INT32_IMPL(data);
+    }
+
+    MOZ_ALWAYS_INLINE
+    bool isInt32(int32_t i32) const {
+        return JSVAL_IS_SPECIFIC_INT32_IMPL(data, i32);
+    }
+
+    MOZ_ALWAYS_INLINE
+    bool isDouble() const {
+        return JSVAL_IS_DOUBLE_IMPL(data);
+    }
+
+    MOZ_ALWAYS_INLINE
+    bool isNumber() const {
+        return JSVAL_IS_NUMBER_IMPL(data);
+    }
+
+    MOZ_ALWAYS_INLINE
+    bool isString() const {
+        return JSVAL_IS_STRING_IMPL(data);
+    }
+
+    MOZ_ALWAYS_INLINE
+    bool isObject() const {
+        return JSVAL_IS_OBJECT_IMPL(data);
+    }
+
+    MOZ_ALWAYS_INLINE
+    bool isPrimitive() const {
+        return JSVAL_IS_PRIMITIVE_IMPL(data);
+    }
+
+    MOZ_ALWAYS_INLINE
+    bool isObjectOrNull() const {
+        return JSVAL_IS_OBJECT_OR_NULL_IMPL(data);
+    }
+
+    MOZ_ALWAYS_INLINE
+    bool isGCThing() const {
+        return JSVAL_IS_GCTHING_IMPL(data);
+    }
+
+    MOZ_ALWAYS_INLINE
+    bool isBoolean() const {
+        return JSVAL_IS_BOOLEAN_IMPL(data);
+    }
+
+    MOZ_ALWAYS_INLINE
+    bool isTrue() const {
+        return JSVAL_IS_SPECIFIC_BOOLEAN(data, true);
+    }
+
+    MOZ_ALWAYS_INLINE
+    bool isFalse() const {
+        return JSVAL_IS_SPECIFIC_BOOLEAN(data, false);
+    }
+
+    MOZ_ALWAYS_INLINE
+    bool isMagic() const {
+        return JSVAL_IS_MAGIC_IMPL(data);
+    }
+
+    MOZ_ALWAYS_INLINE
+    bool isMagic(JSWhyMagic why) const {
+        MOZ_ASSERT_IF(isMagic(), data.s.payload.why == why);
+        return JSVAL_IS_MAGIC_IMPL(data);
+    }
+
+    MOZ_ALWAYS_INLINE
+    bool isMarkable() const {
+        return JSVAL_IS_TRACEABLE_IMPL(data);
+    }
+
+    MOZ_ALWAYS_INLINE
+    JSGCTraceKind gcKind() const {
+        MOZ_ASSERT(isMarkable());
+        return JSGCTraceKind(JSVAL_TRACE_KIND_IMPL(data));
+    }
+
+    MOZ_ALWAYS_INLINE
+    JSWhyMagic whyMagic() const {
+        MOZ_ASSERT(isMagic());
+        return data.s.payload.why;
+    }
+
+    /*** Comparison ***/
+
+    MOZ_ALWAYS_INLINE
+    bool operator==(const Value &rhs) const {
+        return data.asBits == rhs.data.asBits;
+    }
+
+    MOZ_ALWAYS_INLINE
+    bool operator!=(const Value &rhs) const {
+        return data.asBits != rhs.data.asBits;
+    }
+
+    friend inline bool SameType(const Value &lhs, const Value &rhs);
+
+    /*** Extract the value's typed payload ***/
+
+    MOZ_ALWAYS_INLINE
+    int32_t toInt32() const {
+        MOZ_ASSERT(isInt32());
+        return JSVAL_TO_INT32_IMPL(data);
+    }
+
+    MOZ_ALWAYS_INLINE
+    double toDouble() const {
+        MOZ_ASSERT(isDouble());
+        return data.asDouble;
+    }
+
+    MOZ_ALWAYS_INLINE
+    double toNumber() const {
+        MOZ_ASSERT(isNumber());
+        return isDouble() ? toDouble() : double(toInt32());
+    }
+
+    MOZ_ALWAYS_INLINE
+    JSString *toString() const {
+        MOZ_ASSERT(isString());
+        return JSVAL_TO_STRING_IMPL(data);
+    }
+
+    MOZ_ALWAYS_INLINE
+    JSObject &toObject() const {
+        MOZ_ASSERT(isObject());
+        return *JSVAL_TO_OBJECT_IMPL(data);
+    }
+
+    MOZ_ALWAYS_INLINE
+    JSObject *toObjectOrNull() const {
+        MOZ_ASSERT(isObjectOrNull());
+        return JSVAL_TO_OBJECT_IMPL(data);
+    }
+
+    MOZ_ALWAYS_INLINE
+    void *toGCThing() const {
+        MOZ_ASSERT(isGCThing());
+        return JSVAL_TO_GCTHING_IMPL(data);
+    }
+
+    MOZ_ALWAYS_INLINE
+    bool toBoolean() const {
+        MOZ_ASSERT(isBoolean());
+        return JSVAL_TO_BOOLEAN_IMPL(data);
+    }
+
+    MOZ_ALWAYS_INLINE
+    uint32_t payloadAsRawUint32() const {
+        MOZ_ASSERT(!isDouble());
+        return data.s.payload.u32;
+    }
+
+    MOZ_ALWAYS_INLINE
+    uint64_t asRawBits() const {
+        return data.asBits;
+    }
+
+    MOZ_ALWAYS_INLINE
+    JSValueType extractNonDoubleType() const {
+        return JSVAL_EXTRACT_NON_DOUBLE_TYPE_IMPL(data);
+    }
+
+    /*
+     * Private API
+     *
+     * Private setters/getters allow the caller to read/write arbitrary types
+     * that fit in the 64-bit payload. It is the caller's responsibility, after
+     * storing to a value with setPrivateX to read only using getPrivateX.
+     * Privates values are given a type type which ensures they are not marked.
+     */
+
+    MOZ_ALWAYS_INLINE
+    void setPrivate(void *ptr) {
+        data = PRIVATE_PTR_TO_JSVAL_IMPL(ptr);
+    }
+
+    MOZ_ALWAYS_INLINE
+    void *toPrivate() const {
+        MOZ_ASSERT(JSVAL_IS_DOUBLE_IMPL(data));
+        return JSVAL_TO_PRIVATE_PTR_IMPL(data);
+    }
+
+    MOZ_ALWAYS_INLINE
+    void setPrivateUint32(uint32_t ui) {
+        MOZ_ASSERT(uint32_t(int32_t(ui)) == ui);
+        setInt32(int32_t(ui));
+    }
+
+    MOZ_ALWAYS_INLINE
+    uint32_t toPrivateUint32() const {
+        return uint32_t(toInt32());
+    }
+
+    /*
+     * An unmarked value is just a void* cast as a Value. Thus, the Value is
+     * not safe for GC and must not be marked. This API avoids raw casts
+     * and the ensuing strict-aliasing warnings.
+     */
+
+    MOZ_ALWAYS_INLINE
+    void setUnmarkedPtr(void *ptr) {
+        data.asPtr = ptr;
+    }
+
+    MOZ_ALWAYS_INLINE
+    void *toUnmarkedPtr() const {
+        return data.asPtr;
+    }
+
+    const size_t *payloadWord() const {
+#if JS_BITS_PER_WORD == 32
+        return &data.s.payload.word;
+#elif JS_BITS_PER_WORD == 64
+        return &data.asWord;
+#endif
+    }
+
+    const uintptr_t *payloadUIntPtr() const {
+#if JS_BITS_PER_WORD == 32
+        return &data.s.payload.uintptr;
+#elif JS_BITS_PER_WORD == 64
+        return &data.asUIntPtr;
+#endif
+    }
+
+#if !defined(_MSC_VER) && !defined(__sparc)
+  // Value must be POD so that MSVC will pass it by value and not in memory
+  // (bug 689101); the same is true for SPARC as well (bug 737344).  More
+  // precisely, we don't want Value return values compiled as out params.
+  private:
 #endif
 
-#endif /* jsvalimpl_h__ */
+    jsval_layout data;
+
+  private:
+    void staticAssertions() {
+        JS_STATIC_ASSERT(sizeof(JSValueType) == 1);
+        JS_STATIC_ASSERT(sizeof(JSValueTag) == 4);
+        JS_STATIC_ASSERT(sizeof(JSBool) == 4);
+        JS_STATIC_ASSERT(sizeof(JSWhyMagic) <= 4);
+        JS_STATIC_ASSERT(sizeof(Value) == 8);
+    }
+
+    friend jsval_layout (::JSVAL_TO_IMPL)(Value);
+    friend Value (::IMPL_TO_JSVAL)(jsval_layout l);
+};
+
+inline bool
+IsPoisonedValue(const Value &v)
+{
+    if (v.isString())
+        return IsPoisonedPtr(v.toString());
+    if (v.isObject())
+        return IsPoisonedPtr(&v.toObject());
+    return false;
+}
+
+/************************************************************************/
+
+static MOZ_ALWAYS_INLINE Value
+NullValue()
+{
+    Value v;
+    v.setNull();
+    return v;
+}
+
+static MOZ_ALWAYS_INLINE Value
+UndefinedValue()
+{
+    Value v;
+    v.setUndefined();
+    return v;
+}
+
+static MOZ_ALWAYS_INLINE Value
+Int32Value(int32_t i32)
+{
+    Value v;
+    v.setInt32(i32);
+    return v;
+}
+
+static MOZ_ALWAYS_INLINE Value
+DoubleValue(double dbl)
+{
+    Value v;
+    v.setDouble(dbl);
+    return v;
+}
+
+static MOZ_ALWAYS_INLINE Value
+StringValue(JSString *str)
+{
+    Value v;
+    v.setString(str);
+    return v;
+}
+
+static MOZ_ALWAYS_INLINE Value
+BooleanValue(bool boo)
+{
+    Value v;
+    v.setBoolean(boo);
+    return v;
+}
+
+static MOZ_ALWAYS_INLINE Value
+ObjectValue(JSObject &obj)
+{
+    Value v;
+    v.setObject(obj);
+    return v;
+}
+
+static MOZ_ALWAYS_INLINE Value
+ObjectValueCrashOnTouch()
+{
+    Value v;
+    v.setObject(*reinterpret_cast<JSObject *>(0x42));
+    return v;
+}
+
+static MOZ_ALWAYS_INLINE Value
+MagicValue(JSWhyMagic why)
+{
+    Value v;
+    v.setMagic(why);
+    return v;
+}
+
+static MOZ_ALWAYS_INLINE Value
+NumberValue(float f)
+{
+    Value v;
+    v.setNumber(f);
+    return v;
+}
+
+static MOZ_ALWAYS_INLINE Value
+NumberValue(double dbl)
+{
+    Value v;
+    v.setNumber(dbl);
+    return v;
+}
+
+static MOZ_ALWAYS_INLINE Value
+NumberValue(int8_t i)
+{
+    return Int32Value(i);
+}
+
+static MOZ_ALWAYS_INLINE Value
+NumberValue(uint8_t i)
+{
+    return Int32Value(i);
+}
+
+static MOZ_ALWAYS_INLINE Value
+NumberValue(int16_t i)
+{
+    return Int32Value(i);
+}
+
+static MOZ_ALWAYS_INLINE Value
+NumberValue(uint16_t i)
+{
+    return Int32Value(i);
+}
+
+static MOZ_ALWAYS_INLINE Value
+NumberValue(int32_t i)
+{
+    return Int32Value(i);
+}
+
+static MOZ_ALWAYS_INLINE Value
+NumberValue(uint32_t i)
+{
+    Value v;
+    v.setNumber(i);
+    return v;
+}
+
+namespace detail {
+
+template <bool Signed>
+class MakeNumberValue
+{
+  public:
+    template<typename T>
+    static inline Value create(const T t)
+    {
+        Value v;
+        if (JSVAL_INT_MIN <= t && t <= JSVAL_INT_MAX)
+            v.setInt32(int32_t(t));
+        else
+            v.setDouble(double(t));
+        return v;
+    }
+};
+
+template <>
+class MakeNumberValue<false>
+{
+  public:
+    template<typename T>
+    static inline Value create(const T t)
+    {
+        Value v;
+        if (t <= JSVAL_INT_MAX)
+            v.setInt32(int32_t(t));
+        else
+            v.setDouble(double(t));
+        return v;
+    }
+};
+
+} // namespace detail
+
+template <typename T>
+static MOZ_ALWAYS_INLINE Value
+NumberValue(const T t)
+{
+    MOZ_ASSERT(T(double(t)) == t, "value creation would be lossy");
+    return detail::MakeNumberValue<std::numeric_limits<T>::is_signed>::create(t);
+}
+
+static MOZ_ALWAYS_INLINE Value
+ObjectOrNullValue(JSObject *obj)
+{
+    Value v;
+    v.setObjectOrNull(obj);
+    return v;
+}
+
+static MOZ_ALWAYS_INLINE Value
+PrivateValue(void *ptr)
+{
+    Value v;
+    v.setPrivate(ptr);
+    return v;
+}
+
+static MOZ_ALWAYS_INLINE Value
+PrivateUint32Value(uint32_t ui)
+{
+    Value v;
+    v.setPrivateUint32(ui);
+    return v;
+}
+
+MOZ_ALWAYS_INLINE bool
+SameType(const Value &lhs, const Value &rhs)
+{
+    return JSVAL_SAME_TYPE_IMPL(lhs.data, rhs.data);
+}
+
+} // namespace JS
+
+/************************************************************************/
+
+namespace js {
+
+template <> struct RootMethods<const JS::Value>
+{
+    static JS::Value initial() { return UndefinedValue(); }
+    static ThingRootKind kind() { return THING_ROOT_VALUE; }
+    static bool poisoned(const JS::Value &v) { return IsPoisonedValue(v); }
+};
+
+template <> struct RootMethods<JS::Value>
+{
+    static JS::Value initial() { return UndefinedValue(); }
+    static ThingRootKind kind() { return THING_ROOT_VALUE; }
+    static bool poisoned(const JS::Value &v) { return IsPoisonedValue(v); }
+};
+
+template <class Outer> class MutableValueOperations;
+
+/*
+ * A class designed for CRTP use in implementing the non-mutating parts of the
+ * Value interface in Value-like classes.  Outer must be a class inheriting
+ * ValueOperations<Outer> with a visible extract() method returning the
+ * const Value* abstracted by Outer.
+ */
+template <class Outer>
+class ValueOperations
+{
+    friend class MutableValueOperations<Outer>;
+    const JS::Value * value() const { return static_cast<const Outer*>(this)->extract(); }
+
+  public:
+    bool isUndefined() const { return value()->isUndefined(); }
+    bool isNull() const { return value()->isNull(); }
+    bool isBoolean() const { return value()->isBoolean(); }
+    bool isTrue() const { return value()->isTrue(); }
+    bool isFalse() const { return value()->isFalse(); }
+    bool isNumber() const { return value()->isNumber(); }
+    bool isInt32() const { return value()->isInt32(); }
+    bool isDouble() const { return value()->isDouble(); }
+    bool isString() const { return value()->isString(); }
+    bool isObject() const { return value()->isObject(); }
+    bool isMagic() const { return value()->isMagic(); }
+    bool isMagic(JSWhyMagic why) const { return value()->isMagic(why); }
+    bool isMarkable() const { return value()->isMarkable(); }
+    bool isPrimitive() const { return value()->isPrimitive(); }
+    bool isGCThing() const { return value()->isGCThing(); }
+
+    bool isNullOrUndefined() const { return value()->isNullOrUndefined(); }
+    bool isObjectOrNull() const { return value()->isObjectOrNull(); }
+
+    bool toBoolean() const { return value()->toBoolean(); }
+    double toNumber() const { return value()->toNumber(); }
+    int32_t toInt32() const { return value()->toInt32(); }
+    double toDouble() const { return value()->toDouble(); }
+    JSString *toString() const { return value()->toString(); }
+    JSObject &toObject() const { return value()->toObject(); }
+    JSObject *toObjectOrNull() const { return value()->toObjectOrNull(); }
+    void *toGCThing() const { return value()->toGCThing(); }
+
+    JSValueType extractNonDoubleType() const { return value()->extractNonDoubleType(); }
+
+#ifdef DEBUG
+    JSWhyMagic whyMagic() const { return value()->whyMagic(); }
+#endif
+};
+
+/*
+ * A class designed for CRTP use in implementing the mutating parts of the
+ * Value interface in Value-like classes.  Outer must be a class inheriting
+ * MutableValueOperations<Outer> with visible extractMutable() and extract()
+ * methods returning the const Value* and Value* abstracted by Outer.
+ */
+template <class Outer>
+class MutableValueOperations : public ValueOperations<Outer>
+{
+    JS::Value * value() { return static_cast<Outer*>(this)->extractMutable(); }
+
+  public:
+    void setNull() { value()->setNull(); }
+    void setUndefined() { value()->setUndefined(); }
+    void setInt32(int32_t i) { value()->setInt32(i); }
+    void setDouble(double d) { value()->setDouble(d); }
+    void setString(JSString *str) { value()->setString(str); }
+    void setString(const JS::Anchor<JSString *> &str) { value()->setString(str); }
+    void setObject(JSObject &obj) { value()->setObject(obj); }
+    void setBoolean(bool b) { value()->setBoolean(b); }
+    void setMagic(JSWhyMagic why) { value()->setMagic(why); }
+    bool setNumber(uint32_t ui) { return value()->setNumber(ui); }
+    bool setNumber(double d) { return value()->setNumber(d); }
+    void setObjectOrNull(JSObject *arg) { value()->setObjectOrNull(arg); }
+};
+
+/*
+ * Augment the generic Handle<T> interface when T = Value with type-querying
+ * and value-extracting operations.
+ */
+template <>
+class HandleBase<JS::Value> : public ValueOperations<Handle<JS::Value> >
+{
+    friend class ValueOperations<Handle<JS::Value> >;
+    const JS::Value * extract() const {
+        return static_cast<const Handle<JS::Value>*>(this)->address();
+    }
+};
+
+/*
+ * Augment the generic MutableHandle<T> interface when T = Value with
+ * type-querying, value-extracting, and mutating operations.
+ */
+template <>
+class MutableHandleBase<JS::Value> : public MutableValueOperations<MutableHandle<JS::Value> >
+{
+    friend class ValueOperations<MutableHandle<JS::Value> >;
+    const JS::Value * extract() const {
+        return static_cast<const MutableHandle<JS::Value>*>(this)->address();
+    }
+
+    friend class MutableValueOperations<MutableHandle<JS::Value> >;
+    JS::Value * extractMutable() {
+        return static_cast<MutableHandle<JS::Value>*>(this)->address();
+    }
+};
+
+/*
+ * Augment the generic Rooted<T> interface when T = Value with type-querying,
+ * value-extracting, and mutating operations.
+ */
+template <>
+class RootedBase<JS::Value> : public MutableValueOperations<Rooted<JS::Value> >
+{
+    friend class ValueOperations<Rooted<JS::Value> >;
+    const JS::Value * extract() const {
+        return static_cast<const Rooted<JS::Value>*>(this)->address();
+    }
+
+    friend class MutableValueOperations<Rooted<JS::Value> >;
+    JS::Value * extractMutable() {
+        return static_cast<Rooted<JS::Value>*>(this)->address();
+    }
+};
+
+} // namespace js
+
+MOZ_ALWAYS_INLINE jsval_layout
+JSVAL_TO_IMPL(JS::Value v)
+{
+    return v.data;
+}
+
+MOZ_ALWAYS_INLINE JS::Value
+IMPL_TO_JSVAL(jsval_layout l)
+{
+    JS::Value v;
+    v.data = l;
+    return v;
+}
+
+namespace JS {
+
+#ifndef __GNUC__
+/*
+ * The default assignment operator for |struct C| has the signature:
+ *
+ *   C& C::operator=(const C&)
+ *
+ * And in particular requires implicit conversion of |this| to type |C| for the
+ * return value. But |volatile C| cannot thus be converted to |C|, so just
+ * doing |sink = hold| as in the non-specialized version would fail to compile.
+ * Do the assignment on asBits instead, since I don't think we want to give
+ * jsval_layout an assignment operator returning |volatile jsval_layout|.
+ */
+template<>
+inline Anchor<Value>::~Anchor()
+{
+    volatile uint64_t bits;
+    bits = JSVAL_TO_IMPL(hold).asBits;
+}
+#endif
+
+#ifdef DEBUG
+namespace detail {
+
+struct ValueAlignmentTester { char c; JS::Value v; };
+MOZ_STATIC_ASSERT(sizeof(ValueAlignmentTester) == 16,
+                  "JS::Value must be 16-byte-aligned");
+
+struct LayoutAlignmentTester { char c; jsval_layout l; };
+MOZ_STATIC_ASSERT(sizeof(LayoutAlignmentTester) == 16,
+                  "jsval_layout must be 16-byte-aligned");
+
+} // namespace detail
+#endif /* DEBUG */
+
+} // namespace JS
+
+/*
+ * JS::Value and jsval are the same type; jsval is the old name, kept around
+ * for backwards compatibility along with all the JSVAL_* operations below.
+ * jsval_layout is an implementation detail and should not be used externally.
+ */
+typedef JS::Value jsval;
+
+MOZ_STATIC_ASSERT(sizeof(jsval_layout) == sizeof(JS::Value),
+                  "jsval_layout and JS::Value must have identical layouts");
+
+/************************************************************************/
+
+static MOZ_ALWAYS_INLINE JSBool
+JSVAL_IS_NULL(jsval v)
+{
+    return JSVAL_IS_NULL_IMPL(JSVAL_TO_IMPL(v));
+}
+
+static MOZ_ALWAYS_INLINE JSBool
+JSVAL_IS_VOID(jsval v)
+{
+    return JSVAL_IS_UNDEFINED_IMPL(JSVAL_TO_IMPL(v));
+}
+
+static MOZ_ALWAYS_INLINE JSBool
+JSVAL_IS_INT(jsval v)
+{
+    return JSVAL_IS_INT32_IMPL(JSVAL_TO_IMPL(v));
+}
+
+static MOZ_ALWAYS_INLINE int32_t
+JSVAL_TO_INT(jsval v)
+{
+    MOZ_ASSERT(JSVAL_IS_INT(v));
+    return JSVAL_TO_INT32_IMPL(JSVAL_TO_IMPL(v));
+}
+
+static MOZ_ALWAYS_INLINE jsval
+INT_TO_JSVAL(int32_t i)
+{
+    return IMPL_TO_JSVAL(INT32_TO_JSVAL_IMPL(i));
+}
+
+static MOZ_ALWAYS_INLINE JSBool
+JSVAL_IS_DOUBLE(jsval v)
+{
+    return JSVAL_IS_DOUBLE_IMPL(JSVAL_TO_IMPL(v));
+}
+
+static MOZ_ALWAYS_INLINE double
+JSVAL_TO_DOUBLE(jsval v)
+{
+    jsval_layout l;
+    MOZ_ASSERT(JSVAL_IS_DOUBLE(v));
+    l = JSVAL_TO_IMPL(v);
+    return l.asDouble;
+}
+
+static MOZ_ALWAYS_INLINE jsval
+DOUBLE_TO_JSVAL(double d)
+{
+    /*
+     * This is a manually inlined version of:
+     *    d = JS_CANONICALIZE_NAN(d);
+     *    return IMPL_TO_JSVAL(DOUBLE_TO_JSVAL_IMPL(d));
+     * because GCC from XCode 3.1.4 miscompiles the above code.
+     */
+    jsval_layout l;
+    if (MOZ_UNLIKELY(d != d))
+        l.asBits = 0x7FF8000000000000LL;
+    else
+        l.asDouble = d;
+    return IMPL_TO_JSVAL(l);
+}
+
+static MOZ_ALWAYS_INLINE jsval
+UINT_TO_JSVAL(uint32_t i)
+{
+    if (i <= JSVAL_INT_MAX)
+        return INT_TO_JSVAL((int32_t)i);
+    return DOUBLE_TO_JSVAL((double)i);
+}
+
+static MOZ_ALWAYS_INLINE JSBool
+JSVAL_IS_NUMBER(jsval v)
+{
+    return JSVAL_IS_NUMBER_IMPL(JSVAL_TO_IMPL(v));
+}
+
+static MOZ_ALWAYS_INLINE JSBool
+JSVAL_IS_STRING(jsval v)
+{
+    return JSVAL_IS_STRING_IMPL(JSVAL_TO_IMPL(v));
+}
+
+static MOZ_ALWAYS_INLINE JSString *
+JSVAL_TO_STRING(jsval v)
+{
+    MOZ_ASSERT(JSVAL_IS_STRING(v));
+    return JSVAL_TO_STRING_IMPL(JSVAL_TO_IMPL(v));
+}
+
+static MOZ_ALWAYS_INLINE jsval
+STRING_TO_JSVAL(JSString *str)
+{
+    return IMPL_TO_JSVAL(STRING_TO_JSVAL_IMPL(str));
+}
+
+static MOZ_ALWAYS_INLINE JSObject *
+JSVAL_TO_OBJECT(jsval v)
+{
+    MOZ_ASSERT(JSVAL_IS_OBJECT_OR_NULL_IMPL(JSVAL_TO_IMPL(v)));
+    return JSVAL_TO_OBJECT_IMPL(JSVAL_TO_IMPL(v));
+}
+
+static MOZ_ALWAYS_INLINE jsval
+OBJECT_TO_JSVAL(JSObject *obj)
+{
+    if (obj)
+        return IMPL_TO_JSVAL(OBJECT_TO_JSVAL_IMPL(obj));
+    return IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_NULL, 0));
+}
+
+static MOZ_ALWAYS_INLINE JSBool
+JSVAL_IS_BOOLEAN(jsval v)
+{
+    return JSVAL_IS_BOOLEAN_IMPL(JSVAL_TO_IMPL(v));
+}
+
+static MOZ_ALWAYS_INLINE JSBool
+JSVAL_TO_BOOLEAN(jsval v)
+{
+    MOZ_ASSERT(JSVAL_IS_BOOLEAN(v));
+    return JSVAL_TO_BOOLEAN_IMPL(JSVAL_TO_IMPL(v));
+}
+
+static MOZ_ALWAYS_INLINE jsval
+BOOLEAN_TO_JSVAL(JSBool b)
+{
+    return IMPL_TO_JSVAL(BOOLEAN_TO_JSVAL_IMPL(b));
+}
+
+static MOZ_ALWAYS_INLINE JSBool
+JSVAL_IS_PRIMITIVE(jsval v)
+{
+    return JSVAL_IS_PRIMITIVE_IMPL(JSVAL_TO_IMPL(v));
+}
+
+static MOZ_ALWAYS_INLINE JSBool
+JSVAL_IS_GCTHING(jsval v)
+{
+    return JSVAL_IS_GCTHING_IMPL(JSVAL_TO_IMPL(v));
+}
+
+static MOZ_ALWAYS_INLINE void *
+JSVAL_TO_GCTHING(jsval v)
+{
+    MOZ_ASSERT(JSVAL_IS_GCTHING(v));
+    return JSVAL_TO_GCTHING_IMPL(JSVAL_TO_IMPL(v));
+}
+
+/* To be GC-safe, privates are tagged as doubles. */
+
+static MOZ_ALWAYS_INLINE jsval
+PRIVATE_TO_JSVAL(void *ptr)
+{
+    return IMPL_TO_JSVAL(PRIVATE_PTR_TO_JSVAL_IMPL(ptr));
+}
+
+static MOZ_ALWAYS_INLINE void *
+JSVAL_TO_PRIVATE(jsval v)
+{
+    MOZ_ASSERT(JSVAL_IS_DOUBLE(v));
+    return JSVAL_TO_PRIVATE_PTR_IMPL(JSVAL_TO_IMPL(v));
+}
+
+#endif /* js_Value_h___ */
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -122,16 +122,17 @@ CPPSRCS		= \
 		prmjtime.cpp \
 		sharkctl.cpp \
 		ArgumentsObject.cpp \
 		DateTime.cpp \
 		Debugger.cpp \
 		GlobalObject.cpp \
 		Object.cpp \
 		ObjectImpl.cpp \
+		PropertyKey.cpp \
 		ScopeObject.cpp \
 		Shape.cpp \
 		Stack.cpp \
 		String.cpp \
 		BytecodeCompiler.cpp \
 		BytecodeEmitter.cpp \
 		CharacterEncoding.cpp \
 		FoldConstants.cpp \
@@ -185,17 +186,16 @@ INSTALLED_HEADERS = \
 		jsprf.h \
 		jsprototypes.h \
 		jsprvtd.h \
 		jspubtd.h \
 		jstypes.h \
 		jsutil.h \
 		jsversion.h \
 		jswrapper.h \
-		jsval.h \
 		$(NULL)
 
 ######################################################
 # BEGIN exported headers that are only exported
 #       because of inclusion by an INSTALLED_HEADER
 #
 EXPORTS_NAMESPACES += ds gc
 
@@ -219,24 +219,27 @@ VPATH		+= \
 		$(NULL)
 
 EXPORTS_NAMESPACES += js
 
 # If you add a header here, add it to js/src/jsapi-tests/testIntTypesABI.cpp so
 # that we ensure we don't over-expose our internal integer typedefs.  Note that
 # LegacyIntTypes.h below is deliberately exempted from this requirement.
 EXPORTS_js = \
+		Anchor.h \
 		CharacterEncoding.h \
+		GCAPI.h \
 		HashTable.h \
 		HeapAPI.h \
-		GCAPI.h \
 		LegacyIntTypes.h \
 		MemoryMetrics.h \
+		PropertyKey.h \
 		TemplateLib.h \
 		Utility.h \
+		Value.h \
 		Vector.h \
 		$(NULL)
 
 ###############################################
 # BEGIN enable non-releasable features
 #
 ifeq (,$(filter beta release esr,$(MOZ_UPDATE_CHANNEL)))
 DEFINES += -DENABLE_TYPEDARRAY_MOVE
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -577,16 +577,16 @@ js_InitIntlClass(JSContext *cx, HandleOb
 
     return Intl;
 }
 
 bool
 GlobalObject::initIntlObject(JSContext *cx, Handle<GlobalObject*> global)
 {
     RootedObject Intl(cx);
-    Intl = NewObjectWithGivenProto(cx, &IntlClass,
-                                   global->getOrCreateObjectPrototype(cx), global);
-    if (!Intl || !JSObject::setSingletonType(cx, Intl))
+    Intl = NewObjectWithGivenProto(cx, &IntlClass, global->getOrCreateObjectPrototype(cx),
+                                   global, SingletonObject);
+    if (!Intl)
         return false;
 
     global->setReservedSlot(JSProto_Intl, ObjectValue(*Intl));
     return true;
 }
--- a/js/src/builtin/ParallelArray.cpp
+++ b/js/src/builtin/ParallelArray.cpp
@@ -1001,20 +1001,20 @@ ParallelArrayObject::initClass(JSContext
         return NULL;
     }
 
     // Define the length and shape properties.
     RootedId lengthId(cx, AtomToId(cx->names().length));
     RootedId shapeId(cx, AtomToId(cx->names().shape));
     unsigned flags = JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_GETTER;
 
-    RootedObject scriptedLength(cx, js_NewFunction(cx, NullPtr(), NonGenericMethod<lengthGetter>,
-                                                   0, JSFunction::NATIVE_FUN, global, NullPtr()));
-    RootedObject scriptedShape(cx, js_NewFunction(cx, NullPtr(), NonGenericMethod<dimensionsGetter>,
-                                                  0, JSFunction::NATIVE_FUN, global, NullPtr()));
+    RootedObject scriptedLength(cx, NewFunction(cx, NullPtr(), NonGenericMethod<lengthGetter>,
+                                                0, JSFunction::NATIVE_FUN, global, NullPtr()));
+    RootedObject scriptedShape(cx, NewFunction(cx, NullPtr(), NonGenericMethod<dimensionsGetter>,
+                                               0, JSFunction::NATIVE_FUN, global, NullPtr()));
 
     RootedValue value(cx, UndefinedValue());
     if (!scriptedLength || !scriptedShape ||
         !DefineNativeProperty(cx, proto, lengthId, value,
                               JS_DATA_TO_FUNC_PTR(PropertyOp, scriptedLength.get()), NULL,
                               flags, 0, 0) ||
         !DefineNativeProperty(cx, proto, shapeId, value,
                               JS_DATA_TO_FUNC_PTR(PropertyOp, scriptedShape.get()), NULL,
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1081,17 +1081,18 @@ Parser::newFunction(ParseContext *pc, Ha
 
     RootedObject parent(context);
     parent = pc->sc->isFunctionBox() ? NULL : pc->sc->asGlobalSharedContext()->scopeChain();
 
     RootedFunction fun(context);
     JSFunction::Flags flags = (kind == Expression)
                               ? JSFunction::INTERPRETED_LAMBDA
                               : JSFunction::INTERPRETED;
-    fun = js_NewFunction(context, NullPtr(), NULL, 0, flags, parent, atom);
+    fun = NewFunction(context, NullPtr(), NULL, 0, flags, parent, atom,
+                      JSFunction::FinalizeKind, MaybeSingletonObject);
     if (selfHostingMode)
         fun->setIsSelfHostedBuiltin();
     if (fun && !compileAndGo) {
         if (!JSObject::clearParent(context, fun))
             return NULL;
         if (!JSObject::clearType(context, fun))
             return NULL;
         fun->setEnvironment(NULL);
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -29,16 +29,26 @@ namespace js {
 class FreeOp;
 
 namespace gc {
 
 struct Arena;
 struct ArenaHeader;
 struct Chunk;
 
+/*
+ * This flag allows an allocation site to request a specific heap based upon the
+ * estimated lifetime or lifetime requirements of objects allocated from that
+ * site.
+ */
+enum InitialHeap {
+    DefaultHeap,
+    TenuredHeap
+};
+
 /* The GC allocation kinds. */
 enum AllocKind {
     FINALIZE_OBJECT0,
     FINALIZE_OBJECT0_BACKGROUND,
     FINALIZE_OBJECT2,
     FINALIZE_OBJECT2_BACKGROUND,
     FINALIZE_OBJECT4,
     FINALIZE_OBJECT4_BACKGROUND,
--- a/js/src/gdb/mozilla/jsval.py
+++ b/js/src/gdb/mozilla/jsval.py
@@ -67,22 +67,22 @@ mozilla.prettyprinters.clear_module_prin
 # an address are significant, and only those values whose top bit is zero
 # are used for user-space addresses. This means that x86_64 addresses are
 # effectively 47 bits long, and thus fit nicely in the available portion of
 # the fraction field.
 #
 #
 # In detail:
 #
-# - jsval (jsapi.h) is a typedef for JS::Value.
+# - jsval (Value.h) is a typedef for JS::Value.
 #
-# - JS::Value (jsapi.h) is a class with a lot of methods and a single data
+# - JS::Value (Value.h) is a class with a lot of methods and a single data
 #   member, of type jsval_layout.
 #
-# - jsval_layout (jsval.h) is a helper type for picking apart values. This
+# - jsval_layout (Value.h) is a helper type for picking apart values. This
 #   is always 64 bits long, with a variant for each address size (32 bits
 #   or 64 bits) and endianness (little- or big-endian).
 #
 #   jsval_layout is a union with 'asBits', 'asDouble', and 'asPtr'
 #   branches, and an 's' branch, which is a struct that tries to break out
 #   the bitfields a little for the non-double types. On 64-bit machines,
 #   jsval_layout also has an 'asUIntPtr' branch.
 #
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -2042,19 +2042,25 @@ CodeGenerator::visitCreateThis(LCreateTh
     if (callee->isConstant())
         pushArg(ImmGCPtr(&callee->toConstant()->toObject()));
     else
         pushArg(ToRegister(callee));
 
     return callVM(CreateThisInfo, lir);
 }
 
+static JSObject *
+CreateThisForFunctionWithProtoWrapper(JSContext *cx, js::HandleObject callee, JSObject *proto)
+{
+    return CreateThisForFunctionWithProto(cx, callee, proto);
+}
+
 typedef JSObject *(*CreateThisWithProtoFn)(JSContext *cx, HandleObject callee, JSObject *proto);
 static const VMFunction CreateThisWithProtoInfo =
-FunctionInfo<CreateThisWithProtoFn>(js_CreateThisForFunctionWithProto);
+FunctionInfo<CreateThisWithProtoFn>(CreateThisForFunctionWithProtoWrapper);
 
 bool
 CodeGenerator::visitCreateThisWithProto(LCreateThisWithProto *lir)
 {
     const LAllocation *callee = lir->getCallee();
     const LAllocation *proto = lir->getPrototype();
 
     if (proto->isConstant())
--- a/js/src/ion/Ion.cpp
+++ b/js/src/ion/Ion.cpp
@@ -326,17 +326,17 @@ IonActivation::~IonActivation()
     cx_->mainThread().ionJSContext = prevIonJSContext_;
 }
 
 IonCode *
 IonCode::New(JSContext *cx, uint8_t *code, uint32_t bufferSize, JSC::ExecutablePool *pool)
 {
     AssertCanGC();
 
-    IonCode *codeObj = gc::NewGCThing<IonCode, CanGC>(cx, gc::FINALIZE_IONCODE, sizeof(IonCode));
+    IonCode *codeObj = gc::NewGCThing<IonCode, CanGC>(cx, gc::FINALIZE_IONCODE, sizeof(IonCode), gc::DefaultHeap);
     if (!codeObj) {
         pool->release();
         return NULL;
     }
 
     new (codeObj) IonCode(code, bufferSize, pool);
     return codeObj;
 }
@@ -1461,17 +1461,17 @@ ion::CanEnter(JSContext *cx, JSScript *s
         return Method_Skipped;
 
     // If constructing, allocate a new |this| object before building Ion.
     // Creating |this| is done before building Ion because it may change the
     // type information and invalidate compilation results.
     if (isConstructing && fp.thisValue().isPrimitive()) {
         RootedScript scriptRoot(cx, script);
         RootedObject callee(cx, &fp.callee());
-        RootedObject obj(cx, js_CreateThisForFunction(cx, callee, newType));
+        RootedObject obj(cx, CreateThisForFunction(cx, callee, newType));
         if (!obj)
             return Method_Skipped;
         fp.thisValue().setObject(*obj);
         script = scriptRoot;
     }
 
     // Mark as forbidden if frame can't be handled.
     if (!CheckFrame(fp)) {
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -3770,17 +3770,17 @@ IonBuilder::createThisScriptedSingleton(
     // Generate an inline path to create a new |this| object with
     // the given singleton prototype.
     types::TypeObject *type = proto->getNewType(cx, &ObjectClass, target);
     if (!type)
         return NULL;
     if (!types::TypeScript::ThisTypes(target->nonLazyScript())->hasType(types::Type::ObjectType(type)))
         return NULL;
 
-    RootedObject templateObject(cx, js_CreateThisForFunctionWithProto(cx, target, proto));
+    RootedObject templateObject(cx, CreateThisForFunctionWithProto(cx, target, proto));
     if (!templateObject)
         return NULL;
 
     // Trigger recompilation if the templateObject changes.
     if (templateObject->type()->newScript)
         types::HeapTypeSet::WatchObjectStateChange(cx, templateObject->type());
 
     MCreateThisWithTemplate *createThis = MCreateThisWithTemplate::New(templateObject);
@@ -4346,25 +4346,23 @@ IonBuilder::jsop_compare(JSOp op)
     if (ins->isEffectful() && !resumeAfter(ins))
         return false;
     return true;
 }
 
 JSObject *
 IonBuilder::getNewArrayTemplateObject(uint32_t count)
 {
-    RootedObject templateObject(cx, NewDenseUnallocatedArray(cx, count));
+    RootedScript scriptRoot(cx, script());
+    NewObjectKind newKind = types::UseNewTypeForInitializer(cx, scriptRoot, pc, JSProto_Array);
+    RootedObject templateObject(cx, NewDenseUnallocatedArray(cx, count, NULL, newKind));
     if (!templateObject)
         return NULL;
 
-    RootedScript scriptRoot(cx, script());
-    if (types::UseNewTypeForInitializer(cx, scriptRoot, pc, JSProto_Array)) {
-        if (!JSObject::setSingletonType(cx, templateObject))
-            return NULL;
-    } else {
+    if (newKind != SingletonObject) {
         types::TypeObject *type = types::TypeScript::InitObject(cx, scriptRoot, pc, JSProto_Array);
         if (!type)
             return NULL;
         templateObject->setType(type);
     }
 
     return templateObject;
 }
@@ -4392,31 +4390,29 @@ IonBuilder::jsop_newarray(uint32_t count
 bool
 IonBuilder::jsop_newobject(HandleObject baseObj)
 {
     // Don't bake in the TypeObject for non-CNG scripts.
     JS_ASSERT(script()->compileAndGo);
 
     RootedObject templateObject(cx);
 
+    RootedScript scriptRoot(cx, script());
+    NewObjectKind newKind = types::UseNewTypeForInitializer(cx, scriptRoot, pc, JSProto_Object);
     if (baseObj) {
-        templateObject = CopyInitializerObject(cx, baseObj);
+        templateObject = CopyInitializerObject(cx, baseObj, newKind);
     } else {
-        gc::AllocKind kind = GuessObjectGCKind(0);
-        templateObject = NewBuiltinClassInstance(cx, &ObjectClass, kind);
+        gc::AllocKind allocKind = GuessObjectGCKind(0);
+        templateObject = NewBuiltinClassInstance(cx, &ObjectClass, allocKind, newKind);
     }
 
     if (!templateObject)
         return false;
 
-    RootedScript scriptRoot(cx, script());
-    if (types::UseNewTypeForInitializer(cx, scriptRoot, pc, JSProto_Object)) {
-        if (!JSObject::setSingletonType(cx, templateObject))
-            return false;
-    } else {
+    if (newKind != SingletonObject) {
         types::TypeObject *type = types::TypeScript::InitObject(cx, scriptRoot, pc, JSProto_Object);
         if (!type)
             return false;
         templateObject->setType(type);
     }
 
     MNewObject *ins = MNewObject::New(templateObject);
 
--- a/js/src/ion/IonCompartment.h
+++ b/js/src/ion/IonCompartment.h
@@ -4,18 +4,18 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jsion_ion_compartment_h__
 #define jsion_ion_compartment_h__
 
 #include "IonCode.h"
-#include "jsval.h"
 #include "jsweakcache.h"
+#include "js/Value.h"
 #include "vm/Stack.h"
 #include "IonFrames.h"
 
 namespace js {
 namespace ion {
 
 class FrameSizeClass;
 
--- a/js/src/ion/VMFunctions.cpp
+++ b/js/src/ion/VMFunctions.cpp
@@ -111,17 +111,17 @@ InvokeFunction(JSContext *cx, HandleFunc
         types::TypeScript::Monitor(cx, *rval);
 
     return ok;
 }
 
 JSObject *
 NewGCThing(JSContext *cx, gc::AllocKind allocKind, size_t thingSize)
 {
-    return gc::NewGCThing<JSObject, CanGC>(cx, allocKind, thingSize);
+    return gc::NewGCThing<JSObject, CanGC>(cx, allocKind, thingSize, gc::DefaultHeap);
 }
 
 bool
 CheckOverRecursed(JSContext *cx)
 {
     // IonMonkey's stackLimit is equal to nativeStackLimit by default. When we
     // want to trigger an operation callback, we set the ionStackLimit to NULL,
     // which causes the stack limit check to fail.
@@ -264,48 +264,42 @@ IteratorMore(JSContext *cx, HandleObject
     *res = tmp.toBoolean();