Merge fx-team to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 07 Oct 2016 09:38:06 -0400
changeset 316895 9c11fedf43675388a547f09cc4a64e78739f5220
parent 316884 1de5e07bed6601d2647e88ed2690b3f30418d8aa (current diff)
parent 316894 b5c1f385ee5209796b4808cc9c40c0cb0cb4521a (diff)
child 316925 ea8624a9b11e89b831b830cef70c47ae67ccead1
push id30786
push userryanvm@gmail.com
push dateFri, 07 Oct 2016 13:38:16 +0000
treeherdermozilla-central@9c11fedf4367 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge fx-team to m-c. a=merge
--- a/.eslintignore
+++ b/.eslintignore
@@ -97,17 +97,17 @@ devtools/client/shared/webgl-utils.js
 devtools/client/shared/developer-toolbar.js
 devtools/client/shared/components/test/**
 devtools/client/shared/redux/middleware/test/**
 devtools/client/shared/test/**
 !devtools/client/shared/test/test-actor-registry.js
 devtools/client/shared/widgets/*.jsm
 devtools/client/sourceeditor/test/*.js
 devtools/client/webaudioeditor/**
-#devtools/client/webconsole/**
+devtools/client/webconsole/**
 !devtools/client/webconsole/panel.js
 !devtools/client/webconsole/jsterm.js
 !devtools/client/webconsole/console-commands.js
 devtools/client/webide/**
 !devtools/client/webide/components/webideCli.js
 devtools/server/*.js
 devtools/server/*.jsm
 !devtools/server/child.js
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,3 +1,3 @@
 This is the pdf.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 1.5.498
+Current extension version is: 1.6.221
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -23,18 +23,18 @@ define('pdfjs-dist/build/pdf', ['exports
     factory(exports);
   } else {
 factory((root.pdfjsDistBuildPdf = {}));
   }
 }(this, function (exports) {
   // Use strict in our context only - users might not want it
   'use strict';
 
-var pdfjsVersion = '1.5.498';
-var pdfjsBuild = '1564dc3';
+var pdfjsVersion = '1.6.221';
+var pdfjsBuild = 'f8bd3d4';
 
   var pdfjsFilePath =
     typeof document !== 'undefined' && document.currentScript ?
       document.currentScript.src : null;
 
   var pdfjsLibs = {};
 
   (function pdfjsWrapper() {
@@ -846,25 +846,25 @@ var Util = (function UtilClosure() {
   };
 
   Util.extendObj = function extendObj(obj1, obj2) {
     for (var key in obj2) {
       obj1[key] = obj2[key];
     }
   };
 
-  Util.getInheritableProperty = function Util_getInheritableProperty(dict,
-                                                                     name) {
+  Util.getInheritableProperty =
+      function Util_getInheritableProperty(dict, name, getArray) {
     while (dict && !dict.has(name)) {
       dict = dict.get('Parent');
     }
     if (!dict) {
       return null;
     }
-    return dict.get(name);
+    return getArray ? dict.getArray(name) : dict.get(name);
   };
 
   Util.inherit = function Util_inherit(sub, base, prototype) {
     sub.prototype = Object.create(base.prototype);
     sub.prototype.constructor = sub;
     for (var prop in prototype) {
       sub.prototype[prop] = prototype[prop];
     }
@@ -1988,16 +1988,18 @@ AnnotationElementFactory.prototype =
         return new TextAnnotationElement(parameters);
 
       case AnnotationType.WIDGET:
         var fieldType = parameters.data.fieldType;
 
         switch (fieldType) {
           case 'Tx':
             return new TextWidgetAnnotationElement(parameters);
+          case 'Ch':
+            return new ChoiceWidgetAnnotationElement(parameters);
         }
         return new WidgetAnnotationElement(parameters);
 
       case AnnotationType.POPUP:
         return new PopupAnnotationElement(parameters);
 
       case AnnotationType.HIGHLIGHT:
         return new HighlightAnnotationElement(parameters);
@@ -2312,19 +2314,17 @@ var TextAnnotationElement = (function Te
   return TextAnnotationElement;
 })();
 
 /**
  * @class
  * @alias WidgetAnnotationElement
  */
 var WidgetAnnotationElement = (function WidgetAnnotationElementClosure() {
-  function WidgetAnnotationElement(parameters) {
-    var isRenderable = parameters.renderInteractiveForms ||
-      (!parameters.data.hasAppearance && !!parameters.data.fieldValue);
+  function WidgetAnnotationElement(parameters, isRenderable) {
     AnnotationElement.call(this, parameters, isRenderable);
   }
 
   Util.inherit(WidgetAnnotationElement, AnnotationElement, {
     /**
      * Render the widget annotation's HTML element in the empty container.
      *
      * @public
@@ -2344,17 +2344,19 @@ var WidgetAnnotationElement = (function 
  * @class
  * @alias TextWidgetAnnotationElement
  */
 var TextWidgetAnnotationElement = (
     function TextWidgetAnnotationElementClosure() {
   var TEXT_ALIGNMENT = ['left', 'center', 'right'];
 
   function TextWidgetAnnotationElement(parameters) {
-    WidgetAnnotationElement.call(this, parameters);
+    var isRenderable = parameters.renderInteractiveForms ||
+      (!parameters.data.hasAppearance && !!parameters.data.fieldValue);
+    WidgetAnnotationElement.call(this, parameters, isRenderable);
   }
 
   Util.inherit(TextWidgetAnnotationElement, WidgetAnnotationElement, {
     /**
      * Render the text widget annotation's HTML element in the empty container.
      *
      * @public
      * @memberof TextWidgetAnnotationElement
@@ -2442,16 +2444,74 @@ var TextWidgetAnnotationElement = (
     }
   });
 
   return TextWidgetAnnotationElement;
 })();
 
 /**
  * @class
+ * @alias ChoiceWidgetAnnotationElement
+ */
+var ChoiceWidgetAnnotationElement = (
+    function ChoiceWidgetAnnotationElementClosure() {
+  function ChoiceWidgetAnnotationElement(parameters) {
+    WidgetAnnotationElement.call(this, parameters,
+                                 parameters.renderInteractiveForms);
+  }
+
+  Util.inherit(ChoiceWidgetAnnotationElement, WidgetAnnotationElement, {
+    /**
+     * Render the choice widget annotation's HTML element in the empty
+     * container.
+     *
+     * @public
+     * @memberof ChoiceWidgetAnnotationElement
+     * @returns {HTMLSectionElement}
+     */
+    render: function ChoiceWidgetAnnotationElement_render() {
+      this.container.className = 'choiceWidgetAnnotation';
+
+      var selectElement = document.createElement('select');
+      selectElement.disabled = this.data.readOnly;
+
+      if (!this.data.combo) {
+        // List boxes have a size and (optionally) multiple selection.
+        selectElement.size = this.data.options.length;
+
+        if (this.data.multiSelect) {
+          selectElement.multiple = true;
+        }
+      }
+
+      // Insert the options into the choice field.
+      for (var i = 0, ii = this.data.options.length; i < ii; i++) {
+        var option = this.data.options[i];
+
+        var optionElement = document.createElement('option');
+        optionElement.textContent = option.displayValue;
+        optionElement.value = option.exportValue;
+
+        if (this.data.fieldValue.indexOf(option.displayValue) >= 0) {
+          optionElement.setAttribute('selected', true);
+        }
+
+        selectElement.appendChild(optionElement);
+      }
+
+      this.container.appendChild(selectElement);
+      return this.container;
+    }
+  });
+
+  return ChoiceWidgetAnnotationElement;
+})();
+
+/**
+ * @class
  * @alias PopupAnnotationElement
  */
 var PopupAnnotationElement = (function PopupAnnotationElementClosure() {
   function PopupAnnotationElement(parameters) {
     var isRenderable = !!(parameters.data.title || parameters.data.contents);
     AnnotationElement.call(this, parameters, isRenderable);
   }
 
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -23,18 +23,18 @@ define('pdfjs-dist/build/pdf.worker', ['
     factory(exports);
   } else {
 factory((root.pdfjsDistBuildPdfWorker = {}));
   }
 }(this, function (exports) {
   // Use strict in our context only - users might not want it
   'use strict';
 
-var pdfjsVersion = '1.5.498';
-var pdfjsBuild = '1564dc3';
+var pdfjsVersion = '1.6.221';
+var pdfjsBuild = 'f8bd3d4';
 
   var pdfjsFilePath =
     typeof document !== 'undefined' && document.currentScript ?
       document.currentScript.src : null;
 
   var pdfjsLibs = {};
 
   (function pdfjsWrapper() {
@@ -1832,25 +1832,25 @@ var Util = (function UtilClosure() {
   };
 
   Util.extendObj = function extendObj(obj1, obj2) {
     for (var key in obj2) {
       obj1[key] = obj2[key];
     }
   };
 
-  Util.getInheritableProperty = function Util_getInheritableProperty(dict,
-                                                                     name) {
+  Util.getInheritableProperty =
+      function Util_getInheritableProperty(dict, name, getArray) {
     while (dict && !dict.has(name)) {
       dict = dict.get('Parent');
     }
     if (!dict) {
       return null;
     }
-    return dict.get(name);
+    return getArray ? dict.getArray(name) : dict.get(name);
   };
 
   Util.inherit = function Util_inherit(sub, base, prototype) {
     sub.prototype = Object.create(base.prototype);
     sub.prototype.constructor = sub;
     for (var prop in prototype) {
       sub.prototype[prop] = prototype[prop];
     }
@@ -24655,17 +24655,20 @@ var Parser = (function ParserClosure() {
           stream = this.makeFilter(stream, filter.name, maybeLength, params);
           // after the first stream the length variable is invalid
           maybeLength = null;
         }
       }
       return stream;
     },
     makeFilter: function Parser_makeFilter(stream, name, maybeLength, params) {
-      if (stream.dict.get('Length') === 0 && !maybeLength) {
+      // Since the 'Length' entry in the stream dictionary can be completely
+      // wrong, e.g. zero for non-empty streams, only skip parsing the stream
+      // when we can be absolutely certain that it actually is empty.
+      if (maybeLength === 0) {
         warn('Empty "' + name + '" stream.');
         return new NullStream(stream);
       }
       try {
         if (params && this.xref) {
           params = this.xref.fetchIfRef(params);
         }
         var xrefStreamStats = this.xref.stats.streamTypes;
@@ -39363,16 +39366,18 @@ AnnotationFactory.prototype = /** @lends
 
       case 'Widget':
         var fieldType = Util.getInheritableProperty(dict, 'FT');
         fieldType = isName(fieldType) ? fieldType.name : null;
 
         switch (fieldType) {
           case 'Tx':
             return new TextWidgetAnnotation(parameters);
+          case 'Ch':
+            return new ChoiceWidgetAnnotation(parameters);
         }
         warn('Unimplemented widget field type "' + fieldType + '", ' +
              'falling back to base field type.');
         return new WidgetAnnotation(parameters);
 
       case 'Popup':
         return new PopupAnnotation(parameters);
 
@@ -39876,29 +39881,31 @@ var AnnotationBorderStyle = (function An
 var WidgetAnnotation = (function WidgetAnnotationClosure() {
   function WidgetAnnotation(params) {
     Annotation.call(this, params);
 
     var dict = params.dict;
     var data = this.data;
 
     data.annotationType = AnnotationType.WIDGET;
-    data.fieldValue = stringToPDFString(
-      Util.getInheritableProperty(dict, 'V') || '');
+    data.fieldValue = Util.getInheritableProperty(dict, 'V',
+                                                  /* getArray = */ true);
     data.alternativeText = stringToPDFString(dict.get('TU') || '');
     data.defaultAppearance = Util.getInheritableProperty(dict, 'DA') || '';
     var fieldType = Util.getInheritableProperty(dict, 'FT');
     data.fieldType = isName(fieldType) ? fieldType.name : null;
     this.fieldResources = Util.getInheritableProperty(dict, 'DR') || Dict.empty;
 
     data.fieldFlags = Util.getInheritableProperty(dict, 'Ff');
     if (!isInt(data.fieldFlags) || data.fieldFlags < 0) {
       data.fieldFlags = 0;
     }
 
+    data.readOnly = this.hasFieldFlag(AnnotationFieldFlag.READONLY);
+
     // Hide signatures because we cannot validate them.
     if (data.fieldType === 'Sig') {
       this.setFlags(AnnotationFlag.HIDDEN);
     }
 
     // Building the full field name by collecting the field and
     // its ancestors 'T' data and joining them using '.'.
     var fieldName = [];
@@ -39950,32 +39957,34 @@ var WidgetAnnotation = (function WidgetA
 
   return WidgetAnnotation;
 })();
 
 var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() {
   function TextWidgetAnnotation(params) {
     WidgetAnnotation.call(this, params);
 
+    // The field value is always a string.
+    this.data.fieldValue = stringToPDFString(this.data.fieldValue || '');
+
     // Determine the alignment of text in the field.
     var alignment = Util.getInheritableProperty(params.dict, 'Q');
     if (!isInt(alignment) || alignment < 0 || alignment > 2) {
       alignment = null;
     }
     this.data.textAlignment = alignment;
 
     // Determine the maximum length of text in the field.
     var maximumLength = Util.getInheritableProperty(params.dict, 'MaxLen');
     if (!isInt(maximumLength) || maximumLength < 0) {
       maximumLength = null;
     }
     this.data.maxLen = maximumLength;
 
     // Process field flags for the display layer.
-    this.data.readOnly = this.hasFieldFlag(AnnotationFieldFlag.READONLY);
     this.data.multiLine = this.hasFieldFlag(AnnotationFieldFlag.MULTILINE);
     this.data.comb = this.hasFieldFlag(AnnotationFieldFlag.COMB) &&
                      !this.hasFieldFlag(AnnotationFieldFlag.MULTILINE) &&
                      !this.hasFieldFlag(AnnotationFieldFlag.PASSWORD) &&
                      !this.hasFieldFlag(AnnotationFieldFlag.FILESELECT) &&
                      this.data.maxLen !== null;
   }
 
@@ -40009,16 +40018,72 @@ var TextWidgetAnnotation = (function Tex
           return operatorList;
         });
     }
   });
 
   return TextWidgetAnnotation;
 })();
 
+var ChoiceWidgetAnnotation = (function ChoiceWidgetAnnotationClosure() {
+  function ChoiceWidgetAnnotation(params) {
+    WidgetAnnotation.call(this, params);
+
+    // Determine the options. The options array may consist of strings or
+    // arrays. If the array consists of arrays, then the first element of
+    // each array is the export value and the second element of each array is
+    // the display value. If the array consists of strings, then these
+    // represent both the export and display value. In this case, we convert
+    // it to an array of arrays as well for convenience in the display layer.
+    this.data.options = [];
+
+    var options = params.dict.getArray('Opt');
+    if (isArray(options)) {
+      for (var i = 0, ii = options.length; i < ii; i++) {
+        var option = options[i];
+
+        this.data.options[i] = {
+          exportValue: isArray(option) ? option[0] : option,
+          displayValue: isArray(option) ? option[1] : option,
+        };
+      }
+    }
+
+    // Determine the field value. In this case, it may be a string or an
+    // array of strings. For convenience in the display layer, convert the
+    // string to an array of one string as well.
+    if (!isArray(this.data.fieldValue)) {
+      this.data.fieldValue = [this.data.fieldValue];
+    }
+
+    // Process field flags for the display layer.
+    this.data.combo = this.hasFieldFlag(AnnotationFieldFlag.COMBO);
+    this.data.multiSelect = this.hasFieldFlag(AnnotationFieldFlag.MULTISELECT);
+  }
+
+  Util.inherit(ChoiceWidgetAnnotation, WidgetAnnotation, {
+    getOperatorList:
+        function ChoiceWidgetAnnotation_getOperatorList(evaluator, task,
+                                                        renderForms) {
+      var operatorList = new OperatorList();
+
+      // Do not render form elements on the canvas when interactive forms are
+      // enabled. The display layer is responsible for rendering them instead.
+      if (renderForms) {
+        return Promise.resolve(operatorList);
+      }
+
+      return Annotation.prototype.getOperatorList.call(this, evaluator, task,
+                                                       renderForms);
+    }
+  });
+
+  return ChoiceWidgetAnnotation;
+})();
+
 var TextAnnotation = (function TextAnnotationClosure() {
   var DEFAULT_ICON_SIZE = 22; // px
 
   function TextAnnotation(parameters) {
     Annotation.call(this, parameters);
 
     this.data.annotationType = AnnotationType.TEXT;
 
--- a/browser/extensions/pdfjs/content/web/viewer.css
+++ b/browser/extensions/pdfjs/content/web/viewer.css
@@ -97,17 +97,18 @@
 }
 
 .annotationLayer .textAnnotation img {
   position: absolute;
   cursor: pointer;
 }
 
 .annotationLayer .textWidgetAnnotation input,
-.annotationLayer .textWidgetAnnotation textarea {
+.annotationLayer .textWidgetAnnotation textarea,
+.annotationLayer .choiceWidgetAnnotation select {
   background-color: rgba(0, 54, 255, 0.13);
   border: 1px solid transparent;
   box-sizing: border-box;
   font-size: 9px;
   height: 100%;
   padding: 0 3px;
   vertical-align: top;
   width: 100%;
@@ -115,29 +116,32 @@
 
 .annotationLayer .textWidgetAnnotation textarea {
   font: message-box;
   font-size: 9px;
   resize: none;
 }
 
 .annotationLayer .textWidgetAnnotation input[disabled],
-.annotationLayer .textWidgetAnnotation textarea[disabled] {
+.annotationLayer .textWidgetAnnotation textarea[disabled],
+.annotationLayer .choiceWidgetAnnotation select[disabled] {
   background: none;
   border: 1px solid transparent;
   cursor: not-allowed;
 }
 
 .annotationLayer .textWidgetAnnotation input:hover,
-.annotationLayer .textWidgetAnnotation textarea:hover {
+.annotationLayer .textWidgetAnnotation textarea:hover,
+.annotationLayer .choiceWidgetAnnotation select:hover {
   border: 1px solid #000;
 }
 
 .annotationLayer .textWidgetAnnotation input:focus,
-.annotationLayer .textWidgetAnnotation textarea:focus {
+.annotationLayer .textWidgetAnnotation textarea:focus,
+.annotationLayer .choiceWidgetAnnotation select:focus {
   background: none;
   border: 1px solid transparent;
 }
 
 .annotationLayer .textWidgetAnnotation input.comb {
   font-family: monospace;
   padding-left: 2px;
   padding-right: 0;
@@ -1931,18 +1935,21 @@ html[dir='rtl'] #documentPropertiesOverl
   #printContainer {
     height: 100%;
   }
   /* wrapper around (scaled) print canvas elements */
   #printContainer > div {
     position: relative;
     top: 0;
     left: 0;
-    height: 100%;
-    overflow: hidden;
+    width: 1px;
+    height: 1px;
+    overflow: visible;
+    page-break-after: always;
+    page-break-inside: avoid;
   }
   #printContainer canvas {
     display: block;
   }
 }
 
 .visibleLargeView,
 .visibleMediumView,
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -5663,48 +5663,44 @@ var PDFPageView = (function PDFPageViewC
       return promise;
     },
 
     beforePrint: function PDFPageView_beforePrint(printContainer) {
       var CustomStyle = pdfjsLib.CustomStyle;
       var pdfPage = this.pdfPage;
 
       var viewport = pdfPage.getViewport(1);
-      // Use the same hack we use for high dpi displays for printing to get
-      // better output until bug 811002 is fixed in FF.
-      var PRINT_OUTPUT_SCALE = 2;
+
       var canvas = document.createElement('canvas');
 
-      // The logical size of the canvas.
-      canvas.width = Math.floor(viewport.width) * PRINT_OUTPUT_SCALE;
-      canvas.height = Math.floor(viewport.height) * PRINT_OUTPUT_SCALE;
-
-      // The rendered size of the canvas, relative to the size of canvasWrapper.
-      canvas.style.width = (PRINT_OUTPUT_SCALE * 100) + '%';
-
-      var cssScale = 'scale(' + (1 / PRINT_OUTPUT_SCALE) + ', ' +
-                                (1 / PRINT_OUTPUT_SCALE) + ')';
-      CustomStyle.setProp('transform' , canvas, cssScale);
-      CustomStyle.setProp('transformOrigin' , canvas, '0% 0%');
+      // The size of the canvas in pixels for printing.
+      var PRINT_RESOLUTION = 150;
+      var PRINT_UNITS = PRINT_RESOLUTION / 72.0;
+      canvas.width = Math.floor(viewport.width * PRINT_UNITS);
+      canvas.height = Math.floor(viewport.height * PRINT_UNITS);
+
+      // The physical size of the canvas as specified by the PDF document.
+      canvas.style.width = Math.floor(viewport.width * CSS_UNITS) + 'px';
+      canvas.style.height = Math.floor(viewport.height * CSS_UNITS) + 'px';
 
       var canvasWrapper = document.createElement('div');
       canvasWrapper.appendChild(canvas);
       printContainer.appendChild(canvasWrapper);
 
       canvas.mozPrintCallback = function(obj) {
         var ctx = obj.context;
 
         ctx.save();
         ctx.fillStyle = 'rgb(255, 255, 255)';
         ctx.fillRect(0, 0, canvas.width, canvas.height);
         ctx.restore();
-        ctx.scale(PRINT_OUTPUT_SCALE, PRINT_OUTPUT_SCALE);
 
         var renderContext = {
           canvasContext: ctx,
+          transform: [PRINT_UNITS, 0, 0, PRINT_UNITS, 0, 0],
           viewport: viewport,
           intent: 'print'
         };
 
         pdfPage.render(renderContext).promise.then(function() {
           // Tell the printEngine that rendering this canvas/page has finished.
           obj.done();
         }, function(error) {
--- a/devtools/client/framework/test/shared-head.js
+++ b/devtools/client/framework/test/shared-head.js
@@ -91,16 +91,17 @@ function getFrameScript() {
 }
 
 flags.testing = true;
 registerCleanupFunction(() => {
   flags.testing = false;
   Services.prefs.clearUserPref("devtools.dump.emit");
   Services.prefs.clearUserPref("devtools.toolbox.host");
   Services.prefs.clearUserPref("devtools.toolbox.previousHost");
+  Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled");
 });
 
 registerCleanupFunction(function* cleanup() {
   while (gBrowser.tabs.length > 1) {
     yield closeTabAndToolbox(gBrowser.selectedTab);
   }
 });
 
--- a/devtools/client/preferences/devtools.js
+++ b/devtools/client/preferences/devtools.js
@@ -309,18 +309,22 @@ pref("devtools.webconsole.persistlog", f
 // in the Web Console to display a timestamp, or |false| to not display
 // any timestamps.
 pref("devtools.webconsole.timestampMessages", false);
 
 // Web Console automatic multiline mode: |true| if you want incomplete statements
 // to automatically trigger multiline editing (equivalent to shift + enter).
 pref("devtools.webconsole.autoMultiline", true);
 
-// Enable the experimental webconsole frontend (work in progress)
+// Enable the experimental webconsole frontend
+#if defined(NIGHTLY_BUILD)
+pref("devtools.webconsole.new-frontend-enabled", true);
+#else
 pref("devtools.webconsole.new-frontend-enabled", false);
+#endif
 
 // Enable the experimental support for source maps in console (work in progress)
 pref("devtools.sourcemap.locations.enabled", false);
 
 // The number of lines that are displayed in the web console.
 pref("devtools.hud.loglimit", 1000);
 
 // The number of lines that are displayed in the web console for the Net,
--- a/devtools/client/shared/components/reps/array.js
+++ b/devtools/client/shared/components/reps/array.js
@@ -33,36 +33,33 @@ define(function (require, exports, modul
 
       for (let i = 0; i < array.length && i < max; i++) {
         try {
           let value = array[i];
 
           delim = (i == array.length - 1 ? "" : ", ");
 
           items.push(ItemRep({
-            key: i,
             object: value,
             // Hardcode tiny mode to avoid recursive handling.
             mode: "tiny",
             delim: delim
           }));
         } catch (exc) {
           items.push(ItemRep({
-            key: i,
             object: exc,
             mode: "tiny",
             delim: delim
           }));
         }
       }
 
       if (array.length > max) {
         let objectLink = this.props.objectLink || DOM.span;
         items.push(Caption({
-          key: "more",
           object: objectLink({
             object: this.props.object
           }, (array.length - max) + " more…")
         }));
       }
 
       return items;
     },
@@ -119,34 +116,34 @@ define(function (require, exports, modul
       let items;
       let brackets;
       let needSpace = function (space) {
         return space ? { left: "[ ", right: " ]"} : { left: "[", right: "]"};
       };
 
       if (mode == "tiny") {
         let isEmpty = object.length === 0;
-        items = DOM.span({className: "length"}, isEmpty ? "" : object.length);
+        items = [DOM.span({className: "length"}, isEmpty ? "" : object.length)];
         brackets = needSpace(false);
       } else {
         let max = (mode == "short") ? 3 : 300;
         items = this.arrayIterator(object, max);
         brackets = needSpace(items.length > 0);
       }
 
       let objectLink = this.props.objectLink || DOM.span;
 
       return (
         DOM.span({
           className: "objectBox objectBox-array"},
           objectLink({
             className: "arrayLeftBracket",
             object: object
           }, brackets.left),
-          items,
+          ...items,
           objectLink({
             className: "arrayRightBracket",
             object: object
           }, brackets.right),
           DOM.span({
             className: "arrayProperties",
             role: "group"}
           )
--- a/devtools/client/shared/components/reps/grip-array.js
+++ b/devtools/client/shared/components/reps/grip-array.js
@@ -65,34 +65,31 @@ define(function (require, exports, modul
       for (let i = 0; i < array.length && i < max; i++) {
         try {
           let itemGrip = array[i];
           let value = provider ? provider.getValue(itemGrip) : itemGrip;
 
           delim = (i == delimMax ? "" : ", ");
 
           items.push(GripArrayItem(Object.assign({}, this.props, {
-            key: i,
             object: value,
-            delim: delim}
-          )));
+            delim: delim
+          })));
         } catch (exc) {
           items.push(GripArrayItem(Object.assign({}, this.props, {
             object: exc,
-            delim: delim,
-            key: i}
-          )));
+            delim: delim
+          })));
         }
       }
       if (array.length > max || grip.preview.length > array.length) {
         let objectLink = this.props.objectLink || span;
         let leftItemNum = grip.preview.length - max > 0 ?
           grip.preview.length - max : grip.preview.length - array.length;
         items.push(Caption({
-          key: "more",
           object: objectLink({
             object: this.props.object
           }, leftItemNum + " more…")
         }));
       }
 
       return items;
     },
@@ -105,17 +102,17 @@ define(function (require, exports, modul
       let brackets;
       let needSpace = function (space) {
         return space ? { left: "[ ", right: " ]"} : { left: "[", right: "]"};
       };
 
       if (mode == "tiny") {
         let objectLength = this.getLength(object);
         let isEmpty = objectLength === 0;
-        items = span({className: "length"}, isEmpty ? "" : objectLength);
+        items = [span({className: "length"}, isEmpty ? "" : objectLength)];
         brackets = needSpace(false);
       } else {
         let max = (mode == "short") ? 3 : 300;
         items = this.arrayIterator(object, max);
         brackets = needSpace(items.length > 0);
       }
 
       let objectLink = this.props.objectLink || span;
@@ -124,17 +121,17 @@ define(function (require, exports, modul
       return (
         span({
           className: "objectBox objectBox-array"},
           title,
           objectLink({
             className: "arrayLeftBracket",
             object: object
           }, brackets.left),
-          items,
+          ...items,
           objectLink({
             className: "arrayRightBracket",
             object: object
           }, brackets.right),
           span({
             className: "arrayProperties",
             role: "group"}
           )
--- a/devtools/client/shared/components/reps/grip.js
+++ b/devtools/client/shared/components/reps/grip.js
@@ -71,17 +71,16 @@ define(function (require, exports, modul
       }
 
       let props = this.getProps(ownProperties, indexes);
       if (props.length < object.ownPropertyLength) {
         // There are some undisplayed props. Then display "more...".
         let objectLink = this.props.objectLink || span;
 
         props.push(Caption({
-          key: "more",
           object: objectLink({
             object: object
           }, ((object ? object.ownPropertyLength : 0) - max) + " more…")
         }));
       } else if (props.length > 0) {
         // Remove the last comma.
         // NOTE: do not change comp._store.props directly to update a property,
         // it should be re-rendered or cloned with changed props
@@ -109,17 +108,16 @@ define(function (require, exports, modul
         return a - b;
       });
 
       indexes.forEach((i) => {
         let name = Object.keys(ownProperties)[i];
         let prop = ownProperties[name];
         let value = prop.value !== undefined ? prop.value : prop;
         props.push(PropRep(Object.assign({}, this.props, {
-          key: name,
           mode: "tiny",
           name: name,
           object: value,
           equal: ": ",
           delim: ", ",
           defaultRep: Grip
         })));
       });
@@ -185,17 +183,17 @@ define(function (require, exports, modul
 
       return (
         span({className: "objectBox objectBox-object"},
           this.getTitle(object),
           objectLink({
             className: "objectLeftBrace",
             object: object
           }, " { "),
-          props,
+          ...props,
           objectLink({
             className: "objectRightBrace",
             object: object
           }, " }")
         )
       );
     },
   });
--- a/devtools/client/shared/components/reps/object.js
+++ b/devtools/client/shared/components/reps/object.js
@@ -70,17 +70,16 @@ define(function (require, exports, modul
         }));
       }
 
       if (props.length > max) {
         props.pop();
         let objectLink = this.props.objectLink || span;
 
         props.push(Caption({
-          key: "more",
           object: objectLink({
             object: object
           }, (Object.keys(object).length - max) + " more…")
         }));
       } else if (props.length > 0) {
         // Remove the last comma.
         props[props.length - 1] = React.cloneElement(
           props[props.length - 1], { delim: "" });
@@ -111,17 +110,16 @@ define(function (require, exports, modul
             value = object[name];
           } catch (exc) {
             continue;
           }
 
           let t = typeof value;
           if (filter(t, value)) {
             props.push(PropRep({
-              key: name,
               mode: mode,
               name: name,
               object: value,
               equal: ": ",
               delim: ", ",
             }));
           }
         }
@@ -147,17 +145,17 @@ define(function (require, exports, modul
 
       return (
         span({className: "objectBox objectBox-object"},
           this.getTitle(object),
           objectLink({
             className: "objectLeftBrace",
             object: object
           }, " { "),
-          props,
+          ...props,
           objectLink({
             className: "objectRightBrace",
             object: object
           }, " }")
         )
       );
     },
   });
--- a/devtools/client/shared/components/test/mochitest/test_reps_grip.html
+++ b/devtools/client/shared/components/test/mochitest/test_reps_grip.html
@@ -26,16 +26,19 @@ window.onload = Task.async(function* () 
     // Test property iterator
     yield testMaxProps();
     yield testMoreThanMaxProps();
     yield testUninterestingProps();
 
     // Test that properties are rendered as expected by PropRep
     yield testNestedObject();
     yield testNestedArray();
+
+    // Test that 'more' property doesn't clobber the caption.
+    yield testMoreProp();
   } catch(e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
   }
 
   function testBasic() {
     // Test object: `{}`
@@ -194,16 +197,45 @@ window.onload = Task.async(function* () 
         mode: "long",
         expectedOutput: defaultOutput,
       }
     ];
 
     testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
   }
 
+  function testMoreProp() {
+    // Test object: `{a: undefined, b: 1, more: 2, d: 3}`;
+    const testName = "testMoreProp";
+
+    const defaultOutput = `Object { b: 1, more: 2, d: 3, 1 more… }`;
+    const longOutput = `Object { a: undefined, b: 1, more: 2, d: 3 }`;
+
+    const modeTests = [
+      {
+        mode: undefined,
+        expectedOutput: defaultOutput,
+      },
+      {
+        mode: "tiny",
+        expectedOutput: `Object`,
+      },
+      {
+        mode: "short",
+        expectedOutput: defaultOutput,
+      },
+      {
+        mode: "long",
+        expectedOutput: longOutput,
+      }
+    ];
+
+    testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
+  }
+
   function getGripStub(functionName) {
     switch (functionName) {
       case "testBasic":
         return {
           "type": "object",
           "class": "Object",
           "actor": "server1.conn0.obj304",
           "extensible": true,
@@ -400,15 +432,59 @@ window.onload = Task.async(function* () 
                 }
               }
             },
             "ownPropertiesLength": 1,
             "safeGetterValues": {}
           },
         };
 
+      case "testMoreProp":
+        return {
+          "type": "object",
+          "class": "Object",
+          "actor": "server1.conn0.obj342",
+          "extensible": true,
+          "frozen": false,
+          "sealed": false,
+          "ownPropertyLength": 4,
+          "preview": {
+            "kind": "Object",
+            "ownProperties": {
+              "a": {
+                "configurable": true,
+                "enumerable": true,
+                "writable": true,
+                "value": {
+                  "type": "undefined"
+                }
+              },
+              "b": {
+                "configurable": true,
+                "enumerable": true,
+                "writable": true,
+                "value": 1
+              },
+              "more": {
+                "configurable": true,
+                "enumerable": true,
+                "writable": true,
+                "value": 2
+              },
+              "d": {
+                "configurable": true,
+                "enumerable": true,
+                "writable": true,
+                "value": 3
+              }
+            },
+            "ownPropertiesLength": 4,
+            "safeGetterValues": {}
+          }
+        };
+
     }
   }
 });
 </script>
 </pre>
 </body>
 </html>
--- a/devtools/client/shared/components/test/mochitest/test_reps_object.html
+++ b/devtools/client/shared/components/test/mochitest/test_reps_object.html
@@ -25,16 +25,19 @@ window.onload = Task.async(function* () 
 
     // Test property iterator
     yield testMaxProps();
     yield testMoreThanMaxProps();
     yield testUninterestingProps();
 
     // Test that properties are rendered as expected by PropRep
     yield testNested();
+
+    // Test that 'more' property doesn't clobber the caption.
+    yield testMoreProp();
   } catch(e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
   }
 
   function testBasic() {
     const stub = {};
@@ -178,13 +181,43 @@ window.onload = Task.async(function* () 
       {
         mode: "long",
         expectedOutput: defaultOutput,
       }
     ];
 
     testRepRenderModes(modeTests, "testNestedObject", componentUnderTest, stub);
   }
-});
+
+  function testMoreProp() {
+    const stub = {
+      a: undefined,
+      b: 1,
+      'more': 2,
+      d: 3
+    };
+    const defaultOutput = `Object { b: 1, more: 2, d: 3, 1 more… }`;
+
+    const modeTests = [
+      {
+        mode: undefined,
+        expectedOutput: defaultOutput,
+      },
+      {
+        mode: "tiny",
+        expectedOutput: `Object`,
+      },
+      {
+        mode: "short",
+        expectedOutput: defaultOutput,
+      },
+      {
+        mode: "long",
+        expectedOutput: defaultOutput,
+      }
+    ];
+
+    testRepRenderModes(modeTests, "testMoreProp", componentUnderTest, stub);
+  }});
 </script>
 </pre>
 </body>
 </html>
--- a/devtools/client/themes/common.css
+++ b/devtools/client/themes/common.css
@@ -361,21 +361,21 @@ checkbox:-moz-focusring {
 }
 
 /* Icon-only buttons */
 .devtools-button:empty::before,
 .devtools-toolbarbutton:not([label]):not([disabled]) > image {
   opacity: 0.8;
 }
 
-.devtools-button:hover:empty::before,
+.devtools-button:hover:empty:not(:disabled):before,
 .devtools-button.checked:empty::before,
 .devtools-button[checked]:empty::before,
 .devtools-button[open]:empty::before,
-.devtools-toolbarbutton:not([label]):hover > image,
+.devtools-toolbarbutton:not([label]):not([disabled=true]):hover > image,
 .devtools-toolbarbutton:not([label])[checked=true] > image,
 .devtools-toolbarbutton:not([label])[open=true] > image {
   opacity: 1;
 }
 
 .devtools-button:disabled,
 .devtools-button[disabled],
 .devtools-toolbarbutton[disabled] {
--- a/devtools/client/webconsole/jsterm.js
+++ b/devtools/client/webconsole/jsterm.js
@@ -1181,59 +1181,59 @@ JSTerm.prototype = {
 
       case KeyCodes.DOM_VK_PAGE_UP:
         if (this.autocompletePopup.isOpen) {
           inputUpdated = this.complete(this.COMPLETE_PAGEUP);
           if (inputUpdated) {
             this._autocompletePopupNavigated = true;
           }
         } else {
-          this.hud.outputWrapper.scrollTop =
+          this.hud.outputScroller.scrollTop =
             Math.max(0,
-              this.hud.outputWrapper.scrollTop -
-              this.hud.outputWrapper.clientHeight
+              this.hud.outputScroller.scrollTop -
+              this.hud.outputScroller.clientHeight
             );
         }
         event.preventDefault();
         break;
 
       case KeyCodes.DOM_VK_PAGE_DOWN:
         if (this.autocompletePopup.isOpen) {
           inputUpdated = this.complete(this.COMPLETE_PAGEDOWN);
           if (inputUpdated) {
             this._autocompletePopupNavigated = true;
           }
         } else {
-          this.hud.outputWrapper.scrollTop =
-            Math.min(this.hud.outputWrapper.scrollHeight,
-              this.hud.outputWrapper.scrollTop +
-              this.hud.outputWrapper.clientHeight
+          this.hud.outputScroller.scrollTop =
+            Math.min(this.hud.outputScroller.scrollHeight,
+              this.hud.outputScroller.scrollTop +
+              this.hud.outputScroller.clientHeight
             );
         }
         event.preventDefault();
         break;
 
       case KeyCodes.DOM_VK_HOME:
         if (this.autocompletePopup.isOpen) {
           this.autocompletePopup.selectedIndex = 0;
           event.preventDefault();
         } else if (inputValue.length <= 0) {
-          this.hud.outputWrapper.scrollTop = 0;
+          this.hud.outputScroller.scrollTop = 0;
           event.preventDefault();
         }
         break;
 
       case KeyCodes.DOM_VK_END:
         if (this.autocompletePopup.isOpen) {
           this.autocompletePopup.selectedIndex =
             this.autocompletePopup.itemCount - 1;
           event.preventDefault();
         } else if (inputValue.length <= 0) {
-          this.hud.outputWrapper.scrollTop =
-            this.hud.outputWrapper.scrollHeight;
+          this.hud.outputScroller.scrollTop =
+            this.hud.outputScroller.scrollHeight;
           event.preventDefault();
         }
         break;
 
       case KeyCodes.DOM_VK_LEFT:
         if (this.autocompletePopup.isOpen || this.lastCompletion.value) {
           this.clearCompletion();
         }
--- a/devtools/client/webconsole/new-console-output/components/console-output.js
+++ b/devtools/client/webconsole/new-console-output/components/console-output.js
@@ -26,17 +26,17 @@ const ConsoleOutput = createClass({
     serviceContainer: PropTypes.shape({
       attachRefToHud: PropTypes.func.isRequired,
     }),
     autoscroll: PropTypes.bool.isRequired,
   },
 
   componentDidMount() {
     scrollToBottom(this.outputNode);
-    this.props.serviceContainer.attachRefToHud("outputWrapper", this.outputNode);
+    this.props.serviceContainer.attachRefToHud("outputScroller", this.outputNode);
   },
 
   componentWillUpdate(nextProps, nextState) {
     if (!this.outputNode) {
       return;
     }
 
     const outputNode = this.outputNode;
--- a/devtools/client/webconsole/new-console-output/main.js
+++ b/devtools/client/webconsole/new-console-output/main.js
@@ -14,11 +14,10 @@ const { BrowserLoader } = Cu.import("res
 // Initialize module loader and load all modules of the new inline
 // preview feature. The entire code-base doesn't need any extra
 // privileges and runs entirely in content scope.
 const NewConsoleOutputWrapper = BrowserLoader({
   baseURI: "resource://devtools/client/webconsole/new-console-output/",
   window}).require("./new-console-output-wrapper");
 
 this.NewConsoleOutput = function (parentNode, jsterm, toolbox, owner, serviceContainer) {
-  console.log("Creating NewConsoleOutput", parentNode, NewConsoleOutputWrapper);
   return new NewConsoleOutputWrapper(parentNode, jsterm, toolbox, owner, serviceContainer);
 };
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_keyboard_accessibility.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_keyboard_accessibility.js
@@ -15,43 +15,43 @@ const TEST_URI =
     }
   </script>
   `;
 
 add_task(function* () {
   let hud = yield openNewTabAndConsole(TEST_URI);
   info("Web Console opened");
 
-  const outputWrapper = hud.ui.outputWrapper;
+  const outputScroller = hud.ui.outputScroller;
 
   yield waitFor(() => findMessages(hud, "").length == 100);
 
-  let currentPosition = outputWrapper.scrollTop;
+  let currentPosition = outputScroller.scrollTop;
   const bottom = currentPosition;
 
   EventUtils.sendMouseEvent({type: "click"}, hud.jsterm.inputNode);
 
   // Page up.
   EventUtils.synthesizeKey("VK_PAGE_UP", {});
-  isnot(outputWrapper.scrollTop, currentPosition,
+  isnot(outputScroller.scrollTop, currentPosition,
     "scroll position changed after page up");
 
   // Page down.
-  currentPosition = outputWrapper.scrollTop;
+  currentPosition = outputScroller.scrollTop;
   EventUtils.synthesizeKey("VK_PAGE_DOWN", {});
-  ok(outputWrapper.scrollTop > currentPosition,
+  ok(outputScroller.scrollTop > currentPosition,
      "scroll position now at bottom");
 
   // Home
   EventUtils.synthesizeKey("VK_HOME", {});
-  is(outputWrapper.scrollTop, 0, "scroll position now at top");
+  is(outputScroller.scrollTop, 0, "scroll position now at top");
 
   // End
   EventUtils.synthesizeKey("VK_END", {});
-  let scrollTop = outputWrapper.scrollTop;
+  let scrollTop = outputScroller.scrollTop;
   ok(scrollTop > 0 && Math.abs(scrollTop - bottom) <= 5,
      "scroll position now at bottom");
 
   // Clear output
   info("try ctrl-l to clear output");
   let clearShortcut;
   if (Services.appinfo.OS === "Darwin") {
     clearShortcut = WCUL10n.getStr("webconsole.clear.keyOSX");
@@ -61,11 +61,11 @@ add_task(function* () {
   synthesizeKeyShortcut(clearShortcut);
   yield waitFor(() => findMessages(hud, "").length == 0);
   is(hud.jsterm.inputNode.getAttribute("focused"), "true", "jsterm input is focused");
 
   // Focus filter
   info("try ctrl-f to focus filter");
   synthesizeKeyShortcut(WCUL10n.getStr("webconsole.find.key"));
   ok(!hud.jsterm.inputNode.getAttribute("focused"), "jsterm input is not focused");
-  is(hud.ui.filterBox, outputWrapper.ownerDocument.activeElement,
+  is(hud.ui.filterBox, outputScroller.ownerDocument.activeElement,
     "filter input is focused");
 });
--- a/devtools/client/webconsole/webconsole.js
+++ b/devtools/client/webconsole/webconsole.js
@@ -537,47 +537,29 @@ WebConsoleFrame.prototype = {
    */
   _initUI: function () {
     this.document = this.window.document;
     this.rootElement = this.document.documentElement;
     this.NEW_CONSOLE_OUTPUT_ENABLED = !this.isBrowserConsole
       && !this.owner.target.chrome
       && Services.prefs.getBoolPref(PREF_NEW_FRONTEND_ENABLED);
 
-    this._initDefaultFilterPrefs();
-
-    // Register the controller to handle "select all" properly.
-    this._commandController = new CommandController(this);
-    this.window.controllers.insertControllerAt(0, this._commandController);
-
-    this._contextMenuHandler = new ConsoleContextMenu(this);
-
-    let doc = this.document;
-
-    this.filterBox = doc.querySelector(".hud-filter-box");
-    this.outputNode = doc.getElementById("output-container");
-    this.outputWrapper = doc.getElementById("output-wrapper");
-
-    this.completeNode = doc.querySelector(".jsterm-complete-node");
-    this.inputNode = doc.querySelector(".jsterm-input-node");
-
-    this._setFilterTextBoxEvents();
-    this._initFilterButtons();
+    this.outputNode = this.document.getElementById("output-container");
+    this.outputWrapper = this.document.getElementById("output-wrapper");
+    this.completeNode = this.document.querySelector(".jsterm-complete-node");
+    this.inputNode = this.document.querySelector(".jsterm-input-node");
+
+    // In the old frontend, the area that scrolls is outputWrapper, but in the new
+    // frontend this will be reassigned.
+    this.outputScroller = this.outputWrapper;
 
     // Update the character width and height needed for the popup offset
     // calculations.
     this._updateCharSize();
 
-    let clearButton =
-      doc.getElementsByClassName("webconsole-clear-console-button")[0];
-    clearButton.addEventListener("command", () => {
-      this.owner._onClearButton();
-      this.jsterm.clearOutput(true);
-    });
-
     this.jsterm = new JSTerm(this);
     this.jsterm.init();
 
     let toolbox = gDevTools.getToolbox(this.owner.target);
 
     if (this.NEW_CONSOLE_OUTPUT_ENABLED) {
       // @TODO Remove this once JSTerm is handled with React/Redux.
       this.window.jsterm = this.jsterm;
@@ -591,20 +573,37 @@ WebConsoleFrame.prototype = {
       this.experimentalOutputNode.removeAttribute("tabindex");
       this.outputNode.hidden = true;
       this.outputNode.parentNode.appendChild(this.experimentalOutputNode);
       // @TODO Once the toolbox has been converted to React, see if passing
       // in JSTerm is still necessary.
 
       this.newConsoleOutput = new this.window.NewConsoleOutput(
         this.experimentalOutputNode, this.jsterm, toolbox, this.owner);
-      console.log("Created newConsoleOutput", this.newConsoleOutput);
-
-      let filterToolbar = doc.querySelector(".hud-console-filter-toolbar");
+
+      let filterToolbar = this.document.querySelector(".hud-console-filter-toolbar");
       filterToolbar.hidden = true;
+    } else {
+      // Register the controller to handle "select all" properly.
+      this._commandController = new CommandController(this);
+      this.window.controllers.insertControllerAt(0, this._commandController);
+
+      this._contextMenuHandler = new ConsoleContextMenu(this);
+
+      this._initDefaultFilterPrefs();
+      this.filterBox = this.document.querySelector(".hud-filter-box");
+      this._setFilterTextBoxEvents();
+      this._initFilterButtons();
+      let clearButton =
+        this.document.getElementsByClassName("webconsole-clear-console-button")[0];
+      clearButton.addEventListener("command", () => {
+        this.owner._onClearButton();
+        this.jsterm.clearOutput(true);
+      });
+
     }
 
     this.resize();
     this.window.addEventListener("resize", this.resize, true);
     this.jsterm.on("sidebar-opened", this.resize);
     this.jsterm.on("sidebar-closed", this.resize);
 
     if (toolbox) {
--- a/extensions/spellcheck/locales/en-US/hunspell/en-US.dic
+++ b/extensions/spellcheck/locales/en-US/hunspell/en-US.dic
@@ -1,9 +1,9 @@
-52343
+52346
 0/nm
 0th/pt
 1/n1
 1st/p
 1th/tc
 2/nm
 2nd/p
 2th/tc
@@ -18231,16 +18231,18 @@ cannonball/SM
 cannot
 canny/UTR
 canoe/MDS
 canoeing
 canoeist/SM
 canola/M
 canon/MS
 canonical/Y
+canonicalization/MS
+canonicalize/DGS
 canonization/SM
 canonize/DSG
 canoodle/DSG
 canopy/GDSM
 canst
 cant's
 cant/CZRDGS
 cantabile
@@ -37479,16 +37481,17 @@ outhouse/SM
 outing/M
 outlaid
 outlandish/PY
 outlandishness/M
 outlast/DSG
 outlaw/SGMD
 outlay/SGM
 outlet/SM
+outlier/SM
 outline/MGDS
 outlive/GDS
 outlook/MS
 outlying
 outmaneuver/GDS
 outmatch/GDS
 outmoded
 outnumber/DSG