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 382685 5f3f19824efa14cc6db546baf59c54a0fc15ddc9
parent 382684 13ce77b78e364cc952d51b959f22202502be2941 (current diff)
parent 382665 9180e59a44f78fca784b8fc6dbadccf2bc300feb (diff)
child 382686 ff48fab67d3c8e41035cd8ea1cbda1696fdd12fc
child 382739 bd42696ae79b1b04b10a3a36e96f56540decfb55
child 382790 b42a687abadeb41a3de632490e5d77a38344d02c
push id95394
push userarchaeopteryx@coole-files.de
push dateMon, 25 Sep 2017 10:03:57 +0000
treeherdermozilla-inbound@ff48fab67d3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone58.0a1
first release with
nightly linux32
5f3f19824efa / 58.0a1 / 20170925100307 / files
nightly linux64
5f3f19824efa / 58.0a1 / 20170925100307 / files
nightly mac
5f3f19824efa / 58.0a1 / 20170925100307 / files
nightly win32
5f3f19824efa / 58.0a1 / 20170925100307 / files
nightly win64
5f3f19824efa / 58.0a1 / 20170925100307 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge 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