Merge inbound to mozilla-central. a=merge
authorNarcis Beleuzu <nbeleuzu@mozilla.com>
Fri, 10 May 2019 00:42:34 +0300
changeset 473234 f28f80e2466839e5b50c9b9f728988044fecb186
parent 473233 912b44cef0bcf2ddf29778ee2f5d8586933095af (current diff)
parent 473212 57f13fa5da0abf289a4b02a88703ecdc7e917f0f (diff)
child 473235 38895d59d3d007f10a771552e5055b51c4425050
child 473312 1d1443055d8f09ad6fe8170c714705167ef34d94
push id113071
push usernbeleuzu@mozilla.com
push dateThu, 09 May 2019 22:20:39 +0000
treeherdermozilla-inbound@38895d59d3d0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone68.0a1
first release with
nightly linux32
f28f80e24668 / 68.0a1 / 20190509214305 / files
nightly linux64
f28f80e24668 / 68.0a1 / 20190509214305 / files
nightly mac
f28f80e24668 / 68.0a1 / 20190509214305 / files
nightly win32
f28f80e24668 / 68.0a1 / 20190509214305 / files
nightly win64
f28f80e24668 / 68.0a1 / 20190509214305 / 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 inbound to mozilla-central. a=merge
--- 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: 2.2.160
+Current extension version is: 2.2.167
 
