Merge m-c to b2g-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 23 Apr 2014 15:02:46 +0200
changeset 198288 276eb640a3fe822313f16c48b37af731793a4971
parent 198287 a2dcf639dee13b670083f9d6894f346072ec9362 (current diff)
parent 198215 b79c2995d25e5e2ed11ba1706a42a62ee07d35f3 (diff)
child 198289 509937940bc637561bacff9822516a557b113d7a
push id3624
push userasasaki@mozilla.com
push dateMon, 09 Jun 2014 21:49:01 +0000
treeherdermozilla-beta@b1a5da15899a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone31.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to b2g-inbound
mobile/android/themes/core/images/reader-list-off-icon-hdpi.png
mobile/android/themes/core/images/reader-list-off-icon-mdpi.png
mobile/android/themes/core/images/reader-list-off-icon-xhdpi.png
mobile/android/themes/core/images/reader-list-on-icon-hdpi.png
mobile/android/themes/core/images/reader-list-on-icon-mdpi.png
mobile/android/themes/core/images/reader-list-on-icon-xhdpi.png
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -596,16 +596,22 @@ pref("dom.forms.number", true);
 
 // Don't enable <input type=color> yet as we don't have a color picker
 // implemented for b2g (bug 875751)
 pref("dom.forms.color", false);
 
 // Turns on gralloc-based direct texturing for Gonk
 pref("gfx.gralloc.enabled", false);
 
+// This preference instructs the JS engine to discard the
+// source of any privileged JS after compilation. This saves
+// memory, but makes things like Function.prototype.toSource()
+// fail.
+pref("javascript.options.discardSystemSource", true);
+
 // XXXX REMOVE FOR PRODUCTION. Turns on GC and CC logging
 pref("javascript.options.mem.log", false);
 
 // Increase mark slice time from 10ms to 30ms
 pref("javascript.options.mem.gc_incremental_slice_ms", 30);
 
 // Increase time to get more high frequency GC on benchmarks from 1000ms to 1500ms
 pref("javascript.options.mem.gc_high_frequency_time_limit_ms", 1500);
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -74,24 +74,18 @@ this.__defineGetter__("AddonManager", fu
   Cu.import("resource://gre/modules/AddonManager.jsm", tmp);
   return this.AddonManager = tmp.AddonManager;
 });
 this.__defineSetter__("AddonManager", function (val) {
   delete this.AddonManager;
   return this.AddonManager = val;
 });
 
-this.__defineGetter__("PluralForm", function() {
-  Cu.import("resource://gre/modules/PluralForm.jsm");
-  return this.PluralForm;
-});
-this.__defineSetter__("PluralForm", function (val) {
-  delete this.PluralForm;
-  return this.PluralForm = val;
-});
+XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
+  "resource://gre/modules/PluralForm.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch",
   "resource://gre/modules/TelemetryStopwatch.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "gCustomizeMode", function() {
   let scope = {};
   Cu.import("resource:///modules/CustomizeMode.jsm", scope);
   return new scope.CustomizeMode(window);
--- a/browser/components/customizableui/src/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/src/CustomizableWidgets.jsm
@@ -279,57 +279,58 @@ const CustomizableWidgets = [{
         if (typeof win.OpenBrowserWindow == "function") {
           win.OpenBrowserWindow({private: true});
         }
       }
     }
   }, {
     id: "save-page-button",
     shortcutId: "key_savePage",
-    tooltiptext: "save-page-button.tooltiptext2",
+    tooltiptext: "save-page-button.tooltiptext3",
     defaultArea: CustomizableUI.AREA_PANEL,
     onCommand: function(aEvent) {
       let win = aEvent.target &&
                 aEvent.target.ownerDocument &&
                 aEvent.target.ownerDocument.defaultView;
       if (win && typeof win.saveDocument == "function") {
         win.saveDocument(win.content.document);
       }
     }
   }, {
     id: "find-button",
     shortcutId: "key_find",
-    tooltiptext: "find-button.tooltiptext2",
+    tooltiptext: "find-button.tooltiptext3",
     defaultArea: CustomizableUI.AREA_PANEL,
     onCommand: function(aEvent) {
       let win = aEvent.target &&
                 aEvent.target.ownerDocument &&
                 aEvent.target.ownerDocument.defaultView;
       if (win && win.gFindBar) {
         win.gFindBar.onFindCommand();
       }
     }
   }, {
     id: "open-file-button",
     shortcutId: "openFileKb",
-    tooltiptext: "open-file-button.tooltiptext2",
+    tooltiptext: "open-file-button.tooltiptext3",
     defaultArea: CustomizableUI.AREA_PANEL,
     onCommand: function(aEvent) {
       let win = aEvent.target
                 && aEvent.target.ownerDocument
                 && aEvent.target.ownerDocument.defaultView;
       if (win && typeof win.BrowserOpenFileWindow == "function") {
         win.BrowserOpenFileWindow();
       }
     }
   }, {
     id: "developer-button",
     type: "view",
     viewId: "PanelUI-developer",
     shortcutId: "key_devToolboxMenuItem",
+    tooltiptext: "developer-button.tooltiptext2",
     defaultArea: CustomizableUI.AREA_PANEL,
     onViewShowing: function(aEvent) {
       // Populate the subview with whatever menuitems are in the developer
       // menu. We skip menu elements, because the menu panel has no way
       // of dealing with those right now.
       let doc = aEvent.target.ownerDocument;
       let win = doc.defaultView;
 
@@ -345,16 +346,17 @@ const CustomizableWidgets = [{
     onViewHiding: function(aEvent) {
       let doc = aEvent.target.ownerDocument;
       clearSubview(doc.getElementById("PanelUI-developerItems"));
     }
   }, {
     id: "sidebar-button",
     type: "view",
     viewId: "PanelUI-sidebar",
+    tooltiptext: "sidebar-button.tooltiptext2",
     onViewShowing: function(aEvent) {
       // Largely duplicated from the developer-button above with a couple minor
       // alterations.
       // Populate the subview with whatever menuitems are in the
       // sidebar menu. We skip menu elements, because the menu panel has no way
       // of dealing with those right now.
       let doc = aEvent.target.ownerDocument;
       let win = doc.defaultView;
@@ -372,17 +374,17 @@ const CustomizableWidgets = [{
     },
     onViewHiding: function(aEvent) {
       let doc = aEvent.target.ownerDocument;
       clearSubview(doc.getElementById("PanelUI-sidebarItems"));
     }
   }, {
     id: "add-ons-button",
     shortcutId: "key_openAddons",
-    tooltiptext: "add-ons-button.tooltiptext2",
+    tooltiptext: "add-ons-button.tooltiptext3",
     defaultArea: CustomizableUI.AREA_PANEL,
     onCommand: function(aEvent) {
       let win = aEvent.target &&
                 aEvent.target.ownerDocument &&
                 aEvent.target.ownerDocument.defaultView;
       if (win && typeof win.BrowserOpenAddonsMgr == "function") {
         win.BrowserOpenAddonsMgr();
       }
@@ -407,16 +409,17 @@ const CustomizableWidgets = [{
                 aEvent.target.ownerDocument.defaultView;
       if (win && typeof win.openPreferences == "function") {
         win.openPreferences();
       }
     }
   }, {
     id: "zoom-controls",
     type: "custom",
+    tooltiptext: "zoom-controls.tooltiptext2",
     defaultArea: CustomizableUI.AREA_PANEL,
     onBuild: function(aDocument) {
       const kPanelId = "PanelUI-popup";
       let areaType = CustomizableUI.getAreaType(this.currentArea);
       let inPanel = areaType == CustomizableUI.TYPE_MENU_PANEL;
       let inToolbar = areaType == CustomizableUI.TYPE_TOOLBAR;
 
       let buttons = [{
@@ -579,16 +582,17 @@ const CustomizableWidgets = [{
       };
       CustomizableUI.addListener(listener);
 
       return node;
     }
   }, {
     id: "edit-controls",
     type: "custom",
+    tooltiptext: "edit-controls.tooltiptext2",
     defaultArea: CustomizableUI.AREA_PANEL,
     onBuild: function(aDocument) {
       let buttons = [{
         id: "cut-button",
         command: "cmd_cut",
         label: true,
         tooltiptext: "tooltiptext2",
         shortcutId: "key_cut",
@@ -670,16 +674,17 @@ const CustomizableWidgets = [{
 
       return node;
     }
   },
   {
     id: "feed-button",
     type: "view",
     viewId: "PanelUI-feeds",
+    tooltiptext: "feed-button.tooltiptext2",
     defaultArea: CustomizableUI.AREA_PANEL,
     onClick: function(aEvent) {
       let win = aEvent.target.ownerDocument.defaultView;
       let feeds = win.gBrowser.selectedBrowser.feeds;
 
       // Here, we only care about the case where we have exactly 1 feed and the
       // user clicked...
       let isClick = (aEvent.button == 0 || aEvent.button == 1);
@@ -872,17 +877,17 @@ const CustomizableWidgets = [{
       };
       CustomizableUI.addListener(listener);
       if (!this.charsetInfo) {
         this.charsetInfo = CharsetMenu.getData();
       }
     }
   }, {
     id: "email-link-button",
-    tooltiptext: "email-link-button.tooltiptext2",
+    tooltiptext: "email-link-button.tooltiptext3",
     onCommand: function(aEvent) {
       let win = aEvent.view;
       win.MailIntegration.sendLinkForWindow(win.content);
     }
   }];
 
 #ifdef XP_WIN
 #ifdef MOZ_METRO
--- a/browser/components/preferences/aboutPermissions.js
+++ b/browser/components/preferences/aboutPermissions.js
@@ -1,22 +1,25 @@
 /* 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/. */
 
 let Ci = Components.interfaces;
 let Cc = Components.classes;
 let Cu = Components.utils;
 
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/PluralForm.jsm");
 Cu.import("resource://gre/modules/DownloadUtils.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource://gre/modules/ForgetAboutSite.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
+                                  "resource://gre/modules/PluralForm.jsm");
+
 let gFaviconService = Cc["@mozilla.org/browser/favicon-service;1"].
                       getService(Ci.nsIFaviconService);
 
 let gPlacesDatabase = Cc["@mozilla.org/browser/nav-history-service;1"].
                       getService(Ci.nsPIPlacesDatabase).
                       DBConnection.
                       clone(true);
 
--- a/browser/devtools/app-manager/app-projects.js
+++ b/browser/devtools/app-manager/app-projects.js
@@ -1,11 +1,11 @@
 const {Cc,Ci,Cu} = require("chrome");
 const ObservableObject = require("devtools/shared/observable-object");
-const promise = require("sdk/core/promise");
+const promise = require("devtools/toolkit/deprecated-sync-thenables");
 
 const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js");
 const {generateUUID} = Cc['@mozilla.org/uuid-generator;1'].getService(Ci.nsIUUIDGenerator);
 
 /**
  * IndexedDB wrapper that just save project objects
  *
  * The only constraint is that project objects have to have
--- a/browser/devtools/app-manager/app-validator.js
+++ b/browser/devtools/app-manager/app-validator.js
@@ -1,15 +1,15 @@
 /* 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/. */
 "use strict";
 
 let {Ci,Cu,CC} = require("chrome");
-const promise = require("sdk/core/promise");
+const promise = require("devtools/toolkit/deprecated-sync-thenables");
 
 const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm");
 const {Services} = Cu.import("resource://gre/modules/Services.jsm");
 let XMLHttpRequest = CC("@mozilla.org/xmlextras/xmlhttprequest;1");
 let strings = Services.strings.createBundle("chrome://browser/locale/devtools/app-manager.properties");
 
 function AppValidator(project) {
   this.project = project;
--- a/browser/devtools/app-manager/content/device.js
+++ b/browser/devtools/app-manager/content/device.js
@@ -12,17 +12,17 @@ const {require} = devtools;
 
 const {ConnectionManager, Connection}
   = require("devtools/client/connection-manager");
 const {getDeviceFront} = require("devtools/server/actors/device");
 const {getTargetForApp, launchApp, closeApp}
   = require("devtools/app-actor-front");
 const DeviceStore = require("devtools/app-manager/device-store");
 const WebappsStore = require("devtools/app-manager/webapps-store");
-const promise = require("sdk/core/promise");
+const promise = require("devtools/toolkit/deprecated-sync-thenables");
 const DEFAULT_APP_ICON = "chrome://browser/skin/devtools/app-manager/default-app-icon.png";
 
 window.addEventListener("message", function(event) {
   try {
     let message = JSON.parse(event.data);
     if (message.name == "connection") {
       let cid = parseInt(message.cid);
       for (let c of ConnectionManager.connections) {
--- a/browser/devtools/app-manager/content/index.js
+++ b/browser/devtools/app-manager/content/index.js
@@ -2,17 +2,17 @@
  * 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/. */
 
 const Cu = Components.utils;
 Cu.import("resource:///modules/devtools/gDevTools.jsm");
 const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 const {require} = devtools;
 const {ConnectionManager, Connection} = require("devtools/client/connection-manager");
-const promise = require("sdk/core/promise");
+const promise = require("devtools/toolkit/deprecated-sync-thenables");
 const prefs = require('sdk/preferences/service');
 
 
 let UI = {
   _toolboxTabCursor: 0,
   _handledTargets: new Map(),
 
   connection: null,
--- a/browser/devtools/app-manager/content/projects.js
+++ b/browser/devtools/app-manager/content/projects.js
@@ -13,17 +13,17 @@ const {ConnectionManager, Connection} = 
 const {AppProjects} = require("devtools/app-manager/app-projects");
 const {AppValidator} = require("devtools/app-manager/app-validator");
 const {Services} = Cu.import("resource://gre/modules/Services.jsm");
 const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm");
 const {installHosted, installPackaged, getTargetForApp,
        reloadApp, launchApp, closeApp} = require("devtools/app-actor-front");
 const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js");
 
-const promise = require("sdk/core/promise");
+const promise = require("devtools/toolkit/deprecated-sync-thenables");
 
 const MANIFEST_EDITOR_ENABLED = "devtools.appmanager.manifestEditor.enabled";
 
 window.addEventListener("message", function(event) {
   try {
     let json = JSON.parse(event.data);
     if (json.name == "connection") {
       let cid = parseInt(json.cid);
--- a/browser/devtools/app-manager/test/head.js
+++ b/browser/devtools/app-manager/test/head.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 const {utils: Cu, classes: Cc, interfaces: Ci} = Components;
 
 const {Promise: promise} =
-  Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {});
+  Cu.import("resource://gre/modules/devtools/deprecated-sync-thenables.js", {});
 const {devtools} =
   Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 const {require} = devtools;
 
 const {AppProjects} = require("devtools/app-manager/app-projects");
 
 const APP_MANAGER_URL = "about:app-manager";
 const TEST_BASE =
--- a/browser/devtools/app-manager/webapps-store.js
+++ b/browser/devtools/app-manager/webapps-store.js
@@ -1,14 +1,14 @@
 /* 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/. */
 
 const ObservableObject = require("devtools/shared/observable-object");
-const promise = require("sdk/core/promise");
+const promise = require("devtools/toolkit/deprecated-sync-thenables");
 const {Connection} = require("devtools/client/connection-manager");
 
 const {Cu} = require("chrome");
 const dbgClient = Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
 const _knownWebappsStores = new WeakMap();
 
 let WebappsStore;
 
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -93,17 +93,17 @@ Cu.import("resource://gre/modules/devtoo
 Cu.import("resource:///modules/devtools/SimpleListWidget.jsm");
 Cu.import("resource:///modules/devtools/BreadcrumbsWidget.jsm");
 Cu.import("resource:///modules/devtools/SideMenuWidget.jsm");
 Cu.import("resource:///modules/devtools/VariablesView.jsm");
 Cu.import("resource:///modules/devtools/VariablesViewController.jsm");
 Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
 
 const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
-const promise = require("sdk/core/promise");
+const promise = require("devtools/toolkit/deprecated-sync-thenables");
 const Editor = require("devtools/sourceeditor/editor");
 const DebuggerEditor = require("devtools/sourceeditor/debugger.js");
 const {Tooltip} = require("devtools/shared/widgets/Tooltip");
 const FastListWidget = require("devtools/shared/widgets/FastListWidget");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Parser",
   "resource:///modules/devtools/Parser.jsm");
 
--- a/browser/devtools/eyedropper/test/head.js
+++ b/browser/devtools/eyedropper/test/head.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const TEST_BASE = "chrome://mochitests/content/browser/browser/devtools/eyedropper/test/";
 const TEST_HOST = 'mochi.test:8888';
 
-const promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js").Promise;
+const promise = Cu.import("resource://gre/modules/devtools/deprecated-sync-thenables.js").Promise;
 const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
 const { Eyedropper } = require("devtools/eyedropper/eyedropper");
 
 
 waitForExplicitFinish();
 
 function cleanup()
 {
--- a/browser/devtools/inspector/breadcrumbs.js
+++ b/browser/devtools/inspector/breadcrumbs.js
@@ -11,17 +11,17 @@ const ENSURE_SELECTION_VISIBLE_DELAY = 5
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource:///modules/devtools/DOMHelpers.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
 const ELLIPSIS = Services.prefs.getComplexValue("intl.ellipsis", Ci.nsIPrefLocalizedString).data;
 const MAX_LABEL_LENGTH = 40;
 
-let promise = require("sdk/core/promise");
+let promise = require("devtools/toolkit/deprecated-sync-thenables");
 
 const LOW_PRIORITY_ELEMENTS = {
   "HEAD": true,
   "BASE": true,
   "BASEFONT": true,
   "ISINDEX": true,
   "LINK": true,
   "META": true,
--- a/browser/devtools/inspector/inspector-panel.js
+++ b/browser/devtools/inspector/inspector-panel.js
@@ -3,17 +3,17 @@
 /* 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/. */
 
 const {Cc, Ci, Cu, Cr} = require("chrome");
 
 Cu.import("resource://gre/modules/Services.jsm");
 
-let promise = require("sdk/core/promise");
+let promise = require("devtools/toolkit/deprecated-sync-thenables");
 let EventEmitter = require("devtools/toolkit/event-emitter");
 let {CssLogic} = require("devtools/styleinspector/css-logic");
 
 loader.lazyGetter(this, "MarkupView", () => require("devtools/markupview/markup-view").MarkupView);
 loader.lazyGetter(this, "HTMLBreadcrumbs", () => require("devtools/inspector/breadcrumbs").HTMLBreadcrumbs);
 loader.lazyGetter(this, "ToolSidebar", () => require("devtools/framework/sidebar").ToolSidebar);
 loader.lazyGetter(this, "SelectorSearch", () => require("devtools/inspector/selector-search").SelectorSearch);
 
--- a/browser/devtools/inspector/selector-search.js
+++ b/browser/devtools/inspector/selector-search.js
@@ -1,15 +1,15 @@
 /* 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/. */
 
 "use strict";
 
-const promise = require("sdk/core/promise");
+const promise = require("devtools/toolkit/deprecated-sync-thenables");
 
 loader.lazyGetter(this, "AutocompletePopup", () => require("devtools/shared/autocomplete-popup").AutocompletePopup);
 
 // Maximum number of selector suggestions shown in the panel.
 const MAX_SUGGESTIONS = 15;
 
 /**
  * Converts any input box on a page to a CSS selector search and suggestion box.
--- a/browser/devtools/markupview/markup-view.js
+++ b/browser/devtools/markupview/markup-view.js
@@ -15,17 +15,17 @@ const COLLAPSE_DATA_URL_REGEX = /^data.+
 const COLLAPSE_DATA_URL_LENGTH = 60;
 const CONTAINER_FLASHING_DURATION = 500;
 const NEW_SELECTION_HIGHLIGHTER_TIMER = 1000;
 
 const {UndoStack} = require("devtools/shared/undo");
 const {editableField, InplaceEditor} = require("devtools/shared/inplace-editor");
 const {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
 const {HTMLEditor} = require("devtools/markupview/html-editor");
-const promise = require("sdk/core/promise");
+const promise = require("devtools/toolkit/deprecated-sync-thenables");
 const {Tooltip} = require("devtools/shared/widgets/Tooltip");
 const EventEmitter = require("devtools/toolkit/event-emitter");
 
 Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
 Cu.import("resource://gre/modules/devtools/Templater.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
--- a/browser/devtools/markupview/test/head.js
+++ b/browser/devtools/markupview/test/head.js
@@ -1,17 +1,17 @@
 /* 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/. */
 
 const Cu = Components.utils;
 let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 let TargetFactory = devtools.TargetFactory;
 let {console} = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
-let promise = devtools.require("sdk/core/promise");
+let promise = devtools.require("devtools/toolkit/deprecated-sync-thenables");
 let {getInplaceEditorForSpan: inplaceEditor} = devtools.require("devtools/shared/inplace-editor");
 
 // All test are asynchronous
 waitForExplicitFinish();
 
 //Services.prefs.setBoolPref("devtools.dump.emit", true);
 
 // Set the testing flag on gDevTools and reset it when the test ends
--- a/browser/devtools/netmonitor/netmonitor-view.js
+++ b/browser/devtools/netmonitor/netmonitor-view.js
@@ -389,16 +389,17 @@ RequestsMenuView.prototype = Heritage.ex
       $("#headers-summary-resend").hidden = true;
     }
 
     if (NetMonitorController.supportsPerfStats) {
       $("#request-menu-context-perf").addEventListener("command", this._onContextPerfCommand, false);
       $("#requests-menu-perf-notice-button").addEventListener("command", this._onContextPerfCommand, false);
       $("#requests-menu-network-summary-button").addEventListener("command", this._onContextPerfCommand, false);
       $("#requests-menu-network-summary-label").addEventListener("click", this._onContextPerfCommand, false);
+      $("#network-statistics-back-button").addEventListener("command", this._onContextPerfCommand, false);
     } else {
       $("#notice-perf-message").hidden = true;
       $("#request-menu-context-perf").hidden = true;
       $("#requests-menu-network-summary-button").hidden = true;
       $("#requests-menu-network-summary-label").hidden = true;
     }
   },
 
@@ -423,16 +424,17 @@ RequestsMenuView.prototype = Heritage.ex
     $("#request-menu-context-copy-url").removeEventListener("command", this._onContextCopyUrlCommand, false);
     $("#request-menu-context-copy-image-as-data-uri").removeEventListener("command", this._onContextCopyImageAsDataUriCommand, false);
     $("#request-menu-context-resend").removeEventListener("command", this._onContextResendCommand, false);
     $("#request-menu-context-perf").removeEventListener("command", this._onContextPerfCommand, false);
 
     $("#requests-menu-perf-notice-button").removeEventListener("command", this._onContextPerfCommand, false);
     $("#requests-menu-network-summary-button").removeEventListener("command", this._onContextPerfCommand, false);
     $("#requests-menu-network-summary-label").removeEventListener("click", this._onContextPerfCommand, false);
+    $("#network-statistics-back-button").removeEventListener("command", this._onContextPerfCommand, false);
 
     $("#custom-request-send-button").removeEventListener("click", this.sendCustomRequestEvent, false);
     $("#custom-request-close-button").removeEventListener("click", this.closeCustomRequestEvent, false);
     $("#headers-summary-resend").removeEventListener("click", this.cloneSelectedRequestEvent, false);
   },
 
   /**
    * Resets this container (removes all the networking information).
--- a/browser/devtools/netmonitor/netmonitor.xul
+++ b/browser/devtools/netmonitor/netmonitor.xul
@@ -511,17 +511,16 @@
 
     </vbox>
 
     <box id="network-statistics-view">
       <toolbar id="network-statistics-toolbar"
                class="devtools-toolbar">
         <button id="network-statistics-back-button"
                 class="devtools-toolbarbutton"
-                onclick="NetMonitorView.toggleFrontendMode()"
                 label="&netmonitorUI.backButton;"/>
       </toolbar>
       <box id="network-statistics-charts"
            class="devtools-responsive-container"
            flex="1">
         <vbox id="primed-cache-chart" pack="center" flex="1"/>
         <splitter id="network-statistics-view-splitter"
                   class="devtools-side-splitter"/>
--- a/browser/devtools/shared/test/browser_telemetry_button_paintflashing.js
+++ b/browser/devtools/shared/test/browser_telemetry_button_paintflashing.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const TEST_URI = "data:text/html;charset=utf-8,<p>browser_telemetry_button_paintflashing.js</p>";
 
 // Because we need to gather stats for the period of time that a tool has been
 // opened we make use of setTimeout() to create tool active times.
 const TOOL_DELAY = 200;
 
-let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {}).Promise;
+let promise = Cu.import("resource://gre/modules/devtools/deprecated-sync-thenables.js", {}).Promise;
 let {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
 
 let require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
 let Telemetry = require("devtools/shared/telemetry");
 
 function init() {
   Telemetry.prototype.telemetryInfo = {};
   Telemetry.prototype._oldlog = Telemetry.prototype.log;
--- a/browser/devtools/shared/test/browser_telemetry_button_responsive.js
+++ b/browser/devtools/shared/test/browser_telemetry_button_responsive.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const TEST_URI = "data:text/html;charset=utf-8,<p>browser_telemetry_button_responsive.js</p>";
 
 // Because we need to gather stats for the period of time that a tool has been
 // opened we make use of setTimeout() to create tool active times.
 const TOOL_DELAY = 200;
 
-let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {}).Promise;
+let promise = Cu.import("resource://gre/modules/devtools/deprecated-sync-thenables.js", {}).Promise;
 let {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
 
 let require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
 let Telemetry = require("devtools/shared/telemetry");
 
 function init() {
   Telemetry.prototype.telemetryInfo = {};
   Telemetry.prototype._oldlog = Telemetry.prototype.log;
--- a/browser/devtools/shared/test/browser_telemetry_button_scratchpad.js
+++ b/browser/devtools/shared/test/browser_telemetry_button_scratchpad.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const TEST_URI = "data:text/html;charset=utf-8,<p>browser_telemetry_button_scratchpad.js</p>";
 
 // Because we need to gather stats for the period of time that a tool has been
 // opened we make use of setTimeout() to create tool active times.
 const TOOL_DELAY = 200;
 
-let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {}).Promise;
+let promise = Cu.import("resource://gre/modules/devtools/deprecated-sync-thenables.js", {}).Promise;
 let {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
 
 let require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
 let Telemetry = require("devtools/shared/telemetry");
 
 let numScratchpads = 0;
 
 function init() {
--- a/browser/devtools/shared/test/browser_telemetry_button_tilt.js
+++ b/browser/devtools/shared/test/browser_telemetry_button_tilt.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const TEST_URI = "data:text/html;charset=utf-8,<p>browser_telemetry_button_tilt.js</p>";
 
 // Because we need to gather stats for the period of time that a tool has been
 // opened we make use of setTimeout() to create tool active times.
 const TOOL_DELAY = 200;
 
-let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {}).Promise;
+let promise = Cu.import("resource://gre/modules/devtools/deprecated-sync-thenables.js", {}).Promise;
 let {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
 
 let require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
 let Telemetry = require("devtools/shared/telemetry");
 
 function init() {
   Telemetry.prototype.telemetryInfo = {};
   Telemetry.prototype._oldlog = Telemetry.prototype.log;
--- a/browser/devtools/shared/test/browser_telemetry_sidebar.js
+++ b/browser/devtools/shared/test/browser_telemetry_sidebar.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const TEST_URI = "data:text/html;charset=utf-8,<p>browser_telemetry_sidebar.js</p>";
 
 // Because we need to gather stats for the period of time that a tool has been
 // opened we make use of setTimeout() to create tool active times.
 const TOOL_DELAY = 200;
 
-let {Promise: promise} = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {});
+let {Promise: promise} = Cu.import("resource://gre/modules/devtools/deprecated-sync-thenables.js", {});
 let {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
 
 let require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
 let Telemetry = require("devtools/shared/telemetry");
 
 function init() {
   Telemetry.prototype.telemetryInfo = {};
   Telemetry.prototype._oldlog = Telemetry.prototype.log;
--- a/browser/devtools/shared/test/browser_telemetry_toolboxtabs_inspector.js
+++ b/browser/devtools/shared/test/browser_telemetry_toolboxtabs_inspector.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const TEST_URI = "data:text/html;charset=utf-8,<p>browser_telemetry_toolboxtabs_inspector.js</p>";
 
 // Because we need to gather stats for the period of time that a tool has been
 // opened we make use of setTimeout() to create tool active times.
 const TOOL_DELAY = 200;
 
-let {Promise: promise} = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {});
+let {Promise: promise} = Cu.import("resource://gre/modules/devtools/deprecated-sync-thenables.js", {});
 let {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
 
 let require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
 let Telemetry = require("devtools/shared/telemetry");
 
 function init() {
   Telemetry.prototype.telemetryInfo = {};
   Telemetry.prototype._oldlog = Telemetry.prototype.log;
--- a/browser/devtools/shared/test/browser_telemetry_toolboxtabs_jsdebugger.js
+++ b/browser/devtools/shared/test/browser_telemetry_toolboxtabs_jsdebugger.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const TEST_URI = "data:text/html;charset=utf-8,<p>browser_telemetry_toolboxtabs_jsdebugger.js</p>";
 
 // Because we need to gather stats for the period of time that a tool has been
 // opened we make use of setTimeout() to create tool active times.
 const TOOL_DELAY = 200;
 
-let {Promise: promise} = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {});
+let {Promise: promise} = Cu.import("resource://gre/modules/devtools/deprecated-sync-thenables.js", {});
 let {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
 
 let require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
 let Telemetry = require("devtools/shared/telemetry");
 
 function init() {
   Telemetry.prototype.telemetryInfo = {};
   Telemetry.prototype._oldlog = Telemetry.prototype.log;
--- a/browser/devtools/shared/test/browser_telemetry_toolboxtabs_jsprofiler.js
+++ b/browser/devtools/shared/test/browser_telemetry_toolboxtabs_jsprofiler.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const TEST_URI = "data:text/html;charset=utf-8,<p>browser_telemetry_toolboxtabs_jsprofiler.js</p>";
 
 // Because we need to gather stats for the period of time that a tool has been
 // opened we make use of setTimeout() to create tool active times.
 const TOOL_DELAY = 200;
 
-let {Promise: promise} = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {});
+let {Promise: promise} = Cu.import("resource://gre/modules/devtools/deprecated-sync-thenables.js", {});
 let {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
 
 let require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
 let Telemetry = require("devtools/shared/telemetry");
 
 function init() {
   Telemetry.prototype.telemetryInfo = {};
   Telemetry.prototype._oldlog = Telemetry.prototype.log;
--- a/browser/devtools/shared/test/browser_telemetry_toolboxtabs_netmonitor.js
+++ b/browser/devtools/shared/test/browser_telemetry_toolboxtabs_netmonitor.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const TEST_URI = "data:text/html;charset=utf-8,<p>browser_telemetry_toolboxtabs_netmonitor.js</p>";
 
 // Because we need to gather stats for the period of time that a tool has been
 // opened we make use of setTimeout() to create tool active times.
 const TOOL_DELAY = 200;
 
-let {Promise: promise} = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {});
+let {Promise: promise} = Cu.import("resource://gre/modules/devtools/deprecated-sync-thenables.js", {});
 let {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
 
 let require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
 let Telemetry = require("devtools/shared/telemetry");
 
 function init() {
   Telemetry.prototype.telemetryInfo = {};
   Telemetry.prototype._oldlog = Telemetry.prototype.log;
--- a/browser/devtools/shared/test/browser_telemetry_toolboxtabs_options.js
+++ b/browser/devtools/shared/test/browser_telemetry_toolboxtabs_options.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const TEST_URI = "data:text/html;charset=utf-8,<p>browser_telemetry_toolboxtabs_options.js</p>";
 
 // Because we need to gather stats for the period of time that a tool has been
 // opened we make use of setTimeout() to create tool active times.
 const TOOL_DELAY = 200;
 
-let {Promise: promise} = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {});
+let {Promise: promise} = Cu.import("resource://gre/modules/devtools/deprecated-sync-thenables.js", {});
 let {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
 
 let require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
 let Telemetry = require("devtools/shared/telemetry");
 
 function init() {
   Telemetry.prototype.telemetryInfo = {};
   Telemetry.prototype._oldlog = Telemetry.prototype.log;
--- a/browser/devtools/shared/test/browser_telemetry_toolboxtabs_styleeditor.js
+++ b/browser/devtools/shared/test/browser_telemetry_toolboxtabs_styleeditor.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const TEST_URI = "data:text/html;charset=utf-8,<p>browser_telemetry_toolboxtabs_styleeditor.js</p>";
 
 // Because we need to gather stats for the period of time that a tool has been
 // opened we make use of setTimeout() to create tool active times.
 const TOOL_DELAY = 200;
 
-let {Promise: promise} = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {});
+let {Promise: promise} = Cu.import("resource://gre/modules/devtools/deprecated-sync-thenables.js", {});
 let {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
 
 let require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
 let Telemetry = require("devtools/shared/telemetry");
 
 function init() {
   Telemetry.prototype.telemetryInfo = {};
   Telemetry.prototype._oldlog = Telemetry.prototype.log;
--- a/browser/devtools/shared/test/browser_telemetry_toolboxtabs_webconsole.js
+++ b/browser/devtools/shared/test/browser_telemetry_toolboxtabs_webconsole.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const TEST_URI = "data:text/html;charset=utf-8,<p>browser_telemetry_toolboxtabs_styleeditor_webconsole.js</p>";
 
 // Because we need to gather stats for the period of time that a tool has been
 // opened we make use of setTimeout() to create tool active times.
 const TOOL_DELAY = 200;
 
-let {Promise: promise} = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {});
+let {Promise: promise} = Cu.import("resource://gre/modules/devtools/deprecated-sync-thenables.js", {});
 let {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
 
 let require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
 let Telemetry = require("devtools/shared/telemetry");
 
 function init() {
   Telemetry.prototype.telemetryInfo = {};
   Telemetry.prototype._oldlog = Telemetry.prototype.log;
--- a/browser/devtools/shared/test/browser_templater_basic.js
+++ b/browser/devtools/shared/test/browser_templater_basic.js
@@ -4,17 +4,17 @@
 // Tests that the DOM Template engine works properly
 
 /*
  * These tests run both in Mozilla/Mochitest and plain browsers (as does
  * domtemplate)
  * We should endevour to keep the source in sync.
  */
 
-var promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {}).Promise;
+var promise = Cu.import("resource://gre/modules/devtools/deprecated-sync-thenables.js", {}).Promise;
 var template = Cu.import("resource://gre/modules/devtools/Templater.jsm", {}).template;
 
 const TEST_URI = "http://example.com/browser/browser/devtools/shared/test/browser_templater_basic.html";
 
 function test() {
   addTab(TEST_URI, function() {
     info("Starting DOM Templater Tests");
     runTest(0);
--- a/browser/devtools/styleeditor/StyleEditorUI.jsm
+++ b/browser/devtools/styleeditor/StyleEditorUI.jsm
@@ -6,28 +6,31 @@
 "use strict";
 
 this.EXPORTED_SYMBOLS = ["StyleEditorUI"];
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/PluralForm.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/devtools/event-emitter.js");
 Cu.import("resource:///modules/devtools/gDevTools.jsm");
 Cu.import("resource:///modules/devtools/StyleEditorUtil.jsm");
 Cu.import("resource:///modules/devtools/SplitView.jsm");
 Cu.import("resource:///modules/devtools/StyleSheetEditor.jsm");
 const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
 
+XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
+                                  "resource://gre/modules/PluralForm.jsm");
+
 const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
 const { PrefObserver, PREF_ORIG_SOURCES } = require("devtools/styleeditor/utils");
 
 const LOAD_ERROR = "error-load";
 const STYLE_EDITOR_TEMPLATE = "stylesheet";
 
 /**
  * StyleEditorUI is controls and builds the UI of the Style Editor, including
--- a/browser/devtools/styleinspector/computed-view.js
+++ b/browser/devtools/styleinspector/computed-view.js
@@ -12,20 +12,22 @@ const {ELEMENT_STYLE} = require("devtool
 const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 const {EventEmitter} = require("devtools/toolkit/event-emitter");
 const {OutputParser} = require("devtools/output-parser");
 const {Tooltip} = require("devtools/shared/widgets/Tooltip");
 const {PrefObserver, PREF_ORIG_SOURCES} = require("devtools/styleeditor/utils");
 const {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
 
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/PluralForm.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/devtools/Templater.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
+                                  "resource://gre/modules/PluralForm.jsm");
+
 const FILTER_CHANGED_TIMEOUT = 300;
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 /**
  * Helper for long-running processes that should yield occasionally to
  * the mainloop.
  *
--- a/browser/devtools/webconsole/console-output.js
+++ b/browser/devtools/webconsole/console-output.js
@@ -865,17 +865,17 @@ Messages.Simple.prototype = Heritage.ext
   /**
    * Render the message body DOM element.
    * @private
    * @return Element
    */
   _renderBody: function()
   {
     let body = this.document.createElementNS(XHTML_NS, "span");
-    body.className = "body devtools-monospace";
+    body.className = "message-body-wrapper message-body devtools-monospace";
 
     let anchor, container = body;
     if (this._link || this._linkCallback) {
       container = anchor = this.document.createElementNS(XHTML_NS, "a");
       anchor.href = this._link || "#";
       anchor.draggable = false;
       this._addLinkCallback(anchor, this._linkCallback);
       body.appendChild(anchor);
@@ -1218,70 +1218,158 @@ Messages.ConsoleGeneric = function(packe
     severity: CONSOLE_API_LEVELS_TO_SEVERITIES[packet.level],
     private: packet.private,
     filterDuplicates: true,
     location: {
       url: packet.filename,
       line: packet.lineNumber,
     },
   };
+
   switch (packet.level) {
     case "count": {
       let counter = packet.counter, label = counter.label;
       if (!label) {
         label = l10n.getStr("noCounterLabel");
       }
       Messages.Extended.call(this, [label+ ": " + counter.count], options);
       break;
     }
     default:
       Messages.Extended.call(this, packet.arguments, options);
       break;
   }
 
   this._repeatID.consoleApiLevel = packet.level;
   this._repeatID.styles = packet.styles;
+  this._stacktrace = this._repeatID.stacktrace = packet.stacktrace;
   this._styles = packet.styles || [];
+
+  this._onClickCollapsible = this._onClickCollapsible.bind(this);
 };
 
 Messages.ConsoleGeneric.prototype = Heritage.extend(Messages.Extended.prototype,
 {
   _styles: null,
+  _stacktrace: null,
+
+  /**
+   * Tells if the message can be expanded/collapsed.
+   * @type boolean
+   */
+  collapsible: false,
+
+  /**
+   * Getter that tells if this message is collapsed - no details are shown.
+   * @type boolean
+   */
+  get collapsed() {
+    return this.collapsible && this.element && !this.element.hasAttribute("open");
+  },
 
   _renderBodyPieceSeparator: function()
   {
     return this.document.createTextNode(" ");
   },
 
   render: function()
   {
+    let msg = this.document.createElementNS(XHTML_NS, "span");
+    msg.className = "message-body devtools-monospace";
+
+    this._renderBodyPieces(msg);
+
+    let repeatNode = Messages.Simple.prototype._renderRepeatNode.call(this);
+    let location = Messages.Simple.prototype._renderLocation.call(this);
+    if (location) {
+      location.target = "jsdebugger";
+    }
+
+    let stack = null;
+    let twisty = null;
+    if (this._stacktrace && this._stacktrace.length > 0) {
+      stack = new Widgets.Stacktrace(this, this._stacktrace).render().element;
+
+      twisty = this.document.createElementNS(XHTML_NS, "a");
+      twisty.className = "theme-twisty";
+      twisty.href = "#";
+      twisty.title = l10n.getStr("messageToggleDetails");
+      twisty.addEventListener("click", this._onClickCollapsible);
+    }
+
+    let flex = this.document.createElementNS(XHTML_NS, "span");
+    flex.className = "message-flex-body";
+
+    if (twisty) {
+      flex.appendChild(twisty);
+    }
+
+    flex.appendChild(msg);
+
+    if (repeatNode) {
+      flex.appendChild(repeatNode);
+    }
+    if (location) {
+      flex.appendChild(location);
+    }
+
+    let result = this.document.createDocumentFragment();
+    result.appendChild(flex);
+
+    if (stack) {
+      result.appendChild(this.document.createTextNode("\n"));
+      result.appendChild(stack);
+    }
+
+    this._message = result;
+    this._stacktrace = null;
+
+    Messages.Simple.prototype.render.call(this);
+
+    if (stack) {
+      this.collapsible = true;
+      this.element.setAttribute("collapsible", true);
+
+      let icon = this.element.querySelector(".icon");
+      icon.addEventListener("click", this._onClickCollapsible);
+    }
+
+    return this;
+  },
+
+  _renderBody: function()
+  {
+    let body = Messages.Simple.prototype._renderBody.apply(this, arguments);
+    body.classList.remove("devtools-monospace", "message-body");
+    return body;
+  },
+
+  _renderBodyPieces: function(container)
+  {
     let lastStyle = null;
-    let result = this.document.createDocumentFragment();
 
     for (let i = 0; i < this._messagePieces.length; i++) {
       let separator = i > 0 ? this._renderBodyPieceSeparator() : null;
       if (separator) {
-        result.appendChild(separator);
+        container.appendChild(separator);
       }
 
       let piece = this._messagePieces[i];
       let style = this._styles[i];
 
       // No long string support.
       if (style && typeof style == "string" ) {
         lastStyle = this.cleanupStyle(style);
       }
 
-      result.appendChild(this._renderBodyPiece(piece, lastStyle));
+      container.appendChild(this._renderBodyPiece(piece, lastStyle));
     }
 
-    this._message = result;
     this._messagePieces = null;
     this._styles = null;
-    return Messages.Simple.prototype.render.call(this);
   },
 
   _renderBodyPiece: function(piece, style)
   {
     let elem = Messages.Extended.prototype._renderBodyPiece.call(this, piece);
     let result = elem;
 
     if (style) {
@@ -1293,16 +1381,51 @@ Messages.ConsoleGeneric.prototype = Heri
         span.appendChild(elem);
         result = span;
       }
     }
 
     return result;
   },
 
+  // no-op for the message location and .repeats elements.
+  // |this.render()| handles customized message output.
+  _renderLocation: function() { },
+  _renderRepeatNode: function() { },
+
+  /**
+   * Expand/collapse message details.
+   */
+  toggleDetails: function()
+  {
+    let twisty = this.element.querySelector(".theme-twisty");
+    if (this.element.hasAttribute("open")) {
+      this.element.removeAttribute("open");
+      twisty.removeAttribute("open");
+    } else {
+      this.element.setAttribute("open", true);
+      twisty.setAttribute("open", true);
+    }
+  },
+
+  /**
+   * The click event handler for the message expander arrow element. This method
+   * toggles the display of message details.
+   *
+   * @private
+   * @param nsIDOMEvent ev
+   *        The DOM event object.
+   * @see this.toggleDetails()
+   */
+  _onClickCollapsible: function(ev)
+  {
+    ev.preventDefault();
+    this.toggleDetails();
+  },
+
   /**
    * Given a style attribute value, return a cleaned up version of the string
    * such that:
    *
    * - no external URL is allowed to load. See RE_CLEANUP_STYLES.
    * - only some of the properties are allowed, based on a whitelist. See
    *   RE_ALLOWED_STYLES.
    *
@@ -1350,17 +1473,17 @@ Messages.ConsoleGeneric.prototype = Heri
  * @constructor
  * @extends Messages.Simple
  * @param object packet
  *        The Console API call packet received from the server.
  */
 Messages.ConsoleTrace = function(packet)
 {
   let options = {
-    className: "consoleTrace cm-s-mozilla",
+    className: "cm-s-mozilla",
     timestamp: packet.timeStamp,
     category: "webdev",
     severity: CONSOLE_API_LEVELS_TO_SEVERITIES[packet.level],
     private: packet.private,
     filterDuplicates: true,
     location: {
       url: packet.filename,
       line: packet.lineNumber,
@@ -1407,16 +1530,23 @@ Messages.ConsoleTrace.prototype = Herita
         }
       }
     }
     this._arguments = null;
 
     return result;
   },
 
+  render: function()
+  {
+    Messages.Simple.prototype.render.apply(this, arguments);
+    this.element.setAttribute("open", true);
+    return this;
+  },
+
   /**
    * Render the stack frames.
    *
    * @private
    * @return DOMElement
    */
   _renderStack: function()
   {
@@ -1424,31 +1554,32 @@ Messages.ConsoleTrace.prototype = Herita
     cmvar.className = "cm-variable";
     cmvar.textContent = "console";
 
     let cmprop = this.document.createElementNS(XHTML_NS, "span");
     cmprop.className = "cm-property";
     cmprop.textContent = "trace";
 
     let title = this.document.createElementNS(XHTML_NS, "span");
-    title.className = "title devtools-monospace";
+    title.className = "message-body devtools-monospace";
     title.appendChild(cmvar);
     title.appendChild(this.document.createTextNode("."));
     title.appendChild(cmprop);
     title.appendChild(this.document.createTextNode("():"));
 
     let repeatNode = Messages.Simple.prototype._renderRepeatNode.call(this);
     let location = Messages.Simple.prototype._renderLocation.call(this);
     if (location) {
       location.target = "jsdebugger";
     }
 
     let widget = new Widgets.Stacktrace(this, this._stacktrace).render();
 
-    let body = this.document.createElementNS(XHTML_NS, "div");
+    let body = this.document.createElementNS(XHTML_NS, "span");
+    body.className = "message-flex-body";
     body.appendChild(title);
     if (repeatNode) {
       body.appendChild(repeatNode);
     }
     if (location) {
       body.appendChild(location);
     }
     body.appendChild(this.document.createTextNode("\n"));
@@ -1458,17 +1589,17 @@ Messages.ConsoleTrace.prototype = Herita
     frag.appendChild(widget.element);
 
     return frag;
   },
 
   _renderBody: function()
   {
     let body = Messages.Simple.prototype._renderBody.apply(this, arguments);
-    body.classList.remove("devtools-monospace");
+    body.classList.remove("devtools-monospace", "message-body");
     return body;
   },
 
   // no-op for the message location and .repeats elements.
   // |this._renderStack| handles customized message output.
   _renderLocation: function() { },
   _renderRepeatNode: function() { },
 }); // Messages.ConsoleTrace.prototype
@@ -2717,17 +2848,16 @@ Widgets.Stacktrace.prototype = Heritage.
 
     let elem = this.document.createElementNS(XHTML_NS, "li");
     elem.appendChild(fn);
     elem.appendChild(location);
     elem.appendChild(this.document.createTextNode("\n"));
 
     return elem;
   },
-
 }); // Widgets.Stacktrace.prototype
 
 
 function gSequenceId()
 {
   return gSequenceId.n++;
 }
 gSequenceId.n = 0;
--- a/browser/devtools/webconsole/test/browser.ini
+++ b/browser/devtools/webconsole/test/browser.ini
@@ -108,16 +108,17 @@ support-files =
   test-bug_923281_console_log_filter.html
   test-bug_923281_test1.js
   test-bug_923281_test2.js
   test-bug_939783_console_trace_duplicates.html
   test-bug-952277-highlight-nodes-in-vview.html
   test-bug-609872-cd-iframe-parent.html
   test-bug-609872-cd-iframe-child.html
   test-bug-989025-iframe-parent.html
+  test-console-api-stackframe.html
 
 [browser_bug664688_sandbox_update_after_navigation.js]
 [browser_bug_638949_copy_link_location.js]
 [browser_bug_862916_console_dir_and_filter_off.js]
 [browser_bug_865288_repeat_different_objects.js]
 [browser_bug_865871_variables_view_close_on_esc_key.js]
 [browser_bug_869003_inspect_cross_domain_object.js]
 [browser_bug_871156_ctrlw_close_tab.js]
@@ -278,8 +279,9 @@ run-if = os == "mac"
 [browser_webconsole_output_dom_elements_04.js]
 [browser_webconsole_output_events.js]
 [browser_console_variables_view_highlighter.js]
 [browser_webconsole_start_netmon_first.js]
 [browser_webconsole_console_trace_duplicates.js]
 [browser_webconsole_cd_iframe.js]
 [browser_webconsole_autocomplete_crossdomain_iframe.js]
 [browser_webconsole_console_custom_styles.js]
+[browser_webconsole_console_api_stackframe.js]
--- a/browser/devtools/webconsole/test/browser_bug_638949_copy_link_location.js
+++ b/browser/devtools/webconsole/test/browser_bug_638949_copy_link_location.js
@@ -62,16 +62,17 @@ function onConsoleMessage(aResults) {
   output.focus();
   let message = [...aResults[0].matched][0];
 
   goUpdateCommand(COMMAND_NAME);
   ok(!isEnabled(), COMMAND_NAME + "is disabled");
 
   // Test that the "Copy Link Location" menu item is hidden for non-network
   // messages.
+  message.scrollIntoView();
   waitForContextMenu(menu, message, () => {
     let isHidden = menu.querySelector(CONTEXT_MENU_ID).hidden;
     ok(isHidden, CONTEXT_MENU_ID + " is hidden");
   }, testWithNetActivity);
 }
 
 function testWithNetActivity() {
   HUD.jsterm.clearOutput();
@@ -102,15 +103,16 @@ function onNetworkMessage(aResults) {
 
   waitForClipboard((aData) => { return aData.trim() == message.url; },
     () => { goDoCommand(COMMAND_NAME) },
     testMenuWithNetActivity, testMenuWithNetActivity);
 
   function testMenuWithNetActivity() {
     // Test that the "Copy Link Location" menu item is visible for network-related
     // messages.
+    message.scrollIntoView();
     waitForContextMenu(menu, message, () => {
       let isVisible = !menu.querySelector(CONTEXT_MENU_ID).hidden;
       ok(isVisible, CONTEXT_MENU_ID + " is visible");
     }, finishTest);
   }
 }
 
--- a/browser/devtools/webconsole/test/browser_bug_865288_repeat_different_objects.js
+++ b/browser/devtools/webconsole/test/browser_bug_865288_repeat_different_objects.js
@@ -48,17 +48,17 @@ function checkMessages([result])
   let m = -1;
 
   function nextMessage()
   {
     let msg = msgs[++m];
     if (msg) {
       ok(msg, "message element #" + m);
 
-      let clickable = msg.querySelector(".body a");
+      let clickable = msg.querySelector(".message-body a");
       ok(clickable, "clickable object #" + m);
 
       msg.scrollIntoView(false);
       clickObject(clickable);
     }
     else {
       finishTest();
     }
--- a/browser/devtools/webconsole/test/browser_bug_865871_variables_view_close_on_esc_key.js
+++ b/browser/devtools/webconsole/test/browser_bug_865871_variables_view_close_on_esc_key.js
@@ -18,17 +18,17 @@ function test()
     let {tab} = yield loadTab(TEST_URI);
     hud = yield openConsole(tab);
     let jsterm = hud.jsterm;
 
     let msg = yield execute("fooObj");
     ok(msg, "output message found");
 
     let anchor = msg.querySelector("a");
-    let body = msg.querySelector(".body");
+    let body = msg.querySelector(".message-body");
     ok(anchor, "object anchor");
     ok(body, "message body");
     ok(body.textContent.contains('testProp: "testValue"'), "message text check");
 
     msg.scrollIntoView();
     executeSoon(() => {
       EventUtils.synthesizeMouse(anchor, 2, 2, {}, hud.iframeWindow);
     });
@@ -54,17 +54,17 @@ function test()
     });
     yield jsterm.once("sidebar-closed");
 
     jsterm.clearOutput();
 
     msg = yield execute("window.location");
     ok(msg, "output message found");
 
-    body = msg.querySelector(".body");
+    body = msg.querySelector(".message-body");
     ok(body, "message body");
     anchor = msg.querySelector("a");
     ok(anchor, "object anchor");
     ok(body.textContent.contains("Location \u2192 http://example.com/browser/"),
        "message text check");
 
     msg.scrollIntoView();
     executeSoon(() => {
--- a/browser/devtools/webconsole/test/browser_bug_869003_inspect_cross_domain_object.js
+++ b/browser/devtools/webconsole/test/browser_bug_869003_inspect_cross_domain_object.js
@@ -42,17 +42,17 @@ function consoleOpened(hud)
   }).then(onConsoleMessage);
 }
 
 function onConsoleMessage(aResults)
 {
   let msg = [...aResults[0].matched][0];
   ok(msg, "message element");
 
-  let body = msg.querySelector(".body");
+  let body = msg.querySelector(".message-body");
   ok(body, "message body");
 
   let clickable = aResults[0].clickableElements[0];
   ok(clickable, "clickable object found");
   ok(body.textContent.contains('{ hello: "world!",'), "message text check");
 
   gJSTerm.once("variablesview-fetched", onObjFetch);
 
--- a/browser/devtools/webconsole/test/browser_console_click_focus.js
+++ b/browser/devtools/webconsole/test/browser_console_click_focus.js
@@ -20,17 +20,17 @@ function testInputFocus() {
       webconsole: hud,
       messages: [{
         text: "Dolske Digs Bacon",
         category: CATEGORY_WEBDEV,
         severity: SEVERITY_LOG,
       }],
     }).then(([result]) => {
       let msg = [...result.matched][0];
-      let outputItem = msg.querySelector(".body");
+      let outputItem = msg.querySelector(".message-body");
       ok(outputItem, "found a logged message");
       let inputNode = hud.jsterm.inputNode;
       ok(inputNode.getAttribute("focused"), "input node is focused, first");
 
       let lostFocus = () => {
         inputNode.removeEventListener("blur", lostFocus);
         info("input node lost focus");
       }
--- a/browser/devtools/webconsole/test/browser_console_log_inspectable_object.js
+++ b/browser/devtools/webconsole/test/browser_console_log_inspectable_object.js
@@ -29,17 +29,17 @@ function performTest(hud)
       text: "fooBug676722",
       category: CATEGORY_WEBDEV,
       severity: SEVERITY_LOG,
       objects: true,
     }],
   }).then(([result]) => {
     let msg = [...result.matched][0];
     ok(msg, "message element");
-    let body = msg.querySelector(".body");
+    let body = msg.querySelector(".message-body");
     ok(body, "message body");
     let clickable = result.clickableElements[0];
     ok(clickable, "the console.log() object anchor was found");
     ok(body.textContent.contains('{ abba: "omgBug676722" }'),
        "clickable node content is correct");
 
     hud.jsterm.once("variablesview-fetched",
       (aEvent, aVar) => {
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_594477_clickable_output.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_594477_clickable_output.js
@@ -31,17 +31,17 @@ function tabLoad2(aEvent) {
     webconsole: HUD,
     messages: [{
       text: "test-console.html",
       category: CATEGORY_NETWORK,
       severity: SEVERITY_LOG,
     }],
   }).then(([result]) => {
     let msg = [...result.matched][0];
-    outputItem = msg.querySelector(".body .url");
+    outputItem = msg.querySelector(".message-body .url");
     ok(outputItem, "found a network message");
     document.addEventListener("popupshown", networkPanelShown, false);
 
     // Send the mousedown and click events such that the network panel opens.
     EventUtils.sendMouseEvent({type: "mousedown"}, outputItem);
     EventUtils.sendMouseEvent({type: "click"}, outputItem);
   });
 }
@@ -97,17 +97,17 @@ function networkPanelHidden(aEvent) {
     document.removeEventListener("popupshown", networkPanelShowFailure, false);
 
     // Done with the network output. Now test the jsterm output and the property
     // panel.
     HUD.jsterm.execute("document", (msg) => {
       info("jsterm execute 'document' callback");
 
       HUD.jsterm.once("variablesview-open", onVariablesViewOpen);
-      let outputItem = msg.querySelector(".body a");
+      let outputItem = msg.querySelector(".message-body a");
       ok(outputItem, "jsterm output message found");
 
       // Send the mousedown and click events such that the property panel opens.
       EventUtils.sendMouseEvent({type: "mousedown"}, outputItem);
       EventUtils.sendMouseEvent({type: "click"}, outputItem);
     });
   });
 }
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_632347_iterators_generators.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_632347_iterators_generators.js
@@ -61,17 +61,17 @@ function consoleOpened(HUD) {
   completion = JSPropertyProvider(dbgWindow, null, "window._container.");
   ok(completion, "matches available for window._container");
   ok(completion.matches.length, "matches available for window (length)");
 
   jsterm.clearOutput();
 
   jsterm.execute("window._container", (msg) => {
     jsterm.once("variablesview-fetched", testVariablesView.bind(null, HUD));
-    let anchor = msg.querySelector(".body a");
+    let anchor = msg.querySelector(".message-body a");
     EventUtils.synthesizeMouse(anchor, 2, 2, {}, HUD.iframeWindow);
   });
 }
 
 function testVariablesView(aWebconsole, aEvent, aView) {
   findVariableViewProperties(aView, [
     { name: "gen1", isGenerator: true },
     { name: "gen2", isGenerator: true },
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_651501_document_body_autocomplete.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_651501_document_body_autocomplete.js
@@ -70,17 +70,17 @@ function autocompletePopupHidden()
 }
 
 function testPropertyPanel()
 {
   let jsterm = gHUD.jsterm;
   jsterm.clearOutput();
   jsterm.execute("document", (msg) => {
     jsterm.once("variablesview-fetched", onVariablesViewReady);
-    let anchor = msg.querySelector(".body a");
+    let anchor = msg.querySelector(".message-body a");
     EventUtils.synthesizeMouse(anchor, 2, 2, {}, gHUD.iframeWindow);
   });
 }
 
 function onVariablesViewReady(aEvent, aView)
 {
   findVariableViewProperties(aView, [
     { name: "body", value: "<body>" },
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_764572_output_open_url.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_764572_output_open_url.js
@@ -53,16 +53,17 @@ function onConsoleMessage(aResults) {
   // Check if the command is disabled non-network messages.
   goUpdateCommand(COMMAND_NAME);
   let controller = top.document.commandDispatcher
                    .getControllerForCommand(COMMAND_NAME);
 
   let isDisabled = !controller || !controller.isCommandEnabled(COMMAND_NAME);
   ok(isDisabled, COMMAND_NAME + " should be disabled.");
 
+  outputNode.selectedItem.scrollIntoView();
   waitForContextMenu(contextMenu, outputNode.selectedItem, () => {
     let isHidden = contextMenu.querySelector(CONTEXT_MENU_ID).hidden;
     ok(isHidden, CONTEXT_MENU_ID + " should be hidden.");
   }, testOnNetActivity);
 }
 
 function testOnNetActivity() {
   HUD.jsterm.clearOutput();
@@ -112,13 +113,14 @@ function onNetworkMessage(aResults) {
   // Try to open the URL.
   goDoCommand(COMMAND_NAME);
 }
 
 function testOnNetActivity_contextmenu(msg) {
   outputNode.focus();
   HUD.ui.output.selectMessage(msg);
 
+  msg.scrollIntoView();
   waitForContextMenu(contextMenu, msg, () => {
     let isShown = !contextMenu.querySelector(CONTEXT_MENU_ID).hidden;
     ok(isShown, CONTEXT_MENU_ID + " should be shown.");
   }, finishTest);
 }
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_766001_JS_Console_in_Debugger.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_766001_JS_Console_in_Debugger.js
@@ -1,13 +1,17 @@
 /* vim:set ts=2 sw=2 sts=2 et: */
-/* ***** BEGIN LICENSE BLOCK *****
- * Any copyright is dedicated to the Public Domain.
+/* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
- * ***** END LICENSE BLOCK ***** */
+ */
+
+// Test that message source links for js errors and console API calls open in
+// the jsdebugger when clicked.
+
+"use strict";
 
 const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test" +
                  "/test-bug-766001-js-console-links.html";
 
 function test() {
   let hud;
 
   requestLongerTimeout(2);
@@ -56,18 +60,16 @@ function test() {
 
     let line = node.sourceLine;
     ok(line, "found source line for index " + index);
 
     executeSoon(() => {
       EventUtils.sendMouseEvent({ type: "click" }, node);
     });
 
-    yield hud.ui.once("source-in-debugger-opened", checkLine.bind(null, url, line));
-  }
+    yield hud.ui.once("source-in-debugger-opened");
 
-  function* checkLine(url, line) {
     let toolbox = yield gDevTools.getToolbox(hud.target);
     let {panelWin: { DebuggerView: view }} = toolbox.getPanel("jsdebugger");
     is(view.Sources.selectedValue, url, "expected source url");
     is(view.editor.getCursor().line, line - 1, "expected source line");
   }
 }
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_webconsole_console_api_stackframe.js
@@ -0,0 +1,81 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that the console API messages for console.error()/exception()/assert()
+// include a stackframe. See bug 920116.
+
+function test() {
+  let hud;
+
+  const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console-api-stackframe.html";
+  const TEST_FILE = TEST_URI.substr(TEST_URI.lastIndexOf("/"));
+
+  Task.spawn(runner).then(finishTest);
+
+  function* runner() {
+    const {tab} = yield loadTab(TEST_URI);
+    hud = yield openConsole(tab);
+
+    const stack = [{
+      file: TEST_FILE,
+      fn: "thirdCall",
+      line: /\b2[123]\b/, // 21,22,23
+    }, {
+      file: TEST_FILE,
+      fn: "secondCall",
+      line: 16,
+    }, {
+      file: TEST_FILE,
+      fn: "firstCall",
+      line: 12,
+    }];
+
+    let results = yield waitForMessages({
+      webconsole: hud,
+      messages: [{
+        text: "foo-log",
+        category: CATEGORY_WEBDEV,
+        severity: SEVERITY_LOG,
+        collapsible: false,
+      }, {
+        text: "foo-error",
+        category: CATEGORY_WEBDEV,
+        severity: SEVERITY_ERROR,
+        collapsible: true,
+        stacktrace: stack,
+      }, {
+        text: "foo-exception",
+        category: CATEGORY_WEBDEV,
+        severity: SEVERITY_ERROR,
+        collapsible: true,
+        stacktrace: stack,
+      }, {
+        text: "foo-assert",
+        category: CATEGORY_WEBDEV,
+        severity: SEVERITY_ERROR,
+        collapsible: true,
+        stacktrace: stack,
+      }],
+    });
+
+    let elem = [...results[1].matched][0];
+    ok(elem, "message element");
+
+    let msg = elem._messageObject;
+    ok(msg, "message object");
+
+    ok(msg.collapsed, "message is collapsed");
+
+    msg.toggleDetails();
+
+    ok(!msg.collapsed, "message is not collapsed");
+
+    msg.toggleDetails();
+
+    ok(msg.collapsed, "message is collapsed");
+
+    yield closeConsole(tab);
+  }
+}
--- a/browser/devtools/webconsole/test/browser_webconsole_console_custom_styles.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_console_custom_styles.js
@@ -54,17 +54,17 @@ function test() {
         text: "foobar",
         category: CATEGORY_WEBDEV,
       }],
     });
 
     let msg = [...result.matched][0];
     ok(msg, "message element");
 
-    let span = msg.querySelector(".body span[style]");
+    let span = msg.querySelector(".message-body span[style]");
     ok(span, "span element");
 
     info("span textContent is: " + span.textContent);
     isnot(span.textContent.indexOf("foobar"), -1, "span textContent check");
 
     let outputStyle = span.getAttribute("style").replace(/\s+|;+$/g, "");
     if (check.sameStyleExpected) {
       is(outputStyle, check.style, "span style is correct");
--- a/browser/devtools/webconsole/test/browser_webconsole_jsterm.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_jsterm.js
@@ -25,17 +25,17 @@ function nextTest() {
 function checkResult(msg, desc) {
   waitForMessages({
     webconsole: jsterm.hud.owner,
     messages: [{
       name: desc,
       category: CATEGORY_OUTPUT,
     }],
   }).then(([result]) => {
-    let node = [...result.matched][0].querySelector(".body");
+    let node = [...result.matched][0].querySelector(".message-body");
     if (typeof msg == "string") {
       is(node.textContent.trim(), msg,
         "correct message shown for " + desc);
     }
     else if (typeof msg == "function") {
       ok(msg(node), "correct message shown for " + desc);
     }
 
--- a/browser/devtools/webconsole/test/head.js
+++ b/browser/devtools/webconsole/test/head.js
@@ -1,13 +1,15 @@
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+"use strict";
+
 let {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
 let {console} = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
 let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 let {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
 let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 let {require, TargetFactory} = devtools;
 let {Utils: WebConsoleUtils} = require("devtools/toolkit/webconsole/utils");
 let {Messages} = require("devtools/webconsole/console-output");
@@ -877,24 +879,28 @@ function openDebugger(aOptions = {})
  *            Provide this if you want to match a console.time() message.
  *            - consoleTimeEnd: same as above, but for console.timeEnd().
  *            - consoleDir: boolean, set to |true| to match a console.dir()
  *            message.
  *            - consoleGroup: boolean, set to |true| to match a console.group()
  *            message.
  *            - longString: boolean, set to |true} to match long strings in the
  *            message.
+ *            - collapsible: boolean, set to |true| to match messages that can
+ *            be collapsed/expanded.
  *            - type: match messages that are instances of the given object. For
  *            example, you can point to Messages.NavigationMarker to match any
  *            such message.
  *            - objects: boolean, set to |true| if you expect inspectable
  *            objects in the message.
  *            - source: object of the shape { url, line }. This is used to
  *            match the source URL and line number of the error message or
  *            console API call.
+ *            - stacktrace: array of objects of the form { file, fn, line } that
+ *            can match frames in the stacktrace associated with the message.
  *            - groupDepth: number used to check the depth of the message in
  *            a group.
  *            - url: URL to match for network requests.
  * @return object
  *         A promise object is returned once the messages you want are found.
  *         The promise is resolved with the array of rule objects you give in
  *         the |messages| property. Each objects is the same as provided, with
  *         additional properties:
@@ -913,73 +919,53 @@ function waitForMessages(aOptions)
   let rules = WebConsoleUtils.cloneObject(aOptions.messages, true);
   let rulesMatched = 0;
   let listenerAdded = false;
   let deferred = promise.defer();
   aOptions.matchCondition = aOptions.matchCondition || "all";
 
   function checkText(aRule, aText)
   {
-    let result;
+    let result = false;
     if (Array.isArray(aRule)) {
       result = aRule.every((s) => checkText(s, aText));
     }
     else if (typeof aRule == "string") {
       result = aText.indexOf(aRule) > -1;
     }
     else if (aRule instanceof RegExp) {
       result = aRule.test(aText);
     }
+    else {
+      result = aRule == aText;
+    }
     return result;
   }
 
   function checkConsoleTrace(aRule, aElement)
   {
     let elemText = aElement.textContent;
     let trace = aRule.consoleTrace;
 
     if (!checkText("console.trace():", elemText)) {
       return false;
     }
 
-    let frame = aElement.querySelector(".stacktrace li:first-child");
-    if (trace.file) {
-      let file = frame.querySelector(".message-location").title;
-      if (!checkText(trace.file, file)) {
-        ok(false, "console.trace() message is missing the file name: " +
-                  trace.file);
-        displayErrorContext(aRule, aElement);
-        return false;
-      }
-    }
-
-    if (trace.fn) {
-      let fn = frame.querySelector(".function").textContent;
-      if (!checkText(trace.fn, fn)) {
-        ok(false, "console.trace() message is missing the function name: " +
-                  trace.fn);
-        displayErrorContext(aRule, aElement);
-        return false;
-      }
-    }
-
-    if (trace.line) {
-      let line = frame.querySelector(".message-location").sourceLine;
-      if (!checkText(trace.line, line)) {
-        ok(false, "console.trace() message is missing the line number: " +
-                  trace.line);
-        displayErrorContext(aRule, aElement);
-        return false;
-      }
-    }
-
     aRule.category = CATEGORY_WEBDEV;
     aRule.severity = SEVERITY_LOG;
     aRule.type = Messages.ConsoleTrace;
 
+    if (!aRule.stacktrace && typeof trace == "object" && trace !== true) {
+      if (Array.isArray(trace)) {
+        aRule.stacktrace = trace;
+      } else {
+        aRule.stacktrace = [trace];
+      }
+    }
+
     return true;
   }
 
   function checkConsoleTime(aRule, aElement)
   {
     let elemText = aElement.textContent;
     let time = aRule.consoleTime;
 
@@ -1053,16 +1039,76 @@ function waitForMessages(aOptions)
 
     if ("line" in aRule.source && location.sourceLine != aRule.source.line) {
       return false;
     }
 
     return true;
   }
 
+  function checkCollapsible(aRule, aElement)
+  {
+    let msg = aElement._messageObject;
+    if (!msg || !!msg.collapsible != aRule.collapsible) {
+      return false;
+    }
+
+    return true;
+  }
+
+  function checkStacktrace(aRule, aElement)
+  {
+    let stack = aRule.stacktrace;
+    let frames = aElement.querySelectorAll(".stacktrace > li");
+    if (!frames.length) {
+      return false;
+    }
+
+    for (let i = 0; i < stack.length; i++) {
+      let frame = frames[i];
+      let expected = stack[i];
+      if (!frame) {
+        ok(false, "expected frame #" + i + " but didnt find it");
+        return false;
+      }
+
+      if (expected.file) {
+        let file = frame.querySelector(".message-location").title;
+        if (!checkText(expected.file, file)) {
+          ok(false, "frame #" + i + " does not match file name: " +
+                    expected.file);
+          displayErrorContext(aRule, aElement);
+          return false;
+        }
+      }
+
+      if (expected.fn) {
+        let fn = frame.querySelector(".function").textContent;
+        if (!checkText(expected.fn, fn)) {
+          ok(false, "frame #" + i + " does not match the function name: " +
+                    expected.fn);
+          displayErrorContext(aRule, aElement);
+          return false;
+        }
+      }
+
+      if (expected.line) {
+        let line = frame.querySelector(".message-location").sourceLine;
+        if (!checkText(expected.line, line)) {
+          ok(false, "frame #" + i + " does not match the line number: " +
+                    expected.line);
+          displayErrorContext(aRule, aElement);
+          return false;
+        }
+      }
+    }
+
+    return true;
+  }
+
   function checkMessage(aRule, aElement)
   {
     let elemText = aElement.textContent;
 
     if (aRule.text && !checkText(aRule.text, elemText)) {
       return false;
     }
 
@@ -1089,16 +1135,20 @@ function waitForMessages(aOptions)
     if (aRule.consoleGroup && !checkConsoleGroup(aRule, aElement)) {
       return false;
     }
 
     if (aRule.source && !checkSource(aRule, aElement)) {
       return false;
     }
 
+    if ("collapsible" in aRule && !checkCollapsible(aRule, aElement)) {
+      return false;
+    }
+
     let partialMatch = !!(aRule.consoleTrace || aRule.consoleTime ||
                           aRule.consoleTimeEnd);
 
     // The rule tries to match the newer types of messages, based on their
     // object constructor.
     if (aRule.type) {
       if (!aElement._messageObject ||
           !(aElement._messageObject instanceof aRule.type)) {
@@ -1124,16 +1174,28 @@ function waitForMessages(aOptions)
       if (partialMatch) {
         is(aElement.severity, aRule.severity,
            "message severity for rule: " + displayRule(aRule));
         displayErrorContext(aRule, aElement);
       }
       return false;
     }
 
+    if (aRule.text) {
+      partialMatch = true;
+    }
+
+    if (aRule.stacktrace && !checkStacktrace(aRule, aElement)) {
+      if (partialMatch) {
+        ok(false, "failed to match stacktrace for rule: " + displayRule(aRule));
+        displayErrorContext(aRule, aElement);
+      }
+      return false;
+    }
+
     if (aRule.category == CATEGORY_NETWORK && "url" in aRule &&
         !checkText(aRule.url, aElement.url)) {
       return false;
     }
 
     if ("repeats" in aRule) {
       let repeats = aElement.querySelector(".message-repeats");
       if (!repeats || repeats.getAttribute("value") != aRule.repeats) {
@@ -1161,17 +1223,17 @@ function waitForMessages(aOptions)
           displayErrorContext(aRule, aElement);
         }
         return false;
       }
       aRule.longStrings = longStrings;
     }
 
     if ("objects" in aRule) {
-      let clickables = aElement.querySelectorAll(".body a");
+      let clickables = aElement.querySelectorAll(".message-body a");
       if (aRule.objects != !!clickables[0]) {
         if (partialMatch) {
           is(!!clickables[0], aRule.objects,
              "objects existence check failed for message rule: " +
              displayRule(aRule));
           displayErrorContext(aRule, aElement);
         }
         return false;
@@ -1405,17 +1467,18 @@ function checkOutputForInputs(hud, input
     }
     if (typeof entry.inspectorIcon == "boolean") {
       yield checkLinkToInspector(entry, msg);
     }
   }
 
   function checkObjectClick(entry, msg)
   {
-    let body = msg.querySelector(".body a") || msg.querySelector(".body");
+    let body = msg.querySelector(".message-body a") ||
+               msg.querySelector(".message-body");
     ok(body, "the message body");
 
     let deferred = promise.defer();
 
     entry._onVariablesViewOpen = onVariablesViewOpen.bind(null, entry, deferred);
     hud.jsterm.on("variablesview-open", entry._onVariablesViewOpen);
     eventHandlers.add(entry._onVariablesViewOpen);
 
--- a/browser/devtools/webconsole/test/test-bug-766001-console-log.js
+++ b/browser/devtools/webconsole/test/test-bug-766001-console-log.js
@@ -1,8 +1,10 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-window.addEventListener("load", function() {
+function onLoad123() {
   console.log("Blah Blah");
-}, false);
+}
+
+window.addEventListener("load", onLoad123, false);
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/test-console-api-stackframe.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html dir="ltr" lang="en">
+  <head>
+    <meta charset="utf8">
+    <!--
+    - Any copyright is dedicated to the Public Domain.
+    - http://creativecommons.org/publicdomain/zero/1.0/
+    -->
+    <title>Test for bug 920116 - stacktraces for console API messages</title>
+    <script>
+      function firstCall() {
+        secondCall();
+      }
+
+      function secondCall() {
+        thirdCall();
+      }
+
+      function thirdCall() {
+        console.log("foo-log");
+        console.error("foo-error");
+        console.exception("foo-exception");
+        console.assert("red" == "blue", "foo-assert");
+      }
+
+      window.onload = firstCall;
+    </script>
+  </head>
+  <body>
+    <p>Hello world!</p>
+  </body>
+</html>
--- a/browser/devtools/webconsole/webconsole.js
+++ b/browser/devtools/webconsole/webconsole.js
@@ -1368,17 +1368,17 @@ WebConsoleFrame.prototype = {
 
     let node = this.createMessageNode(aCategory, severity,
                                       errorMessage,
                                       aScriptError.sourceName,
                                       aScriptError.lineNumber, null, null,
                                       aScriptError.timeStamp);
 
     // Select the body of the message node that is displayed in the console
-    let msgBody = node.getElementsByClassName("body")[0];
+    let msgBody = node.getElementsByClassName("message-body")[0];
     // Add the more info link node to messages that belong to certain categories
     this.addMoreInfoLink(msgBody, aScriptError);
 
     if (aScriptError.private) {
       node.setAttribute("private", true);
     }
 
     if (objectActors.size > 0) {
@@ -2456,17 +2456,17 @@ WebConsoleFrame.prototype = {
     iconContainer.className = "icon";
 
     // Apply the current group by indenting appropriately.
     let iconMarginLeft = this.groupDepth * GROUP_INDENT + GROUP_INDENT_DEFAULT;
     iconContainer.style.marginLeft = iconMarginLeft + "px";
 
     // Create the message body, which contains the actual text of the message.
     let bodyNode = this.document.createElementNS(XHTML_NS, "span");
-    bodyNode.className = "body devtools-monospace";
+    bodyNode.className = "message-body-wrapper message-body devtools-monospace";
 
     // Store the body text, since it is needed later for the variables view.
     let body = aBody;
     // If a string was supplied for the body, turn it into a DOM node and an
     // associated clipboard string now.
     aClipboardText = aClipboardText ||
                      (aBody + (aSourceURL ? " @ " + aSourceURL : "") +
                               (aSourceLine ? ":" + aSourceLine : ""));
@@ -2603,17 +2603,19 @@ WebConsoleFrame.prototype = {
     }
 
     filenameNode.className = "filename";
     filenameNode.textContent = " " + (filename || l10n.getStr("unknownLocation"));
     locationNode.appendChild(filenameNode);
 
     locationNode.href = isScratchpad || !fullURL ? "#" : fullURL;
     locationNode.draggable = false;
-    locationNode.target = aTarget;
+    if (aTarget) {
+      locationNode.target = aTarget;
+    }
     locationNode.setAttribute("title", aSourceURL);
     locationNode.className = "message-location theme-link devtools-monospace";
 
     // Make the location clickable.
     let onClick = () => {
       let target = locationNode.target;
       if (target == "scratchpad" || isScratchpad) {
         this.owner.viewSourceInScratchpad(aSourceURL);
--- 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: 1.0.21
+Current extension version is: 1.0.68
 
--- a/browser/extensions/pdfjs/content/PdfStreamConverter.jsm
+++ b/browser/extensions/pdfjs/content/PdfStreamConverter.jsm
@@ -222,17 +222,18 @@ ChromeActions.prototype = {
   },
   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 filename = data.filename;
-    if (typeof filename !== 'string' || !/\.pdf$/i.test(filename)) {
+    if (typeof filename !== 'string' || 
+        (!/\.pdf$/i.test(filename) && !data.isAttachment)) {
       filename = 'document.pdf';
     }
     var blobUri = data.blobUrl ? NetUtil.newURI(data.blobUrl) : originalUri;
     var extHelperAppSvc =
           Cc['@mozilla.org/uriloader/external-helper-app-service;1'].
              getService(Ci.nsIExternalHelperAppService);
     var frontWindow = Cc['@mozilla.org/embedcomp/window-watcher;1'].
                          getService(Ci.nsIWindowWatcher).activeWindow;
@@ -268,17 +269,18 @@ ChromeActions.prototype = {
       if ('nsIPrivateBrowsingChannel' in Ci &&
           channel instanceof Ci.nsIPrivateBrowsingChannel) {
         channel.setPrivate(docIsPrivate);
       }
 
       var listener = {
         extListener: null,
         onStartRequest: function(aRequest, aContext) {
-          this.extListener = extHelperAppSvc.doContent('application/pdf',
+          this.extListener = extHelperAppSvc.doContent((data.isAttachment ? '' :
+                                                        'application/pdf'),
                                 aRequest, frontWindow, false);
           this.extListener.onStartRequest(aRequest, aContext);
         },
         onStopRequest: function(aRequest, aContext, aStatusCode) {
           if (this.extListener)
             this.extListener.onStopRequest(aRequest, aContext, aStatusCode);
           // Notify the content code we're done downloading.
           if (sendResponse)
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -16,18 +16,18 @@
  */
 /*jshint globalstrict: false */
 
 // Initializing PDFJS global object (if still undefined)
 if (typeof PDFJS === 'undefined') {
   (typeof window !== 'undefined' ? window : this).PDFJS = {};
 }
 
-PDFJS.version = '1.0.21';
-PDFJS.build = 'f954cde';
+PDFJS.version = '1.0.68';
+PDFJS.build = 'ead4cbf';
 
 (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
@@ -448,16 +448,38 @@ function stringToBytes(str) {
   return bytes;
 }
 
 function string32(value) {
   return String.fromCharCode((value >> 24) & 0xff, (value >> 16) & 0xff,
                              (value >> 8) & 0xff, value & 0xff);
 }
 
+function log2(x) {
+  var n = 1, i = 0;
+  while (x > n) {
+    n <<= 1;
+    i++;
+  }
+  return i;
+}
+
+function readInt8(data, start) {
+  return (data[start] << 24) >> 24;
+}
+
+function readUint16(data, offset) {
+  return (data[offset] << 8) | data[offset + 1];
+}
+
+function readUint32(data, offset) {
+  return ((data[offset] << 24) | (data[offset + 1] << 16) |
+         (data[offset + 2] << 8) | data[offset + 3]) >>> 0;
+}
+
 // Lazy test the endianness of the platform
 // NOTE: This will be 'true' for simulated TypedArrays
 function isLittleEndian() {
   var buffer8 = new Uint8Array(2);
   buffer8[0] = 1;
   var buffer16 = new Uint16Array(buffer8.buffer);
   return (buffer16[0] === 1);
 }
@@ -1740,41 +1762,41 @@ var DeviceCmykCS = (function DeviceCmykC
   //   f(A, B,.. N) = Acc+Bcm+Ccy+Dck+c+Fmm+Gmy+Hmk+Im+Jyy+Kyk+Ly+Mkk+Nk+255
   function convertToRgb(src, srcOffset, srcScale, dest, destOffset) {
     var c = src[srcOffset + 0] * srcScale;
     var m = src[srcOffset + 1] * srcScale;
     var y = src[srcOffset + 2] * srcScale;
     var k = src[srcOffset + 3] * srcScale;
 
     var r =
-      c * (-4.387332384609988 * c + 54.48615194189176 * m +
-           18.82290502165302 * y + 212.25662451639585 * k +
-           -285.2331026137004) +
-      m * (1.7149763477362134 * m - 5.6096736904047315 * y +
-           -17.873870861415444 * k - 5.497006427196366) +
-      y * (-2.5217340131683033 * y - 21.248923337353073 * k +
-           17.5119270841813) +
-      k * (-21.86122147463605 * k - 189.48180835922747) + 255;
+      (c * (-4.387332384609988 * c + 54.48615194189176 * m +
+            18.82290502165302 * y + 212.25662451639585 * k +
+            -285.2331026137004) +
+       m * (1.7149763477362134 * m - 5.6096736904047315 * y +
+            -17.873870861415444 * k - 5.497006427196366) +
+       y * (-2.5217340131683033 * y - 21.248923337353073 * k +
+            17.5119270841813) +
+       k * (-21.86122147463605 * k - 189.48180835922747) + 255) | 0;
     var g =
-      c * (8.841041422036149 * c + 60.118027045597366 * m +
-           6.871425592049007 * y + 31.159100130055922 * k +
-           -79.2970844816548) +
-      m * (-15.310361306967817 * m + 17.575251261109482 * y +
-           131.35250912493976 * k - 190.9453302588951) +
-      y * (4.444339102852739 * y + 9.8632861493405 * k - 24.86741582555878) +
-      k * (-20.737325471181034 * k - 187.80453709719578) + 255;
+      (c * (8.841041422036149 * c + 60.118027045597366 * m +
+            6.871425592049007 * y + 31.159100130055922 * k +
+            -79.2970844816548) +
+       m * (-15.310361306967817 * m + 17.575251261109482 * y +
+            131.35250912493976 * k - 190.9453302588951) +
+       y * (4.444339102852739 * y + 9.8632861493405 * k - 24.86741582555878) +
+       k * (-20.737325471181034 * k - 187.80453709719578) + 255) | 0;
     var b =
-      c * (0.8842522430003296 * c + 8.078677503112928 * m +
-           30.89978309703729 * y - 0.23883238689178934 * k +
-           -14.183576799673286) +
-      m * (10.49593273432072 * m + 63.02378494754052 * y +
-           50.606957656360734 * k - 112.23884253719248) +
-      y * (0.03296041114873217 * y + 115.60384449646641 * k +
-           -193.58209356861505) +
-      k * (-22.33816807309886 * k - 180.12613974708367) + 255;
+      (c * (0.8842522430003296 * c + 8.078677503112928 * m +
+            30.89978309703729 * y - 0.23883238689178934 * k +
+            -14.183576799673286) +
+       m * (10.49593273432072 * m + 63.02378494754052 * y +
+            50.606957656360734 * k - 112.23884253719248) +
+       y * (0.03296041114873217 * y + 115.60384449646641 * k +
+            -193.58209356861505) +
+       k * (-22.33816807309886 * k - 180.12613974708367) + 255) | 0;
 
     dest[destOffset] = r > 255 ? 255 : r < 0 ? 0 : r;
     dest[destOffset + 1] = g > 255 ? 255 : g < 0 ? 0 : g;
     dest[destOffset + 2] = b > 255 ? 255 : b < 0 ? 0 : b;
   }
 
   function DeviceCmykCS() {
     this.name = 'DeviceCMYK';
@@ -2137,17 +2159,16 @@ var PDFFunction = (function PDFFunctionC
     parse: function PDFFunction_parse(xref, fn) {
       var IR = this.getIR(xref, fn);
       return this.fromIR(IR);
     },
 
     constructSampled: function PDFFunction_constructSampled(str, dict) {
       function toMultiArray(arr) {
         var inputLength = arr.length;
-        var outputLength = arr.length / 2;
         var out = [];
         var index = 0;
         for (var i = 0; i < inputLength; i += 2) {
           out[index] = [arr[i], arr[i + 1]];
           ++index;
         }
         return out;
       }
@@ -2208,17 +2229,17 @@ var PDFFunction = (function PDFFunctionC
         // See chapter 3, page 110 of the PDF reference.
         var m = IR[1];
         var domain = IR[2];
         var encode = IR[3];
         var decode = IR[4];
         var samples = IR[5];
         var size = IR[6];
         var n = IR[7];
-        var mask = IR[8];
+        //var mask = IR[8];
         var range = IR[9];
 
         if (m != args.length) {
           error('Incorrect number of arguments: ' + m + ' != ' +
                 args.length);
         }
 
         var x = args;
@@ -2879,16 +2900,17 @@ var Annotation = (function AnnotationClo
             data.borderWidth = 0;
           }
         }
       }
     }
 
     this.appearance = getDefaultAppearance(dict);
     data.hasAppearance = !!this.appearance;
+    data.id = params.ref.num;
   }
 
   Annotation.prototype = {
 
     getData: function Annotation_getData() {
       return this.data;
     },
 
@@ -2987,18 +3009,16 @@ var Annotation = (function AnnotationClo
         'Font'
         // ProcSet
         // Properties
       ]);
       var bbox = appearanceDict.get('BBox') || [0, 0, 1, 1];
       var matrix = appearanceDict.get('Matrix') || [1, 0, 0, 1, 0 ,0];
       var transform = getTransformMatrix(data.rect, bbox, matrix);
 
-      var border = data.border;
-
       resourcesPromise.then(function(resources) {
         var opList = new OperatorList();
         opList.addOp(OPS.beginAnnotation, [data.rect, transform, matrix]);
         evaluator.getOperatorList(this.appearance, resources, opList);
         opList.addOp(OPS.endAnnotation, []);
         promise.resolve(opList);
 
         this.appearance.reset();
@@ -3214,17 +3234,16 @@ var TextWidgetAnnotation = (function Tex
     var fontName = fontObj.loadedName;
     var fontFamily = fontName ? '"' + fontName + '", ' : '';
     // Use a reasonable default font if the font doesn't specify a fallback
     var fallbackName = fontObj.fallbackName || 'Helvetica, sans-serif';
     style.fontFamily = fontFamily + fallbackName;
   }
 
 
-  var parent = WidgetAnnotation.prototype;
   Util.inherit(TextWidgetAnnotation, WidgetAnnotation, {
     hasHtml: function TextWidgetAnnotation_hasHtml() {
       return !this.data.hasAppearance && !!this.data.fieldValue;
     },
 
     getHtmlElement: function TextWidgetAnnotation_getHtmlElement(commonObjs) {
       assert(!isWorker, 'getHtmlElement() shall be called from main thread');
 
@@ -3237,17 +3256,17 @@ var TextWidgetAnnotation = (function Tex
       content.textContent = item.fieldValue;
       var textAlignment = item.textAlignment;
       content.style.textAlign = ['left', 'center', 'right'][textAlignment];
       content.style.verticalAlign = 'middle';
       content.style.display = 'table-cell';
 
       var fontObj = item.fontRefName ?
                     commonObjs.getData(item.fontRefName) : null;
-      var cssRules = setTextStyles(content, item, fontObj);
+      setTextStyles(content, item, fontObj);
 
       element.appendChild(content);
 
       return element;
     },
 
     getOperatorList: function TextWidgetAnnotation_getOperatorList(evaluator) {
       if (this.appearance) {
@@ -3269,17 +3288,16 @@ var TextWidgetAnnotation = (function Tex
 
       // Include any font resources found in the default appearance
 
       var stream = new Stream(stringToBytes(defaultAppearance));
       evaluator.getOperatorList(stream, this.fieldResources, opList);
       var appearanceFnArray = opList.fnArray;
       var appearanceArgsArray = opList.argsArray;
       var fnArray = [];
-      var argsArray = [];
 
       // TODO(mack): Add support for stroke color
       data.rgb = [0, 0, 0];
       // TODO THIS DOESN'T MAKE ANY SENSE SINCE THE fnArray IS EMPTY!
       for (var i = 0, n = fnArray.length; i < n; ++i) {
         var fnId = appearanceFnArray[i];
         var args = appearanceArgsArray[i];
 
@@ -3485,17 +3503,16 @@ var TextAnnotation = (function TextAnnot
         var toggleAnnotation = function toggleAnnotation() {
           if (pinned) {
             hideAnnotation(true);
           } else {
             showAnnotation(true);
           }
         };
 
-        var self = this;
         image.addEventListener('click', function image_clickHandler() {
           toggleAnnotation();
         }, false);
         image.addEventListener('mouseover', function image_mouseOverHandler() {
           showAnnotation();
         }, false);
         image.addEventListener('mouseout', function image_mouseOutHandler() {
           hideAnnotation();
@@ -3590,17 +3607,16 @@ var LinkAnnotation = (function LinkAnnot
     },
 
     getHtmlElement: function LinkAnnotation_getHtmlElement(commonObjs) {
 
       var container = this.initContainer();
       container.className = 'annotLink';
 
       var item = this.data;
-      var rect = item.rect;
 
       container.style.borderColor = item.colorCssRgb;
       container.style.borderStyle = 'solid';
 
       var link = document.createElement('a');
       link.href = link.title = this.data.url || '';
 
       container.appendChild(link);
@@ -3846,16 +3862,23 @@ var PDFDocumentProxy = (function PDFDocu
     /**
      * @return {Promise} A promise that is resolved with a lookup table for
      * mapping named destinations to reference numbers.
      */
     getDestinations: function PDFDocumentProxy_getDestinations() {
       return this.transport.getDestinations();
     },
     /**
+     * @return {Promise} A promise that is resolved with a lookup table for
+     * mapping named attachments to their content.
+     */
+    getAttachments: function PDFDocumentProxy_getAttachments() {
+      return this.transport.getAttachments();
+    },
+    /**
      * @return {Promise} A promise that is resolved with an array of all the
      * JavaScript strings in the name tree.
      */
     getJavaScript: function PDFDocumentProxy_getJavaScript() {
       var promise = new PDFJS.LegacyPromise();
       var js = this.pdfInfo.javaScript;
       promise.resolve(js);
       return promise;
@@ -4622,16 +4645,26 @@ var WorkerTransport = (function WorkerTr
       this.messageHandler.send('GetDestinations', null,
         function transportDestinations(destinations) {
           promise.resolve(destinations);
         }
       );
       return promise;
     },
 
+    getAttachments: function WorkerTransport_getAttachments() {
+      var promise = new PDFJS.LegacyPromise();
+      this.messageHandler.send('GetAttachments', null,
+        function transportAttachments(attachments) {
+          promise.resolve(attachments);
+        }
+      );
+      return promise;
+    },
+
     startCleanup: function WorkerTransport_startCleanup() {
       this.messageHandler.send('Cleanup', null,
         function endCleanup() {
           for (var i = 0, ii = this.pageCache.length; i < ii; i++) {
             var page = this.pageCache[i];
             if (page) {
               page.destroy();
             }
@@ -5416,17 +5449,16 @@ var CanvasGraphics = (function CanvasGra
     var src = imgData.data;
     var dest = chunkImgData.data;
     var i, j, thisChunkHeight, elemsInThisChunk;
 
     // There are multiple forms in which the pixel data can be passed, and
     // imgData.kind tells us which one this is.
     if (imgData.kind === ImageKind.GRAYSCALE_1BPP) {
       // Grayscale, 1 bit per pixel (i.e. black-and-white).
-      var destDataLength = dest.length;
       var srcLength = src.byteLength;
       var dest32 = PDFJS.hasCanvasTypedArrays ? new Uint32Array(dest.buffer) :
         new Uint32ArrayView(dest);
       var dest32DataLength = dest32.length;
       var fullSrcDiff = (width + 7) >> 3;
       var white = 0xFFFFFFFF;
       var black = (PDFJS.isLittleEndian || !PDFJS.hasCanvasTypedArrays) ?
         0xFF000000 : 0x000000FF;
@@ -5687,17 +5719,16 @@ var CanvasGraphics = (function CanvasGra
       var i = executionStartIdx || 0;
       var argsArrayLen = argsArray.length;
 
       // Sometimes the OperatorList to execute is empty.
       if (argsArrayLen == i) {
         return i;
       }
 
-      var executionEndIdx;
       var endTime = Date.now() + EXECUTION_TIME;
 
       var commonObjs = this.commonObjs;
       var objs = this.objs;
       var fnId;
       var deferred = Promise.resolve();
 
       while (true) {
@@ -6263,17 +6294,16 @@ var CanvasGraphics = (function CanvasGra
       var charSpacing = current.charSpacing;
       var wordSpacing = current.wordSpacing;
       var textHScale = current.textHScale * current.fontDirection;
       var fontMatrix = current.fontMatrix || FONT_IDENTITY_MATRIX;
       var glyphsLength = glyphs.length;
       var vertical = font.vertical;
       var defaultVMetrics = font.defaultVMetrics;
       var i, glyph, width;
-      var VERTICAL_TEXT_ROTATION = Math.PI / 2;
 
       if (fontSize === 0) {
         return;
       }
 
       // Type3 fonts - each glyph is a "mini-PDF"
       if (font.coded) {
         ctx.save();
@@ -6391,17 +6421,16 @@ var CanvasGraphics = (function CanvasGra
           current.y -= x * textHScale;
         } else {
           current.x += x * textHScale;
         }
         ctx.restore();
       }
     },
     showSpacedText: function CanvasGraphics_showSpacedText(arr) {
-      var ctx = this.ctx;
       var current = this.current;
       var font = current.font;
       var fontSize = current.fontSize;
       // TJ array's number is independent from fontMatrix
       var textHScale = current.textHScale * 0.001 * current.fontDirection;
       var arrLength = arr.length;
       var vertical = font.vertical;
 
@@ -6467,18 +6496,16 @@ var CanvasGraphics = (function CanvasGra
     },
     getColorN_Pattern: function CanvasGraphics_getColorN_Pattern(IR, cs) {
       var pattern;
       if (IR[0] == 'TilingPattern') {
         var args = IR[1];
         var base = cs.base;
         var color;
         if (base) {
-          var baseComps = base.numComps;
-
           color = base.getRgb(args, 0);
         }
         pattern = new TilingPattern(IR, color, this.ctx, this.objs,
                                     this.commonObjs, this.baseTransform);
       } else {
         pattern = getShadingPatternFromIR(IR);
       }
       return pattern;
@@ -7727,23 +7754,23 @@ var createMeshCanvas = (function createM
     return {canvas: canvas, offsetX: offsetX, offsetY: offsetY,
             scaleX: scaleX, scaleY: scaleY};
   }
   return createMeshCanvas;
 })();
 
 ShadingIRs.Mesh = {
   fromIR: function Mesh_fromIR(raw) {
-    var type = raw[1];
+    //var type = raw[1];
     var coords = raw[2];
     var colors = raw[3];
     var figures = raw[4];
     var bounds = raw[5];
     var matrix = raw[6];
-    var bbox = raw[7];
+    //var bbox = raw[7];
     var background = raw[8];
     return {
       type: 'Pattern',
       getPattern: function Mesh_getPattern(ctx, owner, shadingFill) {
         var combinedScale;
         // Obtain scale from matrix and current transformation matrix.
         if (shadingFill) {
           combinedScale = Util.singularValueDecompose2dScale(
@@ -7830,17 +7857,16 @@ var TilingPattern = (function TilingPatt
       var bbox = this.bbox;
       var xstep = this.xstep;
       var ystep = this.ystep;
       var paintType = this.paintType;
       var tilingType = this.tilingType;
       var color = this.color;
       var objs = this.objs;
       var commonObjs = this.commonObjs;
-      var ctx = this.ctx;
 
       info('TilingType: ' + tilingType);
 
       var x0 = bbox[0], y0 = bbox[1], x1 = bbox[2], y1 = bbox[3];
 
       var topLeft = [x0, y0];
       // we want the canvas to be as large as the step size
       var botRight = [x0 + xstep, y0 + ystep];
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -16,18 +16,18 @@
  */
 /*jshint globalstrict: false */
 
 // Initializing PDFJS global object (if still undefined)
 if (typeof PDFJS === 'undefined') {
   (typeof window !== 'undefined' ? window : this).PDFJS = {};
 }
 
-PDFJS.version = '1.0.21';
-PDFJS.build = 'f954cde';
+PDFJS.version = '1.0.68';
+PDFJS.build = 'ead4cbf';
 
 (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
@@ -448,16 +448,38 @@ function stringToBytes(str) {
   return bytes;
 }
 
 function string32(value) {
   return String.fromCharCode((value >> 24) & 0xff, (value >> 16) & 0xff,
                              (value >> 8) & 0xff, value & 0xff);
 }
 
+function log2(x) {
+  var n = 1, i = 0;
+  while (x > n) {
+    n <<= 1;
+    i++;
+  }
+  return i;
+}
+
+function readInt8(data, start) {
+  return (data[start] << 24) >> 24;
+}
+
+function readUint16(data, offset) {
+  return (data[offset] << 8) | data[offset + 1];
+}
+
+function readUint32(data, offset) {
+  return ((data[offset] << 24) | (data[offset + 1] << 16) |
+         (data[offset + 2] << 8) | data[offset + 3]) >>> 0;
+}
+
 // Lazy test the endianness of the platform
 // NOTE: This will be 'true' for simulated TypedArrays
 function isLittleEndian() {
   var buffer8 = new Uint8Array(2);
   buffer8[0] = 1;
   var buffer16 = new Uint16Array(buffer8.buffer);
   return (buffer16[0] === 1);
 }
@@ -1740,41 +1762,41 @@ var DeviceCmykCS = (function DeviceCmykC
   //   f(A, B,.. N) = Acc+Bcm+Ccy+Dck+c+Fmm+Gmy+Hmk+Im+Jyy+Kyk+Ly+Mkk+Nk+255
   function convertToRgb(src, srcOffset, srcScale, dest, destOffset) {
     var c = src[srcOffset + 0] * srcScale;
     var m = src[srcOffset + 1] * srcScale;
     var y = src[srcOffset + 2] * srcScale;
     var k = src[srcOffset + 3] * srcScale;
 
     var r =
-      c * (-4.387332384609988 * c + 54.48615194189176 * m +
-           18.82290502165302 * y + 212.25662451639585 * k +
-           -285.2331026137004) +
-      m * (1.7149763477362134 * m - 5.6096736904047315 * y +
-           -17.873870861415444 * k - 5.497006427196366) +
-      y * (-2.5217340131683033 * y - 21.248923337353073 * k +
-           17.5119270841813) +
-      k * (-21.86122147463605 * k - 189.48180835922747) + 255;
+      (c * (-4.387332384609988 * c + 54.48615194189176 * m +
+            18.82290502165302 * y + 212.25662451639585 * k +
+            -285.2331026137004) +
+       m * (1.7149763477362134 * m - 5.6096736904047315 * y +
+            -17.873870861415444 * k - 5.497006427196366) +
+       y * (-2.5217340131683033 * y - 21.248923337353073 * k +
+            17.5119270841813) +
+       k * (-21.86122147463605 * k - 189.48180835922747) + 255) | 0;
     var g =
-      c * (8.841041422036149 * c + 60.118027045597366 * m +
-           6.871425592049007 * y + 31.159100130055922 * k +
-           -79.2970844816548) +
-      m * (-15.310361306967817 * m + 17.575251261109482 * y +
-           131.35250912493976 * k - 190.9453302588951) +
-      y * (4.444339102852739 * y + 9.8632861493405 * k - 24.86741582555878) +
-      k * (-20.737325471181034 * k - 187.80453709719578) + 255;
+      (c * (8.841041422036149 * c + 60.118027045597366 * m +
+            6.871425592049007 * y + 31.159100130055922 * k +
+            -79.2970844816548) +
+       m * (-15.310361306967817 * m + 17.575251261109482 * y +
+            131.35250912493976 * k - 190.9453302588951) +
+       y * (4.444339102852739 * y + 9.8632861493405 * k - 24.86741582555878) +
+       k * (-20.737325471181034 * k - 187.80453709719578) + 255) | 0;
     var b =
-      c * (0.8842522430003296 * c + 8.078677503112928 * m +
-           30.89978309703729 * y - 0.23883238689178934 * k +
-           -14.183576799673286) +
-      m * (10.49593273432072 * m + 63.02378494754052 * y +
-           50.606957656360734 * k - 112.23884253719248) +
-      y * (0.03296041114873217 * y + 115.60384449646641 * k +
-           -193.58209356861505) +
-      k * (-22.33816807309886 * k - 180.12613974708367) + 255;
+      (c * (0.8842522430003296 * c + 8.078677503112928 * m +
+            30.89978309703729 * y - 0.23883238689178934 * k +
+            -14.183576799673286) +
+       m * (10.49593273432072 * m + 63.02378494754052 * y +
+            50.606957656360734 * k - 112.23884253719248) +
+       y * (0.03296041114873217 * y + 115.60384449646641 * k +
+            -193.58209356861505) +
+       k * (-22.33816807309886 * k - 180.12613974708367) + 255) | 0;
 
     dest[destOffset] = r > 255 ? 255 : r < 0 ? 0 : r;
     dest[destOffset + 1] = g > 255 ? 255 : g < 0 ? 0 : g;
     dest[destOffset + 2] = b > 255 ? 255 : b < 0 ? 0 : b;
   }
 
   function DeviceCmykCS() {
     this.name = 'DeviceCMYK';
@@ -2137,17 +2159,16 @@ var PDFFunction = (function PDFFunctionC
     parse: function PDFFunction_parse(xref, fn) {
       var IR = this.getIR(xref, fn);
       return this.fromIR(IR);
     },
 
     constructSampled: function PDFFunction_constructSampled(str, dict) {
       function toMultiArray(arr) {
         var inputLength = arr.length;
-        var outputLength = arr.length / 2;
         var out = [];
         var index = 0;
         for (var i = 0; i < inputLength; i += 2) {
           out[index] = [arr[i], arr[i + 1]];
           ++index;
         }
         return out;
       }
@@ -2208,17 +2229,17 @@ var PDFFunction = (function PDFFunctionC
         // See chapter 3, page 110 of the PDF reference.
         var m = IR[1];
         var domain = IR[2];
         var encode = IR[3];
         var decode = IR[4];
         var samples = IR[5];
         var size = IR[6];
         var n = IR[7];
-        var mask = IR[8];
+        //var mask = IR[8];
         var range = IR[9];
 
         if (m != args.length) {
           error('Incorrect number of arguments: ' + m + ' != ' +
                 args.length);
         }
 
         var x = args;
@@ -2879,16 +2900,17 @@ var Annotation = (function AnnotationClo
             data.borderWidth = 0;
           }
         }
       }
     }
 
     this.appearance = getDefaultAppearance(dict);
     data.hasAppearance = !!this.appearance;
+    data.id = params.ref.num;
   }
 
   Annotation.prototype = {
 
     getData: function Annotation_getData() {
       return this.data;
     },
 
@@ -2987,18 +3009,16 @@ var Annotation = (function AnnotationClo
         'Font'
         // ProcSet
         // Properties
       ]);
       var bbox = appearanceDict.get('BBox') || [0, 0, 1, 1];
       var matrix = appearanceDict.get('Matrix') || [1, 0, 0, 1, 0 ,0];
       var transform = getTransformMatrix(data.rect, bbox, matrix);
 
-      var border = data.border;
-
       resourcesPromise.then(function(resources) {
         var opList = new OperatorList();
         opList.addOp(OPS.beginAnnotation, [data.rect, transform, matrix]);
         evaluator.getOperatorList(this.appearance, resources, opList);
         opList.addOp(OPS.endAnnotation, []);
         promise.resolve(opList);
 
         this.appearance.reset();
@@ -3214,17 +3234,16 @@ var TextWidgetAnnotation = (function Tex
     var fontName = fontObj.loadedName;
     var fontFamily = fontName ? '"' + fontName + '", ' : '';
     // Use a reasonable default font if the font doesn't specify a fallback
     var fallbackName = fontObj.fallbackName || 'Helvetica, sans-serif';
     style.fontFamily = fontFamily + fallbackName;
   }
 
 
-  var parent = WidgetAnnotation.prototype;
   Util.inherit(TextWidgetAnnotation, WidgetAnnotation, {
     hasHtml: function TextWidgetAnnotation_hasHtml() {
       return !this.data.hasAppearance && !!this.data.fieldValue;
     },
 
     getHtmlElement: function TextWidgetAnnotation_getHtmlElement(commonObjs) {
       assert(!isWorker, 'getHtmlElement() shall be called from main thread');
 
@@ -3237,17 +3256,17 @@ var TextWidgetAnnotation = (function Tex
       content.textContent = item.fieldValue;
       var textAlignment = item.textAlignment;
       content.style.textAlign = ['left', 'center', 'right'][textAlignment];
       content.style.verticalAlign = 'middle';
       content.style.display = 'table-cell';
 
       var fontObj = item.fontRefName ?
                     commonObjs.getData(item.fontRefName) : null;
-      var cssRules = setTextStyles(content, item, fontObj);
+      setTextStyles(content, item, fontObj);
 
       element.appendChild(content);
 
       return element;
     },
 
     getOperatorList: function TextWidgetAnnotation_getOperatorList(evaluator) {
       if (this.appearance) {
@@ -3269,17 +3288,16 @@ var TextWidgetAnnotation = (function Tex
 
       // Include any font resources found in the default appearance
 
       var stream = new Stream(stringToBytes(defaultAppearance));
       evaluator.getOperatorList(stream, this.fieldResources, opList);
       var appearanceFnArray = opList.fnArray;
       var appearanceArgsArray = opList.argsArray;
       var fnArray = [];
-      var argsArray = [];
 
       // TODO(mack): Add support for stroke color
       data.rgb = [0, 0, 0];
       // TODO THIS DOESN'T MAKE ANY SENSE SINCE THE fnArray IS EMPTY!
       for (var i = 0, n = fnArray.length; i < n; ++i) {
         var fnId = appearanceFnArray[i];
         var args = appearanceArgsArray[i];
 
@@ -3485,17 +3503,16 @@ var TextAnnotation = (function TextAnnot
         var toggleAnnotation = function toggleAnnotation() {
           if (pinned) {
             hideAnnotation(true);
           } else {
             showAnnotation(true);
           }
         };
 
-        var self = this;
         image.addEventListener('click', function image_clickHandler() {
           toggleAnnotation();
         }, false);
         image.addEventListener('mouseover', function image_mouseOverHandler() {
           showAnnotation();
         }, false);
         image.addEventListener('mouseout', function image_mouseOutHandler() {
           hideAnnotation();
@@ -3590,17 +3607,16 @@ var LinkAnnotation = (function LinkAnnot
     },
 
     getHtmlElement: function LinkAnnotation_getHtmlElement(commonObjs) {
 
       var container = this.initContainer();
       container.className = 'annotLink';
 
       var item = this.data;
-      var rect = item.rect;
 
       container.style.borderColor = item.colorCssRgb;
       container.style.borderStyle = 'solid';
 
       var link = document.createElement('a');
       link.href = link.title = this.data.url || '';
 
       container.appendChild(link);
@@ -3737,17 +3753,17 @@ var ChunkedStream = (function ChunkedStr
     },
 
     getUint16: function ChunkedStream_getUint16() {
       var b0 = this.getByte();
       var b1 = this.getByte();
       return (b0 << 8) + b1;
     },
 
-    getUint32: function ChunkedStream_getUint32() {
+    getInt32: function ChunkedStream_getInt32() {
       var b0 = this.getByte();
       var b1 = this.getByte();
       var b2 = this.getByte();
       var b3 = this.getByte();
       return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;
     },
 
     // returns subarray of original buffer
@@ -3826,17 +3842,16 @@ var ChunkedStream = (function ChunkedStr
   };
 
   return ChunkedStream;
 })();
 
 var ChunkedStreamManager = (function ChunkedStreamManagerClosure() {
 
   function ChunkedStreamManager(length, chunkSize, url, args) {
-    var self = this;
     this.stream = new ChunkedStream(length, chunkSize, this);
     this.length = length;
     this.chunkSize = chunkSize;
     this.url = url;
     this.disableAutoFetch = args.disableAutoFetch;
     var msgHandler = this.msgHandler = args.msgHandler;
 
     if (args.chunkedViewerLoading) {
@@ -5259,16 +5274,40 @@ var Catalog = (function CatalogClosure()
           if (!names.hasOwnProperty(name)) {
             continue;
           }
           dests[name] = fetchDestination(names[name]);
         }
       }
       return shadow(this, 'destinations', dests);
     },
+    get attachments() {
+      var xref = this.xref;
+      var attachments, nameTreeRef;
+      var obj = this.catDict.get('Names');
+      if (obj) {
+        nameTreeRef = obj.getRaw('EmbeddedFiles');
+      }
+
+      if (nameTreeRef) {
+        var nameTree = new NameTree(nameTreeRef, xref);
+        var names = nameTree.getAll();
+        for (var name in names) {
+          if (!names.hasOwnProperty(name)) {
+            continue;
+          }
+          var fs = new FileSpec(names[name], xref);
+          if (!attachments) {
+            attachments = {};
+          }
+          attachments[stringToPDFString(name)] = fs.serializable;
+        }
+      }
+      return shadow(this, 'attachments', attachments);
+    },
     get javaScript() {
       var xref = this.xref;
       var obj = this.catDict.get('Names');
 
       var javaScript = [];
       if (obj && obj.has('JavaScript')) {
         var nameTree = new NameTree(obj.getRaw('JavaScript'), xref);
         var names = nameTree.getAll();
@@ -5745,18 +5784,16 @@ var XRef = (function XRefClosure() {
       var endobjBytes = new Uint8Array([101, 110, 100, 111, 98, 106]);
       var xrefBytes = new Uint8Array([47, 88, 82, 101, 102]);
 
       var stream = this.stream;
       stream.pos = 0;
       var buffer = stream.getBytes();
       var position = stream.start, length = buffer.length;
       var trailers = [], xrefStms = [];
-      var state = 0;
-      var currentToken;
       while (position < length) {
         var ch = buffer[position];
         if (ch === 32 || ch === 9 || ch === 13 || ch === 10) {
           ++position;
           continue;
         }
         if (ch === 37) { // %-comment
           do {
@@ -6123,16 +6160,107 @@ var NameTree = (function NameTreeClosure
       }
       return dict;
     }
   };
   return NameTree;
 })();
 
 /**
+ * "A PDF file can refer to the contents of another file by using a File 
+ * Specification (PDF 1.1)", see the spec (7.11) for more details.
+ * NOTE: Only embedded files are supported (as part of the attachments support)
+ * TODO: support the 'URL' file system (with caching if !/V), portable 
+ * collections attributes and related files (/RF)
+ */
+var FileSpec = (function FileSpecClosure() {
+  function FileSpec(root, xref) {
+    if (!root || !isDict(root)) {
+      return;
+    }
+    this.xref = xref;
+    this.root = root;
+    if (root.has('FS')) {
+      this.fs = root.get('FS');
+    }
+    this.description = root.has('Desc') ?
+                         stringToPDFString(root.get('Desc')) :
+                         '';
+    if (root.has('RF')) {
+      warn('Related file specifications are not supported');
+    }
+    this.contentAvailable = true;
+    if (!root.has('EF')) {
+      this.contentAvailable = false;
+      warn('Non-embedded file specifications are not supported');
+    }
+  }
+
+  function pickPlatformItem(dict) {
+    // Look for the filename in this order:
+    // UF, F, Unix, Mac, DOS
+    if (dict.has('UF')) {
+      return dict.get('UF');
+    } else if (dict.has('F')) {
+      return dict.get('F');
+    } else if (dict.has('Unix')) {
+      return dict.get('Unix');
+    } else if (dict.has('Mac')) {
+      return dict.get('Mac');
+    } else if (dict.has('DOS')) {
+      return dict.get('DOS');
+    } else {
+      return null;
+    }
+  }
+
+  FileSpec.prototype = {
+    get filename() {
+      if (!this._filename && this.root) {
+        var filename = pickPlatformItem(this.root) || 'unnamed';
+        this._filename = stringToPDFString(filename).
+          replace(/\\\\/g, '\\').
+          replace(/\\\//g, '/').
+          replace(/\\/g, '/');
+      }
+      return this._filename;
+    },
+    get content() {
+      if (!this.contentAvailable) {
+        return null;
+      }
+      if (!this.contentRef && this.root) {
+        this.contentRef = pickPlatformItem(this.root.get('EF'));
+      }
+      var content = null;
+      if (this.contentRef) {
+        var xref = this.xref;
+        var fileObj = xref.fetchIfRef(this.contentRef);
+        if (fileObj && isStream(fileObj)) {
+          content = fileObj.getBytes();
+        } else {
+          warn('Embedded file specification points to non-existing/invalid ' +
+            'content');
+        }
+      } else {
+        warn('Embedded file specification does not have a content');
+      }
+      return content;
+    },
+    get serializable() {
+      return {
+        filename: this.filename,
+        content: this.content
+      };
+    }
+  };
+  return FileSpec;
+})();
+
+/**
  * A helper for loading missing data in object graphs. It traverses the graph
  * depth first and queues up any objects that have missing data. Once it has
  * has traversed as many objects that are available it attempts to bundle the
  * missing data requests and then resume from the nodes that weren't ready.
  *
  * NOTE: It provides protection from circular references by keeping track of
  * of loaded references. However, you must be careful not to load any graphs
  * that have references to the catalog or other pages since that will cause the
@@ -14321,17 +14449,16 @@ Shadings.Mesh = (function MeshClosure() 
       coords: psPacked,
       colors: psPacked
     });
   }
 
   function decodeType5Shading(mesh, reader, verticesPerRow) {
     var coords = mesh.coords;
     var colors = mesh.colors;
-    var operators = [];
     var ps = []; // not maintaining cs since that will match ps
     while (reader.hasData) {
       var coord = reader.readCoordinate();
       var color = reader.readComponents();
       ps.push(coords.length);
       coords.push(coord);
       colors.push(color);
     }
@@ -15275,28 +15402,26 @@ var PartialEvaluator = (function Partial
 
     getOperatorList: function PartialEvaluator_getOperatorList(stream,
                                                                resources,
                                                                operatorList,
                                                                initialState) {
 
       var self = this;
       var xref = this.xref;
-      var handler = this.handler;
       var imageCache = {};
 
       operatorList = (operatorList || new OperatorList());
 
       resources = (resources || Dict.empty);
       var xobjs = (resources.get('XObject') || Dict.empty);
       var patterns = (resources.get('Pattern') || Dict.empty);
       var stateManager = new StateManager(initialState || new EvalState());
       var preprocessor = new EvaluatorPreprocessor(stream, xref, stateManager);
 
-      var promise = new LegacyPromise();
       var operation, i, ii;
       while ((operation = preprocessor.read())) {
         var args = operation.args;
         var fn = operation.fn;
         var shading;
 
         switch (fn) {
           case OPS.setStrokeColorN:
@@ -15472,17 +15597,16 @@ var PartialEvaluator = (function Partial
 
       resources = (xref.fetchIfRef(resources) || Dict.empty);
 
       // The xobj is parsed iff it's needed, e.g. if there is a `DO` cmd.
       var xobjs = null;
       var xobjsCache = {};
 
       var preprocessor = new EvaluatorPreprocessor(stream, xref, stateManager);
-      var res = resources;
 
       var operation;
       var textState;
 
       function newTextChunk() {
         var font = textState.font;
         if (!(font.loadedName in textContent.styles)) {
           textContent.styles[font.loadedName] = {
@@ -15534,16 +15658,17 @@ var PartialEvaluator = (function Partial
         }
         var width = 0;
         var height = 0;
         var glyphs = font.charsToGlyphs(chars);
         var defaultVMetrics = font.defaultVMetrics;
         for (var i = 0; i < glyphs.length; i++) {
           var glyph = glyphs[i];
           if (!glyph) { // Previous glyph was a space.
+            width += textState.wordSpacing * textState.textHScale;
             continue;
           }
           var vMetricX = null;
           var vMetricY = null;
           var glyphWidth = null;
           if (font.vertical) {
             if (glyph.vmetric) {
               glyphWidth = glyph.vmetric[0];
@@ -15639,40 +15764,41 @@ var PartialEvaluator = (function Partial
             break;
           case OPS.setTextMatrix:
             textState.setTextMatrix(args[0], args[1], args[2], args[3],
                                     args[4], args[5]);
             textState.setTextLineMatrix(args[0], args[1], args[2], args[3],
                                         args[4], args[5]);
             break;
           case OPS.setCharSpacing:
-            textState.charSpace = args[0];
+            textState.charSpacing = args[0];
             break;
           case OPS.setWordSpacing:
-            textState.wordSpace = args[0];
+            textState.wordSpacing = args[0];
             break;
           case OPS.beginText:
             textState.textMatrix = IDENTITY_MATRIX.slice();
             textState.textLineMatrix = IDENTITY_MATRIX.slice();
             break;
           case OPS.showSpacedText:
             var items = args[0];
             var textChunk = newTextChunk();
             var offset;
             for (var j = 0, jj = items.length; j < jj; j++) {
               if (typeof items[j] === 'string') {
                 buildTextGeometry(items[j], textChunk);
               } else {
                 var val = items[j] / 1000;
                 if (!textState.font.vertical) {
-                  offset = -val * textState.fontSize * textState.textHScale;
+                  offset = -val * textState.fontSize * textState.textHScale *
+                           textState.textMatrix[0];
                   textState.translateTextMatrix(offset, 0);
                   textChunk.width += offset;
                 } else {
-                  offset = -val * textState.fontSize;
+                  offset = -val * textState.fontSize * textState.textMatrix[3];
                   textState.translateTextMatrix(0, offset);
                   textChunk.height += offset;
                 }
                 if (items[j] < 0 && textState.font.spaceWidth > 0) {
                   var fakeSpaces = -items[j] / textState.font.spaceWidth;
                   if (fakeSpaces > MULTI_SPACE_FACTOR) {
                     fakeSpaces = Math.round(fakeSpaces);
                     while (fakeSpaces--) {
@@ -15857,17 +15983,16 @@ var PartialEvaluator = (function Partial
 
       properties.differences = differences;
       properties.baseEncodingName = baseEncodingName;
       properties.dict = dict;
     },
 
     readToUnicode: function PartialEvaluator_readToUnicode(toUnicode) {
       var cmapObj = toUnicode;
-      var charToUnicode = [];
       if (isName(cmapObj)) {
         return CMapFactory.create(cmapObj).map;
       } else if (isStream(cmapObj)) {
         var cmap = CMapFactory.create(cmapObj).map;
         // Convert UTF-16BE
         // NOTE: cmap can be a sparse array, so use forEach instead of for(;;)
         // to iterate over all keys.
         cmap.forEach(function(token, i) {
@@ -20222,17 +20347,16 @@ var Font = (function FontClosure() {
    * 'charCodeToGlyphId' - maps the new font char codes to glyph ids
    */
   function adjustMapping(charCodeToGlyphId, properties) {
     var toUnicode = properties.toUnicode;
     var isSymbolic = !!(properties.flags & FontFlags.Symbolic);
     var isIdentityUnicode = properties.isIdentityUnicode;
     var newMap = Object.create(null);
     var toFontChar = [];
-    var usedCharCodes = [];
     var usedFontCharCodes = [];
     var nextAvailableFontCharCode = PRIVATE_USE_OFFSET_START;
     for (var originalCharCode in charCodeToGlyphId) {
       originalCharCode |= 0;
       var glyphId = charCodeToGlyphId[originalCharCode];
       var fontCharCode = originalCharCode;
       // First try to map the value to a unicode position if a non identity map
       // was created.
@@ -20340,17 +20464,17 @@ var Font = (function FontClosure() {
 
     // Fill up the 4 parallel arrays describing the segments.
     var startCount = '';
     var endCount = '';
     var idDeltas = '';
     var idRangeOffsets = '';
     var glyphsIds = '';
     var bias = 0;
-    
+
     var range, start, end, codes;
     for (i = 0, ii = bmpLength; i < ii; i++) {
       range = ranges[i];
       start = range[0];
       end = range[1];
       startCount += string16(start);
       endCount += string16(end);
       codes = range[2];
@@ -20664,19 +20788,19 @@ var Font = (function FontClosure() {
       }
       return data;
     },
 
     checkAndRepair: function Font_checkAndRepair(name, font, properties) {
       function readTableEntry(file) {
         var tag = bytesToString(file.getBytes(4));
 
-        var checksum = file.getUint32();
-        var offset = file.getUint32();
-        var length = file.getUint32();
+        var checksum = file.getInt32();
+        var offset = file.getInt32() >>> 0;
+        var length = file.getInt32() >>> 0;
 
         // Read the table associated data
         var previousPosition = file.pos;
         file.pos = file.start ? file.start : 0;
         file.skip(offset);
         var data = file.getBytes(length);
         file.pos = previousPosition;
 
@@ -20723,17 +20847,17 @@ var Font = (function FontClosure() {
         // use:
         // - non-symbolic fonts the preference is a 3,1 table then a 1,0 table
         // - symbolic fonts the preference is a 3,0 table then a 1,0 table
         // The following takes advantage of the fact that the tables are sorted
         // to work.
         for (var i = 0; i < numTables; i++) {
           var platformId = font.getUint16();
           var encodingId = font.getUint16();
-          var offset = font.getUint32();
+          var offset = font.getInt32() >>> 0;
           var useTable = false;
 
           if (platformId == 1 && encodingId === 0) {
             useTable = true;
             // Continue the loop since there still may be a higher priority
             // table.
           } else if (!isSymbolicFont && platformId === 3 && encodingId === 1) {
             useTable = true;
@@ -20848,18 +20972,16 @@ var Font = (function FontClosure() {
           // 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 = font.getUint16();
           var entryCount = font.getUint16();
 
-          var glyphs = [];
-          var ids = [];
           for (j = 0; j < entryCount; j++) {
             glyphId = font.getUint16();
             var charCode = firstCode + j;
 
             mappings.push({
               charCode: charCode,
               glyphId: glyphId
             });
@@ -21135,17 +21257,17 @@ var Font = (function FontClosure() {
         }
       }
 
       function readPostScriptTable(post, properties, maxpNumGlyphs) {
         var start = (font.start ? font.start : 0) + post.offset;
         font.pos = start;
 
         var length = post.length, end = start + length;
-        var version = font.getUint32();
+        var version = font.getInt32();
         // skip rest to the tables
         font.getBytes(28);
 
         var glyphNames;
         var valid = true;
         var i;
 
         switch (version) {
@@ -21237,17 +21359,16 @@ var Font = (function FontClosure() {
         for (i = 0, ii = records.length; i < ii; i++) {
           var record = records[i];
           var pos = start + stringsStart + record.offset;
           if (pos + record.length > end) {
             continue; // outside of name table, ignoring
           }
           font.pos = pos;
           var nameIndex = record.name;
-          var encoding = record.encoding ? 1 : 0;
           if (record.encoding) {
             // unicode
             var str = '';
             for (var j = 0, jj = record.length; j < jj; j += 2) {
               str += String.fromCharCode(font.getUint16());
             }
             names[1][nameIndex] = str;
           } else {
@@ -21542,17 +21663,17 @@ var Font = (function FontClosure() {
         }
       }
 
       if (!tables.maxp) {
         error('Required "maxp" table is not found');
       }
 
       font.pos = (font.start || 0) + tables.maxp.offset;
-      var version = font.getUint32();
+      var version = font.getInt32();
       var numGlyphs = font.getUint16();
       var maxFunctionDefs = 0;
       if (version >= 0x00010000 && tables.maxp.length >= 22) {
         // maxZones can be invalid
         font.pos += 8;
         var maxZones = font.getUint16();
         if (maxZones > 2) { // reset to 2 if font has invalid maxZones
           tables.maxp.data[14] = 0;
@@ -21829,18 +21950,16 @@ var Font = (function FontClosure() {
       return stringToArray(ttf.file);
     },
 
     convert: function Font_convert(fontName, font, properties) {
       // The offsets object holds at the same time a representation of where
       // to write the table entry information about a table and another offset
       // representing the offset where to draw the actual data of a particular
       // table
-      var REQ_TABLES_CNT = 9;
-
       var otf = {
         file: '',
         virtualOffset: 9 * (4 * 4)
       };
 
       createOpenTypeHeader('\x4F\x54\x54\x4F', otf, 9);
 
       // TODO: Check the charstring widths to determine this.
@@ -22681,40 +22800,72 @@ var Type1Parser = (function Type1ParserC
   /*
    * Decrypt a Sequence of Ciphertext Bytes to Produce the Original Sequence
    * of Plaintext Bytes. The function took a key as a parameter which can be
    * for decrypting the eexec block of for decoding charStrings.
    */
   var EEXEC_ENCRYPT_KEY = 55665;
   var CHAR_STRS_ENCRYPT_KEY = 4330;
 
-  function decrypt(stream, key, discardNumber) {
-    var r = key, c1 = 52845, c2 = 22719;
-    var decryptedString = [];
-
-    var value = '';
-    var count = stream.length;
+  function isHexDigit(code) {
+    return code >= 48 && code <= 57 || // '0'-'9'
+           code >= 65 && code <= 70 || // 'A'-'F'
+           code >= 97 && code <= 102;  // 'a'-'f'
+  }
+
+  function decrypt(data, key, discardNumber) {
+    var r = key | 0, c1 = 52845, c2 = 22719;
+    var count = data.length;
+    var decrypted = new Uint8Array(count);
     for (var i = 0; i < count; i++) {
-      value = stream[i];
-      decryptedString[i] = value ^ (r >> 8);
+      var value = data[i];
+      decrypted[i] = value ^ (r >> 8);
       r = ((value + r) * c1 + c2) & ((1 << 16) - 1);
     }
-    return decryptedString.slice(discardNumber);
+    return Array.prototype.slice.call(decrypted, discardNumber);
+  }
+
+  function decryptAscii(data, key, discardNumber) {
+    var r = key | 0, c1 = 52845, c2 = 22719;
+    var count = data.length, maybeLength = count >>> 1;
+    var decrypted = new Uint8Array(maybeLength);
+    var i, j;
+    for (i = 0, j = 0; i < count; i++) {
+      var digit1 = data[i];
+      if (!isHexDigit(digit1)) {
+        continue;
+      }
+      i++;
+      var digit2;
+      while (i < count && !isHexDigit(digit2 = data[i])) {
+        i++;
+      }
+      if (i < count) {
+        var value = parseInt(String.fromCharCode(digit1, digit2), 16);
+        decrypted[j++] = value ^ (r >> 8);
+        r = ((value + r) * c1 + c2) & ((1 << 16) - 1);
+      }
+    }
+    return Array.prototype.slice.call(decrypted, discardNumber, j);
   }
 
   function isSpecial(c) {
     return c === 0x2F || // '/'
            c === 0x5B || c === 0x5D || // '[', ']'
            c === 0x7B || c === 0x7D || // '{', '}'
            c === 0x28 || c === 0x29; // '(', ')'
   }
 
   function Type1Parser(stream, encrypted) {
     if (encrypted) {
-      stream = new Stream(decrypt(stream.getBytes(), EEXEC_ENCRYPT_KEY, 4));
+      var data = stream.getBytes();
+      var isBinary = !(isHexDigit(data[0]) && isHexDigit(data[1]) &&
+                       isHexDigit(data[2]) && isHexDigit(data[3]));
+      stream = new Stream(isBinary ? decrypt(data, EEXEC_ENCRYPT_KEY, 4) :
+                          decryptAscii(data, EEXEC_ENCRYPT_KEY, 4));
     }
     this.stream = stream;
     this.nextChar();
   }
 
   Type1Parser.prototype = {
     readNumberArray: function Type1Parser_readNumberArray() {
       this.getToken(); // read '[' or '{' (arrays can start with either)
@@ -23611,17 +23762,16 @@ var CFFParser = (function CFFParserClosu
       }
       return entries;
     },
     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;
       var i, ii;
 
       if (count !== 0) {
         var offsetSize = bytes[pos++];
         // add 1 for offset to determine size of last object
         var startPos = pos + ((count + 1) * offsetSize) - 1;
 
@@ -23654,17 +23804,17 @@ var CFFParser = (function CFFParserClosu
           var c = name[j];
           if (j === 0 && c === 0) {
             data[j] = c;
             continue;
           }
           if ((c < 33 || c > 126) || c === 91 /* [ */ || c === 93 /* ] */ ||
               c === 40 /* ( */ || c === 41 /* ) */ || c === 123 /* { */ ||
               c === 125 /* } */ || c === 60 /* < */ || c === 62 /* > */ ||
-              c === 47 /* / */ || c === 37 /* % */) {
+              c === 47 /* / */ || c === 37 /* % */ || c === 35 /* # */) {
             data[j] = 95;
             continue;
           }
           data[j] = c;
         }
         names.push(bytesToString(data));
       }
       return names;
@@ -23674,18 +23824,16 @@ var CFFParser = (function CFFParserClosu
       for (var i = 0, ii = index.count; i < ii; ++i) {
         var data = index.get(i);
         strings.add(bytesToString(data));
       }
       return 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);
       }
       return cffDict;
     },
@@ -24243,39 +24391,26 @@ var CFFPrivateDict = (function CFFPrivat
   return CFFPrivateDict;
 })();
 
 var CFFCharsetPredefinedTypes = {
   ISO_ADOBE: 0,
   EXPERT: 1,
   EXPERT_SUBSET: 2
 };
-var CFFCharsetEmbeddedTypes = {
-  FORMAT0: 0,
-  FORMAT1: 1,
-  FORMAT2: 2
-};
 var CFFCharset = (function CFFCharsetClosure() {
   function CFFCharset(predefined, format, charset, raw) {
     this.predefined = predefined;
     this.format = format;
     this.charset = charset;
     this.raw = raw;
   }
   return CFFCharset;
 })();
 
-var CFFEncodingPredefinedTypes = {
-  STANDARD: 0,
-  EXPERT: 1
-};
-var CFFCharsetEmbeddedTypes = {
-  FORMAT0: 0,
-  FORMAT1: 1
-};
 var CFFEncoding = (function CFFEncodingClosure() {
   function CFFEncoding(predefined, format, encoding, raw) {
     this.predefined = predefined;
     this.format = format;
     this.encoding = encoding;
     this.raw = raw;
   }
   return CFFEncoding;
@@ -24729,17 +24864,16 @@ var CFFCompiler = (function CFFCompilerC
                     (relativeOffset >> 8) & 0xFF,
                      relativeOffset & 0xFF);
         }
 
         if (objects[i]) {
           relativeOffset += objects[i].length;
         }
       }
-      var offset = data.length;
 
       for (i = 0; i < count; i++) {
         // Notify the tracker where the object will be offset in the data.
         if (trackers[i]) {
           trackers[i].offset(data.length);
         }
         for (var j = 0, jj = objects[i].length; j < jj; j++) {
           data.push(objects[i][j]);
@@ -24892,20 +25026,16 @@ var FontRendererFactory = (function Font
     }
     function quadraticCurveTo(xa, ya, x, y) {
       js.push('c.quadraticCurveTo(' + xa + ',' + ya + ',' +
                                    x + ',' + y + ');');
     }
 
     var i = 0;
     var numberOfContours = ((code[i] << 24) | (code[i + 1] << 16)) >> 16;
-    var xMin = ((code[i + 2] << 24) | (code[i + 3] << 16)) >> 16;
-    var yMin = ((code[i + 4] << 24) | (code[i + 5] << 16)) >> 16;
-    var xMax = ((code[i + 6] << 24) | (code[i + 7] << 16)) >> 16;
-    var yMax = ((code[i + 8] << 24) | (code[i + 9] << 16)) >> 16;
     var flags;
     var x = 0, y = 0;
     i += 10;
     if (numberOfContours < 0) {
       // composite glyph
       do {
         flags = (code[i] << 8) | code[i + 1];
         var glyphIndex = (code[i + 2] << 8) | code[i + 3];
@@ -29710,17 +29840,16 @@ var PDFImage = (function PDFImageClosure
     return (value < 0 ? 0 : (value > max ? max : value));
   }
   function PDFImage(xref, res, image, inline, smask, mask, isMask) {
     this.image = image;
     var dict = image.dict;
     if (dict.has('Filter')) {
       var filter = dict.get('Filter').name;
       if (filter === 'JPXDecode') {
-        info('get image params from JPX stream');
         var jpxImage = new JpxImage();
         jpxImage.parseImageProperties(image.stream);
         image.stream.reset();
         image.bitsPerComponent = jpxImage.bitsPerComponent;
         image.numComps = jpxImage.componentsCount;
       } else if (filter === 'JBIG2Decode') {
         image.bitsPerComponent = 1;
         image.numComps = 1;
@@ -29759,18 +29888,20 @@ var PDFImage = (function PDFImageClosure
         info('JPX images (which do not require color spaces)');
         switch (image.numComps) {
           case 1:
             colorSpace = Name.get('DeviceGray');
             break;
           case 3:
             colorSpace = Name.get('DeviceRGB');
             break;
+          case 4:
+            colorSpace = Name.get('DeviceCMYK');
+            break;
           default:
-            // TODO: Find out how four color channels are handled. CMYK? Alpha?
             error('JPX images with ' + this.numComps +
                   ' color components not supported.');
         }
       }
       this.colorSpace = ColorSpace.parse(colorSpace, xref, res);
       this.numComps = this.colorSpace.numComps;
     }
 
@@ -29916,17 +30047,16 @@ var PDFImage = (function PDFImageClosure
     },
     get drawHeight() {
       return Math.max(this.height,
                       this.smask && this.smask.height || 0,
                       this.mask && this.mask.height || 0);
     },
     decodeBuffer: function PDFImage_decodeBuffer(buffer) {
       var bpc = this.bpc;
-      var decodeMap = this.decode;
       var numComps = this.numComps;
 
       var decodeAddends = this.decodeAddends;
       var decodeCoefficients = this.decodeCoefficients;
       var max = (1 << bpc) - 1;
       var i, ii;
 
       if (bpc === 1) {
@@ -33677,17 +33807,16 @@ var Lexer = (function LexerClosure() {
           }
         } else if (ch === 0x2D) { // '-'
           // ignore minus signs in the middle of numbers to match
           // Adobe's behavior
           warn('Badly formated number');
         } else if (ch === 0x45 || ch === 0x65) { // 'E', 'e'
           // 'E' can be either a scientific notation or the beginning of a new
           // operator
-          var hasE = true;
           ch = this.peekChar();
           if (ch === 0x2B || ch === 0x2D) { // '+', '-'
             powerValueSign = (ch === 0x2D) ? -1 : 1;
             this.nextChar(); // Consume the sign character
           } else if (ch < 0x30 || ch > 0x39) { // '0' - '9'
             // The 'E' must be the beginning of a new operator
             break;
           }
@@ -33955,17 +34084,16 @@ var Lexer = (function LexerClosure() {
         return false;
       }
       if (str == 'null') {
         return null;
       }
       return Cmd.get(str);
     },
     skipToNextLine: function Lexer_skipToNextLine() {
-      var stream = this.stream;
       var ch = this.currentChar;
       while (ch >= 0) {
         if (ch === 0x0D) { // CR
           ch = this.nextChar();
           if (ch === 0x0A) { // LF
             this.nextChar();
           }
           break;
@@ -34175,17 +34303,16 @@ var PostScriptLexer = (function PostScri
     this.stream = stream;
     this.nextChar();
   }
   PostScriptLexer.prototype = {
     nextChar: function PostScriptLexer_nextChar() {
       return (this.currentChar = this.stream.getByte());
     },
     getToken: function PostScriptLexer_getToken() {
-      var s = '';
       var comment = false;
       var ch = this.currentChar;
 
       // skip comments
       while (true) {
         if (ch < 0) {
           return EOF;
         }
@@ -34273,17 +34400,17 @@ var Stream = (function StreamClosure() {
       }
       return this.bytes[this.pos++];
     },
     getUint16: function Stream_getUint16() {
       var b0 = this.getByte();
       var b1 = this.getByte();
       return (b0 << 8) + b1;
     },
-    getUint32: function Stream_getUint32() {
+    getInt32: function Stream_getInt32() {
       var b0 = this.getByte();
       var b1 = this.getByte();
       var b2 = this.getByte();
       var b3 = this.getByte();
       return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;
     },
     // returns subarray of original buffer
     // should only be read
@@ -34391,17 +34518,17 @@ var DecodeStream = (function DecodeStrea
       }
       return this.buffer[this.pos++];
     },
     getUint16: function DecodeStream_getUint16() {
       var b0 = this.getByte();
       var b1 = this.getByte();
       return (b0 << 8) + b1;
     },
-    getUint32: function DecodeStream_getUint32() {
+    getInt32: function DecodeStream_getInt32() {
       var b0 = this.getByte();
       var b1 = this.getByte();
       var b2 = this.getByte();
       var b3 = this.getByte();
       return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;
     },
     getBytes: function DecodeStream_getBytes(length) {
       var end, pos = this.pos;
@@ -35169,88 +35296,45 @@ var JpxStream = (function JpxStreamClosu
     }
 
     var jpxImage = new JpxImage();
     jpxImage.parse(this.bytes);
 
     var width = jpxImage.width;
     var height = jpxImage.height;
     var componentsCount = jpxImage.componentsCount;
-    if (componentsCount != 1 && componentsCount != 3 && componentsCount != 4) {
-      error('JPX with ' + componentsCount + ' components is not supported');
-    }
-
-    var data = new Uint8Array(width * height * componentsCount);
-
-    for (var k = 0, kk = jpxImage.tiles.length; k < kk; k++) {
-      var tileCompoments = jpxImage.tiles[k];
-      var tileWidth = tileCompoments[0].width;
-      var tileHeight = tileCompoments[0].height;
-      var tileLeft = tileCompoments[0].left;
-      var tileTop = tileCompoments[0].top;
-
-      var dataPosition, sourcePosition, data0, data1, data2, data3, rowFeed;
-      var i, j;
-      switch (componentsCount) {
-        case 1:
-          data0 = tileCompoments[0].items;
-
-          dataPosition = width * tileTop + tileLeft;
-          rowFeed = width - tileWidth;
-          sourcePosition = 0;
-          for (j = 0; j < tileHeight; j++) {
-            for (i = 0; i < tileWidth; i++) {
-              data[dataPosition++] = data0[sourcePosition++];
-            }
-            dataPosition += rowFeed;
-          }
-          break;
-        case 3:
-          data0 = tileCompoments[0].items;
-          data1 = tileCompoments[1].items;
-          data2 = tileCompoments[2].items;
-
-          dataPosition = (width * tileTop + tileLeft) * 3;
-          rowFeed = (width - tileWidth) * 3;
-          sourcePosition = 0;
-          for (j = 0; j < tileHeight; j++) {
-            for (i = 0; i < tileWidth; i++) {
-              data[dataPosition++] = data0[sourcePosition];
-              data[dataPosition++] = data1[sourcePosition];
-              data[dataPosition++] = data2[sourcePosition];
-              sourcePosition++;
-            }
-            dataPosition += rowFeed;
-          }
-          break;
-        case 4:
-          data0 = tileCompoments[0].items;
-          data1 = tileCompoments[1].items;
-          data2 = tileCompoments[2].items;
-          data3 = tileCompoments[3].items;
-
-          dataPosition = (width * tileTop + tileLeft) * 4;
-          rowFeed = (width - tileWidth) * 4;
-          sourcePosition = 0;
-          for (j = 0; j < tileHeight; j++) {
-            for (i = 0; i < tileWidth; i++) {
-              data[dataPosition++] = data0[sourcePosition];
-              data[dataPosition++] = data1[sourcePosition];
-              data[dataPosition++] = data2[sourcePosition];
-              data[dataPosition++] = data3[sourcePosition];
-              sourcePosition++;
-            }
-            dataPosition += rowFeed;
-          }
-          break;
-      }
-    }
-
-    this.buffer = data;
-    this.bufferLength = data.length;
+    var tileCount = jpxImage.tiles.length;
+    if (tileCount === 1) {
+      this.buffer = jpxImage.tiles[0].items;
+    } else {
+      var data = new Uint8Array(width * height * componentsCount);
+
+      for (var k = 0; k < tileCount; k++) {
+        var tileComponents = jpxImage.tiles[k];
+        var tileWidth = tileComponents.width;
+        var tileHeight = tileComponents.height;
+        var tileLeft = tileComponents.left;
+        var tileTop = tileComponents.top;
+
+        var src = tileComponents.items;
+        var srcPosition = 0;
+        var dataPosition = (width * tileTop + tileLeft) * componentsCount;
+        var imgRowSize = width * componentsCount;
+        var tileRowSize = tileWidth * componentsCount;
+
+        for (var j = 0; j < tileHeight; j++) {
+          var rowBytes = src.subarray(srcPosition, srcPosition + tileRowSize);
+          data.set(rowBytes, dataPosition);
+          srcPosition += tileRowSize;
+          dataPosition += imgRowSize;
+        }
+      }
+      this.buffer = data;
+    }
+    this.bufferLength = this.buffer.length;
     this.eof = true;
   };
 
   return JpxStream;
 })();
 
 /**
  * For JBIG2's we use a library to decode these images and
@@ -36439,17 +36523,16 @@ var CCITTFaxStream = (function CCITTFaxS
     return EOF;
   };
 
   CCITTFaxStream.prototype.getWhiteCode =
       function ccittFaxStreamGetWhiteCode() {
 
     var code = 0;
     var p;
-    var n;
     if (this.eoblock) {
       code = this.lookBits(12);
       if (code == EOF) {
         return 1;
       }
 
       if ((code >> 5) === 0) {
         p = whiteTable1[code];
@@ -36965,16 +37048,24 @@ var WorkerMessageHandler = PDFJS.WorkerM
     handler.on('GetDestinations',
       function wphSetupGetDestinations(data, deferred) {
         pdfManager.ensureCatalog('destinations').then(function(destinations) {
           deferred.resolve(destinations);
         });
       }
     );
 
+    handler.on('GetAttachments',
+      function wphSetupGetAttachments(data, deferred) {
+        pdfManager.ensureCatalog('attachments').then(function(attachments) {
+          deferred.resolve(attachments);
+        }, deferred.reject);
+      }
+    );
+
     handler.on('GetData', function wphSetupGetData(data, deferred) {
       pdfManager.requestLoadedStream();
       pdfManager.onLoadedStream().then(function(stream) {
         deferred.resolve(stream.bytes);
       });
     });
 
     handler.on('UpdatePassword', function wphSetupUpdatePassword(data) {
@@ -37309,39 +37400,35 @@ var JpxImage = (function JpxImageClosure
         this.parse(data);
         if (this.onload) {
           this.onload();
         }
       }).bind(this);
       xhr.send(null);
     },
     parse: function JpxImage_parse(data) {
-      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 head = readUint(data, 0, 2);
+
+      var head = readUint16(data, 0);
       // No box header, immediate start of codestream (SOC)
       if (head === 0xFF4F) {
         this.parseCodestream(data, 0, data.length);
         return;
       }
 
       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 = readUint32(data, position);
+        var tbox = readUint32(data, position + 4);
         position += headerSize;
-        if (lbox == 1) {
-          lbox = readUint(data, position, 8);
+        if (lbox === 1) {
+          // XLBox: read UInt64 according to spec.
+          // JavaScript's int precision of 53 bit should be sufficient here.
+          lbox = readUint32(data, position) * 4294967296 +
+                 readUint32(data, position + 4);
           position += 8;
           headerSize += 8;
         }
         if (lbox === 0) {
           lbox = length - position + headerSize;
         }
         if (lbox < headerSize) {
           error('JPX error: Invalid box field size');
@@ -37372,26 +37459,26 @@ var JpxImage = (function JpxImageClosure
         var newByte = stream.getByte();
         while (newByte >= 0) {
           var oldByte = newByte;
           newByte = stream.getByte();
           var code = (oldByte << 8) | newByte;
           // Image and tile size (SIZ)
           if (code == 0xFF51) {
             stream.skip(4);
-            var Xsiz = stream.getUint32(); // Byte 4
-            var Ysiz = stream.getUint32(); // Byte 8
-            var XOsiz = stream.getUint32(); // Byte 12
-            var YOsiz = stream.getUint32(); // Byte 16
+            var Xsiz = stream.getInt32() >>> 0; // Byte 4
+            var Ysiz = stream.getInt32() >>> 0; // Byte 8
+            var XOsiz = stream.getInt32() >>> 0; // Byte 12
+            var YOsiz = stream.getInt32() >>> 0; // Byte 16
             stream.skip(16);
             var Csiz = stream.getUint16(); // Byte 36
             this.width = Xsiz - XOsiz;
             this.height = Ysiz - YOsiz;
             this.componentsCount = Csiz;
-            // Results are always returned as UInt8Arrays
+            // Results are always returned as Uint8Arrays
             this.bitsPerComponent = 8;
             return;
           }
         }
         throw 'No size marker found in JPX stream';
       } catch (e) {
         if (this.failOnCorruptedImage) {
           error('JPX error: ' + e);
@@ -37399,17 +37486,17 @@ var JpxImage = (function JpxImageClosure
           warn('JPX error: ' + e + '. Trying to recover');
         }
       }
     },
     parseCodestream: function JpxImage_parseCodestream(data, start, end) {
       var context = {};
       try {
         var position = start;
-        while (position < end) {
+        while (position + 1 < end) {
           var code = readUint16(data, position);
           position += 2;
 
           var length = 0, j, sqcd, spqcds, spqcdSize, scalarExpounded, tile;
           switch (code) {
             case 0xFF4F: // Start of codestream (SOC)
               context.mainHeader = true;
               break;
@@ -37545,17 +37632,16 @@ var JpxImage = (function JpxImageClosure
             case 0xFF52: // Coding style default (COD)
               length = readUint16(data, position);
               var cod = {};
               j = position + 2;
               var scod = data[j++];
               cod.entropyCoderWithCustomPrecincts = !!(scod & 1);
               cod.sopMarkerUsed = !!(scod & 2);
               cod.ephMarkerUsed = !!(scod & 4);
-              var codingStyle = {};
               cod.progressionOrder = data[j++];
               cod.layersCount = readUint16(data, j);
               j += 2;
               cod.multipleComponentTransform = data[j++];
 
               cod.decompositionLevelsCount = data[j++];
               cod.xcb = (data[j++] & 0xF) + 2;
               cod.ycb = (data[j++] & 0xF) + 2;
@@ -37644,31 +37730,16 @@ var JpxImage = (function JpxImageClosure
         }
       }
       this.tiles = transformComponents(context);
       this.width = context.SIZ.Xsiz - context.SIZ.XOsiz;
       this.height = context.SIZ.Ysiz - context.SIZ.YOsiz;
       this.componentsCount = context.SIZ.Csiz;
     }
   };
-  function readUint32(data, offset) {
-    return (data[offset] << 24) | (data[offset + 1] << 16) |
-            (data[offset + 2] << 8) | data[offset + 3];
-  }
-  function readUint16(data, offset) {
-    return (data[offset] << 8) | data[offset + 1];
-  }
-  function log2(x) {
-    var n = 1, i = 0;
-    while (x > n) {
-      n <<= 1;
-      i++;
-    }
-    return i;
-  }
   function calculateComponentDimensions(component, siz) {
     // Section B.2 Component mapping
     component.x0 = Math.ceil(siz.XOsiz / component.XRsiz);
     component.x1 = Math.ceil(siz.Xsiz / component.XRsiz);
     component.y0 = Math.ceil(siz.YOsiz / component.YRsiz);
     component.y1 = Math.ceil(siz.Ysiz / component.YRsiz);
     component.width = component.x1 - component.x0;
     component.height = component.y1 - component.y0;
@@ -37692,17 +37763,16 @@ var JpxImage = (function JpxImageClosure
         tiles.push(tile);
       }
     }
     context.tiles = tiles;
 
     var componentsCount = siz.Csiz;
     for (var i = 0, ii = componentsCount; i < ii; i++) {
       var component = components[i];
-      var tileComponents = [];
       for (var j = 0, jj = tiles.length; j < jj; j++) {
         var tileComponent = {};
         tile = tiles[j];
         tileComponent.tcx0 = Math.ceil(tile.tx0 / component.XRsiz);
         tileComponent.tcy0 = Math.ceil(tile.ty0 / component.YRsiz);
         tileComponent.tcx1 = Math.ceil(tile.tx1 / component.XRsiz);
         tileComponent.tcy1 = Math.ceil(tile.ty1 / component.YRsiz);
         tileComponent.width = tileComponent.tcx1 - tileComponent.tcx0;
@@ -37761,17 +37831,17 @@ var JpxImage = (function JpxImageClosure
     var codeblockHeight = 1 << ycb_;
     var cbx0 = subband.tbx0 >> xcb_;
     var cby0 = subband.tby0 >> ycb_;
     var cbx1 = (subband.tbx1 + codeblockWidth - 1) >> xcb_;
     var cby1 = (subband.tby1 + codeblockHeight - 1) >> ycb_;
     var precinctParameters = subband.resolution.precinctParameters;
     var codeblocks = [];
     var precincts = [];
-    var i, ii, j, codeblock, precinctNumber;
+    var i, j, codeblock, precinctNumber;
     for (j = cby0; j < cby1; j++) {
       for (i = cbx0; i < cbx1; i++) {
         codeblock = {
           cbx: i,
           cby: j,
           tbx0: codeblockWidth * i,
           tby0: codeblockHeight * j,
           tbx1: codeblockWidth * (i + 1),
@@ -37786,18 +37856,16 @@ var JpxImage = (function JpxImageClosure
                  precinctParameters.precinctHeight);
         precinctNumber = pj + pi * precinctParameters.numprecinctswide;
         codeblock.tbx0_ = Math.max(subband.tbx0, codeblock.tbx0);
         codeblock.tby0_ = Math.max(subband.tby0, codeblock.tby0);
         codeblock.tbx1_ = Math.min(subband.tbx1, codeblock.tbx1);
         codeblock.tby1_ = Math.min(subband.tby1, codeblock.tby1);
         codeblock.precinctNumber = precinctNumber;
         codeblock.subbandType = subband.type;
-        var coefficientsLength = (codeblock.tbx1_ - codeblock.tbx0_) *
-                                 (codeblock.tby1_ - codeblock.tby0_);
         codeblock.Lblock = 3;
         codeblocks.push(codeblock);
         // building precinct for the sub-band
         var precinct = precincts[precinctNumber];
         if (precinct !== undefined) {
           if (i < precinct.cbxMin) {
             precinct.cbxMin = i;
           } else if (i > precinct.cbxMax) {
@@ -38006,17 +38074,16 @@ var JpxImage = (function JpxImageClosure
           resolution.subbands = resolutionSubbands;
         }
       }
       component.resolutions = resolutions;
       component.subbands = subbands;
     }
     // Generate the packets sequence
     var progressionOrder = tile.codingStyleDefaultParameters.progressionOrder;
-    var packetsIterator;
     switch (progressionOrder) {
       case 0:
         tile.packetsIterator =
           new LayerResolutionComponentPositionIterator(context);
         break;
       case 1:
         tile.packetsIterator =
           new ResolutionLayerComponentPositionIterator(context);
@@ -38050,34 +38117,32 @@ var JpxImage = (function JpxImageClosure
     function alignToByte() {
       bufferSize = 0;
       if (skipNextBit) {
         position++;
         skipNextBit = false;
       }
     }
     function readCodingpasses() {
-      var value = readBits(1);
-      if (value === 0) {
+      if (readBits(1) === 0) {
         return 1;
       }
-      value = (value << 1) | readBits(1);
-      if (value == 0x02) {
+      if (readBits(1) === 0) {
         return 2;
       }
-      value = (value << 2) | readBits(2);
-      if (value <= 0x0E) {
-        return (value & 0x03) + 3;
-      }
-      value = (value << 5) | readBits(5);
-      if (value <= 0x1FE) {
-        return (value & 0x1F) + 6;
-      }
-      value = (value << 7) | readBits(7);
-      return (value & 0x7F) + 37;
+      var value = readBits(2);
+      if (value < 3) {
+        return value + 3;
+      }
+      value = readBits(5);
+      if (value < 31) {
+        return value + 6;
+      }
+      value = readBits(7);
+      return value + 37;
     }
     var tileIndex = context.currentTile.index;
     var tile = context.tiles[tileIndex];
     var packetsIterator = tile.packetsIterator;
     while (position < dataLength) {
       var packet = packetsIterator.nextPacket();
       if (!readBits(1)) {
         alignToByte();
@@ -38173,55 +38238,62 @@ var JpxImage = (function JpxImageClosure
           end: offset + position + packetItem.dataLength,
           codingpasses: packetItem.codingpasses
         });
         position += packetItem.dataLength;
       }
     }
     return position;
   }
-  function copyCoefficients(coefficients, x0, y0, width, height,
-                            delta, mb, codeblocks, reversible,
-                            segmentationSymbolUsed) {
+  function copyCoefficients(coefficients, levelWidth, levelHeight, subband,
+                            delta, mb, reversible, segmentationSymbolUsed) {
+    var x0 = subband.tbx0;
+    var y0 = subband.tby0;
+    var width = subband.tbx1 - subband.tbx0;
+    var codeblocks = subband.codeblocks;
+    var right = subband.type.charAt(0) === 'H' ? 1 : 0;
+    var bottom = subband.type.charAt(1) === 'H' ? levelWidth : 0;
+
     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) {
         continue;
       }
       if (!('data' in codeblock)) {
         continue;
       }
 
       var bitModel, currentCodingpassType;
       bitModel = new BitModel(blockWidth, blockHeight, codeblock.subbandType,
-                              codeblock.zeroBitPlanes);
+                              codeblock.zeroBitPlanes, mb);
       currentCodingpassType = 2; // first bit plane starts from cleanup
 
       // collect data
       var data = codeblock.data, totalLength = 0, codingpasses = 0;
-      var q, qq, dataItem;
-      for (q = 0, qq = data.length; q < qq; q++) {
-        dataItem = data[q];
+      var j, jj, dataItem;
+      for (j = 0, jj = data.length; j < jj; j++) {
+        dataItem = data[j];
         totalLength += dataItem.end - dataItem.start;
         codingpasses += dataItem.codingpasses;
       }
-      var encodedData = new Uint8Array(totalLength), k = 0;
-      for (q = 0, qq = data.length; q < qq; q++) {
-        dataItem = data[q];
+      var encodedData = new Uint8Array(totalLength);
+      var position = 0;
+      for (j = 0, jj = data.length; j < jj; j++) {
+        dataItem = data[j];
         var chunk = dataItem.data.subarray(dataItem.start, dataItem.end);
-        encodedData.set(chunk, k);
-        k += chunk.length;
+        encodedData.set(chunk, position);
+        position += chunk.length;
       }
       // decoding the item
       var decoder = new ArithmeticDecoder(encodedData, 0, totalLength);
       bitModel.setDecoder(decoder);
 
-      for (q = 0; q < codingpasses; q++) {
+      for (j = 0; j < codingpasses; j++) {
         switch (currentCodingpassType) {
           case 0:
             bitModel.runSignificancePropogationPass();
             break;
           case 1:
             bitModel.runMagnitudeRefinementPass();
             break;
           case 2:
@@ -38230,35 +38302,41 @@ var JpxImage = (function JpxImageClosure
               bitModel.checkSegmentationSymbol();
             }
             break;
         }
         currentCodingpassType = (currentCodingpassType + 1) % 3;
       }
 
       var offset = (codeblock.tbx0_ - x0) + (codeblock.tby0_ - y0) * width;
-      var n, nb, correction, position = 0;
-      var irreversible = !reversible;
       var sign = bitModel.coefficentsSign;
       var magnitude = bitModel.coefficentsMagnitude;
       var bitsDecoded = bitModel.bitsDecoded;
       var magnitudeCorrection = reversible ? 0 : 0.5;
-      for (var j = 0; j < blockHeight; j++) {
+      var k, n, nb;
+      position = 0;
+      // Do the interleaving of Section F.3.3 here, so we do not need
+      // to copy later. LL level is not interleaved, just copied.
+      var interleave = (subband.type !== 'LL');
+      for (j = 0; j < blockHeight; j++) {
+        var row = (offset / width) | 0; // row in the non-interleaved subband
+        var levelOffset = 2 * row * (levelWidth - width) + right + bottom;
         for (k = 0; k < blockWidth; k++) {
           n = magnitude[position];
           if (n !== 0) {
             n = (n + magnitudeCorrection) * delta;
             if (sign[position] !== 0) {
               n = -n;
             }
             nb = bitsDecoded[position];
-            if (irreversible || mb > nb) {
-              coefficients[offset] = n * (1 << (mb - nb));
+            var pos = interleave ? (levelOffset + (offset << 1)) : offset;
+            if (reversible && (nb >= mb)) {
+              coefficients[pos] = n;
             } else {
-              coefficients[offset] = n;
+              coefficients[pos] = n * (1 << (mb - nb));
             }
           }
           offset++;
           position++;
         }
         offset += width - blockWidth;
       }
     }
@@ -38279,50 +38357,54 @@ var JpxImage = (function JpxImageClosure
     var transform = (reversible ? new ReversibleTransform() :
                                   new IrreversibleTransform());
 
     var subbandCoefficients = [];
     var b = 0;
     for (var i = 0; i <= decompositionLevelsCount; i++) {
       var resolution = component.resolutions[i];
 
+      var width = resolution.trx1 - resolution.trx0;
+      var height = resolution.try1 - resolution.try0;
+      // Allocate space for the whole sublevel.
+      var coefficients = new Float32Array(width * height);
+
       for (var j = 0, jj = resolution.subbands.length; j < jj; j++) {
         var mu, epsilon;
         if (!scalarExpounded) {
           // formula E-5
           mu = spqcds[0].mu;
           epsilon = spqcds[0].epsilon + (i > 0 ? 1 - i : 0);
         } else {
           mu = spqcds[b].mu;
           epsilon = spqcds[b].epsilon;
+          b++;
         }
 
         var subband = resolution.subbands[j];
-        var width = subband.tbx1 - subband.tbx0;
-        var height = subband.tby1 - subband.tby0;
         var gainLog2 = SubbandsGainLog2[subband.type];
 
         // calulate quantization coefficient (Section E.1.1.1)
         var delta = (reversible ? 1 :
           Math.pow(2, precision + gainLog2 - epsilon) * (1 + mu / 2048));
         var mb = (guardBits + epsilon - 1);
 
-        var coefficients = new Float32Array(width * height);
-        copyCoefficients(coefficients, subband.tbx0, subband.tby0,
-          width, height, delta, mb, subband.codeblocks, reversible,
-          segmentationSymbolUsed);
-
-        subbandCoefficients.push({
-          width: width,
-          height: height,
-          items: coefficients
-        });
-
-        b++;
-      }
+        // In the first resolution level, copyCoefficients will fill the
+        // whole array with coefficients. In the succeding passes,
+        // copyCoefficients will consecutively fill in the values that belong
+        // to the interleaved positions of the HL, LH, and HH coefficients.
+        // The LL coefficients will then be interleaved in Transform.iterate().
+        copyCoefficients(coefficients, width, height, subband, delta, mb,
+                         reversible, segmentationSymbolUsed);
+      }
+      subbandCoefficients.push({
+        width: width,
+        height: height,
+        items: coefficients
+      });
     }
 
     var result = transform.calculate(subbandCoefficients,
                                      component.tcx0, component.tcy0);
     return {
       left: component.tcx0,
       top: component.tcy0,
       width: result.width,
@@ -38332,79 +38414,110 @@ var JpxImage = (function JpxImageClosure
   }
   function transformComponents(context) {
     var siz = context.SIZ;
     var components = context.components;
     var componentsCount = siz.Csiz;
     var resultImages = [];
     for (var i = 0, ii = context.tiles.length; i < ii; i++) {
       var tile = context.tiles[i];
-      var result = [];
+      var transformedTiles = [];
       var c;
       for (c = 0; c < componentsCount; c++) {
-        var image = transformTile(context, tile, c);
-        result.push(image);
-      }
+        transformedTiles[c] = transformTile(context, tile, c);
+      }
+      var tile0 = transformedTiles[0];
+      var out = new Uint8Array(tile0.items.length * componentsCount);
+      var result = {
+        left: tile0.left,
+        top: tile0.top,
+        width: tile0.width,
+        height: tile0.height,
+        items: out
+      };
 
       // Section G.2.2 Inverse multi component transform
-      var y0items, y1items, y2items, j, jj, y0, y1, y2;
-      var component, offset, tileImage, items;
+      var shift, offset, max, min;
+      var pos = 0, j, jj, y0, y1, y2, r, g, b, k, val;
       if (tile.codingStyleDefaultParameters.multipleComponentTransform) {
+        var fourComponents = componentsCount === 4;
+        var y0items = transformedTiles[0].items;
+        var y1items = transformedTiles[1].items;
+        var y2items = transformedTiles[2].items;
+        var y3items = fourComponents ? transformedTiles[3].items : null;
+
+        // HACK: The multiple component transform formulas below assume that
+        // all components have the same precision. With this in mind, we
+        // compute shift and offset only once.
+        shift = components[0].precision - 8;
+        offset = (128 << shift) + 0.5;
+        max = (127.5 * (1 << shift));
+        min = -max;
+
         var component0 = tile.components[0];
         if (!component0.codingStyleParameters.reversibleTransformation) {
           // inverse irreversible multiple component transform
-          y0items = result[0].items;
-          y1items = result[1].items;
-          y2items = result[2].items;
           for (j = 0, jj = y0items.length; j < jj; ++j) {
-            y0 = y0items[j] + 0.5; y1 = y1items[j]; y2 = y2items[j];
-            y0items[j] = y0 + 1.402 * y2;
-            y1items[j] = y0 - 0.34413 * y1 - 0.71414 * y2;
-            y2items[j] = y0 + 1.772 * y1;
+            y0 = y0items[j];
+            y1 = y1items[j];
+            y2 = y2items[j];
+            r = y0 + 1.402 * y2;
+            g = y0 - 0.34413 * y1 - 0.71414 * y2;
+            b = y0 + 1.772 * y1;
+            out[pos++] = r <= min ? 0 : r >= max ? 255 : (r + offset) >> shift;
+            out[pos++] = g <= min ? 0 : g >= max ? 255 : (g + offset) >> shift;
+            out[pos++] = b <= min ? 0 : b >= max ? 255 : (b + offset) >> shift;
+            if (fourComponents) {
+              k = y3items[j];
+              out[pos++] =
+                k <= min ? 0 : k >= max ? 255 : (k + offset) >> shift;
+            }
           }
         } else {
           // inverse reversible multiple component transform
-          y0items = result[0].items;
-          y1items = result[1].items;
-          y2items = result[2].items;
           for (j = 0, jj = y0items.length; j < jj; ++j) {
-            y0 = y0items[j]; y1 = y1items[j]; y2 = y2items[j];
-            var i1 = y0 - ((y2 + y1) >> 2);
-            y1items[j] = i1;
-            y0items[j] = y2 + i1;
-            y2items[j] = y1 + i1;
-          }
-        }
-      }
-
-      // To simplify things: shift and clamp output to 8 bit unsigned
-      for (c = 0; c < componentsCount; c++) {
-        component = components[c];
-        var shift = component.precision - 8;
-        tileImage = result[c];
-        items = tileImage.items;
-        var data = new Uint8Array(items.length);
-        var low = -(128 << shift);
-        var high = 127 << shift;
-        for (j = 0, jj = items.length; j < jj; j++) {
-          var val = items[j];
-          data[j] = val <= low ? 0 : val >= high ? 255 : (val >> shift) + 128;
-        }
-        result[c].items = data;
-      }
-
+            y0 = y0items[j];
+            y1 = y1items[j];
+            y2 = y2items[j];
+            g = y0 - ((y2 + y1) >> 2);
+            r = g + y2;
+            b = g + y1;
+            out[pos++] = r <= min ? 0 : r >= max ? 255 : (r + offset) >> shift;
+            out[pos++] = g <= min ? 0 : g >= max ? 255 : (g + offset) >> shift;
+            out[pos++] = b <= min ? 0 : b >= max ? 255 : (b + offset) >> shift;
+            if (fourComponents) {
+              k = y3items[j];
+              out[pos++] =
+                k <= min ? 0 : k >= max ? 255 : (k + offset) >> shift;
+            }
+          }
+        }
+      } else { // no multi-component transform
+        for (c = 0; c < componentsCount; c++) {
+          var items = transformedTiles[c].items;
+          shift = components[c].precision - 8;
+          offset = (128 << shift) + 0.5;
+          max = (127.5 * (1 << shift));
+          min = -max;
+          for (pos = c, j = 0, jj = items.length; j < jj; j++) {
+            val = items[j];
+            out[pos] = val <= min ? 0 :
+                       val >= max ? 255 : (val + offset) >> shift;
+            pos += componentsCount;
+          }
+        }
+      }
       resultImages.push(result);
     }
     return resultImages;
   }
   function initializeTile(context, tileIndex) {
     var siz = context.SIZ;
     var componentsCount = siz.Csiz;
     var tile = context.tiles[tileIndex];
-    var resultTiles = [];
     for (var c = 0; c < componentsCount; c++) {
       var component = tile.components[c];
       var qcdOrQcc = (c in context.currentTile.QCC ?
         context.currentTile.QCC[c] : context.currentTile.QCD);
       component.quantizationParameters = qcdOrQcc;
       var codOrCoc = (c in context.currentTile.COC ?
         context.currentTile.COC[c] : context.currentTile.COD);
       component.codingStyleParameters = codOrCoc;
@@ -38571,30 +38684,32 @@ var JpxImage = (function JpxImageClosure
       4, 0, 6, 7, 7, 0, 8, 8, 8, 0, 0, 0, 0, 0, 2, 3, 4, 0, 6, 7, 7, 0, 8, 8, 8
     ]);
     var HHContextLabel = new Uint8Array([
       0, 1, 2, 0, 1, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 0, 3, 4, 5, 0, 4, 5, 5, 0, 5,
       5, 5, 0, 0, 0, 0, 0, 6, 7, 7, 0, 7, 7, 7, 0, 7, 7, 7, 0, 0, 0, 0, 0, 8, 8,
       8, 0, 8, 8, 8, 0, 8, 8, 8, 0, 0, 0, 0, 0, 8, 8, 8, 0, 8, 8, 8, 0, 8, 8, 8
     ]);
 
-    function BitModel(width, height, subband, zeroBitPlanes) {
+    function BitModel(width, height, subband, zeroBitPlanes, mb) {
       this.width = width;
       this.height = height;
 
       this.contextLabelTable = (subband == 'HH' ? HHContextLabel :
         (subband == 'HL' ? HLContextLabel : LLAndLHContextsLabel));
 
       var coefficientCount = width * height;
 
       // coefficients outside the encoding region treated as insignificant
       // add border state cells for significanceState
       this.neighborsSignificance = new Uint8Array(coefficientCount);
       this.coefficentsSign = new Uint8Array(coefficientCount);
-      this.coefficentsMagnitude = new Uint32Array(coefficientCount);
+      this.coefficentsMagnitude = mb > 14 ? new Uint32Array(coefficientCount) :
+                                  mb > 6 ? new Uint16Array(coefficientCount) :
+                                  new Uint8Array(coefficientCount);
       this.processingFlags = new Uint8Array(coefficientCount);
 
       var bitsDecoded = new Uint8Array(coefficientCount);
       if (zeroBitPlanes !== 0) {
         for (var i = 0; i < coefficientCount; i++) {
           bitsDecoded[i] = zeroBitPlanes;
         }
       }
@@ -38657,17 +38772,16 @@ var JpxImage = (function JpxImageClosure
         neighborsSignificance[index] |= 0x80;
       },
       runSignificancePropogationPass:
         function BitModel_runSignificancePropogationPass() {
         var decoder = this.decoder;
         var width = this.width, height = this.height;
         var coefficentsMagnitude = this.coefficentsMagnitude;
         var coefficentsSign = this.coefficentsSign;
-        var contextLabels = this.contextLabels;
         var neighborsSignificance = this.neighborsSignificance;
         var processingFlags = this.processingFlags;
         var contexts = this.contexts;
         var labels = this.contextLabelTable;
         var bitsDecoded = this.bitsDecoded;
         var processedInverseMask = ~1;
         var processedMask = 1;
         var firstMagnitudeBitMask = 2;
@@ -38795,86 +38909,86 @@ var JpxImage = (function JpxImageClosure
             }
           }
         }
       },
       runCleanupPass: function BitModel_runCleanupPass() {
         var decoder = this.decoder;
         var width = this.width, height = this.height;
         var neighborsSignificance = this.neighborsSignificance;
-        var significanceState = this.significanceState;
         var coefficentsMagnitude = this.coefficentsMagnitude;
         var coefficentsSign = this.coefficentsSign;
         var contexts = this.contexts;
         var labels = this.contextLabelTable;
         var bitsDecoded = this.bitsDecoded;
         var processingFlags = this.processingFlags;
         var processedMask = 1;
         var firstMagnitudeBitMask = 2;
         var oneRowDown = width;
         var twoRowsDown = width * 2;
         var threeRowsDown = width * 3;
-        for (var i0 = 0; i0 < height; i0 += 4) {
+        var iNext;
+        for (var i0 = 0; i0 < height; i0 = iNext) {
+          iNext = Math.min(i0 + 4, height);
+          var indexBase = i0 * width;
+          var checkAllEmpty = i0 + 3 < height;
           for (var j = 0; j < width; j++) {
-            var index0 = i0 * width + j;
+            var index0 = indexBase + j;
             // using the property: labels[neighborsSignificance[index]] == 0
             // when neighborsSignificance[index] == 0
-            var allEmpty = (i0 + 3 < height &&
+            var allEmpty = (checkAllEmpty &&
               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 i, sign;
+            var i = i0, sign;
             if (allEmpty) {
               var hasSignificantCoefficent =
                 decoder.readBit(contexts, RUNLENGTH_CONTEXT);
               if (!hasSignificantCoefficent) {
                 bitsDecoded[index0]++;
                 bitsDecoded[index0 + oneRowDown]++;
                 bitsDecoded[index0 + twoRowsDown]++;
                 bitsDecoded[index0 + threeRowsDown]++;
                 continue; // next column
               }
               i1 = (decoder.readBit(contexts, UNIFORM_CONTEXT) << 1) |
                     decoder.readBit(contexts, UNIFORM_CONTEXT);
-              i = i0 + i1;
-              index += i1 * width;
+              if (i1 !== 0) {
+                i = i0 + i1;
+                index += i1 * width;
+              }
 
               sign = this.decodeSignBit(i, j, index);
               coefficentsSign[index] = sign;
               coefficentsMagnitude[index] = 1;
               this.setNeighborsSignificance(i, j, index);
               processingFlags[index] |= firstMagnitudeBitMask;
 
               index = index0;
               for (var i2 = i0; i2 <= i; i2++, index += width) {
                 bitsDecoded[index]++;
               }
 
               i1++;
             }
-            for (; i1 < 4; i1++, index += width) {
-              i = i0 + i1;
-              if (i >= height) {
-                break;
-              }
-
+            for (i = i0 + i1; i < iNext; i++, index += width) {
               if (coefficentsMagnitude[index] ||
                 (processingFlags[index] & processedMask) !== 0) {
                 continue;
               }
 
               var contextLabel = labels[neighborsSignificance[index]];
               var decision = decoder.readBit(contexts, contextLabel);
-              if (decision == 1) {
+              if (decision === 1) {
                 sign = this.decodeSignBit(i, j, index);
                 coefficentsSign[index] = sign;
                 coefficentsMagnitude[index] = 1;
                 this.setNeighborsSignificance(i, j, index);
                 processingFlags[index] |= firstMagnitudeBitMask;
               }
               bitsDecoded[index]++;
             }
@@ -38899,72 +39013,51 @@ var JpxImage = (function JpxImageClosure
 
   // Section F, Discrete wavelet transformation
   var Transform = (function TransformClosure() {
     function Transform() {}
 
     Transform.prototype.calculate =
       function transformCalculate(subbands, u0, v0) {
       var ll = subbands[0];
-      for (var i = 1, ii = subbands.length; i < ii; i += 3) {
-        ll = this.iterate(ll, subbands[i], subbands[i + 1],
-                          subbands[i + 2], u0, v0);
+      for (var i = 1, ii = subbands.length; i < ii; i++) {
+        ll = this.iterate(ll, subbands[i], u0, v0);
       }
       return ll;
     };
     Transform.prototype.extend = function extend(buffer, offset, size) {
       // Section F.3.7 extending... using max extension of 4
       var i1 = offset - 1, j1 = offset + 1;
       var i2 = offset + size - 2, j2 = offset + size;
       buffer[i1--] = buffer[j1++];
       buffer[j2++] = buffer[i2--];
       buffer[i1--] = buffer[j1++];
       buffer[j2++] = buffer[i2--];
       buffer[i1--] = buffer[j1++];
       buffer[j2++] = buffer[i2--];
       buffer[i1] = buffer[j1];
       buffer[j2] = buffer[i2];
     };
-    Transform.prototype.iterate = function Transform_iterate(ll, hl, lh, hh,
-                                                            u0, v0) {
+    Transform.prototype.iterate = function Transform_iterate(ll, hl_lh_hh,
+                                                             u0, v0) {
       var llWidth = ll.width, llHeight = ll.height, llItems = ll.items;
-      var hlWidth = hl.width, hlHeight = hl.height, hlItems = hl.items;
-      var lhWidth = lh.width, lhHeight = lh.height, lhItems = lh.items;
-      var hhWidth = hh.width, hhHeight = hh.height, hhItems = hh.items;
-
-      // Section F.3.3 interleave
-      var width = llWidth + hlWidth;
-      var height = llHeight + lhHeight;
-      var items = new Float32Array(width * height);
-      var i, j, k, l, v, u;
-
-      for (i = 0, k = 0; i < llHeight; i++) {
+      var width = hl_lh_hh.width;
+      var height = hl_lh_hh.height;
+      var items = hl_lh_hh.items;
+      var i, j, k, l, u, v;
+
+      // Interleave LL according to Section F.3.3
+      for (k = 0, i = 0; i < llHeight; i++) {
         l = i * 2 * width;
         for (j = 0; j < llWidth; j++, k++, l += 2) {
           items[l] = llItems[k];
         }
       }
-      for (i = 0, k = 0; i < hlHeight; i++) {
-        l = i * 2 * width + 1;
-        for (j = 0; j < hlWidth; j++, k++, l += 2) {
-          items[l] = hlItems[k];
-        }
-      }
-      for (i = 0, k = 0; i < lhHeight; i++) {
-        l = (i * 2 + 1) * width;
-        for (j = 0; j < lhWidth; j++, k++, l += 2) {
-          items[l] = lhItems[k];
-        }
-      }
-      for (i = 0, k = 0; i < hhHeight; i++) {
-        l = (i * 2 + 1) * width + 1;
-        for (j = 0; j < hhWidth; j++, k++, l += 2) {
-          items[l] = hhItems[k];
-        }
-      }
+      // The LL band is not needed anymore.
+      llItems = ll.items = null;
 
       var bufferPadding = 4;
       var rowBuffer = new Float32Array(width + 2 * bufferPadding);
 
       // Section F.3.4 HOR_SR
       if (width === 1) {
         // if width = 1, when u0 even keep items as is, when odd divide by 2
         if ((u0 & 1) !== 0) {
@@ -38972,17 +39065,17 @@ var JpxImage = (function JpxImageClosure
             items[k] *= 0.5;
           }
         }
       } else {
         for (v = 0, k = 0; v < height; v++, k += width) {
           rowBuffer.set(items.subarray(k, k + width), bufferPadding);
 
           this.extend(rowBuffer, bufferPadding, width);
-          this.filter(rowBuffer, bufferPadding, width, u0, rowBuffer);
+          this.filter(rowBuffer, bufferPadding, width);
 
           items.set(
             rowBuffer.subarray(bufferPadding, bufferPadding + width),
             k);
         }
       }
 
       // Accesses to the items array can take long, because it may not fit into
@@ -39018,17 +39111,17 @@ var JpxImage = (function JpxImageClosure
               }
             }
             currentBuffer = numBuffers;
           }
 
           currentBuffer--;
           var buffer = colBuffers[currentBuffer];
           this.extend(buffer, bufferPadding, height);
-          this.filter(buffer, bufferPadding, height, v0, buffer);
+          this.filter(buffer, bufferPadding, height);
 
           // If this is last buffer in this group of buffers, flush all buffers.
           if (currentBuffer === 0) {
             k = u - numBuffers + 1;
             for (l = bufferPadding; l < ll; k += width, l++) {
               for (b = 0; b < numBuffers; b++) {
                 items[k + b] = colBuffers[b][l];
               }
@@ -39049,79 +39142,121 @@ var JpxImage = (function JpxImageClosure
   // Section 3.8.2 Irreversible 9-7 filter
   var IrreversibleTransform = (function IrreversibleTransformClosure() {
     function IrreversibleTransform() {
       Transform.call(this);
     }
 
     IrreversibleTransform.prototype = Object.create(Transform.prototype);
     IrreversibleTransform.prototype.filter =
-      function irreversibleTransformFilter(y, offset, length, i0, x) {
+      function irreversibleTransformFilter(x, offset, length) {
       var len = length >> 1;
       offset = offset | 0;
+      var j, n, current, next;
 
       var alpha = -1.586134342059924;
       var beta = -0.052980118572961;
       var gamma = 0.882911075530934;
       var delta = 0.443506852043971;
       var K = 1.230174104914001;
       var K_ = 1 / K;
-      var j, n, nn;
 
       // step 1 is combined with step 3
 
       // step 2
-      for (j = offset - 3, n = len + 4; n--; j += 2) {
-        x[j] = K_ * y[j];
+      j = offset - 3;
+      for (n = len + 4; n--; j += 2) {
+        x[j] *= K_;
       }
 
       // step 1 & 3
-      for (j = offset - 2, n = len + 3; n--; j += 2) {
-        x[j] = K * y[j] -
-          delta * (x[j - 1] + x[j + 1]);
+      j = offset - 2;
+      current = delta * x[j -1];
+      for (n = len + 3; n--; j += 2) {
+        next = delta * x[j + 1];
+        x[j] = K * x[j] - current - next;
+        if (n--) {
+          j += 2;
+          current = delta * x[j + 1];
+          x[j] = K * x[j] - current - next;
+        } else {
+          break;
+        }
       }
 
       // step 4
-      for (j = offset - 1, n = len + 2; n--; j += 2) {
-        x[j] -= gamma * (x[j - 1] + x[j + 1]);
+      j = offset - 1;
+      current = gamma * x[j - 1];
+      for (n = len + 2; n--; j += 2) {
+        next = gamma * x[j + 1];
+        x[j] -= current + next;
+        if (n--) {
+          j += 2;
+          current = gamma * x[j + 1];
+          x[j] -= current + next;
+        } else {
+          break;
+        }
       }
 
       // step 5
-      for (j = offset, n = len + 1; n--; j += 2) {
-        x[j] -= beta * (x[j - 1] + x[j + 1]);
+      j = offset;
+      current = beta * x[j - 1];
+      for (n = len + 1; n--; j += 2) {
+        next = beta * x[j + 1];
+        x[j] -= current + next;
+        if (n--) {
+          j += 2;
+          current = beta * x[j + 1];
+          x[j] -= current + next;
+        } else {
+          break;
+        }
       }
 
       // step 6
-      for (j = offset + 1, n = len; n--; j += 2) {
-        x[j] -= alpha * (x[j - 1] + x[j + 1]);
+      if (len !== 0) {
+        j = offset + 1;
+        current = alpha * x[j - 1];
+        for (n = len; n--; j += 2) {
+          next = alpha * x[j + 1];
+          x[j] -= current + next;
+          if (n--) {
+            j += 2;
+            current = alpha * x[j + 1];
+            x[j] -= current + next;
+          } else {
+            break;
+          }
+        }
       }
     };
 
     return IrreversibleTransform;
   })();
 
   // Section 3.8.1 Reversible 5-3 filter
   var ReversibleTransform = (function ReversibleTransformClosure() {
     function ReversibleTransform() {
       Transform.call(this);
     }
 
     ReversibleTransform.prototype = Object.create(Transform.prototype);
     ReversibleTransform.prototype.filter =
-      function reversibleTransformFilter(y, offset, length, i0, x) {
+      function reversibleTransformFilter(x, offset, length) {
       var len = length >> 1;
       offset = offset | 0;
       var j, n;
 
       for (j = offset, n = len + 1; n--; j += 2) {
-        x[j] = y[j] - ((y[j - 1] + y[j + 1] + 2) >> 2);
+        x[j] -= (x[j - 1] + x[j + 1] + 2) >> 2;
       }
 
       for (j = offset + 1, n = len; n--; j += 2) {
-        x[j] = y[j] + ((x[j - 1] + x[j + 1]) >> 1);
+        x[j] += (x[j - 1] + x[j + 1]) >> 1;
       }
     };
 
     return ReversibleTransform;
   })();
 
   return JpxImage;
 })();
@@ -39293,43 +39428,16 @@ var Jbig2Image = (function Jbig2ImageClo
     0x018B  // '011000101' + '1'
   ];
 
   var RefinementReusedContexts = [
     0x0020, // '000' + '0' (coding) + '00010000' + '0' (reference)
     0x0008  // '0000' + '001000'
   ];
 
-  function log2(x) {
-    var n = 1, i = 0;
-    while (x > n) {
-      n <<= 1;
-      i++;
-    }
-    return i;
-  }
-
-  function readInt32(data, start) {
-    return (data[start] << 24) | (data[start + 1] << 16) |
-           (data[start + 2] << 8) | data[start + 3];
-  }
-
-  function readUint32(data, start) {
-    var value = readInt32(data, start);
-    return value & 0x80000000 ? (value + 4294967296) : value;
-  }
-
-  function readUint16(data, start) {
-    return (data[start] << 8) | data[start + 1];
-  }
-
-  function readInt8(data, start) {
-    return (data[start] << 24) >> 24;
-  }
-
   // 6.2 Generic Region Decoding Procedure
   function decodeBitmap(mmr, width, height, templateIndex, prediction, skip, at,
                         decodingContext) {
     if (mmr) {
       error('JBIG2 error: MMR encoding is not supported');
     }
 
     var useskip = !!skip;
@@ -39750,17 +39858,17 @@ var Jbig2Image = (function Jbig2ImageClo
     segmentHeader.deferredNonRetain = !!(flags & 0x80);
 
     var pageAssociationFieldSize = !!(flags & 0x40);
     var referredFlags = data[start + 5];
     var referredToCount = (referredFlags >> 5) & 7;
     var retainBits = [referredFlags & 31];
     var position = start + 6;
     if (referredFlags == 7) {
-      referredToCount = readInt32(data, position - 1) & 0x1FFFFFFF;
+      referredToCount = readUint32(data, position - 1) & 0x1FFFFFFF;
       position += 3;
       var bytes = (referredToCount + 7) >> 3;
       retainBits[0] = data[position++];
       while (--bytes > 0) {
         retainBits.push(data[position++]);
       }
     } else if (referredFlags == 5 || referredFlags == 6) {
       error('JBIG2 error: invalid referred-to flags');
@@ -40294,56 +40402,16 @@ var bidi = PDFJS.bidi = (function bidiCl
   function reverseValues(arr, start, end) {
     for (var i = start, j = end - 1; i < j; ++i, --j) {
       var temp = arr[i];
       arr[i] = arr[j];
       arr[j] = temp;
     }
   }
 
-  function mirrorGlyphs(c) {
-    /*
-     # BidiMirroring-1.txt
-     0028; 0029 # LEFT PARENTHESIS
-     0029; 0028 # RIGHT PARENTHESIS
-     003C; 003E # LESS-THAN SIGN
-     003E; 003C # GREATER-THAN SIGN
-     005B; 005D # LEFT SQUARE BRACKET
-     005D; 005B # RIGHT SQUARE BRACKET
-     007B; 007D # LEFT CURLY BRACKET
-     007D; 007B # RIGHT CURLY BRACKET
-     00AB; 00BB # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
-     00BB; 00AB # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
-     */
-    switch (c) {
-      case '(':
-        return ')';
-      case ')':
-        return '(';
-      case '<':
-        return '>';
-      case '>':
-        return '<';
-      case ']':
-        return '[';
-      case '[':
-        return ']';
-      case '}':
-        return '{';
-      case '{':
-        return '}';
-      case '\u00AB':
-        return '\u00BB';
-      case '\u00BB':
-        return '\u00AB';
-      default:
-        return c;
-    }
-  }
-
   function createBidiText(str, isLTR, vertical) {
     return {
       str: str,
       dir: (vertical ? 'ttb' : (isLTR ? 'ltr' : 'rtl'))
     };
   }
 
   // These are used in bidi(), which is called frequently. We re-use them on
--- a/browser/extensions/pdfjs/content/web/debugger.js
+++ b/browser/extensions/pdfjs/content/web/debugger.js
@@ -15,17 +15,16 @@
  * limitations under the License.
  */
 /* globals PDFJS */
 
 'use strict';
 
 var FontInspector = (function FontInspectorClosure() {
   var fonts;
-  var panelWidth = 300;
   var active = false;
   var fontAttribute = 'data-font-name';
   function removeSelection() {
     var divs = document.querySelectorAll('div[' + fontAttribute + ']');
     for (var i = 0, ii = divs.length; i < ii; ++i) {
       var div = divs[i];
       div.className = '';
     }
@@ -322,32 +321,33 @@ var Stepper = (function StepperClosure()
       if (!opMap) {
         opMap = Object.create(null);
         for (var key in PDFJS.OPS) {
           opMap[PDFJS.OPS[key]] = key;
         }
       }
     },
     updateOperatorList: function updateOperatorList(operatorList) {
+      var self = this;
+
       function cboxOnClick() {
         var x = +this.dataset.idx;
         if (this.checked) {
           self.breakPoints.push(x);
         } else {
           self.breakPoints.splice(self.breakPoints.indexOf(x), 1);
         }
         StepperManager.saveBreakPoints(self.pageIndex, self.breakPoints);
       }
 
       var MAX_OPERATORS_COUNT = 15000;
       if (this.operatorListIdx > MAX_OPERATORS_COUNT) {
         return;
       }
 
-      var self = this;
       var chunk = document.createDocumentFragment();
       var operatorsToDisplay = Math.min(MAX_OPERATORS_COUNT,
                                         operatorList.fnArray.length);
       for (var i = this.operatorListIdx; i < operatorsToDisplay; i++) {
         var line = c('tr');
         line.className = 'line';
         line.dataset.idx = i;
         chunk.appendChild(line);
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..fcd0b268a475662d421e9144764a09d20faf4155
GIT binary patch
literal 384
zc$@)%0e}99P)<h;3K|Lk000e1NJLTq000mG000mO1ONa4wfZ;e0003;Nkl<ZC{tr#
zAP4+c{J-V@rT_c?8=^}H|91eAn*Yvh$YQXZ8u#zqe;yR+e+NFa{@46_c2+8b2vC6k
z$(rvD$kO|7&79);=k(-c20^e0<LBL5O%T$1Z%v=z{^#VRBnE;1hX0HHGySi6vY3Gj
zCcXRS)IPU=$0jF&r2iaW;rYMf(;9t<BmWEj+j)C(x68jHlM;Z^27it=MSZOKyk5<W
zfeEbO_k>4t*wuc`Tb2cq{&%P`@>A8<^(NL(>Ho}s&vtk+Fua<7E9$@Hp92jMpDMqu
z1xm9(o%fjY|H;!pyNX@EE&si$I{ZV$w@o%SaA_c5db|2dDUjx3Fk~?LRQ7GXy(6+T
z5KNBxck+Mrf8+l~|GU4fb8ui_Wk3UhZHZr2{6GKy)RPIe_L$OOB@D*mE*uUFn%Jcw
efQf+-n=k;HAC|A!G+~AS0000<MNUMnLSTX%g~0Cs
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..b979e523e742027645e980d8a27c54d0060ebf21
GIT binary patch
literal 871
zc$@)e1DO1YP)<h;3K|Lk000e1NJLTq001BW001Be1ONa4*>kdg0009nNkl<ZNXO;X
zYe-XJ7zgmjsfm##T}4UiLl7)ME2|F$d8w=@G%~9%Mi3EQAT2M1(T8pjLRo1AR#01N
zp^|B~teLBsZcdxc>*n00Qixp6B9(Lgy*p~IIal_z=X^QudHJ1}^E~hK9-{v*5Qt#-
z{wq$zD|}{GH?lE%s@NMj=*7nYb>AD_j2@`4U~2pZK7P|=?WfhWa$EeT3Rw9Hh&-bW
zD8u?uo`(+us$E+sK$!9qNA}mjiannACYWKd9vtP?fqKUMeoc$2Ps8-R%me35Fq%um
zp^WFkGEkSa*@?4|$HO?Jot$^0%Um2BA@|{OVft3P(}fdRlhI{Yy5}3w1-<2wgX227
z?aGN9-apEq*X6k926S2;hJ-VIjji%#jKd#?5Fy-oaTj42)FiKP!+Aa0Eky#q3WHrS
z&iNu3zh*f&F2FF{d|@BC?nrFlJJD(?6nG9g?KO#UW1K5QhplRFIOFG`=iX%>pji{^
z-VU9qz`?m3bl9t%cmdjM&o@Skr{NqrIBnh`3UuYMd>h`IrNZi0&}Nq>jPnO*wLKH~
zS+sLaH;<5OCm;$f(3&M7;b%mwQCjRToVXM^TV?bJZ{;;N57Qi1KA+d1$#g#?oV*YX
z-EUliW?Mx}6!~(QXyG-tk4@l2gQw8={YAt|@*=XGhvvcIU;I9_wsHr@N$8-ymqPR0
z)PAT6YQ-7ycHEmULEV%fa9{2on!Y?4<;}cGa-7_?FlM3wDoO0fsC+L}!%wjSVcZ=w
z_B|H(320F-O(pjyfPNwBQ1@ub$S7d0{uVndbS$Y2pNj>aig$QhoIye4KGFhsC2Bjx
zW8)cOX{s^be77=d(Hem_qP8@hf@#ta^+=)YOLlq^M9V0Q7Lo`l&_G$5;fb@TI)>MS
z6_`FDJ1HzXuVtqxggl2h)K&r|mk+1y2_Ixa4TUU=Lg|m;m9}F@hFV^Mbk@$1Y+oMb
zWvE9@)n9QTb16yvrKn-`XkbaxFs$HTmZbeH7wWfoU3q4&gqNcl)q|C~%exc)`0cLM
xV0mA@ASQ16s;xe2X({<njgP;^kEZbN`w1rY0*o@`?gan<002ovPDHLkV1kKWoksuw
--- a/browser/extensions/pdfjs/content/web/viewer.css
+++ b/browser/extensions/pdfjs/content/web/viewer.css
@@ -528,16 +528,19 @@ html[dir='rtl'] .splitToolbarButton > .t
 .toolbarButton.textButton:hover,
 .toolbarButton.textButton:focus {
   background-color: hsla(0,0%,0%,.2);
   box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset,
               0 0 1px hsla(0,0%,100%,.15) inset,
               0 0 1px hsla(0,0%,0%,.05);
   z-index: 199;
 }
+.splitToolbarButton > .toolbarButton {
+  position: relative;
+}
 html[dir='ltr'] .splitToolbarButton > .toolbarButton:first-child,
 html[dir='rtl'] .splitToolbarButton > .toolbarButton:last-child {
   position: relative;
   margin: 0;
   margin-right: -1px;
   border-top-left-radius: 2px;
   border-bottom-left-radius: 2px;
   border-right-color: transparent;
@@ -856,16 +859,20 @@ html[dir='rtl'] .toolbarButton.pageDown:
 
 html[dir="ltr"] #viewOutline.toolbarButton::before {
   content: url(images/toolbarButton-viewOutline.png);
 }
 html[dir="rtl"] #viewOutline.toolbarButton::before {
   content: url(images/toolbarButton-viewOutline-rtl.png);
 }
 
+#viewAttachments.toolbarButton::before {
+  content: url(images/toolbarButton-viewAttachments.png);
+}
+
 #viewFind.toolbarButton::before {
   content: url(images/toolbarButton-search.png);
 }
 
 .secondaryToolbarButton {
   position: relative;
   margin: 0 0 4px 0;
   padding: 3px 0 1px 0;
@@ -1065,17 +1072,18 @@ a:focus > .thumbnail > .thumbnailSelecti
   background-image: linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0));
   background-clip: padding-box;
   box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset,
               0 0 1px hsla(0,0%,100%,.1) inset,
               0 0 1px hsla(0,0%,0%,.2);
   color: hsla(0,0%,100%,1);
 }
 
-#outlineView {
+#outlineView,
+#attachmentsView {
   position: absolute;
   width: 192px;
   top: 0;
   bottom: 0;
   padding: 4px 4px 0;
   overflow: auto;
   -moz-user-select: none;
 }
@@ -1083,39 +1091,43 @@ a:focus > .thumbnail > .thumbnailSelecti
 html[dir='ltr'] .outlineItem > .outlineItems {
   margin-left: 20px;
 }
 
 html[dir='rtl'] .outlineItem > .outlineItems {
   margin-right: 20px;
 }
 
-.outlineItem > a {
+.outlineItem > a,
+.attachmentsItem > a {
   text-decoration: none;
   display: inline-block;
   min-width: 95%;
   height: auto;
   margin-bottom: 1px;
   border-radius: 2px;
   color: hsla(0,0%,100%,.8);
   font-size: 13px;
   line-height: 15px;
   -moz-user-select: none;
   white-space: normal;
 }
 
-html[dir='ltr'] .outlineItem > a {
+html[dir='ltr'] .outlineItem > a,
+html[dir='ltr'] .attachmentsItem > a {
   padding: 2px 0 5px 10px;
 }
 
-html[dir='rtl'] .outlineItem > a {
+html[dir='rtl'] .outlineItem > a,
+html[dir='rtl'] .attachmentsItem > a {
   padding: 2px 10px 5px 0;
 }
 
-.outlineItem > a:hover {
+.outlineItem > a:hover,
+.attachmentsItem > a:hover {
   background-color: hsla(0,0%,100%,.02);
   background-image: linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0));
   background-clip: padding-box;
   box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset,
               0 0 1px hsla(0,0%,100%,.2) inset,
               0 0 1px hsla(0,0%,0%,.2);
   color: hsla(0,0%,100%,.9);
 }
@@ -1636,16 +1648,20 @@ html[dir='rtl'] #documentPropertiesConta
 
   html[dir="ltr"] #viewOutline.toolbarButton::before {
     content: url(images/toolbarButton-viewOutline@2x.png);
   }
   html[dir="rtl"] #viewOutline.toolbarButton::before {
     content: url(images/toolbarButton-viewOutline-rtl@2x.png);
   }
 
+  #viewAttachments.toolbarButton::before {
+    content: url(images/toolbarButton-viewAttachments@2x.png);
+  }
+
   #viewFind.toolbarButton::before {
     content: url(images/toolbarButton-search@2x.png);
   }
 
   .secondaryToolbarButton.firstPage::before {
     content: url(images/secondaryToolbarButton-firstPage@2x.png);
   }
 
--- a/browser/extensions/pdfjs/content/web/viewer.html
+++ b/browser/extensions/pdfjs/content/web/viewer.html
@@ -18,17 +18,17 @@ Adobe CMap resources are covered by thei
 http://sourceforge.net/adobe/cmap/wiki/License/
 -->
 <html dir="ltr" mozdisallowselectionprint moznomarginboxes>
   <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 -->
+<!-- This snippet is used in the Firefox extension (included from viewer.html) -->
 <base href="resource://pdf.js/web/" />
 <script type="text/javascript" src="l10n.js"></script>
 <script type="text/javascript" src="../build/pdf.js"></script>
 
 
     <link rel="stylesheet" href="viewer.css"/>
 
 
@@ -48,23 +48,28 @@ http://sourceforge.net/adobe/cmap/wiki/L
         <div id="toolbarSidebar">
           <div class="splitToolbarButton toggled">
             <button id="viewThumbnail" class="toolbarButton group toggled" title="Show Thumbnails" tabindex="2" data-l10n-id="thumbs">
                <span data-l10n-id="thumbs_label">Thumbnails</span>
             </button>
             <button id="viewOutline" class="toolbarButton group" title="Show Document Outline" tabindex="3" data-l10n-id="outline">
                <span data-l10n-id="outline_label">Document Outline</span>
             </button>
+            <button id="viewAttachments" class="toolbarButton group" title="Show Attachments" tabindex="4" data-l10n-id="attachments">
+               <span data-l10n-id="attachments_label">Attachments</span>
+            </button>
           </div>
         </div>
         <div id="sidebarContent">
           <div id="thumbnailView">
           </div>
           <div id="outlineView" class="hidden">
           </div>
+          <div id="attachmentsView" class="hidden">
+          </div>
         </div>
       </div>  <!-- sidebarContainer -->
 
       <div id="mainContainer">
         <div class="findbar hidden doorHanger hiddenSmallView" id="findbar">
           <label for="findInput" class="toolbarLabel" data-l10n-id="find_label">Find:</label>
           <input id="findInput" class="toolbarField" tabindex="41">
           <div class="splitToolbarButton">
@@ -80,143 +85,143 @@ http://sourceforge.net/adobe/cmap/wiki/L
           <label for="findHighlightAll" class="toolbarLabel" tabindex="44" data-l10n-id="find_highlight">Highlight all</label>
           <input type="checkbox" id="findMatchCase" class="toolbarField">
           <label for="findMatchCase" class="toolbarLabel" tabindex="45" data-l10n-id="find_match_case_label">Match case</label>
           <span id="findMsg" class="toolbarLabel"></span>
         </div>  <!-- findbar -->
 
         <div id="secondaryToolbar" class="secondaryToolbar hidden doorHangerRight">
           <div id="secondaryToolbarButtonContainer">
-            <button id="secondaryPresentationMode" class="secondaryToolbarButton presentationMode visibleLargeView" title="Switch to Presentation Mode" tabindex="18" data-l10n-id="presentation_mode">
+            <button id="secondaryPresentationMode" class="secondaryToolbarButton presentationMode visibleLargeView" title="Switch to Presentation Mode" tabindex="19" data-l10n-id="presentation_mode">
               <span data-l10n-id="presentation_mode_label">Presentation Mode</span>
             </button>
 
-            <button id="secondaryOpenFile" class="secondaryToolbarButton openFile visibleLargeView" title="Open File" tabindex="19" data-l10n-id="open_file">
+            <button id="secondaryOpenFile" class="secondaryToolbarButton openFile visibleLargeView" title="Open File" tabindex="20" data-l10n-id="open_file">
               <span data-l10n-id="open_file_label">Open</span>
             </button>
 
-            <button id="secondaryPrint" class="secondaryToolbarButton print visibleMediumView" title="Print" tabindex="20" data-l10n-id="print">
+            <button id="secondaryPrint" class="secondaryToolbarButton print visibleMediumView" title="Print" tabindex="21" data-l10n-id="print">
               <span data-l10n-id="print_label">Print</span>
             </button>
 
-            <button id="secondaryDownload" class="secondaryToolbarButton download visibleMediumView" title="Download" tabindex="21" data-l10n-id="download">
+            <button id="secondaryDownload" class="secondaryToolbarButton download visibleMediumView" title="Download" tabindex="22" data-l10n-id="download">
               <span data-l10n-id="download_label">Download</span>
             </button>
 
-            <a href="#" id="secondaryViewBookmark" class="secondaryToolbarButton bookmark visibleSmallView" title="Current view (copy or open in new window)" tabindex="22" data-l10n-id="bookmark">
+            <a href="#" id="secondaryViewBookmark" class="secondaryToolbarButton bookmark visibleSmallView" title="Current view (copy or open in new window)" tabindex="23" data-l10n-id="bookmark">
               <span data-l10n-id="bookmark_label">Current View</span>
             </a>
 
             <div class="horizontalToolbarSeparator visibleLargeView"></div>
 
-            <button id="firstPage" class="secondaryToolbarButton firstPage" title="Go to First Page" tabindex="23" data-l10n-id="first_page">
+            <button id="firstPage" class="secondaryToolbarButton firstPage" title="Go to First Page" tabindex="24" data-l10n-id="first_page">
               <span data-l10n-id="first_page_label">Go to First Page</span>
             </button>
-            <button id="lastPage" class="secondaryToolbarButton lastPage" title="Go to Last Page" tabindex="24" data-l10n-id="last_page">
+            <button id="lastPage" class="secondaryToolbarButton lastPage" title="Go to Last Page" tabindex="25" data-l10n-id="last_page">
               <span data-l10n-id="last_page_label">Go to Last Page</span>
             </button>
 
             <div class="horizontalToolbarSeparator"></div>
 
-            <button id="pageRotateCw" class="secondaryToolbarButton rotateCw" title="Rotate Clockwise" tabindex="25" data-l10n-id="page_rotate_cw">
+            <button id="pageRotateCw" class="secondaryToolbarButton rotateCw" title="Rotate Clockwise" tabindex="26" data-l10n-id="page_rotate_cw">
               <span data-l10n-id="page_rotate_cw_label">Rotate Clockwise</span>
             </button>
-            <button id="pageRotateCcw" class="secondaryToolbarButton rotateCcw" title="Rotate Counterclockwise" tabindex="26" data-l10n-id="page_rotate_ccw">
+            <button id="pageRotateCcw" class="secondaryToolbarButton rotateCcw" title="Rotate Counterclockwise" tabindex="27" data-l10n-id="page_rotate_ccw">
               <span data-l10n-id="page_rotate_ccw_label">Rotate Counterclockwise</span>
             </button>
 
             <div class="horizontalToolbarSeparator"></div>
 
-            <button id="toggleHandTool" class="secondaryToolbarButton handTool" title="Enable hand tool" tabindex="27" data-l10n-id="hand_tool_enable">
+            <button id="toggleHandTool" class="secondaryToolbarButton handTool" title="Enable hand tool" tabindex="28" data-l10n-id="hand_tool_enable">
               <span data-l10n-id="hand_tool_enable_label">Enable hand tool</span>
             </button>
             
             <div class="horizontalToolbarSeparator"></div>
 
-            <button id="documentProperties" class="secondaryToolbarButton documentProperties" title="Document Properties…" tabindex="28" data-l10n-id="document_properties">
+            <button id="documentProperties" class="secondaryToolbarButton documentProperties" title="Document Properties…" tabindex="29" data-l10n-id="document_properties">
               <span data-l10n-id="document_properties_label">Document Properties…</span>
             </button>
           </div>
         </div>  <!-- secondaryToolbar -->
 
         <div class="toolbar">
           <div id="toolbarContainer">
             <div id="toolbarViewer">
               <div id="toolbarViewerLeft">
-                <button id="sidebarToggle" class="toolbarButton" title="Toggle Sidebar" tabindex="4" data-l10n-id="toggle_sidebar">
+                <button id="sidebarToggle" class="toolbarButton" title="Toggle Sidebar" tabindex="5" 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 hiddenSmallView" title="Find in Document" tabindex="5" data-l10n-id="findbar">
+                <button id="viewFind" class="toolbarButton group hiddenSmallView" title="Find in Document" tabindex="6" 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="6" data-l10n-id="previous">
+                  <button class="toolbarButton pageUp" title="Previous Page" id="previous" tabindex="7" data-l10n-id="previous">
                     <span data-l10n-id="previous_label">Previous</span>
                   </button>
                   <div class="splitToolbarButtonSeparator"></div>
-                  <button class="toolbarButton pageDown" title="Next Page" id="next" tabindex="7" data-l10n-id="next">
+                  <button class="toolbarButton pageDown" title="Next Page" id="next" tabindex="8" data-l10n-id="next">
                     <span data-l10n-id="next_label">Next</span>
                   </button>
                 </div>
                 <label id="pageNumberLabel" class="toolbarLabel" for="pageNumber" data-l10n-id="page_label">Page: </label>
-                <input type="number" id="pageNumber" class="toolbarField pageNumber" value="1" size="4" min="1" tabindex="8">
+                <input type="number" id="pageNumber" class="toolbarField pageNumber" value="1" size="4" min="1" tabindex="9">
                 <span id="numPages" class="toolbarLabel"></span>
               </div>
               <div id="toolbarViewerRight">
-                <button id="presentationMode" class="toolbarButton presentationMode hiddenLargeView" title="Switch to Presentation Mode" tabindex="12" data-l10n-id="presentation_mode">
+                <button id="presentationMode" class="toolbarButton presentationMode hiddenLargeView" title="Switch to Presentation Mode" tabindex="13" data-l10n-id="presentation_mode">
                   <span data-l10n-id="presentation_mode_label">Presentation Mode</span>
                 </button>
 
-                <button id="openFile" class="toolbarButton openFile hiddenLargeView" title="Open File" tabindex="13" data-l10n-id="open_file">
+                <button id="openFile" class="toolbarButton openFile hiddenLargeView" title="Open File" tabindex="14" data-l10n-id="open_file">
                   <span data-l10n-id="open_file_label">Open</span>
                 </button>
 
-                <button id="print" class="toolbarButton print hiddenMediumView" title="Print" tabindex="14" data-l10n-id="print">
+                <button id="print" class="toolbarButton print hiddenMediumView" title="Print" tabindex="15" data-l10n-id="print">
                   <span data-l10n-id="print_label">Print</span>
                 </button>
 
-                <button id="download" class="toolbarButton download hiddenMediumView" title="Download" tabindex="15" data-l10n-id="download">
+                <button id="download" class="toolbarButton download hiddenMediumView" title="Download" tabindex="16" data-l10n-id="download">
                   <span data-l10n-id="download_label">Download</span>
                 </button>
                 <!-- <div class="toolbarButtonSpacer"></div> -->
-                <a href="#" id="viewBookmark" class="toolbarButton bookmark hiddenSmallView" title="Current view (copy or open in new window)" tabindex="16" data-l10n-id="bookmark">
+                <a href="#" id="viewBookmark" class="toolbarButton bookmark hiddenSmallView" title="Current view (copy or open in new window)" tabindex="17" data-l10n-id="bookmark">
                   <span data-l10n-id="bookmark_label">Current View</span>
                 </a>
 
                 <div class="verticalToolbarSeparator hiddenSmallView"></div>
                 
-                <button id="secondaryToolbarToggle" class="toolbarButton" title="Tools" tabindex="17" data-l10n-id="tools">
+                <button id="secondaryToolbarToggle" class="toolbarButton" title="Tools" tabindex="18" data-l10n-id="tools">
                   <span data-l10n-id="tools_label">Tools</span>
                 </button> 
               </div>
               <div class="outerCenter">
                 <div class="innerCenter" id="toolbarViewerMiddle">
                   <div class="splitToolbarButton">
-                    <button id="zoomOut" class="toolbarButton zoomOut" title="Zoom Out" tabindex="9" data-l10n-id="zoom_out">
+                    <button id="zoomOut" class="toolbarButton zoomOut" title="Zoom Out" tabindex="10" data-l10n-id="zoom_out">
                       <span data-l10n-id="zoom_out_label">Zoom Out</span>
                     </button>
                     <div class="splitToolbarButtonSeparator"></div>
-                    <button id="zoomIn" class="toolbarButton zoomIn" title="Zoom In" tabindex="10" data-l10n-id="zoom_in">
+                    <button id="zoomIn" class="toolbarButton zoomIn" title="Zoom In" tabindex="11" data-l10n-id="zoom_in">
                       <span data-l10n-id="zoom_in_label">Zoom In</span>
                      </button>
                   </div>
                   <span id="scaleSelectContainer" class="dropdownToolbarButton">
-                     <select id="scaleSelect" title="Zoom" tabindex="11" data-l10n-id="zoom">
-                      <option id="pageAutoOption" value="auto" selected="selected" data-l10n-id="page_scale_auto">Automatic Zoom</option>
-                      <option id="pageActualOption" value="page-actual" data-l10n-id="page_scale_actual">Actual Size</option>
-                      <option id="pageFitOption" value="page-fit" data-l10n-id="page_scale_fit">Fit Page</option>
-                      <option id="pageWidthOption" value="page-width" data-l10n-id="page_scale_width">Full Width</option>
-                      <option id="customScaleOption" value="custom"></option>
-                      <option value="0.5">50%</option>
-                      <option value="0.75">75%</option>
-                      <option value="1">100%</option>
-                      <option value="1.25">125%</option>
-                      <option value="1.5">150%</option>
-                      <option value="2">200%</option>
+                     <select id="scaleSelect" title="Zoom" tabindex="12" data-l10n-id="zoom">
+                      <option id="pageAutoOption" title="" value="auto" selected="selected" data-l10n-id="page_scale_auto">Automatic Zoom</option>
+                      <option id="pageActualOption" title="" value="page-actual" data-l10n-id="page_scale_actual">Actual Size</option>
+                      <option id="pageFitOption" title="" value="page-fit" data-l10n-id="page_scale_fit">Fit Page</option>
+                      <option id="pageWidthOption" title="" value="page-width" data-l10n-id="page_scale_width">Full Width</option>
+                      <option id="customScaleOption" title="" value="custom"></option>
+                      <option title="" value="0.5">50%</option>
+                      <option title="" value="0.75">75%</option>
+                      <option title="" value="1">100%</option>
+                      <option title="" value="1.25">125%</option>
+                      <option title="" value="1.5">150%</option>
+                      <option title="" value="2">200%</option>
                     </select>
                   </span>
                 </div>
               </div>
             </div>
             <div id="loadingBar">
               <div class="progress">
                 <div class="glimmer">
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -515,16 +515,28 @@ var DownloadManager = (function Download
   DownloadManager.prototype = {
     downloadUrl: function DownloadManager_downloadUrl(url, filename) {
       FirefoxCom.request('download', {
         originalUrl: url,
         filename: filename
       });
     },
 
+    downloadData: function DownloadManager_downloadData(data, filename,
+                                                        contentType) {
+      var blobUrl = PDFJS.createObjectURL(data, contentType);
+      
+      FirefoxCom.request('download', {
+        blobUrl: blobUrl,
+        originalUrl: blobUrl,
+        filename: filename,
+        isAttachment: true
+      });
+    },
+
     download: function DownloadManager_download(blob, url, filename) {
       var blobUrl = window.URL.createObjectURL(blob);
 
       FirefoxCom.request('download', {
         blobUrl: blobUrl,
         originalUrl: url,
         filename: filename
       },
@@ -2824,26 +2836,26 @@ var PDFView = {
     }
   },
 
   setTitle: function pdfViewSetTitle(title) {
     document.title = title;
   },
 
   close: function pdfViewClose() {
+    var errorWrapper = document.getElementById('errorWrapper');
+    errorWrapper.setAttribute('hidden', 'true');
+
     if (!this.pdfDocument) {
       return;
     }
 
     this.pdfDocument.destroy();
     this.pdfDocument = null;
 
-    var errorWrapper = document.getElementById('errorWrapper');
-    errorWrapper.setAttribute('hidden', 'true');
-
     var thumbsView = document.getElementById('thumbnailView');
     while (thumbsView.hasChildNodes()) {
       thumbsView.removeChild(thumbsView.lastChild);
     }
 
     if ('_loadingInterval' in thumbsView) {
       clearInterval(thumbsView._loadingInterval);
     }
@@ -2857,21 +2869,20 @@ var PDFView = {
       PDFBug.cleanup();
     }
   },
 
   // TODO(mack): This function signature should really be pdfViewOpen(url, args)
   open: function pdfViewOpen(url, scale, password,
                              pdfDataRangeTransport, args) {
     if (this.pdfDocument) {
-      this.close();
-
       // Reload the preferences if a document was previously opened.
       Preferences.reload();
     }
+    this.close();
 
     var parameters = {password: password};
     if (typeof url === 'string') { // URL
       this.setTitleUsingUrl(url);
       parameters.url = url;
     } else if (url && 'byteLength' in url) { // ArrayBuffer
       parameters.data = url;
     }
@@ -3302,16 +3313,20 @@ var PDFView = {
                 if (!self.sidebarOpen) {
                   document.getElementById('sidebarToggle').click();
                 }
                 self.switchSidebarView('outline');
               }
             });
         }
       });
+      pdfDocument.getAttachments().then(function(attachments) {
+        self.attachments = new DocumentAttachmentsView(attachments);
+        document.getElementById('viewAttachments').disabled = !attachments;
+      });
     });
 
     pdfDocument.getMetadata().then(function(data) {
       var info = data.info, metadata = data.metadata;
       self.documentInfo = info;
       self.metadata = metadata;
 
       // Provides some basic debug information
@@ -3543,70 +3558,91 @@ var PDFView = {
       if (dest) {
         var currentPage = this.pages[(pageNumber || this.page) - 1];
         currentPage.scrollIntoView(dest);
       } else if (pageNumber) {
         this.page = pageNumber; // simple page
       }
       if ('pagemode' in params) {
         var toggle = document.getElementById('sidebarToggle');
-        if (params.pagemode === 'thumbs' || params.pagemode === 'bookmarks') {
+        if (params.pagemode === 'thumbs' || params.pagemode === 'bookmarks' ||
+            params.pagemode === 'attachments') {
           if (!this.sidebarOpen) {
             toggle.click();
           }
-          this.switchSidebarView(params.pagemode === 'thumbs' ?
-                                 'thumbs' : 'outline');
+          this.switchSidebarView(params.pagemode === 'bookmarks' ?
+                                   'outline' :
+                                   params.pagemode);
         } else if (params.pagemode === 'none' && this.sidebarOpen) {
           toggle.click();
         }
       }
     } else if (/^\d+$/.test(hash)) { // page number
       this.page = hash;
     } else { // named destination
       PDFHistory.updateNextHashParam(unescape(hash));
       PDFView.navigateTo(unescape(hash));
     }
   },
 
   switchSidebarView: function pdfViewSwitchSidebarView(view) {
     var thumbsView = document.getElementById('thumbnailView');
     var outlineView = document.getElementById('outlineView');
+    var attachmentsView = document.getElementById('attachmentsView');
 
     var thumbsButton = document.getElementById('viewThumbnail');
     var outlineButton = document.getElementById('viewOutline');
+    var attachmentsButton = document.getElementById('viewAttachments');
 
     switch (view) {
       case 'thumbs':
-        var wasOutlineViewVisible = thumbsView.classList.contains('hidden');
+        var wasAnotherViewVisible = thumbsView.classList.contains('hidden');
 
         thumbsButton.classList.add('toggled');
         outlineButton.classList.remove('toggled');
+        attachmentsButton.classList.remove('toggled');
         thumbsView.classList.remove('hidden');
         outlineView.classList.add('hidden');
+        attachmentsView.classList.add('hidden');
 
         PDFView.renderHighestPriority();
 
-        if (wasOutlineViewVisible) {
+        if (wasAnotherViewVisible) {
           // Ensure that the thumbnail of the current page is visible
-          // when switching from the outline view.
+          // when switching from another view.
           scrollIntoView(document.getElementById('thumbnailContainer' +
                                                  this.page));
         }
         break;
 
       case 'outline':
         thumbsButton.classList.remove('toggled');
         outlineButton.classList.add('toggled');
+        attachmentsButton.classList.remove('toggled');
         thumbsView.classList.add('hidden');
         outlineView.classList.remove('hidden');
+        attachmentsView.classList.add('hidden');
 
         if (outlineButton.getAttribute('disabled')) {
           return;
         }
         break;
+
+      case 'attachments':
+        thumbsButton.classList.remove('toggled');
+        outlineButton.classList.remove('toggled');
+        attachmentsButton.classList.add('toggled');
+        thumbsView.classList.add('hidden');
+        outlineView.classList.add('hidden');
+        attachmentsView.classList.remove('hidden');
+
+        if (attachmentsButton.getAttribute('disabled')) {
+          return;
+        }
+        break;
     }
   },
 
   getVisiblePages: function pdfViewGetVisiblePages() {
     if (!PresentationMode.active) {
       return this.getVisibleElements(this.container, this.pages, true);
     } else {
       // The algorithm in getVisibleElements doesn't work in all browsers and
@@ -3668,17 +3704,17 @@ var PDFView = {
     }
     return {first: first, last: last, views: visible};
   },
 
   // Helper function to parse query string (e.g. ?param1=value&parm2=...).
   parseQueryString: function pdfViewParseQueryString(query) {
     var parts = query.split('&');
     var params = {};
-    for (var i = 0, ii = parts.length; i < parts.length; ++i) {
+    for (var i = 0, ii = parts.length; i < ii; ++i) {
       var param = parts[i].split('=');
       var key = param[0];
       var value = param.length > 1 ? param[1] : null;
       params[decodeURIComponent(key)] = decodeURIComponent(value);
     }
     return params;
   },
 
@@ -3859,37 +3895,46 @@ var PageView = function pageView(contain
   this.destroy = function pageViewDestroy() {
     this.zoomLayer = null;
     this.reset();
     if (this.pdfPage) {
       this.pdfPage.destroy();
     }
   };
 
-  this.reset = function pageViewReset() {
+  this.reset = function pageViewReset(keepAnnotations) {
     if (this.renderTask) {
       this.renderTask.cancel();
     }
     this.resume = null;
     this.renderingState = RenderingStates.INITIAL;
 
     div.style.width = Math.floor(this.viewport.width) + 'px';
     div.style.height = Math.floor(this.viewport.height) + 'px';
 
     var childNodes = div.childNodes;
     for (var i = div.childNodes.length - 1; i >= 0; i--) {
       var node = childNodes[i];
-      if (this.zoomLayer && this.zoomLayer === node) {
+      if ((this.zoomLayer && this.zoomLayer === node) ||
+          (keepAnnotations && this.annotationLayer === node)) {
         continue;
       }
       div.removeChild(node);
     }
     div.removeAttribute('data-loaded');
 
-    this.annotationLayer = null;
+    if (keepAnnotations) {
+      if (this.annotationLayer) {
+        // Hide annotationLayer until all elements are resized
+        // so they are not displayed on the already-resized page
+        this.annotationLayer.setAttribute('hidden', 'true');
+      }
+    } else {
+      this.annotationLayer = null;
+    }
 
     delete this.canvas;
 
     this.loadingIconDiv = document.createElement('div');
     this.loadingIconDiv.className = 'loadingIcon';
     div.appendChild(this.loadingIconDiv);
   };
 
@@ -3911,17 +3956,17 @@ var PageView = function pageView(contain
       return;
     } else if (this.canvas && !this.zoomLayer) {
       this.zoomLayer = this.canvas.parentNode;
       this.zoomLayer.style.position = 'absolute';
     }
     if (this.zoomLayer) {
       this.cssTransform(this.zoomLayer.firstChild);
     }
-    this.reset();
+    this.reset(true);
   };
 
   this.cssTransform = function pageCssTransform(canvas) {
     // Scale canvas, canvas wrapper, and page container.
     var width = this.viewport.width;
     var height = this.viewport.height;
     canvas.style.width = canvas.parentNode.style.width = div.style.width =
         Math.floor(width) + 'px';
@@ -4060,70 +4105,83 @@ var PageView = function pageView(contain
             break; // No action according to spec
         }
         return false;
       };
       link.className = 'internalLink';
     }
 
     pdfPage.getAnnotations().then(function(annotationsData) {
+      viewport = viewport.clone({ dontFlip: true });
+      var transform = viewport.transform;
+      var transformStr = 'matrix(' + transform.join(',') + ')';
+      var data, element, i, ii;
+
       if (self.annotationLayer) {
-        // If an annotationLayer already exists, delete it to avoid creating
-        // duplicate annotations when rapidly re-zooming the document.
-        pageDiv.removeChild(self.annotationLayer);
-        self.annotationLayer = null;
-      }
-      viewport = viewport.clone({ dontFlip: true });
-      for (var i = 0; i < annotationsData.length; i++) {
-        var data = annotationsData[i];
-        var annotation = PDFJS.Annotation.fromData(data);
-        if (!annotation || !annotation.hasHtml()) {
-          continue;
+        // If an annotationLayer already exists, refresh its children's
+        // transformation matrices
+        for (i = 0, ii = annotationsData.length; i < ii; i++) {
+          data = annotationsData[i];
+          element = self.annotationLayer.querySelector(
+            '[data-annotation-id="' + data.id + '"]');
+          if (element) {
+            CustomStyle.setProp('transform', element, transformStr);
+          }
         }
-
-        var element = annotation.getHtmlElement(pdfPage.commonObjs);
-        mozL10n.translate(element);
-
-        data = annotation.getData();
-        var rect = data.rect;
-        var view = pdfPage.view;
-        rect = PDFJS.Util.normalizeRect([
-          rect[0],
-          view[3] - rect[1] + view[1],
-          rect[2],
-          view[3] - rect[3] + view[1]
-        ]);
-        element.style.left = rect[0] + 'px';
-        element.style.top = rect[1] + 'px';
-        element.style.position = 'absolute';
-
-        var transform = viewport.transform;
-        var transformStr = 'matrix(' + transform.join(',') + ')';
-        CustomStyle.setProp('transform', element, transformStr);
-        var transformOriginStr = -rect[0] + 'px ' + -rect[1] + 'px';
-        CustomStyle.setProp('transformOrigin', element, transformOriginStr);
-
-        if (data.subtype === 'Link' && !data.url) {
-          var link = element.getElementsByTagName('a')[0];
-          if (link) {
-            if (data.action) {
-              bindNamedAction(link, data.action);
-            } else {
-              bindLink(link, ('dest' in data) ? data.dest : null);
+        // See this.reset()
+        self.annotationLayer.removeAttribute('hidden');
+      } else {
+        for (i = 0, ii = annotationsData.length; i < ii; i++) {
+          data = annotationsData[i];
+          var annotation = PDFJS.Annotation.fromData(data);
+          if (!annotation || !annotation.hasHtml()) {
+            continue;
+          }
+
+          element = annotation.getHtmlElement(pdfPage.commonObjs);
+          element.setAttribute('data-annotation-id', data.id);
+          mozL10n.translate(element);
+
+          data = annotation.getData();
+          var rect = data.rect;
+          var view = pdfPage.view;
+          rect = PDFJS.Util.normalizeRect([
+            rect[0],
+            view[3] - rect[1] + view[1],
+            rect[2],
+            view[3] - rect[3] + view[1]
+          ]);
+          element.style.left = rect[0] + 'px';
+          element.style.top = rect[1] + 'px';
+          element.style.position = 'absolute';
+
+          CustomStyle.setProp('transform', element, transformStr);
+          var transformOriginStr = -rect[0] + 'px ' + -rect[1] + 'px';
+          CustomStyle.setProp('transformOrigin', element, transformOriginStr);
+
+          if (data.subtype === 'Link' && !data.url) {
+            var link = element.getElementsByTagName('a')[0];
+            if (link) {
+              if (data.action) {
+                bindNamedAction(link, data.action);
+              } else {
+                bindLink(link, ('dest' in data) ? data.dest : null);
+              }
             }
           }
+
+          if (!self.annotationLayer) {
+            var annotationLayerDiv = document.createElement('div');
+            annotationLayerDiv.className = 'annotationLayer';
+            pageDiv.appendChild(annotationLayerDiv);
+            self.annotationLayer = annotationLayerDiv;
+          }
+
+          self.annotationLayer.appendChild(element);
         }
-
-        if (!self.annotationLayer) {
-          var annotationLayerDiv = document.createElement('div');
-          annotationLayerDiv.className = 'annotationLayer';
-          pageDiv.appendChild(annotationLayerDiv);
-          self.annotationLayer = annotationLayerDiv;
-        }
-        self.annotationLayer.appendChild(element);
       }
     });
   }
 
   this.getPagePoint = function pageViewGetPagePoint(x, y) {
     return this.viewport.convertToPdfPoint(x, y);
   };
 
@@ -4249,20 +4307,24 @@ var PageView = function pageView(contain
     var canvasWrapper = document.createElement('div');
     canvasWrapper.style.width = div.style.width;
     canvasWrapper.style.height = div.style.height;
     canvasWrapper.classList.add('canvasWrapper');
 
     var canvas = document.createElement('canvas');
     canvas.id = 'page' + this.id;
     canvasWrapper.appendChild(canvas);
-    div.appendChild(canvasWrapper);
+    if (this.annotationLayer) {
+      // annotationLayer needs to stay on top
+      div.insertBefore(canvasWrapper, this.annotationLayer);
+    } else {
+      div.appendChild(canvasWrapper);
+    }
     this.canvas = canvas;
 
-    var scale = this.scale;
     var ctx = canvas.getContext('2d');
     var outputScale = getOutputScale(ctx);
 
     if (USE_ONLY_CSS_ZOOM) {
       var actualSizeViewport = viewport.clone({ scale: CSS_UNITS });
       // Use a scale that will make the canvas be the original intended size
       // of the page.
       outputScale.sx *= actualSizeViewport.width / viewport.width;
@@ -4278,17 +4340,22 @@ var PageView = function pageView(contain
     canvas._viewport = viewport;
 
     var textLayerDiv = null;
     if (!PDFJS.disableTextLayer) {
       textLayerDiv = document.createElement('div');
       textLayerDiv.className = 'textLayer';
       textLayerDiv.style.width = canvas.style.width;
       textLayerDiv.style.height = canvas.style.height;
-      div.appendChild(textLayerDiv);
+      if (this.annotationLayer) {
+        // annotationLayer needs to stay on top
+        div.insertBefore(textLayerDiv, this.annotationLayer);
+      } else {
+        div.appendChild(textLayerDiv);
+      }
     }
     var textLayer = this.textLayer =
       textLayerDiv ? new TextLayerBuilder({
         textLayerDiv: textLayerDiv,
         pageIndex: this.id - 1,
         lastScrollSource: PDFView,
         viewport: this.viewport,
         isViewerInPresentationMode: PresentationMode.active
@@ -4420,17 +4487,16 @@ var PageView = function pageView(contain
 
     var printContainer = document.getElementById('printContainer');
     var canvasWrapper = document.createElement('div');
     canvasWrapper.style.width = viewport.width + 'pt';
     canvasWrapper.style.height = viewport.height + 'pt';
     canvasWrapper.appendChild(canvas);
     printContainer.appendChild(canvasWrapper);
 
-    var self = this;
     canvas.mozPrintCallback = function(obj) {
       var ctx = obj.context;
 
       ctx.save();
       ctx.fillStyle = 'rgb(255, 255, 255)';
       ctx.fillRect(0, 0, canvas.width, canvas.height);
       ctx.restore();
       ctx.scale(PRINT_OUTPUT_SCALE, PRINT_OUTPUT_SCALE);
@@ -4789,19 +4855,16 @@ var TextLayerBuilder = function textLaye
   this.convertMatches = function textLayerBuilderConvertMatches(matches) {
     var i = 0;
     var iIndex = 0;
     var bidiTexts = this.textContent.items;
     var end = bidiTexts.length - 1;
     var queryLen = (PDFFindController === null ?
                     0 : PDFFindController.state.query.length);
 
-    var lastDivIdx = -1;
-    var pos;
-
     var ret = [];
 
     // Loop over all the matches.
     for (var m = 0; m < matches.length; m++) {
       var matchIdx = matches[m];
       // # Calculate the begin position.
 
       // Loop over the divIdxs.
@@ -4982,17 +5045,16 @@ var TextLayerBuilder = function textLaye
     this.renderMatches(this.matches);
   };
 };
 
 
 
 var DocumentOutlineView = function documentOutlineView(outline) {
   var outlineView = document.getElementById('outlineView');
-  var outlineButton = document.getElementById('viewOutline');
   while (outlineView.firstChild) {
     outlineView.removeChild(outlineView.firstChild);
   }
 
   if (!outline) {
     if (!outlineView.classList.contains('hidden')) {
       PDFView.switchSidebarView('thumbs');
     }
@@ -5028,16 +5090,54 @@ var DocumentOutlineView = function docum
         queue.push({parent: itemsDiv, items: item.items});
       }
 
       levelData.parent.appendChild(div);
     }
   }
 };
 
+var DocumentAttachmentsView = function documentAttachmentsView(attachments) {
+  var attachmentsView = document.getElementById('attachmentsView');
+  while (attachmentsView.firstChild) {
+    attachmentsView.removeChild(attachmentsView.firstChild);
+  }
+
+  if (!attachments) {
+    if (!attachmentsView.classList.contains('hidden')) {
+      PDFView.switchSidebarView('thumbs');
+    }
+    return;
+  }
+
+  function bindItemLink(domObj, item) {
+    domObj.href = '#';
+    domObj.onclick = function documentAttachmentsViewOnclick(e) {
+      var downloadManager = new DownloadManager();
+      downloadManager.downloadData(item.content, getFileName(item.filename),
+                                   '');
+      return false;
+    };
+  }
+
+  var names = Object.keys(attachments).sort(function(a,b) {
+    return a.toLowerCase().localeCompare(b.toLowerCase());
+  });
+  for (var i = 0, ii = names.length; i < ii; i++) {
+    var item = attachments[names[i]];
+    var div = document.createElement('div');
+    div.className = 'attachmentsItem';
+    var a = document.createElement('a');
+    bindItemLink(a, item);
+    a.textContent = getFileName(item.filename);
+    div.appendChild(a);
+    attachmentsView.appendChild(div);
+  }
+};
+
 
 function webViewerLoad(evt) {
   PDFView.initialize().then(webViewerInitialized);
 }
 
 function webViewerInitialized() {
   var file = window.location.href.split('#')[0];
 
@@ -5161,16 +5261,21 @@ function webViewerInitialized() {
       PDFView.switchSidebarView('thumbs');
     });
 
   document.getElementById('viewOutline').addEventListener('click',
     function() {
       PDFView.switchSidebarView('outline');
     });
 
+  document.getElementById('viewAttachments').addEventListener('click',
+    function() {
+      PDFView.switchSidebarView('attachments');
+    });
+
   document.getElementById('previous').addEventListener('click',
     function() {
       PDFView.page--;
     });
 
   document.getElementById('next').addEventListener('click',
     function() {
       PDFView.page++;
--- a/browser/locales/en-US/chrome/browser/customizableui/customizableWidgets.properties
+++ b/browser/locales/en-US/chrome/browser/customizableui/customizableWidgets.properties
@@ -6,55 +6,52 @@ history-panelmenu.label = History
 # LOCALIZATION NOTE(history-panelmenu.tooltiptext2): %S is the keyboard shortcut
 history-panelmenu.tooltiptext2 = Show your history (%S)
 
 privatebrowsing-button.label = New Private Window
 # LOCALIZATION NOTE(privatebrowsing-button.tooltiptext): %S is the keyboard shortcut
 privatebrowsing-button.tooltiptext = Open a new Private Browsing window (%S)
 
 save-page-button.label = Save Page
-# LOCALIZATION NOTE(save-page-button.tooltiptext2): %S is the keyboard shortcut
-# Use the unicode ellipsis char, \u2026, or use "..." if \u2026 doesn't suit traditions in your locale.
-save-page-button.tooltiptext2 = Save this page… (%S)
+# LOCALIZATION NOTE(save-page-button.tooltiptext3): %S is the keyboard shortcut
+save-page-button.tooltiptext3 = Save this page (%S)
 
 find-button.label = Find
-# LOCALIZATION NOTE(find-button.tooltiptext2): %S is the keyboard shortcut.
-# Use the unicode ellipsis char, \u2026, or use "..." if \u2026 doesn't suit traditions in your locale.
-find-button.tooltiptext2 = Find in this page… (%S)
+# LOCALIZATION NOTE(find-button.tooltiptext3): %S is the keyboard shortcut.
+find-button.tooltiptext3 = Find in this page (%S)
 
 open-file-button.label = Open File
-# LOCALIZATION NOTE (open-file-button.tooltiptext2): %S is the keyboard shortcut.
-# Use the unicode ellipsis char, \u2026, or use "..." if \u2026 doesn't suit traditions in your locale.
-open-file-button.tooltiptext2 = Open file… (%S)
+# LOCALIZATION NOTE (open-file-button.tooltiptext3): %S is the keyboard shortcut.
+open-file-button.tooltiptext3 = Open a file (%S)
 
 developer-button.label = Developer
 # LOCALIZATION NOTE(developer-button.tooltiptext): %S is the keyboard shortcut
-developer-button.tooltiptext = Web Developer Tools (%S)
+developer-button.tooltiptext2 = Open Web developer tools (%S)
 
 sidebar-button.label = Sidebars
-sidebar-button.tooltiptext = Show Sidebars
+sidebar-button.tooltiptext2 = Show sidebars
 
 add-ons-button.label = Add-ons
-# LOCALIZATION NOTE(add-ons-button.tooltiptext2): %S is the keyboard shortcut
-add-ons-button.tooltiptext2 = Open Add-ons Manager (%S)
+# LOCALIZATION NOTE(add-ons-button.tooltiptext3): %S is the keyboard shortcut
+add-ons-button.tooltiptext3 = Manage your add-ons (%S)
 
 switch-to-metro-button2.label = Windows 8 Touch
 # LOCALIZATION NOTE(switch-to-metro-button2.tooltiptext): %S is the brand short name
 switch-to-metro-button2.tooltiptext = Relaunch in %S for Windows 8 Touch
 
 preferences-button.label = Preferences
-preferences-button.tooltiptext2 = Open Preferences
-preferences-button.tooltiptext.withshortcut = Open Preferences (%S)
+preferences-button.tooltiptext2 = Open preferences
+preferences-button.tooltiptext.withshortcut = Open preferences (%S)
 # LOCALIZATION NOTE (preferences-button.labelWin): Windows-only label for Options
 preferences-button.labelWin = Options
 # LOCALIZATION NOTE (preferences-button.tooltipWin): Windows-only tooltip for Options
-preferences-button.tooltipWin2 = Open Options
+preferences-button.tooltipWin2 = Open options
 
 zoom-controls.label = Zoom Controls
-zoom-controls.tooltiptext = Zoom Controls
+zoom-controls.tooltiptext2 = Zoom controls
 
 zoom-out-button.label = Zoom out
 # LOCALIZATION NOTE(zoom-out-button.tooltiptext2): %S is the keyboard shortcut.
 zoom-out-button.tooltiptext2 = Zoom out (%S)
 
 # LOCALIZATION NOTE(zoom-reset-button.label): %S is the current zoom level,
 # %% will be displayed as a single % character (% is commonly used to define
 # format specifiers, so it needs to be escaped).
@@ -62,44 +59,40 @@ zoom-reset-button.label = %S%%
 # LOCALIZATION NOTE(zoom-reset-button.tooltiptext2): %S is the keyboard shortcut.
 zoom-reset-button.tooltiptext2 = Reset zoom level (%S)
 
 zoom-in-button.label = Zoom in
 # LOCALIZATION NOTE(zoom-in-button.tooltiptext2): %S is the keyboard shortcut.
 zoom-in-button.tooltiptext2 = Zoom in (%S)
 
 edit-controls.label = Edit Controls
-edit-controls.tooltiptext = Edit Controls
+edit-controls.tooltiptext2 = Edit controls
 
 cut-button.label = Cut
 # LOCALIZATION NOTE(cut-button.tooltiptext2): %S is the keyboard shortcut.
 cut-button.tooltiptext2 = Cut (%S)
 
 copy-button.label = Copy
 # LOCALIZATION NOTE(copy-button.tooltiptext2): %S is the keyboard shortcut.
 copy-button.tooltiptext2 = Copy (%S)
 
 paste-button.label = Paste
 # LOCALIZATION NOTE(paste-button.tooltiptext2): %S is the keyboard shortcut.
 paste-button.tooltiptext2 = Paste (%S)
 
-# LOCALIZATION NOTE (feed-button.tooltiptext): Use the unicode ellipsis char,
-# \u2026, or use "..." if \u2026 doesn't suit traditions in your locale.
 feed-button.label = Subscribe
-feed-button.tooltiptext = Subscribe to this page…
+feed-button.tooltiptext2 = Subscribe to this page
 
 # LOCALIZATION NOTE (characterencoding-button.label): The \u00ad character at the beginning
 # of the string is used to disable auto hyphenation on the button text when it is displayed
 # in the menu panel.
 characterencoding-button.label = \u00adCharacter Encoding
-characterencoding-button.tooltiptext2 = Show Character Encoding options
+characterencoding-button.tooltiptext2 = Show character encoding options
 
 email-link-button.label = Email Link
-# LOCALIZATION NOTE (email-link-button.tooltiptext2): Use the unicode ellipsis char,
-# \u2026, or use "..." if \u2026 doesn't suit traditions in your locale.
-email-link-button.tooltiptext2 = Email Link…
+email-link-button.tooltiptext3 = Email a link to this page
 
 # LOCALIZATION NOTE(quit-button.tooltiptext.linux2): %1$S is the brand name (e.g. Firefox),
 # %2$S is the keyboard shortcut
 quit-button.tooltiptext.linux2 = Quit %1$S (%2$S)
 # LOCALIZATION NOTE(quit-button.tooltiptext.mac): %1$S is the brand name (e.g. Firefox),
 # %2$S is the keyboard shortcut
 quit-button.tooltiptext.mac = Quit %1$S (%2$S)
--- a/browser/locales/en-US/chrome/browser/devtools/webconsole.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/webconsole.properties
@@ -217,8 +217,13 @@ messageRepeats.tooltip2=#1 repeat;#1 rep
 # LOCALIZATION NOTE (openNodeInInspector): the text that is displayed in a
 # tooltip when hovering over the inspector icon next to a DOM Node in the console
 # output
 openNodeInInspector=Click to select the node in the inspector
 
 # LOCALIZATION NOTE (cdFunctionInvalidArgument): the text that is displayed when
 # cd() is invoked with an invalid argument.
 cdFunctionInvalidArgument=Cannot cd() to the given window. Invalid argument.
+
+# LOCALIZATION NOTE (messageToggleDetails): the text that is displayed when
+# you hover the arrow for expanding/collapsing the message details. For
+# console.error() and other messages we show the stacktrace.
+messageToggleDetails=Show/hide message details.
--- a/browser/locales/en-US/pdfviewer/viewer.properties
+++ b/browser/locales/en-US/pdfviewer/viewer.properties
@@ -84,16 +84,18 @@ document_properties_close=Close
 
 # Tooltips and alt text for side panel toolbar buttons
 # (the _label strings are alt text for the buttons, the .title strings are
 # tooltips)
 toggle_sidebar.title=Toggle Sidebar
 toggle_sidebar_label=Toggle Sidebar
 outline.title=Show Document Outline
 outline_label=Document Outline
+attachments.title=Show Attachments
+attachments_label=Attachments
 thumbs.title=Show Thumbnails
 thumbs_label=Thumbnails
 findbar.title=Find in Document
 findbar_label=Find
 
 # Thumbnails panel item (tooltip and alt text for images)
 # LOCALIZATION NOTE (thumb_page_title): "{{page}}" will be replaced by the page
 # number.
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -6,19 +6,21 @@
 
 this.EXPORTED_SYMBOLS = ["webrtcUI"];
 
 const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/PluralForm.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
+                                  "resource://gre/modules/PluralForm.jsm");
+
 XPCOMUtils.defineLazyServiceGetter(this, "MediaManagerService",
                                    "@mozilla.org/mediaManagerService;1",
                                    "nsIMediaManagerService");
 
 this.webrtcUI = {
   init: function () {
     Services.obs.addObserver(handleRequest, "getUserMedia:request", false);
     Services.obs.addObserver(updateIndicators, "recording-device-events", false);
--- a/browser/themes/shared/devtools/webconsole.inc.css
+++ b/browser/themes/shared/devtools/webconsole.inc.css
@@ -33,20 +33,18 @@ a {
   background: -moz-image-rect(url(chrome://browser/skin/devtools/webconsole.png), 0, 1, 0, 0) no-repeat;
   background-position: center 0.5em;
   flex: 0 0 auto;
   margin: 0 6px;
   padding: 0 4px;
   width: 8px;
 }
 
-.message > .body {
+.message > .message-body-wrapper {
   flex: 1 1 100%;
-  white-space: pre-wrap;
-  word-wrap: break-word;
   margin-top: 4px;
 }
 
 /* The red bubble that shows the number of times a message is repeated */
 .message-repeats {
   -moz-user-select: none;
   flex: 0 0 auto;
   margin: 2px 6px;
@@ -66,17 +64,17 @@ a {
 
 .message-location {
   -moz-margin-start: 6px;
   display: flex;
   flex: 0 0 auto;
   align-self: flex-start;
   justify-content: flex-end;
   width: 10em;
-  margin-top: 4px;
+  margin: 3px 0;
   color: -moz-nativehyperlinktext;
   text-decoration: none;
   white-space: nowrap;
 }
 
 .message-location:hover,
 .message-location:focus {
   text-decoration: underline;
@@ -87,16 +85,30 @@ a {
   text-align: end;
   overflow: hidden;
 }
 
 .message-location > .line-number {
   flex: 0 0 auto;
 }
 
+.message-flex-body {
+  display: flex;
+}
+
+.message-body {
+  white-space: pre-wrap;
+  word-wrap: break-word;
+}
+
+.message-flex-body > .message-body {
+  display: block;
+  flex: 1 1 auto;
+}
+
 .jsterm-input-container {
   border-top-width: 1px;
   border-top-style: solid;
 }
 
 #output-wrapper {
   direction: ltr;
   overflow: auto;
@@ -148,25 +160,33 @@ a {
 }
 
 /* Network styles */
 .webconsole-filter-button[category="net"] > .toolbarbutton-menubutton-button:before {
   background-image: linear-gradient(#444444, #000000);
   border-color: #777;
 }
 
+.theme-light .message[severity=error] {
+  background-color: rgba(255, 150, 150, 0.3);
+}
+
+.theme-dark .message[severity=error] {
+  background-color: rgba(255, 100, 100, 0.3);
+}
+
 .message[category=network] > .icon {
   -moz-border-start: solid #000 6px;
 }
 
 .message[category=network][severity=error] > .icon {
   background-image: -moz-image-rect(url(chrome://browser/skin/devtools/webconsole.png), 0, 16, 8, 8);
 }
 
-.message[category=network] > .body {
+.message[category=network] > .message-body {
   display: flex;
 }
 
 .message[category=network] .method {
   flex: 0 0 auto;
 }
 
 .message[category=network]:not(.navigation-marker) .url {
@@ -279,17 +299,17 @@ a {
   background: -moz-image-rect(url("chrome://browser/skin/devtools/commandline-icon.png"), 0, 32, 16, 16) no-repeat;
 }
 
 :-moz-any(.jsterm-input-node,
           .jsterm-complete-node) > .textbox-input-box > .textbox-textarea {
   overflow-x: hidden;
 }
 
-.inlined-variables-view .body {
+.inlined-variables-view .message-body {
   display: flex;
   flex-direction: column;
 }
 .inlined-variables-view iframe {
   display: block;
   flex: 1;
   margin-top: 5px;
   margin-bottom: 15px;
@@ -335,35 +355,43 @@ a {
   font-size: 0.9em;
 }
 
 .navigation-marker .url {
   -moz-padding-end: 9px;
   text-decoration: none;
 }
 
-.consoleTrace .body > div {
-  display: flex;
-  margin-bottom: 5px;
-}
-
-.consoleTrace .title {
-  display: block;
-  flex: 1 1 auto;
+.stacktrace {
+  display: none;
+  list-style: none;
+  padding: 0 1em 0 1.5em;
+  margin: 5px 0 0 0;
+  max-height: 10em;
+  overflow-y: auto;
+  border: 1px solid rgb(200,200,200);
+  border-radius: 3px;
 }
 
-.stacktrace {
-  list-style: none;
-  padding: 0 1em 0 1.5em;
-  margin: 0;
-  max-height: 10em;
-  overflow-y: auto;
+.theme-light .message[severity=error] .stacktrace {
+  background-color: rgba(255, 255, 255, 0.5);
+}
+
+.theme-dark .message[severity=error] .stacktrace {
+  background-color: rgba(0, 0, 0, 0.5);
+}
 
-  border: 1px solid rgba(128, 128, 128, .5);
-  border-radius: 3px;
+.message[open] .stacktrace {
+  display: block;
+}
+
+.message .theme-twisty {
+  display: inline-block;
+  vertical-align: middle;
+  margin: 2px 3px 0 0;
 }
 
 .stacktrace li {
   display: flex;
   margin: 0;
 }
 
 .stacktrace .function {
--- a/config/system-headers
+++ b/config/system-headers
@@ -1042,18 +1042,16 @@ X11/Xft/Xft.h
 X11/Xfuncproto.h
 X11/X.h
 X11/XKBlib.h
 X11/Xlib.h
 X11/Xlibint.h
 X11/Xlocale.h
 X11/Xos.h
 X11/Xutil.h
-xpt_struct.h
-xpt_xdr.h
 zmouse.h
 speex/speex_resampler.h
 soundtouch/SoundTouch.h
 #if MOZ_NATIVE_PNG==1
 png.h
 #endif
 #if MOZ_NATIVE_ZLIB==1
 zlib.h
--- a/content/base/public/nsDeprecatedOperationList.h
+++ b/content/base/public/nsDeprecatedOperationList.h
@@ -34,8 +34,9 @@ DEPRECATED_OPERATION(GetSetUserData)
 DEPRECATED_OPERATION(MozGetAsFile)
 DEPRECATED_OPERATION(UseOfCaptureEvents)
 DEPRECATED_OPERATION(UseOfReleaseEvents)
 DEPRECATED_OPERATION(UseOfDOM3LoadMethod)
 DEPRECATED_OPERATION(ShowModalDialog)
 DEPRECATED_OPERATION(Window_Content)
 DEPRECATED_OPERATION(SyncXMLHttpRequest)
 DEPRECATED_OPERATION(DataContainerEvent)
+DEPRECATED_OPERATION(SendAsBinary)
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -2256,16 +2256,20 @@ nsXMLHttpRequest::SendAsBinary(const nsA
                                ErrorResult& aRv)
 {
   char *data = static_cast<char*>(NS_Alloc(aBody.Length() + 1));
   if (!data) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
 
+  if (GetOwner() && GetOwner()->GetExtantDoc()) {
+    GetOwner()->GetExtantDoc()->WarnOnceAbout(nsIDocument::eSendAsBinary);
+  }
+
   nsAString::const_iterator iter, end;
   aBody.BeginReading(iter);
   aBody.EndReading(end);
   char *p = data;
   while (iter != end) {
     if (*iter & 0xFF00) {
       NS_Free(data);
       aRv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR);
--- a/content/base/test/mochitest.ini
+++ b/content/base/test/mochitest.ini
@@ -214,21 +214,19 @@ support-files =
   somedatas.resource
   somedatas.resource^headers^
   variable_style_sheet.sjs
   viewport_helpers.js
   w3element_traversal.svg
   wholeTexty-helper.xml
 
 [test_CrossSiteXHR.html]
-skip-if = toolkit == 'android'
 [test_CrossSiteXHR_cache.html]
-skip-if = toolkit == 'android'
 [test_CrossSiteXHR_origin.html]
-skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # b2g(https not working, bug 907770) b2g-debug(https not working, bug 907770) b2g-desktop(https not working, bug 907770)
+skip-if = buildapp == 'b2g' || e10s # last test fails to trigger onload on e10s/b2g
 [test_DOMException.html]
 [test_EventSource_redirects.html]
 [test_NodeIterator_basics_filters.xhtml]
 [test_NodeIterator_mutations_1.xhtml]
 [test_NodeIterator_mutations_2.html]
 [test_NodeIterator_mutations_3.html]
 [test_XHR.html]
 [test_XHRDocURI.html]
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -2640,18 +2640,17 @@ nsXULPrototypeScript::Compile(const char
     NS_ENSURE_TRUE(JSVersion(mLangVersion) != JSVERSION_UNKNOWN, NS_OK);
     JS::CompileOptions options(cx);
     options.setIntroductionType("scriptElement")
            .setFileAndLine(urlspec.get(), aLineNo)
            .setVersion(JSVersion(mLangVersion));
     // If the script was inline, tell the JS parser to save source for
     // Function.prototype.toSource(). If it's out of line, we retrieve the
     // source from the files on demand.
-    options.setSourcePolicy(mOutOfLine ? JS::CompileOptions::LAZY_SOURCE
-                                       : JS::CompileOptions::SAVE_SOURCE);
+    options.setSourceIsLazy(mOutOfLine);
     JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
     if (scope) {
       JS::ExposeObjectToActiveJS(scope);
     }
 
     if (aOffThreadReceiver && JS::CanCompileOffThread(cx, options, aTextLength)) {
         if (!JS::CompileOffThread(cx, options,
                                   static_cast<const jschar*>(aText), aTextLength,
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -3529,44 +3529,46 @@ EventStateManager::DispatchMouseOrPointe
       NS_WARNING("Should have pointer locked element, but didn't.");
       return nullptr;
     }
     nsCOMPtr<nsIContent> content = do_QueryInterface(pointerLockedElement);
     return mPresContext->GetPrimaryFrameFor(content);
   }
 
   nsEventStatus status = nsEventStatus_eIgnore;
-  nsAutoPtr<WidgetPointerEvent> newPointerEvent;
-  nsAutoPtr<WidgetMouseEvent> newMouseEvent;
-  WidgetMouseEvent* event = nullptr;
+  nsAutoPtr<WidgetMouseEvent> event;
   WidgetPointerEvent* sourcePointer = aMouseEvent->AsPointerEvent();
   if (sourcePointer) {
     PROFILER_LABEL("Input", "DispatchPointerEvent");
+    nsAutoPtr<WidgetPointerEvent> newPointerEvent;
     newPointerEvent =
       new WidgetPointerEvent(aMouseEvent->mFlags.mIsTrusted, aMessage,
                              aMouseEvent->widget);
     newPointerEvent->isPrimary = sourcePointer->isPrimary;
     newPointerEvent->pointerId = sourcePointer->pointerId;
     newPointerEvent->width = sourcePointer->width;
     newPointerEvent->height = sourcePointer->height;
     newPointerEvent->inputSource = sourcePointer->inputSource;
-    event = newPointerEvent.get();
+    newPointerEvent->relatedTarget = nsIPresShell::GetPointerCapturingContent(sourcePointer->pointerId)
+                                       ? nullptr
+                                       : aRelatedContent;
+    event = newPointerEvent.forget();
   } else {
     PROFILER_LABEL("Input", "DispatchMouseEvent");
-    newMouseEvent =
+    event =
       new WidgetMouseEvent(aMouseEvent->mFlags.mIsTrusted, aMessage,
                            aMouseEvent->widget, WidgetMouseEvent::eReal);
-    event = newMouseEvent.get();
+    event->relatedTarget = aRelatedContent;
   }
   event->refPoint = aMouseEvent->refPoint;
   event->modifiers = aMouseEvent->modifiers;
   event->button = aMouseEvent->button;
   event->buttons = aMouseEvent->buttons;
+  event->pressure = aMouseEvent->pressure;
   event->pluginEvent = aMouseEvent->pluginEvent;
-  event->relatedTarget = aRelatedContent;
   event->inputSource = aMouseEvent->inputSource;
 
   nsWeakFrame previousTarget = mCurrentTarget;
 
   mCurrentTargetContent = aTargetContent;
 
   nsIFrame* targetFrame = nullptr;
   if (aTargetContent) {
--- a/dom/ipc/FilePickerParent.cpp
+++ b/dom/ipc/FilePickerParent.cpp
@@ -128,36 +128,43 @@ FilePickerParent::SendFiles(const nsCOMA
   unused << Send__delete__(this, infiles, mResult);
 }
 
 void
 FilePickerParent::Done(int16_t aResult)
 {
   mResult = aResult;
 
+  if (mResult != nsIFilePicker::returnOK) {
+    unused << Send__delete__(this, void_t(), mResult);
+    return;
+  }
+
   nsCOMArray<nsIDOMFile> domfiles;
-
   if (mMode == nsIFilePicker::modeOpenMultiple) {
     nsCOMPtr<nsISimpleEnumerator> iter;
     NS_ENSURE_SUCCESS_VOID(mFilePicker->GetFiles(getter_AddRefs(iter)));
 
     nsCOMPtr<nsISupports> supports;
-    nsCOMPtr<nsIFile> file;
     bool loop = true;
     while (NS_SUCCEEDED(iter->HasMoreElements(&loop)) && loop) {
       iter->GetNext(getter_AddRefs(supports));
-      file = do_QueryInterface(supports);
-      nsCOMPtr<nsIDOMFile> domfile = new nsDOMFileFile(file);
-      domfiles.AppendElement(domfile);
+      if (supports) {
+        nsCOMPtr<nsIFile> file = do_QueryInterface(supports);
+        nsCOMPtr<nsIDOMFile> domfile = new nsDOMFileFile(file);
+        domfiles.AppendElement(domfile);
+      }
     }
   } else {
     nsCOMPtr<nsIFile> file;
     mFilePicker->GetFile(getter_AddRefs(file));
-    nsCOMPtr<nsIDOMFile> domfile = new nsDOMFileFile(file);
-    domfiles.AppendElement(domfile);
+    if (file) {
+      nsCOMPtr<nsIDOMFile> domfile = new nsDOMFileFile(file);
+      domfiles.AppendElement(domfile);
+    }
   }
 
   MOZ_ASSERT(!mRunnable);
   mRunnable = new FileSizeAndDateRunnable(this, domfiles);
   if (!mRunnable->Dispatch()) {
     unused << Send__delete__(this, void_t(), nsIFilePicker::returnCancel);
   }
 }
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -113,16 +113,23 @@ static TabChildMap* sTabChildren;
 TabChildBase::TabChildBase()
   : mOldViewportWidth(0.0f)
   , mContentDocumentIsDisplayed(false)
   , mTabChildGlobal(nullptr)
   , mInnerSize(0, 0)
 {
 }
 
+NS_IMPL_CYCLE_COLLECTING_ADDREF(TabChildBase)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(TabChildBase)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TabChildBase)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION_2(TabChildBase, mTabChildGlobal, mGlobal)
+
 void
 TabChildBase::InitializeRootMetrics()
 {
   // Calculate a really simple resolution that we probably won't
   // be keeping, as well as putting the scroll offset back to
   // the top-left of the page.
   mLastRootMetrics.mViewport = CSSRect(CSSPoint(), kDefaultViewportSize);
   mLastRootMetrics.mCompositionBounds = ParentLayerIntRect(
@@ -1348,25 +1355,16 @@ TabChild::ActorDestroy(ActorDestroyReaso
 TabChild::~TabChild()
 {
     DestroyWindow();
 
     nsCOMPtr<nsIWebBrowser> webBrowser = do_QueryInterface(WebNavigation());
     if (webBrowser) {
       webBrowser->SetContainerWindow(nullptr);
     }
-    mGlobal = nullptr;
-
-    if (mTabChildGlobal) {
-      EventListenerManager* elm = mTabChildGlobal->GetExistingListenerManager();
-      if (elm) {
-        elm->Disconnect();
-      }
-      mTabChildGlobal->mTabChild = nullptr;
-    }
 }
 
 void
 TabChild::SetProcessNameToAppName()
 {
   nsCOMPtr<mozIApplication> app = GetOwnApp();
   if (!app) {
     return;
@@ -2717,18 +2715,18 @@ void
 TabChildGlobal::Init()
 {
   NS_ASSERTION(!mMessageManager, "Re-initializing?!?");
   mMessageManager = new nsFrameMessageManager(mTabChild,
                                               nullptr,
                                               MM_CHILD);
 }
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED_1(TabChildGlobal, DOMEventTargetHelper,
-                                     mMessageManager)
+NS_IMPL_CYCLE_COLLECTION_INHERITED_2(TabChildGlobal, DOMEventTargetHelper,
+                                     mMessageManager, mTabChild)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TabChildGlobal)
   NS_INTERFACE_MAP_ENTRY(nsIMessageListenerManager)
   NS_INTERFACE_MAP_ENTRY(nsIMessageSender)
   NS_INTERFACE_MAP_ENTRY(nsISyncMessageSender)
   NS_INTERFACE_MAP_ENTRY(nsIContentFrameMessageManager)
   NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
   NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -133,38 +133,41 @@ public:
     return NS_OK;
   }
 
   virtual JSContext* GetJSContextForEventHandlers() MOZ_OVERRIDE;
   virtual nsIPrincipal* GetPrincipal() MOZ_OVERRIDE;
   virtual JSObject* GetGlobalJSObject() MOZ_OVERRIDE;
 
   nsCOMPtr<nsIContentFrameMessageManager> mMessageManager;
-  TabChildBase* mTabChild;
+  nsRefPtr<TabChildBase> mTabChild;
 };
 
 class ContentListener MOZ_FINAL : public nsIDOMEventListener
 {
 public:
   ContentListener(TabChild* aTabChild) : mTabChild(aTabChild) {}
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMEVENTLISTENER
 protected:
   TabChild* mTabChild;
 };
 
 // This is base clase which helps to share Viewport and touch related functionality
 // between b2g/android FF/embedlite clients implementation.
 // It make sense to place in this class all helper functions, and functionality which could be shared between
 // Cross-process/Cross-thread implmentations.
-class TabChildBase : public nsFrameScriptExecutor,
+class TabChildBase : public nsISupports,
+                     public nsFrameScriptExecutor,
                      public ipc::MessageManagerCallback
 {
 public:
     TabChildBase();
+    NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+    NS_DECL_CYCLE_COLLECTION_CLASS(TabChildBase)
 
     virtual nsIWebNavigation* WebNavigation() = 0;
     virtual nsIWidget* WebWidget() = 0;
     nsIPrincipal* GetPrincipal() { return mPrincipal; }
     bool IsAsyncPanZoomEnabled();
     // Recalculates the display state, including the CSS
     // viewport. This should be called whenever we believe the
     // viewport data on a document may have changed. If it didn't
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -141,8 +141,10 @@ UseOfDOM3LoadMethodWarning=Use of docume
 ShowModalDialogWarning=Use of window.showModalDialog() is deprecated. Use window.open() instead. For more help https://developer.mozilla.org/en-US/docs/Web/API/Window.open
 # LOCALIZATION NOTE: Do not translate "window._content" or "window.content"
 Window_ContentWarning=window._content is deprecated.  Please use window.content instead.
 # LOCALIZATION NOTE: Do not translate "XMLHttpRequest"
 SyncXMLHttpRequestWarning=Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help http://xhr.spec.whatwg.org/
 ImplicitMetaViewportTagFallback=No meta-viewport tag found. Please explicitly specify one to prevent unexpected behavioural changes in future versions. For more help https://developer.mozilla.org/en/docs/Mozilla/Mobile/Viewport_meta_tag
 # LOCALIZATION NOTE: Do not translate "DataContainerEvent" or "CustomEvent"
 DataContainerEventWarning=Use of DataContainerEvent is deprecated. Use CustomEvent instead.
+# LOCALIZATION NOTE: Do not translate "sendAsBinary" or "send(Blob data)"
+SendAsBinaryWarning=The non-standard sendAsBinary method is deprecated and will soon be removed. Use the standard send(Blob data) method instead.
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -345,17 +345,17 @@ RTCPeerConnection.prototype = {
           "RTCPeerConnection is gone (did you enter Offline mode?)");
     }
     return this._pc;
   },
 
   _initIdp: function() {
     let prefName = "media.peerconnection.identity.timeout";
     let idpTimeout = Services.prefs.getIntPref(prefName);
-    let warningFunc = this.reportWarning.bind(this);
+    let warningFunc = this.logWarning.bind(this);
     this._localIdp = new PeerConnectionIdp(this._win, idpTimeout, warningFunc,
                                            this.dispatchEvent.bind(this));
     this._remoteIdp = new PeerConnectionIdp(this._win, idpTimeout, warningFunc,
                                             this.dispatchEvent.bind(this));
   },
 
   /**
    * Add a function to the queue or run it immediately if the queue is empty.
@@ -495,51 +495,48 @@ RTCPeerConnection.prototype = {
     }
   },
 
   dispatchEvent: function(event) {
     this.__DOM_IMPL__.dispatchEvent(event);
   },
 
   // Log error message to web console and window.onerror, if present.
-  reportError: function(msg, file, line) {
-    this.reportMsg(msg, file, line, Ci.nsIScriptError.exceptionFlag);
+  logErrorAndCallOnError: function(msg, file, line) {
+    this.logMsg(msg, file, line, Ci.nsIScriptError.exceptionFlag);
+
+    // Safely call onerror directly if present (necessary for testing)
+    try {
+      if (typeof this._win.onerror === "function") {
+        this._win.onerror(msg, file, line);
+      }
+    } catch(e) {
+      // If onerror itself throws, service it.
+      try {
+        this.logError(e.message, e.fileName, e.lineNumber);
+      } catch(e) {}
+    }
   },
 
-  reportWarning: function(msg, file, line) {
-    this.reportMsg(msg, file, line, Ci.nsIScriptError.warningFlag);
+  logError: function(msg, file, line) {
+    this.logMsg(msg, file, line, Ci.nsIScriptError.errorFlag);
   },
 
-  reportMsg: function(msg, file, line, flag) {
+  logWarning: function(msg, file, line) {
+    this.logMsg(msg, file, line, Ci.nsIScriptError.warningFlag);
+  },
+
+  logMsg: function(msg, file, line, flag) {
     let scriptErrorClass = Cc["@mozilla.org/scripterror;1"];
     let scriptError = scriptErrorClass.createInstance(Ci.nsIScriptError);
     scriptError.initWithWindowID(msg, file, null, line, 0, flag,
                                  "content javascript", this._winID);
     let console = Cc["@mozilla.org/consoleservice;1"].
       getService(Ci.nsIConsoleService);
     console.logMessage(scriptError);
-
-    if (flag != Ci.nsIScriptError.warningFlag) {
-      // Safely call onerror directly if present (necessary for testing)
-      try {
-        if (typeof this._win.onerror === "function") {
-          this._win.onerror(msg, file, line);
-        }
-      } catch(e) {
-        // If onerror itself throws, service it.
-        try {
-          let scriptError = scriptErrorClass.createInstance(Ci.nsIScriptError);
-          scriptError.initWithWindowID(e.message, e.fileName, null, e.lineNumber,
-                                       0, Ci.nsIScriptError.exceptionFlag,
-                                       "content javascript",
-                                       this._winID);
-          console.logMessage(scriptError);
-        } catch(e) {}
-      }
-    }
   },
 
   getEH: function(type) {
     return this.__DOM_IMPL__.getEventHandler(type);
   },
 
   setEH: function(type, handler) {
     this.__DOM_IMPL__.setEventHandler(type, handler);
@@ -658,17 +655,17 @@ RTCPeerConnection.prototype = {
         throw new this._win.DOMError("",
             "Invalid type " + desc.type + " provided to setRemoteDescription");
     }
 
     try {
       let processIdentity = this._processIdentity.bind(this);
       this._remoteIdp.verifyIdentityFromSDP(desc.sdp, processIdentity);
     } catch (e) {
-      this.reportWarning(e.message, e.fileName, e.lineNumber);
+      this.logWarning(e.message, e.fileName, e.lineNumber);
       // only happens if processing the SDP for identity doesn't work
       // let _setRemoteDescription do the error reporting
     }
 
     this._queueOrRun({
       func: this._setRemoteDescription,
       args: [type, desc.sdp, onSuccess, onError],
       wait: true,
@@ -858,30 +855,30 @@ RTCPeerConnection.prototype = {
 
   createDataChannel: function(label, dict) {
     this._checkClosed();
     if (dict == undefined) {
       dict = {};
     }
     if (dict.maxRetransmitNum != undefined) {
       dict.maxRetransmits = dict.maxRetransmitNum;
-      this.reportWarning("Deprecated RTCDataChannelInit dictionary entry maxRetransmitNum used!", null, 0);
+      this.logWarning("Deprecated RTCDataChannelInit dictionary entry maxRetransmitNum used!", null, 0);
     }
     if (dict.outOfOrderAllowed != undefined) {
       dict.ordered = !dict.outOfOrderAllowed; // the meaning is swapped with
                                               // the name change
-      this.reportWarning("Deprecated RTCDataChannelInit dictionary entry outOfOrderAllowed used!", null, 0);
+      this.logWarning("Deprecated RTCDataChannelInit dictionary entry outOfOrderAllowed used!", null, 0);
     }
     if (dict.preset != undefined) {
       dict.negotiated = dict.preset;
-      this.reportWarning("Deprecated RTCDataChannelInit dictionary entry preset used!", null, 0);
+      this.logWarning("Deprecated RTCDataChannelInit dictionary entry preset used!", null, 0);
     }
     if (dict.stream != undefined) {
       dict.id = dict.stream;
-      this.reportWarning("Deprecated RTCDataChannelInit dictionary entry stream used!", null, 0);
+      this.logWarning("Deprecated RTCDataChannelInit dictionary entry stream used!", null, 0);
     }
 
     if (dict.maxRetransmitTime != undefined &&
         dict.maxRetransmits != undefined) {
       throw new this._win.DOMError("",
           "Both maxRetransmitTime and maxRetransmits cannot be provided");
     }
     let protocol;
@@ -970,17 +967,19 @@ PeerConnectionObserver.prototype = {
   callCB: function(callback, arg) {
     if (callback) {
       try {
         callback(arg);
       } catch(e) {
         // A content script (user-provided) callback threw an error. We don't
         // want this to take down peerconnection, but we still want the user
         // to see it, so we catch it, report it, and move on.
-        this._dompc.reportError(e.message, e.fileName, e.lineNumber);
+        this._dompc.logErrorAndCallOnError(e.message,
+                                           e.fileName,
+                                           e.lineNumber);
       }
     }
   },
 
   onCreateOfferSuccess: function(sdp) {
     let pc = this._dompc;
     let fp = pc._impl.fingerprint;
     pc._localIdp.appendIdentityToSDP(sdp, fp, function(sdp, assertion) {
@@ -1111,16 +1110,17 @@ PeerConnectionObserver.prototype = {
   //   closed        The ICE Agent has shut down and is no longer responding to
   //                 STUN requests.
 
   handleIceConnectionStateChange: function(iceConnectionState) {
     var histogram = Services.telemetry.getHistogramById("WEBRTC_ICE_SUCCESS_RATE");
 
     if (iceConnectionState === 'failed') {
       histogram.add(false);
+      this._dompc.logError("ICE failed, see about:webrtc for more details", null, 0);
     }
     if (this._dompc.iceConnectionState === 'checking' &&
         (iceConnectionState === 'completed' ||
          iceConnectionState === 'connected')) {
           histogram.add(true);
     }
     this._dompc.changeIceConnectionState(iceConnectionState);
   },
@@ -1181,17 +1181,17 @@ PeerConnectionObserver.prototype = {
         // No-op
         break;
 
       case "SipccState":
         // No-op
         break;
 
       default:
-        this._dompc.reportWarning("Unhandled state type: " + state, null, 0);
+        this._dompc.logWarning("Unhandled state type: " + state, null, 0);
         break;
     }
   },
 
   onGetStatsSuccess: function(dict) {
     let chromeobj = new RTCStatsReport(this._dompc._win, dict);
     let webidlobj = this._dompc._win.RTCStatsReport._create(this._dompc._win,
                                                             chromeobj);
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -114,20 +114,20 @@ public:
       sPluginContext->fGenTextures(1, &mTextureInfo.mTexture);
     }
 
     mLock.Lock();
     return mTextureInfo;
   }
 
   void Release(nsNPAPIPluginInstance::TextureInfo& aTextureInfo)
-  { 
+  {
     mTextureInfo = aTextureInfo;
     mLock.Unlock();
-  } 
+  }
 
   SharedTextureHandle CreateSharedHandle()
   {
     MutexAutoLock lock(mLock);
 
     if (!EnsureGLContext())
       return 0;
 
@@ -139,28 +139,28 @@ public:
                              gl::SharedTextureShareType::SameProcess,
                              (void*)mTextureInfo.mTexture,
                              gl::SharedTextureBufferType::TextureID);
 
     // We want forget about this now, so delete the texture. Assigning it to zero
     // ensures that we create a new one in Lock()
     sPluginContext->fDeleteTextures(1, &mTextureInfo.mTexture);
     mTextureInfo.mTexture = 0;
-    
+
     return handle;
   }
 
 private:
   // Private destructor, to discourage deletion outside of Release():
   ~SharedPluginTexture()
   {
   }
 
   nsNPAPIPluginInstance::TextureInfo mTextureInfo;
- 
+
   Mutex mLock;
 };
 
 static std::map<NPP, nsNPAPIPluginInstance*> sPluginNPPMap;
 
 #endif
 
 using namespace mozilla;
@@ -226,17 +226,17 @@ void
 nsNPAPIPluginInstance::Destroy()
 {
   Stop();
   mPlugin = nullptr;
 
 #if MOZ_WIDGET_ANDROID
   if (mContentSurface)
     mContentSurface->SetFrameAvailableCallback(nullptr);
-  
+
   mContentTexture = nullptr;
   mContentSurface = nullptr;
 
   std::map<void*, VideoInfo*>::iterator it;
   for (it = mVideos.begin(); it != mVideos.end(); it++) {
     it->second->mSurfaceTexture->SetFrameAvailableCallback(nullptr);
     delete it->second;
   }
@@ -262,17 +262,17 @@ nsresult nsNPAPIPluginInstance::Initiali
   mOwner = aOwner;
 
   if (aMIMEType) {
     mMIMEType = (char*)PR_Malloc(strlen(aMIMEType) + 1);
     if (mMIMEType) {
       PL_strcpy(mMIMEType, aMIMEType);
     }
   }
-  
+
   return Start();
 }
 
 nsresult nsNPAPIPluginInstance::Stop()
 {
   PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance::Stop this=%p\n",this));
 
   // Make sure the plugin didn't leave popups enabled.
@@ -432,26 +432,26 @@ nsNPAPIPluginInstance::Start()
   const char* const* names = nullptr;
   const char* const* values = nullptr;
   nsPluginTagType tagtype;
   nsresult rv = GetTagType(&tagtype);
   if (NS_SUCCEEDED(rv)) {
     // Note: If we failed to get the tag type, we may be a full page plugin, so no arguments
     rv = GetAttributes(count, names, values);
     NS_ENSURE_SUCCESS(rv, rv);
-    
+
     // nsPluginTagType_Object or Applet may also have PARAM tags
     // Note: The arrays handed back by GetParameters() are
     // crafted specially to be directly behind the arrays from GetAttributes()
     // with a null entry as a separator. This is for 4.x backwards compatibility!
     // see bug 111008 for details
     if (tagtype != nsPluginTagType_Embed) {
       uint16_t pcount = 0;
       const char* const* pnames = nullptr;
-      const char* const* pvalues = nullptr;    
+      const char* const* pvalues = nullptr;
       if (NS_SUCCEEDED(GetParameters(pcount, pnames, pvalues))) {
         // Android expects an empty string as the separator instead of null
 #ifdef MOZ_WIDGET_ANDROID
         NS_ASSERTION(PL_strcmp(values[count], "") == 0, "attribute/parameter array not setup correctly for Android NPAPI plugins");
 #else
         NS_ASSERTION(!values[count], "attribute/parameter array not setup correctly for NPAPI plugins");
 #endif
         if (pcount)
@@ -548,17 +548,17 @@ nsNPAPIPluginInstance::Start()
   ("NPP New called: this=%p, npp=%p, mime=%s, mode=%d, argc=%d, return=%d\n",
   this, &mNPP, mimetype, mode, count, error));
 
   if (NS_FAILED(newResult) || error != NPERR_NO_ERROR) {
     mRunning = DESTROYED;
     nsJSNPRuntime::OnPluginDestroy(&mNPP);
     return NS_ERROR_FAILURE;
   }
-  
+
   return NS_OK;
 }
 
 nsresult nsNPAPIPluginInstance::SetWindow(NPWindow* window)
 {
   // NPAPI plugins don't want a SetWindow(nullptr).
   if (!window || RUNNING != mRunning)
     return NS_OK;
@@ -702,17 +702,17 @@ nsresult nsNPAPIPluginInstance::HandleEv
 #if defined(XP_WIN)
     NS_TRY_SAFE_CALL_RETURN(tmpResult, (*pluginFunctions->event)(&mNPP, event), this,
                             aSafeToReenterGecko);
 #else
     MAIN_THREAD_JNI_REF_GUARD;
     tmpResult = (*pluginFunctions->event)(&mNPP, event);
 #endif
     NPP_PLUGIN_LOG(PLUGIN_LOG_NOISY,
-      ("NPP HandleEvent called: this=%p, npp=%p, event=%p, return=%d\n", 
+      ("NPP HandleEvent called: this=%p, npp=%p, event=%p, return=%d\n",
       this, &mNPP, event, tmpResult));
 
     if (result)
       *result = tmpResult;
     mCurrentPluginEvent = nullptr;
   }
 
   return NS_OK;
@@ -729,33 +729,33 @@ nsresult nsNPAPIPluginInstance::GetValue
 
   if (pluginFunctions->getvalue && RUNNING == mRunning) {
     PluginDestructionGuard guard(this);
 
     NPError pluginError = NPERR_GENERIC_ERROR;
     NS_TRY_SAFE_CALL_RETURN(pluginError, (*pluginFunctions->getvalue)(&mNPP, variable, value), this,
                             NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
     NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
-    ("NPP GetValue called: this=%p, npp=%p, var=%d, value=%d, return=%d\n", 
+    ("NPP GetValue called: this=%p, npp=%p, var=%d, value=%d, return=%d\n",
     this, &mNPP, variable, value, pluginError));
 
     if (pluginError == NPERR_NO_ERROR) {
       rv = NS_OK;
     }
   }
 
   return rv;
 }
 
 nsNPAPIPlugin* nsNPAPIPluginInstance::GetPlugin()
 {
   return mPlugin;
 }
 
-nsresult nsNPAPIPluginInstance::GetNPP(NPP* aNPP) 
+nsresult nsNPAPIPluginInstance::GetNPP(NPP* aNPP)
 {
   if (aNPP)
     *aNPP = &mNPP;
   else
     return NS_ERROR_NULL_POINTER;
 
   return NS_OK;
 }
@@ -895,17 +895,17 @@ void nsNPAPIPluginInstance::NotifySize(n
 
 void nsNPAPIPluginInstance::SetANPDrawingModel(uint32_t aModel)
 {
   mANPDrawingModel = aModel;
 }
 
 void* nsNPAPIPluginInstance::GetJavaSurface()
 {
-  void* surface = nullptr; 
+  void* surface = nullptr;
   nsresult rv = GetValueFromPlugin(kJavaSurface_ANPGetValue, &surface);
   if (NS_FAILED(rv))
     return nullptr;
 
   return surface;
 }
 
 void nsNPAPIPluginInstance::PostEvent(void* event)
@@ -1105,17 +1105,17 @@ nsresult nsNPAPIPluginInstance::IsRemote
 {
 #ifdef XP_MACOSX
   if (!mPlugin)
       return NS_ERROR_FAILURE;
 
   PluginLibrary* library = mPlugin->GetLibrary();
   if (!library)
       return NS_ERROR_FAILURE;
-  
+
   return library->IsRemoteDrawingCoreAnimation(&mNPP, aDrawing);
 #else
   return NS_ERROR_FAILURE;
 #endif
 }
 
 nsresult nsNPAPIPluginInstance::ContentsScaleFactorChanged(double aContentsScaleFactor)
 {
@@ -1125,17 +1125,17 @@ nsresult nsNPAPIPluginInstance::Contents
 
   PluginLibrary* library = mPlugin->GetLibrary();
   if (!library)
       return NS_ERROR_FAILURE;
 
   // We only need to call this if the plugin is running OOP.
   if (!library->IsOOP())
       return NS_OK;
-  
+
   return library->ContentsScaleFactorChanged(&mNPP, aContentsScaleFactor);
 #else
   return NS_ERROR_FAILURE;
 #endif
 }
 
 nsresult
 nsNPAPIPluginInstance::GetJSObject(JSContext *cx, JSObject** outObject)
@@ -1355,17 +1355,17 @@ nsNPAPIPluginInstance::PopPopupsEnabledS
   if (!window)
     return NS_ERROR_FAILURE;
 
   PopupControlState &oldState = mPopupStates[last];
 
   window->PopPopupControlState(oldState);
 
   mPopupStates.RemoveElementAt(last);
-  
+
   return NS_OK;
 }
 
 nsresult
 nsNPAPIPluginInstance::GetPluginAPIVersion(uint16_t* version)
 {
   NS_ENSURE_ARG_POINTER(version);
 
--- a/dom/plugins/base/nsNPAPIPluginInstance.h
+++ b/dom/plugins/base/nsNPAPIPluginInstance.h
@@ -160,17 +160,17 @@ public:
   // These are really mozilla::dom::ScreenOrientation, but it's
   // difficult to include that here
   uint32_t FullScreenOrientation() { return mFullScreenOrientation; }
   void SetFullScreenOrientation(uint32_t orientation);
 
   void SetWakeLock(bool aLock);
 
   mozilla::gl::GLContext* GLContext();
-  
+
   // For ANPOpenGL
   class TextureInfo {
   public:
     TextureInfo() :
       mTexture(0), mWidth(0), mHeight(0), mInternalFormat(0)
     {
     }
 
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -694,16 +694,30 @@ ScriptExecutorRunnable::WorkerRun(JSCont
     if (!loadInfo.mExecutionResult) {
       return true;
     }
   }
 
   JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
   NS_ASSERTION(global, "Must have a global by now!");
 
+  // Determine whether we want to be discarding source on this global to save
+  // memory. It would make more sense to do this when we create the global, but
+  // the information behind UsesSystemPrincipal() et al isn't finalized until
+  // the call to SetPrincipal during the first script load. After that, however,
+  // it never changes. So we can just idempotently set the bits here.
+  //
+  // Note that we read a pref that is cached on the main thread. This is benignly
+  // racey.
+  if (xpc::ShouldDiscardSystemSource()) {
+    bool discard = aWorkerPrivate->UsesSystemPrincipal() ||
+                   aWorkerPrivate->IsInPrivilegedApp();
+    JS::CompartmentOptionsRef(global).setDiscardSource(discard);
+  }
+
   for (uint32_t index = mFirstIndex; index <= mLastIndex; index++) {
     ScriptLoadInfo& loadInfo = loadInfos.ElementAt(index);
 
     NS_ASSERTION(!loadInfo.mChannel, "Should no longer have a channel!");
     NS_ASSERTION(loadInfo.mExecutionScheduled, "Should be scheduled!");
     NS_ASSERTION(!loadInfo.mExecutionResult, "Should not have executed yet!");
 
     if (NS_FAILED(loadInfo.mLoadResult)) {
--- a/gfx/layers/opengl/CompositorOGL.h
+++ b/gfx/layers/opengl/CompositorOGL.h
@@ -151,17 +151,17 @@ protected:
   RefPtr<gl::GLContext> mGL;
   nsTArray<GLuint> mCreatedTextures;
   nsTArray<GLuint> mUnusedTextures;
 };
 
 class CompositorOGL : public Compositor
 {
   typedef mozilla::gl::GLContext GLContext;
-  
+
   friend class GLManagerCompositor;
 
   std::map<ShaderConfigOGL, ShaderProgramOGL*> mPrograms;
 public:
   CompositorOGL(nsIWidget *aWidget, int aSurfaceWidth = -1, int aSurfaceHeight = -1,
                 bool aUseExternalSurfaceSize = false);
 
   virtual ~CompositorOGL();
@@ -177,17 +177,17 @@ public:
   {
     return TextureFactoryIdentifier(LayersBackend::LAYERS_OPENGL,
                                     XRE_GetProcessType(),
                                     GetMaxTextureSize(),
                                     mFBOTextureTarget == LOCAL_GL_TEXTURE_2D,
                                     SupportsPartialTextureUpdate());
   }
 
-  virtual TemporaryRef<CompositingRenderTarget> 
+  virtual TemporaryRef<CompositingRenderTarget>
   CreateRenderTarget(const gfx::IntRect &aRect, SurfaceInitMode aInit) MOZ_OVERRIDE;
 
   virtual TemporaryRef<CompositingRenderTarget>
   CreateRenderTargetFromSource(const gfx::IntRect &aRect,
                                const CompositingRenderTarget *aSource,
                                const gfx::IntPoint &aSourcePoint) MOZ_OVERRIDE;
 
   virtual void SetRenderTarget(CompositingRenderTarget *aSurface) MOZ_OVERRIDE;
@@ -280,17 +280,17 @@ public:
 private:
   virtual void DrawQuadInternal(const gfx::Rect& aRect,
                                 const gfx::Rect& aClipRect,
                                 const EffectChain &aEffectChain,
                                 gfx::Float aOpacity,
                                 const gfx::Matrix4x4 &aTransformi,
                                 GLuint aDrawMode);
 
-  /** 
+  /**
    * Context target, nullptr when drawing directly to our swap chain.
    */
   RefPtr<gfx::DrawTarget> mTarget;
 
   /** Widget associated with this compositor */
   nsIWidget *mWidget;
   nsIntSize mWidgetSize;
   nsRefPtr<GLContext> mGLContext;
--- a/gfx/thebes/gfxDWriteFontList.cpp
+++ b/gfx/thebes/gfxDWriteFontList.cpp
@@ -744,27 +744,21 @@ gfxDWriteFontList::GetDefaultFont(const 
 }
 
 gfxFontEntry *
 gfxDWriteFontList::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
                                    const nsAString& aFullname)
 {
     gfxFontEntry *lookup;
 
-    // initialize name lookup tables if needed
-    if (!mFaceNamesInitialized) {
-        InitFaceNameLists();
+    lookup = LookupInFaceNameLists(aFullname);
+    if (!lookup) {
+        return nullptr;
     }
 
-    // lookup in name lookup tables, return null if not found
-    if (!(lookup = mExtraNames->mPostscriptNames.GetWeak(aFullname)) &&
-        !(lookup = mExtraNames->mFullnames.GetWeak(aFullname)))
-    {
-        return nullptr;
-    }
     gfxDWriteFontEntry* dwriteLookup = static_cast<gfxDWriteFontEntry*>(lookup);
     gfxDWriteFontEntry *fe =
         new gfxDWriteFontEntry(lookup->Name(),
                                dwriteLookup->mFont,
                                aProxyEntry->Weight(),
                                aProxyEntry->Stretch(),
                                aProxyEntry->IsItalic());
     fe->SetForceGDIClassic(dwriteLookup->GetForceGDIClassic());
--- a/gfx/thebes/gfxGDIFontList.cpp
+++ b/gfx/thebes/gfxGDIFontList.cpp
@@ -721,25 +721,18 @@ gfxGDIFontList::EnumFontFamExProc(ENUMLO
 }
 
 gfxFontEntry* 
 gfxGDIFontList::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
                                 const nsAString& aFullname)
 {
     gfxFontEntry *lookup;
 
-    // initialize name lookup tables if needed
-    if (!mFaceNamesInitialized) {
-        InitFaceNameLists();
-    }
-
-    // lookup in name lookup tables, return null if not found
-    if (!(lookup = mExtraNames->mPostscriptNames.GetWeak(aFullname)) &&
-        !(lookup = mExtraNames->mFullnames.GetWeak(aFullname)))
-    {
+    lookup = LookupInFaceNameLists(aFullname);
+    if (!lookup) {
         return nullptr;
     }
 
     bool isCFF = false; // jtdfix -- need to determine this
     
     // use the face name from the lookup font entry, which will be the localized
     // face name which GDI mapping tables use (e.g. with the system locale set to
     // Dutch, a fullname of 'Arial Bold' will find a font entry with the face name
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -1059,17 +1059,17 @@ gfxPlatform::CreateOffscreenContentDrawT
   return CreateDrawTargetForBackend(mContentBackend, aSize, aFormat);
 }
 
 RefPtr<DrawTarget>
 gfxPlatform::CreateDrawTargetForData(unsigned char* aData, const IntSize& aSize, int32_t aStride, SurfaceFormat aFormat)
 {
   NS_ASSERTION(mContentBackend != BackendType::NONE, "No backend.");
   if (mContentBackend == BackendType::CAIRO) {
-    nsRefPtr<gfxImageSurface> image = new gfxImageSurface(aData, gfxIntSize(aSize.width, aSize.height), aStride, SurfaceFormatToImageFormat(aFormat)); 
+    nsRefPtr<gfxImageSurface> image = new gfxImageSurface(aData, gfxIntSize(aSize.width, aSize.height), aStride, SurfaceFormatToImageFormat(aFormat));
     return Factory::CreateDrawTargetForCairoSurface(image->CairoSurface(), aSize);
   }
   return Factory::CreateDrawTargetForData(mContentBackend, aData, aSize, aStride, aFormat);
 }
 
 /* static */ BackendType
 gfxPlatform::BackendTypeForName(const nsCString& aName)
 {
--- a/gfx/thebes/gfxPlatformFontList.cpp
+++ b/gfx/thebes/gfxPlatformFontList.cpp
@@ -122,17 +122,17 @@ gfxPlatformFontList::gfxPlatformFontList
       mPrefFonts(10), mBadUnderlineFamilyNames(10), mSharedCmaps(16),
       mStartIndex(0), mIncrement(1), mNumFamilies(0)
 {
     mOtherFamilyNamesInitialized = false;
 
     if (aNeedFullnamePostscriptNames) {
         mExtraNames = new ExtraNames();
     }
-    mFaceNamesInitialized = false;
+    mFaceNameListsInitialized = false;
 
     LoadBadUnderlineList();
 
     // pref changes notification setup
     NS_ASSERTION(!gFontListPrefObserver,
                  "There has been font list pref observer already");
     gFontListPrefObserver = new gfxFontListPrefObserver();
     NS_ADDREF(gFontListPrefObserver);
@@ -154,17 +154,17 @@ gfxPlatformFontList::InitFontList()
 {
     mFontFamilies.Clear();
     mOtherFamilyNames.Clear();
     mOtherFamilyNamesInitialized = false;
     if (mExtraNames) {
         mExtraNames->mFullnames.Clear();
         mExtraNames->mPostscriptNames.Clear();
     }
-    mFaceNamesInitialized = false;
+    mFaceNameListsInitialized = false;
     mPrefFonts.Clear();
     mReplacementCharFallbackFamily = nullptr;
     CancelLoader();
 
     // initialize ranges of characters for which system-wide font search should be skipped
     mCodepointsWithNoFonts.reset();
     mCodepointsWithNoFonts.SetRange(0,0x1f);     // C0 controls
     mCodepointsWithNoFonts.SetRange(0x7f,0x9f);  // C1 controls
@@ -176,77 +176,194 @@ gfxPlatformFontList::InitFontList()
 
 void
 gfxPlatformFontList::GenerateFontListKey(const nsAString& aKeyName, nsAString& aResult)
 {
     aResult = aKeyName;
     ToLowerCase(aResult);
 }
 
+struct InitOtherNamesData {
+    InitOtherNamesData(gfxPlatformFontList *aFontList,
+                       TimeStamp aStartTime)
+        : mFontList(aFontList), mStartTime(aStartTime), mTimedOut(false)
+    {}
+
+    gfxPlatformFontList *mFontList;
+    TimeStamp mStartTime;
+    bool mTimedOut;
+};
+
 void 
 gfxPlatformFontList::InitOtherFamilyNames()
 {
-    mOtherFamilyNamesInitialized = true;
+    if (mOtherFamilyNamesInitialized) {
+        return;
+    }
+
     TimeStamp start = TimeStamp::Now();
 
     // iterate over all font families and read in other family names
-    mFontFamilies.Enumerate(gfxPlatformFontList::InitOtherFamilyNamesProc, this);
+    InitOtherNamesData otherNamesData(this, start);
 
+    mFontFamilies.Enumerate(gfxPlatformFontList::InitOtherFamilyNamesProc,
+                            &otherNamesData);
+
+    if (!otherNamesData.mTimedOut) {
+        mOtherFamilyNamesInitialized = true;
+    }
     TimeStamp end = TimeStamp::Now();
     Telemetry::AccumulateTimeDelta(Telemetry::FONTLIST_INITOTHERFAMILYNAMES,
                                    start, end);
+
 #ifdef PR_LOGGING
     if (LOG_FONTINIT_ENABLED()) {
         TimeDuration elapsed = end - start;
-        LOG_FONTINIT(("(fontinit) InitOtherFamilyNames took %8.2f ms",
-                      elapsed.ToMilliseconds()));
+        LOG_FONTINIT(("(fontinit) InitOtherFamilyNames took %8.2f ms %s",
+                      elapsed.ToMilliseconds(),
+                      (otherNamesData.mTimedOut ? "timeout" : "")));
     }
 #endif
 }
-                                                         
+
+#define OTHERNAMES_TIMEOUT 200
+
 PLDHashOperator
 gfxPlatformFontList::InitOtherFamilyNamesProc(nsStringHashKey::KeyType aKey,
                                               nsRefPtr<gfxFontFamily>& aFamilyEntry,
                                               void* userArg)
 {
-    gfxPlatformFontList *fc = static_cast<gfxPlatformFontList*>(userArg);
-    aFamilyEntry->ReadOtherFamilyNames(fc);
+    InitOtherNamesData *data = static_cast<InitOtherNamesData*>(userArg);
+
+    aFamilyEntry->ReadOtherFamilyNames(data->mFontList);
+    TimeDuration elapsed = TimeStamp::Now() - data->mStartTime;
+    if (elapsed.ToMilliseconds() > OTHERNAMES_TIMEOUT) {
+        data->mTimedOut = true;
+        return PL_DHASH_STOP;
+    }
     return PL_DHASH_NEXT;
 }
+ 
+struct ReadFaceNamesData {
+    ReadFaceNamesData(gfxPlatformFontList *aFontList, TimeStamp aStartTime)
+        : mFontList(aFontList), mStartTime(aStartTime), mTimedOut(false)
+    {}
 
-void
-gfxPlatformFontList::InitFaceNameLists()
+    gfxPlatformFontList *mFontList;
+    TimeStamp mStartTime;
+    bool mTimedOut;
+
+    // if mFirstChar is not empty, only load facenames for families
+    // that start with this character
+    nsString mFirstChar;
+};
+
+gfxFontEntry*
+gfxPlatformFontList::SearchFamiliesForFaceName(const nsAString& aFaceName)
 {
-    mFaceNamesInitialized = true;
+    TimeStamp start = TimeStamp::Now();
+    gfxFontEntry *lookup = nullptr;
+
+    ReadFaceNamesData faceNameListsData(this, start);
 
-    TimeStamp start = TimeStamp::Now();
-
-    // iterate over all font families and read in other family names
-    mFontFamilies.Enumerate(gfxPlatformFontList::InitFaceNameListsProc, this);
+    // iterate over familes starting with the same letter
+    faceNameListsData.mFirstChar.Assign(aFaceName.CharAt(0));
+    ToLowerCase(faceNameListsData.mFirstChar);
+    mFontFamilies.Enumerate(gfxPlatformFontList::ReadFaceNamesProc,
+                            &faceNameListsData);
+    lookup = FindFaceName(aFaceName);
 
     TimeStamp end = TimeStamp::Now();
     Telemetry::AccumulateTimeDelta(Telemetry::FONTLIST_INITFACENAMELISTS,
                                    start, end);
 #ifdef PR_LOGGING
     if (LOG_FONTINIT_ENABLED()) {
         TimeDuration elapsed = end - start;
-        LOG_FONTINIT(("(fontinit) InitFaceNameLists took %8.2f ms",
-                      elapsed.ToMilliseconds()));
+        LOG_FONTINIT(("(fontinit) SearchFamiliesForFaceName took %8.2f ms %s %s",
+                      elapsed.ToMilliseconds(),
+                      (lookup ? "found name" : ""),
+                      (faceNameListsData.mTimedOut ? "timeout" : "")));
     }
 #endif
+
+    return lookup;
 }
 
+// time limit for loading facename lists (ms)
+#define NAMELIST_TIMEOUT  200
+
 PLDHashOperator
-gfxPlatformFontList::InitFaceNameListsProc(nsStringHashKey::KeyType aKey,
-                                           nsRefPtr<gfxFontFamily>& aFamilyEntry,
-                                           void* userArg)
+gfxPlatformFontList::ReadFaceNamesProc(nsStringHashKey::KeyType aKey,
+                                       nsRefPtr<gfxFontFamily>& aFamilyEntry,
+                                       void* userArg)
+{
+    ReadFaceNamesData *data = static_cast<ReadFaceNamesData*>(userArg);
+    gfxPlatformFontList *fc = data->mFontList;
+
+    // when filtering, skip names that don't start with the filter character
+    if (!(data->mFirstChar.IsEmpty())) {
+        char16_t firstChar = aKey.CharAt(0);
+        nsAutoString firstCharStr(&firstChar, 1);
+        ToLowerCase(firstCharStr);
+        if (!firstCharStr.Equals(data->mFirstChar)) {
+            return PL_DHASH_NEXT;
+        }
+    }
+    aFamilyEntry->ReadFaceNames(fc, fc->NeedFullnamePostscriptNames());
+
+    TimeDuration elapsed = TimeStamp::Now() - data->mStartTime;
+    if (elapsed.ToMilliseconds() > NAMELIST_TIMEOUT) {
+        data->mTimedOut = true;
+        return PL_DHASH_STOP;
+    }
+    return PL_DHASH_NEXT;
+}
+
+gfxFontEntry*
+gfxPlatformFontList::FindFaceName(const nsAString& aFaceName)
 {
-    gfxPlatformFontList *fc = static_cast<gfxPlatformFontList*>(userArg);
-    aFamilyEntry->ReadFaceNames(fc, fc->NeedFullnamePostscriptNames());
-    return PL_DHASH_NEXT;
+    gfxFontEntry *lookup;
+
+    // lookup in name lookup tables, return null if not found
+    if (mExtraNames &&
+        ((lookup = mExtraNames->mPostscriptNames.GetWeak(aFaceName)) ||
+         (lookup = mExtraNames->mFullnames.GetWeak(aFaceName)))) {
+        return lookup;
+    }
+
+    return nullptr;
+}
+
+gfxFontEntry*
+gfxPlatformFontList::LookupInFaceNameLists(const nsAString& aFaceName)
+{
+    gfxFontEntry *lookup = nullptr;
+
+    // initialize facename lookup tables if needed
+    // note: this can terminate early or time out, in which case
+    //       mFaceNameListsInitialized remains false
+    if (!mFaceNameListsInitialized) {
+        lookup = SearchFamiliesForFaceName(aFaceName);
+        if (lookup) {
+            return lookup;
+        }
+    }
+
+    // lookup in name lookup tables, return null if not found
+    if (!(lookup = FindFaceName(aFaceName))) {
+        // names not completely initialized, so keep track of lookup misses
+        if (!mFaceNameListsInitialized) {
+            if (!mFaceNamesMissed) {
+                mFaceNamesMissed = new nsTHashtable<nsStringHashKey>(4);
+            }
+            mFaceNamesMissed->PutEntry(aFaceName);
+        }
+    }
+
+    return lookup;
 }
 
 void
 gfxPlatformFontList::PreloadNamesList()
 {
     nsAutoTArray<nsString, 10> preloadFonts;
     gfxFontUtils::GetPrefsFontList("font.preload-names-list", preloadFonts);
 
@@ -616,16 +733,23 @@ gfxPlatformFontList::FindFamily(const ns
     // initialize the rest of the list and try again.  this is done lazily
     // since reading name table entries is expensive.
     // although ASCII localized family names are possible they don't occur
     // in practice so avoid pulling in names at startup
     if (!mOtherFamilyNamesInitialized && !IsASCII(aFamily)) {
         InitOtherFamilyNames();
         if ((familyEntry = mOtherFamilyNames.GetWeak(key)) != nullptr) {
             return CheckFamily(familyEntry);
+        } else if (!mOtherFamilyNamesInitialized) {
+            // localized family names load timed out, add name to list of
+            // names to check after localized names are loaded
+            if (!mOtherNamesMissed) {
+                mOtherNamesMissed = new nsTHashtable<nsStringHashKey>(4);
+            }
+            mOtherNamesMissed->PutEntry(key);
         }
     }
 
     return nullptr;
 }
 
 gfxFontEntry*
 gfxPlatformFontList::FindFontForFamily(const nsAString& aFamily, const gfxFontStyle* aStyle, bool& aNeedsBold)
@@ -810,39 +934,109 @@ gfxPlatformFontList::LoadFontInfo()
         TimeDuration elapsed = TimeStamp::Now() - start;
         LOG_FONTINIT(("(fontinit) fontloader load pass %8.2f ms done %s\n",
                       elapsed.ToMilliseconds(), (done ? "true" : "false")));
     }
 #endif
 
     if (done) {
         mOtherFamilyNamesInitialized = true;
-        mFaceNamesInitialized = true;
+        mFaceNameListsInitialized = true;
     }
 
     return done;
 }
 
+struct LookupMissedFaceNamesData {
+    LookupMissedFaceNamesData(gfxPlatformFontList *aFontList)
+        : mFontList(aFontList), mFoundName(false) {}
+
+    gfxPlatformFontList *mFontList;
+    bool mFoundName;
+};
+
+/*static*/ PLDHashOperator
+gfxPlatformFontList::LookupMissedFaceNamesProc(nsStringHashKey *aKey,
+                                               void *aUserArg)
+{
+    LookupMissedFaceNamesData *data =
+        reinterpret_cast<LookupMissedFaceNamesData*>(aUserArg);
+
+    if (data->mFontList->FindFaceName(aKey->GetKey())) {
+        data->mFoundName = true;
+        return PL_DHASH_STOP;
+    }
+    return PL_DHASH_NEXT;
+}
+
+struct LookupMissedOtherNamesData {
+    LookupMissedOtherNamesData(gfxPlatformFontList *aFontList)
+        : mFontList(aFontList), mFoundName(false) {}
+
+    gfxPlatformFontList *mFontList;
+    bool mFoundName;
+};
+
+/*static*/ PLDHashOperator
+gfxPlatformFontList::LookupMissedOtherNamesProc(nsStringHashKey *aKey,
+                                                void *aUserArg)
+{
+    LookupMissedOtherNamesData *data =
+        reinterpret_cast<LookupMissedOtherNamesData*>(aUserArg);
+
+    if (data->mFontList->FindFamily(aKey->GetKey())) {
+        data->mFoundName = true;
+        return PL_DHASH_STOP;
+    }
+    return PL_DHASH_NEXT;
+}
+
 void 
 gfxPlatformFontList::CleanupLoader()
 {
     mFontFamiliesToLoad.Clear();
     mNumFamilies = 0;
+    bool rebuilt = false, forceReflow = false;
+
+    // if had missed face names that are now available, force reflow all
+    if (mFaceNamesMissed &&
+        mFaceNamesMissed->Count() != 0) {
+        LookupMissedFaceNamesData namedata(this);
+        mFaceNamesMissed->EnumerateEntries(LookupMissedFaceNamesProc, &namedata);
+        if (namedata.mFoundName) {
+            rebuilt = true;
+            mUserFontSetList.EnumerateEntries(RebuildLocalFonts, nullptr);
+        }
+        mFaceNamesMissed = nullptr;
+    }
+
+    if (mOtherNamesMissed) {
+        LookupMissedOtherNamesData othernamesdata(this);
+        mOtherNamesMissed->EnumerateEntries(LookupMissedOtherNamesProc,
+                                            &othernamesdata);
+        mOtherNamesMissed = nullptr;
+        if (othernamesdata.mFoundName) {
+            forceReflow = true;
+            ForceGlobalReflow();
+        }
+    }
 
 #ifdef PR_LOGGING
     if (LOG_FONTINIT_ENABLED() && mFontInfo) {
         LOG_FONTINIT(("(fontinit) fontloader load thread took %8.2f ms "
                       "%d families %d fonts %d cmaps "
-                      "%d facenames %d othernames",
+                      "%d facenames %d othernames %s %s",
                       mLoadTime.ToMilliseconds(),
                       mFontInfo->mLoadStats.families,
                       mFontInfo->mLoadStats.fonts,
                       mFontInfo->mLoadStats.cmaps,
                       mFontInfo->mLoadStats.facenames,
-                      mFontInfo->mLoadStats.othernames));
+                      mFontInfo->mLoadStats.othernames,
+                      (rebuilt ? "(userfont sets rebuilt)" : ""),
+                      (forceReflow ? "(global reflow)" : "")));
     }
 #endif
 
     gfxFontInfoLoader::CleanupLoader();
 }
 
 void
 gfxPlatformFontList::GetPrefsAndStartLoader()
--- a/gfx/thebes/gfxPlatformFontList.h
+++ b/gfx/thebes/gfxPlatformFontList.h
@@ -220,29 +220,46 @@ protected:
 
     // whether system-based font fallback is used or not
     // if system fallback is used, no need to load all cmaps
     virtual bool UsesSystemFallback() { return false; }
 
     // verifies that a family contains a non-zero font count
     gfxFontFamily* CheckFamily(gfxFontFamily *aFamily);
 
-    // separate initialization for reading in name tables, since this is expensive
+    // initialize localized family names
     void InitOtherFamilyNames();
 
-    static PLDHashOperator InitOtherFamilyNamesProc(nsStringHashKey::KeyType aKey,
-                                                    nsRefPtr<gfxFontFamily>& aFamilyEntry,
-                                                    void* userArg);
+    static PLDHashOperator
+    InitOtherFamilyNamesProc(nsStringHashKey::KeyType aKey,
+                             nsRefPtr<gfxFontFamily>& aFamilyEntry,
+                             void* userArg);
+
+    // search through font families, looking for a given name, initializing
+    // facename lists along the way. first checks all families with names
+    // close to face name, then searchs all families if not found.
+    gfxFontEntry* SearchFamiliesForFaceName(const nsAString& aFaceName);
 
-    // read in all fullname/Postscript names for all font faces
-    void InitFaceNameLists();
+    static PLDHashOperator
+    ReadFaceNamesProc(nsStringHashKey::KeyType aKey,
+                      nsRefPtr<gfxFontFamily>& aFamilyEntry,
+                      void* userArg);
+
+    // helper method for finding fullname/postscript names in facename lists
+    gfxFontEntry* FindFaceName(const nsAString& aFaceName);
 
-    static PLDHashOperator InitFaceNameListsProc(nsStringHashKey::KeyType aKey,
-                                                 nsRefPtr<gfxFontFamily>& aFamilyEntry,
-                                                 void* userArg);
+    // look up a font by name, for cases where platform font list
+    // maintains explicit mappings of fullname/psname ==> font
+    virtual gfxFontEntry* LookupInFaceNameLists(const nsAString& aFontName);
+
+    static PLDHashOperator LookupMissedFaceNamesProc(nsStringHashKey *aKey,
+                                                     void *aUserArg);
+
+    static PLDHashOperator LookupMissedOtherNamesProc(nsStringHashKey *aKey,
+                                                      void *aUserArg);
 
     // commonly used fonts for which the name table should be loaded at startup
     virtual void PreloadNamesList();
 
     // load the bad underline blacklist from pref.
     void LoadBadUnderlineList();
 
     // explicitly set fixed-pitch flag for all faces
@@ -281,27 +298,33 @@ protected:
     // other family name ==> family entry (not unique, can have multiple names per
     // family entry, only names *other* than the canonical names are stored here)
     nsRefPtrHashtable<nsStringHashKey, gfxFontFamily> mOtherFamilyNames;
 
     // flag set after InitOtherFamilyNames is called upon first name lookup miss
     bool mOtherFamilyNamesInitialized;
 
     // flag set after fullname and Postcript name lists are populated
-    bool mFaceNamesInitialized;
+    bool mFaceNameListsInitialized;
 
     struct ExtraNames {
       ExtraNames() : mFullnames(100), mPostscriptNames(100) {}
       // fullname ==> font entry (unique, one name per font entry)
       nsRefPtrHashtable<nsStringHashKey, gfxFontEntry> mFullnames;
       // Postscript name ==> font entry (unique, one name per font entry)
       nsRefPtrHashtable<nsStringHashKey, gfxFontEntry> mPostscriptNames;
     };
     nsAutoPtr<ExtraNames> mExtraNames;
 
+    // face names missed when face name loading takes a long time
+    nsAutoPtr<nsTHashtable<nsStringHashKey> > mFaceNamesMissed;
+
+    // localized family names missed when face name loading takes a long time
+    nsAutoPtr<nsTHashtable<nsStringHashKey> > mOtherNamesMissed;
+
     // cached pref font lists
     // maps list of family names ==> array of family entries, one per lang group
     nsDataHashtable<nsUint32HashKey, nsTArray<nsRefPtr<gfxFontFamily> > > mPrefFonts;
 
     // when system-wide font lookup fails for a character, cache it to skip future searches
     gfxSparseBitSet mCodepointsWithNoFonts;
 
     // the family to use for U+FFFD fallback, to avoid expensive search every time
--- a/intl/strres/src/nsStringBundle.cpp
+++ b/intl/strres/src/nsStringBundle.cpp
@@ -468,33 +468,33 @@ nsresult nsExtensibleStringBundle::GetSi
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /////////////////////////////////////////////////////////////////////////////////////////
 
 #define MAX_CACHED_BUNDLES 16
 
 struct bundleCacheEntry_t MOZ_FINAL : public LinkedListElement<bundleCacheEntry_t> {
-  nsAutoPtr<nsCStringKey> mHashKey;
+  nsCString mHashKey;
   nsCOMPtr<nsIStringBundle> mBundle;
 
   bundleCacheEntry_t()
   {
     MOZ_COUNT_CTOR(bundleCacheEntry_t);
   }
 
   ~bundleCacheEntry_t()
   {
     MOZ_COUNT_DTOR(bundleCacheEntry_t);
   }
 };
 
 
 nsStringBundleService::nsStringBundleService() :
-  mBundleMap(MAX_CACHED_BUNDLES, true)
+  mBundleMap(MAX_CACHED_BUNDLES)
 {
   mErrorService = do_GetService(kErrorServiceCID);
   NS_ASSERTION(mErrorService, "Couldn't get error service");
 }
 
 NS_IMPL_ISUPPORTS3(nsStringBundleService,
                    nsIStringBundleService,
                    nsIObserver,
@@ -543,17 +543,17 @@ nsStringBundleService::Observe(nsISuppor
 
   return NS_OK;
 }
 
 void
 nsStringBundleService::flushBundleCache()
 {
   // release all bundles in the cache
-  mBundleMap.Reset();
+  mBundleMap.Clear();
 
   while (!mBundleCache.isEmpty()) {
     delete mBundleCache.popFirst();
   }
 }
 
 NS_IMETHODIMP
 nsStringBundleService::FlushBundles()
@@ -561,70 +561,68 @@ nsStringBundleService::FlushBundles()
   flushBundleCache();
   return NS_OK;
 }
 
 nsresult
 nsStringBundleService::getStringBundle(const char *aURLSpec,
                                        nsIStringBundle **aResult)
 {
-  nsCStringKey completeKey(aURLSpec);
-
-  bundleCacheEntry_t* cacheEntry =
-    (bundleCacheEntry_t*)mBundleMap.Get(&completeKey);
+  nsDependentCString key(aURLSpec);
+  bundleCacheEntry_t* cacheEntry = mBundleMap.Get(key);
 
   if (cacheEntry) {
     // cache hit!
     // remove it from the list, it will later be reinserted
     // at the head of the list
     cacheEntry->remove();
 
   } else {
 
     // hasn't been cached, so insert it into the hash table
     nsRefPtr<nsStringBundle> bundle = new nsStringBundle(aURLSpec, mOverrideStrings);
-    cacheEntry = insertIntoCache(bundle.forget(), &completeKey);
+    cacheEntry = insertIntoCache(bundle.forget(), key);
   }
 
   // at this point the cacheEntry should exist in the hashtable,
   // but is not in the LRU cache.
   // put the cache entry at the front of the list
   mBundleCache.insertFront(cacheEntry);
 
   // finally, return the value
   *aResult = cacheEntry->mBundle;
   NS_ADDREF(*aResult);
 
   return NS_OK;
 }
 
 bundleCacheEntry_t *
 nsStringBundleService::insertIntoCache(already_AddRefed<nsIStringBundle> aBundle,
-                                       nsCStringKey* aHashKey)
+                                       nsCString &aHashKey)
 {
   bundleCacheEntry_t *cacheEntry;
 
   if (mBundleMap.Count() < MAX_CACHED_BUNDLES) {
     // cache not full - create a new entry
     cacheEntry = new bundleCacheEntry_t();
   } else {
     // cache is full
     // take the last entry in the list, and recycle it.
     cacheEntry = mBundleCache.getLast();
 
     // remove it from the hash table and linked list
-    NS_ASSERTION(mBundleMap.Exists(cacheEntry->mHashKey),
+    NS_ASSERTION(mBundleMap.Contains(cacheEntry->mHashKey),
                  "Element will not be removed!");
     mBundleMap.Remove(cacheEntry->mHashKey);
     cacheEntry->remove();
   }
 
   // at this point we have a new cacheEntry that doesn't exist
   // in the hashtable, so set up the cacheEntry
-  cacheEntry->mHashKey = (nsCStringKey*)aHashKey->Clone();
+  cacheEntry->mHashKey = aHashKey;
   cacheEntry->mBundle = aBundle;
 
   // insert the entry into the cache and map, make it the MRU
   mBundleMap.Put(cacheEntry->mHashKey, cacheEntry);
 
   return cacheEntry;
 }
 
--- a/intl/strres/src/nsStringBundleService.h
+++ b/intl/strres/src/nsStringBundleService.h
@@ -2,17 +2,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 nsStringBundleService_h__
 #define nsStringBundleService_h__
 
 #include "nsCOMPtr.h"
-#include "nsHashtable.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
 #include "nsIPersistentProperties2.h"
 #include "nsIStringBundle.h"
 #include "nsIObserver.h"
 #include "nsWeakReference.h"
 #include "nsIErrorService.h"
 #include "nsIStringBundleOverride.h"
 
 #include "mozilla/LinkedList.h"
@@ -24,31 +25,31 @@ class nsStringBundleService : public nsI
                               public nsSupportsWeakReference
 {
 public:
   nsStringBundleService();
   virtual ~nsStringBundleService();
 
   nsresult Init();
 
-  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_ISUPPORTS
   NS_DECL_NSISTRINGBUNDLESERVICE
   NS_DECL_NSIOBSERVER
 
 private:
   nsresult getStringBundle(const char *aUrl, nsIStringBundle** aResult);
   nsresult FormatWithBundle(nsIStringBundle* bundle, nsresult aStatus,
                             uint32_t argCount, char16_t** argArray,
                             char16_t* *result);
 
   void flushBundleCache();
 
   bundleCacheEntry_t *insertIntoCache(already_AddRefed<nsIStringBundle> aBundle,
-                                      nsCStringKey *aHashKey);
+                                      nsCString &aHashKey);
 
-  nsHashtable mBundleMap;
+  nsDataHashtable<nsCStringHashKey, bundleCacheEntry_t*> mBundleMap;
   mozilla::LinkedList<bundleCacheEntry_t> mBundleCache;
 
-  nsCOMPtr<nsIErrorService>     mErrorService;
+  nsCOMPtr<nsIErrorService> mErrorService;
   nsCOMPtr<nsIStringBundleOverride> mOverrideStrings;
 };
 
 #endif
--- a/js/src/assembler/jit/ExecutableAllocatorPosix.cpp
+++ b/js/src/assembler/jit/ExecutableAllocatorPosix.cpp
@@ -94,19 +94,20 @@ void ExecutableAllocator::reprotectRegio
 
 void
 ExecutablePool::toggleAllCodeAsAccessible(bool accessible)
 {
     char* begin = m_allocation.pages;
     size_t size = m_freePtr - begin;
 
     if (size) {
-        int flags = accessible
-                    ? PROT_READ | PROT_WRITE | PROT_EXEC
-                    : PROT_READ | PROT_WRITE;
+        // N.B. Some systems, like 32bit Mac OS 10.6, implicitly add PROT_EXEC
+        // when mprotect'ing memory with any flag other than PROT_NONE. Be
+        // sure to use PROT_NONE when making inaccessible.
+        int flags = accessible ? PROT_READ | PROT_WRITE | PROT_EXEC : PROT_NONE;
         if (mprotect(begin, size, flags))
             MOZ_CRASH();
     }
 }
 
 }
 
 #endif // HAVE(ASSEMBLER)
--- a/js/src/assembler/jit/ExecutableAllocatorWin.cpp
+++ b/js/src/assembler/jit/ExecutableAllocatorWin.cpp
@@ -111,18 +111,20 @@ void ExecutableAllocator::systemRelease(
 
 void
 ExecutablePool::toggleAllCodeAsAccessible(bool accessible)
 {
     char* begin = m_allocation.pages;
     size_t size = m_freePtr - begin;
 
     if (size) {
+        // N.B. DEP is not on automatically in Windows XP, so be sure to use
+        // PAGE_NOACCESS instead of PAGE_READWRITE when making inaccessible.
         DWORD oldProtect;
-        int flags = accessible ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
+        int flags = accessible ? PAGE_EXECUTE_READWRITE : PAGE_NOACCESS;
         if (!VirtualProtect(begin, size, flags, &oldProtect))
             MOZ_CRASH();
     }
 }
 
 #if ENABLE_ASSEMBLER_WX_EXCLUSIVE
 #error "ASSEMBLER_WX_EXCLUSIVE not yet suported on this platform."
 #endif
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -141,17 +141,18 @@ MaybeCheckEvalFreeVariables(ExclusiveCon
     return true;
 }
 
 static inline bool
 CanLazilyParse(ExclusiveContext *cx, const ReadOnlyCompileOptions &options)
 {
     return options.canLazilyParse &&
         options.compileAndGo &&
-        options.sourcePolicy == CompileOptions::SAVE_SOURCE &&
+        !cx->compartment()->options().discardSource() &&
+        !options.sourceIsLazy &&
         !(cx->compartment()->debugMode() &&
           cx->compartment()->runtimeFromAnyThread()->debugHooks.newScriptHook);
 }
 
 void
 frontend::MaybeCallSourceHandler(JSContext *cx, const ReadOnlyCompileOptions &options,
                                  const jschar *chars, size_t length)
 {
@@ -207,37 +208,32 @@ frontend::CompileScript(ExclusiveContext
      * and non-zero static level requires callerFrame.
      */
     JS_ASSERT_IF(evalCaller, options.compileAndGo);
     JS_ASSERT_IF(evalCaller, options.forEval);
     JS_ASSERT_IF(staticLevel != 0, evalCaller);
 
     if (!CheckLength(cx, length))
         return nullptr;
-    JS_ASSERT_IF(staticLevel != 0, options.sourcePolicy != CompileOptions::LAZY_SOURCE);
+    JS_ASSERT_IF(staticLevel != 0, !options.sourceIsLazy);
 
     RootedScriptSource sourceObject(cx, CreateScriptSourceObject(cx, options));
     if (!sourceObject)
         return nullptr;
 
     ScriptSource *ss = sourceObject->source();
 
     SourceCompressionTask mysct(cx);
     SourceCompressionTask *sct = extraSct ? extraSct : &mysct;
 
-    switch (options.sourcePolicy) {
-      case CompileOptions::SAVE_SOURCE:
-        if (!ss->setSourceCopy(cx, chars, length, false, sct))
+    if (!cx->compartment()->options().discardSource()) {
+        if (options.sourceIsLazy)
+            ss->setSourceRetrievable();
+        else if (!ss->setSourceCopy(cx, chars, length, false, sct))
             return nullptr;
-        break;
-      case CompileOptions::LAZY_SOURCE:
-        ss->setSourceRetrievable();
-        break;
-      case CompileOptions::NO_SOURCE:
-        break;
     }
 
     bool canLazilyParse = CanLazilyParse(cx, options);
 
     Maybe<Parser<SyntaxParseHandler> > syntaxParser;
     if (canLazilyParse) {
         syntaxParser.construct(cx, alloc, options, chars, length, /* foldConstants = */ false,
                                (Parser<SyntaxParseHandler> *) nullptr,
@@ -512,18 +508,18 @@ CompileFunctionBody(JSContext *cx, Mutab
         return false;
 
     RootedScriptSource sourceObject(cx, CreateScriptSourceObject(cx, options));
     if (!sourceObject)
         return nullptr;
     ScriptSource *ss = sourceObject->source();
 
     SourceCompressionTask sct(cx);
-    JS_ASSERT(options.sourcePolicy != CompileOptions::LAZY_SOURCE);
-    if (options.sourcePolicy == CompileOptions::SAVE_SOURCE) {
+    JS_ASSERT(!options.sourceIsLazy);
+    if (!cx->compartment()->options().discardSource()) {
         if (!ss->setSourceCopy(cx, chars, length, true, &sct))
             return false;
     }
 
     bool canLazilyParse = CanLazilyParse(cx, options);
 
     Maybe<Parser<SyntaxParseHandler> > syntaxParser;
     if (canLazilyParse) {
--- a/js/src/jit-test/tests/basic/withSourceHook.js
+++ b/js/src/jit-test/tests/basic/withSourceHook.js
@@ -30,27 +30,27 @@ withSourceHook(function (url) {
       // and verify that it is in force.
       assertEq(withSourceHook(function (url) {
                                 log += 'i';
                                 assertEq(url, 'inner');
                                 return '(function inner() { 1; })';
                               }, function () {
                                 log += 'I';
                                 return evaluate('(function inner() { 2; })',
-                                                { fileName: 'inner', sourcePolicy: 'LAZY_SOURCE' })
+                                                { fileName: 'inner', sourceIsLazy: true })
                                        .toSource();
                               }),
                '(function inner() { 1; })');
       // Verify that the source hook that throws has been reinstated.
       evaluate('(function middle() { })',
-               { fileName: 'middle', sourcePolicy: 'LAZY_SOURCE' })
+               { fileName: 'middle', sourceIsLazy: true })
       .toSource();
     });
   }, 'borborygmus');
 
   // Verify that the outermost source hook has been restored.
   assertEq(evaluate('(function outer() { 4; })',
-                    { fileName: 'outer', sourcePolicy: 'LAZY_SOURCE' })
+                    { fileName: 'outer', sourceIsLazy: true })
            .toSource(),
            '(function outer() { 3; })');
 });
 
 assertEq(log, 'OMIimo');
--- a/js/src/jit-test/tests/debug/Source-text-lazy.js
+++ b/js/src/jit-test/tests/debug/Source-text-lazy.js
@@ -23,17 +23,17 @@ function test(source) {
     return frobbed;
   }, () => {
     dbg.onDebuggerStatement = function (frame) {
       log += 'd';
       assertEq(frame.script.source.text, frobbed);
     }
 
     g.evaluate(source, { fileName: "BanalBivalve.jsm",
-                         sourcePolicy: "LAZY_SOURCE"});
+                         sourceIsLazy: true });
   });
 
   assertEq(log, 'ds');
 }
 
 test("debugger; // Ignominious Iguana");
 test("(function () { debugger; /* Meretricious Marmoset */})();");
 test("(() => { debugger; })(); // Gaunt Gibbon");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/iloop.js
@@ -0,0 +1,4 @@
+// |jit-test| exitstatus: 6;
+
+timeout(1);
+for(;;);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/parallel/gc-timeout.js
@@ -0,0 +1,27 @@
+// |jit-test| exitstatus: 6;
+
+// One sneaky way to run GC during a parallel section is to invoke the
+// gc() function during the parallel timeout!
+
+load(libdir + "parallelarray-helpers.js");
+
+function iterate(x) {
+  while (x == 2046) {
+    // for exactly one index, this infinitely loops!
+    // this ensures that the warmup doesn't loop.
+  }
+  return 22;
+}
+
+function timeoutfunc() {
+  print("Timed out, invoking the GC");
+  gc();
+  return false;
+}
+
+timeout(1, timeoutfunc);
+
+if (getBuildConfiguration().parallelJS)
+  Array.buildPar(2048, iterate);
+else
+  while(true);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/parallel/timeout.js
@@ -0,0 +1,17 @@
+// |jit-test| exitstatus: 6;
+
+load(libdir + "parallelarray-helpers.js");
+
+function iterate(x) {
+  while (x == 2046) {
+    // for exactly one index, this infinitely loops!
+    // this ensures that the warmup doesn't loop.
+  }
+  return 22;
+}
+
+timeout(1);
+if (getBuildConfiguration().parallelJS)
+  Array.buildPar(2048, iterate);
+else
+  while (true);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -4042,17 +4042,17 @@ CodeGenerator::visitGetArgumentsObjectAr
     ValueOperand out = ToOutValue(lir);
 
     masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()), temp);
     Address argAddr(temp, ArgumentsData::offsetOfArgs() + lir->mir()->argno() * sizeof(Value));
     masm.loadValue(argAddr, out);
 #ifdef DEBUG
     Label success;
     masm.branchTestMagic(Assembler::NotEqual, out, &success);
-    masm.assumeUnreachable("Result from ArgumentObject shouldn't be MIRType_Magic.");
+    masm.assumeUnreachable("Result from ArgumentObject shouldn't be JSVAL_TYPE_MAGIC.");
     masm.bind(&success);
 #endif
     return true;
 }
 
 bool
 CodeGenerator::visitSetArgumentsObjectArg(LSetArgumentsObjectArg *lir)
 {
@@ -4061,17 +4061,17 @@ CodeGenerator::visitSetArgumentsObjectAr
     ValueOperand value = ToValue(lir, LSetArgumentsObjectArg::ValueIndex);
 
     masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()), temp);
     Address argAddr(temp, ArgumentsData::offsetOfArgs() + lir->mir()->argno() * sizeof(Value));
     emitPreBarrier(argAddr, MIRType_Value);
 #ifdef DEBUG
     Label success;
     masm.branchTestMagic(Assembler::NotEqual, argAddr, &success);
-    masm.assumeUnreachable("Result in ArgumentObject shouldn't be MIRType_Magic.");
+    masm.assumeUnreachable("Result in ArgumentObject shouldn't be JSVAL_TYPE_MAGIC.");
     masm.bind(&success);
 #endif
     masm.storeValue(value, argAddr);
     return true;
 }
 
 bool
 CodeGenerator::visitReturnFromCtor(LReturnFromCtor *lir)
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -437,16 +437,32 @@ class TypeAnalyzer
 };
 
 } /* anonymous namespace */
 
 // Try to specialize this phi based on its non-cyclic inputs.
 static MIRType
 GuessPhiType(MPhi *phi, bool *hasInputsWithEmptyTypes)
 {
+#ifdef DEBUG
+    // Check that different magic constants aren't flowing together.
+    MIRType magicType = MIRType_None;
+    for (size_t i = 0; i < phi->numOperands(); i++) {
+        MDefinition *in = phi->getOperand(i);
+        if (in->type() == MIRType_MagicOptimizedArguments ||
+            in->type() == MIRType_MagicHole ||
+            in->type() == MIRType_MagicIsConstructing)
+        {
+            if (magicType == MIRType_None)
+                magicType = in->type();
+            MOZ_ASSERT(magicType == in->type());
+        }
+    }
+#endif
+
     *hasInputsWithEmptyTypes = false;
 
     MIRType type = MIRType_None;
     bool convertibleToFloat32 = false;
     bool hasPhiInputs = false;
     for (size_t i = 0, e = phi->numOperands(); i < e; i++) {
         MDefinition *in = phi->getOperand(i);
         if (in->isPhi()) {
@@ -709,17 +725,17 @@ TypeAnalyzer::replaceRedundantPhi(MPhi *
     js::Value v;
     switch (phi->type()) {
       case MIRType_Undefined:
         v = UndefinedValue();
         break;
       case MIRType_Null:
         v = NullValue();
         break;
-      case MIRType_Magic:
+      case MIRType_MagicOptimizedArguments:
         v = MagicValue(JS_OPTIMIZED_ARGUMENTS);
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("unexpected type");
     }
     MConstant *c = MConstant::New(alloc(), v);
     // The instruction pass will insert the box
     block->insertBefore(*(block->begin()), c);
@@ -732,17 +748,20 @@ TypeAnalyzer::insertConversions()
     // Instructions are processed in reverse postorder: all uses are defs are
     // seen before uses. This ensures that output adjustment (which may rewrite
     // inputs of uses) does not conflict with input adjustment.
     for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) {
         if (mir->shouldCancel("Insert Conversions"))
             return false;
 
         for (MPhiIterator phi(block->phisBegin()); phi != block->phisEnd();) {
-            if (phi->type() <= MIRType_Null || phi->type() == MIRType_Magic) {
+            if (phi->type() == MIRType_Undefined ||
+                phi->type() == MIRType_Null ||
+                phi->type() == MIRType_MagicOptimizedArguments)
+            {
                 replaceRedundantPhi(*phi);
                 phi = block->discardPhiAt(phi);
             } else {
                 adjustPhiInputs(*phi);
                 phi++;
             }
         }
         for (MInstructionIterator iter(block->begin()); iter != block->end(); iter++) {
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -472,17 +472,17 @@ IonBuilder::analyzeNewLoopTypes(MBasicBl
         MPhi *phi = entry->getSlot(slot)->toPhi();
 
         if (*last == JSOP_POS)
             last = earlier;
 
         if (js_CodeSpec[*last].format & JOF_TYPESET) {
             types::TemporaryTypeSet *typeSet = bytecodeTypes(last);
             if (!typeSet->empty()) {
-                MIRType type = MIRTypeFromValueType(typeSet->getKnownTypeTag());
+                MIRType type = typeSet->getKnownMIRType();
                 if (!phi->addBackedgeType(type, typeSet))
                     return false;
             }
         } else if (*last == JSOP_GETLOCAL || *last == JSOP_GETARG) {
             uint32_t slot = (*last == JSOP_GETLOCAL)
                             ? info().localSlot(GET_LOCALNO(last))
                             : info().argSlotUnchecked(GET_ARGNO(last));
             if (slot < info().firstStackSlot()) {
@@ -884,19 +884,17 @@ IonBuilder::buildInline(IonBuilder *call
 }
 
 void
 IonBuilder::rewriteParameter(uint32_t slotIdx, MDefinition *param, int32_t argIndex)
 {
     JS_ASSERT(param->isParameter() || param->isGetArgumentsObjectArg());
 
     types::TemporaryTypeSet *types = param->resultTypeSet();
-    JSValueType definiteType = types->getKnownTypeTag();
-
-    MDefinition *actual = ensureDefiniteType(param, definiteType);
+    MDefinition *actual = ensureDefiniteType(param, types->getKnownMIRType());
     if (actual == param)
         return;
 
     // Careful! We leave the original MParameter in the entry resume point. The
     // arguments still need to be checked unless proven otherwise at the call
     // site, and these checks can bailout. We can end up:
     //   v0 = Parameter(0)
     //   v1 = Unbox(v0, INT32)
@@ -1028,17 +1026,17 @@ IonBuilder::addOsrValueTypeBarrier(uint3
 
     if (typeSet && !typeSet->unknown()) {
         MInstruction *barrier = MTypeBarrier::New(alloc(), def, typeSet);
         osrBlock->insertBefore(osrBlock->lastIns(), barrier);
         osrBlock->rewriteSlot(slot, barrier);
         def = barrier;
     } else if (type == MIRType_Null ||
                type == MIRType_Undefined ||
-               type == MIRType_Magic)
+               type == MIRType_MagicOptimizedArguments)
     {
         // No unbox instruction will be added below, so check the type by
         // adding a type barrier for a singleton type set.
         types::Type ntype = types::Type::PrimitiveType(ValueTypeFromMIRType(type));
         typeSet = alloc_->lifoAlloc()->new_<types::TemporaryTypeSet>(ntype);
         if (!typeSet)
             return false;
         MInstruction *barrier = MTypeBarrier::New(alloc(), def, typeSet);
@@ -1074,17 +1072,17 @@ IonBuilder::addOsrValueTypeBarrier(uint3
       {
         MConstant *c = MConstant::New(alloc(), UndefinedValue());
         osrBlock->insertBefore(osrBlock->lastIns(), c);
         osrBlock->rewriteSlot(slot, c);
         def = c;
         break;
       }
 
-      case MIRType_Magic:
+      case MIRType_MagicOptimizedArguments:
         JS_ASSERT(lazyArguments_);
         osrBlock->rewriteSlot(slot, lazyArguments_);
         def = lazyArguments_;
         break;
 
       default:
         break;
     }
@@ -4940,24 +4938,24 @@ IonBuilder::jsop_funapply(uint32_t argc)
             return false;
         return makeCall(native, callInfo, false);
     }
 
     // Disable compilation if the second argument to |apply| cannot be guaranteed
     // to be either definitely |arguments| or definitely not |arguments|.
     MDefinition *argument = current->peek(-1);
     if (script()->argumentsHasVarBinding() &&
-        argument->mightBeType(MIRType_Magic) &&
-        argument->type() != MIRType_Magic)
+        argument->mightBeType(MIRType_MagicOptimizedArguments) &&
+        argument->type() != MIRType_MagicOptimizedArguments)
     {
         return abort("fun.apply with MaybeArguments");
     }
 
     // Fallback to regular call if arg 2 is not definitely |arguments|.
-    if (argument->type() != MIRType_Magic) {
+    if (argument->type() != MIRType_MagicOptimizedArguments) {
         CallInfo callInfo(alloc(), false);
         if (!callInfo.init(current, argc))
             return false;
         return makeCall(native, callInfo, false);
     }
 
     if (!native ||
         !native->isNative() ||
@@ -5189,17 +5187,17 @@ ArgumentTypesMatch(MDefinition *def, typ
     }
 
     if (def->type() == MIRType_Value)
         return false;
 
     if (def->type() == MIRType_Object)
         return calleeTypes->unknownObject();
 
-    return calleeTypes->mightBeType(ValueTypeFromMIRType(def->type()));
+    return calleeTypes->mightBeMIRType(def->type());
 }
 
 bool
 IonBuilder::testNeedsArgumentCheck(JSFunction *target, CallInfo &callInfo)
 {
     // If we have a known target, check if the caller arg types are a subset of callee.
     // Since typeset accumulates and can't decrease that means we don't need to check
     // the arguments anymore.
@@ -5214,17 +5212,17 @@ IonBuilder::testNeedsArgumentCheck(JSFun
     if (!ArgumentTypesMatch(callInfo.thisArg(), types::TypeScript::ThisTypes(targetScript)))
         return true;
     uint32_t expected_args = Min<uint32_t>(callInfo.argc(), target->nargs());
     for (size_t i = 0; i < expected_args; i++) {
         if (!ArgumentTypesMatch(callInfo.getArg(i), types::TypeScript::ArgTypes(targetScript, i)))
             return true;
     }
     for (size_t i = callInfo.argc(); i < target->nargs(); i++) {
-        if (!types::TypeScript::ArgTypes(targetScript, i)->mightBeType(JSVAL_TYPE_UNDEFINED))
+        if (!types::TypeScript::ArgTypes(targetScript, i)->mightBeMIRType(MIRType_Undefined))
             return true;
     }
 
     return false;
 }
 
 MCall *
 IonBuilder::makeCallHelper(JSFunction *target, CallInfo &callInfo, bool cloneAtCallsite)
@@ -5315,17 +5313,17 @@ DOMCallNeedsBarrier(const JSJitInfo* jit
         return true;
 
     // JSVAL_TYPE_OBJECT doesn't tell us much; we still have to barrier on the
     // actual type of the object.
     if (jitinfo->returnType() == JSVAL_TYPE_OBJECT)
         return true;
 
     // No need for a barrier if we're already expecting the type we'll produce.
-    return jitinfo->returnType() != types->getKnownTypeTag();
+    return MIRTypeFromValueType(jitinfo->returnType()) != types->getKnownMIRType();
 }
 
 bool
 IonBuilder::makeCall(JSFunction *target, CallInfo &callInfo, bool cloneAtCallsite)
 {
     // Constructor calls to non-constructors should throw. We don't want to use
     // CallKnown in this case.
     JS_ASSERT_IF(callInfo.constructing() && target,
@@ -5368,18 +5366,18 @@ IonBuilder::jsop_eval(uint32_t argc)
 
         if (!info().funMaybeLazy())
             return abort("Direct eval in global code");
 
         // The 'this' value for the outer and eval scripts must be the
         // same. This is not guaranteed if a primitive string/number/etc.
         // is passed through to the eval invoke as the primitive may be
         // boxed into different objects if accessed via 'this'.
-        JSValueType type = thisTypes->getKnownTypeTag();
-        if (type != JSVAL_TYPE_OBJECT && type != JSVAL_TYPE_NULL && type != JSVAL_TYPE_UNDEFINED)
+        MIRType type = thisTypes->getKnownMIRType();
+        if (type != MIRType_Object && type != MIRType_Null && type != MIRType_Undefined)
             return abort("Direct eval from script with maybe-primitive 'this'");
 
         CallInfo callInfo(alloc(), /* constructing = */ false);
         if (!callInfo.init(current, argc))
             return false;
         callInfo.setImplicitlyUsedUnchecked();
 
         callInfo.fun()->setImplicitlyUsedUnchecked();
@@ -5528,17 +5526,17 @@ IonBuilder::jsop_initelem_array()
     MDefinition *value = current->pop();
     MDefinition *obj = current->peek(-1);
 
     // Make sure that arrays have the type being written to them by the
     // intializer, and that arrays are marked as non-packed when writing holes
     // to them during initialization.
     bool needStub = false;
     types::TypeObjectKey *initializer = obj->resultTypeSet()->getObject(0);
-    if (value->isConstant() && value->toConstant()->value().isMagic(JS_ELEMENTS_HOLE)) {
+    if (value->type() == MIRType_MagicHole) {
         if (!initializer->hasFlags(constraints(), types::OBJECT_FLAG_NON_PACKED))
             needStub = true;
     } else if (!initializer->unknownProperties()) {
         types::HeapTypeSetKey elemTypes = initializer->property(JSID_VOID);
         if (!TypeSetIncludes(elemTypes.maybeTypes(), value->type(), value->resultTypeSet())) {
             elemTypes.freeze(constraints());
             needStub = true;
         }
@@ -5953,17 +5951,17 @@ IonBuilder::newPendingLoopHeader(MBasicB
             else
                 existingType = baselineFrame_->varTypes[var];
 
             // Extract typeset from value.
             types::TemporaryTypeSet *typeSet =
                 alloc_->lifoAlloc()->new_<types::TemporaryTypeSet>(existingType);
             if (!typeSet)
                 return nullptr;
-            MIRType type = MIRTypeFromValueType(typeSet->getKnownTypeTag());
+            MIRType type = typeSet->getKnownMIRType();
             if (!phi->addBackedgeType(type, typeSet))
                 return nullptr;
         }
     }
 
     return block;
 }
 
@@ -6254,18 +6252,17 @@ IonBuilder::pushTypeBarrier(MDefinition 
 
     // If the instruction has no side effects, we'll resume the entire operation.
     // The actual type barrier will occur in the interpreter. If the
     // instruction is effectful, even if it has a singleton type, there
     // must be a resume point capturing the original def, and resuming
     // to that point will explicitly monitor the new type.
 
     if (!needsBarrier) {
-        JSValueType type = observed->getKnownTypeTag();
-        MDefinition *replace = ensureDefiniteType(def, type);
+        MDefinition *replace = ensureDefiniteType(def, observed->getKnownMIRType());
         if (replace != def) {
             current->pop();
             current->push(replace);
         }
         replace->setResultTypeSet(observed);
         return true;
     }
 
@@ -6289,82 +6286,81 @@ IonBuilder::pushTypeBarrier(MDefinition 
 bool
 IonBuilder::pushDOMTypeBarrier(MInstruction *ins, types::TemporaryTypeSet *observed, JSFunction* func)
 {
     JS_ASSERT(func && func->isNative() && func->jitInfo());
 
     const JSJitInfo *jitinfo = func->jitInfo();
     bool barrier = DOMCallNeedsBarrier(jitinfo, observed);
     // Need to be a bit careful: if jitinfo->returnType is JSVAL_TYPE_DOUBLE but
-    // types->getKnownTypeTag() is JSVAL_TYPE_INT32, then don't unconditionally
+    // types->getKnownMIRType() is MIRType_Int32, then don't unconditionally
     // unbox as a double.  Instead, go ahead and barrier on having an int type,
     // since we know we need a barrier anyway due to the type mismatch.  This is
     // the only situation in which TI actually has more information about the
     // JSValueType than codegen can, short of jitinfo->returnType just being
     // JSVAL_TYPE_UNKNOWN.
     MDefinition* replace = ins;
     if (jitinfo->returnType() != JSVAL_TYPE_DOUBLE ||
-        observed->getKnownTypeTag() != JSVAL_TYPE_INT32) {
+        observed->getKnownMIRType() != MIRType_Int32) {
         JS_ASSERT(jitinfo->returnType() == JSVAL_TYPE_UNKNOWN ||
-                  observed->getKnownTypeTag() == JSVAL_TYPE_UNKNOWN ||
-                  jitinfo->returnType() == observed->getKnownTypeTag());
-        replace = ensureDefiniteType(ins, jitinfo->returnType());
+                  observed->getKnownMIRType() == MIRType_Value ||
+                  MIRTypeFromValueType(jitinfo->returnType()) == observed->getKnownMIRType());
+        replace = ensureDefiniteType(ins, MIRTypeFromValueType(jitinfo->returnType()));
         if (replace != ins) {
             current->pop();
             current->push(replace);
         }
     } else {
         JS_ASSERT(barrier);
     }
 
     return pushTypeBarrier(replace, observed, barrier);
 }
 
 MDefinition *
-IonBuilder::ensureDefiniteType(MDefinition *def, JSValueType definiteType)
+IonBuilder::ensureDefiniteType(MDefinition *def, MIRType definiteType)
 {
     MInstruction *replace;
     switch (definiteType) {
-      case JSVAL_TYPE_UNDEFINED:
+      case MIRType_Undefined:
         def->setImplicitlyUsedUnchecked();
         replace = MConstant::New(alloc(), UndefinedValue());
         break;
 
-      case JSVAL_TYPE_NULL:
+      case MIRType_Null:
         def->setImplicitlyUsedUnchecked();
         replace = MConstant::New(alloc(), NullValue());
         break;
 
-      case JSVAL_TYPE_UNKNOWN:
+      case MIRType_Value:
         return def;
 
       default: {
-        MIRType replaceType = MIRTypeFromValueType(definiteType);
         if (def->type() != MIRType_Value) {
-            JS_ASSERT(def->type() == replaceType);
+            JS_ASSERT(def->type() == definiteType);
             return def;
         }
-        replace = MUnbox::New(alloc(), def, replaceType, MUnbox::Infallible);
+        replace = MUnbox::New(alloc(), def, definiteType, MUnbox::Infallible);
         break;
       }
     }
 
     current->add(replace);
     return replace;
 }
 
 MDefinition *
 IonBuilder::ensureDefiniteTypeSet(MDefinition *def, types::TemporaryTypeSet *types)
 {
     // We cannot arbitrarily add a typeset to a definition. It can be shared
     // in another path. So we always need to create a new MIR.
 
     // Use ensureDefiniteType to do unboxing. If that happened the type can
     // be added on the newly created unbox operation.
-    MDefinition *replace = ensureDefiniteType(def, types->getKnownTypeTag());
+    MDefinition *replace = ensureDefiniteType(def, types->getKnownMIRType());
     if (replace != def) {
         replace->setResultTypeSet(types);
         return replace;
     }
 
     // Create a NOP mir instruction to filter the typeset.
     MFilterTypeSet *filter = MFilterTypeSet::New(alloc(), def, types);
     current->add(filter);
@@ -6423,32 +6419,32 @@ IonBuilder::getStaticName(JSObject *stat
     }
 
     types::TemporaryTypeSet *types = bytecodeTypes(pc);
     bool barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), staticType,
                                                 name, types, /* updateObserved = */ true);
 
     JSObject *singleton = types->getSingleton();
 
-    JSValueType knownType = types->getKnownTypeTag();
+    MIRType knownType = types->getKnownMIRType();
     if (!barrier) {
         if (singleton) {
             // Try to inline a known constant value.
             if (testSingletonProperty(staticObject, name) == singleton)
                 return pushConstant(ObjectValue(*singleton));
         }
-        if (knownType == JSVAL_TYPE_UNDEFINED)
+        if (knownType == MIRType_Undefined)
             return pushConstant(UndefinedValue());
-        if (knownType == JSVAL_TYPE_NULL)
+        if (knownType == MIRType_Null)
             return pushConstant(NullValue());
     }
 
     MInstruction *obj = constant(ObjectValue(*staticObject));
 
-    MIRType rvalType = MIRTypeFromValueType(types->getKnownTypeTag());
+    MIRType rvalType = types->getKnownMIRType();
     if (barrier)
         rvalType = MIRType_Value;
 
     return loadSlot(obj, property.maybeTypes()->definiteSlot(), NumFixedSlots(staticObject),
                     rvalType, barrier, types);
 }
 
 // Whether 'types' includes all possible values represented by input/inputTypes.
@@ -6461,17 +6457,17 @@ jit::TypeSetIncludes(types::TypeSet *typ
     switch (input) {
       case MIRType_Undefined:
       case MIRType_Null:
       case MIRType_Boolean:
       case MIRType_Int32:
       case MIRType_Double:
       case MIRType_Float32:
       case MIRType_String:
-      case MIRType_Magic:
+      case MIRType_MagicOptimizedArguments:
         return types->hasType(types::Type::PrimitiveType(ValueTypeFromMIRType(input)));
 
       case MIRType_Object:
         return types->unknownObject() || (inputTypes && inputTypes->isSubset(types));
 
       case MIRType_Value:
         return types->unknown() || (inputTypes && inputTypes->isSubset(types));
 
@@ -6521,19 +6517,19 @@ IonBuilder::setStaticName(JSObject *stat
     JS_ASSERT(&obj->toConstant()->value().toObject() == staticObject);
 
     if (NeedsPostBarrier(info(), value))
         current->add(MPostWriteBarrier::New(alloc(), obj, value));
 
     // If the property has a known type, we may be able to optimize typed stores by not
     // storing the type tag.
     MIRType slotType = MIRType_None;
-    JSValueType knownType = property.knownTypeTag(constraints());
-    if (knownType != JSVAL_TYPE_UNKNOWN)
-        slotType = MIRTypeFromValueType(knownType);
+    MIRType knownType = property.knownMIRType(constraints());
+    if (knownType != MIRType_Value)
+        slotType = knownType;
 
     bool needsBarrier = property.needsBarrier(constraints());
     return storeSlot(obj, property.maybeTypes()->definiteSlot(), NumFixedSlots(staticObject),
                      value, needsBarrier, slotType);
 }
 
 bool
 IonBuilder::jsop_getgname(PropertyName *name)
@@ -6588,21 +6584,20 @@ IonBuilder::jsop_getname(PropertyName *n
     types::TemporaryTypeSet *types = bytecodeTypes(pc);
     return pushTypeBarrier(ins, types, true);
 }
 
 bool
 IonBuilder::jsop_intrinsic(PropertyName *name)
 {
     types::TemporaryTypeSet *types = bytecodeTypes(pc);
-    JSValueType type = types->getKnownTypeTag();
 
     // If we haven't executed this opcode yet, we need to get the intrinsic
     // value and monitor the result.
-    if (type == JSVAL_TYPE_UNKNOWN) {
+    if (types->empty()) {
         MCallGetIntrinsicValue *ins = MCallGetIntrinsicValue::New(alloc(), name);
 
         current->add(ins);
         current->push(ins);
 
         if (!resumeAfter(ins))
             return false;
 
@@ -6627,33 +6622,33 @@ IonBuilder::jsop_bindname(PropertyName *
     MBindNameCache *ins = MBindNameCache::New(alloc(), scopeChain, name, script(), pc);
 
     current->add(ins);
     current->push(ins);
 
     return resumeAfter(ins);
 }
 
-static JSValueType
+static MIRType
 GetElemKnownType(bool needsHoleCheck, types::TemporaryTypeSet *types)
 {
-    JSValueType knownType = types->getKnownTypeTag();
+    MIRType knownType = types->getKnownMIRType();
 
     // Null and undefined have no payload so they can't be specialized.
     // Since folding null/undefined while building SSA is not safe (see the
     // comment in IsPhiObservable), we just add an untyped load instruction
     // and rely on pushTypeBarrier and DCE to replace it with a null/undefined
     // constant.
-    if (knownType == JSVAL_TYPE_UNDEFINED || knownType == JSVAL_TYPE_NULL)
-        knownType = JSVAL_TYPE_UNKNOWN;
+    if (knownType == MIRType_Undefined || knownType == MIRType_Null)
+        knownType = MIRType_Value;
 
     // Different architectures may want typed element reads which require
     // hole checks to be done as either value or typed reads.
     if (needsHoleCheck && !LIRGenerator::allowTypedElementHoleCheck())
-        knownType = JSVAL_TYPE_UNKNOWN;
+        knownType = MIRType_Value;
 
     return knownType;
 }
 
 bool
 IonBuilder::jsop_getelem()
 {
     MDefinition *index = current->pop();
@@ -6677,17 +6672,17 @@ IonBuilder::jsop_getelem()
         return emitted;
 
     if (!getElemTryArguments(&emitted, obj, index) || emitted)
         return emitted;
 
     if (!getElemTryArgumentsInlined(&emitted, obj, index) || emitted)
         return emitted;
 
-    if (script()->argumentsHasVarBinding() && obj->mightBeType(MIRType_Magic))
+    if (script()->argumentsHasVarBinding() && obj->mightBeType(MIRType_MagicOptimizedArguments))
         return abort("Type is not definitely lazy arguments.");
 
     if (!getElemTryCache(&emitted, obj, index) || emitted)
         return emitted;
 
     // Emit call.
     MInstruction *ins = MCallGetElement::New(alloc(), obj, index);
 
@@ -7113,17 +7108,17 @@ IonBuilder::getElemTryString(bool *emitt
 bool
 IonBuilder::getElemTryArguments(bool *emitted, MDefinition *obj, MDefinition *index)
 {
     JS_ASSERT(*emitted == false);
 
     if (inliningDepth_ > 0)
         return true;
 
-    if (obj->type() != MIRType_Magic)
+    if (obj->type() != MIRType_MagicOptimizedArguments)
         return true;
 
     // Emit GetFrameArgument.
 
     JS_ASSERT(!info().argsObjAliasesFormals());
 
     // Type Inference has guaranteed this is an optimized arguments object.
     obj->setImplicitlyUsedUnchecked();
@@ -7156,17 +7151,17 @@ IonBuilder::getElemTryArguments(bool *em
 bool
 IonBuilder::getElemTryArgumentsInlined(bool *emitted, MDefinition *obj, MDefinition *index)
 {
     JS_ASSERT(*emitted == false);
 
     if (inliningDepth_ == 0)
         return true;
 
-    if (obj->type() != MIRType_Magic)
+    if (obj->type() != MIRType_MagicOptimizedArguments)
         return true;
 
     // Emit inlined arguments.
     obj->setImplicitlyUsedUnchecked();
 
     JS_ASSERT(!info().argsObjAliasesFormals());
 
     // When the id is constant, we can just return the corresponding inlined argument
@@ -7232,20 +7227,20 @@ IonBuilder::getElemTryCache(bool *emitte
     current->push(ins);
 
     if (!resumeAfter(ins))
         return false;
 
     // Spice up type information.
     if (index->type() == MIRType_Int32 && !barrier) {
         bool needHoleCheck = !ElementAccessIsPacked(constraints(), obj);
-        JSValueType knownType = GetElemKnownType(needHoleCheck, types);
-
-        if (knownType != JSVAL_TYPE_UNKNOWN && knownType != JSVAL_TYPE_DOUBLE)
-            ins->setResultType(MIRTypeFromValueType(knownType));
+        MIRType knownType = GetElemKnownType(needHoleCheck, types);
+
+        if (knownType != MIRType_Value && knownType != MIRType_Double)
+            ins->setResultType(knownType);
     }
 
     if (!pushTypeBarrier(ins, types, barrier))
         return false;
 
     *emitted = true;
     return true;
 }
@@ -7267,17 +7262,17 @@ IonBuilder::jsop_getelem_dense(MDefiniti
 
     // Reads which are on holes in the object do not have to bail out if
     // undefined values have been observed at this access site and the access
     // cannot hit another indexed property on the object or its prototypes.
     bool readOutOfBounds =
         types->hasType(types::Type::UndefinedType()) &&
         !ElementAccessHasExtraIndexedProperty(constraints(), obj);
 
-    JSValueType knownType = JSVAL_TYPE_UNKNOWN;
+    MIRType knownType = MIRType_Value;
     if (!barrier)
         knownType = GetElemKnownType(needsHoleCheck, types);
 
     // Ensure index is an integer.
     MInstruction *idInt32 = MToInt32::New(alloc(), index);
     current->add(idInt32);
     index = idInt32;
 
@@ -7300,17 +7295,17 @@ IonBuilder::jsop_getelem_dense(MDefiniti
     types::TemporaryTypeSet *objTypes = obj->resultTypeSet();
     ExecutionMode executionMode = info().executionMode();
     bool loadDouble =
         executionMode == SequentialExecution &&
         !barrier &&
         loopDepth_ &&
         !readOutOfBounds &&
         !needsHoleCheck &&
-        knownType == JSVAL_TYPE_DOUBLE &&
+        knownType == MIRType_Double &&
         objTypes &&
         objTypes->convertDoubleElements(constraints()) == types::TemporaryTypeSet::AlwaysConvertToDoubles;
     if (loadDouble)
         elements = addConvertElementsToDoubles(elements);
 
     MInstruction *load;
 
     if (!readOutOfBounds) {
@@ -7327,17 +7322,17 @@ IonBuilder::jsop_getelem_dense(MDefiniti
         // or that we can read out-of-bounds accesses. In this case, the bounds
         // check is part of the opcode.
         load = MLoadElementHole::New(alloc(), elements, index, initLength, needsHoleCheck);
         current->add(load);
 
         // If maybeUndefined was true, the typeset must have undefined, and
         // then either additional types or a barrier. This means we should
         // never have a typed version of LoadElementHole.
-        JS_ASSERT(knownType == JSVAL_TYPE_UNKNOWN);
+        JS_ASSERT(knownType == MIRType_Value);
     }
 
     // If the array is being converted to doubles, but we've observed
     // just int, substitute a type set of int+double into the observed
     // type set. The reason for this is that, in the
     // interpreter+baseline, such arrays may consist of mixed
     // ints/doubles, but when we enter ion code, we will be coercing
     // all inputs to doubles. Therefore, the type barrier checking for
@@ -7348,30 +7343,30 @@ IonBuilder::jsop_getelem_dense(MDefiniti
     // and doubles. There is perhaps a tradeoff here, so we limit this
     // optimization to parallel code, where it is needed to prevent
     // perpetual bailouts in some extreme cases. (Bug 977853)
     //
     // NB: we have not added a MConvertElementsToDoubles MIR, so we
     // cannot *assume* the result is a double.
     if (executionMode == ParallelExecution &&
         barrier &&
-        types->getKnownTypeTag() == JSVAL_TYPE_INT32 &&
+        types->getKnownMIRType() == MIRType_Int32 &&
         objTypes &&
         objTypes->convertDoubleElements(constraints()) == types::TemporaryTypeSet::AlwaysConvertToDoubles)
     {
         // Note: double implies int32 as well for typesets
         types = alloc_->lifoAlloc()->new_<types::TemporaryTypeSet>(types::Type::DoubleType());
         if (!types)
             return false;
 
         barrier = false; // Don't need a barrier anymore
     }
 
-    if (knownType != JSVAL_TYPE_UNKNOWN)
-        load->setResultType(MIRTypeFromValueType(knownType));
+    if (knownType != MIRType_Value)
+        load->setResultType(knownType);
 
     current->push(load);
     return pushTypeBarrier(load, types, barrier);
 }
 
 MInstruction *
 IonBuilder::getTypedArrayLength(MDefinition *obj)
 {
@@ -7573,17 +7568,17 @@ IonBuilder::jsop_setelem()
         return emitted;
 
     if (!setElemTryDense(&emitted, object, index, value) || emitted)
         return emitted;
 
     if (!setElemTryArguments(&emitted, object, index, value) || emitted)
         return emitted;
 
-    if (script()->argumentsHasVarBinding() && object->mightBeType(MIRType_Magic))
+    if (script()->argumentsHasVarBinding() && object->mightBeType(MIRType_MagicOptimizedArguments))
         return abort("Type is not definitely lazy arguments.");
 
     if (!setElemTryCache(&emitted, object, index, value) || emitted)
         return emitted;
 
     // Emit call.
     MInstruction *ins = MCallSetElement::New(alloc(), object, index, value);
     current->add(ins);
@@ -7784,17 +7779,17 @@ IonBuilder::setElemTryDense(bool *emitte
 }
 
 bool
 IonBuilder::setElemTryArguments(bool *emitted, MDefinition *object,
                                 MDefinition *index, MDefinition *value)
 {
     JS_ASSERT(*emitted == false);
 
-    if (object->type() != MIRType_Magic)
+    if (object->type() != MIRType_MagicOptimizedArguments)
         return true;
 
     // Arguments are not supported yet.
     return abort("NYI arguments[]=");
 }
 
 bool
 IonBuilder::setElemTryCache(bool *emitted, MDefinition *object,
@@ -8044,17 +8039,17 @@ IonBuilder::jsop_length()
     return jsop_getprop(name);
 }
 
 bool
 IonBuilder::jsop_length_fastPath()
 {
     types::TemporaryTypeSet *types = bytecodeTypes(pc);
 
-    if (types->getKnownTypeTag() != JSVAL_TYPE_INT32)
+    if (types->getKnownMIRType() != MIRType_Int32)
         return false;
 
     MDefinition *obj = current->peek(-1);
 
     if (obj->mightBeType(MIRType_String)) {
         if (obj->mightBeType(MIRType_Object))
             return false;
         current->pop();
@@ -8588,19 +8583,22 @@ IonBuilder::jsop_getprop(PropertyName *n
 
     return pushTypeBarrier(call, types, true);
 }
 
 bool
 IonBuilder::getPropTryArgumentsLength(bool *emitted)
 {
     JS_ASSERT(*emitted == false);
-    if (current->peek(-1)->type() != MIRType_Magic) {
-        if (script()->argumentsHasVarBinding() && current->peek(-1)->mightBeType(MIRType_Magic))
+    if (current->peek(-1)->type() != MIRType_MagicOptimizedArguments) {
+        if (script()->argumentsHasVarBinding() &&
+            current->peek(-1)->mightBeType(MIRType_MagicOptimizedArguments))
+        {
             return abort("Type is not definitely lazy arguments.");
+        }
         return true;
     }
     if (JSOp(*pc) != JSOP_LENGTH)
         return true;
 
     *emitted = true;
     return jsop_arguments_length();
 }
@@ -8734,17 +8732,17 @@ IonBuilder::getPropTryDefiniteSlot(bool 
     if (obj->type() != MIRType_Object) {
         MGuardObject *guard = MGuardObject::New(alloc(), obj);
         current->add(guard);
         useObj = guard;
     }
 
     MLoadFixedSlot *fixed = MLoadFixedSlot::New(alloc(), useObj, property.maybeTypes()->definiteSlot());
     if (!barrier)
-        fixed->setResultType(MIRTypeFromValueType(types->getKnownTypeTag()));
+        fixed->setResultType(types->getKnownMIRType());
 
     current->add(fixed);
     current->push(fixed);
 
     if (!pushTypeBarrier(fixed, types, barrier))
         return false;
 
     *emitted = true;
@@ -8792,17 +8790,17 @@ IonBuilder::getPropTryCommonGetter(bool 
         if (!pushDOMTypeBarrier(get, types, commonGetter))
             return false;
 
         *emitted = true;
         return true;
     }
 
     // Don't call the getter with a primitive value.
-    if (objTypes->getKnownTypeTag() != JSVAL_TYPE_OBJECT) {
+    if (objTypes->getKnownMIRType() != MIRType_Object) {
         MGuardObject *guardObj = MGuardObject::New(alloc(), obj);
         current->add(guardObj);
         obj = guardObj;
     }
 
     // Spoof stack to expected state for call.
 
     // Make sure there's enough room
@@ -8868,17 +8866,17 @@ IonBuilder::getPropTryInlineAccess(bool 
 
     BaselineInspector::ShapeVector shapes(alloc());
     if (!inspector->maybeShapesForPropertyOp(pc, shapes))
         return false;
 
     if (shapes.empty() || !CanInlinePropertyOpShapes(shapes))
         return true;
 
-    MIRType rvalType = MIRTypeFromValueType(types->getKnownTypeTag());
+    MIRType rvalType = types->getKnownMIRType();
     if (barrier || IsNullOrUndefined(rvalType))
         rvalType = MIRType_Value;
 
     MDefinition *obj = current->pop();
     if (shapes.length() == 1) {
         // In the monomorphic case, use separate ShapeGuard and LoadSlot
         // instructions.
         spew("Inlining monomorphic GETPROP");
@@ -8970,17 +8968,17 @@ IonBuilder::getPropTryCache(bool *emitte
     }
 
     current->add(load);
     current->push(load);
 
     if (load->isEffectful() && !resumeAfter(load))
         return false;
 
-    MIRType rvalType = MIRTypeFromValueType(types->getKnownTypeTag());
+    MIRType rvalType = types->getKnownMIRType();
     if (barrier || IsNullOrUndefined(rvalType))
         rvalType = MIRType_Value;
     load->setResultType(rvalType);
 
     if (!pushTypeBarrier(load, types, barrier))
         return false;
 
     *emitted = true;
@@ -9079,17 +9077,17 @@ IonBuilder::setPropTryCommonSetter(bool 
     // Try emitting dom call.
     if (!setPropTryCommonDOMSetter(emitted, obj, value, commonSetter, isDOM))
         return false;
 
     if (*emitted)
         return true;
 
     // Don't call the setter with a primitive value.
-    if (objTypes->getKnownTypeTag() != JSVAL_TYPE_OBJECT) {
+    if (objTypes->getKnownMIRType() != MIRType_Object) {
         MGuardObject *guardObj = MGuardObject::New(alloc(), obj);
         current->add(guardObj);
         obj = guardObj;
     }
 
     // Dummy up the stack, as in getprop. We are pushing an extra value, so
     // ensure there is enough space.
     if (!current->ensureHasSlots(3))
@@ -9579,17 +9577,17 @@ IonBuilder::jsop_this()
     }
 
     if (script()->strict() || info().funMaybeLazy()->isSelfHostedBuiltin()) {
         // No need to wrap primitive |this| in strict mode or self-hosted code.
         current->pushSlot(info().thisSlot());
         return true;
     }
 
-    if (thisTypes->getKnownTypeTag() == JSVAL_TYPE_OBJECT ||
+    if (thisTypes->getKnownMIRType() == MIRType_Object ||
         (thisTypes->empty() && baselineFrame_ && baselineFrame_->thisType.isSomeObject()))
     {
         // This is safe, because if the entry type of |this| is an object, it
         // will necessarily be an object throughout the entire function. OSR
         // can introduce a phi, but this phi will be specialized.
         current->pushSlot(info().thisSlot());
         return true;
     }
@@ -10032,17 +10030,17 @@ IonBuilder::lookupTypeDescrSet(MDefiniti
     return typeSetToTypeDescrSet(types, out);
 }
 
 bool
 IonBuilder::typeSetToTypeDescrSet(types::TemporaryTypeSet *types,
                                   TypeDescrSet *out)
 {
     // Extract TypeDescrSet directly if we can
-    if (!types || types->getKnownTypeTag() != JSVAL_TYPE_OBJECT)
+    if (!types || types->getKnownMIRType() != MIRType_Object)
         return true;
 
     // And only known objects.
     if (types->unknownObject())
         return true;
 
     TypeDescrSetBuilder set;
     for (uint32_t i = 0; i < types->getObjectCount(); i++) {
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -349,17 +349,17 @@ class IonBuilder : public MIRGenerator
     // on observed and the JSFunction that we're planning to call. The
     // JSFunction must be a DOM method or getter.
     bool pushDOMTypeBarrier(MInstruction *ins, types::TemporaryTypeSet *observed, JSFunction* func);
 
     // If definiteType is not known or def already has the right type, just
     // returns def.  Otherwise, returns an MInstruction that has that definite
     // type, infallibly unboxing ins as needed.  The new instruction will be
     // added to |current| in this case.
-    MDefinition *ensureDefiniteType(MDefinition* def, JSValueType definiteType);
+    MDefinition *ensureDefiniteType(MDefinition* def, MIRType definiteType);
 
     // Creates a MDefinition based on the given def improved with type as TypeSet.
     MDefinition *ensureDefiniteTypeSet(MDefinition* def, types::TemporaryTypeSet *types);
 
     JSObject *getSingletonPrototype(JSFunction *target);
 
     MDefinition *createThisScripted(MDefinition *callee);
     MDefinition *createThisScriptedSingleton(JSFunction *target, MDefinition *callee);
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -122,22 +122,26 @@ class MacroAssembler : public MacroAssem
           : Branch(cond, reg, jump),
             type_(type)
         { }
 
         void emit(MacroAssembler &masm) {
             JS_ASSERT(isInitialized());
             MIRType mirType = MIRType_None;
 
-            if (type_.isPrimitive())
-                mirType = MIRTypeFromValueType(type_.primitive());
-            else if (type_.isAnyObject())
+            if (type_.isPrimitive()) {
+                if (type_.isMagicArguments())
+                    mirType = MIRType_MagicOptimizedArguments;
+                else
+                    mirType = MIRTypeFromValueType(type_.primitive());
+            } else if (type_.isAnyObject()) {
                 mirType = MIRType_Object;
-            else
+            } else {
                 MOZ_ASSUME_UNREACHABLE("Unknown conversion to mirtype");
+            }
 
             if (mirType == MIRType_Double)
                 masm.branchTestNumber(cond(), reg(), jump());
             else
                 masm.branchTestMIRType(cond(), reg(), mirType, jump());
         }
 
     };
@@ -327,24 +331,26 @@ class MacroAssembler : public MacroAssem
         loadPrivate(handlerAddr, scratch);
         Address familyAddr(scratch, BaseProxyHandler::offsetOfFamily());
         branchPtr(cond, familyAddr, ImmPtr(handlerp), label);
     }
 
     template <typename Value>
     void branchTestMIRType(Condition cond, const Value &val, MIRType type, Label *label) {
         switch (type) {
-          case MIRType_Null:        return branchTestNull(cond, val, label);
-          case MIRType_Undefined:   return branchTestUndefined(cond, val, label);
-          case MIRType_Boolean:     return branchTestBoolean(cond, val, label);
-          case MIRType_Int32:       return branchTestInt32(cond, val, label);
-          case MIRType_String:      return branchTestString(cond, val, label);
-          case MIRType_Object:      return branchTestObject(cond, val, label);
-          case MIRType_Double:      return branchTestDouble(cond, val, label);
-          case MIRType_Magic:       return branchTestMagic(cond, val, label);
+          case MIRType_Null:      return branchTestNull(cond, val, label);
+          case MIRType_Undefined: return branchTestUndefined(cond, val, label);
+          case MIRType_Boolean:   return branchTestBoolean(cond, val, label);
+          case MIRType_Int32:     return branchTestInt32(cond, val, label);
+          case MIRType_String:    return branchTestString(cond, val, label);
+          case MIRType_Object:    return branchTestObject(cond, val, label);
+          case MIRType_Double:    return branchTestDouble(cond, val, label);
+          case MIRType_MagicOptimizedArguments: // Fall through.
+          case MIRType_MagicIsConstructing:
+          case MIRType_MagicHole: return branchTestMagic(cond, val, label);
           default:
             MOZ_ASSUME_UNREACHABLE("Bad MIRType");
         }
     }
 
     // Branches to |label| if |reg| is false. |reg| should be a C++ bool.
     void branchIfFalseBool(Register reg, Label *label) {
         // Note that C++ bool is only 1 byte, so ignore the higher-order bits.
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -65,17 +65,17 @@ BailoutKindString(BailoutKind kind)
         return "Bailout_ShapeGuard";
       case Bailout_BaselineInfo:
         return "Bailout_BaselineInfo";
       default:
         MOZ_ASSUME_UNREACHABLE("Invalid BailoutKind");
     }
 }
 
-static const uint32_t ELEMENT_TYPE_BITS = 4;
+static const uint32_t ELEMENT_TYPE_BITS = 5;
 static const uint32_t ELEMENT_TYPE_SHIFT = 0;
 static const uint32_t ELEMENT_TYPE_MASK = (1 << ELEMENT_TYPE_BITS) - 1;
 static const uint32_t VECTOR_SCALE_BITS = 2;
 static const uint32_t VECTOR_SCALE_SHIFT = ELEMENT_TYPE_BITS + ELEMENT_TYPE_SHIFT;
 static const uint32_t VECTOR_SCALE_MASK = (1 << VECTOR_SCALE_BITS) - 1;
 
 // The ordering of this enumeration is important: Anything < Value is a
 // specialized type. Furthermore, anything < String has trivial conversion to
@@ -85,24 +85,26 @@ enum MIRType
     MIRType_Undefined,
     MIRType_Null,
     MIRType_Boolean,
     MIRType_Int32,
     MIRType_Double,
     MIRType_Float32,
     MIRType_String,
     MIRType_Object,
-    MIRType_Magic,
+    MIRType_MagicOptimizedArguments, // JS_OPTIMIZED_ARGUMENTS magic value.
+    MIRType_MagicHole,               // JS_ELEMENTS_HOLE magic value.
+    MIRType_MagicIsConstructing,     // JS_IS_CONSTRUCTING magic value.
     MIRType_Value,
-    MIRType_None,          // Invalid, used as a placeholder.
-    MIRType_Slots,         // A slots vector
-    MIRType_Elements,      // An elements vector
-    MIRType_Pointer,       // An opaque pointer that receives no special treatment
-    MIRType_Shape,         // A Shape pointer.
-    MIRType_ForkJoinContext, // js::ForkJoinContext*
+    MIRType_None,                    // Invalid, used as a placeholder.
+    MIRType_Slots,                   // A slots vector
+    MIRType_Elements,                // An elements vector
+    MIRType_Pointer,                 // An opaque pointer that receives no special treatment
+    MIRType_Shape,                   // A Shape pointer.
+    MIRType_ForkJoinContext,         // js::ForkJoinContext*
     MIRType_Last = MIRType_ForkJoinContext,
     MIRType_Float32x4 = MIRType_Float32 | (2 << VECTOR_SCALE_SHIFT),
     MIRType_Int32x4   = MIRType_Int32   | (2 << VECTOR_SCALE_SHIFT),
     MIRType_Doublex2  = MIRType_Double  | (1 << VECTOR_SCALE_SHIFT)
 };
 
 static inline MIRType
 ElementType(MIRType type)
@@ -115,33 +117,33 @@ static inline uint32_t
 VectorSize(MIRType type)
 {
     return 1 << ((type >> VECTOR_SCALE_SHIFT) & VECTOR_SCALE_MASK);
 }
 
 static inline MIRType
 MIRTypeFromValueType(JSValueType type)
 {
+    // This function does not deal with magic types. Magic constants should be
+    // filtered out in MIRTypeFromValue.
     switch (type) {
       case JSVAL_TYPE_DOUBLE:
         return MIRType_Double;
       case JSVAL_TYPE_INT32:
         return MIRType_Int32;
       case JSVAL_TYPE_UNDEFINED:
         return MIRType_Undefined;
       case JSVAL_TYPE_STRING:
         return MIRType_String;
       case JSVAL_TYPE_BOOLEAN:
         return MIRType_Boolean;
       case JSVAL_TYPE_NULL:
         return MIRType_Null;
       case JSVAL_TYPE_OBJECT:
         return MIRType_Object;
-      case JSVAL_TYPE_MAGIC:
-        return MIRType_Magic;
       case JSVAL_TYPE_UNKNOWN:
         return MIRType_Value;
       default:
         MOZ_ASSUME_UNREACHABLE("unexpected jsval type");
     }
 }
 
 static inline JSValueType
@@ -156,17 +158,19 @@ ValueTypeFromMIRType(MIRType type)
       return JSVAL_TYPE_BOOLEAN;
     case MIRType_Int32:
       return JSVAL_TYPE_INT32;
     case MIRType_Float32: // Fall through, there's no JSVAL for Float32
     case MIRType_Double:
       return JSVAL_TYPE_DOUBLE;
     case MIRType_String:
       return JSVAL_TYPE_STRING;
-    case MIRType_Magic:
+    case MIRType_MagicOptimizedArguments:
+    case MIRType_MagicHole:
+    case MIRType_MagicIsConstructing:
       return JSVAL_TYPE_MAGIC;
     default:
       JS_ASSERT(type == MIRType_Object);
       return JSVAL_TYPE_OBJECT;
   }
 }
 
 static inline JSValueTag
@@ -190,18 +194,22 @@ StringFromMIRType(MIRType type)
     case MIRType_Double:
       return "Double";
     case MIRType_Float32:
       return "Float32";
     case MIRType_String:
       return "String";
     case MIRType_Object:
       return "Object";
-    case MIRType_Magic:
-      return "Magic";
+    case MIRType_MagicOptimizedArguments:
+      return "MagicOptimizedArguments";
+    case MIRType_MagicHole:
+      return "MagicHole";
+    case MIRType_MagicIsConstructing:
+      return "MagicIsConstructing";
     case MIRType_Value:
       return "Value";
     case MIRType_None:
       return "None";
     case MIRType_Slots:
       return "Slots";
     case MIRType_Elements:
       return "Elements";
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -197,17 +197,17 @@ IonBuilder::getInlineReturnTypeSet()
 {
     return bytecodeTypes(pc);
 }
 
 MIRType
 IonBuilder::getInlineReturnType()
 {
     types::TemporaryTypeSet *returnTypes = getInlineReturnTypeSet();
-    return MIRTypeFromValueType(returnTypes->getKnownTypeTag());
+    return returnTypes->getKnownMIRType();
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineMathFunction(CallInfo &callInfo, MMathFunction::Function function)
 {
     if (callInfo.constructing())
         return InliningStatus_NotInlined;
 
@@ -1532,17 +1532,17 @@ IonBuilder::inlineNewDenseArrayForSequen
 
 IonBuilder::InliningStatus
 IonBuilder::inlineNewDenseArrayForParallelExecution(CallInfo &callInfo)
 {
     // Create the new parallel array object.  Parallel arrays have specially
     // constructed type objects, so we can only perform the inlining if we
     // already have one of these type objects.
     types::TemporaryTypeSet *returnTypes = getInlineReturnTypeSet();
-    if (returnTypes->getKnownTypeTag() != JSVAL_TYPE_OBJECT)
+    if (returnTypes->getKnownMIRType() != MIRType_Object)
         return InliningStatus_NotInlined;
     if (returnTypes->unknownObject() || returnTypes->getObjectCount() != 1)
         return InliningStatus_NotInlined;
     if (callInfo.getArg(0)->type() != MIRType_Int32)
         return InliningStatus_NotInlined;
     types::TypeObject *typeObject = returnTypes->getTypeObject(0);
 
     JSObject *templateObject = inspector->getTemplateObjectForNative(pc, intrinsic_NewDenseArray);
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -543,18 +543,24 @@ MConstant::printOpcode(FILE *fp) const
             break;
         }
         fprintf(fp, "object %p (%s)", (void *)&value().toObject(),
                 value().toObject().getClass()->name);
         break;
       case MIRType_String:
         fprintf(fp, "string %p", (void *)value().toString());
         break;
-      case MIRType_Magic:
-        fprintf(fp, "magic");
+      case MIRType_MagicOptimizedArguments:
+        fprintf(fp, "magic lazyargs");
+        break;
+      case MIRType_MagicHole:
+        fprintf(fp, "magic hole");
+        break;
+      case MIRType_MagicIsConstructing:
+        fprintf(fp, "magic is-constructing");
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("unexpected type");
     }
 }
 
 bool
 MConstant::canProduceFloat32() const
@@ -1117,17 +1123,17 @@ MPhi::typeIncludes(MDefinition *def)
     if (def->type() == MIRType_Int32 && this->type() == MIRType_Double)
         return true;
 
     if (types::TemporaryTypeSet *types = def->resultTypeSet()) {
         if (this->resultTypeSet())
             return types->isSubset(this->resultTypeSet());
         if (this->type() == MIRType_Value || types->empty())
             return true;
-        return this->type() == MIRTypeFromValueType(types->getKnownTypeTag());
+        return this->type() == types->getKnownMIRType();
     }
 
     if (def->type() == MIRType_Value) {
         // This phi must be able to be any value.
         return this->type() == MIRType_Value
             && (!this->resultTypeSet() || this->resultTypeSet()->unknown());
     }
 
@@ -1690,17 +1696,19 @@ MUrsh::fallible() const
     return !range() || !range()->hasInt32Bounds();
 }
 
 static inline bool
 KnownNonStringPrimitive(MDefinition *op)
 {
     return !op->mightBeType(MIRType_Object)
         && !op->mightBeType(MIRType_String)
-        && !op->mightBeType(MIRType_Magic);
+        && !op->mightBeType(MIRType_MagicOptimizedArguments)
+        && !op->mightBeType(MIRType_MagicHole)
+        && !op->mightBeType(MIRType_MagicIsConstructing);
 }
 
 void
 MBinaryArithInstruction::infer(TempAllocator &alloc, BaselineInspector *inspector, jsbytecode *pc)
 {
     JS_ASSERT(this->type() == MIRType_Value);
 
     specialization_ = MIRType_None;
@@ -1814,17 +1822,19 @@ SafelyCoercesToDouble(MDefinition *op)
 
 static bool
 ObjectOrSimplePrimitive(MDefinition *op)
 {
     // Return true if op is either undefined/null/boolean/int32 or an object.
     return !op->mightBeType(MIRType_String)
         && !op->mightBeType(MIRType_Double)
         && !op->mightBeType(MIRType_Float32)
-        && !op->mightBeType(MIRType_Magic);
+        && !op->mightBeType(MIRType_MagicOptimizedArguments)
+        && !op->mightBeType(MIRType_MagicHole)
+        && !op->mightBeType(MIRType_MagicIsConstructing);
 }
 
 static bool
 CanDoValueBitwiseCmp(MDefinition *lhs, MDefinition *rhs, bool looseEq)
 {
     // Only primitive (not double/string) or objects are supported.
     // I.e. Undefined/Null/Boolean/Int32 and Object
     if (!ObjectOrSimplePrimitive(lhs) || !ObjectOrSimplePrimitive(rhs))
@@ -3060,17 +3070,17 @@ jit::DenseNativeElementType(types::Compi
         if (!object)
             continue;
 
         if (object->unknownProperties())
             return MIRType_None;
 
         types::HeapTypeSetKey elementTypes = object->property(JSID_VOID);
 
-        MIRType type = MIRTypeFromValueType(elementTypes.knownTypeTag(constraints));
+        MIRType type = elementTypes.knownMIRType(constraints);
         if (type == MIRType_None)
             return MIRType_None;
 
         if (elementType == MIRType_None)
             elementType = type;
         else if (elementType != type)
             return MIRType_None;
     }
@@ -3337,17 +3347,17 @@ TryAddTypeBarrierForWrite(TempAllocator 
             {
                 return false;
             }
         }
     }
 
     JS_ASSERT(!aggregateProperty.empty());
 
-    MIRType propertyType = MIRTypeFromValueType(aggregateProperty.ref().knownTypeTag(constraints));
+    MIRType propertyType = aggregateProperty.ref().knownMIRType(constraints);
     switch (propertyType) {
       case MIRType_Boolean:
       case MIRType_Int32:
       case MIRType_Double:
       case MIRType_String: {
         // The property is a particular primitive type, guard by unboxing the
         // value before the write.
         if (!(*pvalue)->mightBeType(propertyType)) {
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -37,16 +37,28 @@ class BaselineInspector;
 class ValueNumberData;
 class Range;
 
 static const inline
 MIRType MIRTypeFromValue(const js::Value &vp)
 {
     if (vp.isDouble())
         return MIRType_Double;
+    if (vp.isMagic()) {
+        switch (vp.whyMagic()) {
+          case JS_OPTIMIZED_ARGUMENTS:
+            return MIRType_MagicOptimizedArguments;
+          case JS_ELEMENTS_HOLE:
+            return MIRType_MagicHole;
+          case JS_IS_CONSTRUCTING:
+            return MIRType_MagicIsConstructing;
+          default:
+            MOZ_ASSERT(!"Unexpected magic constant");
+        }
+    }
     return MIRTypeFromValueType(vp.extractNonDoubleType());
 }
 
 #define MIR_FLAG_LIST(_)                                                        \
     _(InWorklist)                                                               \
     _(EmittedAtUses)                                                            \
     _(LoopInvariant)                                                            \
     _(Commutative)                                                              \
@@ -450,25 +462,25 @@ class MDefinition : public MNode
     }
 
     types::TemporaryTypeSet *resultTypeSet() const {
         return resultTypeSet_;
     }
     bool emptyResultTypeSet() const;
 
     bool mightBeType(MIRType type) const {
-        JS_ASSERT(type != MIRType_Value);
+        MOZ_ASSERT(type != MIRType_Value);
 
         if (type == this->type())
             return true;
 
         if (MIRType_Value != this->type())
             return false;
 
-        return !resultTypeSet() || resultTypeSet()->mightBeType(ValueTypeFromMIRType(type));
+        return !resultTypeSet() || resultTypeSet()->mightBeMIRType(type);
     }
 
     // Float32 specialization operations (see big comment in IonAnalysis before the Float32
     // specialization algorithm).
     virtual bool isFloat32Commutative() const { return false; }
     virtual bool canProduceFloat32() const { return false; }
     virtual bool canConsumeFloat32(MUse *use) const { return false; }
     virtual void trySpecializeFloat32(TempAllocator &alloc) {}
@@ -8983,19 +8995,17 @@ class MGuardThreadExclusive
 
 class MFilterTypeSet
   : public MUnaryInstruction
 {
     MFilterTypeSet(MDefinition *def, types::TemporaryTypeSet *types)
       : MUnaryInstruction(def)
     {
         JS_ASSERT(!types->unknown());
-
-        MIRType type = MIRTypeFromValueType(types->getKnownTypeTag());
-        setResultType(type);
+        setResultType(types->getKnownMIRType());
         setResultTypeSet(types);
     }
 
   public:
     INSTRUCTION_HEADER(FilterTypeSet)
 
     static MFilterTypeSet *New(TempAllocator &alloc, MDefinition *def, types::TemporaryTypeSet *types) {
         return new(alloc) MFilterTypeSet(def, types);
@@ -9017,19 +9027,17 @@ class MFilterTypeSet
 class MTypeBarrier
   : public MUnaryInstruction,
     public TypeBarrierPolicy
 {
     MTypeBarrier(MDefinition *def, types::TemporaryTypeSet *types)
       : MUnaryInstruction(def)
     {
         JS_ASSERT(!types->unknown());
-
-        MIRType type = MIRTypeFromValueType(types->getKnownTypeTag());
-        setResultType(type);
+        setResultType(types->getKnownMIRType());
         setResultTypeSet(types);
 
         setGuard();
         setMovable();
     }
 
   public:
     INSTRUCTION_HEADER(TypeBarrier)
@@ -9052,17 +9060,17 @@ class MTypeBarrier
     }
     virtual bool neverHoist() const {
         return resultTypeSet()->empty();
     }
 
     bool alwaysBails() const {
         // If mirtype of input doesn't agree with mirtype of barrier,
         // we will definitely bail.
-        MIRType type = MIRTypeFromValueType(resultTypeSet()->getKnownTypeTag());
+        MIRType type = resultTypeSet()->getKnownMIRType();
         if (type == MIRType_Value)
             return false;
         if (input()->type() == MIRType_Value)
             return false;
         return input()->type() != type;
     }
 };
 
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -288,18 +288,19 @@ TypeBarrierPolicy::adjustInputs(TempAllo
         ins->replaceOperand(0, boxAt(alloc, ins, ins->getOperand(0)));
         return true;
     }
 
     // Input is a value. Unbox the input to the requested type.
     if (inputType == MIRType_Value) {
         JS_ASSERT(outputType != MIRType_Value);
 
-        // We can't unbox a value to null/undefined. So keep output also a value.
-        if (IsNullOrUndefined(outputType) || outputType == MIRType_Magic) {
+        // We can't unbox a value to null/undefined/lazyargs. So keep output
+        // also a value.
+        if (IsNullOrUndefined(outputType) || outputType == MIRType_MagicOptimizedArguments) {
             JS_ASSERT(ins->defUseCount() == 0);
             ins->setResultType(MIRType_Value);
             return true;
         }
 
         MUnbox *unbox = MUnbox::New(alloc, ins->getOperand(0), outputType, MUnbox::TypeBarrier);
         ins->block()->insertBefore(ins, unbox);
         ins->replaceOperand(0, unbox);
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -188,17 +188,17 @@ CodeGeneratorShared::encodeAllocations(L
                 MConstant *constant = mir->toConstant();
                 uint32_t index;
                 if (!graph.addConstantToPool(constant->value(), &index))
                     return false;
                 alloc = RValueAllocation::ConstantPool(index);
             }
             break;
           }
-          case MIRType_Magic:
+          case MIRType_MagicOptimizedArguments:
           {
             uint32_t index;
             if (!graph.addConstantToPool(MagicValue(JS_OPTIMIZED_ARGUMENTS), &index))
                 return false;
             alloc = RValueAllocation::ConstantPool(index);
             break;
           }
           default:
--- a/js/src/jsapi-tests/testSourcePolicy.cpp
+++ b/js/src/jsapi-tests/testSourcePolicy.cpp
@@ -4,17 +4,17 @@
 
 #include "jsscript.h"
 
 #include "jsapi-tests/tests.h"
 
 BEGIN_TEST(testBug795104)
 {
     JS::CompileOptions opts(cx);
-    opts.setSourcePolicy(JS::CompileOptions::NO_SOURCE);
+    JS::CompartmentOptionsRef(cx->compartment()).setDiscardSource(true);
     const size_t strLen = 60002;
     char *s = static_cast<char *>(JS_malloc(cx, strLen));
     CHECK(s);
     s[0] = '"';
     memset(s + 1, 'x', strLen - 2);
     s[strLen - 1] = '"';
     CHECK(JS::Evaluate(cx, global, opts, s, strLen));
     CHECK(JS::CompileFunction(cx, global, opts, "f", 0, nullptr, s, strLen));
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2475,16 +2475,22 @@ JS::CompartmentOptions::setSameZoneAs(JS
 
 JS::CompartmentOptions &
 JS::CompartmentOptionsRef(JSCompartment *compartment)
 {
     return compartment->options();
 }
 
 JS::CompartmentOptions &
+JS::CompartmentOptionsRef(JSObject *obj)
+{
+    return obj->compartment()->options();
+}
+
+JS::CompartmentOptions &
 JS::CompartmentOptionsRef(JSContext *cx)
 {
     return cx->compartment()->options();
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_NewGlobalObject(JSContext *cx, const JSClass *clasp, JSPrincipals *principals,
                    JS::OnNewGlobalHookOption hookOption,
@@ -4343,17 +4349,17 @@ JS::ReadOnlyCompileOptions::copyPODOptio
     selfHostingMode = rhs.selfHostingMode;
     canLazilyParse = rhs.canLazilyParse;
     strictOption = rhs.strictOption;
     extraWarningsOption = rhs.extraWarningsOption;
     werrorOption = rhs.werrorOption;
     asmJSOption = rhs.asmJSOption;
     forceAsync = rhs.forceAsync;
     installedFile = rhs.installedFile;
-    sourcePolicy = rhs.sourcePolicy;
+    sourceIsLazy = rhs.sourceIsLazy;
     introductionType = rhs.introductionType;
     introductionLineno = rhs.introductionLineno;
     introductionOffset = rhs.introductionOffset;
     hasIntroductionInfo = rhs.hasIntroductionInfo;
 }
 
 JSPrincipals *
 JS::ReadOnlyCompileOptions::originPrincipals(ExclusiveContext *cx) const
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2530,16 +2530,17 @@ class JS_PUBLIC_API(CompartmentOptions)
 
         Mode mode_;
     };
 
     explicit CompartmentOptions()
       : version_(JSVERSION_UNKNOWN)
       , invisibleToDebugger_(false)
       , mergeable_(false)
+      , discardSource_(false)
       , traceGlobal_(nullptr)
       , singletonsAsTemplates_(true)
     {
         zone_.spec = JS::FreshZone;
     }
 
     JSVersion version() const { return version_; }
     CompartmentOptions &setVersion(JSVersion aVersion) {
@@ -2563,16 +2564,25 @@ class JS_PUBLIC_API(CompartmentOptions)
     // allowed if this flag is set.  The invisibleToDebugger flag must also be
     // set for such compartments.
     bool mergeable() const { return mergeable_; }
     CompartmentOptions &setMergeable(bool flag) {
         mergeable_ = flag;
         return *this;
     }
 
+    // For certain globals, we know enough about the code that will run in them
+    // that we can discard script source entirely.
+    bool discardSource() const { return discardSource_; }
+    CompartmentOptions &setDiscardSource(bool flag) {
+        discardSource_ = flag;
+        return *this;
+    }
+
+
     bool cloneSingletons(JSContext *cx) const;
     Override &cloneSingletonsOverride() { return cloneSingletonsOverride_; }
 
     void *zonePointer() const {
         JS_ASSERT(uintptr_t(zone_.pointer) > uintptr_t(JS::SystemZone));
         return zone_.pointer;
     }
     ZoneSpecifier zoneSpecifier() const { return zone_.spec; }
@@ -2593,16 +2603,17 @@ class JS_PUBLIC_API(CompartmentOptions)
     JSTraceOp getTrace() const {
         return traceGlobal_;
     }
 
   private:
     JSVersion version_;
     bool invisibleToDebugger_;
     bool mergeable_;
+    bool discardSource_;
     Override cloneSingletonsOverride_;
     union {
         ZoneSpecifier spec;
         void *pointer; // js::Zone* is not exposed in the API.
     } zone_;
     JSTraceOp traceGlobal_;
 
     // To XDR singletons, we need to ensure that all singletons are all used as
@@ -2610,16 +2621,19 @@ class JS_PUBLIC_API(CompartmentOptions)
     // singleton, instead of returning the value which is baked in the JSScript.
     bool singletonsAsTemplates_;
 };
 
 JS_PUBLIC_API(CompartmentOptions &)
 CompartmentOptionsRef(JSCompartment *compartment);
 
 JS_PUBLIC_API(CompartmentOptions &)
+CompartmentOptionsRef(JSObject *obj);
+
+JS_PUBLIC_API(CompartmentOptions &)
 CompartmentOptionsRef(JSContext *cx);
 
 // During global creation, we fire notifications to callbacks registered
 // via the Debugger API. These callbacks are arbitrary script, and can touch
 // the global in arbitrary ways. When that happens, the global should not be
 // in a half-baked state. But this creates a problem for consumers that need
 // to set slots on the global to put it in a consistent state.
 //
@@ -3383,17 +3397,17 @@ class JS_FRIEND_API(ReadOnlyCompileOptio
         selfHostingMode(false),
         canLazilyParse(true),
         strictOption(false),
         extraWarningsOption(false),
         werrorOption(false),
         asmJSOption(false),
         forceAsync(false),
         installedFile(false),
-        sourcePolicy(SAVE_SOURCE),
+        sourceIsLazy(false),
         introductionType(nullptr),
         introductionLineno(0),
         introductionOffset(0),
         hasIntroductionInfo(false)
     { }
 
     // Set all POD options (those not requiring reference counts, copies,
     // rooting, or other hand-holding) to their values in |rhs|.
@@ -3423,21 +3437,17 @@ class JS_FRIEND_API(ReadOnlyCompileOptio
     bool selfHostingMode;
     bool canLazilyParse;
     bool strictOption;
     bool extraWarningsOption;
     bool werrorOption;
     bool asmJSOption;
     bool forceAsync;
     bool installedFile;  // 'true' iff pre-compiling js file in packaged app
-    enum SourcePolicy {
-        NO_SOURCE,
-        LAZY_SOURCE,
-        SAVE_SOURCE
-    } sourcePolicy;
+    bool sourceIsLazy;
 
     // |introductionType| is a statically allocated C string:
     // one of "eval", "Function", or "GeneratorFunction".
     const char *introductionType;
     unsigned introductionLineno;
     uint32_t introductionOffset;
     bool hasIntroductionInfo;
 
@@ -3519,17 +3529,17 @@ class JS_FRIEND_API(OwningCompileOptions
     OwningCompileOptions &setUTF8(bool u) { utf8 = u; return *this; }
     OwningCompileOptions &setColumn(unsigned c) { column = c; return *this; }
     OwningCompileOptions &setCompileAndGo(bool cng) { compileAndGo = cng; return *this; }
     OwningCompileOptions &setForEval(bool eval) { forEval = eval; return *this; }
     OwningCompileOptions &setDefineOnScope(bool define) { defineOnScope = define; return *this; }
     OwningCompileOptions &setNoScriptRval(bool nsr) { noScriptRval = nsr; return *this; }
     OwningCompileOptions &setSelfHostingMode(bool shm) { selfHostingMode = shm; return *this; }
     OwningCompileOptions &setCanLazilyParse(bool clp) { canLazilyParse = clp; return *this; }
-    OwningCompileOptions &setSourcePolicy(SourcePolicy sp) { sourcePolicy = sp; return *this; }
+    OwningCompileOptions &setSourceIsLazy(bool l) { sourceIsLazy = l; return *this; }
     OwningCompileOptions &setIntroductionType(const char *t) { introductionType = t; return *this; }
     bool setIntroductionInfo(JSContext *cx, const char *introducerFn, const char *intro,
                              unsigned line, JSScript *script, uint32_t offset)
     {
         if (!setIntroducerFilename(cx, introducerFn))
             return false;
         introductionType = intro;
         introductionLineno = line;
@@ -3605,17 +3615,17 @@ class MOZ_STACK_CLASS JS_FRIEND_API(Comp
     CompileOptions &setUTF8(bool u) { utf8 = u; return *this; }
     CompileOptions &setColumn(unsigned c) { column = c; return *this; }
     CompileOptions &setCompileAndGo(bool cng) { compileAndGo = cng; return *this; }
     CompileOptions &setForEval(bool eval) { forEval = eval; return *this; }
     CompileOptions &setDefineOnScope(bool define) { defineOnScope = define; return *this; }
     CompileOptions &setNoScriptRval(bool nsr) { noScriptRval = nsr; return *this; }
     CompileOptions &setSelfHostingMode(bool shm) { selfHostingMode = shm; return *this; }
     CompileOptions &setCanLazilyParse(bool clp) { canLazilyParse = clp; return *this; }
-    CompileOptions &setSourcePolicy(SourcePolicy sp) { sourcePolicy = sp; return *this; }
+    CompileOptions &setSourceIsLazy(bool l) { sourceIsLazy = l; return *this; }
     CompileOptions &setIntroductionType(const char *t) { introductionType = t; return *this; }
     CompileOptions &setIntroductionInfo(const char *introducerFn, const char *intro,
                                         unsigned line, JSScript *script, uint32_t offset)
     {
         introducerFilename_ = introducerFn;
         introductionType = intro;
         introductionLineno = line;
         introductionScriptRoot = script;
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -378,38 +378,38 @@ extern JS_FRIEND_API(bool)
 proxy_Unwatch(JSContext *cx, JS::HandleObject obj, JS::HandleId id);
 extern JS_FRIEND_API(bool)
 proxy_Slice(JSContext *cx, JS::HandleObject proxy, uint32_t begin, uint32_t end,
             JS::HandleObject result);
 
 /*
  * A class of objects that return source code on demand.
  *
- * When code is compiled with CompileOptions::LAZY_SOURCE, SpiderMonkey
- * doesn't retain the source code (and doesn't do lazy bytecode
- * generation). If we ever need the source code, say, in response to a call
- * to Function.prototype.toSource or Debugger.Source.prototype.text, then
- * we call the 'load' member function of the instance of this class that
- * has hopefully been registered with the runtime, passing the code's URL,
- * and hope that it will be able to find the source.
+ * When code is compiled with setSourceIsLazy(true), SpiderMonkey doesn't
+ * retain the source code (and doesn't do lazy bytecode generation). If we ever
+ * need the source code, say, in response to a call to Function.prototype.
+ * toSource or Debugger.Source.prototype.text, then we call the 'load' member
+ * function of the instance of this class that has hopefully been registered
+ * with the runtime, passing the code's URL, and hope that it will be able to
+ * find the source.
  */
 class SourceHook {
   public:
     virtual ~SourceHook() { }
 
     /*
      * Set |*src| and |*length| to refer to the source code for |filename|.
      * On success, the caller owns the buffer to which |*src| points, and
      * should use JS_free to free it.
      */
     virtual bool load(JSContext *cx, const char *filename, jschar **src, size_t *length) = 0;
 };
 
 /*
- * Have |rt| use |hook| to retrieve LAZY_SOURCE source code. See the
+ * Have |rt| use |hook| to retrieve lazily-retrieved source code. See the
  * comments for SourceHook. The runtime takes ownership of the hook, and
  * will delete it when the runtime itself is deleted, or when a new hook is
  * set.
  */
 extern JS_FRIEND_API(void)
 SetSourceHook(JSRuntime *rt, SourceHook *hook);
 
 /* Remove |rt|'s source hook, and return it. The caller now owns the hook. */
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -306,25 +306,55 @@ TemporaryTypeSet::TemporaryTypeSet(Type 
         flags |= TYPE_FLAG_ANYOBJECT;
     } else {
         setBaseObjectCount(1);
         objectSet = reinterpret_cast<TypeObjectKey**>(type.objectKey());
     }
 }
 
 bool
-TypeSet::mightBeType(JSValueType type)
+TypeSet::mightBeMIRType(jit::MIRType type)
 {
     if (unknown())
         return true;
 
-    if (type == JSVAL_TYPE_OBJECT)
+    if (type == jit::MIRType_Object)
         return unknownObject() || baseObjectCount() != 0;
 
-    return baseFlags() & PrimitiveTypeFlag(type);
+    switch (type) {
+      case jit::MIRType_Undefined:
+        return baseFlags() & TYPE_FLAG_UNDEFINED;
+      case jit::MIRType_Null:
+        return baseFlags() & TYPE_FLAG_NULL;
+      case jit::MIRType_Boolean:
+        return baseFlags() & TYPE_FLAG_BOOLEAN;
+      case jit::MIRType_Int32:
+        return baseFlags() & TYPE_FLAG_INT32;
+      case jit::MIRType_Float32: // Fall through, there's no JSVAL for Float32.
+      case jit::MIRType_Double:
+        return baseFlags() & TYPE_FLAG_DOUBLE;
+      case jit::MIRType_String:
+        return baseFlags() & TYPE_FLAG_STRING;
+      case jit::MIRType_MagicOptimizedArguments:
+        return baseFlags() & TYPE_FLAG_LAZYARGS;
+      case jit::MIRType_MagicHole:
+      case jit::MIRType_MagicIsConstructing:
+        // These magic constants do not escape to script and are not observed
+        // in the type sets.
+        //
+        // The reason we can return false here is subtle: if Ion is asking the
+        // type set if it has seen such a magic constant, then the MIR in
+        // question is the most generic type, MIRType_Value. A magic constant
+        // could only be emitted by a MIR of MIRType_Value if that MIR is a
+        // phi, and we check that different magic constants do not flow to the
+        // same join point in GuessPhiType.
+        return false;
+      default:
+        MOZ_ASSUME_UNREACHABLE("Bad MIR type");
+    }
 }
 
 bool
 TypeSet::isSubset(TypeSet *other)
 {
     if ((baseFlags() & other->baseFlags()) != baseFlags())
         return false;
 
@@ -1137,92 +1167,92 @@ void
 HeapTypeSetKey::freeze(CompilerConstraintList *constraints)
 {
     LifoAlloc *alloc = constraints->alloc();
 
     typedef CompilerConstraintInstance<ConstraintDataFreeze> T;
     constraints->add(alloc->new_<T>(alloc, *this, ConstraintDataFreeze()));
 }
 
-static inline JSValueType
-GetValueTypeFromTypeFlags(TypeFlags flags)
+static inline jit::MIRType
+GetMIRTypeFromTypeFlags(TypeFlags flags)
 {
     switch (flags) {
       case TYPE_FLAG_UNDEFINED:
-        return JSVAL_TYPE_UNDEFINED;
+        return jit::MIRType_Undefined;
       case TYPE_FLAG_NULL:
-        return JSVAL_TYPE_NULL;
+        return jit::MIRType_Null;
       case TYPE_FLAG_BOOLEAN:
-        return JSVAL_TYPE_BOOLEAN;
+        return jit::MIRType_Boolean;
       case TYPE_FLAG_INT32:
-        return JSVAL_TYPE_INT32;
+        return jit::MIRType_Int32;
       case (TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE):
-        return JSVAL_TYPE_DOUBLE;
+        return jit::MIRType_Double;
       case TYPE_FLAG_STRING:
-        return JSVAL_TYPE_STRING;
+        return jit::MIRType_String;
       case TYPE_FLAG_LAZYARGS:
-        return JSVAL_TYPE_MAGIC;
+        return jit::MIRType_MagicOptimizedArguments;
       case TYPE_FLAG_ANYOBJECT:
-        return JSVAL_TYPE_OBJECT;
+        return jit::MIRType_Object;
       default:
-        return JSVAL_TYPE_UNKNOWN;
+        return jit::MIRType_Value;
     }
 }
 
-JSValueType
-TemporaryTypeSet::getKnownTypeTag()
+jit::MIRType
+TemporaryTypeSet::getKnownMIRType()
 {
     TypeFlags flags = baseFlags();
-    JSValueType type;
+    jit::MIRType type;
 
     if (baseObjectCount())
-        type = flags ? JSVAL_TYPE_UNKNOWN : JSVAL_TYPE_OBJECT;
+        type = flags ? jit::MIRType_Value : jit::MIRType_Object;
     else
-        type = GetValueTypeFromTypeFlags(flags);
+        type = GetMIRTypeFromTypeFlags(flags);
 
     /*
      * If the type set is totally empty then it will be treated as unknown,
      * but we still need to record the dependency as adding a new type can give
      * it a definite type tag. This is not needed if there are enough types
      * that the exact tag is unknown, as it will stay unknown as more types are
      * added to the set.
      */
     DebugOnly<bool> empty = flags == 0 && baseObjectCount() == 0;
-    JS_ASSERT_IF(empty, type == JSVAL_TYPE_UNKNOWN);
+    JS_ASSERT_IF(empty, type == jit::MIRType_Value);
 
     return type;
 }
 
-JSValueType
-HeapTypeSetKey::knownTypeTag(CompilerConstraintList *constraints)
+jit::MIRType
+HeapTypeSetKey::knownMIRType(CompilerConstraintList *constraints)
 {
     TypeSet *types = maybeTypes();
 
     if (!types || types->unknown())
-        return JSVAL_TYPE_UNKNOWN;
+        return jit::MIRType_Value;
 
     TypeFlags flags = types->baseFlags() & ~TYPE_FLAG_ANYOBJECT;
-    JSValueType type;
+    jit::MIRType type;
 
     if (types->unknownObject() || types->getObjectCount())
-        type = flags ? JSVAL_TYPE_UNKNOWN : JSVAL_TYPE_OBJECT;
+        type = flags ? jit::MIRType_Value : jit::MIRType_Object;
     else
-        type = GetValueTypeFromTypeFlags(flags);
-
-    if (type != JSVAL_TYPE_UNKNOWN)
+        type = GetMIRTypeFromTypeFlags(flags);
+
+    if (type != jit::MIRType_Value)
         freeze(constraints);
 
     /*
      * If the type set is totally empty then it will be treated as unknown,
      * but we still need to record the dependency as adding a new type can give
      * it a definite type tag. This is not needed if there are enough types
      * that the exact tag is unknown, as it will stay unknown as more types are
      * added to the set.
      */
-    JS_ASSERT_IF(types->empty(), type == JSVAL_TYPE_UNKNOWN);
+    JS_ASSERT_IF(types->empty(), type == jit::MIRType_Value);
 
     return type;
 }
 
 bool
 HeapTypeSetKey::isOwnProperty(CompilerConstraintList *constraints)
 {
     if (maybeTypes() && (!maybeTypes()->empty() || maybeTypes()->nonDataProperty()))
@@ -1666,17 +1696,17 @@ TemporaryTypeSet::convertDoubleElements(
             dontConvert = true;
             alwaysConvert = false;
             continue;
         }
 
         // Only bother with converting known packed arrays whose possible
         // element types are int or double. Other arrays require type tests
         // when elements are accessed regardless of the conversion.
-        if (property.knownTypeTag(constraints) == JSVAL_TYPE_DOUBLE &&
+        if (property.knownMIRType(constraints) == jit::MIRType_Double &&
             !type->hasFlags(constraints, OBJECT_FLAG_NON_PACKED))
         {
             maybeConvert = true;
         } else {
             alwaysConvert = false;
         }
     }
 
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -15,16 +15,17 @@
 #include "jsalloc.h"
 #include "jsfriendapi.h"
 #include "jstypes.h"
 
 #include "ds/IdValuePair.h"
 #include "ds/LifoAlloc.h"
 #include "gc/Barrier.h"
 #include "gc/Marking.h"
+#include "jit/IonTypes.h"
 #include "js/Utility.h"
 #include "js/Vector.h"
 
 namespace js {
 
 class TypeDescr;
 
 class TaggedProto
@@ -218,16 +219,20 @@ class Type
         return (uintptr_t) type == data;
     }
 
     JSValueType primitive() const {
         JS_ASSERT(isPrimitive());
         return (JSValueType) data;
     }
 
+    bool isMagicArguments() const {
+        return primitive() == JSVAL_TYPE_MAGIC;
+    }
+
     bool isSomeObject() const {
         return data == JSVAL_TYPE_OBJECT || data > JSVAL_TYPE_UNKNOWN;
     }
 
     bool isAnyObject() const {
         return data == JSVAL_TYPE_OBJECT;
     }
 
@@ -557,17 +562,17 @@ class TypeSet
     }
     void setDefinite(unsigned slot) {
         JS_ASSERT(canSetDefinite(slot));
         flags |= ((slot + 1) << TYPE_FLAG_DEFINITE_SHIFT);
         JS_ASSERT(definiteSlot() == slot);
     }
 
     /* Whether any values in this set might have the specified type. */
-    bool mightBeType(JSValueType type);
+    bool mightBeMIRType(jit::MIRType type);
 
     /*
      * Get whether this type set is known to be a subset of other.
      * This variant doesn't freeze constraints. That variant is called knownSubset
      */
     bool isSubset(TypeSet *other);
 
     /* Forward all types in this set to the specified constraint. */
@@ -649,19 +654,19 @@ class TemporaryTypeSet : public TypeSet
      *
      * Methods for JIT compilation. These must be used when a script is
      * currently being compiled (see AutoEnterCompilation) and will add
      * constraints ensuring that if the return value change in the future due
      * to new type information, the script's jitcode will be discarded.
      */
 
     /* Get any type tag which all values in this set must have. */
-    JSValueType getKnownTypeTag();
+    jit::MIRType getKnownMIRType();
 
-    bool isMagicArguments() { return getKnownTypeTag() == JSVAL_TYPE_MAGIC; }
+    bool isMagicArguments() { return getKnownMIRType() == jit::MIRType_MagicOptimizedArguments; }
 
     /* Whether this value may be an object. */
     bool maybeObject() { return unknownObject() || baseObjectCount() > 0; }
 
     /*
      * Whether this typeset represents a potentially sentineled object value:
      * the value may be an object or null or undefined.
      * Returns false if the value cannot ever be an object.
@@ -1398,17 +1403,17 @@ class HeapTypeSetKey
 
     TypeObjectKey *object() const { return object_; }
     jsid id() const { return id_; }
     HeapTypeSet *maybeTypes() const { return maybeTypes_; }
 
     bool instantiate(JSContext *cx);
 
     void freeze(CompilerConstraintList *constraints);
-    JSValueType knownTypeTag(CompilerConstraintList *constraints);
+    jit::MIRType knownMIRType(CompilerConstraintList *constraints);
     bool nonData(CompilerConstraintList *constraints);
     bool nonWritable(CompilerConstraintList *constraints);
     bool isOwnProperty(CompilerConstraintList *constraints);
     bool knownSubset(CompilerConstraintList *constraints, const HeapTypeSetKey &other);
     JSObject *singleton(CompilerConstraintList *constraints);
     bool needsBarrier(CompilerConstraintList *constraints);
 };
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -861,38 +861,20 @@ ParseCompileOptions(JSContext *cx, Compi
         return false;
     if (!v.isUndefined()) {
         uint32_t u;
         if (!ToUint32(cx, v, &u))
             return false;
         options.setLine(u);
     }
 
-    if (!JS_GetProperty(cx, opts, "sourcePolicy", &v))
-        return false;
-    if (!v.isUndefined()) {
-        RootedString s(cx, ToString(cx, v));
-        if (!s)
-            return false;
-
-        JSAutoByteString bytes;
-        char *policy = bytes.encodeUtf8(cx, s);
-        if (!policy)
-            return false;
-        if (strcmp(policy, "NO_SOURCE") == 0) {
-            options.setSourcePolicy(CompileOptions::NO_SOURCE);
-        } else if (strcmp(policy, "LAZY_SOURCE") == 0) {
-            options.setSourcePolicy(CompileOptions::LAZY_SOURCE);
-        } else if (strcmp(policy, "SAVE_SOURCE") == 0) {
-            options.setSourcePolicy(CompileOptions::SAVE_SOURCE);
-        } else {
-            JS_ReportError(cx, "bad 'sourcePolicy' option: '%s'", policy);
-            return false;
-        }
-    }
+    if (!JS_GetProperty(cx, opts, "sourceIsLazy", &v))
+        return false;
+    if (v.isBoolean())
+        options.setSourceIsLazy(v.toBoolean());
 
     return true;
 }
 
 class AutoNewContext
 {
   private:
     JSContext *oldcx;
@@ -3631,17 +3613,17 @@ OffThreadCompileScript(JSContext *cx, un
 
         RootedObject opts(cx, &args[1].toObject());
         if (!ParseCompileOptions(cx, options, opts, fileNameBytes))
             return false;
     }
 
     // These option settings must override whatever the caller requested.
     options.setCompileAndGo(true)
-           .setSourcePolicy(CompileOptions::SAVE_SOURCE);
+           .setSourceIsLazy(false);
 
     // We assume the caller wants caching if at all possible, ignoring
     // heuristics that make sense for a real browser.
     options.forceAsync = true;
 
     JSString *scriptContents = args[0].toString();
     const jschar *chars = JS_GetStringCharsZ(cx, scriptContents);
     if (!chars)
@@ -4384,19 +4366,19 @@ static const JSFunctionSpecWithHelp shel
 "         any DOM element.\n"
 "      elementAttributeName: if present and not undefined, the name of\n"
 "         property of 'element' that holds this code. This is what\n"
 "         Debugger.Source.prototype.elementAttributeName returns.\n"
 "      sourceMapURL: if present with value |v|, convert |v| to a string, and\n"
 "         provide that as the code's source map URL. If omitted, attach no\n"
 "         source map URL to the code (although the code may provide one itself,\n"
 "         via a //#sourceMappingURL comment).\n"
-"      sourcePolicy: if present, the value converted to a string must be either\n"
-"         'NO_SOURCE', 'LAZY_SOURCE', or 'SAVE_SOURCE'; use the given source\n"
-"         retention policy for this compilation.\n"
+"      sourceIsLazy: if present and true, indicates that, after compilation, \n"
+          "script source should not be cached by the JS engine and should be \n"
+          "lazily loaded from the embedding as-needed.\n"
 "      loadBytecode: if true, and if the source is a CacheEntryObject,\n"
 "         the bytecode would be loaded and decoded from the cache entry instead\n"
 "         of being parsed, then it would be executed as usual.\n"
 "      saveBytecode: if true, and if the source is a CacheEntryObject,\n"
 "         the bytecode would be encoded and saved into the cache entry after\n"
 "         the script execution.\n"
 "