merge mozilla-inbound to mozilla-central. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Mon, 25 Sep 2017 11:58:15 +0200
changeset 669742 5f3f19824efa14cc6db546baf59c54a0fc15ddc9
parent 669739 13ce77b78e364cc952d51b959f22202502be2941 (current diff)
parent 669741 9180e59a44f78fca784b8fc6dbadccf2bc300feb (diff)
child 669743 91e7589e47682a90bec7dedb9eddd7491a0926cc
child 669750 ff48fab67d3c8e41035cd8ea1cbda1696fdd12fc
child 669760 d596ba1b66e628c9d79fab1ccc68034dd32e5190
child 669836 ec7af383dc9dc48ac979c38f603880993db72c2b
child 669849 a0cea75ed5f00b8493c1012115609278247c449d
child 669852 1dffe90e83988bf2a28acf34a076657578253577
child 669857 f0927682ceba1c3521e40114c3be206f5fedaeed
child 669865 48f97f351bd818a11c9cea3ed756903c208ada53
child 669866 1454249c1a4dcc29cbcd7b80c6d0231758882b73
child 669871 02fd9d7f5d2a75dd5c1429f9359bd346e6da5ef5
child 669872 db45ffac661dfd9e738574175fcd91b7f25cf13e
child 669879 0e6eab6aff0c3bfc3e34ac1c1d51a7a6a1efe887
child 669912 40b85347288e901be3ca6a3745479deb06d67aae
child 669928 a62cee730d74a2c32736961067e72f0193f2e286
child 669932 cb88cf5f585347b3ea2180da4ec103653dcf9071
child 669941 eb4baf01a157c84663fac83d1d0e73a59386ac1b
child 669945 534785cf9708093a19cf672093703f5f069d9fda
child 669948 e95c0339a528a97421d99118247bebb8b2351a50
child 669949 9a3243a2503f1569e81d0494573fd6050225415e
child 669986 b42a687abadeb41a3de632490e5d77a38344d02c
child 670020 eccf0135259cf4cae1b8b5f3164fbc3bb114262f
child 670021 da51bc378fb205972bd3e6f286b92d8ad0714ea6
child 670022 46ac960fa2ccd7c3e1b77626f0089cf66f3b2c1d
child 670023 983bd4a8d67e8fe8febe88bfd28f91696863cbe9
child 670024 574cbfeee9b04d3f547c73ffc3c93e3b6ab58695
child 670025 13cab9e2f95990d0c7d6bcc8235706c92920c382
child 670026 100fbb4d5225e337b88aea9288e0aad8af7194ce
child 670027 6e116799638d9d602bb3c56ed6243456b45b2666
child 670034 bd42696ae79b1b04b10a3a36e96f56540decfb55
child 670081 3c3f22a8ecf025be228ecd8f3ec2b919fb7f278b
child 670082 556e5b7cf273d44385ebf8967f936b47fad588dd
child 670083 a7aa612010296c3d4cd7d625e177b35b3150be73
child 670084 8d36df3089db1300aeaa722fddd004544582dd32
child 670085 36ec25464659850048fd587d5017832b9c1cecee
child 670088 c6c45b32c725fd8d1ffcb8e901b14bf6578deaf3
child 670089 0a8b1c41cb4a341de2f6ddc988df9bf21014d518
child 670096 eee8ec20dd30365846a0491fd28a2937974447c5
child 670097 735c59ef74daf4b142f68f66dacf851740afd8f0
child 670101 eddd9d46133d6cdc7d165e53d945dfc0d9b70e1d
child 670151 d2503b492909bd1b86eec0fae3703293bdd493da
child 670153 07de08fe3c116319e086492165ce92e4a5b82555
child 670160 061591e350ae6178abea5860e953d7c561971478
child 670163 5a5ee90e3d1ab413ee0636011329eee604134931
child 670284 297d5256d0259be80eea325a60a9c334bfe2af5a
child 670503 33428465ff212d110e7e7867cfb76bbdb529ff70
child 670517 bd51e9c71ece3040396f6da630bacf19f020c7f4
child 670520 d1af4c6d90d5076b654cb51d12f8ff7fc5ced3a0
child 670706 8bdd37aa6ea08529a4508ad8c528b43f85a6d7fc
child 670731 e97a48bd18e2080d72e4e0856bf1a0a796c9d8b6
child 670847 f57ae72fb453b7a4840c0223e1b2fa3e9685c044
child 670849 298c4131d0c1e844c33c6bf2ed1ce74a7decfa6e
child 670880 116000a837a3526b9b465ab4130da9103402876f
child 670939 9a458397d7e008887466fffe7d68d1bad160401f
child 670964 a2abe4d59d3d911ab7aeb1881baf0e9de47e24c1
child 671037 c10f071d72e8eef2f34213e7dedc117dc58efdab
child 671099 eb64fec1a2e551b366a82abb05842ac9081b456c
child 671100 7e9b2c3ce40e5cc286daa03f369293e02b01001e
child 671371 6985a6f4634d04764429e96fd535e73d6eed37df
child 672772 30e62a5be2c5a99f446b60ce6e7ac1b41e796d84
child 674182 9e46382e2f1cf17193160ca9eeda2abb01265fd8
child 679396 9ef4bef4655e68eff94437ad5ca2219006b5dde7
push id81419
push userbmo:standard8@mozilla.com
push dateMon, 25 Sep 2017 10:59:32 +0000
reviewersmerge, merge
milestone58.0a1
merge mozilla-inbound to mozilla-central. r=merge a=merge MozReview-Commit-ID: 517PGVX0wPB
docshell/base/nsDocShell.cpp
--- a/browser/base/content/test/performance/browser_windowopen_reflows.js
+++ b/browser/base/content/test/performance/browser_windowopen_reflows.js
@@ -90,18 +90,17 @@ if (Services.appinfo.OS == "WINNT" || Se
         "init@chrome://browser/content/browser-tabsintitlebar.js",
         "handleEvent@chrome://browser/content/tabbrowser.xml",
       ],
       times: 4, // This number should only ever go down - never up.
     },
   );
 }
 
-// Windows Vista, 7 or 8
-if (navigator.userAgent.indexOf("Windows NT 6") != -1) {
+if (Services.appinfo.OS == "WINNT" && screen.width <= 1280) {
   EXPECTED_REFLOWS.push(
     {
       stack: [
         "handleEvent@chrome://browser/content/tabbrowser.xml",
       ],
     },
   );
 }
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,5 +1,5 @@
 This is the PDF.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 1.9.583
+Current extension version is: 1.9.597
 
-Taken from upstream commit: d7b37ae7
+Taken from upstream commit: f3987bba
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -3304,18 +3304,18 @@ var _UnsupportedManager = function Unsup
       for (var i = 0, ii = listeners.length; i < ii; i++) {
         listeners[i](featureId);
       }
     }
   };
 }();
 var version, build;
 {
-  exports.version = version = '1.9.583';
-  exports.build = build = 'd7b37ae7';
+  exports.version = version = '1.9.597';
+  exports.build = build = 'f3987bba';
 }
 exports.getDocument = getDocument;
 exports.LoopbackPort = LoopbackPort;
 exports.PDFDataRangeTransport = PDFDataRangeTransport;
 exports.PDFWorker = PDFWorker;
 exports.PDFDocumentProxy = PDFDocumentProxy;
 exports.PDFPageProxy = PDFPageProxy;
 exports.setPDFNetworkStreamClass = setPDFNetworkStreamClass;
@@ -3819,16 +3819,20 @@ class AnnotationElementFactory {
       case _util.AnnotationType.POPUP:
         return new PopupAnnotationElement(parameters);
       case _util.AnnotationType.LINE:
         return new LineAnnotationElement(parameters);
       case _util.AnnotationType.SQUARE:
         return new SquareAnnotationElement(parameters);
       case _util.AnnotationType.CIRCLE:
         return new CircleAnnotationElement(parameters);
+      case _util.AnnotationType.POLYLINE:
+        return new PolylineAnnotationElement(parameters);
+      case _util.AnnotationType.POLYGON:
+        return new PolygonAnnotationElement(parameters);
       case _util.AnnotationType.HIGHLIGHT:
         return new HighlightAnnotationElement(parameters);
       case _util.AnnotationType.UNDERLINE:
         return new UnderlineAnnotationElement(parameters);
       case _util.AnnotationType.SQUIGGLY:
         return new SquigglyAnnotationElement(parameters);
       case _util.AnnotationType.STRIKEOUT:
         return new StrikeOutAnnotationElement(parameters);
@@ -4124,17 +4128,17 @@ class ChoiceWidgetAnnotationElement exte
   }
 }
 class PopupAnnotationElement extends AnnotationElement {
   constructor(parameters) {
     let isRenderable = !!(parameters.data.title || parameters.data.contents);
     super(parameters, isRenderable);
   }
   render() {
-    const IGNORE_TYPES = ['Line', 'Square', 'Circle'];
+    const IGNORE_TYPES = ['Line', 'Square', 'Circle', 'PolyLine', 'Polygon'];
     this.container.className = 'popupAnnotation';
     if (IGNORE_TYPES.indexOf(this.data.parentType) >= 0) {
       return this.container;
     }
     let selector = '[data-annotation-id="' + this.data.parentId + '"]';
     let parentElement = this.layer.querySelector(selector);
     if (!parentElement) {
       return this.container;
@@ -4300,16 +4304,56 @@ class CircleAnnotationElement extends An
     circle.setAttribute('stroke', 'transparent');
     circle.setAttribute('fill', 'none');
     svg.appendChild(circle);
     this.container.append(svg);
     this._createPopup(this.container, circle, data);
     return this.container;
   }
 }
+class PolylineAnnotationElement extends AnnotationElement {
+  constructor(parameters) {
+    let isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents);
+    super(parameters, isRenderable, true);
+    this.containerClassName = 'polylineAnnotation';
+    this.svgElementName = 'svg:polyline';
+  }
+  render() {
+    this.container.className = this.containerClassName;
+    let data = this.data;
+    let width = data.rect[2] - data.rect[0];
+    let height = data.rect[3] - data.rect[1];
+    let svg = this.svgFactory.create(width, height);
+    let vertices = data.vertices;
+    let points = [];
+    for (let i = 0, ii = vertices.length; i < ii; i++) {
+      let x = vertices[i].x - data.rect[0];
+      let y = data.rect[3] - vertices[i].y;
+      points.push(x + ',' + y);
+    }
+    points = points.join(' ');
+    let borderWidth = data.borderStyle.width;
+    let polyline = this.svgFactory.createElement(this.svgElementName);
+    polyline.setAttribute('points', points);
+    polyline.setAttribute('stroke-width', borderWidth);
+    polyline.setAttribute('stroke', 'transparent');
+    polyline.setAttribute('fill', 'none');
+    svg.appendChild(polyline);
+    this.container.append(svg);
+    this._createPopup(this.container, polyline, data);
+    return this.container;
+  }
+}
+class PolygonAnnotationElement extends PolylineAnnotationElement {
+  constructor(parameters) {
+    super(parameters);
+    this.containerClassName = 'polygonAnnotation';
+    this.svgElementName = 'svg:polygon';
+  }
+}
 class HighlightAnnotationElement extends AnnotationElement {
   constructor(parameters) {
     let isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents);
     super(parameters, isRenderable, true);
   }
   render() {
     this.container.className = 'highlightAnnotation';
     if (!this.data.hasPopup) {
@@ -5000,18 +5044,18 @@ exports.SVGGraphics = SVGGraphics;
 
 /***/ }),
 /* 9 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-var pdfjsVersion = '1.9.583';
-var pdfjsBuild = 'd7b37ae7';
+var pdfjsVersion = '1.9.597';
+var pdfjsBuild = 'f3987bba';
 var pdfjsSharedUtil = __w_pdfjs_require__(0);
 var pdfjsDisplayGlobal = __w_pdfjs_require__(13);
 var pdfjsDisplayAPI = __w_pdfjs_require__(3);
 var pdfjsDisplayTextLayer = __w_pdfjs_require__(7);
 var pdfjsDisplayAnnotationLayer = __w_pdfjs_require__(6);
 var pdfjsDisplayDOMUtils = __w_pdfjs_require__(1);
 var pdfjsDisplaySVG = __w_pdfjs_require__(8);
 ;
@@ -8127,18 +8171,18 @@ var _svg = __w_pdfjs_require__(8);
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 var isWorker = typeof window === 'undefined';
 if (!_global_scope2.default.PDFJS) {
   _global_scope2.default.PDFJS = {};
 }
 var PDFJS = _global_scope2.default.PDFJS;
 {
-  PDFJS.version = '1.9.583';
-  PDFJS.build = 'd7b37ae7';
+  PDFJS.version = '1.9.597';
+  PDFJS.build = 'f3987bba';
 }
 PDFJS.pdfBug = false;
 if (PDFJS.verbosity !== undefined) {
   (0, _util.setVerbosityLevel)(PDFJS.verbosity);
 }
 delete PDFJS.verbosity;
 Object.defineProperty(PDFJS, 'verbosity', {
   get() {
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -3515,20 +3515,17 @@ var ColorSpace = function ColorSpaceClos
             destPos += alpha01;
           }
         }
       }
     },
     usesZeroToOneRange: true
   };
   ColorSpace.parse = function ColorSpace_parse(cs, xref, res) {
-    var IR = ColorSpace.parseToIR(cs, xref, res);
-    if (IR instanceof AlternateCS) {
-      return IR;
-    }
+    let IR = ColorSpace.parseToIR(cs, xref, res);
     return ColorSpace.fromIR(IR);
   };
   ColorSpace.fromIR = function ColorSpace_fromIR(IR) {
     var name = Array.isArray(IR) ? IR[0] : IR;
     var whitePoint, blackPoint, gamma;
     switch (name) {
       case 'DeviceGrayCS':
         return this.singletons.gray;
@@ -23725,18 +23722,18 @@ exports.getUnicodeForGlyph = getUnicodeF
 
 /***/ }),
 /* 17 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-var pdfjsVersion = '1.9.583';
-var pdfjsBuild = 'd7b37ae7';
+var pdfjsVersion = '1.9.597';
+var pdfjsBuild = 'f3987bba';
 var pdfjsCoreWorker = __w_pdfjs_require__(18);
 exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;
 
 /***/ }),
 /* 18 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
@@ -28623,18 +28620,18 @@ var Jbig2Image = function Jbig2ImageClos
           var rdw = decodeInteger(contextCache, 'IARDW', decoder);
           var rdh = decodeInteger(contextCache, 'IARDH', decoder);
           var rdx = decodeInteger(contextCache, 'IARDX', decoder);
           var rdy = decodeInteger(contextCache, 'IARDY', decoder);
           symbolWidth += rdw;
           symbolHeight += rdh;
           symbolBitmap = decodeRefinement(symbolWidth, symbolHeight, refinementTemplateIndex, symbolBitmap, (rdw >> 1) + rdx, (rdh >> 1) + rdy, false, refinementAt, decodingContext);
         }
-        var offsetT = t - (referenceCorner & 1 ? 0 : symbolHeight);
-        var offsetS = currentS - (referenceCorner & 2 ? symbolWidth : 0);
+        var offsetT = t - (referenceCorner & 1 ? 0 : symbolHeight - 1);
+        var offsetS = currentS - (referenceCorner & 2 ? symbolWidth - 1 : 0);
         var s2, t2, symbolRow;
         if (transposed) {
           for (s2 = 0; s2 < symbolHeight; s2++) {
             row = bitmap[offsetS + s2];
             if (!row) {
               continue;
             }
             symbolRow = symbolBitmap[s2];
@@ -39542,16 +39539,20 @@ class AnnotationFactory {
       case 'Popup':
         return new PopupAnnotation(parameters);
       case 'Line':
         return new LineAnnotation(parameters);
       case 'Square':
         return new SquareAnnotation(parameters);
       case 'Circle':
         return new CircleAnnotation(parameters);
+      case 'PolyLine':
+        return new PolylineAnnotation(parameters);
+      case 'Polygon':
+        return new PolygonAnnotation(parameters);
       case 'Highlight':
         return new HighlightAnnotation(parameters);
       case 'Underline':
         return new UnderlineAnnotation(parameters);
       case 'Squiggly':
         return new SquigglyAnnotation(parameters);
       case 'StrikeOut':
         return new StrikeOutAnnotation(parameters);
@@ -40050,16 +40051,38 @@ class SquareAnnotation extends Annotatio
 }
 class CircleAnnotation extends Annotation {
   constructor(parameters) {
     super(parameters);
     this.data.annotationType = _util.AnnotationType.CIRCLE;
     this._preparePopup(parameters.dict);
   }
 }
+class PolylineAnnotation extends Annotation {
+  constructor(parameters) {
+    super(parameters);
+    this.data.annotationType = _util.AnnotationType.POLYLINE;
+    let dict = parameters.dict;
+    let rawVertices = dict.getArray('Vertices');
+    this.data.vertices = [];
+    for (let i = 0, ii = rawVertices.length; i < ii; i += 2) {
+      this.data.vertices.push({
+        x: rawVertices[i],
+        y: rawVertices[i + 1]
+      });
+    }
+    this._preparePopup(dict);
+  }
+}
+class PolygonAnnotation extends PolylineAnnotation {
+  constructor(parameters) {
+    super(parameters);
+    this.data.annotationType = _util.AnnotationType.POLYGON;
+  }
+}
 class HighlightAnnotation extends Annotation {
   constructor(parameters) {
     super(parameters);
     this.data.annotationType = _util.AnnotationType.HIGHLIGHT;
     this._preparePopup(parameters.dict);
   }
 }
 class UnderlineAnnotation extends Annotation {
--- a/browser/extensions/pdfjs/content/web/viewer.css
+++ b/browser/extensions/pdfjs/content/web/viewer.css
@@ -244,16 +244,18 @@
 
 .annotationLayer .highlightAnnotation,
 .annotationLayer .underlineAnnotation,
 .annotationLayer .squigglyAnnotation,
 .annotationLayer .strikeoutAnnotation,
 .annotationLayer .lineAnnotation svg line,
 .annotationLayer .squareAnnotation svg rect,
 .annotationLayer .circleAnnotation svg ellipse,
+.annotationLayer .polylineAnnotation svg polyline,
+.annotationLayer .polygonAnnotation svg polygon,
 .annotationLayer .stampAnnotation,
 .annotationLayer .fileAttachmentAnnotation {
   cursor: pointer;
 }
 
 .pdfViewer .canvasWrapper {
   overflow: hidden;
 }
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -83,29 +83,35 @@
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.waitOnEventOrTimeout = exports.WaitOnType = exports.localized = exports.animationStarted = exports.normalizeWheelEventDelta = exports.binarySearchFirstItem = exports.watchScroll = exports.scrollIntoView = exports.getOutputScale = exports.approximateFraction = exports.roundToDivide = exports.getVisibleElements = exports.parseQueryString = exports.noContextMenuHandler = exports.getPDFFileNameFromURL = exports.ProgressBar = exports.EventBus = exports.NullL10n = exports.mozL10n = exports.RendererType = exports.cloneObj = exports.isValidRotation = exports.VERTICAL_PADDING = exports.SCROLLBAR_PADDING = exports.MAX_AUTO_SCALE = exports.UNKNOWN_SCALE = exports.MAX_SCALE = exports.MIN_SCALE = exports.DEFAULT_SCALE = exports.DEFAULT_SCALE_VALUE = exports.CSS_UNITS = undefined;
+exports.waitOnEventOrTimeout = exports.WaitOnType = exports.localized = exports.animationStarted = exports.normalizeWheelEventDelta = exports.binarySearchFirstItem = exports.watchScroll = exports.scrollIntoView = exports.getOutputScale = exports.approximateFraction = exports.roundToDivide = exports.getVisibleElements = exports.parseQueryString = exports.noContextMenuHandler = exports.getPDFFileNameFromURL = exports.ProgressBar = exports.EventBus = exports.NullL10n = exports.mozL10n = exports.RendererType = exports.PresentationModeState = exports.cloneObj = exports.isValidRotation = exports.VERTICAL_PADDING = exports.SCROLLBAR_PADDING = exports.MAX_AUTO_SCALE = exports.UNKNOWN_SCALE = exports.MAX_SCALE = exports.MIN_SCALE = exports.DEFAULT_SCALE = exports.DEFAULT_SCALE_VALUE = exports.CSS_UNITS = undefined;
 
 var _pdfjsLib = __webpack_require__(1);
 
 const CSS_UNITS = 96.0 / 72.0;
 const DEFAULT_SCALE_VALUE = 'auto';
 const DEFAULT_SCALE = 1.0;
 const MIN_SCALE = 0.25;
 const MAX_SCALE = 10.0;
 const UNKNOWN_SCALE = 0;
 const MAX_AUTO_SCALE = 1.25;
 const SCROLLBAR_PADDING = 40;
 const VERTICAL_PADDING = 5;
+const PresentationModeState = {
+  UNKNOWN: 0,
+  NORMAL: 1,
+  CHANGING: 2,
+  FULLSCREEN: 3
+};
 const RendererType = {
   CANVAS: 'canvas',
   SVG: 'svg'
 };
 function formatL10nValue(text, args) {
   if (!args) {
     return text;
   }
@@ -513,16 +519,17 @@ exports.DEFAULT_SCALE = DEFAULT_SCALE;
 exports.MIN_SCALE = MIN_SCALE;
 exports.MAX_SCALE = MAX_SCALE;
 exports.UNKNOWN_SCALE = UNKNOWN_SCALE;
 exports.MAX_AUTO_SCALE = MAX_AUTO_SCALE;
 exports.SCROLLBAR_PADDING = SCROLLBAR_PADDING;
 exports.VERTICAL_PADDING = VERTICAL_PADDING;
 exports.isValidRotation = isValidRotation;
 exports.cloneObj = cloneObj;
+exports.PresentationModeState = PresentationModeState;
 exports.RendererType = RendererType;
 exports.mozL10n = mozL10n;
 exports.NullL10n = NullL10n;
 exports.EventBus = EventBus;
 exports.ProgressBar = ProgressBar;
 exports.getPDFFileNameFromURL = getPDFFileNameFromURL;
 exports.noContextMenuHandler = noContextMenuHandler;
 exports.parseQueryString = parseQueryString;
@@ -802,47 +809,47 @@ var _ui_utils = __webpack_require__(0);
 var _pdfjsLib = __webpack_require__(1);
 
 var _pdf_cursor_tools = __webpack_require__(6);
 
 var _pdf_rendering_queue = __webpack_require__(3);
 
 var _pdf_sidebar = __webpack_require__(10);
 
-var _pdf_viewer = __webpack_require__(11);
-
 var _dom_events = __webpack_require__(2);
 
-var _overlay_manager = __webpack_require__(15);
-
-var _password_prompt = __webpack_require__(16);
-
-var _pdf_attachment_viewer = __webpack_require__(17);
-
-var _pdf_document_properties = __webpack_require__(18);
-
-var _pdf_find_bar = __webpack_require__(19);
+var _overlay_manager = __webpack_require__(11);
+
+var _password_prompt = __webpack_require__(12);
+
+var _pdf_attachment_viewer = __webpack_require__(13);
+
+var _pdf_document_properties = __webpack_require__(14);
+
+var _pdf_find_bar = __webpack_require__(15);
 
 var _pdf_find_controller = __webpack_require__(7);
 
-var _pdf_history = __webpack_require__(20);
+var _pdf_history = __webpack_require__(16);
 
 var _pdf_link_service = __webpack_require__(5);
 
-var _pdf_outline_viewer = __webpack_require__(21);
-
-var _pdf_presentation_mode = __webpack_require__(22);
-
-var _pdf_thumbnail_viewer = __webpack_require__(23);
-
-var _secondary_toolbar = __webpack_require__(25);
-
-var _toolbar = __webpack_require__(26);
-
-var _view_history = __webpack_require__(27);
+var _pdf_outline_viewer = __webpack_require__(17);
+
+var _pdf_presentation_mode = __webpack_require__(18);
+
+var _pdf_thumbnail_viewer = __webpack_require__(19);
+
+var _pdf_viewer = __webpack_require__(21);
+
+var _secondary_toolbar = __webpack_require__(26);
+
+var _toolbar = __webpack_require__(27);
+
+var _view_history = __webpack_require__(28);
 
 const DEFAULT_SCALE_DELTA = 1.1;
 const DISABLE_AUTO_FETCH_LOADING_BAR_TIMEOUT = 5000;
 function configure(PDFJS) {
   PDFJS.imageResourcesPath = './images/';
   PDFJS.workerSrc = '../build/pdf.worker.js';
   PDFJS.cMapUrl = '../web/cmaps/';
   PDFJS.cMapPacked = true;
@@ -1945,17 +1952,17 @@ function webViewerNamedAction(evt) {
       if (!PDFViewerApplication.supportsIntegratedFind) {
         PDFViewerApplication.findBar.toggle();
       }
       break;
   }
 }
 function webViewerPresentationModeChanged(evt) {
   let { active, switchInProgress } = evt;
-  PDFViewerApplication.pdfViewer.presentationModeState = switchInProgress ? _pdf_viewer.PresentationModeState.CHANGING : active ? _pdf_viewer.PresentationModeState.FULLSCREEN : _pdf_viewer.PresentationModeState.NORMAL;
+  PDFViewerApplication.pdfViewer.presentationModeState = switchInProgress ? _ui_utils.PresentationModeState.CHANGING : active ? _ui_utils.PresentationModeState.FULLSCREEN : _ui_utils.PresentationModeState.NORMAL;
 }
 function webViewerSidebarViewChanged(evt) {
   PDFViewerApplication.pdfRenderingQueue.isThumbnailViewEnabled = PDFViewerApplication.pdfSidebar.isThumbnailViewVisible;
   let store = PDFViewerApplication.store;
   if (store && PDFViewerApplication.isInitialViewSet) {
     store.set('sidebarView', evt.view).catch(function () {});
   }
 }
@@ -3173,18 +3180,18 @@ exports.PDFFindController = PDFFindContr
 
 let DEFAULT_URL = 'compressed.tracemonkey-pldi-09.pdf';
 ;
 let pdfjsWebApp;
 {
   pdfjsWebApp = __webpack_require__(4);
 }
 {
-  __webpack_require__(28);
-  __webpack_require__(31);
+  __webpack_require__(29);
+  __webpack_require__(32);
 }
 ;
 ;
 ;
 function getViewerConfiguration() {
   return {
     appContainer: document.body,
     mainContainer: document.getElementById('viewerContainer'),
@@ -3753,44 +3760,2022 @@ exports.PDFSidebar = PDFSidebar;
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.PDFViewer = exports.PresentationModeState = undefined;
+class OverlayManager {
+  constructor() {
+    this._overlays = {};
+    this._active = null;
+    this._keyDownBound = this._keyDown.bind(this);
+  }
+  get active() {
+    return this._active;
+  }
+  register(name, element, callerCloseMethod = null, canForceClose = false) {
+    return new Promise(resolve => {
+      let container;
+      if (!name || !element || !(container = element.parentNode)) {
+        throw new Error('Not enough parameters.');
+      } else if (this._overlays[name]) {
+        throw new Error('The overlay is already registered.');
+      }
+      this._overlays[name] = {
+        element,
+        container,
+        callerCloseMethod,
+        canForceClose
+      };
+      resolve();
+    });
+  }
+  unregister(name) {
+    return new Promise(resolve => {
+      if (!this._overlays[name]) {
+        throw new Error('The overlay does not exist.');
+      } else if (this._active === name) {
+        throw new Error('The overlay cannot be removed while it is active.');
+      }
+      delete this._overlays[name];
+      resolve();
+    });
+  }
+  open(name) {
+    return new Promise(resolve => {
+      if (!this._overlays[name]) {
+        throw new Error('The overlay does not exist.');
+      } else if (this._active) {
+        if (this._overlays[name].canForceClose) {
+          this._closeThroughCaller();
+        } else if (this._active === name) {
+          throw new Error('The overlay is already active.');
+        } else {
+          throw new Error('Another overlay is currently active.');
+        }
+      }
+      this._active = name;
+      this._overlays[this._active].element.classList.remove('hidden');
+      this._overlays[this._active].container.classList.remove('hidden');
+      window.addEventListener('keydown', this._keyDownBound);
+      resolve();
+    });
+  }
+  close(name) {
+    return new Promise(resolve => {
+      if (!this._overlays[name]) {
+        throw new Error('The overlay does not exist.');
+      } else if (!this._active) {
+        throw new Error('The overlay is currently not active.');
+      } else if (this._active !== name) {
+        throw new Error('Another overlay is currently active.');
+      }
+      this._overlays[this._active].container.classList.add('hidden');
+      this._overlays[this._active].element.classList.add('hidden');
+      this._active = null;
+      window.removeEventListener('keydown', this._keyDownBound);
+      resolve();
+    });
+  }
+  _keyDown(evt) {
+    if (this._active && evt.keyCode === 27) {
+      this._closeThroughCaller();
+      evt.preventDefault();
+    }
+  }
+  _closeThroughCaller() {
+    if (this._overlays[this._active].callerCloseMethod) {
+      this._overlays[this._active].callerCloseMethod();
+    }
+    if (this._active) {
+      this.close(this._active);
+    }
+  }
+}
+exports.OverlayManager = OverlayManager;
+
+/***/ }),
+/* 12 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.PasswordPrompt = undefined;
+
+var _ui_utils = __webpack_require__(0);
+
+var _pdfjsLib = __webpack_require__(1);
+
+class PasswordPrompt {
+  constructor(options, overlayManager, l10n = _ui_utils.NullL10n) {
+    this.overlayName = options.overlayName;
+    this.container = options.container;
+    this.label = options.label;
+    this.input = options.input;
+    this.submitButton = options.submitButton;
+    this.cancelButton = options.cancelButton;
+    this.overlayManager = overlayManager;
+    this.l10n = l10n;
+    this.updateCallback = null;
+    this.reason = null;
+    this.submitButton.addEventListener('click', this.verify.bind(this));
+    this.cancelButton.addEventListener('click', this.close.bind(this));
+    this.input.addEventListener('keydown', e => {
+      if (e.keyCode === 13) {
+        this.verify();
+      }
+    });
+    this.overlayManager.register(this.overlayName, this.container, this.close.bind(this), true);
+  }
+  open() {
+    this.overlayManager.open(this.overlayName).then(() => {
+      this.input.focus();
+      let promptString;
+      if (this.reason === _pdfjsLib.PasswordResponses.INCORRECT_PASSWORD) {
+        promptString = this.l10n.get('password_invalid', null, 'Invalid password. Please try again.');
+      } else {
+        promptString = this.l10n.get('password_label', null, 'Enter the password to open this PDF file.');
+      }
+      promptString.then(msg => {
+        this.label.textContent = msg;
+      });
+    });
+  }
+  close() {
+    this.overlayManager.close(this.overlayName).then(() => {
+      this.input.value = '';
+    });
+  }
+  verify() {
+    let password = this.input.value;
+    if (password && password.length > 0) {
+      this.close();
+      return this.updateCallback(password);
+    }
+  }
+  setUpdateCallback(updateCallback, reason) {
+    this.updateCallback = updateCallback;
+    this.reason = reason;
+  }
+}
+exports.PasswordPrompt = PasswordPrompt;
+
+/***/ }),
+/* 13 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.PDFAttachmentViewer = undefined;
+
+var _pdfjsLib = __webpack_require__(1);
+
+class PDFAttachmentViewer {
+  constructor({ container, eventBus, downloadManager }) {
+    this.container = container;
+    this.eventBus = eventBus;
+    this.downloadManager = downloadManager;
+    this.reset();
+    this.eventBus.on('fileattachmentannotation', this._appendAttachment.bind(this));
+  }
+  reset(keepRenderedCapability = false) {
+    this.attachments = null;
+    this.container.textContent = '';
+    if (!keepRenderedCapability) {
+      this._renderedCapability = (0, _pdfjsLib.createPromiseCapability)();
+    }
+  }
+  _dispatchEvent(attachmentsCount) {
+    this._renderedCapability.resolve();
+    this.eventBus.dispatch('attachmentsloaded', {
+      source: this,
+      attachmentsCount
+    });
+  }
+  _bindPdfLink(button, content, filename) {
+    if (_pdfjsLib.PDFJS.disableCreateObjectURL) {
+      throw new Error('bindPdfLink: ' + 'Unsupported "PDFJS.disableCreateObjectURL" value.');
+    }
+    let blobUrl;
+    button.onclick = function () {
+      if (!blobUrl) {
+        blobUrl = (0, _pdfjsLib.createObjectURL)(content, 'application/pdf');
+      }
+      let viewerUrl;
+      viewerUrl = blobUrl + '?' + encodeURIComponent(filename);
+      window.open(viewerUrl);
+      return false;
+    };
+  }
+  _bindLink(button, content, filename) {
+    button.onclick = () => {
+      this.downloadManager.downloadData(content, filename, '');
+      return false;
+    };
+  }
+  render({ attachments, keepRenderedCapability = false }) {
+    let attachmentsCount = 0;
+    if (this.attachments) {
+      this.reset(keepRenderedCapability === true);
+    }
+    this.attachments = attachments || null;
+    if (!attachments) {
+      this._dispatchEvent(attachmentsCount);
+      return;
+    }
+    let names = Object.keys(attachments).sort(function (a, b) {
+      return a.toLowerCase().localeCompare(b.toLowerCase());
+    });
+    attachmentsCount = names.length;
+    for (let i = 0; i < attachmentsCount; i++) {
+      let item = attachments[names[i]];
+      let filename = (0, _pdfjsLib.removeNullCharacters)((0, _pdfjsLib.getFilenameFromUrl)(item.filename));
+      let div = document.createElement('div');
+      div.className = 'attachmentsItem';
+      let button = document.createElement('button');
+      button.textContent = filename;
+      if (/\.pdf$/i.test(filename) && !_pdfjsLib.PDFJS.disableCreateObjectURL) {
+        this._bindPdfLink(button, item.content, filename);
+      } else {
+        this._bindLink(button, item.content, filename);
+      }
+      div.appendChild(button);
+      this.container.appendChild(div);
+    }
+    this._dispatchEvent(attachmentsCount);
+  }
+  _appendAttachment({ id, filename, content }) {
+    this._renderedCapability.promise.then(() => {
+      let attachments = this.attachments;
+      if (!attachments) {
+        attachments = Object.create(null);
+      } else {
+        for (let name in attachments) {
+          if (id === name) {
+            return;
+          }
+        }
+      }
+      attachments[id] = {
+        filename,
+        content
+      };
+      this.render({
+        attachments,
+        keepRenderedCapability: true
+      });
+    });
+  }
+}
+exports.PDFAttachmentViewer = PDFAttachmentViewer;
+
+/***/ }),
+/* 14 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.PDFDocumentProperties = undefined;
+
+var _ui_utils = __webpack_require__(0);
+
+var _pdfjsLib = __webpack_require__(1);
+
+const DEFAULT_FIELD_CONTENT = '-';
+class PDFDocumentProperties {
+  constructor({ overlayName, fields, container, closeButton }, overlayManager, l10n = _ui_utils.NullL10n) {
+    this.overlayName = overlayName;
+    this.fields = fields;
+    this.container = container;
+    this.overlayManager = overlayManager;
+    this.l10n = l10n;
+    this._reset();
+    if (closeButton) {
+      closeButton.addEventListener('click', this.close.bind(this));
+    }
+    this.overlayManager.register(this.overlayName, this.container, this.close.bind(this));
+  }
+  open() {
+    let freezeFieldData = data => {
+      Object.defineProperty(this, 'fieldData', {
+        value: Object.freeze(data),
+        writable: false,
+        enumerable: true,
+        configurable: true
+      });
+    };
+    Promise.all([this.overlayManager.open(this.overlayName), this._dataAvailableCapability.promise]).then(() => {
+      if (this.fieldData) {
+        this._updateUI();
+        return;
+      }
+      this.pdfDocument.getMetadata().then(({ info, metadata }) => {
+        return Promise.all([info, metadata, this._parseFileSize(this.maybeFileSize), this._parseDate(info.CreationDate), this._parseDate(info.ModDate)]);
+      }).then(([info, metadata, fileSize, creationDate, modificationDate]) => {
+        freezeFieldData({
+          'fileName': (0, _ui_utils.getPDFFileNameFromURL)(this.url),
+          'fileSize': fileSize,
+          'title': info.Title,
+          'author': info.Author,
+          'subject': info.Subject,
+          'keywords': info.Keywords,
+          'creationDate': creationDate,
+          'modificationDate': modificationDate,
+          'creator': info.Creator,
+          'producer': info.Producer,
+          'version': info.PDFFormatVersion,
+          'pageCount': this.pdfDocument.numPages
+        });
+        this._updateUI();
+        return this.pdfDocument.getDownloadInfo();
+      }).then(({ length }) => {
+        return this._parseFileSize(length);
+      }).then(fileSize => {
+        let data = (0, _ui_utils.cloneObj)(this.fieldData);
+        data['fileSize'] = fileSize;
+        freezeFieldData(data);
+        this._updateUI();
+      });
+    });
+  }
+  close() {
+    this.overlayManager.close(this.overlayName);
+  }
+  setDocument(pdfDocument, url) {
+    if (this.pdfDocument) {
+      this._reset();
+      this._updateUI(true);
+    }
+    if (!pdfDocument) {
+      return;
+    }
+    this.pdfDocument = pdfDocument;
+    this.url = url;
+    this._dataAvailableCapability.resolve();
+  }
+  setFileSize(fileSize) {
+    if (typeof fileSize === 'number' && fileSize > 0) {
+      this.maybeFileSize = fileSize;
+    }
+  }
+  _reset() {
+    this.pdfDocument = null;
+    this.url = null;
+    this.maybeFileSize = 0;
+    delete this.fieldData;
+    this._dataAvailableCapability = (0, _pdfjsLib.createPromiseCapability)();
+  }
+  _updateUI(reset = false) {
+    if (reset || !this.fieldData) {
+      for (let id in this.fields) {
+        this.fields[id].textContent = DEFAULT_FIELD_CONTENT;
+      }
+      return;
+    }
+    if (this.overlayManager.active !== this.overlayName) {
+      return;
+    }
+    for (let id in this.fields) {
+      let content = this.fieldData[id];
+      this.fields[id].textContent = content || content === 0 ? content : DEFAULT_FIELD_CONTENT;
+    }
+  }
+  _parseFileSize(fileSize = 0) {
+    let kb = fileSize / 1024;
+    if (!kb) {
+      return Promise.resolve(undefined);
+    } else if (kb < 1024) {
+      return this.l10n.get('document_properties_kb', {
+        size_kb: (+kb.toPrecision(3)).toLocaleString(),
+        size_b: fileSize.toLocaleString()
+      }, '{{size_kb}} KB ({{size_b}} bytes)');
+    }
+    return this.l10n.get('document_properties_mb', {
+      size_mb: (+(kb / 1024).toPrecision(3)).toLocaleString(),
+      size_b: fileSize.toLocaleString()
+    }, '{{size_mb}} MB ({{size_b}} bytes)');
+  }
+  _parseDate(inputDate) {
+    if (!inputDate) {
+      return;
+    }
+    let dateToParse = inputDate;
+    if (dateToParse.substring(0, 2) === 'D:') {
+      dateToParse = dateToParse.substring(2);
+    }
+    let year = parseInt(dateToParse.substring(0, 4), 10);
+    let month = parseInt(dateToParse.substring(4, 6), 10) - 1;
+    let day = parseInt(dateToParse.substring(6, 8), 10);
+    let hours = parseInt(dateToParse.substring(8, 10), 10);
+    let minutes = parseInt(dateToParse.substring(10, 12), 10);
+    let seconds = parseInt(dateToParse.substring(12, 14), 10);
+    let utRel = dateToParse.substring(14, 15);
+    let offsetHours = parseInt(dateToParse.substring(15, 17), 10);
+    let offsetMinutes = parseInt(dateToParse.substring(18, 20), 10);
+    if (utRel === '-') {
+      hours += offsetHours;
+      minutes += offsetMinutes;
+    } else if (utRel === '+') {
+      hours -= offsetHours;
+      minutes -= offsetMinutes;
+    }
+    let date = new Date(Date.UTC(year, month, day, hours, minutes, seconds));
+    let dateString = date.toLocaleDateString();
+    let timeString = date.toLocaleTimeString();
+    return this.l10n.get('document_properties_date_string', {
+      date: dateString,
+      time: timeString
+    }, '{{date}}, {{time}}');
+  }
+}
+exports.PDFDocumentProperties = PDFDocumentProperties;
+
+/***/ }),
+/* 15 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.PDFFindBar = undefined;
+
+var _pdf_find_controller = __webpack_require__(7);
+
+var _ui_utils = __webpack_require__(0);
+
+class PDFFindBar {
+  constructor(options, l10n = _ui_utils.NullL10n) {
+    this.opened = false;
+    this.bar = options.bar || null;
+    this.toggleButton = options.toggleButton || null;
+    this.findField = options.findField || null;
+    this.highlightAll = options.highlightAllCheckbox || null;
+    this.caseSensitive = options.caseSensitiveCheckbox || null;
+    this.findMsg = options.findMsg || null;
+    this.findResultsCount = options.findResultsCount || null;
+    this.findStatusIcon = options.findStatusIcon || null;
+    this.findPreviousButton = options.findPreviousButton || null;
+    this.findNextButton = options.findNextButton || null;
+    this.findController = options.findController || null;
+    this.eventBus = options.eventBus;
+    this.l10n = l10n;
+    if (this.findController === null) {
+      throw new Error('PDFFindBar cannot be used without a ' + 'PDFFindController instance.');
+    }
+    this.toggleButton.addEventListener('click', () => {
+      this.toggle();
+    });
+    this.findField.addEventListener('input', () => {
+      this.dispatchEvent('');
+    });
+    this.bar.addEventListener('keydown', e => {
+      switch (e.keyCode) {
+        case 13:
+          if (e.target === this.findField) {
+            this.dispatchEvent('again', e.shiftKey);
+          }
+          break;
+        case 27:
+          this.close();
+          break;
+      }
+    });
+    this.findPreviousButton.addEventListener('click', () => {
+      this.dispatchEvent('again', true);
+    });
+    this.findNextButton.addEventListener('click', () => {
+      this.dispatchEvent('again', false);
+    });
+    this.highlightAll.addEventListener('click', () => {
+      this.dispatchEvent('highlightallchange');
+    });
+    this.caseSensitive.addEventListener('click', () => {
+      this.dispatchEvent('casesensitivitychange');
+    });
+    this.eventBus.on('resize', this._adjustWidth.bind(this));
+  }
+  reset() {
+    this.updateUIState();
+  }
+  dispatchEvent(type, findPrev) {
+    this.eventBus.dispatch('find', {
+      source: this,
+      type,
+      query: this.findField.value,
+      caseSensitive: this.caseSensitive.checked,
+      phraseSearch: true,
+      highlightAll: this.highlightAll.checked,
+      findPrevious: findPrev
+    });
+  }
+  updateUIState(state, previous, matchCount) {
+    let notFound = false;
+    let findMsg = '';
+    let status = '';
+    switch (state) {
+      case _pdf_find_controller.FindState.FOUND:
+        break;
+      case _pdf_find_controller.FindState.PENDING:
+        status = 'pending';
+        break;
+      case _pdf_find_controller.FindState.NOT_FOUND:
+        findMsg = this.l10n.get('find_not_found', null, 'Phrase not found');
+        notFound = true;
+        break;
+      case _pdf_find_controller.FindState.WRAPPED:
+        if (previous) {
+          findMsg = this.l10n.get('find_reached_top', null, 'Reached top of document, continued from bottom');
+        } else {
+          findMsg = this.l10n.get('find_reached_bottom', null, 'Reached end of document, continued from top');
+        }
+        break;
+    }
+    if (notFound) {
+      this.findField.classList.add('notFound');
+    } else {
+      this.findField.classList.remove('notFound');
+    }
+    this.findField.setAttribute('data-status', status);
+    Promise.resolve(findMsg).then(msg => {
+      this.findMsg.textContent = msg;
+      this._adjustWidth();
+    });
+    this.updateResultsCount(matchCount);
+  }
+  updateResultsCount(matchCount) {
+    if (!this.findResultsCount) {
+      return;
+    }
+    if (!matchCount) {
+      this.findResultsCount.classList.add('hidden');
+      this.findResultsCount.textContent = '';
+    } else {
+      this.findResultsCount.textContent = matchCount.toLocaleString();
+      this.findResultsCount.classList.remove('hidden');
+    }
+    this._adjustWidth();
+  }
+  open() {
+    if (!this.opened) {
+      this.opened = true;
+      this.toggleButton.classList.add('toggled');
+      this.bar.classList.remove('hidden');
+    }
+    this.findField.select();
+    this.findField.focus();
+    this._adjustWidth();
+  }
+  close() {
+    if (!this.opened) {
+      return;
+    }
+    this.opened = false;
+    this.toggleButton.classList.remove('toggled');
+    this.bar.classList.add('hidden');
+    this.findController.active = false;
+  }
+  toggle() {
+    if (this.opened) {
+      this.close();
+    } else {
+      this.open();
+    }
+  }
+  _adjustWidth() {
+    if (!this.opened) {
+      return;
+    }
+    this.bar.classList.remove('wrapContainers');
+    let findbarHeight = this.bar.clientHeight;
+    let inputContainerHeight = this.bar.firstElementChild.clientHeight;
+    if (findbarHeight > inputContainerHeight) {
+      this.bar.classList.add('wrapContainers');
+    }
+  }
+}
+exports.PDFFindBar = PDFFindBar;
+
+/***/ }),
+/* 16 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.isDestsEqual = exports.PDFHistory = undefined;
+
+var _ui_utils = __webpack_require__(0);
+
+var _dom_events = __webpack_require__(2);
+
+const HASH_CHANGE_TIMEOUT = 1000;
+const POSITION_UPDATED_THRESHOLD = 50;
+const UPDATE_VIEWAREA_TIMEOUT = 1000;
+function getCurrentHash() {
+  return document.location.hash;
+}
+function parseCurrentHash(linkService) {
+  let hash = unescape(getCurrentHash()).substring(1);
+  let params = (0, _ui_utils.parseQueryString)(hash);
+  let page = params.page | 0;
+  if (!(Number.isInteger(page) && page > 0 && page <= linkService.pagesCount)) {
+    page = null;
+  }
+  return {
+    hash,
+    page,
+    rotation: linkService.rotation
+  };
+}
+class PDFHistory {
+  constructor({ linkService, eventBus }) {
+    this.linkService = linkService;
+    this.eventBus = eventBus || (0, _dom_events.getGlobalEventBus)();
+    this.initialized = false;
+    this.initialBookmark = null;
+    this.initialRotation = null;
+    this._boundEvents = Object.create(null);
+    this._isViewerInPresentationMode = false;
+    this._isPagesLoaded = false;
+    this.eventBus.on('presentationmodechanged', evt => {
+      this._isViewerInPresentationMode = evt.active || evt.switchInProgress;
+    });
+    this.eventBus.on('pagesloaded', evt => {
+      this._isPagesLoaded = !!evt.pagesCount;
+    });
+  }
+  initialize(fingerprint, resetHistory = false) {
+    if (!fingerprint || typeof fingerprint !== 'string') {
+      console.error('PDFHistory.initialize: The "fingerprint" must be a non-empty string.');
+      return;
+    }
+    let reInitialized = this.initialized && this.fingerprint !== fingerprint;
+    this.fingerprint = fingerprint;
+    if (!this.initialized) {
+      this._bindEvents();
+    }
+    let state = window.history.state;
+    this.initialized = true;
+    this.initialBookmark = null;
+    this.initialRotation = null;
+    this._popStateInProgress = false;
+    this._blockHashChange = 0;
+    this._currentHash = getCurrentHash();
+    this._numPositionUpdates = 0;
+    this._currentUid = this._uid = 0;
+    this._destination = null;
+    this._position = null;
+    if (!this._isValidState(state) || resetHistory) {
+      let { hash, page, rotation } = parseCurrentHash(this.linkService);
+      if (!hash || reInitialized || resetHistory) {
+        this._pushOrReplaceState(null, true);
+        return;
+      }
+      this._pushOrReplaceState({
+        hash,
+        page,
+        rotation
+      }, true);
+      return;
+    }
+    let destination = state.destination;
+    this._updateInternalState(destination, state.uid, true);
+    if (destination.rotation !== undefined) {
+      this.initialRotation = destination.rotation;
+    }
+    if (destination.dest) {
+      this.initialBookmark = JSON.stringify(destination.dest);
+      this._destination.page = null;
+    } else if (destination.hash) {
+      this.initialBookmark = destination.hash;
+    } else if (destination.page) {
+      this.initialBookmark = `page=${destination.page}`;
+    }
+  }
+  push({ namedDest, explicitDest, pageNumber }) {
+    if (!this.initialized) {
+      return;
+    }
+    if (namedDest && typeof namedDest !== 'string' || !(explicitDest instanceof Array) || !(Number.isInteger(pageNumber) && pageNumber > 0 && pageNumber <= this.linkService.pagesCount)) {
+      console.error('PDFHistory.push: Invalid parameters.');
+      return;
+    }
+    let hash = namedDest || JSON.stringify(explicitDest);
+    if (!hash) {
+      return;
+    }
+    let forceReplace = false;
+    if (this._destination && (this._destination.hash === hash || isDestsEqual(this._destination.dest, explicitDest))) {
+      if (this._destination.page) {
+        return;
+      }
+      forceReplace = true;
+    }
+    if (this._popStateInProgress && !forceReplace) {
+      return;
+    }
+    this._pushOrReplaceState({
+      dest: explicitDest,
+      hash,
+      page: pageNumber,
+      rotation: this.linkService.rotation
+    }, forceReplace);
+    if (!this._popStateInProgress) {
+      this._popStateInProgress = true;
+      Promise.resolve().then(() => {
+        this._popStateInProgress = false;
+      });
+    }
+  }
+  pushCurrentPosition() {
+    if (!this.initialized || this._popStateInProgress) {
+      return;
+    }
+    this._tryPushCurrentPosition();
+  }
+  back() {
+    if (!this.initialized || this._popStateInProgress) {
+      return;
+    }
+    let state = window.history.state;
+    if (this._isValidState(state) && state.uid > 0) {
+      window.history.back();
+    }
+  }
+  forward() {
+    if (!this.initialized || this._popStateInProgress) {
+      return;
+    }
+    let state = window.history.state;
+    if (this._isValidState(state) && state.uid < this._uid - 1) {
+      window.history.forward();
+    }
+  }
+  get popStateInProgress() {
+    return this.initialized && (this._popStateInProgress || this._blockHashChange > 0);
+  }
+  _pushOrReplaceState(destination, forceReplace = false) {
+    let shouldReplace = forceReplace || !this._destination;
+    let newState = {
+      fingerprint: this.fingerprint,
+      uid: shouldReplace ? this._currentUid : this._uid,
+      destination
+    };
+    this._updateInternalState(destination, newState.uid);
+    if (shouldReplace) {
+      window.history.replaceState(newState, '');
+    } else {
+      window.history.pushState(newState, '');
+    }
+  }
+  _tryPushCurrentPosition(temporary = false) {
+    if (!this._position) {
+      return;
+    }
+    let position = this._position;
+    if (temporary) {
+      position = (0, _ui_utils.cloneObj)(this._position);
+      position.temporary = true;
+    }
+    if (!this._destination) {
+      this._pushOrReplaceState(position);
+      return;
+    }
+    if (this._destination.temporary) {
+      this._pushOrReplaceState(position, true);
+      return;
+    }
+    if (this._destination.hash === position.hash) {
+      return;
+    }
+    if (!this._destination.page && (POSITION_UPDATED_THRESHOLD <= 0 || this._numPositionUpdates <= POSITION_UPDATED_THRESHOLD)) {
+      return;
+    }
+    let forceReplace = false;
+    if (this._destination.page === position.first || this._destination.page === position.page) {
+      if (this._destination.dest || !this._destination.first) {
+        return;
+      }
+      forceReplace = true;
+    }
+    this._pushOrReplaceState(position, forceReplace);
+  }
+  _isValidState(state) {
+    if (!state) {
+      return false;
+    }
+    if (state.fingerprint !== this.fingerprint) {
+      return false;
+    }
+    if (!Number.isInteger(state.uid) || state.uid < 0) {
+      return false;
+    }
+    if (state.destination === null || typeof state.destination !== 'object') {
+      return false;
+    }
+    return true;
+  }
+  _updateInternalState(destination, uid, removeTemporary = false) {
+    if (this._updateViewareaTimeout) {
+      clearTimeout(this._updateViewareaTimeout);
+      this._updateViewareaTimeout = null;
+    }
+    if (removeTemporary && destination && destination.temporary) {
+      delete destination.temporary;
+    }
+    this._destination = destination;
+    this._currentUid = uid;
+    this._uid = this._currentUid + 1;
+    this._numPositionUpdates = 0;
+  }
+  _updateViewarea({ location }) {
+    if (this._updateViewareaTimeout) {
+      clearTimeout(this._updateViewareaTimeout);
+      this._updateViewareaTimeout = null;
+    }
+    this._position = {
+      hash: this._isViewerInPresentationMode ? `page=${location.pageNumber}` : location.pdfOpenParams.substring(1),
+      page: this.linkService.page,
+      first: location.pageNumber,
+      rotation: location.rotation
+    };
+    if (this._popStateInProgress) {
+      return;
+    }
+    if (POSITION_UPDATED_THRESHOLD > 0 && this._isPagesLoaded && this._destination && !this._destination.page) {
+      this._numPositionUpdates++;
+    }
+    if (UPDATE_VIEWAREA_TIMEOUT > 0) {
+      this._updateViewareaTimeout = setTimeout(() => {
+        if (!this._popStateInProgress) {
+          this._tryPushCurrentPosition(true);
+        }
+        this._updateViewareaTimeout = null;
+      }, UPDATE_VIEWAREA_TIMEOUT);
+    }
+  }
+  _popState({ state }) {
+    let newHash = getCurrentHash(),
+        hashChanged = this._currentHash !== newHash;
+    this._currentHash = newHash;
+    if (!state || false) {
+      this._currentUid = this._uid;
+      let { hash, page, rotation } = parseCurrentHash(this.linkService);
+      this._pushOrReplaceState({
+        hash,
+        page,
+        rotation
+      }, true);
+      return;
+    }
+    if (!this._isValidState(state)) {
+      return;
+    }
+    this._popStateInProgress = true;
+    if (hashChanged) {
+      this._blockHashChange++;
+      (0, _ui_utils.waitOnEventOrTimeout)({
+        target: window,
+        name: 'hashchange',
+        delay: HASH_CHANGE_TIMEOUT
+      }).then(() => {
+        this._blockHashChange--;
+      });
+    }
+    let destination = state.destination;
+    this._updateInternalState(destination, state.uid, true);
+    if ((0, _ui_utils.isValidRotation)(destination.rotation)) {
+      this.linkService.rotation = destination.rotation;
+    }
+    if (destination.dest) {
+      this.linkService.navigateTo(destination.dest);
+    } else if (destination.hash) {
+      this.linkService.setHash(destination.hash);
+    } else if (destination.page) {
+      this.linkService.page = destination.page;
+    }
+    Promise.resolve().then(() => {
+      this._popStateInProgress = false;
+    });
+  }
+  _bindEvents() {
+    let { _boundEvents, eventBus } = this;
+    _boundEvents.updateViewarea = this._updateViewarea.bind(this);
+    _boundEvents.popState = this._popState.bind(this);
+    _boundEvents.pageHide = evt => {
+      if (!this._destination) {
+        this._tryPushCurrentPosition();
+      }
+    };
+    eventBus.on('updateviewarea', _boundEvents.updateViewarea);
+    window.addEventListener('popstate', _boundEvents.popState);
+    window.addEventListener('pagehide', _boundEvents.pageHide);
+  }
+}
+function isDestsEqual(firstDest, secondDest) {
+  function isEntryEqual(first, second) {
+    if (typeof first !== typeof second) {
+      return false;
+    }
+    if (first instanceof Array || second instanceof Array) {
+      return false;
+    }
+    if (first !== null && typeof first === 'object' && second !== null) {
+      if (Object.keys(first).length !== Object.keys(second).length) {
+        return false;
+      }
+      for (var key in first) {
+        if (!isEntryEqual(first[key], second[key])) {
+          return false;
+        }
+      }
+      return true;
+    }
+    return first === second || Number.isNaN(first) && Number.isNaN(second);
+  }
+  if (!(firstDest instanceof Array && secondDest instanceof Array)) {
+    return false;
+  }
+  if (firstDest.length !== secondDest.length) {
+    return false;
+  }
+  for (let i = 0, ii = firstDest.length; i < ii; i++) {
+    if (!isEntryEqual(firstDest[i], secondDest[i])) {
+      return false;
+    }
+  }
+  return true;
+}
+exports.PDFHistory = PDFHistory;
+exports.isDestsEqual = isDestsEqual;
+
+/***/ }),
+/* 17 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.PDFOutlineViewer = undefined;
+
+var _pdfjsLib = __webpack_require__(1);
+
+const DEFAULT_TITLE = '\u2013';
+class PDFOutlineViewer {
+  constructor({ container, linkService, eventBus }) {
+    this.container = container;
+    this.linkService = linkService;
+    this.eventBus = eventBus;
+    this.reset();
+  }
+  reset() {
+    this.outline = null;
+    this.lastToggleIsShow = true;
+    this.container.textContent = '';
+    this.container.classList.remove('outlineWithDeepNesting');
+  }
+  _dispatchEvent(outlineCount) {
+    this.eventBus.dispatch('outlineloaded', {
+      source: this,
+      outlineCount
+    });
+  }
+  _bindLink(element, item) {
+    if (item.url) {
+      (0, _pdfjsLib.addLinkAttributes)(element, {
+        url: item.url,
+        target: item.newWindow ? _pdfjsLib.PDFJS.LinkTarget.BLANK : undefined
+      });
+      return;
+    }
+    let destination = item.dest;
+    element.href = this.linkService.getDestinationHash(destination);
+    element.onclick = () => {
+      if (destination) {
+        this.linkService.navigateTo(destination);
+      }
+      return false;
+    };
+  }
+  _setStyles(element, item) {
+    let styleStr = '';
+    if (item.bold) {
+      styleStr += 'font-weight: bold;';
+    }
+    if (item.italic) {
+      styleStr += 'font-style: italic;';
+    }
+    if (styleStr) {
+      element.setAttribute('style', styleStr);
+    }
+  }
+  _addToggleButton(div) {
+    let toggler = document.createElement('div');
+    toggler.className = 'outlineItemToggler';
+    toggler.onclick = evt => {
+      evt.stopPropagation();
+      toggler.classList.toggle('outlineItemsHidden');
+      if (evt.shiftKey) {
+        let shouldShowAll = !toggler.classList.contains('outlineItemsHidden');
+        this._toggleOutlineItem(div, shouldShowAll);
+      }
+    };
+    div.insertBefore(toggler, div.firstChild);
+  }
+  _toggleOutlineItem(root, show) {
+    this.lastToggleIsShow = show;
+    let togglers = root.querySelectorAll('.outlineItemToggler');
+    for (let i = 0, ii = togglers.length; i < ii; ++i) {
+      togglers[i].classList[show ? 'remove' : 'add']('outlineItemsHidden');
+    }
+  }
+  toggleOutlineTree() {
+    if (!this.outline) {
+      return;
+    }
+    this._toggleOutlineItem(this.container, !this.lastToggleIsShow);
+  }
+  render({ outline }) {
+    let outlineCount = 0;
+    if (this.outline) {
+      this.reset();
+    }
+    this.outline = outline || null;
+    if (!outline) {
+      this._dispatchEvent(outlineCount);
+      return;
+    }
+    let fragment = document.createDocumentFragment();
+    let queue = [{
+      parent: fragment,
+      items: this.outline
+    }];
+    let hasAnyNesting = false;
+    while (queue.length > 0) {
+      let levelData = queue.shift();
+      for (let i = 0, len = levelData.items.length; i < len; i++) {
+        let item = levelData.items[i];
+        let div = document.createElement('div');
+        div.className = 'outlineItem';
+        let element = document.createElement('a');
+        this._bindLink(element, item);
+        this._setStyles(element, item);
+        element.textContent = (0, _pdfjsLib.removeNullCharacters)(item.title) || DEFAULT_TITLE;
+        div.appendChild(element);
+        if (item.items.length > 0) {
+          hasAnyNesting = true;
+          this._addToggleButton(div);
+          let itemsDiv = document.createElement('div');
+          itemsDiv.className = 'outlineItems';
+          div.appendChild(itemsDiv);
+          queue.push({
+            parent: itemsDiv,
+            items: item.items
+          });
+        }
+        levelData.parent.appendChild(div);
+        outlineCount++;
+      }
+    }
+    if (hasAnyNesting) {
+      this.container.classList.add('outlineWithDeepNesting');
+    }
+    this.container.appendChild(fragment);
+    this._dispatchEvent(outlineCount);
+  }
+}
+exports.PDFOutlineViewer = PDFOutlineViewer;
+
+/***/ }),
+/* 18 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.PDFPresentationMode = undefined;
+
+var _ui_utils = __webpack_require__(0);
+
+const DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS = 1500;
+const DELAY_BEFORE_HIDING_CONTROLS = 3000;
+const ACTIVE_SELECTOR = 'pdfPresentationMode';
+const CONTROLS_SELECTOR = 'pdfPresentationModeControls';
+const MOUSE_SCROLL_COOLDOWN_TIME = 50;
+const PAGE_SWITCH_THRESHOLD = 0.1;
+const SWIPE_MIN_DISTANCE_THRESHOLD = 50;
+const SWIPE_ANGLE_THRESHOLD = Math.PI / 6;
+class PDFPresentationMode {
+  constructor({ container, viewer = null, pdfViewer, eventBus, contextMenuItems = null }) {
+    this.container = container;
+    this.viewer = viewer || container.firstElementChild;
+    this.pdfViewer = pdfViewer;
+    this.eventBus = eventBus;
+    this.active = false;
+    this.args = null;
+    this.contextMenuOpen = false;
+    this.mouseScrollTimeStamp = 0;
+    this.mouseScrollDelta = 0;
+    this.touchSwipeState = null;
+    if (contextMenuItems) {
+      contextMenuItems.contextFirstPage.addEventListener('click', () => {
+        this.contextMenuOpen = false;
+        this.eventBus.dispatch('firstpage');
+      });
+      contextMenuItems.contextLastPage.addEventListener('click', () => {
+        this.contextMenuOpen = false;
+        this.eventBus.dispatch('lastpage');
+      });
+      contextMenuItems.contextPageRotateCw.addEventListener('click', () => {
+        this.contextMenuOpen = false;
+        this.eventBus.dispatch('rotatecw');
+      });
+      contextMenuItems.contextPageRotateCcw.addEventListener('click', () => {
+        this.contextMenuOpen = false;
+        this.eventBus.dispatch('rotateccw');
+      });
+    }
+  }
+  request() {
+    if (this.switchInProgress || this.active || !this.viewer.hasChildNodes()) {
+      return false;
+    }
+    this._addFullscreenChangeListeners();
+    this._setSwitchInProgress();
+    this._notifyStateChange();
+    if (this.container.requestFullscreen) {
+      this.container.requestFullscreen();
+    } else if (this.container.mozRequestFullScreen) {
+      this.container.mozRequestFullScreen();
+    } else if (this.container.webkitRequestFullscreen) {
+      this.container.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
+    } else if (this.container.msRequestFullscreen) {
+      this.container.msRequestFullscreen();
+    } else {
+      return false;
+    }
+    this.args = {
+      page: this.pdfViewer.currentPageNumber,
+      previousScale: this.pdfViewer.currentScaleValue
+    };
+    return true;
+  }
+  _mouseWheel(evt) {
+    if (!this.active) {
+      return;
+    }
+    evt.preventDefault();
+    let delta = (0, _ui_utils.normalizeWheelEventDelta)(evt);
+    let currentTime = new Date().getTime();
+    let storedTime = this.mouseScrollTimeStamp;
+    if (currentTime > storedTime && currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME) {
+      return;
+    }
+    if (this.mouseScrollDelta > 0 && delta < 0 || this.mouseScrollDelta < 0 && delta > 0) {
+      this._resetMouseScrollState();
+    }
+    this.mouseScrollDelta += delta;
+    if (Math.abs(this.mouseScrollDelta) >= PAGE_SWITCH_THRESHOLD) {
+      let totalDelta = this.mouseScrollDelta;
+      this._resetMouseScrollState();
+      let success = totalDelta > 0 ? this._goToPreviousPage() : this._goToNextPage();
+      if (success) {
+        this.mouseScrollTimeStamp = currentTime;
+      }
+    }
+  }
+  get isFullscreen() {
+    return !!(document.fullscreenElement || document.mozFullScreen || document.webkitIsFullScreen || document.msFullscreenElement);
+  }
+  _goToPreviousPage() {
+    let page = this.pdfViewer.currentPageNumber;
+    if (page <= 1) {
+      return false;
+    }
+    this.pdfViewer.currentPageNumber = page - 1;
+    return true;
+  }
+  _goToNextPage() {
+    let page = this.pdfViewer.currentPageNumber;
+    if (page >= this.pdfViewer.pagesCount) {
+      return false;
+    }
+    this.pdfViewer.currentPageNumber = page + 1;
+    return true;
+  }
+  _notifyStateChange() {
+    this.eventBus.dispatch('presentationmodechanged', {
+      source: this,
+      active: this.active,
+      switchInProgress: !!this.switchInProgress
+    });
+  }
+  _setSwitchInProgress() {
+    if (this.switchInProgress) {
+      clearTimeout(this.switchInProgress);
+    }
+    this.switchInProgress = setTimeout(() => {
+      this._removeFullscreenChangeListeners();
+      delete this.switchInProgress;
+      this._notifyStateChange();
+    }, DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS);
+  }
+  _resetSwitchInProgress() {
+    if (this.switchInProgress) {
+      clearTimeout(this.switchInProgress);
+      delete this.switchInProgress;
+    }
+  }
+  _enter() {
+    this.active = true;
+    this._resetSwitchInProgress();
+    this._notifyStateChange();
+    this.container.classList.add(ACTIVE_SELECTOR);
+    setTimeout(() => {
+      this.pdfViewer.currentPageNumber = this.args.page;
+      this.pdfViewer.currentScaleValue = 'page-fit';
+    }, 0);
+    this._addWindowListeners();
+    this._showControls();
+    this.contextMenuOpen = false;
+    this.container.setAttribute('contextmenu', 'viewerContextMenu');
+    window.getSelection().removeAllRanges();
+  }
+  _exit() {
+    let page = this.pdfViewer.currentPageNumber;
+    this.container.classList.remove(ACTIVE_SELECTOR);
+    setTimeout(() => {
+      this.active = false;
+      this._removeFullscreenChangeListeners();
+      this._notifyStateChange();
+      this.pdfViewer.currentScaleValue = this.args.previousScale;
+      this.pdfViewer.currentPageNumber = page;
+      this.args = null;
+    }, 0);
+    this._removeWindowListeners();
+    this._hideControls();
+    this._resetMouseScrollState();
+    this.container.removeAttribute('contextmenu');
+    this.contextMenuOpen = false;
+  }
+  _mouseDown(evt) {
+    if (this.contextMenuOpen) {
+      this.contextMenuOpen = false;
+      evt.preventDefault();
+      return;
+    }
+    if (evt.button === 0) {
+      let isInternalLink = evt.target.href && evt.target.classList.contains('internalLink');
+      if (!isInternalLink) {
+        evt.preventDefault();
+        if (evt.shiftKey) {
+          this._goToPreviousPage();
+        } else {
+          this._goToNextPage();
+        }
+      }
+    }
+  }
+  _contextMenu() {
+    this.contextMenuOpen = true;
+  }
+  _showControls() {
+    if (this.controlsTimeout) {
+      clearTimeout(this.controlsTimeout);
+    } else {
+      this.container.classList.add(CONTROLS_SELECTOR);
+    }
+    this.controlsTimeout = setTimeout(() => {
+      this.container.classList.remove(CONTROLS_SELECTOR);
+      delete this.controlsTimeout;
+    }, DELAY_BEFORE_HIDING_CONTROLS);
+  }
+  _hideControls() {
+    if (!this.controlsTimeout) {
+      return;
+    }
+    clearTimeout(this.controlsTimeout);
+    this.container.classList.remove(CONTROLS_SELECTOR);
+    delete this.controlsTimeout;
+  }
+  _resetMouseScrollState() {
+    this.mouseScrollTimeStamp = 0;
+    this.mouseScrollDelta = 0;
+  }
+  _touchSwipe(evt) {
+    if (!this.active) {
+      return;
+    }
+    if (evt.touches.length > 1) {
+      this.touchSwipeState = null;
+      return;
+    }
+    switch (evt.type) {
+      case 'touchstart':
+        this.touchSwipeState = {
+          startX: evt.touches[0].pageX,
+          startY: evt.touches[0].pageY,
+          endX: evt.touches[0].pageX,
+          endY: evt.touches[0].pageY
+        };
+        break;
+      case 'touchmove':
+        if (this.touchSwipeState === null) {
+          return;
+        }
+        this.touchSwipeState.endX = evt.touches[0].pageX;
+        this.touchSwipeState.endY = evt.touches[0].pageY;
+        evt.preventDefault();
+        break;
+      case 'touchend':
+        if (this.touchSwipeState === null) {
+          return;
+        }
+        let delta = 0;
+        let dx = this.touchSwipeState.endX - this.touchSwipeState.startX;
+        let dy = this.touchSwipeState.endY - this.touchSwipeState.startY;
+        let absAngle = Math.abs(Math.atan2(dy, dx));
+        if (Math.abs(dx) > SWIPE_MIN_DISTANCE_THRESHOLD && (absAngle <= SWIPE_ANGLE_THRESHOLD || absAngle >= Math.PI - SWIPE_ANGLE_THRESHOLD)) {
+          delta = dx;
+        } else if (Math.abs(dy) > SWIPE_MIN_DISTANCE_THRESHOLD && Math.abs(absAngle - Math.PI / 2) <= SWIPE_ANGLE_THRESHOLD) {
+          delta = dy;
+        }
+        if (delta > 0) {
+          this._goToPreviousPage();
+        } else if (delta < 0) {
+          this._goToNextPage();
+        }
+        break;
+    }
+  }
+  _addWindowListeners() {
+    this.showControlsBind = this._showControls.bind(this);
+    this.mouseDownBind = this._mouseDown.bind(this);
+    this.mouseWheelBind = this._mouseWheel.bind(this);
+    this.resetMouseScrollStateBind = this._resetMouseScrollState.bind(this);
+    this.contextMenuBind = this._contextMenu.bind(this);
+    this.touchSwipeBind = this._touchSwipe.bind(this);
+    window.addEventListener('mousemove', this.showControlsBind);
+    window.addEventListener('mousedown', this.mouseDownBind);
+    window.addEventListener('wheel', this.mouseWheelBind);
+    window.addEventListener('keydown', this.resetMouseScrollStateBind);
+    window.addEventListener('contextmenu', this.contextMenuBind);
+    window.addEventListener('touchstart', this.touchSwipeBind);
+    window.addEventListener('touchmove', this.touchSwipeBind);
+    window.addEventListener('touchend', this.touchSwipeBind);
+  }
+  _removeWindowListeners() {
+    window.removeEventListener('mousemove', this.showControlsBind);
+    window.removeEventListener('mousedown', this.mouseDownBind);
+    window.removeEventListener('wheel', this.mouseWheelBind);
+    window.removeEventListener('keydown', this.resetMouseScrollStateBind);
+    window.removeEventListener('contextmenu', this.contextMenuBind);
+    window.removeEventListener('touchstart', this.touchSwipeBind);
+    window.removeEventListener('touchmove', this.touchSwipeBind);
+    window.removeEventListener('touchend', this.touchSwipeBind);
+    delete this.showControlsBind;
+    delete this.mouseDownBind;
+    delete this.mouseWheelBind;
+    delete this.resetMouseScrollStateBind;
+    delete this.contextMenuBind;
+    delete this.touchSwipeBind;
+  }
+  _fullscreenChange() {
+    if (this.isFullscreen) {
+      this._enter();
+    } else {
+      this._exit();
+    }
+  }
+  _addFullscreenChangeListeners() {
+    this.fullscreenChangeBind = this._fullscreenChange.bind(this);
+    window.addEventListener('fullscreenchange', this.fullscreenChangeBind);
+    window.addEventListener('mozfullscreenchange', this.fullscreenChangeBind);
+  }
+  _removeFullscreenChangeListeners() {
+    window.removeEventListener('fullscreenchange', this.fullscreenChangeBind);
+    window.removeEventListener('mozfullscreenchange', this.fullscreenChangeBind);
+    delete this.fullscreenChangeBind;
+  }
+}
+exports.PDFPresentationMode = PDFPresentationMode;
+
+/***/ }),
+/* 19 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.PDFThumbnailViewer = undefined;
+
+var _ui_utils = __webpack_require__(0);
+
+var _pdf_thumbnail_view = __webpack_require__(20);
+
+const THUMBNAIL_SCROLL_MARGIN = -19;
+class PDFThumbnailViewer {
+  constructor({ container, linkService, renderingQueue, l10n = _ui_utils.NullL10n }) {
+    this.container = container;
+    this.linkService = linkService;
+    this.renderingQueue = renderingQueue;
+    this.l10n = l10n;
+    this.scroll = (0, _ui_utils.watchScroll)(this.container, this._scrollUpdated.bind(this));
+    this._resetView();
+  }
+  _scrollUpdated() {
+    this.renderingQueue.renderHighestPriority();
+  }
+  getThumbnail(index) {
+    return this._thumbnails[index];
+  }
+  _getVisibleThumbs() {
+    return (0, _ui_utils.getVisibleElements)(this.container, this._thumbnails);
+  }
+  scrollThumbnailIntoView(page) {
+    let selected = document.querySelector('.thumbnail.selected');
+    if (selected) {
+      selected.classList.remove('selected');
+    }
+    let thumbnail = document.querySelector('div.thumbnail[data-page-number="' + page + '"]');
+    if (thumbnail) {
+      thumbnail.classList.add('selected');
+    }
+    let visibleThumbs = this._getVisibleThumbs();
+    let numVisibleThumbs = visibleThumbs.views.length;
+    if (numVisibleThumbs > 0) {
+      let first = visibleThumbs.first.id;
+      let last = numVisibleThumbs > 1 ? visibleThumbs.last.id : first;
+      if (page <= first || page >= last) {
+        (0, _ui_utils.scrollIntoView)(thumbnail, { top: THUMBNAIL_SCROLL_MARGIN });
+      }
+    }
+  }
+  get pagesRotation() {
+    return this._pagesRotation;
+  }
+  set pagesRotation(rotation) {
+    if (!(0, _ui_utils.isValidRotation)(rotation)) {
+      throw new Error('Invalid thumbnails rotation angle.');
+    }
+    if (!this.pdfDocument) {
+      return;
+    }
+    if (this._pagesRotation === rotation) {
+      return;
+    }
+    this._pagesRotation = rotation;
+    for (let i = 0, ii = this._thumbnails.length; i < ii; i++) {
+      this._thumbnails[i].update(rotation);
+    }
+  }
+  cleanup() {
+    _pdf_thumbnail_view.PDFThumbnailView.cleanup();
+  }
+  _resetView() {
+    this._thumbnails = [];
+    this._pageLabels = null;
+    this._pagesRotation = 0;
+    this._pagesRequests = [];
+    this.container.textContent = '';
+  }
+  setDocument(pdfDocument) {
+    if (this.pdfDocument) {
+      this._cancelRendering();
+      this._resetView();
+    }
+    this.pdfDocument = pdfDocument;
+    if (!pdfDocument) {
+      return;
+    }
+    pdfDocument.getPage(1).then(firstPage => {
+      let pagesCount = pdfDocument.numPages;
+      let viewport = firstPage.getViewport(1.0);
+      for (let pageNum = 1; pageNum <= pagesCount; ++pageNum) {
+        let thumbnail = new _pdf_thumbnail_view.PDFThumbnailView({
+          container: this.container,
+          id: pageNum,
+          defaultViewport: viewport.clone(),
+          linkService: this.linkService,
+          renderingQueue: this.renderingQueue,
+          disableCanvasToImageConversion: false,
+          l10n: this.l10n
+        });
+        this._thumbnails.push(thumbnail);
+      }
+    }).catch(reason => {
+      console.error('Unable to initialize thumbnail viewer', reason);
+    });
+  }
+  _cancelRendering() {
+    for (let i = 0, ii = this._thumbnails.length; i < ii; i++) {
+      if (this._thumbnails[i]) {
+        this._thumbnails[i].cancelRendering();
+      }
+    }
+  }
+  setPageLabels(labels) {
+    if (!this.pdfDocument) {
+      return;
+    }
+    if (!labels) {
+      this._pageLabels = null;
+    } else if (!(labels instanceof Array && this.pdfDocument.numPages === labels.length)) {
+      this._pageLabels = null;
+      console.error('PDFThumbnailViewer_setPageLabels: Invalid page labels.');
+    } else {
+      this._pageLabels = labels;
+    }
+    for (let i = 0, ii = this._thumbnails.length; i < ii; i++) {
+      let label = this._pageLabels && this._pageLabels[i];
+      this._thumbnails[i].setPageLabel(label);
+    }
+  }
+  _ensurePdfPageLoaded(thumbView) {
+    if (thumbView.pdfPage) {
+      return Promise.resolve(thumbView.pdfPage);
+    }
+    let pageNumber = thumbView.id;
+    if (this._pagesRequests[pageNumber]) {
+      return this._pagesRequests[pageNumber];
+    }
+    let promise = this.pdfDocument.getPage(pageNumber).then(pdfPage => {
+      thumbView.setPdfPage(pdfPage);
+      this._pagesRequests[pageNumber] = null;
+      return pdfPage;
+    }).catch(reason => {
+      console.error('Unable to get page for thumb view', reason);
+      this._pagesRequests[pageNumber] = null;
+    });
+    this._pagesRequests[pageNumber] = promise;
+    return promise;
+  }
+  forceRendering() {
+    let visibleThumbs = this._getVisibleThumbs();
+    let thumbView = this.renderingQueue.getHighestPriority(visibleThumbs, this._thumbnails, this.scroll.down);
+    if (thumbView) {
+      this._ensurePdfPageLoaded(thumbView).then(() => {
+        this.renderingQueue.renderView(thumbView);
+      });
+      return true;
+    }
+    return false;
+  }
+}
+exports.PDFThumbnailViewer = PDFThumbnailViewer;
+
+/***/ }),
+/* 20 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.PDFThumbnailView = undefined;
 
 var _pdfjsLib = __webpack_require__(1);
 
 var _ui_utils = __webpack_require__(0);
 
 var _pdf_rendering_queue = __webpack_require__(3);
 
-var _annotation_layer_builder = __webpack_require__(12);
+const MAX_NUM_SCALING_STEPS = 3;
+const THUMBNAIL_CANVAS_BORDER_WIDTH = 1;
+const THUMBNAIL_WIDTH = 98;
+const TempImageFactory = function TempImageFactoryClosure() {
+  let tempCanvasCache = null;
+  return {
+    getCanvas(width, height) {
+      let tempCanvas = tempCanvasCache;
+      if (!tempCanvas) {
+        tempCanvas = document.createElement('canvas');
+        tempCanvasCache = tempCanvas;
+      }
+      tempCanvas.width = width;
+      tempCanvas.height = height;
+      tempCanvas.mozOpaque = true;
+      let ctx = tempCanvas.getContext('2d', { alpha: false });
+      ctx.save();
+      ctx.fillStyle = 'rgb(255, 255, 255)';
+      ctx.fillRect(0, 0, width, height);
+      ctx.restore();
+      return tempCanvas;
+    },
+    destroyCanvas() {
+      let tempCanvas = tempCanvasCache;
+      if (tempCanvas) {
+        tempCanvas.width = 0;
+        tempCanvas.height = 0;
+      }
+      tempCanvasCache = null;
+    }
+  };
+}();
+class PDFThumbnailView {
+  constructor({ container, id, defaultViewport, linkService, renderingQueue, disableCanvasToImageConversion = false, l10n = _ui_utils.NullL10n }) {
+    this.id = id;
+    this.renderingId = 'thumbnail' + id;
+    this.pageLabel = null;
+    this.pdfPage = null;
+    this.rotation = 0;
+    this.viewport = defaultViewport;
+    this.pdfPageRotate = defaultViewport.rotation;
+    this.linkService = linkService;
+    this.renderingQueue = renderingQueue;
+    this.renderTask = null;
+    this.renderingState = _pdf_rendering_queue.RenderingStates.INITIAL;
+    this.resume = null;
+    this.disableCanvasToImageConversion = disableCanvasToImageConversion;
+    this.pageWidth = this.viewport.width;
+    this.pageHeight = this.viewport.height;
+    this.pageRatio = this.pageWidth / this.pageHeight;
+    this.canvasWidth = THUMBNAIL_WIDTH;
+    this.canvasHeight = this.canvasWidth / this.pageRatio | 0;
+    this.scale = this.canvasWidth / this.pageWidth;
+    this.l10n = l10n;
+    let anchor = document.createElement('a');
+    anchor.href = linkService.getAnchorUrl('#page=' + id);
+    this.l10n.get('thumb_page_title', { page: id }, 'Page {{page}}').then(msg => {
+      anchor.title = msg;
+    });
+    anchor.onclick = function () {
+      linkService.page = id;
+      return false;
+    };
+    this.anchor = anchor;
+    let div = document.createElement('div');
+    div.className = 'thumbnail';
+    div.setAttribute('data-page-number', this.id);
+    this.div = div;
+    if (id === 1) {
+      div.classList.add('selected');
+    }
+    let ring = document.createElement('div');
+    ring.className = 'thumbnailSelectionRing';
+    let borderAdjustment = 2 * THUMBNAIL_CANVAS_BORDER_WIDTH;
+    ring.style.width = this.canvasWidth + borderAdjustment + 'px';
+    ring.style.height = this.canvasHeight + borderAdjustment + 'px';
+    this.ring = ring;
+    div.appendChild(ring);
+    anchor.appendChild(div);
+    container.appendChild(anchor);
+  }
+  setPdfPage(pdfPage) {
+    this.pdfPage = pdfPage;
+    this.pdfPageRotate = pdfPage.rotate;
+    let totalRotation = (this.rotation + this.pdfPageRotate) % 360;
+    this.viewport = pdfPage.getViewport(1, totalRotation);
+    this.reset();
+  }
+  reset() {
+    this.cancelRendering();
+    this.pageWidth = this.viewport.width;
+    this.pageHeight = this.viewport.height;
+    this.pageRatio = this.pageWidth / this.pageHeight;
+    this.canvasHeight = this.canvasWidth / this.pageRatio | 0;
+    this.scale = this.canvasWidth / this.pageWidth;
+    this.div.removeAttribute('data-loaded');
+    let ring = this.ring;
+    let childNodes = ring.childNodes;
+    for (let i = childNodes.length - 1; i >= 0; i--) {
+      ring.removeChild(childNodes[i]);
+    }
+    let borderAdjustment = 2 * THUMBNAIL_CANVAS_BORDER_WIDTH;
+    ring.style.width = this.canvasWidth + borderAdjustment + 'px';
+    ring.style.height = this.canvasHeight + borderAdjustment + 'px';
+    if (this.canvas) {
+      this.canvas.width = 0;
+      this.canvas.height = 0;
+      delete this.canvas;
+    }
+    if (this.image) {
+      this.image.removeAttribute('src');
+      delete this.image;
+    }
+  }
+  update(rotation) {
+    if (typeof rotation !== 'undefined') {
+      this.rotation = rotation;
+    }
+    let totalRotation = (this.rotation + this.pdfPageRotate) % 360;
+    this.viewport = this.viewport.clone({
+      scale: 1,
+      rotation: totalRotation
+    });
+    this.reset();
+  }
+  cancelRendering() {
+    if (this.renderTask) {
+      this.renderTask.cancel();
+      this.renderTask = null;
+    }
+    this.renderingState = _pdf_rendering_queue.RenderingStates.INITIAL;
+    this.resume = null;
+  }
+  _getPageDrawContext(noCtxScale = false) {
+    let canvas = document.createElement('canvas');
+    this.canvas = canvas;
+    canvas.mozOpaque = true;
+    let ctx = canvas.getContext('2d', { alpha: false });
+    let outputScale = (0, _ui_utils.getOutputScale)(ctx);
+    canvas.width = this.canvasWidth * outputScale.sx | 0;
+    canvas.height = this.canvasHeight * outputScale.sy | 0;
+    canvas.style.width = this.canvasWidth + 'px';
+    canvas.style.height = this.canvasHeight + 'px';
+    if (!noCtxScale && outputScale.scaled) {
+      ctx.scale(outputScale.sx, outputScale.sy);
+    }
+    return ctx;
+  }
+  _convertCanvasToImage() {
+    if (!this.canvas) {
+      return;
+    }
+    if (this.renderingState !== _pdf_rendering_queue.RenderingStates.FINISHED) {
+      return;
+    }
+    let id = this.renderingId;
+    let className = 'thumbnailImage';
+    if (this.disableCanvasToImageConversion) {
+      this.canvas.id = id;
+      this.canvas.className = className;
+      this.l10n.get('thumb_page_canvas', { page: this.pageId }, 'Thumbnail of Page {{page}}').then(msg => {
+        this.canvas.setAttribute('aria-label', msg);
+      });
+      this.div.setAttribute('data-loaded', true);
+      this.ring.appendChild(this.canvas);
+      return;
+    }
+    let image = document.createElement('img');
+    image.id = id;
+    image.className = className;
+    this.l10n.get('thumb_page_canvas', { page: this.pageId }, 'Thumbnail of Page {{page}}').then(msg => {
+      image.setAttribute('aria-label', msg);
+    });
+    image.style.width = this.canvasWidth + 'px';
+    image.style.height = this.canvasHeight + 'px';
+    image.src = this.canvas.toDataURL();
+    this.image = image;
+    this.div.setAttribute('data-loaded', true);
+    this.ring.appendChild(image);
+    this.canvas.width = 0;
+    this.canvas.height = 0;
+    delete this.canvas;
+  }
+  draw() {
+    if (this.renderingState !== _pdf_rendering_queue.RenderingStates.INITIAL) {
+      console.error('Must be in new state before drawing');
+      return Promise.resolve(undefined);
+    }
+    this.renderingState = _pdf_rendering_queue.RenderingStates.RUNNING;
+    let renderCapability = (0, _pdfjsLib.createPromiseCapability)();
+    let finishRenderTask = error => {
+      if (renderTask === this.renderTask) {
+        this.renderTask = null;
+      }
+      if (error instanceof _pdfjsLib.RenderingCancelledException) {
+        renderCapability.resolve(undefined);
+        return;
+      }
+      this.renderingState = _pdf_rendering_queue.RenderingStates.FINISHED;
+      this._convertCanvasToImage();
+      if (!error) {
+        renderCapability.resolve(undefined);
+      } else {
+        renderCapability.reject(error);
+      }
+    };
+    let ctx = this._getPageDrawContext();
+    let drawViewport = this.viewport.clone({ scale: this.scale });
+    let renderContinueCallback = cont => {
+      if (!this.renderingQueue.isHighestPriority(this)) {
+        this.renderingState = _pdf_rendering_queue.RenderingStates.PAUSED;
+        this.resume = () => {
+          this.renderingState = _pdf_rendering_queue.RenderingStates.RUNNING;
+          cont();
+        };
+        return;
+      }
+      cont();
+    };
+    let renderContext = {
+      canvasContext: ctx,
+      viewport: drawViewport
+    };
+    let renderTask = this.renderTask = this.pdfPage.render(renderContext);
+    renderTask.onContinue = renderContinueCallback;
+    renderTask.promise.then(function () {
+      finishRenderTask(null);
+    }, function (error) {
+      finishRenderTask(error);
+    });
+    return renderCapability.promise;
+  }
+  setImage(pageView) {
+    if (this.renderingState !== _pdf_rendering_queue.RenderingStates.INITIAL) {
+      return;
+    }
+    let img = pageView.canvas;
+    if (!img) {
+      return;
+    }
+    if (!this.pdfPage) {
+      this.setPdfPage(pageView.pdfPage);
+    }
+    this.renderingState = _pdf_rendering_queue.RenderingStates.FINISHED;
+    let ctx = this._getPageDrawContext(true);
+    let canvas = ctx.canvas;
+    if (img.width <= 2 * canvas.width) {
+      ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height);
+      this._convertCanvasToImage();
+      return;
+    }
+    let reducedWidth = canvas.width << MAX_NUM_SCALING_STEPS;
+    let reducedHeight = canvas.height << MAX_NUM_SCALING_STEPS;
+    let reducedImage = TempImageFactory.getCanvas(reducedWidth, reducedHeight);
+    let reducedImageCtx = reducedImage.getContext('2d');
+    while (reducedWidth > img.width || reducedHeight > img.height) {
+      reducedWidth >>= 1;
+      reducedHeight >>= 1;
+    }
+    reducedImageCtx.drawImage(img, 0, 0, img.width, img.height, 0, 0, reducedWidth, reducedHeight);
+    while (reducedWidth > 2 * canvas.width) {
+      reducedImageCtx.drawImage(reducedImage, 0, 0, reducedWidth, reducedHeight, 0, 0, reducedWidth >> 1, reducedHeight >> 1);
+      reducedWidth >>= 1;
+      reducedHeight >>= 1;
+    }
+    ctx.drawImage(reducedImage, 0, 0, reducedWidth, reducedHeight, 0, 0, canvas.width, canvas.height);
+    this._convertCanvasToImage();
+  }
+  get pageId() {
+    return this.pageLabel !== null ? this.pageLabel : this.id;
+  }
+  setPageLabel(label) {
+    this.pageLabel = typeof label === 'string' ? label : null;
+    this.l10n.get('thumb_page_title', { page: this.pageId }, 'Page {{page}}').then(msg => {
+      this.anchor.title = msg;
+    });
+    if (this.renderingState !== _pdf_rendering_queue.RenderingStates.FINISHED) {
+      return;
+    }
+    this.l10n.get('thumb_page_canvas', { page: this.pageId }, 'Thumbnail of Page {{page}}').then(ariaLabel => {
+      if (this.image) {
+        this.image.setAttribute('aria-label', ariaLabel);
+      } else if (this.disableCanvasToImageConversion && this.canvas) {
+        this.canvas.setAttribute('aria-label', ariaLabel);
+      }
+    });
+  }
+  static cleanup() {
+    TempImageFactory.destroyCanvas();
+  }
+}
+exports.PDFThumbnailView = PDFThumbnailView;
+
+/***/ }),
+/* 21 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.PDFViewer = undefined;
+
+var _ui_utils = __webpack_require__(0);
+
+var _base_viewer = __webpack_require__(22);
+
+var _pdfjsLib = __webpack_require__(1);
+
+class PDFViewer extends _base_viewer.BaseViewer {
+  get _setDocumentViewerElement() {
+    return (0, _pdfjsLib.shadow)(this, '_setDocumentViewerElement', this.viewer);
+  }
+  _scrollIntoView({ pageDiv, pageSpot = null }) {
+    (0, _ui_utils.scrollIntoView)(pageDiv, pageSpot);
+  }
+  _getVisiblePages() {
+    if (!this.isInPresentationMode) {
+      return (0, _ui_utils.getVisibleElements)(this.container, this._pages, true);
+    }
+    let currentPage = this._pages[this._currentPageNumber - 1];
+    let visible = [{
+      id: currentPage.id,
+      view: currentPage
+    }];
+    return {
+      first: currentPage,
+      last: currentPage,
+      views: visible
+    };
+  }
+  update() {
+    let visible = this._getVisiblePages();
+    let visiblePages = visible.views,
+        numVisiblePages = visiblePages.length;
+    if (numVisiblePages === 0) {
+      return;
+    }
+    this._resizeBuffer(numVisiblePages);
+    this.renderingQueue.renderHighestPriority(visible);
+    let currentId = this._currentPageNumber;
+    let stillFullyVisible = false;
+    for (let i = 0; i < numVisiblePages; ++i) {
+      let page = visiblePages[i];
+      if (page.percent < 100) {
+        break;
+      }
+      if (page.id === currentId) {
+        stillFullyVisible = true;
+        break;
+      }
+    }
+    if (!stillFullyVisible) {
+      currentId = visiblePages[0].id;
+    }
+    if (!this.isInPresentationMode) {
+      this._setCurrentPageNumber(currentId);
+    }
+    this._updateLocation(visible.first);
+    this.eventBus.dispatch('updateviewarea', {
+      source: this,
+      location: this._location
+    });
+  }
+}
+exports.PDFViewer = PDFViewer;
+
+/***/ }),
+/* 22 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.BaseViewer = undefined;
+
+var _pdfjsLib = __webpack_require__(1);
+
+var _ui_utils = __webpack_require__(0);
+
+var _pdf_rendering_queue = __webpack_require__(3);
+
+var _annotation_layer_builder = __webpack_require__(23);
 
 var _dom_events = __webpack_require__(2);
 
-var _pdf_page_view = __webpack_require__(13);
+var _pdf_page_view = __webpack_require__(24);
 
 var _pdf_link_service = __webpack_require__(5);
 
-var _text_layer_builder = __webpack_require__(14);
-
-const PresentationModeState = {
-  UNKNOWN: 0,
-  NORMAL: 1,
-  CHANGING: 2,
-  FULLSCREEN: 3
-};
+var _text_layer_builder = __webpack_require__(25);
+
 const DEFAULT_CACHE_SIZE = 10;
 function PDFPageViewBuffer(size) {
   let data = [];
-  this.push = function cachePush(view) {
+  this.push = function (view) {
     let i = data.indexOf(view);
     if (i >= 0) {
       data.splice(i, 1);
     }
     data.push(view);
     if (data.length > size) {
       data.shift().destroy();
     }
@@ -3809,18 +5794,22 @@ function isSameScale(oldScale, newScale)
   if (Math.abs(newScale - oldScale) < 1e-15) {
     return true;
   }
   return false;
 }
 function isPortraitOrientation(size) {
   return size.width <= size.height;
 }
-class PDFViewer {
+class BaseViewer {
   constructor(options) {
+    if (this.constructor === BaseViewer) {
+      throw new Error('Cannot initialize BaseViewer.');
+    }
+    this._name = this.constructor.name;
     this.container = options.container;
     this.viewer = options.viewer || options.container.firstElementChild;
     this.eventBus = options.eventBus || (0, _dom_events.getGlobalEventBus)();
     this.linkService = options.linkService || new _pdf_link_service.SimpleLinkService();
     this.downloadManager = options.downloadManager || null;
     this.removePageBorders = options.removePageBorders || false;
     this.enhanceTextSelection = options.enhanceTextSelection || false;
     this.renderInteractiveForms = options.renderInteractiveForms || false;
@@ -3830,17 +5819,17 @@ class PDFViewer {
     this.defaultRenderingQueue = !options.renderingQueue;
     if (this.defaultRenderingQueue) {
       this.renderingQueue = new _pdf_rendering_queue.PDFRenderingQueue();
       this.renderingQueue.setViewer(this);
     } else {
       this.renderingQueue = options.renderingQueue;
     }
     this.scroll = (0, _ui_utils.watchScroll)(this.container, this._scrollUpdate.bind(this));
-    this.presentationModeState = PresentationModeState.UNKNOWN;
+    this.presentationModeState = _ui_utils.PresentationModeState.UNKNOWN;
     this._resetView();
     if (this.removePageBorders) {
       this.viewer.classList.add('removePageBorders');
     }
   }
   get pagesCount() {
     return this._pages.length;
   }
@@ -3865,17 +5854,17 @@ class PDFViewer {
   _setCurrentPageNumber(val, resetCurrentPageView = false) {
     if (this._currentPageNumber === val) {
       if (resetCurrentPageView) {
         this._resetCurrentPageView();
       }
       return;
     }
     if (!(0 < val && val <= this.pagesCount)) {
-      console.error(`PDFViewer._setCurrentPageNumber: "${val}" is out of bounds.`);
+      console.error(`${this._name}._setCurrentPageNumber: "${val}" is out of bounds.`);
       return;
     }
     let arg = {
       source: this,
       pageNumber: val,
       pageLabel: this._pageLabels && this._pageLabels[val - 1]
     };
     this._currentPageNumber = val;
@@ -3945,16 +5934,19 @@ class PDFViewer {
       source: this,
       pagesRotation: rotation,
       pageNumber
     });
     if (this.defaultRenderingQueue) {
       this.update();
     }
   }
+  get _setDocumentViewerElement() {
+    throw new Error('Not implemented: _setDocumentViewerElement');
+  }
   setDocument(pdfDocument) {
     if (this.pdfDocument) {
       this._cancelRendering();
       this._resetView();
     }
     this.pdfDocument = pdfDocument;
     if (!pdfDocument) {
       return;
@@ -3989,17 +5981,17 @@ class PDFViewer {
       let scale = this.currentScale;
       let viewport = pdfPage.getViewport(scale * _ui_utils.CSS_UNITS);
       for (let pageNum = 1; pageNum <= pagesCount; ++pageNum) {
         let textLayerFactory = null;
         if (!_pdfjsLib.PDFJS.disableTextLayer) {
           textLayerFactory = this;
         }
         let pageView = new _pdf_page_view.PDFPageView({
-          container: this.viewer,
+          container: this._setDocumentViewerElement,
           eventBus: this.eventBus,
           id: pageNum,
           scale,
           defaultViewport: viewport.clone(),
           renderingQueue: this.renderingQueue,
           textLayerFactory,
           annotationLayerFactory: this,
           enhanceTextSelection: this.enhanceTextSelection,
@@ -4048,17 +6040,17 @@ class PDFViewer {
   setPageLabels(labels) {
     if (!this.pdfDocument) {
       return;
     }
     if (!labels) {
       this._pageLabels = null;
     } else if (!(labels instanceof Array && this.pdfDocument.numPages === labels.length)) {
       this._pageLabels = null;
-      console.error('PDFViewer.setPageLabels: Invalid page labels.');
+      console.error(`${this._name}.setPageLabels: Invalid page labels.`);
     } else {
       this._pageLabels = labels;
     }
     for (let i = 0, ii = this._pages.length; i < ii; i++) {
       let pageView = this._pages[i];
       let label = this._pageLabels && this._pageLabels[i];
       pageView.setPageLabel(label);
     }
@@ -4077,16 +6069,19 @@ class PDFViewer {
     this.viewer.textContent = '';
   }
   _scrollUpdate() {
     if (this.pagesCount === 0) {
       return;
     }
     this.update();
   }
+  _scrollIntoView({ pageDiv, pageSpot = null, pageNumber = null }) {
+    throw new Error('Not implemented: _scrollIntoView');
+  }
   _setScaleDispatchEvent(newScale, newValue, preset = false) {
     let arg = {
       source: this,
       scale: newScale,
       presetValue: preset ? newValue : undefined
     };
     this.eventBus.dispatch('scalechanging', arg);
     this.eventBus.dispatch('scalechange', arg);
@@ -4148,43 +6143,43 @@ class PDFViewer {
           scale = Math.min(pageWidthScale, pageHeightScale);
           break;
         case 'auto':
           let isLandscape = currentPage.width > currentPage.height;
           let horizontalScale = isLandscape ? Math.min(pageHeightScale, pageWidthScale) : pageWidthScale;
           scale = Math.min(_ui_utils.MAX_AUTO_SCALE, horizontalScale);
           break;
         default:
-          console.error(`PDFViewer._setScale: "${value}" is an unknown zoom value.`);
+          console.error(`${this._name}._setScale: "${value}" is an unknown zoom value.`);
           return;
       }
       this._setScaleUpdatePages(scale, value, noScroll, true);
     }
   }
   _resetCurrentPageView() {
     if (this.isInPresentationMode) {
       this._setScale(this._currentScaleValue, true);
     }
     let pageView = this._pages[this._currentPageNumber - 1];
-    (0, _ui_utils.scrollIntoView)(pageView.div);
+    this._scrollIntoView({ pageDiv: pageView.div });
   }
   scrollPageIntoView(params) {
     if (!this.pdfDocument) {
       return;
     }
     let pageNumber = params.pageNumber || 0;
     let dest = params.destArray || null;
     let allowNegativeOffset = params.allowNegativeOffset || false;
     if (this.isInPresentationMode || !dest) {
       this._setCurrentPageNumber(pageNumber, true);
       return;
     }
     let pageView = this._pages[pageNumber - 1];
     if (!pageView) {
-      console.error('PDFViewer.scrollPageIntoView: Invalid "pageNumber" parameter.');
+      console.error(`${this._name}.scrollPageIntoView: Invalid "pageNumber" parameter.`);
       return;
     }
     let x = 0,
         y = 0;
     let width = 0,
         height = 0,
         widthScale,
         heightScale;
@@ -4227,39 +6222,50 @@ class PDFViewer {
         height = dest[5] - y;
         let hPadding = this.removePageBorders ? 0 : _ui_utils.SCROLLBAR_PADDING;
         let vPadding = this.removePageBorders ? 0 : _ui_utils.VERTICAL_PADDING;
         widthScale = (this.container.clientWidth - hPadding) / width / _ui_utils.CSS_UNITS;
         heightScale = (this.container.clientHeight - vPadding) / height / _ui_utils.CSS_UNITS;
         scale = Math.min(Math.abs(widthScale), Math.abs(heightScale));
         break;
       default:
-        console.error(`PDFViewer.scrollPageIntoView: "${dest[1].name}" ` + 'is not a valid destination type.');
+        console.error(`${this._name}.scrollPageIntoView: "${dest[1].name}" ` + 'is not a valid destination type.');
         return;
     }
     if (scale && scale !== this._currentScale) {
       this.currentScaleValue = scale;
     } else if (this._currentScale === _ui_utils.UNKNOWN_SCALE) {
       this.currentScaleValue = _ui_utils.DEFAULT_SCALE_VALUE;
     }
     if (scale === 'page-fit' && !dest[4]) {
-      (0, _ui_utils.scrollIntoView)(pageView.div);
+      this._scrollIntoView({
+        pageDiv: pageView.div,
+        pageNumber
+      });
       return;
     }
     let boundingRect = [pageView.viewport.convertToViewportPoint(x, y), pageView.viewport.convertToViewportPoint(x + width, y + height)];
     let left = Math.min(boundingRect[0][0], boundingRect[1][0]);
     let top = Math.min(boundingRect[0][1], boundingRect[1][1]);
     if (!allowNegativeOffset) {
       left = Math.max(left, 0);
       top = Math.max(top, 0);
     }
-    (0, _ui_utils.scrollIntoView)(pageView.div, {
-      left,
-      top
-    });
+    this._scrollIntoView({
+      pageDiv: pageView.div,
+      pageSpot: {
+        left,
+        top
+      },
+      pageNumber
+    });
+  }
+  _resizeBuffer(numVisiblePages) {
+    let suggestedCacheSize = Math.max(DEFAULT_CACHE_SIZE, 2 * numVisiblePages + 1);
+    this._buffer.resize(suggestedCacheSize);
   }
   _updateLocation(firstPage) {
     let currentScale = this._currentScale;
     let currentScaleValue = this._currentScaleValue;
     let normalizedScaleValue = parseFloat(currentScaleValue) === currentScale ? Math.round(currentScale * 10000) / 100 : currentScaleValue;
     let pageNumber = firstPage.id;
     let pdfOpenParams = '#page=' + pageNumber;
     pdfOpenParams += '&zoom=' + normalizedScaleValue;
@@ -4274,79 +6280,35 @@ class PDFViewer {
       scale: normalizedScaleValue,
       top: intTop,
       left: intLeft,
       rotation: this._pagesRotation,
       pdfOpenParams
     };
   }
   update() {
-    let visible = this._getVisiblePages();
-    let visiblePages = visible.views;
-    if (visiblePages.length === 0) {
-      return;
-    }
-    let suggestedCacheSize = Math.max(DEFAULT_CACHE_SIZE, 2 * visiblePages.length + 1);
-    this._buffer.resize(suggestedCacheSize);
-    this.renderingQueue.renderHighestPriority(visible);
-    let currentId = this._currentPageNumber;
-    let firstPage = visible.first;
-    let stillFullyVisible = false;
-    for (let i = 0, ii = visiblePages.length; i < ii; ++i) {
-      let page = visiblePages[i];
-      if (page.percent < 100) {
-        break;
-      }
-      if (page.id === currentId) {
-        stillFullyVisible = true;
-        break;
-      }
-    }
-    if (!stillFullyVisible) {
-      currentId = visiblePages[0].id;
-    }
-    if (!this.isInPresentationMode) {
-      this._setCurrentPageNumber(currentId);
-    }
-    this._updateLocation(firstPage);
-    this.eventBus.dispatch('updateviewarea', {
-      source: this,
-      location: this._location
-    });
+    throw new Error('Not implemented: update');
   }
   containsElement(element) {
     return this.container.contains(element);
   }
   focus() {
     this.container.focus();
   }
   get isInPresentationMode() {
-    return this.presentationModeState === PresentationModeState.FULLSCREEN;
+    return this.presentationModeState === _ui_utils.PresentationModeState.FULLSCREEN;
   }
   get isChangingPresentationMode() {
-    return this.presentationModeState === PresentationModeState.CHANGING;
+    return this.presentationModeState === _ui_utils.PresentationModeState.CHANGING;
   }
   get isHorizontalScrollbarEnabled() {
     return this.isInPresentationMode ? false : this.container.scrollWidth > this.container.clientWidth;
   }
   _getVisiblePages() {
-    if (!this.isInPresentationMode) {
-      return (0, _ui_utils.getVisibleElements)(this.container, this._pages, true);
-    }
-    let visible = [];
-    let currentPage = this._pages[this._currentPageNumber - 1];
-    visible.push({
-      id: currentPage.id,
-      view: currentPage
-    });
-    return {
-      first: currentPage,
-      last: currentPage,
-      views: visible
-    };
+    throw new Error('Not implemented: _getVisiblePages');
   }
   cleanup() {
     for (let i = 0, ii = this._pages.length; i < ii; i++) {
       if (this._pages[i] && this._pages[i].renderingState !== _pdf_rendering_queue.RenderingStates.FINISHED) {
         this._pages[i].reset();
       }
     }
   }
@@ -4447,21 +6409,20 @@ class PDFViewer {
       return {
         width: size.height,
         height: size.width,
         rotation: (size.rotation + 90) % 360
       };
     });
   }
 }
-exports.PresentationModeState = PresentationModeState;
-exports.PDFViewer = PDFViewer;
+exports.BaseViewer = BaseViewer;
 
 /***/ }),
-/* 12 */
+/* 23 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -4526,17 +6487,17 @@ class DefaultAnnotationLayerFactory {
       l10n
     });
   }
 }
 exports.AnnotationLayerBuilder = AnnotationLayerBuilder;
 exports.DefaultAnnotationLayerFactory = DefaultAnnotationLayerFactory;
 
 /***/ }),
-/* 13 */
+/* 24 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -4970,17 +6931,17 @@ class PDFPageView {
     } else {
       this.div.removeAttribute('data-page-label');
     }
   }
 }
 exports.PDFPageView = PDFPageView;
 
 /***/ }),
-/* 14 */
+/* 25 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -5239,1925 +7200,17 @@ class DefaultTextLayerFactory {
       enhanceTextSelection
     });
   }
 }
 exports.TextLayerBuilder = TextLayerBuilder;
 exports.DefaultTextLayerFactory = DefaultTextLayerFactory;
 
 /***/ }),
-/* 15 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-class OverlayManager {
-  constructor() {
-    this._overlays = {};
-    this._active = null;
-    this._keyDownBound = this._keyDown.bind(this);
-  }
-  get active() {
-    return this._active;
-  }
-  register(name, element, callerCloseMethod = null, canForceClose = false) {
-    return new Promise(resolve => {
-      let container;
-      if (!name || !element || !(container = element.parentNode)) {
-        throw new Error('Not enough parameters.');
-      } else if (this._overlays[name]) {
-        throw new Error('The overlay is already registered.');
-      }
-      this._overlays[name] = {
-        element,
-        container,
-        callerCloseMethod,
-        canForceClose
-      };
-      resolve();
-    });
-  }
-  unregister(name) {
-    return new Promise(resolve => {
-      if (!this._overlays[name]) {
-        throw new Error('The overlay does not exist.');
-      } else if (this._active === name) {
-        throw new Error('The overlay cannot be removed while it is active.');
-      }
-      delete this._overlays[name];
-      resolve();
-    });
-  }
-  open(name) {
-    return new Promise(resolve => {
-      if (!this._overlays[name]) {
-        throw new Error('The overlay does not exist.');
-      } else if (this._active) {
-        if (this._overlays[name].canForceClose) {
-          this._closeThroughCaller();
-        } else if (this._active === name) {
-          throw new Error('The overlay is already active.');
-        } else {
-          throw new Error('Another overlay is currently active.');
-        }
-      }
-      this._active = name;
-      this._overlays[this._active].element.classList.remove('hidden');
-      this._overlays[this._active].container.classList.remove('hidden');
-      window.addEventListener('keydown', this._keyDownBound);
-      resolve();
-    });
-  }
-  close(name) {
-    return new Promise(resolve => {
-      if (!this._overlays[name]) {
-        throw new Error('The overlay does not exist.');
-      } else if (!this._active) {
-        throw new Error('The overlay is currently not active.');
-      } else if (this._active !== name) {
-        throw new Error('Another overlay is currently active.');
-      }
-      this._overlays[this._active].container.classList.add('hidden');
-      this._overlays[this._active].element.classList.add('hidden');
-      this._active = null;
-      window.removeEventListener('keydown', this._keyDownBound);
-      resolve();
-    });
-  }
-  _keyDown(evt) {
-    if (this._active && evt.keyCode === 27) {
-      this._closeThroughCaller();
-      evt.preventDefault();
-    }
-  }
-  _closeThroughCaller() {
-    if (this._overlays[this._active].callerCloseMethod) {
-      this._overlays[this._active].callerCloseMethod();
-    }
-    if (this._active) {
-      this.close(this._active);
-    }
-  }
-}
-exports.OverlayManager = OverlayManager;
-
-/***/ }),
-/* 16 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.PasswordPrompt = undefined;
-
-var _ui_utils = __webpack_require__(0);
-
-var _pdfjsLib = __webpack_require__(1);
-
-class PasswordPrompt {
-  constructor(options, overlayManager, l10n = _ui_utils.NullL10n) {
-    this.overlayName = options.overlayName;
-    this.container = options.container;
-    this.label = options.label;
-    this.input = options.input;
-    this.submitButton = options.submitButton;
-    this.cancelButton = options.cancelButton;
-    this.overlayManager = overlayManager;
-    this.l10n = l10n;
-    this.updateCallback = null;
-    this.reason = null;
-    this.submitButton.addEventListener('click', this.verify.bind(this));
-    this.cancelButton.addEventListener('click', this.close.bind(this));
-    this.input.addEventListener('keydown', e => {
-      if (e.keyCode === 13) {
-        this.verify();
-      }
-    });
-    this.overlayManager.register(this.overlayName, this.container, this.close.bind(this), true);
-  }
-  open() {
-    this.overlayManager.open(this.overlayName).then(() => {
-      this.input.focus();
-      let promptString;
-      if (this.reason === _pdfjsLib.PasswordResponses.INCORRECT_PASSWORD) {
-        promptString = this.l10n.get('password_invalid', null, 'Invalid password. Please try again.');
-      } else {
-        promptString = this.l10n.get('password_label', null, 'Enter the password to open this PDF file.');
-      }
-      promptString.then(msg => {
-        this.label.textContent = msg;
-      });
-    });
-  }
-  close() {
-    this.overlayManager.close(this.overlayName).then(() => {
-      this.input.value = '';
-    });
-  }
-  verify() {
-    let password = this.input.value;
-    if (password && password.length > 0) {
-      this.close();
-      return this.updateCallback(password);
-    }
-  }
-  setUpdateCallback(updateCallback, reason) {
-    this.updateCallback = updateCallback;
-    this.reason = reason;
-  }
-}
-exports.PasswordPrompt = PasswordPrompt;
-
-/***/ }),
-/* 17 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.PDFAttachmentViewer = undefined;
-
-var _pdfjsLib = __webpack_require__(1);
-
-class PDFAttachmentViewer {
-  constructor({ container, eventBus, downloadManager }) {
-    this.container = container;
-    this.eventBus = eventBus;
-    this.downloadManager = downloadManager;
-    this.reset();
-    this.eventBus.on('fileattachmentannotation', this._appendAttachment.bind(this));
-  }
-  reset(keepRenderedCapability = false) {
-    this.attachments = null;
-    this.container.textContent = '';
-    if (!keepRenderedCapability) {
-      this._renderedCapability = (0, _pdfjsLib.createPromiseCapability)();
-    }
-  }
-  _dispatchEvent(attachmentsCount) {
-    this._renderedCapability.resolve();
-    this.eventBus.dispatch('attachmentsloaded', {
-      source: this,
-      attachmentsCount
-    });
-  }
-  _bindPdfLink(button, content, filename) {
-    if (_pdfjsLib.PDFJS.disableCreateObjectURL) {
-      throw new Error('bindPdfLink: ' + 'Unsupported "PDFJS.disableCreateObjectURL" value.');
-    }
-    let blobUrl;
-    button.onclick = function () {
-      if (!blobUrl) {
-        blobUrl = (0, _pdfjsLib.createObjectURL)(content, 'application/pdf');
-      }
-      let viewerUrl;
-      viewerUrl = blobUrl + '?' + encodeURIComponent(filename);
-      window.open(viewerUrl);
-      return false;
-    };
-  }
-  _bindLink(button, content, filename) {
-    button.onclick = () => {
-      this.downloadManager.downloadData(content, filename, '');
-      return false;
-    };
-  }
-  render({ attachments, keepRenderedCapability = false }) {
-    let attachmentsCount = 0;
-    if (this.attachments) {
-      this.reset(keepRenderedCapability === true);
-    }
-    this.attachments = attachments || null;
-    if (!attachments) {
-      this._dispatchEvent(attachmentsCount);
-      return;
-    }
-    let names = Object.keys(attachments).sort(function (a, b) {
-      return a.toLowerCase().localeCompare(b.toLowerCase());
-    });
-    attachmentsCount = names.length;
-    for (let i = 0; i < attachmentsCount; i++) {
-      let item = attachments[names[i]];
-      let filename = (0, _pdfjsLib.removeNullCharacters)((0, _pdfjsLib.getFilenameFromUrl)(item.filename));
-      let div = document.createElement('div');
-      div.className = 'attachmentsItem';
-      let button = document.createElement('button');
-      button.textContent = filename;
-      if (/\.pdf$/i.test(filename) && !_pdfjsLib.PDFJS.disableCreateObjectURL) {
-        this._bindPdfLink(button, item.content, filename);
-      } else {
-        this._bindLink(button, item.content, filename);
-      }
-      div.appendChild(button);
-      this.container.appendChild(div);
-    }
-    this._dispatchEvent(attachmentsCount);
-  }
-  _appendAttachment({ id, filename, content }) {
-    this._renderedCapability.promise.then(() => {
-      let attachments = this.attachments;
-      if (!attachments) {
-        attachments = Object.create(null);
-      } else {
-        for (let name in attachments) {
-          if (id === name) {
-            return;
-          }
-        }
-      }
-      attachments[id] = {
-        filename,
-        content
-      };
-      this.render({
-        attachments,
-        keepRenderedCapability: true
-      });
-    });
-  }
-}
-exports.PDFAttachmentViewer = PDFAttachmentViewer;
-
-/***/ }),
-/* 18 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.PDFDocumentProperties = undefined;
-
-var _ui_utils = __webpack_require__(0);
-
-var _pdfjsLib = __webpack_require__(1);
-
-const DEFAULT_FIELD_CONTENT = '-';
-class PDFDocumentProperties {
-  constructor({ overlayName, fields, container, closeButton }, overlayManager, l10n = _ui_utils.NullL10n) {
-    this.overlayName = overlayName;
-    this.fields = fields;
-    this.container = container;
-    this.overlayManager = overlayManager;
-    this.l10n = l10n;
-    this._reset();
-    if (closeButton) {
-      closeButton.addEventListener('click', this.close.bind(this));
-    }
-    this.overlayManager.register(this.overlayName, this.container, this.close.bind(this));
-  }
-  open() {
-    let freezeFieldData = data => {
-      Object.defineProperty(this, 'fieldData', {
-        value: Object.freeze(data),
-        writable: false,
-        enumerable: true,
-        configurable: true
-      });
-    };
-    Promise.all([this.overlayManager.open(this.overlayName), this._dataAvailableCapability.promise]).then(() => {
-      if (this.fieldData) {
-        this._updateUI();
-        return;
-      }
-      this.pdfDocument.getMetadata().then(({ info, metadata }) => {
-        return Promise.all([info, metadata, this._parseFileSize(this.maybeFileSize), this._parseDate(info.CreationDate), this._parseDate(info.ModDate)]);
-      }).then(([info, metadata, fileSize, creationDate, modificationDate]) => {
-        freezeFieldData({
-          'fileName': (0, _ui_utils.getPDFFileNameFromURL)(this.url),
-          'fileSize': fileSize,
-          'title': info.Title,
-          'author': info.Author,
-          'subject': info.Subject,
-          'keywords': info.Keywords,
-          'creationDate': creationDate,
-          'modificationDate': modificationDate,
-          'creator': info.Creator,
-          'producer': info.Producer,
-          'version': info.PDFFormatVersion,
-          'pageCount': this.pdfDocument.numPages
-        });
-        this._updateUI();
-        return this.pdfDocument.getDownloadInfo();
-      }).then(({ length }) => {
-        return this._parseFileSize(length);
-      }).then(fileSize => {
-        let data = (0, _ui_utils.cloneObj)(this.fieldData);
-        data['fileSize'] = fileSize;
-        freezeFieldData(data);
-        this._updateUI();
-      });
-    });
-  }
-  close() {
-    this.overlayManager.close(this.overlayName);
-  }
-  setDocument(pdfDocument, url) {
-    if (this.pdfDocument) {
-      this._reset();
-      this._updateUI(true);
-    }
-    if (!pdfDocument) {
-      return;
-    }
-    this.pdfDocument = pdfDocument;
-    this.url = url;
-    this._dataAvailableCapability.resolve();
-  }
-  setFileSize(fileSize) {
-    if (typeof fileSize === 'number' && fileSize > 0) {
-      this.maybeFileSize = fileSize;
-    }
-  }
-  _reset() {
-    this.pdfDocument = null;
-    this.url = null;
-    this.maybeFileSize = 0;
-    delete this.fieldData;
-    this._dataAvailableCapability = (0, _pdfjsLib.createPromiseCapability)();
-  }
-  _updateUI(reset = false) {
-    if (reset || !this.fieldData) {
-      for (let id in this.fields) {
-        this.fields[id].textContent = DEFAULT_FIELD_CONTENT;
-      }
-      return;
-    }
-    if (this.overlayManager.active !== this.overlayName) {
-      return;
-    }
-    for (let id in this.fields) {
-      let content = this.fieldData[id];
-      this.fields[id].textContent = content || content === 0 ? content : DEFAULT_FIELD_CONTENT;
-    }
-  }
-  _parseFileSize(fileSize = 0) {
-    let kb = fileSize / 1024;
-    if (!kb) {
-      return Promise.resolve(undefined);
-    } else if (kb < 1024) {
-      return this.l10n.get('document_properties_kb', {
-        size_kb: (+kb.toPrecision(3)).toLocaleString(),
-        size_b: fileSize.toLocaleString()
-      }, '{{size_kb}} KB ({{size_b}} bytes)');
-    }
-    return this.l10n.get('document_properties_mb', {
-      size_mb: (+(kb / 1024).toPrecision(3)).toLocaleString(),
-      size_b: fileSize.toLocaleString()
-    }, '{{size_mb}} MB ({{size_b}} bytes)');
-  }
-  _parseDate(inputDate) {
-    if (!inputDate) {
-      return;
-    }
-    let dateToParse = inputDate;
-    if (dateToParse.substring(0, 2) === 'D:') {
-      dateToParse = dateToParse.substring(2);
-    }
-    let year = parseInt(dateToParse.substring(0, 4), 10);
-    let month = parseInt(dateToParse.substring(4, 6), 10) - 1;
-    let day = parseInt(dateToParse.substring(6, 8), 10);
-    let hours = parseInt(dateToParse.substring(8, 10), 10);
-    let minutes = parseInt(dateToParse.substring(10, 12), 10);
-    let seconds = parseInt(dateToParse.substring(12, 14), 10);
-    let utRel = dateToParse.substring(14, 15);
-    let offsetHours = parseInt(dateToParse.substring(15, 17), 10);
-    let offsetMinutes = parseInt(dateToParse.substring(18, 20), 10);
-    if (utRel === '-') {
-      hours += offsetHours;
-      minutes += offsetMinutes;
-    } else if (utRel === '+') {
-      hours -= offsetHours;
-      minutes -= offsetMinutes;
-    }
-    let date = new Date(Date.UTC(year, month, day, hours, minutes, seconds));
-    let dateString = date.toLocaleDateString();
-    let timeString = date.toLocaleTimeString();
-    return this.l10n.get('document_properties_date_string', {
-      date: dateString,
-      time: timeString
-    }, '{{date}}, {{time}}');
-  }
-}
-exports.PDFDocumentProperties = PDFDocumentProperties;
-
-/***/ }),
-/* 19 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.PDFFindBar = undefined;
-
-var _pdf_find_controller = __webpack_require__(7);
-
-var _ui_utils = __webpack_require__(0);
-
-class PDFFindBar {
-  constructor(options, l10n = _ui_utils.NullL10n) {
-    this.opened = false;
-    this.bar = options.bar || null;
-    this.toggleButton = options.toggleButton || null;
-    this.findField = options.findField || null;
-    this.highlightAll = options.highlightAllCheckbox || null;
-    this.caseSensitive = options.caseSensitiveCheckbox || null;
-    this.findMsg = options.findMsg || null;
-    this.findResultsCount = options.findResultsCount || null;
-    this.findStatusIcon = options.findStatusIcon || null;
-    this.findPreviousButton = options.findPreviousButton || null;
-    this.findNextButton = options.findNextButton || null;
-    this.findController = options.findController || null;
-    this.eventBus = options.eventBus;
-    this.l10n = l10n;
-    if (this.findController === null) {
-      throw new Error('PDFFindBar cannot be used without a ' + 'PDFFindController instance.');
-    }
-    this.toggleButton.addEventListener('click', () => {
-      this.toggle();
-    });
-    this.findField.addEventListener('input', () => {
-      this.dispatchEvent('');
-    });
-    this.bar.addEventListener('keydown', e => {
-      switch (e.keyCode) {
-        case 13:
-          if (e.target === this.findField) {
-            this.dispatchEvent('again', e.shiftKey);
-          }
-          break;
-        case 27:
-          this.close();
-          break;
-      }
-    });
-    this.findPreviousButton.addEventListener('click', () => {
-      this.dispatchEvent('again', true);
-    });
-    this.findNextButton.addEventListener('click', () => {
-      this.dispatchEvent('again', false);
-    });
-    this.highlightAll.addEventListener('click', () => {
-      this.dispatchEvent('highlightallchange');
-    });
-    this.caseSensitive.addEventListener('click', () => {
-      this.dispatchEvent('casesensitivitychange');
-    });
-    this.eventBus.on('resize', this._adjustWidth.bind(this));
-  }
-  reset() {
-    this.updateUIState();
-  }
-  dispatchEvent(type, findPrev) {
-    this.eventBus.dispatch('find', {
-      source: this,
-      type,
-      query: this.findField.value,
-      caseSensitive: this.caseSensitive.checked,
-      phraseSearch: true,
-      highlightAll: this.highlightAll.checked,
-      findPrevious: findPrev
-    });
-  }
-  updateUIState(state, previous, matchCount) {
-    let notFound = false;
-    let findMsg = '';
-    let status = '';
-    switch (state) {
-      case _pdf_find_controller.FindState.FOUND:
-        break;
-      case _pdf_find_controller.FindState.PENDING:
-        status = 'pending';
-        break;
-      case _pdf_find_controller.FindState.NOT_FOUND:
-        findMsg = this.l10n.get('find_not_found', null, 'Phrase not found');
-        notFound = true;
-        break;
-      case _pdf_find_controller.FindState.WRAPPED:
-        if (previous) {
-          findMsg = this.l10n.get('find_reached_top', null, 'Reached top of document, continued from bottom');
-        } else {
-          findMsg = this.l10n.get('find_reached_bottom', null, 'Reached end of document, continued from top');
-        }
-        break;
-    }
-    if (notFound) {
-      this.findField.classList.add('notFound');
-    } else {
-      this.findField.classList.remove('notFound');
-    }
-    this.findField.setAttribute('data-status', status);
-    Promise.resolve(findMsg).then(msg => {
-      this.findMsg.textContent = msg;
-      this._adjustWidth();
-    });
-    this.updateResultsCount(matchCount);
-  }
-  updateResultsCount(matchCount) {
-    if (!this.findResultsCount) {
-      return;
-    }
-    if (!matchCount) {
-      this.findResultsCount.classList.add('hidden');
-      this.findResultsCount.textContent = '';
-    } else {
-      this.findResultsCount.textContent = matchCount.toLocaleString();
-      this.findResultsCount.classList.remove('hidden');
-    }
-    this._adjustWidth();
-  }
-  open() {
-    if (!this.opened) {
-      this.opened = true;
-      this.toggleButton.classList.add('toggled');
-      this.bar.classList.remove('hidden');
-    }
-    this.findField.select();
-    this.findField.focus();
-    this._adjustWidth();
-  }
-  close() {
-    if (!this.opened) {
-      return;
-    }
-    this.opened = false;
-    this.toggleButton.classList.remove('toggled');
-    this.bar.classList.add('hidden');
-    this.findController.active = false;
-  }
-  toggle() {
-    if (this.opened) {
-      this.close();
-    } else {
-      this.open();
-    }
-  }
-  _adjustWidth() {
-    if (!this.opened) {
-      return;
-    }
-    this.bar.classList.remove('wrapContainers');
-    let findbarHeight = this.bar.clientHeight;
-    let inputContainerHeight = this.bar.firstElementChild.clientHeight;
-    if (findbarHeight > inputContainerHeight) {
-      this.bar.classList.add('wrapContainers');
-    }
-  }
-}
-exports.PDFFindBar = PDFFindBar;
-
-/***/ }),
-/* 20 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.isDestsEqual = exports.PDFHistory = undefined;
-
-var _ui_utils = __webpack_require__(0);
-
-var _dom_events = __webpack_require__(2);
-
-const HASH_CHANGE_TIMEOUT = 1000;
-const POSITION_UPDATED_THRESHOLD = 50;
-const UPDATE_VIEWAREA_TIMEOUT = 1000;
-function getCurrentHash() {
-  return document.location.hash;
-}
-function parseCurrentHash(linkService) {
-  let hash = unescape(getCurrentHash()).substring(1);
-  let params = (0, _ui_utils.parseQueryString)(hash);
-  let page = params.page | 0;
-  if (!(Number.isInteger(page) && page > 0 && page <= linkService.pagesCount)) {
-    page = null;
-  }
-  return {
-    hash,
-    page,
-    rotation: linkService.rotation
-  };
-}
-class PDFHistory {
-  constructor({ linkService, eventBus }) {
-    this.linkService = linkService;
-    this.eventBus = eventBus || (0, _dom_events.getGlobalEventBus)();
-    this.initialized = false;
-    this.initialBookmark = null;
-    this.initialRotation = null;
-    this._boundEvents = Object.create(null);
-    this._isViewerInPresentationMode = false;
-    this._isPagesLoaded = false;
-    this.eventBus.on('presentationmodechanged', evt => {
-      this._isViewerInPresentationMode = evt.active || evt.switchInProgress;
-    });
-    this.eventBus.on('pagesloaded', evt => {
-      this._isPagesLoaded = !!evt.pagesCount;
-    });
-  }
-  initialize(fingerprint, resetHistory = false) {
-    if (!fingerprint || typeof fingerprint !== 'string') {
-      console.error('PDFHistory.initialize: The "fingerprint" must be a non-empty string.');
-      return;
-    }
-    let reInitialized = this.initialized && this.fingerprint !== fingerprint;
-    this.fingerprint = fingerprint;
-    if (!this.initialized) {
-      this._bindEvents();
-    }
-    let state = window.history.state;
-    this.initialized = true;
-    this.initialBookmark = null;
-    this.initialRotation = null;
-    this._popStateInProgress = false;
-    this._blockHashChange = 0;
-    this._currentHash = getCurrentHash();
-    this._numPositionUpdates = 0;
-    this._currentUid = this._uid = 0;
-    this._destination = null;
-    this._position = null;
-    if (!this._isValidState(state) || resetHistory) {
-      let { hash, page, rotation } = parseCurrentHash(this.linkService);
-      if (!hash || reInitialized || resetHistory) {
-        this._pushOrReplaceState(null, true);
-        return;
-      }
-      this._pushOrReplaceState({
-        hash,
-        page,
-        rotation
-      }, true);
-      return;
-    }
-    let destination = state.destination;
-    this._updateInternalState(destination, state.uid, true);
-    if (destination.rotation !== undefined) {
-      this.initialRotation = destination.rotation;
-    }
-    if (destination.dest) {
-      this.initialBookmark = JSON.stringify(destination.dest);
-      this._destination.page = null;
-    } else if (destination.hash) {
-      this.initialBookmark = destination.hash;
-    } else if (destination.page) {
-      this.initialBookmark = `page=${destination.page}`;
-    }
-  }
-  push({ namedDest, explicitDest, pageNumber }) {
-    if (!this.initialized) {
-      return;
-    }
-    if (namedDest && typeof namedDest !== 'string' || !(explicitDest instanceof Array) || !(Number.isInteger(pageNumber) && pageNumber > 0 && pageNumber <= this.linkService.pagesCount)) {
-      console.error('PDFHistory.push: Invalid parameters.');
-      return;
-    }
-    let hash = namedDest || JSON.stringify(explicitDest);
-    if (!hash) {
-      return;
-    }
-    let forceReplace = false;
-    if (this._destination && (this._destination.hash === hash || isDestsEqual(this._destination.dest, explicitDest))) {
-      if (this._destination.page) {
-        return;
-      }
-      forceReplace = true;
-    }
-    if (this._popStateInProgress && !forceReplace) {
-      return;
-    }
-    this._pushOrReplaceState({
-      dest: explicitDest,
-      hash,
-      page: pageNumber,
-      rotation: this.linkService.rotation
-    }, forceReplace);
-    if (!this._popStateInProgress) {
-      this._popStateInProgress = true;
-      Promise.resolve().then(() => {
-        this._popStateInProgress = false;
-      });
-    }
-  }
-  pushCurrentPosition() {
-    if (!this.initialized || this._popStateInProgress) {
-      return;
-    }
-    this._tryPushCurrentPosition();
-  }
-  back() {
-    if (!this.initialized || this._popStateInProgress) {
-      return;
-    }
-    let state = window.history.state;
-    if (this._isValidState(state) && state.uid > 0) {
-      window.history.back();
-    }
-  }
-  forward() {
-    if (!this.initialized || this._popStateInProgress) {
-      return;
-    }
-    let state = window.history.state;
-    if (this._isValidState(state) && state.uid < this._uid - 1) {
-      window.history.forward();
-    }
-  }
-  get popStateInProgress() {
-    return this.initialized && (this._popStateInProgress || this._blockHashChange > 0);
-  }
-  _pushOrReplaceState(destination, forceReplace = false) {
-    let shouldReplace = forceReplace || !this._destination;
-    let newState = {
-      fingerprint: this.fingerprint,
-      uid: shouldReplace ? this._currentUid : this._uid,
-      destination
-    };
-    this._updateInternalState(destination, newState.uid);
-    if (shouldReplace) {
-      window.history.replaceState(newState, '');
-    } else {
-      window.history.pushState(newState, '');
-    }
-  }
-  _tryPushCurrentPosition(temporary = false) {
-    if (!this._position) {
-      return;
-    }
-    let position = this._position;
-    if (temporary) {
-      position = (0, _ui_utils.cloneObj)(this._position);
-      position.temporary = true;
-    }
-    if (!this._destination) {
-      this._pushOrReplaceState(position);
-      return;
-    }
-    if (this._destination.temporary) {
-      this._pushOrReplaceState(position, true);
-      return;
-    }
-    if (this._destination.hash === position.hash) {
-      return;
-    }
-    if (!this._destination.page && (POSITION_UPDATED_THRESHOLD <= 0 || this._numPositionUpdates <= POSITION_UPDATED_THRESHOLD)) {
-      return;
-    }
-    let forceReplace = false;
-    if (this._destination.page === position.first || this._destination.page === position.page) {
-      if (this._destination.dest || !this._destination.first) {
-        return;
-      }
-      forceReplace = true;
-    }
-    this._pushOrReplaceState(position, forceReplace);
-  }
-  _isValidState(state) {
-    if (!state) {
-      return false;
-    }
-    if (state.fingerprint !== this.fingerprint) {
-      return false;
-    }
-    if (!Number.isInteger(state.uid) || state.uid < 0) {
-      return false;
-    }
-    if (state.destination === null || typeof state.destination !== 'object') {
-      return false;
-    }
-    return true;
-  }
-  _updateInternalState(destination, uid, removeTemporary = false) {
-    if (this._updateViewareaTimeout) {
-      clearTimeout(this._updateViewareaTimeout);
-      this._updateViewareaTimeout = null;
-    }
-    if (removeTemporary && destination && destination.temporary) {
-      delete destination.temporary;
-    }
-    this._destination = destination;
-    this._currentUid = uid;
-    this._uid = this._currentUid + 1;
-    this._numPositionUpdates = 0;
-  }
-  _updateViewarea({ location }) {
-    if (this._updateViewareaTimeout) {
-      clearTimeout(this._updateViewareaTimeout);
-      this._updateViewareaTimeout = null;
-    }
-    this._position = {
-      hash: this._isViewerInPresentationMode ? `page=${location.pageNumber}` : location.pdfOpenParams.substring(1),
-      page: this.linkService.page,
-      first: location.pageNumber,
-      rotation: location.rotation
-    };
-    if (this._popStateInProgress) {
-      return;
-    }
-    if (POSITION_UPDATED_THRESHOLD > 0 && this._isPagesLoaded && this._destination && !this._destination.page) {
-      this._numPositionUpdates++;
-    }
-    if (UPDATE_VIEWAREA_TIMEOUT > 0) {
-      this._updateViewareaTimeout = setTimeout(() => {
-        if (!this._popStateInProgress) {
-          this._tryPushCurrentPosition(true);
-        }
-        this._updateViewareaTimeout = null;
-      }, UPDATE_VIEWAREA_TIMEOUT);
-    }
-  }
-  _popState({ state }) {
-    let newHash = getCurrentHash(),
-        hashChanged = this._currentHash !== newHash;
-    this._currentHash = newHash;
-    if (!state || false) {
-      this._currentUid = this._uid;
-      let { hash, page, rotation } = parseCurrentHash(this.linkService);
-      this._pushOrReplaceState({
-        hash,
-        page,
-        rotation
-      }, true);
-      return;
-    }
-    if (!this._isValidState(state)) {
-      return;
-    }
-    this._popStateInProgress = true;
-    if (hashChanged) {
-      this._blockHashChange++;
-      (0, _ui_utils.waitOnEventOrTimeout)({
-        target: window,
-        name: 'hashchange',
-        delay: HASH_CHANGE_TIMEOUT
-      }).then(() => {
-        this._blockHashChange--;
-      });
-    }
-    let destination = state.destination;
-    this._updateInternalState(destination, state.uid, true);
-    if ((0, _ui_utils.isValidRotation)(destination.rotation)) {
-      this.linkService.rotation = destination.rotation;
-    }
-    if (destination.dest) {
-      this.linkService.navigateTo(destination.dest);
-    } else if (destination.hash) {
-      this.linkService.setHash(destination.hash);
-    } else if (destination.page) {
-      this.linkService.page = destination.page;
-    }
-    Promise.resolve().then(() => {
-      this._popStateInProgress = false;
-    });
-  }
-  _bindEvents() {
-    let { _boundEvents, eventBus } = this;
-    _boundEvents.updateViewarea = this._updateViewarea.bind(this);
-    _boundEvents.popState = this._popState.bind(this);
-    _boundEvents.pageHide = evt => {
-      if (!this._destination) {
-        this._tryPushCurrentPosition();
-      }
-    };
-    eventBus.on('updateviewarea', _boundEvents.updateViewarea);
-    window.addEventListener('popstate', _boundEvents.popState);
-    window.addEventListener('pagehide', _boundEvents.pageHide);
-  }
-}
-function isDestsEqual(firstDest, secondDest) {
-  function isEntryEqual(first, second) {
-    if (typeof first !== typeof second) {
-      return false;
-    }
-    if (first instanceof Array || second instanceof Array) {
-      return false;
-    }
-    if (first !== null && typeof first === 'object' && second !== null) {
-      if (Object.keys(first).length !== Object.keys(second).length) {
-        return false;
-      }
-      for (var key in first) {
-        if (!isEntryEqual(first[key], second[key])) {
-          return false;
-        }
-      }
-      return true;
-    }
-    return first === second || Number.isNaN(first) && Number.isNaN(second);
-  }
-  if (!(firstDest instanceof Array && secondDest instanceof Array)) {
-    return false;
-  }
-  if (firstDest.length !== secondDest.length) {
-    return false;
-  }
-  for (let i = 0, ii = firstDest.length; i < ii; i++) {
-    if (!isEntryEqual(firstDest[i], secondDest[i])) {
-      return false;
-    }
-  }
-  return true;
-}
-exports.PDFHistory = PDFHistory;
-exports.isDestsEqual = isDestsEqual;
-
-/***/ }),
-/* 21 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.PDFOutlineViewer = undefined;
-
-var _pdfjsLib = __webpack_require__(1);
-
-const DEFAULT_TITLE = '\u2013';
-class PDFOutlineViewer {
-  constructor({ container, linkService, eventBus }) {
-    this.container = container;
-    this.linkService = linkService;
-    this.eventBus = eventBus;
-    this.reset();
-  }
-  reset() {
-    this.outline = null;
-    this.lastToggleIsShow = true;
-    this.container.textContent = '';
-    this.container.classList.remove('outlineWithDeepNesting');
-  }
-  _dispatchEvent(outlineCount) {
-    this.eventBus.dispatch('outlineloaded', {
-      source: this,
-      outlineCount
-    });
-  }
-  _bindLink(element, item) {
-    if (item.url) {
-      (0, _pdfjsLib.addLinkAttributes)(element, {
-        url: item.url,
-        target: item.newWindow ? _pdfjsLib.PDFJS.LinkTarget.BLANK : undefined
-      });
-      return;
-    }
-    let destination = item.dest;
-    element.href = this.linkService.getDestinationHash(destination);
-    element.onclick = () => {
-      if (destination) {
-        this.linkService.navigateTo(destination);
-      }
-      return false;
-    };
-  }
-  _setStyles(element, item) {
-    let styleStr = '';
-    if (item.bold) {
-      styleStr += 'font-weight: bold;';
-    }
-    if (item.italic) {
-      styleStr += 'font-style: italic;';
-    }
-    if (styleStr) {
-      element.setAttribute('style', styleStr);
-    }
-  }
-  _addToggleButton(div) {
-    let toggler = document.createElement('div');
-    toggler.className = 'outlineItemToggler';
-    toggler.onclick = evt => {
-      evt.stopPropagation();
-      toggler.classList.toggle('outlineItemsHidden');
-      if (evt.shiftKey) {
-        let shouldShowAll = !toggler.classList.contains('outlineItemsHidden');
-        this._toggleOutlineItem(div, shouldShowAll);
-      }
-    };
-    div.insertBefore(toggler, div.firstChild);
-  }
-  _toggleOutlineItem(root, show) {
-    this.lastToggleIsShow = show;
-    let togglers = root.querySelectorAll('.outlineItemToggler');
-    for (let i = 0, ii = togglers.length; i < ii; ++i) {
-      togglers[i].classList[show ? 'remove' : 'add']('outlineItemsHidden');
-    }
-  }
-  toggleOutlineTree() {
-    if (!this.outline) {
-      return;
-    }
-    this._toggleOutlineItem(this.container, !this.lastToggleIsShow);
-  }
-  render({ outline }) {
-    let outlineCount = 0;
-    if (this.outline) {
-      this.reset();
-    }
-    this.outline = outline || null;
-    if (!outline) {
-      this._dispatchEvent(outlineCount);
-      return;
-    }
-    let fragment = document.createDocumentFragment();
-    let queue = [{
-      parent: fragment,
-      items: this.outline
-    }];
-    let hasAnyNesting = false;
-    while (queue.length > 0) {
-      let levelData = queue.shift();
-      for (let i = 0, len = levelData.items.length; i < len; i++) {
-        let item = levelData.items[i];
-        let div = document.createElement('div');
-        div.className = 'outlineItem';
-        let element = document.createElement('a');
-        this._bindLink(element, item);
-        this._setStyles(element, item);
-        element.textContent = (0, _pdfjsLib.removeNullCharacters)(item.title) || DEFAULT_TITLE;
-        div.appendChild(element);
-        if (item.items.length > 0) {
-          hasAnyNesting = true;
-          this._addToggleButton(div);
-          let itemsDiv = document.createElement('div');
-          itemsDiv.className = 'outlineItems';
-          div.appendChild(itemsDiv);
-          queue.push({
-            parent: itemsDiv,
-            items: item.items
-          });
-        }
-        levelData.parent.appendChild(div);
-        outlineCount++;
-      }
-    }
-    if (hasAnyNesting) {
-      this.container.classList.add('outlineWithDeepNesting');
-    }
-    this.container.appendChild(fragment);
-    this._dispatchEvent(outlineCount);
-  }
-}
-exports.PDFOutlineViewer = PDFOutlineViewer;
-
-/***/ }),
-/* 22 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.PDFPresentationMode = undefined;
-
-var _ui_utils = __webpack_require__(0);
-
-const DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS = 1500;
-const DELAY_BEFORE_HIDING_CONTROLS = 3000;
-const ACTIVE_SELECTOR = 'pdfPresentationMode';
-const CONTROLS_SELECTOR = 'pdfPresentationModeControls';
-const MOUSE_SCROLL_COOLDOWN_TIME = 50;
-const PAGE_SWITCH_THRESHOLD = 0.1;
-const SWIPE_MIN_DISTANCE_THRESHOLD = 50;
-const SWIPE_ANGLE_THRESHOLD = Math.PI / 6;
-class PDFPresentationMode {
-  constructor({ container, viewer = null, pdfViewer, eventBus, contextMenuItems = null }) {
-    this.container = container;
-    this.viewer = viewer || container.firstElementChild;
-    this.pdfViewer = pdfViewer;
-    this.eventBus = eventBus;
-    this.active = false;
-    this.args = null;
-    this.contextMenuOpen = false;
-    this.mouseScrollTimeStamp = 0;
-    this.mouseScrollDelta = 0;
-    this.touchSwipeState = null;
-    if (contextMenuItems) {
-      contextMenuItems.contextFirstPage.addEventListener('click', () => {
-        this.contextMenuOpen = false;
-        this.eventBus.dispatch('firstpage');
-      });
-      contextMenuItems.contextLastPage.addEventListener('click', () => {
-        this.contextMenuOpen = false;
-        this.eventBus.dispatch('lastpage');
-      });
-      contextMenuItems.contextPageRotateCw.addEventListener('click', () => {
-        this.contextMenuOpen = false;
-        this.eventBus.dispatch('rotatecw');
-      });
-      contextMenuItems.contextPageRotateCcw.addEventListener('click', () => {
-        this.contextMenuOpen = false;
-        this.eventBus.dispatch('rotateccw');
-      });
-    }
-  }
-  request() {
-    if (this.switchInProgress || this.active || !this.viewer.hasChildNodes()) {
-      return false;
-    }
-    this._addFullscreenChangeListeners();
-    this._setSwitchInProgress();
-    this._notifyStateChange();
-    if (this.container.requestFullscreen) {
-      this.container.requestFullscreen();
-    } else if (this.container.mozRequestFullScreen) {
-      this.container.mozRequestFullScreen();
-    } else if (this.container.webkitRequestFullscreen) {
-      this.container.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
-    } else if (this.container.msRequestFullscreen) {
-      this.container.msRequestFullscreen();
-    } else {
-      return false;
-    }
-    this.args = {
-      page: this.pdfViewer.currentPageNumber,
-      previousScale: this.pdfViewer.currentScaleValue
-    };
-    return true;
-  }
-  _mouseWheel(evt) {
-    if (!this.active) {
-      return;
-    }
-    evt.preventDefault();
-    let delta = (0, _ui_utils.normalizeWheelEventDelta)(evt);
-    let currentTime = new Date().getTime();
-    let storedTime = this.mouseScrollTimeStamp;
-    if (currentTime > storedTime && currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME) {
-      return;
-    }
-    if (this.mouseScrollDelta > 0 && delta < 0 || this.mouseScrollDelta < 0 && delta > 0) {
-      this._resetMouseScrollState();
-    }
-    this.mouseScrollDelta += delta;
-    if (Math.abs(this.mouseScrollDelta) >= PAGE_SWITCH_THRESHOLD) {
-      let totalDelta = this.mouseScrollDelta;
-      this._resetMouseScrollState();
-      let success = totalDelta > 0 ? this._goToPreviousPage() : this._goToNextPage();
-      if (success) {
-        this.mouseScrollTimeStamp = currentTime;
-      }
-    }
-  }
-  get isFullscreen() {
-    return !!(document.fullscreenElement || document.mozFullScreen || document.webkitIsFullScreen || document.msFullscreenElement);
-  }
-  _goToPreviousPage() {
-    let page = this.pdfViewer.currentPageNumber;
-    if (page <= 1) {
-      return false;
-    }
-    this.pdfViewer.currentPageNumber = page - 1;
-    return true;
-  }
-  _goToNextPage() {
-    let page = this.pdfViewer.currentPageNumber;
-    if (page >= this.pdfViewer.pagesCount) {
-      return false;
-    }
-    this.pdfViewer.currentPageNumber = page + 1;
-    return true;
-  }
-  _notifyStateChange() {
-    this.eventBus.dispatch('presentationmodechanged', {
-      source: this,
-      active: this.active,
-      switchInProgress: !!this.switchInProgress
-    });
-  }
-  _setSwitchInProgress() {
-    if (this.switchInProgress) {
-      clearTimeout(this.switchInProgress);
-    }
-    this.switchInProgress = setTimeout(() => {
-      this._removeFullscreenChangeListeners();
-      delete this.switchInProgress;
-      this._notifyStateChange();
-    }, DELAY_BEFORE_RESETTING_SWITCH_IN_PROGRESS);
-  }
-  _resetSwitchInProgress() {
-    if (this.switchInProgress) {
-      clearTimeout(this.switchInProgress);
-      delete this.switchInProgress;
-    }
-  }
-  _enter() {
-    this.active = true;
-    this._resetSwitchInProgress();
-    this._notifyStateChange();
-    this.container.classList.add(ACTIVE_SELECTOR);
-    setTimeout(() => {
-      this.pdfViewer.currentPageNumber = this.args.page;
-      this.pdfViewer.currentScaleValue = 'page-fit';
-    }, 0);
-    this._addWindowListeners();
-    this._showControls();
-    this.contextMenuOpen = false;
-    this.container.setAttribute('contextmenu', 'viewerContextMenu');
-    window.getSelection().removeAllRanges();
-  }
-  _exit() {
-    let page = this.pdfViewer.currentPageNumber;
-    this.container.classList.remove(ACTIVE_SELECTOR);
-    setTimeout(() => {
-      this.active = false;
-      this._removeFullscreenChangeListeners();
-      this._notifyStateChange();
-      this.pdfViewer.currentScaleValue = this.args.previousScale;
-      this.pdfViewer.currentPageNumber = page;
-      this.args = null;
-    }, 0);
-    this._removeWindowListeners();
-    this._hideControls();
-    this._resetMouseScrollState();
-    this.container.removeAttribute('contextmenu');
-    this.contextMenuOpen = false;
-  }
-  _mouseDown(evt) {
-    if (this.contextMenuOpen) {
-      this.contextMenuOpen = false;
-      evt.preventDefault();
-      return;
-    }
-    if (evt.button === 0) {
-      let isInternalLink = evt.target.href && evt.target.classList.contains('internalLink');
-      if (!isInternalLink) {
-        evt.preventDefault();
-        if (evt.shiftKey) {
-          this._goToPreviousPage();
-        } else {
-          this._goToNextPage();
-        }
-      }
-    }
-  }
-  _contextMenu() {
-    this.contextMenuOpen = true;
-  }
-  _showControls() {
-    if (this.controlsTimeout) {
-      clearTimeout(this.controlsTimeout);
-    } else {
-      this.container.classList.add(CONTROLS_SELECTOR);
-    }
-    this.controlsTimeout = setTimeout(() => {
-      this.container.classList.remove(CONTROLS_SELECTOR);
-      delete this.controlsTimeout;
-    }, DELAY_BEFORE_HIDING_CONTROLS);
-  }
-  _hideControls() {
-    if (!this.controlsTimeout) {
-      return;
-    }
-    clearTimeout(this.controlsTimeout);
-    this.container.classList.remove(CONTROLS_SELECTOR);
-    delete this.controlsTimeout;
-  }
-  _resetMouseScrollState() {
-    this.mouseScrollTimeStamp = 0;
-    this.mouseScrollDelta = 0;
-  }
-  _touchSwipe(evt) {
-    if (!this.active) {
-      return;
-    }
-    if (evt.touches.length > 1) {
-      this.touchSwipeState = null;
-      return;
-    }
-    switch (evt.type) {
-      case 'touchstart':
-        this.touchSwipeState = {
-          startX: evt.touches[0].pageX,
-          startY: evt.touches[0].pageY,
-          endX: evt.touches[0].pageX,
-          endY: evt.touches[0].pageY
-        };
-        break;
-      case 'touchmove':
-        if (this.touchSwipeState === null) {
-          return;
-        }
-        this.touchSwipeState.endX = evt.touches[0].pageX;
-        this.touchSwipeState.endY = evt.touches[0].pageY;
-        evt.preventDefault();
-        break;
-      case 'touchend':
-        if (this.touchSwipeState === null) {
-          return;
-        }
-        let delta = 0;
-        let dx = this.touchSwipeState.endX - this.touchSwipeState.startX;
-        let dy = this.touchSwipeState.endY - this.touchSwipeState.startY;
-        let absAngle = Math.abs(Math.atan2(dy, dx));
-        if (Math.abs(dx) > SWIPE_MIN_DISTANCE_THRESHOLD && (absAngle <= SWIPE_ANGLE_THRESHOLD || absAngle >= Math.PI - SWIPE_ANGLE_THRESHOLD)) {
-          delta = dx;
-        } else if (Math.abs(dy) > SWIPE_MIN_DISTANCE_THRESHOLD && Math.abs(absAngle - Math.PI / 2) <= SWIPE_ANGLE_THRESHOLD) {
-          delta = dy;
-        }
-        if (delta > 0) {
-          this._goToPreviousPage();
-        } else if (delta < 0) {
-          this._goToNextPage();
-        }
-        break;
-    }
-  }
-  _addWindowListeners() {
-    this.showControlsBind = this._showControls.bind(this);
-    this.mouseDownBind = this._mouseDown.bind(this);
-    this.mouseWheelBind = this._mouseWheel.bind(this);
-    this.resetMouseScrollStateBind = this._resetMouseScrollState.bind(this);
-    this.contextMenuBind = this._contextMenu.bind(this);
-    this.touchSwipeBind = this._touchSwipe.bind(this);
-    window.addEventListener('mousemove', this.showControlsBind);
-    window.addEventListener('mousedown', this.mouseDownBind);
-    window.addEventListener('wheel', this.mouseWheelBind);
-    window.addEventListener('keydown', this.resetMouseScrollStateBind);
-    window.addEventListener('contextmenu', this.contextMenuBind);
-    window.addEventListener('touchstart', this.touchSwipeBind);
-    window.addEventListener('touchmove', this.touchSwipeBind);
-    window.addEventListener('touchend', this.touchSwipeBind);
-  }
-  _removeWindowListeners() {
-    window.removeEventListener('mousemove', this.showControlsBind);
-    window.removeEventListener('mousedown', this.mouseDownBind);
-    window.removeEventListener('wheel', this.mouseWheelBind);
-    window.removeEventListener('keydown', this.resetMouseScrollStateBind);
-    window.removeEventListener('contextmenu', this.contextMenuBind);
-    window.removeEventListener('touchstart', this.touchSwipeBind);
-    window.removeEventListener('touchmove', this.touchSwipeBind);
-    window.removeEventListener('touchend', this.touchSwipeBind);
-    delete this.showControlsBind;
-    delete this.mouseDownBind;
-    delete this.mouseWheelBind;
-    delete this.resetMouseScrollStateBind;
-    delete this.contextMenuBind;
-    delete this.touchSwipeBind;
-  }
-  _fullscreenChange() {
-    if (this.isFullscreen) {
-      this._enter();
-    } else {
-      this._exit();
-    }
-  }
-  _addFullscreenChangeListeners() {
-    this.fullscreenChangeBind = this._fullscreenChange.bind(this);
-    window.addEventListener('fullscreenchange', this.fullscreenChangeBind);
-    window.addEventListener('mozfullscreenchange', this.fullscreenChangeBind);
-  }
-  _removeFullscreenChangeListeners() {
-    window.removeEventListener('fullscreenchange', this.fullscreenChangeBind);
-    window.removeEventListener('mozfullscreenchange', this.fullscreenChangeBind);
-    delete this.fullscreenChangeBind;
-  }
-}
-exports.PDFPresentationMode = PDFPresentationMode;
-
-/***/ }),
-/* 23 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.PDFThumbnailViewer = undefined;
-
-var _ui_utils = __webpack_require__(0);
-
-var _pdf_thumbnail_view = __webpack_require__(24);
-
-const THUMBNAIL_SCROLL_MARGIN = -19;
-class PDFThumbnailViewer {
-  constructor({ container, linkService, renderingQueue, l10n = _ui_utils.NullL10n }) {
-    this.container = container;
-    this.linkService = linkService;
-    this.renderingQueue = renderingQueue;
-    this.l10n = l10n;
-    this.scroll = (0, _ui_utils.watchScroll)(this.container, this._scrollUpdated.bind(this));
-    this._resetView();
-  }
-  _scrollUpdated() {
-    this.renderingQueue.renderHighestPriority();
-  }
-  getThumbnail(index) {
-    return this._thumbnails[index];
-  }
-  _getVisibleThumbs() {
-    return (0, _ui_utils.getVisibleElements)(this.container, this._thumbnails);
-  }
-  scrollThumbnailIntoView(page) {
-    let selected = document.querySelector('.thumbnail.selected');
-    if (selected) {
-      selected.classList.remove('selected');
-    }
-    let thumbnail = document.querySelector('div.thumbnail[data-page-number="' + page + '"]');
-    if (thumbnail) {
-      thumbnail.classList.add('selected');
-    }
-    let visibleThumbs = this._getVisibleThumbs();
-    let numVisibleThumbs = visibleThumbs.views.length;
-    if (numVisibleThumbs > 0) {
-      let first = visibleThumbs.first.id;
-      let last = numVisibleThumbs > 1 ? visibleThumbs.last.id : first;
-      if (page <= first || page >= last) {
-        (0, _ui_utils.scrollIntoView)(thumbnail, { top: THUMBNAIL_SCROLL_MARGIN });
-      }
-    }
-  }
-  get pagesRotation() {
-    return this._pagesRotation;
-  }
-  set pagesRotation(rotation) {
-    if (!(0, _ui_utils.isValidRotation)(rotation)) {
-      throw new Error('Invalid thumbnails rotation angle.');
-    }
-    if (!this.pdfDocument) {
-      return;
-    }
-    if (this._pagesRotation === rotation) {
-      return;
-    }
-    this._pagesRotation = rotation;
-    for (let i = 0, ii = this._thumbnails.length; i < ii; i++) {
-      this._thumbnails[i].update(rotation);
-    }
-  }
-  cleanup() {
-    _pdf_thumbnail_view.PDFThumbnailView.cleanup();
-  }
-  _resetView() {
-    this._thumbnails = [];
-    this._pageLabels = null;
-    this._pagesRotation = 0;
-    this._pagesRequests = [];
-    this.container.textContent = '';
-  }
-  setDocument(pdfDocument) {
-    if (this.pdfDocument) {
-      this._cancelRendering();
-      this._resetView();
-    }
-    this.pdfDocument = pdfDocument;
-    if (!pdfDocument) {
-      return;
-    }
-    pdfDocument.getPage(1).then(firstPage => {
-      let pagesCount = pdfDocument.numPages;
-      let viewport = firstPage.getViewport(1.0);
-      for (let pageNum = 1; pageNum <= pagesCount; ++pageNum) {
-        let thumbnail = new _pdf_thumbnail_view.PDFThumbnailView({
-          container: this.container,
-          id: pageNum,
-          defaultViewport: viewport.clone(),
-          linkService: this.linkService,
-          renderingQueue: this.renderingQueue,
-          disableCanvasToImageConversion: false,
-          l10n: this.l10n
-        });
-        this._thumbnails.push(thumbnail);
-      }
-    }).catch(reason => {
-      console.error('Unable to initialize thumbnail viewer', reason);
-    });
-  }
-  _cancelRendering() {
-    for (let i = 0, ii = this._thumbnails.length; i < ii; i++) {
-      if (this._thumbnails[i]) {
-        this._thumbnails[i].cancelRendering();
-      }
-    }
-  }
-  setPageLabels(labels) {
-    if (!this.pdfDocument) {
-      return;
-    }
-    if (!labels) {
-      this._pageLabels = null;
-    } else if (!(labels instanceof Array && this.pdfDocument.numPages === labels.length)) {
-      this._pageLabels = null;
-      console.error('PDFThumbnailViewer_setPageLabels: Invalid page labels.');
-    } else {
-      this._pageLabels = labels;
-    }
-    for (let i = 0, ii = this._thumbnails.length; i < ii; i++) {
-      let label = this._pageLabels && this._pageLabels[i];
-      this._thumbnails[i].setPageLabel(label);
-    }
-  }
-  _ensurePdfPageLoaded(thumbView) {
-    if (thumbView.pdfPage) {
-      return Promise.resolve(thumbView.pdfPage);
-    }
-    let pageNumber = thumbView.id;
-    if (this._pagesRequests[pageNumber]) {
-      return this._pagesRequests[pageNumber];
-    }
-    let promise = this.pdfDocument.getPage(pageNumber).then(pdfPage => {
-      thumbView.setPdfPage(pdfPage);
-      this._pagesRequests[pageNumber] = null;
-      return pdfPage;
-    }).catch(reason => {
-      console.error('Unable to get page for thumb view', reason);
-      this._pagesRequests[pageNumber] = null;
-    });
-    this._pagesRequests[pageNumber] = promise;
-    return promise;
-  }
-  forceRendering() {
-    let visibleThumbs = this._getVisibleThumbs();
-    let thumbView = this.renderingQueue.getHighestPriority(visibleThumbs, this._thumbnails, this.scroll.down);
-    if (thumbView) {
-      this._ensurePdfPageLoaded(thumbView).then(() => {
-        this.renderingQueue.renderView(thumbView);
-      });
-      return true;
-    }
-    return false;
-  }
-}
-exports.PDFThumbnailViewer = PDFThumbnailViewer;
-
-/***/ }),
-/* 24 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.PDFThumbnailView = undefined;
-
-var _pdfjsLib = __webpack_require__(1);
-
-var _ui_utils = __webpack_require__(0);
-
-var _pdf_rendering_queue = __webpack_require__(3);
-
-const MAX_NUM_SCALING_STEPS = 3;
-const THUMBNAIL_CANVAS_BORDER_WIDTH = 1;
-const THUMBNAIL_WIDTH = 98;
-const TempImageFactory = function TempImageFactoryClosure() {
-  let tempCanvasCache = null;
-  return {
-    getCanvas(width, height) {
-      let tempCanvas = tempCanvasCache;
-      if (!tempCanvas) {
-        tempCanvas = document.createElement('canvas');
-        tempCanvasCache = tempCanvas;
-      }
-      tempCanvas.width = width;
-      tempCanvas.height = height;
-      tempCanvas.mozOpaque = true;
-      let ctx = tempCanvas.getContext('2d', { alpha: false });
-      ctx.save();
-      ctx.fillStyle = 'rgb(255, 255, 255)';
-      ctx.fillRect(0, 0, width, height);
-      ctx.restore();
-      return tempCanvas;
-    },
-    destroyCanvas() {
-      let tempCanvas = tempCanvasCache;
-      if (tempCanvas) {
-        tempCanvas.width = 0;
-        tempCanvas.height = 0;
-      }
-      tempCanvasCache = null;
-    }
-  };
-}();
-class PDFThumbnailView {
-  constructor({ container, id, defaultViewport, linkService, renderingQueue, disableCanvasToImageConversion = false, l10n = _ui_utils.NullL10n }) {
-    this.id = id;
-    this.renderingId = 'thumbnail' + id;
-    this.pageLabel = null;
-    this.pdfPage = null;
-    this.rotation = 0;
-    this.viewport = defaultViewport;
-    this.pdfPageRotate = defaultViewport.rotation;
-    this.linkService = linkService;
-    this.renderingQueue = renderingQueue;
-    this.renderTask = null;
-    this.renderingState = _pdf_rendering_queue.RenderingStates.INITIAL;
-    this.resume = null;
-    this.disableCanvasToImageConversion = disableCanvasToImageConversion;
-    this.pageWidth = this.viewport.width;
-    this.pageHeight = this.viewport.height;
-    this.pageRatio = this.pageWidth / this.pageHeight;
-    this.canvasWidth = THUMBNAIL_WIDTH;
-    this.canvasHeight = this.canvasWidth / this.pageRatio | 0;
-    this.scale = this.canvasWidth / this.pageWidth;
-    this.l10n = l10n;
-    let anchor = document.createElement('a');
-    anchor.href = linkService.getAnchorUrl('#page=' + id);
-    this.l10n.get('thumb_page_title', { page: id }, 'Page {{page}}').then(msg => {
-      anchor.title = msg;
-    });
-    anchor.onclick = function () {
-      linkService.page = id;
-      return false;
-    };
-    this.anchor = anchor;
-    let div = document.createElement('div');
-    div.className = 'thumbnail';
-    div.setAttribute('data-page-number', this.id);
-    this.div = div;
-    if (id === 1) {
-      div.classList.add('selected');
-    }
-    let ring = document.createElement('div');
-    ring.className = 'thumbnailSelectionRing';
-    let borderAdjustment = 2 * THUMBNAIL_CANVAS_BORDER_WIDTH;
-    ring.style.width = this.canvasWidth + borderAdjustment + 'px';
-    ring.style.height = this.canvasHeight + borderAdjustment + 'px';
-    this.ring = ring;
-    div.appendChild(ring);
-    anchor.appendChild(div);
-    container.appendChild(anchor);
-  }
-  setPdfPage(pdfPage) {
-    this.pdfPage = pdfPage;
-    this.pdfPageRotate = pdfPage.rotate;
-    let totalRotation = (this.rotation + this.pdfPageRotate) % 360;
-    this.viewport = pdfPage.getViewport(1, totalRotation);
-    this.reset();
-  }
-  reset() {
-    this.cancelRendering();
-    this.pageWidth = this.viewport.width;
-    this.pageHeight = this.viewport.height;
-    this.pageRatio = this.pageWidth / this.pageHeight;
-    this.canvasHeight = this.canvasWidth / this.pageRatio | 0;
-    this.scale = this.canvasWidth / this.pageWidth;
-    this.div.removeAttribute('data-loaded');
-    let ring = this.ring;
-    let childNodes = ring.childNodes;
-    for (let i = childNodes.length - 1; i >= 0; i--) {
-      ring.removeChild(childNodes[i]);
-    }
-    let borderAdjustment = 2 * THUMBNAIL_CANVAS_BORDER_WIDTH;
-    ring.style.width = this.canvasWidth + borderAdjustment + 'px';
-    ring.style.height = this.canvasHeight + borderAdjustment + 'px';
-    if (this.canvas) {
-      this.canvas.width = 0;
-      this.canvas.height = 0;
-      delete this.canvas;
-    }
-    if (this.image) {
-      this.image.removeAttribute('src');
-      delete this.image;
-    }
-  }
-  update(rotation) {
-    if (typeof rotation !== 'undefined') {
-      this.rotation = rotation;
-    }
-    let totalRotation = (this.rotation + this.pdfPageRotate) % 360;
-    this.viewport = this.viewport.clone({
-      scale: 1,
-      rotation: totalRotation
-    });
-    this.reset();
-  }
-  cancelRendering() {
-    if (this.renderTask) {
-      this.renderTask.cancel();
-      this.renderTask = null;
-    }
-    this.renderingState = _pdf_rendering_queue.RenderingStates.INITIAL;
-    this.resume = null;
-  }
-  _getPageDrawContext(noCtxScale = false) {
-    let canvas = document.createElement('canvas');
-    this.canvas = canvas;
-    canvas.mozOpaque = true;
-    let ctx = canvas.getContext('2d', { alpha: false });
-    let outputScale = (0, _ui_utils.getOutputScale)(ctx);
-    canvas.width = this.canvasWidth * outputScale.sx | 0;
-    canvas.height = this.canvasHeight * outputScale.sy | 0;
-    canvas.style.width = this.canvasWidth + 'px';
-    canvas.style.height = this.canvasHeight + 'px';
-    if (!noCtxScale && outputScale.scaled) {
-      ctx.scale(outputScale.sx, outputScale.sy);
-    }
-    return ctx;
-  }
-  _convertCanvasToImage() {
-    if (!this.canvas) {
-      return;
-    }
-    if (this.renderingState !== _pdf_rendering_queue.RenderingStates.FINISHED) {
-      return;
-    }
-    let id = this.renderingId;
-    let className = 'thumbnailImage';
-    if (this.disableCanvasToImageConversion) {
-      this.canvas.id = id;
-      this.canvas.className = className;
-      this.l10n.get('thumb_page_canvas', { page: this.pageId }, 'Thumbnail of Page {{page}}').then(msg => {
-        this.canvas.setAttribute('aria-label', msg);
-      });
-      this.div.setAttribute('data-loaded', true);
-      this.ring.appendChild(this.canvas);
-      return;
-    }
-    let image = document.createElement('img');
-    image.id = id;
-    image.className = className;
-    this.l10n.get('thumb_page_canvas', { page: this.pageId }, 'Thumbnail of Page {{page}}').then(msg => {
-      image.setAttribute('aria-label', msg);
-    });
-    image.style.width = this.canvasWidth + 'px';
-    image.style.height = this.canvasHeight + 'px';
-    image.src = this.canvas.toDataURL();
-    this.image = image;
-    this.div.setAttribute('data-loaded', true);
-    this.ring.appendChild(image);
-    this.canvas.width = 0;
-    this.canvas.height = 0;
-    delete this.canvas;
-  }
-  draw() {
-    if (this.renderingState !== _pdf_rendering_queue.RenderingStates.INITIAL) {
-      console.error('Must be in new state before drawing');
-      return Promise.resolve(undefined);
-    }
-    this.renderingState = _pdf_rendering_queue.RenderingStates.RUNNING;
-    let renderCapability = (0, _pdfjsLib.createPromiseCapability)();
-    let finishRenderTask = error => {
-      if (renderTask === this.renderTask) {
-        this.renderTask = null;
-      }
-      if (error instanceof _pdfjsLib.RenderingCancelledException) {
-        renderCapability.resolve(undefined);
-        return;
-      }
-      this.renderingState = _pdf_rendering_queue.RenderingStates.FINISHED;
-      this._convertCanvasToImage();
-      if (!error) {
-        renderCapability.resolve(undefined);
-      } else {
-        renderCapability.reject(error);
-      }
-    };
-    let ctx = this._getPageDrawContext();
-    let drawViewport = this.viewport.clone({ scale: this.scale });
-    let renderContinueCallback = cont => {
-      if (!this.renderingQueue.isHighestPriority(this)) {
-        this.renderingState = _pdf_rendering_queue.RenderingStates.PAUSED;
-        this.resume = () => {
-          this.renderingState = _pdf_rendering_queue.RenderingStates.RUNNING;
-          cont();
-        };
-        return;
-      }
-      cont();
-    };
-    let renderContext = {
-      canvasContext: ctx,
-      viewport: drawViewport
-    };
-    let renderTask = this.renderTask = this.pdfPage.render(renderContext);
-    renderTask.onContinue = renderContinueCallback;
-    renderTask.promise.then(function () {
-      finishRenderTask(null);
-    }, function (error) {
-      finishRenderTask(error);
-    });
-    return renderCapability.promise;
-  }
-  setImage(pageView) {
-    if (this.renderingState !== _pdf_rendering_queue.RenderingStates.INITIAL) {
-      return;
-    }
-    let img = pageView.canvas;
-    if (!img) {
-      return;
-    }
-    if (!this.pdfPage) {
-      this.setPdfPage(pageView.pdfPage);
-    }
-    this.renderingState = _pdf_rendering_queue.RenderingStates.FINISHED;
-    let ctx = this._getPageDrawContext(true);
-    let canvas = ctx.canvas;
-    if (img.width <= 2 * canvas.width) {
-      ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height);
-      this._convertCanvasToImage();
-      return;
-    }
-    let reducedWidth = canvas.width << MAX_NUM_SCALING_STEPS;
-    let reducedHeight = canvas.height << MAX_NUM_SCALING_STEPS;
-    let reducedImage = TempImageFactory.getCanvas(reducedWidth, reducedHeight);
-    let reducedImageCtx = reducedImage.getContext('2d');
-    while (reducedWidth > img.width || reducedHeight > img.height) {
-      reducedWidth >>= 1;
-      reducedHeight >>= 1;
-    }
-    reducedImageCtx.drawImage(img, 0, 0, img.width, img.height, 0, 0, reducedWidth, reducedHeight);
-    while (reducedWidth > 2 * canvas.width) {
-      reducedImageCtx.drawImage(reducedImage, 0, 0, reducedWidth, reducedHeight, 0, 0, reducedWidth >> 1, reducedHeight >> 1);
-      reducedWidth >>= 1;
-      reducedHeight >>= 1;
-    }
-    ctx.drawImage(reducedImage, 0, 0, reducedWidth, reducedHeight, 0, 0, canvas.width, canvas.height);
-    this._convertCanvasToImage();
-  }
-  get pageId() {
-    return this.pageLabel !== null ? this.pageLabel : this.id;
-  }
-  setPageLabel(label) {
-    this.pageLabel = typeof label === 'string' ? label : null;
-    this.l10n.get('thumb_page_title', { page: this.pageId }, 'Page {{page}}').then(msg => {
-      this.anchor.title = msg;
-    });
-    if (this.renderingState !== _pdf_rendering_queue.RenderingStates.FINISHED) {
-      return;
-    }
-    this.l10n.get('thumb_page_canvas', { page: this.pageId }, 'Thumbnail of Page {{page}}').then(ariaLabel => {
-      if (this.image) {
-        this.image.setAttribute('aria-label', ariaLabel);
-      } else if (this.disableCanvasToImageConversion && this.canvas) {
-        this.canvas.setAttribute('aria-label', ariaLabel);
-      }
-    });
-  }
-  static cleanup() {
-    TempImageFactory.destroyCanvas();
-  }
-}
-exports.PDFThumbnailView = PDFThumbnailView;
-
-/***/ }),
-/* 25 */
+/* 26 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -7327,17 +7380,17 @@ class SecondaryToolbar {
     }
     this.toolbarButtonContainer.setAttribute('style', 'max-height: ' + (this.containerHeight - _ui_utils.SCROLLBAR_PADDING) + 'px;');
     this.previousContainerHeight = this.containerHeight;
   }
 }
 exports.SecondaryToolbar = SecondaryToolbar;
 
 /***/ }),
-/* 26 */
+/* 27 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -7512,17 +7565,17 @@ class Toolbar {
         container.setAttribute('style', 'min-width: ' + width + 'px; ' + 'max-width: ' + width + 'px;');
       }
     });
   }
 }
 exports.Toolbar = Toolbar;
 
 /***/ }),
-/* 27 */
+/* 28 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -7595,32 +7648,32 @@ class ViewHistory {
       }
       return values;
     });
   }
 }
 exports.ViewHistory = ViewHistory;
 
 /***/ }),
-/* 28 */
+/* 29 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.FirefoxCom = exports.DownloadManager = undefined;
 
-__webpack_require__(29);
+__webpack_require__(30);
 
 var _pdfjsLib = __webpack_require__(1);
 
-var _preferences = __webpack_require__(30);
+var _preferences = __webpack_require__(31);
 
 var _app = __webpack_require__(4);
 
 ;
 let FirefoxCom = function FirefoxComClosure() {
   return {
     requestSync(action, data) {
       let request = document.createTextNode('');
@@ -7836,17 +7889,17 @@ document.mozL10n.setExternalLocalizerSer
   getStrings(key) {
     return FirefoxCom.requestSync('getStrings', key);
   }
 });
 exports.DownloadManager = DownloadManager;
 exports.FirefoxCom = FirefoxCom;
 
 /***/ }),
-/* 29 */
+/* 30 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 (function (window) {
   var gLanguage = "";
   var gExternalLocalizerServices = null;
@@ -7934,17 +7987,17 @@ exports.FirefoxCom = FirefoxCom;
       gLanguage = gExternalLocalizerServices.getLocale();
       gReadyState = "complete";
     },
     translate: translateFragment
   };
 })(undefined);
 
 /***/ }),
-/* 30 */
+/* 31 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -8059,17 +8112,17 @@ class BasePreferences {
       }
       return defaultValue;
     });
   }
 }
 exports.BasePreferences = BasePreferences;
 
 /***/ }),
-/* 31 */
+/* 32 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
--- a/devtools/client/themes/markup.css
+++ b/devtools/client/themes/markup.css
@@ -1,17 +1,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 :root {
+  --markup-hidden-attr-color: var(--theme-comment);
+  --markup-hidden-tag-color: #A1A1A4;
   --markup-outline: var(--theme-splitter-color);
 }
 
 .theme-dark:root {
+  --markup-hidden-attr-color: var(--theme-body-color-inactive);
+  --markup-hidden-tag-color: var(--theme-content-color1);
   --markup-outline: var(--theme-selection-background);
 }
 
 * {
   padding: 0;
   margin: 0;
 }
 
@@ -327,16 +331,26 @@ ul.children + .tag-line::before {
   width: .8em;
   height: .8em;
   margin-top: .3em;
   left: 1px;
   position: absolute;
   z-index: 1;
 }
 
+
+.not-displayed .attr-name,
+.not-displayed .attr-value {
+  color: var(--markup-hidden-attr-color);
+}
+
+.not-displayed .theme-fg-color3 {
+  color: var(--markup-hidden-tag-color);
+}
+
 /* Firebug Theme */
 
 .theme-firebug .theme-fg-color3 {
   color: var(--theme-graphs-full-blue);
   font-weight: normal;
 }
 
 .theme-firebug .open,
@@ -348,16 +362,22 @@ ul.children + .tag-line::before {
 .theme-firebug .attr-value.theme-fg-color6 {
   color: var(--theme-highlight-red);
 }
 
 .theme-firebug .markupview-events {
   font-size: var(--theme-toolbar-font-size);
 }
 
+/* In case a node isn't displayed in the page, we fade the syntax highlighting */
+.theme-firebug .not-displayed .open,
+.theme-firebug .not-displayed .close {
+  opacity: .5;
+}
+
 /* Selected nodes in the tree should have light selected text.
    theme-selected doesn't work in this case since the text is a
    sibling of the class, not a child. */
 .theme-selected ~ .editor,
 .theme-selected ~ .editor .theme-fg-color1,
 .theme-selected ~ .editor .theme-fg-color2,
 .theme-selected ~ .editor .theme-fg-color3,
 .theme-selected ~ .editor .theme-fg-color4,
@@ -378,22 +398,16 @@ ul.children + .tag-line::before {
 .doctype {
   font-style: italic;
 }
 
 .theme-firebug .doctype {
   color: #787878;
 }
 
-/* In case a node isn't displayed in the page, we fade the syntax highlighting */
-.not-displayed .open,
-.not-displayed .close {
-  opacity: .5;
-}
-
 /* Events */
 .markupview-events {
   font-size: 8px;
   font-weight: bold;
   line-height: 10px;
   border-radius: 3px;
   padding: 0px 2px;
   margin-inline-start: 5px;
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -6627,26 +6627,24 @@ nsDocShell::SetFocus()
 NS_IMETHODIMP
 nsDocShell::GetMainWidget(nsIWidget** aMainWidget)
 {
   // We don't create our own widget, so simply return the parent one.
   return GetParentWidget(aMainWidget);
 }
 
 NS_IMETHODIMP
-nsDocShell::GetTitle(char16_t** aTitle)
-{
-  NS_ENSURE_ARG_POINTER(aTitle);
-
-  *aTitle = ToNewUnicode(mTitle);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDocShell::SetTitle(const char16_t* aTitle)
+nsDocShell::GetTitle(nsAString& aTitle)
+{
+  aTitle = mTitle;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetTitle(const nsAString& aTitle)
 {
   // Store local title
   mTitle = aTitle;
 
   nsCOMPtr<nsIDocShellTreeItem> parent;
   GetSameTypeParent(getter_AddRefs(parent));
 
   // When title is set on the top object it should then be passed to the
--- a/docshell/base/nsDocShellTreeOwner.cpp
+++ b/docshell/base/nsDocShellTreeOwner.cpp
@@ -715,27 +715,27 @@ nsDocShellTreeOwner::SetFocus()
   nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
   if (ownerWin) {
     return ownerWin->SetFocus();
   }
   return NS_ERROR_NULL_POINTER;
 }
 
 NS_IMETHODIMP
-nsDocShellTreeOwner::GetTitle(char16_t** aTitle)
+nsDocShellTreeOwner::GetTitle(nsAString& aTitle)
 {
   nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
   if (ownerWin) {
     return ownerWin->GetTitle(aTitle);
   }
   return NS_ERROR_NULL_POINTER;
 }
 
 NS_IMETHODIMP
-nsDocShellTreeOwner::SetTitle(const char16_t* aTitle)
+nsDocShellTreeOwner::SetTitle(const nsAString& aTitle)
 {
   nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
   if (ownerWin) {
     return ownerWin->SetTitle(aTitle);
   }
   return NS_ERROR_NULL_POINTER;
 }
 
--- a/docshell/base/nsDocShellTreeOwner.h
+++ b/docshell/base/nsDocShellTreeOwner.h
@@ -84,21 +84,16 @@ protected:
 
   nsWebBrowser* WebBrowser();
   NS_IMETHOD SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner);
   NS_IMETHOD SetWebBrowserChrome(nsIWebBrowserChrome* aWebBrowserChrome);
 
   NS_IMETHOD AddChromeListeners();
   NS_IMETHOD RemoveChromeListeners();
 
-  nsresult FindItemWithNameAcrossWindows(
-    const char16_t* aName,
-    nsIDocShellTreeItem* aRequestor, nsIDocShellTreeItem* aOriginalRequestor,
-    nsIDocShellTreeItem** aFoundItem);
-
   void EnsurePrompter();
   void EnsureAuthPrompter();
 
   void AddToWatcher();
   void RemoveFromWatcher();
 
   void EnsureContentTreeOwner();
 
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -7336,17 +7336,17 @@ nsDocument::DoNotifyPossibleTitleChange(
 
   nsCOMPtr<nsIPresShell> shell = GetShell();
   if (shell) {
     nsCOMPtr<nsISupports> container =
       shell->GetPresContext()->GetContainerWeak();
     if (container) {
       nsCOMPtr<nsIBaseWindow> docShellWin = do_QueryInterface(container);
       if (docShellWin) {
-        docShellWin->SetTitle(title.get());
+        docShellWin->SetTitle(title);
       }
     }
   }
 
   // Fire a DOM event for the title change.
   nsContentUtils::DispatchChromeEvent(this, static_cast<nsIDocument*>(this),
                                       NS_LITERAL_STRING("DOMTitleChanged"),
                                       true, true);
--- a/dom/base/nsRange.cpp
+++ b/dom/base/nsRange.cpp
@@ -1043,22 +1043,29 @@ IndexOf(nsINode* aChild)
 }
 
 void
 nsRange::SetSelection(mozilla::dom::Selection* aSelection)
 {
   if (mSelection == aSelection) {
     return;
   }
+
   // At least one of aSelection and mSelection must be null
   // aSelection will be null when we are removing from a selection
   // and a range can't be in more than one selection at a time,
   // thus mSelection must be null too.
   MOZ_ASSERT(!aSelection || !mSelection);
 
+  // Extra step in case our parent failed to ensure the above
+  // invariant.
+  if (aSelection && mSelection) {
+    mSelection->RemoveRange(this);
+  }
+
   mSelection = aSelection;
   if (mSelection) {
     nsINode* commonAncestor = GetCommonAncestor();
     NS_ASSERTION(commonAncestor, "unexpected disconnected nodes");
     RegisterCommonAncestor(commonAncestor);
   } else {
     UnregisterCommonAncestor(mRegisteredCommonAncestor, false);
     MOZ_DIAGNOSTIC_ASSERT(!mRegisteredCommonAncestor,
--- a/dom/base/nsWindowMemoryReporter.cpp
+++ b/dom/base/nsWindowMemoryReporter.cpp
@@ -297,291 +297,249 @@ CollectWindowReports(nsGlobalWindow *aWi
 
   // Use |windowPath|, but replace "explicit/" with "event-counts/".
   nsCString censusWindowPath(windowPath);
   censusWindowPath.Replace(0, strlen("explicit"), "event-counts");
 
   // Remember the path for later.
   aWindowPaths->Put(aWindow->WindowID(), windowPath);
 
-#define REPORT_SIZE(_pathTail, _amount, _desc) \
+// Report the size from windowSizes and add to the appropriate total in
+// aWindowTotalSizes.
+#define REPORT_SIZE(_pathTail, _field, _desc) \
+  ReportSize(windowPath, _pathTail, windowSizes._field, \
+             NS_LITERAL_CSTRING(_desc), aHandleReport, aData); \
+  aWindowTotalSizes->_field += windowSizes._field;
+
+// Report the size, which is a sum of other sizes, and so doesn't require
+// updating aWindowTotalSizes.
+#define REPORT_SUM_SIZE(_pathTail, _amount, _desc) \
   ReportSize(windowPath, _pathTail, _amount, NS_LITERAL_CSTRING(_desc), \
              aHandleReport, aData);
 
-#define REPORT_COUNT(_pathTail, _amount, _desc) \
-  ReportCount(censusWindowPath, _pathTail, _amount, NS_LITERAL_CSTRING(_desc), \
-              aHandleReport, aData);
+// Like REPORT_SIZE, but for a count.
+#define REPORT_COUNT(_pathTail, _field, _desc) \
+  ReportCount(censusWindowPath, _pathTail, windowSizes._field, \
+              NS_LITERAL_CSTRING(_desc), aHandleReport, aData); \
+  aWindowTotalSizes->_field += windowSizes._field;
 
   // This SizeOfState contains the SeenPtrs used for all memory reporting of
   // this window.
   SizeOfState state(WindowsMallocSizeOf);
   nsWindowSizes windowSizes(state);
   aWindow->AddSizeOfIncludingThis(windowSizes);
 
-  REPORT_SIZE("/dom/element-nodes", windowSizes.mDOMElementNodesSize,
+  REPORT_SIZE("/dom/element-nodes", mDOMElementNodesSize,
               "Memory used by the element nodes in a window's DOM.");
-  aWindowTotalSizes->mDOMElementNodesSize += windowSizes.mDOMElementNodesSize;
 
-  REPORT_SIZE("/dom/text-nodes", windowSizes.mDOMTextNodesSize,
+  REPORT_SIZE("/dom/text-nodes", mDOMTextNodesSize,
               "Memory used by the text nodes in a window's DOM.");
-  aWindowTotalSizes->mDOMTextNodesSize += windowSizes.mDOMTextNodesSize;
 
-  REPORT_SIZE("/dom/cdata-nodes", windowSizes.mDOMCDATANodesSize,
+  REPORT_SIZE("/dom/cdata-nodes", mDOMCDATANodesSize,
               "Memory used by the CDATA nodes in a window's DOM.");
-  aWindowTotalSizes->mDOMCDATANodesSize += windowSizes.mDOMCDATANodesSize;
 
-  REPORT_SIZE("/dom/comment-nodes", windowSizes.mDOMCommentNodesSize,
+  REPORT_SIZE("/dom/comment-nodes", mDOMCommentNodesSize,
               "Memory used by the comment nodes in a window's DOM.");
-  aWindowTotalSizes->mDOMCommentNodesSize += windowSizes.mDOMCommentNodesSize;
 
-  REPORT_SIZE("/dom/event-targets", windowSizes.mDOMEventTargetsSize,
+  REPORT_SIZE("/dom/event-targets", mDOMEventTargetsSize,
               "Memory used by the event targets table in a window's DOM, and "
               "the objects it points to, which include XHRs.");
-  aWindowTotalSizes->mDOMEventTargetsSize += windowSizes.mDOMEventTargetsSize;
 
-  REPORT_SIZE("/dom/performance/user-entries",
-              windowSizes.mDOMPerformanceUserEntries,
+  REPORT_SIZE("/dom/performance/user-entries", mDOMPerformanceUserEntries,
               "Memory used for performance user entries.");
-  aWindowTotalSizes->mDOMPerformanceUserEntries +=
-    windowSizes.mDOMPerformanceUserEntries;
 
   REPORT_SIZE("/dom/performance/resource-entries",
-              windowSizes.mDOMPerformanceResourceEntries,
+              mDOMPerformanceResourceEntries,
               "Memory used for performance resource entries.");
-  aWindowTotalSizes->mDOMPerformanceResourceEntries +=
-    windowSizes.mDOMPerformanceResourceEntries;
 
-  REPORT_SIZE("/dom/other", windowSizes.mDOMOtherSize,
+  REPORT_SIZE("/dom/other", mDOMOtherSize,
               "Memory used by a window's DOM that isn't measured by the "
               "other 'dom/' numbers.");
-  aWindowTotalSizes->mDOMOtherSize += windowSizes.mDOMOtherSize;
 
-  REPORT_SIZE("/layout/style-sheets", windowSizes.mLayoutStyleSheetsSize,
+  REPORT_SIZE("/layout/style-sheets", mLayoutStyleSheetsSize,
               "Memory used by style sheets within a window.");
-  aWindowTotalSizes->mLayoutStyleSheetsSize +=
-    windowSizes.mLayoutStyleSheetsSize;
 
-  REPORT_SIZE("/layout/pres-shell", windowSizes.mLayoutPresShellSize,
+  REPORT_SIZE("/layout/pres-shell", mLayoutPresShellSize,
               "Memory used by layout's PresShell, along with any structures "
               "allocated in its arena and not measured elsewhere, "
               "within a window.");
-  aWindowTotalSizes->mLayoutPresShellSize += windowSizes.mLayoutPresShellSize;
 
-  REPORT_SIZE("/layout/gecko-style-sets", windowSizes.mLayoutGeckoStyleSets,
+  REPORT_SIZE("/layout/gecko-style-sets", mLayoutGeckoStyleSets,
               "Memory used by Gecko style sets within a window.");
-  aWindowTotalSizes->mLayoutGeckoStyleSets += windowSizes.mLayoutGeckoStyleSets;
 
   REPORT_SIZE("/layout/servo-style-sets/stylist/rule-tree",
-              windowSizes.mLayoutServoStyleSetsStylistRuleTree,
+              mLayoutServoStyleSetsStylistRuleTree,
               "Memory used by rule trees within Servo style sets within a "
               "window.");
-  aWindowTotalSizes->mLayoutServoStyleSetsStylistRuleTree +=
-    windowSizes.mLayoutServoStyleSetsStylistRuleTree;
 
   REPORT_SIZE("/layout/servo-style-sets/stylist/element-and-pseudos-maps",
-              windowSizes.mLayoutServoStyleSetsStylistElementAndPseudosMaps,
+              mLayoutServoStyleSetsStylistElementAndPseudosMaps,
               "Memory used by element and pseudos maps within Servo style "
               "sets within a window.");
-  aWindowTotalSizes->mLayoutServoStyleSetsStylistElementAndPseudosMaps +=
-    windowSizes.mLayoutServoStyleSetsStylistElementAndPseudosMaps;
 
   REPORT_SIZE("/layout/servo-style-sets/stylist/invalidation-map",
-              windowSizes.mLayoutServoStyleSetsStylistInvalidationMap,
+              mLayoutServoStyleSetsStylistInvalidationMap,
               "Memory used by invalidation maps within Servo style sets "
               "within a window.");
-  aWindowTotalSizes->mLayoutServoStyleSetsStylistInvalidationMap +=
-    windowSizes.mLayoutServoStyleSetsStylistInvalidationMap;
 
   REPORT_SIZE("/layout/servo-style-sets/stylist/revalidation-selectors",
-              windowSizes.mLayoutServoStyleSetsStylistRevalidationSelectors,
+              mLayoutServoStyleSetsStylistRevalidationSelectors,
               "Memory used by selectors for cache revalidation within Servo "
               "style sets within a window.");
-  aWindowTotalSizes->mLayoutServoStyleSetsStylistRevalidationSelectors +=
-    windowSizes.mLayoutServoStyleSetsStylistRevalidationSelectors;
 
   REPORT_SIZE("/layout/servo-style-sets/stylist/other",
-              windowSizes.mLayoutServoStyleSetsStylistOther,
+              mLayoutServoStyleSetsStylistOther,
               "Memory used by other Stylist data within Servo style sets "
               "within a window.");
-  aWindowTotalSizes->mLayoutServoStyleSetsStylistOther +=
-    windowSizes.mLayoutServoStyleSetsStylistOther;
 
-  REPORT_SIZE("/layout/servo-style-sets/other",
-              windowSizes.mLayoutServoStyleSetsOther,
+  REPORT_SIZE("/layout/servo-style-sets/other", mLayoutServoStyleSetsOther,
               "Memory used by other parts of Servo style sets within a "
               "window.");
-  aWindowTotalSizes->mLayoutServoStyleSetsOther +=
-    windowSizes.mLayoutServoStyleSetsOther;
 
   REPORT_SIZE("/layout/servo-element-data-objects",
-              windowSizes.mLayoutServoElementDataObjects,
+              mLayoutServoElementDataObjects,
               "Memory used for Servo ElementData objects, but not the things"
               "hanging off them.");
-  aWindowTotalSizes->mLayoutServoElementDataObjects +=
-    windowSizes.mLayoutServoElementDataObjects;
 
-  REPORT_SIZE("/layout/text-runs", windowSizes.mLayoutTextRunsSize,
+  REPORT_SIZE("/layout/text-runs", mLayoutTextRunsSize,
               "Memory used for text-runs (glyph layout) in the PresShell's "
               "frame tree, within a window.");
-  aWindowTotalSizes->mLayoutTextRunsSize += windowSizes.mLayoutTextRunsSize;
 
-  REPORT_SIZE("/layout/pres-contexts", windowSizes.mLayoutPresContextSize,
+  REPORT_SIZE("/layout/pres-contexts", mLayoutPresContextSize,
               "Memory used for the PresContext in the PresShell's frame "
               "within a window.");
-  aWindowTotalSizes->mLayoutPresContextSize +=
-    windowSizes.mLayoutPresContextSize;
 
-  REPORT_SIZE("/layout/frame-properties",
-              windowSizes.mLayoutFramePropertiesSize,
+  REPORT_SIZE("/layout/frame-properties", mLayoutFramePropertiesSize,
               "Memory used for frame properties attached to frames "
               "within a window.");
-  aWindowTotalSizes->mLayoutFramePropertiesSize +=
-    windowSizes.mLayoutFramePropertiesSize;
 
-  REPORT_SIZE("/layout/computed-values/dom",
-              windowSizes.mLayoutComputedValuesDom,
+  REPORT_SIZE("/layout/computed-values/dom", mLayoutComputedValuesDom,
               "Memory used by ComputedValues objects accessible from DOM "
               "elements.");
-  aWindowTotalSizes->mLayoutComputedValuesDom +=
-    windowSizes.mLayoutComputedValuesDom;
 
-  REPORT_SIZE("/layout/computed-values/non-dom",
-              windowSizes.mLayoutComputedValuesNonDom,
+  REPORT_SIZE("/layout/computed-values/non-dom", mLayoutComputedValuesNonDom,
               "Memory used by ComputedValues objects not accessible from DOM "
               "elements.");
-  aWindowTotalSizes->mLayoutComputedValuesNonDom +=
-    windowSizes.mLayoutComputedValuesNonDom;
 
-  REPORT_SIZE("/layout/computed-values/visited",
-              windowSizes.mLayoutComputedValuesVisited,
+  REPORT_SIZE("/layout/computed-values/visited", mLayoutComputedValuesVisited,
               "Memory used by ComputedValues objects used for visited styles.");
-  aWindowTotalSizes->mLayoutComputedValuesVisited +=
-    windowSizes.mLayoutComputedValuesVisited;
 
-  REPORT_SIZE("/property-tables",
-              windowSizes.mPropertyTablesSize,
+  REPORT_SIZE("/property-tables", mPropertyTablesSize,
               "Memory used for the property tables within a window.");
-  aWindowTotalSizes->mPropertyTablesSize += windowSizes.mPropertyTablesSize;
 
-  REPORT_COUNT("/dom/event-targets", windowSizes.mDOMEventTargetsCount,
+  REPORT_COUNT("/dom/event-targets", mDOMEventTargetsCount,
                "Number of non-node event targets in the event targets table "
                "in a window's DOM, such as XHRs.");
-  aWindowTotalSizes->mDOMEventTargetsCount +=
-    windowSizes.mDOMEventTargetsCount;
 
-  REPORT_COUNT("/dom/event-listeners", windowSizes.mDOMEventListenersCount,
+  REPORT_COUNT("/dom/event-listeners", mDOMEventListenersCount,
                "Number of event listeners in a window, including event "
                "listeners on nodes and other event targets.");
-  aWindowTotalSizes->mDOMEventListenersCount +=
-    windowSizes.mDOMEventListenersCount;
 
-  REPORT_SIZE("/layout/line-boxes", windowSizes.mArenaSizes.mLineBoxes,
+  REPORT_SIZE("/layout/line-boxes", mArenaSizes.mLineBoxes,
               "Memory used by line boxes within a window.");
-  aWindowTotalSizes->mArenaSizes.mLineBoxes
-    += windowSizes.mArenaSizes.mLineBoxes;
 
-  REPORT_SIZE("/layout/rule-nodes", windowSizes.mArenaSizes.mRuleNodes,
+  REPORT_SIZE("/layout/rule-nodes", mArenaSizes.mRuleNodes,
               "Memory used by CSS rule nodes within a window.");
-  aWindowTotalSizes->mArenaSizes.mRuleNodes
-    += windowSizes.mArenaSizes.mRuleNodes;
 
-  REPORT_SIZE("/layout/style-contexts", windowSizes.mArenaSizes.mStyleContexts,
+  REPORT_SIZE("/layout/style-contexts", mArenaSizes.mStyleContexts,
               "Memory used by style contexts within a window.");
-  aWindowTotalSizes->mArenaSizes.mStyleContexts
-    += windowSizes.mArenaSizes.mStyleContexts;
 
   // There are many different kinds of style structs, but it is likely that
   // only a few matter. Implement a cutoff so we don't bloat about:memory with
   // many uninteresting entries.
   const size_t STYLE_SUNDRIES_THRESHOLD =
     js::MemoryReportingSundriesThreshold();
 
   // This is the Gecko style structs, which are in the nsPresArena.
   size_t geckoStyleSundriesSize = 0;
 #define STYLE_STRUCT(name_, cb_) \
   { \
     size_t size = \
       windowSizes.mArenaSizes.mGeckoStyleSizes.NS_STYLE_SIZES_FIELD(name_); \
     if (size < STYLE_SUNDRIES_THRESHOLD) { \
       geckoStyleSundriesSize += size; \
     } else { \
-      REPORT_SIZE("/layout/gecko-style-structs/" # name_, size, \
-                  "Memory used by the " #name_ " Gecko style structs " \
-                  "within a window."); \
+      REPORT_SUM_SIZE( \
+        "/layout/gecko-style-structs/" # name_, size, \
+        "Memory used by the " #name_ " Gecko style structs within a window."); \
     } \
     aWindowTotalSizes->mArenaSizes.mGeckoStyleSizes.NS_STYLE_SIZES_FIELD(name_) += \
       size; \
   }
 #define STYLE_STRUCT_LIST_IGNORE_VARIABLES
 #include "nsStyleStructList.h"
 #undef STYLE_STRUCT
 #undef STYLE_STRUCT_LIST_IGNORE_VARIABLES
 
   if (geckoStyleSundriesSize > 0) {
-    REPORT_SIZE("/layout/gecko-style-structs/sundries", geckoStyleSundriesSize,
-                "The sum of all memory used by Gecko style structs which were "
-                "too small to be shown individually.");
+    REPORT_SUM_SIZE(
+      "/layout/gecko-style-structs/sundries", geckoStyleSundriesSize,
+      "The sum of all memory used by Gecko style structs which were too small "
+      "to be shown individually.");
   }
 
   // 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.
   const size_t FRAME_SUNDRIES_THRESHOLD =
     js::MemoryReportingSundriesThreshold();
 
   size_t frameSundriesSize = 0;
 #define FRAME_ID(classname, ...) \
   { \
     size_t size = windowSizes.mArenaSizes.NS_ARENA_SIZES_FIELD(classname); \
     if (size < FRAME_SUNDRIES_THRESHOLD) { \
       frameSundriesSize += size; \
     } else { \
-      REPORT_SIZE("/layout/frames/" # classname, size, \
-                  "Memory used by frames of " \
-                  "type " #classname " within a window."); \
+      REPORT_SUM_SIZE( \
+        "/layout/frames/" # classname, size, \
+        "Memory used by frames of type " #classname " within a window."); \
     } \
     aWindowTotalSizes->mArenaSizes.NS_ARENA_SIZES_FIELD(classname) += size; \
   }
 #define ABSTRACT_FRAME_ID(...)
 #include "nsFrameIdList.h"
 #undef FRAME_ID
 #undef ABSTRACT_FRAME_ID
 
   if (frameSundriesSize > 0) {
-    REPORT_SIZE("/layout/frames/sundries", frameSundriesSize,
-                "The sum of all memory used by frames which were too small "
-                "to be shown individually.");
+    REPORT_SUM_SIZE(
+      "/layout/frames/sundries", frameSundriesSize,
+      "The sum of all memory used by frames which were too small to be shown "
+      "individually.");
   }
 
   // This is the Servo style structs.
   size_t servoStyleSundriesSize = 0;
 #define STYLE_STRUCT(name_, cb_) \
   { \
     size_t size = windowSizes.mServoStyleSizes.NS_STYLE_SIZES_FIELD(name_); \
     if (size < STYLE_SUNDRIES_THRESHOLD) { \
       servoStyleSundriesSize += size; \
     } else { \
-      REPORT_SIZE("/layout/servo-style-structs/" # name_, size, \
-                  "Memory used by the " #name_ " Servo style structs " \
-                  "within a window."); \
+      REPORT_SUM_SIZE( \
+        "/layout/servo-style-structs/" # name_, size, \
+        "Memory used by the " #name_ " Servo style structs within a window."); \
     } \
     aWindowTotalSizes->mServoStyleSizes.NS_STYLE_SIZES_FIELD(name_) += size; \
   }
 #define STYLE_STRUCT_LIST_IGNORE_VARIABLES
 #include "nsStyleStructList.h"
 #undef STYLE_STRUCT
 #undef STYLE_STRUCT_LIST_IGNORE_VARIABLES
 
   if (servoStyleSundriesSize > 0) {
-    REPORT_SIZE("/layout/servo-style-structs/sundries", servoStyleSundriesSize,
-                "The sum of all memory used by Servo style structs which were "
-                "too small to be shown individually.");
+    REPORT_SUM_SIZE(
+      "/layout/servo-style-structs/sundries", servoStyleSundriesSize,
+      "The sum of all memory used by Servo style structs which were too "
+      "small to be shown individually.");
   }
 
 #undef REPORT_SIZE
+#undef REPORT_SUM_SIZE
 #undef REPORT_COUNT
 }
 
 typedef nsTArray< RefPtr<nsGlobalWindow> > WindowArray;
 
 NS_IMETHODIMP
 nsWindowMemoryReporter::CollectReports(nsIHandleReportCallback* aHandleReport,
                                        nsISupports* aData, bool aAnonymize)
--- a/dom/base/nsXHTMLContentSerializer.cpp
+++ b/dom/base/nsXHTMLContentSerializer.cpp
@@ -661,27 +661,22 @@ nsXHTMLContentSerializer::LineBreakAfter
   }
 
   if ((aName == nsGkAtoms::html) ||
       (aName == nsGkAtoms::head) ||
       (aName == nsGkAtoms::body) ||
       (aName == nsGkAtoms::tr) ||
       (aName == nsGkAtoms::th) ||
       (aName == nsGkAtoms::td) ||
-      (aName == nsGkAtoms::pre) ||
       (aName == nsGkAtoms::title) ||
-      (aName == nsGkAtoms::li) ||
       (aName == nsGkAtoms::dt) ||
       (aName == nsGkAtoms::dd) ||
-      (aName == nsGkAtoms::blockquote) ||
       (aName == nsGkAtoms::select) ||
       (aName == nsGkAtoms::option) ||
-      (aName == nsGkAtoms::p) ||
-      (aName == nsGkAtoms::map) ||
-      (aName == nsGkAtoms::div)) {
+      (aName == nsGkAtoms::map)) {
     return true;
   }
 
   return nsHTMLElement::IsBlock(nsHTMLTags::CaseSensitiveAtomTagToId(aName));
 }
 
 
 void
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -409,17 +409,17 @@ TabChild::TabChild(nsIContentChild* aMan
   , mTabGroup(aTabGroup)
   , mRemoteFrame(nullptr)
   , mManager(aManager)
   , mChromeFlags(aChromeFlags)
   , mMaxTouchPoints(0)
   , mActiveSuppressDisplayport(0)
   , mLayersId(0)
   , mBeforeUnloadListeners(0)
-  , mLayersConnected(true)
+  , mLayersConnected(false)
   , mDidFakeShow(false)
   , mNotified(false)
   , mTriedBrowserInit(false)
   , mOrientation(eScreenOrientation_PortraitPrimary)
   , mIgnoreKeyPressEvent(false)
   , mHasValidInnerSize(false)
   , mDestroyed(false)
   , mUniqueId(aTabId)
@@ -936,25 +936,25 @@ TabChild::GetVisibility(bool* aVisibilit
 NS_IMETHODIMP
 TabChild::SetVisibility(bool aVisibility)
 {
   // should the platform support this? Bug 666365
   return NS_OK;
 }
 
 NS_IMETHODIMP
-TabChild::GetTitle(char16_t** aTitle)
+TabChild::GetTitle(nsAString& aTitle)
 {
   NS_WARNING("TabChild::GetTitle not supported in TabChild");
 
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
-TabChild::SetTitle(const char16_t* aTitle)
+TabChild::SetTitle(const nsAString& aTitle)
 {
   // JavaScript sends the "DOMTitleChanged" event to the parent
   // via the message manager.
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TabChild::GetSiteWindow(void** aSiteWindow)
@@ -1169,16 +1169,17 @@ TabChild::RecvLoadURL(const nsCString& a
 }
 
 void
 TabChild::DoFakeShow(const TextureFactoryIdentifier& aTextureFactoryIdentifier,
                      const uint64_t& aLayersId,
                      const CompositorOptions& aCompositorOptions,
                      PRenderFrameChild* aRenderFrame, const ShowInfo& aShowInfo)
 {
+  mLayersConnected = aRenderFrame ? true : false;
   InitRenderingState(aTextureFactoryIdentifier, aLayersId, aCompositorOptions, aRenderFrame);
   RecvShow(ScreenIntSize(0, 0), aShowInfo, mParentIsActive, nsSizeMode_Normal);
   mDidFakeShow = true;
 }
 
 void
 TabChild::ApplyShowInfo(const ShowInfo& aInfo)
 {
@@ -2792,31 +2793,30 @@ TabChild::InitRenderingState(const Textu
       if (!sTabChildren) {
         sTabChildren = new TabChildMap;
       }
       MOZ_ASSERT(!sTabChildren->Get(aLayersId));
       sTabChildren->Put(aLayersId, this);
       mLayersId = aLayersId;
     }
 
-    ShadowLayerForwarder* lf =
-        mPuppetWidget->GetLayerManager(
-            nullptr, mTextureFactoryIdentifier.mParentBackend)
-                ->AsShadowForwarder();
-
     LayerManager* lm = mPuppetWidget->GetLayerManager();
     if (lm->AsWebRenderLayerManager()) {
       lm->AsWebRenderLayerManager()->Initialize(compositorChild,
                                                 wr::AsPipelineId(aLayersId),
                                                 &mTextureFactoryIdentifier);
       ImageBridgeChild::IdentifyCompositorTextureHost(mTextureFactoryIdentifier);
       gfx::VRManagerChild::IdentifyTextureHost(mTextureFactoryIdentifier);
       InitAPZState();
     }
 
+    ShadowLayerForwarder* lf =
+        mPuppetWidget->GetLayerManager(
+            nullptr, mTextureFactoryIdentifier.mParentBackend)
+                ->AsShadowForwarder();
     if (lf) {
       nsTArray<LayersBackend> backends;
       backends.AppendElement(mTextureFactoryIdentifier.mParentBackend);
       PLayerTransactionChild* shadowManager =
           compositorChild->SendPLayerTransactionConstructor(backends, aLayersId);
       if (shadowManager) {
         lf->SetShadowManager(shadowManager);
         lf->IdentifyTextureHost(mTextureFactoryIdentifier);
@@ -3162,16 +3162,17 @@ TabChild::ReinitRendering()
     });
   }
 
   if (!success) {
     NS_WARNING("failed to recreate layer manager");
     return;
   }
 
+  mLayersConnected = true;
   ImageBridgeChild::IdentifyCompositorTextureHost(mTextureFactoryIdentifier);
   gfx::VRManagerChild::IdentifyTextureHost(mTextureFactoryIdentifier);
 
   InitAPZState();
 
   nsCOMPtr<nsIDocument> doc(GetDocument());
   doc->NotifyLayerManagerRecreated();
 }
new file mode 100644
--- /dev/null
+++ b/dom/svg/crashtests/1347617-1.svg
@@ -0,0 +1,10 @@
+<svg height='600'
+     xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink"
+     filter="url(#f)">
+
+<filter id="f" filterRes="19" filterUnits="userSpaceOnUse">
+<feConvolveMatrix kernelMatrix="1 1 1 1 1 1 1 1 1" kernelUnitLength="1 -1" />
+</filter>
+
+</svg>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/svg/crashtests/1347617-2.svg
@@ -0,0 +1,10 @@
+<svg height='600'
+     xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink"
+     filter="url(#f)">
+
+<filter id="f" filterRes="19" filterUnits="userSpaceOnUse">
+<feConvolveMatrix kernelMatrix="1 1 1 1 1 1 1 1 1" kernelUnitLength="-1 1" />
+</filter>
+
+</svg>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/svg/crashtests/1347617-3.svg
@@ -0,0 +1,10 @@
+<svg height='600'
+     xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink"
+     filter="url(#f)">
+
+<filter id="f" filterRes="19" filterUnits="userSpaceOnUse">
+<feConvolveMatrix kernelMatrix="1 1 1 1 1 1 1 1 1" kernelUnitLength="-1" />
+</filter>
+
+</svg>
\ No newline at end of file
--- a/dom/svg/crashtests/crashtests.list
+++ b/dom/svg/crashtests/crashtests.list
@@ -85,8 +85,11 @@ load 1322286.html
 load 1329849-1.svg
 load 1329849-2.svg
 load 1329849-3.svg
 load 1329849-4.svg
 load 1329849-5.svg
 load 1329849-6.svg
 load 1329093-1.html
 load 1329093-2.html
+load 1347617-1.svg
+load 1347617-2.svg
+load 1347617-3.svg
--- a/dom/webbrowserpersist/nsWebBrowserPersist.cpp
+++ b/dom/webbrowserpersist/nsWebBrowserPersist.cpp
@@ -8,16 +8,17 @@
 #include "nspr.h"
 
 #include "nsIFileStreams.h"       // New Necko file streams
 #include <algorithm>
 
 #include "nsAutoPtr.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
+#include "nsIClassOfService.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsILoadContext.h"
 #include "nsIPrivateBrowsingChannel.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIComponentRegistrar.h"
 #include "nsIStorageStream.h"
 #include "nsISeekableStream.h"
 #include "nsIHttpChannel.h"
@@ -1492,16 +1493,22 @@ nsresult nsWebBrowserPersist::SaveChanne
         rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedInputStream),
                                        fileInputStream, BUFFERED_OUTPUT_SIZE);
         NS_ENSURE_SUCCESS(rv, rv);
         nsAutoCString contentType;
         aChannel->GetContentType(contentType);
         return StartUpload(bufferedInputStream, aFile, contentType);
     }
 
+    // Mark save channel as throttleable.
+    nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(aChannel));
+    if (cos) {
+      cos->AddClassFlags(nsIClassOfService::Throttleable);
+    }
+
     // Read from the input channel
     nsresult rv = NS_MaybeOpenChannelUsingAsyncOpen2(aChannel, this);
     if (rv == NS_ERROR_NO_CONTENT)
     {
         // Assume this is a protocol such as mailto: which does not feed out
         // data and just ignore it.
         return NS_SUCCESS_DONT_FIXUP;
     }
--- a/extensions/cookie/test/unit/test_cookies_sync_failure.js
+++ b/extensions/cookie/test/unit/test_cookies_sync_failure.js
@@ -13,17 +13,17 @@
 // 4) Migration fails. This will have different modes depending on the initial
 //    version:
 //    a) Schema 1: the 'lastAccessed' column already exists.
 //    b) Schema 2: the 'baseDomain' column already exists; or 'baseDomain'
 //       cannot be computed for a particular host.
 //    c) Schema 3: the 'creationTime' column already exists; or the
 //       'moz_uniqueid' index already exists.
 
-var COOKIE_DATABASE_SCHEMA_CURRENT = 8;
+var COOKIE_DATABASE_SCHEMA_CURRENT = 9;
 
 var test_generator = do_run_test();
 
 function run_test() {
   do_test_pending();
   do_run_generator(test_generator);
 }
 
--- a/gfx/layers/wr/IpcResourceUpdateQueue.cpp
+++ b/gfx/layers/wr/IpcResourceUpdateQueue.cpp
@@ -177,16 +177,20 @@ ShmSegmentsReader::ReadLarge(const layer
   aInto.PushBytes(Range<uint8_t>(srcPtr, aRange.length()));
 
   return true;
 }
 
 bool
 ShmSegmentsReader::Read(const layers::OffsetRange& aRange, wr::Vec_u8& aInto)
 {
+  if (aRange.length() == 0) {
+    return true;
+  }
+
   if (aRange.source() != 0) {
     return ReadLarge(aRange, aInto);
   }
 
   if (mChunkSize == 0) {
     return false;
   }
 
--- a/image/decoders/nsPNGDecoder.cpp
+++ b/image/decoders/nsPNGDecoder.cpp
@@ -279,16 +279,17 @@ nsPNGDecoder::InitInternal()
     bool(GetSurfaceFlags() & SurfaceFlags::NO_PREMULTIPLY_ALPHA);
 
 #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
   static png_byte color_chunks[]=
        { 99,  72,  82,  77, '\0',   // cHRM
         105,  67,  67,  80, '\0'};  // iCCP
   static png_byte unused_chunks[]=
        { 98,  75,  71,  68, '\0',   // bKGD
+        101,  88,  73, 102, '\0',   // eXIf
         104,  73,  83,  84, '\0',   // hIST
         105,  84,  88, 116, '\0',   // iTXt
         111,  70,  70, 115, '\0',   // oFFs
         112,  67,  65,  76, '\0',   // pCAL
         115,  67,  65,  76, '\0',   // sCAL
         112,  72,  89, 115, '\0',   // pHYs
         115,  66,  73,  84, '\0',   // sBIT
         115,  80,  76,  84, '\0',   // sPLT
new file mode 100644
--- /dev/null
+++ b/intl/icu-patches/bug-1387476-timezone-detection.diff
@@ -0,0 +1,67 @@
+Update time detection for newer Linux distros and macOS 10.13
+
+https://ssl.icu-project.org/trac/ticket/12770
+https://ssl.icu-project.org/trac/changeset/40427
+https://ssl.icu-project.org/trac/changeset/40432
+https://ssl.icu-project.org/trac/changeset?old=40300&old_path=trunk%2Ficu4c%2Fsource%2Fcommon%2Fputil.cpp&new_path=trunk%2Ficu4c%2Fsource%2Fcommon%2Fputil.cpp&new=40432
+
+Index: /intl/icu/source/common/putil.cpp
+===================================================================
+--- /intl/icu/source/common/putil.cpp	(revision 40300)
++++ /intl/icu/source/common/putil.cpp	(revision 40432)
+@@ -676,4 +676,14 @@
+ #if !UCONFIG_NO_FILE_IO && ((U_PLATFORM_IS_DARWIN_BASED && (U_PLATFORM != U_PF_IPHONE || defined(U_TIMEZONE))) || U_PLATFORM_IS_LINUX_BASED || U_PLATFORM == U_PF_BSD || U_PLATFORM == U_PF_SOLARIS)
+ /* These platforms are likely to use Olson timezone IDs. */
++/* common targets of the symbolic link at TZDEFAULT are:
++ * "/usr/share/zoneinfo/<olsonID>" default, older Linus distros, macOS to 10.12
++ * "../usr/share/zoneinfo/<olsonID>" newer Linux distros: Red Hat Enterprise Linux 7, Ubuntu, SuSe Linux
++ * "/usr/share/lib/zoneinfo/<olsonID>" Solaris
++ * "../usr/share/lib/zoneinfo/<olsonID>" Solaris
++ * "/var/db/timezone/zoneinfo/<olsonID>" macOS 10.13
++ * To avoid checking lots of paths, just check that the target path
++ * before the <olsonID> ends with "/zoneinfo/", and the <olsonID> is valid.
++ */
++
+ #define CHECK_LOCALTIME_LINK 1
+ #if U_PLATFORM_IS_DARWIN_BASED
+@@ -683,5 +693,4 @@
+ #define TZDEFAULT       "/etc/localtime"
+ #define TZZONEINFO      "/usr/share/lib/zoneinfo/"
+-#define TZZONEINFO2     "../usr/share/lib/zoneinfo/"
+ #define TZ_ENV_CHECK    "localtime"
+ #else
+@@ -689,4 +698,5 @@
+ #define TZZONEINFO      "/usr/share/zoneinfo/"
+ #endif
++#define TZZONEINFOTAIL  "/zoneinfo/"
+ #if U_HAVE_DIRENT_H
+ #define TZFILE_SKIP     "posixrules" /* tz file to skip when searching. */
+@@ -1132,22 +1142,13 @@
+         int32_t ret = (int32_t)readlink(TZDEFAULT, gTimeZoneBuffer, sizeof(gTimeZoneBuffer)-1);
+         if (0 < ret) {
+-            int32_t tzZoneInfoLen = uprv_strlen(TZZONEINFO);
++            int32_t tzZoneInfoTailLen = uprv_strlen(TZZONEINFOTAIL);
+             gTimeZoneBuffer[ret] = 0;
+-            if (uprv_strncmp(gTimeZoneBuffer, TZZONEINFO, tzZoneInfoLen) == 0
+-                && isValidOlsonID(gTimeZoneBuffer + tzZoneInfoLen))
++            char *  tzZoneInfoTailPtr = uprv_strstr(gTimeZoneBuffer, TZZONEINFOTAIL);
++            
++            if (tzZoneInfoTailPtr != NULL
++                && isValidOlsonID(tzZoneInfoTailPtr + tzZoneInfoTailLen))
+             {
+-                return (gTimeZoneBufferPtr = gTimeZoneBuffer + tzZoneInfoLen);
++                return (gTimeZoneBufferPtr = tzZoneInfoTailPtr + tzZoneInfoTailLen);
+             }
+-#if U_PLATFORM == U_PF_SOLARIS
+-            else
+-            {
+-                tzZoneInfoLen = uprv_strlen(TZZONEINFO2);
+-                if (uprv_strncmp(gTimeZoneBuffer, TZZONEINFO2, tzZoneInfoLen) == 0
+-                                && isValidOlsonID(gTimeZoneBuffer + tzZoneInfoLen))
+-                {
+-                    return (gTimeZoneBufferPtr = gTimeZoneBuffer + tzZoneInfoLen);
+-                }
+-            }
+-#endif
+         } else {
+ #if defined(SEARCH_TZFILE)
--- a/intl/icu/source/common/putil.cpp
+++ b/intl/icu/source/common/putil.cpp
@@ -670,29 +670,39 @@ uprv_timezone()
 
 #if defined(U_TZNAME) && (U_PLATFORM == U_PF_IRIX || U_PLATFORM_IS_DARWIN_BASED)
 /* RS6000 and others reject char **tzname.  */
 extern U_IMPORT char *U_TZNAME[];
 #endif
 
 #if !UCONFIG_NO_FILE_IO && ((U_PLATFORM_IS_DARWIN_BASED && (U_PLATFORM != U_PF_IPHONE || defined(U_TIMEZONE))) || U_PLATFORM_IS_LINUX_BASED || U_PLATFORM == U_PF_BSD || U_PLATFORM == U_PF_SOLARIS)
 /* These platforms are likely to use Olson timezone IDs. */
+/* common targets of the symbolic link at TZDEFAULT are:
+ * "/usr/share/zoneinfo/<olsonID>" default, older Linus distros, macOS to 10.12
+ * "../usr/share/zoneinfo/<olsonID>" newer Linux distros: Red Hat Enterprise Linux 7, Ubuntu, SuSe Linux
+ * "/usr/share/lib/zoneinfo/<olsonID>" Solaris
+ * "../usr/share/lib/zoneinfo/<olsonID>" Solaris
+ * "/var/db/timezone/zoneinfo/<olsonID>" macOS 10.13
+ * To avoid checking lots of paths, just check that the target path
+ * before the <olsonID> ends with "/zoneinfo/", and the <olsonID> is valid.
+ */
+
 #define CHECK_LOCALTIME_LINK 1
 #if U_PLATFORM_IS_DARWIN_BASED
 #include <tzfile.h>
 #define TZZONEINFO      (TZDIR "/")
 #elif U_PLATFORM == U_PF_SOLARIS
 #define TZDEFAULT       "/etc/localtime"
 #define TZZONEINFO      "/usr/share/lib/zoneinfo/"
-#define TZZONEINFO2     "../usr/share/lib/zoneinfo/"
 #define TZ_ENV_CHECK    "localtime"
 #else
 #define TZDEFAULT       "/etc/localtime"
 #define TZZONEINFO      "/usr/share/zoneinfo/"
 #endif
+#define TZZONEINFOTAIL  "/zoneinfo/"
 #if U_HAVE_DIRENT_H
 #define TZFILE_SKIP     "posixrules" /* tz file to skip when searching. */
 /* Some Linux distributions have 'localtime' in /usr/share/zoneinfo
    symlinked to /etc/localtime, which makes searchForTZFile return
    'localtime' when it's the first match. */
 #define TZFILE_SKIP2    "localtime"
 #define SEARCH_TZFILE
 #include <dirent.h>  /* Needed to search through system timezone files */
@@ -1120,34 +1130,25 @@ uprv_tzname(int n)
     if (gTimeZoneBufferPtr == NULL) {
         /*
         This is a trick to look at the name of the link to get the Olson ID
         because the tzfile contents is underspecified.
         This isn't guaranteed to work because it may not be a symlink.
         */
         int32_t ret = (int32_t)readlink(TZDEFAULT, gTimeZoneBuffer, sizeof(gTimeZoneBuffer)-1);
         if (0 < ret) {
-            int32_t tzZoneInfoLen = uprv_strlen(TZZONEINFO);
+            int32_t tzZoneInfoTailLen = uprv_strlen(TZZONEINFOTAIL);
             gTimeZoneBuffer[ret] = 0;
-            if (uprv_strncmp(gTimeZoneBuffer, TZZONEINFO, tzZoneInfoLen) == 0
-                && isValidOlsonID(gTimeZoneBuffer + tzZoneInfoLen))
+            char *  tzZoneInfoTailPtr = uprv_strstr(gTimeZoneBuffer, TZZONEINFOTAIL);
+            
+            if (tzZoneInfoTailPtr != NULL
+                && isValidOlsonID(tzZoneInfoTailPtr + tzZoneInfoTailLen))
             {
-                return (gTimeZoneBufferPtr = gTimeZoneBuffer + tzZoneInfoLen);
+                return (gTimeZoneBufferPtr = tzZoneInfoTailPtr + tzZoneInfoTailLen);
             }
-#if U_PLATFORM == U_PF_SOLARIS
-            else
-            {
-                tzZoneInfoLen = uprv_strlen(TZZONEINFO2);
-                if (uprv_strncmp(gTimeZoneBuffer, TZZONEINFO2, tzZoneInfoLen) == 0
-                                && isValidOlsonID(gTimeZoneBuffer + tzZoneInfoLen))
-                {
-                    return (gTimeZoneBufferPtr = gTimeZoneBuffer + tzZoneInfoLen);
-                }
-            }
-#endif
         } else {
 #if defined(SEARCH_TZFILE)
             DefaultTZInfo* tzInfo = (DefaultTZInfo*)uprv_malloc(sizeof(DefaultTZInfo));
             if (tzInfo != NULL) {
                 tzInfo->defaultTZBuffer = NULL;
                 tzInfo->defaultTZFileSize = 0;
                 tzInfo->defaultTZFilePtr = NULL;
                 tzInfo->defaultTZstatus = FALSE;
--- a/intl/update-icu.sh
+++ b/intl/update-icu.sh
@@ -69,16 +69,17 @@ for patch in \
  bug-915735 \
  suppress-warnings.diff \
  bug-1172609-timezone-recreateDefault.diff \
  bug-1198952-workaround-make-3.82-bug.diff \
  u_setMemoryFunctions-callconvention-anachronism-msvc.diff \
  bug-1373763-convertToPosix-stack-value-out-of-scope.diff \
  bug-1380083 \
  bug-1387937.diff \
+ bug-1387476-timezone-detection.diff \
 ; do
   echo "Applying local patch $patch"
   patch -d ${icu_dir}/../../ -p1 --no-backup-if-mismatch < ${icu_dir}/../icu-patches/$patch
 done
 
 topsrcdir=`dirname $0`/../
 python ${topsrcdir}/js/src/tests/ecma_6/String/make-normalize-generateddata-input.py $topsrcdir
 
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -137,16 +137,22 @@ template <typename T> struct IsHeapConst
     template <> struct IsHeapConstructibleType<T> { static constexpr bool value = true; };
 FOR_EACH_PUBLIC_GC_POINTER_TYPE(DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE)
 FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE)
 #undef DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE
 
 template <typename T, typename Wrapper>
 class PersistentRootedBase : public MutableWrappedPtrOperations<T, Wrapper> {};
 
+template <typename T>
+class FakeRooted;
+
+template <typename T>
+class FakeMutableHandle;
+
 namespace gc {
 struct Cell;
 template<typename T>
 struct PersistentRootedMarker;
 } /* namespace gc */
 
 // Important: Return a reference so passing a Rooted<T>, etc. to
 // something that takes a |const T&| is not a GC hazard.
@@ -899,74 +905,16 @@ class RootedBase<JSObject*, Container> :
 template <typename Container>
 class HandleBase<JSObject*, Container> : public WrappedPtrOperations<JSObject*, Container>
 {
   public:
     template <class U>
     JS::Handle<U*> as() const;
 };
 
-/** Interface substitute for Rooted<T> which does not root the variable's memory. */
-template <typename T>
-class MOZ_RAII FakeRooted : public RootedBase<T, FakeRooted<T>>
-{
-  public:
-    using ElementType = T;
-
-    template <typename CX>
-    explicit FakeRooted(CX* cx) : ptr(JS::GCPolicy<T>::initial()) {}
-
-    template <typename CX>
-    FakeRooted(CX* cx, T initial) : ptr(initial) {}
-
-    DECLARE_POINTER_CONSTREF_OPS(T);
-    DECLARE_POINTER_ASSIGN_OPS(FakeRooted, T);
-    DECLARE_NONPOINTER_ACCESSOR_METHODS(ptr);
-    DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(ptr);
-
-  private:
-    T ptr;
-
-    void set(const T& value) {
-        ptr = value;
-    }
-
-    FakeRooted(const FakeRooted&) = delete;
-};
-
-/** Interface substitute for MutableHandle<T> which is not required to point to rooted memory. */
-template <typename T>
-class FakeMutableHandle : public js::MutableHandleBase<T, FakeMutableHandle<T>>
-{
-  public:
-    using ElementType = T;
-
-    MOZ_IMPLICIT FakeMutableHandle(T* t) {
-        ptr = t;
-    }
-
-    MOZ_IMPLICIT FakeMutableHandle(FakeRooted<T>* root) {
-        ptr = root->address();
-    }
-
-    void set(const T& v) {
-        *ptr = v;
-    }
-
-    DECLARE_POINTER_CONSTREF_OPS(T);
-    DECLARE_NONPOINTER_ACCESSOR_METHODS(*ptr);
-    DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(*ptr);
-
-  private:
-    FakeMutableHandle() {}
-    DELETE_ASSIGNMENT_OPS(FakeMutableHandle, T);
-
-    T* ptr;
-};
-
 /**
  * Types for a variable that either should or shouldn't be rooted, depending on
  * the template parameter allowGC. Used for implementing functions that can
  * operate on either rooted or unrooted data.
  *
  * The toHandle() and toMutableHandle() functions are for calling functions
  * which require handle types and are only called in the CanGC case. These
  * allow the calling code to type check.
@@ -996,37 +944,16 @@ template <typename T> class MaybeRooted<
     }
 
     template <typename T2>
     static inline JS::Handle<T2*> downcastHandle(HandleType v) {
         return v.template as<T2>();
     }
 };
 
-template <typename T> class MaybeRooted<T, NoGC>
-{
-  public:
-    typedef const T& HandleType;
-    typedef FakeRooted<T> RootType;
-    typedef FakeMutableHandle<T> MutableHandleType;
-
-    static JS::Handle<T> toHandle(HandleType v) {
-        MOZ_CRASH("Bad conversion");
-    }
-
-    static JS::MutableHandle<T> toMutableHandle(MutableHandleType v) {
-        MOZ_CRASH("Bad conversion");
-    }
-
-    template <typename T2>
-    static inline T2* downcastHandle(HandleType v) {
-        return &v->template as<T2>();
-    }
-};
-
 } /* namespace js */
 
 namespace JS {
 
 template <typename T> template <typename S>
 inline
 Handle<T>::Handle(const Rooted<S>& root,
                   typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy)
@@ -1514,11 +1441,9 @@ operator==(const T& a, std::nullptr_t b)
 
 template <typename T>
 typename mozilla::EnableIf<js::detail::DefineComparisonOps<T>::value &&
                            mozilla::IsPointer<typename T::ElementType>::value, bool>::Type
 operator!=(const T& a, std::nullptr_t b) {
     return !(a == b);
 }
 
-#undef DELETE_ASSIGNMENT_OPS
-
 #endif  /* js_RootingAPI_h */
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -466,39 +466,39 @@ PromiseHasAnyFlag(PromiseObject& promise
     return promise.getFixedSlot(PromiseSlot_Flags).toInt32() & flag;
 }
 
 static bool ResolvePromiseFunction(JSContext* cx, unsigned argc, Value* vp);
 static bool RejectPromiseFunction(JSContext* cx, unsigned argc, Value* vp);
 
 // ES2016, 25.4.1.3.
 static MOZ_MUST_USE bool
-CreateResolvingFunctions(JSContext* cx, HandleValue promise,
-                         MutableHandleValue resolveVal,
-                         MutableHandleValue rejectVal)
+CreateResolvingFunctions(JSContext* cx, HandleObject promise,
+                         MutableHandleObject resolveFn,
+                         MutableHandleObject rejectFn)
 {
     RootedAtom funName(cx, cx->names().empty);
     RootedFunction resolve(cx, NewNativeFunction(cx, ResolvePromiseFunction, 1, funName,
                                                  gc::AllocKind::FUNCTION_EXTENDED, GenericObject));
     if (!resolve)
         return false;
 
     RootedFunction reject(cx, NewNativeFunction(cx, RejectPromiseFunction, 1, funName,
                                                  gc::AllocKind::FUNCTION_EXTENDED, GenericObject));
     if (!reject)
         return false;
 
-    resolve->setExtendedSlot(ResolveFunctionSlot_Promise, promise);
+    resolve->setExtendedSlot(ResolveFunctionSlot_Promise, ObjectValue(*promise));
     resolve->setExtendedSlot(ResolveFunctionSlot_RejectFunction, ObjectValue(*reject));
 
-    reject->setExtendedSlot(RejectFunctionSlot_Promise, promise);
+    reject->setExtendedSlot(RejectFunctionSlot_Promise, ObjectValue(*promise));
     reject->setExtendedSlot(RejectFunctionSlot_ResolveFunction, ObjectValue(*resolve));
 
-    resolveVal.setObject(*resolve);
-    rejectVal.setObject(*reject);
+    resolveFn.set(resolve);
+    rejectFn.set(reject);
 
     return true;
 }
 
 static void ClearResolutionFunctionSlots(JSFunction* resolutionFun);
 static MOZ_MUST_USE bool RejectMaybeWrappedPromise(JSContext *cx, HandleObject promiseObj,
                                                    HandleValue reason);
 
@@ -855,16 +855,42 @@ CreatePromiseObjectWithoutResolutionFunc
     if (!promise)
         return nullptr;
 
     AddPromiseFlags(*promise, PROMISE_FLAG_DEFAULT_RESOLVE_FUNCTION |
                     PROMISE_FLAG_DEFAULT_REJECT_FUNCTION);
     return promise;
 }
 
+static MOZ_MUST_USE PromiseObject*
+CreatePromiseWithDefaultResolutionFunctions(JSContext* cx, MutableHandleObject resolve,
+                                            MutableHandleObject reject)
+{
+    // ES2016, 25.4.3.1., as if called with GetCapabilitiesExecutor as the
+    // executor argument.
+
+    // Steps 1-2 (Not applicable).
+
+    // Steps 3-7.
+    Rooted<PromiseObject*> promise(cx, CreatePromiseObjectInternal(cx));
+    if (!promise)
+        return nullptr;
+
+    // Step 8.
+    if (!CreateResolvingFunctions(cx, promise, resolve, reject))
+        return nullptr;
+
+    promise->setFixedSlot(PromiseSlot_RejectFunction, ObjectValue(*reject));
+
+    // Steps 9-10 (Not applicable).
+
+    // Step 11.
+    return promise;
+}
+
 // ES2016, 25.4.1.5.
 static MOZ_MUST_USE bool
 NewPromiseCapability(JSContext* cx, HandleObject C, MutableHandleObject promise,
                      MutableHandleObject resolve, MutableHandleObject reject,
                      bool canOmitResolutionFunctions)
 {
     RootedValue cVal(cx, ObjectValue(*C));
 
@@ -878,18 +904,25 @@ NewPromiseCapability(JSContext* cx, Hand
     // resolve/reject functions won't ever escape to content, we can skip
     // creating and calling the executor function and instead return a Promise
     // marked as having default resolve/reject functions.
     //
     // This can't be used in Promise.all and Promise.race because we have to
     // pass the reject (and resolve, in the race case) function to thenables
     // in the list passed to all/race, which (potentially) means exposing them
     // to content.
-    if (canOmitResolutionFunctions && IsNativeFunction(cVal, PromiseConstructor)) {
-        promise.set(CreatePromiseObjectWithoutResolutionFunctions(cx));
+    //
+    // For Promise.all and Promise.race we can only optimize away the creation
+    // of the GetCapabilitiesExecutor function, and directly allocate the
+    // result promise instead of invoking the Promise constructor.
+    if (IsNativeFunction(cVal, PromiseConstructor)) {
+        if (canOmitResolutionFunctions)
+            promise.set(CreatePromiseObjectWithoutResolutionFunctions(cx));
+        else
+            promise.set(CreatePromiseWithDefaultResolutionFunctions(cx, resolve, reject));
         if (!promise)
             return false;
         return true;
     }
 
     // Step 3 (omitted).
 
     // Step 4.
@@ -1250,42 +1283,43 @@ PromiseResolveThenableJob(JSContext* cx,
     CallArgs args = CallArgsFromVp(argc, vp);
 
     RootedFunction job(cx, &args.callee().as<JSFunction>());
     RootedValue then(cx, job->getExtendedSlot(ThenableJobSlot_Handler));
     MOZ_ASSERT(!IsWrapper(&then.toObject()));
     RootedNativeObject jobArgs(cx, &job->getExtendedSlot(ThenableJobSlot_JobData)
                                     .toObject().as<NativeObject>());
 
-    RootedValue promise(cx, jobArgs->getDenseElement(ThenableJobDataIndex_Promise));
+    RootedObject promise(cx, &jobArgs->getDenseElement(ThenableJobDataIndex_Promise).toObject());
     RootedValue thenable(cx, jobArgs->getDenseElement(ThenableJobDataIndex_Thenable));
 
     // Step 1.
-    RootedValue resolveVal(cx);
-    RootedValue rejectVal(cx);
-    if (!CreateResolvingFunctions(cx, promise, &resolveVal, &rejectVal))
+    RootedObject resolveFn(cx);
+    RootedObject rejectFn(cx);
+    if (!CreateResolvingFunctions(cx, promise, &resolveFn, &rejectFn))
         return false;
 
     // Step 2.
     FixedInvokeArgs<2> args2(cx);
-    args2[0].set(resolveVal);
-    args2[1].set(rejectVal);
+    args2[0].setObject(*resolveFn);
+    args2[1].setObject(*rejectFn);
 
     RootedValue rval(cx);
 
     // In difference to the usual pattern, we return immediately on success.
     if (Call(cx, then, thenable, args2, &rval))
         return true;
 
     if (!MaybeGetAndClearException(cx, &rval))
         return false;
 
     FixedInvokeArgs<1> rejectArgs(cx);
     rejectArgs[0].set(rval);
 
+    RootedValue rejectVal(cx, ObjectValue(*rejectFn));
     return Call(cx, rejectVal, UndefinedHandleValue, rejectArgs, &rval);
 }
 
 /**
  * Tells the embedding to enqueue a Promise resolve thenable job, based on
  * three parameters:
  * promiseToResolve_ - The promise to resolve, obviously.
  * thenable_ - The thenable to resolve the Promise with.
@@ -1580,64 +1614,64 @@ PromiseObject::create(JSContext* cx, Han
 
 
     // Steps 3-7.
     Rooted<PromiseObject*> promise(cx, CreatePromiseObjectInternal(cx, usedProto, needsWrapping,
                                                                    false));
     if (!promise)
         return nullptr;
 
-    RootedValue promiseVal(cx, ObjectValue(*promise));
-    if (needsWrapping && !cx->compartment()->wrap(cx, &promiseVal))
+    RootedObject promiseObj(cx, promise);
+    if (needsWrapping && !cx->compartment()->wrap(cx, &promiseObj))
         return nullptr;
 
     // Step 8.
     // The resolving functions are created in the compartment active when the
     // (maybe wrapped) Promise constructor was called. They contain checks and
     // can unwrap the Promise if required.
-    RootedValue resolveVal(cx);
-    RootedValue rejectVal(cx);
-    if (!CreateResolvingFunctions(cx, promiseVal, &resolveVal, &rejectVal))
+    RootedObject resolveFn(cx);
+    RootedObject rejectFn(cx);
+    if (!CreateResolvingFunctions(cx, promiseObj, &resolveFn, &rejectFn))
         return nullptr;
 
     // Need to wrap the resolution functions before storing them on the Promise.
     if (needsWrapping) {
         AutoCompartment ac(cx, promise);
-        RootedValue wrappedRejectVal(cx, rejectVal);
-        if (!cx->compartment()->wrap(cx, &wrappedRejectVal))
+        RootedObject wrappedRejectFn(cx, rejectFn);
+        if (!cx->compartment()->wrap(cx, &wrappedRejectFn))
             return nullptr;
-        promise->setFixedSlot(PromiseSlot_RejectFunction, wrappedRejectVal);
+        promise->setFixedSlot(PromiseSlot_RejectFunction, ObjectValue(*wrappedRejectFn));
     } else {
-        promise->setFixedSlot(PromiseSlot_RejectFunction, rejectVal);
+        promise->setFixedSlot(PromiseSlot_RejectFunction, ObjectValue(*rejectFn));
     }
 
     // Step 9.
     bool success;
     {
         FixedInvokeArgs<2> args(cx);
 
-        args[0].set(resolveVal);
-        args[1].set(rejectVal);
+        args[0].setObject(*resolveFn);
+        args[1].setObject(*rejectFn);
 
         RootedValue calleeOrRval(cx, ObjectValue(*executor));
         success = Call(cx, calleeOrRval, UndefinedHandleValue, args, &calleeOrRval);
     }
 
     // Step 10.
     if (!success) {
         RootedValue exceptionVal(cx);
         if (!MaybeGetAndClearException(cx, &exceptionVal))
             return nullptr;
 
         FixedInvokeArgs<1> args(cx);
 
         args[0].set(exceptionVal);
 
-        // |rejectVal| is unused after this, so we can safely write to it.
-        if (!Call(cx, rejectVal, UndefinedHandleValue, args, &rejectVal))
+        RootedValue calleeOrRval(cx, ObjectValue(*rejectFn));
+        if (!Call(cx, calleeOrRval, UndefinedHandleValue, args, &calleeOrRval))
             return nullptr;
     }
 
     // Let the Debugger know about this Promise.
     JS::dbg::onNewPromise(cx, promise);
 
     // Step 11.
     return promise;
@@ -1993,17 +2027,17 @@ PerformPromiseAll(JSContext *cx, JS::For
                 return false;
         }
 
         // Step i.
         // Sadly, because someone could have overridden
         // "resolve" on the canonical Promise constructor.
         RootedValue nextPromise(cx);
         RootedValue staticResolve(cx);
-        if (!GetProperty(cx, CVal, cx->names().resolve, &staticResolve))
+        if (!GetProperty(cx, C, CVal, cx->names().resolve, &staticResolve))
             return false;
 
         FixedInvokeArgs<1> resolveArgs(cx);
         resolveArgs[0].set(nextValue);
         if (!Call(cx, staticResolve, CVal, resolveArgs, &nextPromise))
             return false;
 
         // Step j.
@@ -2202,17 +2236,17 @@ PerformPromiseRace(JSContext *cx, JS::Fo
             return true;
         }
 
         // Step h.
         // Sadly, because someone could have overridden
         // "resolve" on the canonical Promise constructor.
         RootedValue nextPromise(cx);
         RootedValue staticResolve(cx);
-        if (!GetProperty(cx, CVal, cx->names().resolve, &staticResolve))
+        if (!GetProperty(cx, C, CVal, cx->names().resolve, &staticResolve))
             return false;
 
         FixedInvokeArgs<1> resolveArgs(cx);
         resolveArgs[0].set(nextValue);
         if (!Call(cx, staticResolve, CVal, resolveArgs, &nextPromise))
             return false;
 
         // Step i.
@@ -3035,25 +3069,25 @@ PerformPromiseThenWithReaction(JSContext
  * the call to |promise.then| would use the original |Promise| constructor to
  * create the resulting promise, this function skips the call to |promise.then|
  * and thus creating a new promise that would not be observable by content.
  */
 static MOZ_MUST_USE bool
 BlockOnPromise(JSContext* cx, HandleValue promiseVal, HandleObject blockedPromise_,
                HandleValue onFulfilled, HandleValue onRejected)
 {
-    RootedValue thenVal(cx);
-    if (!GetProperty(cx, promiseVal, cx->names().then, &thenVal))
+    RootedObject promiseObj(cx, ToObject(cx, promiseVal));
+    if (!promiseObj)
         return false;
 
-    RootedObject promiseObj(cx);
-    if (promiseVal.isObject())
-        promiseObj = &promiseVal.toObject();
-
-    if (promiseObj && promiseObj->is<PromiseObject>() && IsNativeFunction(thenVal, Promise_then)) {
+    RootedValue thenVal(cx);
+    if (!GetProperty(cx, promiseObj, promiseVal, cx->names().then, &thenVal))
+        return false;
+
+    if (promiseObj->is<PromiseObject>() && IsNativeFunction(thenVal, Promise_then)) {
         // |promise| is an unwrapped Promise, and |then| is the original
         // |Promise.prototype.then|, inline it here.
         // 25.4.5.3., step 3.
         RootedObject PromiseCtor(cx);
         if (!GetBuiltinConstructor(cx, JSProto_Promise, &PromiseCtor))
             return false;
 
         RootedObject C(cx, SpeciesConstructor(cx, promiseObj, JSProto_Promise, IsPromiseSpecies));
@@ -3093,17 +3127,17 @@ BlockOnPromise(JSContext* cx, HandleValu
             return false;
     }
 
     // In case the value to depend on isn't an object at all, there's nothing
     // more to do here: we can only add reactions to Promise objects
     // (potentially after unwrapping them), and non-object values can't be
     // Promise objects. This can happen if Promise.all is called on an object
     // with a `resolve` method that returns primitives.
-    if (!promiseObj)
+    if (!promiseVal.isObject())
         return true;
 
     // The object created by the |promise.then| call or the inlined version
     // of it above is visible to content (either because |promise.then| was
     // overridden by content and could leak it, or because a constructor
     // other than the original value of |Promise| was used to create it).
     // To have both that object and |blockedPromise| show up as dependent
     // promises in the debugger, add a dummy reaction to the list of reject
--- a/js/src/gc/Rooting.h
+++ b/js/src/gc/Rooting.h
@@ -76,11 +76,90 @@ typedef JS::Rooted<DebuggerFrame*>      
 typedef JS::Rooted<DebuggerObject*>         RootedDebuggerObject;
 typedef JS::Rooted<Scope*>                  RootedScope;
 
 typedef JS::GCVector<JSFunction*>   FunctionVector;
 typedef JS::GCVector<PropertyName*> PropertyNameVector;
 typedef JS::GCVector<Shape*>        ShapeVector;
 typedef JS::GCVector<JSString*>     StringVector;
 
+/** Interface substitute for Rooted<T> which does not root the variable's memory. */
+template <typename T>
+class MOZ_RAII FakeRooted : public RootedBase<T, FakeRooted<T>>
+{
+  public:
+    using ElementType = T;
+
+    template <typename CX>
+    explicit FakeRooted(CX* cx) : ptr(JS::GCPolicy<T>::initial()) {}
+
+    template <typename CX>
+    FakeRooted(CX* cx, T initial) : ptr(initial) {}
+
+    DECLARE_POINTER_CONSTREF_OPS(T);
+    DECLARE_POINTER_ASSIGN_OPS(FakeRooted, T);
+    DECLARE_NONPOINTER_ACCESSOR_METHODS(ptr);
+    DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(ptr);
+
+  private:
+    T ptr;
+
+    void set(const T& value) {
+        ptr = value;
+    }
+
+    FakeRooted(const FakeRooted&) = delete;
+};
+
+/** Interface substitute for MutableHandle<T> which is not required to point to rooted memory. */
+template <typename T>
+class FakeMutableHandle : public js::MutableHandleBase<T, FakeMutableHandle<T>>
+{
+  public:
+    using ElementType = T;
+
+    MOZ_IMPLICIT FakeMutableHandle(T* t) {
+        ptr = t;
+    }
+
+    MOZ_IMPLICIT FakeMutableHandle(FakeRooted<T>* root) {
+        ptr = root->address();
+    }
+
+    void set(const T& v) {
+        *ptr = v;
+    }
+
+    DECLARE_POINTER_CONSTREF_OPS(T);
+    DECLARE_NONPOINTER_ACCESSOR_METHODS(*ptr);
+    DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(*ptr);
+
+  private:
+    FakeMutableHandle() {}
+    DELETE_ASSIGNMENT_OPS(FakeMutableHandle, T);
+
+    T* ptr;
+};
+
+template <typename T> class MaybeRooted<T, NoGC>
+{
+  public:
+    typedef const T& HandleType;
+    typedef FakeRooted<T> RootType;
+    typedef FakeMutableHandle<T> MutableHandleType;
+
+    static JS::Handle<T> toHandle(HandleType v) {
+        MOZ_CRASH("Bad conversion");
+    }
+
+    static JS::MutableHandle<T> toMutableHandle(MutableHandleType v) {
+        MOZ_CRASH("Bad conversion");
+    }
+
+    template <typename T2>
+    static inline T2* downcastHandle(HandleType v) {
+        return &v->template as<T2>();
+    }
+};
+
 } /* namespace js */
 
 #endif /* gc_Rooting_h */
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -4261,17 +4261,17 @@ jit::AnalyzeNewScriptDefiniteProperties(
         ReportOutOfMemory(cx);
         return false;
     }
 
     MDefinition* thisValue = graph.entryBlock()->getSlot(info.thisSlot());
 
     // Get a list of instructions using the |this| value in the order they
     // appear in the graph.
-    Vector<MInstruction*> instructions(cx);
+    Vector<MInstruction*, 4> instructions(cx);
 
     for (MUseDefIterator uses(thisValue); uses; uses++) {
         MDefinition* use = uses.def();
 
         // Don't track |this| through assignments to phis.
         if (!use->isInstruction())
             return true;
 
--- a/js/src/jit/MoveResolver.h
+++ b/js/src/jit/MoveResolver.h
@@ -211,17 +211,18 @@ class MoveOp
     { }
     MoveOp(const MoveOperand& from, const MoveOperand& to, Type type)
       : from_(from),
         to_(to),
         cycleBegin_(false),
         cycleEnd_(false),
         cycleBeginSlot_(-1),
         cycleEndSlot_(-1),
-        type_(type)
+        type_(type),
+        endCycleType_(GENERAL) // initialize to silence UBSan warning
     { }
 
     bool isCycleBegin() const {
         return cycleBegin_;
     }
     bool isCycleEnd() const {
         return cycleEnd_;
     }
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -3152,17 +3152,17 @@ size_t
 JSScript::sizeOfData(mozilla::MallocSizeOf mallocSizeOf) const
 {
     return mallocSizeOf(data);
 }
 
 size_t
 JSScript::sizeOfTypeScript(mozilla::MallocSizeOf mallocSizeOf) const
 {
-    return types_->sizeOfIncludingThis(mallocSizeOf);
+    return types_ ? types_->sizeOfIncludingThis(mallocSizeOf) : 0;
 }
 
 /*
  * Nb: srcnotes are variable-length.  This function computes the number of
  * srcnote *slots*, which may be greater than the number of srcnotes.
  */
 uint32_t
 JSScript::numNotes()
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -2512,34 +2512,34 @@ CASE(JSOP_GE)
     if (!GreaterThanOrEqualOperation(cx, lval, rval, &cond))
         goto error;
     TRY_BRANCH_AFTER_COND(cond, 2);
     REGS.sp[-2].setBoolean(cond);
     REGS.sp--;
 }
 END_CASE(JSOP_GE)
 
-#define SIGNED_SHIFT_OP(OP)                                                   \
+#define SIGNED_SHIFT_OP(OP, TYPE)                                             \
     JS_BEGIN_MACRO                                                            \
         int32_t i, j;                                                         \
         if (!ToInt32(cx, REGS.stackHandleAt(-2), &i))                         \
             goto error;                                                       \
         if (!ToInt32(cx, REGS.stackHandleAt(-1), &j))                         \
             goto error;                                                       \
-        i = i OP (j & 31);                                                    \
+        i = TYPE(i) OP (j & 31);                                              \
         REGS.sp--;                                                            \
         REGS.sp[-1].setInt32(i);                                              \
     JS_END_MACRO
 
 CASE(JSOP_LSH)
-    SIGNED_SHIFT_OP(<<);
+    SIGNED_SHIFT_OP(<<, uint32_t);
 END_CASE(JSOP_LSH)
 
 CASE(JSOP_RSH)
-    SIGNED_SHIFT_OP(>>);
+    SIGNED_SHIFT_OP(>>, int32_t);
 END_CASE(JSOP_RSH)
 
 #undef SIGNED_SHIFT_OP
 
 CASE(JSOP_URSH)
 {
     HandleValue lval = REGS.stackHandleAt(-2);
     HandleValue rval = REGS.stackHandleAt(-1);
--- a/js/src/vm/NativeObject-inl.h
+++ b/js/src/vm/NativeObject-inl.h
@@ -132,20 +132,23 @@ NativeObject::elementsRangeWriteBarrierP
 }
 
 inline void
 NativeObject::copyDenseElements(uint32_t dstStart, const Value* src, uint32_t count)
 {
     MOZ_ASSERT(dstStart + count <= getDenseCapacity());
     MOZ_ASSERT(!denseElementsAreCopyOnWrite());
     MOZ_ASSERT(!denseElementsAreFrozen());
+    MOZ_ASSERT_IF(count > 0, src != nullptr);
 #ifdef DEBUG
     for (uint32_t i = 0; i < count; ++i)
         checkStoredValue(src[i]);
 #endif
+    if (count == 0)
+        return;
     if (JS::shadow::Zone::asShadowZone(zone())->needsIncrementalBarrier()) {
         uint32_t numShifted = getElementsHeader()->numShiftedElements();
         for (uint32_t i = 0; i < count; ++i) {
             elements_[dstStart + i].set(this, HeapSlot::Element,
                                         dstStart + i + numShifted,
                                         src[i]);
         }
     } else {
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -2020,31 +2020,33 @@ js::NativeHasProperty(JSContext* cx, Han
         // done can be true in exactly these unlikely-sounding cases:
         // - We're looking up an element, and pobj is a TypedArray that
         //   doesn't have that many elements.
         // - We're being called from a resolve hook to assign to the property
         //   being resolved.
         // What they all have in common is we do not want to keep walking
         // the prototype chain, and always claim that the property
         // doesn't exist.
-        RootedObject proto(cx, done ? nullptr : pobj->staticPrototype());
+        JSObject* proto = done ? nullptr : pobj->staticPrototype();
 
         // Step 8.
         if (!proto) {
             *foundp = false;
             return true;
         }
 
         // Step 7.a. If the prototype is also native, this step is a
         // recursive tail call, and we don't need to go through all the
         // plumbing of HasProperty; the top of the loop is where
         // we're going to end up anyway. But if pobj is non-native,
         // that optimization would be incorrect.
-        if (!proto->isNative())
-            return HasProperty(cx, proto, id, foundp);
+        if (!proto->isNative()) {
+            RootedObject protoRoot(cx, proto);
+            return HasProperty(cx, protoRoot, id, foundp);
+        }
 
         pobj = &proto->as<NativeObject>();
     }
 }
 
 
 /*** [[GetOwnPropertyDescriptor]] ****************************************************************/
 
--- a/js/src/vm/PIC.cpp
+++ b/js/src/vm/PIC.cpp
@@ -76,34 +76,16 @@ js::ForOfPIC::Chain::initialize(JSContex
     arrayProtoIteratorSlot_ = iterShape->slot();
     canonicalIteratorFunc_ = iterator;
     arrayIteratorProtoShape_ = arrayIteratorProto->lastProperty();
     arrayIteratorProtoNextSlot_ = nextShape->slot();
     canonicalNextFunc_ = next;
     return true;
 }
 
-js::ForOfPIC::Stub*
-js::ForOfPIC::Chain::isArrayOptimized(ArrayObject* obj)
-{
-    Stub* stub = getMatchingStub(obj);
-    if (!stub)
-        return nullptr;
-
-    // Ensure that this is an otherwise optimizable array.
-    if (!isOptimizableArray(obj))
-        return nullptr;
-
-    // Not yet enough!  Ensure that the world as we know it remains sane.
-    if (!isArrayStateStillSane())
-        return nullptr;
-
-    return stub;
-}
-
 bool
 js::ForOfPIC::Chain::tryOptimizeArray(JSContext* cx, HandleArrayObject array, bool* optimized)
 {
     MOZ_ASSERT(optimized);
 
     *optimized = false;
 
     if (!initialized_) {
@@ -122,71 +104,62 @@ js::ForOfPIC::Chain::tryOptimizeArray(JS
 
     // If PIC is disabled, don't bother trying to optimize.
     if (disabled_)
         return true;
 
     // By the time we get here, we should have a sane array state to work with.
     MOZ_ASSERT(isArrayStateStillSane());
 
+    // Ensure array's prototype is the actual Array.prototype
+    if (array->staticPrototype() != arrayProto_)
+        return true;
+
     // Check if stub already exists.
-    ForOfPIC::Stub* stub = isArrayOptimized(&array->as<ArrayObject>());
-    if (stub) {
+    if (hasMatchingStub(array)) {
         *optimized = true;
         return true;
     }
 
+    // Ensure array doesn't define @@iterator directly.
+    if (array->lookup(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator)))
+        return true;
+
     // If the number of stubs is about to exceed the limit, throw away entire
     // existing cache before adding new stubs.  We shouldn't really have heavy
     // churn on these.
     if (numStubs() >= MAX_STUBS)
         eraseChain();
 
-    // Ensure array's prototype is the actual Array.prototype
-    if (!isOptimizableArray(array))
-        return true;
-
-    // Ensure array doesn't define @@iterator directly.
-    if (array->lookup(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator)))
-        return true;
-
     // Good to optimize now, create stub to add.
     RootedShape shape(cx, array->lastProperty());
-    stub = cx->new_<Stub>(shape);
+    Stub* stub = cx->new_<Stub>(shape);
     if (!stub)
         return false;
 
     // Add the stub.
     addStub(stub);
 
     *optimized = true;
     return true;
 }
 
-js::ForOfPIC::Stub*
-js::ForOfPIC::Chain::getMatchingStub(JSObject* obj)
+bool
+js::ForOfPIC::Chain::hasMatchingStub(ArrayObject* obj)
 {
     // Ensure PIC is initialized and not disabled.
-    if (!initialized_ || disabled_)
-        return nullptr;
+    MOZ_ASSERT(initialized_ && !disabled_);
 
     // Check if there is a matching stub.
     for (Stub* stub = stubs(); stub != nullptr; stub = stub->next()) {
-        if (stub->shape() == obj->maybeShape())
-            return stub;
+        if (stub->shape() == obj->shape())
+            return true;
     }
 
-    return nullptr;
-}
-
-bool
-js::ForOfPIC::Chain::isOptimizableArray(JSObject* obj)
-{
-    MOZ_ASSERT(obj->is<ArrayObject>());
-    return obj->staticPrototype() == arrayProto_;
+    return false;
 }
 
 bool
 js::ForOfPIC::Chain::isArrayStateStillSane()
 {
     // Ensure that canonical Array.prototype has matching shape.
     if (arrayProto_->lastProperty() != arrayProtoShape_)
         return false;
--- a/js/src/vm/PIC.h
+++ b/js/src/vm/PIC.h
@@ -214,19 +214,16 @@ struct ForOfPIC
             arrayIteratorProtoNextSlot_(-1),
             initialized_(false),
             disabled_(false)
         {}
 
         // Initialize the canonical iterator function.
         bool initialize(JSContext* cx);
 
-        // Check if a given array object is optimized by this PIC.
-        Stub* isArrayOptimized(ArrayObject* obj);
-
         // Try to optimize this chain for an object.
         bool tryOptimizeArray(JSContext* cx, HandleArrayObject array, bool* optimized);
 
         // Check if the global array-related objects have not been messed with
         // in a way that would disable this PIC.
         bool isArrayStateStillSane();
 
         // Check if ArrayIterator.next is still optimizable.
@@ -234,21 +231,18 @@ struct ForOfPIC
             return (arrayIteratorProto_->lastProperty() == arrayIteratorProtoShape_) &&
                 (arrayIteratorProto_->getSlot(arrayIteratorProtoNextSlot_) == canonicalNextFunc_);
         }
 
         void trace(JSTracer* trc);
         void sweep(FreeOp* fop);
 
       private:
-        // Get a matching optimized stub for the given object.
-        Stub* getMatchingStub(JSObject* obj);
-
-        // Check if the given object is for-of optimizable with this PIC.
-        bool isOptimizableArray(JSObject* obj);
+        // Check if a matching optimized stub for the given object exists.
+        bool hasMatchingStub(ArrayObject* obj);
 
         // Reset the PIC and all info associated with it.
         void reset(JSContext* cx);
 
         // Erase the stub chain.
         void eraseChain();
     };
 
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -1881,39 +1881,39 @@ class MOZ_STACK_CLASS ModuleValidator
     bool atomicsPresent() const              { return atomicsPresent_; }
     uint32_t minMemoryLength() const         { return mg_.minMemoryLength(); }
 
     void initModuleFunctionName(PropertyName* name) {
         MOZ_ASSERT(!moduleFunctionName_);
         moduleFunctionName_ = name;
     }
     MOZ_MUST_USE bool initGlobalArgumentName(PropertyName* n) {
-        MOZ_ASSERT(n->isTenured());
         globalArgumentName_ = n;
         if (n) {
+            MOZ_ASSERT(n->isTenured());
             asmJSMetadata_->globalArgumentName = StringToNewUTF8CharsZ(cx_, *n);
             if (!asmJSMetadata_->globalArgumentName)
                 return false;
         }
         return true;
     }
     MOZ_MUST_USE bool initImportArgumentName(PropertyName* n) {
-        MOZ_ASSERT(n->isTenured());
         importArgumentName_ = n;
         if (n) {
+            MOZ_ASSERT(n->isTenured());
             asmJSMetadata_->importArgumentName = StringToNewUTF8CharsZ(cx_, *n);
             if (!asmJSMetadata_->importArgumentName)
                 return false;
         }
         return true;
     }
     MOZ_MUST_USE bool initBufferArgumentName(PropertyName* n) {
-        MOZ_ASSERT(n->isTenured());
         bufferArgumentName_ = n;
         if (n) {
+            MOZ_ASSERT(n->isTenured());
             asmJSMetadata_->bufferArgumentName = StringToNewUTF8CharsZ(cx_, *n);
             if (!asmJSMetadata_->bufferArgumentName)
                 return false;
         }
         return true;
     }
     bool addGlobalVarInit(PropertyName* var, const NumLit& lit, Type type, bool isConst) {
         MOZ_ASSERT(type.isGlobalVarType());
new file mode 100644
--- /dev/null
+++ b/layout/style/crashtests/1399006.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<script>
+window.onload=function(){
+  let n=document.getElementById('a');
+  n.parentNode.removeChild(n);
+  document.designMode='on';
+}
+</script>
+<wbr/>
+<script></script>
+<img space='-155.002502498'/>
+<blockquote style="display:ruby-text-container">
+<mark id='a'></mark>
+</blockquote>
+<video></video>
+<area/>
+<section></section>
+<img/>
+<noscript></noscript>
+<bdo></bdo>
+<select></select>
+<select></select>
+<option></option>
+<figure style="border-image:stretch url(data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=) 27% fill / / 297.456633622ex"></figure>
+<wbr/>
+<pre></pre>
+<table>
+</html>
\ No newline at end of file
--- a/layout/style/crashtests/crashtests.list
+++ b/layout/style/crashtests/crashtests.list
@@ -213,18 +213,19 @@ load 1384232.html
 load 1395725.html
 load 1396041.html
 load 1397363-1.html
 load 1397439-1.html
 load 1395719.html
 load 1397091.html
 load 1398479.html
 load 1398581.html
+load 1399006.html
+load 1399546.html
 load 1400035.html
-load 1399546.html
 load 1400325.html
 load 1400926.html
 load 1401256.html
 load 1402419.html
 load 1401706.html
 load 1400936-1.html
 load 1400936-2.html
 load 1402472.html
--- a/netwerk/cookie/CookieServiceChild.cpp
+++ b/netwerk/cookie/CookieServiceChild.cpp
@@ -173,17 +173,18 @@ CookieServiceChild::RecvAddCookie(const 
                                              aCookie.host(),
                                              aCookie.path(),
                                              aCookie.expiry(),
                                              aCookie.lastAccessed(),
                                              aCookie.creationTime(),
                                              aCookie.isSession(),
                                              aCookie.isSecure(),
                                              false,
-                                             aAttrs);
+                                             aAttrs,
+                                             aCookie.sameSite());
   RecordDocumentCookie(cookie, aAttrs);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 CookieServiceChild::RecvRemoveBatchDeletedCookies(nsTArray<CookieStruct>&& aCookiesList,
                                                   nsTArray<OriginAttributes>&& aAttrsList)
 {
@@ -205,17 +206,18 @@ CookieServiceChild::RecvTrackCookiesLoad
                                                aCookiesList[i].host(),
                                                aCookiesList[i].path(),
                                                aCookiesList[i].expiry(),
                                                aCookiesList[i].lastAccessed(),
                                                aCookiesList[i].creationTime(),
                                                aCookiesList[i].isSession(),
                                                aCookiesList[i].isSecure(),
                                                false,
-                                               aAttrs);
+                                               aAttrs,
+                                               aCookiesList[i].sameSite());
     RecordDocumentCookie(cookie, aAttrs);
   }
 
   return IPC_OK();
 }
 
 void
 CookieServiceChild::PrefChanged(nsIPrefBranch *aPrefBranch)
@@ -360,17 +362,18 @@ CookieServiceChild::SetCookieInternal(ns
                      aCookieAttributes.host,
                      aCookieAttributes.path,
                      aCookieAttributes.expiryTime,
                      currentTimeInUsec,
                      nsCookie::GenerateUniqueCreationTime(currentTimeInUsec),
                      aCookieAttributes.isSession,
                      aCookieAttributes.isSecure,
                      aCookieAttributes.isHttpOnly,
-                     aAttrs);
+                     aAttrs,
+                     aCookieAttributes.sameSite);
 
   RecordDocumentCookie(cookie, aAttrs);
 }
 
 bool
 CookieServiceChild::RequireThirdPartyCheck()
 {
   return mCookieBehavior == nsICookieService::BEHAVIOR_REJECT_FOREIGN ||
--- a/netwerk/cookie/CookieServiceParent.cpp
+++ b/netwerk/cookie/CookieServiceParent.cpp
@@ -86,16 +86,17 @@ GetInfoFromCookie(nsCookie         *aCoo
   aCookieStruct.value() = aCookie->Value();
   aCookieStruct.host() = aCookie->Host();
   aCookieStruct.path() = aCookie->Path();
   aCookieStruct.expiry() = aCookie->Expiry();
   aCookieStruct.lastAccessed() = aCookie->LastAccessed();
   aCookieStruct.creationTime() = aCookie->CreationTime();
   aCookieStruct.isSession() = aCookie->IsSession();
   aCookieStruct.isSecure() = aCookie->IsSecure();
+  aCookieStruct.sameSite() = aCookie->SameSite();
 }
 
 void
 CookieServiceParent::RemoveBatchDeletedCookies(nsIArray *aCookieList) {
   uint32_t len = 0;
   aCookieList->GetLength(&len);
   OriginAttributes attrs;
   CookieStruct cookieStruct;
@@ -181,16 +182,17 @@ CookieServiceParent::SerialializeCookieL
     cookieStruct->value() = cookie->Value();
     cookieStruct->host() = cookie->Host();
     cookieStruct->path() = cookie->Path();
     cookieStruct->expiry() = cookie->Expiry();
     cookieStruct->lastAccessed() = cookie->LastAccessed();
     cookieStruct->creationTime() = cookie->CreationTime();
     cookieStruct->isSession() = cookie->IsSession();
     cookieStruct->isSecure() = cookie->IsSecure();
+    cookieStruct->sameSite() = cookie->SameSite();
   }
 }
 
 mozilla::ipc::IPCResult
 CookieServiceParent::RecvPrepareCookieList(const URIParams        &aHost,
                                            const bool             &aIsForeign,
                                            const OriginAttributes &aAttrs)
 {
--- a/netwerk/cookie/nsCookie.cpp
+++ b/netwerk/cookie/nsCookie.cpp
@@ -74,17 +74,18 @@ nsCookie::Create(const nsACString &aName
                  const nsACString &aHost,
                  const nsACString &aPath,
                  int64_t           aExpiry,
                  int64_t           aLastAccessed,
                  int64_t           aCreationTime,
                  bool              aIsSession,
                  bool              aIsSecure,
                  bool              aIsHttpOnly,
-                 const OriginAttributes& aOriginAttributes)
+                 const OriginAttributes& aOriginAttributes,
+                 int32_t           aSameSite)
 {
   // Ensure mValue contains a valid UTF-8 sequence. Otherwise XPConnect will
   // truncate the string after the first invalid octet.
   RefPtr<nsUTF8ConverterService> converter = new nsUTF8ConverterService();
   nsAutoCString aUTF8Value;
   converter->ConvertStringToUTF8(aValue, "UTF-8", false, true, 1, aUTF8Value);
 
   // find the required string buffer size, adding 4 for the terminating nulls
@@ -103,21 +104,26 @@ nsCookie::Create(const nsACString &aName
   StrBlockCopy(aName, aUTF8Value, aHost, aPath,
                name, value, host, path, end);
 
   // If the creationTime given to us is higher than the running maximum, update
   // our maximum.
   if (aCreationTime > gLastCreationTime)
     gLastCreationTime = aCreationTime;
 
+  // If aSameSite is not a sensible value, assume strict
+  if (aSameSite < 0 || aSameSite > nsICookie2::SAMESITE_STRICT) {
+    aSameSite = nsICookie2::SAMESITE_STRICT;
+  }
+
   // construct the cookie. placement new, oh yeah!
   return new (place) nsCookie(name, value, host, path, end,
                               aExpiry, aLastAccessed, aCreationTime,
                               aIsSession, aIsSecure, aIsHttpOnly,
-                              aOriginAttributes);
+                              aOriginAttributes, aSameSite);
 }
 
 size_t
 nsCookie::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 {
     // There is no need to measure the sizes of the individual string
     // members, since the strings are stored in-line with the nsCookie.
     return aMallocSizeOf(this);
@@ -146,16 +152,17 @@ NS_IMETHODIMP nsCookie::GetExpiry(int64_
 NS_IMETHODIMP nsCookie::GetIsSession(bool *aIsSession)   { *aIsSession = IsSession(); return NS_OK; }
 NS_IMETHODIMP nsCookie::GetIsDomain(bool *aIsDomain)     { *aIsDomain = IsDomain();   return NS_OK; }
 NS_IMETHODIMP nsCookie::GetIsSecure(bool *aIsSecure)     { *aIsSecure = IsSecure();   return NS_OK; }
 NS_IMETHODIMP nsCookie::GetIsHttpOnly(bool *aHttpOnly)   { *aHttpOnly = IsHttpOnly(); return NS_OK; }
 NS_IMETHODIMP nsCookie::GetStatus(nsCookieStatus *aStatus) { *aStatus = 0;              return NS_OK; }
 NS_IMETHODIMP nsCookie::GetPolicy(nsCookiePolicy *aPolicy) { *aPolicy = 0;              return NS_OK; }
 NS_IMETHODIMP nsCookie::GetCreationTime(int64_t *aCreation){ *aCreation = CreationTime(); return NS_OK; }
 NS_IMETHODIMP nsCookie::GetLastAccessed(int64_t *aTime)    { *aTime = LastAccessed();   return NS_OK; }
+NS_IMETHODIMP nsCookie::GetSameSite(int32_t *aSameSite)    { *aSameSite = SameSite();   return NS_OK; }
 
 NS_IMETHODIMP
 nsCookie::GetOriginAttributes(JSContext *aCx, JS::MutableHandle<JS::Value> aVal)
 {
   if (NS_WARN_IF(!ToJSValue(aCx, mOriginAttributes, aVal))) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
--- a/netwerk/cookie/nsCookie.h
+++ b/netwerk/cookie/nsCookie.h
@@ -43,29 +43,31 @@ class nsCookie : public nsICookie2
              const char     *aPath,
              const char     *aEnd,
              int64_t         aExpiry,
              int64_t         aLastAccessed,
              int64_t         aCreationTime,
              bool            aIsSession,
              bool            aIsSecure,
              bool            aIsHttpOnly,
-             const OriginAttributes& aOriginAttributes)
+             const OriginAttributes& aOriginAttributes,
+             int32_t         aSameSite)
      : mName(aName)
      , mValue(aValue)
      , mHost(aHost)
      , mPath(aPath)
      , mEnd(aEnd)
      , mExpiry(aExpiry)
      , mLastAccessed(aLastAccessed)
      , mCreationTime(aCreationTime)
      , mIsSession(aIsSession)
      , mIsSecure(aIsSecure)
      , mIsHttpOnly(aIsHttpOnly)
      , mOriginAttributes(aOriginAttributes)
+     , mSameSite(aSameSite)
     {
     }
 
     static int CookieStaleThreshold()
     {
       static bool initialized = false;
       static int value = 60;
       if (!initialized) {
@@ -87,17 +89,18 @@ class nsCookie : public nsICookie2
                              const nsACString &aHost,
                              const nsACString &aPath,
                              int64_t           aExpiry,
                              int64_t           aLastAccessed,
                              int64_t           aCreationTime,
                              bool              aIsSession,
                              bool              aIsSecure,
                              bool              aIsHttpOnly,
-                             const OriginAttributes& aOriginAttributes);
+                             const OriginAttributes& aOriginAttributes,
+                             int32_t           aSameSite);
 
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
     // fast (inline, non-xpcom) getters
     inline const nsDependentCString Name()  const { return nsDependentCString(mName, mValue - 1); }
     inline const nsDependentCString Value() const { return nsDependentCString(mValue, mHost - 1); }
     inline const nsDependentCString Host()  const { return nsDependentCString(mHost, mPath - 1); }
     inline const nsDependentCString RawHost() const { return nsDependentCString(IsDomain() ? mHost + 1 : mHost, mPath - 1); }
@@ -105,16 +108,17 @@ class nsCookie : public nsICookie2
     inline int64_t Expiry()                 const { return mExpiry; }        // in seconds
     inline int64_t LastAccessed()           const { return mLastAccessed; }  // in microseconds
     inline int64_t CreationTime()           const { return mCreationTime; }  // in microseconds
     inline bool IsSession()               const { return mIsSession; }
     inline bool IsDomain()                const { return *mHost == '.'; }
     inline bool IsSecure()                const { return mIsSecure; }
     inline bool IsHttpOnly()              const { return mIsHttpOnly; }
     inline const OriginAttributes& OriginAttributesRef() const { return mOriginAttributes; }
+    inline int32_t SameSite()               const { return mSameSite; }
 
     // setters
     inline void SetExpiry(int64_t aExpiry)        { mExpiry = aExpiry; }
     inline void SetLastAccessed(int64_t aTime)    { mLastAccessed = aTime; }
     inline void SetIsSession(bool aIsSession)     { mIsSession = aIsSession; }
     // Set the creation time manually, overriding the monotonicity checks in
     // Create(). Use with caution!
     inline void SetCreationTime(int64_t aTime)    { mCreationTime = aTime; }
@@ -139,16 +143,17 @@ class nsCookie : public nsICookie2
     const char  *mEnd;
     int64_t      mExpiry;
     int64_t      mLastAccessed;
     int64_t      mCreationTime;
     bool mIsSession;
     bool mIsSecure;
     bool mIsHttpOnly;
     mozilla::OriginAttributes mOriginAttributes;
+    int32_t     mSameSite;
 };
 
 // Comparator class for sorting cookies before sending to a server.
 class CompareCookiesForSending
 {
 public:
   bool Equals(const nsCookie* aCookie1, const nsCookie* aCookie2) const
   {
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -75,31 +75,32 @@ using namespace mozilla::net;
 
 static StaticRefPtr<nsCookieService> gCookieService;
 
 // XXX_hack. See bug 178993.
 // This is a hack to hide HttpOnly cookies from older browsers
 #define HTTP_ONLY_PREFIX "#HttpOnly_"
 
 #define COOKIES_FILE "cookies.sqlite"
-#define COOKIES_SCHEMA_VERSION 8
+#define COOKIES_SCHEMA_VERSION 9
 
 // parameter indexes; see EnsureReadDomain, EnsureReadComplete and
 // ReadCookieDBListener::HandleResult
 #define IDX_NAME 0
 #define IDX_VALUE 1
 #define IDX_HOST 2
 #define IDX_PATH 3
 #define IDX_EXPIRY 4
 #define IDX_LAST_ACCESSED 5
 #define IDX_CREATION_TIME 6
 #define IDX_SECURE 7
 #define IDX_HTTPONLY 8
 #define IDX_BASE_DOMAIN 9
 #define IDX_ORIGIN_ATTRIBUTES 10
+#define IDX_SAME_SITE 11
 
 #define TOPIC_CLEAR_ORIGIN_DATA "clear-origin-attributes-data"
 
 static const int64_t kCookiePurgeAge =
   int64_t(30 * 24 * 60 * 60) * PR_USEC_PER_SEC; // 30 days in microseconds
 
 #define OLD_COOKIE_FILE_NAME "cookies.txt"
 
@@ -1386,16 +1387,26 @@ nsCookieService::TryInitDB(bool aRecreat
 
         // Recreate our index.
         rv = CreateIndex();
         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
 
         COOKIE_LOGSTRING(LogLevel::Debug,
           ("Upgraded database to schema version 8"));
       }
+      MOZ_FALLTHROUGH;
+
+    case 8:
+      {
+        // Add the sameSite column to the table.
+        rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(
+          NS_LITERAL_CSTRING("ALTER TABLE moz_cookies ADD sameSite INTEGER"));
+        COOKIE_LOGSTRING(LogLevel::Debug,
+          ("Upgraded database to schema version 9"));
+      }
 
       // No more upgrades. Update the schema version.
       rv = mDefaultDBState->dbConn->SetSchemaVersion(COOKIES_SCHEMA_VERSION);
       NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
       MOZ_FALLTHROUGH;
 
     case COOKIES_SCHEMA_VERSION:
       break;
@@ -1433,17 +1444,18 @@ nsCookieService::TryInitDB(bool aRecreat
             "name, "
             "value, "
             "host, "
             "path, "
             "expiry, "
             "lastAccessed, "
             "creationTime, "
             "isSecure, "
-            "isHttpOnly "
+            "isHttpOnly, "
+            "sameSite "
           "FROM moz_cookies"), getter_AddRefs(stmt));
         if (NS_SUCCEEDED(rv))
           break;
 
         // our columns aren't there - drop the table!
         rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
           "DROP TABLE moz_cookies"));
         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
@@ -1474,29 +1486,31 @@ nsCookieService::TryInitDB(bool aRecreat
       "name, "
       "value, "
       "host, "
       "path, "
       "expiry, "
       "lastAccessed, "
       "creationTime, "
       "isSecure, "
-      "isHttpOnly"
+      "isHttpOnly, "
+      "sameSite "
     ") VALUES ("
       ":baseDomain, "
       ":originAttributes, "
       ":name, "
       ":value, "
       ":host, "
       ":path, "
       ":expiry, "
       ":lastAccessed, "
       ":creationTime, "
       ":isSecure, "
-      ":isHttpOnly"
+      ":isHttpOnly, "
+      ":sameSite"
     ")"),
     getter_AddRefs(mDefaultDBState->stmtInsert));
   NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
 
   rv = mDefaultDBState->dbConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
     "DELETE FROM moz_cookies "
     "WHERE name = :name AND host = :host AND path = :path AND originAttributes = :originAttributes"),
     getter_AddRefs(mDefaultDBState->stmtDelete));
@@ -1553,16 +1567,17 @@ nsCookieService::CreateTableWorker(const
       "host TEXT, "
       "path TEXT, "
       "expiry INTEGER, "
       "lastAccessed INTEGER, "
       "creationTime INTEGER, "
       "isSecure INTEGER, "
       "isHttpOnly INTEGER, "
       "inBrowserElement INTEGER DEFAULT 0, "
+      "sameSite INTEGER DEFAULT 0, "
       "CONSTRAINT moz_uniqueid UNIQUE (name, host, path, originAttributes)"
     ")");
   return mDefaultDBState->dbConn->ExecuteSimpleSQL(command);
 }
 
 // Sets the schema version and creates the moz_cookies table.
 nsresult
 nsCookieService::CreateTable()
@@ -2471,44 +2486,46 @@ nsCookieService::Add(const nsACString &a
                      const nsACString &aPath,
                      const nsACString &aName,
                      const nsACString &aValue,
                      bool              aIsSecure,
                      bool              aIsHttpOnly,
                      bool              aIsSession,
                      int64_t           aExpiry,
                      JS::HandleValue   aOriginAttributes,
+                     int32_t           aSameSite,
                      JSContext*        aCx,
                      uint8_t           aArgc)
 {
   MOZ_ASSERT(aArgc == 0 || aArgc == 1);
 
   OriginAttributes attrs;
   nsresult rv = InitializeOriginAttributes(&attrs,
                                            aOriginAttributes,
                                            aCx,
                                            aArgc,
                                            u"nsICookieManager2.add()",
                                            u"2");
   NS_ENSURE_SUCCESS(rv, rv);
 
   return AddNative(aHost, aPath, aName, aValue, aIsSecure, aIsHttpOnly,
-                   aIsSession, aExpiry, &attrs);
+                   aIsSession, aExpiry, &attrs, aSameSite);
 }
 
 NS_IMETHODIMP_(nsresult)
 nsCookieService::AddNative(const nsACString &aHost,
                            const nsACString &aPath,
                            const nsACString &aName,
                            const nsACString &aValue,
                            bool              aIsSecure,
                            bool              aIsHttpOnly,
                            bool              aIsSession,
                            int64_t           aExpiry,
-                           OriginAttributes* aOriginAttributes)
+                           OriginAttributes* aOriginAttributes,
+                           int32_t           aSameSite)
 {
   if (NS_WARN_IF(!aOriginAttributes)) {
     return NS_ERROR_FAILURE;
   }
 
   if (!mDBState) {
     NS_WARNING("No DBState! Profile already closed?");
     return NS_ERROR_NOT_AVAILABLE;
@@ -2534,17 +2551,18 @@ nsCookieService::AddNative(const nsACStr
   RefPtr<nsCookie> cookie =
     nsCookie::Create(aName, aValue, host, aPath,
                      aExpiry,
                      currentTimeInUsec,
                      nsCookie::GenerateUniqueCreationTime(currentTimeInUsec),
                      aIsSession,
                      aIsSecure,
                      aIsHttpOnly,
-                     key.mOriginAttributes);
+                     key.mOriginAttributes,
+                     aSameSite);
   if (!cookie) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   AddInternal(key, cookie, currentTimeInUsec, nullptr, nullptr, true);
   return NS_OK;
 }
 
@@ -2666,17 +2684,18 @@ nsCookieService::Read()
       "host, "
       "path, "
       "expiry, "
       "lastAccessed, "
       "creationTime, "
       "isSecure, "
       "isHttpOnly, "
       "baseDomain, "
-      "originAttributes "
+      "originAttributes, "
+      "sameSite "
     "FROM moz_cookies "
     "WHERE baseDomain NOTNULL"), getter_AddRefs(stmtRead));
   NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
 
   // Set up a statement to delete any rows with a nullptr 'baseDomain'
   // column. This takes care of any cookies set by browsers that don't
   // understand the 'baseDomain' column, where the database schema version
   // is from one that does. (This would occur when downgrading.)
@@ -2727,26 +2746,28 @@ nsCookieService::GetCookieFromRow(T &aRo
   rv = aRow->GetUTF8String(IDX_PATH, path);
   NS_ASSERT_SUCCESS(rv);
 
   int64_t expiry = aRow->AsInt64(IDX_EXPIRY);
   int64_t lastAccessed = aRow->AsInt64(IDX_LAST_ACCESSED);
   int64_t creationTime = aRow->AsInt64(IDX_CREATION_TIME);
   bool isSecure = 0 != aRow->AsInt32(IDX_SECURE);
   bool isHttpOnly = 0 != aRow->AsInt32(IDX_HTTPONLY);
+  int32_t sameSite = aRow->AsInt32(IDX_SAME_SITE);
 
   // Create a new nsCookie and assign the data.
   return nsCookie::Create(name, value, host, path,
                           expiry,
                           lastAccessed,
                           creationTime,
                           false,
                           isSecure,
                           isHttpOnly,
-                          aOriginAttributes);
+                          aOriginAttributes,
+                          sameSite);
 }
 
 void
 nsCookieService::AsyncReadComplete()
 {
   // We may be in the private browsing DB state, with a pending read on the
   // default DB state. (This would occur if we started up in private browsing
   // mode.) As long as we do all our operations on the default state, we're OK.
@@ -2845,17 +2866,20 @@ nsCookieService::EnsureReadDomain(const 
         "name, "
         "value, "
         "host, "
         "path, "
         "expiry, "
         "lastAccessed, "
         "creationTime, "
         "isSecure, "
-        "isHttpOnly "
+        "isHttpOnly, "
+        "baseDomain, "
+        "originAttributes, "
+        "sameSite "
       "FROM moz_cookies "
       "WHERE baseDomain = :baseDomain "
       "  AND originAttributes = :originAttributes"),
       getter_AddRefs(mDefaultDBState->stmtReadDomain));
 
     if (NS_FAILED(rv)) {
       // Recreate the database.
       COOKIE_LOGSTRING(LogLevel::Debug,
@@ -2942,17 +2966,18 @@ nsCookieService::EnsureReadComplete()
       "host, "
       "path, "
       "expiry, "
       "lastAccessed, "
       "creationTime, "
       "isSecure, "
       "isHttpOnly, "
       "baseDomain, "
-      "originAttributes  "
+      "originAttributes, "
+      "sameSite "
     "FROM moz_cookies "
     "WHERE baseDomain NOTNULL"), getter_AddRefs(stmt));
 
   if (NS_FAILED(rv)) {
     // Recreate the database.
     COOKIE_LOGSTRING(LogLevel::Debug,
       ("EnsureReadComplete(): corruption detected when creating statement "
        "with rv 0x%" PRIx32, static_cast<uint32_t>(rv)));
@@ -3146,17 +3171,18 @@ nsCookieService::ImportCookies(nsIFile *
                        host,
                        Substring(buffer, pathIndex, secureIndex - pathIndex - 1),
                        expires,
                        lastAccessedCounter,
                        nsCookie::GenerateUniqueCreationTime(currentTimeInUsec),
                        false,
                        Substring(buffer, secureIndex, expiresIndex - secureIndex - 1).EqualsLiteral(kTrue),
                        isHttpOnly,
-                       key.mOriginAttributes);
+                       key.mOriginAttributes,
+                       nsICookie2::SAMESITE_UNSET);
     if (!newCookie) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
     // trick: preserve the most-recently-used cookie ordering,
     // by successively decrementing the lastAccessed time
     lastAccessedCounter--;
 
@@ -3596,17 +3622,18 @@ nsCookieService::SetCookieInternal(nsIUR
                      cookieAttributes.host,
                      cookieAttributes.path,
                      cookieAttributes.expiryTime,
                      currentTimeInUsec,
                      nsCookie::GenerateUniqueCreationTime(currentTimeInUsec),
                      cookieAttributes.isSession,
                      cookieAttributes.isSecure,
                      cookieAttributes.isHttpOnly,
-                     aKey.mOriginAttributes);
+                     aKey.mOriginAttributes,
+                     cookieAttributes.sameSite);
   if (!cookie)
     return newCookie;
 
   // check permissions from site permission list, or ask the user,
   // to determine if we can set the cookie
   if (mPermissionService) {
     bool permission;
     mPermissionService->CanSetCookie(aHostURI,
@@ -3988,24 +4015,27 @@ nsCookieService::ParseAttributes(nsDepen
                                  nsCookieAttributes &aCookieAttributes)
 {
   static const char kPath[]    = "path";
   static const char kDomain[]  = "domain";
   static const char kExpires[] = "expires";
   static const char kMaxage[]  = "max-age";
   static const char kSecure[]  = "secure";
   static const char kHttpOnly[]  = "httponly";
+  static const char kSameSite[]       = "samesite";
+  static const char kSameSiteLax[]    = "lax";
 
   nsACString::const_char_iterator tempBegin, tempEnd;
   nsACString::const_char_iterator cookieStart, cookieEnd;
   aCookieHeader.BeginReading(cookieStart);
   aCookieHeader.EndReading(cookieEnd);
 
   aCookieAttributes.isSecure = false;
   aCookieAttributes.isHttpOnly = false;
+  aCookieAttributes.sameSite = nsICookie2::SAMESITE_UNSET;
 
   nsDependentCSubstring tokenString(cookieStart, cookieStart);
   nsDependentCSubstring tokenValue (cookieStart, cookieStart);
   bool newCookie, equalsFound;
 
   // extract cookie <NAME> & <VALUE> (first attribute), and copy the strings.
   // if we find multiple cookies, return for processing
   // note: if there's no '=', we assume token is <VALUE>. this is required by
@@ -4044,16 +4074,24 @@ nsCookieService::ParseAttributes(nsDepen
     // ignore any tokenValue for isSecure; just set the boolean
     else if (tokenString.LowerCaseEqualsLiteral(kSecure))
       aCookieAttributes.isSecure = true;
 
     // ignore any tokenValue for isHttpOnly (see bug 178993);
     // just set the boolean
     else if (tokenString.LowerCaseEqualsLiteral(kHttpOnly))
       aCookieAttributes.isHttpOnly = true;
+
+    else if (tokenString.LowerCaseEqualsLiteral(kSameSite)) {
+      if (tokenValue.LowerCaseEqualsLiteral(kSameSiteLax)) {
+        aCookieAttributes.sameSite = nsICookie2::SAMESITE_LAX;
+      } else {
+        aCookieAttributes.sameSite = nsICookie2::SAMESITE_STRICT;
+      }
+    }
   }
 
   // rebind aCookieHeader, in case we need to process another cookie
   aCookieHeader.Rebind(cookieStart, cookieEnd);
   return newCookie;
 }
 
 /******************************************************************************
@@ -5208,16 +5246,20 @@ bindCookieParameters(mozIStorageBindingP
   rv = params->BindInt32ByName(NS_LITERAL_CSTRING("isSecure"),
                                aCookie->IsSecure());
   NS_ASSERT_SUCCESS(rv);
 
   rv = params->BindInt32ByName(NS_LITERAL_CSTRING("isHttpOnly"),
                                aCookie->IsHttpOnly());
   NS_ASSERT_SUCCESS(rv);
 
+  rv = params->BindInt32ByName(NS_LITERAL_CSTRING("sameSite"),
+                               aCookie->SameSite());
+  NS_ASSERT_SUCCESS(rv);
+
   // Bind the params to the array.
   rv = aParamsArray->AddParams(params);
   NS_ASSERT_SUCCESS(rv);
 }
 
 void
 nsCookieService::UpdateCookieOldestTime(DBState* aDBState,
                                         nsCookie* aCookie)
--- a/netwerk/cookie/nsCookieService.h
+++ b/netwerk/cookie/nsCookieService.h
@@ -190,16 +190,17 @@ struct nsCookieAttributes
   nsAutoCString host;
   nsAutoCString path;
   nsAutoCString expires;
   nsAutoCString maxage;
   int64_t expiryTime;
   bool isSession;
   bool isSecure;
   bool isHttpOnly;
+  int8_t sameSite;
 };
 
 class nsCookieService final : public nsICookieService
                             , public nsICookieManager2
                             , public nsIObserver
                             , public nsSupportsWeakReference
                             , public nsIMemoryReporter
 {
--- a/netwerk/cookie/nsICookie2.idl
+++ b/netwerk/cookie/nsICookie2.idl
@@ -6,20 +6,23 @@
 #include "nsICookie.idl"
 
 /** 
  * Main cookie object interface for use by consumers:
  * extends nsICookie, a frozen interface for external
  * access of cookie objects
  */
 
-[scriptable, uuid(05c420e5-03d0-4c7b-a605-df7ebe5ca326)]
+[scriptable, uuid(be205dae-4f4c-11e6-80ba-ea5cd310c1a8)]
 
 interface nsICookie2 : nsICookie
 {
+    const uint32_t SAMESITE_UNSET  = 0;
+    const uint32_t SAMESITE_LAX    = 1;
+    const uint32_t SAMESITE_STRICT = 2;
 
     /**
      * the host (possibly fully qualified) of the cookie,
      * without a leading dot to represent if it is a
      * domain cookie.
      */
     readonly attribute AUTF8String rawHost;
 
@@ -55,9 +58,20 @@ interface nsICookie2 : nsICookie
      * the last time the cookie was accessed (i.e. created,
      * modified, or read by the server), in microseconds
      * since midnight (00:00:00), January 1, 1970 UTC.
      *
      * note that this time may be approximate.
      */
     readonly attribute int64_t lastAccessed;
 
+    /**
+     * the sameSite attribute; this controls the cookie behavior for cross-site
+     * requests as per
+     * https://tools.ietf.org/html/draft-west-first-party-cookies-07
+     *
+     * This should be one of:
+     * - SAMESITE_UNSET - the SameSite attribute is not present
+     * - SAMESITE_LAX - the SameSite attribute is present, but not strict
+     * - SAMESITE_STRICT - the SameSite attribute is present and strict
+     */
+    readonly attribute int32_t sameSite;
 };
--- a/netwerk/cookie/nsICookieManager2.idl
+++ b/netwerk/cookie/nsICookieManager2.idl
@@ -41,40 +41,46 @@ interface nsICookieManager2 : nsICookieM
    *        modified by, an http connection.
    * @param aIsSession
    *        true if the cookie should exist for the current session only.
    *        see aExpiry.
    * @param aExpiry
    *        expiration date, in seconds since midnight (00:00:00), January 1,
    *        1970 UTC. note that expiry time will also be honored for session cookies;
    *        in this way, the more restrictive of the two will take effect.
-   * @param aOriginAttributes The originAttributes of this cookie. This
-   *                          attribute is optional to avoid breaking add-ons.
+   * @param aOriginAttributes
+   *        the originAttributes of this cookie. This attribute is optional to
+   *        avoid breaking add-ons.
+   * @param aSameSite
+   *        the SameSite attribute. This attribute is optional to avoid breaking
+   *        addons
    */
   [implicit_jscontext, optional_argc]
   void add(in AUTF8String aHost,
            in AUTF8String aPath,
            in ACString    aName,
            in ACString    aValue,
            in boolean     aIsSecure,
            in boolean     aIsHttpOnly,
            in boolean     aIsSession,
            in int64_t     aExpiry,
-           [optional] in jsval aOriginAttributes);
+           [optional] in jsval aOriginAttributes,
+           [optional] in int32_t aSameSite);
 
   [notxpcom]
   nsresult addNative(in AUTF8String aHost,
                      in AUTF8String aPath,
                      in ACString    aName,
                      in ACString    aValue,
                      in boolean     aIsSecure,
                      in boolean     aIsHttpOnly,
                      in boolean     aIsSession,
                      in int64_t     aExpiry,
-                     in OriginAttributesPtr aOriginAttributes);
+                     in OriginAttributesPtr aOriginAttributes,
+                     in int32_t aSameSite);
 
   /**
    * Find whether a given cookie already exists.
    *
    * @param aCookie
    *        the cookie to look for
    * @param aOriginAttributes
    *        nsICookie2 contains an originAttributes but if nsICookie2 is
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -207,12 +207,13 @@ struct CookieStruct
   nsCString value;
   nsCString host;
   nsCString path;
   int64_t   expiry;
   int64_t   lastAccessed;
   int64_t   creationTime;
   bool      isSession;
   bool      isSecure;
+  int8_t    sameSite;
 };
 
 } // namespace ipc
 } // namespace mozilla
--- a/netwerk/test/TestCookie.cpp
+++ b/netwerk/test/TestCookie.cpp
@@ -629,35 +629,38 @@ TEST(TestCookie,TestCookieMain)
     EXPECT_TRUE(NS_SUCCEEDED(cookieMgr2->AddNative(NS_LITERAL_CSTRING("cookiemgr.test"), // domain
                                                    NS_LITERAL_CSTRING("/foo"),           // path
                                                    NS_LITERAL_CSTRING("test1"),          // name
                                                    NS_LITERAL_CSTRING("yes"),            // value
                                                    false,                             // is secure
                                                    false,                             // is httponly
                                                    true,                              // is session
                                                    INT64_MAX,                            // expiry time
-                                                   &attrs)));                         // originAttributes
+                                                   &attrs,                            // originAttributes
+                                                   nsICookie2::SAMESITE_UNSET)));
     EXPECT_TRUE(NS_SUCCEEDED(cookieMgr2->AddNative(NS_LITERAL_CSTRING("cookiemgr.test"), // domain
                                                    NS_LITERAL_CSTRING("/foo"),           // path
                                                    NS_LITERAL_CSTRING("test2"),          // name
                                                    NS_LITERAL_CSTRING("yes"),            // value
                                                    false,                             // is secure
                                                    true,                              // is httponly
                                                    true,                              // is session
                                                    PR_Now() / PR_USEC_PER_SEC + 2,       // expiry time
-                                                   &attrs)));                         // originAttributes
+                                                   &attrs,                            // originAttributes
+                                                   nsICookie2::SAMESITE_UNSET)));
     EXPECT_TRUE(NS_SUCCEEDED(cookieMgr2->AddNative(NS_LITERAL_CSTRING("new.domain"),     // domain
                                                    NS_LITERAL_CSTRING("/rabbit"),        // path
                                                    NS_LITERAL_CSTRING("test3"),          // name
                                                    NS_LITERAL_CSTRING("yes"),            // value
                                                    false,                             // is secure
                                                    false,                             // is httponly
                                                    true,                              // is session
                                                    INT64_MAX,                            // expiry time
-                                                   &attrs)));                         // originAttributes
+                                                   &attrs,                            // originAttributes
+                                                   nsICookie2::SAMESITE_UNSET)));
     // confirm using enumerator
     nsCOMPtr<nsISimpleEnumerator> enumerator;
     EXPECT_TRUE(NS_SUCCEEDED(cookieMgr->GetEnumerator(getter_AddRefs(enumerator))));
     int32_t i = 0;
     bool more;
     nsCOMPtr<nsICookie2> expiredCookie, newDomainCookie;
     while (NS_SUCCEEDED(enumerator->HasMoreElements(&more)) && more) {
         nsCOMPtr<nsISupports> cookie;
@@ -701,17 +704,18 @@ TEST(TestCookie,TestCookieMain)
     EXPECT_TRUE(NS_SUCCEEDED(cookieMgr2->AddNative(NS_LITERAL_CSTRING("new.domain"),     // domain
                                                    NS_LITERAL_CSTRING("/rabbit"),        // path
                                                    NS_LITERAL_CSTRING("test3"),          // name
                                                    NS_LITERAL_CSTRING("yes"),            // value
                                                    false,                             // is secure
                                                    false,                             // is httponly
                                                    true,                              // is session
                                                    INT64_MIN,                            // expiry time
-                                                   &attrs)));                         // originAttributes
+                                                   &attrs,                            // originAttributes
+                                                   nsICookie2::SAMESITE_UNSET)));
     EXPECT_TRUE(NS_SUCCEEDED(cookieMgr2->CookieExistsNative(newDomainCookie, &attrs, &found)));
     EXPECT_FALSE(found);
     // sleep four seconds, to make sure the second cookie has expired
     PR_Sleep(4 * PR_TicksPerSecond());
     // check that both CountCookiesFromHost() and CookieExistsNative() count the
     // expired cookie
     EXPECT_TRUE(NS_SUCCEEDED(cookieMgr2->CountCookiesFromHost(NS_LITERAL_CSTRING("cookiemgr.test"), &hostCookies)));
     EXPECT_EQ(hostCookies, 2u);
@@ -759,15 +763,60 @@ TEST(TestCookie,TestCookieMain)
         name += NS_LITERAL_CSTRING("; secure");
         SetACookie(cookieService, "https://creation.ordering.tests/", nullptr, name.get(), nullptr);
       } else {
         // non-security cookies will be removed beside the latest cookie that be created.
         SetACookie(cookieService, "http://creation.ordering.tests/", nullptr, name.get(), nullptr);
       }
     }
     GetACookie(cookieService, "http://creation.ordering.tests/", nullptr, getter_Copies(cookie));
+
     EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
 
+
+    // *** SameSite attribute - parsing and cookie storage tests
+    // Clear the cookies
+    EXPECT_TRUE(NS_SUCCEEDED(cookieMgr->RemoveAll()));
+
+    // Set cookies with various incantations of the samesite attribute:
+    // No same site attribute present
+    SetACookie(cookieService, "http://samesite.test", nullptr, "unset=yes", nullptr);
+    // samesite attribute present but with no value
+    SetACookie(cookieService, "http://samesite.test", nullptr, "unspecified=yes; samesite", nullptr);
+    // samesite=strict
+    SetACookie(cookieService, "http://samesite.test", nullptr, "strict=yes; samesite=strict", nullptr);
+    // samesite=lax
+    SetACookie(cookieService, "http://samesite.test", nullptr, "lax=yes; samesite=lax", nullptr);
+
+    EXPECT_TRUE(NS_SUCCEEDED(cookieMgr->GetEnumerator(getter_AddRefs(enumerator))));
+    i = 0;
+
+    // check the cookies for the required samesite value
+    while (NS_SUCCEEDED(enumerator->HasMoreElements(&more)) && more) {
+      nsCOMPtr<nsISupports> cookie;
+      if (NS_FAILED(enumerator->GetNext(getter_AddRefs(cookie)))) break;
+      ++i;
+
+      // keep tabs on the second and third cookies, so we can check them later
+      nsCOMPtr<nsICookie2> cookie2(do_QueryInterface(cookie));
+      if (!cookie2) break;
+      nsAutoCString name;
+      cookie2->GetName(name);
+      int32_t sameSiteAttr;
+      cookie2->GetSameSite(&sameSiteAttr);
+      if (name.EqualsLiteral("unset")) {
+        EXPECT_TRUE(sameSiteAttr == nsICookie2::SAMESITE_UNSET);
+      } else if (name.EqualsLiteral("unspecified")) {
+        EXPECT_TRUE(sameSiteAttr == nsICookie2::SAMESITE_STRICT);
+      } else if (name.EqualsLiteral("strict")) {
+        EXPECT_TRUE(sameSiteAttr == nsICookie2::SAMESITE_STRICT);
+      } else if (name.EqualsLiteral("lax")) {
+        EXPECT_TRUE(sameSiteAttr == nsICookie2::SAMESITE_LAX);
+      }
+    }
+
+    EXPECT_TRUE(i == 4);
+
     // XXX the following are placeholders: add these tests please!
     // *** "noncompliant cookie" tests
     // *** IP address tests
     // *** speed tests
 }
--- a/parser/htmlparser/nsElementTable.cpp
+++ b/parser/htmlparser/nsElementTable.cpp
@@ -1,244 +1,206 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 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 "nsElementTable.h"
 
-static const int kNone= 0x0;
-
-static const int kHTMLContent   = 0x0001; //  HEAD, (FRAMESET | BODY)
-static const int kHeadContent   = 0x0002; //  Elements that *must* be in the head.
-static const int kHeadMisc      = 0x0004; //  Elements that *can* be in the head.
-
-static const int kSpecial       = 0x0008; //  A,    IMG,  APPLET, OBJECT, FONT, BASEFONT, BR, SCRIPT, 
-                                          //  MAP,  Q,    SUB,    SUP,    SPAN, BDO,      IFRAME
-
-static const int kFormControl   = 0x0010; //  INPUT SELECT  TEXTAREA  LABEL BUTTON
-static const int kPreformatted  = 0x0020; //  PRE
-static const int kPreExclusion  = 0x0040; //  IMG,  OBJECT, APPLET, BIG,  SMALL,  SUB,  SUP,  FONT, BASEFONT
-static const int kFontStyle     = 0x0080; //  TT, I, B, U, S, STRIKE, BIG, SMALL
-static const int kPhrase        = 0x0100; //  EM, STRONG, DFN, CODE, SAMP, KBD, VAR, CITE, ABBR, ACRONYM
-static const int kHeading       = 0x0200; //  H1..H6
-static const int kBlockMisc     = 0x0400; //  OBJECT, SCRIPT
-static const int kBlock         = 0x0800; //  ADDRESS, BLOCKQUOTE, CENTER, DIV, DL, FIELDSET, FORM, 
-                                          //  ISINDEX, HR, NOSCRIPT, NOFRAMES, P, TABLE
-static const int kList          = 0x1000; //  UL, OL, DIR, MENU
-static const int kPCDATA        = 0x2000; //  plain text and entities...
-static const int kSelf          = 0x4000; //  whatever THIS tag is...
-static const int kExtensions    = 0x8000; //  BGSOUND, WBR, NOBR
-static const int kTable         = 0x10000;//  TR,TD,THEAD,TBODY,TFOOT,CAPTION,TH
-static const int kDLChild       = 0x20000;//  DL, DT
-static const int kCDATA         = 0x40000;//  just plain text...
-
-static const int kInlineEntity  = (kPCDATA|kFontStyle|kPhrase|kSpecial|kFormControl|kExtensions);  //  #PCDATA, %fontstyle, %phrase, %special, %formctrl
-static const int kBlockEntity   = (kHeading|kList|kPreformatted|kBlock); //  %heading, %list, %preformatted, %block
-static const int kFlowEntity    = (kBlockEntity|kInlineEntity); //  %blockentity, %inlineentity
-static const int kAllTags       = 0xffffff;
-
-// Is aTest a member of aBitset?
-static bool
-TestBits(int32_t aBitset, int32_t aTest)
-{
-  if (aTest) {
-    int32_t result = aBitset & aTest;
-    return result == aTest;
-  }
-  return false;
-}
-
 struct HTMLElement
 {
-  bool IsMemberOf(int32_t aBitset) const
-  {
-    return TestBits(aBitset, mParentBits);
-  }
-
 #ifdef DEBUG
   nsHTMLTag mTagID;
 #endif
-  int mParentBits;  // defines groups that can contain this element
-  bool mLeaf;
+  bool mIsBlock;
+  bool mIsContainer;
 };
 
 #ifdef DEBUG
-#define ELEM(tag, parent, leaf) { eHTMLTag_##tag, parent, leaf },
+#define ELEM(tag, block, container) { eHTMLTag_##tag, block, container },
 #else
-#define ELEM(tag, parent, leaf) { parent, leaf },
+#define ELEM(tag, block, container) { block, container },
 #endif
 
+#define ____ false    // This makes the table easier to read.
+
+// Note that the mIsBlock field disagrees with
+// https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements for
+// the following elements: center, details, dialog, dir, dt, figcaption,
+// listing, menu, multicol, noscript, output, summary, tfoot, video.
+//
+// mrbkap thinks that the field values were pulled from the old HTML4 DTD and
+// then got modified in mostly random ways to make the old parser's behavior
+// compatible with the web. So it might make sense to change the mIsBlock
+// values for the abovementioned tags at some point.
+//
 static const HTMLElement gHTMLElements[] = {
-  ELEM(unknown,     kNone,                       true)
-  ELEM(a,           kSpecial,                    false)
-  ELEM(abbr,        kPhrase,                     false)
-  ELEM(acronym,     kPhrase,                     false)
-  ELEM(address,     kBlock,                      false)
-  ELEM(applet,      kSpecial,                    false)
-  ELEM(area,        kNone,                       true)
-  ELEM(article,     kBlock,                      false)
-  ELEM(aside,       kBlock,                      false)
-  ELEM(audio,       kSpecial,                    false)
-  ELEM(b,           kFontStyle,                  false)
-  ELEM(base,        kHeadContent,                true)
-  ELEM(basefont,    kSpecial,                    true)
-  ELEM(bdo,         kSpecial,                    false)
-  ELEM(bgsound,     (kFlowEntity|kHeadMisc),     true)
-  ELEM(big,         kFontStyle,                  false)
-  ELEM(blockquote,  kBlock,                      false)
-  ELEM(body,        kHTMLContent,                false)
-  ELEM(br,          kSpecial,                    true)
-  ELEM(button,      kFormControl,                false)
-  ELEM(canvas,      kSpecial,                    false)
-  ELEM(caption,     kNone,                       false)
-  ELEM(center,      kBlock,                      false)
-  ELEM(cite,        kPhrase,                     false)
-  ELEM(code,        kPhrase,                     false)
-  ELEM(col,         kNone,                       true)
-  ELEM(colgroup,    kNone,                       false)
-  ELEM(content,     kNone,                       false)
-  ELEM(data,        kPhrase,                     false)
-  ELEM(datalist,    kSpecial,                    false)
-  ELEM(dd,          kInlineEntity,               false)
-  ELEM(del,         kFlowEntity,                 false)
-  ELEM(details,     kBlock,                      false)
-  ELEM(dfn,         kPhrase,                     false)
-  ELEM(dialog,      kBlock,                      false)
-  ELEM(dir,         kList,                       false)
-  ELEM(div,         kBlock,                      false)
-  ELEM(dl,          kBlock,                      false)
-  ELEM(dt,          kInlineEntity,               false)
-  ELEM(em,          kPhrase,                     false)
-  ELEM(embed,       kSpecial,                    true)
-  ELEM(fieldset,    kBlock,                      false)
-  ELEM(figcaption,  kPhrase,                     false)
-  ELEM(figure,      kBlock,                      false)
-  ELEM(font,        kFontStyle,                  false)
-  ELEM(footer,      kBlock,                      false)
-  ELEM(form,        kBlock,                      false)
-  ELEM(frame,       kNone,                       true)
-  ELEM(frameset,    kHTMLContent,                false)
-  ELEM(h1,          kHeading,                    false)
-  ELEM(h2,          kHeading,                    false)
-  ELEM(h3,          kHeading,                    false)
-  ELEM(h4,          kHeading,                    false)
-  ELEM(h5,          kHeading,                    false)
-  ELEM(h6,          kHeading,                    false)
-  ELEM(head,        kHTMLContent,                false)
-  ELEM(header,      kBlock,                      false)
-  ELEM(hgroup,      kBlock,                      false)
-  ELEM(hr,          kBlock,                      true)
-  ELEM(html,        kNone,                       false)
-  ELEM(i,           kFontStyle,                  false)
-  ELEM(iframe,      kSpecial,                    false)
-  ELEM(image,       kSpecial,                    true)
-  ELEM(img,         kSpecial,                    true)
-  ELEM(input,       kFormControl,                true)
-  ELEM(ins,         kFlowEntity,                 false)
-  ELEM(kbd,         kPhrase,                     false)
-  ELEM(keygen,      kFlowEntity,                 true)
-  ELEM(label,       kFormControl,                false)
-  ELEM(legend,      kNone,                       false)
-  ELEM(li,          kBlockEntity,                false)
-  ELEM(link,        kAllTags - kHeadContent,     true)
-  ELEM(listing,     kPreformatted,               false)
-  ELEM(main,        kBlock,                      false)
-  ELEM(map,         kSpecial,                    false)
-  ELEM(mark,        kSpecial,                    false)
-  ELEM(marquee,     kSpecial,                    false)
-  ELEM(menu,        kList,                       false)
-  ELEM(menuitem,    kFlowEntity,                 false)
-  ELEM(meta,        kHeadContent,                true)
-  ELEM(meter,       kFormControl,                false)
-  ELEM(multicol,    kBlock,                      false)
-  ELEM(nav,         kBlock,                      false)
-  ELEM(nobr,        kExtensions,                 false)
-  ELEM(noembed,     kFlowEntity,                 false)
-  ELEM(noframes,    kFlowEntity,                 false)
-  ELEM(noscript,    kFlowEntity|kHeadMisc,       false)
-  ELEM(object,      kSpecial,                    false)
-  ELEM(ol,          kList,                       false)
-  ELEM(optgroup,    kNone,                       false)
-  ELEM(option,      kNone,                       false)
-  ELEM(output,      kSpecial,                    false)
-  ELEM(p,           kBlock,                      false)
-  ELEM(param,       kSpecial,                    true)
-  ELEM(picture,     kSpecial,                    false)
-  ELEM(plaintext,   kExtensions,                 false)
-  ELEM(pre,         kBlock|kPreformatted,        false)
-  ELEM(progress,    kFormControl,                false)
-  ELEM(q,           kSpecial,                    false)
-  ELEM(rb,          kPhrase,                     false)
-  ELEM(rp,          kPhrase,                     false)
-  ELEM(rt,          kPhrase,                     false)
-  ELEM(rtc,         kPhrase,                     false)
-  ELEM(ruby,        kPhrase,                     false)
-  ELEM(s,           kFontStyle,                  false)
-  ELEM(samp,        kPhrase,                     false)
-  ELEM(script,      (kSpecial|kHeadContent),     false)
-  ELEM(section,     kBlock,                      false)
-  ELEM(select,      kFormControl,                false)
-  ELEM(shadow,      kFlowEntity,                 false)
-  ELEM(small,       kFontStyle,                  false)
-  ELEM(source,      kSpecial,                    true)
-  ELEM(span,        kSpecial,                    false)
-  ELEM(strike,      kFontStyle,                  false)
-  ELEM(strong,      kPhrase,                     false)
-  ELEM(style,       kAllTags - kHeadContent,     false)
-  ELEM(sub,         kSpecial,                    false)
-  ELEM(summary,     kBlock,                      false)
-  ELEM(sup,         kSpecial,                    false)
-  ELEM(table,       kBlock,                      false)
-  ELEM(tbody,       kNone,                       false)
-  ELEM(td,          kNone,                       false)
-  ELEM(textarea,    kFormControl,                false)
-  ELEM(tfoot,       kNone,                       false)
-  ELEM(th,          kNone,                       false)
-  ELEM(thead,       kNone,                       false)
-  ELEM(template,    kNone,                       false)
-  ELEM(time,        kPhrase,                     false)
-  ELEM(title,       kHeadContent,                false)
-  ELEM(tr,          kNone,                       false)
-  ELEM(track,       kSpecial,                    true)
-  ELEM(tt,          kFontStyle,                  false)
-  ELEM(u,           kFontStyle,                  false)
-  ELEM(ul,          kList,                       false)
-  ELEM(var,         kPhrase,                     false)
-  ELEM(video,       kSpecial,                    false)
-  ELEM(wbr,         kExtensions,                 true)
-  ELEM(xmp,         kInlineEntity|kPreformatted, false)
-  ELEM(text,        kFlowEntity,                 true)
-  ELEM(whitespace,  kFlowEntity|kHeadMisc,       true)
-  ELEM(newline,     kFlowEntity|kHeadMisc,       true)
-  ELEM(comment,     kFlowEntity|kHeadMisc,       false)
-  ELEM(entity,      kFlowEntity,                 false)
-  ELEM(doctypeDecl, kFlowEntity,                 false)
-  ELEM(markupDecl,  kFlowEntity,                 false)
-  ELEM(instruction, kFlowEntity,                 false)
-  ELEM(userdefined, (kFlowEntity|kHeadMisc),     false)
+  ELEM(unknown,     ____, ____)
+  ELEM(a,           ____, true)
+  ELEM(abbr,        ____, true)
+  ELEM(acronym,     ____, true)
+  ELEM(address,     true, true)
+  ELEM(applet,      ____, true)
+  ELEM(area,        ____, ____)
+  ELEM(article,     true, true)
+  ELEM(aside,       true, true)
+  ELEM(audio,       ____, true)
+  ELEM(b,           ____, true)
+  ELEM(base,        ____, ____)
+  ELEM(basefont,    ____, ____)
+  ELEM(bdo,         ____, true)
+  ELEM(bgsound,     ____, ____)
+  ELEM(big,         ____, true)
+  ELEM(blockquote,  true, true)
+  ELEM(body,        ____, true)
+  ELEM(br,          ____, ____)
+  ELEM(button,      ____, true)
+  ELEM(canvas,      ____, true)
+  ELEM(caption,     ____, true)
+  ELEM(center,      true, true)
+  ELEM(cite,        ____, true)
+  ELEM(code,        ____, true)
+  ELEM(col,         ____, ____)
+  ELEM(colgroup,    ____, true)
+  ELEM(content,     ____, true)
+  ELEM(data,        ____, true)
+  ELEM(datalist,    ____, true)
+  ELEM(dd,          ____, true)
+  ELEM(del,         ____, true)
+  ELEM(details,     true, true)
+  ELEM(dfn,         ____, true)
+  ELEM(dialog,      true, true)
+  ELEM(dir,         true, true)
+  ELEM(div,         true, true)
+  ELEM(dl,          true, true)
+  ELEM(dt,          ____, true)
+  ELEM(em,          ____, true)
+  ELEM(embed,       ____, ____)
+  ELEM(fieldset,    true, true)
+  ELEM(figcaption,  ____, true)
+  ELEM(figure,      true, true)
+  ELEM(font,        ____, true)
+  ELEM(footer,      true, true)
+  ELEM(form,        true, true)
+  ELEM(frame,       ____, ____)
+  ELEM(frameset,    ____, true)
+  ELEM(h1,          true, true)
+  ELEM(h2,          true, true)
+  ELEM(h3,          true, true)
+  ELEM(h4,          true, true)
+  ELEM(h5,          true, true)
+  ELEM(h6,          true, true)
+  ELEM(head,        ____, true)
+  ELEM(header,      true, true)
+  ELEM(hgroup,      true, true)
+  ELEM(hr,          true, ____)
+  ELEM(html,        ____, true)
+  ELEM(i,           ____, true)
+  ELEM(iframe,      ____, true)
+  ELEM(image,       ____, ____)
+  ELEM(img,         ____, ____)
+  ELEM(input,       ____, ____)
+  ELEM(ins,         ____, true)
+  ELEM(kbd,         ____, true)
+  ELEM(keygen,      ____, ____)
+  ELEM(label,       ____, true)
+  ELEM(legend,      ____, true)
+  ELEM(li,          true, true)
+  ELEM(link,        ____, ____)
+  ELEM(listing,     true, true)
+  ELEM(main,        true, true)
+  ELEM(map,         ____, true)
+  ELEM(mark,        ____, true)
+  ELEM(marquee,     ____, true)
+  ELEM(menu,        true, true)
+  ELEM(menuitem,    ____, true)
+  ELEM(meta,        ____, ____)
+  ELEM(meter,       ____, true)
+  ELEM(multicol,    true, true)
+  ELEM(nav,         true, true)
+  ELEM(nobr,        ____, true)
+  ELEM(noembed,     ____, true)
+  ELEM(noframes,    ____, true)
+  ELEM(noscript,    ____, true)
+  ELEM(object,      ____, true)
+  ELEM(ol,          true, true)
+  ELEM(optgroup,    ____, true)
+  ELEM(option,      ____, true)
+  ELEM(output,      ____, true)
+  ELEM(p,           true, true)
+  ELEM(param,       ____, ____)
+  ELEM(picture,     ____, true)
+  ELEM(plaintext,   ____, true)
+  ELEM(pre,         true, true)
+  ELEM(progress,    ____, true)
+  ELEM(q,           ____, true)
+  ELEM(rb,          ____, true)
+  ELEM(rp,          ____, true)
+  ELEM(rt,          ____, true)
+  ELEM(rtc,         ____, true)
+  ELEM(ruby,        ____, true)
+  ELEM(s,           ____, true)
+  ELEM(samp,        ____, true)
+  ELEM(script,      ____, true)
+  ELEM(section,     true, true)
+  ELEM(select,      ____, true)
+  ELEM(shadow,      ____, true)
+  ELEM(small,       ____, true)
+  ELEM(source,      ____, ____)
+  ELEM(span,        ____, true)
+  ELEM(strike,      ____, true)
+  ELEM(strong,      ____, true)
+  ELEM(style,       ____, true)
+  ELEM(sub,         ____, true)
+  ELEM(summary,     true, true)
+  ELEM(sup,         ____, true)
+  ELEM(table,       true, true)
+  ELEM(tbody,       ____, true)
+  ELEM(td,          ____, true)
+  ELEM(textarea,    ____, true)
+  ELEM(tfoot,       ____, true)
+  ELEM(th,          ____, true)
+  ELEM(thead,       ____, true)
+  ELEM(template,    ____, true)
+  ELEM(time,        ____, true)
+  ELEM(title,       ____, true)
+  ELEM(tr,          ____, true)
+  ELEM(track,       ____, ____)
+  ELEM(tt,          ____, true)
+  ELEM(u,           ____, true)
+  ELEM(ul,          true, true)
+  ELEM(var,         ____, true)
+  ELEM(video,       ____, true)
+  ELEM(wbr,         ____, ____)
+  ELEM(xmp,         ____, true)
+  ELEM(text,        ____, ____)
+  ELEM(whitespace,  ____, ____)
+  ELEM(newline,     ____, ____)
+  ELEM(comment,     ____, true)
+  ELEM(entity,      ____, true)
+  ELEM(doctypeDecl, ____, true)
+  ELEM(markupDecl,  ____, true)
+  ELEM(instruction, ____, true)
+  ELEM(userdefined, ____, true)
 };
 
 #undef ELEM
+#undef ____
 
 bool
 nsHTMLElement::IsContainer(nsHTMLTag aId)
 {
-  return !gHTMLElements[aId].mLeaf;
+  return gHTMLElements[aId].mIsContainer;
 }
 
 bool
 nsHTMLElement::IsBlock(nsHTMLTag aId)
 {
-  return gHTMLElements[aId].IsMemberOf(kBlock)       ||
-         gHTMLElements[aId].IsMemberOf(kBlockEntity) ||
-         gHTMLElements[aId].IsMemberOf(kHeading)     ||
-         gHTMLElements[aId].IsMemberOf(kPreformatted)||
-         gHTMLElements[aId].IsMemberOf(kList);
+  return gHTMLElements[aId].mIsBlock;
 }
 
 #ifdef DEBUG
 void
 CheckElementTable()
 {
   for (nsHTMLTag t = eHTMLTag_unknown;
        t <= eHTMLTag_userdefined;
--- a/toolkit/components/browser/nsIEmbeddingSiteWindow.idl
+++ b/toolkit/components/browser/nsIEmbeddingSiteWindow.idl
@@ -129,17 +129,17 @@ interface nsIEmbeddingSiteWindow : nsISu
     /**
      * Visibility of the window.
      */
     attribute boolean visibility;
 
     /**
      * Title of the window.
      */
-    attribute wstring title;
+    attribute AString title;
 
     /**
      * Native window for the site's window. The implementor should copy the
      * native window object into the address supplied by the caller. The
      * type of the native window that the address refers to is  platform
      * and OS specific as follows:
      *
      * <ul>
--- a/toolkit/components/browser/nsWebBrowser.cpp
+++ b/toolkit/components/browser/nsWebBrowser.cpp
@@ -1577,28 +1577,27 @@ nsWebBrowser::SetFocus()
   nsCOMPtr<nsPIDOMWindowOuter> window = GetWindow();
   NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
   return fm ? fm->SetFocusedWindow(window) : NS_OK;
 }
 
 NS_IMETHODIMP
-nsWebBrowser::GetTitle(char16_t** aTitle)
+nsWebBrowser::GetTitle(nsAString& aTitle)
 {
-  NS_ENSURE_ARG_POINTER(aTitle);
   NS_ENSURE_STATE(mDocShell);
 
   NS_ENSURE_SUCCESS(mDocShellAsWin->GetTitle(aTitle), NS_ERROR_FAILURE);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsWebBrowser::SetTitle(const char16_t* aTitle)
+nsWebBrowser::SetTitle(const nsAString& aTitle)
 {
   NS_ENSURE_STATE(mDocShell);
 
   NS_ENSURE_SUCCESS(mDocShellAsWin->SetTitle(aTitle), NS_ERROR_FAILURE);
 
   return NS_OK;
 }
 
--- a/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp
@@ -394,16 +394,18 @@ nsUrlClassifierPrefixSet::StoreToFile(ns
   LOG(("Saving PrefixSet successful\n"));
 
   return NS_OK;
 }
 
 nsresult
 nsUrlClassifierPrefixSet::LoadPrefixes(nsIInputStream* in)
 {
+  mCanary.Check();
+
   uint32_t magic;
   uint32_t read;
 
   nsresult rv = in->Read(reinterpret_cast<char*>(&magic), sizeof(uint32_t), &read);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(read == sizeof(uint32_t), NS_ERROR_FAILURE);
 
   if (magic == PREFIXSET_VERSION_MAGIC) {
@@ -484,16 +486,18 @@ nsUrlClassifierPrefixSet::CalculatePreal
   fileSize += 2 * mIndexPrefixes.Length() * sizeof(uint32_t);
   fileSize += deltas * sizeof(uint16_t);
   return fileSize;
 }
 
 nsresult
 nsUrlClassifierPrefixSet::WritePrefixes(nsIOutputStream* out)
 {
+  mCanary.Check();
+
   uint32_t written;
   uint32_t writelen = sizeof(uint32_t);
   uint32_t magic = PREFIXSET_VERSION_MAGIC;
   nsresult rv = out->Write(reinterpret_cast<char*>(&magic), writelen, &written);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
 
   uint32_t indexSize = mIndexPrefixes.Length();
--- a/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.h
+++ b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.h
@@ -14,16 +14,17 @@
 #include "nsIMutableArray.h"
 #include "nsIFileStreams.h"
 #include "nsIUrlClassifierPrefixSet.h"
 #include "nsTArray.h"
 #include "nsToolkitCompsCID.h"
 #include "mozilla/FileUtils.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Mutex.h"
+#include "mozilla/Poison.h"
 
 namespace mozilla {
 namespace safebrowsing {
 
 class VariableLengthPrefixSet;
 
 } // namespace safebrowsing
 } // namespace mozilla
@@ -79,11 +80,12 @@ private:
   // Index to the place that matches the closest lower
   // prefix from mIndexPrefix. Then every "delta" corresponds
   // to a prefix in the PrefixSet.
   nsTArray<nsTArray<uint16_t> > mIndexDeltas;
   // how many prefixes we have.
   uint32_t mTotalPrefixes;
 
   nsCString mMemoryReportPath;
+  mozilla::CorruptionCanary mCanary;
 };
 
 #endif
--- a/toolkit/content/contentAreaUtils.js
+++ b/toolkit/content/contentAreaUtils.js
@@ -1259,15 +1259,19 @@ function openURL(aURL) {
       }
     }
 
     var channel = NetUtil.newChannel({
       uri,
       loadUsingSystemPrincipal: true
     });
 
+    if (channel) {
+      channel.channelIsForDownload = true;
+    }
+
     var uriLoader = Components.classes["@mozilla.org/uriloader;1"]
                               .getService(Components.interfaces.nsIURILoader);
     uriLoader.openURI(channel,
                       Components.interfaces.nsIURILoader.IS_CONTENT_PREFERRED,
                       uriListener);
   }
 }
--- a/toolkit/content/tests/browser/browser_saveImageURL.js
+++ b/toolkit/content/tests/browser/browser_saveImageURL.js
@@ -32,16 +32,25 @@ add_task(async function preferred_API() 
     url: IMAGE_PAGE,
   }, async function(browser) {
     let url = await ContentTask.spawn(browser, null, async function() {
       let image = content.document.getElementById("image");
       return image.href;
     });
 
     saveImageURL(url, "image.jpg", null, true, false, null, null, null, null, false);
+    let channel = content.document.docShell.currentDocumentChannel;
+    if (channel) {
+      ok(true, channel.QueryInterface(Ci.nsIHttpChannelInternal)
+                      .channelIsForDownload);
+
+      // Throttleable is the only class flag assigned to downloads.
+      ok(channel.QueryInterface(Ci.nsIClassOfService).classFlags,
+         Ci.nsIClassOfService.Throttleable);
+    }
     await waitForFilePicker();
   });
 });
 
 /**
  * Test that saveImageURL will still work when passed a document instead
  * of the aIsContentWindowPrivate argument. This is the deprecated API, and
  * will not work in apps using remote browsers having PREF_UNSAFE_FORBIDDEN
@@ -59,12 +68,21 @@ add_task(async function deprecated_API()
       return image.href;
     });
 
     // Now get the document directly from content. If we run this test with
     // e10s-enabled, this will be a CPOW, which is forbidden. We'll just
     // pass the XUL document instead to test this interface.
     let doc = document;
 
+    let channel = content.document.docShell.currentDocumentChannel;
+    if (channel) {
+      ok(true, channel.QueryInterface(Ci.nsIHttpChannelInternal)
+                      .channelIsForDownload);
+
+      // Throttleable is the only class flag assigned to downloads.
+      ok(channel.QueryInterface(Ci.nsIClassOfService).classFlags,
+         Ci.nsIClassOfService.Throttleable);
+    }
     saveImageURL(url, "image.jpg", null, true, false, null, doc, null, null);
     await waitForFilePicker();
   });
 });
--- a/toolkit/xre/nsNativeAppSupportWin.cpp
+++ b/toolkit/xre/nsNativeAppSupportWin.cpp
@@ -988,17 +988,17 @@ nsNativeAppSupportWin::HandleDDENotifica
                         if ( !baseWindow ) {
                             break;
                         }
                         // And from the base window we can get the title.
                         nsString title;
                         if(!baseWindow) {
                             break;
                         }
-                        baseWindow->GetTitle(getter_Copies(title));
+                        baseWindow->GetTitle(title);
                         // Escape any double-quotes in the title.
                         escapeQuotes( title );
 
                         // Use a string buffer for the output data, first
                         // save a quote.
                         nsAutoCString   outpt( NS_LITERAL_CSTRING("\"") );
                         // Now copy the URL converting the Unicode string
                         // to a single-byte ASCII string
--- a/widget/cocoa/nsCocoaWindow.mm
+++ b/widget/cocoa/nsCocoaWindow.mm
@@ -482,25 +482,16 @@ nsresult nsCocoaWindow::CreateNativeWind
   // BorderlessWindow class.
   else if (features == NSBorderlessWindowMask)
     windowClass = [BorderlessWindow class];
 
   // Create the window
   mWindow = [[windowClass alloc] initWithContentRect:contentRect styleMask:features 
                                  backing:NSBackingStoreBuffered defer:YES];
 
-  if ([mWindow respondsToSelector:@selector(setTitleVisibility:)]) {
-    // By default, hide window titles.
-    [mWindow setTitleVisibility:NSWindowTitleHidden];
-  }
-  if ([mWindow respondsToSelector:@selector(setTitlebarAppearsTransparent:)]) {
-    // By default, hide window titlebars.
-    [mWindow setTitlebarAppearsTransparent:YES];
-  }
-
   // setup our notification delegate. Note that setDelegate: does NOT retain.
   mDelegate = [[WindowDelegate alloc] initWithGeckoWindow:this];
   [mWindow setDelegate:mDelegate];
 
   // Make sure that the content rect we gave has been honored.
   NSRect wantedFrame = [mWindow frameRectForContentRect:contentRect];
   if (!NSEqualRects([mWindow frame], wantedFrame)) {
     // This can happen when the window is not on the primary screen.
--- a/widget/nsIBaseWindow.idl
+++ b/widget/nsIBaseWindow.idl
@@ -221,10 +221,10 @@ interface nsIBaseWindow : nsISupports
 	/**
 	* Give the window focus.
 	*/
 	void setFocus();
 
 	/*
 	Title of the window.
 	*/
-	attribute wstring title;
+	attribute AString title;
 };
--- a/xpfe/appshell/nsAppShellService.cpp
+++ b/xpfe/appshell/nsAppShellService.cpp
@@ -375,22 +375,22 @@ WebBrowserChrome2Stub::GetVisibility(boo
 }
 NS_IMETHODIMP
 WebBrowserChrome2Stub::SetVisibility(bool aVisibility)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
-WebBrowserChrome2Stub::GetTitle(char16_t** aTitle)
+WebBrowserChrome2Stub::GetTitle(nsAString& aTitle)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 NS_IMETHODIMP
-WebBrowserChrome2Stub::SetTitle(const char16_t* aTitle)
+WebBrowserChrome2Stub::SetTitle(const nsAString& aTitle)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 WebBrowserChrome2Stub::GetSiteWindow(void** aSiteWindow)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
--- a/xpfe/appshell/nsChromeTreeOwner.cpp
+++ b/xpfe/appshell/nsChromeTreeOwner.cpp
@@ -425,23 +425,23 @@ NS_IMETHODIMP nsChromeTreeOwner::GetMain
 }
 
 NS_IMETHODIMP nsChromeTreeOwner::SetFocus()
 {
    NS_ENSURE_STATE(mXULWindow);
    return mXULWindow->SetFocus();
 }
 
-NS_IMETHODIMP nsChromeTreeOwner::GetTitle(char16_t** aTitle)
+NS_IMETHODIMP nsChromeTreeOwner::GetTitle(nsAString& aTitle)
 {
    NS_ENSURE_STATE(mXULWindow);
    return mXULWindow->GetTitle(aTitle);
 }
 
-NS_IMETHODIMP nsChromeTreeOwner::SetTitle(const char16_t* aTitle)
+NS_IMETHODIMP nsChromeTreeOwner::SetTitle(const nsAString& aTitle)
 {
    NS_ENSURE_STATE(mXULWindow);
    return mXULWindow->SetTitle(aTitle);
 }
 
 //*****************************************************************************
 // nsChromeTreeOwner::nsIWebProgressListener
 //*****************************************************************************
--- a/xpfe/appshell/nsContentTreeOwner.cpp
+++ b/xpfe/appshell/nsContentTreeOwner.cpp
@@ -708,25 +708,24 @@ NS_IMETHODIMP nsContentTreeOwner::GetMai
 }
 
 NS_IMETHODIMP nsContentTreeOwner::SetFocus()
 {
    NS_ENSURE_STATE(mXULWindow);
    return mXULWindow->SetFocus();
 }
 
-NS_IMETHODIMP nsContentTreeOwner::GetTitle(char16_t** aTitle)
+NS_IMETHODIMP nsContentTreeOwner::GetTitle(nsAString& aTitle)
 {
-   NS_ENSURE_ARG_POINTER(aTitle);
    NS_ENSURE_STATE(mXULWindow);
 
    return mXULWindow->GetTitle(aTitle);
 }
 
-NS_IMETHODIMP nsContentTreeOwner::SetTitle(const char16_t* aTitle)
+NS_IMETHODIMP nsContentTreeOwner::SetTitle(const nsAString& aTitle)
 {
    // We only allow the title to be set from the primary content shell
   if(!mPrimary || !mContentTitleSetting)
     return NS_OK;
 
   NS_ENSURE_STATE(mXULWindow);
 
   nsAutoString   title;
@@ -805,17 +804,17 @@ NS_IMETHODIMP nsContentTreeOwner::SetTit
       }
     }
     nsIDocument* document = docShellElement->OwnerDoc();
     ErrorResult rv;
     document->SetTitle(title, rv);
     return rv.StealNSResult();
   }
 
-  return mXULWindow->SetTitle(title.get());
+  return mXULWindow->SetTitle(title);
 }
 
 //*****************************************************************************
 // nsContentTreeOwner: nsIWindowProvider
 //*****************************************************************************
 NS_IMETHODIMP
 nsContentTreeOwner::ProvideWindow(mozIDOMWindowProxy* aParent,
                                   uint32_t aChromeFlags,
@@ -1143,23 +1142,23 @@ nsSiteWindow::GetVisibility(bool *aVisib
 
 NS_IMETHODIMP
 nsSiteWindow::SetVisibility(bool aVisibility)
 {
   return mAggregator->SetVisibility(aVisibility);
 }
 
 NS_IMETHODIMP
-nsSiteWindow::GetTitle(char16_t * *aTitle)
+nsSiteWindow::GetTitle(nsAString& aTitle)
 {
   return mAggregator->GetTitle(aTitle);
 }
 
 NS_IMETHODIMP
-nsSiteWindow::SetTitle(const char16_t * aTitle)
+nsSiteWindow::SetTitle(const nsAString& aTitle)
 {
   return mAggregator->SetTitle(aTitle);
 }
 
 NS_IMETHODIMP
 nsSiteWindow::GetSiteWindow(void **aSiteWindow)
 {
   return mAggregator->GetParentNativeWindow(aSiteWindow);
--- a/xpfe/appshell/nsIWindowMediator.idl
+++ b/xpfe/appshell/nsIWindowMediator.idl
@@ -104,17 +104,17 @@ interface nsIWindowMediator: nsISupports
   [noscript] void updateWindowTimeStamp(in nsIXULWindow aWindow);
 
   /** Call this method when a window's title changes. Listeners (see
     * addListener) will be notified through their onWindowTitleChange method.
     * @param aWindow the window whose title has changed
     * @param inTitle the window's new title
     */
   [noscript] void updateWindowTitle(in nsIXULWindow aWindow,
-                                    in wstring inTitle );
+                                    in AString inTitle);
 
   /* z-ordering: */
 
   const unsigned long zLevelTop    = 1;
   const unsigned long zLevelBottom = 2;
   const unsigned long zLevelBelow  = 3; // below some window
 
   /** A window wants to be moved in z-order. Calculate whether and how
--- a/xpfe/appshell/nsIWindowMediatorListener.idl
+++ b/xpfe/appshell/nsIWindowMediatorListener.idl
@@ -5,15 +5,14 @@
 
 #include "nsISupports.idl"
 
 interface nsIXULWindow;
 
 [scriptable, uuid(2F276982-0D60-4377-A595-D350BA516395)]
 interface nsIWindowMediatorListener : nsISupports
 {
-    void onWindowTitleChange(in nsIXULWindow window,
-                             in wstring newTitle);
+    void onWindowTitleChange(in nsIXULWindow window, in AString newTitle);
 
     void onOpenWindow(in nsIXULWindow window);
     void onCloseWindow(in nsIXULWindow window);
 };
 
--- a/xpfe/appshell/nsWindowMediator.cpp
+++ b/xpfe/appshell/nsWindowMediator.cpp
@@ -391,17 +391,17 @@ nsWindowMediator::UpdateWindowTimeStamp(
     info->mTimeStamp = ++mTimeStamp;
     return NS_OK;
   }
   return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsWindowMediator::UpdateWindowTitle(nsIXULWindow* inWindow,
-                                    const char16_t* inTitle)
+                                    const nsAString& inTitle)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   NS_ENSURE_STATE(mReady);
   if (GetInfoFor(inWindow)) {
     ListenerArray::ForwardIterator iter(mListeners);
     while (iter.HasMore()) {
       iter.GetNext()->OnWindowTitleChange(inWindow, inTitle);
     }
--- a/xpfe/appshell/nsXULWindow.cpp
+++ b/xpfe/appshell/nsXULWindow.cpp
@@ -885,27 +885,23 @@ NS_IMETHODIMP nsXULWindow::GetMainWidget
 
 NS_IMETHODIMP nsXULWindow::SetFocus()
 {
   //XXX First Check In
   NS_ASSERTION(false, "Not Yet Implemented");
   return NS_OK;
 }
 
-NS_IMETHODIMP nsXULWindow::GetTitle(char16_t** aTitle)
+NS_IMETHODIMP nsXULWindow::GetTitle(nsAString& aTitle)
 {
-  NS_ENSURE_ARG_POINTER(aTitle);
-
-  *aTitle = ToNewUnicode(mTitle);
-  if (!*aTitle)
-    return NS_ERROR_OUT_OF_MEMORY;
+  aTitle = mTitle;
   return NS_OK;
 }
 
-NS_IMETHODIMP nsXULWindow::SetTitle(const char16_t* aTitle)
+NS_IMETHODIMP nsXULWindow::SetTitle(const nsAString& aTitle)
 {
   NS_ENSURE_STATE(mWindow);
   mTitle.Assign(aTitle);
   mTitle.StripCRLF();
   NS_ENSURE_SUCCESS(mWindow->SetTitle(mTitle), NS_ERROR_FAILURE);
 
   // Tell the window mediator that a title has changed
   nsCOMPtr<nsIWindowMediator> windowMediator(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
--- a/xpfe/components/windowds/nsWindowDataSource.cpp
+++ b/xpfe/components/windowds/nsWindowDataSource.cpp
@@ -116,33 +116,34 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
 NS_INTERFACE_MAP_END
 
 // nsIWindowMediatorListener implementation
 // handle notifications from the window mediator and reflect them into
 // RDF
 
 NS_IMETHODIMP
 nsWindowDataSource::OnWindowTitleChange(nsIXULWindow *window,
-                                        const char16_t *newTitle)
+                                        const nsAString& newTitle)
 {
     nsresult rv;
 
     nsCOMPtr<nsIRDFResource> windowResource;
     mWindowResources.Get(window, getter_AddRefs(windowResource));
 
     // oops, make sure this window is in the hashtable!
     if (!windowResource) {
         OnOpenWindow(window);
         mWindowResources.Get(window, getter_AddRefs(windowResource));
     }
 
     NS_ENSURE_TRUE(windowResource, NS_ERROR_UNEXPECTED);
 
     nsCOMPtr<nsIRDFLiteral> newTitleLiteral;
-    rv = gRDFService->GetLiteral(newTitle, getter_AddRefs(newTitleLiteral));
+    rv = gRDFService->GetLiteral(PromiseFlatString(newTitle).get(),
+                                 getter_AddRefs(newTitleLiteral));
     NS_ENSURE_SUCCESS(rv, rv);
 
     // get the old title
     nsCOMPtr<nsIRDFNode> oldTitleNode;
     rv = GetTarget(windowResource, kNC_Name, true,
                    getter_AddRefs(oldTitleNode));
 
     // assert the change