Merge m-c to b2g-inbound a=merge
authorWes Kocher <wkocher@mozilla.com>
Tue, 30 Sep 2014 17:11:20 -0700
changeset 208131 c3bd999c1d2dd9ad08cdc9d0dd387c3c398839c8
parent 208130 4ef1a7c18b0b404e2bd10c18d8a3fb942008ac24 (current diff)
parent 208042 6c824fbb73e87044e63f52917f4159ffa4500be8 (diff)
child 208132 fa48349c79451fa148a61848549fab65bde6f06f
push id27577
push usercbook@mozilla.com
push dateWed, 01 Oct 2014 13:05:47 +0000
treeherderautoland@835ef55e175e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone35.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to b2g-inbound a=merge
js/src/jit-test/tests/sharedbuf/bug1068458-toolong.js
--- a/browser/base/content/browser-plugins.js
+++ b/browser/base/content/browser-plugins.js
@@ -39,24 +39,24 @@ var gPluginHandler = {
       this.uninit();
     }
   },
 
   receiveMessage: function (msg) {
     switch (msg.name) {
       case "PluginContent:ShowClickToPlayNotification":
         this.showClickToPlayNotification(msg.target, msg.data.plugins, msg.data.showNow,
-                                         msg.principal, msg.data.host);
+                                         msg.principal, msg.data.host, msg.data.location);
         break;
       case "PluginContent:RemoveNotification":
         this.removeNotification(msg.target, msg.data.name);
         break;
       case "PluginContent:UpdateHiddenPluginUI":
         this.updateHiddenPluginUI(msg.target, msg.data.haveInsecure, msg.data.actions,
-                                  msg.principal, msg.data.host);
+                                  msg.principal, msg.data.host, msg.data.location);
         break;
       case "PluginContent:HideNotificationBar":
         this.hideNotificationBar(msg.target, msg.data.name);
         break;
       case "PluginContent:ShowInstallNotification":
         return this.showInstallNotification(msg.target, msg.data.pluginInfo);
       case "PluginContent:InstallSinglePlugin":
         this.installSinglePlugin(msg.data.pluginInfo);
@@ -134,21 +134,16 @@ var gPluginHandler = {
       Services.telemetry.getHistogramById("PLUGINS_NOTIFICATION_PLUGIN_COUNT")
         .add(histogramCount);
     }
     else if (event == "dismissed") {
       // Once the popup is dismissed, clicking the icon should show the full
       // list again
       this.options.primaryPlugin = null;
     }
-    else if (event == "removed") {
-      // Once the notification is removed, let the content script clear any
-      // caches it may have populated.
-      this.browser.messageManager.sendAsyncMessage("BrowserPlugins:NotificationRemoved");
-    }
   },
 
   /**
    * Called from the plugin doorhanger to set the new permissions for a plugin
    * and activate plugins if necessary.
    * aNewState should be either "allownow" "allowalways" or "block"
    */
   _updatePluginPermission: function (aNotification, aPluginInfo, aNewState) {
@@ -216,26 +211,36 @@ var gPluginHandler = {
     }
 
     browser.messageManager.sendAsyncMessage("BrowserPlugins:ActivatePlugins", {
       pluginInfo: aPluginInfo,
       newState: aNewState,
     });
   },
 
-  showClickToPlayNotification: function (browser, plugins, showNow, principal, host) {
+  showClickToPlayNotification: function (browser, plugins, showNow, principal,
+                                         host, location) {
     // It is possible that we've received a message from the frame script to show
     // a click to play notification for a principal that no longer matches the one
     // that the browser's content now has assigned (ie, the browser has browsed away
     // after the message was sent, but before the message was received). In that case,
     // we should just ignore the message.
     if (!principal.equals(browser.contentPrincipal)) {
       return;
     }
 
+    // Data URIs, when linked to from some page, inherit the principal of that
+    // page. That means that we also need to compare the actual locations to
+    // ensure we aren't getting a message from a Data URI that we're no longer
+    // looking at.
+    let receivedURI = BrowserUtils.makeURI(location);
+    if (!browser.documentURI.equalsExceptRef(receivedURI)) {
+      return;
+    }
+
     let notification = PopupNotifications.getNotification("click-to-play-plugins", browser);
 
     // If this is a new notification, create a pluginData map, otherwise append
     let pluginData;
     if (notification) {
       pluginData = notification.options.pluginData;
     } else {
       pluginData = new Map();
@@ -300,26 +305,36 @@ var gPluginHandler = {
 
   hideNotificationBar: function (browser, name) {
     let notificationBox = gBrowser.getNotificationBox(browser);
     let notification = notificationBox.getNotificationWithValue(name);
     if (notification)
       notificationBox.removeNotification(notification, true);
   },
 
-  updateHiddenPluginUI: function (browser, haveInsecure, actions, principal, host) {
+  updateHiddenPluginUI: function (browser, haveInsecure, actions, principal,
+                                  host, location) {
     // It is possible that we've received a message from the frame script to show
     // the hidden plugin notification for a principal that no longer matches the one
     // that the browser's content now has assigned (ie, the browser has browsed away
     // after the message was sent, but before the message was received). In that case,
     // we should just ignore the message.
     if (!principal.equals(browser.contentPrincipal)) {
       return;
     }
 
+    // Data URIs, when linked to from some page, inherit the principal of that
+    // page. That means that we also need to compare the actual locations to
+    // ensure we aren't getting a message from a Data URI that we're no longer
+    // looking at.
+    let receivedURI = BrowserUtils.makeURI(location);
+    if (!browser.documentURI.equalsExceptRef(receivedURI)) {
+      return;
+    }
+
     // Set up the icon
     document.getElementById("plugins-notification-icon").classList.
       toggle("plugin-blocked", haveInsecure);
 
     // Now configure the notification bar
     let notificationBox = gBrowser.getNotificationBox(browser);
 
     function hideNotification() {
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -379,17 +379,18 @@ nsContextMenu.prototype = {
     else
       this.showItem("spell-no-suggestions", false);
 
     // dictionary list
     this.showItem("spell-dictionaries", canSpell && InlineSpellCheckerUI.enabled);
     if (canSpell) {
       var dictMenu = document.getElementById("spell-dictionaries-menu");
       var dictSep = document.getElementById("spell-language-separator");
-      InlineSpellCheckerUI.addDictionaryListToMenu(dictMenu, dictSep);
+      let count = InlineSpellCheckerUI.addDictionaryListToMenu(dictMenu, dictSep);
+      this.showItem(dictSep, count > 0);
       this.showItem("spell-add-dictionaries-main", false);
     }
     else if (this.onEditableArea) {
       // when there is no spellchecker but we might be able to spellcheck
       // add the add to dictionaries item. This will ensure that people
       // with no dictionaries will be able to download them
       this.showItem("spell-add-dictionaries-main", true);
     }
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -1020,35 +1020,52 @@ if (Services.prefs.getBoolPref("privacy.
             this._updateHeights(popup, true);
           }
           break;
       }
     },
     // Workaround bug 451997 by hardcoding heights for (potentially) wrapped items:
     _updateHeights: function(aContainer, aSetHeights) {
       // Make sure we don't get stuck not finding anything because of the XBL binding between
-      // the popup and the radio/label elements:
+      // the popup and the radio/label/description elements:
       let view = aContainer.ownerDocument.getElementById("PanelUI-panicView");
-      let variableHeightItems = view.querySelectorAll("radio, label");
+      let variableHeightItems = view.querySelectorAll("radio, label, description");
       let win = aContainer.ownerDocument.defaultView;
       for (let item of variableHeightItems) {
         if (aSetHeights) {
-          item.style.height = win.getComputedStyle(item, null).getPropertyValue("height");
+          let height = win.getComputedStyle(item, null).getPropertyValue("height");
+          item.style.height = height;
+          // In the main menu panel, need to set the height of the container of this
+          // description because otherwise the text will overflow:
+          if (item.id == "PanelUI-panic-mainDesc" &&
+              view.getAttribute("current") == "true" &&
+              // Ensure we don't make this less than the size of the icon:
+              parseInt(height) > 32) {
+            item.parentNode.style.minHeight = height;
+          }
         } else {
           item.style.removeProperty("height");
+          if (item.id == "PanelUI-panic-mainDesc") {
+            item.parentNode.style.removeProperty("min-height");
+          }
         }
       }
     },
     onViewShowing: function(aEvent) {
       let view = aEvent.target;
       let forgetButton = view.querySelector("#PanelUI-panic-view-button");
       forgetButton.addEventListener("command", this);
-      // When the popup starts showing, fix the label and radio heights
-      // if we're in a standalone view (can't tell from here) - see updateHeights.
-      view.ownerDocument.addEventListener("popupshowing", this);
+      if (view.getAttribute("current") == "true") {
+        // In the main menupanel, fix heights immediately:
+        this._updateHeights(view, true);
+      } else {
+        // In a standalone panel, so fix the label and radio heights
+        // when the popup starts showing.
+        view.ownerDocument.addEventListener("popupshowing", this);
+      }
     },
     onViewHiding: function(aEvent) {
       let view = aEvent.target;
       let forgetButton = view.querySelector("#PanelUI-panic-view-button");
       forgetButton.removeEventListener("command", this);
       this._updateHeights(view, false);
     },
   });
--- a/browser/components/customizableui/content/panelUI.inc.xul
+++ b/browser/components/customizableui/content/panelUI.inc.xul
@@ -181,37 +181,37 @@
 
     <panelview id="PanelUI-panicView" flex="1">
       <vbox class="panel-subview-body">
         <hbox id="PanelUI-panic-timeframe">
           <image id="PanelUI-panic-timeframe-icon" alt=""/>
           <vbox flex="1">
             <hbox id="PanelUI-panic-header">
               <image id="PanelUI-panic-timeframe-icon-small" alt=""/>
-              <description value="&panicButton.view.mainTimeframeDesc;" id="PanelUI-panic-mainDesc"/>
+              <description id="PanelUI-panic-mainDesc" flex="1">&panicButton.view.mainTimeframeDesc;</description>
             </hbox>
             <radiogroup id="PanelUI-panic-timeSpan" aria-labelledby="PanelUI-panic-mainDesc">
               <radio id="PanelUI-panic-5min" label="&panicButton.view.5min;" selected="true"
                      value="5" class="subviewradio"/>
               <radio id="PanelUI-panic-2hr" label="&panicButton.view.2hr;"
                      value="2" class="subviewradio"/>
               <radio id="PanelUI-panic-day" label="&panicButton.view.day;"
                      value="6" class="subviewradio"/>
             </radiogroup>
           </vbox>
         </hbox>
         <vbox id="PanelUI-panic-explanations">
-          <label id="PanelUI-panic-actionlist-main-label" value="&panicButton.view.mainActionDesc;"/>
+          <label id="PanelUI-panic-actionlist-main-label">&panicButton.view.mainActionDesc;</label>
 
           <label id="PanelUI-panic-actionlist-cookies" class="PanelUI-panic-actionlist">&panicButton.view.deleteCookies;</label>
           <label id="PanelUI-panic-actionlist-history" class="PanelUI-panic-actionlist">&panicButton.view.deleteHistory;</label>
           <label id="PanelUI-panic-actionlist-windows" class="PanelUI-panic-actionlist">&panicButton.view.deleteTabsAndWindows;</label>
           <label id="PanelUI-panic-actionlist-newwindow" class="PanelUI-panic-actionlist">&panicButton.view.openNewWindow;</label>
 
-          <label id="PanelUI-panic-warning" value="&panicButton.view.undoWarning;"/>
+          <label id="PanelUI-panic-warning">&panicButton.view.undoWarning;</label>
         </vbox>
         <button id="PanelUI-panic-view-button"
                 label="&panicButton.view.forgetButton;"/>
       </vbox>
     </panelview>
 
   </panelmultiview>
   <!-- These menupopups are located here to prevent flickering,
--- a/browser/components/customizableui/test/browser.ini
+++ b/browser/components/customizableui/test/browser.ini
@@ -61,16 +61,42 @@ skip-if = e10s # Bug ?????? - test uses 
 [browser_940107_home_button_in_bookmarks_toolbar.js]
 [browser_940307_panel_click_closure_handling.js]
 [browser_940946_removable_from_navbar_customizemode.js]
 [browser_941083_invalidate_wrapper_cache_createWidget.js]
 [browser_942581_unregisterArea_keeps_placements.js]
 [browser_943683_migration_test.js]
 [browser_944887_destroyWidget_should_destroy_in_palette.js]
 [browser_945739_showInPrivateBrowsing_customize_mode.js]
+[browser_947914_button_addons.js]
+skip-if = os == "linux" # Intermittent failures
+[browser_947914_button_copy.js]
+skip-if = os == "linux" # Intermittent failures
+[browser_947914_button_cut.js]
+skip-if = os == "linux" # Intermittent failures
+[browser_947914_button_find.js]
+skip-if = os == "linux" # Intermittent failures
+[browser_947914_button_history.js]
+skip-if = os == "linux" # Intermittent failures
+[browser_947914_button_newPrivateWindow.js]
+skip-if = os == "linux" # Intermittent failures
+[browser_947914_button_newWindow.js]
+skip-if = os == "linux" # Intermittent failures
+[browser_947914_button_paste.js]
+skip-if = os == "linux" # Intermittent failures
+[browser_947914_button_print.js]
+skip-if = os == "linux" # Intermittent failures
+[browser_947914_button_savePage.js]
+skip-if = os == "linux" # Intermittent failures
+[browser_947914_button_zoomIn.js]
+skip-if = os == "linux" # Intermittent failures
+[browser_947914_button_zoomOut.js]
+skip-if = os == "linux" # Intermittent failures
+[browser_947914_button_zoomReset.js]
+skip-if = os == "linux" # Intermittent failures
 [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_962069_drag_to_overflow_chevron.js]
 [browser_962884_opt_in_disable_hyphens.js]
 [browser_963639_customizing_attribute_non_customizable_toolbar.js]
 [browser_967000_button_charEncoding.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/test/browser_947914_button_addons.js
@@ -0,0 +1,33 @@
+/* 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 initialLocation = gBrowser.currentURI.spec;
+let newTab = null;
+
+add_task(function() {
+  info("Check addons button existence and functionality");
+
+  yield PanelUI.show();
+  info("Menu panel was opened");
+
+  let addonsButton = document.getElementById("add-ons-button");
+  ok(addonsButton, "Add-ons button exists in Panel Menu");
+  addonsButton.click();
+
+  newTab = gBrowser.selectedTab;
+  yield waitForCondition(function() gBrowser.currentURI &&
+                                    gBrowser.currentURI.spec == "about:addons");
+
+  let addonsPage = gBrowser.selectedBrowser.contentWindow.document.
+                            getElementById("addons-page");
+  ok(addonsPage, "Add-ons page was opened");
+});
+
+add_task(function asyncCleanup() {
+  gBrowser.addTab(initialLocation);
+  gBrowser.removeTab(gBrowser.selectedTab);
+  info("Tabs were restored");
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/test/browser_947914_button_copy.js
@@ -0,0 +1,64 @@
+/* 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 initialLocation = gBrowser.currentURI.spec;
+let globalClipboard;
+
+add_task(function() {
+  info("Check copy button existence and functionality");
+
+  let testText = "copy text test";
+
+  gURLBar.focus();
+  info("The URL bar was focused");
+  yield PanelUI.show();
+  info("Menu panel was opened");
+
+  let copyButton = document.getElementById("copy-button");
+  ok(copyButton, "Copy button exists in Panel Menu");
+  is(copyButton.getAttribute("disabled"), "true", "Copy button is initially disabled");
+
+  // copy text from URL bar
+  gURLBar.value = testText;
+  gURLBar.focus();
+  gURLBar.select();
+  yield PanelUI.show();
+  info("Menu panel was opened");
+
+  ok(!copyButton.hasAttribute("disabled"), "Copy button gets enabled");
+
+  copyButton.click();
+  is(gURLBar.value, testText, "Selected text is unaltered when clicking copy");
+
+  // check that the text was added to the clipboard
+  let clipboard = Services.clipboard;
+  let transferable = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
+  globalClipboard = clipboard.kGlobalClipboard;
+
+  transferable.init(null);
+  transferable.addDataFlavor("text/unicode");
+  clipboard.getData(transferable, globalClipboard);
+  let str = {}, strLength = {};
+  transferable.getTransferData("text/unicode", str, strLength);
+  let clipboardValue = "";
+
+  if (str.value) {
+    str.value.QueryInterface(Ci.nsISupportsString);
+    clipboardValue = str.value.data;
+  }
+  is(clipboardValue, testText, "Data was copied to the clipboard.");
+});
+
+add_task(function asyncCleanup() {
+  // clear the clipboard
+  Services.clipboard.emptyClipboard(globalClipboard);
+  info("Clipboard was cleared");
+
+  // restore the tab as it was at the begining of the test
+  gBrowser.addTab(initialLocation);
+  gBrowser.removeTab(gBrowser.selectedTab);
+  info("Tabs were restored");
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/test/browser_947914_button_cut.js
@@ -0,0 +1,62 @@
+/* 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 initialLocation = gBrowser.currentURI.spec;
+let globalClipboard;
+
+add_task(function() {
+  info("Check cut button existence and functionality");
+
+  let testText = "cut text test";
+
+  gURLBar.focus();
+  yield PanelUI.show();
+  info("Menu panel was opened");
+
+  let cutButton = document.getElementById("cut-button");
+  ok(cutButton, "Cut button exists in Panel Menu");
+  ok(cutButton.getAttribute("disabled"), "Cut button is disabled");
+
+  // cut text from URL bar
+  gURLBar.value = testText;
+  gURLBar.focus();
+  gURLBar.select();
+  yield PanelUI.show();
+  info("Menu panel was opened");
+
+  ok(!cutButton.hasAttribute("disabled"), "Cut button gets enabled");
+  cutButton.click();
+  is(gURLBar.value, "", "Selected text is removed from source when clicking on cut");
+
+  // check that the text was added to the clipboard
+  let clipboard = Services.clipboard;
+  let transferable = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
+  globalClipboard = clipboard.kGlobalClipboard;
+
+  transferable.init(null);
+  transferable.addDataFlavor("text/unicode");
+  clipboard.getData(transferable, globalClipboard);
+  let str = {}, strLength = {};
+  transferable.getTransferData("text/unicode", str, strLength);
+  let clipboardValue = "";
+
+  if (str.value) {
+    str.value.QueryInterface(Ci.nsISupportsString);
+    clipboardValue = str.value.data;
+  }
+  is(clipboardValue, testText, "Data was copied to the clipboard.");
+});
+
+add_task(function asyncCleanup() {
+  // clear the clipboard
+  Services.clipboard.emptyClipboard(globalClipboard);
+  info("Clipboard was cleared");
+
+  // restore the tab as it was at the begining of the test
+  gBrowser.addTab(initialLocation);
+  gBrowser.removeTab(gBrowser.selectedTab);
+  info("Tabs were restored");
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/test/browser_947914_button_find.js
@@ -0,0 +1,22 @@
+/* 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";
+
+add_task(function() {
+  info("Check find button existence and functionality");
+
+  yield PanelUI.show();
+  info("Menu panel was opened");
+
+  let findButton = document.getElementById("find-button");
+  ok(findButton, "Find button exists in Panel Menu");
+
+  findButton.click();
+  ok(!gFindBar.hasAttribute("hidden"), "Findbar opened successfully");
+
+  // close find bar
+  gFindBar.close();
+  info("Findbar was closed");
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/test/browser_947914_button_history.js
@@ -0,0 +1,24 @@
+/* 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";
+
+add_task(function() {
+  info("Check history button existence and functionality");
+
+  yield PanelUI.show();
+  info("Menu panel was opened");
+
+  let historyButton = document.getElementById("history-panelmenu");
+  ok(historyButton, "History button appears in Panel Menu");
+
+  historyButton.click();
+  let historyPanel = document.getElementById("PanelUI-history");
+  ok(historyPanel.getAttribute("current"), "History Panel is in view");
+
+  let panelHiddenPromise = promisePanelHidden(window);
+  PanelUI.hide();
+  yield panelHiddenPromise
+  info("Menu panel was closed");
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/test/browser_947914_button_newPrivateWindow.js
@@ -0,0 +1,48 @@
+/* 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";
+
+add_task(function() {
+  info("Check private browsing button existence and functionality");
+
+  yield PanelUI.show();
+  info("Menu panel was opened");
+
+  let windowWasHandled = false;
+  let privateWindow = null;
+
+  let observerWindowOpened = {
+    observe: function(aSubject, aTopic, aData) {
+      if (aTopic == "domwindowopened") {
+        privateWindow = aSubject.QueryInterface(Components.interfaces.nsIDOMWindow);
+        privateWindow.addEventListener("load", function newWindowHandler() {
+          privateWindow.removeEventListener("load", newWindowHandler, false);
+          is(privateWindow.location.href, "chrome://browser/content/browser.xul",
+             "A new browser window was opened");
+          ok(PrivateBrowsingUtils.isWindowPrivate(privateWindow), "Window is private");
+          windowWasHandled = true;
+        }, false);
+      }
+    }
+  }
+
+  Services.ww.registerNotification(observerWindowOpened);
+
+  let privateBrowsingButton = document.getElementById("privatebrowsing-button");
+  ok(privateBrowsingButton, "Private browsing button exists in Panel Menu");
+  privateBrowsingButton.click();
+
+  try {
+    yield waitForCondition(() => windowWasHandled);
+    yield promiseWindowClosed(privateWindow);
+    info("The new private window was closed");
+  }
+  catch(e) {
+    ok(false, "The new private browser window was not properly handled");
+  }
+  finally {
+    Services.ww.unregisterNotification(observerWindowOpened);
+  }
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/test/browser_947914_button_newWindow.js
@@ -0,0 +1,47 @@
+/* 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";
+
+add_task(function() {
+  info("Check new window button existence and functionality");
+  yield PanelUI.show();
+  info("Menu panel was opened");
+
+  let windowWasHandled = false;
+  let newWindow = null;
+
+  let observerWindowOpened = {
+    observe: function(aSubject, aTopic, aData) {
+      if (aTopic == "domwindowopened") {
+        newWindow = aSubject.QueryInterface(Components.interfaces.nsIDOMWindow);
+        newWindow.addEventListener("load", function newWindowHandler() {
+          newWindow.removeEventListener("load", newWindowHandler, false);
+          is(newWindow.location.href, "chrome://browser/content/browser.xul",
+             "A new browser window was opened");
+          ok(!PrivateBrowsingUtils.isWindowPrivate(newWindow), "Window is not private");
+          windowWasHandled = true;
+        }, false);
+      }
+    }
+  }
+
+  Services.ww.registerNotification(observerWindowOpened);
+
+  let newWindowButton = document.getElementById("new-window-button");
+  ok(newWindowButton, "New Window button exists in Panel Menu");
+  newWindowButton.click();
+
+  try {
+    yield waitForCondition(() => windowWasHandled);
+    yield promiseWindowClosed(newWindow);
+    info("The new window was closed");
+  }
+  catch(e) {
+    ok(false, "The new browser window was not properly handled");
+  }
+  finally {
+    Services.ww.unregisterNotification(observerWindowOpened);
+  }
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/test/browser_947914_button_paste.js
@@ -0,0 +1,47 @@
+/* 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 initialLocation = gBrowser.currentURI.spec;
+let globalClipboard;
+
+add_task(function() {
+  info("Check paste button existence and functionality");
+
+  let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
+  globalClipboard = Services.clipboard.kGlobalClipboard;
+
+  yield PanelUI.show();
+  info("Menu panel was opened");
+
+  let pasteButton = document.getElementById("paste-button");
+  ok(pasteButton, "Paste button exists in Panel Menu");
+
+  // add text to clipboard
+  let text = "Sample text for testing";
+  clipboard.copyString(text);
+
+  // test paste button by pasting text to URL bar
+  gURLBar.focus();
+  yield PanelUI.show();
+  info("Menu panel was opened");
+
+  ok(!pasteButton.hasAttribute("disabled"), "Paste button is enabled");
+  pasteButton.click();
+
+  is(gURLBar.value, text, "Text pasted successfully");
+});
+
+add_task(function asyncCleanup() {
+  // clear the clipboard
+  Services.clipboard.emptyClipboard(globalClipboard);
+  info("Clipboard was cleared");
+
+  // restore the tab as it was at the begining of the test
+  gBrowser.addTab(initialLocation);
+  gBrowser.removeTab(gBrowser.selectedTab);
+  info("Tabs were restored");
+});
+
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/test/browser_947914_button_print.js
@@ -0,0 +1,41 @@
+/* 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 isOSX = (Services.appinfo.OS === "Darwin");
+
+add_task(function() {
+  info("Check print button existence and functionality");
+
+  yield PanelUI.show();
+  info("Menu panel was opened");
+
+  yield waitForCondition(() => document.getElementById("print-button") != null);
+
+  let printButton = document.getElementById("print-button");
+  ok(printButton, "Print button exists in Panel Menu");
+
+  if (isOSX) {
+    let panelHiddenPromise = promisePanelHidden(window);
+    PanelUI.hide();
+    yield panelHiddenPromise;
+    info("Menu panel was closed");
+  }
+  else {
+    printButton.click();
+    yield waitForCondition(() => gInPrintPreviewMode);
+
+    ok(gInPrintPreviewMode, "Entered print preview mode");
+  }
+});
+
+add_task(function asyncCleanup() {
+    // close print preview
+    if (gInPrintPreviewMode) {
+      PrintUtils.exitPrintPreview();
+      yield waitForCondition(() => !window.gInPrintPreviewMode);
+      info("Exited print preview")
+    }
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/test/browser_947914_button_savePage.js
@@ -0,0 +1,20 @@
+/* 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";
+
+add_task(function() {
+  info("Check save page button existence");
+
+  yield PanelUI.show();
+  info("Menu panel was opened");
+
+  let savePageButton = document.getElementById("save-page-button");
+  ok(savePageButton, "Save Page button exists in Panel Menu");
+
+  let panelHiddenPromise = promisePanelHidden(window);
+  PanelUI.hide();
+  yield panelHiddenPromise;
+  info("Menu panel was closed");
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/test/browser_947914_button_zoomIn.js
@@ -0,0 +1,37 @@
+/* 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 initialPageZoom = ZoomManager.zoom;
+
+add_task(function() {
+  info("Check zoom in button existence and functionality");
+
+  is(initialPageZoom, 1, "Initial zoom factor should be 1");
+
+  yield PanelUI.show();
+  info("Menu panel was opened");
+
+  let zoomInButton = document.getElementById("zoom-in-button");
+  ok(zoomInButton, "Zoom in button exists in Panel Menu");
+
+  zoomInButton.click();
+  let pageZoomLevel = parseInt(ZoomManager.zoom * 100);
+  let zoomResetButton = document.getElementById("zoom-reset-button");
+  let expectedZoomLevel = parseInt(zoomResetButton.getAttribute("label"), 10);
+  ok(pageZoomLevel > 100 && pageZoomLevel == expectedZoomLevel, "Page zoomed in correctly");
+
+  // close the Panel
+  let panelHiddenPromise = promisePanelHidden(window);
+  PanelUI.hide();
+  yield panelHiddenPromise;
+  info("Menu panel was closed");
+});
+
+add_task(function asyncCleanup() {
+  // reset zoom level
+  ZoomManager.zoom = initialPageZoom;
+  info("Zoom level was restored");
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/test/browser_947914_button_zoomOut.js
@@ -0,0 +1,38 @@
+/* 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 initialPageZoom = ZoomManager.zoom;
+
+add_task(function() {
+  info("Check zoom out button existence and functionality");
+
+  is(initialPageZoom, 1, "Initial zoom factor should be 1");
+
+  yield PanelUI.show();
+  info("Menu panel was opened");
+
+  let zoomOutButton = document.getElementById("zoom-out-button");
+  ok(zoomOutButton, "Zoom out button exists in Panel Menu");
+
+  zoomOutButton.click();
+  let pageZoomLevel = Math.round(ZoomManager.zoom * 100);
+
+  let zoomResetButton = document.getElementById("zoom-reset-button");
+  let expectedZoomLevel = parseInt(zoomResetButton.getAttribute("label"), 10);
+  ok(pageZoomLevel < 100 && pageZoomLevel == expectedZoomLevel, "Page zoomed out correctly");
+
+  // close the panel
+  let panelHiddenPromise = promisePanelHidden(window);
+  PanelUI.hide();
+  yield panelHiddenPromise;
+  info("Menu panel was closed");
+});
+
+add_task(function asyncCleanup() {
+  // reset zoom level
+  ZoomManager.zoom = initialPageZoom;
+  info("Zoom level was restored");
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/test/browser_947914_button_zoomReset.js
@@ -0,0 +1,38 @@
+/* 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 initialPageZoom = ZoomManager.zoom;
+
+add_task(function() {
+  info("Check zoom reset button existence and functionality");
+
+  is(initialPageZoom, 1, "Page zoom reset correctly");
+  ZoomManager.zoom = 0.5;
+  yield PanelUI.show();
+  info("Menu panel was opened");
+
+  let zoomResetButton = document.getElementById("zoom-reset-button");
+  ok(zoomResetButton, "Zoom reset button exists in Panel Menu");
+
+  zoomResetButton.click();
+  let pageZoomLevel = Math.floor(ZoomManager.zoom * 100);
+  let expectedZoomLevel = 100;
+  let buttonZoomLevel = parseInt(zoomResetButton.getAttribute("label"), 10);
+  is(pageZoomLevel, expectedZoomLevel, "Page zoom reset correctly");
+  is(pageZoomLevel, buttonZoomLevel, "Button displays the correct zoom level");
+
+  // close the panel
+  let panelHiddenPromise = promisePanelHidden(window);
+  PanelUI.hide();
+  yield panelHiddenPromise;
+  info("Menu panel was closed");
+});
+
+add_task(function asyncCleanup() {
+  // reset zoom level
+  ZoomManager.zoom = initialPageZoom;
+  info("Zoom level was restored");
+});
--- a/browser/components/loop/content/js/contacts.js
+++ b/browser/components/loop/content/js/contacts.js
@@ -12,25 +12,133 @@ loop.contacts = (function(_, mozL10n) {
   "use strict";
 
   const Button = loop.shared.views.Button;
   const ButtonGroup = loop.shared.views.ButtonGroup;
 
   // Number of contacts to add to the list at the same time.
   const CONTACTS_CHUNK_SIZE = 100;
 
-  const ContactDetail = React.createClass({displayName: 'ContactDetail',
+  const ContactDropdown = React.createClass({displayName: 'ContactDropdown',
     propTypes: {
-      handleContactClick: React.PropTypes.func,
+      handleAction: React.PropTypes.func.isRequired,
+      canEdit: React.PropTypes.bool
+    },
+
+    getInitialState: function () {
+      return {
+        openDirUp: false,
+      };
+    },
+
+    componentDidMount: function () {
+      // This method is called once when the dropdown menu is added to the DOM
+      // inside the contact item.  If the menu extends outside of the visible
+      // area of the scrollable list, it is re-rendered in different direction.
+
+      let menuNode = this.getDOMNode();
+      let menuNodeRect = menuNode.getBoundingClientRect();
+
+      let listNode = document.getElementsByClassName("contact-list")[0];
+      let listNodeRect = listNode.getBoundingClientRect();
+
+      if (menuNodeRect.top + menuNodeRect.height >=
+          listNodeRect.top + listNodeRect.height) {
+        this.setState({
+          openDirUp: true,
+        });
+      }
+    },
+
+    onItemClick: function(event) {
+      this.props.handleAction(event.currentTarget.dataset.action);
+    },
+
+    render: function() {
+      var cx = React.addons.classSet;
+
+      let blockAction = this.props.blocked ? "unblock" : "block";
+      let blockLabel = this.props.blocked ? "unblock_contact_menu_button"
+                                          : "block_contact_menu_button";
+
+      return (
+        React.DOM.ul({className: cx({ "dropdown-menu": true,
+                            "dropdown-menu-up": this.state.openDirUp })}, 
+          React.DOM.li({className: cx({ "dropdown-menu-item": true,
+                              "disabled": true }), 
+              onClick: this.onItemClick, 'data-action': "video-call"}, 
+            React.DOM.i({className: "icon icon-video-call"}), 
+            mozL10n.get("video_call_menu_button")
+          ), 
+          React.DOM.li({className: cx({ "dropdown-menu-item": true,
+                              "disabled": true }), 
+              onClick: this.onItemClick, 'data-action': "audio-call"}, 
+            React.DOM.i({className: "icon icon-audio-call"}), 
+            mozL10n.get("audio_call_menu_button")
+          ), 
+          React.DOM.li({className: cx({ "dropdown-menu-item": true,
+                              "disabled": !this.props.canEdit }), 
+              onClick: this.onItemClick, 'data-action': "edit"}, 
+            React.DOM.i({className: "icon icon-edit"}), 
+            mozL10n.get("edit_contact_menu_button")
+          ), 
+          React.DOM.li({className: "dropdown-menu-item", 
+              onClick: this.onItemClick, 'data-action': blockAction}, 
+            React.DOM.i({className: "icon icon-" + blockAction}), 
+            mozL10n.get(blockLabel)
+          ), 
+          React.DOM.li({className: cx({ "dropdown-menu-item": true,
+                              "disabled": !this.props.canEdit }), 
+              onClick: this.onItemClick, 'data-action': "remove"}, 
+            React.DOM.i({className: "icon icon-remove"}), 
+            mozL10n.get("remove_contact_menu_button")
+          )
+        )
+      );
+    }
+  });
+
+  const ContactDetail = React.createClass({displayName: 'ContactDetail',
+    getInitialState: function() {
+      return {
+        showMenu: false,
+      };
+    },
+
+    propTypes: {
+      handleContactAction: React.PropTypes.func,
       contact: React.PropTypes.object.isRequired
     },
 
-    handleContactClick: function() {
-      if (this.props.handleContactClick) {
-        this.props.handleContactClick(this.props.key);
+    _onBodyClick: function() {
+      // Hide the menu after other click handlers have been invoked.
+      setTimeout(this.hideDropdownMenu, 10);
+    },
+
+    showDropdownMenu: function() {
+      document.body.addEventListener("click", this._onBodyClick);
+      this.setState({showMenu: true});
+    },
+
+    hideDropdownMenu: function() {
+      document.body.removeEventListener("click", this._onBodyClick);
+      // Since this call may be deferred, we need to guard it, for example in
+      // case the contact was removed in the meantime.
+      if (this.isMounted()) {
+        this.setState({showMenu: false});
+      }
+    },
+
+    componentWillUnmount: function() {
+      document.body.removeEventListener("click", this._onBodyClick);
+    },
+
+    handleAction: function(actionName) {
+      if (this.props.handleContactAction) {
+        this.props.handleContactAction(this.props.contact, actionName);
       }
     },
 
     getContactNames: function() {
       // The model currently does not enforce a name to be present, but we're
       // going to assume it is awaiting more advanced validation of required fields
       // by the model. (See bug 1069918)
       // NOTE: this method of finding a firstname and lastname is not i18n-proof.
@@ -51,41 +159,55 @@ loop.contacts = (function(_, mozL10n) {
           email = address;
           return true;
         }
         return false;
       });
       return email;
     },
 
+    canEdit: function() {
+      // We cannot modify imported contacts.  For the moment, the check for
+      // determining whether the contact is imported is based on its category.
+      return this.props.contact.category[0] != "google";
+    },
+
     render: function() {
       let names = this.getContactNames();
       let email = this.getPreferredEmail();
       let cx = React.addons.classSet;
       let contactCSSClass = cx({
         contact: true,
         blocked: this.props.contact.blocked
       });
 
       return (
-        React.DOM.li({onClick: this.handleContactClick, className: contactCSSClass}, 
+        React.DOM.li({className: contactCSSClass, onMouseLeave: this.hideDropdownMenu}, 
           React.DOM.div({className: "avatar"}, 
             React.DOM.img({src: navigator.mozLoop.getUserAvatar(email.value)})
           ), 
           React.DOM.div({className: "details"}, 
             React.DOM.div({className: "username"}, React.DOM.strong(null, names.firstName), " ", names.lastName, 
               React.DOM.i({className: cx({"icon icon-google": this.props.contact.category[0] == "google"})}), 
               React.DOM.i({className: cx({"icon icon-blocked": this.props.contact.blocked})})
             ), 
             React.DOM.div({className: "email"}, email.value)
           ), 
           React.DOM.div({className: "icons"}, 
-            React.DOM.i({className: "icon icon-video"}), 
-            React.DOM.i({className: "icon icon-caret-down"})
-          )
+            React.DOM.i({className: "icon icon-video", 
+               onClick: this.handleAction.bind(null, "video-call")}), 
+            React.DOM.i({className: "icon icon-caret-down", 
+               onClick: this.showDropdownMenu})
+          ), 
+          this.state.showMenu
+            ? ContactDropdown({handleAction: this.handleAction, 
+                               canEdit: this.canEdit(), 
+                               blocked: this.props.contact.blocked})
+            : null
+          
         )
       );
     }
   });
 
   const ContactsList = React.createClass({displayName: 'ContactsList',
     getInitialState: function() {
       return {
@@ -153,39 +275,60 @@ loop.contacts = (function(_, mozL10n) {
 
     handleImportButtonClick: function() {
     },
 
     handleAddContactButtonClick: function() {
       this.props.startForm("contacts_add");
     },
 
+    handleContactAction: function(contact, actionName) {
+      switch (actionName) {
+        case "edit":
+          this.props.startForm("contacts_edit", contact);
+          break;
+        case "remove":
+        case "block":
+        case "unblock":
+          // Invoke the API named like the action.
+          navigator.mozLoop.contacts[actionName](contact._guid, err => {
+            if (err) {
+              throw err;
+            }
+          });
+          break;
+        default:
+          console.error("Unrecognized action: " + actionName);
+          break;
+      }
+    },
+
     sortContacts: function(contact1, contact2) {
       let comp = contact1.name[0].localeCompare(contact2.name[0]);
       if (comp !== 0) {
         return comp;
       }
       // If names are equal, compare against unique ids to make sure we have
       // consistent ordering.
       return contact1._guid - contact2._guid;
     },
 
     render: function() {
       let viewForItem = item => {
-        return ContactDetail({key: item._guid, contact: item})
+        return ContactDetail({key: item._guid, contact: item, 
+                              handleContactAction: this.handleContactAction})
       };
 
       let shownContacts = _.groupBy(this.state.contacts, function(contact) {
         return contact.blocked ? "blocked" : "available";
       });
 
-      // Buttons are temporarily hidden using "style".
       return (
         React.DOM.div(null, 
-          React.DOM.div({className: "content-area", style: {display: "none"}}, 
+          React.DOM.div({className: "content-area"}, 
             ButtonGroup(null, 
               Button({caption: mozL10n.get("import_contacts_button"), 
                       disabled: true, 
                       onClick: this.handleImportButtonClick}), 
               Button({caption: mozL10n.get("new_contact_button"), 
                       onClick: this.handleAddContactButtonClick})
             )
           ), 
@@ -218,17 +361,21 @@ loop.contacts = (function(_, mozL10n) {
         pristine: true,
         name: "",
         email: "",
       };
     },
 
     initForm: function(contact) {
       let state = this.getInitialState();
-      state.contact = contact || null;
+      if (contact) {
+        state.contact = contact;
+        state.name = contact.name[0];
+        state.email = contact.email[0].value;
+      }
       this.setState(state);
     },
 
     handleAcceptButtonClick: function() {
       // Allow validity error indicators to be displayed.
       this.setState({
         pristine: false,
       });
@@ -239,16 +386,23 @@ loop.contacts = (function(_, mozL10n) {
       }
 
       this.props.selectTab("contacts");
 
       let contactsAPI = navigator.mozLoop.contacts;
 
       switch (this.props.mode) {
         case "edit":
+          this.state.contact.name[0] = this.state.name.trim();
+          this.state.contact.email[0].value = this.state.email.trim();
+          contactsAPI.update(this.state.contact, err => {
+            if (err) {
+              throw err;
+            }
+          });
           this.setState({
             contact: null,
           });
           break;
         case "add":
           contactsAPI.add({
             id: navigator.mozLoop.generateUUID(),
             name: [this.state.name.trim()],
@@ -270,31 +424,35 @@ loop.contacts = (function(_, mozL10n) {
     handleCancelButtonClick: function() {
       this.props.selectTab("contacts");
     },
 
     render: function() {
       let cx = React.addons.classSet;
       return (
         React.DOM.div({className: "content-area contact-form"}, 
-          React.DOM.header(null, mozL10n.get("add_contact_button")), 
+          React.DOM.header(null, this.props.mode == "add"
+                   ? mozL10n.get("add_contact_button")
+                   : mozL10n.get("edit_contact_title")), 
           React.DOM.label(null, mozL10n.get("edit_contact_name_label")), 
           React.DOM.input({ref: "name", required: true, pattern: "\\s*\\S.*", 
                  className: cx({pristine: this.state.pristine}), 
                  valueLink: this.linkState("name")}), 
           React.DOM.label(null, mozL10n.get("edit_contact_email_label")), 
           React.DOM.input({ref: "email", required: true, type: "email", 
                  className: cx({pristine: this.state.pristine}), 
                  valueLink: this.linkState("email")}), 
           ButtonGroup(null, 
             Button({additionalClass: "button-cancel", 
                     caption: mozL10n.get("cancel_button"), 
                     onClick: this.handleCancelButtonClick}), 
             Button({additionalClass: "button-accept", 
-                    caption: mozL10n.get("add_contact_button"), 
+                    caption: this.props.mode == "add"
+                             ? mozL10n.get("add_contact_button")
+                             : mozL10n.get("edit_contact_done_button"), 
                     onClick: this.handleAcceptButtonClick})
           )
         )
       );
     }
   });
 
   return {
--- a/browser/components/loop/content/js/contacts.jsx
+++ b/browser/components/loop/content/js/contacts.jsx
@@ -12,25 +12,133 @@ loop.contacts = (function(_, mozL10n) {
   "use strict";
 
   const Button = loop.shared.views.Button;
   const ButtonGroup = loop.shared.views.ButtonGroup;
 
   // Number of contacts to add to the list at the same time.
   const CONTACTS_CHUNK_SIZE = 100;
 
-  const ContactDetail = React.createClass({
+  const ContactDropdown = React.createClass({
     propTypes: {
-      handleContactClick: React.PropTypes.func,
+      handleAction: React.PropTypes.func.isRequired,
+      canEdit: React.PropTypes.bool
+    },
+
+    getInitialState: function () {
+      return {
+        openDirUp: false,
+      };
+    },
+
+    componentDidMount: function () {
+      // This method is called once when the dropdown menu is added to the DOM
+      // inside the contact item.  If the menu extends outside of the visible
+      // area of the scrollable list, it is re-rendered in different direction.
+
+      let menuNode = this.getDOMNode();
+      let menuNodeRect = menuNode.getBoundingClientRect();
+
+      let listNode = document.getElementsByClassName("contact-list")[0];
+      let listNodeRect = listNode.getBoundingClientRect();
+
+      if (menuNodeRect.top + menuNodeRect.height >=
+          listNodeRect.top + listNodeRect.height) {
+        this.setState({
+          openDirUp: true,
+        });
+      }
+    },
+
+    onItemClick: function(event) {
+      this.props.handleAction(event.currentTarget.dataset.action);
+    },
+
+    render: function() {
+      var cx = React.addons.classSet;
+
+      let blockAction = this.props.blocked ? "unblock" : "block";
+      let blockLabel = this.props.blocked ? "unblock_contact_menu_button"
+                                          : "block_contact_menu_button";
+
+      return (
+        <ul className={cx({ "dropdown-menu": true,
+                            "dropdown-menu-up": this.state.openDirUp })}>
+          <li className={cx({ "dropdown-menu-item": true,
+                              "disabled": true })}
+              onClick={this.onItemClick} data-action="video-call">
+            <i className="icon icon-video-call" />
+            {mozL10n.get("video_call_menu_button")}
+          </li>
+          <li className={cx({ "dropdown-menu-item": true,
+                              "disabled": true })}
+              onClick={this.onItemClick} data-action="audio-call">
+            <i className="icon icon-audio-call" />
+            {mozL10n.get("audio_call_menu_button")}
+          </li>
+          <li className={cx({ "dropdown-menu-item": true,
+                              "disabled": !this.props.canEdit })}
+              onClick={this.onItemClick} data-action="edit">
+            <i className="icon icon-edit" />
+            {mozL10n.get("edit_contact_menu_button")}
+          </li>
+          <li className="dropdown-menu-item"
+              onClick={this.onItemClick} data-action={blockAction}>
+            <i className={"icon icon-" + blockAction} />
+            {mozL10n.get(blockLabel)}
+          </li>
+          <li className={cx({ "dropdown-menu-item": true,
+                              "disabled": !this.props.canEdit })}
+              onClick={this.onItemClick} data-action="remove">
+            <i className="icon icon-remove" />
+            {mozL10n.get("remove_contact_menu_button")}
+          </li>
+        </ul>
+      );
+    }
+  });
+
+  const ContactDetail = React.createClass({
+    getInitialState: function() {
+      return {
+        showMenu: false,
+      };
+    },
+
+    propTypes: {
+      handleContactAction: React.PropTypes.func,
       contact: React.PropTypes.object.isRequired
     },
 
-    handleContactClick: function() {
-      if (this.props.handleContactClick) {
-        this.props.handleContactClick(this.props.key);
+    _onBodyClick: function() {
+      // Hide the menu after other click handlers have been invoked.
+      setTimeout(this.hideDropdownMenu, 10);
+    },
+
+    showDropdownMenu: function() {
+      document.body.addEventListener("click", this._onBodyClick);
+      this.setState({showMenu: true});
+    },
+
+    hideDropdownMenu: function() {
+      document.body.removeEventListener("click", this._onBodyClick);
+      // Since this call may be deferred, we need to guard it, for example in
+      // case the contact was removed in the meantime.
+      if (this.isMounted()) {
+        this.setState({showMenu: false});
+      }
+    },
+
+    componentWillUnmount: function() {
+      document.body.removeEventListener("click", this._onBodyClick);
+    },
+
+    handleAction: function(actionName) {
+      if (this.props.handleContactAction) {
+        this.props.handleContactAction(this.props.contact, actionName);
       }
     },
 
     getContactNames: function() {
       // The model currently does not enforce a name to be present, but we're
       // going to assume it is awaiting more advanced validation of required fields
       // by the model. (See bug 1069918)
       // NOTE: this method of finding a firstname and lastname is not i18n-proof.
@@ -51,41 +159,55 @@ loop.contacts = (function(_, mozL10n) {
           email = address;
           return true;
         }
         return false;
       });
       return email;
     },
 
+    canEdit: function() {
+      // We cannot modify imported contacts.  For the moment, the check for
+      // determining whether the contact is imported is based on its category.
+      return this.props.contact.category[0] != "google";
+    },
+
     render: function() {
       let names = this.getContactNames();
       let email = this.getPreferredEmail();
       let cx = React.addons.classSet;
       let contactCSSClass = cx({
         contact: true,
         blocked: this.props.contact.blocked
       });
 
       return (
-        <li onClick={this.handleContactClick} className={contactCSSClass}>
+        <li className={contactCSSClass} onMouseLeave={this.hideDropdownMenu}>
           <div className="avatar">
             <img src={navigator.mozLoop.getUserAvatar(email.value)} />
           </div>
           <div className="details">
             <div className="username"><strong>{names.firstName}</strong> {names.lastName}
               <i className={cx({"icon icon-google": this.props.contact.category[0] == "google"})} />
               <i className={cx({"icon icon-blocked": this.props.contact.blocked})} />
             </div>
             <div className="email">{email.value}</div>
           </div>
           <div className="icons">
-            <i className="icon icon-video" />
-            <i className="icon icon-caret-down" />
+            <i className="icon icon-video"
+               onClick={this.handleAction.bind(null, "video-call")} />
+            <i className="icon icon-caret-down"
+               onClick={this.showDropdownMenu} />
           </div>
+          {this.state.showMenu
+            ? <ContactDropdown handleAction={this.handleAction}
+                               canEdit={this.canEdit()}
+                               blocked={this.props.contact.blocked} />
+            : null
+          }
         </li>
       );
     }
   });
 
   const ContactsList = React.createClass({
     getInitialState: function() {
       return {
@@ -153,39 +275,60 @@ loop.contacts = (function(_, mozL10n) {
 
     handleImportButtonClick: function() {
     },
 
     handleAddContactButtonClick: function() {
       this.props.startForm("contacts_add");
     },
 
+    handleContactAction: function(contact, actionName) {
+      switch (actionName) {
+        case "edit":
+          this.props.startForm("contacts_edit", contact);
+          break;
+        case "remove":
+        case "block":
+        case "unblock":
+          // Invoke the API named like the action.
+          navigator.mozLoop.contacts[actionName](contact._guid, err => {
+            if (err) {
+              throw err;
+            }
+          });
+          break;
+        default:
+          console.error("Unrecognized action: " + actionName);
+          break;
+      }
+    },
+
     sortContacts: function(contact1, contact2) {
       let comp = contact1.name[0].localeCompare(contact2.name[0]);
       if (comp !== 0) {
         return comp;
       }
       // If names are equal, compare against unique ids to make sure we have
       // consistent ordering.
       return contact1._guid - contact2._guid;
     },
 
     render: function() {
       let viewForItem = item => {
-        return <ContactDetail key={item._guid} contact={item} />
+        return <ContactDetail key={item._guid} contact={item}
+                              handleContactAction={this.handleContactAction} />
       };
 
       let shownContacts = _.groupBy(this.state.contacts, function(contact) {
         return contact.blocked ? "blocked" : "available";
       });
 
-      // Buttons are temporarily hidden using "style".
       return (
         <div>
-          <div className="content-area" style={{display: "none"}}>
+          <div className="content-area">
             <ButtonGroup>
               <Button caption={mozL10n.get("import_contacts_button")}
                       disabled
                       onClick={this.handleImportButtonClick} />
               <Button caption={mozL10n.get("new_contact_button")}
                       onClick={this.handleAddContactButtonClick} />
             </ButtonGroup>
           </div>
@@ -218,17 +361,21 @@ loop.contacts = (function(_, mozL10n) {
         pristine: true,
         name: "",
         email: "",
       };
     },
 
     initForm: function(contact) {
       let state = this.getInitialState();
-      state.contact = contact || null;
+      if (contact) {
+        state.contact = contact;
+        state.name = contact.name[0];
+        state.email = contact.email[0].value;
+      }
       this.setState(state);
     },
 
     handleAcceptButtonClick: function() {
       // Allow validity error indicators to be displayed.
       this.setState({
         pristine: false,
       });
@@ -239,16 +386,23 @@ loop.contacts = (function(_, mozL10n) {
       }
 
       this.props.selectTab("contacts");
 
       let contactsAPI = navigator.mozLoop.contacts;
 
       switch (this.props.mode) {
         case "edit":
+          this.state.contact.name[0] = this.state.name.trim();
+          this.state.contact.email[0].value = this.state.email.trim();
+          contactsAPI.update(this.state.contact, err => {
+            if (err) {
+              throw err;
+            }
+          });
           this.setState({
             contact: null,
           });
           break;
         case "add":
           contactsAPI.add({
             id: navigator.mozLoop.generateUUID(),
             name: [this.state.name.trim()],
@@ -270,31 +424,35 @@ loop.contacts = (function(_, mozL10n) {
     handleCancelButtonClick: function() {
       this.props.selectTab("contacts");
     },
 
     render: function() {
       let cx = React.addons.classSet;
       return (
         <div className="content-area contact-form">
-          <header>{mozL10n.get("add_contact_button")}</header>
+          <header>{this.props.mode == "add"
+                   ? mozL10n.get("add_contact_button")
+                   : mozL10n.get("edit_contact_title")}</header>
           <label>{mozL10n.get("edit_contact_name_label")}</label>
           <input ref="name" required pattern="\s*\S.*"
                  className={cx({pristine: this.state.pristine})}
                  valueLink={this.linkState("name")} />
           <label>{mozL10n.get("edit_contact_email_label")}</label>
           <input ref="email" required type="email"
                  className={cx({pristine: this.state.pristine})}
                  valueLink={this.linkState("email")} />
           <ButtonGroup>
             <Button additionalClass="button-cancel"
                     caption={mozL10n.get("cancel_button")}
                     onClick={this.handleCancelButtonClick} />
             <Button additionalClass="button-accept"
-                    caption={mozL10n.get("add_contact_button")}
+                    caption={this.props.mode == "add"
+                             ? mozL10n.get("add_contact_button")
+                             : mozL10n.get("edit_contact_done_button")}
                     onClick={this.handleAcceptButtonClick} />
           </ButtonGroup>
         </div>
       );
     }
   });
 
   return {
--- a/browser/components/loop/content/shared/css/contacts.css
+++ b/browser/components/loop/content/shared/css/contacts.css
@@ -1,18 +1,20 @@
 /* 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/. */
 
 .contact-list {
   border-top: 1px solid #ccc;
   overflow-x: hidden;
   overflow-y: auto;
-  /* Show six contacts and scroll for the rest */
-  max-height: 305px;
+  /* We need enough space to show the context menu of the first contact. */
+  min-height: 204px;
+  /* Show six contacts and scroll for the rest. */
+  max-height: 306px;
 }
 
 .contact,
 .contact-separator {
   padding: 5px 10px;
   font-size: 13px;
 }
 
@@ -39,17 +41,17 @@
 }
 
 .contact:hover {
   background: #eee;
 }
 
 .contact:hover > .icons {
   display: block;
-  z-index: 1000;
+  z-index: 1;
 }
 
 .contact > .avatar {
   width: 40px;
   height: 40px;
   background: #ccc;
   border-radius: 50%;
   margin-right: 10px;
@@ -141,11 +143,57 @@
 
 .icons i.icon-caret-down {
   background-image: url("../img/icons-10x10.svg#dropdown-white");
   background-size: 10px 10px;
   width: 10px;
   height: 16px;
 }
 
+.contact > .dropdown-menu {
+  z-index: 2;
+  top: 10px;
+  bottom: auto;
+  right: 3em;
+  left: auto;
+}
+
+.contact > .dropdown-menu-up {
+  bottom: 10px;
+  top: auto;
+}
+
+.contact > .dropdown-menu > .dropdown-menu-item > .icon {
+  display: inline-block;
+  width: 20px;
+  height: 10px;
+  background-position: center left;
+  background-size: 10px 10px;
+  background-repeat: no-repeat;
+}
+
+.contact > .dropdown-menu > .dropdown-menu-item > .icon-audio-call {
+  background-image: url("../img/icons-16x16.svg#audio");
+}
+
+.contact > .dropdown-menu > .dropdown-menu-item > .icon-video-call {
+  background-image: url("../img/icons-16x16.svg#video");
+}
+
+.contact > .dropdown-menu > .dropdown-menu-item > .icon-edit {
+  background-image: url("../img/icons-16x16.svg#contacts");
+}
+
+.contact > .dropdown-menu > .dropdown-menu-item > .icon-block {
+  background-image: url("../img/icons-16x16.svg#block");
+}
+
+.contact > .dropdown-menu > .dropdown-menu-item > .icon-unblock {
+  background-image: url("../img/icons-16x16.svg#unblock");
+}
+
+.contact > .dropdown-menu > .dropdown-menu-item > .icon-remove {
+  background-image: url("../img/icons-16x16.svg#delete");
+}
+
 .contact-form > .button-group {
   margin-top: 14px;
 }
--- a/browser/modules/PluginContent.jsm
+++ b/browser/modules/PluginContent.jsm
@@ -33,39 +33,35 @@ PluginContent.prototype = {
     this.pluginData = new Map();
 
     // Note that the XBL binding is untrusted
     global.addEventListener("PluginBindingAttached", this, true, true);
     global.addEventListener("PluginCrashed",         this, true);
     global.addEventListener("PluginOutdated",        this, true);
     global.addEventListener("PluginInstantiated",    this, true);
     global.addEventListener("PluginRemoved",         this, true);
+    global.addEventListener("pagehide",              this, true);
+    global.addEventListener("pageshow",              this, true);
     global.addEventListener("unload",                this);
 
-    global.addEventListener("pageshow", (event) => this.onPageShow(event), true);
-
     global.addMessageListener("BrowserPlugins:ActivatePlugins", this);
-    global.addMessageListener("BrowserPlugins:NotificationRemoved", this);
     global.addMessageListener("BrowserPlugins:NotificationShown", this);
     global.addMessageListener("BrowserPlugins:ContextMenuCommand", this);
   },
 
   uninit: function() {
     delete this.global;
     delete this.content;
   },
 
   receiveMessage: function (msg) {
     switch (msg.name) {
       case "BrowserPlugins:ActivatePlugins":
         this.activatePlugins(msg.data.pluginInfo, msg.data.newState);
         break;
-      case "BrowserPlugins:NotificationRemoved":
-        this.clearPluginDataCache();
-        break;
       case "BrowserPlugins:NotificationShown":
         setTimeout(() => this.updateNotificationUI(), 0);
         break;
       case "BrowserPlugins:ContextMenuCommand":
         switch (msg.data.command) {
           case "play":
             this._showClickToPlayNotification(msg.objects.plugin, true);
             break;
@@ -74,28 +70,37 @@ PluginContent.prototype = {
             break;
         }
         break;
     }
   },
 
   onPageShow: function (event) {
     // Ignore events that aren't from the main document.
-    if (this.global.content && event.target != this.global.content.document) {
+    if (!this.content || event.target != this.content.document) {
       return;
     }
 
     // The PluginClickToPlay events are not fired when navigating using the
     // BF cache. |persisted| is true when the page is loaded from the
     // BF cache, so this code reshows the notification if necessary.
     if (event.persisted) {
       this.reshowClickToPlayNotification();
     }
   },
 
+  onPageHide: function (event) {
+    // Ignore events that aren't from the main document.
+    if (!this.content || event.target != this.content.document) {
+      return;
+    }
+
+    this.clearPluginDataCache();
+  },
+
   getPluginUI: function (plugin, anonid) {
     return plugin.ownerDocument.
            getAnonymousElementByAttribute(plugin, "anonid", anonid);
   },
 
   _getPluginInfo: function (pluginElement) {
     let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
     pluginElement.QueryInterface(Ci.nsIObjectLoadingContent);
@@ -279,16 +284,26 @@ PluginContent.prototype = {
   handleEvent: function (event) {
     let eventType = event.type;
 
     if (eventType == "unload") {
       this.uninit();
       return;
     }
 
+    if (eventType == "pagehide") {
+      this.onPageHide(event);
+      return;
+    }
+
+    if (eventType == "pageshow") {
+      this.onPageShow(event);
+      return;
+    }
+
     if (eventType == "PluginRemoved") {
       this.updateNotificationUI(event.target);
       return;
     }
 
     if (eventType == "click") {
       this.onOverlayClick(event);
       return;
@@ -642,35 +657,36 @@ PluginContent.prototype = {
   },
 
   _showClickToPlayNotification: function (plugin, showNow) {
     let plugins = [];
 
     // If plugin is null, that means the user has navigated back to a page with
     // plugins, and we need to collect all the plugins.
     if (plugin === null) {
-      let contentWindow = this.global.content;
+      let contentWindow = this.content;
       let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                              .getInterface(Ci.nsIDOMWindowUtils);
       // cwu.plugins may contain non-plugin <object>s, filter them out
       plugins = cwu.plugins.filter((plugin) =>
         plugin.getContentTypeForMIMEType(plugin.actualType) == Ci.nsIObjectLoadingContent.TYPE_PLUGIN);
 
       if (plugins.length == 0) {
         this.removeNotification("click-to-play-plugins");
         return;
       }
     } else {
       plugins = [plugin];
     }
 
     let pluginData = this.pluginData;
 
-    let principal = this.global.content.document.nodePrincipal;
+    let principal = this.content.document.nodePrincipal;
     let principalHost = this._getHostFromPrincipal(principal);
+    let location = this.content.document.location.href;
 
     for (let p of plugins) {
       let pluginInfo = this._getPluginInfo(p);
       if (pluginInfo.permissionString === null) {
         Cu.reportError("No permission string for active plugin.");
         continue;
       }
       if (pluginData.has(pluginInfo.permissionString)) {
@@ -690,41 +706,39 @@ PluginContent.prototype = {
 
       this.pluginData.set(pluginInfo.permissionString, pluginInfo);
     }
 
     this.global.sendAsyncMessage("PluginContent:ShowClickToPlayNotification", {
       plugins: [... this.pluginData.values()],
       showNow: showNow,
       host: principalHost,
+      location: location,
     }, null, principal);
   },
 
   /**
    * Updates the "hidden plugin" notification bar UI.
    *
    * @param document (optional)
    *        Specify the document that is causing the update.
    *        This is useful when the document is possibly no longer
    *        the current loaded document (for example, if we're
    *        responding to a PluginRemoved event for an unloading
    *        document). If this parameter is omitted, it defaults
    *        to the current top-level document.
    */
   updateNotificationUI: function (document) {
-    let principal;
+    document = document || this.content.document;
 
-    if (document) {
-      // We're only interested in the top-level document, since that's
-      // the one that provides the Principal that we send back to the
-      // parent.
-      principal = document.defaultView.top.document.nodePrincipal;
-    } else {
-      principal = this.content.document.nodePrincipal;
-    }
+    // We're only interested in the top-level document, since that's
+    // the one that provides the Principal that we send back to the
+    // parent.
+    let principal = document.defaultView.top.document.nodePrincipal;
+    let location = document.location.href;
 
     // Make a copy of the actions from the last popup notification.
     let haveInsecure = false;
     let actions = new Map();
     for (let action of this.pluginData.values()) {
       switch (action.fallbackType) {
         // haveInsecure will trigger the red flashing icon and the infobar
         // styling below
@@ -775,16 +789,17 @@ PluginContent.prototype = {
     }
 
     // If there are any items remaining in `actions` now, they are hidden
     // plugins that need a notification bar.
     this.global.sendAsyncMessage("PluginContent:UpdateHiddenPluginUI", {
       haveInsecure: haveInsecure,
       actions: [... actions.values()],
       host: this._getHostFromPrincipal(principal),
+      location: location,
     }, null, principal);
   },
 
   removeNotification: function (name) {
     this.global.sendAsyncMessage("PluginContent:RemoveNotification", { name: name });
   },
 
   clearPluginDataCache: function () {
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1463,24 +1463,26 @@ notification[value="translation"] menuli
 .autocomplete-treebody::-moz-tree-image(bookmark, treecolAutoCompleteImage) {
   list-style-image: url("chrome://browser/skin/places/star-icons.png");
   -moz-image-region: rect(0px 32px 16px 16px);
   width: 16px;
   height: 16px;
 }
 
 .ac-result-type-keyword,
-.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage) {
+.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage),
+richlistitem[type~="action"][actiontype="searchengine"] > .ac-title-box > .ac-site-icon {
   list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
   width: 16px;
   height: 16px;
 }
 
 .ac-result-type-keyword[selected="true"],
-.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected) {
+.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected),
+richlistitem[type~="action"][actiontype="searchengine"][selected="true"] > .ac-title-box > .ac-site-icon {
   list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon-inverted);
 }
 
 .ac-result-type-tag,
 .autocomplete-treebody::-moz-tree-image(tag, treecolAutoCompleteImage) {
   list-style-image: url("chrome://browser/skin/places/tag.png");
   width: 16px;
   height: 16px;
@@ -1500,24 +1502,16 @@ notification[value="translation"] menuli
   font-size: 0.9em;
 }
 
 richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-icon {
   list-style-image: url("chrome://browser/skin/actionicon-tab.png");
   padding: 0 3px;
 }
 
-richlistitem[type~="action"][actiontype="searchengine"] > .ac-title-box > .ac-site-icon {
-  list-style-image: url("chrome://global/skin/icons/autocomplete-search.svg#search-icon");
-}
-
-richlistitem[type~="action"][actiontype="searchengine"][selected="true"] > .ac-title-box > .ac-site-icon {
-  list-style-image: url("chrome://global/skin/icons/autocomplete-search.svg#search-icon-inverted");
-}
-
 .autocomplete-treebody::-moz-tree-cell-text(treecolAutoCompleteComment) {
   color: GrayText;
 }
 
 .ac-comment[selected="true"],
 .ac-url-text[selected="true"],
 .ac-action-text[selected="true"] {
   color: inherit !important;
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -156,16 +156,29 @@ toolbarseparator {
   border-top: 1px solid hsla(0,0%,0%,.3);
   background-clip: padding-box;
   margin-top: -@tabToolbarNavbarOverlap@;
   /* Position the toolbar above the bottom of background tabs */
   position: relative;
   z-index: 1;
 }
 
+/* Always draw a border on Yosemite to ensure the border is well-defined there
+ * (the default border is too light). */
+@media (-moz-mac-yosemite-theme) {
+  #main-window[tabsintitlebar] #TabsToolbar:not([collapsed="true"]) + #nav-bar:not(:-moz-lwtheme) {
+    border-top: 1px solid hsla(0,0%,0%,.2);
+    background-clip: padding-box;
+    margin-top: -@tabToolbarNavbarOverlap@;
+    /* Position the toolbar above the bottom of background tabs */
+    position: relative;
+    z-index: 1;
+  }
+}
+
 #nav-bar-customization-target {
   padding: 4px;
 }
 
 #PersonalToolbar {
   padding: 0 4px 4px;
 }
 
@@ -2207,24 +2220,26 @@ toolbarbutton[sdk-button="true"][cui-are
 
 .ac-result-type-bookmark,
 .autocomplete-treebody::-moz-tree-image(bookmark, treecolAutoCompleteImage) {
   list-style-image: url("chrome://browser/skin/places/star-icons.png");
   -moz-image-region: rect(0, 48px, 16px, 32px);
 }
 
 .ac-result-type-keyword,
-.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage) {
+.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage),
+richlistitem[type~="action"][actiontype="searchengine"] > .ac-title-box > .ac-site-icon {
   list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
   width: 16px;
   height: 16px;
 }
 
 .ac-result-type-keyword[selected="true"],
-.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected) {
+.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected),
+richlistitem[type~="action"][actiontype="searchengine"][selected="true"] > .ac-title-box > .ac-site-icon {
   list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon-inverted);
 }
 
 richlistitem[selected="true"][current="true"] > .ac-title-box > .ac-result-type-bookmark,
 .autocomplete-treebody::-moz-tree-image(selected, current, bookmark, treecolAutoCompleteImage) {
   list-style-image: url("chrome://browser/skin/places/star-icons.png");
   -moz-image-region: rect(0, 64px, 16px, 48px);
 }
@@ -2251,24 +2266,16 @@ richlistitem[type~="action"][actiontype=
   -moz-image-region: rect(0, 16px, 11px, 0);
   padding: 0 3px;
 }
 
 richlistitem[type~="action"][actiontype="switchtab"][selected="true"] > .ac-url-box > .ac-action-icon {
   -moz-image-region: rect(11px, 16px, 22px, 0);
 }
 
-richlistitem[type~="action"][actiontype="searchengine"] > .ac-title-box > .ac-site-icon {
-  list-style-image: url("chrome://global/skin/icons/autocomplete-search.svg#search-icon");
-}
-
-richlistitem[type~="action"][actiontype="searchengine"][selected="true"] > .ac-title-box > .ac-site-icon {
-  list-style-image: url("chrome://global/skin/icons/autocomplete-search.svg#search-icon-inverted");
-}
-
 @media (min-resolution: 2dppx) {
   .ac-result-type-bookmark {
     list-style-image: url("chrome://browser/skin/places/star-icons@2x.png");
     -moz-image-region: rect(0, 64px, 32px, 32px);
   }
 
   richlistitem[selected="true"][current="true"] > .ac-title-box > .ac-result-type-bookmark {
     list-style-image: url("chrome://browser/skin/places/star-icons@2x.png");
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -1389,27 +1389,29 @@ html|*.urlbar-input:-moz-lwtheme::-moz-p
 }
 
 richlistitem[selected="true"][current="true"] > .ac-title-box > .ac-result-type-bookmark,
 .autocomplete-treebody::-moz-tree-image(selected, current, bookmark, treecolAutoCompleteImage) {
   -moz-image-region: rect(0px 48px 16px 32px);
 }
 
 .ac-result-type-keyword,
-.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage) {
+.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage),
+richlistitem[type~="action"][actiontype="searchengine"] > .ac-title-box > .ac-site-icon {
   list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
   width: 16px;
   height: 16px;
 }
 
 %ifdef WINDOWS_AERO
 @media not all and (-moz-windows-default-theme) {
 %endif
   .ac-result-type-keyword[selected="true"],
-  .autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected) {
+  .autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected),
+  richlistitem[type~="action"][actiontype="searchengine"][selected="true"] > .ac-title-box > .ac-site-icon {
     list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon-inverted);
   }
 %ifdef WINDOWS_AERO
 }
 %endif
 
 .ac-result-type-tag,
 .autocomplete-treebody::-moz-tree-image(tag, treecolAutoCompleteImage) {
@@ -1458,30 +1460,16 @@ richlistitem[type~="action"][actiontype=
   .ac-url-text[selected="true"],
   .ac-action-text[selected="true"] {
     color: inherit !important;
   }
 %ifdef WINDOWS_AERO
 }
 %endif
 
-richlistitem[type~="action"][actiontype="searchengine"] > .ac-title-box > .ac-site-icon {
-  list-style-image: url("chrome://global/skin/icons/autocomplete-search.svg#search-icon");
-}
-
-%ifdef WINDOWS_AERO
-@media not all and (-moz-windows-default-theme) {
-%endif
-richlistitem[type~="action"][actiontype="searchengine"][selected="true"] > .ac-title-box > .ac-site-icon {
-  list-style-image: url("chrome://global/skin/icons/autocomplete-search.svg#search-icon-inverted");
-}
-%ifdef WINDOWS_AERO
-}
-%endif
-
 .autocomplete-treebody::-moz-tree-cell-text(treecolAutoCompleteComment) {
   color: GrayText;
 }
 
 .autocomplete-treebody::-moz-tree-cell-text(suggesthint, treecolAutoCompleteComment),
 .autocomplete-treebody::-moz-tree-cell-text(suggestfirst, treecolAutoCompleteComment)
 {
   color: GrayText;
--- a/build/automationutils.py
+++ b/build/automationutils.py
@@ -281,24 +281,19 @@ def processSingleLeakFile(leakLogFileNam
     return
 
   if totalBytesLeaked == 0:
     log.info("TEST-PASS | leakcheck | %s no leaks detected!" % processString)
     return
 
   # totalBytesLeaked was seen and is non-zero.
   if totalBytesLeaked > leakThreshold:
-    if processType == "tab":
-      # For now, ignore tab process leaks. See bug 1051230.
-      log.info("WARNING | leakcheck | ignoring leaks in tab process")
-      prefix = "WARNING"
-    else:
-      logAsWarning = True
-      # Fail the run if we're over the threshold (which defaults to 0)
-      prefix = "TEST-UNEXPECTED-FAIL"
+    logAsWarning = True
+    # Fail the run if we're over the threshold (which defaults to 0)
+    prefix = "TEST-UNEXPECTED-FAIL"
   else:
     prefix = "WARNING"
   # Create a comma delimited string of the first N leaked objects found,
   # to aid with bug summary matching in TBPL. Note: The order of the objects
   # had no significance (they're sorted alphabetically).
   maxSummaryObjects = 5
   leakedObjectSummary = ', '.join(leakedObjectNames[:maxSummaryObjects])
   if len(leakedObjectNames) > maxSummaryObjects:
@@ -306,55 +301,73 @@ def processSingleLeakFile(leakLogFileNam
 
   if logAsWarning:
     log.warning("%s | leakcheck | %s %d bytes leaked (%s)"
                 % (prefix, processString, totalBytesLeaked, leakedObjectSummary))
   else:
     log.info("%s | leakcheck | %s %d bytes leaked (%s)"
              % (prefix, processString, totalBytesLeaked, leakedObjectSummary))
 
-def processLeakLog(leakLogFile, leakThreshold = 0):
+def processLeakLog(leakLogFile, leakThresholds):
   """Process the leak log, including separate leak logs created
   by child processes.
 
   Use this function if you want an additional PASS/FAIL summary.
   It must be used with the |XPCOM_MEM_BLOAT_LOG| environment variable.
 
   The base of leakLogFile for a non-default process needs to end with
     _proctype_pid12345.log
   "proctype" is a string denoting the type of the process, which should
   be the result of calling XRE_ChildProcessTypeToString(). 12345 is
   a series of digits that is the pid for the process. The .log is
   optional.
 
   All other file names are treated as being for default processes.
+
+  leakThresholds should be a dict mapping process types to leak thresholds,
+  in bytes. If a process type is not present in the dict the threshold
+  will be 0.
   """
 
   if not os.path.exists(leakLogFile):
     log.info("WARNING | leakcheck | refcount logging is off, so leaks can't be detected!")
     return
 
-  if leakThreshold != 0:
-    log.info("TEST-INFO | leakcheck | threshold set at %d bytes" % leakThreshold)
+  # This list is based on kGeckoProcessTypeString. ipdlunittest processes likely
+  # are not going to produce leak logs we will ever see.
+  knownProcessTypes = ["default", "plugin", "tab", "geckomediaplugin"]
+
+  for processType in knownProcessTypes:
+    log.info("TEST-INFO | leakcheck | %s process: leak threshold set at %d bytes"
+             % (processType, leakThresholds.get(processType, 0)))
+
+  for processType in leakThresholds:
+    if not processType in knownProcessTypes:
+      log.info("TEST-UNEXPECTED-FAIL | leakcheck | Unknown process type %s in leakThresholds"
+               % processType)
 
   (leakLogFileDir, leakFileBase) = os.path.split(leakLogFile)
   if leakFileBase[-4:] == ".log":
     leakFileBase = leakFileBase[:-4]
     fileNameRegExp = re.compile(r"_([a-z]*)_pid\d*.log$")
   else:
     fileNameRegExp = re.compile(r"_([a-z]*)_pid\d*$")
 
   for fileName in os.listdir(leakLogFileDir):
     if fileName.find(leakFileBase) != -1:
       thisFile = os.path.join(leakLogFileDir, fileName)
       m = fileNameRegExp.search(fileName)
       if m:
         processType = m.group(1)
       else:
         processType = "default"
+      if not processType in knownProcessTypes:
+        log.info("TEST-UNEXPECTED-FAIL | leakcheck | Leak log with unknown process type %s"
+                 % processType)
+      leakThreshold = leakThresholds.get(processType, 0)
       processSingleLeakFile(thisFile, processType, leakThreshold)
 
 def replaceBackSlashes(input):
   return input.replace('\\', '/')
 
 class KeyValueParseError(Exception):
   """error when parsing strings of serialized key-values"""
   def __init__(self, msg, errors=()):
--- a/caps/nsIPrincipal.idl
+++ b/caps/nsIPrincipal.idl
@@ -108,23 +108,19 @@ interface nsIPrincipal : nsISerializable
     }
     %}
 
     /**
      * Checks whether this principal is allowed to load the network resource
      * located at the given URI under the same-origin policy. This means that
      * codebase principals are only allowed to load resources from the same
      * domain, the system principal is allowed to load anything, and null
-     * principals are not allowed to load anything. This is changed slightly
-     * by the optional flag allowIfInheritsPrincipal (which defaults to false)
-     * which allows the load of a data: URI (which inherits the principal of
-     * its loader) or a URI with the same principal as its loader (eg. a
-     * Blob URI).
-     * In these cases, with allowIfInheritsPrincipal set to true, the URI can
-     * be loaded by a null principal.
+     * principals can only load URIs where they are the principal. This is
+     * changed by the optional flag allowIfInheritsPrincipal (which defaults to
+     * false) which allows URIs that inherit their loader's principal.
      *
      * If the load is allowed this function does nothing. If the load is not
      * allowed the function throws NS_ERROR_DOM_BAD_URI.
      *
      * NOTE: Other policies might override this, such as the Access-Control
      *       specification.
      * NOTE: The 'domain' attribute has no effect on the behaviour of this
      *       function.
--- a/caps/nsNullPrincipal.cpp
+++ b/caps/nsNullPrincipal.cpp
@@ -234,27 +234,26 @@ nsNullPrincipal::SubsumesConsideringDoma
 
 NS_IMETHODIMP
 nsNullPrincipal::CheckMayLoad(nsIURI* aURI, bool aReport, bool aAllowIfInheritsPrincipal)
  {
   if (aAllowIfInheritsPrincipal) {
     if (nsPrincipal::IsPrincipalInherited(aURI)) {
       return NS_OK;
     }
+  }
 
-    // Also allow the load if the principal of the URI being checked is exactly
-    // us ie this.
-    nsCOMPtr<nsIURIWithPrincipal> uriPrinc = do_QueryInterface(aURI);
-    if (uriPrinc) {
-      nsCOMPtr<nsIPrincipal> principal;
-      uriPrinc->GetPrincipal(getter_AddRefs(principal));
+  // Also allow the load if we are the principal of the URI being checked.
+  nsCOMPtr<nsIURIWithPrincipal> uriPrinc = do_QueryInterface(aURI);
+  if (uriPrinc) {
+    nsCOMPtr<nsIPrincipal> principal;
+    uriPrinc->GetPrincipal(getter_AddRefs(principal));
 
-      if (principal && principal == this) {
-        return NS_OK;
-      }
+    if (principal == this) {
+      return NS_OK;
     }
   }
 
   if (aReport) {
     nsScriptSecurityManager::ReportError(
       nullptr, NS_LITERAL_STRING("CheckSameOriginError"), mURI, aURI);
   }
 
--- a/content/html/content/test/file_iframe_sandbox_b_if3.html
+++ b/content/html/content/test/file_iframe_sandbox_b_if3.html
@@ -6,33 +6,60 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <script>
   function ok(result, message) {
     window.parent.postMessage({ok: result, desc: message}, "*");
   }
 
   function testXHR() {
-  var xhr = new XMLHttpRequest();
-
-  xhr.open("GET", "file_iframe_sandbox_b_if1.html");
+    // Standard URL should be blocked as we have a unique origin.
+    var xhr = new XMLHttpRequest();
+    xhr.open("GET", "file_iframe_sandbox_b_if1.html");
+    xhr.onreadystatechange = function (oEvent) {
+      var result = false;
+      if (xhr.readyState == 4) {
+        if (xhr.status == 0) {
+          result = true;
+        }
+        ok(result, "XHR should be blocked in an iframe sandboxed WITHOUT 'allow-same-origin'");
+      }
+    }
+    xhr.send(null);
 
-  xhr.onreadystatechange = function (oEvent) {
-    var result = false;
-    if (xhr.readyState == 4) {
-      if (xhr.status == 0) {
-        result = true;
+    // Blob URL should work as it will have our unique origin.
+    var blobXhr = new XMLHttpRequest();
+    var blobUrl = URL.createObjectURL(new Blob(["wibble"], {type: "text/plain"}));
+    blobXhr.open("GET", blobUrl);
+    blobXhr.onreadystatechange = function () {
+      if (this.readyState == 4) {
+        ok(this.status == 200 && this.response == "wibble", "XHR for a blob URL created in this document should NOT be blocked in an iframe sandboxed WITHOUT 'allow-same-origin'");
       }
-      ok(result, "XHR should be blocked in an iframe sandboxed WITHOUT 'allow-same-origin'");
+    }
+    try {
+      blobXhr.send();
+    } catch(e) {
+      ok(false, "failed to send XHR for blob URL: error: " + e);
+    }
+
+    // Data URL should work as it inherits the loader's origin.
+    var dataXhr = new XMLHttpRequest();
+    dataXhr.open("GET", "data:text/html,wibble");
+    dataXhr.onreadystatechange = function () {
+      if (this.readyState == 4) {
+        ok(this.status == 200 && this.response == "wibble", "XHR for a data URL should NOT be blocked in an iframe sandboxed WITHOUT 'allow-same-origin'");
+      }
+    }
+    try {
+      dataXhr.send();
+    } catch(e) {
+      ok(false, "failed to send XHR for data URL: error: " + e);
     }
   }
 
-  xhr.send(null);
-}
-
   function doStuff() {
     try {
       window.parent.ok(false, "documents sandboxed without 'allow-same-origin' should NOT be able to access their parent");
     } catch (error) {
       ok(true, "documents sandboxed without 'allow-same-origin' should NOT be able to access their parent");
     }
 
     // should NOT be able to access document.cookie
@@ -51,15 +78,15 @@
 
     // should NOT be able to access sessionStorage
     try {
       var foo = window.sessionStorage;
     } catch(error) {
       ok(true, "a document sandboxed without allow-same-origin should NOT be able to access sessionStorage");
     }
 
-  testXHR();
+    testXHR();
   }
 </script>
 <body onLoad="doStuff()">
   I am sandboxed but with "allow-scripts"
 </body>
 </html>
--- a/content/html/content/test/test_audio_wakelock.html
+++ b/content/html/content/test/test_audio_wakelock.html
@@ -34,17 +34,17 @@ function testAudioPlayPause() {
   audio.addEventListener('playing', function() {
     startDate = new Date();
 
     // The next step is to unlock the resource.
     lockState = false;
     audio.pause();
   });
 
-  navigator.mozPower.addWakeLockListener(function testAudioPlayListener(topic, state) {
+  function testAudioPlayListener(topic, state) {
     is(topic, "cpu", "Audio element locked the target == cpu");
     var locked = state == "locked-foreground" ||
                  state == "locked-background";
 
     is(locked, lockState, "Audio element locked the cpu - no paused");
     count++;
 
     // count == 1 is when the cpu wakelock is created
@@ -53,18 +53,19 @@ function testAudioPlayPause() {
     if (count == 2) {
       var diffDate = (new Date() - startDate);
       ok(diffDate > 200, "There was at least 200 milliseconds between the stop and the wakelock release");
 
       content.removeChild(audio);
       navigator.mozPower.removeWakeLockListener(testAudioPlayListener);
       runTests();
     }
-  });
+  };
 
+  navigator.mozPower.addWakeLockListener(testAudioPlayListener);
   audio.play();
 }
 
 function testAudioPlay() {
   var lockState = true;
   var count = 0;
 
   var content = document.getElementById('content');
@@ -73,17 +74,17 @@ function testAudioPlay() {
   audio.src = "wakelock.ogg";
   content.appendChild(audio);
 
   var startDate;
   audio.addEventListener('progress', function() {
     startDate = new Date();
   });
 
-  navigator.mozPower.addWakeLockListener(function testAudioPlayListener(topic, state) {
+  function testAudioPlayListener(topic, state) {
     is(topic, "cpu", "Audio element locked the target == cpu");
     var locked = state == "locked-foreground" ||
                  state == "locked-background";
 
     is(locked, lockState, "Audio element locked the cpu - no paused");
     count++;
 
     // count == 1 is when the cpu wakelock is created: the wakelock must be
@@ -96,33 +97,37 @@ function testAudioPlay() {
     } else if (count == 2) {
       var diffDate = (new Date() - startDate);
       ok(diffDate > 200, "There was at least 200 milliseconds between the stop and the wakelock release");
 
       content.removeChild(audio);
       navigator.mozPower.removeWakeLockListener(testAudioPlayListener);
       runTests();
     }
-  });
+  };
 
+  navigator.mozPower.addWakeLockListener(testAudioPlayListener);
   audio.play();
 }
 
 var tests = [ testAudioPlayPause, testAudioPlay ];
 function runTests() {
   if (!tests.length) {
     SimpleTest.finish();
     return;
   }
 
   var test =  tests.pop();
   test();
 };
 
-SpecialPowers.addPermission("power", true, document);
-SpecialPowers.pushPrefEnv({"set": [["media.wakelock_timeout", 500]]}, runTests);
+SpecialPowers.pushPermissions(
+  [{'type': 'power', 'allow': true, 'context': document}],
+  function() {
+    SpecialPowers.pushPrefEnv({"set": [["media.wakelock_timeout", 500]]}, runTests);
+  });
 
 SimpleTest.waitForExplicitFinish();
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/html/content/test/test_iframe_sandbox_same_origin.html
+++ b/content/html/content/test/test_iframe_sandbox_same_origin.html
@@ -23,18 +23,18 @@ function ok_wrapper(result, desc) {
   ok(result, desc);
 
   completedTests++;
 
   if (result) {
     passedTests++;
   }
 
-  if (completedTests == 12) {
-    is(passedTests, 12, "There are 12 same-origin tests that should pass");
+  if (completedTests == 14) {
+    is(passedTests, completedTests, "There are " + completedTests + " same-origin tests that should pass");
 
     SimpleTest.finish();
   }
 }
 
 function receiveMessage(event)
 {
   ok_wrapper(event.data.ok, event.data.desc);
--- a/content/media/AudioBufferUtils.h
+++ b/content/media/AudioBufferUtils.h
@@ -116,16 +116,21 @@ public:
    * the number of frames written. */
   uint32_t Empty(AudioCallbackBufferWrapper<T, CHANNELS>& aBuffer) {
     uint32_t framesToWrite = std::min(aBuffer.Available(),
                                       SamplesToFrames(CHANNELS, mPosition));
 
     aBuffer.WriteFrames(mBuffer, framesToWrite);
 
     mPosition -= FramesToSamples(CHANNELS, framesToWrite);
+    // If we didn't empty the spill buffer for some reason, shift the remaining data down
+    if (mPosition > 0) {
+      PodMove(mBuffer, mBuffer + FramesToSamples(CHANNELS, framesToWrite),
+              mPosition);
+    }
 
     return framesToWrite;
   }
   /* Fill the spill buffer from aInput, containing aFrames frames, return the
    * number of frames written to the spill buffer */
   uint32_t Fill(T* aInput, uint32_t aFrames) {
     uint32_t framesToWrite = std::min(aFrames,
                                       BLOCK_SIZE - SamplesToFrames(CHANNELS,
--- a/content/media/AudioSink.cpp
+++ b/content/media/AudioSink.cpp
@@ -50,28 +50,20 @@ AudioSink::AudioSink(MediaDecoderStateMa
 nsresult
 AudioSink::Init()
 {
   nsresult rv = NS_NewNamedThread("Media Audio",
                                   getter_AddRefs(mThread),
                                   nullptr,
                                   MEDIA_THREAD_STACK_SIZE);
   if (NS_FAILED(rv)) {
-    mStateMachine->OnAudioSinkError();
     return rv;
   }
-
   nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &AudioSink::AudioLoop);
-  rv =  mThread->Dispatch(event, NS_DISPATCH_NORMAL);
-  if (NS_FAILED(rv)) {
-    mStateMachine->OnAudioSinkError();
-    return rv;
-  }
-
-  return NS_OK;
+  return mThread->Dispatch(event, NS_DISPATCH_NORMAL);
 }
 
 int64_t
 AudioSink::GetPosition()
 {
   if (!mAudioStream) {
     return 0;
   }
@@ -141,18 +133,16 @@ AudioSink::StopPlayback()
 void
 AudioSink::AudioLoop()
 {
   AssertOnAudioThread();
   SINK_LOG("AudioLoop started");
 
   if (NS_FAILED(InitializeAudioStream())) {
     NS_WARNING("Initializing AudioStream failed.");
-    ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
-    mStateMachine->OnAudioSinkError();
     return;
   }
 
   while (1) {
     {
       ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
       WaitForAudioToPlay();
       if (!IsPlaybackContinuing()) {
@@ -202,23 +192,20 @@ AudioSink::AudioLoop()
 
 nsresult
 AudioSink::InitializeAudioStream()
 {
   // AudioStream initialization can block for extended periods in unusual
   // circumstances, so we take care to drop the decoder monitor while
   // initializing.
   RefPtr<AudioStream> audioStream(new AudioStream());
-  nsresult rv = audioStream->Init(mInfo.mChannels, mInfo.mRate,
-                                  mChannel, AudioStream::HighLatency);
-  if (NS_FAILED(rv)) {
-    audioStream->Shutdown();
-    return rv;
-  }
-
+  audioStream->Init(mInfo.mChannels, mInfo.mRate,
+                    mChannel, AudioStream::HighLatency);
+  // TODO: Check Init's return value and bail on error.  Unfortunately this
+  // causes some tests to fail due to playback failing.
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   mAudioStream = audioStream;
   UpdateStreamSettings();
 
   return NS_OK;
 }
 
 void
--- a/content/media/GraphDriver.cpp
+++ b/content/media/GraphDriver.cpp
@@ -800,62 +800,70 @@ AudioCallbackDriver::DataCallback(AudioD
 
   uint32_t durationMS = aFrames * 1000 / mSampleRate;
 
   // For now, simply average the duration with the previous
   // duration so there is some damping against sudden changes.
   if (!mIterationDurationMS) {
     mIterationDurationMS = durationMS;
   } else {
-    mIterationDurationMS += durationMS;
-    mIterationDurationMS /= 2;
+    mIterationDurationMS = (mIterationDurationMS*3) + durationMS;
+    mIterationDurationMS /= 4;
   }
 
   mBuffer.SetBuffer(aBuffer, aFrames);
-
+  // fill part or all with leftover data from last iteration (since we
+  // align to Audio blocks)
   mScratchBuffer.Empty(mBuffer);
-
-  mStateComputedTime = mNextStateComputedTime;
+  // if we totally filled the buffer (and mScratchBuffer isn't empty),
+  // we don't need to run an iteration and if we do so we may overflow.
+  if (mBuffer.Available()) {
 
-  // State computed time is decided by the audio callback's buffer length. We
-  // compute the iteration start and end from there, trying to keep the amount
-  // of buffering in the graph constant.
-  mNextStateComputedTime =
-    mGraphImpl->RoundUpToNextAudioBlock(mStateComputedTime + mBuffer.Available());
+    mStateComputedTime = mNextStateComputedTime;
+
+    // State computed time is decided by the audio callback's buffer length. We
+    // compute the iteration start and end from there, trying to keep the amount
+    // of buffering in the graph constant.
+    mNextStateComputedTime =
+      mGraphImpl->RoundUpToNextAudioBlock(mStateComputedTime + mBuffer.Available());
 
-  mIterationStart = mIterationEnd;
-  // inGraph is the number of audio frames there is between the state time and
-  // the current time, i.e. the maximum theoretical length of the interval we
-  // could use as [mIterationStart; mIterationEnd].
-  GraphTime inGraph = mStateComputedTime - mIterationStart;
-  // We want the interval [mIterationStart; mIterationEnd] to be before the
-  // interval [mStateComputedTime; mNextStateComputedTime]. We also want
-  // the distance between these intervals to be roughly equivalent each time, to
-  // ensure there is no clock drift between current time and state time. Since
-  // we can't act on the state time because we have to fill the audio buffer, we
-  // reclock the current time against the state time, here.
-  mIterationEnd = mIterationStart + 0.8 * inGraph;
+    mIterationStart = mIterationEnd;
+    // inGraph is the number of audio frames there is between the state time and
+    // the current time, i.e. the maximum theoretical length of the interval we
+    // could use as [mIterationStart; mIterationEnd].
+    GraphTime inGraph = mStateComputedTime - mIterationStart;
+    // We want the interval [mIterationStart; mIterationEnd] to be before the
+    // interval [mStateComputedTime; mNextStateComputedTime]. We also want
+    // the distance between these intervals to be roughly equivalent each time, to
+    // ensure there is no clock drift between current time and state time. Since
+    // we can't act on the state time because we have to fill the audio buffer, we
+    // reclock the current time against the state time, here.
+    mIterationEnd = mIterationStart + 0.8 * inGraph;
 
-  STREAM_LOG(PR_LOG_DEBUG, ("interval[%ld; %ld] state[%ld; %ld] (frames: %ld) (durationMS: %u) (duration ticks: %ld)\n",
-             (long)mIterationStart, (long)mIterationEnd,
-             (long)mStateComputedTime, (long)mNextStateComputedTime,
-             (long)aFrames, (uint32_t)durationMS,
-             (long)(mNextStateComputedTime - mStateComputedTime)));
+    STREAM_LOG(PR_LOG_DEBUG, ("interval[%ld; %ld] state[%ld; %ld] (frames: %ld) (durationMS: %u) (duration ticks: %ld)\n",
+                              (long)mIterationStart, (long)mIterationEnd,
+                              (long)mStateComputedTime, (long)mNextStateComputedTime,
+                              (long)aFrames, (uint32_t)durationMS,
+                              (long)(mNextStateComputedTime - mStateComputedTime)));
 
-  mCurrentTimeStamp = TimeStamp::Now();
+    mCurrentTimeStamp = TimeStamp::Now();
 
-  if (mStateComputedTime < mIterationEnd) {
-    STREAM_LOG(PR_LOG_WARNING, ("Media graph global underrun detected"));
-    mIterationEnd = mStateComputedTime;
-  }
+    if (mStateComputedTime < mIterationEnd) {
+      STREAM_LOG(PR_LOG_WARNING, ("Media graph global underrun detected"));
+      mIterationEnd = mStateComputedTime;
+    }
 
-  stillProcessing = mGraphImpl->OneIteration(mIterationStart,
-                                             mIterationEnd,
-                                             mStateComputedTime,
-                                             mNextStateComputedTime);
+    stillProcessing = mGraphImpl->OneIteration(mIterationStart,
+                                               mIterationEnd,
+                                               mStateComputedTime,
+                                               mNextStateComputedTime);
+  } else {
+    NS_WARNING("DataCallback buffer filled entirely from scratch buffer, skipping iteration.");
+    stillProcessing = true;
+  }
 
   mBuffer.BufferFilled();
 
   if (mNextDriver && stillProcessing) {
     {
       // If the audio stream has not been started by the previous driver or
       // the graph itself, keep it alive.
       MonitorAutoLock mon(mGraphImpl->GetMonitor());
@@ -891,17 +899,17 @@ AudioCallbackDriver::MixerCallback(Audio
                                    AudioSampleFormat aFormat,
                                    uint32_t aChannels,
                                    uint32_t aFrames,
                                    uint32_t aSampleRate)
 {
   uint32_t toWrite = mBuffer.Available();
 
   if (!mBuffer.Available()) {
-    NS_WARNING("MediaStreamGraph SpillBuffer full, expect frame drop.");
+    NS_WARNING("DataCallback buffer full, expect frame drops.");
   }
 
   MOZ_ASSERT(mBuffer.Available() <= aFrames);
 
   mBuffer.WriteFrames(aMixedBuffer, mBuffer.Available());
   MOZ_ASSERT(mBuffer.Available() == 0, "Missing frames to fill audio callback's buffer.");
 
   DebugOnly<uint32_t> written = mScratchBuffer.Fill(aMixedBuffer + toWrite * aChannels, aFrames - toWrite);
--- a/content/media/MediaDecoderStateMachine.cpp
+++ b/content/media/MediaDecoderStateMachine.cpp
@@ -1149,19 +1149,19 @@ void MediaDecoderStateMachine::StartPlay
     DECODER_LOG("Offloading playback");
     return;
   }
 
   mDecoder->NotifyPlaybackStarted();
   SetPlayStartTime(TimeStamp::Now());
 
   NS_ASSERTION(IsPlaying(), "Should report playing by end of StartPlayback()");
-  nsresult rv = StartAudioThread();
-  NS_ENSURE_SUCCESS_VOID(rv);
-
+  if (NS_FAILED(StartAudioThread())) {
+    DECODER_WARN("Failed to create audio thread");
+  }
   mDecoder->GetReentrantMonitor().NotifyAll();
   mDecoder->UpdateStreamBlockingForStateMachinePlaying();
   DispatchDecodeTasksIfNeeded();
 }
 
 void MediaDecoderStateMachine::UpdatePlaybackPositionInternal(int64_t aTime)
 {
   SAMPLE_LOG("UpdatePlaybackPositionInternal(%lld) (mStartTime=%lld)", aTime, mStartTime);
@@ -1786,22 +1786,25 @@ MediaDecoderStateMachine::StartAudioThre
   if (mAudioCaptured) {
     NS_ASSERTION(mStopAudioThread, "mStopAudioThread must always be true if audio is captured");
     return NS_OK;
   }
 
   mStopAudioThread = false;
   if (HasAudio() && !mAudioSink) {
     mAudioCompleted = false;
-    mAudioSink = new AudioSink(this, mAudioStartTime,
-                               mInfo.mAudio, mDecoder->GetAudioChannel());
-    // OnAudioSinkError() will be called before Init() returns if an error
-    // occurs during initialization.
+    mAudioSink = new AudioSink(this,
+                               mAudioStartTime, mInfo.mAudio, mDecoder->GetAudioChannel());
     nsresult rv = mAudioSink->Init();
-    NS_ENSURE_SUCCESS(rv, rv);
+    if (NS_FAILED(rv)) {
+      DECODER_WARN("Changed state to SHUTDOWN because audio sink initialization failed");
+      SetState(DECODER_STATE_SHUTDOWN);
+      mScheduler->ScheduleAndShutdown();
+      return rv;
+    }
 
     mAudioSink->SetVolume(mVolume);
     mAudioSink->SetPlaybackRate(mPlaybackRate);
     mAudioSink->SetPreservesPitch(mPreservesPitch);
   }
   return NS_OK;
 }
 
@@ -3116,34 +3119,15 @@ void MediaDecoderStateMachine::OnAudioSi
     return;
   }
   mAudioCompleted = true;
   UpdateReadyState();
   // Kick the decode thread; it may be sleeping waiting for this to finish.
   mDecoder->GetReentrantMonitor().NotifyAll();
 }
 
-void MediaDecoderStateMachine::OnAudioSinkError()
-{
-  AssertCurrentThreadInMonitor();
-  // AudioSink not used with captured streams, so ignore errors in this case.
-  if (mAudioCaptured) {
-    return;
-  }
-
-  mAudioCompleted = true;
-
-  // Notify media decoder/element about this error.
-  RefPtr<nsIRunnable> task(
-    NS_NewRunnableMethod(this, &MediaDecoderStateMachine::OnDecodeError));
-  nsresult rv = mDecodeTaskQueue->Dispatch(task);
-  if (NS_FAILED(rv)) {
-    DECODER_WARN("Failed to dispatch OnDecodeError");
-  }
-}
-
 } // namespace mozilla
 
 // avoid redefined macro in unified build
 #undef DECODER_LOG
 #undef VERBOSE_LOG
 #undef DECODER_WARN
 #undef DECODER_WARN_HELPER
--- a/content/media/MediaDecoderStateMachine.h
+++ b/content/media/MediaDecoderStateMachine.h
@@ -631,19 +631,16 @@ protected:
 
   // Update mDecoder's playback offset.
   void OnPlaybackOffsetUpdate(int64_t aPlaybackOffset);
 
   // Called by the AudioSink to signal that all outstanding work is complete
   // and the sink is shutting down.
   void OnAudioSinkComplete();
 
-  // Called by the AudioSink to signal errors.
-  void OnAudioSinkError();
-
   // The decoder object that created this state machine. The state machine
   // holds a strong reference to the decoder to ensure that the decoder stays
   // alive once media element has started the decoder shutdown process, and has
   // dropped its reference to the decoder. This enables the state machine to
   // keep using the decoder's monitor until the state machine has finished
   // shutting down, without fear of the monitor being destroyed. After
   // shutting down, the state machine will then release this reference,
   // causing the decoder to be destroyed. This is accessed on the decode,
--- a/content/media/MediaStreamGraphImpl.h
+++ b/content/media/MediaStreamGraphImpl.h
@@ -148,18 +148,26 @@ public:
    * Called before the thread runs.
    */
   void Init();
   // The following methods run on the graph thread (or possibly the main thread if
   // mLifecycleState > LIFECYCLE_RUNNING)
   void AssertOnGraphThreadOrNotRunning() {
     // either we're on the right thread (and calling CurrentDriver() is safe),
     // or we're going to assert anyways, so don't cross-check CurrentDriver
-    MOZ_ASSERT(mDriver->OnThread() ||
-               (mLifecycleState > LIFECYCLE_RUNNING && NS_IsMainThread()));
+#ifdef DEBUG
+    // if all the safety checks fail, assert we own the monitor
+    if (!mDriver->OnThread()) {
+      if (!(mDetectedNotRunning &&
+            mLifecycleState > LIFECYCLE_RUNNING &&
+            NS_IsMainThread())) {
+        mMonitor.AssertCurrentThreadOwns();
+      }
+    }
+#endif
   }
   /*
    * This does the actual iteration: Message processing, MediaStream ordering,
    * blocking computation and processing.
    */
   void DoIteration();
 
   bool OneIteration(GraphTime aFrom, GraphTime aTo,
@@ -419,40 +427,30 @@ public:
    */
   void PausedIndefinitly();
   void ResumedFromPaused();
 
   /**
    * Not safe to call off the MediaStreamGraph thread unless monitor is held!
    */
   GraphDriver* CurrentDriver() {
-#ifdef DEBUG
-    // #ifdef since we're not wrapping it all in MOZ_ASSERT()
-    if (!mDriver->OnThread()) {
-      mMonitor.AssertCurrentThreadOwns();
-    }
-#endif
+    AssertOnGraphThreadOrNotRunning();
     return mDriver;
   }
 
   /**
    * Effectively set the new driver, while we are switching.
    * It is only safe to call this at the very end of an iteration, when there
    * has been a SwitchAtNextIteration call during the iteration. The driver
    * should return and pass the control to the new driver shortly after.
    * We can also switch from Revive() (on MainThread), in which case the
    * monitor is held
    */
   void SetCurrentDriver(GraphDriver* aDriver) {
-#ifdef DEBUG
-    // #ifdef since we're not wrapping it all in MOZ_ASSERT()
-    if (!mDriver->OnThread()) {
-      mMonitor.AssertCurrentThreadOwns();
-    }
-#endif
+    AssertOnGraphThreadOrNotRunning();
     mDriver = aDriver;
   }
 
   Monitor& GetMonitor() {
     return mMonitor;
   }
 
   void EnsureNextIteration() {
--- a/content/svg/content/src/SVGContentUtils.cpp
+++ b/content/svg/content/src/SVGContentUtils.cpp
@@ -117,20 +117,31 @@ GetStrokeDashData(SVGContentUtils::AutoS
       if (dashLength < 0.0) {
         return eContinuousStroke; // invalid
       }
       dashPattern[i] = dashLength;
       (i % 2 ? totalLengthOfGaps : totalLengthOfDashes) += dashLength;
     }
   }
 
-  // Now that aStrokeOptions.mDashPattern is fully initialized we can safely
-  // set mDashLength:
+  // Now that aStrokeOptions.mDashPattern is fully initialized (we didn't
+  // return early above) we can safely set mDashLength:
   aStrokeOptions->mDashLength = dashArrayLength;
 
+  if ((dashArrayLength % 2) == 1) {
+    // If we have a dash pattern with an odd number of lengths the pattern
+    // repeats a second time, per the SVG spec., and as implemented by Moz2D.
+    // When deciding whether to return eNoStroke or eContinuousStroke below we
+    // need to take into account that in the repeat pattern the dashes become
+    // gaps, and the gaps become dashes.
+    Float origTotalLengthOfDashes = totalLengthOfDashes;
+    totalLengthOfDashes += totalLengthOfGaps;
+    totalLengthOfGaps += origTotalLengthOfDashes;
+  }
+
   if (totalLengthOfDashes <= 0 || totalLengthOfGaps <= 0) {
     if (totalLengthOfGaps > 0 && totalLengthOfDashes <= 0) {
       return eNoStroke;
     }
     return eContinuousStroke;
   }
 
   if (aContextPaint && aStyleSVG->mStrokeDashoffsetFromObject) {
@@ -167,19 +178,19 @@ SVGContentUtils::GetStrokeOptions(AutoSt
   DashState dashState =
     GetStrokeDashData(aStrokeOptions, aElement, styleSVG, aContextPaint);
 
   if (dashState == eNoStroke) {
     // Hopefully this will shortcircuit any stroke operations:
     aStrokeOptions->mLineWidth = 0;
     return;
   }
-  if (dashState == eContinuousStroke) {
-    // Prevent our caller from wasting time looking at the dash array:
-    aStrokeOptions->mDashLength = 0;
+  if (dashState == eContinuousStroke && aStrokeOptions->mDashPattern) {
+    // Prevent our caller from wasting time looking at a pattern without gaps:
+    aStrokeOptions->DiscardDashPattern();
   }
 
   aStrokeOptions->mLineWidth =
     GetStrokeWidth(aElement, styleContext, aContextPaint);
 
   aStrokeOptions->mMiterLimit = Float(styleSVG->mStrokeMiterlimit);
 
   switch (styleSVG->mStrokeLinejoin) {
--- a/content/svg/content/src/SVGContentUtils.h
+++ b/content/svg/content/src/SVGContentUtils.h
@@ -111,16 +111,23 @@ public:
         mDashPattern = mSmallArray;
         return mSmallArray;
       }
       static const mozilla::fallible_t fallible = mozilla::fallible_t();
       Float* nonConstArray = new (fallible) Float[aDashCount];
       mDashPattern = nonConstArray;
       return nonConstArray;
     }
+    void DiscardDashPattern() {
+      if (mDashPattern && mDashPattern != mSmallArray) {
+        delete [] mDashPattern;
+      }
+      mDashLength = 0;
+      mDashPattern = nullptr;
+    }
   private:
     // Most dasharrays will fit in this and save us allocating
     Float mSmallArray[16];
   };
 
   static void GetStrokeOptions(AutoStrokeOptions* aStrokeOptions,
                                nsSVGElement* aElement,
                                nsStyleContext* aStyleContext,
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -6809,32 +6809,44 @@ TransactionBase::VerifyRequestParams(con
       if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) {
         ASSERT_UNLESS_FUZZING();
         return false;
       }
       break;
     }
 
     case RequestParams::TObjectStoreDeleteParams: {
+      if (NS_WARN_IF(mMode != IDBTransaction::READ_WRITE &&
+                     mMode != IDBTransaction::VERSION_CHANGE)) {
+        ASSERT_UNLESS_FUZZING();
+        return false;
+      }
+
       const ObjectStoreDeleteParams& params =
         aParams.get_ObjectStoreDeleteParams();
       const nsRefPtr<FullObjectStoreMetadata> objectStoreMetadata =
         GetMetadataForObjectStoreId(params.objectStoreId());
       if (NS_WARN_IF(!objectStoreMetadata)) {
         ASSERT_UNLESS_FUZZING();
         return false;
       }
       if (NS_WARN_IF(!VerifyRequestParams(params.keyRange()))) {
         ASSERT_UNLESS_FUZZING();
         return false;
       }
       break;
     }
 
     case RequestParams::TObjectStoreClearParams: {
+      if (NS_WARN_IF(mMode != IDBTransaction::READ_WRITE &&
+                     mMode != IDBTransaction::VERSION_CHANGE)) {
+        ASSERT_UNLESS_FUZZING();
+        return false;
+      }
+
       const ObjectStoreClearParams& params =
         aParams.get_ObjectStoreClearParams();
       const nsRefPtr<FullObjectStoreMetadata> objectStoreMetadata =
         GetMetadataForObjectStoreId(params.objectStoreId());
       if (NS_WARN_IF(!objectStoreMetadata)) {
         ASSERT_UNLESS_FUZZING();
         return false;
       }
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -2425,20 +2425,20 @@ WorkerPrivateParent<Derived>::NotifyPriv
 template <class Derived>
 bool
 WorkerPrivateParent<Derived>::Suspend(JSContext* aCx, nsPIDOMWindow* aWindow)
 {
   AssertIsOnParentThread();
   MOZ_ASSERT(aCx);
 
   // Shared workers are only suspended if all of their owning documents are
-  // suspended.
-  if (IsSharedWorker() || IsServiceWorker()) {
+  // suspended. It can happen that mSharedWorkers is empty but this thread has
+  // not been unregistered yet.
+  if ((IsSharedWorker() || IsServiceWorker()) && mSharedWorkers.Count()) {
     AssertIsOnMainThread();
-    MOZ_ASSERT(mSharedWorkers.Count());
 
     struct Closure
     {
       nsPIDOMWindow* mWindow;
       bool mAllSuspended;
 
       explicit Closure(nsPIDOMWindow* aWindow)
       : mWindow(aWindow), mAllSuspended(true)
@@ -2510,19 +2510,20 @@ template <class Derived>
 bool
 WorkerPrivateParent<Derived>::Resume(JSContext* aCx, nsPIDOMWindow* aWindow)
 {
   AssertIsOnParentThread();
   MOZ_ASSERT(aCx);
   MOZ_ASSERT_IF(IsDedicatedWorker(), mParentSuspended);
 
   // Shared workers are resumed if any of their owning documents are resumed.
-  if (IsSharedWorker() || IsServiceWorker()) {
+  // It can happen that mSharedWorkers is empty but this thread has not been
+  // unregistered yet.
+  if ((IsSharedWorker() || IsServiceWorker()) && mSharedWorkers.Count()) {
     AssertIsOnMainThread();
-    MOZ_ASSERT(mSharedWorkers.Count());
 
     struct Closure
     {
       nsPIDOMWindow* mWindow;
       bool mAnyRunning;
 
       explicit Closure(nsPIDOMWindow* aWindow)
       : mWindow(aWindow), mAnyRunning(false)
--- a/gfx/2d/HelpersD2D.h
+++ b/gfx/2d/HelpersD2D.h
@@ -462,17 +462,20 @@ CreateStrokeStyleForOptions(const Stroke
     break;
   case JoinStyle::BEVEL:
     joinStyle = D2D1_LINE_JOIN_BEVEL;
     break;
   }
 
 
   HRESULT hr;
-  if (aStrokeOptions.mDashPattern) {
+  // We need to check mDashLength in addition to mDashPattern here since if
+  // mDashPattern is set but mDashLength is zero then the stroke will fail to
+  // paint.
+  if (aStrokeOptions.mDashLength > 0 && aStrokeOptions.mDashPattern) {
     typedef std::vector<Float> FloatVector;
     // D2D "helpfully" multiplies the dash pattern by the line width.
     // That's not what cairo does, or is what <canvas>'s dash wants.
     // So fix the multiplication in advance.
     Float lineWidth = aStrokeOptions.mLineWidth;
     FloatVector dash(aStrokeOptions.mDashPattern,
                      aStrokeOptions.mDashPattern + aStrokeOptions.mDashLength);
     for (FloatVector::iterator it = dash.begin(); it != dash.end(); ++it) {
--- a/gfx/cairo/cairo/src/cairo-image-surface.c
+++ b/gfx/cairo/cairo/src/cairo-image-surface.c
@@ -1080,16 +1080,22 @@ static pixman_image_t *
     cache[i].image = pixman_image_ref (image);
     cache[i].color = pattern->color;
 
 UNLOCK:
     CAIRO_MUTEX_UNLOCK (_cairo_image_solid_cache_mutex);
     return image;
 }
 
+static double
+clamp (double val, double min, double max)
+{
+    return val < min ? min : (val > max ? max : val);
+}
+
 static pixman_image_t *
 _pixman_image_for_gradient (const cairo_gradient_pattern_t *pattern,
 			    const cairo_rectangle_int_t *extents,
 			    int *ix, int *iy)
 {
     pixman_image_t	  *pixman_image;
     pixman_gradient_stop_t pixman_stops_static[2];
     pixman_gradient_stop_t *pixman_stops = pixman_stops_static;
@@ -1141,16 +1147,17 @@ static pixman_image_t *
 	    else
 		sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (ydim);
 
 	    p1.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.x) * sf);
 	    p1.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.y) * sf);
 	    p2.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.x) * sf);
 	    p2.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.y) * sf);
 
+	    /* cairo_matrix_scale does a pre-scale, we want a post-scale */
 	    cairo_matrix_init_scale (&scale, sf, sf);
 	    cairo_matrix_multiply (&matrix, &matrix, &scale);
 	}
 	else
 	{
 	    p1.x = _cairo_fixed_to_16_16 (linear->p1.x);
 	    p1.y = _cairo_fixed_to_16_16 (linear->p1.y);
 	    p2.x = _cairo_fixed_to_16_16 (linear->p2.x);
@@ -1205,19 +1212,19 @@ static pixman_image_t *
 	    inv = matrix;
 	    status = cairo_matrix_invert (&inv);
 	    assert (status == CAIRO_STATUS_SUCCESS);
 
 	    x = _cairo_lround (inv.x0 / 2);
 	    y = _cairo_lround (inv.y0 / 2);
 
 	    max_x = PIXMAN_MAX_INT - 1 - fabs (extents->x + extents->width);
-	    x = x > max_x ? max_x : (x < -max_x ? -max_x : x);
+	    x = clamp(x, -max_x, max_x);
 	    max_y = PIXMAN_MAX_INT - 1 - fabs (extents->y + extents->height);
-	    y = y > max_y ? max_y : (y < -max_y ? -max_y : y);
+	    y = clamp(y, -max_y, max_y);
 
 	    tx = -x;
 	    ty = -y;
 	    cairo_matrix_init_translate (&inv, x, y);
 	    cairo_matrix_multiply (&m, &inv, &matrix);
 	    _cairo_matrix_to_pixman_matrix (&m, &pixman_transform,
 					    extents->x + extents->width/2.,
 					    extents->y + extents->height/2.);
--- a/gfx/cairo/cairo/src/cairo-pattern.c
+++ b/gfx/cairo/cairo/src/cairo-pattern.c
@@ -1394,16 +1394,17 @@ static cairo_int_status_t
 	    else
 		sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (ydim);
 
 	    p1.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.x) * sf);
 	    p1.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.y) * sf);
 	    p2.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.x) * sf);
 	    p2.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.y) * sf);
 
+	    /* cairo_matrix_scale does a pre-scale, we want a post-scale */
 	    cairo_matrix_init_scale (&scale, sf, sf);
 	    cairo_matrix_multiply (&matrix, &matrix, &scale);
 	}
 	else
 	{
 	    p1.x = _cairo_fixed_to_16_16 (linear->p1.x);
 	    p1.y = _cairo_fixed_to_16_16 (linear->p1.y);
 	    p2.x = _cairo_fixed_to_16_16 (linear->p2.x);
--- a/gfx/layers/RotatedBuffer.cpp
+++ b/gfx/layers/RotatedBuffer.cpp
@@ -224,17 +224,17 @@ RotatedContentBuffer::DrawTo(PaintedLaye
       (ToData(aLayer)->GetClipToVisibleRegion() &&
        !aLayer->GetVisibleRegion().Contains(BufferRect())) ||
       IsClippingCheap(aTarget, aLayer->GetEffectiveVisibleRegion())) {
     // We don't want to draw invalid stuff, so we need to clip. Might as
     // well clip to the smallest area possible --- the visible region.
     // Bug 599189 if there is a non-integer-translation transform in aTarget,
     // we might sample pixels outside GetEffectiveVisibleRegion(), which is wrong
     // and may cause gray lines.
-    gfxUtils::ClipToRegionSnapped(aTarget, aLayer->GetEffectiveVisibleRegion());
+    gfxUtils::ClipToRegion(aTarget, aLayer->GetEffectiveVisibleRegion());
     clipped = true;
   }
 
   DrawBufferWithRotation(aTarget, BUFFER_BLACK, aOpacity, aOp, aMask, aMaskTransform);
   if (clipped) {
     aTarget->PopClip();
   }
 }
--- a/gfx/layers/TiledLayerBuffer.h
+++ b/gfx/layers/TiledLayerBuffer.h
@@ -217,18 +217,21 @@ class TiledLayerComposer
 {
 public:
   /**
    * Update the current retained layer with the updated layer data.
    * It is expected that the tiles described by aTiledDescriptor are all in the
    * ReadLock state, so that the locks can be adopted when recreating a
    * ClientTiledLayerBuffer locally. This lock will be retained until the buffer
    * has completed uploading.
+   *
+   * Returns false if a deserialization error happened, in which case we will
+   * have to kill the child process.
    */
-  virtual void UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
+  virtual bool UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
                                    const SurfaceDescriptorTiles& aTiledDescriptor) = 0;
 
   /**
    * If some part of the buffer is being rendered at a lower precision, this
    * returns that region. If it is not, an empty region will be returned.
    */
   virtual const nsIntRegion& GetValidLowPrecisionRegion() const = 0;
 
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -1573,18 +1573,19 @@ nsEventStatus AsyncPanZoomController::On
   mY.UpdateWithTouchAtDevicePoint(aEvent.mPanStartPoint.y, aEvent.mTime);
 
   ScreenPoint panDisplacement = aEvent.mPanDisplacement;
   ToGlobalScreenCoordinates(&panDisplacement, aEvent.mPanStartPoint);
   HandlePanningUpdate(panDisplacement);
 
   // TODO: Handle pan events sent without pan begin / pan end events properly.
   if (mPanGestureState) {
+    ScreenPoint panDistance(fabs(panDisplacement.x), fabs(panDisplacement.y));
     OverscrollHandoffState handoffState(
-        *mPanGestureState->GetOverscrollHandoffChain(), panDisplacement);
+        *mPanGestureState->GetOverscrollHandoffChain(), panDistance);
     CallDispatchScroll(aEvent.mPanStartPoint, aEvent.mPanStartPoint + aEvent.mPanDisplacement,
                        handoffState);
   }
 
   return nsEventStatus_eConsumeNoDefault;
 }
 
 nsEventStatus AsyncPanZoomController::OnPanEnd(const PanGestureInput& aEvent) {
--- a/gfx/layers/apz/src/OverscrollHandoffState.h
+++ b/gfx/layers/apz/src/OverscrollHandoffState.h
@@ -126,16 +126,17 @@ struct OverscrollHandoffState {
 
   // The index of the APZC in the chain that we are currently giving scroll to.
   // This is non-const to indicate that this changes over the course of handoff.
   uint32_t mChainIndex;
 
   // The total distance since touch-start of the pan that triggered the
   // handoff. This is const to indicate that it does not change over the
   // course of handoff.
+  // The x/y components of this are non-negative.
   const ScreenPoint mPanDistance;
 };
 // Don't pollute other files with this macro for now.
 #undef NS_INLINE_DECL_THREADSAFE_MUTABLE_REFCOUNTING
 
 }
 }
 
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -25,43 +25,45 @@ namespace mozilla {
 using namespace gfx;
 namespace layers {
 
 class Layer;
 
 TiledLayerBufferComposite::TiledLayerBufferComposite()
   : mFrameResolution(1.0)
   , mHasDoubleBufferedTiles(false)
-  , mUninitialized(true)
+  , mIsValid(false)
 {}
 
 /* static */ void
 TiledLayerBufferComposite::RecycleCallback(TextureHost* textureHost, void* aClosure)
 {
   textureHost->CompositorRecycle();
 }
 
 TiledLayerBufferComposite::TiledLayerBufferComposite(ISurfaceAllocator* aAllocator,
                                                      const SurfaceDescriptorTiles& aDescriptor,
                                                      const nsIntRegion& aOldPaintedRegion)
 {
-  mUninitialized = false;
+  mIsValid = true;
   mHasDoubleBufferedTiles = false;
   mValidRegion = aDescriptor.validRegion();
   mPaintedRegion = aDescriptor.paintedRegion();
   mRetainedWidth = aDescriptor.retainedWidth();
   mRetainedHeight = aDescriptor.retainedHeight();
   mResolution = aDescriptor.resolution();
   mFrameResolution = CSSToParentLayerScale(aDescriptor.frameResolution());
 
   // Combine any valid content that wasn't already uploaded
   nsIntRegion oldPaintedRegion(aOldPaintedRegion);
   oldPaintedRegion.And(oldPaintedRegion, mValidRegion);
   mPaintedRegion.Or(mPaintedRegion, oldPaintedRegion);
 
+  bool isSameProcess = aAllocator->IsSameProcess();
+
   const InfallibleTArray<TileDescriptor>& tiles = aDescriptor.tiles();
   for(size_t i = 0; i < tiles.Length(); i++) {
     RefPtr<TextureHost> texture;
     RefPtr<TextureHost> textureOnWhite;
     const TileDescriptor& tileDesc = tiles[i];
     switch (tileDesc.type()) {
       case TileDescriptor::TTexturedTileDescriptor : {
         texture = TextureHost::AsTextureHost(tileDesc.get_TexturedTileDescriptor().textureParent());
@@ -69,16 +71,27 @@ TiledLayerBufferComposite::TiledLayerBuf
         if (onWhite.type() == MaybeTexture::TPTextureParent) {
           textureOnWhite = TextureHost::AsTextureHost(onWhite.get_PTextureParent());
         }
         const TileLock& ipcLock = tileDesc.get_TexturedTileDescriptor().sharedLock();
         nsRefPtr<gfxSharedReadLock> sharedLock;
         if (ipcLock.type() == TileLock::TShmemSection) {
           sharedLock = gfxShmSharedReadLock::Open(aAllocator, ipcLock.get_ShmemSection());
         } else {
+          if (!isSameProcess) {
+            // Trying to use a memory based lock instead of a shmem based one in
+            // the cross-process case is a bad security violation.
+            NS_ERROR("A client process may be trying to peek at the host's address space!");
+            // This tells the TiledContentHost that deserialization failed so that
+            // it can propagate the error.
+            mIsValid = false;
+
+            mRetainedTiles.Clear();
+            return;
+          }
           sharedLock = reinterpret_cast<gfxMemorySharedReadLock*>(ipcLock.get_uintptr_t());
           if (sharedLock) {
             // The corresponding AddRef is in TiledClient::GetTileDescriptor
             sharedLock->Release();
           }
         }
 
         mRetainedTiles.AppendElement(TileHost(sharedLock, texture, textureOnWhite));
@@ -282,17 +295,17 @@ TiledContentHost::Detach(Layer* aLayer,
     mTiledBuffer = TiledLayerBufferComposite();
     mLowPrecisionTiledBuffer = TiledLayerBufferComposite();
     mOldTiledBuffer = TiledLayerBufferComposite();
     mOldLowPrecisionTiledBuffer = TiledLayerBufferComposite();
   }
   CompositableHost::Detach(aLayer,aFlags);
 }
 
-void
+bool
 TiledContentHost::UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
                                       const SurfaceDescriptorTiles& aTiledDescriptor)
 {
   if (aTiledDescriptor.resolution() < 1) {
     if (mPendingLowPrecisionUpload) {
       mLowPrecisionTiledBuffer.ReadUnlock();
     } else {
       mPendingLowPrecisionUpload = true;
@@ -305,29 +318,46 @@ TiledContentHost::UseTiledLayerBuffer(IS
       if (mLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
         mOldLowPrecisionTiledBuffer = mLowPrecisionTiledBuffer;
         mOldLowPrecisionTiledBuffer.ReleaseTextureHosts();
       }
     }
     mLowPrecisionTiledBuffer =
       TiledLayerBufferComposite(aAllocator, aTiledDescriptor,
                                 mLowPrecisionTiledBuffer.GetPaintedRegion());
+    if (!mLowPrecisionTiledBuffer.IsValid()) {
+      // Something bad happened. Stop here, return false (kills the child process),
+      // and do as little work as possible on the received data as it appears
+      // to be corrupted.
+      mPendingLowPrecisionUpload = false;
+      mPendingUpload = false;
+      return false;
+    }
   } else {
     if (mPendingUpload) {
       mTiledBuffer.ReadUnlock();
     } else {
       mPendingUpload = true;
       if (mTiledBuffer.HasDoubleBufferedTiles()) {
         mOldTiledBuffer = mTiledBuffer;
         mOldTiledBuffer.ReleaseTextureHosts();
       }
     }
     mTiledBuffer = TiledLayerBufferComposite(aAllocator, aTiledDescriptor,
                                              mTiledBuffer.GetPaintedRegion());
+    if (!mTiledBuffer.IsValid()) {
+      // Something bad happened. Stop here, return false (kills the child process),
+      // and do as little work as possible on the received data as it appears
+      // to be corrupted.
+      mPendingLowPrecisionUpload = false;
+      mPendingUpload = false;
+      return false;
+    }
   }
+  return true;
 }
 
 void
 TiledContentHost::Composite(EffectChain& aEffectChain,
                             float aOpacity,
                             const gfx::Matrix4x4& aTransform,
                             const gfx::Filter& aFilter,
                             const gfx::Rect& aClipRect,
--- a/gfx/layers/composite/TiledContentHost.h
+++ b/gfx/layers/composite/TiledContentHost.h
@@ -132,17 +132,17 @@ public:
    * don't have an internal buffer, this is unlikely to actually do anything.
    */
   void Upload();
 
   void SetCompositor(Compositor* aCompositor);
 
   bool HasDoubleBufferedTiles() { return mHasDoubleBufferedTiles; }
 
-  bool IsValid() const { return !mUninitialized; }
+  bool IsValid() const { return mIsValid; }
 
 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
   virtual void SetReleaseFence(const android::sp<android::Fence>& aReleaseFence);
 #endif
 
   // Recycle callback for TextureHost.
   // Used when TiledContentClient is present in client side.
   static void RecycleCallback(TextureHost* textureHost, void* aClosure);
@@ -157,17 +157,17 @@ protected:
 
   void SwapTiles(TileHost& aTileA, TileHost& aTileB) { std::swap(aTileA, aTileB); }
 
   void UnlockTile(TileHost aTile) {}
   void PostValidate(const nsIntRegion& aPaintRegion) {}
 private:
   CSSToParentLayerScale mFrameResolution;
   bool mHasDoubleBufferedTiles;
-  bool mUninitialized;
+  bool mIsValid;
 };
 
 /**
  * ContentHost for tiled PaintedLayers. Since tiled layers are special snow
  * flakes, we have a unique update process. All the textures that back the
  * tiles are added in the usual way, but Updated is called on the host side
  * in response to a message that describes the transaction for every tile.
  * Composition happens in the normal way.
@@ -210,18 +210,18 @@ public:
     return false;
   }
 
   const nsIntRegion& GetValidLowPrecisionRegion() const
   {
     return mLowPrecisionTiledBuffer.GetValidRegion();
   }
 
-  void UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
-                           const SurfaceDescriptorTiles& aTiledDescriptor);
+  virtual bool UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
+                                   const SurfaceDescriptorTiles& aTiledDescriptor) MOZ_OVERRIDE;
 
   void Composite(EffectChain& aEffectChain,
                  float aOpacity,
                  const gfx::Matrix4x4& aTransform,
                  const gfx::Filter& aFilter,
                  const gfx::Rect& aClipRect,
                  const nsIntRegion* aVisibleRegion = nullptr);
 
--- a/gfx/layers/ipc/CompositableTransactionParent.cpp
+++ b/gfx/layers/ipc/CompositableTransactionParent.cpp
@@ -143,17 +143,20 @@ CompositableParentManager::ReceiveCompos
       MOZ_LAYERS_LOG(("[ParentSide] Paint TiledLayerBuffer"));
       const OpUseTiledLayerBuffer& op = aEdit.get_OpUseTiledLayerBuffer();
       CompositableHost* compositable = AsCompositable(op);
 
       TiledLayerComposer* tileComposer = compositable->AsTiledLayerComposer();
       NS_ASSERTION(tileComposer, "compositable is not a tile composer");
 
       const SurfaceDescriptorTiles& tileDesc = op.tileLayerDescriptor();
-      tileComposer->UseTiledLayerBuffer(this, tileDesc);
+      bool success = tileComposer->UseTiledLayerBuffer(this, tileDesc);
+      if (!success) {
+        return false;
+      }
       break;
     }
     case CompositableOperation::TOpRemoveTexture: {
       const OpRemoveTexture& op = aEdit.get_OpRemoveTexture();
       CompositableHost* compositable = AsCompositable(op);
       RefPtr<TextureHost> tex = TextureHost::AsTextureHost(op.textureParent());
 
       MOZ_ASSERT(tex.get());
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -680,26 +680,26 @@ CompositorParent::CompositeToTarget(Draw
     if (mCompositionManager->ReadyForCompose()) {
       mForceCompositionTask->Cancel();
       mForceCompositionTask = nullptr;
     } else {
       return;
     }
   }
 
+  mCompositionManager->ComputeRotation();
+
   TimeStamp time = mIsTesting ? mTestTime : mLastCompose;
   bool requestNextFrame = mCompositionManager->TransformShadowTree(time);
   if (requestNextFrame) {
     ScheduleComposition();
   }
 
   RenderTraceLayers(mLayerManager->GetRoot(), "0000");
 
-  mCompositionManager->ComputeRotation();
-
 #ifdef MOZ_DUMP_PAINTING
   static bool gDumpCompositorTree = false;
   if (gDumpCompositorTree) {
     printf_stderr("Painting --- compositing layer tree:\n");
     mLayerManager->Dump();
   }
 #endif
   mLayerManager->SetDebugOverlayWantsNextFrame(false);
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -660,95 +660,64 @@ static void
 ClipToRegionInternal(gfxContext* aContext, const nsIntRegion& aRegion,
                      bool aSnap)
 {
   PathFromRegionInternal(aContext, aRegion, aSnap);
   aContext->Clip();
 }
 
 static TemporaryRef<Path>
-PathFromRegionInternal(DrawTarget* aTarget, const nsIntRegion& aRegion,
-                       bool aSnap)
+PathFromRegionInternal(DrawTarget* aTarget, const nsIntRegion& aRegion)
 {
-  Matrix mat = aTarget->GetTransform();
-  const gfxFloat epsilon = 0.000001;
-#define WITHIN_E(a,b) (fabs((a)-(b)) < epsilon)
-  // We're essentially duplicating the logic in UserToDevicePixelSnapped here.
-  bool shouldNotSnap = !aSnap || (WITHIN_E(mat._11,1.0) &&
-                                  WITHIN_E(mat._22,1.0) &&
-                                  WITHIN_E(mat._12,0.0) &&
-                                  WITHIN_E(mat._21,0.0));
-#undef WITHIN_E
-
   RefPtr<PathBuilder> pb = aTarget->CreatePathBuilder();
   nsIntRegionRectIterator iter(aRegion);
 
   const nsIntRect* r;
-  if (shouldNotSnap) {
-    while ((r = iter.Next()) != nullptr) {
-      pb->MoveTo(Point(r->x, r->y));
-      pb->LineTo(Point(r->XMost(), r->y));
-      pb->LineTo(Point(r->XMost(), r->YMost()));
-      pb->LineTo(Point(r->x, r->YMost()));
-      pb->Close();
-    }
-  } else {
-    while ((r = iter.Next()) != nullptr) {
-      Rect rect(r->x, r->y, r->width, r->height);
-
-      rect.Round();
-      pb->MoveTo(rect.TopLeft());
-      pb->LineTo(rect.TopRight());
-      pb->LineTo(rect.BottomRight());
-      pb->LineTo(rect.BottomLeft());
-      pb->Close();
-    }
+  while ((r = iter.Next()) != nullptr) {
+    pb->MoveTo(Point(r->x, r->y));
+    pb->LineTo(Point(r->XMost(), r->y));
+    pb->LineTo(Point(r->XMost(), r->YMost()));
+    pb->LineTo(Point(r->x, r->YMost()));
+    pb->Close();
   }
   RefPtr<Path> path = pb->Finish();
   return path;
 }
 
 static void
-ClipToRegionInternal(DrawTarget* aTarget, const nsIntRegion& aRegion,
-                     bool aSnap)
+ClipToRegionInternal(DrawTarget* aTarget, const nsIntRegion& aRegion)
 {
   if (!aRegion.IsComplex()) {
     nsIntRect rect = aRegion.GetBounds();
     aTarget->PushClipRect(Rect(rect.x, rect.y, rect.width, rect.height));
     return;
   }
 
-  RefPtr<Path> path = PathFromRegionInternal(aTarget, aRegion, aSnap);
+  RefPtr<Path> path = PathFromRegionInternal(aTarget, aRegion);
   aTarget->PushClip(path);
 }
 
 /*static*/ void
 gfxUtils::ClipToRegion(gfxContext* aContext, const nsIntRegion& aRegion)
 {
   ClipToRegionInternal(aContext, aRegion, false);
 }
 
 /*static*/ void
 gfxUtils::ClipToRegion(DrawTarget* aTarget, const nsIntRegion& aRegion)
 {
-  ClipToRegionInternal(aTarget, aRegion, false);
+  ClipToRegionInternal(aTarget, aRegion);
 }
 
 /*static*/ void
 gfxUtils::ClipToRegionSnapped(gfxContext* aContext, const nsIntRegion& aRegion)
 {
   ClipToRegionInternal(aContext, aRegion, true);
 }
 
-/*static*/ void
-gfxUtils::ClipToRegionSnapped(DrawTarget* aTarget, const nsIntRegion& aRegion)
-{
-  ClipToRegionInternal(aTarget, aRegion, true);
-}
-
 /*static*/ gfxFloat
 gfxUtils::ClampToScaleFactor(gfxFloat aVal)
 {
   // Arbitary scale factor limitation. We can increase this
   // for better scaling performance at the cost of worse
   // quality.
   static const gfxFloat kScaleResolution = 2;
 
--- a/gfx/thebes/gfxUtils.h
+++ b/gfx/thebes/gfxUtils.h
@@ -95,21 +95,16 @@ public:
     static void ClipToRegion(mozilla::gfx::DrawTarget* aTarget, const nsIntRegion& aRegion);
 
     /**
      * Clip aContext to the region aRegion, snapping the rectangles.
      */
     static void ClipToRegionSnapped(gfxContext* aContext, const nsIntRegion& aRegion);
 
     /**
-     * Clip aTarget to the region aRegion, snapping the rectangles.
-     */
-    static void ClipToRegionSnapped(mozilla::gfx::DrawTarget* aTarget, const nsIntRegion& aRegion);
-
-    /**
      * Create a path consisting of rectangles in |aRegion|.
      */
     static void PathFromRegion(gfxContext* aContext, const nsIntRegion& aRegion);
 
     /**
      * Create a path consisting of rectangles in |aRegion|, snapping the rectangles.
      */
     static void PathFromRegionSnapped(gfxContext* aContext, const nsIntRegion& aRegion);
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -1532,28 +1532,32 @@ gfxWindowsPlatform::InitD3D11Devices()
                          D3D11_CREATE_DEVICE_BGRA_SUPPORT,
                          featureLevels.Elements(), featureLevels.Length(),
                          D3D11_SDK_VERSION, byRef(mD3D11Device), nullptr, nullptr);
 
   if (FAILED(hr)) {
     return;
   }
 
+  mD3D11Device->SetExceptionMode(0);
+
 #ifdef USE_D2D1_1
   if (Factory::SupportsD2D1()) {
     hr = d3d11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr,
                            D3D11_CREATE_DEVICE_BGRA_SUPPORT,
                            featureLevels.Elements(), featureLevels.Length(),
                            D3D11_SDK_VERSION, byRef(mD3D11ContentDevice), nullptr, nullptr);
 
     if (FAILED(hr)) {
       mD3D11Device = nullptr;
       return;
     }
 
+    mD3D11ContentDevice->SetExceptionMode(0);
+
     Factory::SetDirect3D11Device(mD3D11ContentDevice);
   }
 #endif
 
   // We leak these everywhere and we need them our entire runtime anyway, let's
   // leak it here as well.
   d3d11Module.disown();
 }
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -1029,25 +1029,29 @@ obj_isExtensible(JSContext *cx, unsigned
         RootedObject obj(cx, &args.get(0).toObject());
         if (!JSObject::isExtensible(cx, obj, &extensible))
             return false;
     }
     args.rval().setBoolean(extensible);
     return true;
 }
 
+// ES6 draft rev27 (2014/08/24) 19.1.2.15 Object.preventExtensions(O)
 static bool
 obj_preventExtensions(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    RootedObject obj(cx);
-    if (!GetFirstArgumentAsObject(cx, args, "Object.preventExtensions", &obj))
-        return false;
+    args.rval().set(args.get(0));
 
-    args.rval().setObject(*obj);
+    // step 1
+    if (!args.get(0).isObject())
+        return true;
+
+    // steps 2-5
+    RootedObject obj(cx, &args.get(0).toObject());
 
     return JSObject::preventExtensions(cx, obj);
 }
 
 static bool
 obj_freeze(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
--- a/js/src/jit-test/tests/asm.js/bug885976.js
+++ b/js/src/jit-test/tests/asm.js/bug885976.js
@@ -3,10 +3,10 @@ function test(stdlib, foreign) {
     "use asm"
     var ff = foreign.ff
     function f(y) {
         y = +y;
         ff(0);
     }
     return f;
 };
-f = test(this, {ff: Object.preventExtensions});
+f = test(this, {ff: Object.defineProperty});
 f();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/asm.js/simd-fbirds.js
@@ -0,0 +1,210 @@
+/* -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 ; js-indent-level : 2 ; js-curly-indent-offset: 0 -*- */
+/* vim: set ts=4 et sw=4 tw=80: */
+
+// Author: Peter Jensen
+
+load(libdir + "asm.js");
+if (!isSimdAvailable() || typeof SIMD === 'undefined') {
+    print("won't run tests as simd extensions aren't activated yet");
+    quit(0);
+}
+
+const NUM_BIRDS = 30;
+const NUM_UPDATES = 20;
+const ACCEL_DATA_STEPS = 30;
+
+var buffer = new ArrayBuffer(0x200000);
+var bufferF32 = new Float32Array(buffer);
+
+var actualBirds = 0;
+
+function init() {
+    actualBirds = 0;
+    // Make it a power of two, for quick modulo wrapping.
+    var accelDataValues = [10.0, 9.5, 9.0, 8.0, 7.0, 6.0, 5.5, 5.0, 5.0, 5.0, 5.5, 6.0, 7.0, 8.0, 9.0, 10.0];
+    accelDataValues = accelDataValues.map(function(v) { return 50*v; });
+    var accelDataValuesLength = accelDataValues.length;
+    assertEq(accelDataValuesLength, 16); // Hard coded in the asm.js module
+    for (i = 0; i < accelDataValuesLength; i++)
+        bufferF32[i + NUM_BIRDS * 2] = accelDataValues[i];
+}
+
+function addBird(pos, vel) {
+    bufferF32[actualBirds] = pos;
+    bufferF32[actualBirds + NUM_BIRDS] = vel;
+    actualBirds++;
+    return actualBirds - 1;
+}
+
+function getActualBirds() {
+    return actualBirds;
+}
+
+var code = `
+    "use asm";
+    var toF = global.Math.fround;
+    var f32 = new global.Float32Array(buffer);
+    const maxBirds = 100000;
+    const maxBirdsx4 = 400000;
+    const maxBirdsx4Plus4 = 400004;
+    const maxBirdsx4Plus8 = 400008;
+    const maxBirdsx4Plus12 = 400012;
+    const maxBirdsx8 = 800000;
+    const accelMask = 0x3c;
+    const mk2 = 0x000ffffc;
+
+    const getMaxPos = 1000.0;
+    const getAccelDataSteps = imp.accelDataSteps | 0;
+    var getActualBirds = imp.getActualBirds;
+
+    var i4 = global.SIMD.int32x4;
+    var f4 = global.SIMD.float32x4;
+    var i4add = i4.add;
+    var i4and = i4.and;
+    var f4select = f4.select;
+    var f4add = f4.add;
+    var f4sub = f4.sub;
+    var f4mul = f4.mul;
+    var f4greaterThan = f4.greaterThan;
+    var f4splat = f4.splat;
+
+    const zerox4 = f4(0.0,0.0,0.0,0.0);
+
+    function declareHeapSize() {
+        f32[0x0007ffff] = toF(0.0);
+    }
+
+    function update(timeDelta) {
+        timeDelta = toF(timeDelta);
+        //      var steps               = Math.ceil(timeDelta/accelData.interval);
+        var steps = 0;
+        var subTimeDelta = toF(0.0);
+        var actualBirds = 0;
+        var maxPos = toF(0.0);
+        var maxPosx4 = f4(0.0,0.0,0.0,0.0);
+        var subTimeDeltax4  = f4(0.0,0.0,0.0,0.0);
+        var subTimeDeltaSquaredx4 = f4(0.0,0.0,0.0,0.0);
+        var point5x4 = f4(0.5, 0.5, 0.5, 0.5);
+        var i = 0;
+        var len = 0;
+        var accelIndex = 0;
+        var newPosx4 = f4(0.0,0.0,0.0,0.0);
+        var newVelx4 = f4(0.0,0.0,0.0,0.0);
+        var accel = toF(0.0);
+        var accelx4 = f4(0.0,0.0,0.0,0.0);
+        var a = 0;
+        var posDeltax4 = f4(0.0,0.0,0.0,0.0);
+        var cmpx4 = i4(0,0,0,0);
+        var newVelTruex4 = f4(0.0,0.0,0.0,0.0);
+
+        steps = getAccelDataSteps | 0;
+        subTimeDelta = toF(toF(timeDelta / toF(steps | 0)) / toF(1000.0));
+        actualBirds = getActualBirds() | 0;
+        maxPos = toF(+getMaxPos);
+        maxPosx4 = f4splat(maxPos);
+        subTimeDeltax4 = f4splat(subTimeDelta);
+        subTimeDeltaSquaredx4 = f4mul(subTimeDeltax4, subTimeDeltax4);
+
+        len = ((actualBirds + 3) >> 2) << 4;
+
+        for (i = 0; (i | 0) < (len | 0); i = (i + 16) | 0) {
+            accelIndex = 0;
+            // Work around unimplemented Float32x4Array
+            newPosx4 = f4(toF(f32[(i & mk2) >> 2]),
+                    toF(f32[(i & mk2) + 4 >> 2]),
+                    toF(f32[(i & mk2) + 8 >> 2]),
+                    toF(f32[(i & mk2) + 12 >> 2]));
+            newVelx4 = f4(toF(f32[(i & mk2) + maxBirdsx4 >> 2]),
+                    toF(f32[(i & mk2) + maxBirdsx4Plus4 >> 2]),
+                    toF(f32[(i & mk2) + maxBirdsx4Plus8 >> 2]),
+                    toF(f32[(i & mk2) + maxBirdsx4Plus12 >> 2]));
+            for (a = 0; (a | 0) < (steps | 0); a = (a + 1) | 0) {
+                accel = toF(f32[(accelIndex & accelMask) + maxBirdsx8 >> 2]);
+                accelx4 = f4splat(accel);
+                accelIndex = (accelIndex + 4) | 0;
+                posDeltax4 = f4mul(point5x4, f4mul(accelx4, subTimeDeltaSquaredx4));
+                posDeltax4 = f4add(posDeltax4, f4mul(newVelx4, subTimeDeltax4));
+                newPosx4 = f4add(newPosx4, posDeltax4);
+                newVelx4 = f4add(newVelx4, f4mul(accelx4, subTimeDeltax4));
+                cmpx4 = f4greaterThan(newPosx4, maxPosx4);
+
+                if (cmpx4.signMask) {
+                    // Work around unimplemented 'neg' operation, using 0 - x.
+                    newVelTruex4 = f4sub(zerox4, newVelx4);
+                    newVelx4 = f4select(cmpx4, newVelTruex4, newVelx4);
+                }
+            }
+            // Work around unimplemented Float32x4Array
+            f32[(i & mk2) >> 2] = newPosx4.x;
+            f32[(i & mk2) + 4 >> 2] = newPosx4.y;
+            f32[(i & mk2) + 8 >> 2] = newPosx4.z;
+            f32[(i & mk2) + 12 >> 2] = newPosx4.w;
+            f32[(i & mk2) + maxBirdsx4 >> 2] = newVelx4.x;
+            f32[(i & mk2) + maxBirdsx4Plus4 >> 2] = newVelx4.y;
+            f32[(i & mk2) + maxBirdsx4Plus8 >> 2] = newVelx4.z;
+            f32[(i & mk2) + maxBirdsx4Plus12 >> 2] = newVelx4.w;
+        }
+    }
+
+    return update;
+`
+
+var ffi = {
+    getActualBirds,
+    accelDataSteps: ACCEL_DATA_STEPS
+};
+
+var fbirds = asmLink(asmCompile('global', 'imp', 'buffer', code), this, ffi, buffer);
+
+init();
+for (var i = 0; i < NUM_BIRDS; i++) {
+    addBird(i / 10, Math.exp(2, NUM_BIRDS - i));
+}
+
+var b = dateNow();
+for (var j = 0; j < NUM_UPDATES; j++) {
+    fbirds(16);
+}
+print(dateNow() - b);
+
+assertEq(bufferF32[0], 0);
+assertEq(bufferF32[1], 0.10000000149011612);
+assertEq(bufferF32[2], 0.20000000298023224);
+assertEq(bufferF32[3], 0.30000001192092896);
+assertEq(bufferF32[4], 0.4000000059604645);
+assertEq(bufferF32[5], 0.5);
+assertEq(bufferF32[6], 0.6000000238418579);
+assertEq(bufferF32[7], 0.699999988079071);
+assertEq(bufferF32[8], 0.800000011920929);
+assertEq(bufferF32[9], 0.8999999761581421);
+assertEq(bufferF32[10], 1);
+assertEq(bufferF32[11], 1.100000023841858);
+assertEq(bufferF32[12], 1.2000000476837158);
+assertEq(bufferF32[13], 1.2999999523162842);
+assertEq(bufferF32[14], 1.399999976158142);
+assertEq(bufferF32[15], 1.5);
+assertEq(bufferF32[16], 1.600000023841858);
+assertEq(bufferF32[17], 1.7000000476837158);
+assertEq(bufferF32[18], 1.7999999523162842);
+assertEq(bufferF32[19], 1.899999976158142);
+assertEq(bufferF32[20], 2);
+assertEq(bufferF32[21], 2.0999999046325684);
+assertEq(bufferF32[22], 2.200000047683716);
+assertEq(bufferF32[23], 2.299999952316284);
+assertEq(bufferF32[24], 2.4000000953674316);
+assertEq(bufferF32[25], 2.5);
+assertEq(bufferF32[26], 2.5999999046325684);
+assertEq(bufferF32[27], 2.700000047683716);
+assertEq(bufferF32[28], 2.799999952316284);
+assertEq(bufferF32[29], 2.9000000953674316);
+
+
+// Code used to generate the assertEq list above.
+function generateAssertList() {
+    var buf = '';
+    for (var k = 0; k < NUM_BIRDS; k++) {
+        buf += 'assertEq(bufferF32['+ k + '], ' + bufferF32[k] + ');\n';
+    }
+    print(buf);
+}
+//generateAssertList();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/asm.js/simd-mandelbrot.js
@@ -0,0 +1,1814 @@
+/* -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 ; js-indent-level : 2 ; js-curly-indent-offset: 0 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+
+// Mandelbrot using SIMD
+// Author: Peter Jensen, Intel Corporation
+
+load(libdir + "asm.js");
+if (!isSimdAvailable() || typeof SIMD === 'undefined') {
+    print("won't run tests as simd extensions aren't activated yet");
+    quit(0);
+}
+
+// global variables
+const MAX_ITERATIONS = 10;
+const DRAW_ITERATIONS = 10;
+
+const CANVAS_WIDTH = 20;
+const CANVAS_HEIGHT = 20;
+
+const LIMIT_SHOW = 20 * 20 * 4;
+
+// Asm.js module buffer.
+var buffer = new ArrayBuffer(16 * 1024 * 1024);
+var view = new Uint8Array(buffer);
+
+var moduleCode = `
+  "use asm"
+  var b8 = new global.Uint8Array(buffer);
+  var toF = global.Math.fround;
+  var i4 = global.SIMD.int32x4;
+  var f4 = global.SIMD.float32x4;
+  var i4add = i4.add;
+  var i4and = i4.and;
+  var f4add = f4.add;
+  var f4sub = f4.sub;
+  var f4mul = f4.mul;
+  var f4lessThanOrEqual = f4.lessThanOrEqual;
+  var f4splat = f4.splat;
+  var imul = global.Math.imul;
+  const one4 = i4(1,1,1,1), two4 = f4(2,2,2,2), four4 = f4(4,4,4,4);
+
+  const mk0 = 0x007fffff;
+
+  function declareHeapLength() {
+    b8[0x00ffffff] = 0;
+  }
+
+  function mapColorAndSetPixel (x, y, width, value, max_iterations) {
+    x = x | 0;
+    y = y | 0;
+    width = width | 0;
+    value = value | 0;
+    max_iterations = max_iterations | 0;
+
+    var rgb = 0, r = 0, g = 0, b = 0, index = 0;
+
+    index = (((imul((width >>> 0), (y >>> 0)) + x) | 0) * 4) | 0;
+    if ((value | 0) == (max_iterations | 0)) {
+      r = 0;
+      g = 0;
+      b = 0;
+    } else {
+      rgb = ~~toF(toF(toF(toF(value >>> 0) * toF(0xffff)) / toF(max_iterations >>> 0)) * toF(0xff));
+      r = rgb & 0xff;
+      g = (rgb >>> 8) & 0xff;
+      b = (rgb >>> 16) & 0xff;
+    }
+    b8[(index & mk0) >> 0] = r;
+    b8[(index & mk0) + 1 >> 0] = g;
+    b8[(index & mk0) + 2 >> 0] = b;
+    b8[(index & mk0) + 3 >> 0] = 255;
+  }
+
+  function mandelPixelX4 (xf, yf, yd, max_iterations) {
+    xf = toF(xf);
+    yf = toF(yf);
+    yd = toF(yd);
+    max_iterations = max_iterations | 0;
+    var c_re4  = f4(0,0,0,0), c_im4  = f4(0,0,0,0);
+    var z_re4  = f4(0,0,0,0), z_im4  = f4(0,0,0,0);
+    var count4 = i4(0,0,0,0);
+    var z_re24 = f4(0,0,0,0), z_im24 = f4(0,0,0,0);
+    var new_re4 = f4(0,0,0,0), new_im4 = f4(0,0,0,0);
+    var i = 0;
+    var mi4 = i4(0,0,0,0);
+
+    c_re4 = f4splat(xf);
+    c_im4 = f4(yf, toF(yd + yf), toF(yd + toF(yd + yf)), toF(yd + toF(yd + toF(yd + yf))));
+
+    z_re4  = c_re4;
+    z_im4  = c_im4;
+
+    for (i = 0; (i | 0) < (max_iterations | 0); i = (i + 1) | 0) {
+      z_re24 = f4mul(z_re4, z_re4);
+      z_im24 = f4mul(z_im4, z_im4);
+      mi4 = f4lessThanOrEqual(f4add(z_re24, z_im24), four4);
+      // If all 4 values are greater than 4.0, there's no reason to continue.
+      if ((mi4.signMask | 0) == 0x00)
+        break;
+
+      new_re4 = f4sub(z_re24, z_im24);
+      new_im4 = f4mul(f4mul(two4, z_re4), z_im4);
+      z_re4   = f4add(c_re4, new_re4);
+      z_im4   = f4add(c_im4, new_im4);
+      count4  = i4add(count4, i4and(mi4, one4));
+    }
+    return i4(count4);
+  }
+
+  function mandelColumnX4 (x, width, height, xf, yf, yd, max_iterations) {
+    x = x | 0;
+    width = width | 0;
+    height = height | 0;
+    xf = toF(xf);
+    yf = toF(yf);
+    yd = toF(yd);
+    max_iterations = max_iterations | 0;
+
+    var y = 0;
+    var ydx4 = toF(0);
+    var m4 = i4(0,0,0,0);
+
+    ydx4 = toF(yd * toF(4));
+    for (y = 0; (y | 0) < (height | 0); y = (y + 4) | 0) {
+      m4   = i4(mandelPixelX4(toF(xf), toF(yf), toF(yd), max_iterations));
+      mapColorAndSetPixel(x | 0, y | 0,   width, m4.x, max_iterations);
+      mapColorAndSetPixel(x | 0, (y + 1) | 0, width, m4.y, max_iterations);
+      mapColorAndSetPixel(x | 0, (y + 2) | 0, width, m4.z, max_iterations);
+      mapColorAndSetPixel(x | 0, (y + 3) | 0, width, m4.w, max_iterations);
+      yf = toF(yf + ydx4);
+    }
+  }
+
+  function mandel (width, height, xc, yc, scale, max_iterations) {
+    width = width | 0;
+    height = height | 0;
+    xc = toF(xc);
+    yc = toF(yc);
+    scale = toF(scale);
+    max_iterations = max_iterations | 0;
+
+    var x0 = toF(0), y0 = toF(0);
+    var xd = toF(0), yd = toF(0);
+    var xf = toF(0);
+    var x = 0;
+
+    x0 = toF(xc - toF(scale * toF(1.5)));
+    y0 = toF(yc - scale);
+    xd = toF(toF(scale * toF(3)) / toF(width >>> 0));
+    yd = toF(toF(scale * toF(2)) / toF(height >>> 0));
+    xf = x0;
+
+    for (x = 0; (x | 0) < (width | 0); x = (x + 1) | 0) {
+      mandelColumnX4(x, width, height, xf, y0, yd, max_iterations);
+      xf = toF(xf + xd);
+    }
+  }
+
+  return mandel;
+`;
+
+var FFI = {};
+var mandelbro = asmLink(asmCompile('global', 'ffi', 'buffer', moduleCode), this, FFI, buffer);
+
+function animateMandelbrot () {
+  var scale_start = 1.0;
+  var scale_end   = 0.0005;
+  var xc_start    = -0.5;
+  var yc_start    = 0.0;
+  var xc_end      = 0.0;
+  var yc_end      = 0.75;
+  var steps       = 200.0;
+  var scale_step  = (scale_end - scale_start)/steps;
+  var xc_step     = (xc_end - xc_start)/steps;
+  var yc_step     = (yc_end - yc_start)/steps;
+  var scale       = scale_start;
+  var xc          = xc_start;
+  var yc          = yc_start;
+  var i           = 0;
+  var now         = dateNow();
+
+  function draw1 () {
+    mandelbro(CANVAS_WIDTH, CANVAS_HEIGHT, xc, yc, scale, MAX_ITERATIONS);
+    if (scale < scale_end || scale > scale_start) {
+      scale_step = -scale_step;
+      xc_step = -xc_step;
+      yc_step = -yc_step;
+    }
+    scale += scale_step;
+    xc += xc_step;
+    yc += yc_step;
+    i++;
+  }
+
+  var b = dateNow();
+  for (var j = DRAW_ITERATIONS; j --> 0;)
+    draw1();
+  print(dateNow() - b);
+}
+
+animateMandelbrot();
+
+assertEq(view[0], 0, "0th value should be 0");
+assertEq(view[1], 0, "1th value should be 0");
+assertEq(view[2], 0, "2th value should be 0");
+assertEq(view[3], 255, "3th value should be 255");
+assertEq(view[4], 230, "4th value should be 230");
+assertEq(view[5], 127, "5th value should be 127");
+assertEq(view[6], 25, "6th value should be 25");
+assertEq(view[7], 255, "7th value should be 255");
+assertEq(view[8], 230, "8th value should be 230");
+assertEq(view[9], 127, "9th value should be 127");
+assertEq(view[10], 25, "10th value should be 25");
+assertEq(view[11], 255, "11th value should be 255");
+assertEq(view[12], 205, "12th value should be 205");
+assertEq(view[13], 255, "13th value should be 255");
+assertEq(view[14], 50, "14th value should be 50");
+assertEq(view[15], 255, "15th value should be 255");
+assertEq(view[16], 205, "16th value should be 205");
+assertEq(view[17], 255, "17th value should be 255");
+assertEq(view[18], 50, "18th value should be 50");
+assertEq(view[19], 255, "19th value should be 255");
+assertEq(view[20], 205, "20th value should be 205");
+assertEq(view[21], 255, "21th value should be 255");
+assertEq(view[22], 50, "22th value should be 50");
+assertEq(view[23], 255, "23th value should be 255");
+assertEq(view[24], 205, "24th value should be 205");
+assertEq(view[25], 255, "25th value should be 255");
+assertEq(view[26], 50, "26th value should be 50");
+assertEq(view[27], 255, "27th value should be 255");
+assertEq(view[28], 205, "28th value should be 205");
+assertEq(view[29], 255, "29th value should be 255");
+assertEq(view[30], 50, "30th value should be 50");
+assertEq(view[31], 255, "31th value should be 255");
+assertEq(view[32], 179, "32th value should be 179");
+assertEq(view[33], 127, "33th value should be 127");
+assertEq(view[34], 76, "34th value should be 76");
+assertEq(view[35], 255, "35th value should be 255");
+assertEq(view[36], 179, "36th value should be 179");
+assertEq(view[37], 127, "37th value should be 127");
+assertEq(view[38], 76, "38th value should be 76");
+assertEq(view[39], 255, "39th value should be 255");
+assertEq(view[40], 179, "40th value should be 179");
+assertEq(view[41], 127, "41th value should be 127");
+assertEq(view[42], 76, "42th value should be 76");
+assertEq(view[43], 255, "43th value should be 255");
+assertEq(view[44], 154, "44th value should be 154");
+assertEq(view[45], 255, "45th value should be 255");
+assertEq(view[46], 101, "46th value should be 101");
+assertEq(view[47], 255, "47th value should be 255");
+assertEq(view[48], 78, "48th value should be 78");
+assertEq(view[49], 127, "49th value should be 127");
+assertEq(view[50], 178, "50th value should be 178");
+assertEq(view[51], 255, "51th value should be 255");
+assertEq(view[52], 52, "52th value should be 52");
+assertEq(view[53], 255, "53th value should be 255");
+assertEq(view[54], 203, "54th value should be 203");
+assertEq(view[55], 255, "55th value should be 255");
+assertEq(view[56], 154, "56th value should be 154");
+assertEq(view[57], 255, "57th value should be 255");
+assertEq(view[58], 101, "58th value should be 101");
+assertEq(view[59], 255, "59th value should be 255");
+assertEq(view[60], 179, "60th value should be 179");
+assertEq(view[61], 127, "61th value should be 127");
+assertEq(view[62], 76, "62th value should be 76");
+assertEq(view[63], 255, "63th value should be 255");
+assertEq(view[64], 205, "64th value should be 205");
+assertEq(view[65], 255, "65th value should be 255");
+assertEq(view[66], 50, "66th value should be 50");
+assertEq(view[67], 255, "67th value should be 255");
+assertEq(view[68], 205, "68th value should be 205");
+assertEq(view[69], 255, "69th value should be 255");
+assertEq(view[70], 50, "70th value should be 50");
+assertEq(view[71], 255, "71th value should be 255");
+assertEq(view[72], 230, "72th value should be 230");
+assertEq(view[73], 127, "73th value should be 127");
+assertEq(view[74], 25, "74th value should be 25");
+assertEq(view[75], 255, "75th value should be 255");
+assertEq(view[76], 230, "76th value should be 230");
+assertEq(view[77], 127, "77th value should be 127");
+assertEq(view[78], 25, "78th value should be 25");
+assertEq(view[79], 255, "79th value should be 255");
+assertEq(view[80], 0, "80th value should be 0");
+assertEq(view[81], 0, "81th value should be 0");
+assertEq(view[82], 0, "82th value should be 0");
+assertEq(view[83], 255, "83th value should be 255");
+assertEq(view[84], 230, "84th value should be 230");
+assertEq(view[85], 127, "85th value should be 127");
+assertEq(view[86], 25, "86th value should be 25");
+assertEq(view[87], 255, "87th value should be 255");
+assertEq(view[88], 205, "88th value should be 205");
+assertEq(view[89], 255, "89th value should be 255");
+assertEq(view[90], 50, "90th value should be 50");
+assertEq(view[91], 255, "91th value should be 255");
+assertEq(view[92], 205, "92th value should be 205");
+assertEq(view[93], 255, "93th value should be 255");
+assertEq(view[94], 50, "94th value should be 50");
+assertEq(view[95], 255, "95th value should be 255");
+assertEq(view[96], 205, "96th value should be 205");
+assertEq(view[97], 255, "97th value should be 255");
+assertEq(view[98], 50, "98th value should be 50");
+assertEq(view[99], 255, "99th value should be 255");
+assertEq(view[100], 205, "100th value should be 205");
+assertEq(view[101], 255, "101th value should be 255");
+assertEq(view[102], 50, "102th value should be 50");
+assertEq(view[103], 255, "103th value should be 255");
+assertEq(view[104], 205, "104th value should be 205");
+assertEq(view[105], 255, "105th value should be 255");
+assertEq(view[106], 50, "106th value should be 50");
+assertEq(view[107], 255, "107th value should be 255");
+assertEq(view[108], 205, "108th value should be 205");
+assertEq(view[109], 255, "109th value should be 255");
+assertEq(view[110], 50, "110th value should be 50");
+assertEq(view[111], 255, "111th value should be 255");
+assertEq(view[112], 179, "112th value should be 179");
+assertEq(view[113], 127, "113th value should be 127");
+assertEq(view[114], 76, "114th value should be 76");
+assertEq(view[115], 255, "115th value should be 255");
+assertEq(view[116], 179, "116th value should be 179");
+assertEq(view[117], 127, "117th value should be 127");
+assertEq(view[118], 76, "118th value should be 76");
+assertEq(view[119], 255, "119th value should be 255");
+assertEq(view[120], 154, "120th value should be 154");
+assertEq(view[121], 255, "121th value should be 255");
+assertEq(view[122], 101, "122th value should be 101");
+assertEq(view[123], 255, "123th value should be 255");
+assertEq(view[124], 103, "124th value should be 103");
+assertEq(view[125], 255, "125th value should be 255");
+assertEq(view[126], 152, "126th value should be 152");
+assertEq(view[127], 255, "127th value should be 255");
+assertEq(view[128], 0, "128th value should be 0");
+assertEq(view[129], 0, "129th value should be 0");
+assertEq(view[130], 0, "130th value should be 0");
+assertEq(view[131], 255, "131th value should be 255");
+assertEq(view[132], 0, "132th value should be 0");
+assertEq(view[133], 0, "133th value should be 0");
+assertEq(view[134], 0, "134th value should be 0");
+assertEq(view[135], 255, "135th value should be 255");
+assertEq(view[136], 128, "136th value should be 128");
+assertEq(view[137], 127, "137th value should be 127");
+assertEq(view[138], 127, "138th value should be 127");
+assertEq(view[139], 255, "139th value should be 255");
+assertEq(view[140], 154, "140th value should be 154");
+assertEq(view[141], 255, "141th value should be 255");
+assertEq(view[142], 101, "142th value should be 101");
+assertEq(view[143], 255, "143th value should be 255");
+assertEq(view[144], 179, "144th value should be 179");
+assertEq(view[145], 127, "145th value should be 127");
+assertEq(view[146], 76, "146th value should be 76");
+assertEq(view[147], 255, "147th value should be 255");
+assertEq(view[148], 205, "148th value should be 205");
+assertEq(view[149], 255, "149th value should be 255");
+assertEq(view[150], 50, "150th value should be 50");
+assertEq(view[151], 255, "151th value should be 255");
+assertEq(view[152], 205, "152th value should be 205");
+assertEq(view[153], 255, "153th value should be 255");
+assertEq(view[154], 50, "154th value should be 50");
+assertEq(view[155], 255, "155th value should be 255");
+assertEq(view[156], 230, "156th value should be 230");
+assertEq(view[157], 127, "157th value should be 127");
+assertEq(view[158], 25, "158th value should be 25");
+assertEq(view[159], 255, "159th value should be 255");
+assertEq(view[160], 0, "160th value should be 0");
+assertEq(view[161], 0, "161th value should be 0");
+assertEq(view[162], 0, "162th value should be 0");
+assertEq(view[163], 255, "163th value should be 255");
+assertEq(view[164], 230, "164th value should be 230");
+assertEq(view[165], 127, "165th value should be 127");
+assertEq(view[166], 25, "166th value should be 25");
+assertEq(view[167], 255, "167th value should be 255");
+assertEq(view[168], 205, "168th value should be 205");
+assertEq(view[169], 255, "169th value should be 255");
+assertEq(view[170], 50, "170th value should be 50");
+assertEq(view[171], 255, "171th value should be 255");
+assertEq(view[172], 205, "172th value should be 205");
+assertEq(view[173], 255, "173th value should be 255");
+assertEq(view[174], 50, "174th value should be 50");
+assertEq(view[175], 255, "175th value should be 255");
+assertEq(view[176], 205, "176th value should be 205");
+assertEq(view[177], 255, "177th value should be 255");
+assertEq(view[178], 50, "178th value should be 50");
+assertEq(view[179], 255, "179th value should be 255");
+assertEq(view[180], 205, "180th value should be 205");
+assertEq(view[181], 255, "181th value should be 255");
+assertEq(view[182], 50, "182th value should be 50");
+assertEq(view[183], 255, "183th value should be 255");
+assertEq(view[184], 205, "184th value should be 205");
+assertEq(view[185], 255, "185th value should be 255");
+assertEq(view[186], 50, "186th value should be 50");
+assertEq(view[187], 255, "187th value should be 255");
+assertEq(view[188], 179, "188th value should be 179");
+assertEq(view[189], 127, "189th value should be 127");
+assertEq(view[190], 76, "190th value should be 76");
+assertEq(view[191], 255, "191th value should be 255");
+assertEq(view[192], 179, "192th value should be 179");
+assertEq(view[193], 127, "193th value should be 127");
+assertEq(view[194], 76, "194th value should be 76");
+assertEq(view[195], 255, "195th value should be 255");
+assertEq(view[196], 154, "196th value should be 154");
+assertEq(view[197], 255, "197th value should be 255");
+assertEq(view[198], 101, "198th value should be 101");
+assertEq(view[199], 255, "199th value should be 255");
+assertEq(view[200], 103, "200th value should be 103");
+assertEq(view[201], 255, "201th value should be 255");
+assertEq(view[202], 152, "202th value should be 152");
+assertEq(view[203], 255, "203th value should be 255");
+assertEq(view[204], 78, "204th value should be 78");
+assertEq(view[205], 127, "205th value should be 127");
+assertEq(view[206], 178, "206th value should be 178");
+assertEq(view[207], 255, "207th value should be 255");
+assertEq(view[208], 0, "208th value should be 0");
+assertEq(view[209], 0, "209th value should be 0");
+assertEq(view[210], 0, "210th value should be 0");
+assertEq(view[211], 255, "211th value should be 255");
+assertEq(view[212], 0, "212th value should be 0");
+assertEq(view[213], 0, "213th value should be 0");
+assertEq(view[214], 0, "214th value should be 0");
+assertEq(view[215], 255, "215th value should be 255");
+assertEq(view[216], 78, "216th value should be 78");
+assertEq(view[217], 127, "217th value should be 127");
+assertEq(view[218], 178, "218th value should be 178");
+assertEq(view[219], 255, "219th value should be 255");
+assertEq(view[220], 128, "220th value should be 128");
+assertEq(view[221], 127, "221th value should be 127");
+assertEq(view[222], 127, "222th value should be 127");
+assertEq(view[223], 255, "223th value should be 255");
+assertEq(view[224], 154, "224th value should be 154");
+assertEq(view[225], 255, "225th value should be 255");
+assertEq(view[226], 101, "226th value should be 101");
+assertEq(view[227], 255, "227th value should be 255");
+assertEq(view[228], 205, "228th value should be 205");
+assertEq(view[229], 255, "229th value should be 255");
+assertEq(view[230], 50, "230th value should be 50");
+assertEq(view[231], 255, "231th value should be 255");
+assertEq(view[232], 205, "232th value should be 205");
+assertEq(view[233], 255, "233th value should be 255");
+assertEq(view[234], 50, "234th value should be 50");
+assertEq(view[235], 255, "235th value should be 255");
+assertEq(view[236], 230, "236th value should be 230");
+assertEq(view[237], 127, "237th value should be 127");
+assertEq(view[238], 25, "238th value should be 25");
+assertEq(view[239], 255, "239th value should be 255");
+assertEq(view[240], 0, "240th value should be 0");
+assertEq(view[241], 0, "241th value should be 0");
+assertEq(view[242], 0, "242th value should be 0");
+assertEq(view[243], 255, "243th value should be 255");
+assertEq(view[244], 205, "244th value should be 205");
+assertEq(view[245], 255, "245th value should be 255");
+assertEq(view[246], 50, "246th value should be 50");
+assertEq(view[247], 255, "247th value should be 255");
+assertEq(view[248], 205, "248th value should be 205");
+assertEq(view[249], 255, "249th value should be 255");
+assertEq(view[250], 50, "250th value should be 50");
+assertEq(view[251], 255, "251th value should be 255");
+assertEq(view[252], 205, "252th value should be 205");
+assertEq(view[253], 255, "253th value should be 255");
+assertEq(view[254], 50, "254th value should be 50");
+assertEq(view[255], 255, "255th value should be 255");
+assertEq(view[256], 205, "256th value should be 205");
+assertEq(view[257], 255, "257th value should be 255");
+assertEq(view[258], 50, "258th value should be 50");
+assertEq(view[259], 255, "259th value should be 255");
+assertEq(view[260], 205, "260th value should be 205");
+assertEq(view[261], 255, "261th value should be 255");
+assertEq(view[262], 50, "262th value should be 50");
+assertEq(view[263], 255, "263th value should be 255");
+assertEq(view[264], 179, "264th value should be 179");
+assertEq(view[265], 127, "265th value should be 127");
+assertEq(view[266], 76, "266th value should be 76");
+assertEq(view[267], 255, "267th value should be 255");
+assertEq(view[268], 179, "268th value should be 179");
+assertEq(view[269], 127, "269th value should be 127");
+assertEq(view[270], 76, "270th value should be 76");
+assertEq(view[271], 255, "271th value should be 255");
+assertEq(view[272], 154, "272th value should be 154");
+assertEq(view[273], 255, "273th value should be 255");
+assertEq(view[274], 101, "274th value should be 101");
+assertEq(view[275], 255, "275th value should be 255");
+assertEq(view[276], 52, "276th value should be 52");
+assertEq(view[277], 255, "277th value should be 255");
+assertEq(view[278], 203, "278th value should be 203");
+assertEq(view[279], 255, "279th value should be 255");
+assertEq(view[280], 0, "280th value should be 0");
+assertEq(view[281], 0, "281th value should be 0");
+assertEq(view[282], 0, "282th value should be 0");
+assertEq(view[283], 255, "283th value should be 255");
+assertEq(view[284], 0, "284th value should be 0");
+assertEq(view[285], 0, "285th value should be 0");
+assertEq(view[286], 0, "286th value should be 0");
+assertEq(view[287], 255, "287th value should be 255");
+assertEq(view[288], 0, "288th value should be 0");
+assertEq(view[289], 0, "289th value should be 0");
+assertEq(view[290], 0, "290th value should be 0");
+assertEq(view[291], 255, "291th value should be 255");
+assertEq(view[292], 0, "292th value should be 0");
+assertEq(view[293], 0, "293th value should be 0");
+assertEq(view[294], 0, "294th value should be 0");
+assertEq(view[295], 255, "295th value should be 255");
+assertEq(view[296], 0, "296th value should be 0");
+assertEq(view[297], 0, "297th value should be 0");
+assertEq(view[298], 0, "298th value should be 0");
+assertEq(view[299], 255, "299th value should be 255");
+assertEq(view[300], 52, "300th value should be 52");
+assertEq(view[301], 255, "301th value should be 255");
+assertEq(view[302], 203, "302th value should be 203");
+assertEq(view[303], 255, "303th value should be 255");
+assertEq(view[304], 52, "304th value should be 52");
+assertEq(view[305], 255, "305th value should be 255");
+assertEq(view[306], 203, "306th value should be 203");
+assertEq(view[307], 255, "307th value should be 255");
+assertEq(view[308], 179, "308th value should be 179");
+assertEq(view[309], 127, "309th value should be 127");
+assertEq(view[310], 76, "310th value should be 76");
+assertEq(view[311], 255, "311th value should be 255");
+assertEq(view[312], 205, "312th value should be 205");
+assertEq(view[313], 255, "313th value should be 255");
+assertEq(view[314], 50, "314th value should be 50");
+assertEq(view[315], 255, "315th value should be 255");
+assertEq(view[316], 205, "316th value should be 205");
+assertEq(view[317], 255, "317th value should be 255");
+assertEq(view[318], 50, "318th value should be 50");
+assertEq(view[319], 255, "319th value should be 255");
+assertEq(view[320], 230, "320th value should be 230");
+assertEq(view[321], 127, "321th value should be 127");
+assertEq(view[322], 25, "322th value should be 25");
+assertEq(view[323], 255, "323th value should be 255");
+assertEq(view[324], 205, "324th value should be 205");
+assertEq(view[325], 255, "325th value should be 255");
+assertEq(view[326], 50, "326th value should be 50");
+assertEq(view[327], 255, "327th value should be 255");
+assertEq(view[328], 205, "328th value should be 205");
+assertEq(view[329], 255, "329th value should be 255");
+assertEq(view[330], 50, "330th value should be 50");
+assertEq(view[331], 255, "331th value should be 255");
+assertEq(view[332], 205, "332th value should be 205");
+assertEq(view[333], 255, "333th value should be 255");
+assertEq(view[334], 50, "334th value should be 50");
+assertEq(view[335], 255, "335th value should be 255");
+assertEq(view[336], 205, "336th value should be 205");
+assertEq(view[337], 255, "337th value should be 255");
+assertEq(view[338], 50, "338th value should be 50");
+assertEq(view[339], 255, "339th value should be 255");
+assertEq(view[340], 179, "340th value should be 179");
+assertEq(view[341], 127, "341th value should be 127");
+assertEq(view[342], 76, "342th value should be 76");
+assertEq(view[343], 255, "343th value should be 255");
+assertEq(view[344], 154, "344th value should be 154");
+assertEq(view[345], 255, "345th value should be 255");
+assertEq(view[346], 101, "346th value should be 101");
+assertEq(view[347], 255, "347th value should be 255");
+assertEq(view[348], 154, "348th value should be 154");
+assertEq(view[349], 255, "349th value should be 255");
+assertEq(view[350], 101, "350th value should be 101");
+assertEq(view[351], 255, "351th value should be 255");
+assertEq(view[352], 128, "352th value should be 128");
+assertEq(view[353], 127, "353th value should be 127");
+assertEq(view[354], 127, "354th value should be 127");
+assertEq(view[355], 255, "355th value should be 255");
+assertEq(view[356], 52, "356th value should be 52");
+assertEq(view[357], 255, "357th value should be 255");
+assertEq(view[358], 203, "358th value should be 203");
+assertEq(view[359], 255, "359th value should be 255");
+assertEq(view[360], 0, "360th value should be 0");
+assertEq(view[361], 0, "361th value should be 0");
+assertEq(view[362], 0, "362th value should be 0");
+assertEq(view[363], 255, "363th value should be 255");
+assertEq(view[364], 0, "364th value should be 0");
+assertEq(view[365], 0, "365th value should be 0");
+assertEq(view[366], 0, "366th value should be 0");
+assertEq(view[367], 255, "367th value should be 255");
+assertEq(view[368], 0, "368th value should be 0");
+assertEq(view[369], 0, "369th value should be 0");
+assertEq(view[370], 0, "370th value should be 0");
+assertEq(view[371], 255, "371th value should be 255");
+assertEq(view[372], 0, "372th value should be 0");
+assertEq(view[373], 0, "373th value should be 0");
+assertEq(view[374], 0, "374th value should be 0");
+assertEq(view[375], 255, "375th value should be 255");
+assertEq(view[376], 0, "376th value should be 0");
+assertEq(view[377], 0, "377th value should be 0");
+assertEq(view[378], 0, "378th value should be 0");
+assertEq(view[379], 255, "379th value should be 255");
+assertEq(view[380], 0, "380th value should be 0");
+assertEq(view[381], 0, "381th value should be 0");
+assertEq(view[382], 0, "382th value should be 0");
+assertEq(view[383], 255, "383th value should be 255");
+assertEq(view[384], 52, "384th value should be 52");
+assertEq(view[385], 255, "385th value should be 255");
+assertEq(view[386], 203, "386th value should be 203");
+assertEq(view[387], 255, "387th value should be 255");
+assertEq(view[388], 179, "388th value should be 179");
+assertEq(view[389], 127, "389th value should be 127");
+assertEq(view[390], 76, "390th value should be 76");
+assertEq(view[391], 255, "391th value should be 255");
+assertEq(view[392], 205, "392th value should be 205");
+assertEq(view[393], 255, "393th value should be 255");
+assertEq(view[394], 50, "394th value should be 50");
+assertEq(view[395], 255, "395th value should be 255");
+assertEq(view[396], 205, "396th value should be 205");
+assertEq(view[397], 255, "397th value should be 255");
+assertEq(view[398], 50, "398th value should be 50");
+assertEq(view[399], 255, "399th value should be 255");
+assertEq(view[400], 205, "400th value should be 205");
+assertEq(view[401], 255, "401th value should be 255");
+assertEq(view[402], 50, "402th value should be 50");
+assertEq(view[403], 255, "403th value should be 255");
+assertEq(view[404], 205, "404th value should be 205");
+assertEq(view[405], 255, "405th value should be 255");
+assertEq(view[406], 50, "406th value should be 50");
+assertEq(view[407], 255, "407th value should be 255");
+assertEq(view[408], 205, "408th value should be 205");
+assertEq(view[409], 255, "409th value should be 255");
+assertEq(view[410], 50, "410th value should be 50");
+assertEq(view[411], 255, "411th value should be 255");
+assertEq(view[412], 205, "412th value should be 205");
+assertEq(view[413], 255, "413th value should be 255");
+assertEq(view[414], 50, "414th value should be 50");
+assertEq(view[415], 255, "415th value should be 255");
+assertEq(view[416], 154, "416th value should be 154");
+assertEq(view[417], 255, "417th value should be 255");
+assertEq(view[418], 101, "418th value should be 101");
+assertEq(view[419], 255, "419th value should be 255");
+assertEq(view[420], 128, "420th value should be 128");
+assertEq(view[421], 127, "421th value should be 127");
+assertEq(view[422], 127, "422th value should be 127");
+assertEq(view[423], 255, "423th value should be 255");
+assertEq(view[424], 154, "424th value should be 154");
+assertEq(view[425], 255, "425th value should be 255");
+assertEq(view[426], 101, "426th value should be 101");
+assertEq(view[427], 255, "427th value should be 255");
+assertEq(view[428], 128, "428th value should be 128");
+assertEq(view[429], 127, "429th value should be 127");
+assertEq(view[430], 127, "430th value should be 127");
+assertEq(view[431], 255, "431th value should be 255");
+assertEq(view[432], 103, "432th value should be 103");
+assertEq(view[433], 255, "433th value should be 255");
+assertEq(view[434], 152, "434th value should be 152");
+assertEq(view[435], 255, "435th value should be 255");
+assertEq(view[436], 0, "436th value should be 0");
+assertEq(view[437], 0, "437th value should be 0");
+assertEq(view[438], 0, "438th value should be 0");
+assertEq(view[439], 255, "439th value should be 255");
+assertEq(view[440], 0, "440th value should be 0");
+assertEq(view[441], 0, "441th value should be 0");
+assertEq(view[442], 0, "442th value should be 0");
+assertEq(view[443], 255, "443th value should be 255");
+assertEq(view[444], 0, "444th value should be 0");
+assertEq(view[445], 0, "445th value should be 0");
+assertEq(view[446], 0, "446th value should be 0");
+assertEq(view[447], 255, "447th value should be 255");
+assertEq(view[448], 0, "448th value should be 0");
+assertEq(view[449], 0, "449th value should be 0");
+assertEq(view[450], 0, "450th value should be 0");
+assertEq(view[451], 255, "451th value should be 255");
+assertEq(view[452], 0, "452th value should be 0");
+assertEq(view[453], 0, "453th value should be 0");
+assertEq(view[454], 0, "454th value should be 0");
+assertEq(view[455], 255, "455th value should be 255");
+assertEq(view[456], 0, "456th value should be 0");
+assertEq(view[457], 0, "457th value should be 0");
+assertEq(view[458], 0, "458th value should be 0");
+assertEq(view[459], 255, "459th value should be 255");
+assertEq(view[460], 0, "460th value should be 0");
+assertEq(view[461], 0, "461th value should be 0");
+assertEq(view[462], 0, "462th value should be 0");
+assertEq(view[463], 255, "463th value should be 255");
+assertEq(view[464], 78, "464th value should be 78");
+assertEq(view[465], 127, "465th value should be 127");
+assertEq(view[466], 178, "466th value should be 178");
+assertEq(view[467], 255, "467th value should be 255");
+assertEq(view[468], 154, "468th value should be 154");
+assertEq(view[469], 255, "469th value should be 255");
+assertEq(view[470], 101, "470th value should be 101");
+assertEq(view[471], 255, "471th value should be 255");
+assertEq(view[472], 205, "472th value should be 205");
+assertEq(view[473], 255, "473th value should be 255");
+assertEq(view[474], 50, "474th value should be 50");
+assertEq(view[475], 255, "475th value should be 255");
+assertEq(view[476], 205, "476th value should be 205");
+assertEq(view[477], 255, "477th value should be 255");
+assertEq(view[478], 50, "478th value should be 50");
+assertEq(view[479], 255, "479th value should be 255");
+assertEq(view[480], 205, "480th value should be 205");
+assertEq(view[481], 255, "481th value should be 255");
+assertEq(view[482], 50, "482th value should be 50");
+assertEq(view[483], 255, "483th value should be 255");
+assertEq(view[484], 205, "484th value should be 205");
+assertEq(view[485], 255, "485th value should be 255");
+assertEq(view[486], 50, "486th value should be 50");
+assertEq(view[487], 255, "487th value should be 255");
+assertEq(view[488], 179, "488th value should be 179");
+assertEq(view[489], 127, "489th value should be 127");
+assertEq(view[490], 76, "490th value should be 76");
+assertEq(view[491], 255, "491th value should be 255");
+assertEq(view[492], 179, "492th value should be 179");
+assertEq(view[493], 127, "493th value should be 127");
+assertEq(view[494], 76, "494th value should be 76");
+assertEq(view[495], 255, "495th value should be 255");
+assertEq(view[496], 128, "496th value should be 128");
+assertEq(view[497], 127, "497th value should be 127");
+assertEq(view[498], 127, "498th value should be 127");
+assertEq(view[499], 255, "499th value should be 255");
+assertEq(view[500], 52, "500th value should be 52");
+assertEq(view[501], 255, "501th value should be 255");
+assertEq(view[502], 203, "502th value should be 203");
+assertEq(view[503], 255, "503th value should be 255");
+assertEq(view[504], 0, "504th value should be 0");
+assertEq(view[505], 0, "505th value should be 0");
+assertEq(view[506], 0, "506th value should be 0");
+assertEq(view[507], 255, "507th value should be 255");
+assertEq(view[508], 78, "508th value should be 78");
+assertEq(view[509], 127, "509th value should be 127");
+assertEq(view[510], 178, "510th value should be 178");
+assertEq(view[511], 255, "511th value should be 255");
+assertEq(view[512], 52, "512th value should be 52");
+assertEq(view[513], 255, "513th value should be 255");
+assertEq(view[514], 203, "514th value should be 203");
+assertEq(view[515], 255, "515th value should be 255");
+assertEq(view[516], 0, "516th value should be 0");
+assertEq(view[517], 0, "517th value should be 0");
+assertEq(view[518], 0, "518th value should be 0");
+assertEq(view[519], 255, "519th value should be 255");
+assertEq(view[520], 0, "520th value should be 0");
+assertEq(view[521], 0, "521th value should be 0");
+assertEq(view[522], 0, "522th value should be 0");
+assertEq(view[523], 255, "523th value should be 255");
+assertEq(view[524], 0, "524th value should be 0");
+assertEq(view[525], 0, "525th value should be 0");
+assertEq(view[526], 0, "526th value should be 0");
+assertEq(view[527], 255, "527th value should be 255");
+assertEq(view[528], 0, "528th value should be 0");
+assertEq(view[529], 0, "529th value should be 0");
+assertEq(view[530], 0, "530th value should be 0");
+assertEq(view[531], 255, "531th value should be 255");
+assertEq(view[532], 0, "532th value should be 0");
+assertEq(view[533], 0, "533th value should be 0");
+assertEq(view[534], 0, "534th value should be 0");
+assertEq(view[535], 255, "535th value should be 255");
+assertEq(view[536], 0, "536th value should be 0");
+assertEq(view[537], 0, "537th value should be 0");
+assertEq(view[538], 0, "538th value should be 0");
+assertEq(view[539], 255, "539th value should be 255");
+assertEq(view[540], 0, "540th value should be 0");
+assertEq(view[541], 0, "541th value should be 0");
+assertEq(view[542], 0, "542th value should be 0");
+assertEq(view[543], 255, "543th value should be 255");
+assertEq(view[544], 0, "544th value should be 0");
+assertEq(view[545], 0, "545th value should be 0");
+assertEq(view[546], 0, "546th value should be 0");
+assertEq(view[547], 255, "547th value should be 255");
+assertEq(view[548], 154, "548th value should be 154");
+assertEq(view[549], 255, "549th value should be 255");
+assertEq(view[550], 101, "550th value should be 101");
+assertEq(view[551], 255, "551th value should be 255");
+assertEq(view[552], 205, "552th value should be 205");
+assertEq(view[553], 255, "553th value should be 255");
+assertEq(view[554], 50, "554th value should be 50");
+assertEq(view[555], 255, "555th value should be 255");
+assertEq(view[556], 205, "556th value should be 205");
+assertEq(view[557], 255, "557th value should be 255");
+assertEq(view[558], 50, "558th value should be 50");
+assertEq(view[559], 255, "559th value should be 255");
+assertEq(view[560], 205, "560th value should be 205");
+assertEq(view[561], 255, "561th value should be 255");
+assertEq(view[562], 50, "562th value should be 50");
+assertEq(view[563], 255, "563th value should be 255");
+assertEq(view[564], 179, "564th value should be 179");
+assertEq(view[565], 127, "565th value should be 127");
+assertEq(view[566], 76, "566th value should be 76");
+assertEq(view[567], 255, "567th value should be 255");
+assertEq(view[568], 179, "568th value should be 179");
+assertEq(view[569], 127, "569th value should be 127");
+assertEq(view[570], 76, "570th value should be 76");
+assertEq(view[571], 255, "571th value should be 255");
+assertEq(view[572], 154, "572th value should be 154");
+assertEq(view[573], 255, "573th value should be 255");
+assertEq(view[574], 101, "574th value should be 101");
+assertEq(view[575], 255, "575th value should be 255");
+assertEq(view[576], 103, "576th value should be 103");
+assertEq(view[577], 255, "577th value should be 255");
+assertEq(view[578], 152, "578th value should be 152");
+assertEq(view[579], 255, "579th value should be 255");
+assertEq(view[580], 0, "580th value should be 0");
+assertEq(view[581], 0, "581th value should be 0");
+assertEq(view[582], 0, "582th value should be 0");
+assertEq(view[583], 255, "583th value should be 255");
+assertEq(view[584], 0, "584th value should be 0");
+assertEq(view[585], 0, "585th value should be 0");
+assertEq(view[586], 0, "586th value should be 0");
+assertEq(view[587], 255, "587th value should be 255");
+assertEq(view[588], 0, "588th value should be 0");
+assertEq(view[589], 0, "589th value should be 0");
+assertEq(view[590], 0, "590th value should be 0");
+assertEq(view[591], 255, "591th value should be 255");
+assertEq(view[592], 0, "592th value should be 0");
+assertEq(view[593], 0, "593th value should be 0");
+assertEq(view[594], 0, "594th value should be 0");
+assertEq(view[595], 255, "595th value should be 255");
+assertEq(view[596], 0, "596th value should be 0");
+assertEq(view[597], 0, "597th value should be 0");
+assertEq(view[598], 0, "598th value should be 0");
+assertEq(view[599], 255, "599th value should be 255");
+assertEq(view[600], 0, "600th value should be 0");
+assertEq(view[601], 0, "601th value should be 0");
+assertEq(view[602], 0, "602th value should be 0");
+assertEq(view[603], 255, "603th value should be 255");
+assertEq(view[604], 0, "604th value should be 0");
+assertEq(view[605], 0, "605th value should be 0");
+assertEq(view[606], 0, "606th value should be 0");
+assertEq(view[607], 255, "607th value should be 255");
+assertEq(view[608], 0, "608th value should be 0");
+assertEq(view[609], 0, "609th value should be 0");
+assertEq(view[610], 0, "610th value should be 0");
+assertEq(view[611], 255, "611th value should be 255");
+assertEq(view[612], 0, "612th value should be 0");
+assertEq(view[613], 0, "613th value should be 0");
+assertEq(view[614], 0, "614th value should be 0");
+assertEq(view[615], 255, "615th value should be 255");
+assertEq(view[616], 0, "616th value should be 0");
+assertEq(view[617], 0, "617th value should be 0");
+assertEq(view[618], 0, "618th value should be 0");
+assertEq(view[619], 255, "619th value should be 255");
+assertEq(view[620], 0, "620th value should be 0");
+assertEq(view[621], 0, "621th value should be 0");
+assertEq(view[622], 0, "622th value should be 0");
+assertEq(view[623], 255, "623th value should be 255");
+assertEq(view[624], 0, "624th value should be 0");
+assertEq(view[625], 0, "625th value should be 0");
+assertEq(view[626], 0, "626th value should be 0");
+assertEq(view[627], 255, "627th value should be 255");
+assertEq(view[628], 154, "628th value should be 154");
+assertEq(view[629], 255, "629th value should be 255");
+assertEq(view[630], 101, "630th value should be 101");
+assertEq(view[631], 255, "631th value should be 255");
+assertEq(view[632], 205, "632th value should be 205");
+assertEq(view[633], 255, "633th value should be 255");
+assertEq(view[634], 50, "634th value should be 50");
+assertEq(view[635], 255, "635th value should be 255");
+assertEq(view[636], 205, "636th value should be 205");
+assertEq(view[637], 255, "637th value should be 255");
+assertEq(view[638], 50, "638th value should be 50");
+assertEq(view[639], 255, "639th value should be 255");
+assertEq(view[640], 179, "640th value should be 179");
+assertEq(view[641], 127, "641th value should be 127");
+assertEq(view[642], 76, "642th value should be 76");
+assertEq(view[643], 255, "643th value should be 255");
+assertEq(view[644], 179, "644th value should be 179");
+assertEq(view[645], 127, "645th value should be 127");
+assertEq(view[646], 76, "646th value should be 76");
+assertEq(view[647], 255, "647th value should be 255");
+assertEq(view[648], 154, "648th value should be 154");
+assertEq(view[649], 255, "649th value should be 255");
+assertEq(view[650], 101, "650th value should be 101");
+assertEq(view[651], 255, "651th value should be 255");
+assertEq(view[652], 128, "652th value should be 128");
+assertEq(view[653], 127, "653th value should be 127");
+assertEq(view[654], 127, "654th value should be 127");
+assertEq(view[655], 255, "655th value should be 255");
+assertEq(view[656], 52, "656th value should be 52");
+assertEq(view[657], 255, "657th value should be 255");
+assertEq(view[658], 203, "658th value should be 203");
+assertEq(view[659], 255, "659th value should be 255");
+assertEq(view[660], 0, "660th value should be 0");
+assertEq(view[661], 0, "661th value should be 0");
+assertEq(view[662], 0, "662th value should be 0");
+assertEq(view[663], 255, "663th value should be 255");
+assertEq(view[664], 0, "664th value should be 0");
+assertEq(view[665], 0, "665th value should be 0");
+assertEq(view[666], 0, "666th value should be 0");
+assertEq(view[667], 255, "667th value should be 255");
+assertEq(view[668], 0, "668th value should be 0");
+assertEq(view[669], 0, "669th value should be 0");
+assertEq(view[670], 0, "670th value should be 0");
+assertEq(view[671], 255, "671th value should be 255");
+assertEq(view[672], 0, "672th value should be 0");
+assertEq(view[673], 0, "673th value should be 0");
+assertEq(view[674], 0, "674th value should be 0");
+assertEq(view[675], 255, "675th value should be 255");
+assertEq(view[676], 0, "676th value should be 0");
+assertEq(view[677], 0, "677th value should be 0");
+assertEq(view[678], 0, "678th value should be 0");
+assertEq(view[679], 255, "679th value should be 255");
+assertEq(view[680], 0, "680th value should be 0");
+assertEq(view[681], 0, "681th value should be 0");
+assertEq(view[682], 0, "682th value should be 0");
+assertEq(view[683], 255, "683th value should be 255");
+assertEq(view[684], 0, "684th value should be 0");
+assertEq(view[685], 0, "685th value should be 0");
+assertEq(view[686], 0, "686th value should be 0");
+assertEq(view[687], 255, "687th value should be 255");
+assertEq(view[688], 0, "688th value should be 0");
+assertEq(view[689], 0, "689th value should be 0");
+assertEq(view[690], 0, "690th value should be 0");
+assertEq(view[691], 255, "691th value should be 255");
+assertEq(view[692], 0, "692th value should be 0");
+assertEq(view[693], 0, "693th value should be 0");
+assertEq(view[694], 0, "694th value should be 0");
+assertEq(view[695], 255, "695th value should be 255");
+assertEq(view[696], 0, "696th value should be 0");
+assertEq(view[697], 0, "697th value should be 0");
+assertEq(view[698], 0, "698th value should be 0");
+assertEq(view[699], 255, "699th value should be 255");
+assertEq(view[700], 0, "700th value should be 0");
+assertEq(view[701], 0, "701th value should be 0");
+assertEq(view[702], 0, "702th value should be 0");
+assertEq(view[703], 255, "703th value should be 255");
+assertEq(view[704], 0, "704th value should be 0");
+assertEq(view[705], 0, "705th value should be 0");
+assertEq(view[706], 0, "706th value should be 0");
+assertEq(view[707], 255, "707th value should be 255");
+assertEq(view[708], 154, "708th value should be 154");
+assertEq(view[709], 255, "709th value should be 255");
+assertEq(view[710], 101, "710th value should be 101");
+assertEq(view[711], 255, "711th value should be 255");
+assertEq(view[712], 179, "712th value should be 179");
+assertEq(view[713], 127, "713th value should be 127");
+assertEq(view[714], 76, "714th value should be 76");
+assertEq(view[715], 255, "715th value should be 255");
+assertEq(view[716], 205, "716th value should be 205");
+assertEq(view[717], 255, "717th value should be 255");
+assertEq(view[718], 50, "718th value should be 50");
+assertEq(view[719], 255, "719th value should be 255");
+assertEq(view[720], 154, "720th value should be 154");
+assertEq(view[721], 255, "721th value should be 255");
+assertEq(view[722], 101, "722th value should be 101");
+assertEq(view[723], 255, "723th value should be 255");
+assertEq(view[724], 52, "724th value should be 52");
+assertEq(view[725], 255, "725th value should be 255");
+assertEq(view[726], 203, "726th value should be 203");
+assertEq(view[727], 255, "727th value should be 255");
+assertEq(view[728], 128, "728th value should be 128");
+assertEq(view[729], 127, "729th value should be 127");
+assertEq(view[730], 127, "730th value should be 127");
+assertEq(view[731], 255, "731th value should be 255");
+assertEq(view[732], 78, "732th value should be 78");
+assertEq(view[733], 127, "733th value should be 127");
+assertEq(view[734], 178, "734th value should be 178");
+assertEq(view[735], 255, "735th value should be 255");
+assertEq(view[736], 0, "736th value should be 0");
+assertEq(view[737], 0, "737th value should be 0");
+assertEq(view[738], 0, "738th value should be 0");
+assertEq(view[739], 255, "739th value should be 255");
+assertEq(view[740], 0, "740th value should be 0");
+assertEq(view[741], 0, "741th value should be 0");
+assertEq(view[742], 0, "742th value should be 0");
+assertEq(view[743], 255, "743th value should be 255");
+assertEq(view[744], 0, "744th value should be 0");
+assertEq(view[745], 0, "745th value should be 0");
+assertEq(view[746], 0, "746th value should be 0");
+assertEq(view[747], 255, "747th value should be 255");
+assertEq(view[748], 0, "748th value should be 0");
+assertEq(view[749], 0, "749th value should be 0");
+assertEq(view[750], 0, "750th value should be 0");
+assertEq(view[751], 255, "751th value should be 255");
+assertEq(view[752], 0, "752th value should be 0");
+assertEq(view[753], 0, "753th value should be 0");
+assertEq(view[754], 0, "754th value should be 0");
+assertEq(view[755], 255, "755th value should be 255");
+assertEq(view[756], 0, "756th value should be 0");
+assertEq(view[757], 0, "757th value should be 0");
+assertEq(view[758], 0, "758th value should be 0");
+assertEq(view[759], 255, "759th value should be 255");
+assertEq(view[760], 0, "760th value should be 0");
+assertEq(view[761], 0, "761th value should be 0");
+assertEq(view[762], 0, "762th value should be 0");
+assertEq(view[763], 255, "763th value should be 255");
+assertEq(view[764], 0, "764th value should be 0");
+assertEq(view[765], 0, "765th value should be 0");
+assertEq(view[766], 0, "766th value should be 0");
+assertEq(view[767], 255, "767th value should be 255");
+assertEq(view[768], 0, "768th value should be 0");
+assertEq(view[769], 0, "769th value should be 0");
+assertEq(view[770], 0, "770th value should be 0");
+assertEq(view[771], 255, "771th value should be 255");
+assertEq(view[772], 0, "772th value should be 0");
+assertEq(view[773], 0, "773th value should be 0");
+assertEq(view[774], 0, "774th value should be 0");
+assertEq(view[775], 255, "775th value should be 255");
+assertEq(view[776], 0, "776th value should be 0");
+assertEq(view[777], 0, "777th value should be 0");
+assertEq(view[778], 0, "778th value should be 0");
+assertEq(view[779], 255, "779th value should be 255");
+assertEq(view[780], 0, "780th value should be 0");
+assertEq(view[781], 0, "781th value should be 0");
+assertEq(view[782], 0, "782th value should be 0");
+assertEq(view[783], 255, "783th value should be 255");
+assertEq(view[784], 78, "784th value should be 78");
+assertEq(view[785], 127, "785th value should be 127");
+assertEq(view[786], 178, "786th value should be 178");
+assertEq(view[787], 255, "787th value should be 255");
+assertEq(view[788], 154, "788th value should be 154");
+assertEq(view[789], 255, "789th value should be 255");
+assertEq(view[790], 101, "790th value should be 101");
+assertEq(view[791], 255, "791th value should be 255");
+assertEq(view[792], 179, "792th value should be 179");
+assertEq(view[793], 127, "793th value should be 127");
+assertEq(view[794], 76, "794th value should be 76");
+assertEq(view[795], 255, "795th value should be 255");
+assertEq(view[796], 205, "796th value should be 205");
+assertEq(view[797], 255, "797th value should be 255");
+assertEq(view[798], 50, "798th value should be 50");
+assertEq(view[799], 255, "799th value should be 255");
+assertEq(view[800], 128, "800th value should be 128");
+assertEq(view[801], 127, "801th value should be 127");
+assertEq(view[802], 127, "802th value should be 127");
+assertEq(view[803], 255, "803th value should be 255");
+assertEq(view[804], 0, "804th value should be 0");
+assertEq(view[805], 0, "805th value should be 0");
+assertEq(view[806], 0, "806th value should be 0");
+assertEq(view[807], 255, "807th value should be 255");
+assertEq(view[808], 26, "808th value should be 26");
+assertEq(view[809], 127, "809th value should be 127");
+assertEq(view[810], 229, "810th value should be 229");
+assertEq(view[811], 255, "811th value should be 255");
+assertEq(view[812], 0, "812th value should be 0");
+assertEq(view[813], 0, "813th value should be 0");
+assertEq(view[814], 0, "814th value should be 0");
+assertEq(view[815], 255, "815th value should be 255");
+assertEq(view[816], 0, "816th value should be 0");
+assertEq(view[817], 0, "817th value should be 0");
+assertEq(view[818], 0, "818th value should be 0");
+assertEq(view[819], 255, "819th value should be 255");
+assertEq(view[820], 0, "820th value should be 0");
+assertEq(view[821], 0, "821th value should be 0");
+assertEq(view[822], 0, "822th value should be 0");
+assertEq(view[823], 255, "823th value should be 255");
+assertEq(view[824], 0, "824th value should be 0");
+assertEq(view[825], 0, "825th value should be 0");
+assertEq(view[826], 0, "826th value should be 0");
+assertEq(view[827], 255, "827th value should be 255");
+assertEq(view[828], 0, "828th value should be 0");
+assertEq(view[829], 0, "829th value should be 0");
+assertEq(view[830], 0, "830th value should be 0");
+assertEq(view[831], 255, "831th value should be 255");
+assertEq(view[832], 0, "832th value should be 0");
+assertEq(view[833], 0, "833th value should be 0");
+assertEq(view[834], 0, "834th value should be 0");
+assertEq(view[835], 255, "835th value should be 255");
+assertEq(view[836], 0, "836th value should be 0");
+assertEq(view[837], 0, "837th value should be 0");
+assertEq(view[838], 0, "838th value should be 0");
+assertEq(view[839], 255, "839th value should be 255");
+assertEq(view[840], 0, "840th value should be 0");
+assertEq(view[841], 0, "841th value should be 0");
+assertEq(view[842], 0, "842th value should be 0");
+assertEq(view[843], 255, "843th value should be 255");
+assertEq(view[844], 0, "844th value should be 0");
+assertEq(view[845], 0, "845th value should be 0");
+assertEq(view[846], 0, "846th value should be 0");
+assertEq(view[847], 255, "847th value should be 255");
+assertEq(view[848], 0, "848th value should be 0");
+assertEq(view[849], 0, "849th value should be 0");
+assertEq(view[850], 0, "850th value should be 0");
+assertEq(view[851], 255, "851th value should be 255");
+assertEq(view[852], 0, "852th value should be 0");
+assertEq(view[853], 0, "853th value should be 0");
+assertEq(view[854], 0, "854th value should be 0");
+assertEq(view[855], 255, "855th value should be 255");
+assertEq(view[856], 0, "856th value should be 0");
+assertEq(view[857], 0, "857th value should be 0");
+assertEq(view[858], 0, "858th value should be 0");
+assertEq(view[859], 255, "859th value should be 255");
+assertEq(view[860], 0, "860th value should be 0");
+assertEq(view[861], 0, "861th value should be 0");
+assertEq(view[862], 0, "862th value should be 0");
+assertEq(view[863], 255, "863th value should be 255");
+assertEq(view[864], 103, "864th value should be 103");
+assertEq(view[865], 255, "865th value should be 255");
+assertEq(view[866], 152, "866th value should be 152");
+assertEq(view[867], 255, "867th value should be 255");
+assertEq(view[868], 154, "868th value should be 154");
+assertEq(view[869], 255, "869th value should be 255");
+assertEq(view[870], 101, "870th value should be 101");
+assertEq(view[871], 255, "871th value should be 255");
+assertEq(view[872], 179, "872th value should be 179");
+assertEq(view[873], 127, "873th value should be 127");
+assertEq(view[874], 76, "874th value should be 76");
+assertEq(view[875], 255, "875th value should be 255");
+assertEq(view[876], 205, "876th value should be 205");
+assertEq(view[877], 255, "877th value should be 255");
+assertEq(view[878], 50, "878th value should be 50");
+assertEq(view[879], 255, "879th value should be 255");
+assertEq(view[880], 179, "880th value should be 179");
+assertEq(view[881], 127, "881th value should be 127");
+assertEq(view[882], 76, "882th value should be 76");
+assertEq(view[883], 255, "883th value should be 255");
+assertEq(view[884], 179, "884th value should be 179");
+assertEq(view[885], 127, "885th value should be 127");
+assertEq(view[886], 76, "886th value should be 76");
+assertEq(view[887], 255, "887th value should be 255");
+assertEq(view[888], 128, "888th value should be 128");
+assertEq(view[889], 127, "889th value should be 127");
+assertEq(view[890], 127, "890th value should be 127");
+assertEq(view[891], 255, "891th value should be 255");
+assertEq(view[892], 103, "892th value should be 103");
+assertEq(view[893], 255, "893th value should be 255");
+assertEq(view[894], 152, "894th value should be 152");
+assertEq(view[895], 255, "895th value should be 255");
+assertEq(view[896], 26, "896th value should be 26");
+assertEq(view[897], 127, "897th value should be 127");
+assertEq(view[898], 229, "898th value should be 229");
+assertEq(view[899], 255, "899th value should be 255");
+assertEq(view[900], 0, "900th value should be 0");
+assertEq(view[901], 0, "901th value should be 0");
+assertEq(view[902], 0, "902th value should be 0");
+assertEq(view[903], 255, "903th value should be 255");
+assertEq(view[904], 0, "904th value should be 0");
+assertEq(view[905], 0, "905th value should be 0");
+assertEq(view[906], 0, "906th value should be 0");
+assertEq(view[907], 255, "907th value should be 255");
+assertEq(view[908], 0, "908th value should be 0");
+assertEq(view[909], 0, "909th value should be 0");
+assertEq(view[910], 0, "910th value should be 0");
+assertEq(view[911], 255, "911th value should be 255");
+assertEq(view[912], 0, "912th value should be 0");
+assertEq(view[913], 0, "913th value should be 0");
+assertEq(view[914], 0, "914th value should be 0");
+assertEq(view[915], 255, "915th value should be 255");
+assertEq(view[916], 0, "916th value should be 0");
+assertEq(view[917], 0, "917th value should be 0");
+assertEq(view[918], 0, "918th value should be 0");
+assertEq(view[919], 255, "919th value should be 255");
+assertEq(view[920], 0, "920th value should be 0");
+assertEq(view[921], 0, "921th value should be 0");
+assertEq(view[922], 0, "922th value should be 0");
+assertEq(view[923], 255, "923th value should be 255");
+assertEq(view[924], 0, "924th value should be 0");
+assertEq(view[925], 0, "925th value should be 0");
+assertEq(view[926], 0, "926th value should be 0");
+assertEq(view[927], 255, "927th value should be 255");
+assertEq(view[928], 0, "928th value should be 0");
+assertEq(view[929], 0, "929th value should be 0");
+assertEq(view[930], 0, "930th value should be 0");
+assertEq(view[931], 255, "931th value should be 255");
+assertEq(view[932], 0, "932th value should be 0");
+assertEq(view[933], 0, "933th value should be 0");
+assertEq(view[934], 0, "934th value should be 0");
+assertEq(view[935], 255, "935th value should be 255");
+assertEq(view[936], 0, "936th value should be 0");
+assertEq(view[937], 0, "937th value should be 0");
+assertEq(view[938], 0, "938th value should be 0");
+assertEq(view[939], 255, "939th value should be 255");
+assertEq(view[940], 0, "940th value should be 0");
+assertEq(view[941], 0, "941th value should be 0");
+assertEq(view[942], 0, "942th value should be 0");
+assertEq(view[943], 255, "943th value should be 255");
+assertEq(view[944], 0, "944th value should be 0");
+assertEq(view[945], 0, "945th value should be 0");
+assertEq(view[946], 0, "946th value should be 0");
+assertEq(view[947], 255, "947th value should be 255");
+assertEq(view[948], 154, "948th value should be 154");
+assertEq(view[949], 255, "949th value should be 255");
+assertEq(view[950], 101, "950th value should be 101");
+assertEq(view[951], 255, "951th value should be 255");
+assertEq(view[952], 179, "952th value should be 179");
+assertEq(view[953], 127, "953th value should be 127");
+assertEq(view[954], 76, "954th value should be 76");
+assertEq(view[955], 255, "955th value should be 255");
+assertEq(view[956], 205, "956th value should be 205");
+assertEq(view[957], 255, "957th value should be 255");
+assertEq(view[958], 50, "958th value should be 50");
+assertEq(view[959], 255, "959th value should be 255");
+assertEq(view[960], 179, "960th value should be 179");
+assertEq(view[961], 127, "961th value should be 127");
+assertEq(view[962], 76, "962th value should be 76");
+assertEq(view[963], 255, "963th value should be 255");
+assertEq(view[964], 179, "964th value should be 179");
+assertEq(view[965], 127, "965th value should be 127");
+assertEq(view[966], 76, "966th value should be 76");
+assertEq(view[967], 255, "967th value should be 255");
+assertEq(view[968], 179, "968th value should be 179");
+assertEq(view[969], 127, "969th value should be 127");
+assertEq(view[970], 76, "970th value should be 76");
+assertEq(view[971], 255, "971th value should be 255");
+assertEq(view[972], 154, "972th value should be 154");
+assertEq(view[973], 255, "973th value should be 255");
+assertEq(view[974], 101, "974th value should be 101");
+assertEq(view[975], 255, "975th value should be 255");
+assertEq(view[976], 103, "976th value should be 103");
+assertEq(view[977], 255, "977th value should be 255");
+assertEq(view[978], 152, "978th value should be 152");
+assertEq(view[979], 255, "979th value should be 255");
+assertEq(view[980], 0, "980th value should be 0");
+assertEq(view[981], 0, "981th value should be 0");
+assertEq(view[982], 0, "982th value should be 0");
+assertEq(view[983], 255, "983th value should be 255");
+assertEq(view[984], 0, "984th value should be 0");
+assertEq(view[985], 0, "985th value should be 0");
+assertEq(view[986], 0, "986th value should be 0");
+assertEq(view[987], 255, "987th value should be 255");
+assertEq(view[988], 0, "988th value should be 0");
+assertEq(view[989], 0, "989th value should be 0");
+assertEq(view[990], 0, "990th value should be 0");
+assertEq(view[991], 255, "991th value should be 255");
+assertEq(view[992], 0, "992th value should be 0");
+assertEq(view[993], 0, "993th value should be 0");
+assertEq(view[994], 0, "994th value should be 0");
+assertEq(view[995], 255, "995th value should be 255");
+assertEq(view[996], 0, "996th value should be 0");
+assertEq(view[997], 0, "997th value should be 0");
+assertEq(view[998], 0, "998th value should be 0");
+assertEq(view[999], 255, "999th value should be 255");
+assertEq(view[1000], 0, "1000th value should be 0");
+assertEq(view[1001], 0, "1001th value should be 0");
+assertEq(view[1002], 0, "1002th value should be 0");
+assertEq(view[1003], 255, "1003th value should be 255");
+assertEq(view[1004], 0, "1004th value should be 0");
+assertEq(view[1005], 0, "1005th value should be 0");
+assertEq(view[1006], 0, "1006th value should be 0");
+assertEq(view[1007], 255, "1007th value should be 255");
+assertEq(view[1008], 0, "1008th value should be 0");
+assertEq(view[1009], 0, "1009th value should be 0");
+assertEq(view[1010], 0, "1010th value should be 0");
+assertEq(view[1011], 255, "1011th value should be 255");
+assertEq(view[1012], 0, "1012th value should be 0");
+assertEq(view[1013], 0, "1013th value should be 0");
+assertEq(view[1014], 0, "1014th value should be 0");
+assertEq(view[1015], 255, "1015th value should be 255");
+assertEq(view[1016], 0, "1016th value should be 0");
+assertEq(view[1017], 0, "1017th value should be 0");
+assertEq(view[1018], 0, "1018th value should be 0");
+assertEq(view[1019], 255, "1019th value should be 255");
+assertEq(view[1020], 0, "1020th value should be 0");
+assertEq(view[1021], 0, "1021th value should be 0");
+assertEq(view[1022], 0, "1022th value should be 0");
+assertEq(view[1023], 255, "1023th value should be 255");
+assertEq(view[1024], 0, "1024th value should be 0");
+assertEq(view[1025], 0, "1025th value should be 0");
+assertEq(view[1026], 0, "1026th value should be 0");
+assertEq(view[1027], 255, "1027th value should be 255");
+assertEq(view[1028], 154, "1028th value should be 154");
+assertEq(view[1029], 255, "1029th value should be 255");
+assertEq(view[1030], 101, "1030th value should be 101");
+assertEq(view[1031], 255, "1031th value should be 255");
+assertEq(view[1032], 205, "1032th value should be 205");
+assertEq(view[1033], 255, "1033th value should be 255");
+assertEq(view[1034], 50, "1034th value should be 50");
+assertEq(view[1035], 255, "1035th value should be 255");
+assertEq(view[1036], 205, "1036th value should be 205");
+assertEq(view[1037], 255, "1037th value should be 255");
+assertEq(view[1038], 50, "1038th value should be 50");
+assertEq(view[1039], 255, "1039th value should be 255");
+assertEq(view[1040], 205, "1040th value should be 205");
+assertEq(view[1041], 255, "1041th value should be 255");
+assertEq(view[1042], 50, "1042th value should be 50");
+assertEq(view[1043], 255, "1043th value should be 255");
+assertEq(view[1044], 179, "1044th value should be 179");
+assertEq(view[1045], 127, "1045th value should be 127");
+assertEq(view[1046], 76, "1046th value should be 76");
+assertEq(view[1047], 255, "1047th value should be 255");
+assertEq(view[1048], 179, "1048th value should be 179");
+assertEq(view[1049], 127, "1049th value should be 127");
+assertEq(view[1050], 76, "1050th value should be 76");
+assertEq(view[1051], 255, "1051th value should be 255");
+assertEq(view[1052], 154, "1052th value should be 154");
+assertEq(view[1053], 255, "1053th value should be 255");
+assertEq(view[1054], 101, "1054th value should be 101");
+assertEq(view[1055], 255, "1055th value should be 255");
+assertEq(view[1056], 128, "1056th value should be 128");
+assertEq(view[1057], 127, "1057th value should be 127");
+assertEq(view[1058], 127, "1058th value should be 127");
+assertEq(view[1059], 255, "1059th value should be 255");
+assertEq(view[1060], 0, "1060th value should be 0");
+assertEq(view[1061], 0, "1061th value should be 0");
+assertEq(view[1062], 0, "1062th value should be 0");
+assertEq(view[1063], 255, "1063th value should be 255");
+assertEq(view[1064], 0, "1064th value should be 0");
+assertEq(view[1065], 0, "1065th value should be 0");
+assertEq(view[1066], 0, "1066th value should be 0");
+assertEq(view[1067], 255, "1067th value should be 255");
+assertEq(view[1068], 26, "1068th value should be 26");
+assertEq(view[1069], 127, "1069th value should be 127");
+assertEq(view[1070], 229, "1070th value should be 229");
+assertEq(view[1071], 255, "1071th value should be 255");
+assertEq(view[1072], 26, "1072th value should be 26");
+assertEq(view[1073], 127, "1073th value should be 127");
+assertEq(view[1074], 229, "1074th value should be 229");
+assertEq(view[1075], 255, "1075th value should be 255");
+assertEq(view[1076], 0, "1076th value should be 0");
+assertEq(view[1077], 0, "1077th value should be 0");
+assertEq(view[1078], 0, "1078th value should be 0");
+assertEq(view[1079], 255, "1079th value should be 255");
+assertEq(view[1080], 0, "1080th value should be 0");
+assertEq(view[1081], 0, "1081th value should be 0");
+assertEq(view[1082], 0, "1082th value should be 0");
+assertEq(view[1083], 255, "1083th value should be 255");
+assertEq(view[1084], 0, "1084th value should be 0");
+assertEq(view[1085], 0, "1085th value should be 0");
+assertEq(view[1086], 0, "1086th value should be 0");
+assertEq(view[1087], 255, "1087th value should be 255");
+assertEq(view[1088], 0, "1088th value should be 0");
+assertEq(view[1089], 0, "1089th value should be 0");
+assertEq(view[1090], 0, "1090th value should be 0");
+assertEq(view[1091], 255, "1091th value should be 255");
+assertEq(view[1092], 0, "1092th value should be 0");
+assertEq(view[1093], 0, "1093th value should be 0");
+assertEq(view[1094], 0, "1094th value should be 0");
+assertEq(view[1095], 255, "1095th value should be 255");
+assertEq(view[1096], 0, "1096th value should be 0");
+assertEq(view[1097], 0, "1097th value should be 0");
+assertEq(view[1098], 0, "1098th value should be 0");
+assertEq(view[1099], 255, "1099th value should be 255");
+assertEq(view[1100], 0, "1100th value should be 0");
+assertEq(view[1101], 0, "1101th value should be 0");
+assertEq(view[1102], 0, "1102th value should be 0");
+assertEq(view[1103], 255, "1103th value should be 255");
+assertEq(view[1104], 0, "1104th value should be 0");
+assertEq(view[1105], 0, "1105th value should be 0");
+assertEq(view[1106], 0, "1106th value should be 0");
+assertEq(view[1107], 255, "1107th value should be 255");
+assertEq(view[1108], 154, "1108th value should be 154");
+assertEq(view[1109], 255, "1109th value should be 255");
+assertEq(view[1110], 101, "1110th value should be 101");
+assertEq(view[1111], 255, "1111th value should be 255");
+assertEq(view[1112], 205, "1112th value should be 205");
+assertEq(view[1113], 255, "1113th value should be 255");
+assertEq(view[1114], 50, "1114th value should be 50");
+assertEq(view[1115], 255, "1115th value should be 255");
+assertEq(view[1116], 205, "1116th value should be 205");
+assertEq(view[1117], 255, "1117th value should be 255");
+assertEq(view[1118], 50, "1118th value should be 50");
+assertEq(view[1119], 255, "1119th value should be 255");
+assertEq(view[1120], 205, "1120th value should be 205");
+assertEq(view[1121], 255, "1121th value should be 255");
+assertEq(view[1122], 50, "1122th value should be 50");
+assertEq(view[1123], 255, "1123th value should be 255");
+assertEq(view[1124], 205, "1124th value should be 205");
+assertEq(view[1125], 255, "1125th value should be 255");
+assertEq(view[1126], 50, "1126th value should be 50");
+assertEq(view[1127], 255, "1127th value should be 255");
+assertEq(view[1128], 205, "1128th value should be 205");
+assertEq(view[1129], 255, "1129th value should be 255");
+assertEq(view[1130], 50, "1130th value should be 50");
+assertEq(view[1131], 255, "1131th value should be 255");
+assertEq(view[1132], 179, "1132th value should be 179");
+assertEq(view[1133], 127, "1133th value should be 127");
+assertEq(view[1134], 76, "1134th value should be 76");
+assertEq(view[1135], 255, "1135th value should be 255");
+assertEq(view[1136], 154, "1136th value should be 154");
+assertEq(view[1137], 255, "1137th value should be 255");
+assertEq(view[1138], 101, "1138th value should be 101");
+assertEq(view[1139], 255, "1139th value should be 255");
+assertEq(view[1140], 128, "1140th value should be 128");
+assertEq(view[1141], 127, "1141th value should be 127");
+assertEq(view[1142], 127, "1142th value should be 127");
+assertEq(view[1143], 255, "1143th value should be 255");
+assertEq(view[1144], 128, "1144th value should be 128");
+assertEq(view[1145], 127, "1145th value should be 127");
+assertEq(view[1146], 127, "1146th value should be 127");
+assertEq(view[1147], 255, "1147th value should be 255");
+assertEq(view[1148], 103, "1148th value should be 103");
+assertEq(view[1149], 255, "1149th value should be 255");
+assertEq(view[1150], 152, "1150th value should be 152");
+assertEq(view[1151], 255, "1151th value should be 255");
+assertEq(view[1152], 78, "1152th value should be 78");
+assertEq(view[1153], 127, "1153th value should be 127");
+assertEq(view[1154], 178, "1154th value should be 178");
+assertEq(view[1155], 255, "1155th value should be 255");
+assertEq(view[1156], 0, "1156th value should be 0");
+assertEq(view[1157], 0, "1157th value should be 0");
+assertEq(view[1158], 0, "1158th value should be 0");
+assertEq(view[1159], 255, "1159th value should be 255");
+assertEq(view[1160], 0, "1160th value should be 0");
+assertEq(view[1161], 0, "1161th value should be 0");
+assertEq(view[1162], 0, "1162th value should be 0");
+assertEq(view[1163], 255, "1163th value should be 255");
+assertEq(view[1164], 0, "1164th value should be 0");
+assertEq(view[1165], 0, "1165th value should be 0");
+assertEq(view[1166], 0, "1166th value should be 0");
+assertEq(view[1167], 255, "1167th value should be 255");
+assertEq(view[1168], 0, "1168th value should be 0");
+assertEq(view[1169], 0, "1169th value should be 0");
+assertEq(view[1170], 0, "1170th value should be 0");
+assertEq(view[1171], 255, "1171th value should be 255");
+assertEq(view[1172], 0, "1172th value should be 0");
+assertEq(view[1173], 0, "1173th value should be 0");
+assertEq(view[1174], 0, "1174th value should be 0");
+assertEq(view[1175], 255, "1175th value should be 255");
+assertEq(view[1176], 0, "1176th value should be 0");
+assertEq(view[1177], 0, "1177th value should be 0");
+assertEq(view[1178], 0, "1178th value should be 0");
+assertEq(view[1179], 255, "1179th value should be 255");
+assertEq(view[1180], 0, "1180th value should be 0");
+assertEq(view[1181], 0, "1181th value should be 0");
+assertEq(view[1182], 0, "1182th value should be 0");
+assertEq(view[1183], 255, "1183th value should be 255");
+assertEq(view[1184], 26, "1184th value should be 26");
+assertEq(view[1185], 127, "1185th value should be 127");
+assertEq(view[1186], 229, "1186th value should be 229");
+assertEq(view[1187], 255, "1187th value should be 255");
+assertEq(view[1188], 154, "1188th value should be 154");
+assertEq(view[1189], 255, "1189th value should be 255");
+assertEq(view[1190], 101, "1190th value should be 101");
+assertEq(view[1191], 255, "1191th value should be 255");
+assertEq(view[1192], 205, "1192th value should be 205");
+assertEq(view[1193], 255, "1193th value should be 255");
+assertEq(view[1194], 50, "1194th value should be 50");
+assertEq(view[1195], 255, "1195th value should be 255");
+assertEq(view[1196], 205, "1196th value should be 205");
+assertEq(view[1197], 255, "1197th value should be 255");
+assertEq(view[1198], 50, "1198th value should be 50");
+assertEq(view[1199], 255, "1199th value should be 255");
+assertEq(view[1200], 230, "1200th value should be 230");
+assertEq(view[1201], 127, "1201th value should be 127");
+assertEq(view[1202], 25, "1202th value should be 25");
+assertEq(view[1203], 255, "1203th value should be 255");
+assertEq(view[1204], 205, "1204th value should be 205");
+assertEq(view[1205], 255, "1205th value should be 255");
+assertEq(view[1206], 50, "1206th value should be 50");
+assertEq(view[1207], 255, "1207th value should be 255");
+assertEq(view[1208], 205, "1208th value should be 205");
+assertEq(view[1209], 255, "1209th value should be 255");
+assertEq(view[1210], 50, "1210th value should be 50");
+assertEq(view[1211], 255, "1211th value should be 255");
+assertEq(view[1212], 205, "1212th value should be 205");
+assertEq(view[1213], 255, "1213th value should be 255");
+assertEq(view[1214], 50, "1214th value should be 50");
+assertEq(view[1215], 255, "1215th value should be 255");
+assertEq(view[1216], 205, "1216th value should be 205");
+assertEq(view[1217], 255, "1217th value should be 255");
+assertEq(view[1218], 50, "1218th value should be 50");
+assertEq(view[1219], 255, "1219th value should be 255");
+assertEq(view[1220], 154, "1220th value should be 154");
+assertEq(view[1221], 255, "1221th value should be 255");
+assertEq(view[1222], 101, "1222th value should be 101");
+assertEq(view[1223], 255, "1223th value should be 255");
+assertEq(view[1224], 154, "1224th value should be 154");
+assertEq(view[1225], 255, "1225th value should be 255");
+assertEq(view[1226], 101, "1226th value should be 101");
+assertEq(view[1227], 255, "1227th value should be 255");
+assertEq(view[1228], 154, "1228th value should be 154");
+assertEq(view[1229], 255, "1229th value should be 255");
+assertEq(view[1230], 101, "1230th value should be 101");
+assertEq(view[1231], 255, "1231th value should be 255");
+assertEq(view[1232], 128, "1232th value should be 128");
+assertEq(view[1233], 127, "1233th value should be 127");
+assertEq(view[1234], 127, "1234th value should be 127");
+assertEq(view[1235], 255, "1235th value should be 255");
+assertEq(view[1236], 26, "1236th value should be 26");
+assertEq(view[1237], 127, "1237th value should be 127");
+assertEq(view[1238], 229, "1238th value should be 229");
+assertEq(view[1239], 255, "1239th value should be 255");
+assertEq(view[1240], 0, "1240th value should be 0");
+assertEq(view[1241], 0, "1241th value should be 0");
+assertEq(view[1242], 0, "1242th value should be 0");
+assertEq(view[1243], 255, "1243th value should be 255");
+assertEq(view[1244], 0, "1244th value should be 0");
+assertEq(view[1245], 0, "1245th value should be 0");
+assertEq(view[1246], 0, "1246th value should be 0");
+assertEq(view[1247], 255, "1247th value should be 255");
+assertEq(view[1248], 0, "1248th value should be 0");
+assertEq(view[1249], 0, "1249th value should be 0");
+assertEq(view[1250], 0, "1250th value should be 0");
+assertEq(view[1251], 255, "1251th value should be 255");
+assertEq(view[1252], 0, "1252th value should be 0");
+assertEq(view[1253], 0, "1253th value should be 0");
+assertEq(view[1254], 0, "1254th value should be 0");
+assertEq(view[1255], 255, "1255th value should be 255");
+assertEq(view[1256], 0, "1256th value should be 0");
+assertEq(view[1257], 0, "1257th value should be 0");
+assertEq(view[1258], 0, "1258th value should be 0");
+assertEq(view[1259], 255, "1259th value should be 255");
+assertEq(view[1260], 0, "1260th value should be 0");
+assertEq(view[1261], 0, "1261th value should be 0");
+assertEq(view[1262], 0, "1262th value should be 0");
+assertEq(view[1263], 255, "1263th value should be 255");
+assertEq(view[1264], 78, "1264th value should be 78");
+assertEq(view[1265], 127, "1265th value should be 127");
+assertEq(view[1266], 178, "1266th value should be 178");
+assertEq(view[1267], 255, "1267th value should be 255");
+assertEq(view[1268], 179, "1268th value should be 179");
+assertEq(view[1269], 127, "1269th value should be 127");
+assertEq(view[1270], 76, "1270th value should be 76");
+assertEq(view[1271], 255, "1271th value should be 255");
+assertEq(view[1272], 205, "1272th value should be 205");
+assertEq(view[1273], 255, "1273th value should be 255");
+assertEq(view[1274], 50, "1274th value should be 50");
+assertEq(view[1275], 255, "1275th value should be 255");
+assertEq(view[1276], 205, "1276th value should be 205");
+assertEq(view[1277], 255, "1277th value should be 255");
+assertEq(view[1278], 50, "1278th value should be 50");
+assertEq(view[1279], 255, "1279th value should be 255");
+assertEq(view[1280], 0, "1280th value should be 0");
+assertEq(view[1281], 0, "1281th value should be 0");
+assertEq(view[1282], 0, "1282th value should be 0");
+assertEq(view[1283], 255, "1283th value should be 255");
+assertEq(view[1284], 205, "1284th value should be 205");
+assertEq(view[1285], 255, "1285th value should be 255");
+assertEq(view[1286], 50, "1286th value should be 50");
+assertEq(view[1287], 255, "1287th value should be 255");
+assertEq(view[1288], 205, "1288th value should be 205");
+assertEq(view[1289], 255, "1289th value should be 255");
+assertEq(view[1290], 50, "1290th value should be 50");
+assertEq(view[1291], 255, "1291th value should be 255");
+assertEq(view[1292], 205, "1292th value should be 205");
+assertEq(view[1293], 255, "1293th value should be 255");
+assertEq(view[1294], 50, "1294th value should be 50");
+assertEq(view[1295], 255, "1295th value should be 255");
+assertEq(view[1296], 205, "1296th value should be 205");
+assertEq(view[1297], 255, "1297th value should be 255");
+assertEq(view[1298], 50, "1298th value should be 50");
+assertEq(view[1299], 255, "1299th value should be 255");
+assertEq(view[1300], 205, "1300th value should be 205");
+assertEq(view[1301], 255, "1301th value should be 255");
+assertEq(view[1302], 50, "1302th value should be 50");
+assertEq(view[1303], 255, "1303th value should be 255");
+assertEq(view[1304], 179, "1304th value should be 179");
+assertEq(view[1305], 127, "1305th value should be 127");
+assertEq(view[1306], 76, "1306th value should be 76");
+assertEq(view[1307], 255, "1307th value should be 255");
+assertEq(view[1308], 154, "1308th value should be 154");
+assertEq(view[1309], 255, "1309th value should be 255");
+assertEq(view[1310], 101, "1310th value should be 101");
+assertEq(view[1311], 255, "1311th value should be 255");
+assertEq(view[1312], 154, "1312th value should be 154");
+assertEq(view[1313], 255, "1313th value should be 255");
+assertEq(view[1314], 101, "1314th value should be 101");
+assertEq(view[1315], 255, "1315th value should be 255");
+assertEq(view[1316], 0, "1316th value should be 0");
+assertEq(view[1317], 0, "1317th value should be 0");
+assertEq(view[1318], 0, "1318th value should be 0");
+assertEq(view[1319], 255, "1319th value should be 255");
+assertEq(view[1320], 0, "1320th value should be 0");
+assertEq(view[1321], 0, "1321th value should be 0");
+assertEq(view[1322], 0, "1322th value should be 0");
+assertEq(view[1323], 255, "1323th value should be 255");
+assertEq(view[1324], 0, "1324th value should be 0");
+assertEq(view[1325], 0, "1325th value should be 0");
+assertEq(view[1326], 0, "1326th value should be 0");
+assertEq(view[1327], 255, "1327th value should be 255");
+assertEq(view[1328], 0, "1328th value should be 0");
+assertEq(view[1329], 0, "1329th value should be 0");
+assertEq(view[1330], 0, "1330th value should be 0");
+assertEq(view[1331], 255, "1331th value should be 255");
+assertEq(view[1332], 0, "1332th value should be 0");
+assertEq(view[1333], 0, "1333th value should be 0");
+assertEq(view[1334], 0, "1334th value should be 0");
+assertEq(view[1335], 255, "1335th value should be 255");
+assertEq(view[1336], 0, "1336th value should be 0");
+assertEq(view[1337], 0, "1337th value should be 0");
+assertEq(view[1338], 0, "1338th value should be 0");
+assertEq(view[1339], 255, "1339th value should be 255");
+assertEq(view[1340], 0, "1340th value should be 0");
+assertEq(view[1341], 0, "1341th value should be 0");
+assertEq(view[1342], 0, "1342th value should be 0");
+assertEq(view[1343], 255, "1343th value should be 255");
+assertEq(view[1344], 0, "1344th value should be 0");
+assertEq(view[1345], 0, "1345th value should be 0");
+assertEq(view[1346], 0, "1346th value should be 0");
+assertEq(view[1347], 255, "1347th value should be 255");
+assertEq(view[1348], 179, "1348th value should be 179");
+assertEq(view[1349], 127, "1349th value should be 127");
+assertEq(view[1350], 76, "1350th value should be 76");
+assertEq(view[1351], 255, "1351th value should be 255");
+assertEq(view[1352], 205, "1352th value should be 205");
+assertEq(view[1353], 255, "1353th value should be 255");
+assertEq(view[1354], 50, "1354th value should be 50");
+assertEq(view[1355], 255, "1355th value should be 255");
+assertEq(view[1356], 205, "1356th value should be 205");
+assertEq(view[1357], 255, "1357th value should be 255");
+assertEq(view[1358], 50, "1358th value should be 50");
+assertEq(view[1359], 255, "1359th value should be 255");
+assertEq(view[1360], 0, "1360th value should be 0");
+assertEq(view[1361], 0, "1361th value should be 0");
+assertEq(view[1362], 0, "1362th value should be 0");
+assertEq(view[1363], 255, "1363th value should be 255");
+assertEq(view[1364], 205, "1364th value should be 205");
+assertEq(view[1365], 255, "1365th value should be 255");
+assertEq(view[1366], 50, "1366th value should be 50");
+assertEq(view[1367], 255, "1367th value should be 255");
+assertEq(view[1368], 205, "1368th value should be 205");
+assertEq(view[1369], 255, "1369th value should be 255");
+assertEq(view[1370], 50, "1370th value should be 50");
+assertEq(view[1371], 255, "1371th value should be 255");
+assertEq(view[1372], 205, "1372th value should be 205");
+assertEq(view[1373], 255, "1373th value should be 255");
+assertEq(view[1374], 50, "1374th value should be 50");
+assertEq(view[1375], 255, "1375th value should be 255");
+assertEq(view[1376], 205, "1376th value should be 205");
+assertEq(view[1377], 255, "1377th value should be 255");
+assertEq(view[1378], 50, "1378th value should be 50");
+assertEq(view[1379], 255, "1379th value should be 255");
+assertEq(view[1380], 205, "1380th value should be 205");
+assertEq(view[1381], 255, "1381th value should be 255");
+assertEq(view[1382], 50, "1382th value should be 50");
+assertEq(view[1383], 255, "1383th value should be 255");
+assertEq(view[1384], 205, "1384th value should be 205");
+assertEq(view[1385], 255, "1385th value should be 255");
+assertEq(view[1386], 50, "1386th value should be 50");
+assertEq(view[1387], 255, "1387th value should be 255");
+assertEq(view[1388], 179, "1388th value should be 179");
+assertEq(view[1389], 127, "1389th value should be 127");
+assertEq(view[1390], 76, "1390th value should be 76");
+assertEq(view[1391], 255, "1391th value should be 255");
+assertEq(view[1392], 179, "1392th value should be 179");
+assertEq(view[1393], 127, "1393th value should be 127");
+assertEq(view[1394], 76, "1394th value should be 76");
+assertEq(view[1395], 255, "1395th value should be 255");
+assertEq(view[1396], 103, "1396th value should be 103");
+assertEq(view[1397], 255, "1397th value should be 255");
+assertEq(view[1398], 152, "1398th value should be 152");
+assertEq(view[1399], 255, "1399th value should be 255");
+assertEq(view[1400], 78, "1400th value should be 78");
+assertEq(view[1401], 127, "1401th value should be 127");
+assertEq(view[1402], 178, "1402th value should be 178");
+assertEq(view[1403], 255, "1403th value should be 255");
+assertEq(view[1404], 52, "1404th value should be 52");
+assertEq(view[1405], 255, "1405th value should be 255");
+assertEq(view[1406], 203, "1406th value should be 203");
+assertEq(view[1407], 255, "1407th value should be 255");
+assertEq(view[1408], 0, "1408th value should be 0");
+assertEq(view[1409], 0, "1409th value should be 0");
+assertEq(view[1410], 0, "1410th value should be 0");
+assertEq(view[1411], 255, "1411th value should be 255");
+assertEq(view[1412], 0, "1412th value should be 0");
+assertEq(view[1413], 0, "1413th value should be 0");
+assertEq(view[1414], 0, "1414th value should be 0");
+assertEq(view[1415], 255, "1415th value should be 255");
+assertEq(view[1416], 52, "1416th value should be 52");
+assertEq(view[1417], 255, "1417th value should be 255");
+assertEq(view[1418], 203, "1418th value should be 203");
+assertEq(view[1419], 255, "1419th value should be 255");
+assertEq(view[1420], 128, "1420th value should be 128");
+assertEq(view[1421], 127, "1421th value should be 127");
+assertEq(view[1422], 127, "1422th value should be 127");
+assertEq(view[1423], 255, "1423th value should be 255");
+assertEq(view[1424], 128, "1424th value should be 128");
+assertEq(view[1425], 127, "1425th value should be 127");
+assertEq(view[1426], 127, "1426th value should be 127");
+assertEq(view[1427], 255, "1427th value should be 255");
+assertEq(view[1428], 205, "1428th value should be 205");
+assertEq(view[1429], 255, "1429th value should be 255");
+assertEq(view[1430], 50, "1430th value should be 50");
+assertEq(view[1431], 255, "1431th value should be 255");
+assertEq(view[1432], 205, "1432th value should be 205");
+assertEq(view[1433], 255, "1433th value should be 255");
+assertEq(view[1434], 50, "1434th value should be 50");
+assertEq(view[1435], 255, "1435th value should be 255");
+assertEq(view[1436], 230, "1436th value should be 230");
+assertEq(view[1437], 127, "1437th value should be 127");
+assertEq(view[1438], 25, "1438th value should be 25");
+assertEq(view[1439], 255, "1439th value should be 255");
+assertEq(view[1440], 0, "1440th value should be 0");
+assertEq(view[1441], 0, "1441th value should be 0");
+assertEq(view[1442], 0, "1442th value should be 0");
+assertEq(view[1443], 255, "1443th value should be 255");
+assertEq(view[1444], 230, "1444th value should be 230");
+assertEq(view[1445], 127, "1445th value should be 127");
+assertEq(view[1446], 25, "1446th value should be 25");
+assertEq(view[1447], 255, "1447th value should be 255");
+assertEq(view[1448], 205, "1448th value should be 205");
+assertEq(view[1449], 255, "1449th value should be 255");
+assertEq(view[1450], 50, "1450th value should be 50");
+assertEq(view[1451], 255, "1451th value should be 255");
+assertEq(view[1452], 205, "1452th value should be 205");
+assertEq(view[1453], 255, "1453th value should be 255");
+assertEq(view[1454], 50, "1454th value should be 50");
+assertEq(view[1455], 255, "1455th value should be 255");
+assertEq(view[1456], 205, "1456th value should be 205");
+assertEq(view[1457], 255, "1457th value should be 255");
+assertEq(view[1458], 50, "1458th value should be 50");
+assertEq(view[1459], 255, "1459th value should be 255");
+assertEq(view[1460], 205, "1460th value should be 205");
+assertEq(view[1461], 255, "1461th value should be 255");
+assertEq(view[1462], 50, "1462th value should be 50");
+assertEq(view[1463], 255, "1463th value should be 255");
+assertEq(view[1464], 205, "1464th value should be 205");
+assertEq(view[1465], 255, "1465th value should be 255");
+assertEq(view[1466], 50, "1466th value should be 50");
+assertEq(view[1467], 255, "1467th value should be 255");
+assertEq(view[1468], 179, "1468th value should be 179");
+assertEq(view[1469], 127, "1469th value should be 127");
+assertEq(view[1470], 76, "1470th value should be 76");
+assertEq(view[1471], 255, "1471th value should be 255");
+assertEq(view[1472], 179, "1472th value should be 179");
+assertEq(view[1473], 127, "1473th value should be 127");
+assertEq(view[1474], 76, "1474th value should be 76");
+assertEq(view[1475], 255, "1475th value should be 255");
+assertEq(view[1476], 179, "1476th value should be 179");
+assertEq(view[1477], 127, "1477th value should be 127");
+assertEq(view[1478], 76, "1478th value should be 76");
+assertEq(view[1479], 255, "1479th value should be 255");
+assertEq(view[1480], 128, "1480th value should be 128");
+assertEq(view[1481], 127, "1481th value should be 127");
+assertEq(view[1482], 127, "1482th value should be 127");
+assertEq(view[1483], 255, "1483th value should be 255");
+assertEq(view[1484], 103, "1484th value should be 103");
+assertEq(view[1485], 255, "1485th value should be 255");
+assertEq(view[1486], 152, "1486th value should be 152");
+assertEq(view[1487], 255, "1487th value should be 255");
+assertEq(view[1488], 0, "1488th value should be 0");
+assertEq(view[1489], 0, "1489th value should be 0");
+assertEq(view[1490], 0, "1490th value should be 0");
+assertEq(view[1491], 255, "1491th value should be 255");
+assertEq(view[1492], 0, "1492th value should be 0");
+assertEq(view[1493], 0, "1493th value should be 0");
+assertEq(view[1494], 0, "1494th value should be 0");
+assertEq(view[1495], 255, "1495th value should be 255");
+assertEq(view[1496], 128, "1496th value should be 128");
+assertEq(view[1497], 127, "1497th value should be 127");
+assertEq(view[1498], 127, "1498th value should be 127");
+assertEq(view[1499], 255, "1499th value should be 255");
+assertEq(view[1500], 154, "1500th value should be 154");
+assertEq(view[1501], 255, "1501th value should be 255");
+assertEq(view[1502], 101, "1502th value should be 101");
+assertEq(view[1503], 255, "1503th value should be 255");
+assertEq(view[1504], 179, "1504th value should be 179");
+assertEq(view[1505], 127, "1505th value should be 127");
+assertEq(view[1506], 76, "1506th value should be 76");
+assertEq(view[1507], 255, "1507th value should be 255");
+assertEq(view[1508], 205, "1508th value should be 205");
+assertEq(view[1509], 255, "1509th value should be 255");
+assertEq(view[1510], 50, "1510th value should be 50");
+assertEq(view[1511], 255, "1511th value should be 255");
+assertEq(view[1512], 205, "1512th value should be 205");
+assertEq(view[1513], 255, "1513th value should be 255");
+assertEq(view[1514], 50, "1514th value should be 50");
+assertEq(view[1515], 255, "1515th value should be 255");
+assertEq(view[1516], 230, "1516th value should be 230");
+assertEq(view[1517], 127, "1517th value should be 127");
+assertEq(view[1518], 25, "1518th value should be 25");
+assertEq(view[1519], 255, "1519th value should be 255");
+assertEq(view[1520], 0, "1520th value should be 0");
+assertEq(view[1521], 0, "1521th value should be 0");
+assertEq(view[1522], 0, "1522th value should be 0");
+assertEq(view[1523], 255, "1523th value should be 255");
+assertEq(view[1524], 230, "1524th value should be 230");
+assertEq(view[1525], 127, "1525th value should be 127");
+assertEq(view[1526], 25, "1526th value should be 25");
+assertEq(view[1527], 255, "1527th value should be 255");
+assertEq(view[1528], 230, "1528th value should be 230");
+assertEq(view[1529], 127, "1529th value should be 127");
+assertEq(view[1530], 25, "1530th value should be 25");
+assertEq(view[1531], 255, "1531th value should be 255");
+assertEq(view[1532], 205, "1532th value should be 205");
+assertEq(view[1533], 255, "1533th value should be 255");
+assertEq(view[1534], 50, "1534th value should be 50");
+assertEq(view[1535], 255, "1535th value should be 255");
+assertEq(view[1536], 205, "1536th value should be 205");
+assertEq(view[1537], 255, "1537th value should be 255");
+assertEq(view[1538], 50, "1538th value should be 50");
+assertEq(view[1539], 255, "1539th value should be 255");
+assertEq(view[1540], 205, "1540th value should be 205");
+assertEq(view[1541], 255, "1541th value should be 255");
+assertEq(view[1542], 50, "1542th value should be 50");
+assertEq(view[1543], 255, "1543th value should be 255");
+assertEq(view[1544], 205, "1544th value should be 205");
+assertEq(view[1545], 255, "1545th value should be 255");
+assertEq(view[1546], 50, "1546th value should be 50");
+assertEq(view[1547], 255, "1547th value should be 255");
+assertEq(view[1548], 205, "1548th value should be 205");
+assertEq(view[1549], 255, "1549th value should be 255");
+assertEq(view[1550], 50, "1550th value should be 50");
+assertEq(view[1551], 255, "1551th value should be 255");
+assertEq(view[1552], 179, "1552th value should be 179");
+assertEq(view[1553], 127, "1553th value should be 127");
+assertEq(view[1554], 76, "1554th value should be 76");
+assertEq(view[1555], 255, "1555th value should be 255");
+assertEq(view[1556], 179, "1556th value should be 179");
+assertEq(view[1557], 127, "1557th value should be 127");
+assertEq(view[1558], 76, "1558th value should be 76");
+assertEq(view[1559], 255, "1559th value should be 255");
+assertEq(view[1560], 179, "1560th value should be 179");
+assertEq(view[1561], 127, "1561th value should be 127");
+assertEq(view[1562], 76, "1562th value should be 76");
+assertEq(view[1563], 255, "1563th value should be 255");
+assertEq(view[1564], 154, "1564th value should be 154");
+assertEq(view[1565], 255, "1565th value should be 255");
+assertEq(view[1566], 101, "1566th value should be 101");
+assertEq(view[1567], 255, "1567th value should be 255");
+assertEq(view[1568], 26, "1568th value should be 26");
+assertEq(view[1569], 127, "1569th value should be 127");
+assertEq(view[1570], 229, "1570th value should be 229");
+assertEq(view[1571], 255, "1571th value should be 255");
+assertEq(view[1572], 0, "1572th value should be 0");
+assertEq(view[1573], 0, "1573th value should be 0");
+assertEq(view[1574], 0, "1574th value should be 0");
+assertEq(view[1575], 255, "1575th value should be 255");
+assertEq(view[1576], 154, "1576th value should be 154");
+assertEq(view[1577], 255, "1577th value should be 255");
+assertEq(view[1578], 101, "1578th value should be 101");
+assertEq(view[1579], 255, "1579th value should be 255");
+assertEq(view[1580], 179, "1580th value should be 179");
+assertEq(view[1581], 127, "1581th value should be 127");
+assertEq(view[1582], 76, "1582th value should be 76");
+assertEq(view[1583], 255, "1583th value should be 255");
+assertEq(view[1584], 205, "1584th value should be 205");
+assertEq(view[1585], 255, "1585th value should be 255");
+assertEq(view[1586], 50, "1586th value should be 50");
+assertEq(view[1587], 255, "1587th value should be 255");
+assertEq(view[1588], 205, "1588th value should be 205");
+assertEq(view[1589], 255, "1589th value should be 255");
+assertEq(view[1590], 50, "1590th value should be 50");
+assertEq(view[1591], 255, "1591th value should be 255");
+assertEq(view[1592], 230, "1592th value should be 230");
+assertEq(view[1593], 127, "1593th value should be 127");
+assertEq(view[1594], 25, "1594th value should be 25");
+assertEq(view[1595], 255, "1595th value should be 255");
+assertEq(view[1596], 230, "1596th value should be 230");
+assertEq(view[1597], 127, "1597th value should be 127");
+assertEq(view[1598], 25, "1598th value should be 25");
+assertEq(view[1599], 255, "1599th value should be 255");
+
+// Code used to generate the assertEq list above.
+function generateAssertList() {
+  function template(i, x) {
+    return 'assertEq(view[' + i + '], ' + x + ', "' + i + 'th value should be ' + x + '");\n';
+  }
+  var buf = ''
+  for (var i = 0; i < LIMIT_SHOW; i++)
+      buf += template(i, view[i]);
+  print(buf);
+}
+//generateAssertList();
--- a/js/src/jit-test/tests/asm.js/testStackWalking.js
+++ b/js/src/jit-test/tests/asm.js/testStackWalking.js
@@ -60,17 +60,17 @@ try {
 } catch (e) {
     caught = true;
     matchStack(e.stack, ['asmRec', 'asmRec', 'asmRec', 'asmRec']);
 }
 assertEq(caught, true);
 
 var caught = false;
 try {
-    callFFI(null, {ffi:Object.preventExtensions})();
+    callFFI(null, {ffi:Object.defineProperty})();
 } catch (e) {
     caught = true;
 }
 assertEq(caught, true);
 
 asmLink(callFFI, null, {ffi:eval})();
 asmLink(callFFI, null, {ffi:Function})();
 asmLink(callFFI, null, {ffi:Error})();
rename from js/src/jit-test/tests/sharedbuf/bug1068458-toolong.js
rename to js/src/jit-test/tests/sharedbuf/toolong.js
--- a/js/src/jit-test/tests/sharedbuf/bug1068458-toolong.js
+++ b/js/src/jit-test/tests/sharedbuf/toolong.js
@@ -1,14 +1,22 @@
-// This used to assert, now it will throw because the limit
-// (in bytes) on a SharedArrayBuffer is INT32_MAX.
+// Various cases that used to assert, but will now throw (as they should).
 
 if (!this.SharedUint16Array)
     quit();
 
 var thrown = false;
 try {
-    new SharedUint16Array(2147483647);
+    new SharedUint16Array(2147483647); // Bug 1068458
 }
 catch (e) {
     thrown = true;
 }
 assertEq(thrown, true);
+
+var thrown = false;
+try {
+    new SharedUint16Array(0xdeadbeef); // Bug 1072176
+}
+catch (e) {
+    thrown = true;
+}
+assertEq(thrown, true);
--- a/js/src/jit/BacktrackingAllocator.cpp
+++ b/js/src/jit/BacktrackingAllocator.cpp
@@ -1123,17 +1123,17 @@ BacktrackingAllocator::reifyAllocations(
                 if (LDefinition *def = FindReusingDefinition(ins, alloc)) {
                     LiveInterval *outputInterval =
                         vregs[def->virtualRegister()].intervalFor(outputOf(ins));
                     LAllocation *res = outputInterval->getAllocation();
                     LAllocation *sourceAlloc = interval->getAllocation();
 
                     if (*res != *alloc) {
                         LMoveGroup *group = getInputMoveGroup(inputOf(ins));
-                        if (!group->addAfter(sourceAlloc, res, def->type()))
+                        if (!group->addAfter(sourceAlloc, res, reg->type()))
                             return false;
                         *alloc = *res;
                     }
                 }
             }
 
             addLiveRegistersForInterval(reg, interval);
         }
--- a/js/src/jit/LIR.cpp
+++ b/js/src/jit/LIR.cpp
@@ -343,16 +343,18 @@ LAllocation::aliases(const LAllocation &
 static const char * const TypeChars[] =
 {
     "g",            // GENERAL
     "i",            // INT32
     "o",            // OBJECT
     "s",            // SLOTS
     "f",            // FLOAT32
     "d",            // DOUBLE
+    "i32x4",        // INT32X4
+    "f32x4",        // FLOAT32X4
 #ifdef JS_NUNBOX32
     "t",            // TYPE
     "p"             // PAYLOAD
 #elif JS_PUNBOX64
     "x"             // BOX
 #endif
 };
 
@@ -536,19 +538,35 @@ LInstruction::initSafepoint(TempAllocato
     safepoint_ = new(alloc) LSafepoint(alloc);
     JS_ASSERT(safepoint_);
 }
 
 bool
 LMoveGroup::add(LAllocation *from, LAllocation *to, LDefinition::Type type)
 {
 #ifdef DEBUG
-    JS_ASSERT(*from != *to);
+    MOZ_ASSERT(*from != *to);
     for (size_t i = 0; i < moves_.length(); i++)
-        JS_ASSERT(*to != *moves_[i].to());
+        MOZ_ASSERT(*to != *moves_[i].to());
+
+    // Check that SIMD moves are aligned according to ABI requirements.
+    if (LDefinition(type).isSimdType()) {
+        if (from->isMemory()) {
+            if (from->isArgument())
+                MOZ_ASSERT(from->toArgument()->index() % SimdStackAlignment == 0);
+            else
+                MOZ_ASSERT(from->toStackSlot()->slot() % SimdStackAlignment == 0);
+        }
+        if (to->isMemory()) {
+            if (to->isArgument())
+                MOZ_ASSERT(to->toArgument()->index() % SimdStackAlignment == 0);
+            else
+                MOZ_ASSERT(to->toStackSlot()->slot() % SimdStackAlignment == 0);
+        }
+    }
 #endif
     return moves_.append(LMove(from, to, type));
 }
 
 bool
 LMoveGroup::addAfter(LAllocation *from, LAllocation *to, LDefinition::Type type)
 {
     // Transform the operands to this move so that performing the result
@@ -577,13 +595,17 @@ LMoveGroup::addAfter(LAllocation *from, 
 
 void
 LMoveGroup::printOperands(FILE *fp)
 {
     for (size_t i = 0; i < numMoves(); i++) {
         const LMove &move = getMove(i);
         // Use two printfs, as LAllocation::toString is not reentrant.
         fprintf(fp, " [%s", move.from()->toString());
-        fprintf(fp, " -> %s]", move.to()->toString());
+        fprintf(fp, " -> %s", move.to()->toString());
+#ifdef DEBUG
+        fprintf(fp, ", %s", TypeChars[move.type()]);
+#endif
+        fprintf(fp, "]");
         if (i != numMoves() - 1)
             fprintf(fp, ",");
     }
 }
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -411,16 +411,17 @@ class LDefinition
         // A random register of an appropriate class will be assigned.
         REGISTER,
 
         // One definition per instruction must re-use the first input
         // allocation, which (for now) must be a register.
         MUST_REUSE_INPUT
     };
 
+    // This should be kept in sync with LIR.cpp's TypeChars.
     enum Type {
         GENERAL,    // Generic, integer or pointer-width data (GPR).
         INT32,      // int32 data (GPR).
         OBJECT,     // Pointer that may be collected as garbage (GPR).
         SLOTS,      // Slots/elements pointer that may be moved by minor GCs (GPR).
         FLOAT32,    // 32-bit floating-point value (FPU).
         DOUBLE,     // 64-bit floating-point value (FPU).
         INT32X4,    // SIMD data containing four 32-bit integers (FPU).
--- a/js/src/jit/RegisterAllocator.cpp
+++ b/js/src/jit/RegisterAllocator.cpp
@@ -402,17 +402,17 @@ AllocationIntegrityState::dump()
             fprintf(stderr, "[");
             if (input != CodePosition::MIN)
                 fprintf(stderr, "%u,%u ", input.bits(), output.bits());
             fprintf(stderr, "%s]", ins->opName());
 
             if (ins->isMoveGroup()) {
                 LMoveGroup *group = ins->toMoveGroup();
                 for (int i = group->numMoves() - 1; i >= 0; i--) {
-                    // Use two printfs, as LAllocation::toString is not reentant.
+                    // Use two printfs, as LAllocation::toString is not reentrant.
                     fprintf(stderr, " [%s", group->getMove(i).from()->toString());
                     fprintf(stderr, " -> %s]", group->getMove(i).to()->toString());
                 }
                 fprintf(stderr, "\n");
                 continue;
             }
 
             for (size_t i = 0; i < ins->numDefs(); i++)
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -737,19 +737,16 @@ random_generateSeed()
      */
     rand_s(&seed.u32[0]);
 #elif defined(XP_UNIX)
     /*
      * In the unlikely event we can't read /dev/urandom, there's not much we can
      * do, so just mix in the fd error code and the current time.
      */
     int fd = open("/dev/urandom", O_RDONLY);
-    if (fd < 0) {
-        perror("FITZGEN: error opening /dev/urandom: ");
-    }
     MOZ_ASSERT(fd >= 0, "Can't open /dev/urandom");
     if (fd >= 0) {
         (void)read(fd, seed.u8, mozilla::ArrayLength(seed.u8));
         close(fd);
     }
     seed.u32[0] ^= fd;
 #else
 # error "Platform needs to implement random_generateSeed()"
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1520,18 +1520,20 @@ JSObject *
 js::NewObjectWithGivenProto(ExclusiveContext *cxArg, const js::Class *clasp,
                             js::TaggedProto protoArg, JSObject *parentArg,
                             gc::AllocKind allocKind, NewObjectKind newKind)
 {
     if (CanBeFinalizedInBackground(allocKind, clasp))
         allocKind = GetBackgroundAllocKind(allocKind);
 
     NewObjectCache::EntryIndex entry = -1;
+    uint64_t gcNumber = 0;
     if (JSContext *cx = cxArg->maybeJSContext()) {
-        NewObjectCache &cache = cx->runtime()->newObjectCache;
+        JSRuntime *rt = cx->runtime();
+        NewObjectCache &cache = rt->newObjectCache;
         if (protoArg.isObject() &&
             newKind == GenericObject &&
             !cx->compartment()->hasObjectMetadataCallback() &&
             (!parentArg || parentArg == protoArg.toObject()->getParent()) &&
             !protoArg.toObject()->is<GlobalObject>())
         {
             if (cache.lookupProto(clasp, protoArg.toObject(), allocKind, &entry)) {
                 JSObject *obj = cache.newObjectFromHit<NoGC>(cx, entry, GetInitialHeap(newKind, clasp));
@@ -1542,16 +1544,17 @@ js::NewObjectWithGivenProto(ExclusiveCon
                     RootedObject parent(cxArg, parentArg);
                     obj = cache.newObjectFromHit<CanGC>(cx, entry, GetInitialHeap(newKind, clasp));
                     JS_ASSERT(!obj);
                     parentArg = parent;
                     protoArg = proto;
                 }
             }
         }
+        gcNumber = rt->gc.gcNumber();
     }
 
     Rooted<TaggedProto> proto(cxArg, protoArg);
     RootedObject parent(cxArg, parentArg);
 
     types::TypeObject *type = cxArg->getNewType(clasp, proto, nullptr);
     if (!type)
         return nullptr;
@@ -1562,17 +1565,19 @@ js::NewObjectWithGivenProto(ExclusiveCon
      */
     if (!parent && proto.isObject())
         parent = proto.toObject()->getParent();
 
     RootedObject obj(cxArg, NewObject(cxArg, type, parent, allocKind, newKind));
     if (!obj)
         return nullptr;
 
-    if (entry != -1 && !obj->hasDynamicSlots()) {
+    if (entry != -1 && !obj->hasDynamicSlots() &&
+        cxArg->asJSContext()->runtime()->gc.gcNumber() == gcNumber)
+    {
         cxArg->asJSContext()->runtime()->newObjectCache.fillProto(entry, clasp,
                                                                   proto, allocKind, obj);
     }
 
     return obj;
 }
 
 static JSProtoKey
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Object/preventExtensions.js
@@ -0,0 +1,21 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var BUGNUMBER = 1073446;
+var summary = "Object.preventExtensions() should return its argument with no conversion when the argument is a primitive value";
+
+print(BUGNUMBER + ": " + summary);
+assertEq(Object.preventExtensions(), undefined);
+assertEq(Object.preventExtensions(undefined), undefined);
+assertEq(Object.preventExtensions(null), null);
+assertEq(Object.preventExtensions(1), 1);
+assertEq(Object.preventExtensions("foo"), "foo");
+assertEq(Object.preventExtensions(true), true);
+if (typeof Symbol === "function") {
+    assertEq(Object.preventExtensions(Symbol.for("foo")), Symbol.for("foo"));
+}
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -310,28 +310,33 @@ class NewObjectCache
         JS_ASSERT(obj->type() == type);
         return fill(entry, type->clasp(), type, kind, obj);
     }
 
     /* Invalidate any entries which might produce an object with shape/proto. */
     void invalidateEntriesForShape(JSContext *cx, HandleShape shape, HandleObject proto);
 
   private:
-    bool lookup(const Class *clasp, gc::Cell *key, gc::AllocKind kind, EntryIndex *pentry) {
+    EntryIndex makeIndex(const Class *clasp, gc::Cell *key, gc::AllocKind kind) {
         uintptr_t hash = (uintptr_t(clasp) ^ uintptr_t(key)) + kind;
-        *pentry = hash % mozilla::ArrayLength(entries);
+        return hash % mozilla::ArrayLength(entries);
+    }
 
+    bool lookup(const Class *clasp, gc::Cell *key, gc::AllocKind kind, EntryIndex *pentry) {
+        *pentry = makeIndex(clasp, key, kind);
         Entry *entry = &entries[*pentry];
 
         /* N.B. Lookups with the same clasp/key but different kinds map to different entries. */
         return entry->clasp == clasp && entry->key == key;
     }
 
-    void fill(EntryIndex entry_, const Class *clasp, gc::Cell *key, gc::AllocKind kind, JSObject *obj) {
+    void fill(EntryIndex entry_, const Class *clasp, gc::Cell *key, gc::AllocKind kind,
+              JSObject *obj) {
         JS_ASSERT(unsigned(entry_) < mozilla::ArrayLength(entries));
+        JS_ASSERT(entry_ == makeIndex(clasp, key, kind));
         Entry *entry = &entries[entry_];
 
         JS_ASSERT(!obj->hasDynamicSlots() && !obj->hasDynamicElements());
 
         entry->clasp = clasp;
         entry->key = key;
         entry->kind = kind;
 
--- a/js/src/vm/SharedTypedArrayObject.cpp
+++ b/js/src/vm/SharedTypedArrayObject.cpp
@@ -140,16 +140,18 @@ class SharedTypedArrayObjectTemplate : p
         obj->setType(type);
 
         return &obj->as<SharedTypedArrayObject>();
     }
 
     static SharedTypedArrayObject *
     makeTypedInstance(JSContext *cx, uint32_t len, AllocKind allocKind)
     {
+        JS_ASSERT(len <= MAX_LENGTH / sizeof(NativeType));
+
         // Multiplication is safe due to preconditions for makeInstance().
         if (len * sizeof(NativeType) >= SharedTypedArrayObject::SINGLETON_TYPE_BYTE_LENGTH) {
             return &NewBuiltinClassInstance(cx, instanceClass(), allocKind,
                                             SingletonObject)->as<SharedTypedArrayObject>();
         }
 
         jsbytecode *pc;
         RootedScript script(cx, cx->currentScript(&pc));
@@ -168,18 +170,18 @@ class SharedTypedArrayObjectTemplate : p
         return &obj->as<SharedTypedArrayObject>();
     }
 
     static JSObject *
     makeInstance(JSContext *cx, Handle<SharedArrayBufferObject *> buffer, uint32_t byteOffset, uint32_t len,
                  HandleObject proto)
     {
         JS_ASSERT(buffer);
+        JS_ASSERT(byteOffset <= MAX_BYTEOFFSET);
         JS_ASSERT(len <= MAX_LENGTH / sizeof(NativeType));
-        JS_ASSERT(byteOffset <= MAX_BYTEOFFSET);
 
         gc::AllocKind allocKind = GetGCObjectKind(instanceClass());
 
         Rooted<SharedTypedArrayObject*> obj(cx);
         if (proto)
             obj = makeProtoInstance(cx, proto, allocKind);
         else
             obj = makeTypedInstance(cx, len, allocKind);
@@ -299,28 +301,16 @@ class SharedTypedArrayObjectTemplate : p
                     return nullptr;
                 }
             }
         }
 
         return fromBuffer(cx, dataObj, byteOffset, length);
     }
 
-    static bool
-    createArrayBuffer(JSContext *cx, uint32_t nelements, MutableHandle<SharedArrayBufferObject *> buffer)
-    {
-        if (nelements > MAX_LENGTH / sizeof(NativeType)) {
-            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
-                                 JSMSG_NEED_DIET, "shared typed array");
-            return false;
-        }
-        buffer.set(SharedArrayBufferObject::New(cx, nelements * sizeof(NativeType)));
-        return !!buffer;
-    }
-
     template<Value ValueGetter(SharedTypedArrayObject *tarr)>
     static bool
     GetterImpl(JSContext *cx, CallArgs args)
     {
         JS_ASSERT(is(args.thisv()));
         args.rval().set(ValueGetter(&args.thisv().toObject().as<SharedTypedArrayObject>()));
         return true;
     }
@@ -407,81 +397,79 @@ class SharedTypedArrayObjectTemplate : p
     setIndex(SharedTypedArrayObject &tarray, uint32_t index, NativeType val)
     {
         MOZ_ASSERT(index < tarray.length());
         static_cast<NativeType*>(tarray.viewData())[index] = val;
     }
 
     static Value getIndexValue(JSObject *tarray, uint32_t index);
 
+    static bool fun_subarray(JSContext *cx, unsigned argc, Value *vp);
+    static bool fun_copyWithin(JSContext *cx, unsigned argc, Value *vp);
+    static bool fun_set(JSContext *cx, unsigned argc, Value *vp);
+
+  public:
     static JSObject *
     fromBufferWithProto(JSContext *cx, HandleObject bufobj, uint32_t byteOffset, uint32_t length,
                         HandleObject proto)
     {
         if (!ObjectClassIs(bufobj, ESClass_SharedArrayBuffer, cx)) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SHARED_TYPED_ARRAY_BAD_OBJECT);
             return nullptr; // must be SharedArrayBuffer
         }
 
-        JS_ASSERT(IsSharedArrayBuffer(bufobj) || bufobj->is<ProxyObject>());
-
         if (bufobj->is<ProxyObject>()) {
             // Complicated, see TypedArrayObject.cpp for code.  For now, punt.
             JS_ReportError(cx, "Permission denied to access object");
             return nullptr;
         }
 
         Rooted<SharedArrayBufferObject *> buffer(cx, &AsSharedArrayBuffer(bufobj));
 
         if (byteOffset > buffer->byteLength() || byteOffset % sizeof(NativeType) != 0) {
             // Invalid byteOffset.
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SHARED_TYPED_ARRAY_BAD_ARGS);
             return nullptr;
         }
 
-        JS_ASSERT(byteOffset <= buffer->byteLength());
         uint32_t bytesAvailable = buffer->byteLength() - byteOffset;
 
         if (length == LENGTH_NOT_PROVIDED) {
             if (bytesAvailable % sizeof(NativeType) != 0) {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SHARED_TYPED_ARRAY_BAD_ARGS);
                 return nullptr;
             }
             length = bytesAvailable / sizeof(NativeType);
         }
 
         if (length > MAX_LENGTH / sizeof(NativeType) || length * sizeof(NativeType) > bytesAvailable) {
-            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SHARED_TYPED_ARRAY_BAD_ARGS);
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
             return nullptr;
         }
 
         return makeInstance(cx, buffer, byteOffset, length, proto);
     }
 
-    static bool fun_subarray(JSContext *cx, unsigned argc, Value *vp);
-    static bool fun_copyWithin(JSContext *cx, unsigned argc, Value *vp);
-    static bool fun_set(JSContext *cx, unsigned argc, Value *vp);
-
-  public:
     static JSObject *
     fromBuffer(JSContext *cx, HandleObject bufobj, uint32_t byteOffset, uint32_t length)
     {
-        JS_ASSERT(byteOffset <= MAX_BYTEOFFSET);
-        JS_ASSERT(length <= MAX_LENGTH || length == LENGTH_NOT_PROVIDED);
         RootedObject proto(cx, nullptr);
         return fromBufferWithProto(cx, bufobj, byteOffset, length, proto);
     }
 
     static JSObject *
     fromLength(JSContext *cx, uint32_t nelements)
     {
-        JS_ASSERT(nelements <= MAX_LENGTH);
-        Rooted<SharedArrayBufferObject *> buffer(cx);
-        // Invariant established by createArrayBuffer(): nelements * sizeof(NativeType) <= MAX_LENGTH.
-        if (!createArrayBuffer(cx, nelements, &buffer))
+        if (nelements > MAX_LENGTH / sizeof(NativeType)) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
+            return nullptr;
+        }
+        Rooted<SharedArrayBufferObject *> buffer(
+            cx, SharedArrayBufferObject::New(cx, nelements * sizeof(NativeType)));
+        if (!buffer)
             return nullptr;
         return makeInstance(cx, buffer, 0, nelements);
     }
 };
 
 class SharedInt8ArrayObject : public SharedTypedArrayObjectTemplate<int8_t> {
   public:
     enum { ACTUAL_TYPE = Scalar::Int8 };
--- a/layout/reftests/svg/reftest.list
+++ b/layout/reftests/svg/reftest.list
@@ -321,17 +321,17 @@ fuzzy-if(OSX==10.7,6,2) fuzzy-if(OSX==10
 == text-layout-05.svg text-layout-05-ref.svg
 fuzzy-if(cocoaWidget&&layersGPUAccelerated,1,3) == text-layout-06.svg text-layout-06-ref.svg
 == text-layout-07.svg text-layout-07-ref.svg
 == text-layout-08.svg text-layout-08-ref.svg
 == text-scale-01.svg text-scale-01-ref.svg
 HTTP(..) == text-scale-02.svg text-scale-02-ref.svg
 HTTP(..) == text-scale-03.svg text-scale-03-ref.svg
 == text-stroke-scaling-01.svg text-stroke-scaling-01-ref.svg
-fails-if(OSX==10.8) == stroke-dasharray-01.svg stroke-dasharray-01-ref.svg # bug 896487
+== stroke-dasharray-01.svg stroke-dasharray-01-ref.svg
 == stroke-dasharray-02.svg pass.svg
 == stroke-dasharray-and-pathLength-01.svg pass.svg
 == stroke-dasharray-and-text-01.svg stroke-dasharray-and-text-01-ref.svg
 == stroke-dashoffset-01.svg pass.svg
 == stroke-linecap-square-w-zero-length-segs-01.svg pass.svg
 == stroke-linecap-square-w-zero-length-segs-02.svg pass.svg
 == textPath-01.svg textPath-01-ref.svg
 == textPath-02.svg pass.svg
--- a/layout/svg/SVGTextFrame.cpp
+++ b/layout/svg/SVGTextFrame.cpp
@@ -2704,17 +2704,17 @@ public:
    * @param aShouldPaintSVGGlyphs Whether SVG glyphs should be painted.
    */
   SVGTextDrawPathCallbacks(nsRenderingContext* aContext,
                            nsTextFrame* aFrame,
                            const gfxMatrix& aCanvasTM,
                            bool aShouldPaintSVGGlyphs)
     : DrawPathCallbacks(aShouldPaintSVGGlyphs),
       gfx(aContext->ThebesContext()),
-      mRenderMode(SVGAutoRenderState::GetRenderMode(aContext)),
+      mRenderMode(SVGAutoRenderState::GetRenderMode(aContext->GetDrawTarget())),
       mFrame(aFrame),
       mCanvasTM(aCanvasTM)
   {
   }
 
   void NotifyBeforeText(nscolor aColor) MOZ_OVERRIDE;
   void NotifyGlyphPathEmitted() MOZ_OVERRIDE;
   void NotifyBeforeSVGGlyphPainted() MOZ_OVERRIDE;
@@ -5112,17 +5112,18 @@ SVGTextFrame::DoGlyphPositioning()
 }
 
 bool
 SVGTextFrame::ShouldRenderAsPath(nsRenderingContext* aContext,
                                  nsTextFrame* aFrame,
                                  bool& aShouldPaintSVGGlyphs)
 {
   // Rendering to a clip path.
-  if (SVGAutoRenderState::GetRenderMode(aContext) != SVGAutoRenderState::NORMAL) {
+  if (SVGAutoRenderState::GetRenderMode(aContext->GetDrawTarget()) !=
+        SVGAutoRenderState::NORMAL) {
     aShouldPaintSVGGlyphs = false;
     return true;
   }
 
   aShouldPaintSVGGlyphs = true;
 
   const nsStyleSVG* style = aFrame->StyleSVG();
 
--- a/layout/svg/nsSVGClipPathFrame.cpp
+++ b/layout/svg/nsSVGClipPathFrame.cpp
@@ -89,17 +89,18 @@ nsSVGClipPathFrame::ApplyClipOrPaintClip
     gfx->Clip();
     gfx->NewPath();
     return NS_OK;
   }
 
   // Seems like this is a non-trivial clipPath, so we need to use a clip mask.
 
   // Notify our children that they're painting into a clip mask:
-  SVGAutoRenderState mode(aContext, SVGAutoRenderState::CLIP_MASK);
+  SVGAutoRenderState mode(aContext->GetDrawTarget(),
+                          SVGAutoRenderState::CLIP_MASK);
 
   // Check if this clipPath is itself clipped by another clipPath:
   nsSVGClipPathFrame *clipPathFrame =
     nsSVGEffects::GetEffectProperties(this).GetClipPathFrame(nullptr);
   bool referencedClipIsTrivial;
   if (clipPathFrame) {
     referencedClipIsTrivial = clipPathFrame->IsTrivial();
     gfx->Save();
--- a/layout/svg/nsSVGForeignObjectFrame.cpp
+++ b/layout/svg/nsSVGForeignObjectFrame.cpp
@@ -261,17 +261,17 @@ nsSVGForeignObjectFrame::PaintSVG(nsRend
   float cssPxPerDevPx = PresContext()->
     AppUnitsToFloatCSSPixels(PresContext()->AppUnitsPerDevPixel());
   gfxMatrix canvasTMForChildren = aTransform;
   canvasTMForChildren.Scale(cssPxPerDevPx, cssPxPerDevPx);
 
   gfx->Multiply(canvasTMForChildren);
 
   uint32_t flags = nsLayoutUtils::PAINT_IN_TRANSFORM;
-  if (SVGAutoRenderState::IsPaintingToWindow(aContext)) {
+  if (SVGAutoRenderState::IsPaintingToWindow(aContext->GetDrawTarget())) {
     flags |= nsLayoutUtils::PAINT_TO_WINDOW;
   }
   nsresult rv = nsLayoutUtils::PaintFrame(aContext, kid, nsRegion(kidDirtyRect),
                                           NS_RGBA(0,0,0,0), flags);
 
   gfx->Restore();
 
   return rv;
--- a/layout/svg/nsSVGGradientFrame.cpp
+++ b/layout/svg/nsSVGGradientFrame.cpp
@@ -258,23 +258,22 @@ nsSVGGradientFrame::GetPaintServerPatter
   // above since this call can be expensive when "gradientUnits" is set to
   // "objectBoundingBox" (since that requiring a GetBBox() call).
   gfxMatrix patternMatrix = GetGradientTransform(aSource, aOverrideBounds);
 
   if (patternMatrix.IsSingular()) {
     return nullptr;
   }
 
-  // revert the vector effect transform so that the gradient appears unchanged
+  // revert any vector effect transform so that the gradient appears unchanged
   if (aFillOrStroke == &nsStyleSVG::mStroke) {
-    gfxMatrix nonScalingStrokeTM = nsSVGUtils::GetStrokeTransform(aSource);
-    if (!nonScalingStrokeTM.Invert()) {
-      return nullptr;
+    gfxMatrix userToOuterSVG;
+    if (nsSVGUtils::GetNonScalingStrokeTransform(aSource, &userToOuterSVG)) {
+      patternMatrix *= userToOuterSVG;
     }
-    patternMatrix *= nonScalingStrokeTM;
   }
 
   if (!patternMatrix.Invert()) {
     return nullptr;
   }
 
   nsRefPtr<gfxPattern> gradient = CreateGradient();
   if (!gradient || gradient->CairoStatus())
--- a/layout/svg/nsSVGOuterSVGFrame.cpp
+++ b/layout/svg/nsSVGOuterSVGFrame.cpp
@@ -585,17 +585,18 @@ nsDisplayOuterSVG::Paint(nsDisplayListBu
                          nsRenderingContext* aContext)
 {
 #if defined(DEBUG) && defined(SVG_DEBUG_PAINT_TIMING)
   PRTime start = PR_Now();
 #endif
 
   // Create an SVGAutoRenderState so we can call SetPaintingToWindow on
   // it, but do so without changing the render mode:
-  SVGAutoRenderState state(aContext, SVGAutoRenderState::GetRenderMode(aContext));
+  SVGAutoRenderState state(aContext->GetDrawTarget(),
+    SVGAutoRenderState::GetRenderMode(aContext->GetDrawTarget()));
 
   if (aBuilder->IsPaintingToWindow()) {
     state.SetPaintingToWindow(true);
   }
 
   nsRect viewportRect =
     mFrame->GetContentRectRelativeToSelf() + ToReferenceFrame();
 
--- a/layout/svg/nsSVGPathGeometryFrame.cpp
+++ b/layout/svg/nsSVGPathGeometryFrame.cpp
@@ -7,16 +7,17 @@
 #include "nsSVGPathGeometryFrame.h"
 
 // Keep others in (case-insensitive) order:
 #include "gfx2DGlue.h"
 #include "gfxContext.h"
 #include "gfxPlatform.h"
 #include "gfxSVGGlyphs.h"
 #include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Helpers.h"
 #include "mozilla/RefPtr.h"
 #include "nsDisplayList.h"
 #include "nsGkAtoms.h"
 #include "nsLayoutUtils.h"
 #include "nsRenderingContext.h"
 #include "nsSVGEffects.h"
 #include "nsSVGIntegrationUtils.h"
 #include "nsSVGMarkerFrame.h"
@@ -102,21 +103,19 @@ nsDisplaySVGPathGeometry::Paint(nsDispla
   // ToReferenceFrame includes our mRect offset, but painting takes
   // account of that too. To avoid double counting, we subtract that
   // here.
   nsPoint offset = ToReferenceFrame() - mFrame->GetPosition();
 
   gfxPoint devPixelOffset =
     nsLayoutUtils::PointToGfxPoint(offset, appUnitsPerDevPixel);
 
-  aCtx->ThebesContext()->Save();
   gfxMatrix tm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(mFrame) *
                    gfxMatrix::Translation(devPixelOffset);
   static_cast<nsSVGPathGeometryFrame*>(mFrame)->PaintSVG(aCtx, tm);
-  aCtx->ThebesContext()->Restore();
 }
 
 //----------------------------------------------------------------------
 // nsIFrame methods
 
 void
 nsSVGPathGeometryFrame::Init(nsIContent*       aContent,
                              nsContainerFrame* aParent,
@@ -214,30 +213,39 @@ nsSVGPathGeometryFrame::BuildDisplayList
 nsresult
 nsSVGPathGeometryFrame::PaintSVG(nsRenderingContext *aContext,
                                  const gfxMatrix& aTransform,
                                  const nsIntRect* aDirtyRect)
 {
   if (!StyleVisibility()->IsVisible())
     return NS_OK;
 
+  gfxContext* gfx = aContext->ThebesContext();
+
+  // Matrix to the geometry's user space:
+  gfxMatrix newMatrix =
+    gfx->CurrentMatrix().PreMultiply(aTransform).NudgeToIntegers();
+  if (newMatrix.IsSingular()) {
+    return NS_OK;
+  }
+
   uint32_t paintOrder = StyleSVG()->mPaintOrder;
   if (paintOrder == NS_STYLE_PAINT_ORDER_NORMAL) {
-    Render(aContext, eRenderFill | eRenderStroke, aTransform);
+    Render(gfx, eRenderFill | eRenderStroke, newMatrix);
     PaintMarkers(aContext, aTransform);
   } else {
     while (paintOrder) {
       uint32_t component =
         paintOrder & ((1 << NS_STYLE_PAINT_ORDER_BITWIDTH) - 1);
       switch (component) {
         case NS_STYLE_PAINT_ORDER_FILL:
-          Render(aContext, eRenderFill, aTransform);
+          Render(gfx, eRenderFill, newMatrix);
           break;
         case NS_STYLE_PAINT_ORDER_STROKE:
-          Render(aContext, eRenderStroke, aTransform);
+          Render(gfx, eRenderStroke, newMatrix);
           break;
         case NS_STYLE_PAINT_ORDER_MARKERS:
           PaintMarkers(aContext, aTransform);
           break;
       }
       paintOrder >>= NS_STYLE_PAINT_ORDER_BITWIDTH;
     }
   }
@@ -289,28 +297,25 @@ nsSVGPathGeometryFrame::GetFrameForPoint
 
   if (hitTestFlags & SVG_HIT_TEST_FILL) {
     isHit = path->ContainsPoint(ToPoint(aPoint), Matrix());
   }
   if (!isHit && (hitTestFlags & SVG_HIT_TEST_STROKE)) {
     Point point = ToPoint(aPoint);
     SVGContentUtils::AutoStrokeOptions stroke;
     SVGContentUtils::GetStrokeOptions(&stroke, content, StyleContext(), nullptr);
-    Matrix nonScalingStrokeMatrix = ToMatrix(nsSVGUtils::GetStrokeTransform(this));
-    if (!nonScalingStrokeMatrix.IsIdentity()) {
+    gfxMatrix userToOuterSVG;
+    if (nsSVGUtils::GetNonScalingStrokeTransform(this, &userToOuterSVG)) {
       // We need to transform the path back into the appropriate ancestor
       // coordinate system in order for non-scaled stroke to be correct.
       // Naturally we also need to transform the point into the same
       // coordinate system in order to hit-test against the path.
-      if (!nonScalingStrokeMatrix.Invert()) {
-        return nullptr;
-      }
-      point = nonScalingStrokeMatrix * point;
+      point = ToMatrix(userToOuterSVG) * point;
       RefPtr<PathBuilder> builder =
-        path->TransformedCopyToBuilder(nonScalingStrokeMatrix, fillRule);
+        path->TransformedCopyToBuilder(ToMatrix(userToOuterSVG), fillRule);
       path = builder->Finish();
     }
     isHit = path->StrokeContainsPoint(stroke, point, Matrix());
   }
 
   if (isHit && nsSVGUtils::HitTestClip(this, aPoint))
     return this;
 
@@ -603,101 +608,103 @@ nsSVGPathGeometryFrame::MarkerProperties
 {
   if (!mMarkerEnd)
     return nullptr;
   return static_cast<nsSVGMarkerFrame *>
     (mMarkerEnd->GetReferencedFrame(nsGkAtoms::svgMarkerFrame, nullptr));
 }
 
 void
-nsSVGPathGeometryFrame::Render(nsRenderingContext *aContext,
+nsSVGPathGeometryFrame::Render(gfxContext* aContext,
                                uint32_t aRenderComponents,
-                               const gfxMatrix& aTransform)
+                               const gfxMatrix& aNewTransform)
 {
-  gfxContext *gfx = aContext->ThebesContext();
+  MOZ_ASSERT(!aNewTransform.IsSingular());
+
+  DrawTarget* drawTarget = aContext->GetDrawTarget();
 
-  gfxMatrix newMatrix =
-    gfx->CurrentMatrix().PreMultiply(aTransform).NudgeToIntegers();
-  if (newMatrix.IsSingular()) {
-    return;
-  }
+  uint16_t renderMode = SVGAutoRenderState::GetRenderMode(drawTarget);
+  MOZ_ASSERT(renderMode == SVGAutoRenderState::NORMAL ||
+             renderMode == SVGAutoRenderState::CLIP_MASK,
+             "Unknown render mode");
 
-  uint16_t renderMode = SVGAutoRenderState::GetRenderMode(aContext);
   FillRule fillRule =
-    nsSVGUtils::ToFillRule(renderMode == SVGAutoRenderState::CLIP_MASK ?
-                             StyleSVG()->mClipRule : StyleSVG()->mFillRule);
+    nsSVGUtils::ToFillRule(renderMode == SVGAutoRenderState::NORMAL ?
+                             StyleSVG()->mFillRule : StyleSVG()->mClipRule);
 
-  RefPtr<PathBuilder> builder =
-    aContext->GetDrawTarget()->CreatePathBuilder(fillRule);
+  RefPtr<PathBuilder> builder = drawTarget->CreatePathBuilder(fillRule);
   if (!builder) {
     return;
   }
 
   RefPtr<Path> path =
     static_cast<nsSVGPathGeometryElement*>(mContent)->BuildPath(builder);
   if (!path) {
     return;
   }
 
-  switch (StyleSVG()->mShapeRendering) {
-  case NS_STYLE_SHAPE_RENDERING_OPTIMIZESPEED:
-  case NS_STYLE_SHAPE_RENDERING_CRISPEDGES:
-    gfx->SetAntialiasMode(AntialiasMode::NONE);
-    break;
-  default:
-    gfx->SetAntialiasMode(AntialiasMode::SUBPIXEL);
-    break;
-  }
+  AntialiasMode aaMode =
+    (StyleSVG()->mShapeRendering == NS_STYLE_SHAPE_RENDERING_OPTIMIZESPEED ||
+     StyleSVG()->mShapeRendering == NS_STYLE_SHAPE_RENDERING_CRISPEDGES) ?
+    AntialiasMode::NONE : AntialiasMode::SUBPIXEL;
+
+  // We wait as late as possible before setting the transform so that we don't
+  // set it unnecessarily if we return early (it's an expensive operation for
+  // some backends).
+  gfxContextMatrixAutoSaveRestore autoRestoreTransform(aContext);
+  aContext->SetMatrix(aNewTransform);
 
   if (renderMode == SVGAutoRenderState::CLIP_MASK) {
-    FillRule oldFillRule = gfx->CurrentFillRule();
-    gfxContextMatrixAutoSaveRestore autoSaveRestore(gfx);
-
-    gfx->SetMatrix(newMatrix);
-    gfx->SetFillRule(fillRule);
-    gfx->SetColor(gfxRGBA(1.0f, 1.0f, 1.0f, 1.0f));
-    gfx->SetPath(path);
-    gfx->Fill();
-
-    gfx->SetFillRule(oldFillRule);
-    gfx->NewPath();
+    drawTarget->Fill(path, ColorPattern(Color(1.0f, 1.0f, 1.0f, 1.0f)),
+                     DrawOptions(1.0f, CompositionOp::OP_OVER, aaMode));
     return;
   }
 
-  NS_ABORT_IF_FALSE(renderMode == SVGAutoRenderState::NORMAL,
-                    "Unknown render mode");
-
-  gfxContextAutoSaveRestore autoSaveRestore(gfx);
-  gfx->SetMatrix(newMatrix);
+  gfxTextContextPaint *contextPaint =
+    (gfxTextContextPaint*)drawTarget->
+      GetUserData(&gfxTextContextPaint::sUserDataKey);
 
-  gfxTextContextPaint *contextPaint =
-    (gfxTextContextPaint*)aContext->GetDrawTarget()->GetUserData(&gfxTextContextPaint::sUserDataKey);
-
-  if ((aRenderComponents & eRenderFill)) {
+  if (aRenderComponents & eRenderFill) {
     GeneralPattern fillPattern;
-    nsSVGUtils::MakeFillPatternFor(this, gfx, &fillPattern, contextPaint);
+    nsSVGUtils::MakeFillPatternFor(this, aContext, &fillPattern, contextPaint);
     if (fillPattern.GetPattern()) {
-      gfx->SetPath(path);
-      gfx->SetFillRule(fillRule);
-      gfx->Fill(fillPattern);
+      drawTarget->Fill(path, fillPattern,
+                       DrawOptions(1.0f, CompositionOp::OP_OVER, aaMode));
     }
   }
 
   if ((aRenderComponents & eRenderStroke) &&
       nsSVGUtils::HasStroke(this, contextPaint)) {
+    // Account for vector-effect:non-scaling-stroke:
+    gfxMatrix userToOuterSVG;
+    if (nsSVGUtils::GetNonScalingStrokeTransform(this, &userToOuterSVG)) {
+      // We need to transform the path back into the appropriate ancestor
+      // coordinate system, and paint it it that coordinate system, in order
+      // for non-scaled stroke to paint correctly.
+      gfxMatrix outerSVGToUser = userToOuterSVG;
+      outerSVGToUser.Invert();
+      aContext->Multiply(outerSVGToUser);
+      builder = path->TransformedCopyToBuilder(ToMatrix(userToOuterSVG), fillRule);
+      path = builder->Finish();
+    }
     GeneralPattern strokePattern;
-    nsSVGUtils::MakeStrokePatternFor(this, gfx, &strokePattern, contextPaint);
+    nsSVGUtils::MakeStrokePatternFor(this, aContext, &strokePattern, contextPaint);
     if (strokePattern.GetPattern()) {
-      gfx->SetPath(path);
-      nsSVGUtils::SetupCairoStrokeGeometry(this, gfx, contextPaint);
-      gfx->Stroke(strokePattern);
+      SVGContentUtils::AutoStrokeOptions strokeOptions;
+      SVGContentUtils::GetStrokeOptions(&strokeOptions,
+                                        static_cast<nsSVGElement*>(mContent),
+                                        StyleContext(), contextPaint);
+      // GetStrokeOptions may set the line width to zero as an optimization
+      if (strokeOptions.mLineWidth <= 0) {
+        return;
+      }
+      drawTarget->Stroke(path, strokePattern, strokeOptions,
+                         DrawOptions(1.0f, CompositionOp::OP_OVER, aaMode));
     }
   }
-
-  gfx->NewPath();
 }
 
 void
 nsSVGPathGeometryFrame::GeneratePath(gfxContext* aContext,
                                      const Matrix &aTransform)
 {
   if (aTransform.IsSingular()) {
     aContext->SetMatrix(gfxMatrix());
--- a/layout/svg/nsSVGPathGeometryFrame.h
+++ b/layout/svg/nsSVGPathGeometryFrame.h
@@ -10,16 +10,22 @@
 #include "gfxMatrix.h"
 #include "gfxRect.h"
 #include "nsFrame.h"
 #include "nsISVGChildFrame.h"
 #include "nsLiteralString.h"
 #include "nsQueryFrame.h"
 #include "nsSVGUtils.h"
 
+namespace mozilla {
+namespace gfx {
+class DrawTarget;
+}
+}
+
 class gfxContext;
 class nsDisplaySVGPathGeometry;
 class nsIAtom;
 class nsIFrame;
 class nsIPresShell;
 class nsRenderingContext;
 class nsStyleContext;
 class nsSVGMarkerFrame;
@@ -29,16 +35,18 @@ struct nsPoint;
 struct nsRect;
 struct nsIntRect;
 
 typedef nsFrame nsSVGPathGeometryFrameBase;
 
 class nsSVGPathGeometryFrame : public nsSVGPathGeometryFrameBase,
                                public nsISVGChildFrame
 {
+  typedef mozilla::gfx::DrawTarget DrawTarget;
+
   friend nsIFrame*
   NS_NewSVGPathGeometryFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 
   friend class nsDisplaySVGPathGeometry;
 
 protected:
   explicit nsSVGPathGeometryFrame(nsStyleContext* aContext)
     : nsSVGPathGeometryFrameBase(aContext)
@@ -108,17 +116,17 @@ protected:
    * This function returns a set of bit flags indicating which parts of the
    * element (fill, stroke, bounds) should intercept pointer events. It takes
    * into account the type of element and the value of the 'pointer-events'
    * property on the element.
    */
   virtual uint16_t GetHitTestFlags();
 private:
   enum { eRenderFill = 1, eRenderStroke = 2 };
-  void Render(nsRenderingContext *aContext, uint32_t aRenderComponents,
+  void Render(gfxContext* aContext, uint32_t aRenderComponents,
               const gfxMatrix& aTransform);
 
   /**
    * @param aMatrix The transform that must be multiplied onto aContext to
    *   establish this frame's SVG user space.
    */
   void PaintMarkers(nsRenderingContext *aContext, const gfxMatrix& aMatrix);
 
--- a/layout/svg/nsSVGPatternFrame.cpp
+++ b/layout/svg/nsSVGPatternFrame.cpp
@@ -246,21 +246,21 @@ nsSVGPatternFrame::PaintPattern(const Dr
    *                        patternTransform)
    *    Create the surface
    *    Calculate the content transformation matrix
    *    Get our children (we may need to get them from another Pattern)
    *    Call SVGPaint on all of our children
    *    Return
    */
 
-  // Get the first child of the pattern data we will render
-  nsIFrame* firstKid = GetPatternFirstChild();
-  if (!firstKid) {
+  nsSVGPatternFrame* patternWithChildren = GetPatternWithChildren();
+  if (!patternWithChildren) {
     return nullptr; // Either no kids or a bad reference
   }
+  nsIFrame* firstKid = patternWithChildren->mFrames.FirstChild();
 
   const nsSVGViewBox& viewBox = GetViewBox();
 
   uint16_t patternContentUnits =
     GetEnumValue(SVGPatternElement::PATTERNCONTENTUNITS);
   uint16_t patternUnits =
     GetEnumValue(SVGPatternElement::PATTERNUNITS);
 
@@ -298,44 +298,43 @@ nsSVGPatternFrame::PaintPattern(const Dr
   // Construct the CTM that we will provide to our children when we
   // render them into the tile.
   gfxMatrix ctm = ConstructCTM(viewBox, patternContentUnits, patternUnits,
                                callerBBox, aContextMatrix, aSource);
   if (ctm.IsSingular()) {
     return nullptr;
   }
 
-  // Get the pattern we are going to render
-  nsSVGPatternFrame *patternFrame =
-    static_cast<nsSVGPatternFrame*>(firstKid->GetParent());
-  if (patternFrame->mCTM) {
-    *patternFrame->mCTM = ctm;
+  if (patternWithChildren->mCTM) {
+    *patternWithChildren->mCTM = ctm;
   } else {
-    patternFrame->mCTM = new gfxMatrix(ctm);
+    patternWithChildren->mCTM = new gfxMatrix(ctm);
   }
 
   // Get the bounding box of the pattern.  This will be used to determine
   // the size of the surface, and will also be used to define the bounding
   // box for the pattern tile.
   gfxRect bbox = GetPatternRect(patternUnits, callerBBox, aContextMatrix, aSource);
   if (bbox.Width() <= 0.0 || bbox.Height() <= 0.0) {
     return nullptr;
   }
 
   // Get the pattern transform
   Matrix patternTransform = ToMatrix(GetPatternTransform());
 
   // revert the vector effect transform so that the pattern appears unchanged
   if (aFillOrStroke == &nsStyleSVG::mStroke) {
-    Matrix strokeTransform = ToMatrix(nsSVGUtils::GetStrokeTransform(aSource));
-    if (!strokeTransform.Invert()) {
-      NS_WARNING("Should we get here if the stroke transform is singular?");
-      return nullptr;
+    gfxMatrix userToOuterSVG;
+    if (nsSVGUtils::GetNonScalingStrokeTransform(aSource, &userToOuterSVG)) {
+      patternTransform *= ToMatrix(userToOuterSVG);
+      if (patternTransform.IsSingular()) {
+        NS_WARNING("Singular matrix painting non-scaling-stroke");
+        return nullptr;
+      }
     }
-    patternTransform *= strokeTransform;
   }
 
   // Get the transformation matrix that we will hand to the renderer's pattern
   // routine.
   *patternMatrix = GetPatternMatrix(patternUnits, patternTransform,
                                     bbox, callerBBox, aContextMatrix);
   if (patternMatrix->IsSingular()) {
     return nullptr;
@@ -361,17 +360,17 @@ nsSVGPatternFrame::PaintPattern(const Dr
   if (resultOverflows ||
       patternWidth != surfaceSize.width ||
       patternHeight != surfaceSize.height) {
     // scale drawing to pattern surface size
     gfxMatrix tempTM =
       gfxMatrix(surfaceSize.width / patternWidth, 0.0f,
                 0.0f, surfaceSize.height / patternHeight,
                 0.0f, 0.0f);
-    patternFrame->mCTM->PreMultiply(tempTM);
+    patternWithChildren->mCTM->PreMultiply(tempTM);
 
     // and rescale pattern to compensate
     patternMatrix->PreScale(patternWidth / surfaceSize.width,
                             patternHeight / surfaceSize.height);
   }
 
   RefPtr<DrawTarget> dt =
     aDrawTarget->CreateSimilarDrawTarget(surfaceSize, SurfaceFormat::B8G8R8A8);
@@ -394,72 +393,71 @@ nsSVGPatternFrame::PaintPattern(const Dr
   }
 
   // OK, now render -- note that we use "firstKid", which
   // we got at the beginning because it takes care of the
   // referenced pattern situation for us
 
   if (aSource->IsFrameOfType(nsIFrame::eSVGGeometry)) {
     // Set the geometrical parent of the pattern we are rendering
-    patternFrame->mSource = static_cast<nsSVGPathGeometryFrame*>(aSource);
+    patternWithChildren->mSource = static_cast<nsSVGPathGeometryFrame*>(aSource);
   }
 
   // Delay checking NS_FRAME_DRAWING_AS_PAINTSERVER bit until here so we can
   // give back a clear surface if there's a loop
-  if (!(patternFrame->GetStateBits() & NS_FRAME_DRAWING_AS_PAINTSERVER)) {
-    patternFrame->AddStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
+  if (!(patternWithChildren->GetStateBits() & NS_FRAME_DRAWING_AS_PAINTSERVER)) {
+    patternWithChildren->AddStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
     for (nsIFrame* kid = firstKid; kid;
          kid = kid->GetNextSibling()) {
       // The CTM of each frame referencing us can be different
       nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
       if (SVGFrame) {
         SVGFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED);
       }
-      gfxMatrix tm = *(patternFrame->mCTM);
+      gfxMatrix tm = *(patternWithChildren->mCTM);
       if (kid->GetContent()->IsSVG()) {
         tm = static_cast<nsSVGElement*>(kid->GetContent())->
               PrependLocalTransformsTo(tm, nsSVGElement::eUserSpaceToParent);
       }
       nsSVGUtils::PaintFrameWithEffects(kid, context, tm);
     }
-    patternFrame->RemoveStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
+    patternWithChildren->RemoveStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
   }
 
-  patternFrame->mSource = nullptr;
+  patternWithChildren->mSource = nullptr;
 
   if (aGraphicOpacity != 1.0f) {
     gfx->PopGroupToSource();
     gfx->Paint(aGraphicOpacity);
     gfx->Restore();
   }
 
   // caller now owns the surface
   return dt->Snapshot();
 }
 
 /* Will probably need something like this... */
 // How do we handle the insertion of a new frame?
 // We really don't want to rerender this every time,
 // do we?
-nsIFrame*
-nsSVGPatternFrame::GetPatternFirstChild()
+nsSVGPatternFrame*
+nsSVGPatternFrame::GetPatternWithChildren()
 {
   // Do we have any children ourselves?
-  nsIFrame* kid = mFrames.FirstChild();
-  if (kid)
-    return kid;
+  if (!mFrames.IsEmpty())
+    return this;
 
   // No, see if we chain to someone who does
   AutoPatternReferencer patternRef(this);
 
   nsSVGPatternFrame* next = GetReferencedPatternIfNotInUse();
   if (!next)
     return nullptr;
 
-  return next->GetPatternFirstChild();
+  return next->GetPatternWithChildren();
 }
 
 uint16_t
 nsSVGPatternFrame::GetEnumValue(uint32_t aIndex, nsIContent *aDefault)
 {
   nsSVGEnum& thisEnum =
     static_cast<SVGPatternElement *>(mContent)->mEnumAttributes[aIndex];
 
--- a/layout/svg/nsSVGPatternFrame.h
+++ b/layout/svg/nsSVGPatternFrame.h
@@ -115,17 +115,27 @@ protected:
   mozilla::TemporaryRef<SourceSurface>
   PaintPattern(const DrawTarget* aDrawTarget,
                Matrix *patternMatrix,
                const Matrix &aContextMatrix,
                nsIFrame *aSource,
                nsStyleSVGPaint nsStyleSVG::*aFillOrStroke,
                float aGraphicOpacity,
                const gfxRect *aOverrideBounds);
-  nsIFrame*  GetPatternFirstChild();
+
+  /**
+   * A <pattern> element may reference another <pattern> element using
+   * xlink:href and, if it doesn't have any child content of its own, then it
+   * will "inherit" the children of the referenced pattern (which may itself be
+   * inheriting its children if it references another <pattern>).  This
+   * function returns this nsSVGPatternFrame or the first pattern along the
+   * reference chain (if there is one) to have children.
+   */
+  nsSVGPatternFrame* GetPatternWithChildren();
+
   gfxRect    GetPatternRect(uint16_t aPatternUnits,
                             const gfxRect &bbox,
                             const Matrix &callerCTM,
                             nsIFrame *aTarget);
   gfxMatrix  ConstructCTM(const nsSVGViewBox& aViewBox,
                           uint16_t aPatternContentUnits,
                           uint16_t aPatternUnits,
                           const gfxRect &callerBBox,
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -78,61 +78,61 @@ NS_SVGNewGetBBoxEnabled()
 {
   return sSVGNewGetBBoxEnabled;
 }
 
 
 // we only take the address of this:
 static mozilla::gfx::UserDataKey sSVGAutoRenderStateKey;
 
-SVGAutoRenderState::SVGAutoRenderState(nsRenderingContext *aContext,
+SVGAutoRenderState::SVGAutoRenderState(DrawTarget* aDrawTarget,
                                        RenderMode aMode
                                        MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
-  : mContext(aContext)
+  : mDrawTarget(aDrawTarget)
   , mOriginalRenderState(nullptr)
   , mMode(aMode)
   , mPaintingToWindow(false)
 {
   MOZ_GUARD_OBJECT_NOTIFIER_INIT;
   mOriginalRenderState =
-    aContext->GetDrawTarget()->RemoveUserData(&sSVGAutoRenderStateKey);
+    aDrawTarget->RemoveUserData(&sSVGAutoRenderStateKey);
   // We always remove ourselves from aContext before it dies, so
   // passing nullptr as the destroy function is okay.
-  aContext->GetDrawTarget()->AddUserData(&sSVGAutoRenderStateKey, this, nullptr);
+  aDrawTarget->AddUserData(&sSVGAutoRenderStateKey, this, nullptr);
 }
 
 SVGAutoRenderState::~SVGAutoRenderState()
 {
-  mContext->GetDrawTarget()->RemoveUserData(&sSVGAutoRenderStateKey);
+  mDrawTarget->RemoveUserData(&sSVGAutoRenderStateKey);
   if (mOriginalRenderState) {
-    mContext->GetDrawTarget()->AddUserData(&sSVGAutoRenderStateKey,
-                                           mOriginalRenderState, nullptr);
+    mDrawTarget->AddUserData(&sSVGAutoRenderStateKey,
+                             mOriginalRenderState, nullptr);
   }
 }
 
 void
 SVGAutoRenderState::SetPaintingToWindow(bool aPaintingToWindow)
 {
   mPaintingToWindow = aPaintingToWindow;
 }
 
 /* static */ SVGAutoRenderState::RenderMode
-SVGAutoRenderState::GetRenderMode(nsRenderingContext *aContext)
+SVGAutoRenderState::GetRenderMode(DrawTarget* aDrawTarget)
 {
-  void *state = aContext->GetDrawTarget()->GetUserData(&sSVGAutoRenderStateKey);
+  void *state = aDrawTarget->GetUserData(&sSVGAutoRenderStateKey);
   if (state) {
     return static_cast<SVGAutoRenderState*>(state)->mMode;
   }
   return NORMAL;
 }
 
 /* static */ bool
-SVGAutoRenderState::IsPaintingToWindow(nsRenderingContext *aContext)
+SVGAutoRenderState::IsPaintingToWindow(DrawTarget* aDrawTarget)
 {
-  void *state = aContext->GetDrawTarget()->GetUserData(&sSVGAutoRenderStateKey);
+  void *state = aDrawTarget->GetUserData(&sSVGAutoRenderStateKey);
   if (state) {
     return static_cast<SVGAutoRenderState*>(state)->mPaintingToWindow;
   }
   return false;
 }
 
 void
 nsSVGUtils::Init()
@@ -769,35 +769,16 @@ nsSVGUtils::GetCoveredRegion(const nsFra
       nsRect childRect = child->GetCoveredRegion();
       rect.UnionRect(rect, childRect);
     }
   }
 
   return rect;
 }
 
-nsPoint
-nsSVGUtils::TransformOuterSVGPointToChildFrame(nsPoint aPoint,
-                                               const gfxMatrix& aFrameToCanvasTM,
-                                               nsPresContext* aPresContext)
-{
-  NS_ABORT_IF_FALSE(!aFrameToCanvasTM.IsSingular(),
-                    "Callers must not pass a singular matrix");
-  gfxMatrix canvasDevToFrameUserSpace = aFrameToCanvasTM;
-  canvasDevToFrameUserSpace.Invert();
-  gfxPoint cssPxPt =
-    gfxPoint(aPoint.x, aPoint.y) / aPresContext->AppUnitsPerCSSPixel();
-  gfxPoint userPt = canvasDevToFrameUserSpace.Transform(cssPxPt);
-  gfxPoint appPt = (userPt * aPresContext->AppUnitsPerCSSPixel()).Round();
-  userPt.x = clamped(appPt.x, gfxFloat(nscoord_MIN), gfxFloat(nscoord_MAX));
-  userPt.y = clamped(appPt.y, gfxFloat(nscoord_MIN), gfxFloat(nscoord_MAX));
-  // now guaranteed to be safe:
-  return nsPoint(nscoord(userPt.x), nscoord(userPt.y));
-}
-
 nsRect
 nsSVGUtils::TransformFrameRectToOuterSVG(const nsRect& aRect,
                                          const gfxMatrix& aMatrix,
                                          nsPresContext* aPresContext)
 {
   gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height);
   r.Scale(1.0 / nsPresContext::AppUnitsPerCSSPixel());
   return nsLayoutUtils::RoundGfxRectToAppRect(
@@ -1114,54 +1095,55 @@ nsSVGUtils::GetFirstNonAAncestorFrame(ns
        ancestorFrame = ancestorFrame->GetParent()) {
     if (ancestorFrame->GetType() != nsGkAtoms::svgAFrame) {
       return ancestorFrame;
     }
   }
   return nullptr;
 }
 
-gfxMatrix
-nsSVGUtils::GetStrokeTransform(nsIFrame *aFrame)
+bool
+nsSVGUtils::GetNonScalingStrokeTransform(nsIFrame *aFrame,
+                                         gfxMatrix* aUserToOuterSVG)
 {
   if (aFrame->GetContent()->IsNodeOfType(nsINode::eTEXT)) {
     aFrame = aFrame->GetParent();
   }
 
-  if (aFrame->StyleSVGReset()->mVectorEffect ==
-      NS_STYLE_VECTOR_EFFECT_NON_SCALING_STROKE) {
- 
-    nsIContent *content = aFrame->GetContent();
-    NS_ABORT_IF_FALSE(content->IsSVG(), "bad cast");
+  if (aFrame->StyleSVGReset()->mVectorEffect !=
+        NS_STYLE_VECTOR_EFFECT_NON_SCALING_STROKE) {
+    return false;
+  }
 
-    // a non-scaling stroke is in the screen co-ordinate
-    // space rather so we need to invert the transform
-    // to the screen co-ordinate space to get there.
-    // See http://www.w3.org/TR/SVGTiny12/painting.html#NonScalingStroke
-    gfx::Matrix transform = SVGContentUtils::GetCTM(
-                              static_cast<nsSVGElement*>(content), true);
-    if (!transform.IsSingular()) {
-      transform.Invert();
-      return ThebesMatrix(transform);
-    }
-  }
-  return gfxMatrix();
+  nsIContent *content = aFrame->GetContent();
+  NS_ABORT_IF_FALSE(content->IsSVG(), "bad cast");
+
+  *aUserToOuterSVG = ThebesMatrix(SVGContentUtils::GetCTM(
+                       static_cast<nsSVGElement*>(content), true));
+
+  return !aUserToOuterSVG->IsIdentity();
 }
 
 // The logic here comes from _cairo_stroke_style_max_distance_from_path
 static gfxRect
 PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
                               nsIFrame* aFrame,
                               double aStyleExpansionFactor,
                               const gfxMatrix& aMatrix)
 {
   double style_expansion =
     aStyleExpansionFactor * nsSVGUtils::GetStrokeWidth(aFrame);
 
-  gfxMatrix matrix = aMatrix * nsSVGUtils::GetStrokeTransform(aFrame);
+  gfxMatrix matrix = aMatrix;
+
+  gfxMatrix outerSVGToUser;
+  if (nsSVGUtils::GetNonScalingStrokeTransform(aFrame, &outerSVGToUser)) {
+    outerSVGToUser.Invert();
+    matrix *= outerSVGToUser;
+  }
 
   double dx = style_expansion * (fabs(matrix._11) + fabs(matrix._21));
   double dy = style_expansion * (fabs(matrix._22) + fabs(matrix._12));
 
   gfxRect strokeExtents = aPathExtents;
   strokeExtents.Inflate(dx, dy);
   return strokeExtents;
 }
@@ -1491,19 +1473,20 @@ nsSVGUtils::SetupCairoStrokeGeometry(nsI
                                      gfxTextContextPaint *aContextPaint)
 {
   float width = GetStrokeWidth(aFrame, aContextPaint);
   if (width <= 0)
     return;
   aContext->SetLineWidth(width);
 
   // Apply any stroke-specific transform
-  gfxMatrix strokeTransform = GetStrokeTransform(aFrame);
-  if (!strokeTransform.IsIdentity()) {
-    aContext->Multiply(strokeTransform);
+  gfxMatrix outerSVGToUser;
+  if (GetNonScalingStrokeTransform(aFrame, &outerSVGToUser) &&
+      outerSVGToUser.Invert()) {
+    aContext->Multiply(outerSVGToUser);
   }
 
   const nsStyleSVG* style = aFrame->StyleSVG();
   
   switch (style->mStrokeLinecap) {
   case NS_STYLE_STROKE_LINECAP_BUTT:
     aContext->SetLineCap(gfxContext::LINE_CAP_BUTT);
     break;
--- a/layout/svg/nsSVGUtils.h
+++ b/layout/svg/nsSVGUtils.h
@@ -56,16 +56,17 @@ struct nsPoint;
 namespace mozilla {
 class SVGAnimatedPreserveAspectRatio;
 class SVGPreserveAspectRatio;
 namespace dom {
 class Element;
 class UserSpaceMetrics;
 } // namespace dom
 namespace gfx {
+class DrawTarget;
 class GeneralPattern;
 class SourceSurface;
 }
 } // namespace mozilla
 
 // maximum dimension of an offscreen surface - choose so that
 // the surface size doesn't overflow a 32-bit signed int using
 // 4 bytes per pixel; in line with gfxASurface::CheckSurfaceSize
@@ -140,43 +141,45 @@ private:
   bool mIsEmpty;
 };
 
 // GRRR WINDOWS HATE HATE HATE
 #undef CLIP_MASK
 
 class MOZ_STACK_CLASS SVGAutoRenderState
 {
+  typedef mozilla::gfx::DrawTarget DrawTarget;
+
 public:
   enum RenderMode {
     /**
      * Used to inform SVG frames that they should paint as normal.
      */
     NORMAL, 
     /**
      * Used to inform SVG frames when they are painting as the child of a
      * complex clipPath that requires the use of a clip mask. In this case they
      * should only draw their basic geometry as a path and then fill it using
      * fully opaque white. They should not stroke, or paint anything else.
      */
     CLIP_MASK 
   };
 
-  SVGAutoRenderState(nsRenderingContext *aContext, RenderMode aMode
+  SVGAutoRenderState(DrawTarget* aDrawTarget, RenderMode aMode
                      MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
   ~SVGAutoRenderState();
 
   void SetPaintingToWindow(bool aPaintingToWindow);
 
-  static RenderMode GetRenderMode(nsRenderingContext *aContext);
-  static bool IsPaintingToWindow(nsRenderingContext *aContext);
+  static RenderMode GetRenderMode(DrawTarget* aDrawTarget);
+  static bool IsPaintingToWindow(DrawTarget* aDrawTarget);
 
 private:
-  nsRenderingContext *mContext;
-  void *mOriginalRenderState;
+  DrawTarget* mDrawTarget;
+  void* mOriginalRenderState;
   RenderMode mMode;
   bool mPaintingToWindow;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 
 #define NS_ISVGFILTERREFERENCE_IID \
 { 0x9744ee20, 0x1bcf, 0x4c62, \
@@ -334,25 +337,16 @@ public:
   NotifyChildrenOfSVGChange(nsIFrame *aFrame, uint32_t aFlags);
 
   /*
    * Get frame's covered region by walking the children and doing union.
    */
   static nsRect
   GetCoveredRegion(const nsFrameList &aFrames);
 
-  // Converts aPoint from an app unit point in outer-<svg> content rect space
-  // to an app unit point in a frame's SVG userspace. 
-  // This is a temporary helper we should no longer need after bug 614732 is
-  // fixed.
-  static nsPoint
-  TransformOuterSVGPointToChildFrame(nsPoint aPoint,
-                                     const gfxMatrix& aFrameToCanvasTM,
-                                     nsPresContext* aPresContext);
-
   static nsRect
   TransformFrameRectToOuterSVG(const nsRect& aRect,
                                const gfxMatrix& aMatrix,
                                nsPresContext* aPresContext);
 
   /*
    * Convert a surface size to an integer for use by thebes
    * possibly making it smaller in the process so the surface does not
@@ -459,21 +453,28 @@ public:
    * Find the first frame, starting with aStartFrame and going up its
    * parent chain, that is not an svgAFrame.
    */
   static nsIFrame* GetFirstNonAAncestorFrame(nsIFrame* aStartFrame);
 
   static bool OuterSVGIsCallingReflowSVG(nsIFrame *aFrame);
   static bool AnyOuterSVGIsCallingReflowSVG(nsIFrame *aFrame);
 
-  /*
-   * Get any additional transforms that apply only to stroking
-   * e.g. non-scaling-stroke
+  /**
+   * See https://svgwg.org/svg2-draft/painting.html#NonScalingStroke
+   *
+   * If the computed value of the 'vector-effect' property on aFrame is
+   * 'non-scaling-stroke', then this function will set aUserToOuterSVG to the
+   * transform from aFrame's SVG user space to the initial coordinate system
+   * established by the viewport of aFrame's outer-<svg>'s (the coordinate
+   * system in which the stroke is fixed).  If aUserToOuterSVG is set to a
+   * non-identity matrix this function returns true, else it returns false.
    */
-  static gfxMatrix GetStrokeTransform(nsIFrame *aFrame);
+  static bool GetNonScalingStrokeTransform(nsIFrame *aFrame,
+                                           gfxMatrix* aUserToOuterSVG);
 
   /**
    * Compute the maximum possible device space stroke extents of a path given
    * the path's device space path extents, its stroke style and its ctm.
    *
    * This is a workaround for the lack of suitable cairo API for getting the
    * tight device space stroke extents of a path. This basically gives us the
    * tightest extents that we can guarantee fully enclose the inked stroke
--- a/layout/tools/reftest/remotereftest.py
+++ b/layout/tools/reftest/remotereftest.py
@@ -157,16 +157,19 @@ class RemoteOptions(ReftestOptions):
             f.close()
 
         # httpd-path is specified by standard makefile targets and may be specified
         # on the command line to select a particular version of httpd.js. If not
         # specified, try to select the one from hostutils.zip, as required in bug 882932.
         if not options.httpdPath:
             options.httpdPath = os.path.join(options.utilityPath, "components")
 
+        # Android does not run leak tests, but set some reasonable defaults to avoid errors.
+        options.leakThresholds = {}
+
         # TODO: Copied from main, but I think these are no longer used in a post xulrunner world
         #options.xrePath = options.remoteTestRoot + self.automation._product + '/xulrunner'
         #options.utilityPath = options.testRoot + self.automation._product + '/bin'
         return options
 
 class ReftestServer:
     """ Web server used to serve Reftests, for closer fidelity to the real web.
         It is virtually identical to the server used in mochitest and will only
--- a/layout/tools/reftest/runreftest.py
+++ b/layout/tools/reftest/runreftest.py
@@ -339,17 +339,17 @@ class RefTest(object):
                                  cmdlineArgs,
                                  utilityPath = options.utilityPath,
                                  xrePath=options.xrePath,
                                  debuggerInfo=debuggerInfo,
                                  symbolsPath=options.symbolsPath,
                                  # give the JS harness 30 seconds to deal
                                  # with its own timeouts
                                  timeout=options.timeout + 30.0)
-      processLeakLog(self.leakLogFile, options.leakThreshold)
+      processLeakLog(self.leakLogFile, options.leakThresholds)
       self.automation.log.info("\nREFTEST INFO | runreftest.py | Running tests: end.")
     finally:
       self.cleanup(profileDir)
     return status
 
   def copyExtraFilesToProfile(self, options, profile):
     "Copy extra files or dirs specified on the command line to the testing profile."
     profileDir = profile.profile
@@ -389,22 +389,22 @@ class ReftestOptions(OptionParser):
                     action = "append", dest = "extraProfileFiles",
                     default = [],
                     help = "copy specified files/dirs to testing profile")
     self.add_option("--timeout",
                     action = "store", dest = "timeout", type = "int",
                     default = 5 * 60, # 5 minutes per bug 479518
                     help = "reftest will timeout in specified number of seconds. [default %default s].")
     self.add_option("--leak-threshold",
-                    action = "store", type = "int", dest = "leakThreshold",
+                    action = "store", type = "int", dest = "defaultLeakThreshold",
                     default = 0,
-                    help = "fail if the number of bytes leaked through "
-                           "refcounted objects (or bytes in classes with "
-                           "MOZ_COUNT_CTOR and MOZ_COUNT_DTOR) is greater "
-                           "than the given number")
+                    help = "fail if the number of bytes leaked in default "
+                           "processes through refcounted objects (or bytes "
+                           "in classes with MOZ_COUNT_CTOR and MOZ_COUNT_DTOR) "
+                           "is greater than the given number")
     self.add_option("--utility-path",
                     action = "store", type = "string", dest = "utilityPath",
                     default = self.automation.DIST_BIN,
                     help = "absolute path to directory containing utility "
                            "programs (xpcshell, ssltunnel, certutil)")
     defaults["utilityPath"] = self.automation.DIST_BIN
 
     self.add_option("--total-chunks",
@@ -506,16 +506,18 @@ class ReftestOptions(OptionParser):
         self.error("cannot specify logfile with parallel tests")
       if options.totalChunks is not None and options.thisChunk is None:
         self.error("cannot specify thisChunk or totalChunks with parallel tests")
       if options.focusFilterMode != "all":
         self.error("cannot specify focusFilterMode with parallel tests")
       if options.debugger is not None:
         self.error("cannot specify a debugger with parallel tests")
 
+      options.leakThresholds = {"default": options.defaultLeakThreshold}
+
     return options
 
 def main():
   automation = Automation()
   parser = ReftestOptions(automation)
   reftest = RefTest(automation)
 
   options, args = parser.parse_args()
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -134,16 +134,23 @@ public class GeckoAppShell
 
     private static Thread.UncaughtExceptionHandler systemUncaughtHandler;
     private static boolean restartScheduled;
     private static GeckoEditableListener editableListener;
 
     private static final Queue<GeckoEvent> PENDING_EVENTS = new ConcurrentLinkedQueue<GeckoEvent>();
     private static final Map<String, String> ALERT_COOKIES = new ConcurrentHashMap<String, String>();
 
+    @SuppressWarnings("serial")
+    private static final List<String> UNKNOWN_MIME_TYPES = new ArrayList<String>(3) {{
+        add("application/octet-stream"); // This will be used as a default mime type for unknown files
+        add("application/unknown");
+        add("application/octet-stream"); // Github uses this for APK files
+    }};
+
     private static volatile boolean locationHighAccuracyEnabled;
 
     // Accessed by NotificationHelper. This should be encapsulated.
     /* package */ static NotificationClient notificationClient;
 
     // See also HardwareUtils.LOW_MEMORY_THRESHOLD_MB.
     private static final int HIGH_MEMORY_DEVICE_THRESHOLD_MB = 768;
 
@@ -1795,34 +1802,51 @@ public class GeckoAppShell
                     Log.d(LOGTAG, "[OPENFILE] " + name + "(" + split[pidColumn] + ") : " + file);
             }
             in.close();
         } catch (Exception e) { }
     }
 
     @WrapElementForJNI
     public static void scanMedia(final String aFile, String aMimeType) {
+        String mimeType = aMimeType;
+        if (UNKNOWN_MIME_TYPES.contains(mimeType)) {
+            // If this is a generic undefined mimetype, erase it so that we can try to determine
+            // one from the file extension below.
+            mimeType = "";
+        }
+
         // If the platform didn't give us a mimetype, try to guess one from the filename
-        if (TextUtils.isEmpty(aMimeType)) {
+        if (TextUtils.isEmpty(mimeType)) {
             int extPosition = aFile.lastIndexOf(".");
             if (extPosition > 0 && extPosition < aFile.length() - 1) {
-                aMimeType = getMimeTypeFromExtension(aFile.substring(extPosition+1));
+                mimeType = getMimeTypeFromExtension(aFile.substring(extPosition+1));
             }
         }
 
-        final File f = new File(aFile);
+        // addCompletedDownload will throw if it received any null parameters. Use aMimeType or a default
+        // if we still don't have one.
+        if (TextUtils.isEmpty(mimeType)) {
+            if (TextUtils.isEmpty(aMimeType)) {
+                mimeType = UNKNOWN_MIME_TYPES.get(0);
+            } else {
+                mimeType = aMimeType;
+            }
+        }
+
         if (AppConstants.ANDROID_DOWNLOADS_INTEGRATION) {
+            final File f = new File(aFile);
             final DownloadManager dm = (DownloadManager) getContext().getSystemService(Context.DOWNLOAD_SERVICE);
             dm.addCompletedDownload(f.getName(),
                                     f.getName(),
-                                    !TextUtils.isEmpty(aMimeType),
+                                    true, // Media scanner should scan this
                                     aMimeType,
                                     f.getAbsolutePath(),
-                                    f.length(),
-                                    false);
+                                    Math.max(0, f.length()),
+                                    false); // Don't show a notification.
         } else {
             Context context = getContext();
             GeckoMediaScannerClient.startScan(context, aFile, aMimeType);
         }
     }
 
     @WrapElementForJNI(stubName = "GetIconForExtensionWrapper")
     public static byte[] getIconForExtension(String aExt, int iconSize) {
--- a/mobile/android/base/GeckoNetworkManager.java
+++ b/mobile/android/base/GeckoNetworkManager.java
@@ -32,17 +32,17 @@ import android.util.Log;
  * Current connection is firstly obtained from Android's ConnectivityManager,
  * which is represented by the constant, and then will be mapped into the
  * connection type defined in Network Information API version 3.
  */
 
 public class GeckoNetworkManager extends BroadcastReceiver implements NativeEventListener {
     private static final String LOGTAG = "GeckoNetworkManager";
 
-    static private GeckoNetworkManager sInstance = new GeckoNetworkManager();
+    static private GeckoNetworkManager sInstance;
 
     public static void destroy() {
         if (sInstance != null) {
             sInstance.onDestroy();
             sInstance = null;
         }
     }
 
@@ -85,16 +85,20 @@ public class GeckoNetworkManager extends
     // Information happened.
     private boolean mShouldNotify;
 
     // The application context used for registering receivers, so
     // we can unregister them again later.
     private volatile Context mApplicationContext;
 
     public static GeckoNetworkManager getInstance() {
+        if (sInstance == null) {
+            sInstance = new GeckoNetworkManager();
+        }
+
         return sInstance;
     }
 
     @Override
     public void onReceive(Context aContext, Intent aIntent) {
         updateConnectionType();
     }
 
--- a/mobile/android/base/RestrictedProfiles.java
+++ b/mobile/android/base/RestrictedProfiles.java
@@ -4,30 +4,64 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
 import java.util.Set;
 
 import org.json.JSONException;
 import org.json.JSONObject;
+
+import java.lang.StringBuilder;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.HashSet;
+
+import org.mozilla.gecko.mozglue.RobocopTarget;
 import org.mozilla.gecko.AppConstants.Versions;
-import org.mozilla.gecko.mozglue.RobocopTarget;
 import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
 
 import android.content.Context;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.UserManager;
 import android.util.Log;
 
 @RobocopTarget
 public class RestrictedProfiles {
     private static final String LOGTAG = "GeckoRestrictedProfiles";
 
-    // These constants should be in sync with the ones from toolkit/components/parentalcontrols/nsIParentalControlServices.java
+    private static Boolean inGuest = null;
+
+    @SuppressWarnings("serial")
+    private static final List<String> BANNED_SCHEMES = new ArrayList<String>() {{
+        add("file");
+        add("chrome");
+        add("resource");
+        add("jar");
+        add("wyciwyg");
+    }};
+
+    private static boolean getInGuest() {
+        if (inGuest == null) {
+            inGuest = GeckoAppShell.getGeckoInterface().getProfile().inGuestMode();
+        }
+
+        return inGuest;
+    }
+
+    @SuppressWarnings("serial")
+    private static final List<String> BANNED_URLS = new ArrayList<String>() {{
+        add("about:config");
+    }};
+
+    /* This is a list of things we can restrict you from doing. Some of these are reflected in Android UserManager constants.
+     * Others are specific to us.
+     * These constants should be in sync with the ones from toolkit/components/parentalcontrols/nsIParentalControlServices.idl
+     */
     public static enum Restriction {
         DISALLOW_DOWNLOADS(1, "no_download_files"),
         DISALLOW_INSTALL_EXTENSIONS(2, "no_install_extensions"),
         DISALLOW_INSTALL_APPS(3, "no_install_apps"), // UserManager.DISALLOW_INSTALL_APPS
         DISALLOW_BROWSE_FILES(4, "no_browse_files"),
         DISALLOW_SHARE(5, "no_share"),
         DISALLOW_BOOKMARK(6, "no_bookmark"),
         DISALLOW_ADD_CONTACTS(7, "no_add_contacts"),
@@ -38,76 +72,118 @@ public class RestrictedProfiles {
         public final String name;
 
         private Restriction(final int id, final String name) {
             this.id = id;
             this.name = name;
         }
     }
 
-    private static String geckoActionToRestriction(int action) {
+    private static Restriction geckoActionToRestriction(int action) {
         for (Restriction rest : Restriction.values()) {
             if (rest.id == action) {
-                return rest.name;
+                return rest;
             }
         }
 
         throw new IllegalArgumentException("Unknown action " + action);
     }
 
     @RobocopTarget
     private static Bundle getRestrictions() {
         final UserManager mgr = (UserManager) GeckoAppShell.getContext().getSystemService(Context.USER_SERVICE);
         return mgr.getUserRestrictions();
     }
 
+    private static boolean canLoadUrl(final String url) {
+        // Null urls are always allowed
+        if (url == null) {
+            return true;
+        }
+
+        try {
+            // If we're not in guest mode, and the system restriction isn't in place, everything is allowed.
+            if (!getInGuest() &&
+                !getRestrictions().getBoolean(Restriction.DISALLOW_BROWSE_FILES.name, false)) {
+                return true;
+            }
+        } catch(IllegalArgumentException ex) {
+            Log.i(LOGTAG, "Invalid action", ex);
+        }
+
+        final Uri u = Uri.parse(url);
+        final String scheme = u.getScheme();
+        if (BANNED_SCHEMES.contains(scheme)) {
+            return false;
+        }
+
+        for (String banned : BANNED_URLS) {
+            if (url.startsWith(banned)) {
+                return false;
+            }
+        }
+
+        // TODO: The UserManager should support blacklisting urls by the device owner.
+        return true;
+    }
+
     @WrapElementForJNI
     public static boolean isUserRestricted() {
         // Guest mode is supported in all Android versions
-        if (GeckoAppShell.getGeckoInterface().getProfile().inGuestMode()) {
+        if (getInGuest()) {
             return true;
         }
 
         if (Versions.preJBMR2) {
             return false;
         }
 
         return !getRestrictions().isEmpty();
     }
 
     public static boolean isAllowed(Restriction action) {
         return isAllowed(action.id, null);
     }
 
     @WrapElementForJNI
     public static boolean isAllowed(int action, String url) {
+        final Restriction restriction;
+        try {
+            restriction = geckoActionToRestriction(action);
+        } catch(IllegalArgumentException ex) {
+            return true;
+        }
+
+        if (Restriction.DISALLOW_BROWSE_FILES == restriction) {
+            return canLoadUrl(url);
+        }
+
         // ALl actions are blocked in Guest mode
-        if (GeckoAppShell.getGeckoInterface().getProfile().inGuestMode()) {
+        if (getInGuest()) {
             return false;
         }
 
         if (Versions.preJBMR2) {
             return true;
         }
 
         try {
             // NOTE: Restrictions hold the opposite intention, so we need to flip it
-            final String restriction = geckoActionToRestriction(action);
-            return !getRestrictions().getBoolean(restriction, false);
+            return !getRestrictions().getBoolean(restriction.name, false);
         } catch(IllegalArgumentException ex) {
             Log.i(LOGTAG, "Invalid action", ex);
         }
 
         return true;
     }
 
     @WrapElementForJNI
     public static String getUserRestrictions() {
         // Guest mode is supported in all Android versions
-        if (GeckoAppShell.getGeckoInterface().getProfile().inGuestMode()) {
+        if (getInGuest()) {
             StringBuilder builder = new StringBuilder("{ ");
 
             for (Restriction restriction : Restriction.values()) {
                 builder.append("\"" + restriction.name + "\": true, ");
             }
 
             builder.append(" }");
             return builder.toString();
--- a/mobile/android/base/tests/testAppMenuPathways.java
+++ b/mobile/android/base/tests/testAppMenuPathways.java
@@ -27,17 +27,17 @@ public class testAppMenuPathways extends
 
         // Generate a mock Content:LocationChange message with video mime-type for the current tab (tabId = 0).
         final JSONObject message = new JSONObject();
         try {
             message.put("contentType", "video/webm");
             message.put("baseDomain", "webmfiles.org");
             message.put("type", "Content:LocationChange");
             message.put("sameDocument", false);
-            message.put("userSearch", "");
+            message.put("userRequested", "");
             message.put("uri", getAbsoluteIpUrl("/big-buck-bunny_trailer.webm"));
             message.put("tabID", 0);
         } catch (Exception ex) {
             mAsserter.ok(false, "exception in testSaveAsPDFPathway", ex.toString());
         }
 
         // Mock video playback with the generated message and Content:LocationChange event.
         Tabs.getInstance().handleMessage("Content:LocationChange", message);
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -4213,26 +4213,22 @@ Tab.prototype = {
     this._hostChanged = true;
 
     let fixedURI = aLocationURI;
     try {
       fixedURI = URIFixup.createExposableURI(aLocationURI);
     } catch (ex) { }
 
     // In restricted profiles, we refuse to let you open any file urls.
-    if (!ParentalControls.isAllowed(ParentalControls.VISIT_FILE_URLS)) {
-      let bannedSchemes = ["file", "chrome", "resource", "jar", "wyciwyg"];
-
-      if (bannedSchemes.indexOf(fixedURI.scheme) > -1) {
-        aRequest.cancel(Cr.NS_BINDING_ABORTED);
-
-        aRequest = this.browser.docShell.displayLoadError(Cr.NS_ERROR_UNKNOWN_PROTOCOL, fixedURI, null);
-        if (aRequest) {
-          fixedURI = aRequest.URI;
-        }
+    if (!ParentalControls.isAllowed(ParentalControls.VISIT_FILE_URLS, fixedURI)) {
+      aRequest.cancel(Cr.NS_BINDING_ABORTED);
+
+      aRequest = this.browser.docShell.displayLoadError(Cr.NS_ERROR_UNKNOWN_PROTOCOL, fixedURI, null);
+      if (aRequest) {
+        fixedURI = aRequest.URI;
       }
     }
 
     let contentType = contentWin.document.contentType;
 
     // If fixedURI matches browser.lastURI, we assume this isn't a real location
     // change but rather a spurious addition like a wyciwyg URI prefix. See Bug 747883.
     // Note that we have to ensure fixedURI is not the same as aLocationURI so we
--- a/netwerk/base/public/nsICachingChannel.idl
+++ b/netwerk/base/public/nsICachingChannel.idl
@@ -12,17 +12,17 @@ interface nsIFile;
  * to affect its behavior with respect to how it uses the cache service.
  *
  * This interface provides:
  *   1) Support for "stream as file" semantics (for JAR and plugins).
  *   2) Support for "pinning" cached data in the cache (for printing and save-as).
  *   3) Support for uniquely identifying cached data in cases when the URL
  *      is insufficient (e.g., HTTP form submission).
  */
-[scriptable, uuid(a77b664e-e707-4017-9c03-47bcedcb5b05)]
+[scriptable, uuid(3d46b469-7405-416e-ba42-84899963b403)]
 interface nsICachingChannel : nsICacheInfoChannel
 {
     /**
      * Set/get the cache token... uniquely identifies the data in the cache.
      * Holding a reference to this token prevents the cached data from being
      * removed.
      * 
      * A cache token retrieved from a particular instance of nsICachingChannel
@@ -59,16 +59,24 @@ interface nsICachingChannel : nsICacheIn
      * the cache entry may be validated, overwritten, or simply read.
      *
      * The cache key may be NULL indicating that the URI of the channel is
      * sufficient to locate the same cache entry.  Setting a NULL cache key
      * is likewise valid.
      */
     attribute nsISupports cacheKey;
 
+    /**
+     * Instructs the channel to only store the metadata of the entry, and not
+     * the content. When reading an existing entry, this automatically sets
+     * LOAD_ONLY_IF_MODIFIED flag.
+     * Must be called before asyncOpen().
+     */
+    attribute boolean cacheOnlyMetadata;
+
     /**************************************************************************
      * Caching channel specific load flags:
      */
 
     /**
      * This load flag inhibits fetching from the net.  An error of
      * NS_ERROR_DOCUMENT_NOT_CACHED will be sent to the listener's
      * onStopRequest if network IO is necessary to complete the request.
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -203,16 +203,17 @@ AutoRedirectVetoNotifier::ReportRedirect
 nsHttpChannel::nsHttpChannel()
     : HttpAsyncAborter<nsHttpChannel>(MOZ_THIS_IN_INITIALIZER_LIST())
     , mLogicalOffset(0)
     , mPostID(0)
     , mRequestTime(0)
     , mOfflineCacheLastModifiedTime(0)
     , mCachedContentIsValid(false)
     , mCachedContentIsPartial(false)
+    , mCacheOnlyMetadata(false)
     , mTransactionReplaced(false)
     , mAuthRetryPending(false)
     , mProxyAuthPending(false)
     , mResuming(false)
     , mInitedCacheEntry(false)
     , mFallbackChannel(false)
     , mCustomConditionalRequest(false)
     , mFallingBack(false)
@@ -2821,18 +2822,26 @@ nsHttpChannel::OnCacheEntryCheck(nsICach
             LOG(("Cached data size does not match the Content-Length header "
                  "[content-length=%lld size=%lld]\n", contentLength, size));
 
             rv = MaybeSetupByteRangeRequest(size, contentLength);
             mCachedContentIsPartial = NS_SUCCEEDED(rv) && mIsPartialRequest;
             if (mCachedContentIsPartial) {
                 rv = OpenCacheInputStream(entry, false, !!appCache);
                 *aResult = ENTRY_NEEDS_REVALIDATION;
+                return rv;
+            } else if (size == 0 && mCacheOnlyMetadata) {
+                // Don't break cache entry load when the entry's data size
+                // is 0 and mCacheOnlyMetadata flag is set. In that case we
+                // want to proceed since the LOAD_ONLY_IF_MODIFIED flag is
+                // also set.
+                MOZ_ASSERT(mLoadFlags & LOAD_ONLY_IF_MODIFIED);
+            } else {
+                return rv;
             }
-            return rv;
         }
     }
 
     bool usingSSL = false;
     rv = mURI->SchemeIs("https", &usingSSL);
     NS_ENSURE_SUCCESS(rv,rv);
 
     bool doValidation = false;
@@ -3998,24 +4007,34 @@ nsHttpChannel::InstallCacheListener(int6
     // We must close the input stream first because cache entries do not
     // correctly handle having an output stream and input streams open at
     // the same time.
     mCacheInputStream.CloseAndRelease();
 
     nsCOMPtr<nsIOutputStream> out;
     rv = mCacheEntry->OpenOutputStream(offset, getter_AddRefs(out));
     if (rv == NS_ERROR_NOT_AVAILABLE) {
-      LOG(("  entry doomed, not writing it [channel=%p]", this));
-      // Entry is already doomed.
-      // This may happen when expiration time is set to past and the entry
-      // has been removed by the background eviction logic.
-      return NS_OK;
+        LOG(("  entry doomed, not writing it [channel=%p]", this));
+        // Entry is already doomed.
+        // This may happen when expiration time is set to past and the entry
+        // has been removed by the background eviction logic.
+        return NS_OK;
     }
     if (NS_FAILED(rv)) return rv;
 
+    if (mCacheOnlyMetadata) {
+        LOG(("Not storing content, cacheOnlyMetadata set"));
+        // We must open and then close the output stream of the cache entry.
+        // This way we indicate the content has been written (despite with zero
+        // length) and the entry is now in the ready state with "having data".
+
+        out->Close();
+        return NS_OK;
+    }
+
     // XXX disk cache does not support overlapped i/o yet
 #if 0
     // Mark entry valid inorder to allow simultaneous reading...
     rv = mCacheEntry->MarkValid();
     if (NS_FAILED(rv)) return rv;
 #endif
 
     nsCOMPtr<nsIStreamListenerTee> tee =
@@ -5711,16 +5730,40 @@ nsHttpChannel::SetCacheKey(nsISupports *
         if (NS_FAILED(rv)) return rv;
 
         rv = container->GetData(&mPostID);
         if (NS_FAILED(rv)) return rv;
     }
     return NS_OK;
 }
 
+NS_IMETHODIMP
+nsHttpChannel::GetCacheOnlyMetadata(bool *aOnlyMetadata)
+{
+    NS_ENSURE_ARG(aOnlyMetadata);
+    *aOnlyMetadata = mCacheOnlyMetadata;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpChannel::SetCacheOnlyMetadata(bool aOnlyMetadata)
+{
+    LOG(("nsHttpChannel::SetCacheOnlyMetadata [this=%p only-metadata=%d]\n",
+        this, aOnlyMetadata));
+
+    ENSURE_CALLED_BEFORE_ASYNC_OPEN();
+
+    mCacheOnlyMetadata = aOnlyMetadata;
+    if (aOnlyMetadata) {
+        mLoadFlags |= LOAD_ONLY_IF_MODIFIED;
+    }
+
+    return NS_OK;
+}
+
 //-----------------------------------------------------------------------------
 // nsHttpChannel::nsIResumableChannel
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsHttpChannel::ResumeAt(uint64_t aStartPos,
                         const nsACString& aEntityID)
 {
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -380,16 +380,17 @@ private:
     uint32_t                          mRedirectType;
 
     static const uint32_t WAIT_FOR_CACHE_ENTRY = 1;
     static const uint32_t WAIT_FOR_OFFLINE_CACHE_ENTRY = 2;
 
     // state flags
     uint32_t                          mCachedContentIsValid     : 1;
     uint32_t                          mCachedContentIsPartial   : 1;
+    uint32_t                          mCacheOnlyMetadata        : 1;
     uint32_t                          mTransactionReplaced      : 1;
     uint32_t                          mAuthRetryPending         : 1;
     uint32_t                          mProxyAuthPending         : 1;
     uint32_t                          mResuming                 : 1;
     uint32_t                          mInitedCacheEntry         : 1;
     // True if we are loading a fallback cache entry from the
     // application cache.
     uint32_t                          mFallbackChannel          : 1;
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_bug1064258.js
@@ -0,0 +1,154 @@
+/**
+ * Check how nsICachingChannel.cacheOnlyMetadata works.
+ * - all channels involved in this test are set cacheOnlyMetadata = true
+ * - do a previously uncached request for a long living content
+ * - check we have downloaded the content from the server (channel provides it)
+ * - check the entry has metadata, but zero-length content
+ * - load the same URL again, now cached
+ * - check the channel is giving no content (no call to OnDataAvailable) but succeeds
+ * - repeat again, but for a different URL that is not cached (immediately expires)
+ * - only difference is that we get a newer version of the content from the server during the second request
+ */
+
+Cu.import("resource://testing-common/httpd.js");
+
+XPCOMUtils.defineLazyGetter(this, "URL", function() {
+  return "http://localhost:" + httpServer.identity.primaryPort;
+});
+
+var httpServer = null;
+
+function make_channel(url, callback, ctx) {
+  var ios = Cc["@mozilla.org/network/io-service;1"].
+            getService(Ci.nsIIOService);
+  return ios.newChannel(url, "", null);
+}
+
+const responseBody1 = "response body 1";
+const responseBody2a = "response body 2a";
+const responseBody2b = "response body 2b";
+
+function contentHandler1(metadata, response)
+{
+  response.setHeader("Content-Type", "text/plain");
+  response.setHeader("Cache-control", "max-age=999999");
+  response.bodyOutputStream.write(responseBody1, responseBody1.length);
+}
+
+var content2passCount = 0;
+
+function contentHandler2(metadata, response)
+{
+  response.setHeader("Content-Type", "text/plain");
+  response.setHeader("Cache-control", "no-cache");
+  switch (content2passCount++) {
+    case 0:
+      response.setHeader("ETag", "testetag");
+      response.bodyOutputStream.write(responseBody2a, responseBody2a.length);
+      break;
+    case 1:
+      do_check_true(metadata.hasHeader("If-None-Match"));
+      do_check_eq(metadata.getHeader("If-None-Match"), "testetag");
+      response.bodyOutputStream.write(responseBody2b, responseBody2b.length);
+      break;
+    default:
+      throw "Unexpected request in the test";
+  }
+}
+
+
+function run_test()
+{
+  httpServer = new HttpServer();
+  httpServer.registerPathHandler("/content1", contentHandler1);
+  httpServer.registerPathHandler("/content2", contentHandler2);
+  httpServer.start(-1);
+
+  run_test_content1a();
+  do_test_pending();
+}
+
+function run_test_content1a()
+{
+  var chan = make_channel(URL + "/content1");
+  caching = chan.QueryInterface(Ci.nsICachingChannel);
+  caching.cacheOnlyMetadata = true;
+  chan.asyncOpen(new ChannelListener(contentListener1a, null), null);
+}
+
+function contentListener1a(request, buffer)
+{
+  do_check_eq(buffer, responseBody1);
+
+  asyncOpenCacheEntry(URL + "/content1", "disk", 0, null, cacheCheck1)
+}
+
+function cacheCheck1(status, entry)
+{
+  do_check_eq(status, 0);
+  do_check_eq(entry.dataSize, 0);
+  try {
+    do_check_neq(entry.getMetaDataElement("response-head"), null);
+  }
+  catch (ex) {
+    do_throw("Missing response head");
+  }
+
+  var chan = make_channel(URL + "/content1");
+  caching = chan.QueryInterface(Ci.nsICachingChannel);
+  caching.cacheOnlyMetadata = true;
+  chan.asyncOpen(new ChannelListener(contentListener1b, null, CL_IGNORE_CL), null);
+}
+
+function contentListener1b(request, buffer)
+{
+  request.QueryInterface(Ci.nsIHttpChannel);
+  do_check_eq(request.requestMethod, "GET");
+  do_check_eq(request.responseStatus, 200);
+  do_check_eq(request.getResponseHeader("Cache-control"), "max-age=999999");
+
+  do_check_eq(buffer, "");
+  run_test_content2a();
+}
+
+// Now same set of steps but this time for an immediately expiring content.
+
+function run_test_content2a()
+{
+  var chan = make_channel(URL + "/content2");
+  caching = chan.QueryInterface(Ci.nsICachingChannel);
+  caching.cacheOnlyMetadata = true;
+  chan.asyncOpen(new ChannelListener(contentListener2a, null), null);
+}
+
+function contentListener2a(request, buffer)
+{
+  do_check_eq(buffer, responseBody2a);
+
+  asyncOpenCacheEntry(URL + "/content2", "disk", 0, null, cacheCheck2)
+}
+
+function cacheCheck2(status, entry)
+{
+  do_check_eq(status, 0);
+  do_check_eq(entry.dataSize, 0);
+  try {
+    do_check_neq(entry.getMetaDataElement("response-head"), null);
+    do_check_true(entry.getMetaDataElement("response-head").match('Etag: testetag'));
+  }
+  catch (ex) {
+    do_throw("Missing response head");
+  }
+
+  var chan = make_channel(URL + "/content2");
+  caching = chan.QueryInterface(Ci.nsICachingChannel);
+  caching.cacheOnlyMetadata = true;
+  chan.asyncOpen(new ChannelListener(contentListener2b, null), null);
+}
+
+function contentListener2b(request, buffer)
+{
+  do_check_eq(buffer, responseBody2b);
+
+  httpServer.stop(do_test_finished);
+}
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -153,16 +153,17 @@ skip-if = os == "android"
 [test_bug667818.js]
 [test_bug669001.js]
 [test_bug712914_secinfo_validation.js]
 [test_bug770243.js]
 [test_bug894586.js]
 # Allocating 4GB might actually succeed on 64 bit machines
 skip-if = bits != 32
 [test_bug935499.js]
+[test_bug1064258.js]
 [test_udpsocket.js]
 [test_doomentry.js]
 [test_cacheflags.js]
 [test_cache_jar.js]
 [test_channel_close.js]
 [test_compareURIs.js]
 [test_compressappend.js]
 [test_content_encoding_gzip.js]
--- a/parser/html/nsHtml5TreeBuilderCppSupplement.h
+++ b/parser/html/nsHtml5TreeBuilderCppSupplement.h
@@ -10,16 +10,18 @@
 #include "nsIFrame.h"
 #include "mozilla/Likely.h"
 
 class nsPresContext;
 
 nsHtml5TreeBuilder::nsHtml5TreeBuilder(nsHtml5OplessBuilder* aBuilder)
   : scriptingEnabled(false)
   , fragment(false)
+  , contextName(nullptr)
+  , contextNamespace(kNameSpaceID_None)
   , contextNode(nullptr)
   , formPointer(nullptr)
   , headPointer(nullptr)
   , mBuilder(aBuilder)
   , mViewSource(nullptr)
   , mOpSink(nullptr)
   , mHandles(nullptr)
   , mHandlesUsed(0)
@@ -32,16 +34,18 @@ nsHtml5TreeBuilder::nsHtml5TreeBuilder(n
 {
   MOZ_COUNT_CTOR(nsHtml5TreeBuilder);
 }
 
 nsHtml5TreeBuilder::nsHtml5TreeBuilder(nsAHtml5TreeOpSink* aOpSink,
                                        nsHtml5TreeOpStage* aStage)
   : scriptingEnabled(false)
   , fragment(false)
+  , contextName(nullptr)
+  , contextNamespace(kNameSpaceID_None)
   , contextNode(nullptr)
   , formPointer(nullptr)
   , headPointer(nullptr)
   , mBuilder(nullptr)
   , mViewSource(nullptr)
   , mOpSink(aOpSink)
   , mHandles(new nsIContent*[NS_HTML5_TREE_BUILDER_HANDLE_ARRAY_LENGTH])
   , mHandlesUsed(0)
new file mode 100644
--- /dev/null
+++ b/testing/config/tooltool-manifests/androidarm/releng.manifest
@@ -0,0 +1,7 @@
+[{
+"size": 261782928,
+"digest": "7140e026b7b747236545dc30e377a959b0bdf91bb4d70efd7f97f92fce12a9196042503124b8df8d30c2d97b7eb5f9df9556afdffa0b5d9625008aead305c32b",
+"algorithm": "sha512",
+"filename": "AVDs-armv7a-gingerbread-build-2014-01-23-ubuntu.tar.gz",
+"unpack": "True"
+}]
new file mode 100644
--- /dev/null
+++ b/testing/config/tooltool-manifests/androidx86/releng.manifest
@@ -0,0 +1,7 @@
+[{
+"size": 561274118,
+"digest": "3b2d18eb0194d82c70c5ee17487ccbac309f9b2e9839fe7ca4a27a9a06f6338bb24394476da78559685d99151fccc85fdde03297aa73ee2f7fb3183e11925c4d",
+"algorithm": "sha512",
+"filename": "AVDs-x86-android-4.2_r1-build-2013-11-13-ubuntu.tar.gz",
+"unpack": "True"
+}]
--- a/testing/mochitest/mochitest_options.py
+++ b/testing/mochitest/mochitest_options.py
@@ -218,22 +218,22 @@ class MochitestOptions(optparse.OptionPa
           "dest": "browserArgs",
           "metavar": "ARG",
           "help": "provides an argument to the test application",
           "default": [],
         }],
         [["--leak-threshold"],
         { "action": "store",
           "type": "int",
-          "dest": "leakThreshold",
+          "dest": "defaultLeakThreshold",
           "metavar": "THRESHOLD",
-          "help": "fail if the number of bytes leaked through "
-                 "refcounted objects (or bytes in classes with "
-                 "MOZ_COUNT_CTOR and MOZ_COUNT_DTOR) is greater "
-                 "than the given number",
+          "help": "fail if the number of bytes leaked in default "
+                 "processes through refcounted objects (or bytes "
+                 "in classes with MOZ_COUNT_CTOR and MOZ_COUNT_DTOR) "
+                 "is greater than the given number",
           "default": 0,
         }],
         [["--fatal-assertions"],
         { "action": "store_true",
           "dest": "fatalAssertions",
           "help": "abort testing whenever an assertion is hit "
                  "(requires a debug build to be effective)",
           "default": False,
@@ -605,16 +605,21 @@ class MochitestOptions(optparse.OptionPa
 
         if options.useTestMediaDevices:
             if not mozinfo.isLinux:
                 self.error('--use-test-media-devices is only supported on Linux currently')
             for f in ['/usr/bin/gst-launch-0.10', '/usr/bin/pactl']:
                 if not os.path.isfile(f):
                     self.error('Missing binary %s required for --use-test-media-devices')
 
+        options.leakThresholds = {
+            "default": options.defaultLeakThreshold,
+            "tab": 10000, # See dependencies of bug 1051230.
+        }
+
         return options
 
 
 class B2GOptions(MochitestOptions):
     b2g_options = [
         [["--b2gpath"],
         { "action": "store",
           "type": "string",
@@ -762,17 +767,17 @@ class B2GOptions(MochitestOptions):
         defaults["httpPort"] = DEFAULT_PORTS['http']
         defaults["sslPort"] = DEFAULT_PORTS['https']
         defaults["logFile"] = "mochitest.log"
         defaults["autorun"] = True
         defaults["closeWhenDone"] = True
         defaults["testPath"] = ""
         defaults["extensionsToExclude"] = ["specialpowers"]
         # See dependencies of bug 1038943.
-        defaults["leakThreshold"] = 5180
+        defaults["defaultLeakThreshold"] = 5180
         self.set_defaults(**defaults)
 
     def verifyRemoteOptions(self, options):
         if options.remoteWebServer == None:
             if os.name != "nt":
                 options.remoteWebServer = moznetwork.get_ip()
             else:
                 self.error("You must specify a --remote-webserver=<ip address>")
--- a/testing/mochitest/runtests.py
+++ b/testing/mochitest/runtests.py
@@ -1837,17 +1837,17 @@ class Mochitest(MochitestUtilsMixin):
         self.log.error("Automation Error: Received unexpected exception while running application\n")
         status = 1
 
     finally:
       if options.vmwareRecording:
         self.stopVMwareRecording();
       self.stopServers()
 
-    processLeakLog(self.leak_report_file, options.leakThreshold)
+    processLeakLog(self.leak_report_file, options.leakThresholds)
 
     if self.nsprLogs:
       with zipfile.ZipFile("%s/nsprlog.zip" % browserEnv["MOZ_UPLOAD_DIR"], "w", zipfile.ZIP_DEFLATED) as logzip:
         for logfile in glob.glob("%s/nspr*.log*" % tempfile.gettempdir()):
           logzip.write(logfile)
           os.remove(logfile)
 
     self.log.info("runtests.py | Running tests: end.")
--- a/testing/mochitest/runtestsb2g.py
+++ b/testing/mochitest/runtestsb2g.py
@@ -197,17 +197,17 @@ class B2GMochitest(MochitestUtilsMixin):
             if status is None:
                 # the runner has timed out
                 status = 124
 
             local_leak_file = tempfile.NamedTemporaryFile()
             self.app_ctx.dm.getFile(self.leak_report_file, local_leak_file.name)
             self.app_ctx.dm.removeFile(self.leak_report_file)
 
-            processLeakLog(local_leak_file.name, options.leakThreshold)
+            processLeakLog(local_leak_file.name, options.leakThresholds)
         except KeyboardInterrupt:
             self.log.info("runtests.py | Received keyboard interrupt.\n");
             status = -1
         except:
             traceback.print_exc()
             self.log.error("Automation Error: Received unexpected exception while running application\n")
             if hasattr(self, 'runner'):
                 self.runner.check_for_crashes()
--- a/toolkit/components/autocomplete/nsAutoCompleteController.cpp
+++ b/toolkit/components/autocomplete/nsAutoCompleteController.cpp
@@ -111,16 +111,17 @@ nsAutoCompleteController::SetInput(nsIAu
   nsAutoString newValue;
   aInput->GetTextValue(newValue);
 
   // Clear out this reference in case the new input's popup has no tree
   mTree = nullptr;
 
   // Reset all search state members to default values
   mSearchString = newValue;
+  mPlaceholderCompletionString.Truncate();
   mDefaultIndexCompleted = false;
   mBackspaced = false;
   mSearchStatus = nsIAutoCompleteController::STATUS_NONE;
   mRowCount = 0;
   mSearchesOngoing = 0;
 
   // Initialize our list of search objects
   uint32_t searchCount;
@@ -225,18 +226,20 @@ nsAutoCompleteController::HandleText()
 
   // Determine if the user has removed text from the end (probably by backspacing)
   if (newValue.Length() < mSearchString.Length() &&
       Substring(mSearchString, 0, newValue.Length()).Equals(newValue))
   {
     // We need to throw away previous results so we don't try to search through them again
     ClearResults();
     mBackspaced = true;
-  } else
+    mPlaceholderCompletionString.Truncate();
+  } else {
     mBackspaced = false;
+  }
 
   mSearchString = newValue;
 
   // Don't search if the value is empty
   if (newValue.Length() == 0) {
     // If autocomplete popup was closed by compositionstart event handler,
     // we should reopen it forcibly even if the value is empty.
     if (popupClosedByCompositionStart && handlingCompositionCommit) {
@@ -1106,25 +1109,69 @@ nsAutoCompleteController::StopSearch()
     mSearchesOngoing = 0;
     // since we were searching, but now we've stopped,
     // we need to call PostSearchCleanup()
     PostSearchCleanup();
   }
   return NS_OK;
 }
 
+void
+nsAutoCompleteController::MaybeCompletePlaceholder()
+{
+  MOZ_ASSERT(mInput);
+
+  if (!mInput) { // or mInput depending on what you choose
+    MOZ_ASSERT_UNREACHABLE("Input should always be valid at this point");
+    return;
+  }
+
+  int32_t selectionStart;
+  mInput->GetSelectionStart(&selectionStart);
+  int32_t selectionEnd;
+  mInput->GetSelectionEnd(&selectionEnd);
+
+  // Check if the current input should be completed with the placeholder string
+  // from the last completion until the actual search results come back.
+  // The new input string needs to be compatible with the last completed string.
+  // E.g. if the new value is "fob", but the last completion was "foobar",
+  // then the last completion is incompatible.
+  // If the search string is the same as the last completion value, then don't
+  // complete the value again (this prevents completion to happen e.g. if the
+  // cursor is moved and StartSeaches() is invoked).
+  // In addition, the selection must be at the end of the current input to
+  // trigger the placeholder completion.
+  bool usePlaceholderCompletion =
+    !mPlaceholderCompletionString.IsEmpty() &&
+    mPlaceholderCompletionString.Length() > mSearchString.Length() &&
+    selectionEnd == selectionStart &&
+    selectionEnd == (int32_t)mSearchString.Length() &&
+    StringBeginsWith(mPlaceholderCompletionString, mSearchString,
+                    nsCaseInsensitiveStringComparator());
+
+  if (usePlaceholderCompletion) {
+    CompleteValue(mPlaceholderCompletionString);
+  } else {
+    mPlaceholderCompletionString.Truncate();
+  }
+}
+
 nsresult
 nsAutoCompleteController::StartSearches()
 {
   // Don't create a new search timer if we're already waiting for one to fire.
   // If we don't check for this, we won't be able to cancel the original timer
   // and may crash when it fires (bug 236659).
   if (mTimer || !mInput)
     return NS_OK;
 
+  // Check if the current input should be completed with the placeholder string
+  // from the last completion until the actual search results come back.
+  MaybeCompletePlaceholder();
+
   nsCOMPtr<nsIAutoCompleteInput> input(mInput);
 
   // Get the timeout for delayed searches.
   uint32_t timeout;
   input->GetTimeout(&timeout);
 
   uint32_t immediateSearchesCount = mImmediateSearchesCount;
   if (timeout == 0) {
@@ -1452,20 +1499,28 @@ nsAutoCompleteController::CompleteDefaul
 
   nsCOMPtr<nsIAutoCompleteInput> input(mInput);
 
   int32_t selectionStart;
   input->GetSelectionStart(&selectionStart);
   int32_t selectionEnd;
   input->GetSelectionEnd(&selectionEnd);
 
+  bool isPlaceholderSelected =
+      selectionEnd == (int32_t)mPlaceholderCompletionString.Length() &&
+      selectionStart == (int32_t)mSearchString.Length() &&
+      StringBeginsWith(mPlaceholderCompletionString,
+        mSearchString, nsCaseInsensitiveStringComparator());
+
   // Don't try to automatically complete to the first result if there's already
-  // a selection or the cursor isn't at the end of the input
-  if (selectionEnd != selectionStart ||
-      selectionEnd != (int32_t)mSearchString.Length())
+  // a selection or the cursor isn't at the end of the input. In case the
+  // selection is from the current placeholder completion value, then still
+  // automatically complete.
+  if (!isPlaceholderSelected && (selectionEnd != selectionStart ||
+        selectionEnd != (int32_t)mSearchString.Length()))
     return NS_OK;
 
   bool shouldComplete;
   input->GetCompleteDefaultIndex(&shouldComplete);
   if (!shouldComplete)
     return NS_OK;
 
   nsAutoString resultValue;
@@ -1598,16 +1653,17 @@ nsAutoCompleteController::CompleteValue(
   int32_t endSelect = aValue.Length();  // By default, select all of aValue.
 
   if (aValue.IsEmpty() ||
       StringBeginsWith(aValue, mSearchString,
                        nsCaseInsensitiveStringComparator())) {
     // aValue is empty (we were asked to clear mInput), or mSearchString
     // matches the beginning of aValue.  In either case we can simply
     // autocomplete to aValue.
+    mPlaceholderCompletionString = aValue;
     input->SetTextValue(aValue);
   } else {
     nsresult rv;
     nsCOMPtr<nsIIOService> ios = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
     nsAutoCString scheme;
     if (NS_SUCCEEDED(ios->ExtractScheme(NS_ConvertUTF16toUTF8(aValue), scheme))) {
       // Trying to autocomplete a URI from somewhere other than the beginning.
@@ -1618,28 +1674,31 @@ nsAutoCompleteController::CompleteValue(
 
       if ((endSelect < findIndex + mSearchStringLength) ||
           !scheme.LowerCaseEqualsLiteral("http") ||
           !Substring(aValue, findIndex, mSearchStringLength).Equals(
             mSearchString, nsCaseInsensitiveStringComparator())) {
         return NS_OK;
       }
 
-      input->SetTextValue(mSearchString +
-                          Substring(aValue, mSearchStringLength + findIndex,
-                                    endSelect));
+      mPlaceholderCompletionString = mSearchString +
+        Substring(aValue, mSearchStringLength + findIndex, endSelect);
+      input->SetTextValue(mPlaceholderCompletionString);
 
       endSelect -= findIndex; // We're skipping this many characters of aValue.
     } else {
       // Autocompleting something other than a URI from the middle.
       // Use the format "searchstring >> full string" to indicate to the user
       // what we are going to replace their search string with.
       input->SetTextValue(mSearchString + NS_LITERAL_STRING(" >> ") + aValue);
 
       endSelect = mSearchString.Length() + 4 + aValue.Length();
+
+      // Reset the last search completion.
+      mPlaceholderCompletionString.Truncate();
     }
   }
 
   input->SelectTextRange(mSearchStringLength, endSelect);
 
   return NS_OK;
 }
 
--- a/toolkit/components/autocomplete/nsAutoCompleteController.h
+++ b/toolkit/components/autocomplete/nsAutoCompleteController.h
@@ -43,16 +43,17 @@ protected:
   nsresult ClosePopup();
 
   nsresult StartSearch(uint16_t aSearchType);
 
   nsresult BeforeSearches();
   nsresult StartSearches();
   void AfterSearches();
   nsresult ClearSearchTimer();
+  void MaybeCompletePlaceholder();
 
   nsresult ProcessResult(int32_t aSearchIndex, nsIAutoCompleteResult *aResult);
   nsresult PostSearchCleanup();
 
   nsresult EnterMatch(bool aIsPopupSelection);
   nsresult RevertTextValue();
 
   nsresult CompleteDefaultIndex(int32_t aResultIndex);
@@ -129,16 +130,17 @@ protected:
   // since otherwise the first search clears mResults.
   nsCOMArray<nsIAutoCompleteResult> mResultCache;
 
   nsCOMPtr<nsITimer> mTimer;
   nsCOMPtr<nsITreeSelection> mSelection;
   nsCOMPtr<nsITreeBoxObject> mTree;
 
   nsString mSearchString;
+  nsString mPlaceholderCompletionString;
   bool mDefaultIndexCompleted;
   bool mBackspaced;
   bool mPopupClosedByCompositionStart;
   enum CompositionState {
     eCompositionState_None,
     eCompositionState_Composing,
     eCompositionState_Committing
   };
--- a/toolkit/components/places/PlacesDBUtils.jsm
+++ b/toolkit/components/places/PlacesDBUtils.jsm
@@ -952,16 +952,28 @@ this.PlacesDBUtils = {
       { histogram: "PLACES_ANNOS_BOOKMARKS_SIZE_KB",
         query:     "SELECT SUM(LENGTH(content))/1024 FROM moz_items_annos" },
 
       { histogram: "PLACES_ANNOS_PAGES_COUNT",
         query:     "SELECT count(*) FROM moz_annos" },
 
       { histogram: "PLACES_ANNOS_PAGES_SIZE_KB",
         query:     "SELECT SUM(LENGTH(content))/1024 FROM moz_annos" },
+
+      { histogram: "PLACES_MAINTENANCE_DAYSFROMLAST",
+        callback: function () {
+          try {
+            let lastMaintenance = Services.prefs.getIntPref("places.database.lastMaintenance");
+            let nowSeconds = parseInt(Date.now() / 1000);
+            return parseInt((nowSeconds - lastMaintenance) / 86400);
+          } catch (ex) {
+            return 60;
+          }
+        }
+      },
     ];
 
     let params = {
       tags_folder: PlacesUtils.tagsFolderId,
       type_folder: PlacesUtils.bookmarks.TYPE_FOLDER,
       type_bookmark: PlacesUtils.bookmarks.TYPE_BOOKMARK,
       places_root: PlacesUtils.placesRootId
     };
--- a/toolkit/components/places/PlacesUtils.jsm
+++ b/toolkit/components/places/PlacesUtils.jsm
@@ -1916,16 +1916,17 @@ XPCOMUtils.defineLazyGetter(this, "gAsyn
   connPromised.then(conn => {
     try {
       Sqlite.shutdown.addBlocker("Places DB readonly connection closing",
                                  conn.close.bind(conn));
     }
     catch(ex) {
       // It's too late to block shutdown, just close the connection.
       return conn.close();
+      throw (ex);
     }
     return Promise.resolve();
   }).then(null, Cu.reportError);
   return connPromised;
 });
 
 // Sometime soon, likely as part of the transition to mozIAsyncBookmarks,
 // itemIds will be deprecated in favour of GUIDs, which play much better
--- a/toolkit/components/places/UnifiedComplete.js
+++ b/toolkit/components/places/UnifiedComplete.js
@@ -6,17 +6,16 @@
 
 "use strict";
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Constants
 
 const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
 
-const TOPIC_SHUTDOWN = "places-shutdown";
 const TOPIC_PREFCHANGED = "nsPref:changed";
 
 const DEFAULT_BEHAVIOR = 0;
 
 const PREF_BRANCH = "browser.urlbar.";
 
 // Prefs are defined as [pref name, default value].
 const PREF_ENABLED =                [ "autocomplete.enabled",   true ];
@@ -624,17 +623,19 @@ Search.prototype = {
     if (!Prefs.filterJavaScript) {
       this.setBehavior("javascript");
     }
 
     return tokens;
   },
 
   /**
-   * Used to cancel this search, will stop providing results.
+   * Cancels this search.
+   * After invoking this method, we won't run any more searches or heuristics,
+   * and no new matches may be added to the current result.
    */
   cancel: function () {
     if (this._sleepTimer)
       this._sleepTimer.cancel();
     if (this._sleepDeferred) {
       this._sleepDeferred.resolve();
       this._sleepDeferred = null;
     }
@@ -975,16 +976,20 @@ Search.prototype = {
         match = this._processUrlRow(row);
         break;
       case QUERYTYPE_FILTERED:
       case QUERYTYPE_KEYWORD:
         match = this._processRow(row);
         break;
     }
     this._addMatch(match);
+    // If the search has been canceled by the user or by _addMatch reaching the
+    // maximum number of results, we can stop the underlying Sqlite query.
+    if (!this.pending)
+      throw StopIteration;
   },
 
   _maybeRestyleSearchMatch: function (match) {
     // Return if the URL does not represent a search result.
     let parseResult =
       PlacesSearchAutocompleteProvider.parseSubmissionURL(match.value);
     if (!parseResult) {
       return;
@@ -1036,32 +1041,29 @@ Search.prototype = {
       if (match.style == "favicon") {
         this._maybeRestyleSearchMatch(match);
       }
 
       this._result.appendMatch(match.value,
                                match.comment,
                                match.icon || PlacesUtils.favicons.defaultFavicon.spec,
                                match.style,
-                               match.finalCompleteValue);
+                               match.finalCompleteValue || "");
       notifyResults = true;
     }
 
     if (this._result.matchCount == 6)
       TelemetryStopwatch.finish(TELEMETRY_6_FIRST_RESULTS);
 
-    if (this._result.matchCount == Prefs.maxRichResults || !this.pending) {
+    if (this._result.matchCount == Prefs.maxRichResults) {
       // We have enough results, so stop running our search.
+      // We don't need to notify results in this case, cause the main promise
+      // chain will do that for us when finishSearch is invoked.
       this.cancel();
-      // This tells Sqlite.jsm to stop providing us results and cancel the
-      // underlying query.
-      throw StopIteration;
-    }
-
-    if (notifyResults) {
+    } else if (notifyResults) {
       // Notify about results if we've gotten them.
       this.notifyResults(true);
     }
   },
 
   _processHostRow: function (row) {
     let match = {};
     let trimmedHost = row.getResultByIndex(QUERYINDEX_URL);
@@ -1455,30 +1457,20 @@ Search.prototype = {
   },
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// UnifiedComplete class
 //// component @mozilla.org/autocomplete/search;1?name=unifiedcomplete
 
 function UnifiedComplete() {
-  Services.obs.addObserver(this, TOPIC_SHUTDOWN, true);
 }
 
 UnifiedComplete.prototype = {
   //////////////////////////////////////////////////////////////////////////////
-  //// nsIObserver
-
-  observe: function (subject, topic, data) {
-    if (topic === TOPIC_SHUTDOWN) {
-      this.ensureShutdown();
-    }
-  },
-
-  //////////////////////////////////////////////////////////////////////////////
   //// Database handling
 
   /**
    * Promise resolved when the database initialization has completed, or null
    * if it has never been requested.
    */
   _promiseDatabase: null,
 
@@ -1492,45 +1484,43 @@ UnifiedComplete.prototype = {
   getDatabaseHandle: function () {
     if (Prefs.enabled && !this._promiseDatabase) {
       this._promiseDatabase = Task.spawn(function* () {
         let conn = yield Sqlite.cloneStorageConnection({
           connection: PlacesUtils.history.DBConnection,
           readOnly: true
         });
 
+        try {
+           Sqlite.shutdown.addBlocker("Places UnifiedComplete.js clone closing",
+                                      Task.async(function* () {
+                                        SwitchToTabStorage.shutdown();
+                                        yield conn.close();
+                                      }));
+        } catch (ex) {
+          // It's too late to block shutdown, just close the connection.
+          yield conn.close();
+          throw ex;
+        }
+
         // Autocomplete often fallbacks to a table scan due to lack of text
         // indices.  A larger cache helps reducing IO and improving performance.
         // The value used here is larger than the default Storage value defined
         // as MAX_CACHE_SIZE_BYTES in storage/src/mozStorageConnection.cpp.
         yield conn.execute("PRAGMA cache_size = -6144"); // 6MiB
 
         yield SwitchToTabStorage.initDatabase(conn);
 
         return conn;
       }.bind(this)).then(null, ex => { dump("Couldn't get database handle: " + ex + "\n");
                                        Cu.reportError(ex); });
     }
     return this._promiseDatabase;
   },
 
-  /**
-   * Used to stop running queries and close the database handle.
-   */
-  ensureShutdown: function () {
-    if (this._promiseDatabase) {
-      Task.spawn(function* () {
-        let conn = yield this.getDatabaseHandle();
-        SwitchToTabStorage.shutdown();
-        yield conn.close()
-      }.bind(this)).then(null, Cu.reportError);
-      this._promiseDatabase = null;
-    }
-  },
-
   //////////////////////////////////////////////////////////////////////////////
   //// mozIPlacesAutoComplete
 
   registerOpenPage: function PAC_registerOpenPage(uri) {
     SwitchToTabStorage.add(uri);
   },
 
   unregisterOpenPage: function PAC_unregisterOpenPage(uri) {
--- a/toolkit/components/places/tests/unit/test_telemetry.js
+++ b/toolkit/components/places/tests/unit/test_telemetry.js
@@ -22,16 +22,17 @@ let histograms = {
   //PLACES_AUTOCOMPLETE_1ST_RESULT_TIME_MS:  function (val) do_check_true(val > 1),
   PLACES_IDLE_FRECENCY_DECAY_TIME_MS: function (val) do_check_true(val > 0),
   PLACES_IDLE_MAINTENANCE_TIME_MS: function (val) do_check_true(val > 0),
   PLACES_ANNOS_BOOKMARKS_COUNT: function (val) do_check_eq(val, 1),
   PLACES_ANNOS_BOOKMARKS_SIZE_KB: function (val) do_check_eq(val, 1),
   PLACES_ANNOS_PAGES_COUNT: function (val) do_check_eq(val, 1),
   PLACES_ANNOS_PAGES_SIZE_KB: function (val) do_check_eq(val, 1),
   PLACES_FRECENCY_CALC_TIME_MS: function (val) do_check_true(val >= 0),
+  PLACES_MAINTENANCE_DAYSFROMLAST: function (val) do_check_true(val >= 0),
 }
 
 function run_test()
 {
   run_next_test();
 }
 
 add_task(function test_execute()
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -3403,16 +3403,24 @@
     "alert_emails": ["places-telemetry-alerts@mozilla.com"],
     "expires_in_version": "40",
     "kind": "exponential",
     "high": "100",
     "n_buckets": 10,
     "extended_statistics_ok": true,
     "description": "PLACES: Time to calculate frecency of a page (ms)"
   },
+  "PLACES_MAINTENANCE_DAYSFROMLAST": {
+    "expires_in_version" : "never",
+    "kind": "exponential",
+    "low": 7,
+    "high": 60,
+    "n_buckets" : 10,
+    "description": "PLACES: Days from last maintenance"
+  },
   "UPDATER_BACKGROUND_CHECK_CODE_EXTERNAL": {
     "expires_in_version": "default",
     "kind": "enumerated",
     "n_values": 50,
     "description": "Updater: externally initiated (typically by the application) background update check result code (see PING_BGUC_* constants defined in /toolkit/mozapps/update/nsUpdateService.js)"
   },
   "UPDATER_BACKGROUND_CHECK_CODE_NOTIFY": {
     "expires_in_version": "40",
--- a/toolkit/content/tests/chrome/chrome.ini
+++ b/toolkit/content/tests/chrome/chrome.ini
@@ -50,16 +50,17 @@ support-files =
 [test_arrowpanel.xul]
 [test_autocomplete2.xul]
 [test_autocomplete3.xul]
 [test_autocomplete4.xul]
 [test_autocomplete5.xul]
 [test_autocomplete_delayOnPaste.xul]
 [test_autocomplete_with_composition_on_input.html]
 [test_autocomplete_with_composition_on_textbox.xul]
+[test_autocomplete_placehold_last_complete.xul]
 [test_browser_drop.xul]
 skip-if = buildapp == 'mulet'
 [test_bug253481.xul]
 [test_bug263683.xul]
 [test_bug304188.xul]
 [test_bug331215.xul]
 [test_bug360220.xul]
 [test_bug360437.xul]
new file mode 100644
--- /dev/null
+++ b/toolkit/content/tests/chrome/test_autocomplete_placehold_last_complete.xul
@@ -0,0 +1,309 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Autocomplete Widget Test"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        onload="runTest();">
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+  <script type="application/javascript"
+          src="chrome://global/content/globalOverlay.js"/>
+
+<textbox id="autocomplete"
+         type="autocomplete"
+         completedefaultindex="true"
+         timeout="0"
+         autocompletesearch="simple"/>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+function autoCompleteSimpleResult(aString, searchId) {
+  this.searchString = aString;
+  this.searchResult = Components.interfaces.nsIAutoCompleteResult.RESULT_SUCCESS;
+  this.matchCount = 1;
+  if (aString.startsWith('ret')) {
+    this._param = autoCompleteSimpleResult.retireCompletion;
+  } else {
+    this._param = "Result";
+  }
+  this._searchId = searchId;
+}
+autoCompleteSimpleResult.retireCompletion = "Retire";
+autoCompleteSimpleResult.prototype = {
+  _param: "",
+  searchString: null,
+  searchResult: Components.interfaces.nsIAutoCompleteResult.RESULT_FAILURE,
+  defaultIndex: 0,
+  errorDescription: null,
+  matchCount: 0,
+  getValueAt: function() { return this._param; },
+  getCommentAt: function() { return null; },
+  getStyleAt: function() { return null; },
+  getImageAt: function() { return null; },
+  getLabelAt: function() { return null; },
+  removeValueAt: function() {}
+};
+
+var searchCounter = 0;
+
+// A basic autocomplete implementation that returns one result.
+let autoCompleteSimple = {
+  classID: Components.ID("0a2afbdb-f30e-47d1-9cb1-0cd160240aca"),
+  contractID: "@mozilla.org/autocomplete/search;1?name=simple",
+  searchAsync: false,
+  pendingSearch: null,
+
+  QueryInterface: XPCOMUtils.generateQI([
+    Components.interfaces.nsIFactory,
+    Components.interfaces.nsIAutoCompleteSearch
+  ]),
+  createInstance: function (outer, iid) {
+    return this.QueryInterface(iid);
+  },
+
+  registerFactory: function () {
+    let registrar =
+      Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar);
+    registrar.registerFactory(this.classID, "Test Simple Autocomplete",
+                              this.contractID, this);
+  },
+  unregisterFactory: function () {
+    let registrar =
+      Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar);
+    registrar.unregisterFactory(this.classID, this);
+  },
+
+  startSearch: function (aString, aParam, aResult, aListener) {
+    let result = new autoCompleteSimpleResult(aString);
+
+    if (this.searchAsync) {
+      // Simulate an async search by using a timeout before invoking the
+      // |onSearchResult| callback.
+      // Store the searchTimeout such that it can be canceled if stopSearch is called.
+      this.pendingSearch = setTimeout(() => {
+        this.pendingSearch = null;
+
+        aListener.onSearchResult(this, result);
+
+         // Move to the next step in the async test.
+        asyncTest.next();
+      }, 0);
+    } else {
+      aListener.onSearchResult(this, result);
+    }
+  },
+  stopSearch: function () {
+    clearTimeout(this.pendingSearch);
+  }
+};
+
+SimpleTest.waitForExplicitFinish();
+
+// XPFE AutoComplete needs to register early.
+autoCompleteSimple.registerFactory();
+
+let gACTimer;
+let gAutoComplete;
+let asyncTest;
+
+let searchCompleteTimeoutId = null;
+
+function finishTest() {
+  // Unregister the factory so that we don't get in the way of other tests
+  autoCompleteSimple.unregisterFactory();
+  SimpleTest.finish();
+}
+
+function runTest() {
+  gAutoComplete = $("autocomplete");
+  gAutoComplete.focus();
+
+  // Return the search results synchronous, which also makes the completion
+  // happen synchronous.
+  autoCompleteSimple.searchAsync = false;
+
+  synthesizeKey("r", {});
+  is(gAutoComplete.value, "result", "Value should be autocompleted immediately");
+
+  synthesizeKey("e", {});
+  is(gAutoComplete.value, "result", "Value should be autocompleted immediately");
+
+  synthesizeKey("VK_DELETE", {});
+  is(gAutoComplete.value, "re", "Deletion should not complete value");
+
+  synthesizeKey("VK_BACK_SPACE", {});
+  is(gAutoComplete.value, "r", "Backspace should not complete value");
+
+  synthesizeKey("VK_LEFT", {});
+  is(gAutoComplete.value, "r", "Value should stay same when navigating with cursor");
+
+  runAsyncTest();
+}
+
+function* asyncTestGenerator() {
+  synthesizeKey("r", {});
+  synthesizeKey("e", {});
+  is(gAutoComplete.value, "re", "Value should not be autocompleted immediately");
+
+  // Calling |yield undefined| makes this generator function wait until
+  // |asyncTest.next();| is called. This happens from within the
+  // |autoCompleteSimple.startSearch()| function once the simulated async
+  // search has finished.
+  // Therefore, the effect of the |yield undefined;| here (and the ones) below
+  // is to wait until the async search result comes back.
+  yield undefined;
+
+  is(gAutoComplete.value, "result", "Value should be autocompleted");
+
+  // Test if typing the `s` character completes directly based on the last
+  // completion
+  synthesizeKey("s", {});
+  is(gAutoComplete.value, "result", "Value should be completed immediately");
+
+  yield undefined;
+
+  is(gAutoComplete.value, "result", "Value should be autocompleted to same value");
+  synthesizeKey("VK_DELETE", {});
+  is(gAutoComplete.value, "res", "Deletion should not complete value");
+
+  // No |yield undefined| needed here as no completion is triggered by the deletion.
+
+  is(gAutoComplete.value, "res", "Still no complete value after deletion");
+
+  synthesizeKey("VK_BACK_SPACE", {});
+  is(gAutoComplete.value, "re", "Backspace should not complete value");
+
+  yield undefined;
+
+  is(gAutoComplete.value, "re", "Value after search due to backspace should stay the same"); (3)
+
+  // Typing a character that is not like the previous match. In this case, the
+  // completion cannot happen directly and therefore the value will be completed
+  // only after the search has finished.
+  synthesizeKey("t", {});
+  is(gAutoComplete.value, "ret", "Value should not be autocompleted immediately");
+
+  yield undefined;
+
+  is(gAutoComplete.value, "retire", "Value should be autocompleted");
+
+  synthesizeKey("i", {});
+  is(gAutoComplete.value, "retire", "Value should be autocompleted immediately");
+
+  yield undefined;
+
+  is(gAutoComplete.value, "retire", "Value should be autocompleted to the same value");
+
+  // Setup the scene to test how the completion behaves once the placeholder
+  // completion and the result from the search do not agree with each other.
+  gAutoComplete.value = 'r';
+  // Need to type two characters as the input was reset and the autocomplete
+  // controller things, ther user hit the backspace button, in which case
+  // no completion is performed. But as a completion is desired, another
+  // character `t` is typed afterwards.
+  synthesizeKey("e", {});
+  yield undefined;
+  synthesizeKey("t", {});
+  is(gAutoComplete.value, "ret", "Value should not be autocompleted");
+
+  yield undefined;
+
+  is(gAutoComplete.value, "retire", "Value should be autocompleted");
+
+  // The placeholder string is now set to "retire". Changing the completion
+  // string to "retirement" and see what the completion will turn out like.
+  autoCompleteSimpleResult.retireCompletion = "Retirement";
+  synthesizeKey("i", {});
+  is(gAutoComplete.value, "retire", "Value should be autocompleted based on placeholder");
+
+  yield undefined;
+
+  is(gAutoComplete.value, "retirement", "Value should be autocompleted based on search result");
+
+  // Change the search result to `Retire` again and see if the new result is
+  // complited.
+  autoCompleteSimpleResult.retireCompletion = "Retire";
+  synthesizeKey("r", {});
+  is(gAutoComplete.value, "retirement", "Value should be autocompleted based on placeholder");
+
+  yield undefined;
+
+  is(gAutoComplete.value, "retire", "Value should be autocompleted based on search result");
+
+  // Complete the value
+  gAutoComplete.value = 're';
+  synthesizeKey("t", {});
+  yield undefined;
+  synthesizeKey("i", {});
+  is(gAutoComplete.value, "reti", "Value should not be autocompleted");
+
+  yield undefined;
+
+  is(gAutoComplete.value, "retire", "Value should be autocompleted");
+
+  // Remove the selected text "re" (1) and the "et" (2). Afterwards, add it again (3).
+  // This should not cause the completion to kick in.
+  synthesizeKey("VK_DELETE", {}); // (1)
+
+  is(gAutoComplete.value, "reti", "Value should not complete after deletion");
+
+  gAutoComplete.selectionStart = 1;
+  gAutoComplete.selectionEnd = 3;
+  synthesizeKey("VK_DELETE", {}); // (2)
+
+  is(gAutoComplete.value, "ri", "Value should stay unchanged after removing character in the middle");
+
+  yield undefined;
+
+  synthesizeKey("e", {}); // (3.1)
+  is(gAutoComplete.value, "rei", "Inserting a character in the middle should not complete the value");
+
+  yield undefined;
+
+  synthesizeKey("t", {}); // (3.2)
+  is(gAutoComplete.value, "reti", "Inserting a character in the middle should not complete the value");
+
+  yield undefined;
+
+  // Adding a new character at the end should not cause the completion to happen again
+  // as the completion failed before.
+  gAutoComplete.selectionStart = 4;
+  gAutoComplete.selectionEnd = 4;
+  synthesizeKey("r", {});
+  is(gAutoComplete.value, "retir", "Value should not be autocompleted immediately");
+
+  yield undefined;
+
+  is(gAutoComplete.value, "retire", "Value should be autocompleted");
+
+  finishTest();
+  yield undefined;
+}
+
+function runAsyncTest() {
+  gAutoComplete.value = '';
+  autoCompleteSimple.searchAsync = true;
+
+  asyncTest = asyncTestGenerator();
+  asyncTest.next();
+}
+]]>
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<p id="display">
+</p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+</body>
+
+</window>
--- a/toolkit/modules/InlineSpellChecker.jsm
+++ b/toolkit/modules/InlineSpellChecker.jsm
@@ -154,16 +154,21 @@ InlineSpellChecker.prototype = {
   {
     this.mDictionaryMenu = menu;
     this.mDictionaryNames = [];
     this.mDictionaryItems = [];
 
     if (! this.mInlineSpellChecker || ! this.enabled)
       return 0;
     var spellchecker = this.mInlineSpellChecker.spellChecker;
+
+    // Cannot access the dictionary list from another process so just return 0.
+    if (Components.utils.isCrossProcessWrapper(spellchecker))
+      return 0;
+
     var o1 = {}, o2 = {};
     spellchecker.GetDictionaryList(o1, o2);
     var list = o1.value;
     var listcount = o2.value;
     var curlang = "";
     try {
         curlang = spellchecker.GetCurrentDictionary();
     } catch(e) {}
--- a/widget/cocoa/nsNativeThemeCocoa.mm
+++ b/widget/cocoa/nsNativeThemeCocoa.mm
@@ -2828,16 +2828,21 @@ nsNativeThemeCocoa::DrawWidgetBackground
       DrawTabPanel(cgContext, macRect, aFrame);
       break;
 
     case NS_THEME_RESIZER:
       DrawResizer(cgContext, macRect, aFrame);
       break;
   }
 
+  if (hidpi) {
+    // Reset the base CTM.
+    CGContextSetBaseCTM(cgContext, CGAffineTransformIdentity);
+  }
+
   nativeDrawing.EndNativeDrawing();
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 nsIntMargin
--- a/widget/windows/GfxInfo.cpp
+++ b/widget/windows/GfxInfo.cpp
@@ -971,22 +971,22 @@ GfxInfo::GetGfxDriverInfo()
       nsIGfxInfo::FEATURE_OPENGL_LAYERS, nsIGfxInfo::FEATURE_DISCOURAGED,
       DRIVER_LESS_THAN, GfxDriverInfo::allDriverVersions );
     APPEND_TO_DRIVER_BLOCKLIST2( DRIVER_OS_ALL,
       (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorIntel), GfxDriverInfo::allDevices,
       nsIGfxInfo::FEATURE_WEBGL_OPENGL, nsIGfxInfo::FEATURE_DISCOURAGED,
       DRIVER_LESS_THAN, GfxDriverInfo::allDriverVersions );
 
     /**
-     * Disable D2D on Intel HD 3000 for graphics drivers <= 8.15.10.2321.
-     * See bug 1018278.
+     * Disable acceleration on Intel HD 3000 for graphics drivers <= 8.15.10.2321.
+     * See bug 1018278 and bug 1060736.
      */
     APPEND_TO_DRIVER_BLOCKLIST( DRIVER_OS_ALL,
         (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorIntel), (GfxDeviceFamily*) GfxDriverInfo::GetDeviceFamily(IntelHD3000),
-      nsIGfxInfo::FEATURE_DIRECT2D, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
+      GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
       DRIVER_LESS_THAN_OR_EQUAL, V(8,15,10,2321), "8.15.10.2342" );
 
     /* Disable D2D on Win7 on Intel HD Graphics on driver <= 8.15.10.2302
      * See bug 806786
      */
     APPEND_TO_DRIVER_BLOCKLIST2( DRIVER_OS_WINDOWS_7,
         (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorIntel), (GfxDeviceFamily*) GfxDriverInfo::GetDeviceFamily(IntelMobileHDGraphics),
       nsIGfxInfo::FEATURE_DIRECT2D, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,