-Taken from upstream commit: 155304a0
+Taken from upstream commit: ca2fee3d
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -118,18 +118,18 @@ return /******/ (function(modules) { // 
 /************************************************************************/
 /******/ ([
 /* 0 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-var pdfjsVersion = '2.2.160';
-var pdfjsBuild = '155304a0';
+var pdfjsVersion = '2.2.167';
+var pdfjsBuild = 'ca2fee3d';
 
 var pdfjsSharedUtil = __w_pdfjs_require__(1);
 
 var pdfjsDisplayAPI = __w_pdfjs_require__(6);
 
 var pdfjsDisplayTextLayer = __w_pdfjs_require__(18);
 
 var pdfjsDisplayAnnotationLayer = __w_pdfjs_require__(19);
@@ -170,16 +170,17 @@ exports.shadow = pdfjsSharedUtil.shadow;
 exports.Util = pdfjsSharedUtil.Util;
 exports.ReadableStream = pdfjsSharedUtil.ReadableStream;
 exports.URL = pdfjsSharedUtil.URL;
 exports.RenderingCancelledException = pdfjsDisplayDisplayUtils.RenderingCancelledException;
 exports.getFilenameFromUrl = pdfjsDisplayDisplayUtils.getFilenameFromUrl;
 exports.LinkTarget = pdfjsDisplayDisplayUtils.LinkTarget;
 exports.addLinkAttributes = pdfjsDisplayDisplayUtils.addLinkAttributes;
 exports.loadScript = pdfjsDisplayDisplayUtils.loadScript;
+exports.PDFDateString = pdfjsDisplayDisplayUtils.PDFDateString;
 exports.GlobalWorkerOptions = pdfjsDisplayWorkerOptions.GlobalWorkerOptions;
 exports.apiCompatibilityParams = pdfjsDisplayAPICompatibility.apiCompatibilityParams;
 
 /***/ }),
 /* 1 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
@@ -1298,17 +1299,17 @@ function _fetchDocument(worker, source, 
   if (pdfDataRangeTransport) {
     source.length = pdfDataRangeTransport.length;
     source.initialData = pdfDataRangeTransport.initialData;
     source.progressiveDone = pdfDataRangeTransport.progressiveDone;
   }
 
   return worker.messageHandler.sendWithPromise('GetDocRequest', {
     docId,
-    apiVersion: '2.2.160',
+    apiVersion: '2.2.167',
     source: {
       data: source.data,
       url: source.url,
       password: source.password,
       disableAutoFetch: source.disableAutoFetch,
       rangeChunkSize: source.rangeChunkSize,
       length: source.length
     },
@@ -3092,19 +3093,19 @@ const InternalRenderTask = function Inte
       }
     }
 
   }
 
   return InternalRenderTask;
 }();
 
-const version = '2.2.160';
+const version = '2.2.167';
 exports.version = version;
-const build = '155304a0';
+const build = 'ca2fee3d';
 exports.build = build;
 
 /***/ }),
 /* 7 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
@@ -3114,17 +3115,17 @@ Object.defineProperty(exports, "__esModu
 });
 exports.addLinkAttributes = addLinkAttributes;
 exports.getFilenameFromUrl = getFilenameFromUrl;
 exports.isFetchSupported = isFetchSupported;
 exports.isValidFetchUrl = isValidFetchUrl;
 exports.loadScript = loadScript;
 exports.deprecated = deprecated;
 exports.releaseImageResources = releaseImageResources;
-exports.DummyStatTimer = exports.StatTimer = exports.DOMSVGFactory = exports.DOMCMapReaderFactory = exports.DOMCanvasFactory = exports.DEFAULT_LINK_REL = exports.LinkTarget = exports.RenderingCancelledException = exports.PageViewport = void 0;
+exports.PDFDateString = exports.DummyStatTimer = exports.StatTimer = exports.DOMSVGFactory = exports.DOMCMapReaderFactory = exports.DOMCanvasFactory = exports.DEFAULT_LINK_REL = exports.LinkTarget = exports.RenderingCancelledException = exports.PageViewport = void 0;
 
 var _util = __w_pdfjs_require__(1);
 
 const DEFAULT_LINK_REL = 'noopener noreferrer nofollow';
 exports.DEFAULT_LINK_REL = DEFAULT_LINK_REL;
 const SVG_NS = 'http://www.w3.org/2000/svg';
 
 class DOMCanvasFactory {
@@ -3511,16 +3512,66 @@ function releaseImageResources(img) {
 
   if (typeof url === 'string' && url.startsWith('blob:') && _util.URL.revokeObjectURL) {
     _util.URL.revokeObjectURL(url);
   }
 
   img.removeAttribute('src');
 }
 
+let pdfDateStringRegex;
+
+class PDFDateString {
+  static toDateObject(input) {
+    if (!input || !(0, _util.isString)(input)) {
+      return null;
+    }
+
+    if (!pdfDateStringRegex) {
+      pdfDateStringRegex = new RegExp('^D:' + '(\\d{4})' + '(\\d{2})?' + '(\\d{2})?' + '(\\d{2})?' + '(\\d{2})?' + '(\\d{2})?' + '([Z|+|-])?' + '(\\d{2})?' + '\'?' + '(\\d{2})?' + '\'?');
+    }
+
+    const matches = pdfDateStringRegex.exec(input);
+
+    if (!matches) {
+      return null;
+    }
+
+    const year = parseInt(matches[1], 10);
+    let month = parseInt(matches[2], 10);
+    month = month >= 1 && month <= 12 ? month - 1 : 0;
+    let day = parseInt(matches[3], 10);
+    day = day >= 1 && day <= 31 ? day : 1;
+    let hour = parseInt(matches[4], 10);
+    hour = hour >= 0 && hour <= 23 ? hour : 0;
+    let minute = parseInt(matches[5], 10);
+    minute = minute >= 0 && minute <= 59 ? minute : 0;
+    let second = parseInt(matches[6], 10);
+    second = second >= 0 && second <= 59 ? second : 0;
+    const universalTimeRelation = matches[7] || 'Z';
+    let offsetHour = parseInt(matches[8], 10);
+    offsetHour = offsetHour >= 0 && offsetHour <= 23 ? offsetHour : 0;
+    let offsetMinute = parseInt(matches[9], 10) || 0;
+    offsetMinute = offsetMinute >= 0 && offsetMinute <= 59 ? offsetMinute : 0;
+
+    if (universalTimeRelation === '-') {
+      hour += offsetHour;
+      minute += offsetMinute;
+    } else if (universalTimeRelation === '+') {
+      hour -= offsetHour;
+      minute -= offsetMinute;
+    }
+
+    return new Date(Date.UTC(year, month, day, hour, minute, second));
+  }
+
+}
+
+exports.PDFDateString = PDFDateString;
+
 /***/ }),
 /* 8 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
@@ -8994,16 +9045,17 @@ class AnnotationElement {
       container.appendChild(trigger);
     }
 
     let popupElement = new PopupElement({
       container,
       trigger,
       color: data.color,
       title: data.title,
+      modificationDate: data.modificationDate,
       contents: data.contents,
       hideWrapper: true
     });
     let popup = popupElement.render();
     popup.style.left = container.style.width;
     container.appendChild(popup);
   }
 
@@ -9291,16 +9343,17 @@ class PopupAnnotationElement extends Ann
       return this.container;
     }
 
     let popup = new PopupElement({
       container: this.container,
       trigger: parentElement,
       color: this.data.color,
       title: this.data.title,
+      modificationDate: this.data.modificationDate,
       contents: this.data.contents
     });
     let parentLeft = parseFloat(parentElement.style.left);
     let parentWidth = parseFloat(parentElement.style.width);
     this.container.style.transformOrigin = -(parentLeft + parentWidth) + 'px -' + parentElement.style.top;
     this.container.style.left = parentLeft + parentWidth + 'px';
     this.container.appendChild(popup.render());
     return this.container;
@@ -9309,16 +9362,17 @@ class PopupAnnotationElement extends Ann
 }
 
 class PopupElement {
   constructor(parameters) {
     this.container = parameters.container;
     this.trigger = parameters.trigger;
     this.color = parameters.color;
     this.title = parameters.title;
+    this.modificationDate = parameters.modificationDate;
     this.contents = parameters.contents;
     this.hideWrapper = parameters.hideWrapper || false;
     this.pinned = false;
   }
 
   render() {
     const BACKGROUND_ENLIGHT = 0.7;
     let wrapper = document.createElement('div');
@@ -9331,26 +9385,40 @@ class PopupElement {
 
     if (color) {
       let r = BACKGROUND_ENLIGHT * (255 - color[0]) + color[0];
       let g = BACKGROUND_ENLIGHT * (255 - color[1]) + color[1];
       let b = BACKGROUND_ENLIGHT * (255 - color[2]) + color[2];
       popup.style.backgroundColor = _util.Util.makeCssRgb(r | 0, g | 0, b | 0);
     }
 
-    let contents = this._formatContents(this.contents);
-
     let title = document.createElement('h1');
     title.textContent = this.title;
+    popup.appendChild(title);
+
+    const dateObject = _display_utils.PDFDateString.toDateObject(this.modificationDate);
+
+    if (dateObject) {
+      const modificationDate = document.createElement('span');
+      modificationDate.textContent = '{{date}}, {{time}}';
+      modificationDate.dataset.l10nId = 'annotation_date_string';
+      modificationDate.dataset.l10nArgs = JSON.stringify({
+        date: dateObject.toLocaleDateString(),
+        time: dateObject.toLocaleTimeString()
+      });
+      popup.appendChild(modificationDate);
+    }
+
+    let contents = this._formatContents(this.contents);
+
+    popup.appendChild(contents);
     this.trigger.addEventListener('click', this._toggle.bind(this));
     this.trigger.addEventListener('mouseover', this._show.bind(this, false));
     this.trigger.addEventListener('mouseout', this._hide.bind(this, false));
     popup.addEventListener('click', this._hide.bind(this, true));
-    popup.appendChild(title);
-    popup.appendChild(contents);
     wrapper.appendChild(popup);
     return wrapper;
   }
 
   _formatContents(contents) {
     let p = document.createElement('p');
     let lines = contents.split(/(?:\r\n?|\n)/);
 
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -118,18 +118,18 @@ return /******/ (function(modules) { // 
 /************************************************************************/
 /******/ ([
 /* 0 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-const pdfjsVersion = '2.2.160';
-const pdfjsBuild = '155304a0';
+const pdfjsVersion = '2.2.167';
+const pdfjsBuild = 'ca2fee3d';
 
 const pdfjsCoreWorker = __w_pdfjs_require__(1);
 
 exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;
 
 /***/ }),
 /* 1 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
@@ -373,17 +373,17 @@ var WorkerMessageHandler = {
 
   createDocumentHandler(docParams, port) {
     var pdfManager;
     var terminated = false;
     var cancelXHRs = null;
     var WorkerTasks = [];
     const verbosity = (0, _util.getVerbosityLevel)();
     let apiVersion = docParams.apiVersion;
-    let workerVersion = '2.2.160';
+    let workerVersion = '2.2.167';
 
     if (apiVersion !== workerVersion) {
       throw new Error(`The API version "${apiVersion}" does not match ` + `the Worker version "${workerVersion}".`);
     }
 
     var docId = docParams.docId;
     var docBaseUrl = docParams.docBaseUrl;
     var workerHandlerName = docParams.docId + '_worker';
@@ -18313,27 +18313,31 @@ function getTransformMatrix(rect, bbox, 
   let xRatio = (rect[2] - rect[0]) / (maxX - minX);
   let yRatio = (rect[3] - rect[1]) / (maxY - minY);
   return [xRatio, 0, 0, yRatio, rect[0] - minX * xRatio, rect[1] - minY * yRatio];
 }
 
 class Annotation {
   constructor(params) {
     let dict = params.dict;
+    this.setCreationDate(dict.get('CreationDate'));
+    this.setModificationDate(dict.get('M'));
     this.setFlags(dict.get('F'));
     this.setRectangle(dict.getArray('Rect'));
     this.setColor(dict.getArray('C'));
     this.setBorderStyle(dict);
     this.setAppearance(dict);
     this.data = {
       annotationFlags: this.flags,
       borderStyle: this.borderStyle,
       color: this.color,
+      creationDate: this.creationDate,
       hasAppearance: !!this.appearance,
       id: params.id,
+      modificationDate: this.modificationDate,
       rect: this.rectangle,
       subtype: params.subtype
     };
   }
 
   _hasFlag(flags, flag) {
     return !!(flags & flag);
   }
@@ -18357,16 +18361,24 @@ class Annotation {
   get printable() {
     if (this.flags === 0) {
       return false;
     }
 
     return this._isPrintable(this.flags);
   }
 
+  setCreationDate(creationDate) {
+    this.creationDate = (0, _util.isString)(creationDate) ? creationDate : null;
+  }
+
+  setModificationDate(modificationDate) {
+    this.modificationDate = (0, _util.isString)(modificationDate) ? modificationDate : null;
+  }
+
   setFlags(flags) {
     this.flags = Number.isInteger(flags) && flags > 0 ? flags : 0;
   }
 
   hasFlag(flag) {
     return this._hasFlag(this.flags, flag);
   }
 
@@ -18943,16 +18955,30 @@ class PopupAnnotation extends Annotation
     }
 
     let parentSubtype = parentItem.get('Subtype');
     this.data.parentType = (0, _primitives.isName)(parentSubtype) ? parentSubtype.name : null;
     this.data.parentId = dict.getRaw('Parent').toString();
     this.data.title = (0, _util.stringToPDFString)(parentItem.get('T') || '');
     this.data.contents = (0, _util.stringToPDFString)(parentItem.get('Contents') || '');
 
+    if (!parentItem.has('CreationDate')) {
+      this.data.creationDate = null;
+    } else {
+      this.setCreationDate(parentItem.get('CreationDate'));
+      this.data.creationDate = this.creationDate;
+    }
+
+    if (!parentItem.has('M')) {
+      this.data.modificationDate = null;
+    } else {
+      this.setModificationDate(parentItem.get('M'));
+      this.data.modificationDate = this.modificationDate;
+    }
+
     if (!parentItem.has('C')) {
       this.data.color = null;
     } else {
       this.setColor(parentItem.getArray('C'));
       this.data.color = this.color;
     }
 
     if (!this.viewable) {
@@ -20577,40 +20603,39 @@ var PartialEvaluator = function PartialE
         }
       } else {
         var opArgs = operatorList.argsArray[lastIndex];
         opArgs[0].push(fn);
         Array.prototype.push.apply(opArgs[1], args);
       }
     },
 
-    handleColorN: function PartialEvaluator_handleColorN(operatorList, fn, args, cs, patterns, resources, task) {
+    async handleColorN(operatorList, fn, args, cs, patterns, resources, task) {
       var patternName = args[args.length - 1];
       var pattern;
 
       if ((0, _primitives.isName)(patternName) && (pattern = patterns.get(patternName.name))) {
         var dict = (0, _primitives.isStream)(pattern) ? pattern.dict : pattern;
         var typeNum = dict.get('PatternType');
 
         if (typeNum === TILING_PATTERN) {
           var color = cs.base ? cs.base.getRgb(args, 0) : null;
           return this.handleTilingType(fn, color, resources, pattern, dict, operatorList, task);
         } else if (typeNum === SHADING_PATTERN) {
           var shading = dict.get('Shading');
           var matrix = dict.getArray('Matrix');
           pattern = _pattern.Pattern.parseShading(shading, matrix, this.xref, resources, this.handler, this.pdfFunctionFactory);
           operatorList.addOp(fn, pattern.getIR());
-          return Promise.resolve();
-        }
-
-        return Promise.reject(new Error('Unknown PatternType: ' + typeNum));
-      }
-
-      operatorList.addOp(fn, args);
-      return Promise.resolve();
+          return undefined;
+        }
+
+        throw new _util.FormatError(`Unknown PatternType: ${typeNum}`);
+      }
+
+      throw new _util.FormatError(`Unknown PatternName: ${patternName}`);
     },
 
     getOperatorList({
       stream,
       task,
       resources,
       operatorList,
       initialState = null
--- a/browser/extensions/pdfjs/content/web/viewer.css
+++ b/browser/extensions/pdfjs/content/web/viewer.css
@@ -217,35 +217,43 @@
   width: 20em;
 }
 
 .annotationLayer .popup {
   position: absolute;
   z-index: 200;
   max-width: 20em;
   background-color: #FFFF99;
-  box-shadow: 0px 2px 5px #333;
+  box-shadow: 0px 2px 5px #888;
   border-radius: 2px;
-  padding: 0.6em;
+  padding: 6px;
   margin-left: 5px;
   cursor: pointer;
   font: message-box;
+  font-size: 9px;
   word-wrap: break-word;
 }
 
+.annotationLayer .popup > * {
+  font-size: 9px;
+}
+
 .annotationLayer .popup h1 {
-  font-size: 1em;
-  border-bottom: 1px solid #000000;
-  margin: 0;
-  padding-bottom: 0.2em;
+  display: inline-block;
+}
+
+.annotationLayer .popup span {
+  display: inline-block;
+  margin-left: 5px;
 }
 
 .annotationLayer .popup p {
-  margin: 0;
-  padding-top: 0.2em;
+  border-top: 1px solid #333;
+  margin-top: 2px;
+  padding-top: 2px;
 }
 
 .annotationLayer .highlightAnnotation,
 .annotationLayer .underlineAnnotation,
 .annotationLayer .squigglyAnnotation,
 .annotationLayer .strikeoutAnnotation,
 .annotationLayer .freeTextAnnotation,
 .annotationLayer .lineAnnotation svg line,
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -4545,20 +4545,20 @@ exports.PDFAttachmentViewer = PDFAttachm
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.PDFDocumentProperties = void 0;
 
+var _pdfjsLib = __webpack_require__(4);
+
 var _ui_utils = __webpack_require__(2);
 
-var _pdfjsLib = __webpack_require__(4);
-
 const DEFAULT_FIELD_CONTENT = '-';
 const NON_METRIC_LOCALES = ['en-us', 'en-lr', 'my'];
 const US_PAGE_NAMES = {
   '8.5x11': 'Letter',
   '8.5x14': 'Legal'
 };
 const METRIC_PAGE_NAMES = {
   '297x420': 'A3',
@@ -4812,51 +4812,26 @@ class PDFDocumentProperties {
         unit,
         name,
         orientation
       }, '{{width}} × {{height}} {{unit}} (' + (name ? '{{name}}, ' : '') + '{{orientation}})');
     });
   }
 
   _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}}');
+    const dateObject = _pdfjsLib.PDFDateString.toDateObject(inputDate);
+
+    if (dateObject) {
+      const dateString = dateObject.toLocaleDateString();
+      const timeString = dateObject.toLocaleTimeString();
+      return this.l10n.get('document_properties_date_string', {
+        date: dateString,
+        time: timeString
+      }, '{{date}}, {{time}}');
+    }
   }
 
   _parseLinearization(isLinearized) {
     return this.l10n.get('document_properties_linearized_' + (isLinearized ? 'yes' : 'no'), null, isLinearized ? 'Yes' : 'No');
   }
 
 }
 
--- a/browser/extensions/pdfjs/moz.yaml
+++ b/browser/extensions/pdfjs/moz.yaml
@@ -15,15 +15,15 @@ origin:
   description: Portable Document Format (PDF) viewer that is built with HTML5
 
   # Full URL for the package's homepage/etc
   # Usually different from repository url
   url: https://github.com/mozilla/pdf.js
 
   # Human-readable identifier for this version/release
   # Generally "version NNN", "tag SSS", "bookmark SSS"
-  release: version 2.2.160
+  release: version 2.2.167
 
   # The package's license, where possible using the mnemonic from
   # https://spdx.org/licenses/
   # Multiple licenses can be specified (as a YAML list)
   # A "LICENSE" file must exist containing the full license text
   license: Apache-2.0
--- a/browser/locales/en-US/pdfviewer/viewer.properties
+++ b/browser/locales/en-US/pdfviewer/viewer.properties
@@ -221,16 +221,20 @@ page_scale_percent={{scale}}%
 
 # Loading indicator messages
 loading_error_indicator=Error
 loading_error=An error occurred while loading the PDF.
 invalid_file_error=Invalid or corrupted PDF file.
 missing_file_error=Missing PDF file.
 unexpected_response_error=Unexpected server response.
 
+# LOCALIZATION NOTE (annotation_date_string): "{{date}}" and "{{time}}" will be
+# replaced by the modification date, and time, of the annotation.
+annotation_date_string={{date}}, {{time}}
+
 # LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip.
 # "{{type}}" will be replaced with an annotation type from a list defined in
 # the PDF spec (32000-1:2008 Table 169 – Annotation types).
 # Some common types are e.g.: "Check", "Text", "Comment", "Note"
 text_annotation_type.alt=[{{type}} Annotation]
 password_label=Enter the password to open this PDF file.
 password_invalid=Invalid password. Please try again.
 password_ok=OK
--- a/dom/interfaces/base/nsIRemoteTab.idl
+++ b/dom/interfaces/base/nsIRemoteTab.idl
@@ -2,16 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 #include "domstubs.idl"
 
 interface nsIPrincipal;
 webidl Element;
+webidl WindowGlobalParent;
 
 [builtinclass, scriptable, uuid(8e49f7b0-1f98-4939-bf91-e9c39cd56434)]
 interface nsIRemoteTab : nsISupports
 {
   /**
    * Manages the docshell active state of the remote browser. Setting the
    * docShell to be active will also cause it to render layers and upload
    * them to the compositor. Setting the docShell as not active will clear
@@ -64,16 +65,21 @@ interface nsIRemoteTab : nsISupports
   readonly attribute boolean hasContentOpener;
   /**
    * True if we've previously received layers for this tab when switching to
    * it.
    */
   readonly attribute boolean hasPresented;
 
   /**
+   * Gets all of the WindowGlobalParent actors for this remote tab.
+   */
+  readonly attribute Array<WindowGlobalParent> windowGlobalParents;
+
+  /**
    * Ensures that the content process which has this remote tab has all of the
    * permissions required to load a document with the given principal.
    */
   void transmitPermissionsForPrincipal(in nsIPrincipal aPrincipal);
 
   /**
    * True if any of the frames loaded in the tab have registered
    * an onbeforeunload event handler.
--- a/dom/ipc/BrowserBridgeParent.cpp
+++ b/dom/ipc/BrowserBridgeParent.cpp
@@ -1,15 +1,16 @@
 /* -*- 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 "mozilla/dom/BrowserBridgeParent.h"
+#include "mozilla/dom/BrowserParent.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ContentProcessManager.h"
 #include "mozilla/dom/CanonicalBrowsingContext.h"
 #include "mozilla/dom/BrowsingContextGroup.h"
 #include "mozilla/layers/InputAPZContext.h"
 
 using namespace mozilla::ipc;
 using namespace mozilla::layout;
@@ -94,16 +95,25 @@ nsresult BrowserBridgeParent::Init(const
     return NS_ERROR_FAILURE;
   }
 
   // Send the newly created layers ID back into content.
   Unused << SendSetLayersId(rf->GetLayersId());
   return NS_OK;
 }
 
+CanonicalBrowsingContext* BrowserBridgeParent::GetBrowsingContext() {
+  return mBrowserParent->GetBrowsingContext();
+}
+
+BrowserParent* BrowserBridgeParent::Manager() {
+  MOZ_ASSERT(mIPCOpen);
+  return static_cast<BrowserParent*>(PBrowserBridgeParent::Manager());
+}
+
 void BrowserBridgeParent::Destroy() {
   if (mBrowserParent) {
     mBrowserParent->Destroy();
     mBrowserParent = nullptr;
   }
 }
 
 IPCResult BrowserBridgeParent::RecvShow(const ScreenIntSize& aSize,
--- a/dom/ipc/BrowserBridgeParent.h
+++ b/dom/ipc/BrowserBridgeParent.h
@@ -3,21 +3,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_BrowserBridgeParent_h
 #define mozilla_dom_BrowserBridgeParent_h
 
 #include "mozilla/dom/PBrowserBridgeParent.h"
-#include "mozilla/dom/BrowserParent.h"
 
 namespace mozilla {
 namespace dom {
 
+class BrowserParent;
+
 /**
  * BrowserBridgeParent implements the parent actor part of the PBrowserBridge
  * protocol. See PBrowserBridge for more information.
  */
 class BrowserBridgeParent : public PBrowserBridgeParent {
  public:
   NS_INLINE_DECL_REFCOUNTING(BrowserBridgeParent);
 
@@ -25,25 +26,20 @@ class BrowserBridgeParent : public PBrow
 
   // Initialize this actor after performing startup.
   nsresult Init(const nsString& aPresentationURL, const nsString& aRemoteType,
                 CanonicalBrowsingContext* aBrowsingContext,
                 const uint32_t& aChromeFlags);
 
   BrowserParent* GetBrowserParent() { return mBrowserParent; }
 
-  CanonicalBrowsingContext* GetBrowsingContext() {
-    return mBrowserParent->GetBrowsingContext();
-  }
+  CanonicalBrowsingContext* GetBrowsingContext();
 
   // Get our manager actor.
-  BrowserParent* Manager() {
-    MOZ_ASSERT(mIPCOpen);
-    return static_cast<BrowserParent*>(PBrowserBridgeParent::Manager());
-  }
+  BrowserParent* Manager();
 
   // Tear down this BrowserBridgeParent.
   void Destroy();
 
  protected:
   friend class PBrowserBridgeParent;
 
   mozilla::ipc::IPCResult RecvShow(const ScreenIntSize& aSize,
--- a/dom/ipc/BrowserParent.cpp
+++ b/dom/ipc/BrowserParent.cpp
@@ -535,24 +535,19 @@ void BrowserParent::SetOwnerElement(Elem
   }
 
   // Set our BrowsingContext's embedder if we're not embedded within a
   // BrowserBridgeParent.
   if (!GetBrowserBridgeParent() && mBrowsingContext) {
     mBrowsingContext->SetEmbedderElement(mFrameElement);
   }
 
-  // Ensure all BrowserParent actors within BrowserBridges are also updated.
-  const auto& browserBridges = ManagedPBrowserBridgeParent();
-  for (auto iter = browserBridges.ConstIter(); !iter.Done(); iter.Next()) {
-    BrowserBridgeParent* browserBridge =
-        static_cast<BrowserBridgeParent*>(iter.Get()->GetKey());
-
-    browserBridge->GetBrowserParent()->SetOwnerElement(aElement);
-  }
+  VisitChildren([aElement](BrowserBridgeParent* aBrowser) {
+    aBrowser->GetBrowserParent()->SetOwnerElement(aElement);
+  });
 }
 
 NS_IMETHODIMP BrowserParent::GetOwnerElement(Element** aElement) {
   *aElement = do_AddRef(GetOwnerElement()).take();
   return NS_OK;
 }
 
 void BrowserParent::CacheFrameLoader(nsFrameLoader* aFrameLoader) {
@@ -3250,16 +3245,31 @@ BrowserParent::GetHasPresented(bool* aRe
   return NS_OK;
 }
 
 void BrowserParent::NavigateByKey(bool aForward, bool aForDocumentNavigation) {
   Unused << SendNavigateByKey(aForward, aForDocumentNavigation);
 }
 
 NS_IMETHODIMP
+BrowserParent::GetWindowGlobalParents(
+    nsTArray<RefPtr<WindowGlobalParent>>& aWindowGlobalParents) {
+  VisitAll([&aWindowGlobalParents](BrowserParent* aBrowser) {
+    const auto& windowGlobalParents = aBrowser->ManagedPWindowGlobalParent();
+    for (auto iter = windowGlobalParents.ConstIter(); !iter.Done();
+         iter.Next()) {
+      WindowGlobalParent* windowGlobalParent =
+          static_cast<WindowGlobalParent*>(iter.Get()->GetKey());
+      aWindowGlobalParents.AppendElement(windowGlobalParent);
+    }
+  });
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 BrowserParent::TransmitPermissionsForPrincipal(nsIPrincipal* aPrincipal) {
   return Manager()->TransmitPermissionsForPrincipal(aPrincipal);
 }
 
 NS_IMETHODIMP
 BrowserParent::GetHasBeforeUnload(bool* aResult) {
   *aResult = mHasBeforeUnload;
   return NS_OK;
--- a/dom/ipc/BrowserParent.h
+++ b/dom/ipc/BrowserParent.h
@@ -6,16 +6,17 @@
 
 #ifndef mozilla_dom_BrowserParent_h
 #define mozilla_dom_BrowserParent_h
 
 #include "js/TypeDecls.h"
 #include "LiveResizeListener.h"
 #include "mozilla/ContentCache.h"
 #include "mozilla/dom/ipc/IdType.h"
+#include "mozilla/dom/BrowserBridgeParent.h"
 #include "mozilla/dom/PBrowserParent.h"
 #include "mozilla/dom/PContent.h"
 #include "mozilla/dom/PFilePickerParent.h"
 #include "mozilla/dom/TabContext.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/gfx/CrossProcessPaint.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
@@ -67,17 +68,16 @@ class DataSourceSurface;
 
 namespace dom {
 
 class CanonicalBrowsingContext;
 class ClonedMessageData;
 class ContentParent;
 class Element;
 class DataTransfer;
-class BrowserBridgeParent;
 
 namespace ipc {
 class StructuredCloneData;
 }  // namespace ipc
 
 /**
  * BrowserParent implements the parent actor part of the PBrowser protocol. See
  * PBrowser for more information.
@@ -182,16 +182,56 @@ class BrowserParent final : public PBrow
   ShowInfo GetShowInfo();
 
   /**
    * Let managees query if Destroy() is already called so they don't send out
    * messages when the PBrowser actor is being destroyed.
    */
   bool IsDestroyed() const { return mIsDestroyed; }
 
+  /*
+   * Visit each BrowserParent in the tree formed by PBrowser and
+   * PBrowserBridge, including `this`.
+   */
+  template <typename Callback>
+  void VisitAll(Callback aCallback) {
+    aCallback(this);
+    VisitAllDescendants(aCallback);
+  }
+
+  /*
+   * Visit each BrowserParent in the tree formed by PBrowser and
+   * PBrowserBridge, excluding `this`.
+   */
+  template <typename Callback>
+  void VisitAllDescendants(Callback aCallback) {
+    const auto& browserBridges = ManagedPBrowserBridgeParent();
+    for (auto iter = browserBridges.ConstIter(); !iter.Done(); iter.Next()) {
+      BrowserBridgeParent* browserBridge =
+          static_cast<BrowserBridgeParent*>(iter.Get()->GetKey());
+      BrowserParent* browserParent = browserBridge->GetBrowserParent();
+
+      aCallback(browserParent);
+      browserParent->VisitAllDescendants(aCallback);
+    }
+  }
+
+  /*
+   * Visit each BrowserBridgeParent that is a child of this BrowserParent.
+   */
+  template <typename Callback>
+  void VisitChildren(Callback aCallback) {
+    const auto& browserBridges = ManagedPBrowserBridgeParent();
+    for (auto iter = browserBridges.ConstIter(); !iter.Done(); iter.Next()) {
+      BrowserBridgeParent* browserBridge =
+          static_cast<BrowserBridgeParent*>(iter.Get()->GetKey());
+      aCallback(browserBridge);
+    }
+  }
+
   void SetOwnerElement(Element* aElement);
 
   void SetBrowserDOMWindow(nsIBrowserDOMWindow* aBrowserDOMWindow) {
     mBrowserDOMWindow = aBrowserDOMWindow;
   }
 
   void SetHasContentOpener(bool aHasContentOpener);
 
--- a/dom/svg/SVGTests.h
+++ b/dom/svg/SVGTests.h
@@ -91,16 +91,17 @@ class SVGTests : public nsISupports {
   void GetAttrValue(uint8_t aAttrEnum, nsAttrValue& aValue) const;
 
   void MaybeInvalidate();
 
   // WebIDL
   already_AddRefed<DOMSVGStringList> RequiredFeatures();
   already_AddRefed<DOMSVGStringList> RequiredExtensions();
   already_AddRefed<DOMSVGStringList> SystemLanguage();
+
   bool HasExtension(const nsAString& aExtension) const;
 
   virtual SVGElement* AsSVGElement() = 0;
 
   const SVGElement* AsSVGElement() const {
     return const_cast<SVGTests*>(this)->AsSVGElement();
   }
 
--- a/dom/webidl/SVGTests.webidl
+++ b/dom/webidl/SVGTests.webidl
@@ -11,12 +11,10 @@
  */
 
 [NoInterfaceObject]
 interface SVGTests {
 
   readonly attribute SVGStringList requiredFeatures;
   readonly attribute SVGStringList requiredExtensions;
   readonly attribute SVGStringList systemLanguage;
-
-  boolean hasExtension(DOMString extension);
 };
 
--- a/layout/mathml/tests/test_disabled_chrome.html
+++ b/layout/mathml/tests/test_disabled_chrome.html
@@ -37,18 +37,15 @@ https://bugzilla.mozilla.org/show_bug.cg
     let loadPromise = ContentTaskUtils.waitForEvent(iframeEl, 'load', false);
     t.appendChild(iframeEl);
     await loadPromise;
 
     const contentBR = iframeEl.contentDocument.body.getBoundingClientRect();
 
     ok(chromeBR.height > contentBR.height, "Chrome content height should be bigger than content due to layout");
 
-    ok(!iframeEl.contentDocument.getElementById('svgel').hasExtension("http://www.w3.org/1998/Math/MathML"), 'SVG namespace support is disabled in content iframe');
-    ok(chromeIframeEl.contentDocument.getElementById('svgel').hasExtension("http://www.w3.org/1998/Math/MathML"), 'SVG namespace support is enabled in chrome iframe');
-
     SpecialPowers.setBoolPref("mathml.disabled", initialPrefValue);
   });
 </script>
 </pre>
 </body>
 </html>
 
--- a/layout/svg/tests/test_disabled_chrome.html
+++ b/layout/svg/tests/test_disabled_chrome.html
@@ -33,19 +33,16 @@ https://bugzilla.mozilla.org/show_bug.cg
     iframeEl.src = url;
     let loadPromise = ContentTaskUtils.waitForEvent(iframeEl, 'load', false);
     t.appendChild(iframeEl);
     await loadPromise;
 
     const contentBR = iframeEl.contentDocument.body.getBoundingClientRect();
     ok(chromeBR.height > contentBR.height, "Chrome content height should be bigger than content due to layout");
 
-    ok(!("hasExtension" in iframeEl.contentDocument.getElementById('svgel')), 'SVG is disabled so no hasExtension support is available in content iframe');
-    ok(chromeIframeEl.contentDocument.getElementById('svgel').hasExtension("http://www.w3.org/1998/Math/MathML"), 'SVG namespace support is enabled in chrome iframe');
-
     url = "http://mochi.test:8888/chrome/layout/svg/tests/svg_example_script.svg";
     const iframeElScript = document.createElement("iframe");
     let loadPromiseScript = ContentTaskUtils.waitForEvent(iframeElScript, "load", false);
     iframeElScript.src = url;
     t.appendChild(iframeElScript);
     await loadPromiseScript;
     ok(!iframeElScript.contentDocument.documentElement.style, "Content should not be styled");
 
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mathml/relations/html5-tree/required-extensions-1.html.ini
@@ -0,0 +1,4 @@
+[required-extensions-1.html]
+  [Testing foreignObject.hasExtension('http://www.w3.org/1998/Math/MathML')]
+    expected: FAIL
+
--- a/testing/web-platform/meta/svg/historical.html.ini
+++ b/testing/web-platform/meta/svg/historical.html.ini
@@ -3,17 +3,14 @@
     expected: FAIL
 
   [SVGZoomAndPan mixin interface must not be exposed]
     expected: FAIL
 
   [SVGGraphicsElement.prototype.getTransformToElement must be removed]
     expected: FAIL
 
-  [SVGGraphicsElement.prototype.hasExtension must be removed]
-    expected: FAIL
-
   [SVGGraphicsElement.prototype.requiredFeatures must be removed]
     expected: FAIL
 
   [SVGSVGElement.prototype.useCurrentView must be removed]
     expected: FAIL