Merge the last PGO-green inbound changeset to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 17 Oct 2012 22:10:08 -0400
changeset 110739 5142bbd4da12bce9e87d95d189d7a56c6ab5d431
parent 110666 f549d4510829867973633e02675c3ef5ebabe7e0 (current diff)
parent 110738 f7cb509ab5b063e1293fea5730ed7682e70347cf (diff)
child 110740 d9520848b41007798e64514877cb89391f9067a0
child 110762 0d10847ab5481533d6055923ef3597147be7db82
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
milestone19.0a1
Merge the last PGO-green inbound changeset to m-c.
--- a/Makefile.in
+++ b/Makefile.in
@@ -43,18 +43,20 @@ ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
 tier_base_dirs += \
   other-licenses/android \
   $(NULL)
 endif
 
 ifdef MOZ_MEMORY
 tier_base_dirs += memory/mozjemalloc
 ifdef MOZ_JEMALLOC
+ifndef MOZ_NATIVE_JEMALLOC
 tier_base_dirs += memory/jemalloc
 endif
+endif
 tier_base_dirs += memory/build
 endif
 ifndef MOZ_NATIVE_ZLIB
 tier_base_dirs += modules/zlib
 endif
 tier_base_dirs += \
   mozglue \
   memory/mozalloc \
--- a/accessible/src/jsat/Presenters.jsm
+++ b/accessible/src/jsat/Presenters.jsm
@@ -124,31 +124,33 @@ VisualPresenter.prototype = {
   type: 'Visual',
 
   /**
    * The padding in pixels between the object and the highlight border.
    */
   BORDER_PADDING: 2,
 
   viewportChanged: function VisualPresenter_viewportChanged(aWindow) {
-    if (this._currentContext)
+    if (this._currentAccessible) {
+      let context = new PresenterContext(this._currentAccessible);
       return {
         type: this.type,
         details: {
           method: 'show',
-          bounds: this._currentContext.bounds,
+          bounds: context.bounds,
           padding: this.BORDER_PADDING
         }
       };
+    }
 
     return null;
   },
 
   pivotChanged: function VisualPresenter_pivotChanged(aContext, aReason) {
-    this._currentContext = aContext;
+    this._currentAccessible = aContext.accessible;
 
     if (!aContext.accessible)
       return {type: this.type, details: {method: 'hide'}};
 
     try {
       aContext.accessible.scrollTo(
         Ci.nsIAccessibleScrollType.SCROLL_TYPE_ANYWHERE);
       return {
--- a/allmakefiles.sh
+++ b/allmakefiles.sh
@@ -54,17 +54,17 @@ if [ ! "$LIBXUL_SDK" ]; then
       build/stlport/stl/config/_android.h
     "
   fi
   add_makefiles "
     memory/mozalloc/Makefile
     mozglue/Makefile
     mozglue/build/Makefile
   "
-  if [ "$MOZ_JEMALLOC" ]; then
+  if [ "$MOZ_JEMALLOC" -a -z "$MOZ_NATIVE_JEMALLOC" ]; then
     add_makefiles "
       memory/jemalloc/Makefile
     "
   fi
   if [ "$MOZ_MEMORY" ]; then
     add_makefiles "
       memory/mozjemalloc/Makefile
       memory/build/Makefile
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -261,20 +261,21 @@ pref("media.cache_size", 4096);    // 4M
 pref("media.video-queue.default-size", 3);
 
 //  0: don't show fullscreen keyboard
 //  1: always show fullscreen keyboard
 // -1: show fullscreen keyboard based on threshold pref
 pref("widget.ime.android.landscape_fullscreen", -1);
 pref("widget.ime.android.fullscreen_threshold", 250); // in hundreths of inches
 
-// optimize images memory usage
+// optimize images' memory usage
 pref("image.mem.decodeondraw", true);
 pref("content.image.allow_locking", false);
 pref("image.mem.min_discard_timeout_ms", 10000);
+pref("image.mem.max_decoded_image_kb", 5120); /* 5MB */
 
 // enable touch events interfaces
 pref("dom.w3c_touch_events.enabled", true);
 pref("dom.w3c_touch_events.safetyX", 0); // escape borders in units of 1/240"
 pref("dom.w3c_touch_events.safetyY", 120); // escape borders in units of 1/240"
 
 #ifdef MOZ_SAFE_BROWSING
 // Safe browsing does nothing unless this pref is set
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -493,16 +493,20 @@ Services.obs.addObserver(function onSyst
   shell.sendSystemMessage(msg);
 }, 'system-messages-open-app', false);
 
 Services.obs.addObserver(function(aSubject, aTopic, aData) {
   shell.sendChromeEvent({ type: "fullscreenoriginchange",
                           fullscreenorigin: aData });
 }, "fullscreen-origin-change", false);
 
+Services.obs.addObserver(function onWebappsStart(subject, topic, data) {
+  shell.sendChromeEvent({ type: 'webapps-registry-start' });
+}, 'webapps-registry-start', false);
+
 Services.obs.addObserver(function onWebappsReady(subject, topic, data) {
   shell.sendChromeEvent({ type: 'webapps-registry-ready' });
 }, 'webapps-registry-ready', false);
 
 Services.obs.addObserver(function onBluetoothVolumeChange(subject, topic, data) {
   if (data == 'up') {
     shell.sendChromeEvent({ type: 'volume-up-button-press' });
     shell.sendChromeEvent({ type: 'volume-up-button-release' });
--- a/b2g/components/YoutubeProtocolHandler.js
+++ b/b2g/components/YoutubeProtocolHandler.js
@@ -88,17 +88,17 @@ YoutubeProtocolHandler.prototype = {
       streams.forEach(function(aStream) {
         let params = extractParameters(aStream);
         let url = params["url"];
         let type = params["type"] ? params["type"].split(";")[0] : null;
 
         let index;
         if (url && type && ((index = recognizedTypes.indexOf(type)) != -1) &&
             index > bestType) {
-          uri = url;
+          uri = url + '&signature=' + (params["sig"] ? params['sig'] : '');
           mimeType = type;
           bestType = index;
         }
         for (let param in params) {
           if (["thumbnail_url", "length_seconds", "title"].indexOf(param) != -1) {
             extras[param] = decodeURIComponent(params[param]);
           }
         }
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,4 +1,4 @@
 This is the pdf.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 0.5.184
+Current extension version is: 0.6.39
 
--- a/browser/extensions/pdfjs/components/PdfStreamConverter.js
+++ b/browser/extensions/pdfjs/components/PdfStreamConverter.js
@@ -26,40 +26,46 @@ const Cu = Components.utils;
 // True only if this is the version of pdf.js that is included with firefox.
 const MOZ_CENTRAL = true;
 const PDFJS_EVENT_ID = 'pdf.js.message';
 const PDF_CONTENT_TYPE = 'application/pdf';
 const PREF_PREFIX = 'pdfjs';
 const PDF_VIEWER_WEB_PAGE = 'resource://pdf.js/web/viewer.html';
 const MAX_DATABASE_LENGTH = 4096;
 const FIREFOX_ID = '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}';
-const SEAMONKEY_ID = '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}';
-const METRO_ID = '{99bceaaa-e3c6-48c1-b981-ef9b46b67d60}';
 
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.import('resource://gre/modules/Services.jsm');
 Cu.import('resource://gre/modules/NetUtil.jsm');
 
 
 let appInfo = Cc['@mozilla.org/xre/app-info;1']
                   .getService(Ci.nsIXULAppInfo);
-let privateBrowsing, inPrivateBrowsing;
 let Svc = {};
 XPCOMUtils.defineLazyServiceGetter(Svc, 'mime',
                                    '@mozilla.org/mime;1',
                                    'nsIMIMEService');
 
+let isInPrivateBrowsing;
 if (appInfo.ID === FIREFOX_ID) {
-  privateBrowsing = Cc['@mozilla.org/privatebrowsing;1']
-                          .getService(Ci.nsIPrivateBrowsingService);
-  inPrivateBrowsing = privateBrowsing.privateBrowsingEnabled;
-} else if (appInfo.ID === SEAMONKEY_ID ||
-           appInfo.ID === METRO_ID) {
-  privateBrowsing = null;
-  inPrivateBrowsing = false;
+  let privateBrowsing = Cc['@mozilla.org/privatebrowsing;1']
+                            .getService(Ci.nsIPrivateBrowsingService);
+  isInPrivateBrowsing = function getInPrivateBrowsing() {
+    return privateBrowsing.privateBrowsingEnabled;
+  };
+} else {
+  isInPrivateBrowsing = function() { return false; };
+}
+
+function getChromeWindow(domWindow) {
+  var containingBrowser = domWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                                   .getInterface(Ci.nsIWebNavigation)
+                                   .QueryInterface(Ci.nsIDocShell)
+                                   .chromeEventHandler;
+  return containingBrowser.ownerDocument.defaultView;
 }
 
 function getBoolPref(pref, def) {
   try {
     return Services.prefs.getBoolPref(pref);
   } catch (ex) {
     return def;
   }
@@ -268,25 +274,25 @@ ChromeActions.prototype = {
                                            aOffset, aCount);
         }
       };
 
       channel.asyncOpen(listener, null);
     });
   },
   setDatabase: function(data) {
-    if (inPrivateBrowsing)
+    if (isInPrivateBrowsing())
       return;
     // Protect against something sending tons of data to setDatabase.
     if (data.length > MAX_DATABASE_LENGTH)
       return;
     setStringPref(PREF_PREFIX + '.database', data);
   },
   getDatabase: function() {
-    if (inPrivateBrowsing)
+    if (isInPrivateBrowsing())
       return '{}';
     return getStringPref(PREF_PREFIX + '.database', '{}');
   },
   getLocale: function() {
     return getStringPref('general.useragent.locale', 'en-US');
   },
   getLoadingType: function() {
     return this.dataListener ? 'passive' : 'active';
@@ -331,18 +337,21 @@ ChromeActions.prototype = {
     } catch (e) {
       log('Unable to retrive localized strings: ' + e);
       return 'null';
     }
   },
   pdfBugEnabled: function() {
     return getBoolPref(PREF_PREFIX + '.pdfBugEnabled', false);
   },
-  searchEnabled: function() {
-    return getBoolPref(PREF_PREFIX + '.searchEnabled', false);
+  supportsIntegratedFind: function() {
+    // Integrated find is only supported when we're not in a frame and when the
+    // new find events code exists.
+    return this.domWindow.frameElement === null &&
+           'updateControlState' in getChromeWindow(this.domWindow).gFindBar;
   },
   fallback: function(url, sendResponse) {
     var self = this;
     var domWindow = this.domWindow;
     var strings = getLocalizedStrings('chrome.properties');
     var message = getLocalizedString(strings, 'unsupported_feature');
 
     var notificationBox = null;
@@ -386,16 +395,30 @@ ChromeActions.prototype = {
       // added in the future we still only care about removed at the moment.
       if (eventType !== 'removed')
         return;
       // Don't send a response again if we already responded when the button was
       // clicked.
       if (!sentResponse)
         sendResponse(false);
     });
+  },
+  updateFindControlState: function(data) {
+    if (!this.supportsIntegratedFind())
+      return;
+    // Verify what we're sending to the findbar.
+    var result = data.result;
+    var findPrevious = data.findPrevious;
+    var findPreviousType = typeof findPrevious;
+    if ((typeof result !== 'number' || result < 0 || result > 3) ||
+        (findPreviousType !== 'undefined' && findPreviousType !== 'boolean')) {
+      return;
+    }
+    getChromeWindow(this.domWindow).gFindBar
+                                   .updateControlState(result, findPrevious);
   }
 };
 
 // Event listener to trigger chrome privedged code.
 function RequestListener(actions) {
   this.actions = actions;
 }
 // Receive an event and synchronously or asynchronously responds.
@@ -426,16 +449,67 @@ RequestListener.prototype.receive = func
         listener.initEvent('pdf.js.response', true, false);
         return message.dispatchEvent(listener);
       }
     }
     actions[action].call(this.actions, data, response);
   }
 };
 
+// Forwards events from the eventElement to the contentWindow only if the
+// content window matches the currently selected browser window.
+function FindEventManager(eventElement, contentWindow, chromeWindow) {
+  this.types = ['find',
+                'findagain',
+                'findhighlightallchange',
+                'findcasesensitivitychange'];
+  this.chromeWindow = chromeWindow;
+  this.contentWindow = contentWindow;
+  this.eventElement = eventElement;
+}
+
+FindEventManager.prototype.bind = function() {
+  var unload = function(e) {
+    this.unbind();
+    this.contentWindow.removeEventListener(e.type, unload);
+  }.bind(this);
+  this.contentWindow.addEventListener('unload', unload);
+
+  for (var i = 0; i < this.types.length; i++) {
+    var type = this.types[i];
+    this.eventElement.addEventListener(type, this, true);
+  }
+};
+
+FindEventManager.prototype.handleEvent = function(e) {
+  var chromeWindow = this.chromeWindow;
+  var contentWindow = this.contentWindow;
+  // Only forward the events if they are for our dom window.
+  if (chromeWindow.gBrowser.selectedBrowser.contentWindow === contentWindow) {
+    var detail = e.detail;
+    detail.__exposedProps__ = {
+      query: 'r',
+      caseSensitive: 'r',
+      highlightAll: 'r',
+      findPrevious: 'r'
+    };
+    var forward = contentWindow.document.createEvent('CustomEvent');
+    forward.initCustomEvent(e.type, true, true, detail);
+    contentWindow.dispatchEvent(forward);
+    e.preventDefault();
+  }
+};
+
+FindEventManager.prototype.unbind = function() {
+  for (var i = 0; i < this.types.length; i++) {
+    var type = this.types[i];
+    this.eventElement.removeEventListener(type, this, true);
+  }
+};
+
 function PdfStreamConverter() {
 }
 
 PdfStreamConverter.prototype = {
 
   // properties required for XPCOM registration:
   classID: Components.ID('{d0c5195d-e798-49d4-b1d3-9324328b2291}'),
   classDescription: 'pdf.js Component',
@@ -538,31 +612,39 @@ PdfStreamConverter.prototype = {
         var domWindow = getDOMWindow(channel);
         // Double check the url is still the correct one.
         if (domWindow.document.documentURIObject.equals(aRequest.URI)) {
           let actions = new ChromeActions(domWindow, dataListener);
           let requestListener = new RequestListener(actions);
           domWindow.addEventListener(PDFJS_EVENT_ID, function(event) {
             requestListener.receive(event);
           }, false, true);
+          if (actions.supportsIntegratedFind()) {
+            var chromeWindow = getChromeWindow(domWindow);
+            var findEventManager = new FindEventManager(chromeWindow.gFindBar,
+                                                        domWindow,
+                                                        chromeWindow);
+            findEventManager.bind();
+          }
         }
         listener.onStopRequest.apply(listener, arguments);
       }
     };
 
     // Keep the URL the same so the browser sees it as the same.
     channel.originalURI = aRequest.URI;
     channel.asyncOpen(proxy, aContext);
     if (useFetchByChrome) {
       // We can use resource principal when data is fetched by the chrome
       // e.g. useful for NoScript
       var securityManager = Cc['@mozilla.org/scriptsecuritymanager;1']
                             .getService(Ci.nsIScriptSecurityManager);
       var uri = ioService.newURI(PDF_VIEWER_WEB_PAGE, null, null);
-      // FF16 and below had getCodebasePrincipal (bug 774585)
+      // FF16 and below had getCodebasePrincipal, it was replaced by
+      // getNoAppCodebasePrincipal (bug 758258).
       var resourcePrincipal = 'getNoAppCodebasePrincipal' in securityManager ?
                               securityManager.getNoAppCodebasePrincipal(uri) :
                               securityManager.getCodebasePrincipal(uri);
       channel.owner = resourcePrincipal;
     }
   },
 
   // nsIRequestObserver::onStopRequest
--- a/browser/extensions/pdfjs/content/PdfJs.jsm
+++ b/browser/extensions/pdfjs/content/PdfJs.jsm
@@ -18,20 +18,22 @@ var EXPORTED_SYMBOLS = ["PdfJs"];
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 const Cm = Components.manager;
 const Cu = Components.utils;
 
 const PREF_PREFIX = 'pdfjs';
 const PREF_DISABLED = PREF_PREFIX + '.disabled';
-const PREF_FIRST_RUN = PREF_PREFIX + '.firstRun';
+const PREF_MIGRATION_VERSION = PREF_PREFIX + '.migrationVersion';
 const PREF_PREVIOUS_ACTION = PREF_PREFIX + '.previousHandler.preferredAction';
 const PREF_PREVIOUS_ASK = PREF_PREFIX + '.previousHandler.alwaysAskBeforeHandling';
+const PREF_DISABLED_PLUGIN_TYPES = 'plugin.disable_full_page_plugin_for_types';
 const TOPIC_PDFJS_HANDLER_CHANGED = 'pdfjs:handlerChanged';
+const PDF_CONTENT_TYPE = 'application/pdf';
 
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.import('resource://gre/modules/Services.jsm');
 Cu.import('resource://pdf.js.components/PdfStreamConverter.js');
 
 let Svc = {};
 XPCOMUtils.defineLazyServiceGetter(Svc, 'mime',
                                    '@mozilla.org/mime;1',
@@ -40,16 +42,24 @@ XPCOMUtils.defineLazyServiceGetter(Svc, 
 function getBoolPref(aPref, aDefaultValue) {
   try {
     return Services.prefs.getBoolPref(aPref);
   } catch (ex) {
     return aDefaultValue;
   }
 }
 
+function getIntPref(aPref, aDefaultValue) {
+  try {
+    return Services.prefs.getIntPref(aPref);
+  } catch (ex) {
+    return aDefaultValue;
+  }
+}
+
 // Register/unregister a constructor as a component.
 let Factory = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory]),
   _targetConstructor: null,
 
   register: function register(targetConstructor) {
     this._targetConstructor = targetConstructor;
     var proto = targetConstructor.prototype;
@@ -79,47 +89,80 @@ let Factory = {
   }
 };
 
 let PdfJs = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
   _registered: false,
 
   init: function init() {
-    // On first run make pdf.js the default handler.
-    if (!getBoolPref(PREF_DISABLED, true) && getBoolPref(PREF_FIRST_RUN, false)) {
-      Services.prefs.setBoolPref(PREF_FIRST_RUN, false);
-
-      let handlerInfo = Svc.mime.getFromTypeAndExtension('application/pdf', 'pdf');
-      // Store the previous settings of preferredAction and
-      // alwaysAskBeforeHandling in case we need to revert them in a hotfix that
-      // would turn pdf.js off.
-      Services.prefs.setIntPref(PREF_PREVIOUS_ACTION, handlerInfo.preferredAction);
-      Services.prefs.setBoolPref(PREF_PREVIOUS_ASK, handlerInfo.alwaysAskBeforeHandling);
-
-      let handlerService = Cc['@mozilla.org/uriloader/handler-service;1'].
-                           getService(Ci.nsIHandlerService);
-
-      // Change and save mime handler settings.
-      handlerInfo.alwaysAskBeforeHandling = false;
-      handlerInfo.preferredAction = Ci.nsIHandlerInfo.handleInternally;
-      handlerService.store(handlerInfo);
+    if (!getBoolPref(PREF_DISABLED, true)) {
+      this._migrate();
     }
 
     if (this.enabled)
       this._ensureRegistered();
     else
       this._ensureUnregistered();
 
     // Listen for when pdf.js is completely disabled or a different pdf handler
     // is chosen.
     Services.prefs.addObserver(PREF_DISABLED, this, false);
     Services.obs.addObserver(this, TOPIC_PDFJS_HANDLER_CHANGED, false);
   },
 
+  _migrate: function migrate() {
+    const VERSION = 1;
+    var currentVersion = getIntPref(PREF_MIGRATION_VERSION, 0);
+    if (currentVersion >= VERSION) {
+      return;
+    }
+    // Make pdf.js the default pdf viewer on the first migration.
+    if (currentVersion < 2) {
+      this._becomeHandler();
+    }
+    Services.prefs.setIntPref(PREF_MIGRATION_VERSION, VERSION);
+  },
+
+  _becomeHandler: function _becomeHandler() {
+    let handlerInfo = Svc.mime.getFromTypeAndExtension(PDF_CONTENT_TYPE, 'pdf');
+    let prefs = Services.prefs;
+    if (handlerInfo.preferredAction !== Ci.nsIHandlerInfo.handleInternally &&
+        handlerInfo.preferredAction !== false) {
+      // Store the previous settings of preferredAction and
+      // alwaysAskBeforeHandling in case we need to revert them in a hotfix that
+      // would turn pdf.js off.
+      prefs.setIntPref(PREF_PREVIOUS_ACTION, handlerInfo.preferredAction);
+      prefs.setBoolPref(PREF_PREVIOUS_ASK, handlerInfo.alwaysAskBeforeHandling);
+    }
+
+    let handlerService = Cc['@mozilla.org/uriloader/handler-service;1'].
+                         getService(Ci.nsIHandlerService);
+
+    // Change and save mime handler settings.
+    handlerInfo.alwaysAskBeforeHandling = false;
+    handlerInfo.preferredAction = Ci.nsIHandlerInfo.handleInternally;
+    handlerService.store(handlerInfo);
+
+    // Also disable any plugins for pdfs.
+    var stringTypes = '';
+    var types = [];
+    if (prefs.prefHasUserValue(PREF_DISABLED_PLUGIN_TYPES)) {
+      stringTypes = prefs.getCharPref(PREF_DISABLED_PLUGIN_TYPES);
+    }
+    if (stringTypes !== '') {
+      types = stringTypes.split(',');
+    }
+
+    if (types.indexOf(PDF_CONTENT_TYPE) === -1) {
+      types.push(PDF_CONTENT_TYPE);
+    }
+    prefs.setCharPref(PREF_DISABLED_PLUGIN_TYPES, types.join(','));
+  },
+
   // nsIObserver
   observe: function observe(aSubject, aTopic, aData) {
     if (this.enabled)
       this._ensureRegistered();
     else
       this._ensureUnregistered();
   },
   
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..08a2c25327d7df3c77186cc3048bf02b7e72f393
GIT binary patch
literal 371
zc%17D@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6n3BBRT^Rni_n+Ah<Z%{wL>4nJ
za0`PlBg3pY5<o%r5>H=O_B&jxVj>1dW-W_hU|?kNba4#PIA1#9tk+=&k=FgQ+pdT`
zUbe$HMe!GtU6WGBI$aLYZm~5FlsUCLM9rJ>x)ki1v&;2jM441q`dLYetDQYE-*|TQ
z^E<_9?^Y%rUfkJzY)ZIaD_<MC&cmBh$CtgYp8dL|p1-2*N5P)WO~<yom=<t<b<MX{
zV0F4SV~dnB5HBwOxorLKx2`PB3<(U3Mv^-!Sehqq>wl!)(o;G8S#tSeKda!GbIv?G
zbmhsbOZ!$BA5pw%wO?3d{?EV_k<*RN+E-q_-@A4@_jI3mOCvtN%FsWiAoSU(>6p5s
zu;<LjPZQOdDxY?~D4*1|{qN@6tRl%q{~C{f`Y<K**{Z}4_g5KqIsN)|hXdZr_a9ya
P3?c?kS3j3^P6<r_kMx(q
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..beef8ccea48c932571c9c9009df916a61c850a1c
GIT binary patch
literal 381
zc$@)!0fPRCP)<h;3K|Lk000e1NJLTq000mG000mO1ONa4wfZ;e00002VoOIv0RM-N
z%)bBt010qNS#tmY3ljhU3ljkVnw%H_000McNliru+zJI29sn4uetG}^0S-w-K~yNu
zb&)+w13?gmpWQt!_hI5eD#4IKO<|GNA0TLBqfH`0&_WQwe<O&XAP9<DC}<P>Cn>#B
z0z$y#_7d0P?!t27TP(BlKC{g1C~Qf5JYZ(0Y`HA=4{6~34>Y$nUyp4>0?NXx%y!n^
zPO8lU5v<pjhGYCX!!bT>vjOn@v3E&~2En%3M*;>A02l=3;j?=|jFv2*(IokqkYu+`
z?f#Sot_0GOokeN4@_t0%AeiK{n7pfbJT>4jCFQRS;-#{C!?(j16Xd+w6vSiLIVVO7
zT0|%UMkoT>Cd44_!h@@9OoxH>aKs|3a2xe<yZqHZjL=WD-gRM@<<aV&v0^Vvl-BK`
bNbKrg3mRK+IzR8P00000NkvXXu0mjfaG9N{
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..beef8ccea48c932571c9c9009df916a61c850a1c
GIT binary patch
literal 381
zc$@)!0fPRCP)<h;3K|Lk000e1NJLTq000mG000mO1ONa4wfZ;e00002VoOIv0RM-N
z%)bBt010qNS#tmY3ljhU3ljkVnw%H_000McNliru+zJI29sn4uetG}^0S-w-K~yNu
zb&)+w13?gmpWQt!_hI5eD#4IKO<|GNA0TLBqfH`0&_WQwe<O&XAP9<DC}<P>Cn>#B
z0z$y#_7d0P?!t27TP(BlKC{g1C~Qf5JYZ(0Y`HA=4{6~34>Y$nUyp4>0?NXx%y!n^
zPO8lU5v<pjhGYCX!!bT>vjOn@v3E&~2En%3M*;>A02l=3;j?=|jFv2*(IokqkYu+`
z?f#Sot_0GOokeN4@_t0%AeiK{n7pfbJT>4jCFQRS;-#{C!?(j16Xd+w6vSiLIVVO7
zT0|%UMkoT>Cd44_!h@@9OoxH>aKs|3a2xe<yZqHZjL=WD-gRM@<<aV&v0^Vvl-BK`
bNbKrg3mRK+IzR8P00000NkvXXu0mjfaG9N{
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..08a2c25327d7df3c77186cc3048bf02b7e72f393
GIT binary patch
literal 371
zc%17D@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6n3BBRT^Rni_n+Ah<Z%{wL>4nJ
za0`PlBg3pY5<o%r5>H=O_B&jxVj>1dW-W_hU|?kNba4#PIA1#9tk+=&k=FgQ+pdT`
zUbe$HMe!GtU6WGBI$aLYZm~5FlsUCLM9rJ>x)ki1v&;2jM441q`dLYetDQYE-*|TQ
z^E<_9?^Y%rUfkJzY)ZIaD_<MC&cmBh$CtgYp8dL|p1-2*N5P)WO~<yom=<t<b<MX{
zV0F4SV~dnB5HBwOxorLKx2`PB3<(U3Mv^-!Sehqq>wl!)(o;G8S#tSeKda!GbIv?G
zbmhsbOZ!$BA5pw%wO?3d{?EV_k<*RN+E-q_-@A4@_jI3mOCvtN%FsWiAoSU(>6p5s
zu;<LjPZQOdDxY?~D4*1|{qN@6tRl%q{~C{f`Y<K**{Z}4_g5KqIsN)|hXdZr_a9ya
P3?c?kS3j3^P6<r_kMx(q
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1b2df8093d630ca5e4b7c9127fd4fc855655599b
GIT binary patch
literal 9025
zc${^ac{G%N`^RUDeczJoO@%B&_UzRp$(Cj!yOMoN_FW<p5|TYx=N7W>OxY5WG<MBM
zl6{LI+war!e1GTk$Mc<Y#(mB(Gu-#<dS9>ih%wZ^!bHzQ4}-y&u4<!=z>yW)*=VW3
z*DvJ~9pFG~a|fdfj*Q?A(`}Jt0|y?v|2_u$|0lry|7MH$35Q#})S`jGC~~i&FJP47
zf}g#;cE_HxGjxQE-t3d6)0#PL^f`Yt?3=Boh|HaZQ8C$4={$9+vj+^1FCHH*{iH`_
zY<KExJ^Rwd^wOzX@p*lQ=SX2m3$)`!`((}B5A(O#xx1=;z1so@eCy{X8S3(u<ANx5
zs69+cZsG7(cw_utoUg`H*YC?r?>a)+C8VSR@zINoIf^>`cn!Rs5}!UIF(oDC`4Boh
zI$v8j$sfMY%}A}GU0`HIO%a}RG51rjb%>|uMn%5%opf4j)TbYfEA}HJBUfejwtxQo
z*+)JgMny$sOG-#2_s(wk78uPtwpf;zm6snm*)dB`E9sB)_xF>vE0>p-MJy~VtY9iV
zwi}C{4XQptL7B;%PdSS*Vlg@e8=uGrXYs4c%fXFxb&9AS*i+7wWD7I1Ky?j`HcuZP
zAF^xQvlSN?mp&IaH~p(0bRH|{^`(v7k&RtmS*a}=nin*}eJQD<>sxa2@K~F>m}^)!
zl1M4@5s$~`nU-=<H+v}Ysj8_R6x=L1AbfC?b#7{I4qDycZ|48Wed5IWQ#pzGnEdr1
z^&=skIA-|pW1&Rt(;K_{h^sSk8twg-G}81z<5N=y6dq=59W(1!@iYPyZwqOL$HsPP
z{p<q*ev@xk_<i%Yj2apF3HJ>P8S%cqx3?E6At{*>78pMhL|c8T2mBH0D6UwJXEJ$t
zdBI^hy23PJd-yA3F13PdcPdOwjee}HdAv3h?UO*El>3iGeyx8qpPij;zZIZ1{B$cO
zKXBbIi17ttMdz_Lz5E$#JE{KHjg5`tEM6|It}Pp?s{>kX%iq7}`*?U5(i~=IWd)D)
zL3Z04&#xLA8><~ZethWJZ`h9?Kf3NWOfp(O`wbBmFgVQPTABL0ayM1?Ltli#HBP__
z#F;zx{z4l5zme`5dOZY?uC%kgfB}zD{DZYGYqv%cIXk`XSRKz)hpK#(TMG4;$2UiW
zr-ogY>?Av_k3}9F4`2`v6jvLq5=fTt8;f|UU^z9Et@cZZ^ZT(_QzZAf-tmW;O~d*u
zT2JM-84#NpX+-JGEUC$dbLmH%JJT(rrJXQ1={zgc+!Qt-7Q(c~&fQS+c5*V4YDaHq
zzl=S=xFS@tH-KK1=fsI~C3>DL4u)7O39eX#)WYJTV0BH+!RY8{{ST#&!K4IDp|vFF
zH;iEoew#v|w^AS{Cnr=CSx(;Gj<>hB9{^`pE2+m8l{3)hZ9$%JQ7|(xHT``H%}C0%
z;r2zLP?ot+Wo_-Y|D{Xqa%->a>d1TG%G!*0t6&KwkR>?!`uMzxK_jVjYpbh+S{fU9
zu-J2#(bz5W{QP|AkGVObfPld77R%ML8Kq8Vae-o|lL85$=M)sSeU-CN@JGm30xz$i
zS7M`Enwwp!>*|g^fBAy>t`@C*mOW-ay~p;0XN>eG`(I@8mH31NXZR^jdct?lh6r<%
zG1OHZ?f1Cz+m+p*uM|!+)YLARnk(2g4B_YR-@irp`T4WLFwU&>kr!iQV{;&<>W!i~
zF?%?h^}D&vU<9VcTbg^H{>67Ab3uH;)BLhB6up-Sd(4oF!d`~!M6fyZiFyIvE!RKV
z)urWTXJ;o!%N=!&-lQ0M{rdH3MMXtH$cacIy%c=G_^4eF(V`jT;P4(UY-VQGgspK8
z3=C9C>Um<Ir*{HYyfEcVe@#wNQ3I9QTZ$K2Lt_zdtv$H4EzaJ=u3DCcMQ6rnpUl!%
z(OiD_?p;o;YtGG$?5U|KMetg8N#!vyG5Tz0qu-A)J<};L+U)7MdJ%g7!(qgDVmrIw
z*Ri3whJMv;ZK35nZ?V_U-y1k0?9=FB5zMuVUekXuj`-h<i(d{^2549O!#L$1#;<9c
z`Ez!KiYT(^5#{ywiM(=J4$4x_5ralaj=UlfdQFCCkLxx!WvY%hGIbujbnA*_$Q8eQ
zPb3+MvS`3{)H3wTeZ!_-R5>X<K0MT!FO>K3R@Ml4J&Pt&Zs6R}$+f*uPkRMK5JHvW
zT}?ez{msn*_zlek-3LMHt5jvb4-NxszFZ-(DsAirC4}*+sj0ONB|i+i5Al4M^_;@r
zxzks-?zgXar%$lfwL2U;Q7c&DM7u`#5_5cwk|I2VhK6Pfxp6x%aIYOCcM%%7e#70}
z-P_)NZVON$b?w?U0uG0pLwX+bqp%G_VX!xijgz`w$VtEGv5hhxSr+4pErm?Wp{=q-
zEpV0H>uPG$;D?nGi(~KKzb^-*%^YLH5)_-7ng++l##)SUUY)DtHo30-V3&?pQ0sj@
zk&(&C{kabxKF}L-CZ1wh%Zu(nqr1Ah6P}!t;^pCqkASl#>R4G3hvMRxjL{NoF>wBy
z$i$tu-ctcWtTAX57k#8+ff3Ggys&+GZLYn&T^zt<DfDS<>;xJ+f!!*D3PHfNIy*W#
zQhk?u;2_77m6_R$gK#{B2hRO8K`g%>UCezHMwX-KzPAF8&^+!Ta2R|VS5k5fK2kNd
zG!YpYY5dmu4I&zGfkeT&9?&wsySw{&ZEcOqTB7#@OdW#-u&+p=P+zYT;%#NdC>gtM
zS2*LDUe+}=sZcPE*9Kc)RfdO$-Si9$G`M+qDq)6%-fne?2@pBPr8ixL=)PkJO7Zs@
zxM3)qlQvc=lpoIKFNK_~sHmW2eevSO4l5%yad;v7V(yxozkdV${t%H!6hq!+^AEj!
zHASc3UTCOB38V)@AP_h&<pUTi)>SOESHs=aHPQe!P&pnmaa2CXz$*fOyRyY9^%vq)
z{}ST~Oqu}k)_)ME{!fe>BssEng<jel+rSlHD<O_Nw?0XTn(B1B)F~<|8rPXFK^4PV
z`G~1bPhkuZO0f^;>xn{`pJt^!Eu5)}kKv$UiDO4}QPVrh>zqvdA{{uIz{FdU)ZEjz
z*QaZj?N4$}x^c))GJXkN6u`hjU@Hi9k!^%`ycN?vm0WPu+h4z`d8Jz{-TQWx)%{Va
z;!Kl3MeN1(z9BwBkh_CJF`XYfEoB*qqoTODxKrNV-f?(hqV{)R>=wLGHmyEfETMa|
zcX04}O?30*__(qH<BYBnU+dK1;602er~2#i@;}KPa)OsP@GR0eTq8@kEd~Ne>hyxd
zQp_2!T3J~5oR*em-e8V0d0s>Frl~1#mPEqq7Y*US8|t@9e`RE0X?r8Fm}Z8?V2R61
zOXgYW>Er#go~h{XHLeqTMK^;=pw&}}@24E(e!Bn``TfL^-B|np^0N?9Mp-$)Wn#Nz
ze0+SU69$r8AVI^*!eT30TNf=rF;(WLdU!)Ur<__f*RWVPqrbIx*0VrI7mTUZD3N%S
zXO60aNy^L1ZxuULK|aM!6H7}jH-dwMUmV5GYsl(VH350bgIx9XHtFnO8Wd{kIjiSL
zWnYB28GR^%@p>e_eeu(PYgbLVG+i#RfUGhKPaw)vij-L%PP+N-O-`D-SzB9kys<!y
z1miWacRf8T18r=kv!O2bn1@uq<mKed5pFORQ!}$mnpQ%r(U236kLBTx4s>H%8<8pu
zvh?D#gI#SS<|wPGGQ#LciO~34>ou;Ya8xm5fR)OA9TpwLnfUGfdu`gaCkY9mGI~`c
z5_ZgG`(R^Z<K$XHQIUuQ@-o}*&atZnMm=qHb*UIEgHTdWbxmDe5s35>6A2$zH#bWh
z+T-$l{mK27%upEhCWQcIMmy#&-f8|N(ocNF0p8#K!#geDJ;_Sj%$KuUqx%PHUD$c(
z46Y&lc1dCxq5h25{c7bU>HHF2hgcJ~jus~BI}cpS)uRm%>#pSs?H$HcR>!#!#2?)E
zB6A7Xo<-ToPhB4EOssnJ_4-8NfqGUbQLyd)M>4+T>5XkHsq0$B@lU^=9Wk%bv2O2I
zX?9c{;DbpYoCQs8-CBp+iE;)EFc4aJm?AGKhV{<O%xLB1pu=B6S|TDM<#4jHii*?^
z&jvFbW+B#ACJolwZ!42i;>1Y{a}C=G69%W-$D-~QI%I-KEa#1yHYgfW1Yv(v<J#2P
z)U>z$<42*po7+dOL@tsuiEk~d82Y}syU4-85$@R#WaHqVJu*DJl$4azz3$gcVOT;y
z)tB)O%z6s0EG;#=r~jDl>+36zvT@l#19f?W5wkG0v<$JG*!E}hx4tz@EHYI!B#3}z
zG>;KW0W1f@R5=p^IG(a6GP5Ou0ByW`D-?}P9I}O>JLXqb4ju2@$-9h>y8NA&)GM2&
zTUca{QbZi0k@sp1Mft{Sm*vvEa2R^!`1>Q?Q`{cv>@l>^cnK5%jkmS7hLpCn9LSWj
z=}U9>>FMbOL(qXh5&0++?u9rt=<rJxKN2PtWwTGaPw`~k+(lc6wVrCS*RD!&b2li}
zF@40pP>I8j_a|kmmAQG}>7<_WFk-l1Sl!2BsJgbcRR#0|FB_Y;f3m^T!{h0~&!5G7
z{QNaA3ov0Rw=3RnZ*O0%Fxi*oV|J&Z2)FC4458M<wr9U%6ylBTbQ2U5tfLTw`C<r8
z&dz(Z+*I!%9xOrHuTBUl&xZT>@uM*8==0~#H-_W{19uBS#DlXwd~R(3EYCA+xX0Sd
zDQC~ebCh7jZeimPPvYaZSr{1^5wD<iry6A-e<PnAE-_7))|2(*kKl8^Xjn*xBRqd0
zPxmho@3gx0SH#o*LEb>y+!$C)ck{@-E)DO6q_jER!6(TbFLc)i#O30@(CNE;&i`V<
zQOuox?Rc5F?NC-d-&0Lm+6PF^$p#6pT*G<c60Rs_s^z}0@`$14)9q%o-{ybRoVNSS
z!aTVdOg3LpBQ~s|zQ=iMc+BvTxzaz*3c?k`<AvN=lwce?d_RL%;Tahj)U1x6xCj{9
zUjU1`h0%n8fk7)T!$dO{GOenrG7g(>ZEZ~e{8ybib&3cw*9?E)qM!mnTx)Pvnhsbx
zsHP;Kd?x4+HC0trSv^@fB_$<gb@h;iz1CY|DQ-(kOK4Dgj#v{FNaF3%%K8QdIj^9~
zrl!TwS~Y5216|$8a)ML*Vq*)$bCd99&R?x;F$7q3E0q8+o8Z)eId>eyjeG@}sw*om
zOpT82lo;XAm-8Jawigw%O^gV~<7e{uy3hXD+&x7yU>g)BrAOzZ(a%#;2V5o&TdJxy
z<>!QuTjAm1b3j)GAHRPStqK#c1YXd%(CEfZdnT!88}!sNjM$b|mb$t+b6~db5c$C8
zrCOpkcd?Txwi_#7)zT7NP*gOBTs@Kb{Q2`qaO21JQ_9bW%5!kTT5Ba1QJ>yB(>-c0
z!4Kfcnj0Fq4GrfRbmu%923uNM43LTMX+7woDMN^e?9$TG0?<hX;TqI~K^L$EAQ(3G
z_xDRc`Tq?DJQnDhWQq<}Z6Fq2ce}!bKtAXOKdzC|I~xI(k{L45;|A`_eHf<Z)C*~j
z_h9b^Pw#~&lK$|n0M^YKgEbB+fcRPWso4C_B5Mi?3Qn_{f;2c5gN{eE;0#6gTUuKW
z5!U%b^8@I7VWbps-Uw75=w``zz`L%V-rcUQu2xDkcBb?8`u5wm7hWe`Vq!DNJZw`t
zg4w0eZPmgGHvi(C;a?(tUzHE=j{k>u#y?79pq=5%-K~*uF79ge&yA>UZ`}BJ@gkRs
zeAa8z^y;gU)t_!J^u(&X*UuKN%-}??85(l?ts8bG30*X}qoO;|lzj10Bs(vnRc~)t
zj!w*nVE?Gl+I3t?buF-UVAbF6FlhSz`$V!Ye)i6!NU_q7P=q<c>DplrI}M4lj6%;!
z;?0{khID>VNr}|+;`V2b78Vw~)=!>1QTpJ08)k`>r>3Eig2e!Hl?6F7=%Ex;UQ*Hv
z`<*xFkO1)%xG`#qh0~|@5-Qr-R=E;IczJmr0U03-4i0j}pueszEh!Dn|C$AtaeRG!
z-J9(m%$T3~5t&3H8T`lKrP2!hskA$8SA<N|O5J)+$cNmp-T)9S5MMf{+0O~taCemg
zUyp`#3Zjc4H#b*T`Tl`{wmw@KZ)Bxgy502qXh-ano1<e)LR{Q|qP+ZFgcKSr9`-ak
zlY=eo#S0}lz0I_NIdKn0q!A-^L;{$(%V=G;d3c!$*ju4*Bzl?ZDZ=qRwlWQAJ+_Z$
z6<X@*=K5wmvp{1&)JMD}(P3&Vu&1E$rs`_1+=_~I2`g58J-z+`Ao%C7%aXIzp-;|-
zuZA~8*-TQ4^?r{XSv+8Rfe4|}e@k=nDi({C`oxvcZ6zfoRUW9CEf^jm1k0p)g^vIo
zyAAY&QzYb?ipRTL$OtqG1v$CooXkuKW=2MrqM`YxHr%T|zP_t4Meqx@MP8gvt*NQ$
z2Lst$Q{xTlwCmo%VG*p@X_+mu^C09%Z=$x1>JTOXLG$Z=UE!f&n~)RuW;YvaVjc2!
zLVP?mY^1MG_v_qdi`cPbTbW%uH@6ij{KCqLx(Kp1y7|E6TIcP-oPN6*D1hf>Ztire
zV>*XsB$WNpu4)}4<_%3oet6~eZM*znS1@Xp$@9XJuJm8TGyO}#E9lz+;=BJLp7~G0
z>uH<a;_MFX{w^;ZLdrl7883?-vv(<t7mU$%GLsBn`Y_se`|&k<oY$#(F=YL!rw29V
znR-@g<8KoAqiI<g&$+5CijydVOs&|>*t|Yv-s<CZaLp7nub&GYXvhrw*sykHqvueV
zwdrNB))>{EI0<ezGfpU@wuX_z3$IDN$jKp7gafHwHOKu#$?H`eqXp{huXC53LJT7>
zr>Yvnpsk>&Xk~DkJp!ovqwMT#)-?wYkF|IxYSQImm|wU|&cKLwK)}NB#1R!0mCNnY
zL11Wnsi~<y#wR4ic`5UonVLdrnVCT%NC8r7_Y6)+L*s}an*uIh@!ea#;kK#*q2HDC
zztk=ZW4B76HbYUl@!F{jUEvwc%Ca(4#hW*u%As$6>T2K0irWx~RZE2o`ODw4?%opA
z@V{+}-2^sa34nP7X6h74ifvHlr3Kn8I$GDj;J9RO052o8h9{iuJlOPsva+)GC5}81
zKYw}o`VwnhCs=lPcz7U11qB)4wZ)hM1AYDG2J<r6)ZP<eY%yqxVy8zCbFtIWw{PD{
zLGgC4xAk~8?Xd?Wx((WHI>&#MbwZPOt;L4B4R*-Bemng~0-kb>RT{1umYkYO1m~ma
z>%_z!8FYBTl9G};czJ80_x|wWmb!j{Q7Guh`@_RGn{2q3L5>y}W7D#-Bo+0BPVn&j
zW@UaP9_9yxeKR5=q8r~20yk%9-fIyUh?aT|(ZbTwk~lfZSXy45gLnzauRO#eoWnJ(
zKi|s?Ae~*yfK0)#X~5ektV%$6b;chsAU#Eq`Vw4=l*~nig_e{NZ^`&$q(5a!Jw>O5
zVxz+3^;I+<+xh)hI-sa~r|AmkLdI}YQ`2md=Ua?x#S4UkgM*#zZJ&C~Bb{%x^)p97
zhwx?j6J_pIK3#u-&+;!(|1v89fZz2G_^f}T{=y%%iE5K|&rUj5IM{kT=@I2!VL3gQ
zLM$==S-#dIx(Ly;w)7VtKInMBJFlhlvny_Kr8Ck2{~G-{4t_j6PUxF3(=#EZa}V6V
zq$;K1juiwnzNf{dZAUPW_vbv7$Qm^2m(?4s;BJ|p4lk3?`{$p+#A4U&FEimO-4NbK
zx2%JLRB4lqF_<8W<oL%|N+76}a_R6eBo8B^;9-hgURhZw(NyM%v(zmxaxGgt{Pg9E
zErJZ%pc@2PEOk+zrlkZ<&<>tqg3h*=Njo(pcZrFK>CXpVM_;F=<bgA~fnAAZW@c_>
zXG7Gxa(B&xoGdDa`uh7j`MY<SwMCM80$W$R)dos#5Z*vhzCJ!qY6DH#`b9~ajg5`U
z^5@RoyM{d@iUf69K|x^wKv223xQIn=e7Kjnf5poD2EiBffP#jG-2*&c6YeL8<bjpN
zXrs}#4h}!x-^)yWes}hKcr@q2;^GmvfWX%j7Ic{V6Yw(Sssjv&lM}UBV4IYhqvldQ
zP0<v)L5D_Qw8Eb*HqM3@6r8cbvS7B#(a4I~y~CQM88y3*kV6*(1B07bGG@#A-o1O9
zO1{*7w6?lKnF#$9F~b`-l3<_l-Y>tID29pPb>w&s9Bpk+b1_m2Qjp;c#nh<Ol$5EQ
z?CjP_0)apq<j&n?ftSOiEH5u_0DtVx%gYnb%F22hq<&O&_tpR~_%fI;&>{vRxh*R#
zZOqcdM9IP3o#<;p#whVw!ZoqtvAjOi+=_aeiK~0^cmzoi>+Lep%CJw7X^b_)il0}w
zhW12$^Y!uyf|~%xYBPIY)A=Zc2_+@fmX(=lPICw<f6U`(DP)6zt{5l%oJ^Xo4ps^V
z7O<eRvvVjF1r%U&|GsQPV`D&T^4l}l2``|o!_|I`9X_^FpNZ|Gzqn`n-`scSisb<A
zoBrXR{lDC2*mHM>8ngy=Nm(giWQMF&WLw|(Tr!Wa(lPCqJ9SF?t&VIOsX*FRWDCh?
zknMZS_EAMFjTE!0X#KTEMbh+YUNkne%DvKbdWBbabogrqnD-tI7w)w)tZs%9$pf4F
zM^ep9{;o%K9&_wuYKqha0TRQ@-f|-OJdA^zihY&S#LSF=Vp{w3Mw&s(Mk?5<MA)nQ
z_wQ>p>Awmqg$C2o(hS01%_xn~M_&BsJyjkR74-^#0IJ_>=z3d*sL%x=R{BUIdwcum
z(fc8OemfuJ`VGZWcmzNfoA=N5_ww?xzkk2V+24Pch3#C}^>*o{&CN|KL+seplwSAD
zx`bih+$Pz%#<bBKC4wOsVHdxD&u=qF5yiM-{a=nwOqiKiSqWap(mjrb7KT8<?%3aN
zURhmLiPM_Nol%M#(NZ-cv;mQt1C6wzzJ8SAkduvShk1u?XRz{h3J!-G^u1hLQ?q3w
zlh({+8#aO0JH-_n7m$4%{-xHn%CuD7t;RI8hJ!UxvuaHDrr0qK+J-V-Ei4VzTNir;
zsC$&_1j;;~Oc4XnudIA#L|{AWCv}bm;_LA3@$vCF;f5;V09ZalN;h~SgE}mTCHeQ>
z-f?4NWB2sJU08i@s757?Hg&{X7aT?hRGFf%z$hDCSYa+y>|`LE<X;VnvBT}#-*W7#
zcD*II0>Alxl^0r8`ZyHOa<~r4XO$6-lTT1k<Gw}RQ041jxFG>0B#ImeFZlRTe_`u4
zORZ}Y9*doN(mgXGo}#0;RsyBtud$tH+F8kdtnKRKvn|GIYGSe@$;Yg**)0p^XaR_*
z2tKx_rzhc$&KJ3-eQ~N$FTPn~1uYy4b5RKNwYDbl3cZAkooY-ENhG1+kEGhQQv&;K
zDn}Gc1$1(iX5#n$f}P`k!!D+{>jPkK{s(qW0DGc=_J96?s<?VN_*_qMJ}x5dV@CSt
z;m9nULH|yXEROkt7{~G=G8W}DU-+Q@o``6&&sbc^3O$N*QdIkD?x)DuPg%V8MBDQp
z=L_rdWgSPzN^kpYKEx@jc}zFW?X~tc%;E38cb;Pj4`-MRWv%~I2hX993=!NqA136%
z@`k8n?e9NEd2!lf=Ty$`ZYft+*8^B|e_tQB@!4JO`dp|2NP{|ld1<LRvB&mv{7lf`
z&=3bg-JobF0^->ubimF9AG3nzAvdP}d{|sz67;lp771LT+OnL6OeWI|kBpdcf#%8y
z0^XimYZK&Yc`@YT;==f_ecC<zqM6x*JJ5g#uvF{*!}`w7&X4J7o1DeQyfEH`Zdq}0
z@k@|cA<%hs9Kp#R)ah9eh!fB6E|1n8?AIJ4W4CU(yRVAiFblhrljcmL9DsG|9r%tq
zPaF!kH&gT3%KrX--)BAFErh5hsoZ$i{Z!4$M~H|rd3pKBYXzSLv*!+i2{1M~zwA_r
z)ZPm(2k&LFLgS?n8SCv35~2=TAGK6hAoE1+GH8M-kn;DVdG$61I0xR~`Q3w`-ClJ3
z{QQo1s^4L2=LMzp?lbQ+l$0zk0)Hz5o8(E`osRN{j_K`@7f+`3&aS|2wMz?7T)2dF
zba1Fwmyy|U@b=zHTx={#(Ybp%QG0*Z-%`aIoH;>$KECFhfw=_w$Oq>Y6t;o62=`VB
zFoZ$1u)^p}3M(xn+u79KHaSJbHU>1-TP0C@QX)lXB%Fwqr)_3WObT0FiHXJ(6%}!4
zI@s7uUlrvHpl_z2XliP@vK!RO-2CFji~2K2EZg}8TcgsOfh9&bRYQVL7#M=w>FMbK
z*mc6TQ;o8z1Q)UO14(@P%H%<)`ymzDlhS{Iuw?lc?Z^Hl+7DtG0quGJ(9ZQI+WW6z
zY&g3@%YD7lGl=H)eJWo>Ik*ySzh+jSSc(dMA(@%>aF^zRwrk!ev4@({7UwhYcVF@z
zM?DlC*K&Im$$L)sdZD4Ci!jZ}<36TdA{_|=e$$!c%sG#b{>*%lKKNGJYQ(||RoG+Z
zT{tQ35~U~&#NWjI)59}fN?Jyyk|x<Pef!<+?`2gS1U16A5eNj0%KjrELlGp1G=99X
z#YmGx?_;y)TW<;qI$^hKU5Tu0?ekr*<lgyjCk%^{18PA1<%;c8-#Iv>0p2Od)IMrv
zW#yiel~u|oAaM9P`EwYv-jM$4>T26%^q0}mQU1D#19rdFt$;tBu1-=?k`Nnx1+5AO
zWU{ME%UM-mmGYZqIeF#RFE11HA<7sXgk)a;Pp@&^+Xe2~Iep@CzKfSvbzo3X^9}Sx
z<Tvq-+jtJrJ-|#Jh}e>DNjhs#%v_|oT@YmmSNySlx&Ed<m$z6{QnD26vAyKpS()x*
zhC0W<3{xs9D#pU=?%q;Hem;#<ynH%SS2!bBop@JYDN|F3Ra-czAIa{cq!;;uHyRc(
zJ3DK@pB9go)8maslxm)he|Y)KnKLb#qm{3vZrr%Bsw!Y0`-%3a<_VH-gLxuYieAV>
z6RM96={^ji!)Qv25#CpW4gsE?8)nGFokaa2DS0_Lm%CNB1U=M29R-!O3nNyerlGOA
zu&^**C9#;?J#!1CjqxYFa~t{cWpZL_suWQMCtv>l#4&v&T-5L+GAbIwrKNc`<%Z5(
zuygmr0>P3fDqAEnoI;^cZbe2o3z9MLuNEV*yyOr3w~W+t%E!jX8zqr7ut5hoz8G{P
s{0y=NY{mQD-UQ!=e(ZcP8HcHxG}a?9+`P23$RBpEUe-sKU9^7ie*^@)R{#J2
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..604e652e5ec48052efa7b342c41c33729dbd287c
GIT binary patch
literal 503
zc$@+E0SNwyP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0005LNkl<Zc-obb
zy-Px27{$$?$UZCyA`$~HWJC>7L`x76v;_%@mM9iB^r1sVf!&a+LRUjYsGy;SXlXDS
zqS4Xa%|ApGMeLk-iHe)y13!4)=bZC?+^cI*oxlhzV3LU5D72Q<MRPnJ-!GL)kJW1R
zov`PO8a=H}9?#`+$!4?p242AnVb2*gdj7h}U?dVbV4z;FKgVLRw8diCHJi-=_MB0p
zhhD7>ti@vS4yLDQG<t>g5*Q=w6NyBM8a-G`S{uxk%jE}{?yXkqCe|j<DHz6ViyA%j
zYHctN@fxOENs>0OHhj|zoKb`4hF+}=rVE9F3R6Z^Rhdr$-2y3-$y`vQ2kR-VKQN;x
z3QcOQR^uk8Hk)mQu*WP-&F}XI;T`=Q9`X5ne*E)Gm=u8uQbhDhGMNm+I|q7xguPy`
zr&6iJU^;I!8e!bzJfF{p@!n6^yWQ?Xco)GC==chIJf1CtWrR-<K6W~tN0_gHRj@8t
z1~xDN^g2;}fk0p#;WLCo2nQVwM@W2O6Lnw|^nxyNvM89c+3YsL`v~vZ?e;_QzJ6i+
tw+vuGmgQZS%eB*Pn57q2od8n1;SVuD34zFH^ql|z002ovPDHLkV1l!1+}HpB
--- a/browser/extensions/pdfjs/content/web/viewer.css
+++ b/browser/extensions/pdfjs/content/web/viewer.css
@@ -39,48 +39,62 @@ select {
   display: none;
 }
 [hidden] {
   display: none !important;
 }
 
 #viewerContainer:-webkit-full-screen {
   top: 0px;
-  border-top: 5px solid transparent;
+  border-top: 2px solid transparent;
   background-color: #404040;
   background-image: url(images/texture.png);
   width: 100%;
   height: 100%;
   overflow: hidden;
+  cursor: none;
 }
 
 #viewerContainer:-moz-full-screen {
   top: 0px;
-  border-top: 5px solid transparent;
+  border-top: 2px solid transparent;
   background-color: #404040;
   background-image: url(images/texture.png);
   width: 100%;
   height: 100%;
   overflow: hidden;
-}
-
-:-webkit-full-screen .page:last-child {
-  margin-bottom: 40px;
+  cursor: none;
 }
 
-:-moz-full-screen .page:last-child {
-  margin-bottom: 40px;
-}
-
-#viewerContainer:full-screen {
+#viewerContainer:fullscreen {
   top: 0px;
+  border-top: 2px solid transparent;
   background-color: #404040;
   background-image: url(images/texture.png);
   width: 100%;
   height: 100%;
+  overflow: hidden;
+  cursor: none;
+}
+
+
+:-webkit-full-screen .page {
+  margin-bottom: 100%;
+}
+
+:-moz-full-screen .page {
+  margin-bottom: 100%;
+}
+
+:fullscreen .page {
+  margin-bottom: 100%;
+}
+
+#viewerContainer.presentationControls {
+  cursor: default;
 }
 
 /* outer/inner center provides horizontal center */
 html[dir='ltr'] .outerCenter {
   float: right;
   position: relative;
   right: 50%;
 }
@@ -199,17 +213,16 @@ html[dir='ltr'] #sidebarContent {
 }
 html[dir='rtl'] #sidebarContent {
   right: 0;
 }
 
 #viewerContainer {
   overflow: auto;
   box-shadow: inset 1px 0 0 hsla(0,0%,100%,.05);
-  padding-top: 30px;
   position: absolute;
   top: 32px;
   right: 0;
   bottom: 0;
   left: 0;
 }
 
 .toolbar {
@@ -240,17 +253,17 @@ html[dir='rtl'] #sidebarContent {
                     linear-gradient(hsla(0,0%,30%,.99), hsla(0,0%,25%,.95));
   box-shadow: inset -2px 0 0 hsla(0,0%,100%,.08),
               inset 0 1px 1px hsla(0,0%,0%,.15),
               inset 0 -1px 0 hsla(0,0%,100%,.05),
               0 1px 0 hsla(0,0%,0%,.15),
               0 1px 1px hsla(0,0%,0%,.1);
 }
 
-#toolbarViewer {
+#toolbarViewer, .findbar {
   position: relative;
   height: 32px;
   background-image: url(images/texture.png),
                     -webkit-linear-gradient(hsla(0,0%,32%,.99), hsla(0,0%,27%,.95));
   background-image: url(images/texture.png),
                     -moz-linear-gradient(hsla(0,0%,32%,.99), hsla(0,0%,27%,.95));
   background-image: url(images/texture.png),
                     -ms-linear-gradient(hsla(0,0%,32%,.99), hsla(0,0%,27%,.95));
@@ -260,16 +273,104 @@ html[dir='rtl'] #sidebarContent {
                     linear-gradient(hsla(0,0%,32%,.99), hsla(0,0%,27%,.95));
   border-left: 1px solid hsla(0,0%,0%,.5);
   box-shadow: inset 1px 0 0 hsla(0,0%,100%,.08),
               inset 0 1px 1px hsla(0,0%,0%,.15),
               inset 0 -1px 0 hsla(0,0%,100%,.05),
               0 1px 0 hsla(0,0%,0%,.15),
               0 1px 1px hsla(0,0%,0%,.1);
 }
+
+.findbar {
+  top: 32px;
+  position: absolute;
+  z-index: 10000;
+  height: 32px;
+
+  min-width: 16px;
+  padding: 0px 6px 0px 6px;
+  margin: 4px 2px 4px 2px;
+  color: hsl(0,0%,85%);
+  font-size: 12px;
+  line-height: 14px;
+  text-align: left;
+  cursor: default;
+}
+
+html[dir='ltr'] .findbar {
+  left: 68px;
+}
+
+html[dir='rtl'] .findbar {
+  right: 68px;
+}
+
+.findbar label {
+  -webkit-user-select:none;
+  -moz-user-select:none;
+}
+
+#findInput[data-status="pending"] {
+  background-image: url(images/loading-small.png);
+  background-repeat: no-repeat;
+  background-position: right;
+}
+
+.doorHanger {
+  border: 1px solid hsla(0,0%,0%,.5);
+  border-radius: 2px;
+  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
+}
+.doorHanger:after, .doorHanger:before {
+  bottom: 100%;
+  border: solid transparent;
+  content: " ";
+  height: 0;
+  width: 0;
+  position: absolute;
+  pointer-events: none;
+}
+.doorHanger:after {
+  border-bottom-color: hsla(0,0%,32%,.99);
+  border-width: 8px;
+}
+.doorHanger:before {
+  border-bottom-color: hsla(0,0%,0%,.5);
+  border-width: 9px;
+}
+
+html[dir='ltr'] .doorHanger:after {
+  left: 16px;
+  margin-left: -8px;
+}
+
+html[dir='ltr'] .doorHanger:before {
+  left: 16px;
+  margin-left: -9px;
+}
+
+html[dir='rtl'] .doorHanger:after {
+  right: 16px;
+  margin-right: -8px;
+}
+
+html[dir='rtl'] .doorHanger:before {
+  right: 16px;
+  margin-right: -9px;
+}
+
+#findMsg {
+  font-style: italic;
+  color: #A6B7D0;
+}
+
+.notFound {
+  background-color: rgb(255, 137, 153);
+}
+
 html[dir='ltr'] #toolbarViewerLeft {
   margin-left: -1px;
 }
 html[dir='rtl'] #toolbarViewerRight {
   margin-left: -1px;
 }
 
 
@@ -282,22 +383,24 @@ html[dir='rtl'] #toolbarViewerRight {
 html[dir='ltr'] #toolbarViewerRight,
 html[dir='rtl'] #toolbarViewerLeft {
   position: absolute;
   top: 0;
   right: 0;
 }
 html[dir='ltr'] #toolbarViewerLeft > *,
 html[dir='ltr'] #toolbarViewerMiddle > *,
-html[dir='ltr'] #toolbarViewerRight > * {
+html[dir='ltr'] #toolbarViewerRight > *,
+html[dir='ltr'] .findbar > * {
   float: left;
 }
 html[dir='rtl'] #toolbarViewerLeft > *,
 html[dir='rtl'] #toolbarViewerMiddle > *,
-html[dir='rtl'] #toolbarViewerRight > * {
+html[dir='rtl'] #toolbarViewerRight > *,
+html[dir='rtl'] .findbar > * {
   float: right;
 }
 
 html[dir='ltr'] .splitToolbarButton {
   margin: 3px 2px 4px 0;
   display: inline-block;
 }
 html[dir='rtl'] .splitToolbarButton {
@@ -625,16 +728,36 @@ html[dir='rtl'] .toolbarButton:first-chi
   min-width: 30px;
 }
 
 .toolbarButton#sidebarToggle::before {
   display: inline-block;
   content: url(images/toolbarButton-sidebarToggle.png);
 }
 
+html[dir='ltr'] .toolbarButton.findPrevious::before {
+  display: inline-block;
+  content: url(images/findbarButton-previous.png);
+}
+
+html[dir='rtl'] .toolbarButton.findPrevious::before {
+  display: inline-block;
+  content: url(images/findbarButton-previous-rtl.png);
+}
+
+html[dir='ltr'] .toolbarButton.findNext::before {
+  display: inline-block;
+  content: url(images/findbarButton-next.png);
+}
+
+html[dir='rtl'] .toolbarButton.findNext::before {
+  display: inline-block;
+  content: url(images/findbarButton-next-rtl.png);
+}
+
 html[dir='ltr'] .toolbarButton.pageUp::before {
   display: inline-block;
   content: url(images/toolbarButton-pageUp.png);
 }
 
 html[dir='rtl'] .toolbarButton.pageUp::before {
   display: inline-block;
   content: url(images/toolbarButton-pageUp-rtl.png);
@@ -697,17 +820,17 @@ html[dir='rtl'] .toolbarButton.pageDown:
   content: url(images/toolbarButton-viewThumbnail.png);
 }
 
 #viewOutline.toolbarButton::before {
   display: inline-block;
   content: url(images/toolbarButton-viewOutline.png);
 }
 
-#viewSearch.toolbarButton::before {
+#viewFind.toolbarButton::before {
   display: inline-block;
   content: url(images/toolbarButton-search.png);
 }
 
 
 .toolbarField {
   padding: 3px 6px;
   margin: 4px 0 4px 0;
@@ -724,16 +847,21 @@ html[dir='rtl'] .toolbarButton.pageDown:
   font-size: 12px;
   line-height: 14px;
   outline-style: none;
   -moz-transition-property: background-color, border-color, box-shadow;
   -moz-transition-duration: 150ms;
   -moz-transition-timing-function: ease;
 }
 
+.toolbarField[type=checkbox] {
+  display: inline-block;
+  margin: 8px 0px;
+}
+
 .toolbarField.pageNumber {
   min-width: 16px;
   text-align: right;
   width: 40px;
 }
 
 .toolbarField.pageNumber::-webkit-inner-spin-button,
 .toolbarField.pageNumber::-webkit-outer-spin-button {
@@ -839,35 +967,33 @@ a:focus > .thumbnail > .thumbnailSelecti
   -webkit-user-select:none;
   -moz-user-select:none;
 }
 
 .outlineItem > .outlineItems {
   margin-left: 20px;
 }
 
-.outlineItem > a,
-#searchResults > a {
+.outlineItem > a {
   text-decoration: none;
   display: inline-block;
   min-width: 95%;
   height: 20px;
   padding: 2px 0 0 10px;
   margin-bottom: 1px;
   border-radius: 2px;
   color: hsla(0,0%,100%,.8);
   font-size: 13px;
   line-height: 15px;
   -moz-user-select:none;
   cursor: default;
   white-space: nowrap;
 }
 
-.outlineItem > a:hover,
-#searchResults > a:hover {
+.outlineItem > a:hover {
   background-color: hsla(0,0%,100%,.02);
   background-image: -moz-linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0));
   background-clip: padding-box;
   box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset,
               0 0 1px hsla(0,0%,100%,.2) inset,
               0 0 1px hsla(0,0%,0%,.2);
   color: hsla(0,0%,100%,.9);
 }
@@ -884,54 +1010,24 @@ a:focus > .thumbnail > .thumbnailSelecti
 
 .noOutline,
 .noResults {
   font-size: 12px;
   color: hsla(0,0%,100%,.8);
   font-style: italic;
 }
 
-#searchScrollView {
+#findScrollView {
   position: absolute;
   top: 10px;
   bottom: 10px;
   left: 10px;
   width: 280px;
 }
 
-#searchToolbar {
-  padding-left: 0px;
-  right: 0px;
-  padding-top: 0px;
-  padding-bottom: 5px;
-}
-
-#searchToolbar > input {
-  margin-left: 4px;
-  width: 124px;
-}
-
-#searchToolbar button {
-  width: auto;
-  margin: 0;
-  padding: 0 6px;
-  height: 22px;
-}
-
-#searchResults {
-  overflow: auto;
-  position: absolute;
-  top: 30px;
-  bottom: 0px;
-  left: 0px;
-  right: 0;
-  padding: 4px 4px 0;
-  font-size: smaller;
-}
-
 #sidebarControls {
   position:absolute;
   width: 180px;
   height: 32px;
   left: 15px;
   bottom: 35px;
 }
 
@@ -1064,16 +1160,40 @@ canvas {
 
 .textLayer > div {
   color: transparent;
   position: absolute;
   line-height:1.3;
   white-space:pre;
 }
 
+.textLayer .highlight {
+  margin: -1px;
+  padding: 1px;
+
+  background-color: rgba(180, 0, 170, 0.2);
+  border-radius: 4px;
+}
+
+.textLayer .highlight.begin {
+  border-radius: 4px 0px 0px 4px;
+}
+
+.textLayer .highlight.end {
+  border-radius: 0px 4px 4px 0px;
+}
+
+.textLayer .highlight.middle {
+  border-radius: 0px;
+}
+
+.textLayer .highlight.selected {
+  background-color: rgba(0, 100, 0, 0.2);
+}
+
 /* TODO: file FF bug to support ::-moz-selection:window-inactive
    so we can override the opaque grey background when the window is inactive;
    see https://bugzilla.mozilla.org/show_bug.cgi?id=706209 */
 ::selection { background:rgba(0,0,255,0.3); }
 ::-moz-selection { background:rgba(0,0,255,0.3); }
 
 .annotComment > div {
   position: absolute;
@@ -1286,17 +1406,17 @@ canvas {
   }
   html[dir='rtl'] .outerCenter {
     float: right;
     right: 180px;
   }
 }
 
 @media all and (max-width: 600px) {
-  #toolbarViewerRight {
+  #toolbarViewerRight, #findbar, #viewFind {
     display: none;
   }
 }
 
 @media all and (max-width: 500px) {
   #scaleSelectContainer, #pageNumberLabel {
     display: none;
   }
--- a/browser/extensions/pdfjs/content/web/viewer.html
+++ b/browser/extensions/pdfjs/content/web/viewer.html
@@ -46,17 +46,17 @@ limitations under the License.
 
 var PDFJS = {};
 
 (function pdfjsWrapper() {
   // Use strict in our context only - users might not want it
   'use strict';
 
   PDFJS.build =
-'e98eba1';
+'c8cf445';
 
 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
 /* Copyright 2012 Mozilla Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
@@ -352,18 +352,27 @@ var Page = (function PageClosure() {
                   // entry in the dictionary.
                   if (!isValidUrl(url))
                     url = '';
                   item.url = url;
                   break;
                 case 'GoTo':
                   item.dest = a.get('D');
                   break;
+                case 'GoToR':
+                  var url = a.get('F');
+                  // TODO: pdf reference says that GoToR
+                  // can also have 'NewWindow' attribute
+                  if (!isValidUrl(url))
+                    url = '';
+                  item.url = url;
+                  item.dest = a.get('D');
+                  break;
                 default:
-                  TODO('other link types');
+                  TODO('unrecognized link type: ' + a.get('S').name);
               }
             } else if (annotation.has('Dest')) {
               // simple destination link
               var dest = annotation.get('Dest');
               item.dest = isName(dest) ? dest.name : dest;
             }
             break;
           case 'Widget':
@@ -633,18 +642,16 @@ var PDFDocument = (function PDFDocumentC
 
 
 
 // Use only for debugging purposes. This should not be used in any code that is
 // in mozilla master.
 var log = (function() {
   if ('console' in globalScope && 'log' in globalScope['console']) {
     return globalScope['console']['log'].bind(globalScope['console']);
-  } else if ('print' in globalScope) {
-    return globalScope['print'].bind(globalScope);
   } else {
     return function nop() {
     };
   }
 })();
 
 // A notice for devs that will not trigger the fallback UI.  These are good
 // for things that are helpful to devs, such as warning that Workers were
@@ -897,17 +904,17 @@ var Util = PDFJS.Util = (function UtilCl
 
 var PageViewport = PDFJS.PageViewport = (function PageViewportClosure() {
   function PageViewport(viewBox, scale, rotate, offsetX, offsetY) {
     // creating transform to convert pdf coordinate system to the normal
     // canvas like coordinates taking in account scale and rotation
     var centerX = (viewBox[2] + viewBox[0]) / 2;
     var centerY = (viewBox[3] + viewBox[1]) / 2;
     var rotateA, rotateB, rotateC, rotateD;
-    switch (rotate) {
+    switch (rotate % 360) {
       case -180:
       case 180:
         rotateA = -1; rotateB = 0; rotateC = 0; rotateD = 1;
         break;
       case -270:
       case 90:
         rotateA = 0; rotateB = 1; rotateC = 1; rotateD = 0;
         break;
@@ -1950,17 +1957,18 @@ var WorkerTransport = (function WorkerTr
 var TextRenderingMode = {
   FILL: 0,
   STROKE: 1,
   FILL_STROKE: 2,
   INVISIBLE: 3,
   FILL_ADD_TO_PATH: 4,
   STROKE_ADD_TO_PATH: 5,
   FILL_STROKE_ADD_TO_PATH: 6,
-  ADD_TO_PATH: 7
+  ADD_TO_PATH: 7,
+  ADD_TO_PATH_FLAG: 4
 };
 
 // Minimal font size that would be used during canvas fillText operations.
 var MIN_FONT_SIZE = 1;
 
 function createScratchCanvas(width, height) {
   var canvas = document.createElement('canvas');
   canvas.width = width;
@@ -2336,16 +2344,20 @@ var CanvasGraphics = (function CanvasGra
     },
     save: function CanvasGraphics_save() {
       this.ctx.save();
       var old = this.current;
       this.stateStack.push(old);
       this.current = old.clone();
     },
     restore: function CanvasGraphics_restore() {
+      if ('textClipLayers' in this) {
+        this.completeTextClipping();
+      }
+
       var prev = this.stateStack.pop();
       if (prev) {
         this.current = prev;
         this.ctx.restore();
       }
     },
     transform: function CanvasGraphics_transform(a, b, c, d, e, f) {
       this.ctx.transform(a, b, c, d, e, f);
@@ -2465,16 +2477,74 @@ var CanvasGraphics = (function CanvasGra
 
     // Text
     beginText: function CanvasGraphics_beginText() {
       this.current.textMatrix = IDENTITY_MATRIX;
       this.current.x = this.current.lineX = 0;
       this.current.y = this.current.lineY = 0;
     },
     endText: function CanvasGraphics_endText() {
+      if ('textClipLayers' in this) {
+        this.swapImageForTextClipping();
+      }
+    },
+    getCurrentTextClipping: function CanvasGraphics_getCurrentTextClipping() {
+      var ctx = this.ctx;
+      var transform = ctx.mozCurrentTransform;
+      if ('textClipLayers' in this) {
+        // we need to reset only font and transform
+        var maskCtx = this.textClipLayers.maskCtx;
+        maskCtx.setTransform.apply(maskCtx, transform);
+        maskCtx.font = ctx.font;
+        return maskCtx;
+      }
+
+      var canvasWidth = ctx.canvas.width;
+      var canvasHeight = ctx.canvas.height;
+      // keeping track of the text clipping of the separate canvas
+      var maskCanvas = createScratchCanvas(canvasWidth, canvasHeight);
+      var maskCtx = maskCanvas.getContext('2d');
+      maskCtx.setTransform.apply(maskCtx, transform);
+      maskCtx.font = ctx.font;
+      var textClipLayers = {
+        maskCanvas: maskCanvas,
+        maskCtx: maskCtx
+      };
+      this.textClipLayers = textClipLayers;
+      return maskCtx;
+    },
+    swapImageForTextClipping:
+      function CanvasGraphics_swapImageForTextClipping() {
+      var ctx = this.ctx;
+      var canvasWidth = ctx.canvas.width;
+      var canvasHeight = ctx.canvas.height;
+      // saving current image content and clearing whole canvas
+      ctx.save();
+      ctx.setTransform(1, 0, 0, 1, 0, 0);
+      var data = ctx.getImageData(0, 0, canvasWidth, canvasHeight);
+      this.textClipLayers.imageData = data;
+      ctx.clearRect(0, 0, canvasWidth, canvasHeight);
+      ctx.restore();
+    },
+    completeTextClipping: function CanvasGraphics_completeTextClipping() {
+      var ctx = this.ctx;
+      // applying mask to the image (result is saved in maskCanvas)
+      var maskCtx = this.textClipLayers.maskCtx;
+      maskCtx.setTransform(1, 0, 0, 1, 0, 0);
+      maskCtx.globalCompositeOperation = 'source-in';
+      maskCtx.drawImage(ctx.canvas, 0, 0);
+
+      // restoring image data and applying the result of masked drawing
+      ctx.save();
+      ctx.setTransform(1, 0, 0, 1, 0, 0);
+      ctx.putImageData(this.textClipLayers.imageData, 0, 0);
+      ctx.drawImage(this.textClipLayers.maskCanvas, 0, 0);
+      ctx.restore();
+
+      delete this.textClipLayers;
     },
     setCharSpacing: function CanvasGraphics_setCharSpacing(spacing) {
       this.current.charSpacing = spacing;
     },
     setWordSpacing: function CanvasGraphics_setWordSpacing(spacing) {
       this.current.wordSpacing = spacing;
     },
     setHScale: function CanvasGraphics_setHScale(scale) {
@@ -2532,18 +2602,16 @@ var CanvasGraphics = (function CanvasGra
       var browserFontSize = size >= MIN_FONT_SIZE ? size : MIN_FONT_SIZE;
       this.current.fontSizeScale = browserFontSize != MIN_FONT_SIZE ? 1.0 :
                                    size / MIN_FONT_SIZE;
 
       var rule = italic + ' ' + bold + ' ' + browserFontSize + 'px ' + typeface;
       this.ctx.font = rule;
     },
     setTextRenderingMode: function CanvasGraphics_setTextRenderingMode(mode) {
-      if (mode >= TextRenderingMode.FILL_ADD_TO_PATH)
-        TODO('unsupported text rendering mode: ' + mode);
       this.current.textRenderingMode = mode;
     },
     setTextRise: function CanvasGraphics_setTextRise(rise) {
       this.current.textRise = rise;
     },
     moveText: function CanvasGraphics_moveText(x, y) {
       this.current.x = this.current.lineX += x;
       this.current.y = this.current.lineY += y;
@@ -2568,30 +2636,33 @@ var CanvasGraphics = (function CanvasGra
       var fontMatrix = current.fontMatrix || IDENTITY_MATRIX;
 
       ctx.transform.apply(ctx, current.textMatrix);
       ctx.scale(1, -1);
       ctx.translate(current.x, -current.y - current.textRise);
       ctx.transform.apply(ctx, fontMatrix);
       ctx.scale(textHScale, 1);
     },
-    getTextGeometry: function CanvasGraphics_getTextGeometry() {
+    createTextGeometry: function CanvasGraphics_createTextGeometry() {
       var geometry = {};
       var ctx = this.ctx;
       var font = this.current.font;
       var ctxMatrix = ctx.mozCurrentTransform;
       if (ctxMatrix) {
         var bl = Util.applyTransform([0, 0], ctxMatrix);
         var tr = Util.applyTransform([1, 1], ctxMatrix);
         geometry.x = bl[0];
         geometry.y = bl[1];
         geometry.hScale = tr[0] - bl[0];
         geometry.vScale = tr[1] - bl[1];
       }
       geometry.spaceWidth = font.spaceWidth;
+      geometry.fontName = font.loadedName;
+      geometry.fontFamily = font.fallbackName;
+      geometry.fontSize = this.current.fontSize;
       return geometry;
     },
 
     showText: function CanvasGraphics_showText(str, skipTextSelection) {
       var ctx = this.ctx;
       var current = this.current;
       var font = current.font;
       var glyphs = font.charsToGlyphs(str);
@@ -2615,17 +2686,17 @@ var CanvasGraphics = (function CanvasGra
         ctx.transform.apply(ctx, current.textMatrix);
         ctx.translate(current.x, current.y);
 
         ctx.scale(textHScale, 1);
 
         if (textSelection) {
           this.save();
           ctx.scale(1, -1);
-          geom = this.getTextGeometry();
+          geom = this.createTextGeometry();
           this.restore();
         }
         for (var i = 0; i < glyphsLength; ++i) {
 
           var glyph = glyphs[i];
           if (glyph === null) {
             // word break
             this.ctx.translate(wordSpacing, 0);
@@ -2656,17 +2727,17 @@ var CanvasGraphics = (function CanvasGra
         var lineWidth = current.lineWidth;
         var scale = Math.abs(current.textMatrix[0] * fontMatrix[0]);
         if (scale == 0 || lineWidth == 0)
           lineWidth = this.getSinglePixelWidth();
         else
           lineWidth /= scale;
 
         if (textSelection)
-          geom = this.getTextGeometry();
+          geom = this.createTextGeometry();
 
         if (fontSizeScale != 1.0) {
           ctx.scale(fontSizeScale, fontSizeScale);
           lineWidth /= fontSizeScale;
         }
 
         ctx.lineWidth = lineWidth;
 
@@ -2696,35 +2767,40 @@ var CanvasGraphics = (function CanvasGra
                 ctx.strokeText(character, scaledX, 0);
                 break;
               case TextRenderingMode.FILL_STROKE:
               case TextRenderingMode.FILL_STROKE_ADD_TO_PATH:
                 ctx.fillText(character, scaledX, 0);
                 ctx.strokeText(character, scaledX, 0);
                 break;
               case TextRenderingMode.INVISIBLE:
+              case TextRenderingMode.ADD_TO_PATH:
                 break;
             }
+            if (textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG) {
+              var clipCtx = this.getCurrentTextClipping();
+              clipCtx.fillText(character, scaledX, 0);
+            }
           }
 
           x += charWidth;
 
           var glyphUnicode = glyph.unicode === ' ' ? '\u00A0' : glyph.unicode;
           if (glyphUnicode in NormalizedUnicodes)
             glyphUnicode = NormalizedUnicodes[glyphUnicode];
 
           canvasWidth += charWidth;
         }
         current.x += x * textHScale2;
         ctx.restore();
       }
 
       if (textSelection) {
         geom.canvasWidth = canvasWidth;
-        this.textLayer.appendText(font.fallbackName, fontSize, geom);
+        this.textLayer.appendText(geom);
       }
 
       return canvasWidth;
     },
     showSpacedText: function CanvasGraphics_showSpacedText(arr) {
       var ctx = this.ctx;
       var current = this.current;
       var font = current.font;
@@ -2743,17 +2819,17 @@ var CanvasGraphics = (function CanvasGra
         // Type3 fonts - each glyph is a "mini-PDF" (see also showText)
         if (font.coded) {
           ctx.transform.apply(ctx, current.textMatrix);
           ctx.scale(1, -1);
           ctx.translate(current.x, -1 * current.y);
           ctx.scale(textHScale, 1);
         } else
           this.applyTextTransforms();
-        geom = this.getTextGeometry();
+        geom = this.createTextGeometry();
         ctx.restore();
       }
 
       for (var i = 0; i < arrLength; ++i) {
         var e = arr[i];
         if (isNum(e)) {
           var spacingLength = -e * 0.001 * fontSize * textHScale;
           current.x += spacingLength;
@@ -2767,17 +2843,17 @@ var CanvasGraphics = (function CanvasGra
             canvasWidth += shownCanvasWidth;
         } else {
           error('TJ array element ' + e + ' is not string or num');
         }
       }
 
       if (textSelection) {
         geom.canvasWidth = canvasWidth;
-        this.textLayer.appendText(font.fallbackName, fontSize, geom);
+        this.textLayer.appendText(geom);
       }
     },
     nextLineShowText: function CanvasGraphics_nextLineShowText(text) {
       this.nextLine();
       this.showText(text);
     },
     nextLineSetSpacingShowText:
       function CanvasGraphics_nextLineSetSpacingShowText(wordSpacing,
@@ -3108,18 +3184,32 @@ var CanvasGraphics = (function CanvasGra
       var tmpCanvas = createScratchCanvas(w, h);
       var tmpCtx = tmpCanvas.getContext('2d');
       this.putBinaryImageData(tmpCtx, imgData, w, h);
 
       ctx.drawImage(tmpCanvas, 0, -h);
       this.restore();
     },
 
-    putBinaryImageData: function CanvasGraphics_putBinaryImageData() {
-      //
+    putBinaryImageData: function CanvasGraphics_putBinaryImageData(ctx, imgData,
+                                                                   w, h) {
+      var tmpImgData = 'createImageData' in ctx ? ctx.createImageData(w, h) :
+        ctx.getImageData(0, 0, w, h);
+
+      var tmpImgDataPixels = tmpImgData.data;
+      var data = imgData.data;
+      if ('set' in tmpImgDataPixels)
+        tmpImgDataPixels.set(data);
+      else {
+        // Copy over the imageData pixel by pixel.
+        for (var i = 0, ii = tmpImgDataPixels.length; i < ii; i++)
+          tmpImgDataPixels[i] = data[i];
+      }
+
+      ctx.putImageData(tmpImgData, 0, 0);
     },
 
     // Marked content
 
     markPoint: function CanvasGraphics_markPoint(tag) {
       // TODO Marked content.
     },
     markPointProps: function CanvasGraphics_markPointProps(tag, properties) {
@@ -3176,57 +3266,16 @@ var CanvasGraphics = (function CanvasGra
       var inverse = this.ctx.mozCurrentTransformInverse;
       return Math.abs(inverse[0] + inverse[2]);
     }
   };
 
   return CanvasGraphics;
 })();
 
-function checkPutBinaryImageDataCompatibility() {
-  // Feature detection if the browser can use an Uint8Array directly as imgData.
-  var canvas = document.createElement('canvas');
-  canvas.width = 1;
-  canvas.height = 1;
-  var ctx = canvas.getContext('2d');
-
-  try {
-    ctx.putImageData({
-      width: 1,
-      height: 1,
-      data: new Uint8Array(4)
-    }, 0, 0);
-
-    CanvasGraphics.prototype.putBinaryImageData =
-      function CanvasGraphicsPutBinaryImageDataNative(ctx, imgData) {
-        ctx.putImageData(imgData, 0, 0);
-      };
-  } catch (e) {
-    CanvasGraphics.prototype.putBinaryImageData =
-      function CanvasGraphicsPutBinaryImageDataShim(ctx, imgData, w, h) {
-        var tmpImgData = 'createImageData' in ctx ? ctx.createImageData(w, h) :
-          ctx.getImageData(0, 0, w, h);
-
-        var tmpImgDataPixels = tmpImgData.data;
-        var data = imgData.data;
-        if ('set' in tmpImgDataPixels)
-          tmpImgDataPixels.set(data);
-        else {
-          // Copy over the imageData pixel by pixel.
-          for (var i = 0, ii = tmpImgDataPixels.length; i < ii; i++)
-            tmpImgDataPixels[i] = data[i];
-        }
-
-        ctx.putImageData(tmpImgData, 0, 0);
-      };
-  }
-}
-if (!isWorker) {
-  checkPutBinaryImageDataCompatibility();
-}
 
 
 var Name = (function NameClosure() {
   function Name(name) {
     this.name = name;
   }
 
   Name.prototype = {};
@@ -13265,33 +13314,39 @@ var PartialEvaluator = (function Partial
 
   PartialEvaluator.prototype = {
     loadFont: function PartialEvaluator_loadFont(fontName, font, xref,
                                                  resources, dependency) {
       var fontRes = resources.get('Font');
 
       assert(fontRes, 'fontRes not available');
 
+      ++this.fontIdCounter;
+
       font = xref.fetchIfRef(font) || fontRes.get(fontName);
-      assertWellFormed(isDict(font));
-
-      ++this.fontIdCounter;
+      if (!isDict(font)) {
+        return {
+          translated: new ErrorFont('Font ' + fontName + ' is not available'),
+          loadedName: 'font_' + this.uniquePrefix + this.fontIdCounter
+        };
+      }
+
       var loadedName = font.loadedName;
       if (!loadedName) {
         // keep track of each font we translated so the caller can
         // load them asynchronously before calling display on a page
         loadedName = 'font_' + this.uniquePrefix + this.fontIdCounter;
         font.loadedName = loadedName;
 
         var translated;
         try {
           translated = this.translateFont(font, xref, resources,
                                           dependency);
         } catch (e) {
-          translated = { error: e };
+          translated = new ErrorFont(e instanceof Error ? e.message : e);
         }
         font.translated = translated;
 
         var data = translated;
         if (data.loadCharProcs) {
           delete data.loadCharProcs;
 
           var charProcs = font.get('CharProcs').getAll();
@@ -13329,20 +13384,17 @@ var PartialEvaluator = (function Partial
         }
       }
 
       function handleSetFont(fontName, font) {
         font = self.loadFont(fontName, font, xref, resources, dependency);
 
         var loadedName = font.loadedName;
         if (!font.sent) {
-          var data = font.translated;
-
-          if (data instanceof Font)
-            data = data.exportData();
+          var data = font.translated.exportData();
 
           handler.send('obj', [
               loadedName,
               'Font',
               data
           ]);
           font.sent = true;
         }
@@ -13630,16 +13682,18 @@ var PartialEvaluator = (function Partial
       }
 
       return queue;
     },
 
     getTextContent: function partialEvaluatorGetIRQueue(
                                                     stream, resources, state) {
       var bidiTexts;
+      var kSpaceFactor = 0.35;
+      var kMultipleSpaceFactor = 1.5;
 
       if (!state) {
         bidiTexts = [];
         state = {
           bidiTexts: bidiTexts
         };
       } else {
         bidiTexts = state.bidiTexts;
@@ -13671,18 +13725,23 @@ var PartialEvaluator = (function Partial
               font = handleSetFont(args[0].name).translated;
               break;
             case 'TJ':
               var items = args[0];
               for (var j = 0, jj = items.length; j < jj; j++) {
                 if (typeof items[j] === 'string') {
                   chunk += fontCharsToUnicode(items[j], font);
                 } else if (items[j] < 0 && font.spaceWidth > 0) {
-                  var numFakeSpaces = Math.round(-items[j] / font.spaceWidth);
-                  if (numFakeSpaces > 0) {
+                  var fakeSpaces = -items[j] / font.spaceWidth;
+                  if (fakeSpaces > kMultipleSpaceFactor) {
+                    fakeSpaces = Math.round(fakeSpaces);
+                    while (fakeSpaces--) {
+                      chunk += ' ';
+                    }
+                  } else if (fakeSpaces > kSpaceFactor) {
                     chunk += ' ';
                   }
                 }
               }
               break;
             case 'Tj':
               chunk += fontCharsToUnicode(args[0], font);
               break;
@@ -16504,65 +16563,68 @@ var Font = (function FontClosure() {
 
     var numTables = 1;
     var cmap = '\x00\x00' + // version
                string16(numTables) +  // numTables
                '\x00\x03' + // platformID
                '\x00\x01' + // encodingID
                string32(4 + numTables * 8); // start of the table record
 
-    var segCount = ranges.length + 1;
+    var trailingRangesCount = ranges[ranges.length - 1][1] < 0xFFFF ? 1 : 0;
+    var segCount = ranges.length + trailingRangesCount;
     var segCount2 = segCount * 2;
     var searchRange = getMaxPower2(segCount) * 2;
     var searchEntry = Math.log(segCount) / Math.log(2);
     var rangeShift = 2 * segCount - searchRange;
 
     // Fill up the 4 parallel arrays describing the segments.
     var startCount = '';
     var endCount = '';
     var idDeltas = '';
     var idRangeOffsets = '';
     var glyphsIds = '';
     var bias = 0;
 
     if (deltas) {
-      for (var i = 0; i < segCount - 1; i++) {
+      for (var i = 0, ii = ranges.length; i < ii; i++) {
         var range = ranges[i];
         var start = range[0];
         var end = range[1];
         var offset = (segCount - i) * 2 + bias * 2;
         bias += (end - start + 1);
 
         startCount += string16(start);
         endCount += string16(end);
         idDeltas += string16(0);
         idRangeOffsets += string16(offset);
 
         var codes = range[2];
         for (var j = 0, jj = codes.length; j < jj; ++j)
           glyphsIds += string16(deltas[codes[j]]);
       }
     } else {
-      for (var i = 0; i < segCount - 1; i++) {
+      for (var i = 0, ii = ranges.length; i < ii; i++) {
         var range = ranges[i];
         var start = range[0];
         var end = range[1];
         var startCode = range[2][0];
 
         startCount += string16(start);
         endCount += string16(end);
         idDeltas += string16((startCode - start + 1) & 0xFFFF);
         idRangeOffsets += string16(0);
       }
     }
 
-    endCount += '\xFF\xFF';
-    startCount += '\xFF\xFF';
-    idDeltas += '\x00\x01';
-    idRangeOffsets += '\x00\x00';
+    if (trailingRangesCount > 0) {
+      endCount += '\xFF\xFF';
+      startCount += '\xFF\xFF';
+      idDeltas += '\x00\x01';
+      idRangeOffsets += '\x00\x00';
+    }
 
     var format314 = '\x00\x00' + // language
                     string16(segCount2) +
                     string16(searchRange) +
                     string16(searchEntry) +
                     string16(rangeShift) +
                     endCount + '\x00\x00' + startCount +
                     idDeltas + idRangeOffsets + glyphsIds;
@@ -17233,16 +17295,19 @@ var Font = (function FontClosure() {
 
         return true;
       }
 
       // Check that required tables are present
       var requiredTables = ['OS/2', 'cmap', 'head', 'hhea',
                              'hmtx', 'maxp', 'name', 'post'];
 
+      var optionalTables = ['cvt ', 'fpgm', 'glyf', 'loca', 'prep',
+                            'CFF ', 'VORG', 'vhea', 'vmtx'];
+
       var header = readOpenTypeHeader(font);
       var numTables = header.numTables;
 
       var cmap, post, maxp, hhea, hmtx, vhea, vmtx, head, loca, glyf, os2;
       var tables = [];
       for (var i = 0; i < numTables; i++) {
         var table = readTableEntry(font);
         var index = requiredTables.indexOf(table.tag);
@@ -17258,16 +17323,19 @@ var Font = (function FontClosure() {
           else if (table.tag == 'hmtx')
             hmtx = table;
           else if (table.tag == 'head')
             head = table;
           else if (table.tag == 'OS/2')
             os2 = table;
 
           requiredTables.splice(index, 1);
+        } else if (optionalTables.indexOf(table.tag) < 0) {
+          // skipping table if it's not a required or optional table
+          continue;
         } else {
           if (table.tag == 'vmtx')
             vmtx = table;
           else if (table.tag == 'vhea')
             vhea = table;
           else if (table.tag == 'loca')
             loca = table;
           else if (table.tag == 'glyf')
@@ -17563,16 +17631,22 @@ var Font = (function FontClosure() {
           }
           this.useToFontChar = true;
         }
 
         createGlyphNameMap(glyphs, ids, properties);
         this.glyphNameMap = properties.glyphNameMap;
       }
 
+      if (glyphs.length === 0) {
+        // defines at least one glyph
+        glyphs.push({ unicode: 0xF000, code: 0xF000, glyph: '.notdef' });
+        ids.push(0);
+      }
+
       // Converting glyphs and ids into font's cmap table
       cmap.data = createCMapTable(glyphs, ids);
       var unicodeIsEnabled = [];
       for (var i = 0, ii = glyphs.length; i < ii; i++) {
         unicodeIsEnabled[glyphs[i].unicode] = true;
       }
       this.unicodeIsEnabled = unicodeIsEnabled;
 
@@ -18075,16 +18149,19 @@ var Font = (function FontClosure() {
 var ErrorFont = (function ErrorFontClosure() {
   function ErrorFont(error) {
     this.error = error;
   }
 
   ErrorFont.prototype = {
     charsToGlyphs: function ErrorFont_charsToGlyphs() {
       return [];
+    },
+    exportData: function ErrorFont_exportData() {
+      return {error: this.error};
     }
   };
 
   return ErrorFont;
 })();
 
 var CallothersubrCmd = (function CallothersubrCmdClosure() {
   function CallothersubrCmd(index) {
@@ -18298,16 +18375,31 @@ var Type1Parser = function type1Parser()
               charstring.push(3);
               i++;
               continue;
             }
 
             assert(argc == 0, 'callothersubr with arguments is not supported');
             charstring.push(new CallothersubrCmd(index));
             continue;
+          } else if (escape == 7) { // sbw
+            var args = breakUpArgs(charstring, 4);
+            var arg0 = args[0];
+            var arg1 = args[1];
+            var arg2 = args[2];
+            lsb = arg0.value;
+            width = arg2.value;
+            // To convert to type2 we have to move the width value to the first
+            // part of the charstring and then use rmoveto with (dx, dy).
+            // The height argument will not be used for vmtx and vhea tables
+            // reconstruction -- ignoring it.
+            charstring = arg2.arg;
+            charstring = charstring.concat(arg0.arg, arg1.arg);
+            charstring.push('rmoveto');
+            continue;
           } else if (escape == 17 || escape == 33) {
             // pop or setcurrentpoint commands can be ignored
             // since we are not doing callothersubr
             continue;
           } else if (escape == 6) {
             // seac is like type 2's special endchar but it doesn't use the
             // first argument asb, so remove it.
             var args = breakUpArgs(charstring, 5);
@@ -19105,20 +19197,19 @@ var CFFFont = (function CFFFontClosure()
       var unassignedUnicodeItems = [];
       var inverseEncoding = [];
       // CID fonts don't have an encoding.
       if (encoding !== null)
         for (var charcode in encoding)
           inverseEncoding[encoding[charcode]] = charcode | 0;
       else
         inverseEncoding = charsets;
-      for (var i = 0, ii = charsets.length; i < ii; i++) {
+      var i = charsets[0] == '.notdef' ? 1 : 0;
+      for (var ii = charsets.length; i < ii; i++) {
         var glyph = charsets[i];
-        if (glyph == '.notdef')
-          continue;
 
         var code = inverseEncoding[i];
         if (!code || isSpecialUnicode(code)) {
           unassignedUnicodeItems.push(i);
           continue;
         }
         charstrings.push({
           unicode: code,
@@ -31240,16 +31331,17 @@ function MessageHandler(name, comObj) {
   // If there's no console available, console_error in the
   // action handler will do nothing.
   if ('console' in globalScope) {
     ah['console_error'] = [function ahConsoleError(data) {
       globalScope['console'].error.apply(null, data);
     }];
   } else {
     ah['console_error'] = [function ahConsoleError(data) {
+      log.apply(null, data);
     }];
   }
   ah['_warn'] = [function ah_Warn(data) {
     warn(data);
   }];
 
   comObj.onmessage = function messageHandlerComObjOnMessage(event) {
     var data = event.data;
@@ -35955,45 +36047,55 @@ var JpegImage = (function jpegImage() {
       <div id="sidebarContainer">
         <div id="toolbarSidebar" class="splitToolbarButton toggled">
           <button id="viewThumbnail" class="toolbarButton group toggled" title="Show Thumbnails" tabindex="1" data-l10n-id="thumbs">
              <span data-l10n-id="thumbs_label">Thumbnails</span>
           </button>
           <button id="viewOutline" class="toolbarButton group" title="Show Document Outline" tabindex="2" data-l10n-id="outline">
              <span data-l10n-id="outline_label">Document Outline</span>
           </button>
-          <button id="viewSearch" class="toolbarButton group hidden" title="Search Document" tabindex="3" data-l10n-id="search_panel">
-             <span data-l10n-id="search_panel_label">Search Document</span>
-          </button>
         </div>
         <div id="sidebarContent">
           <div id="thumbnailView">
           </div>
           <div id="outlineView" class="hidden">
           </div>
-          <div id="searchView" class="hidden">
-            <div id="searchToolbar">
-              <input id="searchTermsInput" class="toolbarField">
-              <button id="searchButton" class="textButton toolbarButton" data-l10n-id="search">Find</button>
-            </div>
-            <div id="searchResults"></div>
-          </div>
         </div>
       </div>  <!-- sidebarContainer -->
 
       <div id="mainContainer">
+        <div class="findbar hidden doorHanger" id="findbar">
+          <label for="findInput" class="toolbarLabel" data-l10n-id="find_label">Find:</label>
+          <input id="findInput" class="toolbarField" tabindex="20">
+          <div class="splitToolbarButton">
+            <button class="toolbarButton findPrevious" title="" id="findPrevious" tabindex="21" data-l10n-id="find_previous">
+              <span data-l10n-id="find_previous_label">Previous</span>
+            </button>
+            <div class="splitToolbarButtonSeparator"></div>
+            <button class="toolbarButton findNext" title="" id="findNext" tabindex="22" data-l10n-id="find_next">
+              <span data-l10n-id="find_next_label">Next</span>
+            </button>
+          </div>
+          <input type="checkbox" id="findHighlightAll" class="toolbarField">
+          <label for="findHighlightAll" class="toolbarLabel" tabindex="23" data-l10n-id="find_highlight">Highlight all</label>
+          <input type="checkbox" id="findMatchCase" class="toolbarField">
+          <label for="findMatchCase" class="toolbarLabel" tabindex="24" data-l10n-id="find_match_case_label">Match case</label>
+          <span id="findMsg" class="toolbarLabel"></span>
+        </div>
         <div class="toolbar">
           <div id="toolbarContainer">
-
             <div id="toolbarViewer">
               <div id="toolbarViewerLeft">
-                <button id="sidebarToggle" class="toolbarButton" title="Toggle Sidebar" tabindex="4" data-l10n-id="toggle_slider">
+                <button id="sidebarToggle" class="toolbarButton" title="Toggle Sidebar" tabindex="3" data-l10n-id="toggle_slider">
                   <span data-l10n-id="toggle_slider_label">Toggle Sidebar</span>
                 </button>
                 <div class="toolbarButtonSpacer"></div>
+                <button id="viewFind" class="toolbarButton group" title="Find in Document" tabindex="4" data-l10n-id="findbar">
+                   <span data-l10n-id="findbar_label">Find</span>
+                </button>
                 <div class="splitToolbarButton">
                   <button class="toolbarButton pageUp" title="Previous Page" id="previous" tabindex="5" data-l10n-id="previous">
                     <span data-l10n-id="previous_label">Previous</span>
                   </button>
                   <div class="splitToolbarButtonSeparator"></div>
                   <button class="toolbarButton pageDown" title="Next Page" id="next" tabindex="6" data-l10n-id="next">
                     <span data-l10n-id="next_label">Next</span>
                   </button>
@@ -36053,16 +36155,20 @@ var JpegImage = (function jpegImage() {
                   </span>
                 </div>
               </div>
             </div>
           </div>
         </div>
 
         <menu type="context" id="viewerContextMenu">
+          <menuitem label="First Page" id="first_page"
+                    data-l10n-id="first_page" ></menuitem>
+          <menuitem label="Last Page" id="last_page"
+                    data-l10n-id="last_page" ></menuitem>
           <menuitem label="Rotate Counter-Clockwise" id="page_rotate_ccw"
                     data-l10n-id="page_rotate_ccw" ></menuitem>
           <menuitem label="Rotate Clockwise" id="page_rotate_cw"
                     data-l10n-id="page_rotate_cw" ></menuitem>
         </menu>
 
         <div id="viewerContainer">
           <div id="viewer" contextmenu="viewerContextMenu"></div>
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -19,26 +19,33 @@
 
 var kDefaultURL = 'compressed.tracemonkey-pldi-09.pdf';
 var kDefaultScale = 'auto';
 var kDefaultScaleDelta = 1.1;
 var kUnknownScale = 0;
 var kCacheSize = 20;
 var kCssUnits = 96.0 / 72.0;
 var kScrollbarPadding = 40;
+var kVerticalPadding = 5;
 var kMinScale = 0.25;
 var kMaxScale = 4.0;
 var kImageDirectory = './images/';
 var kSettingsMemory = 20;
 var RenderingStates = {
   INITIAL: 0,
   RUNNING: 1,
   PAUSED: 2,
   FINISHED: 3
 };
+var FindStates = {
+  FIND_FOUND: 0,
+  FIND_NOTFOUND: 1,
+  FIND_WRAPPED: 2,
+  FIND_PENDING: 3
+};
 
 
 var mozL10n = document.mozL10n || document.webL10n;
 
 function getFileName(url) {
   var anchor = url.indexOf('#');
   var query = url.indexOf('?');
   var end = Math.min(
@@ -200,77 +207,479 @@ var FirefoxCom = (function FirefoxComClo
     }
   };
 })();
 
 
 // Settings Manager - This is a utility for saving settings
 // First we see if localStorage is available
 // If not, we use FUEL in FF
+// Use asyncStorage for B2G
 var Settings = (function SettingsClosure() {
   var isLocalStorageEnabled = (function localStorageEnabledTest() {
     // Feature test as per http://diveintohtml5.info/storage.html
     // The additional localStorage call is to get around a FF quirk, see
     // bug #495747 in bugzilla
     try {
       return 'localStorage' in window && window['localStorage'] !== null &&
           localStorage;
     } catch (e) {
       return false;
     }
   })();
 
   function Settings(fingerprint) {
-    var database = null;
-    var index;
-    database = FirefoxCom.requestSync('getDatabase', null) || '{}';
-
-    database = JSON.parse(database);
-    if (!('files' in database))
-      database.files = [];
-    if (database.files.length >= kSettingsMemory)
-      database.files.shift();
-    for (var i = 0, length = database.files.length; i < length; i++) {
-      var branch = database.files[i];
-      if (branch.fingerprint == fingerprint) {
-        index = i;
-        break;
-      }
-    }
-    if (typeof index != 'number')
-      index = database.files.push({fingerprint: fingerprint}) - 1;
-    this.file = database.files[index];
-    this.database = database;
+    this.fingerprint = fingerprint;
+    this.initializedPromise = new PDFJS.Promise();
+
+    var resolvePromise = (function settingsResolvePromise(db) {
+      this.initialize(db || '{}');
+      this.initializedPromise.resolve();
+    }).bind(this);
+
+
+    resolvePromise(FirefoxCom.requestSync('getDatabase', null));
+
   }
 
   Settings.prototype = {
+    initialize: function settingsInitialize(database) {
+      database = JSON.parse(database);
+      if (!('files' in database))
+        database.files = [];
+      if (database.files.length >= kSettingsMemory)
+        database.files.shift();
+      var index;
+      for (var i = 0, length = database.files.length; i < length; i++) {
+        var branch = database.files[i];
+        if (branch.fingerprint == this.fingerprint) {
+          index = i;
+          break;
+        }
+      }
+      if (typeof index != 'number')
+        index = database.files.push({fingerprint: this.fingerprint}) - 1;
+      this.file = database.files[index];
+      this.database = database;
+    },
+
     set: function settingsSet(name, val) {
-      if (!('file' in this))
-        return false;
+      if (!this.initializedPromise.isResolved)
+        return;
 
       var file = this.file;
       file[name] = val;
       var database = JSON.stringify(this.database);
       FirefoxCom.requestSync('setDatabase', database);
     },
 
     get: function settingsGet(name, defaultValue) {
-      if (!('file' in this))
+      if (!this.initializedPromise.isResolved)
         return defaultValue;
 
       return this.file[name] || defaultValue;
     }
   };
 
   return Settings;
 })();
 
 var cache = new Cache(kCacheSize);
 var currentPageNumber = 1;
 
+var PDFFindController = {
+  extractTextPromise: null,
+
+  // If active, find results will be highlighted.
+  active: false,
+
+  // Stores the text for each page.
+  pageContents: [],
+
+  pageMatches: [],
+
+  selected: {
+    pageIdx: 0,
+    matchIdx: 0
+  },
+
+  state: null,
+
+  dirtyMatch: false,
+
+  findTimeout: null,
+
+  initialize: function() {
+    var events = [
+      'find',
+      'findagain',
+      'findhighlightallchange',
+      'findcasesensitivitychange'
+    ];
+
+    this.handleEvent = this.handleEvent.bind(this);
+
+    for (var i = 0; i < events.length; i++) {
+      window.addEventListener(events[i], this.handleEvent);
+    }
+  },
+
+  calcFindMatch: function(pageContent) {
+    var query = this.state.query;
+    var caseSensitive = this.state.caseSensitive;
+    var queryLen = query.length;
+
+    if (queryLen === 0)
+      return [];
+
+    if (!caseSensitive) {
+      pageContent = pageContent.toLowerCase();
+      query = query.toLowerCase();
+    }
+
+    var matches = [];
+
+    var matchIdx = -queryLen;
+    while (true) {
+      matchIdx = pageContent.indexOf(query, matchIdx + queryLen);
+      if (matchIdx === -1) {
+        break;
+      }
+
+      matches.push(matchIdx);
+    }
+    return matches;
+  },
+
+  extractText: function() {
+    if (this.extractTextPromise) {
+      return this.extractTextPromise;
+    }
+    this.extractTextPromise = new PDFJS.Promise();
+
+    var self = this;
+    function extractPageText(pageIndex) {
+      PDFView.pages[pageIndex].getTextContent().then(
+        function textContentResolved(data) {
+          // Build the find string.
+          var bidiTexts = data.bidiTexts;
+          var str = '';
+
+          for (var i = 0; i < bidiTexts.length; i++) {
+            str += bidiTexts[i].str;
+          }
+
+          // Store the pageContent as a string.
+          self.pageContents.push(str);
+          // Ensure there is a empty array of matches.
+          self.pageMatches.push([]);
+
+          if ((pageIndex + 1) < PDFView.pages.length)
+            extractPageText(pageIndex + 1);
+          else
+            self.extractTextPromise.resolve();
+        }
+      );
+    }
+    extractPageText(0);
+    return this.extractTextPromise;
+  },
+
+  handleEvent: function(e) {
+    if (this.state === null || e.type !== 'findagain') {
+      this.dirtyMatch = true;
+    }
+    this.state = e.detail;
+    this.updateUIState(FindStates.FIND_PENDING);
+
+    var promise = this.extractText();
+
+    clearTimeout(this.findTimeout);
+    if (e.type === 'find') {
+      // Only trigger the find action after 250ms of silence.
+      this.findTimeout = setTimeout(function() {
+        promise.then(this.performFind.bind(this));
+      }.bind(this), 250);
+    } else {
+      promise.then(this.performFind.bind(this));
+    }
+  },
+
+  updatePage: function(idx) {
+    var page = PDFView.pages[idx];
+
+    if (this.selected.pageIdx === idx) {
+      // If the page is selected, scroll the page into view, which triggers
+      // rendering the page, which adds the textLayer. Once the textLayer is
+      // build, it will scroll onto the selected match.
+      page.scrollIntoView();
+    }
+
+    if (page.textLayer) {
+      page.textLayer.updateMatches();
+    }
+  },
+
+  performFind: function() {
+    // Recalculate all the matches.
+    // TODO: Make one match show up as the current match
+
+    var pages = PDFView.pages;
+    var pageContents = this.pageContents;
+    var pageMatches = this.pageMatches;
+
+    this.active = true;
+
+    if (this.dirtyMatch) {
+      // Need to recalculate the matches.
+      this.dirtyMatch = false;
+
+      this.selected = {
+        pageIdx: -1,
+        matchIdx: -1
+      };
+
+      // TODO: Make this way more lasily (aka. efficient) - e.g. calculate only
+      // the matches for the current visible pages.
+      var firstMatch = true;
+      for (var i = 0; i < pageContents.length; i++) {
+        var matches = pageMatches[i] = this.calcFindMatch(pageContents[i]);
+        if (firstMatch && matches.length !== 0) {
+          firstMatch = false;
+          this.selected = {
+            pageIdx: i,
+            matchIdx: 0
+          };
+        }
+        this.updatePage(i, true);
+      }
+      if (!firstMatch || !this.state.query) {
+        this.updateUIState(FindStates.FIND_FOUND);
+      } else {
+        this.updateUIState(FindStates.FIND_NOTFOUND);
+      }
+    } else {
+      // If there is NO selection, then there is no match at all -> no sense to
+      // handle previous/next action.
+      if (this.selected.pageIdx === -1) {
+        this.updateUIState(FindStates.FIND_NOTFOUND);
+        return;
+      }
+
+      // Handle findAgain case.
+      var previous = this.state.findPrevious;
+      var sPageIdx = this.selected.pageIdx;
+      var sMatchIdx = this.selected.matchIdx;
+      var findState = FindStates.FIND_FOUND;
+
+      if (previous) {
+        // Select previous match.
+
+        if (sMatchIdx !== 0) {
+          this.selected.matchIdx -= 1;
+        } else {
+          var len = pageMatches.length;
+          for (var i = sPageIdx - 1; i != sPageIdx; i--) {
+            if (i < 0)
+              i += len;
+
+            if (pageMatches[i].length !== 0) {
+              this.selected = {
+                pageIdx: i,
+                matchIdx: pageMatches[i].length - 1
+              };
+              break;
+            }
+          }
+          // If pageIdx stayed the same, select last match on the page.
+          if (this.selected.pageIdx === sPageIdx) {
+            this.selected.matchIdx = pageMatches[sPageIdx].length - 1;
+            findState = FindStates.FIND_WRAPPED;
+          } else if (this.selected.pageIdx > sPageIdx) {
+            findState = FindStates.FIND_WRAPPED;
+          }
+        }
+      } else {
+        // Select next match.
+
+        if (pageMatches[sPageIdx].length !== sMatchIdx + 1) {
+          this.selected.matchIdx += 1;
+        } else {
+          var len = pageMatches.length;
+          for (var i = sPageIdx + 1; i < len + sPageIdx; i++) {
+            if (pageMatches[i % len].length !== 0) {
+              this.selected = {
+                pageIdx: i % len,
+                matchIdx: 0
+              };
+              break;
+            }
+          }
+
+          // If pageIdx stayed the same, select first match on the page.
+          if (this.selected.pageIdx === sPageIdx) {
+            this.selected.matchIdx = 0;
+            findState = FindStates.FIND_WRAPPED;
+          } else if (this.selected.pageIdx < sPageIdx) {
+            findState = FindStates.FIND_WRAPPED;
+          }
+        }
+      }
+
+      this.updateUIState(findState, previous);
+      this.updatePage(sPageIdx, sPageIdx === this.selected.pageIdx);
+      if (sPageIdx !== this.selected.pageIdx) {
+        this.updatePage(this.selected.pageIdx, true);
+      }
+    }
+  },
+
+  updateUIState: function(state, previous) {
+    if (PDFView.supportsIntegratedFind) {
+      FirefoxCom.request('updateFindControlState',
+                         {result: state, findPrevious: previous});
+      return;
+    }
+    PDFFindBar.updateUIState(state, previous);
+  }
+};
+
+var PDFFindBar = {
+  // TODO: Enable the FindBar *AFTER* the pagesPromise in the load function
+  // got resolved
+
+  opened: false,
+
+  initialize: function() {
+    this.bar = document.getElementById('findbar');
+    this.toggleButton = document.getElementById('viewFind');
+    this.findField = document.getElementById('findInput');
+    this.highlightAll = document.getElementById('findHighlightAll');
+    this.caseSensitive = document.getElementById('findMatchCase');
+    this.findMsg = document.getElementById('findMsg');
+    this.findStatusIcon = document.getElementById('findStatusIcon');
+
+    var self = this;
+    this.toggleButton.addEventListener('click', function() {
+      self.toggle();
+    });
+
+    this.findField.addEventListener('input', function() {
+      self.dispatchEvent('');
+    });
+
+    this.bar.addEventListener('keydown', function(evt) {
+      switch (evt.keyCode) {
+        case 13: // Enter
+          if (evt.target === self.findField) {
+            self.dispatchEvent('again', evt.shiftKey);
+          }
+          break;
+        case 27: // Escape
+          self.close();
+          break;
+      }
+    });
+
+    document.getElementById('findPrevious').addEventListener('click',
+      function() { self.dispatchEvent('again', true); }
+    );
+
+    document.getElementById('findNext').addEventListener('click', function() {
+      self.dispatchEvent('again', false);
+    });
+
+    this.highlightAll.addEventListener('click', function() {
+      self.dispatchEvent('highlightallchange');
+    });
+
+    this.caseSensitive.addEventListener('click', function() {
+      self.dispatchEvent('casesensitivitychange');
+    });
+  },
+
+  dispatchEvent: function(aType, aFindPrevious) {
+    var event = document.createEvent('CustomEvent');
+    event.initCustomEvent('find' + aType, true, true, {
+      query: this.findField.value,
+      caseSensitive: this.caseSensitive.checked,
+      highlightAll: this.highlightAll.checked,
+      findPrevious: aFindPrevious
+    });
+    return window.dispatchEvent(event);
+  },
+
+  updateUIState: function(state, previous) {
+    var notFound = false;
+    var findMsg = '';
+    var status = '';
+
+    switch (state) {
+      case FindStates.FIND_FOUND:
+        break;
+
+      case FindStates.FIND_PENDING:
+        status = 'pending';
+        break;
+
+      case FindStates.FIND_NOTFOUND:
+        findMsg = mozL10n.get('find_not_found', null, 'Phrase not found');
+        notFound = true;
+        break;
+
+      case FindStates.FIND_WRAPPED:
+        if (previous) {
+          findMsg = mozL10n.get('find_wrapped_to_bottom', null,
+                                'Reached end of page, continued from bottom');
+        } else {
+          findMsg = mozL10n.get('find_wrapped_to_top', null,
+                                'Reached end of page, continued from top');
+        }
+        break;
+    }
+
+    if (notFound) {
+      this.findField.classList.add('notFound');
+    } else {
+      this.findField.classList.remove('notFound');
+    }
+
+    this.findField.setAttribute('data-status', status);
+    this.findMsg.textContent = findMsg;
+  },
+
+  open: function() {
+    if (this.opened) return;
+
+    this.opened = true;
+    this.toggleButton.classList.add('toggled');
+    this.bar.classList.remove('hidden');
+    this.findField.select();
+    this.findField.focus();
+  },
+
+  close: function() {
+    if (!this.opened) return;
+
+    this.opened = false;
+    this.toggleButton.classList.remove('toggled');
+    this.bar.classList.add('hidden');
+
+    PDFFindController.active = false;
+  },
+
+  toggle: function() {
+    if (this.opened) {
+      this.close();
+    } else {
+      this.open();
+    }
+  }
+};
+
 var PDFView = {
   pages: [],
   thumbnails: [],
   currentScale: kUnknownScale,
   currentScaleValue: null,
   initialBookmark: document.location.hash.substring(1),
   startedTextExtraction: false,
   pageText: [],
@@ -280,31 +689,36 @@ var PDFView = {
   fellback: false,
   pdfDocument: null,
   sidebarOpen: false,
   pageViewScroll: null,
   thumbnailViewScroll: null,
   isFullscreen: false,
   previousScale: null,
   pageRotation: 0,
+  mouseScrollTimeStamp: 0,
+  mouseScrollDelta: 0,
   lastScroll: 0,
 
   // called once when the document is loaded
   initialize: function pdfViewInitialize() {
     var self = this;
     var container = this.container = document.getElementById('viewerContainer');
     this.pageViewScroll = {};
     this.watchScroll(container, this.pageViewScroll, updateViewarea);
 
     var thumbnailContainer = this.thumbnailContainer =
                              document.getElementById('thumbnailView');
     this.thumbnailViewScroll = {};
     this.watchScroll(thumbnailContainer, this.thumbnailViewScroll,
                      this.renderHighestPriority.bind(this));
 
+    PDFFindBar.initialize();
+    PDFFindController.initialize();
+
     this.initialized = true;
     container.addEventListener('scroll', function() {
       self.lastScroll = Date.now();
     }, false);
   },
 
   // Helper function to keep track whether a div was scrolled up or down and
   // then call a callback.
@@ -351,20 +765,23 @@ var PDFView = {
     this.currentScaleValue = value;
     if (scale) {
       this.setScale(scale, true, noScroll);
       return;
     }
 
     var container = this.container;
     var currentPage = this.pages[this.page - 1];
+    if (!currentPage) {
+      return;
+    }
 
     var pageWidthScale = (container.clientWidth - kScrollbarPadding) /
                           currentPage.width * currentPage.scale / kCssUnits;
-    var pageHeightScale = (container.clientHeight - kScrollbarPadding) /
+    var pageHeightScale = (container.clientHeight - kVerticalPadding) /
                            currentPage.height * currentPage.scale / kCssUnits;
     switch (value) {
       case 'page-actual':
         scale = 1;
         break;
       case 'page-width':
         scale = pageWidthScale;
         break;
@@ -445,16 +862,26 @@ var PDFView = {
                   doc.webkitRequestFullScreen;
     Object.defineProperty(this, 'supportsFullScreen', { value: support,
                                                         enumerable: true,
                                                         configurable: true,
                                                         writable: false });
     return support;
   },
 
+  get supportsIntegratedFind() {
+    var support = false;
+    support = FirefoxCom.requestSync('supportsIntegratedFind');
+    Object.defineProperty(this, 'supportsIntegratedFind', { value: support,
+                                                            enumerable: true,
+                                                            configurable: true,
+                                                            writable: false });
+    return support;
+  },
+
   initPassiveLoading: function pdfViewInitPassiveLoading() {
     if (!PDFView.loadingBar) {
       PDFView.loadingBar = new ProgressBar('#loadingBar', {});
     }
 
     window.addEventListener('message', function window_message(e) {
       var args = e.data;
 
@@ -705,30 +1132,23 @@ var PDFView = {
       clearInterval(thumbsView._loadingInterval);
 
     var container = document.getElementById('viewer');
     while (container.hasChildNodes())
       container.removeChild(container.lastChild);
 
     var pagesCount = pdfDocument.numPages;
     var id = pdfDocument.fingerprint;
-    var storedHash = null;
     document.getElementById('numPages').textContent =
       mozL10n.get('page_of', {pageCount: pagesCount}, 'of {{pageCount}}');
     document.getElementById('pageNumber').max = pagesCount;
+
     PDFView.documentFingerprint = id;
     var store = PDFView.store = new Settings(id);
-    if (store.get('exists', false)) {
-      var page = store.get('page', '1');
-      var zoom = store.get('zoom', PDFView.currentScale);
-      var left = store.get('scrollLeft', '0');
-      var top = store.get('scrollTop', '0');
-
-      storedHash = 'page=' + page + '&zoom=' + zoom + ',' + left + ',' + top;
-    }
+    var storePromise = store.initializedPromise;
 
     this.pageRotation = 0;
 
     var pages = this.pages = [];
     this.pageText = [];
     this.startedTextExtraction = false;
     var pagesRefMap = {};
     var thumbnails = this.thumbnails = [];
@@ -755,21 +1175,32 @@ var PDFView = {
     });
 
     var destinationsPromise = pdfDocument.getDestinations();
     destinationsPromise.then(function(destinations) {
       self.destinations = destinations;
     });
 
     // outline and initial view depends on destinations and pagesRefMap
-    PDFJS.Promise.all([pagesPromise, destinationsPromise]).then(function() {
+    var promises = [pagesPromise, destinationsPromise, storePromise];
+    PDFJS.Promise.all(promises).then(function() {
       pdfDocument.getOutline().then(function(outline) {
         self.outline = new DocumentOutlineView(outline);
       });
 
+      var storedHash = null;
+      if (store.get('exists', false)) {
+        var page = store.get('page', '1');
+        var zoom = store.get('zoom', PDFView.currentScale);
+        var left = store.get('scrollLeft', '0');
+        var top = store.get('scrollTop', '0');
+
+        storedHash = 'page=' + page + '&zoom=' + zoom + ',' + left + ',' + top;
+      }
+
       self.setInitialView(storedHash, scale);
     });
 
     pdfDocument.getMetadata().then(function(data) {
       var info = data.info, metadata = data.metadata;
       self.documentInfo = info;
       self.metadata = metadata;
 
@@ -887,82 +1318,16 @@ var PDFView = {
       case RenderingStates.INITIAL:
         PDFView.highestPriorityPage = type + view.id;
         view.draw(this.renderHighestPriority.bind(this));
         break;
     }
     return true;
   },
 
-  search: function pdfViewStartSearch() {
-    // Limit this function to run every <SEARCH_TIMEOUT>ms.
-    var SEARCH_TIMEOUT = 250;
-    var lastSearch = this.lastSearch;
-    var now = Date.now();
-    if (lastSearch && (now - lastSearch) < SEARCH_TIMEOUT) {
-      if (!this.searchTimer) {
-        this.searchTimer = setTimeout(function resumeSearch() {
-            PDFView.search();
-          },
-          SEARCH_TIMEOUT - (now - lastSearch)
-        );
-      }
-      return;
-    }
-    this.searchTimer = null;
-    this.lastSearch = now;
-
-    function bindLink(link, pageNumber) {
-      link.href = '#' + pageNumber;
-      link.onclick = function searchBindLink() {
-        PDFView.page = pageNumber;
-        return false;
-      };
-    }
-
-    var searchResults = document.getElementById('searchResults');
-
-    var searchTermsInput = document.getElementById('searchTermsInput');
-    searchResults.removeAttribute('hidden');
-    searchResults.textContent = '';
-
-    var terms = searchTermsInput.value;
-
-    if (!terms)
-      return;
-
-    // simple search: removing spaces and hyphens, then scanning every
-    terms = terms.replace(/\s-/g, '').toLowerCase();
-    var index = PDFView.pageText;
-    var pageFound = false;
-    for (var i = 0, ii = index.length; i < ii; i++) {
-      var pageText = index[i].replace(/\s-/g, '').toLowerCase();
-      var j = pageText.indexOf(terms);
-      if (j < 0)
-        continue;
-
-      var pageNumber = i + 1;
-      var textSample = index[i].substr(j, 50);
-      var link = document.createElement('a');
-      bindLink(link, pageNumber);
-      link.textContent = 'Page ' + pageNumber + ': ' + textSample;
-      searchResults.appendChild(link);
-
-      pageFound = true;
-    }
-    if (!pageFound) {
-      searchResults.textContent = '';
-      var noResults = document.createElement('div');
-      noResults.classList.add('noResults');
-      noResults.textContent = mozL10n.get('search_terms_not_found', null,
-                                              '(Not found)');
-      searchResults.appendChild(noResults);
-    }
-  },
-
   setHash: function pdfViewSetHash(hash) {
     if (!hash)
       return;
 
     if (hash.indexOf('=') >= 0) {
       var params = PDFView.parseQueryString(hash);
       // borrowing syntax from "Parameters for Opening PDF Files"
       if ('nameddest' in params) {
@@ -994,80 +1359,42 @@ var PDFView = {
       this.page = hash;
     else // named destination
       PDFView.navigateTo(unescape(hash));
   },
 
   switchSidebarView: function pdfViewSwitchSidebarView(view) {
     var thumbsView = document.getElementById('thumbnailView');
     var outlineView = document.getElementById('outlineView');
-    var searchView = document.getElementById('searchView');
 
     var thumbsButton = document.getElementById('viewThumbnail');
     var outlineButton = document.getElementById('viewOutline');
-    var searchButton = document.getElementById('viewSearch');
 
     switch (view) {
       case 'thumbs':
         thumbsButton.classList.add('toggled');
         outlineButton.classList.remove('toggled');
-        searchButton.classList.remove('toggled');
         thumbsView.classList.remove('hidden');
         outlineView.classList.add('hidden');
-        searchView.classList.add('hidden');
 
         PDFView.renderHighestPriority();
         break;
 
       case 'outline':
         thumbsButton.classList.remove('toggled');
         outlineButton.classList.add('toggled');
-        searchButton.classList.remove('toggled');
         thumbsView.classList.add('hidden');
         outlineView.classList.remove('hidden');
-        searchView.classList.add('hidden');
 
         if (outlineButton.getAttribute('disabled'))
           return;
         break;
-
-      case 'search':
-        thumbsButton.classList.remove('toggled');
-        outlineButton.classList.remove('toggled');
-        searchButton.classList.add('toggled');
-        thumbsView.classList.add('hidden');
-        outlineView.classList.add('hidden');
-        searchView.classList.remove('hidden');
-
-        var searchTermsInput = document.getElementById('searchTermsInput');
-        searchTermsInput.focus();
-        // Start text extraction as soon as the search gets displayed.
-        this.extractText();
-        break;
     }
   },
 
-  extractText: function() {
-    if (this.startedTextExtraction)
-      return;
-    this.startedTextExtraction = true;
-    var self = this;
-    function extractPageText(pageIndex) {
-      self.pages[pageIndex].pdfPage.getTextContent().then(
-        function textContentResolved(textContent) {
-          self.pageText[pageIndex] = textContent.join('');
-          self.search();
-          if ((pageIndex + 1) < self.pages.length)
-            extractPageText(pageIndex + 1);
-        }
-      );
-    }
-    extractPageText(0);
-  },
-
   getVisiblePages: function pdfViewGetVisiblePages() {
     return this.getVisibleElements(this.container,
                                    this.pages, true);
   },
 
   getVisibleThumbs: function pdfViewGetVisibleThumbs() {
     return this.getVisibleElements(this.thumbnailContainer,
                                    this.thumbnails);
@@ -1188,23 +1515,51 @@ var PDFView = {
     this.previousScale = this.currentScaleValue;
     this.parseScale('page-fit', true);
 
     // Wait for fullscreen to take effect
     setTimeout(function() {
       currentPage.scrollIntoView();
     }, 0);
 
+    this.showPresentationControls();
     return true;
   },
 
   exitFullscreen: function pdfViewExitFullscreen() {
     this.isFullscreen = false;
     this.parseScale(this.previousScale);
     this.page = this.page;
+    this.clearMouseScrollState();
+    this.hidePresentationControls();
+  },
+
+  showPresentationControls: function pdfViewShowPresentationControls() {
+    var DELAY_BEFORE_HIDING_CONTROLS = 3000;
+    var wrapper = document.getElementById('viewerContainer');
+    if (this.presentationControlsTimeout) {
+      clearTimeout(this.presentationControlsTimeout);
+    } else {
+      wrapper.classList.add('presentationControls');
+    }
+    this.presentationControlsTimeout = setTimeout(function hideControls() {
+      wrapper.classList.remove('presentationControls');
+      delete PDFView.presentationControlsTimeout;
+    }, DELAY_BEFORE_HIDING_CONTROLS);
+  },
+
+  hidePresentationControls: function pdfViewShowPresentationControls() {
+    if (!this.presentationControlsTimeout) {
+      return;
+    }
+    clearTimeout(this.presentationControlsTimeout);
+    delete this.presentationControlsTimeout;
+
+    var wrapper = document.getElementById('viewerContainer');
+    wrapper.classList.remove('presentationControls');
   },
 
   rotatePages: function pdfViewPageRotation(delta) {
 
     this.pageRotation = (this.pageRotation + 360 + delta) % 360;
 
     for (var i = 0, l = this.pages.length; i < l; i++) {
       var page = this.pages[i];
@@ -1221,41 +1576,107 @@ var PDFView = {
     this.parseScale(this.currentScaleValue, true);
 
     this.renderHighestPriority();
 
     // Wait for fullscreen to take effect
     setTimeout(function() {
       currentPage.scrollIntoView();
     }, 0);
+  },
+
+  /**
+   * This function flips the page in presentation mode if the user scrolls up
+   * or down with large enough motion and prevents page flipping too often.
+   *
+   * @this {PDFView}
+   * @param {number} mouseScrollDelta The delta value from the mouse event.
+   */
+  mouseScroll: function pdfViewMouseScroll(mouseScrollDelta) {
+    var MOUSE_SCROLL_COOLDOWN_TIME = 50;
+
+    var currentTime = (new Date()).getTime();
+    var storedTime = this.mouseScrollTimeStamp;
+
+    // In case one page has already been flipped there is a cooldown time
+    // which has to expire before next page can be scrolled on to.
+    if (currentTime > storedTime &&
+        currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME)
+      return;
+
+    // In case the user decides to scroll to the opposite direction than before
+    // clear the accumulated delta.
+    if ((this.mouseScrollDelta > 0 && mouseScrollDelta < 0) ||
+        (this.mouseScrollDelta < 0 && mouseScrollDelta > 0))
+      this.clearMouseScrollState();
+
+    this.mouseScrollDelta += mouseScrollDelta;
+
+    var PAGE_FLIP_THRESHOLD = 120;
+    if (Math.abs(this.mouseScrollDelta) >= PAGE_FLIP_THRESHOLD) {
+
+      var PageFlipDirection = {
+        UP: -1,
+        DOWN: 1
+      };
+
+      // In fullscreen mode scroll one page at a time.
+      var pageFlipDirection = (this.mouseScrollDelta > 0) ?
+                                PageFlipDirection.UP :
+                                PageFlipDirection.DOWN;
+      this.clearMouseScrollState();
+      var currentPage = this.page;
+
+      // In case we are already on the first or the last page there is no need
+      // to do anything.
+      if ((currentPage == 1 && pageFlipDirection == PageFlipDirection.UP) ||
+          (currentPage == this.pages.length &&
+           pageFlipDirection == PageFlipDirection.DOWN))
+        return;
+
+      this.page += pageFlipDirection;
+      this.mouseScrollTimeStamp = currentTime;
+    }
+  },
+
+  /**
+   * This function clears the member attributes used with mouse scrolling in
+   * presentation mode.
+   *
+   * @this {PDFView}
+   */
+  clearMouseScrollState: function pdfViewClearMouseScrollState() {
+    this.mouseScrollTimeStamp = 0;
+    this.mouseScrollDelta = 0;
   }
 };
 
 var PageView = function pageView(container, pdfPage, id, scale,
                                  stats, navigateTo) {
   this.id = id;
   this.pdfPage = pdfPage;
 
   this.rotation = 0;
   this.scale = scale || 1.0;
   this.viewport = this.pdfPage.getViewport(this.scale, this.pdfPage.rotate);
 
   this.renderingState = RenderingStates.INITIAL;
   this.resume = null;
 
   this.textContent = null;
+  this.textLayer = null;
 
   var anchor = document.createElement('a');
   anchor.name = '' + this.id;
 
   var div = this.el = document.createElement('div');
   div.id = 'pageContainer' + this.id;
   div.className = 'page';
-  div.style.width = this.viewport.width + 'px';
-  div.style.height = this.viewport.height + 'px';
+  div.style.width = Math.floor(this.viewport.width) + 'px';
+  div.style.height = Math.floor(this.viewport.height) + 'px';
 
   container.appendChild(anchor);
   container.appendChild(div);
 
   this.destroy = function pageViewDestroy() {
     this.update();
     this.pdfPage.destroy();
   };
@@ -1269,18 +1690,18 @@ var PageView = function pageView(contain
     }
 
     this.scale = scale || this.scale;
 
     var totalRotation = (this.rotation + this.pdfPage.rotate) % 360;
     var viewport = this.pdfPage.getViewport(this.scale, totalRotation);
 
     this.viewport = viewport;
-    div.style.width = viewport.width + 'px';
-    div.style.height = viewport.height + 'px';
+    div.style.width = Math.floor(viewport.width) + 'px';
+    div.style.height = Math.floor(viewport.height) + 'px';
 
     while (div.hasChildNodes())
       div.removeChild(div.lastChild);
     div.removeAttribute('data-loaded');
 
     delete this.canvas;
 
     this.loadingIconDiv = document.createElement('div');
@@ -1483,21 +1904,22 @@ var PageView = function pageView(contain
     this.canvas = canvas;
 
     var textLayerDiv = null;
     if (!PDFJS.disableTextLayer) {
       textLayerDiv = document.createElement('div');
       textLayerDiv.className = 'textLayer';
       div.appendChild(textLayerDiv);
     }
-    var textLayer = textLayerDiv ? new TextLayerBuilder(textLayerDiv) : null;
+    var textLayer = this.textLayer =
+          textLayerDiv ? new TextLayerBuilder(textLayerDiv, this.id - 1) : null;
 
     var scale = this.scale, viewport = this.viewport;
-    canvas.width = viewport.width;
-    canvas.height = viewport.height;
+    canvas.width = Math.floor(viewport.width);
+    canvas.height = Math.floor(viewport.height);
 
     var ctx = canvas.getContext('2d');
     ctx.save();
     ctx.fillStyle = 'rgb(255, 255, 255)';
     ctx.fillRect(0, 0, canvas.width, canvas.height);
     ctx.restore();
 
     // Rendering area
@@ -1842,60 +2264,70 @@ var CustomStyle = (function CustomStyleC
     var prop = this.getProp(propName);
     if (prop != 'undefined')
       element.style[prop] = str;
   };
 
   return CustomStyle;
 })();
 
-var TextLayerBuilder = function textLayerBuilder(textLayerDiv) {
+var TextLayerBuilder = function textLayerBuilder(textLayerDiv, pageIdx) {
   var textLayerFrag = document.createDocumentFragment();
+
   this.textLayerDiv = textLayerDiv;
   this.layoutDone = false;
   this.divContentDone = false;
+  this.pageIdx = pageIdx;
+  this.matches = [];
 
   this.beginLayout = function textLayerBuilderBeginLayout() {
     this.textDivs = [];
     this.textLayerQueue = [];
+    this.renderingDone = false;
   };
 
   this.endLayout = function textLayerBuilderEndLayout() {
     this.layoutDone = true;
     this.insertDivContent();
-  },
+  };
 
   this.renderLayer = function textLayerBuilderRenderLayer() {
     var self = this;
     var textDivs = this.textDivs;
     var textLayerDiv = this.textLayerDiv;
     var canvas = document.createElement('canvas');
     var ctx = canvas.getContext('2d');
 
     // No point in rendering so many divs as it'd make the browser unusable
     // even after the divs are rendered
-    if (textDivs.length > 100000)
+    var MAX_TEXT_DIVS_TO_RENDER = 100000;
+    if (textDivs.length > MAX_TEXT_DIVS_TO_RENDER)
       return;
 
-    while (textDivs.length > 0) {
-      var textDiv = textDivs.shift();
+    for (var i = 0, ii = textDivs.length; i < ii; i++) {
+      var textDiv = textDivs[i];
       textLayerFrag.appendChild(textDiv);
 
       ctx.font = textDiv.style.fontSize + ' ' + textDiv.style.fontFamily;
       var width = ctx.measureText(textDiv.textContent).width;
 
       if (width > 0) {
         var textScale = textDiv.dataset.canvasWidth / width;
 
         CustomStyle.setProp('transform' , textDiv,
           'scale(' + textScale + ', 1)');
         CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%');
+
+        textLayerDiv.appendChild(textDiv);
       }
     }
 
+    this.renderingDone = true;
+    this.updateMatches();
+
     textLayerDiv.appendChild(textLayerFrag);
   };
 
   this.setupRenderLayoutTimer = function textLayerSetupRenderLayoutTimer() {
     // Schedule renderLayout() if user has been scrolling, otherwise
     // run it right away
     var kRenderDelay = 200; // in ms
     var self = this;
@@ -1907,27 +2339,26 @@ var TextLayerBuilder = function textLaye
       if (this.renderTimer)
         clearTimeout(this.renderTimer);
       this.renderTimer = setTimeout(function() {
         self.setupRenderLayoutTimer();
       }, kRenderDelay);
     }
   };
 
-  this.appendText = function textLayerBuilderAppendText(fontName, fontSize,
-                                                        geom) {
+  this.appendText = function textLayerBuilderAppendText(geom) {
     var textDiv = document.createElement('div');
 
     // vScale and hScale already contain the scaling to pixel units
-    var fontHeight = fontSize * geom.vScale;
+    var fontHeight = geom.fontSize * geom.vScale;
     textDiv.dataset.canvasWidth = geom.canvasWidth * geom.hScale;
-    textDiv.dataset.fontName = fontName;
+    textDiv.dataset.fontName = geom.fontName;
 
     textDiv.style.fontSize = fontHeight + 'px';
-    textDiv.style.fontFamily = fontName;
+    textDiv.style.fontFamily = geom.fontFamily;
     textDiv.style.left = geom.x + 'px';
     textDiv.style.top = (geom.y - fontHeight) + 'px';
 
     // The content of the div is set in the `setTextContent` function.
 
     this.textDivs.push(textDiv);
   };
 
@@ -1952,16 +2383,211 @@ var TextLayerBuilder = function textLaye
 
     this.setupRenderLayoutTimer();
   };
 
   this.setTextContent = function textLayerBuilderSetTextContent(textContent) {
     this.textContent = textContent;
     this.insertDivContent();
   };
+
+  this.convertMatches = function textLayerBuilderConvertMatches(matches) {
+    var i = 0;
+    var iIndex = 0;
+    var bidiTexts = this.textContent.bidiTexts;
+    var end = bidiTexts.length - 1;
+    var queryLen = PDFFindController.state.query.length;
+
+    var lastDivIdx = -1;
+    var pos;
+
+    var ret = [];
+
+    // Loop over all the matches.
+    for (var m = 0; m < matches.length; m++) {
+      var matchIdx = matches[m];
+      // # Calculate the begin position.
+
+      // Loop over the divIdxs.
+      while (i !== end && matchIdx >= (iIndex + bidiTexts[i].str.length)) {
+        iIndex += bidiTexts[i].str.length;
+        i++;
+      }
+
+      // TODO: Do proper handling here if something goes wrong.
+      if (i == bidiTexts.length) {
+        console.error('Could not find matching mapping');
+      }
+
+      var match = {
+        begin: {
+          divIdx: i,
+          offset: matchIdx - iIndex
+        }
+      };
+
+      // # Calculate the end position.
+      matchIdx += queryLen;
+
+      // Somewhat same array as above, but use a > instead of >= to get the end
+      // position right.
+      while (i !== end && matchIdx > (iIndex + bidiTexts[i].str.length)) {
+        iIndex += bidiTexts[i].str.length;
+        i++;
+      }
+
+      match.end = {
+        divIdx: i,
+        offset: matchIdx - iIndex
+      };
+      ret.push(match);
+    }
+
+    return ret;
+  };
+
+  this.renderMatches = function textLayerBuilder_renderMatches(matches) {
+    // Early exit if there is nothing to render.
+    if (matches.length === 0) {
+      return;
+    }
+
+    var bidiTexts = this.textContent.bidiTexts;
+    var textDivs = this.textDivs;
+    var prevEnd = null;
+    var isSelectedPage = this.pageIdx === PDFFindController.selected.pageIdx;
+    var selectedMatchIdx = PDFFindController.selected.matchIdx;
+    var highlightAll = PDFFindController.state.highlightAll;
+
+    var infty = {
+      divIdx: -1,
+      offset: undefined
+    };
+
+    function beginText(begin, className) {
+      var divIdx = begin.divIdx;
+      var div = textDivs[divIdx];
+      div.innerHTML = '';
+
+      var content = bidiTexts[divIdx].str.substring(0, begin.offset);
+      var node = document.createTextNode(content);
+      if (className) {
+        var isSelected = isSelectedPage &&
+                          divIdx === selectedMatchIdx;
+        var span = document.createElement('span');
+        span.className = className + (isSelected ? ' selected' : '');
+        span.appendChild(node);
+        div.appendChild(span);
+        return;
+      }
+      div.appendChild(node);
+    }
+
+    function appendText(from, to, className) {
+      var divIdx = from.divIdx;
+      var div = textDivs[divIdx];
+
+      var content = bidiTexts[divIdx].str.substring(from.offset, to.offset);
+      var node = document.createTextNode(content);
+      if (className) {
+        var span = document.createElement('span');
+        span.className = className;
+        span.appendChild(node);
+        div.appendChild(span);
+        return;
+      }
+      div.appendChild(node);
+    }
+
+    function highlightDiv(divIdx, className) {
+      textDivs[divIdx].className = className;
+    }
+
+    var i0 = selectedMatchIdx, i1 = i0 + 1, i;
+
+    if (highlightAll) {
+      i0 = 0;
+      i1 = matches.length;
+    } else if (!isSelectedPage) {
+      // Not highlighting all and this isn't the selected page, so do nothing.
+      return;
+    }
+
+    for (i = i0; i < i1; i++) {
+      var match = matches[i];
+      var begin = match.begin;
+      var end = match.end;
+
+      var isSelected = isSelectedPage && i === selectedMatchIdx;
+      var highlightSuffix = (isSelected ? ' selected' : '');
+      if (isSelected)
+        scrollIntoView(textDivs[begin.divIdx], {top: -50});
+
+      // Match inside new div.
+      if (!prevEnd || begin.divIdx !== prevEnd.divIdx) {
+        // If there was a previous div, then add the text at the end
+        if (prevEnd !== null) {
+          appendText(prevEnd, infty);
+        }
+        // clears the divs and set the content until the begin point.
+        beginText(begin);
+      } else {
+        appendText(prevEnd, begin);
+      }
+
+      if (begin.divIdx === end.divIdx) {
+        appendText(begin, end, 'highlight' + highlightSuffix);
+      } else {
+        appendText(begin, infty, 'highlight begin' + highlightSuffix);
+        for (var n = begin.divIdx + 1; n < end.divIdx; n++) {
+          highlightDiv(n, 'highlight middle' + highlightSuffix);
+        }
+        beginText(end, 'highlight end' + highlightSuffix);
+      }
+      prevEnd = end;
+    }
+
+    if (prevEnd) {
+      appendText(prevEnd, infty);
+    }
+  };
+
+  this.updateMatches = function textLayerUpdateMatches() {
+    // Only show matches, once all rendering is done.
+    if (!this.renderingDone)
+      return;
+
+    // Clear out all matches.
+    var matches = this.matches;
+    var textDivs = this.textDivs;
+    var bidiTexts = this.textContent.bidiTexts;
+    var clearedUntilDivIdx = -1;
+
+    // Clear out all current matches.
+    for (var i = 0; i < matches.length; i++) {
+      var match = matches[i];
+      var begin = Math.max(clearedUntilDivIdx, match.begin.divIdx);
+      for (var n = begin; n <= match.end.divIdx; n++) {
+        var div = textDivs[n];
+        div.textContent = bidiTexts[n].str;
+        div.className = '';
+      }
+      clearedUntilDivIdx = match.end.divIdx + 1;
+    }
+
+    if (!PDFFindController.active)
+      return;
+
+    // Convert the matches on the page controller into the match format used
+    // for the textLayer.
+    this.matches = matches =
+      this.convertMatches(PDFFindController.pageMatches[this.pageIdx] || []);
+
+    this.renderMatches(this.matches);
+  };
 };
 
 document.addEventListener('DOMContentLoaded', function webViewerLoad(evt) {
   PDFView.initialize();
   var params = PDFView.parseQueryString(document.location.search.substring(1));
 
   var file = window.location.toString()
 
@@ -1992,28 +2618,28 @@ document.addEventListener('DOMContentLoa
   if ('pdfBug' in hashParams && FirefoxCom.requestSync('pdfBugEnabled')) {
     PDFJS.pdfBug = true;
     var pdfBug = hashParams['pdfBug'];
     var enabled = pdfBug.split(',');
     PDFBug.enable(enabled);
     PDFBug.init();
   }
 
-  if (FirefoxCom.requestSync('searchEnabled')) {
-    document.querySelector('#viewSearch').classList.remove('hidden');
-  }
-
   if (!PDFView.supportsPrinting) {
     document.getElementById('print').classList.add('hidden');
   }
 
   if (!PDFView.supportsFullscreen) {
     document.getElementById('fullscreen').classList.add('hidden');
   }
 
+  if (PDFView.supportsIntegratedFind) {
+    document.querySelector('#viewFind').classList.add('hidden');
+  }
+
   // Listen for warnings to trigger the fallback UI.  Errors should be caught
   // and call PDFView.error() so we don't need to listen for those.
   PDFJS.LogManager.addLogger({
     warn: function() {
       PDFView.fallback();
     }
   });
 
@@ -2042,26 +2668,16 @@ document.addEventListener('DOMContentLoa
       PDFView.switchSidebarView('thumbs');
     });
 
   document.getElementById('viewOutline').addEventListener('click',
     function() {
       PDFView.switchSidebarView('outline');
     });
 
-  document.getElementById('viewSearch').addEventListener('click',
-    function() {
-      PDFView.switchSidebarView('search');
-    });
-
-  document.getElementById('searchButton').addEventListener('click',
-    function() {
-      PDFView.search();
-    });
-
   document.getElementById('previous').addEventListener('click',
     function() {
       PDFView.page--;
     });
 
   document.getElementById('next').addEventListener('click',
     function() {
       PDFView.page++;
@@ -2092,58 +2708,64 @@ document.addEventListener('DOMContentLoa
       window.print();
     });
 
   document.getElementById('download').addEventListener('click',
     function() {
       PDFView.download();
     });
 
-  document.getElementById('searchTermsInput').addEventListener('keydown',
-    function(event) {
-      if (event.keyCode == 13) {
-        PDFView.search();
-      }
-    });
-
   document.getElementById('pageNumber').addEventListener('change',
     function() {
       PDFView.page = this.value;
     });
 
   document.getElementById('scaleSelect').addEventListener('change',
     function() {
       PDFView.parseScale(this.value);
     });
 
+  document.getElementById('first_page').addEventListener('click',
+    function() {
+      PDFView.page = 1;
+    });
+
+  document.getElementById('last_page').addEventListener('click',
+    function() {
+      PDFView.page = PDFView.pdfDocument.numPages;
+    });
+
   document.getElementById('page_rotate_ccw').addEventListener('click',
-      function() {
-        PDFView.rotatePages(-90);
-      });
+    function() {
+      PDFView.rotatePages(-90);
+    });
 
   document.getElementById('page_rotate_cw').addEventListener('click',
-      function() {
-        PDFView.rotatePages(90);
-      });
+    function() {
+      PDFView.rotatePages(90);
+    });
 
   if (FirefoxCom.requestSync('getLoadingType') == 'passive') {
     PDFView.setTitleUsingUrl(file);
     PDFView.initPassiveLoading();
     return;
   }
 
   PDFView.open(file, 0);
 }, true);
 
 function updateViewarea() {
 
   if (!PDFView.initialized)
     return;
   var visible = PDFView.getVisiblePages();
   var visiblePages = visible.views;
+  if (visiblePages.length === 0) {
+    return;
+  }
 
   PDFView.renderHighestPriority();
 
   var currentId = PDFView.page;
   var firstPage = visible.first;
 
   for (var i = 0, ii = visiblePages.length, stillFullyVisible = false;
        i < ii; ++i) {
@@ -2177,21 +2799,23 @@ function updateViewarea() {
   var pdfOpenParams = '#page=' + pageNumber;
   pdfOpenParams += '&zoom=' + normalizedScaleValue;
   var currentPage = PDFView.pages[pageNumber - 1];
   var topLeft = currentPage.getPagePoint(PDFView.container.scrollLeft,
     (PDFView.container.scrollTop - firstPage.y));
   pdfOpenParams += ',' + Math.round(topLeft[0]) + ',' + Math.round(topLeft[1]);
 
   var store = PDFView.store;
-  store.set('exists', true);
-  store.set('page', pageNumber);
-  store.set('zoom', normalizedScaleValue);
-  store.set('scrollLeft', Math.round(topLeft[0]));
-  store.set('scrollTop', Math.round(topLeft[1]));
+  store.initializedPromise.then(function() {
+    store.set('exists', true);
+    store.set('page', pageNumber);
+    store.set('zoom', normalizedScaleValue);
+    store.set('scrollLeft', Math.round(topLeft[0]));
+    store.set('scrollTop', Math.round(topLeft[1]));
+  });
   var href = PDFView.getAnchorUrl(pdfOpenParams);
   document.getElementById('viewBookmark').href = href;
 }
 
 window.addEventListener('resize', function webViewerResize(evt) {
   if (PDFView.initialized &&
       (document.getElementById('pageWidthOption').selected ||
       document.getElementById('pageFitOption').selected ||
@@ -2296,30 +2920,54 @@ window.addEventListener('pagechange', fu
 window.addEventListener('DOMMouseScroll', function(evt) {
   if (evt.ctrlKey) {
     evt.preventDefault();
 
     var ticks = evt.detail;
     var direction = (ticks > 0) ? 'zoomOut' : 'zoomIn';
     for (var i = 0, length = Math.abs(ticks); i < length; i++)
       PDFView[direction]();
+  } else if (PDFView.isFullscreen) {
+    var FIREFOX_DELTA_FACTOR = -40;
+    PDFView.mouseScroll(evt.detail * FIREFOX_DELTA_FACTOR);
+  }
+}, false);
+
+window.addEventListener('mousemove', function keydown(evt) {
+  if (PDFView.isFullscreen) {
+    PDFView.showPresentationControls();
+  }
+}, false);
+
+window.addEventListener('mousedown', function mousedown(evt) {
+  if (PDFView.isFullscreen && evt.button === 0) {
+    // Mouse click in fullmode advances a page
+    evt.preventDefault();
+
+    PDFView.page++;
   }
 }, false);
 
 window.addEventListener('keydown', function keydown(evt) {
   var handled = false;
   var cmd = (evt.ctrlKey ? 1 : 0) |
             (evt.altKey ? 2 : 0) |
             (evt.shiftKey ? 4 : 0) |
             (evt.metaKey ? 8 : 0);
 
   // First, handle the key bindings that are independent whether an input
   // control is selected or not.
   if (cmd == 1 || cmd == 8) { // either CTRL or META key.
     switch (evt.keyCode) {
+      case 70:
+        if (!PDFView.supportsIntegratedFind) {
+          PDFFindBar.toggle();
+          handled = true;
+        }
+        break;
       case 61: // FF/Mac '='
       case 107: // FF '+' and '='
       case 187: // Chrome '+'
         PDFView.zoomIn();
         handled = true;
         break;
       case 173: // FF/Mac '-'
       case 109: // FF '-'
@@ -2329,51 +2977,85 @@ window.addEventListener('keydown', funct
         break;
       case 48: // '0'
         PDFView.parseScale(kDefaultScale, true);
         handled = true;
         break;
     }
   }
 
+  // CTRL or META with or without SHIFT.
+  if (cmd == 1 || cmd == 8 || cmd == 5 || cmd == 12) {
+    switch (evt.keyCode) {
+      case 71: // g
+        if (!PDFView.supportsIntegratedFind) {
+          PDFFindBar.dispatchEvent('again', cmd == 5 || cmd == 12);
+          handled = true;
+        }
+        break;
+    }
+  }
+
   if (handled) {
     evt.preventDefault();
     return;
   }
 
   // Some shortcuts should not get handled if a control/input element
   // is selected.
   var curElement = document.activeElement;
-  if (curElement && curElement.tagName == 'INPUT')
+  if (curElement && (curElement.tagName == 'INPUT' ||
+                     curElement.tagName == 'SELECT')) {
     return;
-  var controlsElement = document.getElementById('controls');
+  }
+  var controlsElement = document.getElementById('toolbar');
   while (curElement) {
     if (curElement === controlsElement && !PDFView.isFullscreen)
-      return; // ignoring if the 'controls' element is focused
+      return; // ignoring if the 'toolbar' element is focused
     curElement = curElement.parentNode;
   }
 
   if (cmd == 0) { // no control key pressed at all.
     switch (evt.keyCode) {
+      case 38: // up arrow
+      case 33: // pg up
+      case 8: // backspace
+        if (!PDFView.isFullscreen) {
+          break;
+        }
+        //  in fullscreen mode falls throw here
       case 37: // left arrow
       case 75: // 'k'
       case 80: // 'p'
         PDFView.page--;
         handled = true;
         break;
+      case 40: // down arrow
+      case 34: // pg down
+      case 32: // spacebar
+        if (!PDFView.isFullscreen) {
+          break;
+        }
+        //  in fullscreen mode falls throw here
       case 39: // right arrow
       case 74: // 'j'
       case 78: // 'n'
         PDFView.page++;
         handled = true;
         break;
 
-      case 32: // spacebar
+      case 36: // home
         if (PDFView.isFullscreen) {
-          PDFView.page++;
+          PDFView.page = 1;
+          handled = true;
+        }
+        break;
+      case 35: // end
+        if (PDFView.isFullscreen) {
+          PDFView.page = PDFView.pdfDocument.numPages;
           handled = true;
         }
         break;
 
       case 82: // 'r'
         PDFView.rotatePages(90);
         break;
     }
@@ -2384,16 +3066,17 @@ window.addEventListener('keydown', funct
       case 82: // 'r'
         PDFView.rotatePages(-90);
         break;
     }
   }
 
   if (handled) {
     evt.preventDefault();
+    PDFView.clearMouseScrollState();
   }
 });
 
 window.addEventListener('beforeprint', function beforePrint(evt) {
   PDFView.beforePrint();
 });
 
 window.addEventListener('afterprint', function afterPrint(evt) {
--- a/browser/extensions/pdfjs/extension-files
+++ b/browser/extensions/pdfjs/extension-files
@@ -1,16 +1,21 @@
 chrome.manifest
 components/PdfStreamConverter.js
 content/PdfJs.jsm
 content/web/debugger.js
 content/web/images/annotation-check.svg
 content/web/images/annotation-comment.svg
 content/web/images/annotation-text.svg
+content/web/images/findbarButton-next-rtl.png
+content/web/images/findbarButton-next.png
+content/web/images/findbarButton-previous-rtl.png
+content/web/images/findbarButton-previous.png
 content/web/images/loading-icon.gif
+content/web/images/loading-small.png
 content/web/images/texture.png
 content/web/images/toolbarButton-bookmark.png
 content/web/images/toolbarButton-download.png
 content/web/images/toolbarButton-fullscreen.png
 content/web/images/toolbarButton-menuArrows.png
 content/web/images/toolbarButton-openFile.png
 content/web/images/toolbarButton-pageDown-rtl.png
 content/web/images/toolbarButton-pageDown.png
--- a/browser/locales/en-US/pdfviewer/chrome.properties
+++ b/browser/locales/en-US/pdfviewer/chrome.properties
@@ -1,8 +1,4 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
 # Chrome notification bar messages and buttons
 unsupported_feature=This PDF document might not be displayed correctly.
 open_with_different_viewer=Open With Different Viewer
 open_with_different_viewer.accessKey=o
--- a/browser/locales/en-US/pdfviewer/viewer.properties
+++ b/browser/locales/en-US/pdfviewer/viewer.properties
@@ -1,12 +1,8 @@
-# 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/.
-
 # Main toolbar buttons (tooltips and alt text for images)
 previous.title=Previous Page
 previous_label=Previous
 next.title=Next Page
 next_label=Next
 
 # LOCALIZATION NOTE (page_label, page_of):
 # These strings are concatenated to form the "Page: X of Y" string.
@@ -35,37 +31,47 @@ bookmark_label=Current View
 # (the _label strings are alt text for the buttons, the .title strings are
 # tooltips)
 toggle_slider.title=Toggle Slider
 toggle_slider_label=Toggle Slider
 outline.title=Show Document Outline
 outline_label=Document Outline
 thumbs.title=Show Thumbnails
 thumbs_label=Thumbnails
-search_panel.title=Search Document
-search_panel_label=Search
+findbar.title=Find in Document
+findbar_label=Find
 
 # Document outline messages
 no_outline=No Outline Available
 
 # Thumbnails panel item (tooltip and alt text for images)
 # LOCALIZATION NOTE (thumb_page_title): "{{page}}" will be replaced by the page
 # number.
 thumb_page_title=Page {{page}}
 # LOCALIZATION NOTE (thumb_page_canvas): "{{page}}" will be replaced by the page
 # number.
 thumb_page_canvas=Thumbnail of Page {{page}}
 
 # Context menu
+first_page.label=Go to First Page
+last_page.label=Go to Last Page
 page_rotate_cw.label=Rotate Clockwise
-page_rotate_ccw.label=Rotate Counter-Clockwise
+page_rotate_ccw.label=Rotate Counterclockwise
 
-# Search panel button title and messages
-search=Find
-search_terms_not_found=(Not found)
+# Find panel button title and messages
+find_label=Find:
+find_previous.title=Find the previous occurrence of the phrase
+find_previous_label=Previous
+find_next.title=Find the next occurrence of the phrase
+find_next_label=Next
+find_highlight=Highlight all
+find_match_case_label=Match case
+find_wrapped_to_bottom=Reached end of page, continued from bottom
+find_wrapped_to_top=Reached end of page, continued from top
+find_not_found=Phrase not found
 
 # Error panel labels
 error_more_info=More Information
 error_less_info=Less Information
 error_close=Close
 # LOCALIZATION NOTE (error_build): "{{build}}" will be replaced by the PDF.JS
 # build ID.
 error_build=PDF.JS Build: {{build}}
--- a/build/macosx/universal/mozconfig.common
+++ b/build/macosx/universal/mozconfig.common
@@ -1,19 +1,19 @@
 # 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/.
 
 mk_add_options MOZ_UNIFY_BDATE=1
 
 mk_add_options MOZ_POSTFLIGHT_ALL+=build/macosx/universal/flight.mk
 
-# Note, the version (10) is used by libffi's configure.
-ac_add_app_options i386 --target=i386-apple-darwin10
-ac_add_app_options x86_64 --target=x86_64-apple-darwin10
+DARWIN_VERSION=`uname -r`
+ac_add_app_options i386 --target=i386-apple-darwin$DARWIN_VERSION
+ac_add_app_options x86_64 --target=x86_64-apple-darwin$DARWIN_VERSION
 
 ac_add_options --with-macos-sdk=/Developer/SDKs/MacOSX10.6.sdk
 
 . $topsrcdir/build/macosx/mozconfig.common
 
 # $MOZ_BUILD_APP is only defined when sourced by configure.  That's not a
 # problem, because the variables it affects only need to be set for
 # configure.
--- a/config/system-headers
+++ b/config/system-headers
@@ -476,16 +476,17 @@ mach/mach_interface.h
 mach/mach_port.h
 mach-o/dyld.h
 MacLocales.h
 MacMemory.h
 MacTCP.h
 MacTypes.h
 MacWindows.h
 malloc.h
+malloc_np.h
 map
 mapicode.h
 mapidefs.h
 mapiguid.h
 mapi.h
 mapitags.h
 mapiutil.h
 mapix.h
--- a/configure.in
+++ b/configure.in
@@ -3763,31 +3763,32 @@ if test -n "$MOZ_LINKER" -a -z "$MOZ_OLD
     dnl libraries there.
     DSO_LDOPTS="$DSO_LDOPTS -nostartfiles"
     NSPR_LDFLAGS=-nostartfiles
   fi
 fi
 
 dnl Check for the existence of various allocation headers/functions
 
+MALLOC_HEADERS="malloc.h malloc_np.h malloc/malloc.h sys/malloc.h"
 MALLOC_H=
-MOZ_CHECK_HEADER(malloc.h,        [MALLOC_H=malloc.h])
-if test "$MALLOC_H" = ""; then
-  MOZ_CHECK_HEADER(malloc/malloc.h, [MALLOC_H=malloc/malloc.h])
-  if test "$MALLOC_H" = ""; then
-    MOZ_CHECK_HEADER(sys/malloc.h,    [MALLOC_H=sys/malloc.h])
+
+for file in $MALLOC_HEADERS; do
+  MOZ_CHECK_HEADER($file, [MALLOC_H=$file])
+  if test "$MALLOC_H" != ""; then
+    AC_DEFINE_UNQUOTED(MALLOC_H, <$MALLOC_H>)
+    break
   fi
-fi
-if test "$MALLOC_H" != ""; then
-   AC_DEFINE_UNQUOTED(MALLOC_H, <$MALLOC_H>)
-fi
+done
 
 MOZ_ALLOCATING_FUNCS="strndup posix_memalign memalign valloc"
 AC_CHECK_FUNCS(strndup posix_memalign memalign valloc)
 
+AC_CHECK_FUNCS(malloc_usable_size)
+
 dnl See if compiler supports some gcc-style attributes
 
 AC_CACHE_CHECK(for __attribute__((always_inline)),
                ac_cv_attribute_always_inline,
                [AC_TRY_COMPILE([inline void f(void) __attribute__((always_inline));],
                                [],
                                ac_cv_attribute_always_inline=yes,
                                ac_cv_attribute_always_inline=no)])
@@ -7020,16 +7021,28 @@ else
     MOZ_GLUE_PROGRAM_LDFLAGS="$MOZ_GLUE_PROGRAM_LDFLAGS -rdynamic"
   fi
   if test "$MOZ_LINKER" = 1; then
     MOZ_GLUE_PROGRAM_LDFLAGS="$MOZ_GLUE_PROGRAM_LDFLAGS $ZLIB_LIBS"
   fi
 fi
 
 if test -z "$MOZ_MEMORY"; then
+  if test -n "$MOZ_JEMALLOC"; then
+    MOZ_NATIVE_JEMALLOC=1
+    AC_CHECK_FUNCS(mallctl nallocm,,
+      [MOZ_NATIVE_JEMALLOC=
+       break])
+    if test -n "$MOZ_NATIVE_JEMALLOC"; then
+      MOZ_MEMORY=1
+      AC_DEFINE(MOZ_MEMORY)
+      AC_DEFINE(MOZ_JEMALLOC)
+      AC_DEFINE(MOZ_NATIVE_JEMALLOC)
+    fi
+  fi
   case "${target}" in
     *-mingw*)
       if test -z "$WIN32_REDIST_DIR" -a -z "$MOZ_DEBUG"; then
         AC_MSG_WARN([When not building jemalloc, you need to set WIN32_REDIST_DIR to the path to the Visual C++ Redist (usually VCINSTALLDIR\redist\x86\Microsoft.VC80.CRT, for VC++ v8) if you intend to distribute your build.])
       fi
       ;;
   esac
 else
@@ -7114,16 +7127,17 @@ else
     ;;
   *)
     AC_MSG_ERROR([--enable-jemalloc not supported on ${target}])
     ;;
   esac
 fi # MOZ_MEMORY
 AC_SUBST(MOZ_MEMORY)
 AC_SUBST(MOZ_JEMALLOC)
+AC_SUBST(MOZ_NATIVE_JEMALLOC)
 AC_SUBST(MOZ_GLUE_LDFLAGS)
 AC_SUBST(MOZ_GLUE_PROGRAM_LDFLAGS)
 AC_SUBST(WIN32_CRT_LIBS)
 dnl Need to set this for make because NSS doesn't have configure
 AC_SUBST(DLLFLAGS)
 
 dnl We need to wrap dlopen and related functions on Android because we use
 dnl our own linker.
@@ -8947,20 +8961,32 @@ MOZ_CRASHREPORTER=${MOZ_CRASHREPORTER} \
 if cmp -s ./mozinfo.json.tmp ./mozinfo.json; then
   rm ./mozinfo.json.tmp
 else
   mv -f ./mozinfo.json.tmp ./mozinfo.json
 fi
 
 # Run jemalloc configure script
 
-if test "$MOZ_JEMALLOC" -a "$MOZ_MEMORY"; then
+if test -z "$MOZ_NATIVE_JEMALLOC" -a "$MOZ_JEMALLOC" -a "$MOZ_MEMORY" ; then
   ac_configure_args="$_SUBDIR_CONFIG_ARGS --build=$build --host=$target --enable-stats --with-jemalloc-prefix=je_"
-  if test "$OS_ARCH" = "Linux"; then
-    MANGLE="malloc calloc valloc free realloc memalign posix_memalign malloc_usable_size"
+  case "$OS_ARCH" in
+    Linux|DragonFly|FreeBSD|NetBSD|OpenBSD)
+      MANGLE="malloc calloc valloc free realloc posix_memalign"
+      case "$OS_ARCH" in
+        Linux)
+          MANGLE="$MANGLE memalign malloc_usable_size"
+          ;;
+        FreeBSD)
+          MANGLE="$MANGLE malloc_usable_size"
+          ;;
+      esac
+      ;;
+  esac
+  if test -n "$MANGLE"; then
     MANGLED=
     JEMALLOC_WRAPPER=
     if test -n "$_WRAP_MALLOC"; then
       JEMALLOC_WRAPPER=__wrap_
     fi
     for mangle in ${MANGLE}; do
       if test -n "$MANGLED"; then
         MANGLED="$mangle:$JEMALLOC_WRAPPER$mangle,$MANGLED"
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -10060,16 +10060,33 @@ nsDocument::DocSizeOfExcludingThis(nsWin
     }
 
     *p += nodeSize;
   }
 
   aWindowSizes->mStyleSheets +=
     mStyleSheets.SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
                                      aWindowSizes->mMallocSizeOf);
+  aWindowSizes->mStyleSheets +=
+    mCatalogSheets.SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
+                                       aWindowSizes->mMallocSizeOf);
+  aWindowSizes->mStyleSheets +=
+    mAdditionalSheets[eAgentSheet].
+      SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
+                          aWindowSizes->mMallocSizeOf);
+  aWindowSizes->mStyleSheets +=
+    mAdditionalSheets[eUserSheet].
+      SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
+                          aWindowSizes->mMallocSizeOf);
+  // Lumping in the loader with the style-sheets size is not ideal,
+  // but most of the things in there are in fact stylesheets, so it
+  // doesn't seem worthwhile to separate it out.
+  aWindowSizes->mStyleSheets +=
+    CSSLoader()->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf);
+
   aWindowSizes->mDOMOther +=
     mAttrStyleSheet ?
     mAttrStyleSheet->DOMSizeOfIncludingThis(aWindowSizes->mMallocSizeOf) :
     0;
 
   aWindowSizes->mDOMOther +=
     mStyledLinks.SizeOfExcludingThis(NULL, aWindowSizes->mMallocSizeOf);
 
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -2599,18 +2599,16 @@ CanvasRenderingContext2D::SetFont(const 
 
   nsIPresShell* presShell = GetPresShell();
   if (!presShell) {
     error.Throw(NS_ERROR_FAILURE);
     return;
   }
   nsIDocument* document = presShell->GetDocument();
 
-  nsCOMArray<nsIStyleRule> rules;
-
   nsRefPtr<css::StyleRule> rule;
   error = CreateFontStyleRule(font, document, getter_AddRefs(rule));
 
   if (error.Failed()) {
     return;
   }
 
   css::Declaration *declaration = rule->GetDeclaration();
@@ -2624,17 +2622,18 @@ CanvasRenderingContext2D::SetFont(const 
     declaration->GetNormalBlock()->ValueFor(eCSSProperty_font_size_adjust);
   if (!fsaVal || (fsaVal->GetUnit() != eCSSUnit_None &&
                   fsaVal->GetUnit() != eCSSUnit_System_Font)) {
       // We got an all-property value or a syntax error.  The spec says
       // this value must be ignored.
     return;
   }
 
-  rules.AppendObject(rule);
+  nsTArray< nsCOMPtr<nsIStyleRule> > rules;
+  rules.AppendElement(rule);
 
   nsStyleSet* styleSet = presShell->StyleSet();
 
   // have to get a parent style context for inherit-like relative
   // values (2em, bolder, etc.)
   nsRefPtr<nsStyleContext> parentContext;
 
   if (mCanvasElement && mCanvasElement->IsInDoc()) {
@@ -2649,18 +2648,18 @@ CanvasRenderingContext2D::SetFont(const 
     error = CreateFontStyleRule(NS_LITERAL_STRING("10px sans-serif"),
                                 document,
                                 getter_AddRefs(parentRule));
 
     if (error.Failed()) {
       return;
     }
 
-    nsCOMArray<nsIStyleRule> parentRules;
-    parentRules.AppendObject(parentRule);
+    nsTArray< nsCOMPtr<nsIStyleRule> > parentRules;
+    parentRules.AppendElement(parentRule);
     parentContext = styleSet->ResolveStyleForRules(nullptr, parentRules);
   }
 
   if (!parentContext) {
     error.Throw(NS_ERROR_FAILURE);
     return;
   }
 
--- a/content/canvas/src/Makefile.in
+++ b/content/canvas/src/Makefile.in
@@ -48,16 +48,17 @@ CPPSRCS += \
 	WebGLContextUtils.cpp \
 	WebGLContextReporter.cpp \
 	WebGLContextValidate.cpp \
 	WebGLElementArrayCache.cpp \
 	WebGLExtensionBase.cpp \
 	WebGLExtensionCompressedTextureATC.cpp \
 	WebGLExtensionCompressedTexturePVRTC.cpp \
 	WebGLExtensionCompressedTextureS3TC.cpp \
+	WebGLExtensionDebugRendererInfo.cpp \
 	WebGLExtensionDepthTexture.cpp \
 	WebGLExtensionLoseContext.cpp \
 	WebGLExtensionStandardDerivatives.cpp \
 	WebGLExtensionTextureFilterAnisotropic.cpp \
 	WebGLExtensionTextureFloat.cpp \
 	WebGLFramebuffer.cpp \
 	WebGLProgram.cpp \
 	WebGLRenderbuffer.cpp \
@@ -66,16 +67,20 @@ CPPSRCS += \
 	WebGLTexelConversions.cpp \
 	WebGLTexture.cpp \
 	WebGLUniformLocation.cpp \
 	$(NULL)
 
 DEFINES += -DUSE_ANGLE
 USE_ANGLE=1
 
+LOCAL_INCLUDES += \
+	-I$(topsrcdir)/js/xpconnect/wrappers \
+	$(NULL)
+
 else
 
 CPPSRCS += WebGLContextNotSupported.cpp
 
 endif
 
 # we don't want the shared lib, but we want to force the creation of a static lib.
 FORCE_STATIC_LIB = 1
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -2,16 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLContext.h"
 #include "WebGLExtensions.h"
 #include "WebGLContextUtils.h"
 
+#include "AccessCheck.h"
 #include "nsIConsoleService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIClassInfoImpl.h"
 #include "nsContentUtils.h"
 #include "nsIXPConnect.h"
 #include "nsError.h"
 #include "nsIGfxInfo.h"
 
@@ -943,17 +944,17 @@ WebGLContext::MozGetUnderlyingParamStrin
 
     default:
         return NS_ERROR_INVALID_ARG;
     }
 
     return NS_OK;
 }
 
-bool WebGLContext::IsExtensionSupported(WebGLExtensionID ext) const
+bool WebGLContext::IsExtensionSupported(JSContext *cx, WebGLExtensionID ext) const
 {
     if (mDisableExtensions) {
         return false;
     }
 
     switch (ext) {
         case OES_standard_derivatives:
         case WEBGL_lose_context:
@@ -993,16 +994,18 @@ bool WebGLContext::IsExtensionSupported(
                      gl->IsExtensionSupported(GLContext::EXT_packed_depth_stencil))
             {
                 return true;
             }
             else
             {
                 return false;
             }
+        case WEBGL_debug_renderer_info:
+            return xpc::AccessCheck::isChrome(js::GetContextCompartment(cx));
         default:
             MOZ_ASSERT(false, "should not get there.");
     }
 
     MOZ_ASSERT(false, "should not get there.");
     return false;
 }
 
@@ -1046,27 +1049,31 @@ WebGLContext::GetExtension(JSContext *cx
     else if (CompareWebGLExtensionName(name, "MOZ_WEBGL_compressed_texture_atc"))
     {
         ext = WEBGL_compressed_texture_atc;
     }
     else if (CompareWebGLExtensionName(name, "MOZ_WEBGL_compressed_texture_pvrtc"))
     {
         ext = WEBGL_compressed_texture_pvrtc;
     }
+    else if (CompareWebGLExtensionName(name, "WEBGL_debug_renderer_info"))
+    {
+        ext = WEBGL_debug_renderer_info;
+    }
     else if (CompareWebGLExtensionName(name, "MOZ_WEBGL_depth_texture"))
     {
         ext = WEBGL_depth_texture;
     }
 
     if (ext == WebGLExtensionID_unknown_extension) {
       return nullptr;
     }
 
     // step 2: check if the extension is supported
-    if (!IsExtensionSupported(ext)) {
+    if (!IsExtensionSupported(cx, ext)) {
         return nullptr;
     }
 
     // step 3: if the extension hadn't been previously been created, create it now, thus enabling it
     if (!IsExtensionEnabled(ext)) {
         WebGLExtensionBase *obj = nullptr;
         switch (ext) {
             case OES_standard_derivatives:
@@ -1082,16 +1089,19 @@ WebGLContext::GetExtension(JSContext *cx
                 obj = new WebGLExtensionCompressedTextureS3TC(this);
                 break;
             case WEBGL_compressed_texture_atc:
                 obj = new WebGLExtensionCompressedTextureATC(this);
                 break;
             case WEBGL_compressed_texture_pvrtc:
                 obj = new WebGLExtensionCompressedTexturePVRTC(this);
                 break;
+            case WEBGL_debug_renderer_info:
+                obj = new WebGLExtensionDebugRendererInfo(this);
+                break;
             case WEBGL_depth_texture:
                 obj = new WebGLExtensionDepthTexture(this);
                 break;
             case OES_texture_float:
                 obj = new WebGLExtensionTextureFloat(this);
                 break;
             default:
                 MOZ_ASSERT(false, "should not get there.");
@@ -1347,39 +1357,41 @@ WebGLContext::ForceLoseContext()
 
 void
 WebGLContext::ForceRestoreContext()
 {
     mContextStatus = ContextLostAwaitingRestore;
 }
 
 void
-WebGLContext::GetSupportedExtensions(Nullable< nsTArray<nsString> > &retval)
+WebGLContext::GetSupportedExtensions(JSContext *cx, Nullable< nsTArray<nsString> > &retval)
 {
     retval.SetNull();
     if (!IsContextStable())
         return;
 
     nsTArray<nsString>& arr = retval.SetValue();
 
-    if (IsExtensionSupported(OES_texture_float))
+    if (IsExtensionSupported(cx, OES_texture_float))
         arr.AppendElement(NS_LITERAL_STRING("OES_texture_float"));
-    if (IsExtensionSupported(OES_standard_derivatives))
+    if (IsExtensionSupported(cx, OES_standard_derivatives))
         arr.AppendElement(NS_LITERAL_STRING("OES_standard_derivatives"));
-    if (IsExtensionSupported(EXT_texture_filter_anisotropic))
+    if (IsExtensionSupported(cx, EXT_texture_filter_anisotropic))
         arr.AppendElement(NS_LITERAL_STRING("EXT_texture_filter_anisotropic"));
-    if (IsExtensionSupported(WEBGL_lose_context))
+    if (IsExtensionSupported(cx, WEBGL_lose_context))
         arr.AppendElement(NS_LITERAL_STRING("MOZ_WEBGL_lose_context"));
-    if (IsExtensionSupported(WEBGL_compressed_texture_s3tc))
+    if (IsExtensionSupported(cx, WEBGL_compressed_texture_s3tc))
         arr.AppendElement(NS_LITERAL_STRING("MOZ_WEBGL_compressed_texture_s3tc"));
-    if (IsExtensionSupported(WEBGL_compressed_texture_atc))
+    if (IsExtensionSupported(cx, WEBGL_compressed_texture_atc))
         arr.AppendElement(NS_LITERAL_STRING("MOZ_WEBGL_compressed_texture_atc"));
-    if (IsExtensionSupported(WEBGL_compressed_texture_pvrtc))
+    if (IsExtensionSupported(cx, WEBGL_compressed_texture_pvrtc))
         arr.AppendElement(NS_LITERAL_STRING("MOZ_WEBGL_compressed_texture_pvrtc"));
-    if (IsExtensionSupported(WEBGL_depth_texture))
+    if (IsExtensionSupported(cx, WEBGL_debug_renderer_info))
+        arr.AppendElement(NS_LITERAL_STRING("WEBGL_debug_renderer_info"));
+    if (IsExtensionSupported(cx, WEBGL_depth_texture))
         arr.AppendElement(NS_LITERAL_STRING("MOZ_WEBGL_depth_texture"));
 }
 
 //
 // XPCOM goop
 //
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(WebGLContext)
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -482,17 +482,19 @@ class WebGLContext :
     friend class WebGLExtensionCompressedTexturePVRTC;
     friend class WebGLExtensionDepthTexture;
 
     enum {
         UNPACK_FLIP_Y_WEBGL = 0x9240,
         UNPACK_PREMULTIPLY_ALPHA_WEBGL = 0x9241,
         CONTEXT_LOST_WEBGL = 0x9242,
         UNPACK_COLORSPACE_CONVERSION_WEBGL = 0x9243,
-        BROWSER_DEFAULT_WEBGL = 0x9244
+        BROWSER_DEFAULT_WEBGL = 0x9244,
+        UNMASKED_VENDOR_WEBGL = 0x9245,
+        UNMASKED_RENDERER_WEBGL = 0x9246
     };
 
 public:
     WebGLContext();
     virtual ~WebGLContext();
 
     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
@@ -637,18 +639,18 @@ public:
     WebGLsizei DrawingBufferHeight() const {
         if (!IsContextStable())
             return 0;
         return mHeight;
     }
         
     JSObject *GetContextAttributes(ErrorResult &rv);
     bool IsContextLost() const { return !IsContextStable(); }
-    void GetSupportedExtensions(dom::Nullable< nsTArray<nsString> > &retval);
-    JSObject* GetExtension(JSContext* ctx, const nsAString& aName, ErrorResult& rv);
+    void GetSupportedExtensions(JSContext *cx, dom::Nullable< nsTArray<nsString> > &retval);
+    JSObject* GetExtension(JSContext* cx, const nsAString& aName, ErrorResult& rv);
     void ActiveTexture(WebGLenum texture);
     void AttachShader(WebGLProgram* program, WebGLShader* shader);
     void BindAttribLocation(WebGLProgram* program, WebGLuint location,
                             const nsAString& name);
     void BindBuffer(WebGLenum target, WebGLBuffer* buf);
     void BindFramebuffer(WebGLenum target, WebGLFramebuffer* wfb);
     void BindRenderbuffer(WebGLenum target, WebGLRenderbuffer* wrb);
     void BindTexture(WebGLenum target, WebGLTexture *tex);
@@ -1182,33 +1184,34 @@ protected:
         // The context is lost, an event has been sent to the script, and the
         // script correctly handled the event. We are waiting for the context to
         // be restored.
         ContextLostAwaitingRestore
     };
 
     // extensions
     enum WebGLExtensionID {
-        OES_texture_float,
+        EXT_texture_filter_anisotropic,
         OES_standard_derivatives,
-        EXT_texture_filter_anisotropic,
-        WEBGL_lose_context,
-        WEBGL_compressed_texture_s3tc,
+        OES_texture_float,
         WEBGL_compressed_texture_atc,
         WEBGL_compressed_texture_pvrtc,
+        WEBGL_compressed_texture_s3tc,
+        WEBGL_debug_renderer_info,
         WEBGL_depth_texture,
+        WEBGL_lose_context,
         WebGLExtensionID_unknown_extension
     };
     nsTArray<nsRefPtr<WebGLExtensionBase> > mExtensions;
 
     // returns true if the extension has been enabled by calling getExtension.
     bool IsExtensionEnabled(WebGLExtensionID ext) const;
 
-    // returns true if the extension is supported (as returned by getSupportedExtensions)
-    bool IsExtensionSupported(WebGLExtensionID ext) const;
+    // returns true if the extension is supported for this JSContext (this decides what getSupportedExtensions exposes)
+    bool IsExtensionSupported(JSContext *cx, WebGLExtensionID ext) const;
 
     nsTArray<WebGLenum> mCompressedTextureFormats;
 
     bool InitAndValidateGL();
     bool ValidateBuffers(int32_t *maxAllowedCount, const char *info);
     bool ValidateCapabilityEnum(WebGLenum cap, const char *info);
     bool ValidateBlendEquationEnum(WebGLenum cap, const char *info);
     bool ValidateBlendFuncDstEnum(WebGLenum mode, const char *info);
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -1897,16 +1897,36 @@ WebGLContext::GetParameter(JSContext* cx
             return StringValue(cx, "Mozilla", rv);
         case LOCAL_GL_RENDERER:
             return StringValue(cx, "Mozilla", rv);
         case LOCAL_GL_VERSION:
             return StringValue(cx, "WebGL 1.0", rv);
         case LOCAL_GL_SHADING_LANGUAGE_VERSION:
             return StringValue(cx, "WebGL GLSL ES 1.0", rv);
 
+        // Privileged string params exposed by WEBGL_debug_renderer_info:
+        case UNMASKED_VENDOR_WEBGL:
+        case UNMASKED_RENDERER_WEBGL:
+        {
+            // The privilege check is done in WebGLContext::IsExtensionSupported.
+            // So here we just have to check that the extension is enabled.
+            if (!IsExtensionEnabled(WEBGL_debug_renderer_info)) {
+                ErrorInvalidEnumInfo("getParameter: parameter", pname);
+                return JS::NullValue();
+            }
+            GLenum glstringname = LOCAL_GL_NONE;
+            if (pname == UNMASKED_VENDOR_WEBGL) {
+                glstringname = LOCAL_GL_VENDOR;
+            } else if (pname == UNMASKED_RENDERER_WEBGL) {
+                glstringname = LOCAL_GL_RENDERER;
+            }
+            const char* string = reinterpret_cast<const char*>(gl->fGetString(glstringname));
+            return StringValue(cx, string, rv);
+        }
+
         //
         // Single-value params
         //
 
         // unsigned int
         case LOCAL_GL_CULL_FACE_MODE:
         case LOCAL_GL_FRONT_FACE:
         case LOCAL_GL_ACTIVE_TEXTURE:
@@ -2006,17 +2026,17 @@ WebGLContext::GetParameter(JSContext* cx
 
 // float
         case LOCAL_GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT:
             if (IsExtensionEnabled(EXT_texture_filter_anisotropic)) {
                 GLfloat f = 0.f;
                 gl->fGetFloatv(pname, &f);
                 return JS::DoubleValue(f);
             } else {
-                ErrorInvalidEnum("getParameter: parameter", pname);
+                ErrorInvalidEnumInfo("getParameter: parameter", pname);
                 return JS::NullValue();
             }
         case LOCAL_GL_DEPTH_CLEAR_VALUE:
         case LOCAL_GL_LINE_WIDTH:
         case LOCAL_GL_POLYGON_OFFSET_FACTOR:
         case LOCAL_GL_POLYGON_OFFSET_UNITS:
         case LOCAL_GL_SAMPLE_COVERAGE_VALUE:
         {
new file mode 100644
--- /dev/null
+++ b/content/canvas/src/WebGLExtensionDebugRendererInfo.cpp
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebGLContext.h"
+#include "WebGLExtensions.h"
+#include "mozilla/dom/WebGLRenderingContextBinding.h"
+
+using namespace mozilla;
+
+WebGLExtensionDebugRendererInfo::WebGLExtensionDebugRendererInfo(WebGLContext* context)
+    : WebGLExtensionBase(context)
+{
+}
+
+WebGLExtensionDebugRendererInfo::~WebGLExtensionDebugRendererInfo()
+{
+}
+
+IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionDebugRendererInfo)
--- a/content/canvas/src/WebGLExtensions.h
+++ b/content/canvas/src/WebGLExtensions.h
@@ -61,16 +61,26 @@ class WebGLExtensionCompressedTextureS3T
 {
 public:
     WebGLExtensionCompressedTextureS3TC(WebGLContext*);
     virtual ~WebGLExtensionCompressedTextureS3TC();
 
     DECL_WEBGL_EXTENSION_GOOP
 };
 
+class WebGLExtensionDebugRendererInfo
+    : public WebGLExtensionBase
+{
+public:
+    WebGLExtensionDebugRendererInfo(WebGLContext*);
+    virtual ~WebGLExtensionDebugRendererInfo();
+
+    DECL_WEBGL_EXTENSION_GOOP
+};
+
 class WebGLExtensionDepthTexture
     : public WebGLExtensionBase
 {
 public:
     WebGLExtensionDepthTexture(WebGLContext*);
     virtual ~WebGLExtensionDepthTexture();
 
     DECL_WEBGL_EXTENSION_GOOP
--- a/content/canvas/test/Makefile.in
+++ b/content/canvas/test/Makefile.in
@@ -3,17 +3,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = @relativesrcdir@
-DIRS		+= webgl crossorigin
+DIRS		+= webgl crossorigin chrome
 
 # TEST_DIRS += compiled
 
 include $(DEPTH)/config/autoconf.mk
 MOCHITEST_FILES = \
 	test_canvas.html \
 	image_transparent50.png \
 	image_redtransparent.png \
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/chrome/Makefile.in
@@ -0,0 +1,19 @@
+#
+# 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/.
+
+DEPTH           = @DEPTH@
+topsrcdir       = @top_srcdir@
+srcdir          = @srcdir@
+VPATH           = @srcdir@
+relativesrcdir  = @relativesrcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MOCHITEST_CHROME_FILES = \
+	test_webgl_debug_renderer_info.html \
+	nonchrome_webgl_debug_renderer_info.html \
+	$(NULL)
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/chrome/nonchrome_webgl_debug_renderer_info.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<script>
+
+// This file has the portion of the test_webgl_renderer_info chrome mochitest
+// that has to run as non-chrome to check that this WebGL extension is not exposed to content
+
+// we can't call the chrome Mochitest ok() function ourselves from non-chrome code.
+// So we remote it to the chrome test.
+
+function ok(res, msg) {
+  // Note we post to ourselves as posting to the chrome code doesn't seem to work here.
+  // This works by having the chrome code put an event handler on our own window.
+  window.postMessage({ subTestFinished: true, result: res, message: msg }, "*");
+}
+
+function messageListener(e) {
+  // This is how the chrome test tells us to start running -- we have to wait for this
+  // message to avoid running before it's set up its event handler.
+  if (e.data == "run") {
+    run();
+  }
+}
+
+window.addEventListener("message", messageListener, true);
+
+function run() {
+  const UNMASKED_VENDOR_WEBGL = 0x9245;
+  const UNMASKED_RENDERER_WEBGL = 0x9246;
+
+  var canvas = document.createElement("canvas");
+  var gl = canvas.getContext("experimental-webgl");
+
+  ok(!gl.getError(), "getError on newly created WebGL context should return NO_ERROR");
+
+  ok(!gl.getParameter(UNMASKED_VENDOR_WEBGL) && gl.getError() == gl.INVALID_ENUM,
+      "Should not be able to query UNMASKED_VENDOR_WEBGL without having enabled the WEBGL_debug_renderer_info extension");
+  ok(!gl.getParameter(UNMASKED_RENDERER_WEBGL) && gl.getError() == gl.INVALID_ENUM,
+      "Should not be able to query UNMASKED_RENDERER_WEBGL without having enabled the WEBGL_debug_renderer_info extension");
+
+  var exts = gl.getSupportedExtensions();
+  ok(exts.indexOf("WEBGL_debug_renderer_info") == -1,
+      "WEBGL_debug_renderer_info should not be listed by getSupportedExtensions in non-chrome contexts");
+  var debugRendererInfoExtension = gl.getExtension("WEBGL_debug_renderer_info");
+  ok(!debugRendererInfoExtension,
+      "WEBGL_debug_renderer_info should not be available through getExtension in non-chrome contexts");
+
+  ok(!gl.getParameter(UNMASKED_VENDOR_WEBGL) && gl.getError() == gl.INVALID_ENUM,
+      "Should not be able to query UNMASKED_VENDOR_WEBGL if enabling WEBGL_debug_renderer_info failed");
+  ok(!gl.getParameter(UNMASKED_RENDERER_WEBGL) && gl.getError() == gl.INVALID_ENUM,
+      "Should not be able to query UNMASKED_RENDERER_WEBGL if enabling WEBGL_debug_renderer_info failed");
+
+  window.postMessage({allTestsFinished: true}, "*");
+}
+
+</script>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/chrome/test_webgl_debug_renderer_info.html
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=666446
+-->
+<head>
+  <title>Test for WEBGL_debug_renderer_info chrome-only extension</title>
+  <script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<pre id="test">
+<script>
+
+const UNMASKED_VENDOR_WEBGL = 0x9245;
+const UNMASKED_RENDERER_WEBGL = 0x9246;
+
+function isNonEmptyString(s)
+{
+  return s && (typeof s) == "string";
+}
+
+function messageListener(e) {
+  if (e.data.allTestsFinished) {
+    SimpleTest.finish();
+  } else if (e.data.subTestFinished) {
+    ok(e.data.result, "content iframe: " + e.data.message);
+  }
+}
+
+function checkChromeCase(canvas) {
+
+  var gl = canvas.getContext("experimental-webgl");
+  ok(!gl.getError(), "getError on newly created WebGL context should return NO_ERROR");
+
+  ok(!gl.getParameter(UNMASKED_VENDOR_WEBGL) && gl.getError() == gl.INVALID_ENUM,
+     "Should not be able to query UNMASKED_VENDOR_WEBGL without having enabled the WEBGL_debug_renderer_info extension");
+  ok(!gl.getParameter(UNMASKED_RENDERER_WEBGL) && gl.getError() == gl.INVALID_ENUM,
+     "Should not be able to query UNMASKED_RENDERER_WEBGL without having enabled the WEBGL_debug_renderer_info extension");
+
+  var exts = gl.getSupportedExtensions();
+  ok(exts.indexOf("WEBGL_debug_renderer_info") != -1,
+     "WEBGL_debug_renderer_info should be listed by getSupportedExtensions in chrome contexts");
+  var debugRendererInfoExtension = gl.getExtension("WEBGL_debug_renderer_info");
+  ok(debugRendererInfoExtension,
+     "WEBGL_debug_renderer_info should be available through getExtension in chrome contexts");
+
+  ok(debugRendererInfoExtension.UNMASKED_VENDOR_WEBGL == UNMASKED_VENDOR_WEBGL,
+     "UNMASKED_VENDOR_WEBGL has the correct value");
+  ok(debugRendererInfoExtension.UNMASKED_RENDERER_WEBGL == UNMASKED_RENDERER_WEBGL,
+     "UNMASKED_RENDERER_WEBGL has the correct value");
+
+  ok(isNonEmptyString(gl.getParameter(UNMASKED_VENDOR_WEBGL)) && gl.getError() == gl.NO_ERROR,
+     "Should be able to query UNMASKED_VENDOR_WEBGL in chrome context with WEBGL_debug_renderer_info enabled");
+  ok(isNonEmptyString(gl.getParameter(UNMASKED_RENDERER_WEBGL)) && gl.getError() == gl.NO_ERROR,
+     "Should be able to query UNMASKED_RENDERER_WEBGL in chrome context with WEBGL_debug_renderer_info enabled");
+}
+
+function main()
+{
+  SimpleTest.waitForExplicitFinish();
+
+  checkChromeCase(document.createElement("canvas"));
+
+  // Now run the non-chrome code to verify the security of this WebGL chrome-only extension.
+
+  var iframe = document.createElement("iframe");
+  iframe.src = "http://mochi.test:8888/chrome/content/canvas/test/chrome/nonchrome_webgl_debug_renderer_info.html";
+
+  iframe.onload = function () {
+
+    // test that chrome can get WEBGL_debug_renderer_info on a canvas on the iframe...
+    // this is useful to check in itself, and is also useful so the subsequent non-chrome test
+    // will also test that doing so doesn't confuse our chrome-only check.
+    checkChromeCase(iframe.contentDocument.createElement("canvas"));
+
+    iframe.contentWindow.addEventListener("message", messageListener, false);
+    iframe.contentWindow.postMessage("run", "*");
+  };
+
+  document.body.appendChild(iframe);
+}
+
+window.onload = main;
+</script>
+</pre>
+</body>
+</html>
--- a/content/canvas/test/webgl/conformance/buffers/00_test_list.txt
+++ b/content/canvas/test/webgl/conformance/buffers/00_test_list.txt
@@ -1,8 +1,7 @@
 buffer-bind-test.html
 buffer-data-array-buffer.html
 index-validation-copies-indices.html
 index-validation-crash-with-buffer-sub-data.html
 index-validation-verifies-too-many-indices.html
 index-validation-with-resized-buffer.html
-#index-validation.html # reenable me ASAP!
-
+index-validation.html
--- a/content/events/src/Makefile.in
+++ b/content/events/src/Makefile.in
@@ -56,16 +56,17 @@ CPPSRCS		= \
 		nsDOMSimpleGestureEvent.cpp \
 		nsDOMEventTargetHelper.cpp \
 		nsDOMScrollAreaEvent.cpp \
 		nsDOMTransitionEvent.cpp \
 		nsDOMAnimationEvent.cpp \
 		nsDOMTouchEvent.cpp \
 		nsDOMCompositionEvent.cpp \
 		DOMWheelEvent.cpp \
+		TextComposition.cpp \
 		$(NULL)
 
 ifdef MOZ_B2G_RIL
 CPPSRCS += \
     nsDOMWifiEvent.cpp \
     $(NULL)
 endif
 
new file mode 100644
--- /dev/null
+++ b/content/events/src/TextComposition.cpp
@@ -0,0 +1,234 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TextComposition.h"
+#include "nsContentEventHandler.h"
+#include "nsContentUtils.h"
+#include "nsEventDispatcher.h"
+#include "nsGUIEvent.h"
+#include "nsIMEStateManager.h"
+#include "nsIPresShell.h"
+#include "nsIWidget.h"
+#include "nsPresContext.h"
+
+namespace mozilla {
+
+/******************************************************************************
+ * TextComposition
+ ******************************************************************************/
+
+TextComposition::TextComposition(nsPresContext* aPresContext,
+                                 nsINode* aNode,
+                                 nsGUIEvent* aEvent) :
+  mPresContext(aPresContext), mNode(aNode),
+  // temporarily, we should assume that one native IME context is per native
+  // widget.
+  mNativeContext(aEvent->widget),
+  mIsSynthesizedForTests(
+    (aEvent->flags & NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT) != 0)
+{
+}
+
+TextComposition::TextComposition(const TextComposition& aOther)
+{
+  mNativeContext = aOther.mNativeContext;
+  mPresContext = aOther.mPresContext;
+  mNode = aOther.mNode;
+  mLastData = aOther.mLastData;
+  mIsSynthesizedForTests = aOther.mIsSynthesizedForTests;
+}
+
+bool
+TextComposition::MatchesNativeContext(nsIWidget* aWidget) const
+{
+  // temporarily, we should assume that one native IME context is per one
+  // native widget.
+  return mNativeContext == static_cast<void*>(aWidget);
+}
+
+bool
+TextComposition::MatchesEventTarget(nsPresContext* aPresContext,
+                                    nsINode* aNode) const
+{
+  return mPresContext == aPresContext && mNode == aNode;
+}
+
+void
+TextComposition::DispatchEvent(nsGUIEvent* aEvent,
+                               nsEventStatus* aStatus,
+                               nsDispatchingCallback* aCallBack)
+{
+  if (aEvent->message == NS_COMPOSITION_UPDATE) {
+    mLastData = static_cast<nsCompositionEvent*>(aEvent)->data;
+  }
+
+  nsEventDispatcher::Dispatch(mNode, mPresContext,
+                              aEvent, nullptr, aStatus, aCallBack);
+}
+
+void
+TextComposition::DispatchCompsotionEventRunnable(uint32_t aEventMessage,
+                                                 const nsAString& aData)
+{
+  nsContentUtils::AddScriptRunner(
+    new CompositionEventDispatcher(mPresContext, mNode,
+                                   aEventMessage, aData));
+}
+
+void
+TextComposition::SynthesizeCommit(bool aDiscard)
+{
+  // backup this instance and use it since this instance might be destroyed
+  // by nsIMEStateManager if this is managed by it.
+  TextComposition composition = *this;
+  nsAutoString data(aDiscard ? EmptyString() : composition.mLastData);
+  if (composition.mLastData != data) {
+    composition.DispatchCompsotionEventRunnable(NS_COMPOSITION_UPDATE, data);
+    composition.DispatchCompsotionEventRunnable(NS_TEXT_TEXT, data);
+  }
+  composition.DispatchCompsotionEventRunnable(NS_COMPOSITION_END, data);
+}
+
+nsresult
+TextComposition::NotifyIME(widget::NotificationToIME aNotification)
+{
+  NS_ENSURE_TRUE(mPresContext, NS_ERROR_NOT_AVAILABLE);
+  return nsIMEStateManager::NotifyIME(aNotification, mPresContext);
+}
+
+/******************************************************************************
+ * TextComposition::CompositionEventDispatcher
+ ******************************************************************************/
+
+TextComposition::CompositionEventDispatcher::CompositionEventDispatcher(
+                                               nsPresContext* aPresContext,
+                                               nsINode* aEventTarget,
+                                               uint32_t aEventMessage,
+                                               const nsAString& aData) :
+  mPresContext(aPresContext), mEventTarget(aEventTarget),
+  mEventMessage(aEventMessage), mData(aData)
+{
+  mWidget = mPresContext->GetNearestWidget();
+}
+
+NS_IMETHODIMP
+TextComposition::CompositionEventDispatcher::Run()
+{
+  if (!mPresContext->GetPresShell() ||
+      mPresContext->GetPresShell()->IsDestroying()) {
+    return NS_OK; // cannot dispatch any events anymore
+  }
+
+  nsEventStatus status = nsEventStatus_eIgnore;
+  switch (mEventMessage) {
+    case NS_COMPOSITION_START: {
+      nsCompositionEvent compStart(true, NS_COMPOSITION_START, mWidget);
+      nsQueryContentEvent selectedText(true, NS_QUERY_SELECTED_TEXT, mWidget);
+      nsContentEventHandler handler(mPresContext);
+      handler.OnQuerySelectedText(&selectedText);
+      NS_ASSERTION(selectedText.mSucceeded, "Failed to get selected text");
+      compStart.data = selectedText.mReply.mString;
+      nsIMEStateManager::DispatchCompositionEvent(mEventTarget, mPresContext,
+                                                  &compStart, &status, nullptr);
+      break;
+    }
+    case NS_COMPOSITION_UPDATE:
+    case NS_COMPOSITION_END: {
+      nsCompositionEvent compEvent(true, mEventMessage, mWidget);
+      compEvent.data = mData;
+      nsIMEStateManager::DispatchCompositionEvent(mEventTarget, mPresContext,
+                                                  &compEvent, &status, nullptr);
+      break;
+    }
+    case NS_TEXT_TEXT: {
+      nsTextEvent textEvent(true, NS_TEXT_TEXT, mWidget);
+      textEvent.theText = mData;
+      nsIMEStateManager::DispatchCompositionEvent(mEventTarget, mPresContext,
+                                                  &textEvent, &status, nullptr);
+      break;
+    }
+    default:
+      MOZ_NOT_REACHED("Unsupported event");
+      break;
+  }
+  return NS_OK;
+}
+
+/******************************************************************************
+ * TextCompositionArray
+ ******************************************************************************/
+
+TextCompositionArray::index_type
+TextCompositionArray::IndexOf(nsIWidget* aWidget)
+{
+  for (index_type i = Length(); i > 0; --i) {
+    if (ElementAt(i - 1).MatchesNativeContext(aWidget)) {
+      return i - 1;
+    }
+  }
+  return NoIndex;
+}
+
+TextCompositionArray::index_type
+TextCompositionArray::IndexOf(nsPresContext* aPresContext)
+{
+  for (index_type i = Length(); i > 0; --i) {
+    if (ElementAt(i - 1).GetPresContext() == aPresContext) {
+      return i - 1;
+    }
+  }
+  return NoIndex;
+}
+
+TextCompositionArray::index_type
+TextCompositionArray::IndexOf(nsPresContext* aPresContext,
+                              nsINode* aNode)
+{
+  index_type index = IndexOf(aPresContext);
+  if (index == NoIndex) {
+    return NoIndex;
+  }
+  nsINode* node = ElementAt(index).GetEventTargetNode();
+  return node == aNode ? index : NoIndex;
+}
+
+TextComposition*
+TextCompositionArray::GetCompositionFor(nsIWidget* aWidget)
+{
+  index_type i = IndexOf(aWidget);
+  return i != NoIndex ? &ElementAt(i) : nullptr;
+}
+
+TextComposition*
+TextCompositionArray::GetCompositionFor(nsPresContext* aPresContext)
+{
+  index_type i = IndexOf(aPresContext);
+  return i != NoIndex ? &ElementAt(i) : nullptr;
+}
+
+TextComposition*
+TextCompositionArray::GetCompositionFor(nsPresContext* aPresContext,
+                                           nsINode* aNode)
+{
+  index_type i = IndexOf(aPresContext, aNode);
+  return i != NoIndex ? &ElementAt(i) : nullptr;
+}
+
+TextComposition*
+TextCompositionArray::GetCompositionInContent(nsPresContext* aPresContext,
+                                              nsIContent* aContent)
+{
+  // There should be only one composition per content object.
+  for (index_type i = Length(); i > 0; --i) {
+    nsINode* node = ElementAt(i - 1).GetEventTargetNode();
+    if (node && nsContentUtils::ContentIsDescendantOf(node, aContent)) {
+      return &ElementAt(i - 1);
+    }
+  }
+  return nullptr;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/events/src/TextComposition.h
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_TextComposition_h
+#define mozilla_TextComposition_h
+
+#include "nsCOMPtr.h"
+#include "nsEvent.h"
+#include "nsINode.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Attributes.h"
+
+class nsCompositionEvent;
+class nsDispatchingCallback;
+class nsIMEStateManager;
+class nsIWidget;
+class nsPresContext;
+
+namespace mozilla {
+
+/**
+ * TextComposition represents a text composition.  This class stores the
+ * composition event target and its presContext.  At dispatching the event via
+ * this class, the instances use the stored event target.
+ */
+
+class TextComposition MOZ_FINAL
+{
+  friend class ::nsIMEStateManager;
+public:
+  TextComposition(nsPresContext* aPresContext,
+                  nsINode* aNode,
+                  nsGUIEvent* aEvent);
+
+  TextComposition(const TextComposition& aOther);
+
+  ~TextComposition()
+  {
+    // WARNING: mPresContext may be destroying, so, be careful if you touch it.
+  }
+
+  nsPresContext* GetPresContext() const { return mPresContext; }
+  nsINode* GetEventTargetNode() const { return mNode; }
+  // The latest CompositionEvent.data value except compositionstart event.
+  const nsString& GetLastData() const { return mLastData; }
+  // Returns true if the composition is started with synthesized event which
+  // came from nsDOMWindowUtils.
+  bool IsSynthesizedForTests() const { return mIsSynthesizedForTests; }
+
+  bool MatchesNativeContext(nsIWidget* aWidget) const;
+  bool MatchesEventTarget(nsPresContext* aPresContext,
+                          nsINode* aNode) const;
+
+  /**
+   * SynthesizeCommit() dispatches compositionupdate, text and compositionend
+   * events for emulating commit on the content.
+   *
+   * @param aDiscard true when committing with empty string.  Otherwise, false.
+   */
+  void SynthesizeCommit(bool aDiscard);
+
+  /**
+   * Send a notification to IME.  It depends on the IME or platform spec what
+   * will occur (or not occur).
+   */
+  nsresult NotifyIME(widget::NotificationToIME aNotification);
+
+private:
+  // This class holds nsPresContext weak.  This instance shouldn't block
+  // destroying it.  When the presContext is being destroyed, it's notified to
+  // nsIMEStateManager::OnDestroyPresContext(), and then, it destroy
+  // this instance.
+  nsPresContext* mPresContext;
+  nsCOMPtr<nsINode> mNode;
+
+  // mNativeContext stores a opaque pointer.  This works as the "ID" for this
+  // composition.  Don't access the instance, it may not be available.
+  void* mNativeContext;
+
+  // mLastData stores the data attribute of the latest composition event (except
+  // the compositionstart event).
+  nsString mLastData;
+
+  // See the comment for IsSynthesizedForTests().
+  bool mIsSynthesizedForTests;
+
+  // Hide the default constructor
+  TextComposition() {}
+
+  /**
+   * DispatchEvent() dispatches the aEvent to the mContent synchronously.
+   * The caller must ensure that it's safe to dispatch the event.
+   */
+  void DispatchEvent(nsGUIEvent* aEvent,
+                     nsEventStatus* aStatus,
+                     nsDispatchingCallback* aCallBack);
+
+  /**
+   * CompositionEventDispatcher dispatches the specified composition (or text)
+   * event.
+   */
+  class CompositionEventDispatcher : public nsRunnable
+  {
+  public:
+    CompositionEventDispatcher(nsPresContext* aPresContext,
+                               nsINode* aEventTarget,
+                               uint32_t aEventMessage,
+                               const nsAString& aData);
+    NS_IMETHOD Run();
+
+  private:
+    nsRefPtr<nsPresContext> mPresContext;
+    nsCOMPtr<nsINode> mEventTarget;
+    nsCOMPtr<nsIWidget> mWidget;
+    uint32_t mEventMessage;
+    nsString mData;
+
+    CompositionEventDispatcher() {};
+  };
+
+  /**
+   * DispatchCompsotionEventRunnable() dispatches a composition or text event
+   * to the content.  Be aware, if you use this method, nsPresShellEventCB
+   * isn't used.  That means that nsIFrame::HandleEvent() is never called.
+   * WARNING: The instance which is managed by nsIMEStateManager may be
+   *          destroyed by this method call.
+   *
+   * @param aEventMessage       Must be one of composition event or text event.
+   * @param aData               Used for data value if aEventMessage is
+   *                            NS_COMPOSITION_UPDATE or NS_COMPOSITION_END.
+   *                            Used for theText value if aEventMessage is
+   *                            NS_TEXT_TEXT.
+   */
+  void DispatchCompsotionEventRunnable(uint32_t aEventMessage,
+                                       const nsAString& aData);
+};
+
+/**
+ * TextCompositionArray manages the instances of TextComposition class.
+ * Managing with array is enough because only one composition is typically
+ * there.  Even if user switches native IME context, it's very rare that
+ * second or more composition is started.
+ * It's assumed that this is used by nsIMEStateManager for storing all active
+ * compositions in the process.  If the instance is it, each TextComposition
+ * in the array can be destroyed by calling some methods of itself.
+ */
+
+class TextCompositionArray MOZ_FINAL : public nsAutoTArray<TextComposition, 2>
+{
+public:
+  index_type IndexOf(nsIWidget* aWidget);
+  index_type IndexOf(nsPresContext* aPresContext);
+  index_type IndexOf(nsPresContext* aPresContext, nsINode* aNode);
+
+  TextComposition* GetCompositionFor(nsIWidget* aWidget);
+  TextComposition* GetCompositionFor(nsPresContext* aPresContext);
+  TextComposition* GetCompositionFor(nsPresContext* aPresContext,
+                                     nsINode* aNode);
+  TextComposition* GetCompositionInContent(nsPresContext* aPresContext,
+                                           nsIContent* aContent);
+};
+
+} // namespace mozilla
+
+#endif // #ifndef mozilla_TextComposition_h
--- a/content/events/src/nsDOMEventTargetHelper.h
+++ b/content/events/src/nsDOMEventTargetHelper.h
@@ -28,17 +28,17 @@ public:
   nsDOMEventTargetHelper() : mOwner(nullptr), mHasOrHasHadOwner(false) {}
   virtual ~nsDOMEventTargetHelper();
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(nsDOMEventTargetHelper)
 
   NS_DECL_NSIDOMEVENTTARGET
   void AddEventListener(const nsAString& aType,
                         nsIDOMEventListener* aCallback, // XXX nullable
-                        bool aCapture, Nullable<bool>& aWantsUntrusted,
+                        bool aCapture, const Nullable<bool>& aWantsUntrusted,
                         mozilla::ErrorResult& aRv)
   {
     aRv = AddEventListener(aType, aCallback, aCapture,
                            !aWantsUntrusted.IsNull() && aWantsUntrusted.Value(),
                            aWantsUntrusted.IsNull() ? 1 : 2);
   }
   void RemoveEventListener(const nsAString& aType,
                            nsIDOMEventListener* aCallback,
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -4881,16 +4881,18 @@ nsEventStateManager::ContentRemoved(nsID
   if (aContent->IsHTML() &&
       (aContent->Tag() == nsGkAtoms::a || aContent->Tag() == nsGkAtoms::area) &&
       (aContent->AsElement()->State().HasAtLeastOneOfStates(NS_EVENT_STATE_FOCUS |
                                                             NS_EVENT_STATE_HOVER))) {
     nsGenericHTMLElement* element = static_cast<nsGenericHTMLElement*>(aContent);
     element->LeaveLink(element->GetPresContext());
   }
 
+  nsIMEStateManager::OnRemoveContent(mPresContext, aContent);
+
   // inform the focus manager that the content is being removed. If this
   // content is focused, the focus will be removed without firing events.
   nsFocusManager* fm = nsFocusManager::GetFocusManager();
   if (fm)
     fm->ContentRemoved(aDocument, aContent);
 
   if (mHoverContent &&
       nsContentUtils::ContentIsDescendantOf(mHoverContent, aContent)) {
--- a/content/events/src/nsIMEStateManager.cpp
+++ b/content/events/src/nsIMEStateManager.cpp
@@ -29,34 +29,59 @@
 #include "nsIMutationObserver.h"
 #include "nsContentEventHandler.h"
 #include "nsIObserverService.h"
 #include "mozilla/Services.h"
 #include "nsIFormControl.h"
 #include "nsIForm.h"
 #include "nsHTMLFormElement.h"
 #include "mozilla/Attributes.h"
+#include "nsEventDispatcher.h"
+#include "TextComposition.h"
 
+using namespace mozilla;
 using namespace mozilla::widget;
 
 /******************************************************************/
 /* nsIMEStateManager                                              */
 /******************************************************************/
 
 nsIContent*    nsIMEStateManager::sContent      = nullptr;
 nsPresContext* nsIMEStateManager::sPresContext  = nullptr;
 bool           nsIMEStateManager::sInstalledMenuKeyboardListener = false;
 bool           nsIMEStateManager::sInSecureInputMode = false;
 
 nsTextStateManager* nsIMEStateManager::sTextStateObserver = nullptr;
+TextCompositionArray* nsIMEStateManager::sTextCompositions = nullptr;
+
+void
+nsIMEStateManager::Shutdown()
+{
+  MOZ_ASSERT(!sTextCompositions || !sTextCompositions->Length());
+  delete sTextCompositions;
+  sTextCompositions = nullptr;
+}
 
 nsresult
 nsIMEStateManager::OnDestroyPresContext(nsPresContext* aPresContext)
 {
   NS_ENSURE_ARG_POINTER(aPresContext);
+
+  // First, if there is a composition in the aPresContext, clean up it.
+  if (sTextCompositions) {
+    TextCompositionArray::index_type i =
+      sTextCompositions->IndexOf(aPresContext);
+    if (i != TextCompositionArray::NoIndex) {
+      // there should be only one composition per presContext object.
+      sTextCompositions->RemoveElementAt(i);
+      MOZ_ASSERT(sTextCompositions->IndexOf(aPresContext) ==
+                   TextCompositionArray::NoIndex);
+    }
+  }
+
   if (aPresContext != sPresContext)
     return NS_OK;
   nsCOMPtr<nsIWidget> widget = GetWidget(sPresContext);
   if (widget) {
     IMEState newState = GetNewIMEState(sPresContext, nullptr);
     InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
                               InputContextAction::LOST_FOCUS);
     SetIMEState(newState, nullptr, widget, action);
@@ -67,27 +92,59 @@ nsIMEStateManager::OnDestroyPresContext(
   return NS_OK;
 }
 
 nsresult
 nsIMEStateManager::OnRemoveContent(nsPresContext* aPresContext,
                                    nsIContent* aContent)
 {
   NS_ENSURE_ARG_POINTER(aPresContext);
+
+  // First, if there is a composition in the aContent, clean up it.
+  if (sTextCompositions) {
+    TextComposition* compositionInContent =
+      sTextCompositions->GetCompositionInContent(aPresContext, aContent);
+
+    if (compositionInContent) {
+      // Store the composition before accessing the native IME.
+      TextComposition storedComposition = *compositionInContent;
+      // Try resetting the native IME state.  Be aware, typically, this method
+      // is called during the content being removed.  Then, the native
+      // composition events which are caused by following APIs are ignored due
+      // to unsafe to run script (in PresShell::HandleEvent()).
+      nsCOMPtr<nsIWidget> widget = aPresContext->GetNearestWidget();
+      if (widget) {
+        nsresult rv =
+          storedComposition.NotifyIME(REQUEST_TO_CANCEL_COMPOSITION);
+        if (NS_FAILED(rv)) {
+          storedComposition.NotifyIME(REQUEST_TO_COMMIT_COMPOSITION);
+        }
+        // By calling the APIs, the composition may have been finished normally.
+        compositionInContent =
+          sTextCompositions->GetCompositionFor(
+                               storedComposition.GetPresContext(),
+                               storedComposition.GetEventTargetNode());
+      }
+    }
+
+    // If the compositionInContent is still available, we should finish the
+    // composition just on the content forcibly.
+    if (compositionInContent) {
+      compositionInContent->SynthesizeCommit(true);
+    }
+  }
+
   if (!sPresContext || !sContent ||
-      aPresContext != sPresContext ||
-      aContent != sContent)
+      !nsContentUtils::ContentIsDescendantOf(sContent, aContent)) {
     return NS_OK;
+  }
 
   // Current IME transaction should commit
   nsCOMPtr<nsIWidget> widget = GetWidget(sPresContext);
   if (widget) {
-    nsresult rv = widget->CancelIMEComposition();
-    if (NS_FAILED(rv))
-      widget->ResetInputState();
     IMEState newState = GetNewIMEState(sPresContext, nullptr);
     InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
                               InputContextAction::LOST_FOCUS);
     SetIMEState(newState, nullptr, widget, action);
   }
 
   NS_IF_RELEASE(sContent);
   sPresContext = nullptr;
@@ -159,18 +216,19 @@ nsIMEStateManager::OnChangeFocusInternal
 
   // Current IME transaction should commit
   if (sPresContext) {
     nsCOMPtr<nsIWidget> oldWidget;
     if (sPresContext == aPresContext)
       oldWidget = widget;
     else
       oldWidget = GetWidget(sPresContext);
-    if (oldWidget)
-      oldWidget->ResetInputState();
+    if (oldWidget) {
+      NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget);
+    }
   }
 
   // Update IME state for new focus widget
   SetIMEState(newState, aContent, widget, aAction);
 
   sPresContext = aPresContext;
   if (sContent != aContent) {
     NS_IF_RELEASE(sContent);
@@ -246,17 +304,17 @@ nsIMEStateManager::UpdateIMEState(const 
 
   // Don't update IME state when enabled state isn't actually changed.
   InputContext context = widget->GetInputContext();
   if (context.mIMEState.mEnabled == aNewIMEState.mEnabled) {
     return;
   }
 
   // commit current composition
-  widget->ResetInputState();
+  NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, widget);
 
   InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
                             InputContextAction::FOCUS_NOT_CHANGED);
   SetIMEState(aNewIMEState, aContent, widget, action);
 }
 
 IMEState
 nsIMEStateManager::GetNewIMEState(nsPresContext* aPresContext,
@@ -379,16 +437,176 @@ nsIMEStateManager::GetWidget(nsPresConte
   if (!vm)
     return nullptr;
   nsCOMPtr<nsIWidget> widget = nullptr;
   nsresult rv = vm->GetRootWidget(getter_AddRefs(widget));
   NS_ENSURE_SUCCESS(rv, nullptr);
   return widget;
 }
 
+void
+nsIMEStateManager::EnsureTextCompositionArray()
+{
+  if (sTextCompositions) {
+    return;
+  }
+  sTextCompositions = new TextCompositionArray();
+}
+
+void
+nsIMEStateManager::DispatchCompositionEvent(nsINode* aEventTargetNode,
+                                            nsPresContext* aPresContext,
+                                            nsEvent* aEvent,
+                                            nsEventStatus* aStatus,
+                                            nsDispatchingCallback* aCallBack)
+{
+  MOZ_ASSERT(aEvent->eventStructType == NS_COMPOSITION_EVENT ||
+             aEvent->eventStructType == NS_TEXT_EVENT);
+  if (!NS_IS_TRUSTED_EVENT(aEvent) ||
+      (aEvent->flags & NS_EVENT_FLAG_STOP_DISPATCH) != 0) {
+    return;
+  }
+
+  EnsureTextCompositionArray();
+
+  nsGUIEvent* GUIEvent = static_cast<nsGUIEvent*>(aEvent);
+
+  TextComposition* composition =
+    sTextCompositions->GetCompositionFor(GUIEvent->widget);
+  if (!composition) {
+    MOZ_ASSERT(GUIEvent->message == NS_COMPOSITION_START);
+    TextComposition newComposition(aPresContext, aEventTargetNode, GUIEvent);
+    composition = sTextCompositions->AppendElement(newComposition);
+  }
+#ifdef DEBUG
+  else {
+    MOZ_ASSERT(GUIEvent->message != NS_COMPOSITION_START);
+  }
+#endif // #ifdef DEBUG
+
+  // Dispatch the event on composing target.
+  composition->DispatchEvent(GUIEvent, aStatus, aCallBack);
+
+  // WARNING: the |composition| might have been destroyed already.
+
+  // Remove the ended composition from the array.
+  if (aEvent->message == NS_COMPOSITION_END) {
+    TextCompositionArray::index_type i =
+      sTextCompositions->IndexOf(GUIEvent->widget);
+    if (i != TextCompositionArray::NoIndex) {
+      sTextCompositions->RemoveElementAt(i);
+    }
+  }
+}
+
+// static
+nsresult
+nsIMEStateManager::NotifyIME(NotificationToIME aNotification,
+                             nsIWidget* aWidget)
+{
+  NS_ENSURE_TRUE(aWidget, NS_ERROR_INVALID_ARG);
+
+  TextComposition* composition = nullptr;
+  if (sTextCompositions) {
+    composition = sTextCompositions->GetCompositionFor(aWidget);
+  }
+  if (!composition || !composition->IsSynthesizedForTests()) {
+    switch (aNotification) {
+      case NOTIFY_IME_OF_CURSOR_POS_CHANGED:
+        return aWidget->ResetInputState();
+      case REQUEST_TO_COMMIT_COMPOSITION:
+        return composition ? aWidget->ResetInputState() : NS_OK;
+      case REQUEST_TO_CANCEL_COMPOSITION:
+        return composition ? aWidget->CancelIMEComposition() : NS_OK;
+      default:
+        MOZ_NOT_REACHED("Unsupported notification");
+        return NS_ERROR_INVALID_ARG;
+    }
+    MOZ_NOT_REACHED(
+      "Failed to handle the notification for non-synthesized composition");
+  }
+
+  // If the composition is synthesized events for automated tests, we should
+  // dispatch composition events for emulating the native composition behavior.
+  // NOTE: The dispatched events are discarded if it's not safe to run script.
+  switch (aNotification) {
+    case REQUEST_TO_COMMIT_COMPOSITION: {
+      nsCOMPtr<nsIWidget> widget(aWidget);
+      TextComposition backup = *composition;
+
+      nsEventStatus status = nsEventStatus_eIgnore;
+      if (!backup.GetLastData().IsEmpty()) {
+        nsTextEvent textEvent(true, NS_TEXT_TEXT, widget);
+        textEvent.theText = backup.GetLastData();
+        textEvent.flags |= NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT;
+        widget->DispatchEvent(&textEvent, status);
+        if (widget->Destroyed()) {
+          return NS_OK;
+        }
+      }
+
+      status = nsEventStatus_eIgnore;
+      nsCompositionEvent endEvent(true, NS_COMPOSITION_END, widget);
+      endEvent.data = backup.GetLastData();
+      endEvent.flags |= NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT;
+      widget->DispatchEvent(&endEvent, status);
+
+      return NS_OK;
+    }
+    case REQUEST_TO_CANCEL_COMPOSITION: {
+      nsCOMPtr<nsIWidget> widget(aWidget);
+      TextComposition backup = *composition;
+
+      nsEventStatus status = nsEventStatus_eIgnore;
+      if (!backup.GetLastData().IsEmpty()) {
+        nsCompositionEvent updateEvent(true, NS_COMPOSITION_UPDATE, widget);
+        updateEvent.data = backup.GetLastData();
+        updateEvent.flags |= NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT;
+        widget->DispatchEvent(&updateEvent, status);
+        if (widget->Destroyed()) {
+          return NS_OK;
+        }
+
+        status = nsEventStatus_eIgnore;
+        nsTextEvent textEvent(true, NS_TEXT_TEXT, widget);
+        textEvent.theText = backup.GetLastData();
+        textEvent.flags |= NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT;
+        widget->DispatchEvent(&textEvent, status);
+        if (widget->Destroyed()) {
+          return NS_OK;
+        }
+      }
+
+      status = nsEventStatus_eIgnore;
+      nsCompositionEvent endEvent(true, NS_COMPOSITION_END, widget);
+      endEvent.data = backup.GetLastData();
+      endEvent.flags |= NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT;
+      widget->DispatchEvent(&endEvent, status);
+
+      return NS_OK;
+    }
+    default:
+      return NS_OK;
+  }
+}
+
+// static
+nsresult
+nsIMEStateManager::NotifyIME(NotificationToIME aNotification,
+                             nsPresContext* aPresContext)
+{
+  NS_ENSURE_TRUE(aPresContext, NS_ERROR_INVALID_ARG);
+
+  nsIWidget* widget = aPresContext->GetNearestWidget();
+  if (!widget) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  return NotifyIME(aNotification, widget);
+}
+
 
 // nsTextStateManager notifies widget of any text and selection changes
 //  in the currently focused editor
 // sTextStateObserver points to the currently active nsTextStateManager
 // sTextStateObserver is null if there is no focused editor
 
 class nsTextStateManager MOZ_FINAL : public nsISelectionListener,
                                      public nsStubMutationObserver
--- a/content/events/src/nsIMEStateManager.h
+++ b/content/events/src/nsIMEStateManager.h
@@ -2,37 +2,46 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsIMEStateManager_h__
 #define nsIMEStateManager_h__
 
 #include "nscore.h"
+#include "nsEvent.h"
 #include "nsIWidget.h"
 
+class nsDispatchingCallback;
 class nsIContent;
 class nsIDOMMouseEvent;
+class nsINode;
 class nsPIDOMWindow;
 class nsPresContext;
 class nsTextStateManager;
 class nsISelection;
 
+namespace mozilla {
+class TextCompositionArray;
+} // namespace mozilla
+
 /*
  * IME state manager
  */
 
 class nsIMEStateManager
 {
 protected:
   typedef mozilla::widget::IMEState IMEState;
   typedef mozilla::widget::InputContext InputContext;
   typedef mozilla::widget::InputContextAction InputContextAction;
 
 public:
+  static void Shutdown();
+
   static nsresult OnDestroyPresContext(nsPresContext* aPresContext);
   static nsresult OnRemoveContent(nsPresContext* aPresContext,
                                   nsIContent* aContent);
   /**
    * OnChangeFocus() should be called when focused content is changed or
    * IME enabled state is changed.  If focus isn't actually changed and IME
    * enabled state isn't changed, this will do nothing.
    */
@@ -70,30 +79,59 @@ public:
   // aContent must be:
   //   If the editor is for <input> or <textarea>, the element.
   //   If the editor is for contenteditable, the active editinghost.
   //   If the editor is for designMode, NULL.
   static void OnClickInEditor(nsPresContext* aPresContext,
                               nsIContent* aContent,
                               nsIDOMMouseEvent* aMouseEvent);
 
+  /**
+   * All DOM composition events and DOM text events must be dispatched via
+   * DispatchCompositionEvent() for storing the composition target
+   * and ensuring a set of composition events must be fired the stored target.
+   * If the stored composition event target is destroying, this removes the
+   * stored composition automatically.
+   */
+  static void DispatchCompositionEvent(nsINode* aEventTargetNode,
+                                       nsPresContext* aPresContext,
+                                       nsEvent* aEvent,
+                                       nsEventStatus* aStatus,
+                                       nsDispatchingCallback* aCallBack);
+
+  /**
+   * Send a notification to IME.  It depends on the IME or platform spec what
+   * will occur (or not occur).
+   */
+  static nsresult NotifyIME(mozilla::widget::NotificationToIME aNotification,
+                            nsIWidget* aWidget);
+  static nsresult NotifyIME(mozilla::widget::NotificationToIME aNotification,
+                            nsPresContext* aPresContext);
+
 protected:
   static nsresult OnChangeFocusInternal(nsPresContext* aPresContext,
                                         nsIContent* aContent,
                                         InputContextAction aAction);
   static void SetIMEState(const IMEState &aState,
                           nsIContent* aContent,
                           nsIWidget* aWidget,
                           InputContextAction aAction);
   static IMEState GetNewIMEState(nsPresContext* aPresContext,
                                  nsIContent* aContent);
 
   static nsIWidget* GetWidget(nsPresContext* aPresContext);
+  static void EnsureTextCompositionArray();
 
   static nsIContent*    sContent;
   static nsPresContext* sPresContext;
   static bool           sInstalledMenuKeyboardListener;
   static bool           sInSecureInputMode;
 
   static nsTextStateManager* sTextStateObserver;
+
+  // All active compositions in the process are stored by this array.
+  // When you get an item of this array and use it, please be careful.
+  // The instances in this array can be destroyed automatically if you do
+  // something to cause committing or canceling the composition.
+  static mozilla::TextCompositionArray* sTextCompositions;
 };
 
 #endif // nsIMEStateManager_h__
--- a/content/media/webrtc/MediaEngineWebRTCAudio.cpp
+++ b/content/media/webrtc/MediaEngineWebRTCAudio.cpp
@@ -120,17 +120,22 @@ MediaEngineWebRTCAudioSource::Stop()
 
   if (mVoEBase->StopSend(mChannel)) {
     return NS_ERROR_FAILURE;
   }
   if (mVoEBase->StopReceive(mChannel)) {
     return NS_ERROR_FAILURE;
   }
 
-  mState = kStopped;
+  {
+    ReentrantMonitorAutoEnter enter(mMonitor);
+    mState = kStopped;
+    mSource->EndTrack(mTrackID);
+  }
+
   return NS_OK;
 }
 
 void
 MediaEngineWebRTCAudioSource::NotifyPull(MediaStreamGraph* aGraph,
                                          StreamTime aDesiredTime)
 {
   // Ignore - we push audio data
@@ -219,16 +224,18 @@ MediaEngineWebRTCAudioSource::Shutdown()
 typedef WebRtc_Word16 sample;
 
 void
 MediaEngineWebRTCAudioSource::Process(const int channel,
   const webrtc::ProcessingTypes type, sample* audio10ms,
   const int length, const int samplingFreq, const bool isStereo)
 {
   ReentrantMonitorAutoEnter enter(mMonitor);
+  if (mState != kStarted)
+    return;
 
   nsRefPtr<SharedBuffer> buffer = SharedBuffer::Create(length * sizeof(sample));
 
   sample* dest = static_cast<sample*>(buffer->Data());
   memcpy(dest, audio10ms, length * sizeof(sample));
 
   AudioSegment segment;
   segment.Init(CHANNELS);
--- a/content/media/webrtc/MediaEngineWebRTCVideo.cpp
+++ b/content/media/webrtc/MediaEngineWebRTCVideo.cpp
@@ -97,17 +97,16 @@ MediaEngineWebRTCVideoSource::DeliverFra
 // this means that no *real* frame can be inserted during this period.
 void
 MediaEngineWebRTCVideoSource::NotifyPull(MediaStreamGraph* aGraph,
                                          StreamTime aDesiredTime)
 {
   VideoSegment segment;
 
   ReentrantMonitorAutoEnter enter(mMonitor);
-
   if (mState != kStarted)
     return;
 
   // Note: we're not giving up mImage here
   nsRefPtr<layers::Image> image = mImage;
   TrackTicks target = TimeToTicksRoundUp(USECS_PER_S, aDesiredTime);
   TrackTicks delta = target - mLastEndTime;
 #ifdef LOG_ALL_FRAMES
@@ -273,24 +272,26 @@ MediaEngineWebRTCVideoSource::Start(Sour
 
 nsresult
 MediaEngineWebRTCVideoSource::Stop()
 {
   if (mState != kStarted) {
     return NS_ERROR_FAILURE;
   }
 
-  mSource->EndTrack(mTrackID);
-  mSource->Finish();
+  {
+    ReentrantMonitorAutoEnter enter(mMonitor);
+    mState = kStopped;
+    mSource->EndTrack(mTrackID);
+  }
 
   mViERender->StopRender(mCaptureIndex);
   mViERender->RemoveRenderer(mCaptureIndex);
   mViECapture->StopCapture(mCaptureIndex);
 
-  mState = kStopped;
   return NS_OK;
 }
 
 nsresult
 MediaEngineWebRTCVideoSource::Snapshot(uint32_t aDuration, nsIDOMFile** aFile)
 {
   /**
    * To get a Snapshot we do the following:
--- a/docshell/shistory/src/nsSHistory.cpp
+++ b/docshell/shistory/src/nsSHistory.cpp
@@ -55,17 +55,17 @@ static PRCList gSHistoryList;
 // Max viewers allowed total, across all SHistory objects - negative default
 // means we will calculate how many viewers to cache based on total memory
 int32_t nsSHistory::sHistoryMaxTotalViewers = -1;
 
 // A counter that is used to be able to know the order in which
 // entries were touched, so that we can evict older entries first.
 static uint32_t gTouchCounter = 0;
 
-static PRLogModuleInfo* gLogModule = PR_LOG_DEFINE("nsSHistory");
+static PRLogModuleInfo* gLogModule = PR_NewLogModule("nsSHistory");
 #define LOG(format) PR_LOG(gLogModule, PR_LOG_DEBUG, format)
 
 // This macro makes it easier to print a log message which includes a URI's
 // spec.  Example use:
 //
 //  nsIURI *uri = [...];
 //  LOG_SPEC(("The URI is %s.", _spec), uri);
 //
--- a/dom/activities/src/ActivityProxy.js
+++ b/dom/activities/src/ActivityProxy.js
@@ -6,17 +6,16 @@
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/ObjectWrapper.jsm");
-Cu.import("resource://gre/modules/Webapps.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
                                    "nsISyncMessageSender");
 
 function debug(aMsg) {
   //dump("-- ActivityProxy " + Date.now() + " : " + aMsg + "\n");
 }
@@ -25,16 +24,21 @@ function debug(aMsg) {
   * nsIActivityProxy implementation
   * We keep a reference to the C++ Activity object, and
   * communicate with the Message Manager to know when to
   * fire events on it.
   */
 function ActivityProxy() {
   debug("ActivityProxy");
   this.activity = null;
+  let inParent = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime)
+                   .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
+  debug("inParent: " + inParent);
+  Cu.import(inParent ? "resource://gre/modules/Webapps.jsm"
+                     : "resource://gre/modules/AppsServiceChild.jsm");
 }
 
 ActivityProxy.prototype = {
   startActivity: function actProxy_startActivity(aActivity, aOptions, aWindow) {
     debug("startActivity");
 
     this.window = aWindow;
     this.activity = aActivity;
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -119,34 +119,39 @@ let DOMApplicationRegistry = {
         }
         aNext();
       }).bind(this));
     } else {
       aNext();
     }
   },
 
-  // We are done with loading and initializing. Notify and
-  // save a copy of the registry.
-  onInitDone: function onInitDone() {
+  // Notify we are starting with registering apps.
+  notifyAppsRegistryStart: function notifyAppsRegistryStart() {
+    Services.obs.notifyObservers(this, "webapps-registry-start", null);
+  },
+
+  // Notify we are done with registering apps and save a copy of the registry.
+  notifyAppsRegistryReady: function notifyAppsRegistryReady() {
     Services.obs.notifyObservers(this, "webapps-registry-ready", null);
     this._saveApps();
   },
 
-  // registers all the activities and system messages
+  // Registers all the activities and system messages.
   registerAppsHandlers: function registerAppsHandlers() {
+    this.notifyAppsRegistryStart();
 #ifdef MOZ_SYS_MSG
     let ids = [];
     for (let id in this.webapps) {
       ids.push({ id: id });
     }
     this._processManifestForIds(ids);
 #else
     // Nothing else to do but notifying we're ready.
-    this.onInitDone();
+    this.notifyAppsRegistryReady();
 #endif
   },
 
   updatePermissionsForApp: function updatePermissionsForApp(aId) {
     // Install the permissions for this app, as if we were updating
     // to cleanup the old ones if needed.
     this._readManifests([{ id: aId }], (function(aResult) {
       let data = aResult[0];
@@ -243,17 +248,16 @@ let DOMApplicationRegistry = {
       }
     }
     this.registerAppsHandlers();
 #endif
     }).bind(this));
   },
 
 #ifdef MOZ_SYS_MSG
-
   // aEntryPoint is either the entry_point name or the null, in which case we
   // use the root of the manifest.
   _registerSystemMessagesForEntryPoint: function(aManifest, aApp, aEntryPoint) {
     let root = aManifest;
     if (aEntryPoint && aManifest.entry_points[aEntryPoint]) {
       root = aManifest.entry_points[aEntryPoint];
     }
 
@@ -365,20 +369,24 @@ let DOMApplicationRegistry = {
       return;
     }
 
     for (let entryPoint in aManifest.entry_points) {
       this._unregisterActivitiesForEntryPoint(aManifest, aApp, entryPoint);
     }
   },
 
-  _processManifestForIds: function(aIds) {
+  _initRegisterActivities: function() {
     this.activitiesToRegister = 0;
     this.activitiesRegistered = 0;
     this.allActivitiesSent = false;
+  },
+
+  _processManifestForIds: function(aIds) {
+    this._initRegisterActivities();
     this._readManifests(aIds, (function registerManifests(aResults) {
       aResults.forEach(function registerManifest(aResult) {
         let app = this.webapps[aResult.id];
         let manifest = aResult.manifest;
         app.name = manifest.name;
         this._registerSystemMessages(manifest, app);
         this._registerActivities(manifest, app);
       }, this);
@@ -535,17 +543,17 @@ let DOMApplicationRegistry = {
         break;
       case "Webapps::ApplyDownload":
         this.ApplyDownload(msg.manifestURL);
         break;
       case "Activities:Register:OK":
         this.activitiesRegistered++;
         if (this.allActivitiesSent &&
             this.activitiesRegistered === this.activitiesToRegister) {
-          this.onInitDone();
+          this.notifyAppsRegistryReady();
         }
         break;
       case "child-process-shutdown":
         this.removeMessageListener(mm);
         break;
     }
   },
 
@@ -770,23 +778,29 @@ let DOMApplicationRegistry = {
         aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:OK", aData);
       });
     }
 
     function updateHostedApp(aManifest) {
       debug("updateHostedApp");
       let id = this._appId(app.origin);
 
+      // Update the web apps' registration.
+      this.notifyAppsRegistryStart();
 #ifdef MOZ_SYS_MSG
-      // Update the Web Activities
+      this._initRegisterActivities();
       this._readManifests([{ id: id }], (function unregisterManifest(aResult) {
         this._unregisterActivities(aResult[0].manifest, app);
         this._registerSystemMessages(aManifest, app);
         this._registerActivities(aManifest, app);
+        this.allActivitiesSent = true;
       }).bind(this));
+#else
+      // Nothing else to do but notifying we're ready.
+      this.notifyAppsRegistryReady();
 #endif
 
       // Store the new manifest.
       let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id], true, true);
       let manFile = dir.clone();
       manFile.append("manifest.webapp");
       this._writeFile(manFile, JSON.stringify(aManifest), function() { });
 
@@ -977,22 +991,28 @@ let DOMApplicationRegistry = {
 
     if (!aFromSync)
       this._saveApps((function() {
         this.broadcastMessage("Webapps:Install:Return:OK", aData);
         Services.obs.notifyObservers(this, "webapps-sync-install", appNote);
         this.broadcastMessage("Webapps:AddApp", { id: id, app: appObject });
       }).bind(this));
 
+    if (!aData.isPackage) {
+      this.notifyAppsRegistryStart();
 #ifdef MOZ_SYS_MSG
-    if (!aData.isPackage) {
+      this._initRegisterActivities();
       this._registerSystemMessages(app.manifest, app);
       this._registerActivities(app.manifest, app);
+      this.allActivitiesSent = true;
+#else
+      // Nothing else to do but notifying we're ready.
+      this.notifyAppsRegistryReady();
+#endif
     }
-#endif
 
     this.startOfflineCacheDownload(manifest, appObject, aProfileDir, aOfflineCacheObserver);
     if (manifest.package_path) {
       // origin for install apps is meaningless here, since it's app:// and this
       // can't be used to resolve package paths.
       manifest = new ManifestHelper(jsonManifest, app.manifestURL);
       this.downloadPackage(manifest, appObject, false, function(aId, aManifest) {
         // Success! Move the zip out of TmpD.
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -1593,16 +1593,18 @@ nsDOMWindowUtils::SendCompositionEvent(c
   }
 
   nsCompositionEvent compositionEvent(true, msg, widget);
   InitEvent(compositionEvent);
   if (msg != NS_COMPOSITION_START) {
     compositionEvent.data = aData;
   }
 
+  compositionEvent.flags |= NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT;
+
   nsEventStatus status;
   nsresult rv = widget->DispatchEvent(&compositionEvent, status);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 static void
@@ -1670,16 +1672,18 @@ nsDOMWindowUtils::SendTextEvent(const ns
     textRanges.AppendElement(range);
   }
 
   textEvent.theText = aCompositionString;
 
   textEvent.rangeCount = textRanges.Length();
   textEvent.rangeArray = textRanges.Elements();
 
+  textEvent.flags |= NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT;
+
   nsEventStatus status;
   nsresult rv = widget->DispatchEvent(&textEvent, status);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -807,23 +807,16 @@ nsFocusManager::ContentRemoved(nsIDocume
 
   // if the content is currently focused in the window, or is an ancestor
   // of the currently focused element, reset the focus within that window.
   nsIContent* content = window->GetFocusedNode();
   if (content && nsContentUtils::ContentIsDescendantOf(content, aContent)) {
     bool shouldShowFocusRing = window->ShouldShowFocusRing();
     window->SetFocusedNode(nullptr);
 
-    nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
-    if (docShell) {
-      nsCOMPtr<nsIPresShell> presShell;
-      docShell->GetPresShell(getter_AddRefs(presShell));
-      nsIMEStateManager::OnRemoveContent(presShell->GetPresContext(), content);
-    }
-
     // if this window is currently focused, clear the global focused
     // element as well, but don't fire any events.
     if (window == mFocusedWindow) {
       mFocusedContent = nullptr;
     }
     else {
       // Check if the node that was focused is an iframe or similar by looking
       // if it has a subdocument. This would indicate that this focused iframe
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -8262,17 +8262,35 @@ nsGlobalWindow::UpdateCanvasFocus(bool a
   }
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::GetComputedStyle(nsIDOMElement* aElt,
                                  const nsAString& aPseudoElt,
                                  nsIDOMCSSStyleDeclaration** aReturn)
 {
-  FORWARD_TO_OUTER(GetComputedStyle, (aElt, aPseudoElt, aReturn),
+  return GetComputedStyleHelper(aElt, aPseudoElt, false, aReturn);
+}
+
+NS_IMETHODIMP
+nsGlobalWindow::GetDefaultComputedStyle(nsIDOMElement* aElt,
+                                        const nsAString& aPseudoElt,
+                                        nsIDOMCSSStyleDeclaration** aReturn)
+{
+  return GetComputedStyleHelper(aElt, aPseudoElt, true, aReturn);
+}
+
+nsresult
+nsGlobalWindow::GetComputedStyleHelper(nsIDOMElement* aElt,
+                                       const nsAString& aPseudoElt,
+                                       bool aDefaultStylesOnly,
+                                       nsIDOMCSSStyleDeclaration** aReturn)
+{
+  FORWARD_TO_OUTER(GetComputedStyleHelper, (aElt, aPseudoElt,
+                                            aDefaultStylesOnly, aReturn),
                    NS_ERROR_NOT_INITIALIZED);
 
   NS_ENSURE_ARG_POINTER(aReturn);
   *aReturn = nullptr;
 
   if (!aElt) {
     return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   }
@@ -8280,23 +8298,44 @@ nsGlobalWindow::GetComputedStyle(nsIDOME
   if (!mDocShell) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIPresShell> presShell;
   mDocShell->GetPresShell(getter_AddRefs(presShell));
 
   if (!presShell) {
-    return NS_OK;
+    // Try flushing frames on our parent in case there's a pending
+    // style change that will create the presshell.
+    nsGlobalWindow *parent =
+      static_cast<nsGlobalWindow *>(GetPrivateParent());
+    if (!parent) {
+      return NS_OK;
+    }
+
+    parent->FlushPendingNotifications(Flush_Frames);
+
+    // Might have killed mDocShell
+    if (!mDocShell) {
+      return NS_OK;
+    }
+
+    mDocShell->GetPresShell(getter_AddRefs(presShell));
+
+    if (!presShell) {
+      return NS_OK;
+    }
   }
 
   nsCOMPtr<dom::Element> element = do_QueryInterface(aElt);
   NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
   nsRefPtr<nsComputedDOMStyle> compStyle =
-    NS_NewComputedDOMStyle(element, aPseudoElt, presShell);
+    NS_NewComputedDOMStyle(element, aPseudoElt, presShell,
+                           aDefaultStylesOnly ? nsComputedDOMStyle::eDefaultOnly :
+                                                nsComputedDOMStyle::eAll);
 
   *aReturn = compStyle.forget().get();
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::GetSessionStorage(nsIDOMStorage ** aSessionStorage)
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -927,16 +927,22 @@ protected:
   nsresult GetTopImpl(nsIDOMWindow **aWindow, bool aScriptable);
 
   // Helper for creating performance objects.
   void CreatePerformanceObjectIfNeeded();
 
   // Outer windows only.
   nsDOMWindowList* GetWindowList();
 
+  // Helper for getComputedStyle and getDefaultComputedStyle
+  nsresult GetComputedStyleHelper(nsIDOMElement* aElt,
+                                  const nsAString& aPseudoElt,
+                                  bool aDefaultStylesOnly,
+                                  nsIDOMCSSStyleDeclaration** aReturn);
+
   // When adding new member variables, be careful not to create cycles
   // through JavaScript.  If there is any chance that a member variable
   // could own objects that are implemented in JavaScript, then those
   // objects will keep the global object (this object) alive.  To prevent
   // these cycles, ownership of such members must be released in
   // |CleanUp| and |DetachFromDocShell|.
 
   // This member is also used on both inner and outer windows, but
--- a/dom/base/nsLocation.cpp
+++ b/dom/base/nsLocation.cpp
@@ -207,20 +207,23 @@ nsLocation::CheckURL(nsIURI* aURI, nsIDo
     // Make the load's referrer reflect changes to the document's URI caused by
     // push/replaceState, if possible.  First, get the document corresponding to
     // fp.  If the document's original URI (i.e. its URI before
     // push/replaceState) matches the principal's URI, use the document's
     // current URI as the referrer.  If they don't match, use the principal's
     // URI.
 
     JSScript* script = nullptr;
-    if (!JS_DescribeScriptedCaller(cx, &script, nullptr))
-      return NS_ERROR_FAILURE;
-    nsCOMPtr<nsIDocument> doc = GetScriptDocument(cx, script);
+    nsCOMPtr<nsIDocument> doc;
     nsCOMPtr<nsIURI> docOriginalURI, docCurrentURI, principalURI;
+    // NB: A false return value from JS_DescribeScriptedCaller means no caller
+    // was found. It does not signal that an exception was thrown.
+    if (JS_DescribeScriptedCaller(cx, &script, nullptr)) {
+      doc = GetScriptDocument(cx, script);
+    }
     if (doc) {
       docOriginalURI = doc->GetOriginalURI();
       docCurrentURI = doc->GetDocumentURI();
       rv = doc->NodePrincipal()->GetURI(getter_AddRefs(principalURI));
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     bool urisEqual = false;
--- a/dom/base/nsWindowMemoryReporter.cpp
+++ b/dom/base/nsWindowMemoryReporter.cpp
@@ -232,17 +232,19 @@ CollectWindowReports(nsGlobalWindow *aWi
   REPORT("/layout/pres-contexts", windowSizes.mLayoutPresContext,
          "Memory used for the PresContext in the PresShell's frame "
          "within a window.");
   aWindowTotalSizes->mLayoutPresContext += windowSizes.mLayoutPresContext;
 
   // There are many different kinds of frames, but it is very likely
   // that only a few matter.  Implement a cutoff so we don't bloat
   // about:memory with many uninteresting entries.
-  static const size_t FRAME_SUNDRIES_THRESHOLD = 8192;
+  const size_t FRAME_SUNDRIES_THRESHOLD =
+    js::MemoryReportingSundriesThreshold();
+
   size_t frameSundriesSize = 0;
 #define FRAME_ID(classname)                                             \
   {                                                                     \
     size_t frameSize                                                    \
       = windowSizes.mArenaStats.FRAME_ID_STAT_FIELD(classname);         \
     if (frameSize < FRAME_SUNDRIES_THRESHOLD) {                         \
       frameSundriesSize += frameSize;                                   \
     } else {                                                            \
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -381,16 +381,21 @@ DOMInterfaces = {
    'headerFile': 'WebGLExtensions.h'
 },
 
 'WebGLExtensionDepthTexture': {
    'nativeType': 'mozilla::WebGLExtensionDepthTexture',
    'headerFile': 'WebGLExtensions.h'
 },
 
+'WebGLExtensionDebugRendererInfo': {
+   'nativeType': 'mozilla::WebGLExtensionDebugRendererInfo',
+   'headerFile': 'WebGLExtensions.h'
+},
+
 'WebGLExtensionLoseContext': {
    'nativeType': 'mozilla::WebGLExtensionLoseContext',
    'headerFile': 'WebGLExtensions.h'
 },
 
 'WebGLExtensionStandardDerivatives': {
    'nativeType': 'mozilla::WebGLExtensionStandardDerivatives',
    'headerFile': 'WebGLExtensions.h'
@@ -421,17 +426,17 @@ DOMInterfaces = {
    'headerFile': 'WebGLContext.h'
 },
 
 'WebGLRenderingContext': {
   'nativeType': 'mozilla::WebGLContext',
   'headerFile': 'WebGLContext.h',
   'resultNotAddRefed': [ 'canvas', 'getContextAttributes', 'getExtension',
                          'getAttachedShaders' ],
-  'implicitJSContext': [ 'texImage2D', 'texSubImage2D' ],
+  'implicitJSContext': [ 'texImage2D', 'texSubImage2D', 'getSupportedExtensions' ],
 },
 
 'WebGLShader': {
    'nativeType': 'mozilla::WebGLShader',
    'headerFile': 'WebGLContext.h'
 },
 
 'WebGLShaderPrecisionFormat': {
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1706,16 +1706,17 @@ def typeIsSequenceOrHasSequenceMember(ty
         return typeIsSequenceOrHasSequenceMember(elementType)
     if type.isDictionary():
         return dictionaryHasSequenceMember(type.inner)
     if type.isUnion():
         return any(typeIsSequenceOrHasSequenceMember(m.type) for m in
                    type.flatMemberTypes)
     return False
 
+# If this function is modified, modify CGExampleMember.getArg accordingly
 def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
                                     isDefinitelyObject=False,
                                     isMember=False,
                                     isOptional=False,
                                     invalidEnumValueFatal=True,
                                     defaultValue=None,
                                     treatNullAs="Default",
                                     treatUndefinedAs="Default",
@@ -2534,27 +2535,30 @@ for (uint32_t i = 0; i < length; ++i) {
 
     conversionBehavior = "eDefault"
     if isEnforceRange:
         conversionBehavior = "eEnforceRange"
     elif isClamp:
         conversionBehavior = "eClamp"
 
     if type.nullable():
-        dataLoc = "${declName}.SetValue()"
+        declType = CGGeneric("Nullable<" + typeName + ">")
+        mutableType = declType.define() + "&"
+        if not isOptional and not isMember:
+            declType = CGWrapper(declType, pre="const ")
+        dataLoc = ("const_cast< %s >(${declName}).SetValue()" % mutableType)
         nullCondition = "${val}.isNullOrUndefined()"
         if defaultValue is not None and isinstance(defaultValue, IDLNullValue):
             nullCondition = "!(${haveValue}) || " + nullCondition
         template = (
             "if (%s) {\n"
-            "  ${declName}.SetNull();\n"
+            "  const_cast< %s >(${declName}).SetNull();\n"
             "} else if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n"
             "  return false;\n"
-            "}" % (nullCondition, typeName, conversionBehavior, dataLoc))
-        declType = CGGeneric("Nullable<" + typeName + ">")
+            "}" % (nullCondition, mutableType, typeName, conversionBehavior, dataLoc))
     else:
         assert(defaultValue is None or
                not isinstance(defaultValue, IDLNullValue))
         dataLoc = "${declName}"
         template = (
             "if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n"
             "  return false;\n"
             "}" % (typeName, conversionBehavior, dataLoc))
@@ -3005,16 +3009,19 @@ def typeNeedsCx(type, retVal=False):
         return any(typeNeedsCx(t) for t in type.unroll().flatMemberTypes)
     if retVal and type.isSpiderMonkeyInterface():
         return True
     return type.isCallback() or type.isAny() or type.isObject()
 
 # Returns a tuple consisting of a CGThing containing the type of the return
 # value, or None if there is no need for a return value, and a boolean signaling
 # whether the return value is passed in an out parameter.
+#
+# Whenever this is modified, please update CGExampleMember.getReturnType as
+# needed
 def getRetvalDeclarationForType(returnType, descriptorProvider,
                                 resultAlreadyAddRefed):
     if returnType is None or returnType.isVoid():
         # Nothing to declare
         return None, False
     if returnType.isPrimitive() and returnType.tag() in builtinNames:
         result = CGGeneric(builtinNames[returnType.tag()])
         if returnType.nullable():
@@ -3057,16 +3064,21 @@ def getRetvalDeclarationForType(returnTy
         return result, True
     raise TypeError("Don't know how to declare return value for %s" %
                     returnType)
 
 def isResultAlreadyAddRefed(descriptor, extendedAttributes):
     # Default to already_AddRefed on the main thread, raw pointer in workers
     return not descriptor.workers and not 'resultNotAddRefed' in extendedAttributes
 
+def needCx(returnType, arguments, extendedAttributes):
+    return (typeNeedsCx(returnType, True) or
+            any(typeNeedsCx(a.type) for (a, _) in arguments) or
+            'implicitJSContext' in extendedAttributes)
+
 class CGCallGenerator(CGThing):
     """
     A class to generate an actual call to a C++ object.  Assumes that the C++
     object is stored in a variable whose name is given by the |object| argument.
 
     errorReport should be a CGThing for an error report or None if no
     error reporting is needed.
     """
@@ -3093,19 +3105,17 @@ class CGCallGenerator(CGThing):
             args.append(CGGeneric(name))
 
         # Return values that go in outparams go here
         if resultOutParam:
             args.append(CGGeneric("result"))
         if isFallible:
             args.append(CGGeneric("rv"))
 
-        needsCx = (typeNeedsCx(returnType, True) or
-                   any(typeNeedsCx(a.type) for (a, _) in arguments) or
-                   'implicitJSContext' in extendedAttributes)
+        needsCx = needCx(returnType, arguments, extendedAttributes)
 
         if not "cx" in argsPre and needsCx:
             args.prepend(CGGeneric("cx"))
 
         # Build up our actual call
         self.cgRoot = CGList([], "\n")
 
         call = CGGeneric(nativeMethodName)
@@ -3532,16 +3542,20 @@ class FakeArgument():
         self.type = type
         self.optional = False
         self.variadic = False
         self.defaultValue = None
         self.treatNullAs = interfaceMember.treatNullAs
         self.treatUndefinedAs = interfaceMember.treatUndefinedAs
         self.enforceRange = False
         self.clamp = False
+        class FakeIdentifier():
+            def __init__(self):
+                self.name = "arg"
+        self.identifier = FakeIdentifier()
 
 class CGSetterCall(CGPerSignatureCall):
     """
     A class to generate a native object setter call for a particular IDL
     setter.
     """
     def __init__(self, argType, nativeMethodName, descriptor, attr):
         CGPerSignatureCall.__init__(self, None, [],
@@ -3632,21 +3646,26 @@ class CGSpecializedMethod(CGAbstractStat
         self.method = method
         name = method.identifier.name
         args = [Argument('JSContext*', 'cx'), Argument('JSHandleObject', 'obj'),
                 Argument('%s*' % descriptor.nativeType, 'self'),
                 Argument('unsigned', 'argc'), Argument('JS::Value*', 'vp')]
         CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool', args)
 
     def definition_body(self):
-        name = self.method.identifier.name
-        nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name))
+        nativeName = CGSpecializedMethod.makeNativeName(self.descriptor,
+                                                        self.method)
         return CGMethodCall([], nativeName, self.method.isStatic(),
                             self.descriptor, self.method).define()
 
+    @staticmethod
+    def makeNativeName(descriptor, method):
+        name = method.identifier.name
+        return MakeNativeName(descriptor.binaryNames.get(name, name))
+
 class CGGenericGetter(CGAbstractBindingMethod):
     """
     A class for generating the C++ code for an IDL attribute getter.
     """
     def __init__(self, descriptor, lenientThis=False):
         args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
                 Argument('JS::Value*', 'vp')]
         if lenientThis:
@@ -3677,29 +3696,33 @@ class CGSpecializedGetter(CGAbstractStat
         name = 'get_' + attr.identifier.name
         args = [ Argument('JSContext*', 'cx'),
                  Argument('JSHandleObject', 'obj'),
                  Argument('%s*' % descriptor.nativeType, 'self'),
                  Argument('JS::Value*', 'vp') ]
         CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
 
     def definition_body(self):
-        name = self.attr.identifier.name
-        nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name))
+        nativeName = CGSpecializedGetter.makeNativeName(self.descriptor,
+                                                        self.attr)
+        return CGIndenter(CGGetterCall(self.attr.type, nativeName,
+                                       self.descriptor, self.attr)).define()
+
+    @staticmethod
+    def makeNativeName(descriptor, attr):
+        name = attr.identifier.name
+        nativeName = MakeNativeName(descriptor.binaryNames.get(name, name))
         # resultOutParam does not depend on whether resultAlreadyAddRefed is set
-        (_, resultOutParam) = getRetvalDeclarationForType(self.attr.type,
-                                                          self.descriptor,
+        (_, resultOutParam) = getRetvalDeclarationForType(attr.type, descriptor,
                                                           False)
         infallible = ('infallible' in
-                      self.descriptor.getExtendedAttributes(self.attr,
-                                                            getter=True))
-        if resultOutParam or self.attr.type.nullable() or not infallible:
+                      descriptor.getExtendedAttributes(attr, getter=True))
+        if resultOutParam or attr.type.nullable() or not infallible:
             nativeName = "Get" + nativeName
-        return CGIndenter(CGGetterCall(self.attr.type, nativeName,
-                                       self.descriptor, self.attr)).define()
+        return nativeName
 
 class CGGenericSetter(CGAbstractBindingMethod):
     """
     A class for generating the C++ code for an IDL attribute setter.
     """
     def __init__(self, descriptor, lenientThis=False):
         args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
                 Argument('JS::Value*', 'vp')]
@@ -3739,21 +3762,26 @@ class CGSpecializedSetter(CGAbstractStat
         name = 'set_' + attr.identifier.name
         args = [ Argument('JSContext*', 'cx'),
                  Argument('JSHandleObject', 'obj'),
                  Argument('%s*' % descriptor.nativeType, 'self'),
                  Argument('JS::Value*', 'argv')]
         CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
 
     def definition_body(self):
-        name = self.attr.identifier.name
-        nativeName = "Set" + MakeNativeName(self.descriptor.binaryNames.get(name, name))
+        nativeName = CGSpecializedSetter.makeNativeName(self.descriptor,
+                                                        self.attr)
         return CGIndenter(CGSetterCall(self.attr.type, nativeName,
                                        self.descriptor, self.attr)).define()
 
+    @staticmethod
+    def makeNativeName(descriptor, attr):
+        name = attr.identifier.name
+        return "Set" + MakeNativeName(descriptor.binaryNames.get(name, name))
+
 def memberIsCreator(member):
     return member.getExtendedAttribute("Creator") is not None
 
 class CGMemberJITInfo(CGThing):
     """
     A class for generating the JITInfo for a property that points to
     our specialized getter and setter.
     """
@@ -5775,16 +5803,408 @@ class CGBindingRoot(CGThing):
         # Store the final result.
         self.root = curr
 
     def declare(self):
         return stripTrailingWhitespace(self.root.declare())
     def define(self):
         return stripTrailingWhitespace(self.root.define())
 
+class CGExampleMember(CGThing):
+    def __init__(self, descriptor, member, name, signatures, extendedAttrs):
+        self.descriptor = descriptor
+        self.member = member
+        self.name = name
+        self.signatures = signatures
+        self.extendedAttrs = extendedAttrs
+        self.resultAlreadyAddRefed = isResultAlreadyAddRefed(self.descriptor,
+                                                             self.extendedAttrs)
+
+    def define(self):
+        static = "static " if self.member.isStatic() else ""
+        # Mark our getters, which are attrs that have a non-void return type,
+        # as const.
+        if self.member.isAttr() and not self.signatures[0][0].isVoid():
+            const = " const"
+        else:
+            const = ""
+        return "\n".join("%s%s %s(%s)%s;" %
+                         (static,
+                          self.getReturnType(s[0], False),
+                          self.name,
+                          self.getArgs(s[0], s[1]),
+                          const) for s in self.signatures)
+
+    def getReturnType(self, type, isMember):
+        if type.isVoid():
+            return "void"
+        if type.isPrimitive() and type.tag() in builtinNames:
+            result = CGGeneric(builtinNames[type.tag()])
+            if type.nullable():
+                result = CGWrapper(result, pre="Nullable<", post=">")
+            return result.define()
+        if type.isString():
+            if isMember:
+                return "nsString"
+            # Outparam
+            return "void"
+        if type.isEnum():
+            if type.nullable():
+                raise TypeError("We don't support nullable enum return values")
+            return type.inner.identifier.name
+        if type.isGeckoInterface():
+            nativeType = self.descriptor.getDescriptor(
+                type.unroll().inner.identifier.name).nativeType
+            # Now trim off unnecessary namespaces
+            nativeType = nativeType.split("::")
+            if nativeType[0] == "mozilla":
+                nativeType.pop(0)
+                if nativeType[0] == "dom":
+                    nativeType.pop(0)
+            result = CGGeneric("::".join(nativeType))
+            if self.resultAlreadyAddRefed:
+                if isMember:
+                    holder = "nsRefPtr"
+                else:
+                    holder = "already_AddRefed"
+                if memberIsCreator(self.member):
+                    warning = ""
+                else:
+                    warning = "// Mark this as resultNotAddRefed to return raw pointers\n"
+                result = CGWrapper(result,
+                                   pre=("%s%s<" % (warning, holder)),
+                                   post=">")
+            else:
+                result = CGWrapper(result, post="*")
+            return result.define()
+        if type.isCallback():
+            # XXXbz we're going to assume that callback types are always
+            # nullable for now.
+            return "JSObject*"
+        if type.isAny():
+            return "JS::Value"
+        if type.isObject() or type.isSpiderMonkeyInterface():
+            return "JSObject*"
+        if type.isSequence():
+            assert not isMember
+            # Outparam
+            return "void"
+        raise TypeError("Don't know how to declare return value for %s" %
+                        type)
+
+    def getArgs(self, returnType, argList):
+        args = [self.getArg(arg) for arg in argList]
+        # Now the outparams
+        if returnType.isString():
+            args.append("nsString& retval")
+        elif returnType.isSequence():
+            nullable = returnType.nullable()
+            if nullable:
+                returnType = returnType.inner
+            # And now the actual underlying type
+            elementDecl = self.getReturnType(returnType.inner, True)
+            type = CGWrapper(CGGeneric(elementDecl), pre="nsTArray< ", post=" >")
+            if nullable:
+                type = CGWrapper(type, pre="Nullable< ", post=" >")
+            args.append("%s& retval" % type.define())
+        # And the ErrorResult
+        if not 'infallible' in self.extendedAttrs:
+            args.append("ErrorResult& rv")
+        # And if we're static, a global
+        if self.member.isStatic():
+            args.insert(0, "nsISupports* global")
+        # And jscontext bits.  needCx expects a list of tuples, in each of which
+        # the first element is the actual argument
+        if needCx(returnType, ((a, "") for a in argList), self.extendedAttrs):
+            args.insert(0, "JSContext* cx")
+        return ", ".join(args)
+
+    def doGetArgType(self, type, optional, isMember):
+        """
+        The main work of getArgType.  Returns a string type decl, whether this
+        is a const ref, as well as whether the type should be wrapped in
+        Nullable as needed.
+        """
+        if type.isArray():
+            raise TypeError("Can't handle array arguments yet")
+
+        if type.isSequence():
+            nullable = type.nullable()
+            if nullable:
+                type = type.inner
+            elementType = type.inner
+            decl = CGWrapper(self.getArgType(elementType, False, True)[0],
+                             pre="Sequence< ", post=" >")
+            return decl.define(), True, True
+
+        if type.isUnion():
+            if type.nullable():
+                type = type.inner
+            return str(type), True, True
+
+        if type.isGeckoInterface():
+            iface = type.unroll().inner
+            argIsPointer = type.nullable() or iface.isExternal()
+            forceOwningType = iface.isCallback() or isMember
+            if argIsPointer:
+                if (optional or isMember) and forceOwningType:
+                    typeDecl = "nsRefPtr<%s>"
+                else:
+                    typeDecl = "%s*"
+            else:
+                if optional or isMember:
+                    if forceOwningType:
+                        typeDecl = "OwningNonNull<%s>"
+                    else:
+                        typeDecl = "NonNull<%s>"
+                else:
+                    typeDecl = "%s&"
+            return (typeDecl % iface.identifier.name), False, False
+
+        if type.isSpiderMonkeyInterface():
+            assert not isMember
+            if type.nullable():
+                typeDecl = "%s*"
+            else:
+                typeDecl = "%s&"
+            return (typeDecl % type.name), False, False
+
+        if type.isString():
+            if isMember:
+                declType = "nsString"
+            else:
+                declType = "nsAString"
+            return declType, True, False
+
+        if type.isEnum():
+            return type.inner.identifier.name, False, True
+
+        if type.isCallback():
+            return "JSObject*", False, False
+
+        if type.isAny():
+            return "JS::Value", False, False
+
+        if type.isObject():
+            if type.nullable():
+                declType = "%s*"
+            else:
+                if optional:
+                    declType = "NonNull<%s>"
+                else:
+                    declType = "%s&"
+            return (declType % "JSObject"), False, False
+
+        if type.isDictionary():
+            return type.inner.identifier.name, True, True
+
+        assert type.isPrimitive()
+
+        return builtinNames[type.tag()], False, True
+
+    def getArgType(self, type, optional, isMember):
+        """
+        Get the type of an argument declaration.  Returns the type CGThing, and
+        whether this should be a const ref.
+        """
+        (decl, ref, handleNullable) = self.doGetArgType(type, optional, isMember)
+        decl = CGGeneric(decl)
+        if handleNullable and type.nullable():
+            decl = CGWrapper(decl, pre="Nullable< ", post=" >")
+            ref = True
+        if optional:
+            decl = CGWrapper(decl, pre="Optional< ", post=" >")
+            ref = True
+        return (decl, ref)
+
+    def getArg(self, arg):
+        """
+        Get the full argument declaration for an argument
+        """
+        (decl, ref) = self.getArgType(arg.type,
+                                      arg.optional and not arg.defaultValue,
+                                      False)
+        if ref:
+            decl = CGWrapper(decl, pre="const ", post="&")
+
+        return "%s %s" % (decl.define(), arg.identifier.name)
+
+
+class CGExampleMethod(CGExampleMember):
+    def __init__(self, descriptor, method):
+        CGExampleMember.__init__(self, descriptor, method,
+                                 CGSpecializedMethod.makeNativeName(descriptor,
+                                                                    method),
+                                 method.signatures(),
+                                 descriptor.getExtendedAttributes(method))
+
+class CGExampleGetter(CGExampleMember):
+    def __init__(self, descriptor, attr):
+        CGExampleMember.__init__(self, descriptor, attr,
+                                 CGSpecializedGetter.makeNativeName(descriptor,
+                                                                    attr),
+                                 [(attr.type, [])],
+                                 descriptor.getExtendedAttributes(attr,
+                                                                  getter=True))
+
+class CGExampleSetter(CGExampleMember):
+    def __init__(self, descriptor, attr):
+        CGExampleMember.__init__(self, descriptor, attr,
+                                 CGSpecializedSetter.makeNativeName(descriptor,
+                                                                    attr),
+                                 [(BuiltinTypes[IDLBuiltinType.Types.void],
+                                   [FakeArgument(attr.type, attr)])],
+                                 descriptor.getExtendedAttributes(attr,
+                                                                  setter=True))
+
+class CGExampleClass(CGThing):
+    """
+    Codegen for the actual example class implemenation for this descriptor
+    """
+    def __init__(self, descriptor):
+        self.descriptor = descriptor
+
+        iface = descriptor.interface
+
+        methodDecls = []
+        if iface.ctor():
+            methodDecls.append(CGExampleMethod(descriptor, iface.ctor()))
+        for m in iface.members:
+            if m.isMethod():
+                methodDecls.append(CGExampleMethod(descriptor, m))
+            elif m.isAttr():
+                methodDecls.append(CGExampleGetter(descriptor, m))
+                if not m.readonly:
+                    methodDecls.append(CGExampleSetter(descriptor, m))
+
+        self.decl = CGIndenter(CGList(methodDecls, "\n\n"))
+
+        if descriptor.wrapperCache:
+            wrapFunc = ("  virtual JSObject* WrapObject(JSContext* aCx, JSObject* aScope,\n"
+                        "                               bool* aTriedToWrap);\n")
+        else:
+            wrapFunc = "  virtual JSObject* WrapObject(JSContext* aCx, JSObject* aScope);\n"
+
+        classDecl = CGWrapper(
+            CGGeneric("public nsISupports,\n"
+                      "public nsWrapperCache"),
+            pre=("class %s MOZ_FINAL : " % descriptor.name),
+            post=(string.Template(
+                    "\n"
+                    "{\n"
+                    "public:\n"
+                    "  ${ifaceName}();\n"
+                    "  ~${ifaceName}();\n"
+                    "\n"
+                    "  NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n"
+                    "  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(${ifaceName})\n"
+                    "\n"
+                    "  void* GetParentObject() const\n"
+                    "  {\n"
+                    "    // TODO: return something sensible here, and change the return type\n"
+                    "    return somethingSensible;\n"
+                    "  }\n"
+                    "\n" +
+                    wrapFunc +
+                    "\n").substitute({ "ifaceName": descriptor.name })),
+            reindent=True)
+
+        self.decl = CGWrapper(self.decl,
+                              pre=("\n" + classDecl.define()),
+                              post="\n};\n\n")
+
+    def declare(self):
+        return self.decl.define()
+
+    def define(self):
+        classImpl = """
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(${ifaceName})
+NS_IMPL_CYCLE_COLLECTING_ADDREF(${ifaceName})
+NS_IMPL_CYCLE_COLLECTING_RELEASE(${ifaceName})
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${ifaceName})
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+${ifaceName}::${ifaceName}()
+{
+  SetIsDOMBinding();
+}
+
+${ifaceName}::~${ifaceName}()
+{
+}
+"""
+        if self.descriptor.wrapperCache:
+            classImpl += """
+JSObject*
+${ifaceName}::WrapObject(JSContext* aCx, JSObject* aScope, bool* aTriedToWrap)
+{
+  return ${ifaceName}Binding::Wrap(aCx, aScope, this, aTriedToWrap);
+}
+
+"""
+        else:
+            classImpl += """
+JSObject*
+${ifaceName}::WrapObject(JSContext* aCx, JSObject* aScope)
+{
+  return ${ifaceName}Binding::Wrap(aCx, aScope, this);
+}
+
+"""
+        return string.Template(classImpl).substitute(
+            { "ifaceName": self.descriptor.name }
+            )
+
+
+class CGExampleRoot(CGThing):
+    """
+    Root codegen class for example implementation generation.  Instantiate the
+    class and call declare or define to generate header or cpp code,
+    respectively.
+    """
+    def __init__(self, config, interfaceName):
+        # Let's assume we're not doing workers stuff
+        descriptor = config.getDescriptor(interfaceName, False)
+
+        self.root = CGExampleClass(descriptor)
+
+        self.root = CGNamespace.build(["mozilla", "dom"], self.root);
+
+        self.root = CGList([CGClassForwardDeclare("JSContext", isStruct=True),
+                            self.root], "\n")
+
+        # Throw in our #includes
+        self.root = CGHeaders([], [],
+                              [ "nsWrapperCache.h",
+                                "nsCycleCollectionParticipant.h",
+                                "mozilla/Attributes.h" ],
+                              [ "%s.h" % interfaceName,
+                                "mozilla/dom/%sBinding.h" % interfaceName,
+                                "nsContentUtils.h" ], self.root);
+
+        # In the header, #pragma once before everything
+        self.root = CGWrapper(self.root, declarePre="#pragma once\n\n")
+
+        # And our license block comes before everything else
+        self.root = CGWrapper(self.root, pre="""/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+""")
+
+    def declare(self):
+        return self.root.declare()
+
+    def define(self):
+        return self.root.define()
+
 
 class GlobalGenRoots():
     """
     Roots for global codegen.
 
     To generate code, call the method associated with the target, and then
     call the appropriate define/declare method.
     """
copy from dom/bindings/BindingGen.py
copy to dom/bindings/ExampleGen.py
--- a/dom/bindings/BindingGen.py
+++ b/dom/bindings/ExampleGen.py
@@ -1,69 +1,52 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import os
 import cPickle
 import WebIDL
 from Configuration import *
-from Codegen import CGBindingRoot, replaceFileIfChanged
+from Codegen import CGExampleRoot, replaceFileIfChanged
 # import Codegen in general, so we can set a variable on it
 import Codegen
 
-def generate_binding_header(config, outputprefix, webidlfile):
+def generate_interface_example(config, interfaceName):
     """
     |config| Is the configuration object.
-    |outputprefix| is a prefix to use for the header guards and filename.
+    |interfaceName| is the name of the interface we're generating an example for.
     """
 
-    filename = outputprefix + ".h"
-    root = CGBindingRoot(config, outputprefix, webidlfile)
-    if replaceFileIfChanged(filename, root.declare()):
-        print "Generating binding header: %s" % (filename)
-
-def generate_binding_cpp(config, outputprefix, webidlfile):
-    """
-    |config| Is the configuration object.
-    |outputprefix| is a prefix to use for the header guards and filename.
-    """
-
-    filename = outputprefix + ".cpp"
-    root = CGBindingRoot(config, outputprefix, webidlfile)
-    if replaceFileIfChanged(filename, root.define()):
-        print "Generating binding implementation: %s" % (filename)
+    root = CGExampleRoot(config, interfaceName)
+    exampleHeader = interfaceName + "-example.h"
+    exampleImpl = interfaceName + "-example.cpp"
+    replaceFileIfChanged(exampleHeader, root.declare())
+    replaceFileIfChanged(exampleImpl, root.define())
 
 def main():
 
     # Parse arguments.
     from optparse import OptionParser
-    usagestring = "usage: %prog [header|cpp] configFile outputPrefix webIDLFile"
+    usagestring = "usage: %prog configFile interfaceName"
     o = OptionParser(usage=usagestring)
     o.add_option("--verbose-errors", action='store_true', default=False,
                  help="When an error happens, display the Python traceback.")
     (options, args) = o.parse_args()
 
-    if len(args) != 4 or (args[0] != "header" and args[0] != "cpp"):
+    if len(args) != 2:
         o.error(usagestring)
-    buildTarget = args[0]
-    configFile = os.path.normpath(args[1])
-    outputPrefix = args[2]
-    webIDLFile = os.path.normpath(args[3])
+    configFile = os.path.normpath(args[0])
+    interfaceName = args[1]
 
     # Load the parsing results
     f = open('ParserResults.pkl', 'rb')
     parserData = cPickle.load(f)
     f.close()
 
     # Create the configuration data.
     config = Configuration(configFile, parserData)
 
-    # Generate the prototype classes.
-    if buildTarget == "header":
-        generate_binding_header(config, outputPrefix, webIDLFile);
-    elif buildTarget == "cpp":
-        generate_binding_cpp(config, outputPrefix, webIDLFile);
-    else:
-        assert False # not reached
+    # Generate the example class.
+    generate_interface_example(config, interfaceName)
 
 if __name__ == '__main__':
     main()
--- a/dom/bindings/Makefile.in
+++ b/dom/bindings/Makefile.in
@@ -123,16 +123,24 @@ CSS2Properties.webidl: $(topsrcdir)/layo
 	PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
 	  $(PLY_INCLUDE) -I$(srcdir)/parser \
 	  $(srcdir)/BindingGen.py cpp \
 	  $(srcdir)/Bindings.conf $*Binding \
 	  $*.webidl
 
 $(globalgen_targets): ParserResults.pkl
 
+%-example: $(bindinggen_dependencies) \
+           $(all_webidl_files) \
+           $(NULL)
+	PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
+	  $(PLY_INCLUDE) -I$(srcdir)/parser \
+	  $(srcdir)/ExampleGen.py \
+	  $(srcdir)/Bindings.conf $*
+
 CACHE_DIR = _cache
 
 globalgen_dependencies := \
   GlobalGen.py \
   Bindings.conf \
   Configuration.py \
   Codegen.py \
   parser/WebIDL.py \
@@ -155,15 +163,17 @@ ParserResults.pkl: $(globalgen_dependenc
 GARBAGE += \
   $(binding_header_files) \
   $(binding_cpp_files) \
   $(all_webidl_files) \
   $(globalgen_targets) \
   ParserResults.pkl \
   webidlyacc.py \
   parser.out \
+  $(wildcard *-example.h) \
+  $(wildcard *-example.cpp) \
   $(NULL)
 
 # Make sure all binding header files are created during the export stage, so we
 # don't have issues with .cpp files being compiled before we've generated the
 # headers they depend on.  This is really only needed for the test files, since
 # the non-test headers are all exported above anyway.
 export:: $(binding_header_files)
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -675,17 +675,18 @@ class IDLInterface(IDLObjectWithScope):
 
                 args = attr.args() if attr.hasArgs() else []
 
                 retType = IDLWrapperType(self.location, self)
                 
                 identifier = IDLUnresolvedIdentifier(self.location, "constructor",
                                                      allowForbidden=True)
 
-                method = IDLMethod(self.location, identifier, retType, args)
+                method = IDLMethod(self.location, identifier, retType, args,
+                                   static=True)
                 # Constructors are always Creators and are always
                 # assumed to be able to throw (since there's no way to
                 # indicate otherwise) and never have any other
                 # extended attributes.
                 method.addExtendedAttributes(
                     [IDLExtendedAttribute(self.location, ("Creator",)),
                      IDLExtendedAttribute(self.location, ("Throws",))])
                 method.resolve(self)
--- a/dom/bindings/parser/tests/test_constructor.py
+++ b/dom/bindings/parser/tests/test_constructor.py
@@ -6,17 +6,17 @@ def WebIDLTest(parser, harness):
                    "Should be an IDLArgument")
         harness.check(argument.identifier.QName(), QName, "Argument has the right QName")
         harness.check(argument.identifier.name, name, "Argument has the right name")
         harness.check(str(argument.type), type, "Argument has the right return type")
         harness.check(argument.optional, optional, "Argument has the right optional value")
         harness.check(argument.variadic, variadic, "Argument has the right variadic value")
 
     def checkMethod(method, QName, name, signatures,
-                    static=False, getter=False, setter=False, creator=False,
+                    static=True, getter=False, setter=False, creator=False,
                     deleter=False, legacycaller=False, stringifier=False):
         harness.ok(isinstance(method, WebIDL.IDLMethod),
                    "Should be an IDLMethod")
         harness.ok(method.isMethod(), "Method is a method")
         harness.ok(not method.isAttr(), "Method is not an attr")
         harness.ok(not method.isConst(), "Method is not a const")
         harness.check(method.identifier.QName(), QName, "Method has the right QName")
         harness.check(method.identifier.name, name, "Method has the right name")
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -107,17 +107,18 @@ public:
   // Constructors
   static
   already_AddRefed<TestInterface> Constructor(nsISupports*, ErrorResult&);
   static
   already_AddRefed<TestInterface> Constructor(nsISupports*, const nsAString&,
                                               ErrorResult&);
   static
   already_AddRefed<TestInterface> Constructor(nsISupports*, uint32_t,
-                                              Nullable<bool>&, ErrorResult&);
+                                              const Nullable<bool>&,
+                                              ErrorResult&);
   static
   already_AddRefed<TestInterface> Constructor(nsISupports*, TestInterface*,
                                               ErrorResult&);
   static
   already_AddRefed<TestInterface> Constructor(nsISupports*,
                                               TestNonCastableInterface&,
                                               ErrorResult&);
   /*  static
@@ -130,17 +131,17 @@ public:
   // Integer types
   int8_t ReadonlyByte();
   int8_t WritableByte();
   void SetWritableByte(int8_t);
   void PassByte(int8_t);
   int8_t ReceiveByte();
   void PassOptionalByte(const Optional<int8_t>&);
   void PassOptionalByteWithDefault(int8_t);
-  void PassNullableByte(Nullable<int8_t>&);
+  void PassNullableByte(const Nullable<int8_t>&);
   void PassOptionalNullableByte(const Optional< Nullable<int8_t> >&);
 
   int16_t ReadonlyShort();
   int16_t WritableShort();
   void SetWritableShort(int16_t);
   void PassShort(int16_t);
   int16_t ReceiveShort();
   void PassOptionalShort(const Optional<int16_t>&);
@@ -321,17 +322,17 @@ public:
   // String types
   void PassString(const nsAString&);
   void PassNullableString(const nsAString&);
   void PassOptionalString(const Optional<nsAString>&);
   void PassOptionalStringWithDefaultValue(const nsAString&);
   void PassOptionalNullableString(const Optional<nsAString>&);
   void PassOptionalNullableStringWithDefaultValue(const nsAString&);
 
-  // Enumarated types
+  // Enumerated types
   void PassEnum(TestEnum);
   void PassOptionalEnum(const Optional<TestEnum>&);
   void PassEnumWithDefault(TestEnum);
   TestEnum ReceiveEnum();
   TestEnum EnumAttribute();
   TestEnum ReadonlyEnumAttribute();
   void SetEnumAttribute(TestEnum);
 
@@ -435,16 +436,17 @@ private:
   // We add signatures here that _could_ start matching if the codegen
   // got data types wrong.  That way if it ever does we'll have a call
   // to these private deleted methods and compilation will fail.
   void SetReadonlyByte(int8_t) MOZ_DELETE;
   template<typename T>
   void SetWritableByte(T) MOZ_DELETE;
   template<typename T>
   void PassByte(T) MOZ_DELETE;
+  void PassNullableByte(Nullable<int8_t>&) MOZ_DELETE;
   template<typename T>
   void PassOptionalByte(const Optional<T>&) MOZ_DELETE;
   template<typename T>
   void PassOptionalByteWithDefault(T) MOZ_DELETE;
 
   void SetReadonlyShort(int16_t) MOZ_DELETE;
   template<typename T>
   void SetWritableShort(T) MOZ_DELETE;
--- a/dom/camera/DOMCameraManager.cpp
+++ b/dom/camera/DOMCameraManager.cpp
@@ -39,17 +39,17 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMCam
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMCameraManager)
 
 /**
  * Global camera logging object
  *
  * Set the NSPR_LOG_MODULES environment variable to enable logging
  * in a debug build, e.g. NSPR_LOG_MODULES=Camera:5
  */
-PRLogModuleInfo* gCameraLog = PR_LOG_DEFINE("Camera");
+PRLogModuleInfo* gCameraLog = PR_NewLogModule("Camera");
 
 /**
  * nsDOMCameraManager::GetListOfCameras
  * is implementation-specific, and can be found in (e.g.)
  * GonkCameraManager.cpp and FallbackCameraManager.cpp.
  */
 
 WindowTable nsDOMCameraManager::sActiveWindows;
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -492,17 +492,17 @@ class ThreadLocalJSRuntime
   ThreadLocalJSRuntime()
   : mRuntime(NULL), mContext(NULL), mGlobal(NULL)
   {
       MOZ_COUNT_CTOR(ThreadLocalJSRuntime);
   }
 
   nsresult Init()
   {
-    mRuntime = JS_NewRuntime(sRuntimeHeapSize);
+    mRuntime = JS_NewRuntime(sRuntimeHeapSize, JS_NO_HELPER_THREADS);
     NS_ENSURE_TRUE(mRuntime, NS_ERROR_OUT_OF_MEMORY);
 
     mContext = JS_NewContext(mRuntime, 0);
     NS_ENSURE_TRUE(mContext, NS_ERROR_OUT_OF_MEMORY);
 
     JSAutoRequest ar(mContext);
 
     mGlobal = JS_NewGlobalObject(mContext, &sGlobalClass, NULL);
--- a/dom/interfaces/base/nsIDOMWindow.idl
+++ b/dom/interfaces/base/nsIDOMWindow.idl
@@ -27,17 +27,17 @@ interface nsIDOMMozURLProperty : nsISupp
  * The nsIDOMWindow interface is the primary interface for a DOM
  * window object. It represents a single window object that may
  * contain child windows if the document in the window contains a
  * HTML frameset document or if the document contains iframe elements.
  *
  * @see <http://www.whatwg.org/html/#window>
  */
 
-[scriptable, uuid(25404a1b-6c73-4850-af95-69aa095c8ad0)]
+[scriptable, uuid(43933989-912e-4b6a-b889-3c9fc9dd9ed4)]
 interface nsIDOMWindow : nsISupports
 {
   // the current browsing context
   readonly attribute nsIDOMWindow                       window;
 
   /* [replaceable] self */
   readonly attribute nsIDOMWindow                       self;
 
@@ -311,16 +311,18 @@ interface nsIDOMWindow : nsISupports
 
 
   // CSSOM
   /**
    * @see <http://dev.w3.org/csswg/cssom/#dom-window-getcomputedstyle>
    */
   nsIDOMCSSStyleDeclaration getComputedStyle(in nsIDOMElement elt, 
                                              [optional] in DOMString pseudoElt);
+  nsIDOMCSSStyleDeclaration getDefaultComputedStyle(in nsIDOMElement elt,
+                                                    [optional] in DOMString pseudoElt);
 
 
   // Mozilla extensions
   /**
    * Get the window root for this window. This is useful for hooking
    * up event listeners to this window and every other window nested
    * in the window root.
    */
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -92,16 +92,19 @@ public:
     if (mAudioSource) {
       mAudioSource->Stop();
       mAudioSource->Deallocate();
     }
     if (mVideoSource) {
       mVideoSource->Stop();
       mVideoSource->Deallocate();
     }
+    // Do this after stopping all tracks with EndTrack()
+    mStream->GetStream()->AsSourceStream()->Finish();
+
     nsCOMPtr<GetUserMediaNotificationEvent> event =
       new GetUserMediaNotificationEvent(GetUserMediaNotificationEvent::STOPPING);
 
     NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
   }
 
   void
   NotifyConsumptionChanged(MediaStreamGraph* aGraph, Consumption aConsuming)
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/crashtests/791278.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=791278
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Simple PeerConnection Video Test - Invalid callback</title>
+  <script type="application/javascript">
+    var pc1 = new mozPeerConnection();
+    pc1.setLocalDescription(function() {});
+
+    var pc2 = new mozPeerConnection();
+    pc2.setRemoteDescription(function() {});
+  </script>
+</head>
+
+<body>
+</body>
+</html>
--- a/dom/media/tests/crashtests/crashtests.list
+++ b/dom/media/tests/crashtests/crashtests.list
@@ -1,1 +1,2 @@
 pref(media.peerconnection.enabled,true) load 791270.html
+pref(media.peerconnection.enabled,true) load 791278.html
--- a/dom/messages/SystemMessageInternal.js
+++ b/dom/messages/SystemMessageInternal.js
@@ -46,16 +46,17 @@ function SystemMessageInternal() {
   // list of pending messages for each page here also.
   this._pages = [];
   this._listeners = {};
 
   this._webappsRegistryReady = false;
   this._bufferedSysMsgs = [];
 
   Services.obs.addObserver(this, "xpcom-shutdown", false);
+  Services.obs.addObserver(this, "webapps-registry-start", false);
   Services.obs.addObserver(this, "webapps-registry-ready", false);
   kMessages.forEach(function(aMsg) {
     ppmm.addMessageListener(aMsg, this);
   }, this);
 
   Services.obs.notifyObservers(this, "system-message-internal-ready", null);
 }
 
@@ -231,37 +232,41 @@ SystemMessageInternal.prototype = {
 
   observe: function observe(aSubject, aTopic, aData) {
     switch (aTopic) {
       case "xpcom-shutdown":
         kMessages.forEach(function(aMsg) {
           ppmm.removeMessageListener(aMsg, this);
         }, this);
         Services.obs.removeObserver(this, "xpcom-shutdown");
+        Services.obs.removeObserver(this, "webapps-registry-start");
         Services.obs.removeObserver(this, "webapps-registry-ready");
         ppmm = null;
         this._pages = null;
         this._bufferedSysMsgs = null;
         break;
+      case "webapps-registry-start":
+        this._webappsRegistryReady = false;
+        break;
       case "webapps-registry-ready":
         // After the webapps' registration has been done for sure,
         // re-fire the buffered system messages if there is any.
         this._webappsRegistryReady = true;
         this._bufferedSysMsgs.forEach(function(aSysMsg) {
           switch (aSysMsg.how) {
             case "send":
               this.sendMessage(
                 aSysMsg.type, aSysMsg.msg, aSysMsg.pageURI, aSysMsg.manifestURI);
               break;
             case "broadcast":
               this.broadcastMessage(aSysMsg.type, aSysMsg.msg);
               break;
           }
         }, this);
-        this._bufferedSysMsgs = null;
+        this._bufferedSysMsgs.length = 0;
         break;
     }
   },
 
   _openAppPage: function _openAppPage(aPage, aMessage, aMessageID) {
     // Queue the message for this page because we've never known if an app is
     // opened or not. We'll clean it up when the app has already received it.
     aPage.pendingMessages.push({ msg: aMessage, msgID: aMessageID });
new file mode 100644
--- /dev/null
+++ b/dom/time/DateCacheCleaner.cpp
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Hal.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "DateCacheCleaner.h"
+
+#include "nsIJSContextStack.h"
+#include "mozilla/StaticPtr.h"
+
+using namespace mozilla::hal;
+
+namespace mozilla {
+namespace dom {
+namespace time {
+
+class DateCacheCleaner : public SystemTimeChangeObserver
+{
+public:
+  DateCacheCleaner()
+  {
+    RegisterSystemTimeChangeObserver(this);
+  }
+
+  ~DateCacheCleaner()
+  {
+    UnregisterSystemTimeChangeObserver(this);
+  }
+  void Notify(const SystemTimeChange& aReason)
+  {
+    if (aReason == SYS_TIME_CHANGE_CLOCK) {
+      return;
+    }
+
+    nsCOMPtr<nsIThreadJSContextStack> stack =
+      do_GetService("@mozilla.org/js/xpc/ContextStack;1");
+    if (!stack) {
+      NS_WARNING("Failed to get JSContextStack");
+    }
+    JSContext *cx = stack->GetSafeJSContext();
+    if (!cx) {
+      NS_WARNING("Failed to GetSafeJSContext");
+    }
+    JS_ClearDateCaches(cx);
+  }
+
+};
+
+StaticAutoPtr<DateCacheCleaner> sDateCacheCleaner;
+
+void
+InitializeDateCacheCleaner()
+{
+  if (!sDateCacheCleaner) {
+    sDateCacheCleaner = new DateCacheCleaner();
+    ClearOnShutdown(&sDateCacheCleaner);
+  }
+}
+
+} // namespace time
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/time/DateCacheCleaner.h
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/*
+ * InitializeDateCacheCleaner registers DateCacheCleaner to 
+ * SystemTimeChangeObserver. When time zone is changed, DateCacheCleaner calls
+ * JS_ClearDateCaches to update the time zone information.
+ */
+
+namespace mozilla {
+namespace dom {
+namespace time {
+void InitializeDateCacheCleaner();
+} //namespace time
+} //namespace dom
+} //namespace mozilla
--- a/dom/time/Makefile.in
+++ b/dom/time/Makefile.in
@@ -19,21 +19,23 @@ FAIL_ON_WARNINGS := 1
 include $(topsrcdir)/dom/dom-config.mk
 
 EXPORTS_NAMESPACES = mozilla/dom/time
 
 CPPSRCS = \
   TimeManager.cpp \
   TimeService.cpp \
   TimeChangeObserver.cpp \
+  DateCacheCleaner.cpp \
   $(NULL)
 
 EXPORTS_mozilla/dom/time = \
   TimeService.h \
   TimeChangeObserver.h \
+  DateCacheCleaner.h \
   $(NULL)
 
 XPIDLSRCS = \
   nsIDOMNavigatorTime.idl \
   nsIDOMTimeManager.idl \
   nsITimeService.idl \
   $(NULL)
 
--- a/dom/webidl/WebGLRenderingContext.webidl
+++ b/dom/webidl/WebGLRenderingContext.webidl
@@ -772,34 +772,16 @@ interface WebGLContextEvent : Event {
 /*dictionary WebGLContextEventInit : EventInit {
     DOMString statusMessage;
     };*/
 
 
 // specific extension interfaces
 
 [NoInterfaceObject]
-interface WebGLExtensionStandardDerivatives {
-    const GLenum FRAGMENT_SHADER_DERIVATIVE_HINT_OES = 0x8B8B;
-};
-
-[NoInterfaceObject]
-interface WebGLExtensionLoseContext {
-    void loseContext();
-    void restoreContext();
-};
-
-[NoInterfaceObject]
-interface WebGLExtensionTextureFilterAnisotropic
-{
-    const GLenum TEXTURE_MAX_ANISOTROPY_EXT     = 0x84FE;
-    const GLenum MAX_TEXTURE_MAX_ANISOTROPY_EXT = 0x84FF;
-};
-
-[NoInterfaceObject]
 interface WebGLExtensionCompressedTextureS3TC
 {
     const GLenum COMPRESSED_RGB_S3TC_DXT1_EXT  = 0x83F0;
     const GLenum COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1;
     const GLenum COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2;
     const GLenum COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3;
 };
 
@@ -816,17 +798,42 @@ interface WebGLExtensionCompressedTextur
 {
     const GLenum COMPRESSED_RGB_PVRTC_4BPPV1  = 0x8C00;
     const GLenum COMPRESSED_RGB_PVRTC_2BPPV1  = 0x8C01;
     const GLenum COMPRESSED_RGBA_PVRTC_4BPPV1 = 0x8C02;
     const GLenum COMPRESSED_RGBA_PVRTC_2BPPV1 = 0x8C03;
 };
 
 [NoInterfaceObject]
+interface WebGLExtensionDebugRendererInfo
+{
+    const GLenum UNMASKED_VENDOR_WEBGL        = 0x9245;
+    const GLenum UNMASKED_RENDERER_WEBGL      = 0x9246;
+};
+
+[NoInterfaceObject]
 interface WebGLExtensionDepthTexture
 {
     const GLenum UNSIGNED_INT_24_8_WEBGL = 0x84FA;
 };
 
 [NoInterfaceObject]
+interface WebGLExtensionLoseContext {
+    void loseContext();
+    void restoreContext();
+};
+
+[NoInterfaceObject]
+interface WebGLExtensionTextureFilterAnisotropic
+{
+    const GLenum TEXTURE_MAX_ANISOTROPY_EXT     = 0x84FE;
+    const GLenum MAX_TEXTURE_MAX_ANISOTROPY_EXT = 0x84FF;
+};
+
+[NoInterfaceObject]
+interface WebGLExtensionStandardDerivatives {
+    const GLenum FRAGMENT_SHADER_DERIVATIVE_HINT_OES = 0x8B8B;
+};
+
+[NoInterfaceObject]
 interface WebGLExtensionTextureFloat
 {
 };
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -378,17 +378,17 @@ ContentSecurityPolicyAllows(JSContext* a
 JSContext*
 CreateJSContextForWorker(WorkerPrivate* aWorkerPrivate)
 {
   aWorkerPrivate->AssertIsOnWorkerThread();
   NS_ASSERTION(!aWorkerPrivate->GetJSContext(), "Already has a context!");
 
   // The number passed here doesn't matter, we're about to change it in the call
   // to JS_SetGCParameter.
-  JSRuntime* runtime = JS_NewRuntime(WORKER_DEFAULT_RUNTIME_HEAPSIZE);
+  JSRuntime* runtime = JS_NewRuntime(WORKER_DEFAULT_RUNTIME_HEAPSIZE, JS_NO_HELPER_THREADS);
   if (!runtime) {
     NS_WARNING("Could not create new runtime!");
     return nullptr;
   }
 
   // This is the real place where we set the max memory for the runtime.
   JS_SetGCParameter(runtime, JSGC_MAX_BYTES,
                     aWorkerPrivate->GetJSRuntimeHeapSize());
--- a/editor/libeditor/base/nsEditor.cpp
+++ b/editor/libeditor/base/nsEditor.cpp
@@ -2026,83 +2026,41 @@ nsEditor::GetPhonetic(nsAString& aPhonet
   if (mPhonetic)
     aPhonetic = *mPhonetic;
   else
     aPhonetic.Truncate(0);
 
   return NS_OK;
 }
 
-
-static nsresult
-GetEditorContentWindow(dom::Element *aRoot, nsIWidget **aResult)
-{
-  NS_ENSURE_TRUE(aRoot && aResult, NS_ERROR_NULL_POINTER);
-
-  *aResult = 0;
-
-  // Not ref counted
-  nsIFrame *frame = aRoot->GetPrimaryFrame();
-
-  NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
-
-  *aResult = frame->GetNearestWidget();
-  NS_ENSURE_TRUE(*aResult, NS_ERROR_FAILURE);
-
-  NS_ADDREF(*aResult);
-  return NS_OK;
-}
-
-nsresult
-nsEditor::GetWidget(nsIWidget **aWidget)
-{
-  NS_ENSURE_TRUE(aWidget, NS_ERROR_NULL_POINTER);
-  *aWidget = nullptr;
-
-  nsCOMPtr<nsIWidget> widget;
-  nsresult res = GetEditorContentWindow(GetRoot(), getter_AddRefs(widget));
-  NS_ENSURE_SUCCESS(res, res);
-  NS_ENSURE_TRUE(widget, NS_ERROR_NOT_AVAILABLE);
-
-  NS_ADDREF(*aWidget = widget);
-
-  return NS_OK;
-}
-
 NS_IMETHODIMP
 nsEditor::ForceCompositionEnd()
 {
-
-// We can test mInIMEMode and do some optimization for Mac and Window
-// Howerver, since UNIX support over-the-spot, we cannot rely on that 
-// flag for Unix.
-// We should use LookAndFeel to resolve this
-
-#if defined(XP_MACOSX) || defined(XP_WIN) || defined(XP_OS2)
-  // XXXmnakano see bug 558976, ResetInputState() has two meaning which are
-  // "commit the composition" and "cursor is moved".  This method name is
-  // "ForceCompositionEnd", so, ResetInputState() should be used only for the
-  // former here.  However, ResetInputState() is also used for the latter here
-  // because even if we don't have composition, we call ResetInputState() on
-  // Linux.  Currently, nsGtkIMModule can know the timing of the cursor move,
-  // so, the latter meaning should be gone and we should remove this #if.
-  if(! mInIMEMode)
-    return NS_OK;
-#endif
-
-  nsCOMPtr<nsIWidget> widget;
-  nsresult res = GetWidget(getter_AddRefs(widget));
-  NS_ENSURE_SUCCESS(res, res);
-
-  if (widget) {
-    res = widget->ResetInputState();
-    NS_ENSURE_SUCCESS(res, res);
+  nsCOMPtr<nsIPresShell> ps = GetPresShell();
+  if (!ps) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  nsPresContext* pc = ps->GetPresContext();
+  if (!pc) {
+    return NS_ERROR_NOT_AVAILABLE;
   }
 
-  return NS_OK;
+  if (!mInIMEMode) {
+    // XXXmnakano see bug 558976, ResetInputState() has two meaning which are
+    // "commit the composition" and "cursor is moved".  This method name is
+    // "ForceCompositionEnd", so, ResetInputState() should be used only for the
+    // former here.  However, ResetInputState() is also used for the latter here
+    // because even if we don't have composition, we call ResetInputState() on
+    // Linux.  Currently, nsGtkIMModule can know the timing of the cursor move,
+    // so, the latter meaning should be gone.
+    // XXX This may commit a composition in another editor.
+    return nsIMEStateManager::NotifyIME(NOTIFY_IME_OF_CURSOR_POS_CHANGED, pc);
+  }
+
+  return nsIMEStateManager::NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, pc);
 }
 
 NS_IMETHODIMP
 nsEditor::GetPreferredIMEState(IMEState *aState)
 {
   NS_ENSURE_ARG_POINTER(aState);
   aState->mEnabled = IMEState::ENABLED;
   aState->mOpen = IMEState::DONT_CHANGE_OPEN_STATE;
--- a/editor/libeditor/base/nsEditor.h
+++ b/editor/libeditor/base/nsEditor.h
@@ -383,20 +383,16 @@ protected:
   // stub.  see comment in source.                     
   virtual bool IsBlockNode(nsINode* aNode);
   
   // helper for GetPriorNode and GetNextNode
   nsIContent* FindNextLeafNode(nsINode  *aCurrentNode,
                                bool      aGoForward,
                                bool      bNoBlockCrossing);
 
-  // Get nsIWidget interface
-  nsresult GetWidget(nsIWidget **aWidget);
-
-
   // install the event listeners for the editor 
   virtual nsresult InstallEventListeners();
 
   virtual void CreateEventListeners();
 
   // unregister and release our event listeners
   virtual void RemoveEventListeners();
 
--- a/extensions/gio/nsGIOProtocolHandler.cpp
+++ b/extensions/gio/nsGIOProtocolHandler.cpp
@@ -2,16 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * This code is based on original Mozilla gnome-vfs extension. It implements
  * input stream provided by GVFS/GIO.
 */
+#include "NSPRFormatTime.h" // must include before any headers that include prtime.h
 #include "mozilla/ModuleUtils.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsIObserver.h"
 #include "nsThreadUtils.h"
 #include "nsProxyRelease.h"
 #include "nsIStringBundle.h"
 #include "nsIStandardURL.h"
--- a/gfx/2d/Scale.cpp
+++ b/gfx/2d/Scale.cpp
@@ -39,16 +39,16 @@ bool Scale(uint8_t* srcData, int32_t src
 
   // This returns an SkBitmap backed by dstData; since it also wrote to dstData,
   // we don't need to look at that SkBitmap.
   SkBitmap result = skia::ImageOperations::Resize(imgSrc,
                                                   skia::ImageOperations::RESIZE_BEST,
                                                   dstWidth, dstHeight,
                                                   dstData);
 
-  return result.readyToDraw();
+  return !result.isNull();
 #else
   return false;
 #endif
 }
 
 }
 }
--- a/gfx/skia/src/xml/SkJS.cpp
+++ b/gfx/skia/src/xml/SkJS.cpp
@@ -154,17 +154,17 @@ JSClass global_class = {
     "global", JSCLASS_NEW_RESOLVE,
     JS_PropertyStub,  JS_PropertyStub,
     JS_PropertyStub,  JS_PropertyStub,
     global_enumerate, (JSResolveOp) global_resolve,
     JS_ConvertStub,   JS_FinalizeStub
 };
 
 SkJS::SkJS(void* hwnd) : SkOSWindow(hwnd) {
-    if ((fRuntime = JS_NewRuntime(0x100000)) == NULL) {
+    if ((fRuntime = JS_NewRuntime(0x100000, JS_NO_HELPER_THREADS)) == NULL) {
         SkASSERT(0);
         return;
     }
     if ((fContext = JS_NewContext(fRuntime, 0x1000)) == NULL) {
         SkASSERT(0);
         return;
     }
     ;
--- a/hal/Hal.cpp
+++ b/hal/Hal.cpp
@@ -50,17 +50,17 @@ using namespace mozilla::services;
     } else {                                      \
       return hal_impl::_call;                     \
     }                                             \
   } while (0)
 
 namespace mozilla {
 namespace hal {
 
-PRLogModuleInfo *sHalLog = PR_LOG_DEFINE("hal");
+PRLogModuleInfo *sHalLog = PR_NewLogModule("hal");
 
 namespace {
 
 void
 AssertMainThread()
 {
   MOZ_ASSERT(NS_IsMainThread());
 }
--- a/hal/HalTypes.h
+++ b/hal/HalTypes.h
@@ -162,16 +162,17 @@ enum FMRadioCountry {
   FM_RADIO_COUNTRY_TW,  //Taiwan
   FM_RADIO_COUNTRY_TR,  //Turkey
   FM_RADIO_COUNTRY_UA,  //Ukraine
   FM_RADIO_COUNTRY_USER_DEFINED,
   NUM_FM_RADIO_COUNTRY
 };
 
 typedef Observer<FMRadioOperationInformation> FMRadioObserver;
+typedef Observer<SystemTimeChange> SystemTimeChangeObserver;
 } // namespace hal
 } // namespace mozilla
 
 namespace IPC {
 
 /**
  * Light type serializer.
  */
--- a/image/build/Makefile.in
+++ b/image/build/Makefile.in
@@ -12,16 +12,17 @@ include $(DEPTH)/config/autoconf.mk
 
 MODULE		= imglib2
 LIBRARY_NAME	= imglib2
 EXPORT_LIBRARY = 1
 IS_COMPONENT	= 1
 MODULE_NAME	= nsImageLib2Module
 GRE_MODULE	= 1
 LIBXUL_LIBRARY = 1
+FAIL_ON_WARNINGS = 1
 
 CPPSRCS = \
 		nsImageModule.cpp \
 		$(NULL)
 
 LOCAL_INCLUDES	= \
 		-I. \
 		-I$(srcdir)/../src \
--- a/image/decoders/icon/gtk/Makefile.in
+++ b/image/decoders/icon/gtk/Makefile.in
@@ -8,16 +8,17 @@ topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= imgicon
 LIBRARY_NAME	= imgicongtk_s
 LIBXUL_LIBRARY  = 1
+FAIL_ON_WARNINGS = 1
 
 CPPSRCS		= nsIconChannel.cpp
 
 ifdef MOZ_ENABLE_GNOMEUI
 LOCAL_INCLUDES += $(MOZ_GNOMEUI_CFLAGS)
 else
 LOCAL_INCLUDES += $(MOZ_GTK2_CFLAGS)
 endif
--- a/image/decoders/icon/mac/Makefile.in
+++ b/image/decoders/icon/mac/Makefile.in
@@ -7,16 +7,17 @@ topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= imgicon
 LIBRARY_NAME	= imgiconmac_s
 LIBXUL_LIBRARY = 1
+FAIL_ON_WARNINGS = 1
 
 
 CMMSRCS		= nsIconChannelCocoa.mm
 
 # we don't want the shared lib, but we want to force the creation of a static lib.
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/config/rules.mk
--- a/image/encoders/bmp/Makefile.in
+++ b/image/encoders/bmp/Makefile.in
@@ -9,16 +9,17 @@ VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= imgbmpe
 LIBRARY_NAME	= imgbmpe_s
 FORCE_STATIC_LIB = 1
 MODULE_NAME	= imgEncoderBMPModule
 LIBXUL_LIBRARY = 1
+FAIL_ON_WARNINGS = 1
 
 LOCAL_INCLUDES += -I$(topsrcdir)/image/src/
 
 CPPSRCS		= nsBMPEncoder.cpp
 
 include $(topsrcdir)/config/rules.mk
 
 
--- a/image/encoders/ico/Makefile.in
+++ b/image/encoders/ico/Makefile.in
@@ -9,16 +9,17 @@ VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= imgicoe
 LIBRARY_NAME	= imgicoe_s
 FORCE_STATIC_LIB = 1
 MODULE_NAME	= imgEncoderICOModule
 LIBXUL_LIBRARY = 1
+FAIL_ON_WARNINGS = 1
 
 # Decoders need RasterImage.h
 LOCAL_INCLUDES += -I$(topsrcdir)/image/src/
 LOCAL_INCLUDES += -I$(topsrcdir)/image/encoders/bmp/
 LOCAL_INCLUDES += -I$(topsrcdir)/image/encoders/png/
 
 CPPSRCS		= nsICOEncoder.cpp
 
--- a/image/encoders/jpeg/Makefile.in
+++ b/image/encoders/jpeg/Makefile.in
@@ -9,14 +9,15 @@ VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= imgjpege
 LIBRARY_NAME	= imgjpege_s
 FORCE_STATIC_LIB = 1
 MODULE_NAME	= imgEncoderJPEGModule
 LIBXUL_LIBRARY = 1
+FAIL_ON_WARNINGS = 1
 
 CPPSRCS		= nsJPEGEncoder.cpp
 
 include $(topsrcdir)/config/rules.mk
 
 
--- a/image/encoders/png/Makefile.in
+++ b/image/encoders/png/Makefile.in
@@ -9,16 +9,17 @@ VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= imgpnge
 LIBRARY_NAME	= imgpnge_s
 FORCE_STATIC_LIB = 1
 MODULE_NAME	= imgEncoderPNGModule
 LIBXUL_LIBRARY = 1
+FAIL_ON_WARNINGS = 1
 
 DEFINES		+= -DMOZ_PNG_WRITE \
 	-DMOZ_PNG_READ \
 	$(NULL)
 
 LOCAL_INCLUDES += -I$(topsrcdir)/image/src/
 
 CPPSRCS		= nsPNGEncoder.cpp
--- a/image/src/Makefile.in
+++ b/image/src/Makefile.in
@@ -11,16 +11,17 @@ VPATH		= @srcdir@
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= imglib2
 LIBRARY_NAME	= imglib2_s
 FORCE_STATIC_LIB = 1
 MODULE_NAME	= nsImageLib2Module
 GRE_MODULE	= 1
 LIBXUL_LIBRARY  = 1
+FAIL_ON_WARNINGS = 1
 
 
 EXPORTS		=  imgLoader.h \
 		   imgRequest.h \
 		   $(NULL)
 
 CPPSRCS		= \
 			Image.cpp \
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -22,78 +22,24 @@
 #include "nsPNGDecoder.h"
 #include "nsGIFDecoder2.h"
 #include "nsJPEGDecoder.h"
 #include "nsBMPDecoder.h"
 #include "nsICODecoder.h"
 #include "nsIconDecoder.h"
 
 #include "gfxContext.h"
-#include "gfx2DGlue.h"
 
 #include "mozilla/Preferences.h"
 #include "mozilla/StandardInteger.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/gfx/Scale.h"
 
-// The high-quality scaler requires Skia.
-#ifdef MOZ_ENABLE_SKIA
-
-static bool
-ScaleFrameImage(imgFrame *aSrcFrame, imgFrame *aDstFrame,
-                const gfxSize &aScaleFactors)
-{
-  if (aScaleFactors.width <= 0 || aScaleFactors.height <= 0)
-    return false;
-
-  imgFrame *srcFrame = aSrcFrame;
-  nsIntRect srcRect = srcFrame->GetRect();
-  uint32_t dstWidth = NSToIntRoundUp(srcRect.width * aScaleFactors.width);
-  uint32_t dstHeight = NSToIntRoundUp(srcRect.height * aScaleFactors.height);
-
-  // Destination is unconditionally ARGB32 because that's what the scaler
-  // outputs.
-  nsresult rv = aDstFrame->Init(0, 0, dstWidth, dstHeight,
-                                gfxASurface::ImageFormatARGB32);
-  if (!NS_FAILED(rv)) {
-    uint8_t* srcData;
-    uint32_t srcDataLength;
-    // Source frame data is locked/unlocked on the main thread.
-    srcFrame->GetImageData(&srcData, &srcDataLength);
-    NS_ASSERTION(srcData != nullptr, "Source data is unavailable! Is it locked?");
-
-    uint8_t* dstData;
-    uint32_t dstDataLength;
-    aDstFrame->LockImageData();
-    aDstFrame->GetImageData(&dstData, &dstDataLength);
-
-    // This returns an SkBitmap backed by dstData; since it wrote to dstData,
-    // we don't need to look at that SkBitmap.
-    mozilla::gfx::Scale(srcData, srcRect.width, srcRect.height, aSrcFrame->GetImageBytesPerRow(),
-                        dstData, dstWidth, dstHeight, aDstFrame->GetImageBytesPerRow(),
-                        mozilla::gfx::ImageFormatToSurfaceFormat(aSrcFrame->GetFormat()));
-
-    aDstFrame->UnlockImageData();
-    return true;
-  }
-
-  return false;
-}
-#else // MOZ_ENABLE_SKIA
-static bool
-ScaleFrameImage(imgFrame *aSrcFrame, imgFrame *aDstFrame,
-                const gfxSize &aScaleFactors)
-{
-  return false;
-}
-#endif // MOZ_ENABLE_SKIA
-
-
 #include "sampler.h"
 
 using namespace mozilla;
 using namespace mozilla::image;
 using namespace mozilla::layers;
 
 // a mask for flags that will affect the decoding
 #define DECODE_FLAGS_MASK (imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA | imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION)
@@ -189,22 +135,209 @@ DiscardingEnabled()
     inited = true;
 
     enabled = (PR_GetEnv("MOZ_DISABLE_IMAGE_DISCARD") == nullptr);
   }
 
   return enabled;
 }
 
+struct ScaleRequest
+{
+  ScaleRequest(RasterImage* aImage, const gfxSize& aScale, imgFrame* aSrcFrame)
+    : scale(aScale)
+    , dstLocked(false)
+    , done(false)
+    , stopped(false)
+  {
+    MOZ_ASSERT(!aSrcFrame->GetIsPaletted());
+    MOZ_ASSERT(aScale.width > 0 && aScale.height > 0);
+
+    weakImage = aImage->asWeakPtr();
+    srcRect = aSrcFrame->GetRect();
+    dstSize.width = NSToIntRoundUp(srcRect.width * scale.width);
+    dstSize.height = NSToIntRoundUp(srcRect.height * scale.height);
+  }
+
+  // This can only be called on the main thread.
+  bool GetSurfaces(imgFrame* srcFrame)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    nsRefPtr<RasterImage> image = weakImage.get();
+    if (!image) {
+      return false;
+    }
+
+    bool success = false;
+    if (!dstLocked) {
+      bool srcLocked = NS_SUCCEEDED(srcFrame->LockImageData());
+      dstLocked = NS_SUCCEEDED(dstFrame->LockImageData());
+
+      nsRefPtr<gfxASurface> dstASurf;
+      nsRefPtr<gfxASurface> srcASurf;
+      success = srcLocked && NS_SUCCEEDED(srcFrame->GetSurface(getter_AddRefs(srcASurf)));
+      success = success && dstLocked && NS_SUCCEEDED(dstFrame->GetSurface(getter_AddRefs(dstASurf)));
+
+      success = success && srcLocked && dstLocked && srcASurf && dstASurf;
+
+      if (success) {
+        srcSurface = srcASurf->GetAsImageSurface();
+        dstSurface = dstASurf->GetAsImageSurface();
+        srcData = srcSurface->Data();
+        dstData = dstSurface->Data();
+        srcStride = srcSurface->Stride();
+        dstStride = dstSurface->Stride();
+        srcFormat = mozilla::gfx::ImageFormatToSurfaceFormat(srcFrame->GetFormat());
+      }
+
+      // We have references to the Thebes surfaces, so we don't need to leave
+      // the source frame (that we don't own) locked. We'll unlock the
+      // destination frame in ReleaseSurfaces(), below.
+      if (srcLocked) {
+        success = NS_SUCCEEDED(srcFrame->UnlockImageData()) && success;
+      }
+
+      success = success && srcSurface && dstSurface;
+    }
+
+    return success;
+  }
+
+  // This can only be called on the main thread.
+  bool ReleaseSurfaces()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    nsRefPtr<RasterImage> image = weakImage.get();
+    if (!image) {
+      return false;
+    }
+
+    bool success = false;
+    if (dstLocked) {
+      success = NS_SUCCEEDED(dstFrame->UnlockImageData());
+
+      dstLocked = false;
+      srcData = nullptr;
+      dstData = nullptr;
+      srcSurface = nullptr;
+      dstSurface = nullptr;
+    }
+    return success;
+  }
+
+  // These values may only be touched on the main thread.
+  WeakPtr<RasterImage> weakImage;
+  nsAutoPtr<imgFrame> dstFrame;
+  nsRefPtr<gfxImageSurface> srcSurface;
+  nsRefPtr<gfxImageSurface> dstSurface;
+
+  // Below are the values that may be touched on the scaling thread.
+  gfxSize scale;
+  uint8_t* srcData;
+  uint8_t* dstData;
+  nsIntRect srcRect;
+  gfxIntSize dstSize;
+  uint32_t srcStride;
+  uint32_t dstStride;
+  mozilla::gfx::SurfaceFormat srcFormat;
+  bool dstLocked;
+  bool done;
+  // This boolean is accessed from both threads simultaneously without locking.
+  // That's safe because stopping a ScaleRequest is strictly an optimization;
+  // if we're not cache-coherent, at worst we'll do extra work.
+  bool stopped;
+};
+
+class DrawRunner : public nsRunnable
+{
+public:
+  DrawRunner(ScaleRequest* request)
+   : mScaleRequest(request)
+  {}
+
+  NS_IMETHOD Run()
+  {
+    // ScaleWorker is finished with this request, so we can unlock the data now.
+    mScaleRequest->ReleaseSurfaces();
+
+    nsRefPtr<RasterImage> image = mScaleRequest->weakImage.get();
+
+    if (image) {
+      RasterImage::ScaleStatus status;
+      if (mScaleRequest->done) {
+        status = RasterImage::SCALE_DONE;
+      } else {
+        status = RasterImage::SCALE_INVALID;
+      }
+
+      image->ScalingDone(mScaleRequest, status);
+    }
+
+    return NS_OK;
+  }
+
+private: /* members */
+  nsAutoPtr<ScaleRequest> mScaleRequest;
+};
+
+class ScaleRunner : public nsRunnable
+{
+public:
+  ScaleRunner(RasterImage* aImage, const gfxSize& aScale, imgFrame* aSrcFrame)
+  {
+    nsAutoPtr<ScaleRequest> request(new ScaleRequest(aImage, aScale, aSrcFrame));
+
+    // Destination is unconditionally ARGB32 because that's what the scaler
+    // outputs.
+    request->dstFrame = new imgFrame();
+    nsresult rv = request->dstFrame->Init(0, 0, request->dstSize.width, request->dstSize.height,
+                                          gfxASurface::ImageFormatARGB32);
+
+    if (NS_FAILED(rv) || !request->GetSurfaces(aSrcFrame)) {
+      return;
+    }
+
+    aImage->ScalingStart(request);
+
+    mScaleRequest = request;
+  }
+
+  NS_IMETHOD Run()
+  {
+    // An alias just for ease of typing
+    ScaleRequest* request = mScaleRequest;
+
+    if (!request->stopped) {
+      request->done = mozilla::gfx::Scale(request->srcData, request->srcRect.width, request->srcRect.height, request->srcStride,
+                                          request->dstData, request->dstSize.width, request->dstSize.height, request->dstStride,
+                                          request->srcFormat);
+    } else {
+      request->done = false;
+    }
+
+    // OK, we've got a new scaled image. Let's get the main thread to unlock and
+    // redraw it.
+    nsRefPtr<DrawRunner> runner = new DrawRunner(mScaleRequest.forget());
+    NS_DispatchToMainThread(runner, NS_DISPATCH_NORMAL);
+
+    return NS_OK;
+  }
+
+  bool IsOK() const { return !!mScaleRequest; }
+
+private:
+  nsAutoPtr<ScaleRequest> mScaleRequest;
+};
+
 namespace mozilla {
 namespace image {
 
 /* static */ StaticRefPtr<RasterImage::DecodeWorker> RasterImage::DecodeWorker::sSingleton;
-/* static */ nsRefPtr<RasterImage::ScaleWorker> RasterImage::ScaleWorker::sSingleton;
-/* static */ nsRefPtr<RasterImage::DrawWorker> RasterImage::DrawWorker::sSingleton;
 static nsCOMPtr<nsIThread> sScaleWorkerThread = nullptr;
 
 #ifndef DEBUG
 NS_IMPL_ISUPPORTS2(RasterImage, imgIContainer, nsIProperties)
 #else
 NS_IMPL_ISUPPORTS3(RasterImage, imgIContainer, nsIProperties,
                    imgIContainerDebug)
 #endif
@@ -231,32 +364,29 @@ RasterImage::RasterImage(imgStatusTracke
   mDiscardable(false),
   mHasSourceData(false),
   mDecoded(false),
   mHasBeenDecoded(false),
   mInDecoder(false),
   mAnimationFinished(false),
   mFinishing(false),
   mInUpdateImageContainer(false),
-  mScaleRequest(this)
+  mScaleRequest(nullptr)
 {
   // Set up the discard tracker node.
   mDiscardTrackerNode.img = this;
   Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Add(0);
 
   // Statistics
   num_containers++;
-
 }
 
 //******************************************************************************
 RasterImage::~RasterImage()
 {
-  ScaleRequest::Stop(mScaleRequest.image);
-
   // Discardable statistics
   if (mDiscardable) {
     num_discardable_containers--;
     discardable_source_bytes -= mSourceData.Length();
 
     PR_LOG (gCompressedImageAccountingLog, PR_LOG_DEBUG,
             ("CompressedImageAccounting: destroying RasterImage %p.  "
              "Total Containers: %d, Discardable containers: %d, "
@@ -289,18 +419,16 @@ RasterImage::~RasterImage()
 void
 RasterImage::Initialize()
 {
   InitPrefCaches();
 
   // Create our singletons now, so we don't have to worry about what thread
   // they're created on.
   DecodeWorker::Singleton();
-  DrawWorker::Singleton();
-  ScaleWorker::Singleton();
 }
 
 nsresult
 RasterImage::Init(imgIDecoderObserver *aObserver,
                   const char* aMimeType,
                   const char* aURIString,
                   uint32_t aFlags)
 {
@@ -1012,48 +1140,53 @@ RasterImage::HeapSizeOfSourceWithCompute
   if (n == 0) {
     n = mSourceData.Length();
     NS_ABORT_IF_FALSE(StoringSourceData() || (n == 0),
                       "Non-zero source data size when we aren't storing it?");
   }
   return n;
 }
 
-static size_t
-SizeOfDecodedWithComputedFallbackIfHeap(
-  const nsTArray<imgFrame*>& aFrames, gfxASurface::MemoryLocation aLocation,
-  nsMallocSizeOfFun aMallocSizeOf)
+size_t
+RasterImage::SizeOfDecodedWithComputedFallbackIfHeap(gfxASurface::MemoryLocation aLocation,
+                                                     nsMallocSizeOfFun aMallocSizeOf) const
 {
   size_t n = 0;
-  for (uint32_t i = 0; i < aFrames.Length(); ++i) {
-    imgFrame* frame = aFrames.SafeElementAt(i, nullptr);
+  for (uint32_t i = 0; i < mFrames.Length(); ++i) {
+    imgFrame* frame = mFrames.SafeElementAt(i, nullptr);
     NS_ABORT_IF_FALSE(frame, "Null frame in frame array!");
     n += frame->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation, aMallocSizeOf);
   }
 
+  if (mScaleResult.status == SCALE_DONE) {
+    n += mScaleResult.frame->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation, aMallocSizeOf);
+  }
+
   return n;
 }
 
 size_t
 RasterImage::HeapSizeOfDecodedWithComputedFallback(nsMallocSizeOfFun aMallocSizeOf) const
 {
-  return SizeOfDecodedWithComputedFallbackIfHeap(
-           mFrames, gfxASurface::MEMORY_IN_PROCESS_HEAP, aMallocSizeOf);
+  return SizeOfDecodedWithComputedFallbackIfHeap(gfxASurface::MEMORY_IN_PROCESS_HEAP,
+                                                 aMallocSizeOf);
 }
 
 size_t
 RasterImage::NonHeapSizeOfDecoded() const
 {
-  return SizeOfDecodedWithComputedFallbackIfHeap(mFrames, gfxASurface::MEMORY_IN_PROCESS_NONHEAP, NULL);
+  return SizeOfDecodedWithComputedFallbackIfHeap(gfxASurface::MEMORY_IN_PROCESS_NONHEAP,
+                                                 NULL);
 }
 
 size_t
 RasterImage::OutOfProcessSizeOfDecoded() const
 {
-  return SizeOfDecodedWithComputedFallbackIfHeap(mFrames, gfxASurface::MEMORY_OUT_OF_PROCESS, NULL);
+  return SizeOfDecodedWithComputedFallbackIfHeap(gfxASurface::MEMORY_OUT_OF_PROCESS,
+                                                 NULL);
 }
 
 void
 RasterImage::DeleteImgFrame(uint32_t framenum)
 {
   NS_ABORT_IF_FALSE(framenum < mFrames.Length(), "Deleting invalid frame!");
 
   delete mFrames[framenum];
@@ -2273,16 +2406,20 @@ RasterImage::Discard(bool force)
   // For post-operation logging
   int old_frame_count = mFrames.Length();
 
   // Delete all the decoded frames, then clear the array.
   for (int i = 0; i < old_frame_count; ++i)
     delete mFrames[i];
   mFrames.Clear();
 
+  // Clear our downscaled frame.
+  mScaleResult.status = SCALE_INVALID;
+  mScaleResult.frame = nullptr;
+
   // Flag that we no longer have decoded frames for this image
   mDecoded = false;
 
   // Notify that we discarded
   nsCOMPtr<imgIDecoderObserver> observer(do_QueryReferent(mObserver));
   if (observer)
     observer->OnDiscard();
 
@@ -2674,154 +2811,16 @@ RasterImage::SyncDecode()
     rv = ShutdownDecoder(eShutdownIntent_Done);
     CONTAINER_ENSURE_SUCCESS(rv);
   }
 
   // All good if no errors!
   return mError ? NS_ERROR_FAILURE : NS_OK;
 }
 
-/* static */ RasterImage::ScaleWorker*
-RasterImage::ScaleWorker::Singleton()
-{
-  if (!sSingleton) {
-    sSingleton = new ScaleWorker();
-    ClearOnShutdown(&sSingleton);
-  }
-
-  return sSingleton;
-}
-
-nsresult
-RasterImage::ScaleWorker::Run()
-{
-  if (!mInitialized) {
-    PR_SetCurrentThreadName("Image Scaler");
-    mInitialized = true;
-  }
-
-  ScaleRequest* request;
-  gfxSize scale;
-  imgFrame* frame;
-  {
-    MutexAutoLock lock(ScaleWorker::Singleton()->mRequestsMutex);
-    request = mScaleRequests.popFirst();
-    if (!request)
-      return NS_OK;
-
-    scale = request->scale;
-    frame = request->srcFrame;
-  }
-
-  nsAutoPtr<imgFrame> scaledFrame(new imgFrame());
-  bool scaled = ScaleFrameImage(frame, scaledFrame, scale);
-
-  // OK, we've got a new scaled image. Let's get the main thread to unlock and
-  // redraw it.
-  {
-    MutexAutoLock lock(ScaleWorker::Singleton()->mRequestsMutex);
-    if (scaled && scale == request->scale && !request->isInList()) {
-      request->dstFrame = scaledFrame;
-      request->done = true;
-    }
-
-    DrawWorker::Singleton()->RequestDraw(request->image);
-  }
-  return NS_OK;
-}
-
-// Note: you MUST call RequestScale with the ScaleWorker mutex held.
-void
-RasterImage::ScaleWorker::RequestScale(RasterImage* aImg)
-{
-  mRequestsMutex.AssertCurrentThreadOwns();
-
-  ScaleRequest* request = &aImg->mScaleRequest;
-  if (request->isInList())
-    return;
-
-  mScaleRequests.insertBack(request);
-
-  if (!sScaleWorkerThread) {
-    NS_NewThread(getter_AddRefs(sScaleWorkerThread), this, NS_DISPATCH_NORMAL);
-    ClearOnShutdown(&sScaleWorkerThread);
-  }
-  else {
-    sScaleWorkerThread->Dispatch(this, NS_DISPATCH_NORMAL);
-  }
-}
-
-/* static */ RasterImage::DrawWorker*
-RasterImage::DrawWorker::Singleton()
-{
-  if (!sSingleton) {
-    sSingleton = new DrawWorker();
-    ClearOnShutdown(&sSingleton);
-  }
-
-  return sSingleton;
-}
-
-nsresult
-RasterImage::DrawWorker::Run()
-{
-  ScaleRequest* request;
-  {
-    MutexAutoLock lock(ScaleWorker::Singleton()->mRequestsMutex);
-    request = mDrawRequests.popFirst();
-  }
-  if (request) {
-    // ScaleWorker is finished with this request, so we can unlock the data now.
-    request->UnlockSourceData();
-    // We have to reset dstFrame if request was stopped while ScaleWorker was scaling.
-    if (request->stopped) {
-      ScaleRequest::Stop(request->image);
-    }
-    nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(request->image->mObserver));
-    if (request->done && observer) {
-      imgFrame *scaledFrame = request->dstFrame.get();
-      scaledFrame->ImageUpdated(scaledFrame->GetRect());
-      nsIntRect frameRect = request->srcFrame->GetRect();
-      observer->FrameChanged(&frameRect);
-    }
-  }
-
-  return NS_OK;
-}
-
-void
-RasterImage::DrawWorker::RequestDraw(RasterImage* aImg)
-{
-  ScaleRequest* request = &aImg->mScaleRequest;
-  mDrawRequests.insertBack(request);
-  NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL);
-}
-
-void
-RasterImage::ScaleRequest::Stop(RasterImage* aImg)
-{
-  ScaleRequest* request = &aImg->mScaleRequest;
-  // It's safe to unlock source image data only if request is in the list.
-  // Otherwise we may be reading from the source while performing scaling
-  // and can't interrupt immediately.
-  if (request->isInList()) {
-    request->remove();
-    request->UnlockSourceData();
-  }
-  // We have to check if request is finished before dropping the destination
-  // frame. Otherwise we may be writing to the dest while performing scaling.
-  if (request->done) {
-    request->done = false;
-    request->dstFrame = nullptr;
-    request->scale.width = 0;
-    request->scale.height = 0;
-  }
-  request->stopped = true;
-}
-
 static inline bool
 IsDownscale(const gfxSize& scale)
 {
   if (scale.width > 1.0)
     return false;
   if (scale.height > 1.0)
     return false;
   if (scale.width == 1.0 && scale.height == 1.0)
@@ -2842,63 +2841,108 @@ RasterImage::CanScale(gfxPattern::Graphi
     return (aScale.width < factor || aScale.height < factor);
   }
 #endif
 
   return false;
 }
 
 void
+RasterImage::ScalingStart(ScaleRequest* request)
+{
+  MOZ_ASSERT(request);
+  mScaleResult.scale = request->scale;
+  mScaleResult.status = SCALE_PENDING;
+  mScaleRequest = request;
+}
+
+void
+RasterImage::ScalingDone(ScaleRequest* request, ScaleStatus status)
+{
+  MOZ_ASSERT(status == SCALE_DONE || status == SCALE_INVALID);
+  MOZ_ASSERT(request);
+
+  if (status == SCALE_DONE) {
+    MOZ_ASSERT(request->done);
+
+    nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(mObserver));
+    if (observer) {
+      imgFrame *scaledFrame = request->dstFrame.get();
+      scaledFrame->ImageUpdated(scaledFrame->GetRect());
+      observer->FrameChanged(&request->srcRect);
+    }
+
+    mScaleResult.status = SCALE_DONE;
+    mScaleResult.frame = request->dstFrame;
+    mScaleResult.scale = request->scale;
+  } else {
+    mScaleResult.status = SCALE_INVALID;
+    mScaleResult.frame = nullptr;
+  }
+
+  // If we were waiting for this scale to come through, forget the scale
+  // request. Otherwise, we still have a scale outstanding that it's possible
+  // for us to (want to) stop.
+  if (mScaleRequest == request) {
+    mScaleRequest = nullptr;
+  }
+}
+
+void
 RasterImage::DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
                                           gfxContext *aContext,
                                           gfxPattern::GraphicsFilter aFilter,
                                           const gfxMatrix &aUserSpaceToImageSpace,
                                           const gfxRect &aFill,
                                           const nsIntRect &aSubimage)
 {
   imgFrame *frame = aFrame;
   nsIntRect framerect = frame->GetRect();
   gfxMatrix userSpaceToImageSpace = aUserSpaceToImageSpace;
   gfxMatrix imageSpaceToUserSpace = aUserSpaceToImageSpace;
   imageSpaceToUserSpace.Invert();
   gfxSize scale = imageSpaceToUserSpace.ScaleFactors(true);
   nsIntRect subimage = aSubimage;
 
   if (CanScale(aFilter, scale)) {
-    MutexAutoLock lock(ScaleWorker::Singleton()->mRequestsMutex);
     // If scale factor is still the same that we scaled for and
-    // ScaleWorker has done it's job, then we can use pre-downscaled frame.
+    // ScaleWorker isn't still working, then we can use pre-downscaled frame.
     // If scale factor has changed, order new request.
-    if (mScaleRequest.scale == scale) {
-      if (mScaleRequest.done) {
-        frame = mScaleRequest.dstFrame.get();
-        userSpaceToImageSpace.Multiply(gfxMatrix().Scale(scale.width, scale.height));
-
-        // Since we're switching to a scaled image, we we need to transform the
-        // area of the subimage to draw accordingly, since imgFrame::Draw()
-        // doesn't know about scaled frames.
-        subimage.ScaleRoundOut(scale.width, scale.height);
+    // FIXME: Current implementation doesn't support pre-downscale
+    // mechanism for multiple sizes from same src, since we cache
+    // pre-downscaled frame only for the latest requested scale.
+    // The solution is to cache more than one scaled image frame
+    // for each RasterImage.
+    if (mScaleResult.status == SCALE_DONE && mScaleResult.scale == scale) {
+      frame = mScaleResult.frame;
+      userSpaceToImageSpace.Multiply(gfxMatrix().Scale(scale.width, scale.height));
+
+      // Since we're switching to a scaled image, we we need to transform the
+      // area of the subimage to draw accordingly, since imgFrame::Draw()
+      // doesn't know about scaled frames.
+      subimage.ScaleRoundOut(scale.width, scale.height);
+    }
+
+    // If we're not waiting for exactly this result, and there's only one
+    // instance of this image on this page, ask for a scale.
+    else if (!(mScaleResult.status == SCALE_PENDING && mScaleResult.scale == scale) &&
+             mLockCount == 1) {
+      // If we have an oustanding request, signal it to stop (if it can).
+      if (mScaleRequest) {
+        mScaleRequest->stopped = true;
       }
-    } else {
-      // FIXME: Current implementation doesn't support pre-downscale
-      // mechanism for multiple images from same src, since we cache
-      // pre-downscaled frame only for the latest requested scale.
-      // The solution is to cache more than one scaled image frame
-      // for each RasterImage.
-      int scaling = mScaleRequest.srcDataLocked ? 1 : 0;
-      if (mLockCount - scaling == 1) {
-        ScaleRequest::Stop(this);
-        mScaleRequest.srcFrame = frame;
-        mScaleRequest.scale = scale;
-        mScaleRequest.stopped = false;
-
-        // We need to make sure that source data is available before asking to scale.
-        if (mScaleRequest.LockSourceData()) {
-          ScaleWorker::Singleton()->RequestScale(this);
+
+      nsRefPtr<ScaleRunner> runner = new ScaleRunner(this, scale, frame);
+      if (runner->IsOK()) {
+        if (!sScaleWorkerThread) {
+          NS_NewNamedThread("Image Scaler", getter_AddRefs(sScaleWorkerThread));
+          ClearOnShutdown(&sScaleWorkerThread);
         }
+
+        sScaleWorkerThread->Dispatch(runner, NS_DISPATCH_NORMAL);
       }
     }
   }
 
   nsIntMargin padding(framerect.x, framerect.y,
                       mSize.width - framerect.XMost(),
                       mSize.height - framerect.YMost());
 
@@ -3028,21 +3072,16 @@ RasterImage::UnlockImage()
     return NS_ERROR_ABORT;
 
   // We're locked, so discarding should not be active
   NS_ABORT_IF_FALSE(!DiscardingActive(), "Locked, but discarding activated");
 
   // Decrement our lock count
   mLockCount--;
 
-  if (ScaleWorker::sSingleton && mLockCount == 0) {
-    MutexAutoLock lock(ScaleWorker::Singleton()->mRequestsMutex);
-    ScaleRequest::Stop(this);
-  }
-
   // If we've decoded this image once before, we're currently decoding again,
   // and our lock count is now zero (so nothing is forcing us to keep the
   // decoded data around), try to cancel the decode and throw away whatever
   // we've decoded.
   if (mHasBeenDecoded && mDecoder &&
       mLockCount == 0 && CanForciblyDiscard()) {
     PR_LOG(gCompressedImageAccountingLog, PR_LOG_DEBUG,
            ("RasterImage[0x%p] canceling decode because image "
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -12,33 +12,33 @@
  * @author  Chris Saari <saari@netscape.com>
  * @author  Arron Mogge <paper@animecity.nu>
  * @author  Andrew Smith <asmith15@learn.senecac.on.ca>
  */
 
 #ifndef mozilla_imagelib_RasterImage_h_
 #define mozilla_imagelib_RasterImage_h_
 
-#include "mozilla/Mutex.h"
 #include "Image.h"
 #include "nsCOMArray.h"
 #include "nsCOMPtr.h"
 #include "imgIContainer.h"
 #include "nsIProperties.h"
 #include "nsITimer.h"
 #include "nsWeakReference.h"
 #include "nsTArray.h"
 #include "imgFrame.h"
 #include "nsThreadUtils.h"
 #include "DiscardTracker.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/WeakPtr.h"
+#include "gfx2DGlue.h"
 #ifdef DEBUG
   #include "imgIContainerDebug.h"
 #endif
 
 class imgIDecoder;
 class imgIContainerObserver;
 class nsIInputStream;
 
@@ -120,16 +120,18 @@ class nsIInputStream;
  * it's not allocated until the second frame is added.
  *
  * @note
  * mAnimationMode, mLoopCount and mObserver are not in the mAnim structure
  * because the first two have public setters and the observer we only get
  * in Init().
  */
 
+class ScaleRequest;
+
 namespace mozilla {
 namespace layers {
 class LayerManager;
 class ImageContainer;
 class Image;
 }
 namespace image {
 
@@ -309,16 +311,33 @@ public:
     kDisposeRestorePrevious // Restore the previous (composited) frame
   };
 
   const char* GetURIString() { return mURIString.get();}
 
   // Called from module startup. Sets up RasterImage to be used.
   static void Initialize();
 
+  enum ScaleStatus
+  {
+    SCALE_INVALID,
+    SCALE_PENDING,
+    SCALE_DONE
+  };
+
+  // Call this with a new ScaleRequest to mark this RasterImage's scale result
+  // as waiting for the results of this request. You call to ScalingDone before
+  // request is destroyed!
+  void ScalingStart(ScaleRequest* request);
+
+  // Call this with a finished ScaleRequest to set this RasterImage's scale
+  // result. Give it a ScaleStatus of SCALE_DONE if everything succeeded, and
+  // SCALE_INVALID otherwise.
+  void ScalingDone(ScaleRequest* request, ScaleStatus status);
+
 private:
   struct Anim
   {
     //! Area of the first frame that needs to be redrawn on subsequent loops.
     nsIntRect                  firstFrameRefreshArea;
     uint32_t                   currentAnimationFrameIndex; // 0 to numFrames-1
 
     // the time that the animation advanced to the current frame
@@ -468,112 +487,16 @@ private:
     LinkedList<DecodeRequest> mASAPDecodeRequests;
     LinkedList<DecodeRequest> mNormalDecodeRequests;
 
     /* True if we've posted ourselves to the event loop and expect Run() to
      * be called sometime in the future. */
     bool mPendingInEventLoop;
   };
 
-  struct ScaleRequest : public LinkedListElement<ScaleRequest>
-  {
-    ScaleRequest(RasterImage* aImage)
-      : image(aImage)
-      , srcFrame(nullptr)
-      , dstFrame(nullptr)
-      , scale(0, 0)
-      , done(false)
-      , stopped(false)
-      , srcDataLocked(false)
-    {};
-
-    bool LockSourceData()
-    {
-      if (!srcDataLocked) {
-        bool success = true;
-        success = success && NS_SUCCEEDED(image->LockImage());
-        success = success && NS_SUCCEEDED(srcFrame->LockImageData());
-        srcDataLocked = success;
-      }
-      return srcDataLocked;
-    }
-
-    bool UnlockSourceData()
-    {
-      bool success = true;
-      if (srcDataLocked) {
-        success = success && NS_SUCCEEDED(image->UnlockImage());
-        success = success && NS_SUCCEEDED(srcFrame->UnlockImageData());
-
-        // If unlocking fails, there's nothing we can do to make it work, so we
-        // claim that we're not locked regardless.
-        srcDataLocked = false;
-      }
-      return success;
-    }
-
-    static void Stop(RasterImage* aImg);
-
-    RasterImage* const image;
-    imgFrame *srcFrame;
-    nsAutoPtr<imgFrame> dstFrame;
-    gfxSize scale;
-    bool done;
-    bool stopped;
-    bool srcDataLocked;
-  };
-
-  class ScaleWorker : public nsRunnable
-  {
-  public:
-    static ScaleWorker* Singleton();
-
-    NS_IMETHOD Run();
-
-  /* statics */
-    static nsRefPtr<ScaleWorker> sSingleton;
-
-  private: /* methods */
-    ScaleWorker()
-      : mRequestsMutex("RasterImage.ScaleWorker.mRequestsMutex")
-      , mInitialized(false)
-    {};
-
-    // Note: you MUST call RequestScale with the ScaleWorker mutex held.
-    void RequestScale(RasterImage* aImg);
-
-  private: /* members */
-
-    friend class RasterImage;
-    LinkedList<ScaleRequest> mScaleRequests;
-    Mutex mRequestsMutex;
-    bool mInitialized;
-  };
-
-  class DrawWorker : public nsRunnable
-  {
-  public:
-    static DrawWorker* Singleton();
-
-    NS_IMETHOD Run();
-
-  /* statics */
-    static nsRefPtr<DrawWorker> sSingleton;
-
-  private: /* methods */
-    DrawWorker() {};
-
-    void RequestDraw(RasterImage* aImg);
-
-  private: /* members */
-
-    friend class RasterImage;
-    LinkedList<ScaleRequest> mDrawRequests;
-  };
-
   void DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
                                     gfxContext *aContext,
                                     gfxPattern::GraphicsFilter aFilter,
                                     const gfxMatrix &aUserSpaceToImageSpace,
                                     const gfxRect &aFill,
                                     const nsIntRect &aSubimage);
 
   /**
@@ -607,17 +530,20 @@ private:
 
   imgFrame* GetImgFrameNoDecode(uint32_t framenum);
   imgFrame* GetImgFrame(uint32_t framenum);
   imgFrame* GetDrawableImgFrame(uint32_t framenum);
   imgFrame* GetCurrentImgFrame();
   imgFrame* GetCurrentDrawableImgFrame();
   uint32_t GetCurrentImgFrameIndex() const;
   mozilla::TimeStamp GetCurrentImgFrameEndTime() const;
-  
+
+  size_t SizeOfDecodedWithComputedFallbackIfHeap(gfxASurface::MemoryLocation aLocation,
+                                                 nsMallocSizeOfFun aMallocSizeOf) const;
+
   inline void EnsureAnimExists()
   {
     if (!mAnim) {
 
       // Create the animation context
       mAnim = new Anim();
 
       // We don't support discarding animated images (See bug 414259).
@@ -782,17 +708,34 @@ private: // data
   nsresult SyncDecode();
   nsresult InitDecoder(bool aDoSizeDecode);
   nsresult WriteToDecoder(const char *aBuffer, uint32_t aCount);
   nsresult DecodeSomeData(uint32_t aMaxBytes);
   bool     IsDecodeFinished();
   TimeStamp mDrawStartTime;
 
   inline bool CanScale(gfxPattern::GraphicsFilter aFilter, gfxSize aScale);
-  ScaleRequest mScaleRequest;
+
+  struct ScaleResult
+  {
+    ScaleResult()
+     : status(SCALE_INVALID)
+    {}
+
+    gfxSize scale;
+    nsAutoPtr<imgFrame> frame;
+    ScaleStatus status;
+  };
+
+  ScaleResult mScaleResult;
+
+  // We hold on to a bare pointer to a ScaleRequest while it's outstanding so
+  // we can mark it as stopped if necessary. The ScaleWorker/DrawWorker duo
+  // will inform us when to let go of this pointer.
+  ScaleRequest* mScaleRequest;
 
   // Decoder shutdown
   enum eShutdownIntent {
     eShutdownIntent_Done        = 0,
     eShutdownIntent_Interrupted = 1,
     eShutdownIntent_Error       = 2,
     eShutdownIntent_AllCount    = 3
   };
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -3,47 +3,93 @@
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef js_MemoryMetrics_h
 #define js_MemoryMetrics_h
 
-/*
- * These declarations are not within jsapi.h because they are highly likely
- * to change in the future. Depend on them at your own risk.
- */
+// These declarations are not within jsapi.h because they are highly likely to
+// change in the future. Depend on them at your own risk.
 
 #include <string.h>
 
 #include "jsalloc.h"
 #include "jspubtd.h"
 
 #include "js/Utility.h"
 #include "js/Vector.h"
 
+namespace js {
+
+// In memory reporting, we have concept of "sundries", line items which are too
+// small to be worth reporting individually.  Under some circumstances, a memory
+// reporter gets tossed into the sundries bucket if it's smaller than
+// MemoryReportingSundriesThreshold() bytes.
+//
+// We need to define this value here, rather than in the code which actually
+// generates the memory reports, because HugeStringInfo uses this value.
+JS_FRIEND_API(size_t) MemoryReportingSundriesThreshold();
+
+} // namespace js
+
 namespace JS {
 
-/* Data for tracking analysis/inference memory usage. */
+// Data for tracking analysis/inference memory usage.
 struct TypeInferenceSizes
 {
+    TypeInferenceSizes()
+      : scripts(0)
+      , objects(0)
+      , tables(0)
+      , temporary(0)
+    {}
+
     size_t scripts;
     size_t objects;
     size_t tables;
     size_t temporary;
 
     void add(TypeInferenceSizes &sizes) {
         this->scripts   += sizes.scripts;
         this->objects   += sizes.objects;
         this->tables    += sizes.tables;
         this->temporary += sizes.temporary;
     }
 };
 
+// Holds data about a huge string (one which uses more HugeStringInfo::MinSize
+// bytes of memory), so we can report it individually.
+struct HugeStringInfo
+{
+    HugeStringInfo()
+      : length(0)
+      , size(0)
+    {
+        memset(&buffer, 0, sizeof(buffer));
+    }
+
+    // A string needs to take up this many bytes of storage before we consider
+    // it to be "huge".
+    static size_t MinSize()
+    {
+      return js::MemoryReportingSundriesThreshold();
+    }
+
+    // A string's size in memory is not necessarily equal to twice its length
+    // because the allocator and the JS engine both may round up.
+    size_t length;
+    size_t size;
+
+    // We record the first 32 chars of the escaped string here.  (We escape the
+    // string so we can use a char[] instead of a jschar[] here.
+    char buffer[32];
+};
+
 // These measurements relate directly to the JSRuntime, and not to
 // compartments within it.
 struct RuntimeSizes
 {
     RuntimeSizes()
       : object(0)
       , atomsTable(0)
       , contexts(0)
@@ -73,26 +119,95 @@ struct RuntimeSizes
     size_t gcMarker;
     size_t mathCache;
     size_t scriptFilenames;
     size_t scriptSources;
 };
 
 struct CompartmentStats
 {
-    CompartmentStats() {
-        memset(this, 0, sizeof(*this));
+    CompartmentStats()
+      : extra1(0)
+      , extra2(0)
+      , gcHeapArenaAdmin(0)
+      , gcHeapUnusedGcThings(0)
+      , gcHeapObjectsNonFunction(0)
+      , gcHeapObjectsFunction(0)
+      , gcHeapStrings(0)
+      , gcHeapShapesTree(0)
+      , gcHeapShapesDict(0)
+      , gcHeapShapesBase(0)
+      , gcHeapScripts(0)
+      , gcHeapTypeObjects(0)
+      , gcHeapIonCodes(0)
+#if JS_HAS_XML_SUPPORT
+      , gcHeapXML(0)
+#endif
+      , objectSlots(0)
+      , objectElements(0)
+      , objectMisc(0)
+      , objectPrivate(0)
+      , nonHugeStringChars(0)
+      , shapesExtraTreeTables(0)
+      , shapesExtraDictTables(0)
+      , shapesExtraTreeShapeKids(0)
+      , shapesCompartmentTables(0)
+      , scriptData(0)
+      , jaegerData(0)
+      , ionData(0)
+      , compartmentObject(0)
+      , crossCompartmentWrappers(0)
+      , regexpCompartment(0)
+      , debuggeesSet(0)
+    {}
+
+    CompartmentStats(const CompartmentStats &other)
+      : extra1(other.extra1)
+      , extra2(other.extra2)
+      , gcHeapArenaAdmin(other.gcHeapArenaAdmin)
+      , gcHeapUnusedGcThings(other.gcHeapUnusedGcThings)
+      , gcHeapObjectsNonFunction(other.gcHeapObjectsNonFunction)
+      , gcHeapObjectsFunction(other.gcHeapObjectsFunction)
+      , gcHeapStrings(other.gcHeapStrings)
+      , gcHeapShapesTree(other.gcHeapShapesTree)
+      , gcHeapShapesDict(other.gcHeapShapesDict)
+      , gcHeapShapesBase(other.gcHeapShapesBase)
+      , gcHeapScripts(other.gcHeapScripts)
+      , gcHeapTypeObjects(other.gcHeapTypeObjects)
+      , gcHeapIonCodes(other.gcHeapIonCodes)
+#if JS_HAS_XML_SUPPORT
+      , gcHeapXML(other.gcHeapXML)
+#endif
+      , objectSlots(other.objectSlots)
+      , objectElements(other.objectElements)
+      , objectMisc(other.objectMisc)
+      , objectPrivate(other.objectPrivate)
+      , nonHugeStringChars(other.nonHugeStringChars)
+      , shapesExtraTreeTables(other.shapesExtraTreeTables)
+      , shapesExtraDictTables(other.shapesExtraDictTables)
+      , shapesExtraTreeShapeKids(other.shapesExtraTreeShapeKids)
+      , shapesCompartmentTables(other.shapesCompartmentTables)
+      , scriptData(other.scriptData)
+      , jaegerData(other.jaegerData)
+      , ionData(other.ionData)
+      , compartmentObject(other.compartmentObject)
+      , crossCompartmentWrappers(other.crossCompartmentWrappers)
+      , regexpCompartment(other.regexpCompartment)
+      , debuggeesSet(other.debuggeesSet)
+      , typeInferenceSizes(other.typeInferenceSizes)
+    {
+      hugeStrings.append(other.hugeStrings);
     }
 
     // These fields can be used by embedders.
     void   *extra1;
     void   *extra2;
 
-    // If you add a new number, remember to update add() and maybe
-    // gcHeapThingsSize()!
+    // If you add a new number, remember to update the constructors, add(), and
+    // maybe gcHeapThingsSize()!
     size_t gcHeapArenaAdmin;
     size_t gcHeapUnusedGcThings;
 
     size_t gcHeapObjectsNonFunction;
     size_t gcHeapObjectsFunction;
     size_t gcHeapStrings;
     size_t gcHeapShapesTree;
     size_t gcHeapShapesDict;
@@ -103,33 +218,35 @@ struct CompartmentStats
 #if JS_HAS_XML_SUPPORT
     size_t gcHeapXML;
 #endif
 
     size_t objectSlots;
     size_t objectElements;
     size_t objectMisc;
     size_t objectPrivate;
-    size_t stringChars;
+    size_t nonHugeStringChars;
     size_t shapesExtraTreeTables;
     size_t shapesExtraDictTables;
     size_t shapesExtraTreeShapeKids;
     size_t shapesCompartmentTables;
     size_t scriptData;
     size_t jaegerData;
     size_t ionData;
     size_t compartmentObject;
     size_t crossCompartmentWrappers;
     size_t regexpCompartment;
     size_t debuggeesSet;
 
     TypeInferenceSizes typeInferenceSizes;
+    js::Vector<HugeStringInfo, 0, js::SystemAllocPolicy> hugeStrings;
 
     // Add cStats's numbers to this object's numbers.
-    void add(CompartmentStats &cStats) {
+    void add(CompartmentStats &cStats)
+    {
         #define ADD(x)  this->x += cStats.x
 
         ADD(gcHeapArenaAdmin);
         ADD(gcHeapUnusedGcThings);
 
         ADD(gcHeapObjectsNonFunction);
         ADD(gcHeapObjectsFunction);
         ADD(gcHeapStrings);
@@ -142,32 +259,33 @@ struct CompartmentStats
     #if JS_HAS_XML_SUPPORT
         ADD(gcHeapXML);
     #endif
 
         ADD(objectSlots);
         ADD(objectElements);
         ADD(objectMisc);
         ADD(objectPrivate);
-        ADD(stringChars);
+        ADD(nonHugeStringChars);
         ADD(shapesExtraTreeTables);
         ADD(shapesExtraDictTables);
         ADD(shapesExtraTreeShapeKids);
         ADD(shapesCompartmentTables);
         ADD(scriptData);
         ADD(jaegerData);
         ADD(ionData);
         ADD(compartmentObject);
         ADD(crossCompartmentWrappers);
         ADD(regexpCompartment);
         ADD(debuggeesSet);
 
         #undef ADD
 
         typeInferenceSizes.add(cStats.typeInferenceSizes);
+        hugeStrings.append(cStats.hugeStrings);
     }
 
     // The size of all the live things in the GC heap.
     size_t gcHeapThingsSize();
 };
 
 struct RuntimeStats
 {
@@ -239,17 +357,17 @@ public:
 };
 
 extern JS_PUBLIC_API(bool)
 CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats, ObjectPrivateVisitor *opv);
 
 extern JS_PUBLIC_API(int64_t)
 GetExplicitNonHeapForRuntime(JSRuntime *rt, JSMallocSizeOfFun mallocSizeOf);
 
-#endif /* JS_THREADSAFE */
+#endif // JS_THREADSAFE
 
 extern JS_PUBLIC_API(size_t)
 SystemCompartmentCount(const JSRuntime *rt);
 
 extern JS_PUBLIC_API(size_t)
 UserCompartmentCount(const JSRuntime *rt);
 
 } // namespace JS
--- a/js/src/config/system-headers
+++ b/js/src/config/system-headers
@@ -476,16 +476,17 @@ mach/mach_interface.h
 mach/mach_port.h
 mach-o/dyld.h
 MacLocales.h
 MacMemory.h
 MacTCP.h
 MacTypes.h
 MacWindows.h
 malloc.h
+malloc_np.h
 map
 mapicode.h
 mapidefs.h
 mapiguid.h
 mapi.h
 mapitags.h
 mapiutil.h
 mapix.h
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -3070,33 +3070,16 @@ if test "$ac_cv_thread_keyword" = yes; t
       :
       ;;
     *)
       AC_DEFINE(HAVE_THREAD_TLS_KEYWORD)
       ;;
   esac
 fi
 
-dnl Check for the existence of various allocation headers/functions
-
-MALLOC_H=
-MOZ_CHECK_HEADER(malloc.h,        [MALLOC_H=malloc.h])
-if test "$MALLOC_H" = ""; then
-  MOZ_CHECK_HEADER(malloc/malloc.h, [MALLOC_H=malloc/malloc.h])
-  if test "$MALLOC_H" = ""; then
-    MOZ_CHECK_HEADER(sys/malloc.h,    [MALLOC_H=sys/malloc.h])
-  fi
-fi
-if test "$MALLOC_H" != ""; then
-   AC_DEFINE_UNQUOTED(MALLOC_H, <$MALLOC_H>)
-fi
-
-MOZ_ALLOCATING_FUNCS="strndup posix_memalign memalign valloc"
-AC_CHECK_FUNCS(strndup posix_memalign memalign valloc)
-
 dnl See if compiler supports some gcc-style attributes
 
 AC_CACHE_CHECK(for __attribute__((always_inline)),
                ac_cv_attribute_always_inline,
                [AC_TRY_COMPILE([inline void f(void) __attribute__((always_inline));],
                                [],
                                ac_cv_attribute_always_inline=yes,
                                ac_cv_attribute_always_inline=no)])
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -3057,17 +3057,17 @@ CodeGenerator::visitOutOfLineUnboxDouble
     return true;
 }
 
 typedef bool (*GetPropertyOrNameFn)(JSContext *, HandleObject, HandlePropertyName, Value *);
 
 bool
 CodeGenerator::visitCallGetProperty(LCallGetProperty *lir)
 {
-    typedef bool (*pf)(JSContext *, HandleValue, PropertyName *, MutableHandleValue);
+    typedef bool (*pf)(JSContext *, HandleValue, HandlePropertyName, MutableHandleValue);
     static const VMFunction Info = FunctionInfo<pf>(GetProperty);
 
     pushArg(ImmGCPtr(lir->mir()->name()));
     pushArg(ToValue(lir, LCallGetProperty::Value));
     return callVM(Info, lir);
 }
 
 bool
@@ -3933,16 +3933,28 @@ CodeGenerator::visitClampVToUint8(LClamp
     masm.bind(&isZero);
     masm.move32(Imm32(0), output);
 
     masm.bind(&done);
     return true;
 }
 
 bool
+CodeGenerator::visitIn(LIn *ins)
+{
+    typedef bool (*pf)(JSContext *, HandleValue, HandleObject, JSBool *);
+    static const VMFunction OperatorInInfo = FunctionInfo<pf>(OperatorIn);
+
+    pushArg(ToRegister(ins->rhs()));
+    pushArg(ToValue(ins, LIn::LHS));
+
+    return callVM(OperatorInInfo, ins);
+}
+
+bool
 CodeGenerator::visitInstanceOfO(LInstanceOfO *ins)
 {
     Register rhs = ToRegister(ins->getOperand(1));
     return emitInstanceOf(ins, rhs);
 }
 
 bool
 CodeGenerator::visitInstanceOfV(LInstanceOfV *ins)
--- a/js/src/ion/CodeGenerator.h
+++ b/js/src/ion/CodeGenerator.h
@@ -161,16 +161,17 @@ class CodeGenerator : public CodeGenerat
     bool visitIteratorEnd(LIteratorEnd *lir);
     bool visitArgumentsLength(LArgumentsLength *lir);
     bool visitGetArgument(LGetArgument *lir);
     bool visitCallSetProperty(LCallSetProperty *ins);
     bool visitCallDeleteProperty(LCallDeleteProperty *lir);
     bool visitBitNotV(LBitNotV *lir);
     bool visitBitOpV(LBitOpV *lir);
     bool emitInstanceOf(LInstruction *ins, Register rhs);
+    bool visitIn(LIn *ins);
     bool visitInstanceOfO(LInstanceOfO *ins);
     bool visitInstanceOfV(LInstanceOfV *ins);
     bool visitFunctionBoundary(LFunctionBoundary *lir);
     bool visitGetDOMProperty(LGetDOMProperty *lir);
     bool visitSetDOMProperty(LSetDOMProperty *lir);
     bool visitCallDOMNative(LCallDOMNative *lir);
 
     bool visitCheckOverRecursed(LCheckOverRecursed *lir);
--- a/js/src/ion/Ion.cpp
+++ b/js/src/ion/Ion.cpp
@@ -1013,17 +1013,17 @@ IonCompile(JSContext *cx, JSScript *scri
     IonSpewNewFunction(graph, builder->script().unsafeGet());
 
     if (!builder->build()) {
         IonSpew(IonSpew_Abort, "Builder failed to build.");
         return false;
     }
     builder->clearForBackEnd();
 
-    if (js_IonOptions.parallelCompilation) {
+    if (js_IonOptions.parallelCompilation && OffThreadCompilationAvailable(cx)) {
         builder->script()->ion = ION_COMPILING_SCRIPT;
 
         if (!StartOffThreadIonCompile(cx, builder)) {
             IonSpew(IonSpew_Abort, "Unable to start off-thread ion compilation.");
             return false;
         }
 
         // The allocator and associated data will be destroyed after being
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -1063,16 +1063,19 @@ IonBuilder::inspectOpcode(JSOp op)
         return jsop_iternext();
 
       case JSOP_MOREITER:
         return jsop_itermore();
 
       case JSOP_ENDITER:
         return jsop_iterend();
 
+      case JSOP_IN:
+        return jsop_in();
+
       case JSOP_INSTANCEOF:
         return jsop_instanceof();
 
       default:
 #ifdef DEBUG
         return abort("Unsupported opcode: %s (line %d)", js_CodeName[op], info().lineno(cx, pc));
 #else
         return abort("Unsupported opcode: %d (line %d)", op, info().lineno(cx, pc));
@@ -6445,16 +6448,28 @@ IonBuilder::jsop_setaliasedvar(ScopeCoor
     } else {
         store = MStoreFixedSlot::NewBarriered(obj, sc.slot, rval);
     }
 
     current->add(store);
     return resumeAfter(store);
 }
 
+bool
+IonBuilder::jsop_in()
+{
+    MDefinition *obj = current->pop();
+    MDefinition *id = current->pop();
+    MIn *ins = new MIn(id, obj);
+
+    current->add(ins);
+    current->push(ins);
+
+    return resumeAfter(ins);
+}
 
 bool
 IonBuilder::jsop_instanceof()
 {
     MDefinition *proto = current->pop();
     MDefinition *obj = current->pop();
     MInstanceOf *ins = new MInstanceOf(obj, proto);
 
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -355,16 +355,17 @@ class IonBuilder : public MIRGenerator
     bool jsop_deflocalfun(uint32 local, JSFunction *fun);
     bool jsop_this();
     bool jsop_typeof();
     bool jsop_toid();
     bool jsop_iter(uint8 flags);
     bool jsop_iternext();
     bool jsop_itermore();
     bool jsop_iterend();
+    bool jsop_in();
     bool jsop_instanceof();
     bool jsop_getaliasedvar(ScopeCoordinate sc);
     bool jsop_setaliasedvar(ScopeCoordinate sc);
 
     /* Inlining. */
 
     enum InliningStatus
     {
--- a/js/src/ion/LIR-Common.h
+++ b/js/src/ion/LIR-Common.h
@@ -3073,21 +3073,42 @@ class LPhi : public LInstruction
         JS_NOT_REACHED("no temps");
     }
 
     virtual void printInfo(FILE *fp) {
         printOperands(fp);
     }
 };
 
+class LIn : public LCallInstructionHelper<1, BOX_PIECES+1, 0>
+{
+  public:
+    LIR_HEADER(In);
+    LIn(const LAllocation &rhs) {
+        setOperand(RHS, rhs);
+    }
+
+    const LAllocation *lhs() {
+        return getOperand(LHS);
+    }
+    const LAllocation *rhs() {
+        return getOperand(RHS);
+    }
+
+    static const size_t LHS = 0;
+    static const size_t RHS = BOX_PIECES;
+};
+
 class LInstanceOfO : public LInstructionHelper<1, 2, 2>
 {
   public:
     LIR_HEADER(InstanceOfO);
-    LInstanceOfO(const LAllocation &lhs, const LAllocation &rhs, const LDefinition &temp, const LDefinition &temp2) {
+    LInstanceOfO(const LAllocation &lhs, const LAllocation &rhs,
+                 const LDefinition &temp, const LDefinition &temp2)
+    {
         setOperand(0, lhs);
         setOperand(1, rhs);
         setTemp(0, temp);
         setTemp(1, temp2);
     }
 
     const LAllocation *lhs() {
         return getOperand(0);
--- a/js/src/ion/LOpcodes.h
+++ b/js/src/ion/LOpcodes.h
@@ -159,16 +159,17 @@
     _(TypedArrayElements)           \
     _(StringLength)                 \
     _(ArgumentsLength)              \
     _(GetArgument)                  \
     _(TypeOfV)                      \
     _(ToIdV)                        \
     _(Floor)                        \
     _(Round)                        \
+    _(In)                           \
     _(InstanceOfO)                  \
     _(InstanceOfV)                  \
     _(InterruptCheck)               \
     _(FunctionBoundary)             \
     _(GetDOMProperty)               \
     _(SetDOMProperty)               \
     _(CallDOMNative)
 
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -1825,32 +1825,47 @@ LIRGenerator::visitThrow(MThrow *ins)
 
     LThrow *lir = new LThrow;
     if (!useBoxAtStart(lir, LThrow::Value, value))
         return false;
     return add(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
+LIRGenerator::visitIn(MIn *ins)
+{
+    MDefinition *lhs = ins->lhs();
+    MDefinition *rhs = ins->rhs();
+
+    JS_ASSERT(lhs->type() == MIRType_Value);
+    JS_ASSERT(rhs->type() == MIRType_Object);
+
+    LIn *lir = new LIn(useRegisterAtStart(rhs));
+    if (!useBoxAtStart(lir, LIn::LHS, lhs))
+        return false;
+    return defineVMReturn(lir, ins) && assignSafepoint(lir, ins);
+}
+
+bool
 LIRGenerator::visitInstanceOf(MInstanceOf *ins)
 {
     MDefinition *lhs = ins->lhs();
     MDefinition *rhs = ins->rhs();
 
     JS_ASSERT(lhs->type() == MIRType_Value || lhs->type() == MIRType_Object);
     JS_ASSERT(rhs->type() == MIRType_Object);
 
     // InstanceOf with non-object will always return false
     if (lhs->type() == MIRType_Object) {
         LInstanceOfO *lir = new LInstanceOfO(useRegister(lhs), useRegister(rhs), temp(), temp());
         return define(lir, ins) && assignSafepoint(lir, ins);
-    } else {
-        LInstanceOfV *lir = new LInstanceOfV(useRegister(rhs), temp(), temp());
-        return useBox(lir, LInstanceOfV::LHS, lhs) && define(lir, ins) && assignSafepoint(lir, ins);
     }
+
+    LInstanceOfV *lir = new LInstanceOfV(useRegister(rhs), temp(), temp());
+    return useBox(lir, LInstanceOfV::LHS, lhs) && define(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitFunctionBoundary(MFunctionBoundary *ins)
 {
     LFunctionBoundary *lir = new LFunctionBoundary(temp());
     if (!add(lir, ins))
         return false;
--- a/js/src/ion/Lowering.h
+++ b/js/src/ion/Lowering.h
@@ -174,16 +174,17 @@ class LIRGenerator : public LIRGenerator
     bool visitIteratorStart(MIteratorStart *ins);
     bool visitIteratorNext(MIteratorNext *ins);
     bool visitIteratorMore(MIteratorMore *ins);
     bool visitIteratorEnd(MIteratorEnd *ins);
     bool visitStringLength(MStringLength *ins);
     bool visitArgumentsLength(MArgumentsLength *ins);
     bool visitGetArgument(MGetArgument *ins);
     bool visitThrow(MThrow *ins);
+    bool visitIn(MIn *ins);
     bool visitInstanceOf(MInstanceOf *ins);
     bool visitFunctionBoundary(MFunctionBoundary *ins);
     bool visitSetDOMProperty(MSetDOMProperty *ins);
     bool visitGetDOMProperty(MGetDOMProperty *ins);
 };
 
 } // namespace ion
 } // namespace js
--- a/js/src/ion/MIR.cpp
+++ b/js/src/ion/MIR.cpp
@@ -916,17 +916,17 @@ MMul::foldsTo(bool useValueNumbers)
 {
     MDefinition *out = MBinaryArithInstruction::foldsTo(useValueNumbers);
     if (out != this)
         return out;
 
     if (specialization() != MIRType_Int32)
         return this;
 
-    if (lhs()->congruentTo(rhs()))
+    if (EqualValues(useValueNumbers, lhs(), rhs()))
         canBeNegativeZero_ = false;
 
     return this;
 }
 
 void
 MMul::analyzeEdgeCasesForward()
 {
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -5156,33 +5156,52 @@ class MIteratorMore
 };
 
 class MIteratorEnd
   : public MUnaryInstruction,
     public SingleObjectPolicy
 {
     MIteratorEnd(MDefinition *iter)
       : MUnaryInstruction(iter)
-    {}
+    { }
 
   public:
     INSTRUCTION_HEADER(IteratorEnd);
 
     static MIteratorEnd *New(MDefinition *iter) {
         return new MIteratorEnd(iter);
     }
 
     TypePolicy *typePolicy() {
         return this;
     }
     MDefinition *iterator() const {
         return getOperand(0);
     }
 };
 
+// Implementation for 'in' operator.
+class MIn
+  : public MBinaryInstruction,
+    public MixPolicy<BoxPolicy<0>, ObjectPolicy<1> >
+{
+  public:
+    MIn(MDefinition *key, MDefinition *obj)
+      : MBinaryInstruction(key, obj)
+    {
+        setResultType(MIRType_Boolean);
+    }
+
+    INSTRUCTION_HEADER(In);
+
+    TypePolicy *typePolicy() {
+        return this;
+    }
+};
+
 // Implementation for instanceof operator.
 class MInstanceOf
   : public MBinaryInstruction,
     public InstanceOfPolicy
 {
   public:
     MInstanceOf(MDefinition *obj, MDefinition *proto)
       : MBinaryInstruction(obj, proto)
--- a/js/src/ion/MOpcodes.h
+++ b/js/src/ion/MOpcodes.h
@@ -124,16 +124,17 @@ namespace ion {
     _(IteratorNext)                                                         \
     _(IteratorMore)                                                         \
     _(IteratorEnd)                                                          \
     _(StringLength)                                                         \
     _(ArgumentsLength)                                                      \
     _(GetArgument)                                                          \
     _(Floor)                                                                \
     _(Round)                                                                \
+    _(In)                                                                   \
     _(InstanceOf)                                                           \
     _(InterruptCheck)                                                       \
     _(FunctionBoundary)                                                     \
     _(GetDOMProperty)                                                       \
     _(SetDOMProperty)
 
 // Forward declarations of MIR types.
 #define FORWARD_DECLARE(op) class M##op;
--- a/js/src/ion/VMFunctions.cpp
+++ b/js/src/ion/VMFunctions.cpp
@@ -420,10 +420,26 @@ bool SPSEnter(JSContext *cx, HandleScrip
 }
 
 bool SPSExit(JSContext *cx, HandleScript script)
 {
     cx->runtime->spsProfiler.exit(cx, script, script->function());
     return true;
 }
 
+bool OperatorIn(JSContext *cx, HandleValue key, HandleObject obj, JSBool *out)
+{
+    RootedValue dummy(cx); // Disregards atomization changes: no way to propagate.
+    RootedId id(cx);
+    if (!FetchElementId(cx, obj, key, id.address(), &dummy))
+        return false;
+
+    RootedObject obj2(cx);
+    RootedShape prop(cx);
+    if (!JSObject::lookupGeneric(cx, obj, id, &obj2, &prop))
+        return false;
+
+    *out = !!prop;
+    return true;
+}
+
 } // namespace ion
 } // namespace js
--- a/js/src/ion/VMFunctions.h
+++ b/js/src/ion/VMFunctions.h
@@ -440,14 +440,15 @@ bool InterruptCheck(JSContext *cx);
 
 HeapSlot *NewSlots(JSRuntime *rt, unsigned nslots);
 JSObject *NewCallObject(JSContext *cx, HandleShape shape, HandleTypeObject type, HeapSlot *slots);
 JSObject *NewStringObject(JSContext *cx, HandleString str);
 
 bool SPSEnter(JSContext *cx, HandleScript script);
 bool SPSExit(JSContext *cx, HandleScript script);
 
+bool OperatorIn(JSContext *cx, HandleValue key, HandleObject obj, JSBool *out);
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_vm_functions_h_
 
--- a/js/src/jsapi-tests/testGCOutOfMemory.cpp
+++ b/js/src/jsapi-tests/testGCOutOfMemory.cpp
@@ -45,16 +45,16 @@ BEGIN_TEST(testGCOutOfMemory)
          "        array.push({});"
          "    }"
          "})();", root.address());
     CHECK_EQUAL(errorCount, 1);
     return true;
 }
 
 virtual JSRuntime * createRuntime() {
-    return JS_NewRuntime(512 * 1024);
+    return JS_NewRuntime(512 * 1024, JS_USE_HELPER_THREADS);
 }
 
 virtual void destroyRuntime() {
     JS_DestroyRuntime(rt);
 }
 
 END_TEST(testGCOutOfMemory)
--- a/js/src/jsapi-tests/testOOM.cpp
+++ b/js/src/jsapi-tests/testOOM.cpp
@@ -13,13 +13,13 @@ BEGIN_TEST(testOOM)
     JS_SetProperty(cx, global, "rootme", &tmp);
     mozilla::DebugOnly<const jschar *> s = JS_GetStringCharsZ(cx, jsstr);
     JS_ASSERT(s[0] == '9' && s[1] == '\0');
     return true;
 }
 
 virtual JSRuntime * createRuntime()
 {
-    JSRuntime *rt = JS_NewRuntime(0);
+    JSRuntime *rt = JS_NewRuntime(0, JS_USE_HELPER_THREADS);
     JS_SetGCParameter(rt, JSGC_MAX_BYTES, (uint32_t)-1);
     return rt;
 }
 END_TEST(testOOM)
--- a/js/src/jsapi-tests/tests.h
+++ b/js/src/jsapi-tests/tests.h
@@ -249,17 +249,17 @@ class JSAPITest
         fflush(stdout);
         JS_SET_RVAL(cx, vp, JSVAL_VOID);
         return JS_TRUE;
     }
 
     bool definePrint();
 
     virtual JSRuntime * createRuntime() {
-        JSRuntime *rt = JS_NewRuntime(8L * 1024 * 1024);
+        JSRuntime *rt = JS_NewRuntime(8L * 1024 * 1024, JS_USE_HELPER_THREADS);
         if (!rt)
             return NULL;
 
         const size_t MAX_STACK_SIZE =
 /* Assume we can't use more than 5e5 bytes of C stack by default. */
 #if (defined(DEBUG) && defined(__SUNPRO_CC))  || defined(JS_CPU_SPARC)
             /*
              * Sun compiler uses a larger stack space for js::Interpret() with
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -711,17 +711,17 @@ JS_FRIEND_API(void) LeaveAssertNoGCScope
 JS_FRIEND_API(bool) InNoGCScope() { return false; }
 JS_FRIEND_API(bool) NeedRelaxedRootChecks() { return false; }
 #endif
 
 } /* namespace JS */
 
 static const JSSecurityCallbacks NullSecurityCallbacks = { };
 
-JSRuntime::JSRuntime()
+JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
   : atomsCompartment(NULL),
 #ifdef JS_THREADSAFE
     ownerThread_(NULL),
 #endif
     tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     freeLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     execAlloc_(NULL),
     bumpAlloc_(NULL),
@@ -858,17 +858,18 @@ JSRuntime::JSRuntime()
 #endif
     inOOMReport(0),
     jitHardening(false),
     ionTop(NULL),
     ionJSContext(NULL),
     ionStackLimit(0),
     ionActivation(NULL),
     ionPcScriptCache(NULL),
-    ionReturnOverride_(MagicValue(JS_ARG_POISON))
+    ionReturnOverride_(MagicValue(JS_ARG_POISON)),
+    useHelperThreads_(useHelperThreads)
 {
     /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */
     JS_INIT_CLIST(&contextList);
     JS_INIT_CLIST(&debuggerList);
     JS_INIT_CLIST(&onNewGlobalObjectWatchers);
 
     PodZero(&debugHooks);
     PodZero(&atomState);
@@ -1055,17 +1056,17 @@ JS_FRIEND_API(void)
 JSRuntime::assertValidThread() const
 {
     JS_ASSERT(ownerThread_ == PR_GetCurrentThread());
     JS_ASSERT(this == JS::TlsRuntime.get());
 }
 #endif  /* JS_THREADSAFE */
 
 JS_PUBLIC_API(JSRuntime *)
-JS_NewRuntime(uint32_t maxbytes)
+JS_NewRuntime(uint32_t maxbytes, JSUseHelperThreads useHelperThreads)
 {
     if (!js_NewRuntimeWasCalled) {
 #ifdef DEBUG
         /*
          * This code asserts that the numbers associated with the error names
          * in jsmsg.def are monotonically increasing.  It uses values for the
          * error names enumerated in jscntxt.c.  It's not a compile-time check
          * but it's better than nothing.
@@ -1093,17 +1094,17 @@ JS_NewRuntime(uint32_t maxbytes)
         InitMemorySubsystem();
 
         if (!JS::TlsRuntime.init())
             return NULL;
 
         js_NewRuntimeWasCalled = JS_TRUE;
     }
 
-    JSRuntime *rt = js_new<JSRuntime>();
+    JSRuntime *rt = js_new<JSRuntime>(useHelperThreads);
     if (!rt)
         return NULL;
 
 #if defined(JS_METHODJIT) && defined(JS_ION)
     if (!ion::InitializeIon())
         return NULL;
 #endif
 
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2921,18 +2921,24 @@ JS_IsBuiltinFunctionConstructor(JSFuncti
  * single-threaded fashion, otherwise the behavior of the library is undefined.
  * See: http://developer.mozilla.org/en/docs/Category:JSAPI_Reference
  */
 #define JS_NewRuntime       JS_Init
 #define JS_DestroyRuntime   JS_Finish
 #define JS_LockRuntime      JS_Lock
 #define JS_UnlockRuntime    JS_Unlock
 
+typedef enum JSUseHelperThreads
+{
+    JS_NO_HELPER_THREADS,
+    JS_USE_HELPER_THREADS
+} JSUseHelperThreads;
+
 extern JS_PUBLIC_API(JSRuntime *)
-JS_NewRuntime(uint32_t maxbytes);
+JS_NewRuntime(uint32_t maxbytes, JSUseHelperThreads useHelperThreads);
 
 /* Deprecated. */
 #define JS_CommenceRuntimeShutDown(rt) ((void) 0)
 
 extern JS_PUBLIC_API(void)
 JS_DestroyRuntime(JSRuntime *rt);
 
 extern JS_PUBLIC_API(void)
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -3088,17 +3088,17 @@ array_readonlyCommon(JSContext *cx, Call
         js_ReportMissingArg(cx, args.calleev(), 0);
         return false;
     }
     RootedObject callable(cx, ValueToCallable(cx, &args[0]));
     if (!callable)
         return false;
 
     /* Step 5. */
-    Value thisv = args.length() >= 2 ? args[1] : UndefinedValue();
+    RootedValue thisv(cx, args.length() >= 2 ? args[1] : UndefinedValue());
 
     /* Step 6. */
     uint32_t k = 0;
 
     /* Step 7. */
     RootedValue kValue(cx);
     FastInvokeGuard fig(cx, ObjectValue(*callable));
     InvokeArgsGuard &ag = fig.args();
@@ -3181,17 +3181,17 @@ array_map(JSContext *cx, unsigned argc, 
         js_ReportMissingArg(cx, args.calleev(), 0);
         return false;
     }
     RootedObject callable(cx, ValueToCallable(cx, &args[0]));
     if (!callable)
         return false;
 
     /* Step 5. */
-    Value thisv = args.length() >= 2 ? args[1] : UndefinedValue();
+    RootedValue thisv(cx, args.length() >= 2 ? args[1] : UndefinedValue());
 
     /* Step 6. */
     RootedObject arr(cx, NewDenseAllocatedArray(cx, len));
     if (!arr)
         return false;
     TypeObject *newtype = GetTypeCallerInitObject(cx, JSProto_Array);
     if (!newtype)
         return false;
@@ -3259,17 +3259,17 @@ array_filter(JSContext *cx, unsigned arg
         js_ReportMissingArg(cx, args.calleev(), 0);
         return false;
     }
     RootedObject callable(cx, ValueToCallable(cx, &args[0]));
     if (!callable)
         return false;
 
     /* Step 5. */
-    Value thisv = args.length() >= 2 ? args[1] : UndefinedValue();
+    RootedValue thisv(cx, args.length() >= 2 ? args[1] : UndefinedValue());
 
     /* Step 6. */
     RootedObject arr(cx, NewDenseAllocatedArray(cx, 0));
     if (!arr)
         return false;
     TypeObject *newtype = GetTypeCallerInitObject(cx, JSProto_Array);
     if (!newtype)
         return false;
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -976,17 +976,17 @@ struct JSRuntime : js::RuntimeFriendFiel
         ionReturnOverride_ = js::MagicValue(JS_ARG_POISON);
         return v;
     }
     void setIonReturnOverride(const js::Value &v) {
         JS_ASSERT(!hasIonReturnOverride());
         ionReturnOverride_ = v;
     }
 
-    JSRuntime();
+    JSRuntime(JSUseHelperThreads useHelperThreads);
     ~JSRuntime();
 
     bool init(uint32_t maxbytes);
 
     JSRuntime *thisFromCtor() { return this; }
 
     /*
      * Call the system malloc while checking for GC memory pressure and
@@ -1093,16 +1093,27 @@ struct JSRuntime : js::RuntimeFriendFiel
 
     void setJitHardening(bool enabled);
     bool getJitHardening() const {
         return jitHardening;
     }
 
     void sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf, JS::RuntimeSizes *runtime);
     size_t sizeOfExplicitNonHeap();
+
+  private:
+    JSUseHelperThreads useHelperThreads_;
+  public:
+    bool useHelperThreads() const {
+#ifdef JS_THREADSAFE
+        return useHelperThreads_ == JS_USE_HELPER_THREADS;
+#else
+        return false;
+#endif
+    }
 };
 
 /* Common macros to access thread-local caches in JSRuntime. */
 #define JS_KEEP_ATOMS(rt)   (rt)->gcKeepAtoms++;
 #define JS_UNKEEP_ATOMS(rt) (rt)->gcKeepAtoms--;
 
 namespace js {
 
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -164,20 +164,30 @@ fun_getProperty(JSContext *cx, HandleObj
     if (JSID_IS_ATOM(id, cx->names().caller)) {
         ++iter;
         if (iter.done() || !iter.isFunctionFrame()) {
             JS_ASSERT(vp.isNull());
             return true;
         }
 
         vp.set(iter.calleev());
+        if (!cx->compartment->wrap(cx, vp.address()))
+            return false;
 
-        /* Censor the caller if it is from another compartment. */
+        /*
+         * Censor the caller if we can't PUNCTURE it.
+         *
+         * NB - This will get much much nicer with bug 800915
+         */
         JSObject &caller = vp.toObject();
-        if (caller.compartment() != cx->compartment) {
+        JSErrorReporter reporter = JS_SetErrorReporter(cx, NULL);
+        bool punctureThrew = !UnwrapObjectChecked(cx, &caller);
+        JS_SetErrorReporter(cx, reporter);
+        if (punctureThrew) {
+            JS_ClearPendingException(cx);
             vp.setNull();
         } else if (caller.isFunction()) {
             JSFunction *callerFun = caller.toFunction();
             if (callerFun->isInterpreted() && callerFun->inStrictMode()) {
                 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
                                              JSMSG_CALLER_IS_STRICT);
                 return false;
             }
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2916,37 +2916,46 @@ GetCPUCount()
     }
     return ncpus;
 }
 #endif /* JS_THREADSAFE */
 
 bool
 GCHelperThread::init()
 {
+    if (!rt->useHelperThreads()) {
+        backgroundAllocation = false;
+        return true;
+    }
+
 #ifdef JS_THREADSAFE
     if (!(wakeup = PR_NewCondVar(rt->gcLock)))
         return false;
     if (!(done = PR_NewCondVar(rt->gcLock)))
         return false;
 
     thread = PR_CreateThread(PR_USER_THREAD, threadMain, this, PR_PRIORITY_NORMAL,
                              PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
     if (!thread)
         return false;
 
     backgroundAllocation = (GetCPUCount() >= 2);
-#else
-    backgroundAllocation = false;
 #endif /* JS_THREADSAFE */
     return true;
 }
 
 void
 GCHelperThread::finish()
 {
+    if (!rt->useHelperThreads()) {
+        JS_ASSERT(state == IDLE);
+        return;
+    }
+
+
 #ifdef JS_THREADSAFE
     PRThread *join = NULL;
     {
         AutoLockGC lock(rt);
         if (thread && state != SHUTDOWN) {
             /*
              * We cannot be in the ALLOCATING or CANCEL_ALLOCATION states as
              * the allocations should have been stopped during the last GC.
@@ -2961,21 +2970,16 @@ GCHelperThread::finish()
     if (join) {
         /* PR_DestroyThread is not necessary. */
         PR_JoinThread(join);
     }
     if (wakeup)
         PR_DestroyCondVar(wakeup);
     if (done)
         PR_DestroyCondVar(done);
-#else
-    /*
-     * In non-threadsafe configurations, we do all work synchronously, so we must be IDLE
-     */
-    JS_ASSERT(state == IDLE);
 #endif /* JS_THREADSAFE */
 }
 
 #ifdef JS_THREADSAFE
 /* static */
 void
 GCHelperThread::threadMain(void *arg)
 {
@@ -3031,34 +3035,36 @@ GCHelperThread::threadLoop()
         }
     }
 }
 #endif /* JS_THREADSAFE */
 
 void
 GCHelperThread::startBackgroundSweep(bool shouldShrink)
 {
+    JS_ASSERT(rt->useHelperThreads());
+
 #ifdef JS_THREADSAFE
     AutoLockGC lock(rt);
     JS_ASSERT(state == IDLE);
     JS_ASSERT(!sweepFlag);
     sweepFlag = true;
     shrinkFlag = shouldShrink;
     state = SWEEPING;
     PR_NotifyCondVar(wakeup);
-#else
-    JS_NOT_REACHED("No background sweep if !JS_THREADSAFE");
 #endif /* JS_THREADSAFE */
 }
 
-#ifdef JS_THREADSAFE
 /* Must be called with the GC lock taken. */
 void
 GCHelperThread::startBackgroundShrink()
 {
+    JS_ASSERT(rt->useHelperThreads());
+
+#ifdef JS_THREADSAFE
     switch (state) {
       case IDLE:
         JS_ASSERT(!sweepFlag);
         shrinkFlag = true;
         state = SWEEPING;
         PR_NotifyCondVar(wakeup);
         break;
       case SWEEPING:
@@ -3069,60 +3075,66 @@ GCHelperThread::startBackgroundShrink()
         /*
          * If we have started background allocation there is nothing to
          * shrink.
          */
         break;
       case SHUTDOWN:
         JS_NOT_REACHED("No shrink on shutdown");
     }
-}
 #endif /* JS_THREADSAFE */
+}
 
 void
 GCHelperThread::waitBackgroundSweepEnd()
 {
+    if (!rt->useHelperThreads()) {
+        JS_ASSERT(state == IDLE);
+        return;
+    }
+
 #ifdef JS_THREADSAFE
     AutoLockGC lock(rt);
     while (state == SWEEPING)
         PR_WaitCondVar(done, PR_INTERVAL_NO_TIMEOUT);
     if (rt->gcIncrementalState == NO_INCREMENTAL)
         AssertBackgroundSweepingFinished(rt);
-#else
-    JS_ASSERT(state == IDLE);
 #endif /* JS_THREADSAFE */
 }
 
 void
 GCHelperThread::waitBackgroundSweepOrAllocEnd()
 {
+    if (!rt->useHelperThreads()) {
+        JS_ASSERT(state == IDLE);
+        return;
+    }
+
 #ifdef JS_THREADSAFE
     AutoLockGC lock(rt);
     if (state == ALLOCATING)
         state = CANCEL_ALLOCATION;
     while (state == SWEEPING || state == CANCEL_ALLOCATION)
         PR_WaitCondVar(done, PR_INTERVAL_NO_TIMEOUT);
     if (rt->gcIncrementalState == NO_INCREMENTAL)
         AssertBackgroundSweepingFinished(rt);
-#else
-    JS_ASSERT(state == IDLE);
 #endif /* JS_THREADSAFE */
 }
 
 /* Must be called with the GC lock taken. */
 inline void
 GCHelperThread::startBackgroundAllocationIfIdle()
 {
+    JS_ASSERT(rt->useHelperThreads());
+
 #ifdef JS_THREADSAFE
     if (state == IDLE) {
         state = ALLOCATING;
         PR_NotifyCondVar(wakeup);
     }
-#else
-    JS_ASSERT(state == IDLE);
 #endif /* JS_THREADSAFE */
 }
 
 JS_FRIEND_API(void)
 GCHelperThread::replenishAndFreeLater(void *ptr)
 {
     JS_ASSERT(freeCursor == freeCursorEnd);
     do {
@@ -3798,17 +3810,17 @@ BeginSweepPhase(JSRuntime *rt)
                 c->setGCState(JSCompartment::Sweep);
         } else {
             isFull = false;
         }
     }
     JS_ASSERT_IF(isFull, rt->gcIsFull);
 
 #ifdef JS_THREADSAFE
-    rt->gcSweepOnBackgroundThread = rt->hasContexts();
+    rt->gcSweepOnBackgroundThread = rt->hasContexts() && rt->useHelperThreads();
 #endif
 
     /* Purge the ArenaLists before sweeping. */
     for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
         if (c->isGCSweeping())
             c->arenas.purge();
     }
 
@@ -3999,24 +4011,26 @@ EndSweepPhase(JSRuntime *rt, JSGCInvocat
 
         /*
          * This removes compartments from rt->compartment, so we do it last to make
          * sure we don't miss sweeping any compartments.
          */
         if (!lastGC)
             SweepCompartments(&fop, lastGC);
 
-#ifndef JS_THREADSAFE
-        /*
-         * Destroy arenas after we finished the sweeping so finalizers can safely
-         * use IsAboutToBeFinalized().
-         * This is done on the GCHelperThread if JS_THREADSAFE is defined.
-         */
-        ExpireChunksAndArenas(rt, gckind == GC_SHRINK);
-#endif
+        if (!rt->gcSweepOnBackgroundThread) {
+            /*
+             * Destroy arenas after we finished the sweeping so finalizers can
+             * safely use IsAboutToBeFinalized(). This is done on the
+             * GCHelperThread if possible. We acquire the lock only because
+             * Expire needs to unlock it for other callers.
+             */
+            AutoLockGC lock(rt);
+            ExpireChunksAndArenas(rt, gckind == GC_SHRINK);
+        }
     }
 
     /*
      * Reset the list of arenas marked as being allocated during sweep phase.
      */
     while (ArenaHeader *arena = rt->gcArenasAllocatedDuringSweep) {
         rt->gcArenasAllocatedDuringSweep = arena->getNextAllocDuringSweep();
         arena->unsetAllocDuringSweep();
@@ -4717,21 +4731,21 @@ PrepareForDebugGC(JSRuntime *rt)
     PrepareForFullGC(rt);
 }
 
 void
 ShrinkGCBuffers(JSRuntime *rt)
 {
     AutoLockGC lock(rt);
     JS_ASSERT(!rt->isHeapBusy());
-#ifndef JS_THREADSAFE
-    ExpireChunksAndArenas(rt, true);
-#else
-    rt->gcHelperThread.startBackgroundShrink();
-#endif
+
+    if (!rt->useHelperThreads())
+        ExpireChunksAndArenas(rt, true);
+    else
+        rt->gcHelperThread.startBackgroundShrink();
 }
 
 struct AutoFinishGC
 {
     AutoFinishGC(JSRuntime *rt) {
         if (IsIncrementalGCInProgress(rt)) {
             PrepareForIncrementalGC(rt);
             FinishIncrementalGC(rt, gcreason::API);
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -3845,17 +3845,17 @@ bool
 js::Throw(JSContext *cx, HandleValue v)
 {
     JS_ASSERT(!cx->isExceptionPending());
     cx->setPendingException(v);
     return false;
 }
 
 bool
-js::GetProperty(JSContext *cx, HandleValue v, PropertyName *name, MutableHandleValue vp)
+js::GetProperty(JSContext *cx, HandleValue v, HandlePropertyName name, MutableHandleValue vp)
 {
     if (name == cx->names().length) {
         // Fast path for strings, arrays and arguments.
         if (GetLengthProperty(v, vp))
             return true;
     }
 
     RootedObject obj(cx, ToObjectFromStack(cx, v));
--- a/js/src/jsinterp.h
+++ b/js/src/jsinterp.h
@@ -313,17 +313,17 @@ Debug_SetValueRangeToCrashOnTouch(HeapVa
     Debug_SetValueRangeToCrashOnTouch((Value *) vec, len);
 #endif
 }
 
 bool
 Throw(JSContext *cx, HandleValue v);
 
 bool
-GetProperty(JSContext *cx, HandleValue value, PropertyName *name, MutableHandleValue vp);
+GetProperty(JSContext *cx, HandleValue value, HandlePropertyName name, MutableHandleValue vp);
 
 bool
 GetScopeName(JSContext *cx, HandleObject obj, HandlePropertyName name, MutableHandleValue vp);
 
 bool
 GetScopeNameForTypeOf(JSContext *cx, HandleObject obj, HandlePropertyName name,
                       MutableHandleValue vp);
 
--- a/js/src/jsmemorymetrics.cpp
+++ b/js/src/jsmemorymetrics.cpp
@@ -16,16 +16,25 @@
 #include "jsobj.h"
 #include "jsscope.h"
 #include "jsscript.h"
 
 #include "jsobjinlines.h"
 
 #include "ion/IonCode.h"
 
+namespace js {
+
+size_t MemoryReportingSundriesThreshold()
+{
+    return 8 * 1024;
+}
+
+} // namespace js
+
 #ifdef JS_THREADSAFE
 
 namespace JS {
 
 using namespace js;
 
 typedef HashSet<ScriptSource *, DefaultHasher<ScriptSource *>, SystemAllocPolicy> SourceSet;
 
@@ -151,17 +160,29 @@ StatsCellCallback(JSRuntime *rt, void *d
             }
         }
         break;
     }
     case JSTRACE_STRING:
     {
         JSString *str = static_cast<JSString *>(thing);
         cStats->gcHeapStrings += thingSize;
-        cStats->stringChars += str->sizeOfExcludingThis(rtStats->mallocSizeOf);
+
+        size_t strSize = str->sizeOfExcludingThis(rtStats->mallocSizeOf);
+
+        // If we can't grow hugeStrings, let's just call this string non-huge.
+        // We're probably about to OOM anyway.
+        if (strSize >= HugeStringInfo::MinSize() && cStats->hugeStrings.growBy(1)) {
+            HugeStringInfo &info = cStats->hugeStrings.back();
+            info.length = str->length();
+            info.size = str->sizeOfExcludingThis(rtStats->mallocSizeOf);
+            PutEscapedString(info.buffer, sizeof(info.buffer), &str->asLinear(), 0);
+        } else {
+          cStats->nonHugeStringChars += strSize;
+        }
         break;
     }
     case JSTRACE_SHAPE:
     {
         Shape *shape = static_cast<Shape*>(thing);
         size_t propTableSize, kidsSize;
         shape->sizeOfExcludingThis(rtStats->mallocSizeOf, &propTableSize, &kidsSize);
         if (shape->inDictionary()) {
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1147,17 +1147,17 @@ ScriptSource::setSourceCopy(JSContext *c
     const size_t nbytes = length * sizeof(jschar);
     data.compressed = static_cast<unsigned char *>(cx->malloc_(nbytes));
     if (!data.compressed)
         return false;
     length_ = length;
     argumentsNotIncluded_ = argumentsNotIncluded;
 
 #ifdef JS_THREADSAFE
-    if (tok) {
+    if (tok && cx->runtime->useHelperThreads()) {
 #ifdef DEBUG
         ready_ = false;
 #endif
         tok->ss = this;
         tok->chars = src.get();
         cx->runtime->sourceCompressorThread.compress(tok);
     } else
 #endif
--- a/js/src/jsworkers.cpp
+++ b/js/src/jsworkers.cpp
@@ -10,16 +10,23 @@
 # include "ion/IonBuilder.h"
 #endif
 
 using namespace js;
 
 #ifdef JS_PARALLEL_COMPILATION
 
 bool
+js::OffThreadCompilationAvailable(JSContext *cx)
+{
+    WorkerThreadState &state = *cx->runtime->workerThreadState;
+    return state.numThreads > 0;
+}
+
+bool
 js::StartOffThreadIonCompile(JSContext *cx, ion::IonBuilder *builder)
 {
     JSRuntime *rt = cx->runtime;
     if (!rt->workerThreadState) {
         rt->workerThreadState = rt->new_<WorkerThreadState>();
         if (!rt->workerThreadState)
             return false;
         if (!rt->workerThreadState->init(rt)) {
@@ -106,16 +113,21 @@ js::CancelOffThreadIonCompile(JSCompartm
             compilations.popBack();
         }
     }
 }
 
 bool
 WorkerThreadState::init(JSRuntime *rt)
 {
+    if (!rt->useHelperThreads()) {
+        numThreads = 0;
+        return true;
+    }
+
     workerLock = PR_NewLock();
     if (!workerLock)
         return false;
 
     mainWakeup = PR_NewCondVar(workerLock);
     if (!mainWakeup)
         return false;
 
@@ -314,9 +326,15 @@ js::StartOffThreadIonCompile(JSContext *
     return false;
 }
 
 void
 js::CancelOffThreadIonCompile(JSCompartment *compartment, JSScript *script)
 {
 }
 
+bool
+js::OffThreadCompilationAvailable(JSContext *cx)
+{
+    return false;
+}
+
 #endif /* JS_PARALLEL_COMPILATION */
--- a/js/src/jsworkers.h
+++ b/js/src/jsworkers.h
@@ -99,23 +99,29 @@ struct WorkerThread
 #endif /* JS_THREADSAFE && JS_ION */
 
 /* Methods for interacting with worker threads. */
 
 /*
  * Schedule an Ion compilation for a script, given a builder which has been
  * generated and read everything needed from the VM state.
  */
-bool StartOffThreadIonCompile(JSContext *cx, ion::IonBuilder *builder);
+bool
+StartOffThreadIonCompile(JSContext *cx, ion::IonBuilder *builder);
 
 /*
  * Cancel a scheduled or in progress Ion compilation for script. If script is
  * NULL, all compilations for the compartment are cancelled.
  */
-void CancelOffThreadIonCompile(JSCompartment *compartment, JSScript *script);
+void
+CancelOffThreadIonCompile(JSCompartment *compartment, JSScript *script);
+
+/* Return true iff off-thread compilation is possible. */
+bool
+OffThreadCompilationAvailable(JSContext *cx);
 
 class AutoLockWorkerThreadState
 {
     JSRuntime *rt;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 
   public:
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -2028,16 +2028,17 @@ DumpHeap(JSContext *cx, unsigned argc, j
     void* startThing;
     JSGCTraceKind startTraceKind;
     const char *badTraceArg;
     void *thingToFind;
     size_t maxDepth;
     void *thingToIgnore;
     FILE *dumpFile;
     bool ok;
+    AssertCanGC();
 
     const char *fileName = NULL;
     JSAutoByteString fileNameBytes;
     if (argc > 0) {
         v = JS_ARGV(cx, vp)[0];
         if (!JSVAL_IS_NULL(v)) {
             JSString *str;
 
@@ -2046,16 +2047,30 @@ DumpHeap(JSContext *cx, unsigned argc, j
                 return false;
             JS_ARGV(cx, vp)[0] = STRING_TO_JSVAL(str);
             if (!fileNameBytes.encode(cx, str))
                 return false;
             fileName = fileNameBytes.ptr();
         }
     }
 
+    // Grab the depth param first, because JS_ValueToECMAUint32 can GC, and
+    // there's no easy way to root the traceable void* parameters below.
+    maxDepth = (size_t)-1;
+    if (argc > 3) {
+        v = JS_ARGV(cx, vp)[3];
+        if (!JSVAL_IS_NULL(v)) {
+            uint32_t depth;
+
+            if (!JS_ValueToECMAUint32(cx, v, &depth))
+                return false;
+            maxDepth = depth;
+        }
+    }
+
     startThing = NULL;
     startTraceKind = JSTRACE_OBJECT;
     if (argc > 1) {
         v = JS_ARGV(cx, vp)[1];
         if (JSVAL_IS_TRACEABLE(v)) {
             startThing = JSVAL_TO_TRACEABLE(v);
             startTraceKind = JSVAL_TRACE_KIND(v);
         } else if (!JSVAL_IS_NULL(v)) {
@@ -2070,28 +2085,16 @@ DumpHeap(JSContext *cx, unsigned argc, j
         if (JSVAL_IS_TRACEABLE(v)) {
             thingToFind = JSVAL_TO_TRACEABLE(v);
         } else if (!JSVAL_IS_NULL(v)) {
             badTraceArg = "toFind";
             goto not_traceable_arg;
         }
     }
 
-    maxDepth = (size_t)-1;
-    if (argc > 3) {
-        v = JS_ARGV(cx, vp)[3];
-        if (!JSVAL_IS_NULL(v)) {
-            uint32_t depth;
-
-            if (!JS_ValueToECMAUint32(cx, v, &depth))
-                return false;
-            maxDepth = depth;
-        }
-    }
-
     thingToIgnore = NULL;
     if (argc > 4) {
         v = JS_ARGV(cx, vp)[4];
         if (JSVAL_IS_TRACEABLE(v)) {
             thingToIgnore = JSVAL_TO_TRACEABLE(v);
         } else if (!JSVAL_IS_NULL(v)) {
             badTraceArg = "toIgnore";
             goto not_traceable_arg;
@@ -4936,17 +4939,17 @@ main(int argc, char **argv, char **envp)
 #ifdef XP_WIN
     // Set the timer calibration delay count to 0 so we get high
     // resolution right away, which we need for precise benchmarking.
     extern int CALIBRATION_DELAY_COUNT;
     CALIBRATION_DELAY_COUNT = 0;
 #endif
 
     /* Use the same parameters as the browser in xpcjsruntime.cpp. */
-    rt = JS_NewRuntime(32L * 1024L * 1024L);
+    rt = JS_NewRuntime(32L * 1024L * 1024L, JS_USE_HELPER_THREADS);
     if (!rt)
         return 1;
 
     JS_SetGCParameter(rt, JSGC_MAX_BYTES, 0xffffffff);
 
     JS_SetTrustedPrincipals(rt, &shellTrustedPrincipals);
     JS_SetSecurityCallbacks(rt, &securityCallbacks);
 
--- a/js/src/tests/lib/results.py
+++ b/js/src/tests/lib/results.py
@@ -108,17 +108,17 @@ class ResultsSink:
             if self.options.tinderbox:
                 self.print_tinderbox_result('TEST-KNOWN-FAIL', output.test.path, time=output.dt, skip=True)
             self.counts['SKIP'] += 1
             self.n += 1
         else:
             result = TestResult.from_output(output)
 
             show = self.options.show
-            if self.options.failed_only and result == 'PASS':
+            if self.options.failed_only and result.result == 'PASS':
                 show = False
 
             if show and self.options.show_output:
                 print >> self.fp, '## %s: rc = %d, run time = %f' % (output.test.path, output.rc, output.dt)
 
             if show and self.options.show_cmd:
                 print >> self.fp, escape_cmdline(output.cmd)
 
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -1465,17 +1465,18 @@ StackIter::settleOnNewState()
 
         /* Pop the call and keep looking. */
         popCall();
     }
 }
 
 StackIter::StackIter(JSContext *cx, SavedOption savedOption)
   : maybecx_(cx),
-    savedOption_(savedOption)
+    savedOption_(savedOption),
+    script_(cx, NULL)
 #ifdef JS_ION
     , ionActivations_(cx),
     ionFrames_((uint8_t *)NULL),
     ionInlineFrames_((js::ion::IonFrameIterator*) NULL)
 #endif
 {
 #ifdef JS_METHODJIT
     CompartmentVector &v = cx->runtime->compartments;
@@ -1487,32 +1488,51 @@ StackIter::StackIter(JSContext *cx, Save
         startOnSegment(seg);
         settleOnNewState();
     } else {
         state_ = DONE;
     }
 }
 
 StackIter::StackIter(JSRuntime *rt, StackSegment &seg)
-  : maybecx_(NULL), savedOption_(STOP_AT_SAVED)
+  : maybecx_(NULL), savedOption_(STOP_AT_SAVED),
+    script_(rt, NULL)
 #ifdef JS_ION
     , ionActivations_(rt),
     ionFrames_((uint8_t *)NULL),
     ionInlineFrames_((js::ion::IonFrameIterator*) NULL)
 #endif
 {
 #ifdef JS_METHODJIT
     CompartmentVector &v = rt->compartments;
     for (size_t i = 0; i < v.length(); i++)
         mjit::ExpandInlineFrames(v[i]);
 #endif
     startOnSegment(&seg);
     settleOnNewState();
 }
 
+StackIter::StackIter(const StackIter &other)
+  : maybecx_(other.maybecx_),
+    savedOption_(other.savedOption_),
+    state_(other.state_),
+    fp_(other.fp_),
+    calls_(other.calls_),
+    seg_(other.seg_),
+    pc_(other.pc_),
+    script_(other.maybecx_ ? other.maybecx_->runtime : TlsRuntime.get(), other.script_),
+    args_(other.args_)
+#ifdef JS_ION
+    , ionActivations_(other.ionActivations_),
+    ionFrames_(other.ionFrames_),
+    ionInlineFrames_(other.ionInlineFrames_)
+#endif
+{
+}
+
 #ifdef JS_ION
 void
 StackIter::popIonFrame()
 {
     AutoAssertNoGC nogc;
     // Keep fp which describes all ion frames.
     poisonRegs();
     if (ionFrames_.isScripted() && ionInlineFrames_.more()) {
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -1719,18 +1719,18 @@ class StackIter
 
     State        state_;
 
     StackFrame   *fp_;
     CallArgsList *calls_;
 
     StackSegment *seg_;
     jsbytecode   *pc_;
-    JSScript     *script_;
-    CallArgs     args_;
+    RootedScript  script_;
+    CallArgs      args_;
 
 #ifdef JS_ION
     ion::IonActivationIterator ionActivations_;
     ion::IonFrameIterator ionFrames_;
     ion::InlineFrameIterator ionInlineFrames_;
 #endif
 
     void poisonRegs();
@@ -1741,16 +1741,17 @@ class StackIter
 #endif
     void settleOnNewSegment();
     void settleOnNewState();
     void startOnSegment(StackSegment *seg);
 
   public:
     StackIter(JSContext *cx, SavedOption = STOP_AT_SAVED);
     StackIter(JSRuntime *rt, StackSegment &seg);
+    StackIter(const StackIter &iter);
 
     bool done() const { return state_ == DONE; }
     StackIter &operator++();
 
     bool operator==(const StackIter &rhs) const;
     bool operator!=(const StackIter &rhs) const { return !(*this == rhs); }
 
     JSCompartment *compartment() const;
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -1385,17 +1385,17 @@ NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJS
     "and 'js-compartments-system' might not match the number of compartments "
     "listed under 'js' if a garbage collection occurs at an inopportune time, "
     "but such cases should be rare.")
 
 // The REPORT* macros do an unconditional report.  The CREPORT* macros are for
 // compartments;  they aggregate any entries smaller than SUNDRIES_THRESHOLD
 // into "gc-heap/sundries" and "other-sundries" entries for the compartment.
 
-static const size_t SUNDRIES_THRESHOLD = 8192;
+#define SUNDRIES_THRESHOLD js::MemoryReportingSundriesThreshold()
 
 #define REPORT(_path, _kind, _units, _amount, _desc)                          \
     do {                                                                      \
         nsresult rv;                                                          \
         rv = cb->Callback(EmptyCString(), _path, _kind, _units, _amount,      \
                           NS_LITERAL_CSTRING(_desc), closure);                \
         NS_ENSURE_SUCCESS(rv, rv);                                            \
     } while (0)
@@ -1410,27 +1410,40 @@ static const size_t SUNDRIES_THRESHOLD =
         rv = cb->Callback(EmptyCString(), _path,                              \
                           nsIMemoryReporter::KIND_NONHEAP,                    \
                           nsIMemoryReporter::UNITS_BYTES, amount,             \
                           NS_LITERAL_CSTRING(_desc), closure);                \
         NS_ENSURE_SUCCESS(rv, rv);                                            \
         gcTotal += amount;                                                    \
     } while (0)
 
+// Report compartment bytes.  Note that _descLiteral must be a literal string.
+//
 // Nb: all non-GC compartment reports are currently KIND_HEAP, and this macro
 // relies on that.
-#define CREPORT_BYTES(_path, _amount, _desc)                                  \
+#define CREPORT_BYTES(_path, _amount, _descLiteral)                           \
+    do {                                                                      \
+        /* Assign _descLiteral plus "" into a char* to prove that it's */     \
+        /* actually a literal. */                                             \
+        const char* unusedDesc = _descLiteral "";                             \
+        (void) unusedDesc;                                                    \
+        CREPORT_BYTES2(_path, _amount, NS_LITERAL_CSTRING(_descLiteral));     \
+    } while (0)
+
+// CREPORT_BYTES2 is identical to CREPORT_BYTES, except the description is a
+// nsCString instead of a literal string.
+#define CREPORT_BYTES2(_path, _amount, _desc)                                 \
     do {                                                                      \
         size_t amount = _amount;  /* evaluate _amount only once */            \
         if (amount >= SUNDRIES_THRESHOLD) {                                   \
             nsresult rv;                                                      \
             rv = cb->Callback(EmptyCString(), _path,                          \
                               nsIMemoryReporter::KIND_HEAP,                   \
                               nsIMemoryReporter::UNITS_BYTES, amount,         \
-                              NS_LITERAL_CSTRING(_desc), closure);            \
+                              _desc, closure);                                \
             NS_ENSURE_SUCCESS(rv, rv);                                        \
         } else {                                                              \
             otherSundries += amount;                                          \
         }                                                                     \
     } while (0)
 
 #define CREPORT_GC_BYTES(_path, _amount, _desc)                               \
     do {                                                                      \
@@ -1566,25 +1579,16 @@ ReportCompartmentStats(const JS::Compart
     // Note that we use cDOMPathPrefix here.  This is because we measure orphan
     // DOM nodes in the JS multi-reporter, but we want to report them in a
     // "dom" sub-tree rather than a "js" sub-tree.
     CREPORT_BYTES(cDOMPathPrefix + NS_LITERAL_CSTRING("orphan-nodes"),
                   cStats.objectPrivate,
                   "Memory used by orphan DOM nodes that are only reachable "
                   "from JavaScript objects.");
 
-    CREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("string-chars"),
-                  cStats.stringChars,
-                  "Memory allocated to hold string "
-                  "characters.  Sometimes more memory is allocated than "
-                  "necessary, to simplify string concatenation.  Each string "
-                  "also includes a header which is stored on the "
-                  "compartment's JavaScript heap;  that header is not counted "
-                  "here, but in 'gc-heap/strings' instead.");
-
     CREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes-extra/tree-tables"),
                   cStats.shapesExtraTreeTables,
                   "Memory allocated for the property tables "
                   "that belong to shapes that are in a property tree.");
 
     CREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes-extra/dict-tables"),
                   cStats.shapesExtraDictTables,
                   "Memory allocated for the property tables "
@@ -1646,16 +1650,52 @@ ReportCompartmentStats(const JS::Compart
                   "Memory used during type inference for compartment-wide "
                   "tables.");
 
     CREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("analysis-temporary"),
                   cStats.typeInferenceSizes.temporary,
                   "Memory used during type inference and compilation to hold "
                   "transient analysis information.  Cleared on GC.");
 
+    CREPORT_BYTES2(cJSPathPrefix + NS_LITERAL_CSTRING("string-chars/non-huge"),
+                   cStats.nonHugeStringChars, nsPrintfCString(
+                   "Memory allocated to hold characters of strings whose "
+                   "characters take up less than than %d bytes of memory.\n\n"
+                   "Sometimes more memory is allocated than necessary, to "
+                   "simplify string concatenation.  Each string also includes a "
+                   "header which is stored on the compartment's JavaScript heap; "
+                   "that header is not counted here, but in 'gc-heap/strings' "
+                   "instead.",
+                   JS::HugeStringInfo::MinSize()));
+
+    for (size_t i = 0; i < cStats.hugeStrings.length(); i++) {
+        const JS::HugeStringInfo& info = cStats.hugeStrings[i];
+
+        nsDependentCString hugeString(info.buffer);
+
+        // Escape / to \/ before we put hugeString into the memory reporter
+        // path, because we don't want any forward slashes in the string to
+        // count as path separators.
+        nsCString escapedString(hugeString);
+        escapedString.ReplaceSubstring("/", "\\/");
+
+        CREPORT_BYTES2(
+            cJSPathPrefix +
+            nsPrintfCString("string-chars/huge/string(length=%d, \"%s...\")",
+                            info.length, escapedString.get()),
+            info.size,
+            nsPrintfCString("Memory allocated to hold characters of "
+            "a length-%d string which begins \"%s\".\n\n"
+            "Sometimes more memory is allocated than necessary, to simplify "
+            "string concatenation.  Each string also includes a header which is "
+            "stored on the compartment's JavaScript heap; that header is not "
+            "counted here, but in 'gc-heap/strings' instead.",
+            info.length, hugeString.get()));
+    }
+
     if (gcHeapSundries > 0) {
         // We deliberately don't use CREPORT_GC_BYTES here.
         REPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("gc-heap/sundries"),
                         gcHeapSundries,
                         "The sum of all the gc-heap "
                         "measurements that are too small to be worth showing "
                         "individually.");
     }
@@ -2239,17 +2279,17 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* 
     Preferences::AddBoolVarCache(&gExperimentalBindingsEnabled,
                                  "dom.experimental_bindings",
                                  false);
 
 
     // these jsids filled in later when we have a JSContext to work with.
     mStrIDs[0] = JSID_VOID;
 
-    mJSRuntime = JS_NewRuntime(32L * 1024L * 1024L); // pref ?
+    mJSRuntime = JS_NewRuntime(32L * 1024L * 1024L, JS_USE_HELPER_THREADS); // pref ?
     if (!mJSRuntime)
         NS_RUNTIMEABORT("JS_NewRuntime failed.");
 
     // Unconstrain the runtime's threshold on nominal heap size, to avoid
     // triggering GC too often if operating continuously near an arbitrary
     // finite threshold (0xffffffff is infinity for uint32_t parameters).
     // This leaves the maximum-JS_malloc-bytes threshold still in effect
     // to cause period, and we hope hygienic, last-ditch GCs from within
--- a/js/xpconnect/tests/chrome/Makefile.in
+++ b/js/xpconnect/tests/chrome/Makefile.in
@@ -34,19 +34,21 @@ MOCHITEST_CHROME_FILES = \
 		test_bug726949.xul \
 		test_bug738244.xul \
 		test_bug743843.xul \
 		test_bug760076.xul \
 		test_bug760109.xul \
 		test_bug763343.xul \
 		test_bug771429.xul \
 		test_bug773962.xul \
+		test_bug792280.xul \
 		test_bug793433.xul \
 		test_bug795275.xul \
 		test_bug799348.xul \
+		test_bug801241.xul \
 		test_APIExposer.xul \
 		test_chrometoSource.xul \
 		outoflinexulscript.js \
 		subscript.js \
 		test_cows.xul \
 		test_documentdomain.xul \
 		test_doublewrappedcompartments.xul \
 		test_evalInSandbox.xul \
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug792280.xul
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=792280