Merge fx-team to m-c
authorWes Kocher <wkocher@mozilla.com>
Thu, 06 Mar 2014 17:49:01 -0800
changeset 172300 b0e7f63c213871cb8b2042072d1a770b2def09a6
parent 172276 b627f45f8d0bb2deda48c51893027912cfd62f35 (current diff)
parent 172299 a3115bdac8fdb57e0e7329702a47ee69c21560d0 (diff)
child 172405 22fe15b10fd7d7de225bb0c75592569da31dbd21
child 172428 63f6e23b1c8bcd15e5cc0f5435eff06e6a986d4e
child 172471 51cc6f0d01a0c8eb7fcf9cd3b8f8812f4c5b668d
push id26357
push userkwierso@gmail.com
push dateFri, 07 Mar 2014 01:49:11 +0000
treeherdermozilla-central@b0e7f63c2138 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone30.0a1
first release with
nightly linux32
b0e7f63c2138 / 30.0a1 / 20140307030202 / files
nightly linux64
b0e7f63c2138 / 30.0a1 / 20140307030202 / files
nightly mac
b0e7f63c2138 / 30.0a1 / 20140307030202 / files
nightly win32
b0e7f63c2138 / 30.0a1 / 20140307030202 / files
nightly win64
b0e7f63c2138 / 30.0a1 / 20140307030202 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge fx-team to m-c
browser/extensions/pdfjs/components/PdfRedirector.js
browser/extensions/pdfjs/components/PdfStreamConverter.js
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1090,16 +1090,19 @@ pref("services.sync.prefs.sync.security.
 pref("services.sync.prefs.sync.security.default_personal_cert", true);
 pref("services.sync.prefs.sync.security.tls.version.min", true);
 pref("services.sync.prefs.sync.security.tls.version.max", true);
 pref("services.sync.prefs.sync.signon.rememberSignons", true);
 pref("services.sync.prefs.sync.spellchecker.dictionary", true);
 pref("services.sync.prefs.sync.xpinstall.whitelist.required", true);
 #endif
 
+// Used for devtools debugging
+pref("devtools.dump.emit", false);
+
 // Disable the error console
 pref("devtools.errorconsole.enabled", false);
 
 // Developer toolbar and GCLI preferences
 pref("devtools.toolbar.enabled", true);
 pref("devtools.toolbar.visible", false);
 pref("devtools.commands.dir", "");
 
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -920,17 +920,16 @@ let PlacesToolbarHelper = {
     }
     let shouldWrapNow = this._getShouldWrap();
     if (this._shouldWrap != shouldWrapNow) {
       if (shouldWrapNow) {
         placeholder.setAttribute("wrap", "true");
       } else {
         placeholder.removeAttribute("wrap");
       }
-      placeholder.classList.toggle("toolbarbutton-1", shouldWrapNow);
       this._shouldWrap = shouldWrapNow;
     }
   },
 
   customizeDone: function PTH_customizeDone() {
     this._isCustomizing = false;
     this.init(true);
   },
@@ -965,34 +964,35 @@ let PlacesToolbarHelper = {
 ////////////////////////////////////////////////////////////////////////////////
 //// BookmarkingUI
 
 /**
  * Handles the bookmarks menu-button in the toolbar.
  */
 
 let BookmarkingUI = {
+  BOOKMARK_BUTTON_ID: "bookmarks-menu-button",
   get button() {
     delete this.button;
-    let widgetGroup = CustomizableUI.getWidget("bookmarks-menu-button");
+    let widgetGroup = CustomizableUI.getWidget(this.BOOKMARK_BUTTON_ID);
     return this.button = widgetGroup.forWindow(window).node;
   },
 
   /* Can't make this a self-deleting getter because it's anonymous content
    * and might lose/regain bindings at some point. */
   get star() {
     return document.getAnonymousElementByAttribute(this.button, "anonid",
                                                    "button");
   },
 
   get anchor() {
     if (!this._shouldUpdateStarState()) {
       return null;
     }
-    let widget = CustomizableUI.getWidget("bookmarks-menu-button")
+    let widget = CustomizableUI.getWidget(this.BOOKMARK_BUTTON_ID)
                                .forWindow(window);
     if (widget.overflowed)
       return widget.anchor;
 
     let star = this.star;
     return star ? document.getAnonymousElementByAttribute(star, "class",
                                                           "toolbarbutton-icon")
                 : null;
@@ -1066,17 +1066,17 @@ let BookmarkingUI = {
     // decides to open the popup even though the dropmarker is invisible.
     if (this._currentAreaType == CustomizableUI.TYPE_MENU_PANEL) {
       this._showSubview();
       event.preventDefault();
       event.stopPropagation();
       return;
     }
 
-    let widget = CustomizableUI.getWidget("bookmarks-menu-button")
+    let widget = CustomizableUI.getWidget(this.BOOKMARK_BUTTON_ID)
                                .forWindow(window);
     if (widget.overflowed) {
       // Don't open a popup in the overflow popup, rather just open the Library.
       event.preventDefault();
       widget.node.removeAttribute("closemenu");
       PlacesCommandHook.showPlacesOrganizer("BookmarksMenu");
       return;
     }
@@ -1128,36 +1128,32 @@ let BookmarkingUI = {
     else {
       this.star.removeAttribute("disabled");
       this._updateStar();
     }
     this._updateToolbarStyle();
   },
 
   _updateCustomizationState: function BUI__updateCustomizationState() {
-    let placement = CustomizableUI.getPlacementOfWidget("bookmarks-menu-button");
+    let placement = CustomizableUI.getPlacementOfWidget(this.BOOKMARK_BUTTON_ID);
     this._currentAreaType = placement && CustomizableUI.getAreaType(placement.area);
   },
 
   _updateToolbarStyle: function BUI__updateToolbarStyle() {
     let onPersonalToolbar = false;
     if (this._currentAreaType == CustomizableUI.TYPE_TOOLBAR) {
       let personalToolbar = document.getElementById("PersonalToolbar");
       onPersonalToolbar = this.button.parentNode == personalToolbar ||
                           this.button.parentNode.parentNode == personalToolbar;
     }
 
-    if (onPersonalToolbar) {
+    if (onPersonalToolbar)
       this.button.classList.add("bookmark-item");
-      this.button.classList.remove("toolbarbutton-1");
-    }
-    else {
+    else
       this.button.classList.remove("bookmark-item");
-      this.button.classList.add("toolbarbutton-1");
-    }
   },
 
   _uninitView: function BUI__uninitView() {
     // When an element with a placesView attached is removed and re-inserted,
     // XBL reapplies the binding causing any kind of issues and possible leaks,
     // so kill current view and let popupshowing generate a new one.
     if (this.button._placesView)
       this.button._placesView.uninit();
@@ -1166,48 +1162,55 @@ let BookmarkingUI = {
   onCustomizeStart: function BUI_customizeStart(aWindow) {
     if (aWindow == window) {
       this._uninitView();
       this._isCustomizing = true;
     }
   },
 
   onWidgetAdded: function BUI_widgetAdded(aWidgetId) {
-    if (aWidgetId != "bookmarks-menu-button") {
-      return;
+    if (aWidgetId == this.BOOKMARK_BUTTON_ID) {
+      this._onWidgetWasMoved();
+    }
+  },
+
+  onWidgetRemoved: function BUI_widgetRemoved(aWidgetId) {
+    if (aWidgetId == this.BOOKMARK_BUTTON_ID) {
+      this._onWidgetWasMoved();
     }
+  },
 
+  onWidgetReset: function BUI_widgetReset(aNode, aContainer) {
+    if (aNode == this.button) {
+      this._onWidgetWasMoved();
+    }
+  },
+
+  onWidgetUndoMove: function BUI_undoWidgetUndoMove(aNode, aContainer) {
+    if (aNode == this.button) {
+      this._onWidgetWasMoved();
+    }
+  },
+
+  _onWidgetWasMoved: function BUI_widgetWasMoved() {
     let usedToUpdateStarState = this._shouldUpdateStarState();
     this._updateCustomizationState();
     if (!usedToUpdateStarState && this._shouldUpdateStarState()) {
       this.updateStarState();
     } else if (usedToUpdateStarState && !this._shouldUpdateStarState()) {
       this._updateStar();
     }
     // If we're moved outside of customize mode, we need to uninit
     // our view so it gets reconstructed.
     if (!this._isCustomizing) {
       this._uninitView();
     }
     this._updateToolbarStyle();
   },
 
-  onWidgetRemoved: function BUI_widgetRemoved(aWidgetId) {
-    if (aWidgetId != "bookmarks-menu-button") {
-      return;
-    }
-    // If we're moved outside of customize mode, we need to uninit
-    // our view so it gets reconstructed.
-    if (!this._isCustomizing) {
-      this._uninitView();
-    }
-    this._updateCustomizationState();
-    this._updateToolbarStyle();
-  },
-
   onCustomizeEnd: function BUI_customizeEnd(aWindow) {
     if (aWindow == window) {
       this._isCustomizing = false;
       this.onToolbarVisibilityChange();
       this._updateToolbarStyle();
     }
   },
 
@@ -1360,17 +1363,17 @@ let BookmarkingUI = {
       this.notifier.style.transform = '';
     }, 1000);
   },
 
   _showSubview: function() {
     let view = document.getElementById("PanelUI-bookmarks");
     view.addEventListener("ViewShowing", this);
     view.addEventListener("ViewHiding", this);
-    let anchor = document.getElementById("bookmarks-menu-button");
+    let anchor = document.getElementById(this.BOOKMARK_BUTTON_ID);
     anchor.setAttribute("closemenu", "none");
     PanelUI.showSubView("PanelUI-bookmarks", anchor,
                         CustomizableUI.AREA_PANEL);
   },
 
   onCommand: function BUI_onCommand(aEvent) {
     if (aEvent.target != aEvent.currentTarget) {
       return;
@@ -1378,17 +1381,17 @@ let BookmarkingUI = {
 
     // Handle special case when the button is in the panel.
     let isBookmarked = this._itemIds.length > 0;
 
     if (this._currentAreaType == CustomizableUI.TYPE_MENU_PANEL) {
       this._showSubview();
       return;
     }
-    let widget = CustomizableUI.getWidget("bookmarks-menu-button")
+    let widget = CustomizableUI.getWidget(this.BOOKMARK_BUTTON_ID)
                                .forWindow(window);
     if (widget.overflowed) {
       // Allow to close the panel if the page is already bookmarked, cause
       // we are going to open the edit bookmark panel.
       if (isBookmarked)
         widget.node.removeAttribute("closemenu");
       else
         widget.node.setAttribute("closemenu", "none");
@@ -1516,33 +1519,33 @@ let BookmarkingUI = {
   },
   get _starButtonOverflowedStarredLabel() {
     delete this._starButtonOverflowedStarredLabel;
     return this._starButtonOverflowedStarredLabel =
       gNavigatorBundle.getString("starButtonOverflowedStarred.label");
   },
   onWidgetOverflow: function(aNode, aContainer) {
     let win = aNode.ownerDocument.defaultView;
-    if (aNode.id != "bookmarks-menu-button" || win != window)
+    if (aNode.id != this.BOOKMARK_BUTTON_ID || win != window)
       return;
 
     let currentLabel = aNode.getAttribute("label");
     if (!this._starButtonLabel)
       this._starButtonLabel = currentLabel;
 
     if (currentLabel == this._starButtonLabel) {
       let desiredLabel = this._itemIds.length > 0 ? this._starButtonOverflowedStarredLabel
                                                  : this._starButtonOverflowedLabel;
       aNode.setAttribute("label", desiredLabel);
     }
   },
 
   onWidgetUnderflow: function(aNode, aContainer) {
     let win = aNode.ownerDocument.defaultView;
-    if (aNode.id != "bookmarks-menu-button" || win != window)
+    if (aNode.id != this.BOOKMARK_BUTTON_ID || win != window)
       return;
 
     // The view gets broken by being removed and reinserted. Uninit
     // here so popupshowing will generate a new one:
     this._uninitView();
 
     if (aNode.getAttribute("label") != this._starButtonLabel)
       aNode.setAttribute("label", this._starButtonLabel);
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -6371,23 +6371,19 @@ var gIdentityHandler = {
     // anything useful here.
     let unknown = false;
     try {
       uri.host;
     } catch (e) { unknown = true; }
 
     // Chrome URIs however get special treatment. Some chrome URIs are
     // whitelisted to provide a positive security signal to the user.
-    let chromeWhitelist = ["about:addons", "about:app-manager", "about:config",
-                           "about:crashes", "about:customizing", "about:healthreport",
-                           "about:home", "about:newaddon", "about:permissions",
-                           "about:preferences", "about:privatebrowsing",
-                           "about:sessionstore", "about:support", "about:welcomeback"];
-    let lowercaseSpec = uri.spec.toLowerCase();
-    if (chromeWhitelist.some(function(whitelistedSpec) lowercaseSpec.startsWith(whitelistedSpec))) {
+    let whitelist = /^about:(accounts|addons|app-manager|config|crashes|customizing|healthreport|home|newaddon|permissions|preferences|privatebrowsing|sessionrestore|support|welcomeback)/i;
+    let isChromeUI = uri.schemeIs("about") && whitelist.test(uri.spec);
+    if (isChromeUI) {
       this.setMode(this.IDENTITY_MODE_CHROMEUI);
     } else if (unknown) {
       this.setMode(this.IDENTITY_MODE_UNKNOWN);
     } else if (state & nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL) {
       this.setMode(this.IDENTITY_MODE_IDENTIFIED);
     } else if (state & nsIWebProgressListener.STATE_IS_SECURE) {
       this.setMode(this.IDENTITY_MODE_DOMAIN_VERIFIED);
     } else if (state & nsIWebProgressListener.STATE_IS_BROKEN) {
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -932,16 +932,17 @@
              collapsed="true"
              customizable="true">
       <toolbaritem id="personal-bookmarks"
                    flex="1"
                    title="&bookmarksToolbarItem.label;"
                    cui-areatype="toolbar"
                    removable="true">
         <toolbarbutton id="bookmarks-toolbar-placeholder"
+                       class="toolbarbutton-1"
                        mousethrough="never"
                        label="&bookmarksToolbarItem.label;"
                        oncommand="PlacesToolbarHelper.onPlaceholderCommand();"/>
         <hbox flex="1"
               id="PlacesToolbar"
               context="placesContext"
               onclick="BookmarksEventHandler.onClick(event, this._placesView);"
               oncommand="BookmarksEventHandler.onCommand(event, this._placesView);"
--- a/browser/components/customizableui/content/panelUI.js
+++ b/browser/components/customizableui/content/panelUI.js
@@ -42,16 +42,18 @@ const PanelUI = {
       this.__defineGetter__(getKey, function() {
         delete this[getKey];
         return this[getKey] = document.getElementById(id);
       });
     }
 
     this.menuButton.addEventListener("mousedown", this);
     this.menuButton.addEventListener("keypress", this);
+    this._overlayScrollListenerBoundFn = this._overlayScrollListener.bind(this);
+    window.matchMedia("(-moz-overlay-scrollbars)").addListener(this._overlayScrollListenerBoundFn);
     this._initialized = true;
   },
 
   _eventListenersAdded: false,
   _ensureEventListenersAdded: function() {
     if (this._eventListenersAdded)
       return;
     this._addEventListeners();
@@ -72,16 +74,18 @@ const PanelUI = {
     }
 
     for (let event of this.kEvents) {
       this.panel.removeEventListener(event, this);
     }
     this.helpView.removeEventListener("ViewShowing", this._onHelpViewShow);
     this.menuButton.removeEventListener("mousedown", this);
     this.menuButton.removeEventListener("keypress", this);
+    window.matchMedia("(-moz-overlay-scrollbars)").removeListener(this._overlayScrollListenerBoundFn);
+    this._overlayScrollListenerBoundFn = null;
   },
 
   /**
    * Customize mode extracts the mainView and puts it somewhere else while the
    * user customizes. Upon completion, this function can be called to put the
    * panel back to where it belongs in normal browsing mode.
    *
    * @param aMainView
@@ -438,16 +442,22 @@ const PanelUI = {
 
     let key = document.getElementById("key_quitApplication");
     stringArgs.push(ShortcutUtils.prettifyShortcut(key));
     let tooltipString = CustomizableUI.getLocalizedProperty({x: tooltipId}, "x", stringArgs);
     let quitButton = document.getElementById("PanelUI-quit");
     quitButton.setAttribute("tooltiptext", tooltipString);
 #endif
   },
+
+  _overlayScrollListenerBoundFn: null,
+  _overlayScrollListener: function(aMQL) {
+    ScrollbarSampler.resetSystemScrollbarWidth();
+    this._scrollWidth = null;
+  },
 };
 
 /**
  * Gets the currently selected locale for display.
  * @return  the selected locale or "en-US" if none is selected
  */
 function getLocale() {
   try {
--- a/browser/components/customizableui/src/CustomizableUI.jsm
+++ b/browser/components/customizableui/src/CustomizableUI.jsm
@@ -107,16 +107,17 @@ let gDirtyAreaCache = new Set();
  */
 let gPendingBuildAreas = new Map();
 
 let gSavedState = null;
 let gRestoring = false;
 let gDirty = false;
 let gInBatchStack = 0;
 let gResetting = false;
+let gUndoResetting = false;
 
 /**
  * gBuildAreas maps area IDs to actual area nodes within browser windows.
  */
 let gBuildAreas = new Map();
 
 /**
  * gBuildWindows is a map of windows that have registered build areas, mapped
@@ -517,16 +518,18 @@ let CustomizableUIInternal = {
           } else {
             node.removeAttribute("wrap");
           }
         }
 
         this.insertWidgetBefore(node, currentNode, container, aArea);
         if (gResetting) {
           this.notifyListeners("onWidgetReset", node, container);
+        } else if (gUndoResetting) {
+          this.notifyListeners("onWidgetUndoMove", node, container);
         }
       }
 
       if (currentNode) {
         let palette = aAreaNode.toolbox ? aAreaNode.toolbox.palette : null;
         let limit = currentNode.previousSibling;
         let node = container.lastChild;
         while (node && node != limit) {
@@ -2167,16 +2170,18 @@ let CustomizableUIInternal = {
   /**
    * Undoes a previous reset, restoring the state of the UI to the state prior to the reset.
    */
   undoReset: function() {
     if (gUIStateBeforeReset.uiCustomizationState == null ||
         gUIStateBeforeReset.drawInTitlebar == null) {
       return;
     }
+    gUndoResetting = true;
+
     let uiCustomizationState = gUIStateBeforeReset.uiCustomizationState;
     let drawInTitlebar = gUIStateBeforeReset.drawInTitlebar;
 
     // Need to clear the previous state before setting the prefs
     // because pref observers may check if there is a previous UI state.
     this._clearPreviousUIState();
 
     Services.prefs.setCharPref(kPrefCustomizationState, uiCustomizationState);
@@ -2186,16 +2191,18 @@ let CustomizableUIInternal = {
     // and we don't need to do anything else here:
     if (gSavedState) {
       for (let areaId of Object.keys(gSavedState.placements)) {
         let placements = gSavedState.placements[areaId];
         gPlacements.set(areaId, placements);
       }
       this._rebuildRegisteredAreas();
     }
+
+    gUndoResetting = false;
   },
 
   _clearPreviousUIState: function() {
     Object.getOwnPropertyNames(gUIStateBeforeReset).forEach((prop) => {
       gUIStateBeforeReset[prop] = null;
     });
   },
 
@@ -2459,16 +2466,21 @@ this.CustomizableUI = {
    *     Like onWidgetBeforeDOMChange, but fired after the change to the DOM
    *     node of the widget.
    *
    *   - onWidgetReset(aNode, aContainer)
    *     Fired after a reset to default placements moves a widget's node to a
    *     different location. aNode is the widget's node, aContainer is the
    *     area it was moved into (NB: it might already have been there and been
    *     moved to a different position!)
+   *   - onWidgetUndoMove(aNode, aContainer)
+   *     Fired after undoing a reset to default placements moves a widget's
+   *     node to a different location. aNode is the widget's node, aContainer
+   *     is the area it was moved into (NB: it might already have been there
+   *     and been moved to a different position!)
    *   - onAreaReset(aArea, aContainer)
    *     Fired after a reset to default placements is complete on an area's
    *     DOM node. Note that this is fired for each DOM node. aArea is the area
    *     that was reset, aContainer the DOM node that was reset.
    *
    *   - onWidgetCreated(aWidgetId)
    *     Fired when a widget with id aWidgetId has been created, but before it
    *     is added to any placements or any DOM nodes have been constructed.
--- a/browser/components/customizableui/src/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/src/CustomizableWidgets.jsm
@@ -63,17 +63,17 @@ function setAttributes(aNode, aAttrs) {
       }
       aNode.setAttribute(name, value);
     }
   }
 }
 
 function updateCombinedWidgetStyle(aNode, aArea, aModifyCloseMenu) {
   let inPanel = (aArea == CustomizableUI.AREA_PANEL);
-  let cls = inPanel ? "panel-combined-button" : "toolbarbutton-1";
+  let cls = inPanel ? "panel-combined-button" : "toolbarbutton-1 toolbarbutton-combined";
   let attrs = {class: cls};
   if (aModifyCloseMenu) {
     attrs.closemenu = inPanel ? "none" : null;
   }
   for (let i = 0, l = aNode.childNodes.length; i < l; ++i) {
     if (aNode.childNodes[i].localName == "separator")
       continue;
     setAttributes(aNode.childNodes[i], attrs);
@@ -359,17 +359,17 @@ const CustomizableWidgets = [{
     type: "custom",
     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 closeMenu = inPanel ? "none" : null;
-      let cls = inPanel ? "panel-combined-button" : "toolbarbutton-1";
+      let cls = inPanel ? "panel-combined-button" : "toolbarbutton-1 toolbarbutton-combined";
 
       if (!this.currentArea)
         cls = null;
 
       let buttons = [{
         id: "zoom-out-button",
         closemenu: closeMenu,
         command: "cmd_fullZoomReduce",
@@ -537,17 +537,17 @@ const CustomizableWidgets = [{
       return node;
     }
   }, {
     id: "edit-controls",
     type: "custom",
     defaultArea: CustomizableUI.AREA_PANEL,
     onBuild: function(aDocument) {
       let inPanel = (this.currentArea == CustomizableUI.AREA_PANEL);
-      let cls = inPanel ? "panel-combined-button" : "toolbarbutton-1";
+      let cls = inPanel ? "panel-combined-button" : "toolbarbutton-1 toolbarbutton-combined";
 
       if (!this.currentArea)
         cls = null;
 
       let buttons = [{
         id: "cut-button",
         command: "cmd_cut",
         class: cls,
--- a/browser/components/customizableui/src/CustomizeMode.jsm
+++ b/browser/components/customizableui/src/CustomizeMode.jsm
@@ -1297,30 +1297,32 @@ CustomizeMode.prototype = {
     let targetNode = this._getDragOverNode(aEvent, targetArea, targetIsToolbar, draggedItemId);
 
     // We need to determine the place that the widget is being dropped in
     // the target.
     let dragOverItem, dragValue;
     if (targetNode == targetArea.customizationTarget) {
       // We'll assume if the user is dragging directly over the target, that
       // they're attempting to append a child to that target.
-      dragOverItem = targetNode.lastChild || targetNode;
+      dragOverItem = (targetIsToolbar ? this._findVisiblePreviousSiblingNode(targetNode.lastChild) :
+                                        targetNode.lastChild) || targetNode;
       dragValue = "after";
     } else {
       let targetParent = targetNode.parentNode;
       let position = Array.indexOf(targetParent.children, targetNode);
       if (position == -1) {
-        dragOverItem = targetParent.lastChild;
+        dragOverItem = targetIsToolbar ? this._findVisiblePreviousSiblingNode(targetNode.lastChild) :
+                                         targetParent.lastChild;
         dragValue = "after";
       } else {
         dragOverItem = targetParent.children[position];
         if (!targetIsToolbar) {
           dragValue = "before";
-          dragOverItem = position == -1 ? targetParent.firstChild : targetParent.children[position];
         } else {
+          dragOverItem = this._findVisiblePreviousSiblingNode(targetParent.children[position]);
           // Check if the aDraggedItem is hovered past the first half of dragOverItem
           let window = dragOverItem.ownerDocument.defaultView;
           let direction = window.getComputedStyle(dragOverItem, null).direction;
           let itemRect = dragOverItem.getBoundingClientRect();
           let dropTargetCenter = itemRect.left + (itemRect.width / 2);
           let existingDir = dragOverItem.getAttribute("dragover");
           if ((existingDir == "before") == (direction == "ltr")) {
             dropTargetCenter += (parseInt(dragOverItem.style.borderLeftWidth) || 0) / 2;
@@ -1919,16 +1921,24 @@ CustomizeMode.prototype = {
     if (aToolbarArea) {
       if (CustomizableUI.getAreaType(aToolbarArea.id) != CustomizableUI.TYPE_TOOLBAR)
         return;
       let target = CustomizableUI.getCustomizeTargetForArea(aToolbarArea.id, aWindow);
       target.setAttribute("customizing-dragovertarget", true);
     }
   },
 
+  _findVisiblePreviousSiblingNode: function(aReferenceNode) {
+    while (aReferenceNode &&
+           aReferenceNode.localName == "toolbarpaletteitem" &&
+           aReferenceNode.firstChild.hidden) {
+      aReferenceNode = aReferenceNode.previousSibling;
+    }
+    return aReferenceNode;
+  },
 };
 
 function __dumpDragData(aEvent, caller) {
   if (!gDebug) {
     return;
   }
   let str = "Dumping drag data (" + (caller ? caller + " in " : "") + "CustomizeMode.jsm) {\n";
   str += "  type: " + aEvent["type"] + "\n";
--- a/browser/components/customizableui/src/ScrollbarSampler.jsm
+++ b/browser/components/customizableui/src/ScrollbarSampler.jsm
@@ -26,16 +26,20 @@ this.ScrollbarSampler = {
 
     this._sampleSystemScrollbarWidth().then(function(systemScrollbarWidth) {
       gSystemScrollbarWidth = systemScrollbarWidth;
       deferred.resolve(gSystemScrollbarWidth);
     });
     return deferred.promise;
   },
 
+  resetSystemScrollbarWidth: function() {
+    gSystemScrollbarWidth = null;
+  },
+
   _sampleSystemScrollbarWidth: function() {
     let deferred = Promise.defer();
     let hwin = Services.appShell.hiddenDOMWindow;
     let hdoc = hwin.document.documentElement;
     let iframe = hwin.document.createElementNS("http://www.w3.org/1999/xhtml",
                                                "html:iframe");
     iframe.setAttribute("srcdoc", '<body style="overflow-y: scroll"></body>');
     hdoc.appendChild(iframe);
--- a/browser/components/customizableui/test/browser.ini
+++ b/browser/components/customizableui/test/browser.ini
@@ -61,16 +61,17 @@ skip-if = os == "linux"
 [browser_943683_migration_test.js]
 [browser_944887_destroyWidget_should_destroy_in_palette.js]
 [browser_945739_showInPrivateBrowsing_customize_mode.js]
 [browser_947987_removable_default.js]
 [browser_948985_non_removable_defaultArea.js]
 [browser_952963_areaType_getter_no_area.js]
 [browser_956602_remove_special_widget.js]
 [browser_968447_bookmarks_toolbar_items_in_panel.js]
+[browser_968565_insert_before_hidden_items.js]
 [browser_969427_recreate_destroyed_widget_after_reset.js]
 [browser_969661_character_encoding_navbar_disabled.js]
 [browser_970511_undo_restore_default.js]
 [browser_972267_customizationchange_events.js]
 [browser_973932_addonbar_currentset.js]
 [browser_975719_customtoolbars_behaviour.js]
 [browser_978084_dragEnd_after_move.js]
 [browser_panel_toggle.js]
--- a/browser/components/customizableui/test/browser_876926_customize_mode_wrapping.js
+++ b/browser/components/customizableui/test/browser_876926_customize_mode_wrapping.js
@@ -47,30 +47,62 @@ let move = {
 function isLast(containerId, defaultPlacements, id) {
   assertAreaPlacements(containerId, defaultPlacements.concat([id]));
   is(document.getElementById(containerId).customizationTarget.lastChild.firstChild.id, id,
      "Widget " + id + " should be in " + containerId + " in customizing window.");
   is(otherWin.document.getElementById(containerId).customizationTarget.lastChild.id, id,
      "Widget " + id + " should be in " + containerId + " in other window.");
 }
 
+function getLastVisibleNodeInToolbar(containerId, win=window) {
+  let container = win.document.getElementById(containerId).customizationTarget;
+  let rv = container.lastChild;
+  while (rv && (rv.getAttribute('hidden') == 'true' || (rv.firstChild && rv.firstChild.getAttribute('hidden') == 'true'))) {
+    rv = rv.previousSibling;
+  }
+  return rv;
+}
+
+function isLastVisibleInToolbar(containerId, defaultPlacements, id) {
+  let newPlacements;
+  for (let i = defaultPlacements.length - 1; i >= 0; i--) {
+    let el = document.getElementById(defaultPlacements[i]);
+    if (el && el.getAttribute('hidden') != 'true') {
+      newPlacements = [...defaultPlacements];
+      newPlacements.splice(i + 1, 0, id);
+      break;
+    }
+  }
+  if (!newPlacements) {
+    assertAreaPlacements(containerId, defaultPlacements.concat([id]));
+  } else {
+    assertAreaPlacements(containerId, newPlacements);
+  }
+  is(getLastVisibleNodeInToolbar(containerId).firstChild.id, id,
+     "Widget " + id + " should be in " + containerId + " in customizing window.");
+  is(getLastVisibleNodeInToolbar(containerId, otherWin).id, id,
+     "Widget " + id + " should be in " + containerId + " in other window.");
+}
+
 function isFirst(containerId, defaultPlacements, id) {
   assertAreaPlacements(containerId, [id].concat(defaultPlacements));
   is(document.getElementById(containerId).customizationTarget.firstChild.firstChild.id, id,
      "Widget " + id + " should be in " + containerId + " in customizing window.");
   is(otherWin.document.getElementById(containerId).customizationTarget.firstChild.id, id,
      "Widget " + id + " should be in " + containerId + " in other window.");
 }
 
 function checkToolbar(id, method) {
   // Place at start of the toolbar:
   let toolbarPlacements = getAreaWidgetIds(kToolbar);
   move[method](id, kToolbar);
   if (method == "dragToItem") {
     isFirst(kToolbar, toolbarPlacements, id);
+  } else if (method == "drag") {
+    isLastVisibleInToolbar(kToolbar, toolbarPlacements, id);
   } else {
     isLast(kToolbar, toolbarPlacements, id);
   }
   checkWrapper(id);
 }
 
 function checkPanel(id, method) {
   let panelPlacements = getAreaWidgetIds(kPanel);
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/test/browser_968565_insert_before_hidden_items.js
@@ -0,0 +1,82 @@
+/* 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 kHidden1Id = "test-hidden-button-1";
+const kHidden2Id = "test-hidden-button-2";
+
+let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
+
+// When we drag an item onto a customizable area, and not over a specific target, we
+// should assume that we're appending them to the area. If doing so, we should scan
+// backwards over any hidden items and insert the item before those hidden items.
+add_task(function() {
+  ok(CustomizableUI.inDefaultState, "Should be in the default state");
+
+  // Iterate backwards over the items in the nav-bar until we find the first
+  // one that is not hidden.
+  let placements = CustomizableUI.getWidgetsInArea(CustomizableUI.AREA_NAVBAR);
+  let lastVisible = null;
+  for (let widgetGroup of placements.reverse()) {
+    let widget = widgetGroup.forWindow(window);
+    if (widget && widget.node && !widget.node.hidden) {
+      lastVisible = widget.node;
+      break;
+    }
+  }
+
+  if (!lastVisible) {
+    ok(false, "Apparently, there are no visible items in the nav-bar.");
+  }
+
+  info("The last visible item in the nav-bar has ID: " + lastVisible.id);
+
+  let hidden1 = createDummyXULButton(kHidden1Id, "You can't see me");
+  let hidden2 = createDummyXULButton(kHidden2Id, "You can't see me either.");
+  hidden1.hidden = hidden2.hidden = true;
+
+  // Make sure we have some hidden items at the end of the nav-bar.
+  navbar.insertItem(hidden1.id);
+  navbar.insertItem(hidden2.id);
+
+  // Drag an item and drop it onto the nav-bar customization target, but
+  // not over a particular item.
+  yield startCustomizing();
+  let downloadsButton = document.getElementById("downloads-button");
+  simulateItemDrag(downloadsButton, navbar.customizationTarget);
+
+  yield endCustomizing();
+
+  is(downloadsButton.previousSibling.id, lastVisible.id,
+     "The downloads button should be placed after the last visible item.");
+
+  yield resetCustomization();
+});
+
+// When we drag an item onto a target that has a hidden element before it, we should
+// instead place the new item before the hidden elements.
+add_task(function() {
+  ok(CustomizableUI.inDefaultState, "Should be in the default state");
+
+  let hidden1 = createDummyXULButton(kHidden1Id, "You can't see me");
+  hidden1.hidden = true;
+
+  let homeButton = document.getElementById("home-button");
+  CustomizableUI.addWidgetToArea(kHidden1Id, CustomizableUI.AREA_NAVBAR,
+                                 CustomizableUI.getPlacementOfWidget(homeButton.id).position);
+
+  hidden1 = document.getElementById(kHidden1Id);
+  is(hidden1.nextSibling.id, homeButton.id, "The hidden item should be before the home button");
+
+  yield startCustomizing();
+  let downloadsButton = document.getElementById("downloads-button");
+  simulateItemDrag(downloadsButton.parentNode, homeButton.parentNode);
+  yield endCustomizing();
+
+  is(hidden1.nextSibling.id, homeButton.id, "The hidden item should still be before the home button");
+  is(downloadsButton.nextSibling.id, hidden1.id, "The downloads button should now be before the hidden button");
+
+  yield resetCustomization();
+});
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -1287,17 +1287,17 @@ BrowserGlue.prototype = {
     var notifyBox = win.gBrowser.getNotificationBox();
     var notification = notifyBox.appendNotification(text, title, null,
                                                     notifyBox.PRIORITY_CRITICAL_MEDIUM,
                                                     buttons);
     notification.persistence = -1; // Until user closes it
   },
 
   _migrateUI: function BG__migrateUI() {
-    const UI_VERSION = 20;
+    const UI_VERSION = 21;
     const BROWSER_DOCURL = "chrome://browser/content/browser.xul#";
     let currentUIVersion = 0;
     try {
       currentUIVersion = Services.prefs.getIntPref("browser.migration.version");
     } catch(ex) {}
     if (currentUIVersion >= UI_VERSION)
       return;
 
@@ -1559,16 +1559,26 @@ BrowserGlue.prototype = {
       // Remove persisted collapsed state from TabsToolbar.
       let resource = this._rdf.GetResource("collapsed");
       let toolbar = this._rdf.GetResource(BROWSER_DOCURL + "TabsToolbar");
       if (this._getPersist(toolbar, resource)) {
         this._setPersist(toolbar, resource);
       }
     }
 
+    if (currentUIVersion < 21) {
+      // Make sure the 'toolbarbutton-1' class will always be present from here
+      // on out.
+      let button = this._rdf.GetResource(BROWSER_DOCURL + "bookmarks-menu-button");
+      let classResource = this._rdf.GetResource("class");
+      if (this._getPersist(button, classResource)) {
+        this._setPersist(button, classResource);
+      }
+    }
+
     if (this._dirty)
       this._dataSource.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush();
 
     delete this._rdf;
     delete this._dataSource;
 
     // Update the migration version.
     Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
--- a/browser/devtools/framework/toolbox.js
+++ b/browser/devtools/framework/toolbox.js
@@ -460,17 +460,17 @@ Toolbox.prototype = {
   /**
    * Handle any custom key events.  Returns true if there was a custom key binding run
    * @param {string} toolId
    *        Which tool to run the command on (skip if not current)
    */
   fireCustomKey: function(toolId) {
     let toolDefinition = gDevTools.getToolDefinition(toolId);
 
-    if (toolDefinition.onkey && 
+    if (toolDefinition.onkey &&
         ((this.currentToolId === toolId) ||
           (toolId == "webconsole" && this.splitConsole))) {
       toolDefinition.onkey(this.getCurrentPanel(), this);
     }
   },
 
   /**
    * Build the buttons for changing hosts. Called every time
@@ -1036,37 +1036,27 @@ Toolbox.prototype = {
     }
   },
 
   /**
    * Initialize the inspector/walker/selection/highlighter fronts.
    * Returns a promise that resolves when the fronts are initialized
    */
   initInspector: function() {
-    let deferred = promise.defer();
-
-    if (!this._inspector) {
-      this._inspector = InspectorFront(this._target.client, this._target.form);
-      this._inspector.getWalker().then(walker => {
-        this._walker = walker;
+    if (!this._initInspector) {
+      this._initInspector = Task.spawn(function*() {
+        this._inspector = InspectorFront(this._target.client, this._target.form);
+        this._walker = yield this._inspector.getWalker();
         this._selection = new Selection(this._walker);
         if (this.highlighterUtils.isRemoteHighlightable) {
-          this._inspector.getHighlighter().then(highlighter => {
-            this._highlighter = highlighter;
-            deferred.resolve();
-          });
-        } else {
-          deferred.resolve();
+          this._highlighter = yield this._inspector.getHighlighter();
         }
-      });
-    } else {
-      deferred.resolve();
+      }.bind(this));
     }
-
-    return deferred.promise;
+    return this._initInspector;
   },
 
   /**
    * Destroy the inspector/walker/selection fronts
    * Returns a promise that resolves when the fronts are destroyed
    */
   destroyInspector: function() {
     if (!this._inspector) {
--- a/browser/devtools/inspector/test/head.js
+++ b/browser/devtools/inspector/test/head.js
@@ -6,16 +6,18 @@ const Cu = Components.utils;
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 
 // Services.prefs.setBoolPref("devtools.debugger.log", true);
 // SimpleTest.registerCleanupFunction(() => {
 //   Services.prefs.clearUserPref("devtools.debugger.log");
 // });
 
+//Services.prefs.setBoolPref("devtools.dump.emit", true);
+
 let tempScope = {};
 Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm", tempScope);
 let LayoutHelpers = tempScope.LayoutHelpers;
 
 let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", tempScope);
 let TargetFactory = devtools.TargetFactory;
 
 Components.utils.import("resource://gre/modules/devtools/Console.jsm", tempScope);
@@ -24,16 +26,18 @@ let console = tempScope.console;
 // Import the GCLI test helper
 let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
 Services.scriptloader.loadSubScript(testDir + "../../../commandline/test/helpers.js", this);
 
 SimpleTest.registerCleanupFunction(() => {
   console.error("Here we are\n")
   let {DebuggerServer} = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
   console.error("DebuggerServer open connections: " + Object.getOwnPropertyNames(DebuggerServer._connections).length);
+
+  Services.prefs.clearUserPref("devtools.dump.emit");
 });
 
 function openInspector(callback)
 {
   let target = TargetFactory.forTab(gBrowser.selectedTab);
   gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
     callback(toolbox.getCurrentPanel(), toolbox);
   }).then(null, console.error);
--- a/browser/devtools/markupview/test/head.js
+++ b/browser/devtools/markupview/test/head.js
@@ -5,21 +5,24 @@
 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 {getInplaceEditorForSpan: inplaceEditor} = devtools.require("devtools/shared/inplace-editor");
 
+//Services.prefs.setBoolPref("devtools.dump.emit", true);
+
 // Clear preferences that may be set during the course of tests.
 function clearUserPrefs() {
   Services.prefs.clearUserPref("devtools.inspector.htmlPanelOpen");
   Services.prefs.clearUserPref("devtools.inspector.sidebarOpen");
   Services.prefs.clearUserPref("devtools.inspector.activeSidebar");
+  Services.prefs.clearUserPref("devtools.dump.emit");
 }
 
 registerCleanupFunction(clearUserPrefs);
 
 /**
  * Add a new test tab in the browser and load the given url.
  * @param {String} url The url to be loaded in the new tab
  * @return a promise that resolves when the url is loaded
--- a/browser/devtools/shared/widgets/VariablesView.jsm
+++ b/browser/devtools/shared/widgets/VariablesView.jsm
@@ -47,17 +47,17 @@ Object.defineProperty(this, "WebConsoleU
 Object.defineProperty(this, "NetworkHelper", {
   get: function() {
     return devtools.require("devtools/toolkit/webconsole/network-helper");
   },
   configurable: true,
   enumerable: true
 });
 
-this.EXPORTED_SYMBOLS = ["VariablesView"];
+this.EXPORTED_SYMBOLS = ["VariablesView", "escapeHTML"];
 
 /**
  * Debugger localization strings.
  */
 const STR = Services.strings.createBundle(DBG_STRINGS_URI);
 
 /**
  * A tree view for inspecting scopes, objects and properties.
@@ -3418,18 +3418,35 @@ VariablesView.stringifiers.byObjectClass
     }
 
     if (typeof preview.timestamp != "number") {
       return new Date(preview.timestamp).toString(); // invalid date
     }
 
     return "Date " + new Date(preview.timestamp).toISOString();
   },
+
+  String: function({displayString}) {
+    if (displayString === undefined) {
+      return null;
+    }
+    return VariablesView.getString(displayString);
+  },
+
+  Number: function({preview}) {
+    if (preview === undefined) {
+      return null;
+    }
+    return VariablesView.getString(preview.value);
+  },
 }; // VariablesView.stringifiers.byObjectClass
 
+VariablesView.stringifiers.byObjectClass.Boolean =
+  VariablesView.stringifiers.byObjectClass.Number;
+
 VariablesView.stringifiers.byObjectKind = {
   ArrayLike: function(aGrip, {concise}) {
     let {preview} = aGrip;
     if (concise) {
       return aGrip.class + "[" + preview.length + "]";
     }
 
     if (!preview.items) {
--- a/browser/devtools/styleinspector/test/head.js
+++ b/browser/devtools/styleinspector/test/head.js
@@ -1,19 +1,22 @@
 /* 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/. */
 
 const TEST_BASE_HTTP = "http://example.com/browser/browser/devtools/styleinspector/test/";
 const TEST_BASE_HTTPS = "https://example.com/browser/browser/devtools/styleinspector/test/";
 
+//Services.prefs.setBoolPref("devtools.dump.emit", true);
 Services.prefs.setBoolPref("devtools.debugger.log", true);
+
 SimpleTest.registerCleanupFunction(() => {
   Services.prefs.clearUserPref("devtools.debugger.log");
+  Services.prefs.clearUserPref("devtools.dump.emit");
 });
 
 let tempScope = {};
 
 Cu.import("resource:///modules/devtools/gDevTools.jsm", tempScope);
 let ConsoleUtils = tempScope.ConsoleUtils;
 let gDevTools = tempScope.gDevTools;
 
--- a/browser/devtools/webconsole/console-output.js
+++ b/browser/devtools/webconsole/console-output.js
@@ -3,16 +3,19 @@
  * 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 {Cc, Ci, Cu} = require("chrome");
 
 loader.lazyImporter(this, "VariablesView", "resource:///modules/devtools/VariablesView.jsm");
+loader.lazyImporter(this, "escapeHTML", "resource:///modules/devtools/VariablesView.jsm");
+loader.lazyImporter(this, "gDevTools", "resource:///modules/devtools/gDevTools.jsm");
+loader.lazyImporter(this, "Task","resource://gre/modules/Task.jsm");
 
 const Heritage = require("sdk/core/heritage");
 const XHTML_NS = "http://www.w3.org/1999/xhtml";
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const STRINGS_URI = "chrome://browser/locale/devtools/webconsole.properties";
 
 const WebConsoleUtils = require("devtools/toolkit/webconsole/utils").Utils;
 const l10n = new WebConsoleUtils.l10n(STRINGS_URI);
@@ -125,17 +128,17 @@ ConsoleOutput.prototype = {
     return this.owner.outputNode;
   },
 
   /**
    * The document that holds the output.
    * @type DOMDocument
    */
   get document() {
-    return this.owner.document;
+    return this.owner ? this.owner.document : null;
   },
 
   /**
    * The DOM window that holds the output.
    * @type Window
    */
   get window() {
     return this.owner.window;
@@ -145,16 +148,24 @@ ConsoleOutput.prototype = {
    * Getter for the debugger WebConsoleClient.
    * @type object
    */
   get webConsoleClient() {
     return this.owner.webConsoleClient;
   },
 
   /**
+   * Getter for the current toolbox debuggee target.
+   * @type Target
+   */
+  get toolboxTarget() {
+    return this.owner.owner.target;
+  },
+
+  /**
    * Release an actor.
    *
    * @private
    * @param string actorId
    *        The actor ID you want to release.
    */
   _releaseObject: function(actorId)
   {
@@ -501,16 +512,24 @@ Messages.BaseMessage.prototype = {
    * @private
    * @param Event event
    *        The DOM event that invoked this function.
    */
   _onClickAnchor: function(event)
   {
     this.output.openLink(event.target.href);
   },
+
+  destroy: function()
+  {
+    // Destroy all widgets that have registered themselves in this.widgets
+    for (let widget of this.widgets) {
+      widget.destroy();
+    }
+  }
 }; // Messages.BaseMessage.prototype
 
 
 /**
  * The NavigationMarker is used to show a page load event.
  *
  * @constructor
  * @extends Messages.BaseMessage
@@ -997,43 +1016,141 @@ Messages.Extended.prototype = Heritage.e
   {
     if (piece instanceof Ci.nsIDOMNode) {
       return piece;
     }
     if (typeof piece == "function") {
       return piece(this);
     }
 
-    let isPrimitive = VariablesView.isPrimitive({ value: piece });
-    let isActorGrip = WebConsoleUtils.isActorGrip(piece);
+    return this._renderValueGrip(piece);
+  },
+
+  /**
+   * Render a grip that represents a value received from the server. This method
+   * picks the appropriate widget to render the value with.
+   *
+   * @private
+   * @param object grip
+   *        The value grip received from the server.
+   * @param object options
+   *        Options for displaying the value. Available options:
+   *        - noStringQuotes - boolean that tells the renderer to not use quotes
+   *        around strings.
+   *        - concise - boolean that tells the renderer to compactly display the
+   *        grip. This is typically set to true when the object needs to be
+   *        displayed in an array preview, or as a property value in object
+   *        previews, etc.
+   * @return DOMElement
+   *         The DOM element that displays the given grip.
+   */
+  _renderValueGrip: function(grip, options = {})
+  {
+    let isPrimitive = VariablesView.isPrimitive({ value: grip });
+    let isActorGrip = WebConsoleUtils.isActorGrip(grip);
+    let noStringQuotes = !this._quoteStrings;
+    if ("noStringQuotes" in options) {
+      noStringQuotes = options.noStringQuotes;
+    }
 
     if (isActorGrip) {
-      this._repeatID.actors.add(piece.actor);
+      this._repeatID.actors.add(grip.actor);
 
       if (!isPrimitive) {
-        let widget = new Widgets.JSObject(this, piece).render();
-        return widget.element;
+        return this._renderObjectActor(grip, options);
       }
-      if (piece.type == "longString") {
-        let widget = new Widgets.LongString(this, piece).render();
+      if (grip.type == "longString") {
+        let widget = new Widgets.LongString(this, grip, options).render();
         return widget.element;
       }
     }
 
-    let result = this.document.createDocumentFragment();
+    let result = this.document.createElementNS(XHTML_NS, "span");
     if (isPrimitive) {
-      result.textContent = VariablesView.getString(piece, {
-        noStringQuotes: !this._quoteStrings,
+      let className = this.getClassNameForValueGrip(grip);
+      if (className) {
+        result.className = className;
+      }
+
+      result.textContent = VariablesView.getString(grip, {
+        noStringQuotes: noStringQuotes,
+        concise: options.concise,
       });
     } else {
-      result.textContent = piece;
+      result.textContent = grip;
     }
 
     return result;
   },
+
+  /**
+   * Get a CodeMirror-compatible class name for a given value grip.
+   *
+   * @param object grip
+   *        Value grip from the server.
+   * @return string
+   *         The class name for the grip.
+   */
+  getClassNameForValueGrip: function(grip)
+  {
+    let map = {
+      "number": "cm-number",
+      "longstring": "cm-string",
+      "string": "cm-string",
+      "regexp": "cm-string-2",
+      "boolean": "cm-atom",
+      "-infinity": "cm-atom",
+      "infinity": "cm-atom",
+      "null": "cm-atom",
+      "undefined": "cm-atom",
+    };
+
+    let className = map[typeof grip];
+    if (!className && grip && grip.type) {
+      className = map[grip.type.toLowerCase()];
+    }
+    if (!className && grip && grip.class) {
+      className = map[grip.class.toLowerCase()];
+    }
+
+    return className;
+  },
+
+  /**
+   * Display an object actor with the appropriate renderer.
+   *
+   * @private
+   * @param object objectActor
+   *        The ObjectActor to display.
+   * @param object options
+   *        Options to use for displaying the ObjectActor.
+   * @see this._renderValueGrip for the available options.
+   * @return DOMElement
+   *         The DOM element that displays the object actor.
+   */
+  _renderObjectActor: function(objectActor, options = {})
+  {
+    let widget = null;
+    let {preview} = objectActor;
+
+    if (preview && preview.kind) {
+      widget = Widgets.ObjectRenderers.byKind[preview.kind];
+    }
+
+    if (!widget || (widget.canRender && !widget.canRender(objectActor))) {
+      widget = Widgets.ObjectRenderers.byClass[objectActor.class];
+    }
+
+    if (!widget || (widget.canRender && !widget.canRender(objectActor))) {
+      widget = Widgets.JSObject;
+    }
+
+    let instance = new widget(this, objectActor, options).render();
+    return instance.element;
+  },
 }); // Messages.Extended.prototype
 
 
 
 /**
  * The JavaScriptEvalOutput message.
  *
  * @constructor
@@ -1051,16 +1168,17 @@ Messages.JavaScriptEvalOutput = function
     severity = "error";
     msg = errorMessage;
     quoteStrings = false;
   } else {
     msg = evalResponse.result;
   }
 
   let options = {
+    className: "cm-s-mozilla",
     timestamp: evalResponse.timestamp,
     category: "output",
     severity: severity,
     quoteStrings: quoteStrings,
   };
   Messages.Extended.call(this, [msg], options);
 };
 
@@ -1072,16 +1190,17 @@ Messages.JavaScriptEvalOutput.prototype 
  * @constructor
  * @extends Messages.Extended
  * @param object packet
  *        The Console API call packet received from the server.
  */
 Messages.ConsoleGeneric = function(packet)
 {
   let options = {
+    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,
@@ -1288,16 +1407,78 @@ Widgets.BaseWidget.prototype = {
    * @return this
    */
   render: function() { },
 
   /**
    * Destroy this widget instance.
    */
   destroy: function() { },
+
+  /**
+   * Helper for creating DOM elements for widgets.
+   *
+   * Usage:
+   *   this.el("tag#id.class.names"); // create element "tag" with ID "id" and
+   *   two class names, .class and .names.
+   *
+   *   this.el("span", { attr1: "value1", ... }) // second argument can be an
+   *   object that holds element attributes and values for the new DOM element.
+   *
+   *   this.el("p", { attr1: "value1", ... }, "text content"); // the third
+   *   argument can include the default .textContent of the new DOM element.
+   *
+   *   this.el("p", "text content"); // if the second argument is not an object,
+   *   it will be used as .textContent for the new DOM element.
+   *
+   * @param string tagNameIdAndClasses
+   *        Tag name for the new element, optionally followed by an ID and/or
+   *        class names. Examples: "span", "div#fooId", "div.class.names",
+   *        "p#id.class".
+   * @param string|object [attributesOrTextContent]
+   *        If this argument is an object it will be used to set the attributes
+   *        of the new DOM element. Otherwise, the value becomes the
+   *        .textContent of the new DOM element.
+   * @param string [textContent]
+   *        If this argument is provided the value is used as the textContent of
+   *        the new DOM element.
+   * @return DOMElement
+   *         The new DOM element.
+   */
+  el: function(tagNameIdAndClasses)
+  {
+    let attrs, text;
+    if (typeof arguments[1] == "object") {
+      attrs = arguments[1];
+      text = arguments[2];
+    } else {
+      text = arguments[1];
+    }
+
+    let tagName = tagNameIdAndClasses.split(/#|\./)[0];
+
+    let elem = this.document.createElementNS(XHTML_NS, tagName);
+    for (let name of Object.keys(attrs || {})) {
+      elem.setAttribute(name, attrs[name]);
+    }
+    if (text !== undefined && text !== null) {
+      elem.textContent = text;
+    }
+
+    let idAndClasses = tagNameIdAndClasses.match(/([#.][^#.]+)/g);
+    for (let idOrClass of (idAndClasses || [])) {
+      if (idOrClass.charAt(0) == "#") {
+        elem.id = idOrClass.substr(1);
+      } else {
+        elem.classList.add(idOrClass.substr(1));
+      }
+    }
+
+    return elem;
+  },
 };
 
 /**
  * The timestamp widget.
  *
  * @constructor
  * @param object message
  *        The owning message.
@@ -1329,68 +1510,903 @@ Widgets.MessageTimestamp.prototype = Her
     this.element.textContent = l10n.timestampString(this.timestamp) + " ";
 
     return this;
   },
 }); // Widgets.MessageTimestamp.prototype
 
 
 /**
- * The JavaScript object widget.
+ * Widget used for displaying ObjectActors that have no specialised renderers.
  *
  * @constructor
  * @param object message
  *        The owning message.
  * @param object objectActor
  *        The ObjectActor to display.
+ * @param object [options]
+ *        Options for displaying the given ObjectActor. See
+ *        Messages.Extended.prototype._renderValueGrip for the available
+ *        options.
  */
-Widgets.JSObject = function(message, objectActor)
+Widgets.JSObject = function(message, objectActor, options = {})
 {
   Widgets.BaseWidget.call(this, message);
   this.objectActor = objectActor;
+  this.options = options;
   this._onClick = this._onClick.bind(this);
 };
 
 Widgets.JSObject.prototype = Heritage.extend(Widgets.BaseWidget.prototype,
 {
   /**
    * The ObjectActor displayed by the widget.
    * @type object
    */
   objectActor: null,
 
   render: function()
   {
-    if (this.element) {
-      return this;
+    if (!this.element) {
+      this._render();
+    }
+
+    return this;
+  },
+
+  _render: function()
+  {
+    let str = VariablesView.getString(this.objectActor, this.options);
+    let className = this.message.getClassNameForValueGrip(this.objectActor);
+    if (!className && this.objectActor.class == "Object") {
+      className = "cm-variable";
     }
 
-    let anchor = this.element = this.document.createElementNS(XHTML_NS, "a");
-    anchor.href = "#";
-    anchor.draggable = false;
-    anchor.textContent = VariablesView.getString(this.objectActor);
-    this.message._addLinkCallback(anchor, this._onClick);
+    this.element = this._anchor(str, { className: className });
+  },
 
-    return this;
+  /**
+   * Render an anchor with a given text content and link.
+   *
+   * @private
+   * @param string text
+   *        Text to show in the anchor.
+   * @param object [options]
+   *        Available options:
+   *        - onClick (function): "click" event handler.By default a click on
+   *        the anchor opens the variables view for the current object actor
+   *        (this.objectActor).
+   *        - href (string): if given the string is used as a link, and clicks
+   *        on the anchor open the link in a new tab.
+   *        - appendTo (DOMElement): append the element to the given DOM
+   *        element. If not provided, the anchor is appended to |this.element|
+   *        if it is available. If |appendTo| is provided and if it is a falsy
+   *        value, the anchor is not appended to any element.
+   * @return DOMElement
+   *         The DOM element of the new anchor.
+   */
+  _anchor: function(text, options = {})
+  {
+    if (!options.onClick && !options.href) {
+      options.onClick = this._onClick;
+    }
+
+    let anchor = this.el("a", {
+      class: options.className,
+      draggable: false,
+      href: options.href || "#",
+    }, text);
+
+    this.message._addLinkCallback(anchor, !options.href ? options.onClick : null);
+
+    if (options.appendTo) {
+      options.appendTo.appendChild(anchor);
+    } else if (!("appendTo" in options) && this.element) {
+      this.element.appendChild(anchor);
+    }
+
+    return anchor;
   },
 
   /**
    * The click event handler for objects shown inline.
    * @private
    */
   _onClick: function()
   {
     this.output.openVariablesView({
       label: VariablesView.getString(this.objectActor, { concise: true }),
       objectActor: this.objectActor,
       autofocus: true,
     });
   },
+
+  /**
+   * Add a string to the message.
+   *
+   * @private
+   * @param string str
+   *        String to add.
+   * @param DOMElement [target = this.element]
+   *        Optional DOM element to append the string to. The default is
+   *        this.element.
+   */
+  _text: function(str, target = this.element)
+  {
+    target.appendChild(this.document.createTextNode(str));
+  },
 }); // Widgets.JSObject.prototype
 
+Widgets.ObjectRenderers = {};
+Widgets.ObjectRenderers.byKind = {};
+Widgets.ObjectRenderers.byClass = {};
+
+/**
+ * Add an object renderer.
+ *
+ * @param object obj
+ *        An object that represents the renderer. Properties:
+ *        - byClass (string, optional): this renderer will be used for the given
+ *        object class.
+ *        - byKind (string, optional): this renderer will be used for the given
+ *        object kind.
+ *        One of byClass or byKind must be provided.
+ *        - extends (object, optional): the renderer object extends the given
+ *        object. Default: Widgets.JSObject.
+ *        - canRender (function, optional): this method is invoked when
+ *        a candidate object needs to be displayed. The method is invoked as
+ *        a static method, as such, none of the properties of the renderer
+ *        object will be available. You get one argument: the object actor grip
+ *        received from the server. If the method returns true, then this
+ *        renderer is used for displaying the object, otherwise not.
+ *        - initialize (function, optional): the constructor of the renderer
+ *        widget. This function is invoked with the following arguments: the
+ *        owner message object instance, the object actor grip to display, and
+ *        an options object. See Messages.Extended.prototype._renderValueGrip()
+ *        for details about the options object.
+ *        - render (function, required): the method that displays the given
+ *        object actor.
+ */
+Widgets.ObjectRenderers.add = function(obj)
+{
+  let extendObj = obj.extends || Widgets.JSObject;
+
+  let constructor = function() {
+    if (obj.initialize) {
+      obj.initialize.apply(this, arguments);
+    } else {
+      extendObj.apply(this, arguments);
+    }
+  };
+
+  let proto = WebConsoleUtils.cloneObject(obj, false, function(key) {
+    if (key == "initialize" || key == "canRender" ||
+        (key == "render" && extendObj === Widgets.JSObject)) {
+      return false;
+    }
+    return true;
+  });
+
+  if (extendObj === Widgets.JSObject) {
+    proto._render = obj.render;
+  }
+
+  constructor.canRender = obj.canRender;
+  constructor.prototype = Heritage.extend(extendObj.prototype, proto);
+
+  if (obj.byClass) {
+    Widgets.ObjectRenderers.byClass[obj.byClass] = constructor;
+  } else if (obj.byKind) {
+    Widgets.ObjectRenderers.byKind[obj.byKind] = constructor;
+  } else {
+    throw new Error("You are adding an object renderer without any byClass or " +
+                    "byKind property.");
+  }
+};
+
+
+/**
+ * The widget used for displaying Date objects.
+ */
+Widgets.ObjectRenderers.add({
+  byClass: "Date",
+
+  render: function()
+  {
+    let {preview} = this.objectActor;
+    this.element = this.el("span.class-" + this.objectActor.class);
+
+    let anchorText = this.objectActor.class;
+    let anchorClass = "cm-variable";
+    if ("timestamp" in preview && typeof preview.timestamp != "number") {
+      anchorText = new Date(preview.timestamp).toString(); // invalid date
+      anchorClass = "";
+    }
+
+    this._anchor(anchorText, { className: anchorClass });
+
+    if (!("timestamp" in preview) || typeof preview.timestamp != "number") {
+      return;
+    }
+
+    this._text(" ");
+
+    let elem = this.el("span.cm-string-2", new Date(preview.timestamp).toISOString());
+    this.element.appendChild(elem);
+  },
+});
+
+/**
+ * The widget used for displaying Function objects.
+ */
+Widgets.ObjectRenderers.add({
+  byClass: "Function",
+
+  render: function()
+  {
+    let grip = this.objectActor;
+    this.element = this.el("span.class-" + this.objectActor.class);
+
+    // TODO: Bug 948484 - support arrow functions and ES6 generators
+    let name = grip.userDisplayName || grip.displayName || grip.name || "";
+    name = VariablesView.getString(name, { noStringQuotes: true });
+
+    let str = this.options.concise ? name || "function " : "function " + name;
+
+    if (this.options.concise) {
+      this._anchor(name || "function", {
+        className: name ? "cm-variable" : "cm-keyword",
+      });
+      if (!name) {
+        this._text(" ");
+      }
+    } else if (name) {
+      this.element.appendChild(this.el("span.cm-keyword", "function"));
+      this._text(" ");
+      this._anchor(name, { className: "cm-variable" });
+    } else {
+      this._anchor("function", { className: "cm-keyword" });
+      this._text(" ");
+    }
+
+    this._text("(");
+
+    // TODO: Bug 948489 - Support functions with destructured parameters and
+    // rest parameters
+    let params = grip.parameterNames || [];
+    let shown = 0;
+    for (let param of params) {
+      if (shown > 0) {
+        this._text(", ");
+      }
+      this.element.appendChild(this.el("span.cm-def", param));
+      shown++;
+    }
+
+    this._text(")");
+  },
+}); // Widgets.ObjectRenderers.byClass.Function
+
+/**
+ * The widget used for displaying ArrayLike objects.
+ */
+Widgets.ObjectRenderers.add({
+  byKind: "ArrayLike",
+
+  render: function()
+  {
+    let {preview} = this.objectActor;
+    let {items} = preview;
+    this.element = this.el("span.kind-" + preview.kind);
+
+    this._anchor(this.objectActor.class, { className: "cm-variable" });
+
+    if (!items || this.options.concise) {
+      this._text("[");
+      this.element.appendChild(this.el("span.cm-number", preview.length));
+      this._text("]");
+      return this;
+    }
+
+    this._text(" [ ");
+
+    let shown = 0;
+    for (let item of items) {
+      if (shown > 0) {
+        this._text(", ");
+      }
+
+      if (item !== null) {
+        let elem = this.message._renderValueGrip(item, { concise: true });
+        this.element.appendChild(elem);
+      } else if (shown == (items.length - 1)) {
+        this._text(", ");
+      }
+
+      shown++;
+    }
+
+    if (shown < preview.length) {
+      this._text(", ");
+
+      let n = preview.length - shown;
+      let str = VariablesView.stringifiers._getNMoreString(n);
+      this._anchor(str);
+    }
+
+    this._text(" ]");
+  },
+}); // Widgets.ObjectRenderers.byKind.ArrayLike
+
+/**
+ * The widget used for displaying MapLike objects.
+ */
+Widgets.ObjectRenderers.add({
+  byKind: "MapLike",
+
+  render: function()
+  {
+    let {preview} = this.objectActor;
+    let {entries} = preview;
+
+    let container = this.element = this.el("span.kind-" + preview.kind);
+    this._anchor(this.objectActor.class, { className: "cm-variable" });
+
+    if (!entries || this.options.concise) {
+      if (typeof preview.size == "number") {
+        this._text("[");
+        container.appendChild(this.el("span.cm-number", preview.size));
+        this._text("]");
+      }
+      return;
+    }
+
+    this._text(" { ");
+
+    let shown = 0;
+    for (let [key, value] of entries) {
+      if (shown > 0) {
+        this._text(", ");
+      }
+
+      let keyElem = this.message._renderValueGrip(key, {
+        concise: true,
+        noStringQuotes: true,
+      });
+
+      // Strings are property names.
+      if (keyElem.classList && keyElem.classList.contains("cm-string")) {
+        keyElem.classList.remove("cm-string");
+        keyElem.classList.add("cm-property");
+      }
+
+      container.appendChild(keyElem);
+
+      this._text(": ");
+
+      let valueElem = this.message._renderValueGrip(value, { concise: true });
+      container.appendChild(valueElem);
+
+      shown++;
+    }
+
+    if (typeof preview.size == "number" && shown < preview.size) {
+      this._text(", ");
+
+      let n = preview.size - shown;
+      let str = VariablesView.stringifiers._getNMoreString(n);
+      this._anchor(str);
+    }
+
+    this._text(" }");
+  },
+}); // Widgets.ObjectRenderers.byKind.MapLike
+
+/**
+ * The widget used for displaying objects with a URL.
+ */
+Widgets.ObjectRenderers.add({
+  byKind: "ObjectWithURL",
+
+  render: function()
+  {
+    this.element = this._renderElement(this.objectActor,
+                                       this.objectActor.preview.url);
+  },
+
+  _renderElement: function(objectActor, url)
+  {
+    let container = this.el("span.kind-" + objectActor.preview.kind);
+
+    this._anchor(objectActor.class, {
+      className: "cm-variable",
+      appendTo: container,
+    });
+
+    if (!VariablesView.isFalsy({ value: url })) {
+      this._text(" \u2192 ", container);
+      let shortUrl = WebConsoleUtils.abbreviateSourceURL(url, {
+        onlyCropQuery: !this.options.concise
+      });
+      this._anchor(shortUrl, { href: url, appendTo: container });
+    }
+
+    return container;
+  },
+}); // Widgets.ObjectRenderers.byKind.ObjectWithURL
+
+/**
+ * The widget used for displaying objects with a string next to them.
+ */
+Widgets.ObjectRenderers.add({
+  byKind: "ObjectWithText",
+
+  render: function()
+  {
+    let {preview} = this.objectActor;
+    this.element = this.el("span.kind-" + preview.kind);
+
+    this._anchor(this.objectActor.class, { className: "cm-variable" });
+
+    if (!this.options.concise) {
+      this._text(" ");
+      this.element.appendChild(this.el("span.cm-string",
+                                       VariablesView.getString(preview.text)));
+    }
+  },
+});
+
+/**
+ * The widget used for displaying DOM event previews.
+ */
+Widgets.ObjectRenderers.add({
+  byKind: "DOMEvent",
+
+  render: function()
+  {
+    let {preview} = this.objectActor;
+
+    let container = this.element = this.el("span.kind-" + preview.kind);
+
+    this._anchor(preview.type || this.objectActor.class,
+                 { className: "cm-variable" });
+
+    if (this.options.concise) {
+      return;
+    }
+
+    if (preview.eventKind == "key" && preview.modifiers &&
+        preview.modifiers.length) {
+      this._text(" ");
+
+      let mods = 0;
+      for (let mod of preview.modifiers) {
+        if (mods > 0) {
+          this._text("-");
+        }
+        container.appendChild(this.el("span.cm-keyword", mod));
+        mods++;
+      }
+    }
+
+    this._text(" { ");
+
+    let shown = 0;
+    if (preview.target) {
+      container.appendChild(this.el("span.cm-property", "target"));
+      this._text(": ");
+      let target = this.message._renderValueGrip(preview.target, { concise: true });
+      container.appendChild(target);
+      shown++;
+    }
+
+    for (let key of Object.keys(preview.properties || {})) {
+      if (shown > 0) {
+        this._text(", ");
+      }
+
+      container.appendChild(this.el("span.cm-property", key));
+      this._text(": ");
+
+      let value = preview.properties[key];
+      let valueElem = this.message._renderValueGrip(value, { concise: true });
+      container.appendChild(valueElem);
+
+      shown++;
+    }
+
+    this._text(" }");
+  },
+}); // Widgets.ObjectRenderers.byKind.DOMEvent
+
+/**
+ * The widget used for displaying DOM node previews.
+ */
+Widgets.ObjectRenderers.add({
+  byKind: "DOMNode",
+
+  canRender: function(objectActor) {
+    let {preview} = objectActor;
+    if (!preview) {
+      return false;
+    }
+
+    switch (preview.nodeType) {
+      case Ci.nsIDOMNode.DOCUMENT_NODE:
+      case Ci.nsIDOMNode.ATTRIBUTE_NODE:
+      case Ci.nsIDOMNode.TEXT_NODE:
+      case Ci.nsIDOMNode.COMMENT_NODE:
+      case Ci.nsIDOMNode.DOCUMENT_FRAGMENT_NODE:
+      case Ci.nsIDOMNode.ELEMENT_NODE:
+        return true;
+      default:
+        return false;
+    }
+  },
+
+  render: function()
+  {
+    switch (this.objectActor.preview.nodeType) {
+      case Ci.nsIDOMNode.DOCUMENT_NODE:
+        this._renderDocumentNode();
+        break;
+      case Ci.nsIDOMNode.ATTRIBUTE_NODE: {
+        let {preview} = this.objectActor;
+        this.element = this.el("span.attributeNode.kind-" + preview.kind);
+        let attr = this._renderAttributeNode(preview.nodeName, preview.value, true);
+        this.element.appendChild(attr);
+        break;
+      }
+      case Ci.nsIDOMNode.TEXT_NODE:
+        this._renderTextNode();
+        break;
+      case Ci.nsIDOMNode.COMMENT_NODE:
+        this._renderCommentNode();
+        break;
+      case Ci.nsIDOMNode.DOCUMENT_FRAGMENT_NODE:
+        this._renderDocumentFragmentNode();
+        break;
+      case Ci.nsIDOMNode.ELEMENT_NODE:
+        this._renderElementNode();
+        break;
+      default:
+        throw new Error("Unsupported nodeType: " + preview.nodeType);
+    }
+  },
+
+  _renderDocumentNode: function()
+  {
+    let fn = Widgets.ObjectRenderers.byKind.ObjectWithURL.prototype._renderElement;
+    this.element = fn.call(this, this.objectActor,
+                           this.objectActor.preview.location);
+    this.element.classList.add("documentNode");
+  },
+
+  _renderAttributeNode: function(nodeName, nodeValue, addLink)
+  {
+    let value = VariablesView.getString(nodeValue, { noStringQuotes: true });
+
+    let fragment = this.document.createDocumentFragment();
+    if (addLink) {
+      this._anchor(nodeName, { className: "cm-attribute", appendTo: fragment });
+    } else {
+      fragment.appendChild(this.el("span.cm-attribute", nodeName));
+    }
+
+    this._text("=", fragment);
+    fragment.appendChild(this.el("span.cm-string", '"' + escapeHTML(value) + '"'));
+
+    return fragment;
+  },
+
+  _renderTextNode: function()
+  {
+    let {preview} = this.objectActor;
+    this.element = this.el("span.textNode.kind-" + preview.kind);
+
+    this._anchor(preview.nodeName, { className: "cm-variable" });
+    this._text(" ");
+
+    let text = VariablesView.getString(preview.textContent);
+    this.element.appendChild(this.el("span.cm-string", text));
+  },
+
+  _renderCommentNode: function()
+  {
+    let {preview} = this.objectActor;
+    let comment = "<!-- " + VariablesView.getString(preview.textContent, {
+      noStringQuotes: true,
+    }) + " -->";
+
+    this.element = this._anchor(comment, {
+      className: "kind-" + preview.kind + " commentNode cm-comment",
+    });
+  },
+
+  _renderDocumentFragmentNode: function()
+  {
+    let {preview} = this.objectActor;
+    let {childNodes} = preview;
+    let container = this.element = this.el("span.documentFragmentNode.kind-" +
+                                           preview.kind);
+
+    this._anchor(this.objectActor.class, { className: "cm-variable" });
+
+    if (!childNodes || this.options.concise) {
+      this._text("[");
+      container.appendChild(this.el("span.cm-number", preview.childNodesLength));
+      this._text("]");
+      return;
+    }
+
+    this._text(" [ ");
+
+    let shown = 0;
+    for (let item of childNodes) {
+      if (shown > 0) {
+        this._text(", ");
+      }
+
+      let elem = this.message._renderValueGrip(item, { concise: true });
+      container.appendChild(elem);
+      shown++;
+    }
+
+    if (shown < preview.childNodesLength) {
+      this._text(", ");
+
+      let n = preview.childNodesLength - shown;
+      let str = VariablesView.stringifiers._getNMoreString(n);
+      this._anchor(str);
+    }
+
+    this._text(" ]");
+  },
+
+  _renderElementNode: function()
+  {
+    let doc = this.document;
+    let {attributes, nodeName} = this.objectActor.preview;
+
+    this.element = this.el("span." + "kind-" + this.objectActor.preview.kind + ".elementNode");
+
+    let openTag = this.el("span.cm-tag");
+    openTag.textContent = "<";
+    this.element.appendChild(openTag);
+
+    let tagName = this._anchor(nodeName, {
+      className: "cm-tag",
+      appendTo: openTag
+    });
+
+    if (this.options.concise) {
+      if (attributes.id) {
+        tagName.appendChild(this.el("span.cm-attribute", "#" + attributes.id));
+      }
+      if (attributes.class) {
+        tagName.appendChild(this.el("span.cm-attribute", "." + attributes.class.split(" ").join(".")));
+      }
+    } else {
+      for (let name of Object.keys(attributes)) {
+        let attr = this._renderAttributeNode(" " + name, attributes[name]);
+        this.element.appendChild(attr);
+      }
+    }
+
+    let closeTag = this.el("span.cm-tag");
+    closeTag.textContent = ">";
+    this.element.appendChild(closeTag);
+
+    // Register this widget in the owner message so that it gets destroyed when
+    // the message is destroyed.
+    this.message.widgets.add(this);
+
+    this.linkToInspector();
+  },
+
+  /**
+   * If the DOMNode being rendered can be highlit in the page, this function
+   * will attach mouseover/out event listeners to do so, and the inspector icon
+   * to open the node in the inspector.
+   * @return a promise (always the same) that resolves when the node has been
+   * linked to the inspector, or rejects if it wasn't (either if no toolbox
+   * could be found to access the inspector, or if the node isn't present in the
+   * inspector, i.e. if the node is in a DocumentFragment or not part of the
+   * tree, or not of type Ci.nsIDOMNode.ELEMENT_NODE).
+   */
+  linkToInspector: function()
+  {
+    if (this._linkedToInspector) {
+      return this._linkedToInspector;
+    }
+
+    this._linkedToInspector = Task.spawn(function*() {
+      // Checking the node type
+      if (this.objectActor.preview.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE) {
+        throw null;
+      }
+
+      // Checking the presence of a toolbox
+      let target = this.message.output.toolboxTarget;
+      this.toolbox = gDevTools.getToolbox(target);
+      if (!this.toolbox) {
+        throw null;
+      }
+
+      // Checking that the inspector supports the node
+      yield this.toolbox.initInspector();
+      this._nodeFront = yield this.toolbox.walker.getNodeActorFromObjectActor(this.objectActor.actor);
+      if (!this._nodeFront) {
+        throw null;
+      }
+
+      // At this stage, the message may have been cleared already
+      if (!this.document) {
+        throw null;
+      }
+
+      this.highlightDomNode = this.highlightDomNode.bind(this);
+      this.element.addEventListener("mouseover", this.highlightDomNode, false);
+      this.unhighlightDomNode = this.unhighlightDomNode.bind(this);
+      this.element.addEventListener("mouseout", this.unhighlightDomNode, false);
+
+      this._openInspectorNode = this._anchor("", {
+        className: "open-inspector",
+        onClick: this.openNodeInInspector.bind(this)
+      });
+      this._openInspectorNode.title = l10n.getStr("openNodeInInspector");
+    }.bind(this));
+
+    return this._linkedToInspector;
+  },
+
+  /**
+   * Highlight the DOMNode corresponding to the ObjectActor in the page.
+   * @return a promise that resolves when the node has been highlighted, or
+   * rejects if the node cannot be highlighted (detached from the DOM)
+   */
+  highlightDomNode: function()
+  {
+    return Task.spawn(function*() {
+      yield this.linkToInspector();
+      let isAttached = yield this.toolbox.walker.isInDOMTree(this._nodeFront);
+      if (isAttached) {
+        yield this.toolbox.highlighterUtils.highlightNodeFront(this._nodeFront);
+      } else {
+        throw null;
+      }
+    }.bind(this));
+  },
+
+  /**
+   * Unhighlight a previously highlit node
+   * @see highlightDomNode
+   * @return a promise that resolves when the highlighter has been hidden
+   */
+  unhighlightDomNode: function()
+  {
+    return this.linkToInspector().then(() => {
+      return this.toolbox.highlighterUtils.unhighlight();
+    });
+  },
+
+  /**
+   * Open the DOMNode corresponding to the ObjectActor in the inspector panel
+   * @return a promise that resolves when the inspector has been switched to
+   * and the node has been selected, or rejects if the node cannot be selected
+   * (detached from the DOM). Note that in any case, the inspector panel will
+   * be switched to.
+   */
+  openNodeInInspector: function()
+  {
+    return Task.spawn(function*() {
+      yield this.linkToInspector();
+      yield this.toolbox.selectTool("inspector");
+
+      let isAttached = yield this.toolbox.walker.isInDOMTree(this._nodeFront);
+      if (isAttached) {
+        let onReady = this.toolbox.inspector.once("inspector-updated");
+        yield this.toolbox.selection.setNodeFront(this._nodeFront, "console");
+        yield onReady;
+      } else {
+        throw null;
+      }
+    }.bind(this));
+  },
+
+  destroy: function()
+  {
+    if (this.toolbox && this._nodeFront) {
+      this.element.removeEventListener("mouseover", this.highlightDomNode, false);
+      this.element.removeEventListener("mouseout", this.unhighlightDomNode, false);
+      this._openInspectorNode.removeEventListener("mousedown", this.openNodeInInspector, true);
+      this.toolbox = null;
+      this._nodeFront = null;
+    }
+  },
+}); // Widgets.ObjectRenderers.byKind.DOMNode
+
+/**
+ * The widget used for displaying generic JS object previews.
+ */
+Widgets.ObjectRenderers.add({
+  byKind: "Object",
+
+  render: function()
+  {
+    let {preview} = this.objectActor;
+    let {ownProperties, safeGetterValues} = preview;
+
+    if ((!ownProperties && !safeGetterValues) || this.options.concise) {
+      this.element = this._anchor(this.objectActor.class,
+                                  { className: "cm-variable" });
+      return;
+    }
+
+    let container = this.element = this.el("span.kind-" + preview.kind);
+    this._anchor(this.objectActor.class, { className: "cm-variable" });
+    this._text(" { ");
+
+    let addProperty = (str) => {
+      container.appendChild(this.el("span.cm-property", str));
+    };
+
+    let shown = 0;
+    for (let key of Object.keys(ownProperties || {})) {
+      if (shown > 0) {
+        this._text(", ");
+      }
+
+      let value = ownProperties[key];
+
+      addProperty(key);
+      this._text(": ");
+
+      if (value.get) {
+        addProperty("Getter");
+      } else if (value.set) {
+        addProperty("Setter");
+      } else {
+        let valueElem = this.message._renderValueGrip(value.value, { concise: true });
+        container.appendChild(valueElem);
+      }
+
+      shown++;
+    }
+
+    let ownPropertiesShown = shown;
+
+    for (let key of Object.keys(safeGetterValues || {})) {
+      if (shown > 0) {
+        this._text(", ");
+      }
+
+      addProperty(key);
+      this._text(": ");
+
+      let value = safeGetterValues[key].getterValue;
+      let valueElem = this.message._renderValueGrip(value, { concise: true });
+      container.appendChild(valueElem);
+
+      shown++;
+    }
+
+    if (typeof preview.ownPropertiesLength == "number" &&
+        ownPropertiesShown < preview.ownPropertiesLength) {
+      this._text(", ");
+
+      let n = preview.ownPropertiesLength - ownPropertiesShown;
+      let str = VariablesView.stringifiers._getNMoreString(n);
+      this._anchor(str);
+    }
+
+    this._text(" }");
+  },
+}); // Widgets.ObjectRenderers.byKind.Object
+
 /**
  * The long string widget.
  *
  * @constructor
  * @param object message
  *        The owning message.
  * @param object longStringActor
  *        The LongStringActor to display.
@@ -1413,17 +2429,17 @@ Widgets.LongString.prototype = Heritage.
 
   render: function()
   {
     if (this.element) {
       return this;
     }
 
     let result = this.element = this.document.createElementNS(XHTML_NS, "span");
-    result.className = "longString";
+    result.className = "longString cm-string";
     this._renderString(this.longStringActor.initial);
     result.appendChild(this._renderEllipsis());
 
     return this;
   },
 
   /**
    * Render the long string in the widget element.
--- a/browser/devtools/webconsole/test/browser.ini
+++ b/browser/devtools/webconsole/test/browser.ini
@@ -64,16 +64,17 @@ support-files =
   test-console-count.html
   test-console-count-external-file.js
   test-console-extras.html
   test-console-replaced-api.html
   test-console.html
   test-console-output-02.html
   test-console-output-03.html
   test-console-output-04.html
+  test-console-output-dom-elements.html
   test-console-output-events.html
   test-consoleiframes.html
   test-data.json
   test-data.json^headers^
   test-duplicate-error.html
   test-encoding-ISO-8859-1.html
   test-error.html
   test-eval-in-stackframe.html
@@ -261,13 +262,17 @@ run-if = os == "mac"
 [browser_webconsole_expandable_timestamps.js]
 [browser_webconsole_autocomplete_in_debugger_stackframe.js]
 [browser_webconsole_autocomplete_popup_close_on_tab_switch.js]
 [browser_console_hide_jsterm_when_devtools_chrome_enabled_false.js]
 [browser_webconsole_output_01.js]
 [browser_webconsole_output_02.js]
 [browser_webconsole_output_03.js]
 [browser_webconsole_output_04.js]
+[browser_webconsole_output_dom_elements_01.js]
+[browser_webconsole_output_dom_elements_02.js]
+[browser_webconsole_output_dom_elements_03.js]
+[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]
--- 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,19 +18,20 @@ 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");
     ok(anchor, "object anchor");
-    isnot(anchor.textContent.indexOf('testProp: "testValue"'), -1,
-          "message text check");
+    ok(body, "message body");
+    ok(body.textContent.contains('testProp: "testValue"'), "message text check");
 
     msg.scrollIntoView();
     executeSoon(() => {
       EventUtils.synthesizeMouse(anchor, 2, 2, {}, hud.iframeWindow);
     });
 
     let vviewVar = yield jsterm.once("variablesview-fetched");
     let vview = vviewVar._variablesView;
@@ -53,20 +54,22 @@ function test()
     });
     yield jsterm.once("sidebar-closed");
 
     jsterm.clearOutput();
 
     msg = yield execute("window");
     ok(msg, "output message found");
 
-    let anchor = msg.querySelector("a");
+    body = msg.querySelector(".body");
+    ok(body, "message body");
+    anchor = msg.querySelector("a");
     ok(anchor, "object anchor");
-    isnot(anchor.textContent.indexOf("Window \u2192 http://example.com/browser/"), -1,
-          "message text check");
+    ok(body.textContent.contains("Window \u2192 http://example.com/browser/"),
+       "message text check");
 
     msg.scrollIntoView();
     executeSoon(() => {
       EventUtils.synthesizeMouse(anchor, 2, 2, {}, hud.iframeWindow)
     });
     vviewVar = yield jsterm.once("variablesview-fetched");
 
     vview = vviewVar._variablesView;
--- 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
@@ -39,20 +39,25 @@ function consoleOpened(hud)
       severity: SEVERITY_LOG,
       objects: true,
     }],
   }).then(onConsoleMessage);
 }
 
 function onConsoleMessage(aResults)
 {
+  let msg = [...aResults[0].matched][0];
+  ok(msg, "message element");
+
+  let body = msg.querySelector(".body");
+  ok(body, "message body");
+
   let clickable = aResults[0].clickableElements[0];
   ok(clickable, "clickable object found");
-  isnot(clickable.textContent.indexOf('{hello: "world!",'), -1,
-        "message text check");
+  ok(body.textContent.contains('{ hello: "world!",'), "message text check");
 
   gJSTerm.once("variablesview-fetched", onObjFetch);
 
   EventUtils.synthesizeMouse(clickable, 2, 2, {}, gWebConsole.iframeWindow)
 }
 
 function onObjFetch(aEvent, aVar)
 {
--- a/browser/devtools/webconsole/test/browser_console_consolejsm_output.js
+++ b/browser/devtools/webconsole/test/browser_console_consolejsm_output.js
@@ -66,17 +66,17 @@ function test()
         {
           name: "console.warn output",
           text: "bug851231-warn",
           category: CATEGORY_WEBDEV,
           severity: SEVERITY_WARNING,
         },
         {
           name: "console.error output",
-          text: /\bbug851231-error\b.+\{bug851231prop:\s"bug851231value"\}/,
+          text: /\bbug851231-error\b.+\{\s*bug851231prop:\s"bug851231value"\s*\}/,
           category: CATEGORY_WEBDEV,
           severity: SEVERITY_ERROR,
           objects: true,
         },
         {
           name: "console.debug output",
           text: "bug851231-debug",
           category: CATEGORY_WEBDEV,
@@ -86,17 +86,17 @@ function test()
           name: "console.trace output",
           consoleTrace: {
             file: "browser_console_consolejsm_output.js",
             fn: "onCachedMessage",
           },
         },
         {
           name: "console.dir output",
-          consoleDir: /XULDocument .+ chrome:\/\/.+\/browser\.xul/,
+          consoleDir: /XULDocument\s+.+\s+chrome:\/\/.+\/browser\.xul/,
         },
         {
           name: "console.time output",
           consoleTime: "foobarTimer",
         },
         {
           name: "console.timeEnd output",
           consoleTimeEnd: "foobarTimer",
--- a/browser/devtools/webconsole/test/browser_console_hide_jsterm_when_devtools_chrome_enabled_false.js
+++ b/browser/devtools/webconsole/test/browser_console_hide_jsterm_when_devtools_chrome_enabled_false.js
@@ -31,17 +31,18 @@ function* getVariablesView(hud) {
 
   let deferred = promise.defer();
   hud.jsterm.clearOutput();
   hud.jsterm.execute('new Object()');
 
   let [message] = yield waitForMessages({
     webconsole: hud,
     messages: [{
-      text: "object"
+      text: "Object",
+      category: CATEGORY_OUTPUT,
     }],
   })
 
   hud.jsterm.once("variablesview-fetched", openVariablesView);
 
   let anchor = [...message.matched][0].querySelector("a");
 
   executeSoon(() =>
--- a/browser/devtools/webconsole/test/browser_console_log_inspectable_object.js
+++ b/browser/devtools/webconsole/test/browser_console_log_inspectable_object.js
@@ -27,20 +27,24 @@ function performTest(hud)
     webconsole: hud,
     messages: [{
       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");
+    ok(body, "message body");
     let clickable = result.clickableElements[0];
     ok(clickable, "the console.log() object anchor was found");
-    isnot(clickable.textContent.indexOf('{abba: "omgBug676722"}'), -1,
-          "clickable node content is correct");
+    ok(body.textContent.contains('{ abba: "omgBug676722" }'),
+       "clickable node content is correct");
 
     hud.jsterm.once("variablesview-fetched",
       (aEvent, aVar) => {
         ok(aVar, "object inspector opened on click");
 
         findVariableViewProperties(aVar, [{
           name: "abba",
           value: "omgBug676722",
--- a/browser/devtools/webconsole/test/browser_console_variables_view.js
+++ b/browser/devtools/webconsole/test/browser_console_variables_view.js
@@ -23,18 +23,17 @@ function consoleOpened(hud)
   gWebConsole = hud;
   gJSTerm = hud.jsterm;
   gJSTerm.execute("fooObj", onExecuteFooObj);
 }
 
 function onExecuteFooObj(msg)
 {
   ok(msg, "output message found");
-  isnot(msg.textContent.indexOf('{testProp: "testValue"}'), -1,
-        "message text check");
+  ok(msg.textContent.contains('{ testProp: "testValue" }'), "message text check");
 
   let anchor = msg.querySelector("a");
   ok(anchor, "object link found");
 
   gJSTerm.once("variablesview-fetched", onFooObjFetch);
 
   executeSoon(() =>
     EventUtils.synthesizeMouse(anchor, 2, 2, {}, gWebConsole.iframeWindow)
--- a/browser/devtools/webconsole/test/browser_console_variables_view_while_debugging.js
+++ b/browser/devtools/webconsole/test/browser_console_variables_view_while_debugging.js
@@ -57,18 +57,17 @@ function onFramesAdded()
     )
   );
 }
 
 
 function onExecuteFooObj(msg)
 {
   ok(msg, "output message found");
-  isnot(msg.textContent.indexOf('{testProp2: "testValue2"}'), -1,
-        "message text check");
+  ok(msg.textContent.contains('{ testProp2: "testValue2" }'), "message text check");
 
   let anchor = msg.querySelector("a");
   ok(anchor, "object link found");
 
   gJSTerm.once("variablesview-fetched", onFooObjFetch);
 
   executeSoon(() => EventUtils.synthesizeMouse(anchor, 2, 2, {},
                                                gWebConsole.iframeWindow));
--- a/browser/devtools/webconsole/test/browser_console_variables_view_while_debugging_and_inspecting.js
+++ b/browser/devtools/webconsole/test/browser_console_variables_view_while_debugging_and_inspecting.js
@@ -52,18 +52,18 @@ function onFramesAdded()
   info("onFramesAdded");
 
   openConsole(null, () => gJSTerm.execute("fooObj", onExecuteFooObj));
 }
 
 function onExecuteFooObj(msg)
 {
   ok(msg, "output message found");
-  isnot(msg.textContent.indexOf('{testProp2: "testValue2"}'), -1,
-        "message text check");
+  ok(msg.textContent.contains('{ testProp2: "testValue2" }'),
+     "message text check");
 
   let anchor = msg.querySelector("a");
   ok(anchor, "object link found");
 
   gJSTerm.once("variablesview-fetched", onFooObjFetch);
 
   EventUtils.synthesizeMouse(anchor, 2, 2, {}, gWebConsole.iframeWindow);
 }
--- a/browser/devtools/webconsole/test/browser_webconsole_output_01.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_output_01.js
@@ -100,37 +100,51 @@ let inputTests = [
   // 12
   {
     input: "true",
     output: "true",
   },
 
   // 13
   {
-    input: "false",
+    input: "new Boolean(false)",
     output: "false",
+    inspectable: true,
   },
 
-
   // 14
   {
     input: "new Date(" + dateNow + ")",
     output: "Date " + (new Date(dateNow)).toISOString(),
     printOutput: (new Date(dateNow)).toString(),
     inspectable: true,
   },
 
   // 15
   {
     input: "new Date('test')",
     output: "Invalid Date",
     printOutput: "Invalid Date",
     inspectable: true,
     variablesViewLabel: "Invalid Date",
   },
+
+  // 16
+  {
+    input: "new Number(43)",
+    output: "43",
+    inspectable: true,
+  },
+
+  // 17
+  {
+    input: "new String('hello world')",
+    output: '"hello world"',
+    inspectable: true,
+  },
 ];
 
 longString = initialString = null;
 
 function test() {
   registerCleanupFunction(() => {
     DebuggerServer.LONG_STRING_LENGTH = LONG_STRING_LENGTH;
     DebuggerServer.LONG_STRING_INITIAL_LENGTH = LONG_STRING_INITIAL_LENGTH;
--- a/browser/devtools/webconsole/test/browser_webconsole_output_02.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_output_02.js
@@ -50,104 +50,104 @@ let inputTests = [
     printOutput: "function testfn3() { return 42; }",
     inspectable: true,
     variablesViewLabel: "testfn3DisplayName()",
   },
 
   // 5 - basic array
   {
     input: "window.array1",
-    output: '[1, 2, 3, "a", "b", "c", "4", "5"]',
+    output: 'Array [ 1, 2, 3, "a", "b", "c", "4", "5" ]',
     printOutput: "1,2,3,a,b,c,4,5",
     inspectable: true,
     variablesViewLabel: "Array[8]",
   },
 
   // 6 - array with objects
   {
     input: "window.array2",
-    output: '["a", HTMLDocument \u2192 test-console-output-02.html, <body>, ' +
-            "DOMStringMap[0], DOMTokenList[0]]",
+    output: 'Array [ "a", HTMLDocument \u2192 test-console-output-02.html, <body>, ' +
+            "DOMStringMap[0], DOMTokenList[0] ]",
     printOutput: '"a,[object HTMLDocument],[object HTMLBodyElement],' +
                  '[object DOMStringMap],"',
     inspectable: true,
     variablesViewLabel: "Array[5]",
   },
 
   // 7 - array with more than 10 elements
   {
     input: "window.array3",
-    output: '[1, Window \u2192 test-console-output-02.html, null, "a", "b", ' +
-            'undefined, false, "", -Infinity, testfn3DisplayName(), 3 more\u2026]',
+    output: 'Array [ 1, Window \u2192 test-console-output-02.html, null, "a", "b", ' +
+            'undefined, false, "", -Infinity, testfn3DisplayName(), 3 more\u2026 ]',
     printOutput: '"1,[object Window],,a,b,,false,,-Infinity,' +
                  'function testfn3() { return 42; },[object Object],foo,bar"',
     inspectable: true,
     variablesViewLabel: "Array[13]",
   },
 
   // 8 - array with holes and a cyclic reference
   {
     input: "window.array4",
-    output: '[,,,,, "test", Array[7]]',
+    output: 'Array [ , , , , , "test", Array[7] ]',
     printOutput: '",,,,,test,"',
     inspectable: true,
     variablesViewLabel: "Array[7]",
   },
 
   // 9
   {
     input: "window.typedarray1",
-    output: 'Int32Array [1, 287, 8651, 40983, 8754]',
+    output: 'Int32Array [ 1, 287, 8651, 40983, 8754 ]',
     printOutput: "[object Int32Array]",
     inspectable: true,
     variablesViewLabel: "Int32Array[5]",
   },
 
   // 10 - Set with cyclic reference
   {
     input: "window.set1",
-    output: 'Set [1, 2, null, Array[13], "a", "b", undefined, <head>, Set[9]]',
+    output: 'Set [ 1, 2, null, Array[13], "a", "b", undefined, <head>, Set[9] ]',
     printOutput: "[object Set]",
     inspectable: true,
     variablesViewLabel: "Set[9]",
   },
 
   // 11 - Object with cyclic reference and a getter
   {
     input: "window.testobj2",
-    output: '{a: "b", c: "d", e: 1, f: "2", foo: Object, bar: Object, ' +
-            "getterTest: Getter}",
+    output: 'Object { a: "b", c: "d", e: 1, f: "2", foo: Object, bar: Object, ' +
+            "getterTest: Getter }",
     printOutput: "[object Object]",
     inspectable: true,
     variablesViewLabel: "Object",
   },
 
   // 12 - Object with more than 10 properties
   {
     input: "window.testobj3",
-    output: '{a: "b", c: "d", e: 1, f: "2", g: true, h: null, i: undefined, ' +
-            'j: "", k: StyleSheetList[0], l: NodeList[5], 2 more\u2026}',
+    output: 'Object { a: "b", c: "d", e: 1, f: "2", g: true, h: null, i: undefined, ' +
+            'j: "", k: StyleSheetList[0], l: NodeList[5], 2 more\u2026 }',
     printOutput: "[object Object]",
     inspectable: true,
     variablesViewLabel: "Object",
   },
 
   // 13 - Object with a non-enumerable property that we do not show
   {
     input: "window.testobj4",
-    output: '{a: "b", c: "d", 1 more\u2026}',
+    output: 'Object { a: "b", c: "d", 1 more\u2026 }',
     printOutput: "[object Object]",
     inspectable: true,
     variablesViewLabel: "Object",
   },
 
   // 14 - Map with cyclic references
   {
     input: "window.map1",
-    output: 'Map {a: "b", HTMLCollection[2]: Object, Map[3]: Set[9]}',
+    output: 'Map { a: "b", HTMLCollection[2]: Object, Map[3]: Set[9] }',
     printOutput: "[object Map]",
     inspectable: true,
     variablesViewLabel: "Map[3]",
   },
 ];
 
 function test() {
 
--- a/browser/devtools/webconsole/test/browser_webconsole_output_03.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_output_03.js
@@ -33,26 +33,26 @@ let inputTests = [
     printOutput: "[object HTMLBodyElement]",
     inspectable: true,
     noClick: true,
   },
 
   // 3
   {
     input: "document.body.dataset",
-    output: "DOMStringMap {}",
+    output: "DOMStringMap {  }",
     printOutput: "[object DOMStringMap]",
     inspectable: true,
     variablesViewLabel: "DOMStringMap[0]",
   },
 
   // 4
   {
     input: "document.body.classList",
-    output: "DOMTokenList []",
+    output: "DOMTokenList [  ]",
     printOutput: '""',
     inspectable: true,
     variablesViewLabel: "DOMTokenList[0]",
   },
 
   // 5
   {
     input: "window.location.href",
@@ -66,26 +66,26 @@ let inputTests = [
     printOutput: TEST_URI,
     inspectable: true,
     variablesViewLabel: "Location \u2192 test-console-output-03.html",
   },
 
   // 7
   {
     input: "document.body.attributes",
-    output: "MozNamedAttrMap []",
+    output: "MozNamedAttrMap [  ]",
     printOutput: "[object MozNamedAttrMap]",
     inspectable: true,
     variablesViewLabel: "MozNamedAttrMap[0]",
   },
 
   // 8
   {
     input: "document.styleSheets",
-    output: "StyleSheetList []",
+    output: "StyleSheetList [  ]",
     printOutput: "[object StyleSheetList",
     inspectable: true,
     variablesViewLabel: "StyleSheetList[0]",
   },
 
   // 9
   {
     input: "testBodyClassName()",
@@ -102,17 +102,17 @@ let inputTests = [
     printOutput: "[object HTMLBodyElement]",
     inspectable: true,
     noClick: true,
   },
 
   // 11
   {
     input: "document.body.classList",
-    output: 'DOMTokenList ["test1", "tezt2"]',
+    output: 'DOMTokenList [ "test1", "tezt2" ]',
     printOutput: '"test1 tezt2"',
     inspectable: true,
     variablesViewLabel: "DOMTokenList[2]",
   },
 
   // 12
   {
     input: "testBodyDataset()",
@@ -121,27 +121,27 @@ let inputTests = [
     printOutput: "[object HTMLBodyElement]",
     inspectable: true,
     noClick: true,
   },
 
   // 13
   {
     input: "document.body.dataset",
-    output: 'DOMStringMap {preview: "zuzu"<a>foo"}',
+    output: 'DOMStringMap { preview: "zuzu"<a>foo" }',
     printOutput: "[object DOMStringMap]",
     inspectable: true,
     variablesViewLabel: "DOMStringMap[1]",
   },
 
   // 14
   {
     input: "document.body.attributes",
-    output: 'MozNamedAttrMap [class="test1 tezt2", id="foobarid", ' +
-            'data-preview="zuzu&quot;&lt;a&gt;foo"]',
+    output: 'MozNamedAttrMap [ class="test1 tezt2", id="foobarid", ' +
+            'data-preview="zuzu&quot;&lt;a&gt;foo" ]',
     printOutput: "[object MozNamedAttrMap]",
     inspectable: true,
     variablesViewLabel: "MozNamedAttrMap[3]",
   },
 
   // 15
   {
     input: "document.body.attributes[0]",
--- a/browser/devtools/webconsole/test/browser_webconsole_output_04.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_output_04.js
@@ -15,26 +15,26 @@ let inputTests = [
     printOutput: "[object Text]",
     inspectable: true,
     noClick: true,
   },
 
   // 1
   {
     input: "testCommentNode()",
-    output: "<!--\n  - Any copyright ",
+    output: /<!--\s+- Any copyright /,
     printOutput: "[object Comment]",
     inspectable: true,
     noClick: true,
   },
 
   // 2
   {
     input: "testDocumentFragment()",
-    output: 'DocumentFragment [<div id="foo1" class="bar">, <div id="foo3">]',
+    output: 'DocumentFragment [ <div#foo1.bar>, <div#foo3> ]',
     printOutput: "[object DocumentFragment]",
     inspectable: true,
     variablesViewLabel: "DocumentFragment[2]",
   },
 
   // 3
   {
     input: "testError()",
@@ -53,43 +53,43 @@ let inputTests = [
     printOutput: '[Exception... "An invalid or illegal string was specified"',
     inspectable: true,
     variablesViewLabel: "SyntaxError",
   },
 
   // 5
   {
     input: "testCSSStyleDeclaration()",
-    output: 'CSS2Properties {color: "green", font-size: "2em"}',
+    output: 'CSS2Properties { color: "green", font-size: "2em" }',
     printOutput: "[object CSS2Properties]",
     inspectable: true,
     noClick: true,
   },
 
   // 6
   {
     input: "testStyleSheetList()",
-    output: "StyleSheetList [CSSStyleSheet]",
+    output: "StyleSheetList [ CSSStyleSheet ]",
     printOutput: "[object StyleSheetList",
     inspectable: true,
     variablesViewLabel: "StyleSheetList[1]",
   },
 
   // 7
   {
     input: "document.styleSheets[0]",
     output: "CSSStyleSheet",
     printOutput: "[object CSSStyleSheet]",
     inspectable: true,
   },
 
   // 8
   {
     input: "document.styleSheets[0].cssRules",
-    output: "CSSRuleList [CSSStyleRule, CSSMediaRule]",
+    output: "CSSRuleList [ CSSStyleRule, CSSMediaRule ]",
     printOutput: "[object CSSRuleList",
     inspectable: true,
     variablesViewLabel: "CSSRuleList[2]",
   },
 
   // 9
   {
     input: "document.styleSheets[0].cssRules[0]",
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_webconsole_output_dom_elements_01.js
@@ -0,0 +1,99 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test the webconsole output for various types of DOM Nodes.
+
+const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console-output-dom-elements.html";
+
+let inputTests = [
+  {
+    input: "testBodyNode()",
+    output: '<body id="body-id" class="body-class">',
+    printOutput: "[object HTMLBodyElement]",
+    inspectable: true,
+    noClick: true,
+    inspectorIcon: true
+  },
+
+  {
+    input: "testDocumentElement()",
+    output: '<html lang="en-US" dir="ltr">',
+    printOutput: "[object HTMLHtmlElement]",
+    inspectable: true,
+    noClick: true,
+    inspectorIcon: true
+  },
+
+  {
+    input: "testDocument()",
+    output: 'HTMLDocument \u2192 ' + TEST_URI,
+    printOutput: "[object HTMLDocument]",
+    inspectable: true,
+    noClick: true,
+    inspectorIcon: false
+  },
+
+  {
+    input: "testNode()",
+    output: '<p some-attribute="some-value">',
+    printOutput: "[object HTMLParagraphElement]",
+    inspectable: true,
+    noClick: true,
+    inspectorIcon: true
+  },
+
+  {
+    input: "testNodeList()",
+    output: 'NodeList [ <html>, <head>, <meta>, <title>, <body#body-id.body-class>, <p>, <iframe>, <script> ]',
+    printOutput: "[object NodeList]",
+    inspectable: true,
+    noClick: true,
+    inspectorIcon: true
+  },
+
+  {
+    input: "testNodeInIframe()",
+    output: '<p>',
+    printOutput: "[object HTMLParagraphElement]",
+    inspectable: true,
+    noClick: true,
+    inspectorIcon: true
+  },
+
+  {
+    input: "testDocumentFragment()",
+    output: 'DocumentFragment [ <span.foo>, <div#fragdiv> ]',
+    printOutput: "[object DocumentFragment]",
+    inspectable: true,
+    noClick: true,
+    inspectorIcon: false
+  },
+
+  {
+    input: "testNodeInDocumentFragment()",
+    output: '<span class="foo" data-lolz="hehe">',
+    printOutput: "[object HTMLSpanElement]",
+    inspectable: true,
+    noClick: true,
+    inspectorIcon: false
+  },
+
+  {
+    input: "testUnattachedNode()",
+    output: '<p class="such-class" data-data="such-data">',
+    printOutput: "[object HTMLParagraphElement]",
+    inspectable: true,
+    noClick: true,
+    inspectorIcon: false
+  }
+];
+
+function test() {
+  Task.spawn(function*() {
+    let {tab} = yield loadTab(TEST_URI);
+    let hud = yield openConsole(tab);
+    yield checkOutputForInputs(hud, inputTests);
+  }).then(finishTest);
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_webconsole_output_dom_elements_02.js
@@ -0,0 +1,95 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test the inspector links in the webconsole output for DOM Nodes actually
+// open the inspector and select the right node
+
+const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console-output-dom-elements.html";
+
+const TEST_DATA = [
+  {
+    // The first test shouldn't be returning the body element as this is the
+    // default selected node, so re-selecting it won't fire the inspector-updated
+    // event
+    input: "testNode()",
+    output: '<p some-attribute="some-value">'
+  },
+  {
+    input: "testBodyNode()",
+    output: '<body id="body-id" class="body-class">'
+  },
+  {
+    input: "testNodeInIframe()",
+    output: '<p>'
+  },
+  {
+    input: "testDocumentElement()",
+    output: '<html lang="en-US" dir="ltr">'
+  }
+];
+
+function test() {
+  Task.spawn(function*() {
+    let {tab} = yield loadTab(TEST_URI);
+    let hud = yield openConsole(tab);
+    let toolbox = gDevTools.getToolbox(hud.target);
+
+    // Loading the inspector panel at first, to make it possible to listen for
+    // new node selections
+    yield toolbox.loadTool("inspector");
+    let inspector = toolbox.getPanel("inspector");
+
+    info("Iterating over the test data");
+    for (let data of TEST_DATA) {
+      let [result] = yield jsEval(data.input, hud, {text: data.output});
+      let {widget, msg} = yield getWidgetAndMessage(result);
+
+      let inspectorIcon = msg.querySelector(".open-inspector");
+      ok(inspectorIcon, "Inspector icon found in the ElementNode widget");
+
+      info("Clicking on the inspector icon and waiting for the inspector to be selected");
+      let onInspectorSelected = toolbox.once("inspector-selected");
+      let onInspectorUpdated = inspector.once("inspector-updated");
+
+      EventUtils.synthesizeMouseAtCenter(inspectorIcon, {},
+        inspectorIcon.ownerDocument.defaultView);
+      yield onInspectorSelected;
+      yield onInspectorUpdated;
+      ok(true, "Inspector selected and new node got selected");
+
+      let rawNode = content.wrappedJSObject[data.input.replace(/\(\)/g, "")]();
+      is(rawNode, inspector.selection.node.wrappedJSObject,
+        "The current inspector selection is correct");
+
+      info("Switching back to the console");
+      yield toolbox.selectTool("webconsole");
+    }
+  }).then(finishTest);
+}
+
+function jsEval(input, hud, message) {
+  info("Executing '" + input + "' in the web console");
+
+  hud.jsterm.clearOutput();
+  hud.jsterm.execute(input);
+
+  return waitForMessages({
+    webconsole: hud,
+    messages: [message]
+  });
+}
+
+function* getWidgetAndMessage(result) {
+  info("Getting the output ElementNode widget");
+
+  let msg = [...result.matched][0];
+  let widget = [...msg._messageObject.widgets][0];
+  ok(widget, "ElementNode widget found in the output");
+
+  info("Waiting for the ElementNode widget to be linked to the inspector");
+  yield widget.linkToInspector();
+
+  return {widget: widget, msg: msg};
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_webconsole_output_dom_elements_03.js
@@ -0,0 +1,67 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test that inspector links in webconsole outputs for DOM Nodes highlight
+// the actual DOM Nodes on hover
+
+const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console-output-dom-elements.html";
+
+function test() {
+  Task.spawn(function*() {
+    let {tab} = yield loadTab(TEST_URI);
+    let hud = yield openConsole(tab);
+    let toolbox = gDevTools.getToolbox(hud.target);
+
+    // Loading the inspector panel at first, to make it possible to listen for
+    // new node selections
+    yield toolbox.loadTool("inspector");
+    let inspector = toolbox.getPanel("inspector");
+
+    info("Executing 'testNode()' in the web console to output a DOM Node");
+    let [result] = yield jsEval("testNode()", hud, {
+      text: '<p some-attribute="some-value">'
+    });
+
+    let elementNodeWidget = yield getWidget(result);
+
+    let nodeFront = yield hoverOverWidget(elementNodeWidget, toolbox);
+    let attrs = nodeFront.attributes;
+    is(nodeFront.tagName, "P", "The correct node was highlighted");
+    is(attrs[0].name, "some-attribute", "The correct node was highlighted");
+    is(attrs[0].value, "some-value", "The correct node was highlighted");
+  }).then(finishTest);
+}
+
+function jsEval(input, hud, message) {
+  hud.jsterm.execute(input);
+  return waitForMessages({
+    webconsole: hud,
+    messages: [message]
+  });
+}
+
+function* getWidget(result) {
+  info("Getting the output ElementNode widget");
+
+  let msg = [...result.matched][0];
+  let elementNodeWidget = [...msg._messageObject.widgets][0];
+  ok(elementNodeWidget, "ElementNode widget found in the output");
+
+  info("Waiting for the ElementNode widget to be linked to the inspector");
+  yield elementNodeWidget.linkToInspector();
+
+  return elementNodeWidget;
+}
+
+function* hoverOverWidget(widget, toolbox) {
+  info("Hovering over the output to highlight the node");
+
+  let onHighlight = toolbox.once("node-highlight");
+  EventUtils.sendMouseEvent({type: "mouseover"}, widget.element,
+    widget.element.ownerDocument.defaultView);
+  let nodeFront = yield onHighlight;
+  ok(true, "The highlighter was shown on a node");
+  return nodeFront;
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_webconsole_output_dom_elements_04.js
@@ -0,0 +1,106 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test that inspector links in the webconsole output for DOM Nodes do not try
+// to highlight or select nodes once they have been detached
+
+const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console-output-dom-elements.html";
+
+const TEST_DATA = [
+  {
+    // The first test shouldn't be returning the body element as this is the
+    // default selected node, so re-selecting it won't fire the inspector-updated
+    // event
+    input: "testNode()",
+    output: '<p some-attribute="some-value">'
+  },
+  {
+    input: "testBodyNode()",
+    output: '<body id="body-id" class="body-class">'
+  },
+  {
+    input: "testNodeInIframe()",
+    output: '<p>'
+  },
+  {
+    input: "testDocumentElement()",
+    output: '<html lang="en-US" dir="ltr">'
+  }
+];
+
+const PREF = "devtools.webconsole.persistlog";
+
+function test() {
+  Services.prefs.setBoolPref(PREF, true);
+  registerCleanupFunction(() => Services.prefs.clearUserPref(PREF));
+
+  Task.spawn(function*() {
+    let {tab} = yield loadTab(TEST_URI);
+    let hud = yield openConsole(tab);
+    let toolbox = gDevTools.getToolbox(hud.target);
+
+    info("Executing the test data");
+    let widgets = [];
+    for (let data of TEST_DATA) {
+      let [result] = yield jsEval(data.input, hud, {text: data.output});
+      let {widget} = yield getWidgetAndMessage(result);
+      widgets.push(widget);
+    }
+
+    info("Reloading the page");
+    yield reloadPage();
+
+    info("Iterating over the ElementNode widgets");
+    for (let widget of widgets) {
+      // Verify that openNodeInInspector rejects since the associated dom node
+      // doesn't exist anymore
+      yield widget.openNodeInInspector().then(() => {
+        ok(false, "The openNodeInInspector promise resolved");
+      }, () => {
+        ok(true, "The openNodeInInspector promise rejected as expected");
+      });
+      yield toolbox.selectTool("webconsole");
+
+      // Verify that highlightDomNode rejects too, for the same reason
+      yield widget.highlightDomNode().then(() => {
+        ok(false, "The highlightDomNode promise resolved");
+      }, () => {
+        ok(true, "The highlightDomNode promise rejected as expected");
+      });
+    }
+  }).then(finishTest);
+}
+
+function jsEval(input, hud, message) {
+  info("Executing '" + input + "' in the web console");
+  hud.jsterm.execute(input);
+  return waitForMessages({
+    webconsole: hud,
+    messages: [message]
+  });
+}
+
+function* getWidgetAndMessage(result) {
+  info("Getting the output ElementNode widget");
+
+  let msg = [...result.matched][0];
+  let widget = [...msg._messageObject.widgets][0];
+  ok(widget, "ElementNode widget found in the output");
+
+  info("Waiting for the ElementNode widget to be linked to the inspector");
+  yield widget.linkToInspector();
+
+  return {widget: widget, msg: msg};
+}
+
+function reloadPage() {
+  let def = promise.defer();
+  gBrowser.selectedBrowser.addEventListener("load", function onload() {
+    gBrowser.selectedBrowser.removeEventListener("load", onload, true);
+    def.resolve();
+  }, true);
+  content.location.reload();
+  return def.promise;
+}
--- a/browser/devtools/webconsole/test/browser_webconsole_output_events.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_output_events.js
@@ -31,30 +31,30 @@ function test() {
     });
 
     EventUtils.synthesizeMouse(content.document.body, 2, 2, {type: "mousemove"}, content);
 
     yield waitForMessages({
       webconsole: hud,
       messages: [{
         name: "console.log() output for mousemove",
-        text: /"eventLogger" mousemove {target: .+, buttons: 1, clientX: \d+, clientY: \d+, layerX: \d+, layerY: \d+}/,
+        text: /"eventLogger" mousemove { target: .+, buttons: 1, clientX: \d+, clientY: \d+, layerX: \d+, layerY: \d+ }/,
         category: CATEGORY_WEBDEV,
         severity: SEVERITY_LOG,
       }],
     });
 
     content.focus();
     EventUtils.synthesizeKey("a", {shiftKey: true}, content);
 
     yield waitForMessages({
       webconsole: hud,
       messages: [{
         name: "console.log() output for keypress",
-        text: /"eventLogger" keypress Shift {target: .+, key: .+, charCode: \d+, keyCode: \d+}/,
+        text: /"eventLogger" keypress Shift { target: .+, key: .+, charCode: \d+, keyCode: \d+ }/,
         category: CATEGORY_WEBDEV,
         severity: SEVERITY_LOG,
       }],
     });
 
     finishTest();
   }
 }
--- a/browser/devtools/webconsole/test/head.js
+++ b/browser/devtools/webconsole/test/head.js
@@ -1309,16 +1309,20 @@ function whenDelayedStartupFinished(aWin
  *        builds).
  *
  *        - printOutput: string|RegExp, optional, expected output for
  *        |print(input)|. If this is not provided, printOutput = output.
  *
  *        - variablesViewLabel: string|RegExp, optional, the expected variables
  *        view label when the object is inspected. If this is not provided, then
  *        |output| is used.
+ *
+ *        - inspectorIcon: boolean, when true, the test runner expects the
+ *        result widget to contain an inspectorIcon element (className
+ *        open-inspector).
  */
 function checkOutputForInputs(hud, inputTests)
 {
   let eventHandlers = new Set();
 
   function* runner()
   {
     for (let [i, entry] of inputTests.entries()) {
@@ -1333,30 +1337,35 @@ function checkOutputForInputs(hud, input
 
   function* checkInput(entry)
   {
     yield checkConsoleLog(entry);
     yield checkPrintOutput(entry);
     yield checkJSEval(entry);
   }
 
-  function checkConsoleLog(entry)
+  function* checkConsoleLog(entry)
   {
     hud.jsterm.clearOutput();
     hud.jsterm.execute("console.log(" + entry.input + ")");
 
-    return waitForMessages({
+    let [result] = yield waitForMessages({
       webconsole: hud,
       messages: [{
         name: "console.log() output: " + entry.output,
         text: entry.output,
         category: CATEGORY_WEBDEV,
         severity: SEVERITY_LOG,
       }],
     });
+
+    if (typeof entry.inspectorIcon == "boolean") {
+      let msg = [...result.matched][0];
+      yield checkLinkToInspector(entry, msg);
+    }
   }
 
   function checkPrintOutput(entry)
   {
     hud.jsterm.clearOutput();
     hud.jsterm.execute("print(" + entry.input + ")");
 
     let printOutput = entry.printOutput || entry.output;
@@ -1380,20 +1389,23 @@ function checkOutputForInputs(hud, input
       webconsole: hud,
       messages: [{
         name: "JS eval output: " + entry.output,
         text: entry.output,
         category: CATEGORY_OUTPUT,
       }],
     });
 
+    let msg = [...result.matched][0];
     if (!entry.noClick) {
-      let msg = [...result.matched][0];
       yield checkObjectClick(entry, msg);
     }
+    if (typeof entry.inspectorIcon == "boolean") {
+      yield checkLinkToInspector(entry, msg);
+    }
   }
 
   function checkObjectClick(entry, msg)
   {
     let body = msg.querySelector(".body a") || msg.querySelector(".body");
     ok(body, "the message body");
 
     let deferred = promise.defer();
@@ -1408,16 +1420,39 @@ function checkOutputForInputs(hud, input
     if (entry.inspectable) {
       info("message body tagName '" + body.tagName +  "' className '" + body.className + "'");
       return deferred.promise; // wait for the panel to open if we need to.
     }
 
     return promise.resolve(null);
   }
 
+  function checkLinkToInspector(entry, msg)
+  {
+    let elementNodeWidget = [...msg._messageObject.widgets][0];
+    if (!elementNodeWidget) {
+      ok(!entry.inspectorIcon, "The message has no ElementNode widget");
+      return;
+    }
+
+    return elementNodeWidget.linkToInspector().then(() => {
+      // linkToInspector resolved, check for the .open-inspector element
+      if (entry.inspectorIcon) {
+        ok(msg.querySelectorAll(".open-inspector").length,
+          "The ElementNode widget is linked to the inspector");
+      } else {
+        ok(!msg.querySelectorAll(".open-inspector").length,
+          "The ElementNode widget isn't linked to the inspector");
+      }
+    }, () => {
+      // linkToInspector promise rejected, node not linked to inspector
+      ok(!entry.inspectorIcon, "The ElementNode widget isn't linked to the inspector");
+    });
+  }
+
   function onVariablesViewOpen(entry, deferred, event, view, options)
   {
     let label = entry.variablesViewLabel || entry.output;
     if (typeof label == "string" && options.label != label) {
       return;
     }
     if (label instanceof RegExp && !label.test(options.label)) {
       return;
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/test-console-output-dom-elements.html
@@ -0,0 +1,68 @@
+<!DOCTYPE HTML>
+<html dir="ltr" lang="en-US">
+<head>
+  <meta charset="utf-8">
+  <title>Test the web console output - 05</title>
+  <!--
+  - Any copyright is dedicated to the Public Domain.
+  - http://creativecommons.org/publicdomain/zero/1.0/
+  -->
+</head>
+<body class="body-class" id="body-id">
+  <p some-attribute="some-value">hello world!</p>
+  <iframe src="data:text/html,<p>hello from iframe</p>"></iframe>
+  <script type="text/javascript">
+function testBodyNode() {
+  return document.body;
+}
+
+function testDocumentElement() {
+  return document.documentElement;
+}
+
+function testDocument() {
+  return document;
+}
+
+function testNode() {
+  return document.querySelector("p");
+}
+
+function testNodeList() {
+  return document.querySelectorAll("*");
+}
+
+function testNodeInIframe() {
+  return document.querySelector("iframe").contentWindow.document.querySelector("p");
+}
+
+function testDocumentFragment() {
+  var frag = document.createDocumentFragment();
+
+  var span = document.createElement("span");
+  span.className = 'foo';
+  span.dataset.lolz = 'hehe';
+
+  var div = document.createElement('div')
+  div.id = 'fragdiv';
+
+  frag.appendChild(span);
+  frag.appendChild(div);
+
+  return frag;
+}
+
+function testNodeInDocumentFragment() {
+  var frag = testDocumentFragment();
+  return frag.firstChild;
+}
+
+function testUnattachedNode() {
+  var p = document.createElement("p");
+  p.className = "such-class";
+  p.dataset.data = "such-data";
+  return p;
+}
+  </script>
+</body>
+</html>
--- a/browser/devtools/webconsole/webconsole.js
+++ b/browser/devtools/webconsole/webconsole.js
@@ -2135,23 +2135,18 @@ WebConsoleFrame.prototype = {
     }
 
     // If the queue is not empty, schedule another flush.
     if (this._outputQueue.length > 0) {
       this._initOutputTimer();
     }
     else {
       this._outputTimerInitialized = false;
-      if (this._flushCallback) {
-        try {
-          this._flushCallback();
-        }
-        catch (ex) {
-          console.error(ex);
-        }
+      if (this._flushCallback && this._flushCallback() === false) {
+        this._flushCallback = null;
       }
     }
 
     this._lastOutputFlush = Date.now();
   },
 
   /**
    * Initialize the output timer.
@@ -2362,16 +2357,20 @@ WebConsoleFrame.prototype = {
   /**
    * Remove a given message from the output.
    *
    * @param nsIDOMNode aNode
    *        The message node you want to remove.
    */
   removeOutputMessage: function WCF_removeOutputMessage(aNode)
   {
+    if (aNode._messageObject) {
+      aNode._messageObject.destroy();
+    }
+
     if (aNode._objectActors) {
       for (let actor of aNode._objectActors) {
         this._releaseObject(actor);
       }
       aNode._objectActors.clear();
     }
 
     if (aNode.category == CATEGORY_CSS ||
@@ -3199,20 +3198,20 @@ JSTerm.prototype = {
 
     if (aCallback) {
       let oldFlushCallback = this.hud._flushCallback;
       this.hud._flushCallback = () => {
         aCallback(msg.element);
         if (oldFlushCallback) {
           oldFlushCallback();
           this.hud._flushCallback = oldFlushCallback;
+          return true;
         }
-        else {
-          this.hud._flushCallback = null;
-        }
+
+        return false;
       };
     }
 
     msg._afterMessage = aAfterMessage;
     msg._objectActors = new Set();
 
     if (WebConsoleUtils.isActorGrip(aResponse.exception)) {
       msg._objectActors.add(aResponse.exception.actor);
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,4 +1,4 @@
 This is the pdf.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 0.8.1041
+Current extension version is: 0.8.1114
 
--- a/browser/extensions/pdfjs/chrome.manifest
+++ b/browser/extensions/pdfjs/chrome.manifest
@@ -1,2 +1,1 @@
 resource pdf.js content/
-resource pdf.js.components components/
--- a/browser/extensions/pdfjs/content/PdfJs.jsm
+++ b/browser/extensions/pdfjs/content/PdfJs.jsm
@@ -29,18 +29,18 @@ const PREF_PREVIOUS_ASK = PREF_PREFIX + 
 const PREF_DISABLED_PLUGIN_TYPES = 'plugin.disable_full_page_plugin_for_types';
 const TOPIC_PDFJS_HANDLER_CHANGED = 'pdfjs:handlerChanged';
 const TOPIC_PLUGINS_LIST_UPDATED = "plugins-list-updated";
 const TOPIC_PLUGIN_INFO_UPDATED = "plugin-info-updated";
 const PDF_CONTENT_TYPE = 'application/pdf';
 
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.import('resource://gre/modules/Services.jsm');
-Cu.import('resource://pdf.js.components/PdfStreamConverter.js');
-Cu.import('resource://pdf.js.components/PdfRedirector.js');
+Cu.import('resource://pdf.js/PdfStreamConverter.jsm');
+Cu.import('resource://pdf.js/PdfRedirector.jsm');
 
 let Svc = {};
 XPCOMUtils.defineLazyServiceGetter(Svc, 'mime',
                                    '@mozilla.org/mime;1',
                                    'nsIMIMEService');
 XPCOMUtils.defineLazyServiceGetter(Svc, 'pluginHost',
                                    '@mozilla.org/plugin/host;1',
                                    'nsIPluginHost');
@@ -56,49 +56,35 @@ function getBoolPref(aPref, aDefaultValu
 function getIntPref(aPref, aDefaultValue) {
   try {
     return Services.prefs.getIntPref(aPref);
   } catch (ex) {
     return aDefaultValue;
   }
 }
 
-// Factory that registers/unregisters a constructor as a component.
+// Register/unregister a constructor as a factory.
 function Factory() {}
-
 Factory.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory]),
-  _targetConstructor: null,
+  register: function register(targetConstructor) {
+    var proto = targetConstructor.prototype;
+    this._classID = proto.classID;
 
-  register: function register(targetConstructor) {
-    this._targetConstructor = targetConstructor;
-    var proto = targetConstructor.prototype;
+    var factory = XPCOMUtils._getFactory(targetConstructor);
+    this._factory = factory;
+
     var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
     registrar.registerFactory(proto.classID, proto.classDescription,
-                              proto.contractID, this);
+                              proto.contractID, factory);
   },
 
   unregister: function unregister() {
-    var proto = this._targetConstructor.prototype;
     var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
-    registrar.unregisterFactory(proto.classID, this);
-    this._targetConstructor = null;
-  },
-
-  // nsIFactory
-  createInstance: function createInstance(aOuter, iid) {
-    if (aOuter !== null)
-      throw Cr.NS_ERROR_NO_AGGREGATION;
-    return (new (this._targetConstructor)).QueryInterface(iid);
-  },
-
-  // nsIFactory
-  lockFactory: function lockFactory(lock) { 
-    // No longer used as of gecko 1.7.
-    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+    registrar.unregisterFactory(this._classID, this._factory);
+    this._factory = null;
   }
 };
 
 let PdfJs = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
   _registered: false,
 
   init: function init() {
rename from browser/extensions/pdfjs/components/PdfRedirector.js
rename to browser/extensions/pdfjs/content/PdfRedirector.jsm
--- a/browser/extensions/pdfjs/components/PdfRedirector.js
+++ b/browser/extensions/pdfjs/content/PdfRedirector.jsm
@@ -126,10 +126,8 @@ PdfRedirector.prototype = {
     channel.asyncOpen(this.listener, aContext);
   },
 
   // nsIRequestObserver::onStopRequest
   onStopRequest: function(aRequest, aContext, aStatusCode) {
     // Do nothing
   }
 };
-
-var NSGetFactory = XPCOMUtils.generateNSGetFactory([PdfRedirector]);
rename from browser/extensions/pdfjs/components/PdfStreamConverter.js
rename to browser/extensions/pdfjs/content/PdfStreamConverter.jsm
--- a/browser/extensions/pdfjs/components/PdfStreamConverter.js
+++ b/browser/extensions/pdfjs/content/PdfStreamConverter.jsm
@@ -954,10 +954,8 @@ PdfStreamConverter.prototype = {
     if (Components.isSuccessCode(aStatusCode))
       this.dataListener.finish();
     else
       this.dataListener.error(aStatusCode);
     delete this.dataListener;
     delete this.binaryStream;
   }
 };
-
-var NSGetFactory = XPCOMUtils.generateNSGetFactory([PdfStreamConverter]);
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -9,24 +9,25 @@
  *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+/*jshint globalstrict: false */
 
 // Initializing PDFJS global object (if still undefined)
 if (typeof PDFJS === 'undefined') {
   (typeof window !== 'undefined' ? window : this).PDFJS = {};
 }
 
-PDFJS.version = '0.8.1041';
-PDFJS.build = '2188bcb';
+PDFJS.version = '0.8.1114';
+PDFJS.build = 'ea9c8f8';
 
 (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
@@ -62,16 +63,22 @@ var TextRenderingMode = {
   FILL_ADD_TO_PATH: 4,
   STROKE_ADD_TO_PATH: 5,
   FILL_STROKE_ADD_TO_PATH: 6,
   ADD_TO_PATH: 7,
   FILL_STROKE_MASK: 3,
   ADD_TO_PATH_FLAG: 4
 };
 
+var ImageKind = {
+  GRAYSCALE_1BPP: 1,
+  RGB_24BPP: 2,
+  RGBA_32BPP: 3
+};
+
 // The global PDFJS object exposes the API
 // In production, it will be declared outside a global wrapper
 // In development, it will be declared here
 if (!globalScope.PDFJS) {
   globalScope.PDFJS = {};
 }
 
 globalScope.PDFJS.pdfBug = false;
@@ -252,17 +259,21 @@ var UnsupportedManager = PDFJS.Unsupport
 function combineUrl(baseUrl, url) {
   if (!url)
     return baseUrl;
   if (/^[a-z][a-z0-9+\-.]*:/i.test(url))
     return url;
   if (url.charAt(0) == '/') {
     // absolute path
     var i = baseUrl.indexOf('://');
-    i = baseUrl.indexOf('/', i + 3);
+    if (url.charAt(1) === '/') {
+      ++i;
+    } else {
+      i = baseUrl.indexOf('/', i + 3);
+    }
     return baseUrl.substring(0, i) + url;
   } else {
     // relative path
     var pathLength = baseUrl.length, i;
     i = baseUrl.lastIndexOf('#');
     pathLength = i >= 0 ? i : pathLength;
     i = baseUrl.lastIndexOf('?', pathLength);
     pathLength = i >= 0 ? i : pathLength;
@@ -400,21 +411,22 @@ var XRefParseException = (function XRefP
   XRefParseException.prototype.name = 'XRefParseException';
   XRefParseException.constructor = XRefParseException;
 
   return XRefParseException;
 })();
 
 
 function bytesToString(bytes) {
-  var str = '';
+  var strBuf = [];
   var length = bytes.length;
-  for (var n = 0; n < length; ++n)
-    str += String.fromCharCode(bytes[n]);
-  return str;
+  for (var n = 0; n < length; ++n) {
+    strBuf.push(String.fromCharCode(bytes[n]));
+  }
+  return strBuf.join('');
 }
 
 function stringToBytes(str) {
   var length = str.length;
   var bytes = new Uint8Array(length);
   for (var n = 0; n < length; ++n)
     bytes[n] = str.charCodeAt(n) & 0xFF;
   return bytes;
@@ -729,29 +741,30 @@ var PDFStringTranslateTable = [
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2022, 0x2020, 0x2021, 0x2026, 0x2014,
   0x2013, 0x192, 0x2044, 0x2039, 0x203A, 0x2212, 0x2030, 0x201E, 0x201C,
   0x201D, 0x2018, 0x2019, 0x201A, 0x2122, 0xFB01, 0xFB02, 0x141, 0x152, 0x160,
   0x178, 0x17D, 0x131, 0x142, 0x153, 0x161, 0x17E, 0, 0x20AC
 ];
 
 function stringToPDFString(str) {
-  var i, n = str.length, str2 = '';
+  var i, n = str.length, strBuf = [];
   if (str[0] === '\xFE' && str[1] === '\xFF') {
     // UTF16BE BOM
-    for (i = 2; i < n; i += 2)
-      str2 += String.fromCharCode(
-        (str.charCodeAt(i) << 8) | str.charCodeAt(i + 1));
+    for (i = 2; i < n; i += 2) {
+      strBuf.push(String.fromCharCode(
+        (str.charCodeAt(i) << 8) | str.charCodeAt(i + 1)));
+    }
   } else {
     for (i = 0; i < n; ++i) {
       var code = PDFStringTranslateTable[str.charCodeAt(i)];
-      str2 += code ? String.fromCharCode(code) : str.charAt(i);
+      strBuf.push(code ? String.fromCharCode(code) : str.charAt(i));
     }
   }
-  return str2;
+  return strBuf.join('');
 }
 
 function stringToUTF8String(str) {
   return decodeURIComponent(escape(str));
 }
 
 function isEmptyObj(obj) {
   for (var key in obj) {
@@ -1086,23 +1099,26 @@ var ColorSpace = (function ColorSpaceClo
 
   ColorSpace.prototype = {
     /**
      * Converts the color value to the RGB color. The color components are
      * located in the src array starting from the srcOffset. Returns the array
      * of the rgb components, each value ranging from [0,255].
      */
     getRgb: function ColorSpace_getRgb(src, srcOffset) {
-      error('Should not call ColorSpace.getRgb');
+      var rgb = new Uint8Array(3);
+      this.getRgbItem(src, srcOffset, rgb, 0);
+      return rgb;
     },
     /**
      * Converts the color value to the RGB color, similar to the getRgb method.
      * The result placed into the dest array starting from the destOffset.
      */
-    getRgbItem: function ColorSpace_getRgb(src, srcOffset, dest, destOffset) {
+    getRgbItem: function ColorSpace_getRgbItem(src, srcOffset,
+                                               dest, destOffset) {
       error('Should not call ColorSpace.getRgbItem');
     },
     /**
      * Converts the specified number of the color values to the RGB colors.
      * The colors are located in the src array starting from the srcOffset.
      * The result is placed into the dest array starting from the destOffset.
      * The src array items shall be in [0,2^bits) range, the dest array items
      * will be in [0,255] range. alpha01 indicates how many alpha components
@@ -1125,21 +1141,23 @@ var ColorSpace = (function ColorSpaceClo
     },
     /**
      * Returns true if source data will be equal the result/output data.
      */
     isPassthrough: function ColorSpace_isPassthrough(bits) {
       return false;
     },
     /**
-     * Fills in the RGB colors in an RGBA buffer.
+     * Fills in the RGB colors in the destination buffer.  alpha01 indicates
+     * how many alpha components there are in the dest array; it will be either
+     * 0 (RGB array) or 1 (RGBA array).
      */
-    fillRgb: function ColorSpace_fillRgb(rgbaBuf, originalWidth,
+    fillRgb: function ColorSpace_fillRgb(dest, originalWidth,
                                          originalHeight, width, height,
-                                         actualHeight, bpc, comps) {
+                                         actualHeight, bpc, comps, alpha01) {
       var count = originalWidth * originalHeight;
       var rgbBuf = null;
       var numComponentColors = 1 << bpc;
       var needsResizing = originalHeight != height || originalWidth != width;
 
       if (this.isPassthrough(bpc)) {
         rgbBuf = comps;
 
@@ -1159,58 +1177,59 @@ var ColorSpace = (function ColorSpaceClo
         for (var i = 0; i < numComponentColors; i++) {
           allColors[i] = i;
         }
         var colorMap = new Uint8Array(numComponentColors * 3);
         this.getRgbBuffer(allColors, 0, numComponentColors, colorMap, 0, bpc,
                           /* alpha01 = */ 0);
 
         if (!needsResizing) {
-          // Fill in the RGB values directly into |rgbaBuf|.
-          var rgbaPos = 0;
+          // Fill in the RGB values directly into |dest|.
+          var destPos = 0;
           for (var i = 0; i < count; ++i) {
             var key = comps[i] * 3;
-            rgbaBuf[rgbaPos++] = colorMap[key];
-            rgbaBuf[rgbaPos++] = colorMap[key + 1];
-            rgbaBuf[rgbaPos++] = colorMap[key + 2];
-            rgbaPos++;
+            dest[destPos++] = colorMap[key];
+            dest[destPos++] = colorMap[key + 1];
+            dest[destPos++] = colorMap[key + 2];
+            destPos += alpha01;
           }
         } else {
           rgbBuf = new Uint8Array(count * 3);
           var rgbPos = 0;
           for (var i = 0; i < count; ++i) {
             var key = comps[i] * 3;
             rgbBuf[rgbPos++] = colorMap[key];
             rgbBuf[rgbPos++] = colorMap[key + 1];
             rgbBuf[rgbPos++] = colorMap[key + 2];
           }
         }
       } else {
         if (!needsResizing) {
-          // Fill in the RGB values directly into |rgbaBuf|.
-          this.getRgbBuffer(comps, 0, width * actualHeight, rgbaBuf, 0, bpc,
-                            /* alpha01 = */ 1);
+          // Fill in the RGB values directly into |dest|.
+          this.getRgbBuffer(comps, 0, width * actualHeight, dest, 0, bpc,
+                            alpha01);
         } else {
           rgbBuf = new Uint8Array(count * 3);
           this.getRgbBuffer(comps, 0, count, rgbBuf, 0, bpc,
                             /* alpha01 = */ 0);
         }
       }
 
       if (rgbBuf) {
         if (needsResizing) {
           rgbBuf = PDFImage.resize(rgbBuf, bpc, 3, originalWidth,
                                    originalHeight, width, height);
         }
         var rgbPos = 0;
-        var actualLength = width * actualHeight * 4;
-        for (var i = 0; i < actualLength; i += 4) {
-          rgbaBuf[i] = rgbBuf[rgbPos++];
-          rgbaBuf[i + 1] = rgbBuf[rgbPos++];
-          rgbaBuf[i + 2] = rgbBuf[rgbPos++];
+        var destPos = 0;
+        for (var i = 0, ii = width * actualHeight; i < ii; i++) {
+          dest[destPos++] = rgbBuf[rgbPos++];
+          dest[destPos++] = rgbBuf[rgbPos++];
+          dest[destPos++] = rgbBuf[rgbPos++];
+          destPos += alpha01;
         }
       }
     },
     /**
      * True if the colorspace has components in the default range of [0, 1].
      * This should be true for all colorspaces except for lab color spaces
      * which are [0,100], [-128, 127], [-128, 127].
      */
@@ -1419,21 +1438,17 @@ var AlternateCS = (function AlternateCSC
     for (var i = 0; i < numComps; ++i) {
       this.defaultColor[i] = 1;
     }
     this.base = base;
     this.tintFn = tintFn;
   }
 
   AlternateCS.prototype = {
-    getRgb: function AlternateCS_getRgb(src, srcOffset) {
-      var rgb = new Uint8Array(3);
-      this.getRgbItem(src, srcOffset, rgb, 0);
-      return rgb;
-    },
+    getRgb: ColorSpace.prototype.getRgb,
     getRgbItem: function AlternateCS_getRgbItem(src, srcOffset,
                                                 dest, destOffset) {
       var baseNumComps = this.base.numComps;
       var input = 'subarray' in src ?
         src.subarray(srcOffset, srcOffset + this.numComps) :
         Array.prototype.slice.call(src, srcOffset, srcOffset + this.numComps);
       var tinted = this.tintFn(input);
       this.base.getRgbItem(tinted, 0, dest, destOffset);
@@ -1522,21 +1537,17 @@ var IndexedCS = (function IndexedCSClosu
       lookupArray = lookup;
     } else {
       error('Unrecognized lookup table: ' + lookup);
     }
     this.lookup = lookupArray;
   }
 
   IndexedCS.prototype = {
-    getRgb: function IndexedCS_getRgb(src, srcOffset) {
-      var numComps = this.base.numComps;
-      var start = src[srcOffset] * numComps;
-      return this.base.getRgb(this.lookup, start);
-    },
+    getRgb: ColorSpace.prototype.getRgb,
     getRgbItem: function IndexedCS_getRgbItem(src, srcOffset,
                                               dest, destOffset) {
       var numComps = this.base.numComps;
       var start = src[srcOffset] * numComps;
       this.base.getRgbItem(this.lookup, start, dest, destOffset);
     },
     getRgbBuffer: function IndexedCS_getRgbBuffer(src, srcOffset, count,
                                                   dest, destOffset, bits,
@@ -1570,21 +1581,17 @@ var IndexedCS = (function IndexedCSClosu
 var DeviceGrayCS = (function DeviceGrayCSClosure() {
   function DeviceGrayCS() {
     this.name = 'DeviceGray';
     this.numComps = 1;
     this.defaultColor = new Float32Array([0]);
   }
 
   DeviceGrayCS.prototype = {
-    getRgb: function DeviceGrayCS_getRgb(src, srcOffset) {
-      var rgb = new Uint8Array(3);
-      this.getRgbItem(src, srcOffset, rgb, 0);
-      return rgb;
-    },
+    getRgb: ColorSpace.prototype.getRgb,
     getRgbItem: function DeviceGrayCS_getRgbItem(src, srcOffset,
                                                  dest, destOffset) {
       var c = (src[srcOffset] * 255) | 0;
       c = c < 0 ? 0 : c > 255 ? 255 : c;
       dest[destOffset] = dest[destOffset + 1] = dest[destOffset + 2] = c;
     },
     getRgbBuffer: function DeviceGrayCS_getRgbBuffer(src, srcOffset, count,
                                                      dest, destOffset, bits,
@@ -1615,21 +1622,17 @@ var DeviceGrayCS = (function DeviceGrayC
 
 var DeviceRgbCS = (function DeviceRgbCSClosure() {
   function DeviceRgbCS() {
     this.name = 'DeviceRGB';
     this.numComps = 3;
     this.defaultColor = new Float32Array([0, 0, 0]);
   }
   DeviceRgbCS.prototype = {
-    getRgb: function DeviceRgbCS_getRgb(src, srcOffset) {
-      var rgb = new Uint8Array(3);
-      this.getRgbItem(src, srcOffset, rgb, 0);
-      return rgb;
-    },
+    getRgb: ColorSpace.prototype.getRgb,
     getRgbItem: function DeviceRgbCS_getRgbItem(src, srcOffset,
                                                 dest, destOffset) {
       var r = (src[srcOffset] * 255) | 0;
       var g = (src[srcOffset + 1] * 255) | 0;
       var b = (src[srcOffset + 2] * 255) | 0;
       dest[destOffset] = r < 0 ? 0 : r > 255 ? 255 : r;
       dest[destOffset + 1] = g < 0 ? 0 : g > 255 ? 255 : g;
       dest[destOffset + 2] = b < 0 ? 0 : b > 255 ? 255 : b;
@@ -1712,21 +1715,17 @@ var DeviceCmykCS = (function DeviceCmykC
   }
 
   function DeviceCmykCS() {
     this.name = 'DeviceCMYK';
     this.numComps = 4;
     this.defaultColor = new Float32Array([0, 0, 0, 1]);
   }
   DeviceCmykCS.prototype = {
-    getRgb: function DeviceCmykCS_getRgb(src, srcOffset) {
-      var rgb = new Uint8Array(3);
-      convertToRgb(src, srcOffset, 1, rgb, 0);
-      return rgb;
-    },
+    getRgb: ColorSpace.prototype.getRgb,
     getRgbItem: function DeviceCmykCS_getRgbItem(src, srcOffset,
                                                  dest, destOffset) {
       convertToRgb(src, srcOffset, 1, dest, destOffset);
     },
     getRgbBuffer: function DeviceCmykCS_getRgbBuffer(src, srcOffset, count,
                                                      dest, destOffset, bits,
                                                      alpha01) {
       var scale = 1 / ((1 << bits) - 1);
@@ -1823,21 +1822,17 @@ var CalGrayCS = (function CalGrayCSClosu
 
     // Convert values to rgb range [0, 255].
     dest[destOffset] = Lstar * 255 / 100;
     dest[destOffset + 1] = Lstar * 255 / 100;
     dest[destOffset + 2] = Lstar * 255 / 100;
   }
 
   CalGrayCS.prototype = {
-    getRgb: function CalGrayCS_getRgb(src, srcOffset) {
-      var rgb = new Uint8Array(3);
-      this.getRgbItem(src, srcOffset, rgb, 0);
-      return rgb;
-    },
+    getRgb: ColorSpace.prototype.getRgb,
     getRgbItem: function CalGrayCS_getRgbItem(src, srcOffset,
                                               dest, destOffset) {
       convertToRgb(this, src, srcOffset, dest, destOffset, 1);
     },
     getRgbBuffer: function CalGrayCS_getRgbBuffer(src, srcOffset, count,
                                                   dest, destOffset, bits,
                                                   alpha01) {
       var scale = 1 / ((1 << bits) - 1);
@@ -1960,27 +1955,23 @@ var LabCS = (function LabCSClosure() {
       b = X * 0.0720 + Y * -0.2290 + Z * 1.4057;
     } else {
       // Assuming D65 (X=0.9505, Y=1.00, Z=1.0888)
       r = X * 3.2406 + Y * -1.5372 + Z * -0.4986;
       g = X * -0.9689 + Y * 1.8758 + Z * 0.0415;
       b = X * 0.0557 + Y * -0.2040 + Z * 1.0570;
     }
     // clamp color values to [0,1] range then convert to [0,255] range.
-    dest[destOffset] = Math.sqrt(r < 0 ? 0 : r > 1 ? 1 : r) * 255;
-    dest[destOffset + 1] = Math.sqrt(g < 0 ? 0 : g > 1 ? 1 : g) * 255;
-    dest[destOffset + 2] = Math.sqrt(b < 0 ? 0 : b > 1 ? 1 : b) * 255;
+    dest[destOffset] = r <= 0 ? 0 : r >= 1 ? 255 : Math.sqrt(r) * 255 | 0;
+    dest[destOffset + 1] = g <= 0 ? 0 : g >= 1 ? 255 : Math.sqrt(g) * 255 | 0;
+    dest[destOffset + 2] = b <= 0 ? 0 : b >= 1 ? 255 : Math.sqrt(b) * 255 | 0;
   }
 
   LabCS.prototype = {
-    getRgb: function LabCS_getRgb(src, srcOffset) {
-      var rgb = new Uint8Array(3);
-      convertToRgb(this, src, srcOffset, false, rgb, 0);
-      return rgb;
-    },
+    getRgb: ColorSpace.prototype.getRgb,
     getRgbItem: function LabCS_getRgbItem(src, srcOffset, dest, destOffset) {
       convertToRgb(this, src, srcOffset, false, dest, destOffset);
     },
     getRgbBuffer: function LabCS_getRgbBuffer(src, srcOffset, count,
                                               dest, destOffset, bits,
                                               alpha01) {
       var maxVal = (1 << bits) - 1;
       for (var i = 0; i < count; i++) {
@@ -2876,16 +2867,18 @@ var Annotation = (function AnnotationClo
       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();
       }.bind(this));
 
       return promise;
     }
   };
 
   Annotation.getConstructor =
       function Annotation_getConstructor(subtype, fieldType) {
@@ -3579,23 +3572,16 @@ var PDFDocumentProxy = (function PDFDocu
     /**
      * @return {string} A unique ID to identify a PDF. Not guaranteed to be
      * unique.
      */
     get fingerprint() {
       return this.pdfInfo.fingerprint;
     },
     /**
-     * @return {boolean} true if embedded document fonts are in use. Will be
-     * set during rendering of the pages.
-     */
-    get embeddedFontsUsed() {
-      return this.transport.embeddedFontsUsed;
-    },
-    /**
      * @param {number} pageNumber The page number to get. The first page is 1.
      * @return {Promise} A promise that is resolved with a {@link PDFPageProxy}
      * object.
      */
     getPage: function PDFDocumentProxy_getPage(pageNumber) {
       return this.transport.getPage(pageNumber);
     },
     /**
@@ -3892,16 +3878,17 @@ var PDFPageProxy = (function PDFPageProx
       if (!this.pendingDestroy ||
           this.renderTasks.length !== 0 ||
           this.receivingOperatorList) {
         return;
       }
 
       delete this.operatorList;
       delete this.displayReadyPromise;
+      delete this.annotationsPromise;
       this.objs.clear();
       this.pendingDestroy = false;
     },
     /**
      * For internal use only.
      * @ignore
      */
     _startRenderPage: function PDFPageProxy_startRenderPage(transparency) {
@@ -3943,17 +3930,16 @@ var WorkerTransport = (function WorkerTr
     this.pdfDataRangeTransport = pdfDataRangeTransport;
 
     this.workerReadyPromise = workerReadyPromise;
     this.progressCallback = progressCallback;
     this.commonObjs = new PDFObjects();
 
     this.pageCache = [];
     this.pagePromises = [];
-    this.embeddedFontsUsed = false;
     this.downloadInfoPromise = new PDFJS.LegacyPromise();
     this.passwordCallback = null;
 
     // If worker support isn't disabled explicit and the browser has worker
     // support, create a new web worker and test if it/the browser fullfills
     // all requirements to run parts of pdf.js in a web worker.
     // Right now, the requirement is, that an Uint8Array is still an Uint8Array
     // as it arrives on the worker. Chrome added this with version 15.
@@ -5097,121 +5083,143 @@ var CanvasGraphics = (function CanvasGra
     var fracChunks = height / fullChunkHeight;
     var fullChunks = Math.floor(fracChunks);
     var totalChunks = Math.ceil(fracChunks);
     var partialChunkHeight = height - fullChunks * fullChunkHeight;
 
     var chunkImgData = ctx.createImageData(width, fullChunkHeight);
     var srcPos = 0;
     var src = imgData.data;
-    var dst = chunkImgData.data;
+    var dest = chunkImgData.data;
 
     // There are multiple forms in which the pixel data can be passed, and
     // imgData.kind tells us which one this is.
 
-    if (imgData.kind === 'grayscale_1bpp') {
+    if (imgData.kind === ImageKind.GRAYSCALE_1BPP) {
       // Grayscale, 1 bit per pixel (i.e. black-and-white).
-      var srcData = imgData.data;
-      var destData = chunkImgData.data;
-      var destDataLength = destData.length;
-      var origLength = imgData.origLength;
+      var destDataLength = dest.length;
+      var srcLength = src.byteLength;
       for (var i = 3; i < destDataLength; i += 4) {
-        destData[i] = 255;
+        dest[i] = 255;
       }
       for (var i = 0; i < totalChunks; i++) {
         var thisChunkHeight =
           (i < fullChunks) ? fullChunkHeight : partialChunkHeight;
         var destPos = 0;
         for (var j = 0; j < thisChunkHeight; j++) {
           var mask = 0;
           var srcByte = 0;
           for (var k = 0; k < width; k++, destPos += 4) {
             if (mask === 0) {
-              if (srcPos >= origLength) {
+              if (srcPos >= srcLength) {
                 break;
               }
-              srcByte = srcData[srcPos++];
+              srcByte = src[srcPos++];
               mask = 128;
             }
 
             if ((srcByte & mask)) {
-              destData[destPos] = 255;
-              destData[destPos + 1] = 255;
-              destData[destPos + 2] = 255;
+              dest[destPos] = 255;
+              dest[destPos + 1] = 255;
+              dest[destPos + 2] = 255;
             } else {
-              destData[destPos] = 0;
-              destData[destPos + 1] = 0;
-              destData[destPos + 2] = 0;
+              dest[destPos] = 0;
+              dest[destPos + 1] = 0;
+              dest[destPos + 2] = 0;
             }
 
             mask >>= 1;
           }
         }
         if (destPos < destDataLength) {
           // We ran out of input. Make all remaining pixels transparent.
           destPos += 3;
           do {
-            destData[destPos] = 0;
+            dest[destPos] = 0;
             destPos += 4;
           } while (destPos < destDataLength);
         }
 
         ctx.putImageData(chunkImgData, 0, i * fullChunkHeight);
       }
 
-    } else if (imgData.kind === 'rgba_32bpp') {
+    } else if (imgData.kind === ImageKind.RGBA_32BPP) {
       // RGBA, 32-bits per pixel.
-      var haveSetAndSubarray = 'set' in dst && 'subarray' in src;
+      var haveSetAndSubarray = 'set' in dest && 'subarray' in src;
 
       for (var i = 0; i < totalChunks; i++) {
         var thisChunkHeight =
           (i < fullChunks) ? fullChunkHeight : partialChunkHeight;
         var elemsInThisChunk = imgData.width * thisChunkHeight * 4;
         if (haveSetAndSubarray) {
-          dst.set(src.subarray(srcPos, srcPos + elemsInThisChunk));
+          dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk));
           srcPos += elemsInThisChunk;
         } else {
           for (var j = 0; j < elemsInThisChunk; j++) {
-            chunkImgData.data[j] = imgData.data[srcPos++];
+            dest[j] = src[srcPos++];
           }
         }
         ctx.putImageData(chunkImgData, 0, i * fullChunkHeight);
       }
 
+    } else if (imgData.kind === ImageKind.RGB_24BPP) {
+      // RGB, 24-bits per pixel.
+      for (var i = 0; i < totalChunks; i++) {
+        var thisChunkHeight =
+          (i < fullChunks) ? fullChunkHeight : partialChunkHeight;
+        var elemsInThisChunk = imgData.width * thisChunkHeight * 3;
+        var destPos = 0;
+        for (var j = 0; j < elemsInThisChunk; j += 3) {
+          dest[destPos++] = src[srcPos++];
+          dest[destPos++] = src[srcPos++];
+          dest[destPos++] = src[srcPos++];
+          dest[destPos++] = 255;
+        }
+        ctx.putImageData(chunkImgData, 0, i * fullChunkHeight);
+      }
+
     } else {
         error('bad image kind: ' + imgData.kind);
     }
   }
 
   function putBinaryImageMask(ctx, imgData) {
-    var width = imgData.width, height = imgData.height;
-    var tmpImgData = ctx.createImageData(width, height);
-    var data = imgData.data;
-    var tmpImgDataPixels = tmpImgData.data;
-    var dataPos = 0;
-
-    // Expand the mask so it can be used by the canvas.  Any required inversion
-    // has already been handled.
-    var tmpPos = 3; // alpha component offset
-    for (var i = 0; i < height; i++) {
-      var mask = 0;
-      for (var j = 0; j < width; j++) {
-        if (!mask) {
-          var elem = data[dataPos++];
-          mask = 128;
+    var height = imgData.height, width = imgData.width;
+    var fullChunkHeight = 16;
+    var fracChunks = height / fullChunkHeight;
+    var fullChunks = Math.floor(fracChunks);
+    var totalChunks = Math.ceil(fracChunks);
+    var partialChunkHeight = height - fullChunks * fullChunkHeight;
+
+    var chunkImgData = ctx.createImageData(width, fullChunkHeight);
+    var srcPos = 0;
+    var src = imgData.data;
+    var dest = chunkImgData.data;
+
+    for (var i = 0; i < totalChunks; i++) {
+      var thisChunkHeight =
+        (i < fullChunks) ? fullChunkHeight : partialChunkHeight;
+
+      // Expand the mask so it can be used by the canvas.  Any required
+      // inversion has already been handled.
+      var destPos = 3; // alpha component offset
+      for (var j = 0; j < thisChunkHeight; j++) {
+        var mask = 0;
+        for (var k = 0; k < width; k++) {
+          if (!mask) {
+            var elem = src[srcPos++];
+            mask = 128;
+          }
+          dest[destPos] = (elem & mask) ? 0 : 255;
+          destPos += 4;
+          mask >>= 1;
         }
-        if (!(elem & mask)) {
-          tmpImgDataPixels[tmpPos] = 255;
-        }
-        tmpPos += 4;
-        mask >>= 1;
-      }
+      }
+      ctx.putImageData(chunkImgData, 0, i * fullChunkHeight);
     }
-
-    ctx.putImageData(tmpImgData, 0, 0);
   }
 
   function copyCtxState(sourceCtx, destCtx) {
     var properties = ['strokeStyle', 'fillStyle', 'fillRule', 'globalAlpha',
                       'lineWidth', 'lineCap', 'lineJoin', 'miterLimit',
                       'globalCompositeOperation', 'font'];
     for (var i = 0, ii = properties.length; i < ii; i++) {
       var property = properties[i];
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -9,24 +9,25 @@
  *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+/*jshint globalstrict: false */
 
 // Initializing PDFJS global object (if still undefined)
 if (typeof PDFJS === 'undefined') {
   (typeof window !== 'undefined' ? window : this).PDFJS = {};
 }
 
-PDFJS.version = '0.8.1041';
-PDFJS.build = '2188bcb';
+PDFJS.version = '0.8.1114';
+PDFJS.build = 'ea9c8f8';
 
 (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
@@ -62,16 +63,22 @@ var TextRenderingMode = {
   FILL_ADD_TO_PATH: 4,
   STROKE_ADD_TO_PATH: 5,
   FILL_STROKE_ADD_TO_PATH: 6,
   ADD_TO_PATH: 7,
   FILL_STROKE_MASK: 3,
   ADD_TO_PATH_FLAG: 4
 };
 
+var ImageKind = {
+  GRAYSCALE_1BPP: 1,
+  RGB_24BPP: 2,
+  RGBA_32BPP: 3
+};
+
 // The global PDFJS object exposes the API
 // In production, it will be declared outside a global wrapper
 // In development, it will be declared here
 if (!globalScope.PDFJS) {
   globalScope.PDFJS = {};
 }
 
 globalScope.PDFJS.pdfBug = false;
@@ -252,17 +259,21 @@ var UnsupportedManager = PDFJS.Unsupport
 function combineUrl(baseUrl, url) {
   if (!url)
     return baseUrl;
   if (/^[a-z][a-z0-9+\-.]*:/i.test(url))
     return url;
   if (url.charAt(0) == '/') {
     // absolute path
     var i = baseUrl.indexOf('://');
-    i = baseUrl.indexOf('/', i + 3);
+    if (url.charAt(1) === '/') {
+      ++i;
+    } else {
+      i = baseUrl.indexOf('/', i + 3);
+    }
     return baseUrl.substring(0, i) + url;
   } else {
     // relative path
     var pathLength = baseUrl.length, i;
     i = baseUrl.lastIndexOf('#');
     pathLength = i >= 0 ? i : pathLength;
     i = baseUrl.lastIndexOf('?', pathLength);
     pathLength = i >= 0 ? i : pathLength;
@@ -400,21 +411,22 @@ var XRefParseException = (function XRefP
   XRefParseException.prototype.name = 'XRefParseException';
   XRefParseException.constructor = XRefParseException;
 
   return XRefParseException;
 })();
 
 
 function bytesToString(bytes) {
-  var str = '';
+  var strBuf = [];
   var length = bytes.length;
-  for (var n = 0; n < length; ++n)
-    str += String.fromCharCode(bytes[n]);
-  return str;
+  for (var n = 0; n < length; ++n) {
+    strBuf.push(String.fromCharCode(bytes[n]));
+  }
+  return strBuf.join('');
 }
 
 function stringToBytes(str) {
   var length = str.length;
   var bytes = new Uint8Array(length);
   for (var n = 0; n < length; ++n)
     bytes[n] = str.charCodeAt(n) & 0xFF;
   return bytes;
@@ -729,29 +741,30 @@ var PDFStringTranslateTable = [
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2022, 0x2020, 0x2021, 0x2026, 0x2014,
   0x2013, 0x192, 0x2044, 0x2039, 0x203A, 0x2212, 0x2030, 0x201E, 0x201C,
   0x201D, 0x2018, 0x2019, 0x201A, 0x2122, 0xFB01, 0xFB02, 0x141, 0x152, 0x160,
   0x178, 0x17D, 0x131, 0x142, 0x153, 0x161, 0x17E, 0, 0x20AC
 ];
 
 function stringToPDFString(str) {
-  var i, n = str.length, str2 = '';
+  var i, n = str.length, strBuf = [];
   if (str[0] === '\xFE' && str[1] === '\xFF') {
     // UTF16BE BOM
-    for (i = 2; i < n; i += 2)
-      str2 += String.fromCharCode(
-        (str.charCodeAt(i) << 8) | str.charCodeAt(i + 1));
+    for (i = 2; i < n; i += 2) {
+      strBuf.push(String.fromCharCode(
+        (str.charCodeAt(i) << 8) | str.charCodeAt(i + 1)));
+    }
   } else {
     for (i = 0; i < n; ++i) {
       var code = PDFStringTranslateTable[str.charCodeAt(i)];
-      str2 += code ? String.fromCharCode(code) : str.charAt(i);
-    }
-  }
-  return str2;
+      strBuf.push(code ? String.fromCharCode(code) : str.charAt(i));
+    }
+  }
+  return strBuf.join('');
 }
 
 function stringToUTF8String(str) {
   return decodeURIComponent(escape(str));
 }
 
 function isEmptyObj(obj) {
   for (var key in obj) {
@@ -1086,23 +1099,26 @@ var ColorSpace = (function ColorSpaceClo
 
   ColorSpace.prototype = {
     /**
      * Converts the color value to the RGB color. The color components are
      * located in the src array starting from the srcOffset. Returns the array
      * of the rgb components, each value ranging from [0,255].
      */
     getRgb: function ColorSpace_getRgb(src, srcOffset) {
-      error('Should not call ColorSpace.getRgb');
+      var rgb = new Uint8Array(3);
+      this.getRgbItem(src, srcOffset, rgb, 0);
+      return rgb;
     },
     /**
      * Converts the color value to the RGB color, similar to the getRgb method.
      * The result placed into the dest array starting from the destOffset.
      */
-    getRgbItem: function ColorSpace_getRgb(src, srcOffset, dest, destOffset) {
+    getRgbItem: function ColorSpace_getRgbItem(src, srcOffset,
+                                               dest, destOffset) {
       error('Should not call ColorSpace.getRgbItem');
     },
     /**
      * Converts the specified number of the color values to the RGB colors.
      * The colors are located in the src array starting from the srcOffset.
      * The result is placed into the dest array starting from the destOffset.
      * The src array items shall be in [0,2^bits) range, the dest array items
      * will be in [0,255] range. alpha01 indicates how many alpha components
@@ -1125,21 +1141,23 @@ var ColorSpace = (function ColorSpaceClo
     },
     /**
      * Returns true if source data will be equal the result/output data.
      */
     isPassthrough: function ColorSpace_isPassthrough(bits) {
       return false;
     },
     /**
-     * Fills in the RGB colors in an RGBA buffer.
+     * Fills in the RGB colors in the destination buffer.  alpha01 indicates
+     * how many alpha components there are in the dest array; it will be either
+     * 0 (RGB array) or 1 (RGBA array).
      */
-    fillRgb: function ColorSpace_fillRgb(rgbaBuf, originalWidth,
+    fillRgb: function ColorSpace_fillRgb(dest, originalWidth,
                                          originalHeight, width, height,
-                                         actualHeight, bpc, comps) {
+                                         actualHeight, bpc, comps, alpha01) {
       var count = originalWidth * originalHeight;
       var rgbBuf = null;
       var numComponentColors = 1 << bpc;
       var needsResizing = originalHeight != height || originalWidth != width;
 
       if (this.isPassthrough(bpc)) {
         rgbBuf = comps;
 
@@ -1159,58 +1177,59 @@ var ColorSpace = (function ColorSpaceClo
         for (var i = 0; i < numComponentColors; i++) {
           allColors[i] = i;
         }
         var colorMap = new Uint8Array(numComponentColors * 3);
         this.getRgbBuffer(allColors, 0, numComponentColors, colorMap, 0, bpc,
                           /* alpha01 = */ 0);
 
         if (!needsResizing) {
-          // Fill in the RGB values directly into |rgbaBuf|.
-          var rgbaPos = 0;
+          // Fill in the RGB values directly into |dest|.
+          var destPos = 0;
           for (var i = 0; i < count; ++i) {
             var key = comps[i] * 3;
-            rgbaBuf[rgbaPos++] = colorMap[key];
-            rgbaBuf[rgbaPos++] = colorMap[key + 1];
-            rgbaBuf[rgbaPos++] = colorMap[key + 2];
-            rgbaPos++;
+            dest[destPos++] = colorMap[key];
+            dest[destPos++] = colorMap[key + 1];
+            dest[destPos++] = colorMap[key + 2];
+            destPos += alpha01;
           }
         } else {
           rgbBuf = new Uint8Array(count * 3);
           var rgbPos = 0;
           for (var i = 0; i < count; ++i) {
             var key = comps[i] * 3;
             rgbBuf[rgbPos++] = colorMap[key];
             rgbBuf[rgbPos++] = colorMap[key + 1];
             rgbBuf[rgbPos++] = colorMap[key + 2];
           }
         }
       } else {
         if (!needsResizing) {
-          // Fill in the RGB values directly into |rgbaBuf|.
-          this.getRgbBuffer(comps, 0, width * actualHeight, rgbaBuf, 0, bpc,
-                            /* alpha01 = */ 1);
+          // Fill in the RGB values directly into |dest|.
+          this.getRgbBuffer(comps, 0, width * actualHeight, dest, 0, bpc,
+                            alpha01);
         } else {
           rgbBuf = new Uint8Array(count * 3);
           this.getRgbBuffer(comps, 0, count, rgbBuf, 0, bpc,
                             /* alpha01 = */ 0);
         }
       }
 
       if (rgbBuf) {
         if (needsResizing) {
           rgbBuf = PDFImage.resize(rgbBuf, bpc, 3, originalWidth,
                                    originalHeight, width, height);
         }
         var rgbPos = 0;
-        var actualLength = width * actualHeight * 4;
-        for (var i = 0; i < actualLength; i += 4) {
-          rgbaBuf[i] = rgbBuf[rgbPos++];
-          rgbaBuf[i + 1] = rgbBuf[rgbPos++];
-          rgbaBuf[i + 2] = rgbBuf[rgbPos++];
+        var destPos = 0;
+        for (var i = 0, ii = width * actualHeight; i < ii; i++) {
+          dest[destPos++] = rgbBuf[rgbPos++];
+          dest[destPos++] = rgbBuf[rgbPos++];
+          dest[destPos++] = rgbBuf[rgbPos++];
+          destPos += alpha01;
         }
       }
     },
     /**
      * True if the colorspace has components in the default range of [0, 1].
      * This should be true for all colorspaces except for lab color spaces
      * which are [0,100], [-128, 127], [-128, 127].
      */
@@ -1419,21 +1438,17 @@ var AlternateCS = (function AlternateCSC
     for (var i = 0; i < numComps; ++i) {
       this.defaultColor[i] = 1;
     }
     this.base = base;
     this.tintFn = tintFn;
   }
 
   AlternateCS.prototype = {
-    getRgb: function AlternateCS_getRgb(src, srcOffset) {
-      var rgb = new Uint8Array(3);
-      this.getRgbItem(src, srcOffset, rgb, 0);
-      return rgb;
-    },
+    getRgb: ColorSpace.prototype.getRgb,
     getRgbItem: function AlternateCS_getRgbItem(src, srcOffset,
                                                 dest, destOffset) {
       var baseNumComps = this.base.numComps;
       var input = 'subarray' in src ?
         src.subarray(srcOffset, srcOffset + this.numComps) :
         Array.prototype.slice.call(src, srcOffset, srcOffset + this.numComps);
       var tinted = this.tintFn(input);
       this.base.getRgbItem(tinted, 0, dest, destOffset);
@@ -1522,21 +1537,17 @@ var IndexedCS = (function IndexedCSClosu
       lookupArray = lookup;
     } else {
       error('Unrecognized lookup table: ' + lookup);
     }
     this.lookup = lookupArray;
   }
 
   IndexedCS.prototype = {
-    getRgb: function IndexedCS_getRgb(src, srcOffset) {
-      var numComps = this.base.numComps;
-      var start = src[srcOffset] * numComps;
-      return this.base.getRgb(this.lookup, start);
-    },
+    getRgb: ColorSpace.prototype.getRgb,
     getRgbItem: function IndexedCS_getRgbItem(src, srcOffset,
                                               dest, destOffset) {
       var numComps = this.base.numComps;
       var start = src[srcOffset] * numComps;
       this.base.getRgbItem(this.lookup, start, dest, destOffset);
     },
     getRgbBuffer: function IndexedCS_getRgbBuffer(src, srcOffset, count,
                                                   dest, destOffset, bits,
@@ -1570,21 +1581,17 @@ var IndexedCS = (function IndexedCSClosu
 var DeviceGrayCS = (function DeviceGrayCSClosure() {
   function DeviceGrayCS() {
     this.name = 'DeviceGray';
     this.numComps = 1;
     this.defaultColor = new Float32Array([0]);
   }
 
   DeviceGrayCS.prototype = {
-    getRgb: function DeviceGrayCS_getRgb(src, srcOffset) {
-      var rgb = new Uint8Array(3);
-      this.getRgbItem(src, srcOffset, rgb, 0);
-      return rgb;
-    },
+    getRgb: ColorSpace.prototype.getRgb,
     getRgbItem: function DeviceGrayCS_getRgbItem(src, srcOffset,
                                                  dest, destOffset) {
       var c = (src[srcOffset] * 255) | 0;
       c = c < 0 ? 0 : c > 255 ? 255 : c;
       dest[destOffset] = dest[destOffset + 1] = dest[destOffset + 2] = c;
     },
     getRgbBuffer: function DeviceGrayCS_getRgbBuffer(src, srcOffset, count,
                                                      dest, destOffset, bits,
@@ -1615,21 +1622,17 @@ var DeviceGrayCS = (function DeviceGrayC
 
 var DeviceRgbCS = (function DeviceRgbCSClosure() {
   function DeviceRgbCS() {
     this.name = 'DeviceRGB';
     this.numComps = 3;
     this.defaultColor = new Float32Array([0, 0, 0]);
   }
   DeviceRgbCS.prototype = {
-    getRgb: function DeviceRgbCS_getRgb(src, srcOffset) {
-      var rgb = new Uint8Array(3);
-      this.getRgbItem(src, srcOffset, rgb, 0);
-      return rgb;
-    },
+    getRgb: ColorSpace.prototype.getRgb,
     getRgbItem: function DeviceRgbCS_getRgbItem(src, srcOffset,
                                                 dest, destOffset) {
       var r = (src[srcOffset] * 255) | 0;
       var g = (src[srcOffset + 1] * 255) | 0;
       var b = (src[srcOffset + 2] * 255) | 0;
       dest[destOffset] = r < 0 ? 0 : r > 255 ? 255 : r;
       dest[destOffset + 1] = g < 0 ? 0 : g > 255 ? 255 : g;
       dest[destOffset + 2] = b < 0 ? 0 : b > 255 ? 255 : b;
@@ -1712,21 +1715,17 @@ var DeviceCmykCS = (function DeviceCmykC
   }
 
   function DeviceCmykCS() {
     this.name = 'DeviceCMYK';
     this.numComps = 4;
     this.defaultColor = new Float32Array([0, 0, 0, 1]);
   }
   DeviceCmykCS.prototype = {
-    getRgb: function DeviceCmykCS_getRgb(src, srcOffset) {
-      var rgb = new Uint8Array(3);
-      convertToRgb(src, srcOffset, 1, rgb, 0);
-      return rgb;
-    },
+    getRgb: ColorSpace.prototype.getRgb,
     getRgbItem: function DeviceCmykCS_getRgbItem(src, srcOffset,
                                                  dest, destOffset) {
       convertToRgb(src, srcOffset, 1, dest, destOffset);
     },
     getRgbBuffer: function DeviceCmykCS_getRgbBuffer(src, srcOffset, count,
                                                      dest, destOffset, bits,
                                                      alpha01) {
       var scale = 1 / ((1 << bits) - 1);
@@ -1823,21 +1822,17 @@ var CalGrayCS = (function CalGrayCSClosu
 
     // Convert values to rgb range [0, 255].
     dest[destOffset] = Lstar * 255 / 100;
     dest[destOffset + 1] = Lstar * 255 / 100;
     dest[destOffset + 2] = Lstar * 255 / 100;
   }
 
   CalGrayCS.prototype = {
-    getRgb: function CalGrayCS_getRgb(src, srcOffset) {
-      var rgb = new Uint8Array(3);
-      this.getRgbItem(src, srcOffset, rgb, 0);
-      return rgb;
-    },
+    getRgb: ColorSpace.prototype.getRgb,
     getRgbItem: function CalGrayCS_getRgbItem(src, srcOffset,
                                               dest, destOffset) {
       convertToRgb(this, src, srcOffset, dest, destOffset, 1);
     },
     getRgbBuffer: function CalGrayCS_getRgbBuffer(src, srcOffset, count,
                                                   dest, destOffset, bits,
                                                   alpha01) {
       var scale = 1 / ((1 << bits) - 1);
@@ -1960,27 +1955,23 @@ var LabCS = (function LabCSClosure() {
       b = X * 0.0720 + Y * -0.2290 + Z * 1.4057;
     } else {
       // Assuming D65 (X=0.9505, Y=1.00, Z=1.0888)
       r = X * 3.2406 + Y * -1.5372 + Z * -0.4986;
       g = X * -0.9689 + Y * 1.8758 + Z * 0.0415;
       b = X * 0.0557 + Y * -0.2040 + Z * 1.0570;
     }
     // clamp color values to [0,1] range then convert to [0,255] range.
-    dest[destOffset] = Math.sqrt(r < 0 ? 0 : r > 1 ? 1 : r) * 255;
-    dest[destOffset + 1] = Math.sqrt(g < 0 ? 0 : g > 1 ? 1 : g) * 255;
-    dest[destOffset + 2] = Math.sqrt(b < 0 ? 0 : b > 1 ? 1 : b) * 255;
+    dest[destOffset] = r <= 0 ? 0 : r >= 1 ? 255 : Math.sqrt(r) * 255 | 0;
+    dest[destOffset + 1] = g <= 0 ? 0 : g >= 1 ? 255 : Math.sqrt(g) * 255 | 0;
+    dest[destOffset + 2] = b <= 0 ? 0 : b >= 1 ? 255 : Math.sqrt(b) * 255 | 0;
   }
 
   LabCS.prototype = {
-    getRgb: function LabCS_getRgb(src, srcOffset) {
-      var rgb = new Uint8Array(3);
-      convertToRgb(this, src, srcOffset, false, rgb, 0);
-      return rgb;
-    },
+    getRgb: ColorSpace.prototype.getRgb,
     getRgbItem: function LabCS_getRgbItem(src, srcOffset, dest, destOffset) {
       convertToRgb(this, src, srcOffset, false, dest, destOffset);
     },
     getRgbBuffer: function LabCS_getRgbBuffer(src, srcOffset, count,
                                               dest, destOffset, bits,
                                               alpha01) {
       var maxVal = (1 << bits) - 1;
       for (var i = 0; i < count; i++) {
@@ -2876,16 +2867,18 @@ var Annotation = (function AnnotationClo
       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();
       }.bind(this));
 
       return promise;
     }
   };
 
   Annotation.getConstructor =
       function Annotation_getConstructor(subtype, fieldType) {
@@ -4338,21 +4331,23 @@ var PDFDocument = (function PDFDocumentC
     this.stream = stream;
     var xref = new XRef(this.stream, password, pdfManager);
     this.xref = xref;
   }
 
   function find(stream, needle, limit, backwards) {
     var pos = stream.pos;
     var end = stream.end;
-    var str = '';
+    var strBuf = [];
     if (pos + limit > end)
       limit = end - pos;
-    for (var n = 0; n < limit; ++n)
-      str += String.fromCharCode(stream.getByte());
+    for (var n = 0; n < limit; ++n) {
+      strBuf.push(String.fromCharCode(stream.getByte()));
+    }
+    var str = strBuf.join('');
     stream.pos = pos;
     var index = backwards ? str.lastIndexOf(needle) : str.indexOf(needle);
     if (index == -1)
       return false; /* not found */
     stream.pos += index;
     return true; /* found */
   }
 
@@ -4563,34 +4558,38 @@ var PDFDocument = (function PDFDocumentC
 
 var Name = (function NameClosure() {
   function Name(name) {
     this.name = name;
   }
 
   Name.prototype = {};
 
+  var nameCache = {};
+
+  Name.get = function Name_get(name) {
+    var nameValue = nameCache[name];
+    return nameValue ? nameValue : (nameCache[name] = new Name(name));
+  };
+
   return Name;
 })();
 
 var Cmd = (function CmdClosure() {
   function Cmd(cmd) {
     this.cmd = cmd;
   }
 
   Cmd.prototype = {};
 
   var cmdCache = {};
 
   Cmd.get = function Cmd_get(cmd) {
     var cmdValue = cmdCache[cmd];
-    if (cmdValue)
-      return cmdValue;
-
-    return cmdCache[cmd] = new Cmd(cmd);
+    return cmdValue ? cmdValue : (cmdCache[cmd] = new Cmd(cmd));
   };
 
   return Cmd;
 })();
 
 var Dict = (function DictClosure() {
   var nonSerializable = function nonSerializableClosure() {
     return nonSerializable; // creating closure on some variable
@@ -4767,18 +4766,19 @@ var Catalog = (function CatalogClosure()
       'catalog object is not a dictionary');
 
     this.pagePromises = [];
   }
 
   Catalog.prototype = {
     get metadata() {
       var streamRef = this.catDict.getRaw('Metadata');
-      if (!isRef(streamRef))
+      if (!isRef(streamRef)) {
         return shadow(this, 'metadata', null);
+      }
 
       var encryptMetadata = !this.xref.encrypt ? false :
         this.xref.encrypt.encryptMetadata;
 
       var stream = this.xref.fetch(streamRef, !encryptMetadata);
       var metadata;
       if (stream && isDict(stream.dict)) {
         var type = stream.dict.get('Type');
@@ -4829,27 +4829,30 @@ var Catalog = (function CatalogClosure()
         if (isRef(obj)) {
           var queue = [{obj: obj, parent: root}];
           // to avoid recursion keeping track of the items
           // in the processed dictionary
           processed.put(obj);
           while (queue.length > 0) {
             var i = queue.shift();
             var outlineDict = xref.fetchIfRef(i.obj);
-            if (outlineDict === null)
+            if (outlineDict === null) {
               continue;
-            if (!outlineDict.has('Title'))
+            }
+            if (!outlineDict.has('Title')) {
               error('Invalid outline item');
+            }
             var dest = outlineDict.get('A');
-            if (dest)
+            if (dest) {
               dest = dest.get('D');
-            else if (outlineDict.has('Dest')) {
+            } else if (outlineDict.has('Dest')) {
               dest = outlineDict.getRaw('Dest');
-              if (isName(dest))
+              if (isName(dest)) {
                 dest = dest.name;
+              }
             }
             var title = outlineDict.get('Title');
             var outlineItem = {
               dest: dest,
               title: stringToPDFString(title),
               color: outlineDict.get('C') || [0, 0, 0],
               count: outlineDict.get('Count'),
               bold: !!(outlineDict.get('F') & 2),
@@ -4884,26 +4887,29 @@ var Catalog = (function CatalogClosure()
     get destinations() {
       function fetchDestination(dest) {
         return isDict(dest) ? dest.get('D') : dest;
       }
 
       var xref = this.xref;
       var dests = {}, nameTreeRef, nameDictionaryRef;
       var obj = this.catDict.get('Names');
-      if (obj)
+      if (obj) {
         nameTreeRef = obj.getRaw('Dests');
-      else if (this.catDict.has('Dests'))
+      } else if (this.catDict.has('Dests')) {
         nameDictionaryRef = this.catDict.get('Dests');
+      }
 
       if (nameDictionaryRef) {
         // reading simple destination dictionary
         obj = nameDictionaryRef;
         obj.forEach(function catalogForEach(key, value) {
-          if (!value) return;
+          if (!value) {
+            return;
+          }
           dests[key] = fetchDestination(value);
         });
       }
       if (nameTreeRef) {
         var nameTree = new NameTree(nameTreeRef, xref);
         var names = nameTree.getAll();
         for (var name in names) {
           if (!names.hasOwnProperty(name)) {
@@ -5157,32 +5163,32 @@ var XRef = (function XRefClosure() {
           parserBuf1: parser.buf1,
           parserBuf2: parser.buf2
         };
       }
 
       var obj = this.readXRefTable(parser);
 
       // Sanity check
-      if (!isCmd(obj, 'trailer'))
+      if (!isCmd(obj, 'trailer')) {
         error('Invalid XRef table: could not find trailer dictionary');
-
+      }
       // Read trailer dictionary, e.g.
       // trailer
       //    << /Size 22
       //      /Root 20R
       //      /Info 10R
       //      /ID [ <81b14aafa313db63dbd6f981e49f94f4> ]
       //    >>
       // The parser goes through the entire stream << ... >> and provides
       // a getter interface for the key-value table
       var dict = parser.getObj();
-      if (!isDict(dict))
+      if (!isDict(dict)) {
         error('Invalid XRef table: could not parse trailer dictionary');
-
+      }
       delete this.tableState;
 
       return dict;
     },
 
     readXRefTable: function XRef_readXRefTable(parser) {
       // Example of cross-reference table:
       // xref
@@ -5209,19 +5215,19 @@ var XRef = (function XRefClosure() {
             break;
           }
           tableState.firstEntryNum = obj;
           tableState.entryCount = parser.getObj();
         }
 
         var first = tableState.firstEntryNum;
         var count = tableState.entryCount;
-        if (!isInt(first) || !isInt(count))
+        if (!isInt(first) || !isInt(count)) {
           error('Invalid XRef table: wrong types in subsection header');
-
+        }
         // Inner loop is over objects themselves
         for (var i = tableState.entryNum; i < count; i++) {
           tableState.streamPos = stream.pos;
           tableState.entryNum = i;
           tableState.parserBuf1 = parser.buf1;
           tableState.parserBuf2 = parser.buf2;
 
           var entry = {};
@@ -5237,18 +5243,19 @@ var XRef = (function XRefClosure() {
           // Validate entry obj
           if (!isInt(entry.offset) || !isInt(entry.gen) ||
               !(entry.free || entry.uncompressed)) {
             console.log(entry.offset, entry.gen, entry.free,
                 entry.uncompressed);
             error('Invalid entry in XRef subsection: ' + first + ', ' + count);
           }
 
-          if (!this.entries[i + first])
+          if (!this.entries[i + first]) {
             this.entries[i + first] = entry;
+          }
         }
 
         tableState.entryNum = 0;
         tableState.streamPos = stream.pos;
         tableState.parserBuf1 = parser.buf1;
         tableState.parserBuf2 = parser.buf2;
         delete tableState.firstEntryNum;
         delete tableState.entryCount;
@@ -5256,19 +5263,19 @@ var XRef = (function XRefClosure() {
 
       // Per issue 3248: hp scanners generate bad XRef
       if (first === 1 && this.entries[1] && this.entries[1].free) {
         // shifting the entries
         this.entries.shift();
       }
 
       // Sanity check: as per spec, first object must be free
-      if (this.entries[0] && !this.entries[0].free)
+      if (this.entries[0] && !this.entries[0].free) {
         error('Invalid XRef table: unexpected first object');
-
+      }
       return obj;
     },
 
     processXRefStream: function XRef_processXRefStream(stream) {
       if (!('streamState' in this)) {
         // Stores state of the stream as we process it so we can resume
         // from middle of stream in case of missing data error
         var streamParameters = stream.dict;
@@ -5302,61 +5309,66 @@ var XRef = (function XRefClosure() {
       var generationFieldWidth = byteWidths[2];
 
       var entryRanges = streamState.entryRanges;
       while (entryRanges.length > 0) {
 
         var first = entryRanges[0];
         var n = entryRanges[1];
 
-        if (!isInt(first) || !isInt(n))
+        if (!isInt(first) || !isInt(n)) {
           error('Invalid XRef range fields: ' + first + ', ' + n);
-
+        }
         if (!isInt(typeFieldWidth) || !isInt(offsetFieldWidth) ||
             !isInt(generationFieldWidth)) {
           error('Invalid XRef entry fields length: ' + first + ', ' + n);
         }
         for (i = streamState.entryNum; i < n; ++i) {
           streamState.entryNum = i;
           streamState.streamPos = stream.pos;
 
           var type = 0, offset = 0, generation = 0;
           for (j = 0; j < typeFieldWidth; ++j)
             type = (type << 8) | stream.getByte();
           // if type field is absent, its default value = 1
-          if (typeFieldWidth === 0)
+          if (typeFieldWidth === 0) {
             type = 1;
-          for (j = 0; j < offsetFieldWidth; ++j)
+          }
+          for (j = 0; j < offsetFieldWidth; ++j) {
             offset = (offset << 8) | stream.getByte();
-          for (j = 0; j < generationFieldWidth; ++j)
+          }
+          for (j = 0; j < generationFieldWidth; ++j) {
             generation = (generation << 8) | stream.getByte();
+          }
           var entry = {};
           entry.offset = offset;
           entry.gen = generation;
           switch (type) {
             case 0:
               entry.free = true;
               break;
             case 1:
               entry.uncompressed = true;
               break;
             case 2:
               break;
             default:
               error('Invalid XRef entry type: ' + type);
           }
-          if (!this.entries[first + i])
+          if (!this.entries[first + i]) {
             this.entries[first + i] = entry;
+          }
         }
 
         streamState.entryNum = 0;
         streamState.streamPos = stream.pos;
         entryRanges.splice(0, 2);
       }
     },
+
     indexObjects: function XRef_indexObjects() {
       // Simple scan through the PDF content to find objects,
       // trailers and XRef streams.
       function readToken(data, offset) {
         var token = '', ch = data[offset];
         while (ch !== 13 && ch !== 10) {
           if (++offset >= data.length)
             break;
@@ -5366,18 +5378,19 @@ var XRef = (function XRefClosure() {
         return token;
       }
       function skipUntil(data, offset, what) {
         var length = what.length, dataLength = data.length;
         var skipped = 0;
         // finding byte sequence
         while (offset < dataLength) {
           var i = 0;
-          while (i < length && data[offset + i] == what[i])
+          while (i < length && data[offset + i] == what[i]) {
             ++i;
+          }
           if (i >= length)
             break; // sequence found
 
           offset++;
           skipped++;
         }
         return skipped;
       }
@@ -5431,55 +5444,60 @@ var XRef = (function XRefClosure() {
           var xrefTagOffset = skipUntil(content, 0, xrefBytes);
           if (xrefTagOffset < contentLength &&
               content[xrefTagOffset + 5] < 64) {
             xrefStms.push(position);
             this.xrefstms[position] = 1; // don't read it recursively
           }
 
           position += contentLength;
-        } else
+        } else {
           position += token.length + 1;
+        }
       }
       // reading XRef streams
       for (var i = 0, ii = xrefStms.length; i < ii; ++i) {
         this.startXRefQueue.push(xrefStms[i]);
         this.readXRef(/* recoveryMode */ true);
       }
       // finding main trailer
       var dict;
       for (var i = 0, ii = trailers.length; i < ii; ++i) {
         stream.pos = trailers[i];
         var parser = new Parser(new Lexer(stream), true, null);
         var obj = parser.getObj();
-        if (!isCmd(obj, 'trailer'))
+        if (!isCmd(obj, 'trailer')) {
           continue;
+        }
         // read the trailer dictionary
-        if (!isDict(dict = parser.getObj()))
+        if (!isDict(dict = parser.getObj())) {
           continue;
+        }
         // taking the first one with 'ID'
-        if (dict.has('ID'))
+        if (dict.has('ID')) {
           return dict;
+        }
       }
       // no tailer with 'ID', taking last one (if exists)
-      if (dict)
+      if (dict) {
         return dict;
+      }
       // nothing helps
       // calling error() would reject worker with an UnknownErrorException.
       throw new InvalidPDFException('Invalid PDF structure');
     },
 
     readXRef: function XRef_readXRef(recoveryMode) {
       var stream = this.stream;
 
       try {
         while (this.startXRefQueue.length) {
           var startXRef = this.startXRefQueue[0];
 
-          stream.pos = startXRef;
+          stream.pos = startXRef + stream.start;
 
           var parser = new Parser(new Lexer(stream), true, null);
           var obj = parser.getObj();
           var dict;
 
           // Get dictionary
           if (isCmd(obj, 'xref')) {
 
@@ -5508,18 +5526,19 @@ var XRef = (function XRefClosure() {
                 !isStream(obj = parser.getObj())) {
               error('Invalid XRef stream');
             }
             dict = this.processXRefStream(obj);
             if (!this.topDict) {
               this.topDict = dict;
             }
 
-            if (!dict)
+            if (!dict) {
               error('Failed to read XRef stream');
+            }
           } else {
             error('Invalid XRef stream header');
           }
 
           // Recursively get previous dictionary, if any
           obj = dict.get('Prev');
           if (isInt(obj)) {
             this.startXRefQueue.push(obj);
@@ -5535,105 +5554,122 @@ var XRef = (function XRefClosure() {
         return this.topDict;
       } catch (e) {
         if (e instanceof MissingDataException) {
           throw e;
         }
         info('(while reading XRef): ' + e);
       }
 
-      if (recoveryMode)
+      if (recoveryMode) {
         return;
+      }
       throw new XRefParseException();
     },
 
     getEntry: function XRef_getEntry(i) {
-      var e = this.entries[i];
-      if (e === null)
-        return null;
-      return e.free || !e.offset ? null : e; // returns null if entry is free
-    },
+      var xrefEntry = this.entries[i];
+      if (xrefEntry !== null && !xrefEntry.free && xrefEntry.offset) {
+        return xrefEntry;
+      }
+      return null;
+    },
+
     fetchIfRef: function XRef_fetchIfRef(obj) {
-      if (!isRef(obj))
+      if (!isRef(obj)) {
         return obj;
+      }
       return this.fetch(obj);
     },
+
     fetch: function XRef_fetch(ref, suppressEncryption) {
       assertWellFormed(isRef(ref), 'ref object is not a reference');
       var num = ref.num;
-      var e;
       if (num in this.cache) {
-        e = this.cache[num];
-        if (e instanceof Stream) {
-          return e.makeSubStream(e.start, e.length, e.dict);
-        }
-        return e;
-      }
-
-      e = this.getEntry(num);
+        var cacheEntry = this.cache[num];
+        return cacheEntry;
+      }
+
+      var xrefEntry = this.getEntry(num);
 
       // the referenced entry can be free
-      if (e === null)
-        return (this.cache[num] = e);
-
+      if (xrefEntry === null) {
+        return (this.cache[num] = null);
+      }
+
+      if (xrefEntry.uncompressed) {
+        return this.fetchUncompressed(ref, xrefEntry, suppressEncryption);
+      } else {
+        return this.fetchCompressed(xrefEntry, suppressEncryption);
+      }
+    },
+
+    fetchUncompressed: function XRef_fetchUncompressed(ref,
+                                                       xrefEntry,
+                                                       suppressEncryption) {
       var gen = ref.gen;
-      var stream, parser;
-      if (e.uncompressed) {
-        if (e.gen != gen)
-          error('inconsistent generation in XRef');
-        stream = this.stream.makeSubStream(e.offset);
-        parser = new Parser(new Lexer(stream), true, this);
-        var obj1 = parser.getObj();
-        var obj2 = parser.getObj();
-        var obj3 = parser.getObj();
-        if (!isInt(obj1) || obj1 != num ||
-            !isInt(obj2) || obj2 != gen ||
-            !isCmd(obj3)) {
-          error('bad XRef entry');
-        }
-        if (!isCmd(obj3, 'obj')) {
-          // some bad pdfs use "obj1234" and really mean 1234
-          if (obj3.cmd.indexOf('obj') === 0) {
-            num = parseInt(obj3.cmd.substring(3), 10);
-            if (!isNaN(num))
-              return num;
-          }
-          error('bad XRef entry');
-        }
-        if (this.encrypt && !suppressEncryption) {
-          try {
-            e = parser.getObj(this.encrypt.createCipherTransform(num, gen));
-          } catch (ex) {
-            // almost all streams must be encrypted, but sometimes
-            // they are not probably due to some broken generators
-            // re-trying without encryption
-            return this.fetch(ref, true);
-          }
-        } else {
-          e = parser.getObj();
-        }
-        if (!isStream(e)) {
-          this.cache[num] = e;
-        }
-        return e;
-      }
-
-      // compressed entry
-      var tableOffset = e.offset;
-      stream = this.fetch(new Ref(tableOffset, 0));
-      if (!isStream(stream))
+      var num = ref.num;
+      if (xrefEntry.gen !== gen) {
+        error('inconsistent generation in XRef');
+      }
+      var stream = this.stream.makeSubStream(xrefEntry.offset +
+                                             this.stream.start);
+      var parser = new Parser(new Lexer(stream), true, this);
+      var obj1 = parser.getObj();
+      var obj2 = parser.getObj();
+      var obj3 = parser.getObj();
+      if (!isInt(obj1) || parseInt(obj1, 10) !== num ||
+          !isInt(obj2) || parseInt(obj2, 10) !== gen ||
+          !isCmd(obj3)) {
+        error('bad XRef entry');
+      }
+      if (!isCmd(obj3, 'obj')) {
+        // some bad pdfs use "obj1234" and really mean 1234
+        if (obj3.cmd.indexOf('obj') === 0) {
+          num = parseInt(obj3.cmd.substring(3), 10);
+          if (!isNaN(num)) {
+            return num;
+          }
+        }
+        error('bad XRef entry');
+      }
+      if (this.encrypt && !suppressEncryption) {
+        try {
+          xrefEntry = parser.getObj(this.encrypt.createCipherTransform(num,
+                                                                       gen));
+        } catch (ex) {
+          // almost all streams must be encrypted, but sometimes
+          // they are not probably due to some broken generators
+          // re-trying without encryption
+          return this.fetch(ref, true);
+        }
+      } else {
+        xrefEntry = parser.getObj();
+      }
+      if (!isStream(xrefEntry)) {
+        this.cache[num] = xrefEntry;
+      }
+      return xrefEntry;
+    },
+
+    fetchCompressed: function XRef_fetchCompressed(xrefEntry,
+                                                   suppressEncryption) {
+      var tableOffset = xrefEntry.offset;
+      var stream = this.fetch(new Ref(tableOffset, 0));
+      if (!isStream(stream)) {
         error('bad ObjStm stream');
+      }
       var first = stream.dict.get('First');
       var n = stream.dict.get('N');
       if (!isInt(first) || !isInt(n)) {
         error('invalid first and n parameters for ObjStm stream');
       }
-      parser = new Parser(new Lexer(stream), false, this);
+      var parser = new Parser(new Lexer(stream), false, this);
       parser.allowStreams = true;
-      var i, entries = [], nums = [];
+      var i, entries = [], num, nums = [];
       // read the object numbers to populate cache
       for (i = 0; i < n; ++i) {
         num = parser.getObj();
         if (!isInt(num)) {
           error('invalid object number in the ObjStm stream: ' + num);
         }
         nums.push(num);
         var offset = parser.getObj();
@@ -5645,46 +5681,49 @@ var XRef = (function XRefClosure() {
       for (i = 0; i < n; ++i) {
         entries.push(parser.getObj());
         num = nums[i];
         var entry = this.entries[num];
         if (entry && entry.offset === tableOffset && entry.gen === i) {
           this.cache[num] = entries[i];
         }
       }
-      e = entries[e.gen];
-      if (e === undefined) {
+      xrefEntry = entries[xrefEntry.gen];
+      if (xrefEntry === undefined) {
         error('bad XRef entry for compressed object');
       }
-      return e;
-    },
+      return xrefEntry;
+    },
+
     fetchIfRefAsync: function XRef_fetchIfRefAsync(obj) {
       if (!isRef(obj)) {
         var promise = new LegacyPromise();
         promise.resolve(obj);
         return promise;
       }
       return this.fetchAsync(obj);
     },
+
     fetchAsync: function XRef_fetchAsync(ref, suppressEncryption) {
       var promise = new LegacyPromise();
       var tryFetch = function (promise) {
         try {
           promise.resolve(this.fetch(ref, suppressEncryption));
         } catch (e) {
           if (e instanceof MissingDataException) {
             this.stream.manager.requestRange(e.begin, e.end, tryFetch);
             return;
           }
           promise.reject(e);
         }
       }.bind(this, promise);
       tryFetch();
       return promise;
     },
+
     getCatalogObj: function XRef_getCatalogObj() {
       return this.root;
     }
   };
 
   return XRef;
 })();
 
@@ -5715,18 +5754,19 @@ var NameTree = (function NameTreeClosure
         var obj = xref.fetchIfRef(queue.shift());
         if (!isDict(obj)) {
           continue;
         }
         if (obj.has('Kids')) {
           var kids = obj.get('Kids');
           for (i = 0, n = kids.length; i < n; i++) {
             var kid = kids[i];
-            if (processed.has(kid))
+            if (processed.has(kid)) {
               error('invalid destinations');
+            }
             queue.push(kid);
             processed.put(kid);
           }
           continue;
         }
         var names = obj.get('Names');
         if (names) {
           for (i = 0, n = names.length; i < n; i += 2) {
@@ -5879,18 +5919,16 @@ var ObjectLoader = (function() {
     }
 
   };
 
   return ObjectLoader;
 })();
 
 
-
-
 var ISOAdobeCharset = [
   '.notdef', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar',
   'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright',
   'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero',
   'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight',
   'nine', 'colon', 'semicolon', 'less', 'equal', 'greater', 'question',
   'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
   'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
@@ -13443,17 +13481,17 @@ var CipherTransformFactory = (function C
       }
     } else {
       cipher = new ARCFourCipher(hash.subarray(0, keyLengthInBytes));
       userPassword = cipher.encryptBlock(ownerPassword);
     }
     return userPassword;
   }
 
-  var identityName = new Name('Identity');
+  var identityName = Name.get('Identity');
 
   function CipherTransformFactory(dict, fileId, password) {
     var filter = dict.get('Filter');
     if (!isName(filter) || filter.name != 'Standard')
       error('unknown encryption method');
     this.dict = dict;
     var algorithm = dict.get('V');
     if (!isInt(algorithm) ||
@@ -14433,18 +14471,16 @@ var PartialEvaluator = (function Partial
       }
       return false;
     },
 
     buildFormXObject: function PartialEvaluator_buildFormXObject(resources,
                                                                  xobj, smask,
                                                                  operatorList,
                                                                  state) {
-      var self = this;
-
       var matrix = xobj.dict.get('Matrix');
       var bbox = xobj.dict.get('BBox');
       var group = xobj.dict.get('Group');
       if (group) {
         var groupOptions = {
           matrix: matrix,
           bbox: bbox,
           smask: smask,
@@ -14512,17 +14548,19 @@ var PartialEvaluator = (function Partial
 
       var SMALL_IMAGE_DIMENSIONS = 200;
       // Inlining small images into the queue as RGB data
       if (inline && !softMask && !mask &&
           !(image instanceof JpegStream) &&
           (w + h) < SMALL_IMAGE_DIMENSIONS) {
         var imageObj = new PDFImage(this.xref, resources, image,
                                     inline, null, null);
-        var imgData = imageObj.createImageData();
+        // We force the use of RGBA_32BPP images here, because we can't handle
+        // any other kind.
+        var imgData = imageObj.createImageData(/* forceRGBA = */ true);
         operatorList.addOp(OPS.paintInlineImageXObject, [imgData]);
         return;
       }
 
       // If there is no imageMask, create the PDFImage and a lot
       // of image processing can be done here.
       var uniquePrefix = this.uniquePrefix || '';
       var objId = 'img_' + uniquePrefix + (++this.idCounters.obj);
@@ -14535,17 +14573,17 @@ var PartialEvaluator = (function Partial
         operatorList.addOp(OPS.paintJpegXObject, args);
         this.handler.send(
             'obj', [objId, this.pageIndex, 'JpegStream', image.getIR()]);
         return;
       }
 
 
       PDFImage.buildImage(function(imageObj) {
-          var imgData = imageObj.createImageData();
+          var imgData = imageObj.createImageData(/* forceRGBA = */ false);
           self.handler.send('obj', [objId, self.pageIndex, 'Image', imgData],
                             null, [imgData.data.buffer]);
         }, self.handler, self.xref, resources, image, inline);
 
       operatorList.addOp(OPS.paintImageXObject, args);
     },
 
     handleSMask: function PartialEvaluator_handleSmask(smask, resources,
@@ -15122,20 +15160,16 @@ var PartialEvaluator = (function Partial
 
           if (chunk !== '') {
             var bidiResult = PDFJS.bidi(chunk, -1, font.vertical);
             var bidiText = {
               str: bidiResult.str,
               dir: bidiResult.dir
             };
             var renderParams = textState.calcRenderParams(preprocessor.ctm);
-            bidiText.x = renderParams.renderMatrix[4] - (textState.fontSize *
-                           renderParams.vScale * Math.sin(renderParams.angle));
-            bidiText.y = renderParams.renderMatrix[5] + (textState.fontSize *
-                           renderParams.vScale * Math.cos(renderParams.angle));
             var fontHeight = textState.fontSize * renderParams.vScale;
             var fontAscent = font.ascent ? font.ascent * fontHeight :
               font.descent ? (1 + font.descent) * fontHeight : fontHeight;
             bidiText.x = renderParams.renderMatrix[4] - (fontAscent *
                            Math.sin(renderParams.angle));
             bidiText.y = renderParams.renderMatrix[5] + (fontAscent *
                            Math.cos(renderParams.angle));
             if (bidiText.dir == 'ttb') {
@@ -15450,17 +15484,17 @@ var PartialEvaluator = (function Partial
       var maxCharIndex = composite ? 0xFFFF : 0xFF;
 
       var descriptor = dict.get('FontDescriptor');
       if (!descriptor) {
         if (type.name == 'Type3') {
           // FontDescriptor is only required for Type3 fonts when the document
           // is a tagged pdf. Create a barbebones one to get by.
           descriptor = new Dict();
-          descriptor.set('FontName', new Name(type.name));
+          descriptor.set('FontName', Name.get(type.name));
         } else {
           // Before PDF 1.5 if the font was one of the base 14 fonts, having a
           // FontDescriptor was not required.
           // This case is here for compatibility.
           var baseFontName = dict.get('BaseFont');
           if (!isName(baseFontName))
             error('Base font is not specified');
 
@@ -15498,20 +15532,20 @@ var PartialEvaluator = (function Partial
       // a variant.
       var firstChar = dict.get('FirstChar') || 0;
       var lastChar = dict.get('LastChar') || maxCharIndex;
 
       var fontName = descriptor.get('FontName');
       var baseFont = dict.get('BaseFont');
       // Some bad pdf's have a string as the font name.
       if (isString(fontName)) {
-        fontName = new Name(fontName);
+        fontName = Name.get(fontName);
       }
       if (isString(baseFont)) {
-        baseFont = new Name(baseFont);
+        baseFont = Name.get(baseFont);
       }
 
       if (type.name !== 'Type3') {
         var fontNameStr = fontName && fontName.name;
         var baseFontStr = baseFont && baseFont.name;
         if (fontNameStr !== baseFontStr) {
           info('The FontDescriptor\'s FontName is "' + fontNameStr +
                '" but should be the same as the Font\'s BaseFont "' +
@@ -15665,17 +15699,17 @@ var PartialEvaluator = (function Partial
             data[offset + rowSize + 2] = data[offset + rowSize - 2];
             data[offset + rowSize + 3] = data[offset + rowSize - 1];
             offset -= imgRowSize;
           }
         }
         // replacing queue items
         squash(fnArray, j, count * 4, OPS.paintInlineImageXObjectGroup);
         argsArray.splice(j, count * 4,
-          [{width: imgWidth, height: imgHeight, kind: 'rgba_32bpp',
+          [{width: imgWidth, height: imgHeight, kind: ImageKind.RGBA_32BPP,
             data: imgData}, map]);
         i = j;
         ii = argsArray.length;
       }
     }
     // grouping paintImageMaskXObject's into paintImageMaskXObjectGroup
     // searching for (save, transform, paintImageMaskXObject, restore)+
     var MIN_IMAGES_IN_MASKS_BLOCK = 10;
@@ -15736,17 +15770,17 @@ var OperatorList = (function OperatorLis
     // When there isn't a message handler the fn array needs to be able to grow
     // since we can't flush the operators.
     if (messageHandler) {
       this.fnArray = new Uint8Array(CHUNK_SIZE);
     } else {
       this.fnArray = [];
     }
     this.argsArray = [];
-    this.dependencies = {},
+    this.dependencies = {};
     this.pageIndex = pageIndex;
     this.fnIndex = 0;
   }
 
   OperatorList.prototype = {
 
     get length() {
       return this.argsArray.length;
@@ -15823,21 +15857,21 @@ var TextState = (function TextStateClosu
     //textState variables
     this.leading = 0;
     this.textHScale = 1;
     this.textRise = 0;
   }
   TextState.prototype = {
     initialiseTextObj: function TextState_initialiseTextObj() {
       var m = this.textMatrix;
-      m[0] = 1, m[1] = 0, m[2] = 0, m[3] = 1, m[4] = 0, m[5] = 0;
+      m[0] = 1; m[1] = 0; m[2] = 0; m[3] = 1; m[4] = 0; m[5] = 0;
     },
     setTextMatrix: function TextState_setTextMatrix(a, b, c, d, e, f) {
       var m = this.textMatrix;
-      m[0] = a, m[1] = b, m[2] = c, m[3] = d, m[4] = e, m[5] = f;
+      m[0] = a; m[1] = b; m[2] = c; m[3] = d; m[4] = e; m[5] = f;
     },
     translateTextMatrix: function TextState_translateTextMatrix(x, y) {
       var m = this.textMatrix;
       m[4] = m[0] * x + m[2] * y + m[4];
       m[5] = m[1] * x + m[3] * y + m[5];
     },
     calcRenderParams: function TextState_calcRenderingParams(cm) {
       var tm = this.textMatrix;
@@ -16672,16 +16706,25 @@ function big5ToUnicode(str) {
 // Some characters, e.g. copyrightserif, mapped to the private use area and
 // might not be displayed using standard fonts. Mapping/hacking well-known chars
 // to the similar equivalents in the normal characters range.
 function mapPrivateUseChars(code) {
   switch (code) {
     case 0xF8E9: // copyrightsans
     case 0xF6D9: // copyrightserif
       return 0x00A9; // copyright
+
+    case 0xF8E8: // registersans
+    case 0xF6DA: // registerserif
+      return 0x00AE; // registered
+
+    case 0xF8EA: // trademarksans
+    case 0xF6DB: // trademarkserif
+      return 0x2122; // trademark
+
     default:
       return code;
   }
 }
 
 var UnicodeRanges = [
   { 'begin': 0x0000, 'end': 0x007F }, // Basic Latin
   { 'begin': 0x0080, 'end': 0x00FF }, // Latin-1 Supplement
@@ -18455,21 +18498,21 @@ var Font = (function FontClosure() {
     var array = [];
     for (var i = 0, ii = str.length; i < ii; ++i)
       array[i] = str.charCodeAt(i);
 
     return array;
   }
 
   function arrayToString(arr) {
-    var str = '';
-    for (var i = 0, ii = arr.length; i < ii; ++i)
-      str += String.fromCharCode(arr[i]);
-
-    return str;
+    var strBuf = [];
+    for (var i = 0, ii = arr.length; i < ii; ++i) {
+      strBuf.push(String.fromCharCode(arr[i]));
+    }
+    return strBuf.join('');
   }
 
   function int16(bytes) {
     return (bytes[0] << 8) + (bytes[1] & 0xff);
   }
 
   function int32(bytes) {
     return (bytes[0] << 24) + (bytes[1] << 16) +
@@ -18874,20 +18917,21 @@ var Font = (function FontClosure() {
     ];
 
     // Mac want 1-byte per character strings while Windows want
     // 2-bytes per character, so duplicate the names table
     var stringsUnicode = [];
     for (var i = 0, ii = strings.length; i < ii; i++) {
       var str = proto[1][i] || strings[i];
 
-      var strUnicode = '';
-      for (var j = 0, jj = str.length; j < jj; j++)
-        strUnicode += string16(str.charCodeAt(j));
-      stringsUnicode.push(strUnicode);
+      var strBufUnicode = [];
+      for (var j = 0, jj = str.length; j < jj; j++) {
+        strBufUnicode.push(string16(str.charCodeAt(j)));
+      }
+      stringsUnicode.push(strBufUnicode.join(''));
     }
 
     var names = [strings, stringsUnicode];
     var platforms = ['\x00\x01', '\x00\x03'];
     var encodings = ['\x00\x00', '\x00\x01'];
     var languages = ['\x00\x00', '\x04\x09'];
 
     var namesRecordCount = strings.length * platforms.length;
@@ -19780,16 +19824,21 @@ var Font = (function FontClosure() {
         }
         foldTTTable(table, content);
       }
 
       function checkInvalidFunctions(ttContext, maxFunctionDefs) {
         if (ttContext.tooComplexToFollowFunctions) {
           return;
         }
+        if (ttContext.functionsDefined.length > maxFunctionDefs) {
+          warn('TT: more functions defined than expected');
+          ttContext.hintsValid = false;
+          return;
+        }
         for (var j = 0, jj = ttContext.functionsUsed.length; j < jj; j++) {
           if (j > maxFunctionDefs) {
             warn('TT: invalid function id: ' + j);
             ttContext.hintsValid = false;
             return;
           }
           if (ttContext.functionsUsed[j] && !ttContext.functionsDefined[j]) {
             warn('TT: undefined function: ' + j);
@@ -28187,41 +28236,43 @@ var PDFImage = (function PDFImageClosure
       // var colorspace = ...
     }
     // TODO cache rendered images?
 
     var dict = image.dict;
     this.width = dict.get('Width', 'W');
     this.height = dict.get('Height', 'H');
 
-    if (this.width < 1 || this.height < 1)
+    if (this.width < 1 || this.height < 1) {
       error('Invalid image width: ' + this.width + ' or height: ' +
             this.height);
+    }
 
     this.interpolate = dict.get('Interpolate', 'I') || false;
     this.imageMask = dict.get('ImageMask', 'IM') || false;
     this.matte = dict.get('Matte') || false;
 
     var bitsPerComponent = image.bitsPerComponent;
     if (!bitsPerComponent) {
       bitsPerComponent = dict.get('BitsPerComponent', 'BPC');
       if (!bitsPerComponent) {
-        if (this.imageMask)
+        if (this.imageMask) {
           bitsPerComponent = 1;
-        else
+        } else {
           error('Bits per component missing in image: ' + this.imageMask);
+        }
       }
     }
     this.bpc = bitsPerComponent;
 
     if (!this.imageMask) {
       var colorSpace = dict.get('ColorSpace', 'CS');
       if (!colorSpace) {
         warn('JPX images (which don"t require color spaces');
-        colorSpace = new Name('DeviceRGB');
+        colorSpace = Name.get('DeviceRGB');
       }
       this.colorSpace = ColorSpace.parse(colorSpace, xref, res);
       this.numComps = this.colorSpace.numComps;
     }
 
     this.decode = dict.get('Decode', 'D');
     this.needsDecode = false;
     if (this.decode &&
@@ -28408,33 +28459,44 @@ var PDFImage = (function PDFImageClosure
       var output = bpc <= 8 ? new Uint8Array(length) :
         bpc <= 16 ? new Uint16Array(length) : new Uint32Array(length);
       var rowComps = width * numComps;
 
       var max = (1 << bpc) - 1;
 
       if (bpc === 1) {
         // Optimization for reading 1 bpc images.
-        var mask = 0;
-        var buf = 0;
-
-        for (var i = 0, ii = length; i < ii; ++i) {
-          if (i % rowComps === 0) {
-            mask = 0;
-            buf = 0;
-          } else {
-            mask >>= 1;
-          }
-
-          if (mask <= 0) {
+        var i = 0, buf, mask, loop1End, loop2End;
+        for (var j = 0; j < height; j++) {
+          loop1End = i + (rowComps & ~7);
+          loop2End = i + rowComps;
+
+          // unroll loop for all full bytes
+          while (i < loop1End) {
+            buf = buffer[bufferPos++];
+            output[i] = (buf >> 7) & 1;
+            output[i + 1] = (buf >> 6) & 1;
+            output[i + 2] = (buf >> 5) & 1;
+            output[i + 3] = (buf >> 4) & 1;
+            output[i + 4] = (buf >> 3) & 1;
+            output[i + 5] = (buf >> 2) & 1;
+            output[i + 6] = (buf >> 1) & 1;
+            output[i + 7] = buf & 1;
+            i += 8;
+          }
+
+          // handle remaing bits
+          if (i < loop2End) {
             buf = buffer[bufferPos++];
             mask = 128;
-          }
-
-          output[i] = +!!(buf & mask);
+            while (i < loop2End) {
+              output[i++] = +!!(buf & mask);
+              mask >>= 1;
+            }
+          }
         }
       } else {
         // The general case that handles all other bpc values.
         var bits = 0, buf = 0;
         for (var i = 0, ii = length; i < ii; ++i) {
           if (i % rowComps === 0) {
             buf = 0;
             bits = 0;
@@ -28460,34 +28522,37 @@ var PDFImage = (function PDFImageClosure
       var mask = this.mask;
       var alphaBuf;
 
       if (smask) {
         var sw = smask.width;
         var sh = smask.height;
         alphaBuf = new Uint8Array(sw * sh);
         smask.fillGrayBuffer(alphaBuf);
-        if (sw != width || sh != height)
+        if (sw != width || sh != height) {
           alphaBuf = PDFImage.resize(alphaBuf, smask.bpc, 1, sw, sh, width,
                                      height);
+        }
       } else if (mask) {
         if (mask instanceof PDFImage) {
           var sw = mask.width;
           var sh = mask.height;
           alphaBuf = new Uint8Array(sw * sh);
           mask.numComps = 1;
           mask.fillGrayBuffer(alphaBuf);
 
           // Need to invert values in rgbaBuf
-          for (var i = 0, ii = sw * sh; i < ii; ++i)
+          for (var i = 0, ii = sw * sh; i < ii; ++i) {
             alphaBuf[i] = 255 - alphaBuf[i];
-
-          if (sw != width || sh != height)
+          }
+
+          if (sw != width || sh != height) {
             alphaBuf = PDFImage.resize(alphaBuf, mask.bpc, 1, sw, sh, width,
                                        height);
+          }
         } else if (isArray(mask)) {
           // Color key mask: if any of the compontents are outside the range
           // then they should be painted.
           alphaBuf = new Uint8Array(width * height);
           var numComps = this.numComps;
           for (var i = 0, ii = width * height; i < ii; ++i) {
             var opacity = 0;
             var imageOffset = i * numComps;
@@ -28506,17 +28571,17 @@ var PDFImage = (function PDFImageClosure
         }
       }
 
       if (alphaBuf) {
         for (var i = 0, j = 3, ii = width * actualHeight; i < ii; ++i, j += 4) {
           rgbaBuf[j] = alphaBuf[i];
         }
       } else {
-        // Common case: no mask (and no need to allocate the extra buffer).
+        // No mask.
         for (var i = 0, j = 3, ii = width * actualHeight; i < ii; ++i, j += 4) {
           rgbaBuf[j] = 255;
         }
       }
     },
     undoPreblend: function PDFImage_undoPreblend(buffer, width, height) {
       var matte = this.smask && this.smask.matte;
       if (!matte) {
@@ -28540,98 +28605,145 @@ var PDFImage = (function PDFImageClosure
           continue;
         }
         var k = 255 / alpha;
         buffer[i] = clamp((buffer[i] - matteRgb[0]) * k + matteRgb[0]);
         buffer[i + 1] = clamp((buffer[i + 1] - matteRgb[1]) * k + matteRgb[1]);
         buffer[i + 2] = clamp((buffer[i + 2] - matteRgb[2]) * k + matteRgb[2]);
       }
     },
-    createImageData: function PDFImage_createImageData() {
+    createImageData: function PDFImage_createImageData(forceRGBA) {
       var drawWidth = this.drawWidth;
       var drawHeight = this.drawHeight;
       var imgData = {       // other fields are filled in below
         width: drawWidth,
-        height: drawHeight,
+        height: drawHeight
       };
 
       var numComps = this.numComps;
       var originalWidth = this.width;
       var originalHeight = this.height;
       var bpc = this.bpc;
 
-      // rows start at byte boundary;
+      // Rows start at byte boundary.
       var rowBytes = (originalWidth * numComps * bpc + 7) >> 3;
       var imgArray = this.getImageBytes(originalHeight * rowBytes);
 
-      // imgArray can be incomplete (e.g. after CCITT fax encoding)
+      if (!forceRGBA) {
+        // If it is a 1-bit-per-pixel grayscale (i.e. black-and-white) image
+        // without any complications, we pass a same-sized copy to the main
+        // thread rather than expanding by 32x to RGBA form. This saves *lots*
+        // of memory for many scanned documents. It's also much faster.
+        //
+        // Similarly, if it is a 24-bit-per pixel RGB image without any
+        // complications, we avoid expanding by 1.333x to RGBA form.
+        var kind;
+        if (this.colorSpace.name === 'DeviceGray' && bpc === 1) {
+          kind = ImageKind.GRAYSCALE_1BPP;
+        } else if (this.colorSpace.name === 'DeviceRGB' && bpc === 8) {
+          kind = ImageKind.RGB_24BPP;
+        }
+        if (kind && !this.smask && !this.mask && !this.needsDecode &&
+            drawWidth === originalWidth && drawHeight === originalHeight) {
+          imgData.kind = kind;
+
+          // If imgArray came from a DecodeStream, we're safe to transfer it
+          // (and thus neuter it) because it will constitute the entire
+          // DecodeStream's data.  But if it came from a Stream, we need to
+          // copy it because it'll only be a portion of the Stream's data, and
+          // the rest will be read later on.
+          if (this.image instanceof DecodeStream) {
+            imgData.data = imgArray;
+          } else {
+            var newArray = new Uint8Array(imgArray.length);
+            newArray.set(imgArray);
+            imgData.data = newArray;
+          }
+          return imgData;
+        }
+      }
+
+      // imgArray can be incomplete (e.g. after CCITT fax encoding).
       var actualHeight = 0 | (imgArray.length / rowBytes *
                          drawHeight / originalHeight);
 
-      // If it is a 1-bit-per-pixel grayscale (i.e. black-and-white) image
-      // without any complications, we pass a same-sized copy to the main
-      // thread rather than expanding by 32x to RGBA form. This saves *lots* of
-      // memory for many scanned documents. It's also much faster.
-      if (this.colorSpace.name === 'DeviceGray' && bpc === 1 &&
-          !this.smask && !this.mask && !this.needsDecode &&
-          drawWidth === originalWidth && drawHeight === originalHeight) {
-        imgData.kind = 'grayscale_1bpp';
-
-        // We must make a copy of imgArray, otherwise it'll be neutered upon
-        // transfer which will break any code that subsequently reuses it.
-        var newArray = new Uint8Array(imgArray.length);
-        newArray.set(imgArray);
-        imgData.data = newArray;
-        imgData.origLength = imgArray.length;
-        return imgData;
-      }
-
       var comps = this.getComponents(imgArray);
 
-      var rgbaBuf = new Uint8Array(drawWidth * drawHeight * 4);
-
-      // Handle opacity here since color key masking needs to be performed on
-      // undecoded values.
-      this.fillOpacity(rgbaBuf, drawWidth, drawHeight, actualHeight, comps);
+      // If opacity data is present, use RGBA_32BPP form. Otherwise, use the
+      // more compact RGB_24BPP form if allowable.
+      var alpha01, maybeUndoPreblend;
+      if (!forceRGBA && !this.smask && !this.mask) {
+        imgData.kind = ImageKind.RGB_24BPP;
+        imgData.data = new Uint8Array(drawWidth * drawHeight * 3);
+        alpha01 = 0;
+        maybeUndoPreblend = false;
+      } else {
+        imgData.kind = ImageKind.RGBA_32BPP;
+        imgData.data = new Uint8Array(drawWidth * drawHeight * 4);
+        alpha01 = 1;
+        maybeUndoPreblend = true;
+
+        // Color key masking (opacity) must be performed before decoding.
+        this.fillOpacity(imgData.data, drawWidth, drawHeight, actualHeight,
+                         comps);
+      }
 
       if (this.needsDecode) {
         this.decodeBuffer(comps);
       }
-
-      this.colorSpace.fillRgb(rgbaBuf, originalWidth, originalHeight, drawWidth,
-                              drawHeight, actualHeight, bpc, comps);
-
-      this.undoPreblend(rgbaBuf, drawWidth, actualHeight);
-
-      imgData.kind = 'rgba_32bpp';
-      imgData.data = rgbaBuf;
+      this.colorSpace.fillRgb(imgData.data, originalWidth, originalHeight,
+                              drawWidth, drawHeight, actualHeight, bpc, comps,
+                              alpha01);
+      if (maybeUndoPreblend) {
+        this.undoPreblend(imgData.data, drawWidth, actualHeight);
+      }
+
       return imgData;
     },
     fillGrayBuffer: function PDFImage_fillGrayBuffer(buffer) {
       var numComps = this.numComps;
       if (numComps != 1)
         error('Reading gray scale from a color image: ' + numComps);
 
       var width = this.width;
       var height = this.height;
       var bpc = this.bpc;
 
       // rows start at byte boundary;
       var rowBytes = (width * numComps * bpc + 7) >> 3;
       var imgArray = this.getImageBytes(height * rowBytes);
 
       var comps = this.getComponents(imgArray);
+
+      if (bpc === 1) {
+        // inline decoding (= inversion) for 1 bpc images
+        var length = width * height;
+        if (this.needsDecode) {
+          // invert and scale to {0, 255} 
+          for (var i = 0; i < length; ++i) {
+            buffer[i] = (comps[i] - 1) & 255;
+          }
+        } else {
+          // scale to {0, 255}
+          for (var i = 0; i < length; ++i) {
+            buffer[i] = (-comps[i]) & 255;
+          }
+        }
+        return;
+      }
+
       if (this.needsDecode) {
         this.decodeBuffer(comps);
       }
       var length = width * height;
       // we aren't using a colorspace so we need to scale the value
       var scale = 255 / ((1 << bpc) - 1);
-      for (var i = 0; i < length; ++i)
+      for (var i = 0; i < length; ++i) {
         buffer[i] = (scale * comps[i]) | 0;
+      }
     },
     getImageBytes: function PDFImage_getImageBytes(length) {
       this.image.reset();
       return this.image.getBytes(length);
     }
   };
   return PDFImage;
 })();
@@ -32141,17 +32253,17 @@ var Lexer = (function LexerClosure() {
         } else {
           strBuf.push(String.fromCharCode(ch));
         }
       }
       if (strBuf.length > 128) {
         error('Warning: name token is longer than allowed by the spec: ' +
               strBuf.length);
       }
-      return new Name(strBuf.join(''));
+      return Name.get(strBuf.join(''));
     },
     getHexString: function Lexer_getHexString() {
       var strBuf = this.strBuf;
       strBuf.length = 0;
       var ch = this.currentChar;
       var isFirstHex = true;
       var firstDigit;
       var secondDigit;
@@ -32653,19 +32765,25 @@ var DecodeStream = (function DecodeStrea
     this.bufferLength = 0;
     this.eof = false;
     this.buffer = null;
   }
 
   DecodeStream.prototype = {
     ensureBuffer: function DecodeStream_ensureBuffer(requested) {
       var buffer = this.buffer;
-      var current = buffer ? buffer.byteLength : 0;
-      if (requested < current)
-        return buffer;
+      var current;
+      if (buffer) {
+        current = buffer.byteLength;
+        if (requested <= current) {
+          return buffer;
+        }
+      } else {
+        current = 0;
+      }
       var size = 512;
       while (size < requested)
         size <<= 1;
       var buffer2 = new Uint8Array(size);
       for (var i = 0; i < current; ++i)
         buffer2[i] = buffer[i];
       return (this.buffer = buffer2);
     },
@@ -32731,55 +32849,16 @@ var DecodeStream = (function DecodeStrea
       }
       return [];
     }
   };
 
   return DecodeStream;
 })();
 
-var FakeStream = (function FakeStreamClosure() {
-  function FakeStream(stream) {
-    this.dict = stream.dict;
-    DecodeStream.call(this);
-  }
-
-  FakeStream.prototype = Object.create(DecodeStream.prototype);
-  FakeStream.prototype.readBlock = function FakeStream_readBlock() {
-    var bufferLength = this.bufferLength;
-    bufferLength += 1024;
-    var buffer = this.ensureBuffer(bufferLength);
-    this.bufferLength = bufferLength;
-  };
-
-  FakeStream.prototype.getBytes = function FakeStream_getBytes(length) {
-    var end, pos = this.pos;
-
-    if (length) {
-      this.ensureBuffer(pos + length);
-      end = pos + length;
-
-      while (!this.eof && this.bufferLength < end)
-        this.readBlock();
-
-      var bufEnd = this.bufferLength;
-      if (end > bufEnd)
-        end = bufEnd;
-    } else {
-      this.eof = true;
-      end = this.bufferLength;
-    }
-
-    this.pos = end;
-    return this.buffer.subarray(pos, end);
-  };
-
-  return FakeStream;
-})();
-
 var StreamsSequenceStream = (function StreamsSequenceStreamClosure() {
   function StreamsSequenceStream(streams) {
     this.streams = streams;
     DecodeStream.call(this);
   }
 
   StreamsSequenceStream.prototype = Object.create(DecodeStream.prototype);
 
@@ -34233,17 +34312,16 @@ var CCITTFaxStream = (function CCITTFaxS
     this.codingLine[0] = this.columns;
     this.codingPos = 0;
 
     this.row = 0;
     this.nextLine2D = this.encoding < 0;
     this.inputBits = 0;
     this.inputBuf = 0;
     this.outputBits = 0;
-    this.buf = EOF;
 
     var code1;
     while ((code1 = this.lookBits(12)) === 0) {
       this.eatBits(1);
     }
     if (code1 == 1) {
       this.eatBits(12);
     }
@@ -34255,17 +34333,16 @@ var CCITTFaxStream = (function CCITTFaxS
     DecodeStream.call(this);
   }
 
   CCITTFaxStream.prototype = Object.create(DecodeStream.prototype);
 
   CCITTFaxStream.prototype.readBlock = function CCITTFaxStream_readBlock() {
     while (!this.eof) {
       var c = this.lookChar();
-      this.buf = EOF;
       this.ensureBuffer(this.bufferLength + 1);
       this.buffer[this.bufferLength++] = c;
     }
   };
 
   CCITTFaxStream.prototype.addPixels =
     function ccittFaxStreamAddPixels(a1, blackPixels) {
     var codingLine = this.codingLine;
@@ -34311,19 +34388,16 @@ var CCITTFaxStream = (function CCITTFaxS
         --codingPos;
       codingLine[codingPos] = a1;
     }
 
     this.codingPos = codingPos;
   };
 
   CCITTFaxStream.prototype.lookChar = function CCITTFaxStream_lookChar() {
-    if (this.buf != EOF)
-      return this.buf;
-
     var refLine = this.refLine;
     var codingLine = this.codingLine;
     var columns = this.columns;
 
     var refPos, blackPixels, bits;
 
     if (this.outputBits === 0) {
       if (this.eof)
@@ -34559,57 +34633,58 @@ var CCITTFaxStream = (function CCITTFaxS
 
       if (codingLine[0] > 0)
         this.outputBits = codingLine[this.codingPos = 0];
       else
         this.outputBits = codingLine[this.codingPos = 1];
       this.row++;
     }
 
+    var c;
     if (this.outputBits >= 8) {
-      this.buf = (this.codingPos & 1) ? 0 : 0xFF;
+      c = (this.codingPos & 1) ? 0 : 0xFF;
       this.outputBits -= 8;
       if (this.outputBits === 0 && codingLine[this.codingPos] < columns) {
         this.codingPos++;
         this.outputBits = (codingLine[this.codingPos] -
                            codingLine[this.codingPos - 1]);
       }
     } else {
       var bits = 8;
-      this.buf = 0;
+      c = 0;
       do {
         if (this.outputBits > bits) {
-          this.buf <<= bits;
+          c <<= bits;
           if (!(this.codingPos & 1)) {
-            this.buf |= 0xFF >> (8 - bits);
+            c |= 0xFF >> (8 - bits);
           }
           this.outputBits -= bits;
           bits = 0;
         } else {
-          this.buf <<= this.outputBits;
+          c <<= this.outputBits;
           if (!(this.codingPos & 1)) {
-            this.buf |= 0xFF >> (8 - this.outputBits);
+            c |= 0xFF >> (8 - this.outputBits);
           }
           bits -= this.outputBits;
           this.outputBits = 0;
           if (codingLine[this.codingPos] < columns) {
             this.codingPos++;
             this.outputBits = (codingLine[this.codingPos] -
                                codingLine[this.codingPos - 1]);
           } else if (bits > 0) {
-            this.buf <<= bits;
+            c <<= bits;
             bits = 0;
           }
         }
       } while (bits);
     }
     if (this.black) {
-      this.buf ^= 0xFF;
-    }
-    return this.buf;
+      c ^= 0xFF;
+    }
+    return c;
   };
 
   // This functions returns the code found from the table.
   // The start and end parameters set the boundaries for searching the table.
   // The limit parameter is optional. Function returns an array with three
   // values. The first array element indicates whether a valid code is being
   // returned. The second array element is the actual code. The third array
   // element indicates whether EOF was reached.
@@ -35028,17 +35103,17 @@ var WorkerMessageHandler = PDFJS.WorkerM
                 status + ') while retrieving PDF "' +
                 source.url + '".');
           }
         },
 
         onProgress: function onProgress(evt) {
           handler.send('DocProgress', {
             loaded: evt.loaded,
-            total: evt.lengthComputable ? evt.total : void(0)
+            total: evt.lengthComputable ? evt.total : source.length
           });
         }
       });
 
       return pdfManagerPromise;
     }
 
     handler.on('test', function wphSetupTest(data) {
@@ -35346,16 +35421,24 @@ var JpxImage = (function JpxImageClosure
     },
     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);
+      // 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);
         position += headerSize;
         if (lbox == 1) {
           lbox = readUint(data, position, 8);
@@ -35550,19 +35633,19 @@ var JpxImage = (function JpxImageClosure
               cod.selectiveArithmeticCodingBypass = !!(blockStyle & 1);
               cod.resetContextProbabilities = !!(blockStyle & 2);
               cod.terminationOnEachCodingPass = !!(blockStyle & 4);
               cod.verticalyStripe = !!(blockStyle & 8);
               cod.predictableTermination = !!(blockStyle & 16);
               cod.segmentationSymbolUsed = !!(blockStyle & 32);
               cod.transformation = data[j++];
               if (cod.entropyCoderWithCustomPrecincts) {
-                var precinctsSizes = {};
+                var precinctsSizes = [];
                 while (j < length + position) {
-                  var precinctsSize = data[j];
+                  var precinctsSize = data[j++];
                   precinctsSizes.push({
                     PPx: precinctsSize & 0xF,
                     PPy: precinctsSize >> 4
                   });
                 }
                 cod.precinctsSizes = precinctsSizes;
               }
 
@@ -35815,18 +35898,19 @@ var JpxImage = (function JpxImageClosure
     // Section B.10.8 Order of info in packet
     var subbands = resolution.subbands;
     // sub-bands already ordered in 'LL', 'HL', 'LH', and 'HH' sequence
     for (var i = 0, ii = subbands.length; i < ii; i++) {
       var subband = subbands[i];
       var codeblocks = subband.codeblocks;
       for (var j = 0, jj = codeblocks.length; j < jj; j++) {
         var codeblock = codeblocks[j];
-        if (codeblock.precinctNumber != precinctNumber)
+        if (codeblock.precinctNumber != precinctNumber) {
           continue;
+        }
         precinctCodeblocks.push(codeblock);
       }
     }
     return {
       layerNumber: layerNumber,
       codeblocks: precinctCodeblocks
     };
   }
@@ -36030,27 +36114,31 @@ var JpxImage = (function JpxImageClosure
       bufferSize = 0;
       if (skipNextBit) {
         position++;
         skipNextBit = false;
       }
     }
     function readCodingpasses() {
       var value = readBits(1);
-      if (value === 0)
+      if (value === 0) {
         return 1;
+      }
       value = (value << 1) | readBits(1);
-      if (value == 0x02)
+      if (value == 0x02) {
         return 2;
+      }
       value = (value << 2) | readBits(2);
-      if (value <= 0x0E)
+      if (value <= 0x0E) {
         return (value & 0x03) + 3;
+      }
       value = (value << 5) | readBits(5);
-      if (value <= 0x1FE)
+      if (value <= 0x1FE) {
         return (value & 0x1F) + 6;
+      }
       value = (value << 7) | readBits(7);
       return (value & 0x7F) + 37;
     }
     var tileIndex = context.currentTile.index;
     var tile = context.tiles[tileIndex];
     var packetsIterator = tile.packetsIterator;
     while (position < dataLength) {
       var packet = packetsIterator.nextPacket();
@@ -36096,51 +36184,56 @@ var JpxImage = (function JpxImageClosure
                 }
               } else {
                 inclusionTree.incrementValue(layerNumber);
                 break;
               }
             }
           }
         }
-        if (!codeblockIncluded)
+        if (!codeblockIncluded) {
           continue;
+        }
         if (firstTimeInclusion) {
           zeroBitPlanesTree = precinct.zeroBitPlanesTree;
           zeroBitPlanesTree.reset(codeblockColumn, codeblockRow);
           while (true) {
             if (readBits(1)) {
               var valueReady = !zeroBitPlanesTree.nextLevel();
-              if (valueReady)
-                break;
-            } else
+              if (valueReady) {
+                break;
+              }
+            } else {
               zeroBitPlanesTree.incrementValue();
+            }
           }
           codeblock.zeroBitPlanes = zeroBitPlanesTree.value;
         }
         var codingpasses = readCodingpasses();
-        while (readBits(1))
+        while (readBits(1)) {
           codeblock.Lblock++;
+        }
         var codingpassesLog2 = log2(codingpasses);
         // rounding down log2
         var bits = ((codingpasses < (1 << codingpassesLog2)) ?
           codingpassesLog2 - 1 : codingpassesLog2) + codeblock.Lblock;
         var codedDataLength = readBits(bits);
         queue.push({
           codeblock: codeblock,
           codingpasses: codingpasses,
           dataLength: codedDataLength
         });
       }
       alignToByte();
       while (queue.length > 0) {
         var packetItem = queue.shift();
         var codeblock = packetItem.codeblock;
-        if (!('data' in codeblock))
+        if (!('data' in codeblock)) {
           codeblock.data = [];
+        }
         codeblock.data.push({
           data: data,
           start: offset + position,
           end: offset + position + packetItem.dataLength,
           codingpasses: packetItem.codingpasses
         });
         position += packetItem.dataLength;
       }
@@ -36150,20 +36243,22 @@ var JpxImage = (function JpxImageClosure
   function copyCoefficients(coefficients, x0, y0, width, height,
                             delta, mb, codeblocks, transformation,
                             segmentationSymbolUsed) {
     var r = 0.5; // formula (E-6)
     for (var i = 0, ii = codeblocks.length; i < ii; ++i) {
       var codeblock = codeblocks[i];
       var blockWidth = codeblock.tbx1_ - codeblock.tbx0_;
       var blockHeight = codeblock.tby1_ - codeblock.tby0_;
-      if (blockWidth === 0 || blockHeight === 0)
+      if (blockWidth === 0 || blockHeight === 0) {
         continue;
-      if (!('data' in codeblock))
+      }
+      if (!('data' in codeblock)) {
         continue;
+      }
 
       var bitModel, currentCodingpassType;
       bitModel = new BitModel(blockWidth, blockHeight, codeblock.subbandType,
         codeblock.zeroBitPlanes);
       currentCodingpassType = 2; // first bit plane starts from cleanup
 
       // collect data
       var data = codeblock.data, totalLength = 0, codingpasses = 0;
@@ -36188,18 +36283,19 @@ var JpxImage = (function JpxImageClosure
           case 0:
             bitModel.runSignificancePropogationPass();
             break;
           case 1:
             bitModel.runMagnitudeRefinementPass();
             break;
           case 2:
             bitModel.runCleanupPass();
-            if (segmentationSymbolUsed)
+            if (segmentationSymbolUsed) {
               bitModel.checkSegmentationSymbol();
+            }
             break;
         }
         currentCodingpassType = (currentCodingpassType + 1) % 3;
       }
 
       var offset = (codeblock.tbx0_ - x0) + (codeblock.tby0_ - y0) * width;
       var position = 0;
       for (var j = 0; j < blockHeight; j++) {
@@ -36207,18 +36303,19 @@ var JpxImage = (function JpxImageClosure
           var n = (bitModel.coefficentsSign[position] ? -1 : 1) *
             bitModel.coefficentsMagnitude[position];
           var nb = bitModel.bitsDecoded[position], correction;
           if (transformation === 0 || mb > nb) {
             // use r only if transformation is irreversible or
             // not all bitplanes were decoded for reversible transformation
             n += n < 0 ? n - r : n > 0 ? n + r : 0;
             correction = 1 << (mb - nb);
-          } else
+          } else {
             correction = 1;
+          }
           coefficients[offset++] = n * correction * delta;
           position++;
         }
         offset += width - blockWidth;
       }
     }
   }
   function transformTile(context, tile, c) {
@@ -36313,24 +36410,26 @@ var JpxImage = (function JpxImageClosure
           y0items[j] = y2 + i1;
           y2items[j] = y1 + i1;
         }
       }
 
       // Section G.1 DC level shifting to unsigned component values
       for (var c = 0; c < componentsCount; c++) {
         var component = components[c];
-        if (component.isSigned)
+        if (component.isSigned) {
           continue;
+        }
 
         var offset = 1 << (component.precision - 1);
         var tileImage = result[c];
         var items = tileImage.items;
-        for (var j = 0, jj = items.length; j < jj; j++)
+        for (var j = 0, jj = items.length; j < jj; j++) {
           items[j] += offset;
+        }
       }
 
       // To simplify things: shift and clamp output to 8 bit unsigned
       for (var c = 0; c < componentsCount; c++) {
         var component = components[c];
         var offset = component.isSigned ? 128 : 0;
         var shift = component.precision - 8;
         var tileImage = result[c];
@@ -36425,18 +36524,19 @@ var JpxImage = (function JpxImageClosure
   })();
 
   var InclusionTree = (function InclusionTreeClosure() {
     function InclusionTree(width, height,  defaultValue) {
       var levelsLength = log2(Math.max(width, height)) + 1;
       this.levels = [];
       for (var i = 0; i < levelsLength; i++) {
         var items = new Uint8Array(width * height);
-        for (var j = 0, jj = items.length; j < jj; j++)
+        for (var j = 0, jj = items.length; j < jj; j++) {
           items[j] = defaultValue;
+        }
 
         var level = {
           width: width,
           height: height,
           items: items
         };
         this.levels.push(level);
 
@@ -36448,18 +36548,19 @@ var JpxImage = (function JpxImageClosure
       reset: function InclusionTree_reset(i, j, stopValue) {
         var currentLevel = 0;
         while (currentLevel < this.levels.length) {
           var level = this.levels[currentLevel];
           var index = i + j * level.width;
           level.index = index;
           var value = level.items[index];
 
-          if (value == 0xFF)
-            break;
+          if (value == 0xFF) {
+            break;
+          }
 
           if (value > stopValue) {
             this.currentLevel = currentLevel;
             // already know about this one, propagating the value to top levels
             this.propagateValues();
             return false;
           }
 
@@ -36485,18 +36586,19 @@ var JpxImage = (function JpxImageClosure
         }
       },
       nextLevel: function InclusionTree_nextLevel() {
         var currentLevel = this.currentLevel;
         var level = this.levels[currentLevel];
         var value = level.items[level.index];
         level.items[level.index] = 0xFF;
         currentLevel--;
-        if (currentLevel < 0)
+        if (currentLevel < 0) {
           return false;
+        }
 
         this.currentLevel = currentLevel;
         var level = this.levels[currentLevel];
         level.items[level.index] = value;
         return true;
       }
     };
     return InclusionTree;
@@ -36612,18 +36714,19 @@ var JpxImage = (function JpxImageClosure
             return d;
           } else {
             return cx.mps;
           }
         }
       },
       renormD: function ArithmeticDecoder_renormD() {
         do {
-          if (this.ct === 0)
+          if (this.ct === 0) {
             this.byteIn();
+          }
 
           this.a <<= 1;
           this.chigh = ((this.chigh << 1) & 0xFFFF) | ((this.clow >> 15) & 1);
           this.clow = (this.clow << 1) & 0xFFFF;
           this.ct--;
         } while ((this.a & 0x8000) === 0);
       },
       exchangeMps: function ArithmeticDecoder_exchangeMps(cx) {
@@ -36684,22 +36787,24 @@ var JpxImage = (function JpxImageClosure
       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
     ]);
 
     // Table D-2
     function calcSignContribution(significance0, sign0, significance1, sign1) {
       if (significance1) {
-        if (!sign1)
+        if (!sign1) {
           return significance0 ? (!sign0 ? 1 : 0) : 1;
-        else
+        } else {
           return significance0 ? (!sign0 ? 0 : -1) : -1;
-      } else
+        }
+      } else {
         return significance0 ? (!sign0 ? 1 : -1) : 0;
+      }
     }
     // Table D-3
     var SignContextLabels = [
       {contextLabel: 13, xorBit: 0},
       {contextLabel: 12, xorBit: 0},
       {contextLabel: 11, xorBit: 0},
       {contextLabel: 10, xorBit: 0},
       {contextLabel: 9, xorBit: 0},
@@ -36737,42 +36842,49 @@ var JpxImage = (function JpxImageClosure
       setDecoder: function BitModel_setDecoder(decoder) {
         this.decoder = decoder;
       },
       reset: function BitModel_reset() {
         this.uniformContext = {index: 46, mps: 0};
         this.runLengthContext = {index: 3, mps: 0};
         this.contexts = [];
         this.contexts.push({index: 4, mps: 0});
-        for (var i = 1; i <= 16; i++)
+        for (var i = 1; i <= 16; i++) {
           this.contexts.push({index: 0, mps: 0});
+        }
       },
       setNeighborsSignificance:
         function BitModel_setNeighborsSignificance(row, column) {
         var neighborsSignificance = this.neighborsSignificance;
         var width = this.width, height = this.height;
         var index = row * width + column;
         if (row > 0) {
-          if (column > 0)
+          if (column > 0) {
             neighborsSignificance[index - width - 1] += 0x10;
-          if (column + 1 < width)
+          }
+          if (column + 1 < width) {
             neighborsSignificance[index - width + 1] += 0x10;
+          }
           neighborsSignificance[index - width] += 0x04;
         }
         if (row + 1 < height) {
-          if (column > 0)
+          if (column > 0) {
             neighborsSignificance[index + width - 1] += 0x10;
-          if (column + 1 < width)
+          }
+          if (column + 1 < width) {
             neighborsSignificance[index + width + 1] += 0x10;
+          }
           neighborsSignificance[index + width] += 0x04;
         }
-        if (column > 0)
+        if (column > 0) {
           neighborsSignificance[index - 1] += 0x01;
-        if (column + 1 < width)
+        }
+        if (column + 1 < width) {
           neighborsSignificance[index + 1] += 0x01;
+        }
         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;
@@ -36781,26 +36893,28 @@ var JpxImage = (function JpxImageClosure
         var processingFlags = this.processingFlags;
         var contexts = this.contexts;
         var labels = this.contextLabelTable;
         var bitsDecoded = this.bitsDecoded;
         // clear processed flag
         var processedInverseMask = ~1;
         var processedMask = 1;
         var firstMagnitudeBitMask = 2;
-        for (var q = 0, qq = width * height; q < qq; q++)
+        for (var q = 0, qq = width * height; q < qq; q++) {
           processingFlags[q] &= processedInverseMask;
+        }
 
         for (var i0 = 0; i0 < height; i0 += 4) {
           for (var j = 0; j < width; j++) {
             var index = i0 * width + j;
             for (var i1 = 0; i1 < 4; i1++, index += width) {
               var i = i0 + i1;
-              if (i >= height)
-                break;
+              if (i >= height) {
+                break;
+              }
 
               if (coefficentsMagnitude[index] || !neighborsSignificance[index])
                 continue;
 
               var contextLabel = labels[neighborsSignificance[index]];
               var cx = contexts[contextLabel];
               var decision = decoder.readBit(cx);
               if (decision) {
@@ -36849,24 +36963,26 @@ var JpxImage = (function JpxImageClosure
         var bitsDecoded = this.bitsDecoded;
         var processingFlags = this.processingFlags;
         var processedMask = 1;
         var firstMagnitudeBitMask = 2;
         for (var i0 = 0; i0 < height; i0 += 4) {
           for (var j = 0; j < width; j++) {
             for (var i1 = 0; i1 < 4; i1++) {
               var i = i0 + i1;
-              if (i >= height)
-                break;
+              if (i >= height) {
+                break;
+              }
               var index = i * width + j;
 
               // significant but not those that have just become
               if (!coefficentsMagnitude[index] ||
-                (processingFlags[index] & processedMask) !== 0)
+                (processingFlags[index] & processedMask) !== 0) {
                 continue;
+              }
 
               var contextLabel = 16;
               if ((processingFlags[index] &
                 firstMagnitudeBitMask) !== 0) {
                 processingFlags[i * width + j] ^= firstMagnitudeBitMask;
                 // first refinement
                 var significance = neighborsSignificance[index];
                 var sumOfSignificance = (significance & 3) +
@@ -36933,29 +37049,32 @@ var JpxImage = (function JpxImageClosure
 
               var sign = this.decodeSignBit(i, j);
               coefficentsSign[index] = sign;
               coefficentsMagnitude[index] = 1;
               this.setNeighborsSignificance(i, j);
               processingFlags[index] |= firstMagnitudeBitMask;
 
               index = index0;
-              for (var i2 = i0; i2 <= i; i2++, index += width)
+              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;
+              if (i >= height) {
+                break;
+              }
 
               if (coefficentsMagnitude[index] ||
-                (processingFlags[index] & processedMask) !== 0)
+                (processingFlags[index] & processedMask) !== 0) {
                 continue;
+              }
 
               var contextLabel = labels[neighborsSignificance[index]];
               cx = contexts[contextLabel];
               var decision = decoder.readBit(cx);
               if (decision == 1) {
                 var sign = this.decodeSignBit(i, j);
                 coefficentsSign[index] = sign;
                 coefficentsMagnitude[index] = 1;
@@ -36967,18 +37086,19 @@ var JpxImage = (function JpxImageClosure
           }
         }
       },
       checkSegmentationSymbol: function BitModel_checkSegmentationSymbol() {
         var decoder = this.decoder;
         var cx = this.uniformContext;
         var symbol = (decoder.readBit(cx) << 3) | (decoder.readBit(cx) << 2) |
                      (decoder.readBit(cx) << 1) | decoder.readBit(cx);
-        if (symbol != 0xA)
+        if (symbol != 0xA) {
           throw 'Invalid segmentation symbol';
+        }
       }
     };
 
     return BitModel;
   })();
 
   // Section F, Discrete wavelet transofrmation
   var Transform = (function TransformClosure() {
@@ -37021,29 +37141,29 @@ var JpxImage = (function JpxImageClosure
 
       for (i = 0; i < llHeight; i++) {
         var k = i * llWidth, l = i * 2 * width;
         for (var j = 0; j < llWidth; j++, k++, l += 2) {
           items[l] = llItems[k];
         }
       }
       for (i = 0; i < hlHeight; i++) {
-        k = i * hlWidth, l = i * 2 * width + 1;
+        k = i * hlWidth; l = i * 2 * width + 1;
         for (j = 0; j < hlWidth; j++, k++, l += 2) {
           items[l] = hlItems[k];
         }
       }
       for (i = 0; i < lhHeight; i++) {
-        k = i * lhWidth, l = (i * 2 + 1) * width;
+        k = i * lhWidth; l = (i * 2 + 1) * width;
         for (j = 0; j < lhWidth; j++, k++, l += 2) {
           items[l] = lhItems[k];
         }
       }
       for (i = 0; i < hhHeight; i++) {
-        k = i * hhWidth, l = (i * 2 + 1) * width + 1;
+        k = i * hhWidth; l = (i * 2 + 1) * width + 1;
         for (j = 0; j < hhWidth; j++, k++, l += 2) {
           items[l] = hhItems[k];
         }
       }
 
       var bufferPadding = 4;
       var rowBuffer = new Float32Array(width + 2 * bufferPadding);
 
@@ -37298,17 +37418,17 @@ var Jbig2Image = (function Jbig2ImageClo
           this.bp = bp;
         }
         if (this.clow > 0xFFFF) {
           this.chigh += (this.clow >> 16);
           this.clow &= 0xFFFF;
         }
       },
       readBit: function ArithmeticDecoder_readBit(contexts, pos) {
-        // contexts are packed into 1 byte: 
+        // contexts are packed into 1 byte:
         // highest 7 bits carry cx.index, lowest bit carries cx.mps
         var cx_index = contexts[pos] >> 1, cx_mps = contexts[pos] & 1;
         var qeTableIcx = QeTable[cx_index];
         var qeIcx = qeTableIcx.qe;
         var nmpsIcx = qeTableIcx.nmps;
         var nlpsIcx = qeTableIcx.nlps;
         var switchIcx = qeTableIcx.switchFlag;
         var d;
@@ -37551,54 +37671,95 @@ var Jbig2Image = (function Jbig2ImageClo
   // 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;
     var template = CodingTemplates[templateIndex].concat(at);
+
+    // Sorting is non-standard, and it is not required. But sorting increases
+    // the number of template bits that can be reused from the previous
+    // contextLabel in the main loop.
+    template.sort(function (a, b) {
+      return (a.y - b.y) || (a.x - b.x);
+    });
+
     var templateLength = template.length;
-    var templateX = new Int32Array(templateLength);
-    var templateY = new Int32Array(templateLength);
+    var templateX = new Int8Array(templateLength);
+    var templateY = new Int8Array(templateLength);
+    var changingTemplateEntries = [];
+    var reuseMask = 0, minX = 0, maxX = 0, minY = 0;
+
     for (var k = 0; k < templateLength; k++) {
       templateX[k] = template[k].x;
       templateY[k] = template[k].y;
-    }
+      minX = Math.min(minX, template[k].x);
+      maxX = Math.max(maxX, template[k].x);
+      minY = Math.min(minY, template[k].y);
+      // Check if the template pixel appears in two consecutive context labels,
+      // so it can be reused. Otherwise, we add it to the list of changing
+      // template entries.
+      if (k < templateLength - 1 &&
+          template[k].y === template[k + 1].y &&
+          template[k].x === template[k + 1].x - 1) {
+        reuseMask |= 1 << (templateLength - 1 - k);
+      } else {
+        changingTemplateEntries.push(k);
+      }
+    }
+    changingTemplateEntries = new Uint8Array(changingTemplateEntries);
+    var changingEntriesLength = changingTemplateEntries.length;
 
     var pseudoPixelContext = ReusedContexts[templateIndex];
     var bitmap = [];
 
     var decoder = decodingContext.decoder;
     var contexts = decodingContext.contextCache.getContexts('GB');
 
-    var ltp = 0;
+    var ltp = 0, c, j, i0, j0, k, contextLabel = 0;
     for (var i = 0; i < height; i++) {
       if (prediction) {
         var sltp = decoder.readBit(contexts, pseudoPixelContext);
         ltp ^= sltp;
       }
       if (ltp) {
         bitmap.push(bitmap[bitmap.length - 1]); // duplicate previous row
         continue;
       }
       var row = new Uint8Array(width);
       bitmap.push(row);
-      for (var j = 0; j < width; j++) {
+      for (j = 0; j < width; j++) {
         if (useskip && skip[i][j]) {
           row[j] = 0;
           continue;
         }
-        var contextLabel = 0;
-        for (var k = 0; k < templateLength; k++) {
-          var i0 = i + templateY[k], j0 = j + templateX[k];
-          if (i0 < 0 || j0 < 0 || j0 >= width)
-            contextLabel <<= 1; // out of bound pixel
-          else
-            contextLabel = (contextLabel << 1) | bitmap[i0][j0];
+        // Are we in the middle of a scanline, so we can reuse contextLabel
+        // bits?
+        if (i + minY > 0 && j + minX >= 0 && j + maxX < width) {
+          // If yes, we can just shift the bits that are reusable and only
+          // fetch the remaining ones.
+          contextLabel = (contextLabel << 1) & reuseMask;
+          for (c = 0; c < changingEntriesLength; c++) {
+            k = changingTemplateEntries[c];
+            i0 = i + templateY[k];
+            j0 = j + templateX[k];
+            contextLabel |= bitmap[i0][j0] << (templateLength - 1 - k);
+          }
+        } else {
+          // compute the contextLabel from scratch
+          contextLabel = 0;
+          for (k = 0; k < templateLength; k++) {
+            i0 = i + templateY[k];
+            j0 = j + templateX[k];
+            if (i0 >= 0 && j0 >= 0 && j0 < width) {
+              contextLabel |= bitmap[i0][j0] << (templateLength - 1 - k);
+            }
+          }
         }
         var pixel = decoder.readBit(contexts, contextLabel);
         row[j] = pixel;
       }
     }
     return bitmap;
   }
 
@@ -37790,17 +37951,17 @@ var Jbig2Image = (function Jbig2ImageClo
           symbolBitmap = decodeRefinement(symbolWidth, symbolHeight,
             refinementTemplateIndex, symbolBitmap, (rdw >> 1) + rdx,
             (rdh >> 1) + rdy, false, refinementAt,
             decodingContext);
         }
         var offsetT = t - ((referenceCorner & 1) ? 0 : symbolHeight);
         var offsetS = currentS - ((referenceCorner & 2) ? symbolWidth : 0);
         if (transposed) {
-          // Place Symbol Bitmap from T1,S1  
+          // Place Symbol Bitmap from T1,S1
           for (var s2 = 0; s2 < symbolHeight; s2++) {
             var row = bitmap[offsetS + s2];
             if (!row) {
               continue;
             }
             var symbolRow = symbolBitmap[s2];
             // To ignore Parts of Symbol bitmap which goes
             // outside bitmap region
@@ -38177,19 +38338,23 @@ var Jbig2Image = (function Jbig2ImageClo
 
   function SimpleSegmentVisitor() {}
 
   SimpleSegmentVisitor.prototype = {
     onPageInformation: function SimpleSegmentVisitor_onPageInformation(info) {
       this.currentPageInfo = info;
       var rowSize = (info.width + 7) >> 3;
       var buffer = new Uint8Array(rowSize * info.height);
-      var fill = info.defaultPixelValue ? 0xFF : 0;
-      for (var i = 0, ii = buffer.length; i < ii; i++)
-        buffer[i] = fill;
+      // The contents of ArrayBuffers are initialized to 0.
+      // Fill the buffer with 0xFF only if info.defaultPixelValue is set
+      if (info.defaultPixelValue) {
+        for (var i = 0, ii = buffer.length; i < ii; i++) {
+          buffer[i] = 0xFF;
+        }
+      }
       this.buffer = buffer;
     },
     drawBitmap: function SimpleSegmentVisitor_drawBitmap(regionInfo, bitmap) {
       var pageInfo = this.currentPageInfo;
       var width = regionInfo.width, height = regionInfo.height;
       var rowSize = (pageInfo.width + 7) >> 3;
       var combinationOperator = pageInfo.combinationOperatorOverride ?
         regionInfo.combinationOperator : pageInfo.combinationOperator;
@@ -38436,17 +38601,16 @@ var bidi = PDFJS.bidi = (function bidiCl
     var strLength = str.length;
     if (strLength === 0 || vertical)
       return new BidiResult(str, isLTR, vertical);
 
     // get types, fill arrays
 
     var chars = [];
     var types = [];
-    var oldtypes = [];
     var numBidi = 0;
 
     for (var i = 0; i < strLength; ++i) {
       chars[i] = str.charAt(i);
 
       var charCode = str.charCodeAt(i);
       var charType = 'L';
       if (charCode <= 0x00ff)
@@ -38456,17 +38620,17 @@ var bidi = PDFJS.bidi = (function bidiCl
       else if (0x0600 <= charCode && charCode <= 0x06ff)
         charType = arabicTypes[charCode & 0xff];
       else if (0x0700 <= charCode && charCode <= 0x08AC)
         charType = 'AL';
 
       if (charType == 'R' || charType == 'AL' || charType == 'AN')
         numBidi++;
 
-      oldtypes[i] = types[i] = charType;
+      types[i] = charType;
     }
 
     // detect the bidi method
     //  if there are no rtl characters then no bidi needed
     //  if less than 30% chars are rtl then string is primarily ltr
     //  if more than 30% chars are rtl then string is primarily rtl
     if (numBidi === 0) {
       isLTR = true;
--- a/browser/extensions/pdfjs/content/web/viewer.css
+++ b/browser/extensions/pdfjs/content/web/viewer.css
@@ -1504,16 +1504,160 @@ html[dir='rtl'] #documentPropertiesConta
 @page {
   margin: 0;
 }
 
 #printContainer {
   display: none;
 }
 
+@media screen and (min-resolution: 2dppx) {
+  /* Rules for Retina screens */
+  .toolbarButton::before {
+    transform: scale(0.5);
+    top: -5px;
+  }
+
+  .secondaryToolbarButton::before {
+    transform: scale(0.5);
+    top: -4px;
+  }
+
+  html[dir='ltr'] .toolbarButton::before,
+  html[dir='rtl'] .toolbarButton::before {
+    left: -1px;
+  }
+
+  html[dir='ltr'] .secondaryToolbarButton::before {
+    left: -2px;
+  }
+  html[dir='rtl'] .secondaryToolbarButton::before {
+    left: 186px;
+  }
+
+  .dropdownToolbarButton {
+    background: url(images/toolbarButton-menuArrows@2x.png) no-repeat;
+    background-size: 7px 16px;
+  }
+  
+  html[dir='ltr'] .toolbarButton#sidebarToggle::before {
+    content: url(images/toolbarButton-sidebarToggle@2x.png);
+  }
+  html[dir='rtl'] .toolbarButton#sidebarToggle::before {
+    content: url(images/toolbarButton-sidebarToggle-rtl@2x.png);
+  }
+
+  html[dir='ltr'] .toolbarButton#secondaryToolbarToggle::before {
+    content: url(images/toolbarButton-secondaryToolbarToggle@2x.png);
+  }
+  html[dir='rtl'] .toolbarButton#secondaryToolbarToggle::before {
+    content: url(images/toolbarButton-secondaryToolbarToggle-rtl@2x.png);
+  }
+
+  html[dir='ltr'] .toolbarButton.findPrevious::before {
+    content: url(images/findbarButton-previous@2x.png);
+  }
+  html[dir='rtl'] .toolbarButton.findPrevious::before {
+    content: url(images/findbarButton-previous-rtl@2x.png);
+  }
+
+  html[dir='ltr'] .toolbarButton.findNext::before {
+    content: url(images/findbarButton-next@2x.png);
+  }
+  html[dir='rtl'] .toolbarButton.findNext::before {
+    content: url(images/findbarButton-next-rtl@2x.png);
+  }
+
+  html[dir='ltr'] .toolbarButton.pageUp::before {
+    content: url(images/toolbarButton-pageUp@2x.png);
+  }
+  html[dir='rtl'] .toolbarButton.pageUp::before {
+    content: url(images/toolbarButton-pageUp-rtl@2x.png);
+  }
+
+  html[dir='ltr'] .toolbarButton.pageDown::before {
+    content: url(images/toolbarButton-pageDown@2x.png);
+  }
+  html[dir='rtl'] .toolbarButton.pageDown::before {
+    content: url(images/toolbarButton-pageDown-rtl@2x.png);
+  }
+
+  .toolbarButton.zoomIn::before {
+    content: url(images/toolbarButton-zoomIn@2x.png);
+  }
+
+  .toolbarButton.zoomOut::before {
+    content: url(images/toolbarButton-zoomOut@2x.png);
+  }
+
+  .toolbarButton.presentationMode::before,
+  .secondaryToolbarButton.presentationMode::before {
+    content: url(images/toolbarButton-presentationMode@2x.png);
+  }
+
+  .toolbarButton.print::before,
+  .secondaryToolbarButton.print::before {
+    content: url(images/toolbarButton-print@2x.png);
+  }
+
+  .toolbarButton.openFile::before,
+  .secondaryToolbarButton.openFile::before {
+    content: url(images/toolbarButton-openFile@2x.png);
+  }
+
+  .toolbarButton.download::before,
+  .secondaryToolbarButton.download::before {
+    content: url(images/toolbarButton-download@2x.png);
+  }
+
+  .toolbarButton.bookmark::before,
+  .secondaryToolbarButton.bookmark::before {
+    content: url(images/toolbarButton-bookmark@2x.png);
+  }
+
+  #viewThumbnail.toolbarButton::before {
+    content: url(images/toolbarButton-viewThumbnail@2x.png);
+  }
+
+  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);
+  }
+
+  #viewFind.toolbarButton::before {
+    content: url(images/toolbarButton-search@2x.png);
+  }
+
+  .secondaryToolbarButton.firstPage::before {
+    content: url(images/secondaryToolbarButton-firstPage@2x.png);
+  }
+
+  .secondaryToolbarButton.lastPage::before {
+    content: url(images/secondaryToolbarButton-lastPage@2x.png);
+  }
+
+  .secondaryToolbarButton.rotateCcw::before {
+    content: url(images/secondaryToolbarButton-rotateCcw@2x.png);
+  }
+
+  .secondaryToolbarButton.rotateCw::before {
+    content: url(images/secondaryToolbarButton-rotateCw@2x.png);
+  }
+
+  .secondaryToolbarButton.handTool::before {
+    content: url(images/secondaryToolbarButton-handTool@2x.png);
+  }
+
+  .secondaryToolbarButton.documentProperties::before {
+    content: url(images/secondaryToolbarButton-documentProperties@2x.png);
+  }
+}
+
 @media print {
   /* General rules for printing. */
   body {
     background: transparent none;
   }
 
   /* Rules for browsers that don't support mozPrintCallback. */
   #sidebarContainer, #secondaryToolbar, .toolbar, #loadingBox, #errorWrapper, .textLayer {
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -3451,18 +3451,26 @@ var PDFView = {
 
         if (outlineButton.getAttribute('disabled'))
           return;
         break;
     }
   },
 
   getVisiblePages: function pdfViewGetVisiblePages() {
-    return this.getVisibleElements(this.container, this.pages,
-                                   !PresentationMode.active);
+    if (!PresentationMode.active) {
+      return this.getVisibleElements(this.container, this.pages, true);
+    } else {
+      // The algorithm in getVisibleElements doesn't work in all browsers and
+      // configurations when presentation mode is active.
+      var visible = [];
+      var currentPage = this.pages[this.page - 1];
+      visible.push({ id: currentPage.id, view: currentPage });
+      return { first: currentPage, last: currentPage, views: visible };
+    }
   },
 
   getVisibleThumbs: function pdfViewGetVisibleThumbs() {
     return this.getVisibleElements(this.thumbnailContainer, this.thumbnails);
   },
 
   // Generic helper to find out what elements are visible within a scroll pane.
   getVisibleElements: function pdfViewGetVisibleElements(
@@ -3956,17 +3964,22 @@ var PageView = function pageView(contain
     });
   }
 
   this.getPagePoint = function pageViewGetPagePoint(x, y) {
     return this.viewport.convertToPdfPoint(x, y);
   };
 
   this.scrollIntoView = function pageViewScrollIntoView(dest) {
-    if (PresentationMode.active) { // Avoid breaking presentation mode.
+    if (PresentationMode.active) {
+      if (PDFView.page !== this.id) {
+        // Avoid breaking PDFView.getVisiblePages in presentation mode.
+        PDFView.page = this.id;
+        return;
+      }
       dest = null;
       PDFView.setScale(PDFView.currentScaleValue, true, true);
     }
     if (!dest) {
       scrollIntoView(div);
       return;
     }
 
@@ -4131,19 +4144,16 @@ var PageView = function pageView(contain
       var cssScale = 'scale(' + (1 / outputScale.sx) + ', ' +
                                 (1 / outputScale.sy) + ')';
       CustomStyle.setProp('transform' , textLayerDiv, cssScale);
       CustomStyle.setProp('transformOrigin' , textLayerDiv, '0% 0%');
       textLayerDiv.dataset._scaleX = outputScale.sx;
       textLayerDiv.dataset._scaleY = outputScale.sy;
     }
 
-    // Checking if document fonts are used only once
-    var checkIfDocumentFontsUsed = !PDFView.pdfDocument.embeddedFontsUsed;
-
     // Rendering area
 
     var self = this;
     function pageViewDrawCallback(error) {
       // The renderTask may have been replaced by a new one, so only remove the
       // reference to the renderTask if it matches the one that is triggering
       // this callback.
       if (renderTask === self.renderTask) {
@@ -4161,22 +4171,16 @@ var PageView = function pageView(contain
         delete self.loadingIconDiv;
       }
 
       if (self.zoomLayer) {
         div.removeChild(self.zoomLayer);
         self.zoomLayer = null;
       }
 
-      if (checkIfDocumentFontsUsed && PDFView.pdfDocument.embeddedFontsUsed &&
-          PDFJS.disableFontFace) {
-        console.error(mozL10n.get('web_fonts_disabled', null,
-          'Web fonts are disabled: unable to use embedded PDF fonts.'));
-        PDFView.fallback();
-      }
       if (self.textLayer && self.textLayer.textDivs &&
           self.textLayer.textDivs.length > 0 &&
           !PDFView.supportsDocumentColors) {
         console.error(mozL10n.get('document_colors_disabled', null,
           'PDF documents are not allowed to use their own colors: ' +
           '\'Allow pages to choose their own colors\' ' +
           'is deactivated in the browser.'));
         PDFView.fallback();
@@ -4532,20 +4536,17 @@ var TextLayerBuilder = function textLaye
   };
 
   this.endLayout = function textLayerBuilderEndLayout() {
     this.layoutDone = true;
     this.insertDivContent();
   };
 
   this.renderLayer = function textLayerBuilderRenderLayer() {
-    var self = this;
     var textDivs = this.textDivs;
-    var bidiTexts = this.textContent;
-    var textLayerDiv = this.textLayerDiv;
     var canvas = document.createElement('canvas');
     var ctx = canvas.getContext('2d');
 
     // No point in rendering so many divs as it'd make the browser unusable
     // even after the divs are rendered
     var MAX_TEXT_DIVS_TO_RENDER = 100000;
     if (textDivs.length > MAX_TEXT_DIVS_TO_RENDER)
       return;
@@ -4565,17 +4566,17 @@ var TextLayerBuilder = function textLaye
         var rotation = textDiv.dataset.angle;
         var transform = 'scale(' + textScale + ', 1)';
         transform = 'rotate(' + rotation + 'deg) ' + transform;
         CustomStyle.setProp('transform' , textDiv, transform);
         CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%');
       }
     }
 
-    textLayerDiv.appendChild(textLayerFrag);
+    this.textLayerDiv.appendChild(textLayerFrag);
     this.renderingDone = true;
     this.updateMatches();
   };
 
   this.setupRenderLayoutTimer = function textLayerSetupRenderLayoutTimer() {
     // Schedule renderLayout() if user has been scrolling, otherwise
     // run it right away
     var RENDER_DELAY = 200; // in ms
@@ -4905,17 +4906,17 @@ var DocumentOutlineView = function docum
       }
 
       levelData.parent.appendChild(div);
     }
   }
 };
 
 
-document.addEventListener('DOMContentLoaded', function webViewerLoad(evt) {
+function webViewerLoad(evt) {
   PDFView.initialize();
 
   var file = window.location.href.split('#')[0];
 
   document.getElementById('openFile').setAttribute('hidden', 'true');
   document.getElementById('secondaryOpenFile').setAttribute('hidden', 'true');
 
   // Special debugging flags in the hash section of the URL.
@@ -4952,16 +4953,18 @@ document.addEventListener('DOMContentLoa
 
   if ('ignoreCurrentPositionOnZoom' in hashParams) {
     IGNORE_CURRENT_POSITION_ON_ZOOM =
       (hashParams['ignoreCurrentPositionOnZoom'] === 'true');
   }
 
   if (!PDFView.supportsDocumentFonts) {
     PDFJS.disableFontFace = true;
+    console.warn(mozL10n.get('web_fonts_disabled', null,
+      'Web fonts are disabled: unable to use embedded PDF fonts.'));
   }
 
   if ('textLayer' in hashParams) {
     switch (hashParams['textLayer']) {
       case 'off':
         PDFJS.disableTextLayer = true;
         break;
       case 'visible':
@@ -5083,19 +5086,23 @@ document.addEventListener('DOMContentLoa
 
   document.getElementById('download').addEventListener('click',
     SecondaryToolbar.downloadClick.bind(SecondaryToolbar));
 
   PDFView.setTitleUsingUrl(file);
   PDFView.initPassiveLoading();
   return;
 
-  PDFView.open(file, 0);
-
-}, true);
+  if (file) {
+    PDFView.open(file, 0);
+  }
+
+}
+
+document.addEventListener('DOMContentLoaded', webViewerLoad, true);
 
 function updateViewarea() {
 
   if (!PDFView.initialized)
     return;
   var visible = PDFView.getVisiblePages();
   var visiblePages = visible.views;
   if (visiblePages.length === 0) {
@@ -5119,19 +5126,21 @@ function updateViewarea() {
       break;
     }
   }
 
   if (!stillFullyVisible) {
     currentId = visiblePages[0].id;
   }
 
-  updateViewarea.inProgress = true; // used in "set page"
-  PDFView.page = currentId;
-  updateViewarea.inProgress = false;
+  if (!PresentationMode.active) {
+    updateViewarea.inProgress = true; // used in "set page"
+    PDFView.page = currentId;
+    updateViewarea.inProgress = false;
+  }
 
   var currentScale = PDFView.currentScale;
   var currentScaleValue = PDFView.currentScaleValue;
   var normalizedScaleValue = parseFloat(currentScaleValue) === currentScale ?
     Math.round(currentScale * 10000) / 100 : currentScaleValue;
 
   var pageNumber = firstPage.id;
   var pdfOpenParams = '#page=' + pageNumber;
@@ -5351,16 +5360,17 @@ window.addEventListener('keydown', funct
           // ... and resetting the scale after browser adjusts its scale
           PDFView.setScale(DEFAULT_SCALE, true);
         });
         handled = false;
         break;
     }
   }
 
+
   // CTRL+ALT or Option+Command
   if (cmd === 3 || cmd === 10) {
     switch (evt.keyCode) {
       case 80: // p
         SecondaryToolbar.presentationModeClick();
         handled = true;
         break;
       case 71: // g
@@ -5374,33 +5384,25 @@ window.addEventListener('keydown', funct
   if (handled) {
     evt.preventDefault();
     return;
   }
 
   // Some shortcuts should not get handled if a control/input element
   // is selected.
   var curElement = document.activeElement || document.querySelector(':focus');
-  if (curElement && (curElement.tagName.toUpperCase() === 'INPUT' ||
-                     curElement.tagName.toUpperCase() === 'TEXTAREA' ||
-                     curElement.tagName.toUpperCase() === 'SELECT')) {
+  var curElementTagName = curElement && curElement.tagName.toUpperCase();
+  if (curElementTagName === 'INPUT' ||
+      curElementTagName === 'TEXTAREA' ||
+      curElementTagName === 'SELECT') {
     // Make sure that the secondary toolbar is closed when Escape is pressed.
     if (evt.keyCode !== 27) { // 'Esc'
       return;
     }
   }
-  var controlsElement = document.getElementById('toolbar');
-  while (curElement) {
-    if (curElement === controlsElement && !PresentationMode.active)
-      return; // ignoring if the 'toolbar' element is focused
-    curElement = curElement.parentNode;
-  }
-  // Workaround for issue in Firefox, that prevents scroll keys from working
-  // when elements with 'tabindex' are focused.
-  PDFView.container.blur();
 
   if (cmd === 0) { // no control key pressed at all.
     switch (evt.keyCode) {
       case 38: // up arrow
       case 33: // pg up
       case 8: // backspace
         if (!PresentationMode.active &&
             PDFView.currentScaleValue !== 'page-fit') {
@@ -5466,16 +5468,33 @@ window.addEventListener('keydown', funct
         if (!PresentationMode.active) {
           HandTool.toggle();
         }
         break;
       case 82: // 'r'
         PDFView.rotatePages(90);
         break;
     }
+    if (!handled && !PresentationMode.active) {
+      // 33=Page Up  34=Page Down  35=End    36=Home
+      // 37=Left     38=Up         39=Right  40=Down
+      if (evt.keyCode >= 33 && evt.keyCode <= 40 &&
+          !PDFView.container.contains(curElement)) {
+        // The page container is not focused, but a page navigation key has been
+        // pressed. Change the focus to the viewer container to make sure that
+        // navigation by keyboard works as expected.
+        PDFView.container.focus();
+      }
+      // 32=Spacebar
+      if (evt.keyCode === 32 && curElementTagName !== 'BUTTON') {
+  // Workaround for issue in Firefox, that prevents scroll keys from working
+  // when elements with 'tabindex' are focused. (#3499)
+        PDFView.container.blur();
+      }
+    }
   }
 
   if (cmd === 4) { // shift-key
     switch (evt.keyCode) {
       case 32: // spacebar
         if (!PresentationMode.active &&
             PDFView.currentScaleValue !== 'page-fit') {
           break;
--- a/browser/locales/en-US/chrome/browser/devtools/webconsole.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/webconsole.properties
@@ -209,11 +209,16 @@ emptyPropertiesList=No properties to dis
 # when you hover the red bubble that shows how many times a message is repeated
 # in the web console output.
 # This is a semi-colon list of plural forms.
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # #1 number of message repeats
 # example: 3 repeats
 messageRepeats.tooltip2=#1 repeat;#1 repeats
 
+# 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.
--- a/browser/metro/base/content/startui/BookmarksView.js
+++ b/browser/metro/base/content/startui/BookmarksView.js
@@ -4,47 +4,53 @@
 
 "use strict";
 
 /**
  * Wraps a list/grid control implementing nsIDOMXULSelectControlElement and
  * fills it with the user's bookmarks.
  *
  * @param           aSet    Control implementing nsIDOMXULSelectControlElement.
- * @param {Number}  aLimit  Maximum number of items to show in the view.
  * @param           aRoot   Bookmark root to show in the view.
  */
-function BookmarksView(aSet, aLimit, aRoot, aFilterUnpinned) {
+function BookmarksView(aSet, aRoot, aFilterUnpinned) {
   View.call(this, aSet);
 
   this._inBatch = false; // batch up grid updates to avoid redundant arrangeItems calls
 
-  this._limit = aLimit;
+  // View monitors this for maximum tile display counts
+  this.tilePrefName = "browser.display.startUI.bookmarks.maxresults";
+  this.showing = this.maxTiles > 0;
+
   this._filterUnpinned = aFilterUnpinned;
   this._bookmarkService = PlacesUtils.bookmarks;
   this._navHistoryService = gHistSvc;
 
   this._changes = new BookmarkChangeListener(this);
   this._pinHelper = new ItemPinHelper("metro.bookmarks.unpinned");
   this._bookmarkService.addObserver(this._changes, false);
   StartUI.chromeWin.addEventListener('MozAppbarDismissing', this, false);
   StartUI.chromeWin.addEventListener('BookmarksNeedsRefresh', this, false);
   window.addEventListener("TabClose", this, true);
 
   this.root = aRoot;
 }
 
 BookmarksView.prototype = Util.extend(Object.create(View.prototype), {
-  _limit: null,
   _set: null,
   _changes: null,
   _root: null,
   _sort: 0, // Natural bookmark order.
   _toRemove: null,
 
+  // For View's showing property
+  get vbox() {
+    return document.getElementById("start-bookmarks");
+  },
+
   get sort() {
     return this._sort;
   },
 
   set sort(aSort) {
     this._sort = aSort;
     this.clearBookmarks();
     this.getBookmarks();
@@ -62,16 +68,21 @@ BookmarksView.prototype = Util.extend(Ob
     this._bookmarkService.removeObserver(this._changes);
     if (StartUI.chromeWin) {
       StartUI.chromeWin.removeEventListener('MozAppbarDismissing', this, false);
       StartUI.chromeWin.removeEventListener('BookmarksNeedsRefresh', this, false);
     }
     View.prototype.destruct.call(this);
   },
 
+  refreshView: function () {
+    this.clearBookmarks();
+    this.getBookmarks();
+  },
+
   handleItemClick: function bv_handleItemClick(aItem) {
     let url = aItem.getAttribute("value");
     StartUI.goToURI(url);
   },
 
   _getItemForBookmarkId: function bv__getItemForBookmark(aBookmarkId) {
     return this._set.querySelector("richgriditem[anonid='" + aBookmarkId + "']");
   },
@@ -86,17 +97,17 @@ BookmarksView.prototype = Util.extend(Ob
   },
 
   getBookmarks: function bv_getBookmarks(aRefresh) {
     let options = this._navHistoryService.getNewQueryOptions();
     options.queryType = options.QUERY_TYPE_BOOKMARKS;
     options.excludeQueries = true; // Don't include "smart folders"
     options.sortingMode = this._sort;
 
-    let limit = this._limit || Infinity;
+    let limit = this.maxTiles;
 
     let query = this._navHistoryService.getNewQuery();
     query.setFolders([Bookmarks.metroRoot], 1);
 
     let result = this._navHistoryService.executeQuery(query, options);
     let rootNode = result.root;
     rootNode.containerOpen = true;
     let childCount = rootNode.childCount;
@@ -300,17 +311,17 @@ BookmarksView.prototype = Util.extend(Ob
   }
 });
 
 let BookmarksStartView = {
   _view: null,
   get _grid() { return document.getElementById("start-bookmarks-grid"); },
 
   init: function init() {
-    this._view = new BookmarksView(this._grid, StartUI.maxResultsPerSection, Bookmarks.metroRoot, true);
+    this._view = new BookmarksView(this._grid, Bookmarks.metroRoot, true);
     this._view.getBookmarks();
     this._grid.removeAttribute("fade");
   },
 
   uninit: function uninit() {
     if (this._view) {
       this._view.destruct();
     }
--- a/browser/metro/base/content/startui/HistoryView.js
+++ b/browser/metro/base/content/startui/HistoryView.js
@@ -1,59 +1,72 @@
 /* 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';
 
-function HistoryView(aSet, aLimit, aFilterUnpinned) {
+function HistoryView(aSet, aFilterUnpinned) {
   View.call(this, aSet);
 
   this._inBatch = 0;
 
-  this._limit = aLimit;
+  // View monitors this for maximum tile display counts
+  this.tilePrefName = "browser.display.startUI.history.maxresults";
+  this.showing = this.maxTiles > 0;
+
   this._filterUnpinned = aFilterUnpinned;
   this._historyService = PlacesUtils.history;
   this._navHistoryService = gHistSvc;
 
   this._pinHelper = new ItemPinHelper("metro.history.unpinned");
   this._historyService.addObserver(this, false);
   StartUI.chromeWin.addEventListener('MozAppbarDismissing', this, false);
   StartUI.chromeWin.addEventListener('HistoryNeedsRefresh', this, false);
   window.addEventListener("TabClose", this, true);
 }
 
 HistoryView.prototype = Util.extend(Object.create(View.prototype), {
   _set: null,
   _toRemove: null,
 
+  // For View's showing property
+  get vbox() {
+    return document.getElementById("start-history");
+  },
+
   destruct: function destruct() {
     this._historyService.removeObserver(this);
     if (StartUI.chromeWin) {
       StartUI.chromeWin.removeEventListener('MozAppbarDismissing', this, false);
       StartUI.chromeWin.removeEventListener('HistoryNeedsRefresh', this, false);
     }
     View.prototype.destruct.call(this);
   },
 
+  refreshView: function () {
+    this.onClearHistory();
+    this.populateGrid();
+  },
+
   handleItemClick: function tabview_handleItemClick(aItem) {
     let url = aItem.getAttribute("value");
     StartUI.goToURI(url);
   },
 
   populateGrid: function populateGrid(aRefresh) {
     this._inBatch++; // always batch up grid updates to avoid redundant arrangeItems calls
     let query = this._navHistoryService.getNewQuery();
     let options = this._navHistoryService.getNewQueryOptions();
     options.excludeQueries = true;
     options.queryType = options.QUERY_TYPE_HISTORY;
     options.resultType = options.RESULTS_AS_URI;
     options.sortingMode = options.SORT_BY_DATE_DESCENDING;
 
-    let limit = this._limit || Infinity;
+    let limit = this.maxTiles;
     let result = this._navHistoryService.executeQuery(query, options);
     let rootNode = result.root;
     rootNode.containerOpen = true;
     let childCount = rootNode.childCount;
 
     for (let i = 0, addedCount = 0; i < childCount && addedCount < limit; i++) {
       let node = rootNode.getChild(i);
       let uri = node.uri;
@@ -292,17 +305,17 @@ HistoryView.prototype = Util.extend(Obje
   }
 });
 
 let HistoryStartView = {
   _view: null,
   get _grid() { return document.getElementById("start-history-grid"); },
 
   init: function init() {
-    this._view = new HistoryView(this._grid, StartUI.maxResultsPerSection, true);
+    this._view = new HistoryView(this._grid, true);
     this._view.populateGrid();
     this._grid.removeAttribute("fade");
   },
 
   uninit: function uninit() {
     if (this._view) {
       this._view.destruct();
     }
--- a/browser/metro/base/content/startui/StartUI.js
+++ b/browser/metro/base/content/startui/StartUI.js
@@ -4,20 +4,16 @@
 
 "use strict";
 
 Cu.import("resource://gre/modules/Services.jsm");
 
 var StartUI = {
   get startUI() { return document.getElementById("start-container"); },
 
-  get maxResultsPerSection() {
-    return Services.prefs.getIntPref("browser.display.startUI.maxresults");
-  },
-
   get chromeWin() {
     // XXX Not e10s friendly. We use this in a few places.
     return Services.wm.getMostRecentWindow('navigator:browser');
   },
 
   init: function init() {
     this.startUI.addEventListener("click", this, false);
     this.startUI.addEventListener("MozMousePixelScroll", this, false);
--- a/browser/metro/base/content/startui/TopSitesView.js
+++ b/browser/metro/base/content/startui/TopSitesView.js
@@ -1,23 +1,21 @@
 /* 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 prefs = Components.classes["@mozilla.org/preferences-service;1"].
-      getService(Components.interfaces.nsIPrefBranch);
-
 Cu.import("resource://gre/modules/PageThumbs.jsm");
 
-function TopSitesView(aGrid, aMaxSites) {
+function TopSitesView(aGrid) {
   View.call(this, aGrid);
-
-  this._topSitesMax = aMaxSites;
+  // View monitors this for maximum tile display counts
+  this.tilePrefName = "browser.display.startUI.topsites.maxresults";
+  this.showing = this.maxTiles > 0 && !this.isFirstRun();
 
   // clean up state when the appbar closes
   StartUI.chromeWin.addEventListener('MozAppbarDismissing', this, false);
   let history = Cc["@mozilla.org/browser/nav-history-service;1"].
                 getService(Ci.nsINavHistoryService);
   history.addObserver(this, false);
 
   Services.obs.addObserver(this, "Metro:RefreshTopsiteThumbnail", false);
@@ -25,22 +23,26 @@ function TopSitesView(aGrid, aMaxSites) 
   NewTabUtils.allPages.register(this);
   TopSites.prepareCache().then(function(){
     this.populateGrid();
   }.bind(this));
 }
 
 TopSitesView.prototype = Util.extend(Object.create(View.prototype), {
   _set:null,
-  _topSitesMax: null,
   // _lastSelectedSites used to temporarily store blocked/removed sites for undo/restore-ing
   _lastSelectedSites: null,
   // isUpdating used only for testing currently
   isUpdating: false,
 
+  // For View's showing property
+  get vbox() {
+    return document.getElementById("start-topsites");
+  },
+
   destruct: function destruct() {
     Services.obs.removeObserver(this, "Metro:RefreshTopsiteThumbnail");
     NewTabUtils.allPages.unregister(this);
     if (StartUI.chromeWin) {
       StartUI.chromeWin.removeEventListener('MozAppbarDismissing', this, false);
     }
     View.prototype.destruct.call(this);
   },
@@ -185,22 +187,27 @@ TopSitesView.prototype = Util.extend(Obj
       this._set.arrangeItems();
     }
   },
 
   populateGrid: function populateGrid() {
     this.isUpdating = true;
 
     let sites = TopSites.getSites();
-    if (this._topSitesMax) {
-      sites = sites.slice(0, this._topSitesMax);
-    }
+
     let tileset = this._set;
     tileset.clearAll(true);
 
+    if (!this.maxTiles) {
+      this.isUpdating = false;
+      return;
+    } else {
+      sites = sites.slice(0, this.maxTiles);
+    }
+
     for (let site of sites) {
       let slot = tileset.nextSlot();
       this.updateTile(slot, site);
     }
     tileset.arrangeItems();
     this.isUpdating = false;
   },
 
@@ -209,17 +216,17 @@ TopSitesView.prototype = Util.extend(Obj
     for (let item of nodes) {
       if ("isBound" in item && item.isBound) {
         item.refreshBackgroundImage();
       }
     }
   },
 
   isFirstRun: function isFirstRun() {
-    return prefs.getBoolPref("browser.firstrun.show.localepicker");
+    return Services.prefs.getBoolPref("browser.firstrun.show.localepicker");
   },
 
   _adjustDOMforViewState: function _adjustDOMforViewState(aState) {
     if (!this._set)
       return;
     if (!aState)
       aState = this._set.getAttribute("viewstate");
 
@@ -238,23 +245,29 @@ TopSitesView.prototype = Util.extend(Obj
       if (tileType) {
         item.setAttribute("tiletype", tileType);
       } else {
         item.removeAttribute("tiletype");
       }
     }
   },
 
+  refreshView: function () {
+    this.populateGrid();
+  },
+
   // nsIObservers
   observe: function (aSubject, aTopic, aState) {
     switch (aTopic) {
       case "Metro:RefreshTopsiteThumbnail":
         this.forceReloadOfThumbnail(aState);
         break;
     }
+    View.prototype.observe.call(this, aSubject, aTopic, aState);
+    this.showing = this.maxTiles > 0 && !this.isFirstRun();
   },
 
   // nsINavHistoryObserver
   onBeginUpdateBatch: function() {
   },
 
   onEndUpdateBatch: function() {
   },
@@ -290,21 +303,17 @@ TopSitesView.prototype = Util.extend(Obj
 
 });
 
 let TopSitesStartView = {
   _view: null,
   get _grid() { return document.getElementById("start-topsites-grid"); },
 
   init: function init() {
-    this._view = new TopSitesView(this._grid, 8);
-    if (this._view.isFirstRun()) {
-      let topsitesVbox = document.getElementById("start-topsites");
-      topsitesVbox.setAttribute("hidden", "true");
-    }
+    this._view = new TopSitesView(this._grid);
     this._grid.removeAttribute("fade");
   },
 
   uninit: function uninit() {
     if (this._view) {
       this._view.destruct();
     }
   },
--- a/browser/metro/base/tests/mochitest/browser_bookmarks.js
+++ b/browser/metro/base/tests/mochitest/browser_bookmarks.js
@@ -51,17 +51,17 @@ gTests.push({
     let promise = waitForEvent(Elements.contextappbar, "transitionend", null, Elements.contextappbar);
     hideButton.click();
     yield promise;
 
     item = gStartView._getItemForBookmarkId(2);
 
     ok(!item, "Item not in grid");
     ok(!gStartView._pinHelper.isPinned(2), "Item hidden");
-    ok(gStartView._set.itemCount === gStartView._limit, "Grid repopulated");
+    ok(gStartView._set.itemCount === gStartView.maxTiles, "Grid repopulated");
 
     // --------- hide multiple items
 
     let item1 = gStartView._getItemForBookmarkId(0);
     let item2 = gStartView._getItemForBookmarkId(5);
     let item3 = gStartView._getItemForBookmarkId(12);
 
     let promise = waitForEvent(Elements.contextappbar, "transitionend", null, Elements.contextappbar);
@@ -77,17 +77,17 @@ gTests.push({
     yield promise;
 
     item1 = gStartView._getItemForBookmarkId(0);
     item2 = gStartView._getItemForBookmarkId(5);
     item3 = gStartView._getItemForBookmarkId(12);
 
     ok(!item1 && !item2 && !item3, "Items are not in grid");
     ok(!gStartView._pinHelper.isPinned(0) && !gStartView._pinHelper.isPinned(5) && !gStartView._pinHelper.isPinned(12) , "Items hidden");
-    ok(gStartView._set.itemCount === gStartView._limit - 1, "Grid repopulated");
+    ok(gStartView._set.itemCount === gStartView.maxTiles - 1, "Grid repopulated");
   }
 });
 
 gTests.push({
   desc: "Test bookmarks StartUI delete",
   setUp: setup,
   tearDown: tearDown,
   run: function testBookmarksStartDelete() {
@@ -109,17 +109,17 @@ gTests.push({
     EventUtils.synthesizeMouse(deleteButton, 10, 10, {}, window);
     yield promise;
 
     item = gStartView._getItemForBookmarkId(2);
 
     ok(!item, "Item not in grid");
     ok(BookmarksTestHelper._nodes[2], "Item not deleted yet");
     ok(!restoreButton.hidden, "Restore button is visible.");
-    ok(gStartView._set.itemCount === gStartView._limit, "Grid repopulated");
+    ok(gStartView._set.itemCount === gStartView.maxTiles, "Grid repopulated");
 
     let promise = waitForEvent(Elements.contextappbar, "transitionend", null, Elements.contextappbar);
     EventUtils.synthesizeMouse(restoreButton, 10, 10, {}, window);
     yield promise;
 
     item = gStartView._getItemForBookmarkId(2);
     ok(item, "Item back in grid");
     ok(gStartView._set.getIndexOfItem(item) === initialLocation, "Back in same position.");
@@ -147,17 +147,17 @@ gTests.push({
     let promise = waitForEvent(Elements.contextappbar, "transitionend", null, Elements.contextappbar);
     Elements.contextappbar.dismiss();
     yield promise;
 
     item = gStartView._getItemForBookmarkId(2);
 
     ok(!item, "Item not in grid");
     ok(!BookmarksTestHelper._nodes[2], "Item RIP");
-    ok(gStartView._set.itemCount === gStartView._limit, "Grid repopulated");
+    ok(gStartView._set.itemCount === gStartView.maxTiles, "Grid repopulated");
 
     // --------- delete multiple items and restore
 
     let item1 = gStartView._getItemForBookmarkId(0);
     let item2 = gStartView._getItemForBookmarkId(5);
     let item3 = gStartView._getItemForBookmarkId(12);
 
     let initialLocation1 = gStartView._set.getIndexOfItem(item1);
@@ -179,31 +179,31 @@ gTests.push({
     item1 = gStartView._getItemForBookmarkId(0);
     item2 = gStartView._getItemForBookmarkId(5);
     item3 = gStartView._getItemForBookmarkId(12);
 
     ok(!item1 && !item2 && !item3, "Items are not in grid");
     ok(BookmarksTestHelper._nodes[0] && BookmarksTestHelper._nodes[5] && BookmarksTestHelper._nodes[12],
       "Items not deleted yet");
     ok(!restoreButton.hidden, "Restore button is visible.");
-    ok(gStartView._set.itemCount === gStartView._limit - 1, "Grid repopulated");
+    ok(gStartView._set.itemCount === gStartView.maxTiles - 1, "Grid repopulated");
 
     let promise = waitForEvent(Elements.contextappbar, "transitionend", null, Elements.contextappbar);
     EventUtils.synthesizeMouse(restoreButton, 10, 10, {}, window);
     yield promise;
 
     item1 = gStartView._getItemForBookmarkId(0);
     item2 = gStartView._getItemForBookmarkId(5);
     item3 = gStartView._getItemForBookmarkId(12);
 
     ok(item1 && item2 && item3, "Items are back in grid");
     ok(gStartView._set.getIndexOfItem(item1) === initialLocation1 &&
       gStartView._set.getIndexOfItem(item2) === initialLocation2 &&
       gStartView._set.getIndexOfItem(item3) === initialLocation3, "Items back in the same position.");
-    ok(gStartView._set.itemCount === gStartView._limit, "Grid repopulated");
+    ok(gStartView._set.itemCount === gStartView.maxTiles, "Grid repopulated");
 
     // --------- delete multiple items for good
 
     let item1 = gStartView._getItemForBookmarkId(0);
     let item2 = gStartView._getItemForBookmarkId(5);
     let item3 = gStartView._getItemForBookmarkId(12);
 
     let promise = waitForEvent(Elements.contextappbar, "transitionend", null, Elements.contextappbar);
@@ -235,11 +235,11 @@ gTests.push({
 
     item1 = gStartView._getItemForBookmarkId(0);
     item2 = gStartView._getItemForBookmarkId(5);
     item3 = gStartView._getItemForBookmarkId(12);
 
     ok(!item1 && !item2 && !item3, "Items are not in grid");
     ok(!BookmarksTestHelper._nodes[0] && !BookmarksTestHelper._nodes[5] && !BookmarksTestHelper._nodes[12],
       "Items are gone");
-    ok(gStartView._set.itemCount === gStartView._limit - 1, "Grid repopulated");
+    ok(gStartView._set.itemCount === gStartView.maxTiles - 1, "Grid repopulated");
   }
 });
--- a/browser/metro/base/tests/mochitest/browser_history.js
+++ b/browser/metro/base/tests/mochitest/browser_history.js
@@ -62,17 +62,17 @@ gTests.push({
     let promise = waitForEvent(Elements.contextappbar, "transitionend", null, Elements.contextappbar);
     hideButton.click();
     yield promise;
 
     item = gStartView._set.getItemsByUrl(uriFromIndex(2))[0];
 
     ok(!item, "Item not in grid");
     ok(!gStartView._pinHelper.isPinned(uriFromIndex(2)), "Item hidden");
-    is(gStartView._set.itemCount, gStartView._limit, "Grid repopulated");
+    is(gStartView._set.itemCount, gStartView.maxTiles, "Grid repopulated");
 
     // --------- hide multiple items
 
     let item1 = gStartView._set.getItemsByUrl(uriFromIndex(0))[0];
     let item2 = gStartView._set.getItemsByUrl(uriFromIndex(5))[0];
     let item3 = gStartView._set.getItemsByUrl(uriFromIndex(12))[0];
 
     scrollToEnd();
@@ -89,17 +89,17 @@ gTests.push({
     yield promise;
 
     item1 = gStartView._set.getItemsByUrl(uriFromIndex(0))[0];
     item2 = gStartView._set.getItemsByUrl(uriFromIndex(5))[0];
     item3 = gStartView._set.getItemsByUrl(uriFromIndex(12))[0];
 
     ok(!item1 && !item2 && !item3, "Items are not in grid");
     ok(!gStartView._pinHelper.isPinned(uriFromIndex(0)) && !gStartView._pinHelper.isPinned(uriFromIndex(5)) && !gStartView._pinHelper.isPinned(uriFromIndex(12)) , "Items hidden");
-    ok(gStartView._set.itemCount === gStartView._limit - 1, "Grid repopulated");
+    ok(gStartView._set.itemCount === gStartView.maxTiles - 1, "Grid repopulated");
   }
 });
 
 gTests.push({
   desc: "Test history StartUI delete",
   setUp: setup,
   tearDown: tearDown,
   run: function testHistoryStartDelete() {
@@ -121,26 +121,26 @@ gTests.push({
     EventUtils.synthesizeMouse(deleteButton, 10, 10, {}, window);
     yield promise;
 
     item = gStartView._set.getItemsByUrl(uriFromIndex(2))[0];
 
     ok(!item, "Item not in grid");
     ok(HistoryTestHelper._nodes[uriFromIndex(2)], "Item not actually deleted yet");
     ok(!restoreButton.hidden, "Restore button is visible.");
-    ok(gStartView._set.itemCount === gStartView._limit, "Grid repopulated");
+    ok(gStartView._set.itemCount === gStartView.maxTiles, "Grid repopulated");
 
     let promise = waitForEvent(Elements.contextappbar, "transitionend", null, Elements.contextappbar);
     EventUtils.synthesizeMouse(restoreButton, 10, 10, {}, window);
     yield promise;
 
     item = gStartView._set.getItemsByUrl(uriFromIndex(2))[0];
     ok(item, "Item back in grid");
     ok(gStartView._set.getIndexOfItem(item) === initialLocation, "Back in same position.");
-    ok(gStartView._set.itemCount === gStartView._limit, "Grid repopulated");
+    ok(gStartView._set.itemCount === gStartView.maxTiles, "Grid repopulated");
 
     // --------- delete item 2 for realz
 
     let item = gStartView._set.getItemsByUrl(uriFromIndex(2))[0];
 
     let promise = waitForEvent(Elements.contextappbar, "transitionend", null, Elements.contextappbar);
     sendContextMenuClickToElement(window, item, 10, 10);
     yield promise;
@@ -169,17 +169,17 @@ gTests.push({
 
     is(populateGridSpy.callCount, 1, "populateGrid not called when a removed item is actually deleted");
     populateGridSpy.restore();
 
     item = gStartView._set.getItemsByUrl(uriFromIndex(2))[0];
 
     ok(!item, "Item not in grid");
     ok(!HistoryTestHelper._nodes[uriFromIndex(2)], "Item RIP");
-    is(gStartView._set.itemCount, gStartView._limit, "Grid repopulated");
+    is(gStartView._set.itemCount, gStartView.maxTiles, "Grid repopulated");
 
     // --------- delete multiple items and restore
 
     let item1 = gStartView._set.getItemsByUrl(uriFromIndex(0))[0];
     let item2 = gStartView._set.getItemsByUrl(uriFromIndex(5))[0];
     let item3 = gStartView._set.getItemsByUrl(uriFromIndex(12))[0];
 
     let initialLocation1 = gStartView._set.getIndexOfItem(item1);
@@ -204,31 +204,31 @@ gTests.push({
     item1 = gStartView._set.getItemsByUrl(uriFromIndex(0))[0];
     item2 = gStartView._set.getItemsByUrl(uriFromIndex(5))[0];
     item3 = gStartView._set.getItemsByUrl(uriFromIndex(12))[0];
 
     ok(!item1 && !item2 && !item3, "Items are not in grid");
     ok(HistoryTestHelper._nodes[uriFromIndex(0)] && HistoryTestHelper._nodes[uriFromIndex(5)] && HistoryTestHelper._nodes[uriFromIndex(12)],
       "Items not deleted yet");
     ok(!restoreButton.hidden, "Restore button is visible.");
-    ok(gStartView._set.itemCount === gStartView._limit - 1, "Grid repopulated");
+    ok(gStartView._set.itemCount === gStartView.maxTiles - 1, "Grid repopulated");
 
     let promise = waitForEvent(Elements.contextappbar, "transitionend", null, Elements.contextappbar);
     EventUtils.synthesizeMouse(restoreButton, 10, 10, {}, window);
     yield promise;
 
     item1 = gStartView._set.getItemsByUrl(uriFromIndex(0))[0];
     item2 = gStartView._set.getItemsByUrl(uriFromIndex(5))[0];
     item3 = gStartView._set.getItemsByUrl(uriFromIndex(12))[0];
 
     ok(item1 && item2 && item3, "Items are back in grid");
     ok(gStartView._set.getIndexOfItem(item1) === initialLocation1 &&
       gStartView._set.getIndexOfItem(item2) === initialLocation2 &&
       gStartView._set.getIndexOfItem(item3) === initialLocation3, "Items back in the same position.");
-    ok(gStartView._set.itemCount === gStartView._limit, "Grid repopulated");
+    ok(gStartView._set.itemCount === gStartView.maxTiles, "Grid repopulated");
 
     // --------- delete multiple items for good
 
     let item1 = gStartView._set.getItemsByUrl(uriFromIndex(0))[0];
     let item2 = gStartView._set.getItemsByUrl(uriFromIndex(5))[0];
     let item3 = gStartView._set.getItemsByUrl(uriFromIndex(12))[0];
 
     scrollToEnd();
@@ -249,24 +249,24 @@ gTests.push({
     item1 = gStartView._set.getItemsByUrl(uriFromIndex(0))[0];
     item2 = gStartView._set.getItemsByUrl(uriFromIndex(5))[0];
     item3 = gStartView._set.getItemsByUrl(uriFromIndex(12))[0];
 
     ok(!item1 && !item2 && !item3, "Items are not in grid");
     ok(HistoryTestHelper._nodes[uriFromIndex(0)] && HistoryTestHelper._nodes[uriFromIndex(5)] && HistoryTestHelper._nodes[uriFromIndex(12)],
       "Items not deleted yet");
     ok(!restoreButton.hidden, "Restore button is visible.");
-    ok(gStartView._set.itemCount === gStartView._limit - 1, "Grid repopulated");
+    ok(gStartView._set.itemCount === gStartView.maxTiles - 1, "Grid repopulated");
 
     let promise = waitForEvent(Elements.contextappbar, "transitionend", null, Elements.contextappbar);
     Elements.contextappbar.dismiss();
     yield promise;
 
     item1 = gStartView._set.getItemsByUrl(uriFromIndex(0))[0];
     item2 = gStartView._set.getItemsByUrl(uriFromIndex(5))[0];
     item3 = gStartView._set.getItemsByUrl(uriFromIndex(12))[0];
 
     ok(!item1 && !item2 && !item3, "Items are not in grid");
     ok(!HistoryTestHelper._nodes[uriFromIndex(0)] && !HistoryTestHelper._nodes[uriFromIndex(5)] && !HistoryTestHelper._nodes[uriFromIndex(12)],
       "Items are gone");
-    ok(gStartView._set.itemCount === gStartView._limit - 1, "Grid repopulated");
+    ok(gStartView._set.itemCount === gStartView.maxTiles - 1, "Grid repopulated");
   }
 });
--- a/browser/metro/base/tests/mochitest/helpers/BookmarksHelper.js
+++ b/browser/metro/base/tests/mochitest/helpers/BookmarksHelper.js
@@ -66,17 +66,17 @@ var BookmarksTestHelper = {
   },
 
   _originalUpdateFavicon: null,
   setup: function setup() {
     this._startView = Browser.selectedBrowser.contentWindow.BookmarksStartView._view;
 
     // Just enough items so that there will be one less then the limit
     // after removing 4 items.
-    this.createNodes(this._startView._limit + 3);
+    this.createNodes(this._startView.maxTiles + 3);
 
     this._originalNavHistoryService = this._startView._navHistoryService;
     this._startView._navHistoryService = this.MockNavHistoryService;
 
     this._originalBookmarkService = this._startView._bookmarkService;
     this._startView._bookmarkService= this.MockBookmarkService;
 
     this._originalPinHelper = this._startView._pinHelper;
--- a/browser/metro/base/tests/mochitest/helpers/HistoryHelper.js
+++ b/browser/metro/base/tests/mochitest/helpers/HistoryHelper.js
@@ -63,17 +63,17 @@ var HistoryTestHelper = {
     setPinned: function (aItem) HistoryTestHelper._nodes[aItem].pinned = true,
   },
 
   setup: function setup() {
     this._startView = Browser.selectedBrowser.contentWindow.HistoryStartView._view;
 
     // Just enough items so that there will be one less then the limit
     // after removing 4 items.
-    this.createNodes(this._startView._limit + 3);
+    this.createNodes(this._startView.maxTiles + 3);
 
     this._originalNavHistoryService = this._startView._navHistoryService;
     this._startView._navHistoryService = this.MockNavHistoryService;
 
     this._originalHistoryService = this._startView._historyService;
     this._startView._historyService= this.MockHistoryService;
 
     this._originalPinHelper = this._startView._pinHelper;
--- a/browser/metro/modules/View.jsm
+++ b/browser/metro/modules/View.jsm
@@ -23,27 +23,52 @@ function makeURI(aURL, aOriginCharset, a
 
 // --------------------------------
 // View prototype for shared functionality
 
 function View(aSet) {
   this._set = aSet;
   this._set.controller = this;
   this._window = aSet.ownerDocument.defaultView;
+  this._maxTiles = 8;
+  this._tilePrefName = "unknown";
 
   this.onResize = () => this._adjustDOMforViewState();
   this._window.addEventListener("resize", this.onResize);
 
   ColorUtils.init();
   this._adjustDOMforViewState();
 }
 
 View.prototype = {
+  set maxTiles(aVal) {
+    this._maxTiles = aVal;
+  },
+
+  get maxTiles() {
+    return this._maxTiles;
+  },
+
+  set showing(aFlag) {
+    // 'vbox' must be defined on objects that inherit from us
+    this.vbox.setAttribute("hidden", aFlag ? "false" : "true");
+  },
+
+  set tilePrefName(aStr) {
+    // Should be called once on init by objects that inherit from us
+    this._tilePrefName = aStr;
+    this._maxTiles = Services.prefs.getIntPref(this._tilePrefName);
+    Services.prefs.addObserver(this._tilePrefName, this, false);
+  },
+
   destruct: function () {
     this._window.removeEventListener("resize", this.onResize);
+    if (this._tilePrefName != "unknown") {
+      Services.prefs.removeObserver(this._tilePrefName, this);
+    }
   },
 
   _adjustDOMforViewState: function _adjustDOMforViewState(aState) {
     let grid = this._set;
     if (!grid) {
       return;
     }
     if (!aState) {
@@ -106,11 +131,27 @@ View.prototype = {
       // get the rgb value that represents this color at given opacity over a white matte
       let tintColor = ColorUtils.addRgbColors(matteColor, ColorUtils.createDecimalColorWord(r,g,b,alpha));
       aItem.setAttribute("tintColor", ColorUtils.convertDecimalToRgbColor(tintColor));
       // when bound, use the setter to propogate the color change through the tile
       if ('color' in aItem) {
         aItem.color = background;
       }
     });
-  }
+  },
 
+  refreshView: function () {
+  },
+
+  observe: function (aSubject, aTopic, aState) {
+    switch (aTopic) {
+      case "nsPref:changed": {
+        if (aState == this._tilePrefName) {
+          let count = Services.prefs.getIntPref(this._tilePrefName);
+          this.maxTiles = count;
+          this.showing = this.maxTiles > 0;
+          this.refreshView();
+        }
+        break;
+      }
+    }
+  }
 };
--- a/browser/metro/profile/metro.js
+++ b/browser/metro/profile/metro.js
@@ -107,50 +107,50 @@ pref("toolkit.screen.lock", false);
 // From libpref/src/init/all.js. Disabling text zoom in favor of APZ zoom. See bug 936940.
 pref("zoom.minPercent", 100);
 pref("zoom.maxPercent", 100);
 pref("toolkit.zoomManager.zoomValues", "1");
 
 // Device pixel to CSS px ratio, in percent. Set to -1 to calculate based on display density.
 pref("browser.viewport.scaleRatio", -1);
 
-/* use long press to display a context menu */
+// use long press to display a context menu
 pref("ui.click_hold_context_menus", false);
 
-/* offline cache prefs */
+// offline cache prefs
 pref("browser.offline-apps.notify", true);
 
-/* protocol warning prefs */
+// protocol warning prefs
 pref("network.protocol-handler.warn-external.tel", false);
 pref("network.protocol-handler.warn-external.mailto", false);
 pref("network.protocol-handler.warn-external.vnd.youtube", false);
 pref("network.protocol-handler.warn-external.ms-windows-store", false);
 pref("network.protocol-handler.external.ms-windows-store", true);
 
+/* startui prefs */
 // display the overlay nav buttons
 pref("browser.display.overlaynavbuttons", true);
-
-/* history max results display */
-pref("browser.display.history.maxresults", 100);
-
-/* max items per section of the startui */
-pref("browser.display.startUI.maxresults", 16);
-
+// max number of top site tiles to display in the startui
+pref("browser.display.startUI.topsites.maxresults", 8);
+// max items for the bookmarks compartment in the startui
+pref("browser.display.startUI.bookmarks.maxresults", 16);
+// max items for the history compartment in the startui
+pref("browser.display.startUI.history.maxresults", 16);
 // Number of times to display firstrun instructions on new tab page
 pref("browser.firstrun.count", 3);
 // Has the content first run been dismissed
 pref("browser.firstrun-content.dismissed", false);
 
 // Backspace and Shift+Backspace behavior
 // 0 goes Back/Forward
 // 1 act like PgUp/PgDown
 // 2 and other values, nothing
 pref("browser.backspace_action", 0);
 
-/* session history */
+// session history
 pref("browser.sessionhistory.max_entries", 50);
 
 // On startup, don't automatically restore tabs
 pref("browser.startup.page", 1);
 
 /* session store */
 pref("browser.sessionstore.resume_from_crash", true);
 pref("browser.sessionstore.resume_session_once", false);
@@ -636,23 +636,16 @@ pref("urlclassifier.updatecachemax", 419
 // URL for checking the reason for a malware warning.
 pref("browser.safebrowsing.malware.reportURL", "https://safebrowsing.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site=");
 #endif
 
 // True if this is the first time we are showing about:firstrun
 pref("browser.firstrun.show.localepicker", false);
 
 // True if you always want dump() to work
-//
-// On Android, you also need to do the following for the output
-// to show up in logcat:
-//
-// $ adb shell stop
-// $ adb shell setprop log.redirect-stdio true
-// $ adb shell start
 pref("javascript.options.showInConsole", true);
 pref("browser.dom.window.dump.enabled", true);
 
 // controls if we want camera support
 pref("device.camera.enabled", true);
 pref("media.realtime_decoder.enabled", true);
 
 // Metro manages state by autodetection
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -76,17 +76,17 @@
 
 #urlbar:-moz-lwtheme:not([focused="true"]),
 .searchbar-textbox:-moz-lwtheme:not([focused="true"]) {
   opacity: .85;
 }
 
 /* Places toolbar */
 toolbarbutton.bookmark-item,
-#personal-bookmarks[cui-areatype="toolbar"] > #bookmarks-toolbar-placeholder {
+#personal-bookmarks[cui-areatype="toolbar"]:not([overflowedItem=true]) > #bookmarks-toolbar-placeholder {
   margin: 0;
   padding: 2px 3px;
 }
 
 toolbarbutton.bookmark-item:hover:active,
 toolbarbutton.bookmark-item[open="true"] {
   padding-top: 3px;
   padding-bottom: 1px;
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -77,16 +77,17 @@ browser.jar:
   skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png  (customizableui/customizeMode-separatorHorizontal.png)
   skin/classic/browser/customizableui/customizeMode-separatorVertical.png  (customizableui/customizeMode-separatorVertical.png)
   skin/classic/browser/customizableui/customizeFavicon.ico  (../shared/customizableui/customizeFavicon.ico)
   skin/classic/browser/customizableui/info-icon-customizeTip.png  (../shared/customizableui/info-icon-customizeTip.png)
   skin/classic/browser/customizableui/menuPanel-customizeFinish.png  (../shared/customizableui/menuPanel-customizeFinish.png)
   skin/classic/browser/customizableui/panelarrow-customizeTip.png  (../shared/customizableui/panelarrow-customizeTip.png)
 * skin/classic/browser/customizableui/panelUIOverlay.css (customizableui/panelUIOverlay.css)
   skin/classic/browser/customizableui/subView-arrow-back-inverted.png  (../shared/customizableui/subView-arrow-back-inverted.png)
+  skin/classic/browser/customizableui/subView-arrow-back-inverted-rtl.png  (../shared/customizableui/subView-arrow-back-inverted-rtl.png)
   skin/classic/browser/downloads/allDownloadsViewOverlay.css   (downloads/allDownloadsViewOverlay.css)
   skin/classic/browser/downloads/buttons.png          (downloads/buttons.png)
   skin/classic/browser/downloads/contentAreaDownloadsView.css  (downloads/contentAreaDownloadsView.css)
   skin/classic/browser/downloads/download-glow.png    (downloads/download-glow.png)
   skin/classic/browser/downloads/download-glow-menuPanel.png (downloads/download-glow-menuPanel.png)
   skin/classic/browser/downloads/download-notification-finish.png (downloads/download-notification-finish.png)
   skin/classic/browser/downloads/download-notification-start.png (downloads/download-notification-start.png)
   skin/classic/browser/downloads/download-summary.png (downloads/download-summary.png)
index 522f0b2303379e540afcaaeea43c250d36d0c54e..0082253ab47cbcf18efe231ca4a45afc619466b6
GIT binary patch
literal 23791
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
index 4d0d07880826db8ccfdefadc5ec75649ee5faa47..0c8427aedcce3ce9e0a5ce0d5245363554eeb428
GIT binary patch
literal 64835
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
YW}G&Jl)hm@VSk&lQi|^@B@6=pKfU&bu>b%7
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -154,26 +154,30 @@ toolbarbutton.chevron:-moz-locale-dir(rt
 
   toolbarbutton.chevron > .toolbarbutton-icon {
     width: 13px;
   }
 }
 
 /* ----- BOOKMARK BUTTONS ----- */
 
-toolbarbutton.bookmark-item:not(.subviewbutton),
-#personal-bookmarks[cui-areatype="toolbar"] > #bookmarks-toolbar-placeholder {
+toolbarbutton.bookmark-item:not(.subviewbutton):not(#bookmarks-menu-button),
+#personal-bookmarks[cui-areatype="toolbar"]:not([overflowedItem=true]) > #bookmarks-toolbar-placeholder {
   font-weight: bold;
   color: #222;
   border: 0;
   border-radius: 10000px;
   padding: 1px 8px;
   margin: 0 0 1px;
 }
 
+#personal-bookmarks[cui-areatype="toolbar"]:not([overflowedItem=true]) > #bookmarks-toolbar-placeholder {
+  -moz-box-orient: horizontal;
+}
+
 .bookmark-item > .toolbarbutton-menu-dropmarker {
   list-style-image: url("chrome://browser/skin/places/folderDropArrow.png");
   -moz-image-region: rect(0, 7px, 5px, 0);
   margin-top: 1px;
   -moz-margin-start: 3px;
   -moz-margin-end: -2px;
 }
 
@@ -189,18 +193,18 @@ toolbarbutton.bookmark-item:not(.subview
 }
 
 .bookmark-item > .toolbarbutton-text,
 #personal-bookmarks[cui-areatype="toolbar"] > #bookmarks-toolbar-placeholder > .toolbarbutton-text {
   display: -moz-box !important; /* Force the display of the label for bookmarks */
   margin: 0 !important;
 }
 
-toolbarbutton.bookmark-item:hover,
-toolbarbutton.bookmark-item[open="true"] {
+toolbarbutton.bookmark-item:not(#bookmarks-menu-button):hover,
+toolbarbutton.bookmark-item:not(#bookmarks-menu-button)[open="true"] {
   background-color: rgba(0, 0, 0, .205);
 }
 
 toolbarbutton.bookmark-item:hover:not(.subviewbutton),
 toolbarbutton.bookmark-item[open="true"]:not(.subviewbutton) {
   color: #FFF !important;
   text-shadow: 0 1px rgba(0, 0, 0, .4) !important;
 }
@@ -212,18 +216,18 @@ toolbarbutton.bookmark-item[open="true"]
 
 @media (min-resolution: 2dppx) {
   .bookmark-item:hover > .toolbarbutton-menu-dropmarker,
   .bookmark-item[open="true"] > .toolbarbutton-menu-dropmarker {
     -moz-image-region: rect(10px, 14px, 20px, 0);
   }
 }
 
-toolbarbutton.bookmark-item:active:hover,
-toolbarbutton.bookmark-item[open="true"] {
+toolbarbutton.bookmark-item:not(#bookmarks-menu-button):active:hover,
+toolbarbutton.bookmark-item:not(#bookmarks-menu-button)[open="true"] {
   box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.4), 0 1px rgba(255, 255, 255, 0.4);
   background-color: rgba(0, 0, 0, .5);
 }
 
 toolbarbutton.bookmark-item > menupopup {
   margin-top: 2px;
   -moz-margin-start: 3px;
 }
@@ -232,17 +236,17 @@ toolbarbutton.bookmark-item > menupopup 
 #personal-bookmarks[cui-areatype="toolbar"] > #bookmarks-toolbar-placeholder > .toolbarbutton-icon {
   width: 16px;
   min-height: 16px;
   max-height: 16px;
 }
 
 .bookmark-item > .toolbarbutton-icon[label]:not([label=""]),
 .bookmark-item > .toolbarbutton-icon[type="menu"],
-#personal-bookmarks[cui-areatype="toolbar"] > #bookmarks-toolbar-placeholder > .toolbarbutton-icon[label]:not([label=""]) {
+#personal-bookmarks[cui-areatype="toolbar"]:not([overflowedItem=true]) > #bookmarks-toolbar-placeholder > .toolbarbutton-icon[label]:not([label=""]) {
   -moz-margin-end: 5px;
 }
 
 .bookmark-item[container] {
   list-style-image: url("chrome://global/skin/tree/folder.png");
 }
 
 .bookmark-item[container][livemark] {
@@ -359,27 +363,20 @@ toolbarpaletteitem[place="palette"] > #p
   #bookmarks-toolbar-placeholder > .toolbarbutton-icon {
     width: 16px;
   }
 }
 
 /* ----- BOOKMARK STAR ANIMATION ----- */
 
 @keyframes animation-bookmarkAdded {
-  from { transform: rotate(0deg) translateX(-20px) rotate(0deg) scale(1); opacity: 0; }
-  60%  { transform: rotate(180deg) translateX(-20px) rotate(-180deg) scale(2.2); opacity: 1; }
+  from { transform: rotate(0deg) translateX(-14px) rotate(0deg) scale(1); opacity: 0; }
+  60%  { transform: rotate(180deg) translateX(-14px) rotate(-180deg) scale(2.2); opacity: 1; }
   80%  { opacity: 1; }
-  to   { transform: rotate(180deg) translateX(-20px) rotate(-180deg) scale(1); opacity: 0; }
-}
-
-@keyframes animation-bookmarkAddedToBookmarksBar {
-  from { transform: rotate(0deg) translateX(-12px) rotate(0deg) scale(1); opacity: 0; }
-  60%  { transform: rotate(180deg) translateX(-12px) rotate(-180deg) scale(2.2); opacity: 1; }
-  80%  { opacity: 1; }
-  to   { transform: rotate(180deg) translateX(-12px) rotate(-180deg) scale(1); opacity: 0; }
+  to   { transform: rotate(180deg) translateX(-14px) rotate(-180deg) scale(1); opacity: 0; }
 }
 
 @keyframes animation-bookmarkPulse {
   from { transform: scale(1); }
   50%  { transform: scale(1.3); }
   to   { transform: scale(1); }
 }
 
@@ -403,17 +400,17 @@ toolbarpaletteitem[place="palette"] > #p
 
 #bookmarked-notification-anchor[notification="finish"] > #bookmarked-notification {
   background-image: url("chrome://browser/skin/places/bookmarks-notification-finish.png");
   animation: animation-bookmarkAdded 800ms;
   animation-timing-function: ease, ease, ease;
 }
 
 #bookmarked-notification-anchor[notification="finish"][in-bookmarks-toolbar=true] > #bookmarked-notification {
-  animation: animation-bookmarkAddedToBookmarksBar 800ms;
+  animation: animation-bookmarkAdded 800ms;
 }
 
 @media (min-resolution: 2dppx) {
   #bookmarked-notification-anchor[notification="finish"] > #bookmarked-notification {
     background-image: url("chrome://browser/skin/places/bookmarks-notification-finish@2x.png");
   }
 }
 
@@ -459,66 +456,135 @@ toolbar .toolbarbutton-1:not([type="menu
 .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker,
 #restore-button {
   -moz-box-orient: vertical;
   height: 22px;
   padding: 0;
   border: 0;
 }
 
+toolbar .toolbarbutton-1:not(:-moz-any([type="menu-button"],#back-button,#forward-button)),
+toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button,
+toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker,
+#restore-button {
+  border: 1px solid transparent;
+  border-radius: @toolbarbuttonCornerRadius@;
+  transition-property: background, border-color;
+  transition-duration: 250ms;
+}
+
+toolbar .toolbarbutton-1:not(:-moz-any([type="menu-button"],#back-button,#forward-button)),
+#restore-button {
+  padding: 2px 6px;
+}
+
+toolbar .toolbarbutton-1:not(:-moz-any([type="menu-button"],[disabled],[open],#back-button,#forward-button)):hover,
+toolbar .toolbarbutton-1[type="menu-button"]:not(:-moz-any([disabled],[open])):hover > .toolbarbutton-menubutton-button,
+toolbar .toolbarbutton-1[type="menu-button"]:not(:-moz-any([disabled],[open])):hover > .toolbarbutton-menubutton-dropmarker,
+toolbar .toolbaritem-combined-buttons:hover > .toolbarbutton-combined,
+#restore-button:not([disabled]):hover {
+  border-color: hsla(0,0%,0%,.2);
+}
+
+toolbar .toolbarbutton-1:not(:-moz-any([type="menu-button"],[disabled],[open],#back-button,#forward-button)):hover,
+toolbar .toolbarbutton-1[type="menu-button"]:not(:-moz-any([disabled],[open]))[buttonover] > .toolbarbutton-menubutton-button,
+toolbar .toolbarbutton-1[type="menu-button"]:not(:-moz-any([disabled],[open],[buttonover])):hover > .toolbarbutton-menubutton-dropmarker,
+#restore-button:not([disabled]):hover {
+  background: hsla(0,0%,100%,.1) linear-gradient(hsla(0,0%,100%,.3), hsla(0,0%,100%,.1)) padding-box;
+  box-shadow: 0 1px 0 hsla(0,0%,100%,.5),
+              0 1px 0 hsla(0,0%,100%,.5) inset;
+}
+
+toolbar .toolbarbutton-1:not(:-moz-any([type="menu-button"],[disabled],#back-button,#forward-button)):-moz-any(:hover:active,[open]),
+toolbar .toolbarbutton-1[type="menu-button"]:not(:-moz-any([disabled],[open]))[buttonover]:active > .toolbarbutton-menubutton-button,
+toolbar .toolbarbutton-1[type="menu-button"]:not(:-moz-any([disabled],[open],[buttonover])):hover:active > .toolbarbutton-menubutton-dropmarker,
+toolbar .toolbarbutton-1[type="menu-button"][open] > .toolbarbutton-menubutton-dropmarker,
+#restore-button:not([disabled]):hover:active {
+  background: hsla(0,0%,0%,.02) linear-gradient(hsla(0,0%,0%,.12), hsla(0,0%,0%,0)) border-box;
+  border-color: hsla(0,0%,0%,.3);
+  box-shadow: 0 1px 0 hsla(0,0%,100%,.5),
+              0 1px 0 hsla(0,0%,0%,.05) inset,
+              0 1px 1px hsla(0,0%,0%,.2) inset;
+  transition-duration: 10ms;
+}
+
 .toolbarbutton-1[type="menu-button"] {
   padding: 0;
 }
 
+toolbar .toolbarbutton-1[type="menu-button"] {
+  margin: 0;
+}
+
 .toolbarbutton-1 > .toolbarbutton-menubutton-button,
 .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
   margin: 0;
 }
 
 .toolbarbutton-1,
 #restore-button {
   margin: 0 4px;
 }
 
+toolbar .toolbarbutton-1:not([type="menu-button"]) {
+  margin: 0 2px;
+}
+
 /**
  * Draw seperators before toolbar button dropmarkers, as well as between
  * consecutive toolbarbutton-1's within a toolbaritem.
  */
-#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker::before,
-#nav-bar .toolbaritem-combined-buttons > .toolbarbutton-1 + .toolbarbutton-1::before {
+toolbar .toolbaritem-combined-buttons > separator,
+toolbar .toolbarbutton-1:not(:-moz-any([open],:hover)) > .toolbarbutton-menubutton-dropmarker::before {
   content: "";
   display: -moz-box;
   position: relative;
   top: calc(50% - 9px);
   width: 1px;
   height: 18px;
   -moz-margin-end: -1px;
-  background-image: linear-gradient(hsla(210,54%,20%,.2) 0, hsla(210,54%,20%,.2) 18px);
+  background-image: linear-gradient(hsla(0,0%,0%,.15) 0, hsla(0,0%,0%,.15) 18px);
   background-clip: padding-box;
   background-position: center;
   background-repeat: no-repeat;
   background-size: 1px 18px;
-  box-shadow: 0 0 0 1px hsla(0,0%,100%,.2);
-}
-
-#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
+  box-shadow: 0 0 0 1px hsla(0,0%,100%,.15);
+}
+
+toolbar .toolbaritem-combined-buttons:hover > separator {
+  display: none;
+}
+
+toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
   -moz-box-orient: horizontal;
 }
 
-#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
-  -moz-margin-start: 10px;
+toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
+toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
+  margin: 2px 6px;
+}
+
+#nav-bar #PanelUI-button {
+  -moz-box-align: center;
 }
 
 #nav-bar #PanelUI-menu-button {
   margin-top: 0;
   margin-bottom: 0;
+  padding-top: 5px;
+  padding-bottom: 5px;
   -moz-margin-start: 9px;
   -moz-margin-end: 7px;
 }
 
+#nav-bar > .toolbarbutton-1 {
+  margin-top: 4px;
+  margin-bottom: 4px;
+}
+
 @media not all and (min-resolution: 2dppx) {
 %include ../shared/toolbarbuttons.inc.css
 %include ../shared/menupanel.inc.css
 
   #back-button:hover:active:not([disabled="true"]) {
     -moz-image-region: rect(18px, 36px, 36px, 18px);
   }
 
@@ -541,28 +607,20 @@ toolbar .toolbarbutton-1:not([type="menu
   #bookmarks-menu-button[cui-areatype="toolbar"] > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
     -moz-image-region: rect(0px, 630px, 18px, 612px);
   }
 
   #bookmarks-menu-button[cui-areatype="toolbar"] > .toolbarbutton-menubutton-dropmarker:hover:active:not([disabled="true"]) > .dropmarker-icon {
     -moz-image-region: rect(18px, 630px, 36px, 612px);
   }
 
-  #bookmarks-menu-button[cui-areatype="toolbar"][open] > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
-    -moz-image-region: rect(36px, 630px, 54px, 612px);
-  }
-
   #history-panelmenu@toolbarButtonPressed@ {
     -moz-image-region: rect(18px, 180px, 36px, 162px);
   }
 
-  #history-panelmenu[open] {
-    -moz-image-region: rect(36px, 180px, 54px, 162px);
-  }
-
   #downloads-button@toolbarButtonPressed@ {
     -moz-image-region: rect(18px, 198px, 36px, 180px);
   }
 
   #add-ons-button@toolbarButtonPressed@ {
     -moz-image-region: rect(18px, 216px, 36px, 198px);
   }
 
@@ -585,20 +643,16 @@ toolbar .toolbarbutton-1:not([type="menu
   #social-share-button@toolbarButtonPressed@ {
     -moz-image-region: rect(18px, 306px, 36px, 288px);
   }
 
   #characterencoding-button@toolbarButtonPressed@ {
     -moz-image-region: rect(18px, 324px, 36px, 306px);
   }
 
-  #characterencoding-button[open] {
-    -moz-image-region: rect(36px, 324px, 54px, 306px);
-  }
-
   #new-window-button@toolbarButtonPressed@ {
     -moz-image-region: rect(18px, 342px, 36px, 324px);
   }
 
   #new-tab-button@toolbarButtonPressed@ {
     -moz-image-region: rect(18px, 360px, 36px, 342px);
   }
 
@@ -617,32 +671,24 @@ toolbar .toolbarbutton-1:not([type="menu
   #fullscreen-button@toolbarButtonPressed@ {
     -moz-image-region: rect(18px, 432px, 36px, 414px);
   }
 
   #developer-button@toolbarButtonPressed@ {
     -moz-image-region: rect(18px, 450px, 36px, 432px);
   }
 
-  #developer-button[open] {
-    -moz-image-region: rect(36px, 450px, 54px, 432px);
-  }
-
   #preferences-button@toolbarButtonPressed@ {
     -moz-image-region: rect(18px, 468px, 36px, 450px);
   }
 
   #PanelUI-menu-button@toolbarButtonPressed@ {
     -moz-image-region: rect(18px, 486px, 36px, 468px);
   }
 
-  #PanelUI-menu-button[open] {
-    -moz-image-region: rect(36px, 486px, 54px, 468px);
-  }
-
   #cut-button@toolbarButtonPressed@ {
     -moz-image-region: rect(18px, 504px, 36px, 486px);
   }
 
   #copy-button@toolbarButtonPressed@ {
     -moz-image-region: rect(18px, 522px, 36px, 504px);
   }
 
@@ -661,20 +707,16 @@ toolbar .toolbarbutton-1:not([type="menu
   #webrtc-status-button@toolbarButtonPressed@ {
     -moz-image-region: rect(18px, 594px, 36px, 576px);
   }
 
   #nav-bar-overflow-button@toolbarButtonPressed@ {
     -moz-image-region: rect(18px, 612px, 36px, 594px);
   }
 
-  #nav-bar-overflow-button[open] {
-    -moz-image-region: rect(36px, 612px, 54px, 594px);
-  }
-
   #tabview-button@toolbarButtonPressed@ {
     -moz-image-region: rect(18px, 648px, 36px, 630px);
   }
 
   #email-link-button@toolbarButtonPressed@ {
     -moz-image-region: rect(18px, 666px, 36px, 648px);
   }
 
@@ -748,32 +790,24 @@ toolbar .toolbarbutton-1:not([type="menu
   #bookmarks-menu-button[cui-areatype="toolbar"] > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
     -moz-image-region: rect(0px, 1260px, 36px, 1224px);
   }
 
   #bookmarks-menu-button[cui-areatype="toolbar"] > .toolbarbutton-menubutton-dropmarker:hover:active:not([disabled="true"]) > .dropmarker-icon {
     -moz-image-region: rect(36px, 1260px, 72px, 1224px);
   }
 
-  #bookmarks-menu-button[cui-areatype="toolbar"][open] > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
-    -moz-image-region: rect(72px, 1260px, 108px, 1224px);
-  }
-
   #history-panelmenu[cui-areatype="toolbar"] {
     -moz-image-region: rect(0, 360px, 36px, 324px);
   }
 
   #history-panelmenu[cui-areatype="toolbar"]:hover:active:not([disabled="true"]) {
     -moz-image-region: rect(36px, 360px, 72px, 324px);
   }
 
-  #history-panelmenu[cui-areatype="toolbar"][open] {
-    -moz-image-region: rect(72px, 360px, 108px, 324px);
-  }
-
   #downloads-button[cui-areatype="toolbar"] {
     -moz-image-region: rect(0, 396px, 36px, 360px);
   }
 
   #downloads-button[cui-areatype="toolbar"]:hover:active:not([disabled="true"]) {
     -moz-image-region: rect(36px, 396px, 72px, 360px);
   }
 
@@ -828,20 +862,16 @@ toolbar .toolbarbutton-1:not([type="menu
   #characterencoding-button[cui-areatype="toolbar"] {
     -moz-image-region: rect(0, 648px, 36px, 612px);
   }
 
   #characterencoding-button[cui-areatype="toolbar"]:hover:active:not([disabled="true"]) {
     -moz-image-region: rect(36px, 648px, 72px, 612px);
   }
 
-  #characterencoding-button[cui-areatype="toolbar"][open] {
-    -moz-image-region: rect(72px, 648px, 108px, 612px);
-  }
-
   #new-window-button[cui-areatype="toolbar"] {
     -moz-image-region: rect(0, 684px, 36px, 648px);
   }
 
   #new-window-button[cui-areatype="toolbar"]:hover:active:not([disabled="true"]) {
     -moz-image-region: rect(36px, 684px, 72px, 648px);
   }
 
@@ -896,40 +926,32 @@ toolbar .toolbarbutton-1:not([type="menu
   #developer-button[cui-areatype="toolbar"] {
     -moz-image-region: rect(0, 900px, 36px, 864px);
   }
 
   #developer-button[cui-areatype="toolbar"]:hover:active:not([disabled="true"]) {
     -moz-image-region: rect(36px, 900px, 72px, 864px);
   }
 
-  #developer-button[cui-areatype="toolbar"][open] {
-    -moz-image-region: rect(72px, 900px, 108px, 864px);
-  }
-
   #preferences-button[cui-areatype="toolbar"] {
     -moz-image-region: rect(0, 936px, 36px, 900px);
   }
 
   #preferences-button[cui-areatype="toolbar"]:hover:active:not([disabled="true"]) {
     -moz-image-region: rect(36px, 936px, 72px, 900px);
   }
 
   #PanelUI-menu-button {
     -moz-image-region: rect(0, 972px, 36px, 936px);
   }
 
   #PanelUI-menu-button:hover:active:not([disabled="true"]) {
     -moz-image-region: rect(36px, 972px, 72px, 936px);
   }
 
-  #PanelUI-menu-button[open] {
-    -moz-image-region: rect(72px, 972px, 108px, 936px);
-  }
-
   #edit-controls[cui-areatype="toolbar"] > #cut-button {
     -moz-image-region: rect(0, 1008px, 36px, 972px);
   }
 
   #edit-controls[cui-areatype="toolbar"] > #cut-button:hover:active:not([disabled="true"]) {
     -moz-image-region: rect(36px, 1008px, 72px, 972px);
   }
 
@@ -976,18 +998,18 @@ toolbar .toolbarbutton-1:not([type="menu
   #nav-bar-overflow-button {
     -moz-image-region: rect(0, 1224px, 36px, 1188px);
   }
 
   #nav-bar-overflow-button:hover:active:not([disabled="true"]) {
     -moz-image-region: rect(36px, 1224px, 72px, 1188px);
   }
 
-  #nav-bar-overflow-button[open] {
-    -moz-image-region: rect(72px, 1224px, 108px, 1188px);
+  #nav-bar-overflow-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
+    transform: scaleX(-1);
   }
 
   #tabview-button {
     -moz-image-region: rect(0, 1296px, 36px, 1260px);
   }
 
   #tabview-button@toolbarButtonPressed@ {
     -moz-image-region: rect(36px, 1296px, 72px, 1260px);
@@ -1249,19 +1271,18 @@ toolbarbutton[sdk-button="true"][cui-are
     width: 7px;
   }
 }
 
 .toolbarbutton-1 > .toolbarbutton-menu-dropmarker {
   -moz-margin-end: 1px;
 }
 
-.toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
-  padding: 2px 4px 0;
-  -moz-border-start: none !important;
+.toolbarbutton-1 > .toolbarbutton-menubutton-button {
+  -moz-border-end: none !important;
 }
 
 .toolbarbutton-1 > .toolbarbutton-menubutton-button:-moz-locale-dir(rtl),
 .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker:-moz-locale-dir(ltr) {
   border-top-left-radius: 0;
   border-bottom-left-radius: 0;
 }
 
@@ -1482,16 +1503,20 @@ toolbarbutton[sdk-button="true"][cui-are
 #menu_tabview[groups="3"] {
   -moz-image-region: rect(2px, 78px, 18px, 62px);
 }
 
 #cut-button {
   -moz-margin-end: 0;
 }
 
+#edit-controls[cui-areatype="toolbar"] > #cut-button {
+  -moz-border-end: 0;
+}
+
 #paste-button {
   -moz-border-start: none;
   -moz-margin-start: 0;
 }
 
 #cut-button:-moz-locale-dir(ltr),
 #paste-button:-moz-locale-dir(rtl) {
   border-top-right-radius: 0;
@@ -1512,18 +1537,30 @@ toolbarbutton[sdk-button="true"][cui-are
 
 /* zoom controls */
 
 #zoom-out-button {
   -moz-margin-end: 0;
 }
 
 #zoom-in-button {
-  -moz-border-start: none;
   -moz-margin-start: 0;
+  -moz-border-start: 0;
+}
+
+#zoom-controls[cui-areatype="toolbar"] > #zoom-out-button {
+  -moz-border-end: 0;
+}
+
+#zoom-controls[cui-areatype="toolbar"] > #zoom-in-button {
+  -moz-border-start-width: 1px;
+}
+
+#zoom-controls[cui-areatype="toolbar"] > #zoom-reset-button {
+  border-radius: 0;
 }
 
 #zoom-out-button:-moz-locale-dir(ltr),
 #zoom-in-button:-moz-locale-dir(rtl) {
   border-top-right-radius: 0;
   border-bottom-right-radius: 0;
 }
 
@@ -1534,17 +1571,17 @@ toolbarbutton[sdk-button="true"][cui-are
 }
 
 #zoom-controls[cui-areatype="toolbar"]:not([overflowedItem=true]) > #zoom-reset-button {
   min-width: 0;
   margin: 0;
 }
 
 #zoom-controls[cui-areatype="toolbar"]:not([overflowedItem=true]) > #zoom-reset-button > .toolbarbutton-text {
-  padding-top: 8px;
+  padding-top: 6px;
   margin: 2px;
 }
 
 /* sync button */
 
 #sync-button[status="active"] {
   list-style-image: url("chrome://browser/skin/sync-throbber.png");
   -moz-image-region: rect(0, 20px, 20px, 0px);
@@ -2634,17 +2671,16 @@ toolbarbutton.chevron > .toolbarbutton-m
 
 .tab-label:not([selected="true"]) {
   opacity: .7;
 }
 
 .tabbrowser-tab,
 .tabs-newtab-button {
   font: message-box;
-  font-weight: bold;
   border: none;
 }
 
 .tabbrowser-tab[selected=true]:not(:-moz-lwtheme) {
   /* overriding tabbox.css */
   color: inherit;
 }
 
--- a/browser/themes/osx/customizableui/panelUIOverlay.css
+++ b/browser/themes/osx/customizableui/panelUIOverlay.css
@@ -6,16 +6,21 @@
 
 @media (min-resolution: 2dppx) {
   toolbarbutton[panel-multiview-anchor=true] {
     background-image: url(chrome://browser/skin/customizableui/subView-arrow-back-inverted@2x.png),
                       linear-gradient(rgba(255,255,255,0.3), rgba(255,255,255,0));
     background-size: 16px, auto;
   }
 
+  toolbarbutton[panel-multiview-anchor=true]:-moz-locale-dir(rtl) {
+    background-image: url(chrome://browser/skin/customizableui/subView-arrow-back-inverted-rtl@2x.png),
+                      linear-gradient(rgba(255,255,255,0), rgba(255,255,255,0.3));
+  }
+
   #PanelUI-fxa-status {
     list-style-image: url(chrome://browser/skin/sync-horizontalbar@2x.png);
   }
 
   #PanelUI-fxa-status[status="active"] {
     list-style-image: url(chrome://browser/skin/syncProgress-horizontalbar@2x.png);
   }
 
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -131,16 +131,18 @@ browser.jar:
   skin/classic/browser/customizableui/info-icon-customizeTip.png  (../shared/customizableui/info-icon-customizeTip.png)
   skin/classic/browser/customizableui/info-icon-customizeTip@2x.png  (../shared/customizableui/info-icon-customizeTip@2x.png)
   skin/classic/browser/customizableui/menuPanel-customizeFinish.png  (../shared/customizableui/menuPanel-customizeFinish.png)
   skin/classic/browser/customizableui/menuPanel-customizeFinish@2x.png  (../shared/customizableui/menuPanel-customizeFinish@2x.png)
   skin/classic/browser/customizableui/panelarrow-customizeTip.png  (../shared/customizableui/panelarrow-customizeTip.png)
   skin/classic/browser/customizableui/panelarrow-customizeTip@2x.png  (../shared/customizableui/panelarrow-customizeTip@2x.png)
   skin/classic/browser/customizableui/subView-arrow-back-inverted.png  (../shared/customizableui/subView-arrow-back-inverted.png)
   skin/classic/browser/customizableui/subView-arrow-back-inverted@2x.png  (../shared/customizableui/subView-arrow-back-inverted@2x.png)
+  skin/classic/browser/customizableui/subView-arrow-back-inverted-rtl.png  (../shared/customizableui/subView-arrow-back-inverted-rtl.png)
+  skin/classic/browser/customizableui/subView-arrow-back-inverted-rtl@2x.png  (../shared/customizableui/subView-arrow-back-inverted-rtl@2x.png)
 * skin/classic/browser/customizableui/panelUIOverlay.css    (customizableui/panelUIOverlay.css)
   skin/classic/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay.css)
   skin/classic/browser/downloads/buttons.png                (downloads/buttons.png)
   skin/classic/browser/downloads/buttons@2x.png             (downloads/buttons@2x.png)
   skin/classic/browser/downloads/download-glow.png          (downloads/download-glow.png)
   skin/classic/browser/downloads/download-glow@2x.png       (downloads/download-glow@2x.png)
   skin/classic/browser/downloads/download-glow-menuPanel.png (downloads/download-glow-menuPanel.png)
   skin/classic/browser/downloads/download-glow-menuPanel@2x.png (downloads/download-glow-menuPanel@2x.png)
--- a/browser/themes/shared/customizableui/panelUIOverlay.inc.css
+++ b/browser/themes/shared/customizableui/panelUIOverlay.inc.css
@@ -182,18 +182,17 @@ panelview:not([mainview]) .toolbarbutton
   overflow-y: auto;
   overflow-x: hidden;
   width: @menuPanelWidth@;
   padding-left: 5px;
   padding-right: 5px;
   flex: auto;
 }
 
-#edit-controls@inAnyPanel@ > toolbarbutton > .toolbarbutton-icon,
-#zoom-controls@inAnyPanel@ > toolbarbutton > .toolbarbutton-icon {
+.toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton > .toolbarbutton-icon {
   min-width: 0;
   min-height: 0;
   margin: 0;
 }
 
 toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"]:not(.panel-wide-item),
 .panelUI-grid .toolbarbutton-1,
 .panel-customization-placeholder-child {
@@ -327,16 +326,24 @@ toolbaritem[cui-areatype="menu-panel"][s
   */
   margin: 4px calc(@menuPanelButtonWidth@ / 2 - 23px);
 }
 
 toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
   -moz-box-flex: 1;
 }
 
+#personal-bookmarks[overflowedItem=true] > #bookmarks-toolbar-placeholder {
+  -moz-box-flex: 1;
+}
+
+#personal-bookmarks[cui-areatype="toolbar"][overflowedItem=true] > #bookmarks-toolbar-placeholder > .toolbarbutton-icon {
+  -moz-margin-end: 2px;
+}
+
 #edit-controls@inAnyPanel@ > #copy-button,
 #zoom-controls@inAnyPanel@ > #zoom-reset-button {
   border-left: none;
   border-right: none;
   border-radius: 0;
 }
 
 #zoom-in-button > .toolbarbutton-text,
@@ -523,33 +530,31 @@ toolbarpaletteitem[place="palette"] > to
 #customization-palette .toolbarbutton-text {
   display: none;
 }
 
 panelview .toolbarbutton-1,
 .subviewbutton,
 .widget-overflow-list .toolbarbutton-1,
 .panelUI-grid .toolbarbutton-1 > .toolbarbutton-menubutton-button,
-#edit-controls@inAnyPanel@ > toolbarbutton,
-#zoom-controls@inAnyPanel@ > toolbarbutton {
+.toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton {
   -moz-appearance: none;
   padding: 2px 6px;
   background-color: hsla(210,4%,10%,0);
   border-radius: 2px;
   border-style: solid;
   border-color: hsla(210,4%,10%,0);
   transition-property: background-color, border-color;
   transition-duration: 150ms;
 }
 
 panelview .toolbarbutton-1,
 .subviewbutton,
 .widget-overflow-list .toolbarbutton-1,
-#edit-controls@inAnyPanel@ > toolbarbutton,
-#zoom-controls@inAnyPanel@ > toolbarbutton {
+.toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton {
   border-width: 1px;
 }
 
 .subviewbutton.panel-subview-footer {
   border-radius: 0;
   border: none;
 }
 </