Merge m-c to autoland, a=merge
authorWes Kocher <wkocher@mozilla.com>
Thu, 27 Jul 2017 16:33:25 -0700
changeset 420222 81a0e07cdc2c94c84751c404ea0bec40914dd0c6
parent 420221 face8d3f9fb9045e40b1eaabfa2794d6f4bae5ea (current diff)
parent 420149 a4afa89bfdd10a903cfa9aa5a5bd1624dc85548c (diff)
child 420223 43b15ca14bb5468fb3256c498a03ae020d4c413d
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to autoland, a=merge MozReview-Commit-ID: KvG6hKUwF8c
gfx/ots/tests/table_dependencies_test.cc
old-configure.in
taskcluster/ci/test/test-platforms.yml
--- a/.cron.yml
+++ b/.cron.yml
@@ -2,41 +2,46 @@
 # `taskcluster/taskgraph/cron/schema.py`.  For documentation, see
 # `taskcluster/docs/cron.rst`.
 
 jobs:
     - name: nightly-desktop
       job:
           type: decision-task
           treeherder-symbol: Nd
-          target-tasks-method: nightly_linux
+          target-tasks-method: nightly_desktop
       run-on-projects:
           - mozilla-central
           - date
       when:
           by-project:
             # Match buildbot starts for now
             date: [{hour: 15, minute: 0}]
             mozilla-central: [{hour: 10, minute: 0}]
             # No default
 
+    - name: nightly-desktop-linux
+      job:
+          type: decision-task
+          treeherder-symbol: Nd-Ln
+          target-tasks-method: nightly_linux
+      run-on-projects:
+          - mozilla-central
+          - date
+      when: [] # never (hook only)
+
     - name: nightly-desktop-osx
       job:
           type: decision-task
           treeherder-symbol: Nd-OSX
           target-tasks-method: nightly_macosx
       run-on-projects:
           - mozilla-central
           - date
-      when:
-          by-project:
-            # Match buildbot starts for now
-            date: [{hour: 15, minute: 0}]
-            mozilla-central: [{hour: 10, minute: 0}]
-            # No default
+      when: [] # never (hook only)
 
     - name: nightly-desktop-win
       job:
           type: decision-task
           treeherder-symbol: Nd-Win
           target-tasks-method: nightly_win
       run-on-projects:
           - mozilla-central
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -1333,16 +1333,18 @@ DocAccessible::UnbindFromDocument(Access
 #endif
   }
 
   // Remove an accessible from node-to-accessible map if it exists there.
   if (aAccessible->IsNodeMapEntry() &&
       mNodeToAccessibleMap.Get(aAccessible->GetNode()) == aAccessible)
     mNodeToAccessibleMap.Remove(aAccessible->GetNode());
 
+  aAccessible->mStateFlags |= eIsNotInDocument;
+
   // Update XPCOM part.
   xpcAccessibleDocument* xpcDoc = GetAccService()->GetCachedXPCDocument(this);
   if (xpcDoc)
     xpcDoc->NotifyOfShutdown(aAccessible);
 
   void* uniqueID = aAccessible->UniqueID();
 
   NS_ASSERTION(!aAccessible->IsDefunct(), "Shutdown the shutdown accessible!");
--- a/accessible/tests/mochitest/focus/a11y.ini
+++ b/accessible/tests/mochitest/focus/a11y.ini
@@ -1,9 +1,9 @@
 [DEFAULT]
 support-files =
   !/accessible/tests/mochitest/*.js
 
 [test_focusedChild.html]
-skip-if = (os == 'win' && (os_version == '6.2' || os_version == '6.3' || os_version == '10.0.15063')) # bug 845134
+skip-if = (os == 'win' && os_version != '6.1') # bug 845134
 [test_takeFocus.html]
 skip-if = buildapp == 'mulet'
 [test_takeFocus.xul]
--- a/devtools/client/debugger/new/debugger.js
+++ b/devtools/client/debugger/new/debugger.js
@@ -13396,17 +13396,17 @@ return /******/ (function(modules) { // 
 /* 226 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// @flow
 
 	const { isDevelopment } = __webpack_require__(828);
 	const { Services, PrefsHelper } = __webpack_require__(830);
 
-	const prefsSchemaVersion = "1.0.1";
+	const prefsSchemaVersion = "1.0.2";
 
 	const pref = Services.pref;
 
 	if (isDevelopment()) {
 	  pref("devtools.debugger.client-source-maps-enabled", true);
 	  pref("devtools.debugger.pause-on-exceptions", false);
 	  pref("devtools.debugger.ignore-caught-exceptions", false);
 	  pref("devtools.debugger.call-stack-visible", false);
@@ -24233,29 +24233,35 @@ return /******/ (function(modules) { // 
 	    this.setState({ editor });
 	    return editor;
 	  }
 
 	  componentDidMount() {
 	    this.cbPanel = null;
 	    var editor = this.setupEditor();
 
-	    var selectedSource = this.props.selectedSource;
+	    var _props = this.props,
+	        selectedSource = _props.selectedSource,
+	        selectedLocation = _props.selectedLocation;
 	    var shortcuts = this.context.shortcuts;
 
 
 	    var searchAgainKey = L10N.getStr("sourceSearch.search.again.key2");
 	    var searchAgainPrevKey = L10N.getStr("sourceSearch.search.againPrev.key2");
 
 	    shortcuts.on("CmdOrCtrl+B", this.onToggleBreakpoint);
 	    shortcuts.on("CmdOrCtrl+Shift+B", this.onToggleBreakpoint);
 	    shortcuts.on("Esc", this.onEscape);
 	    shortcuts.on(searchAgainPrevKey, this.onSearchAgain);
 	    shortcuts.on(searchAgainKey, this.onSearchAgain);
 
+	    if (selectedLocation && !!selectedLocation.line) {
+	      this.pendingJumpLocation = selectedLocation;
+	    }
+
 	    (0, _editor.updateDocument)(editor, selectedSource);
 	  }
 
 	  componentWillUnmount() {
 	    this.state.editor.destroy();
 	    this.setState({ editor: null });
 
 	    var searchAgainKey = L10N.getStr("sourceSearch.search.again.key2");
@@ -24266,19 +24272,19 @@ return /******/ (function(modules) { // 
 	    shortcuts.off(searchAgainPrevKey);
 	    shortcuts.off(searchAgainKey);
 	  }
 
 	  componentDidUpdate(prevProps) {
 	    // This is in `componentDidUpdate` so helper functions can expect
 	    // `this.props` to be the current props. This lifecycle method is
 	    // responsible for updating the editor annotations.
-	    var _props = this.props,
-	        selectedLocation = _props.selectedLocation,
-	        selectedSource = _props.selectedSource;
+	    var _props2 = this.props,
+	        selectedLocation = _props2.selectedLocation,
+	        selectedSource = _props2.selectedSource;
 
 	    // If the location is different and a new line is requested,
 	    // update the pending jump line. Note that if jumping to a line in
 	    // a source where the text hasn't been loaded yet, we will set the
 	    // line here but not jump until rendering the actual source.
 
 	    if (prevProps.selectedLocation !== selectedLocation) {
 	      if (selectedLocation && selectedLocation.line != undefined) {
@@ -24347,66 +24353,66 @@ return /******/ (function(modules) { // 
 	    }
 	  }
 
 	  onScroll() {
 	    this.clearPreviewSelection();
 	  }
 
 	  onSearchAgain(_, e) {
-	    var _props2 = this.props,
-	        query = _props2.query,
-	        searchModifiers = _props2.searchModifiers;
+	    var _props3 = this.props,
+	        query = _props3.query,
+	        searchModifiers = _props3.searchModifiers;
 	    var codeMirror = this.state.editor.editor.codeMirror;
 
 	    var ctx = { ed: this.state.editor, cm: codeMirror };
 
 	    var direction = e.shiftKey ? "prev" : "next";
 	    (0, _editor.traverseResults)(e, ctx, query, direction, searchModifiers.toJS());
 	  }
 
 	  clearPreviewSelection() {
 	    this.props.clearSelection();
 	  }
 
 	  inSelectedFrameSource() {
-	    var _props3 = this.props,
-	        selectedLocation = _props3.selectedLocation,
-	        selectedFrame = _props3.selectedFrame;
+	    var _props4 = this.props,
+	        selectedLocation = _props4.selectedLocation,
+	        selectedFrame = _props4.selectedFrame;
 
 	    return selectedFrame && selectedLocation && selectedFrame.location.sourceId == selectedLocation.sourceId;
 	  }
 
 	  openMenu(event, codeMirror) {
-	    var _props4 = this.props,
-	        selectedSource = _props4.selectedSource,
-	        selectedLocation = _props4.selectedLocation,
-	        showSource = _props4.showSource,
-	        jumpToMappedLocation = _props4.jumpToMappedLocation,
-	        addExpression = _props4.addExpression,
-	        toggleBlackBox = _props4.toggleBlackBox;
+	    var _props5 = this.props,
+	        selectedSource = _props5.selectedSource,
+	        selectedLocation = _props5.selectedLocation,
+	        showSource = _props5.showSource,
+	        jumpToMappedLocation = _props5.jumpToMappedLocation,
+	        addExpression = _props5.addExpression,
+	        toggleBlackBox = _props5.toggleBlackBox;
 
 
 	    return (0, _EditorMenu2.default)({
 	      codeMirror,
 	      event,
 	      selectedLocation,
 	      selectedSource,
 	      showSource,
 	      jumpToMappedLocation,
 	      addExpression,
 	      toggleBlackBox,
 	      onGutterContextMenu: this.onGutterContextMenu
 	    });
 	  }
 
 	  onGutterClick(cm, line, gutter, ev) {
-	    var _props5 = this.props,
-	        selectedSource = _props5.selectedSource,
-	        toggleBreakpoint = _props5.toggleBreakpoint;
+	    var _props6 = this.props,
+	        selectedSource = _props6.selectedSource,
+	        toggleBreakpoint = _props6.toggleBreakpoint;
 
 	    // ignore right clicks in the gutter
 
 	    if (ev.ctrlKey && ev.button === 0 || ev.which === 3 || selectedSource && selectedSource.get("isBlackBoxed")) {
 	      return;
 	    }
 
 	    if (this.isCbPanelOpen()) {
@@ -24414,21 +24420,21 @@ return /******/ (function(modules) { // 
 	    }
 
 	    if (gutter !== "CodeMirror-foldgutter") {
 	      toggleBreakpoint(line + 1);
 	    }
 	  }
 
 	  onGutterContextMenu(event) {
-	    var _props6 = this.props,
-	        selectedSource = _props6.selectedSource,
-	        breakpoints = _props6.breakpoints,
-	        toggleBreakpoint = _props6.toggleBreakpoint,
-	        toggleDisabledBreakpoint = _props6.toggleDisabledBreakpoint;
+	    var _props7 = this.props,
+	        selectedSource = _props7.selectedSource,
+	        breakpoints = _props7.breakpoints,
+	        toggleBreakpoint = _props7.toggleBreakpoint,
+	        toggleDisabledBreakpoint = _props7.toggleDisabledBreakpoint;
 
 
 	    if (selectedSource && selectedSource.get("isBlackBoxed")) {
 	      event.preventDefault();
 	      return;
 	    }
 
 	    var line = (0, _editor.lineAtHeight)(this.state.editor, event);
@@ -24455,20 +24461,20 @@ return /******/ (function(modules) { // 
 	    }
 	  }
 
 	  toggleConditionalPanel(line) {
 	    if (this.isCbPanelOpen()) {
 	      return this.closeConditionalPanel();
 	    }
 
-	    var _props7 = this.props,
-	        selectedLocation = _props7.selectedLocation,
-	        setBreakpointCondition = _props7.setBreakpointCondition,
-	        breakpoints = _props7.breakpoints;
+	    var _props8 = this.props,
+	        selectedLocation = _props8.selectedLocation,
+	        setBreakpointCondition = _props8.setBreakpointCondition,
+	        breakpoints = _props8.breakpoints;
 
 	    var sourceId = selectedLocation ? selectedLocation.sourceId : "";
 
 	    var breakpoint = breakpoints.find(bp => bp.location.line === line);
 	    var location = { sourceId, line };
 	    var condition = breakpoint ? breakpoint.condition : "";
 
 	    var panel = (0, _ConditionalPanel.renderConditionalPanel)({
@@ -24490,33 +24496,31 @@ return /******/ (function(modules) { // 
 	  }
 
 	  isCbPanelOpen() {
 	    return !!this.cbPanel;
 	  }
 
 	  clearDebugLine(selectedFrame) {
 	    if (this.state.editor && selectedFrame) {
-	      var _selectedFrame$locati = selectedFrame.location,
-	          sourceId = _selectedFrame$locati.sourceId,
-	          line = _selectedFrame$locati.line;
+	      var line = selectedFrame.location.line;
 
 	      if (debugExpression) {
 	        debugExpression.clear();
 	      }
 
 	      this.state.editor.codeMirror.removeLineClass(line - 1, "line", "new-debug-line");
 	    }
 	  }
 
 	  setDebugLine(selectedFrame, selectedLocation) {
 	    if (this.state.editor && selectedFrame && selectedLocation && selectedFrame.location.sourceId === selectedLocation.sourceId) {
-	      var _selectedFrame$locati2 = selectedFrame.location,
-	          line = _selectedFrame$locati2.line,
-	          column = _selectedFrame$locati2.column;
+	      var _selectedFrame$locati = selectedFrame.location,
+	          line = _selectedFrame$locati.line,
+	          column = _selectedFrame$locati.column;
 
 	      this.state.editor.codeMirror.addLineClass(line - 1, "line", "new-debug-line");
 
 	      debugExpression = (0, _editor.markText)(this.state.editor, "debug-expression", {
 	        start: { line, column },
 	        end: { line, column: null }
 	      });
 	    }
@@ -24554,20 +24558,20 @@ return /******/ (function(modules) { // 
 
 	  showMessage(msg) {
 	    this.state.editor.replaceDocument(this.state.editor.createDocument());
 	    this.state.editor.setText(msg);
 	    this.state.editor.setMode({ name: "text" });
 	  }
 
 	  getInlineEditorStyles() {
-	    var _props8 = this.props,
-	        selectedSource = _props8.selectedSource,
-	        horizontal = _props8.horizontal,
-	        searchOn = _props8.searchOn;
+	    var _props9 = this.props,
+	        selectedSource = _props9.selectedSource,
+	        horizontal = _props9.horizontal,
+	        searchOn = _props9.searchOn;
 
 
 	    var subtractions = [];
 
 	    if ((0, _editor.shouldShowFooter)(selectedSource, horizontal)) {
 	      subtractions.push(cssVars.footerHeight);
 	    }
 
@@ -24580,47 +24584,47 @@ return /******/ (function(modules) { // 
 	      height: subtractions.length === 0 ? "100%" : `calc(100% - ${subtractions.join(" - ")})`
 	    };
 	  }
 
 	  renderHighlightLines() {
 	    var highlightedLineRange = this.props.highlightedLineRange;
 
 
-	    if (!highlightedLineRange) {
+	    if (!highlightedLineRange || !this.state.editor) {
 	      return;
 	    }
 
 	    return HighlightLines({
 	      editor: this.state.editor,
 	      highlightedLineRange
 	    });
 	  }
 
 	  renderHitCounts() {
-	    var _props9 = this.props,
-	        hitCount = _props9.hitCount,
-	        selectedSource = _props9.selectedSource;
+	    var _props10 = this.props,
+	        hitCount = _props10.hitCount,
+	        selectedSource = _props10.selectedSource;
 
 
 	    if (!selectedSource || selectedSource.get("loading") || !hitCount || !this.state.editor) {
 	      return;
 	    }
 
 	    return hitCount.filter(marker => marker.get("count") > 0).map(marker => HitMarker({
 	      key: marker.get("line"),
 	      hitData: marker.toJS(),
 	      editor: this.state.editor.codeMirror
 	    }));
 	  }
 
 	  renderPreview() {
-	    var _props10 = this.props,
-	        selectedSource = _props10.selectedSource,
-	        selection = _props10.selection;
+	    var _props11 = this.props,
+	        selectedSource = _props11.selectedSource,
+	        selection = _props11.selection;
 
 	    if (!this.state.editor || !selectedSource) {
 	      return null;
 	    }
 
 	    if (!selection || selection.updating) {
 	      return;
 	    }
@@ -24664,21 +24668,21 @@ return /******/ (function(modules) { // 
 
 	    if (!editor || !(0, _devtoolsConfig.isEnabled)("columnBreakpoints")) {
 	      return null;
 	    }
 	    return CallSites({ editor });
 	  }
 
 	  renderSearchBar() {
-	    var _props11 = this.props,
-	        selectSource = _props11.selectSource,
-	        selectedSource = _props11.selectedSource,
-	        highlightLineRange = _props11.highlightLineRange,
-	        clearHighlightLineRange = _props11.clearHighlightLineRange;
+	    var _props12 = this.props,
+	        selectSource = _props12.selectSource,
+	        selectedSource = _props12.selectedSource,
+	        highlightLineRange = _props12.highlightLineRange,
+	        clearHighlightLineRange = _props12.clearHighlightLineRange;
 
 
 	    if (!this.state.editor) {
 	      return null;
 	    }
 
 	    return SearchBar({
 	      editor: this.state.editor,
@@ -24703,19 +24707,19 @@ return /******/ (function(modules) { // 
 	    if (!this.state.editor) {
 	      return null;
 	    }
 
 	    return Breakpoints({ editor: this.state.editor });
 	  }
 
 	  render() {
-	    var _props12 = this.props,
-	        coverageOn = _props12.coverageOn,
-	        pauseData = _props12.pauseData;
+	    var _props13 = this.props,
+	        coverageOn = _props13.coverageOn,
+	        pauseData = _props13.pauseData;
 
 
 	    return _react.DOM.div({
 	      className: (0, _classnames2.default)("editor-wrapper", {
 	        "coverage-on": coverageOn,
 	        paused: !!pauseData && (0, _devtoolsConfig.isEnabled)("highlightScopeLines")
 	      })
 	    }, this.renderSearchBar(), _react.DOM.div({
@@ -25313,17 +25317,17 @@ return /******/ (function(modules) { // 
 
 	    return _asyncToGenerator(function* () {
 	      var _props5 = _this.props,
 	          selectedSource = _props5.selectedSource,
 	          modifiers = _props5.modifiers,
 	          ed = _props5.editor;
 
 
-	      if (!ed || !selectedSource || !selectedSource.get("text") || !modifiers) {
+	      if (!query || !ed || !selectedSource || !selectedSource.get("text") || !modifiers) {
 	        return;
 	      }
 
 	      var ctx = { ed, cm: ed.codeMirror };
 
 	      var _modifiers = modifiers.toJS();
 	      var matches = yield (0, _search.getMatches)(query, selectedSource.get("text"), _modifiers);
 
@@ -28084,17 +28088,17 @@ return /******/ (function(modules) { // 
 	      return null;
 	    }
 
 	    return _react.DOM.input({
 	      type: "checkbox",
 	      "aria-label": breakpointsDisabled ? L10N.getStr("breakpoints.enable") : L10N.getStr("breakpoints.disable"),
 	      className: boxClassName,
 	      disabled: breakpointsLoading,
-	      onClick: e => {
+	      onChange: e => {
 	        e.stopPropagation();
 	        toggleAllBreakpoints(!breakpointsDisabled);
 	      },
 	      checked: !breakpointsDisabled && !isIndeterminate,
 	      ref: input => {
 	        if (input) {
 	          input.indeterminate = isIndeterminate;
 	        }
@@ -45922,17 +45926,19 @@ return /******/ (function(modules) { // 
 
 	"use strict";
 
 	/* 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/. */
 
 	function forEachLine(codeMirror, iter) {
-	  codeMirror.doc.iter(0, codeMirror.lineCount(), iter);
+	  codeMirror.operation(() => {
+	    codeMirror.doc.iter(0, codeMirror.lineCount(), iter);
+	  });
 	}
 
 	function removeLineClass(codeMirror, line, className) {
 	  codeMirror.removeLineClass(line, "line", className);
 	}
 
 	function clearLineClass(codeMirror, className) {
 	  forEachLine(codeMirror, line => {
@@ -45943,35 +45949,52 @@ return /******/ (function(modules) { // 
 	function getTextForLine(codeMirror, line) {
 	  return codeMirror.getLine(line - 1).trim();
 	}
 
 	function getCursorLine(codeMirror) {
 	  return codeMirror.getCursor().line;
 	}
 
+	function getTokenLocation(codeMirror, tokenEl) {
+	  var lineOffset = 1;
+
+	  var _tokenEl$getBoundingC = tokenEl.getBoundingClientRect(),
+	      left = _tokenEl$getBoundingC.left,
+	      top = _tokenEl$getBoundingC.top;
+
+	  var _codeMirror$coordsCha = codeMirror.coordsChar({ left, top }),
+	      line = _codeMirror$coordsCha.line,
+	      ch = _codeMirror$coordsCha.ch;
+
+	  return {
+	    line: line + lineOffset,
+	    column: ch
+	  };
+	}
+
 	/**
 	 * Forces the breakpoint gutter to be the same size as the line
 	 * numbers gutter. Editor CSS will absolutely position the gutter
 	 * beneath the line numbers. This makes it easy to be flexible with
 	 * how we overlay breakpoints.
 	 */
 	function resizeBreakpointGutter(editor) {
 	  var gutters = editor.display.gutters;
 	  var lineNumbers = gutters.querySelector(".CodeMirror-linenumbers");
 	  var breakpoints = gutters.querySelector(".breakpoints");
-	  var width = lineNumbers.clientWidth;
-	  breakpoints.style.width = `${width}px`;
+	  breakpoints.style.width = `${lineNumbers.clientWidth}px`;
 	}
 
 	module.exports = {
 	  removeLineClass,
 	  clearLineClass,
 	  getTextForLine,
 	  getCursorLine,
+	  getTokenLocation,
 	  resizeBreakpointGutter
 	};
 
 /***/ },
 /* 997 */
 /***/ function(module, exports) {
 
 	module.exports = "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"1.1\" viewBox=\"0 0 32 32\"><script></script><path fill=\"#444444\" d=\"M16 9.875l-9.539-5.438v23.698l9.539-5.438 9.539 5.438v-23.698l-9.539 5.438zM11.248 16.286l4.752-2.709 4.752 2.709-4.752 2.709-4.752-2.709zM9.618 9.643l3.399 1.938-3.399 1.938v-3.876zM9.618 19.053l3.145 1.792-3.145 1.793v-3.585zM22.382 22.638l-3.145-1.793 3.145-1.793v3.585zM18.982 11.581l3.399-1.938v3.876l-3.399-1.938z\"></path></svg>"
@@ -46663,26 +46686,16 @@ return /******/ (function(modules) { // 
 	var FrameComponent = (0, _react.createFactory)(_Frame2.default);
 
 	var Group = (0, _react.createFactory)(_Group3.default);
 
 	var NUM_FRAMES_SHOWN = 7;
 
 	class Frames extends _react.Component {
 
-	  collapseFrames(frames) {
-	    var frameworkGroupingOn = this.props.frameworkGroupingOn;
-
-	    if (!frameworkGroupingOn) {
-	      return frames;
-	    }
-
-	    return (0, _frame.collapseFrames)(frames);
-	  }
-
 	  constructor() {
 	    super(...arguments);
 
 	    this.state = {
 	      showAllFrames: false
 	    };
 
 	    this.toggleFramesDisplay = this.toggleFramesDisplay.bind(this);
@@ -46701,16 +46714,26 @@ return /******/ (function(modules) { // 
 	  }
 
 	  toggleFramesDisplay() {
 	    this.setState({
 	      showAllFrames: !this.state.showAllFrames
 	    });
 	  }
 
+	  collapseFrames(frames) {
+	    var frameworkGroupingOn = this.props.frameworkGroupingOn;
+
+	    if (!frameworkGroupingOn) {
+	      return frames;
+	    }
+
+	    return (0, _frame.collapseFrames)(frames);
+	  }
+
 	  truncateFrames(frames) {
 	    var numFramesToShow = this.state.showAllFrames ? frames.length : NUM_FRAMES_SHOWN;
 
 	    return frames.slice(0, numFramesToShow);
 	  }
 
 	  copyStackTrace() {
 	    var frames = this.props.frames;
@@ -46757,17 +46780,17 @@ return /******/ (function(modules) { // 
 	      toggleBlackBox,
 	      key: frameOrGroup[0].id
 	    })));
 	  }
 
 	  renderToggleButton(frames) {
 	    var buttonMessage = this.state.showAllFrames ? L10N.getStr("callStack.collapse") : L10N.getStr("callStack.expand");
 
-	    frames = (0, _frame.collapseFrames)(frames);
+	    frames = this.collapseFrames(frames);
 	    if (frames.length <= NUM_FRAMES_SHOWN) {
 	      return null;
 	    }
 
 	    return _react.DOM.div({ className: "show-more", onClick: this.toggleFramesDisplay }, buttonMessage);
 	  }
 
 	  render() {
@@ -51715,18 +51738,16 @@ return /******/ (function(modules) { // 
 	});
 
 	var _reactRedux = __webpack_require__(151);
 
 	var _redux = __webpack_require__(3);
 
 	var _react = __webpack_require__(2);
 
-	var _devtoolsConfig = __webpack_require__(828);
-
 	var _Breakpoint2 = __webpack_require__(714);
 
 	var _Breakpoint3 = _interopRequireDefault(_Breakpoint2);
 
 	var _actions = __webpack_require__(244);
 
 	var _actions2 = _interopRequireDefault(_actions);
 
@@ -51758,17 +51779,17 @@ return /******/ (function(modules) { // 
 	        selectedSource = _props.selectedSource,
 	        editor = _props.editor;
 
 
 	    if (!selectedSource || !breakpoints || selectedSource.get("isBlackBoxed")) {
 	      return null;
 	    }
 
-	    return _react.DOM.div({}, breakpoints.valueSeq().filter(b => (0, _devtoolsConfig.isEnabled)("columnBreakpoints") ? !b.location.column : true).map(bp => Breakpoint({
+	    return _react.DOM.div({}, breakpoints.valueSeq().map(bp => Breakpoint({
 	      key: (0, _breakpoint.makeLocationId)(bp.location),
 	      breakpoint: bp,
 	      selectedSource,
 	      editor: editor
 	    })));
 	  }
 	}
 
--- a/devtools/client/debugger/new/parser-worker.js
+++ b/devtools/client/debugger/new/parser-worker.js
@@ -10255,30 +10255,44 @@ return /******/ (function(modules) { // 
 	    lastChild = stack.pop();
 	  }
 
 	  if (!lastChild && firstChild) lastChild = firstChild;
 
 	  // Attach comments that follow a trailing comma on the last
 	  // property in an object literal or a trailing comma in function arguments
 	  // as trailing comments
-	  if (firstChild && (firstChild.type === "ObjectProperty" || node.type === "CallExpression") && this.state.leadingComments.length > 0) {
+	  if (firstChild && this.state.leadingComments.length > 0) {
 	    var lastComment = last(this.state.leadingComments);
-	    if (lastComment.start >= node.start) {
-	      if (this.state.commentPreviousNode) {
-	        for (j = 0; j < this.state.leadingComments.length; j++) {
-	          if (this.state.leadingComments[j].end < this.state.commentPreviousNode.end) {
-	            this.state.leadingComments.splice(j, 1);
-	            j--;
-	          }
-	        }
-
-	        if (this.state.leadingComments.length > 0) {
-	          firstChild.trailingComments = this.state.leadingComments;
-	          this.state.leadingComments = [];
+
+	    if (firstChild.type === "ObjectProperty") {
+	      if (lastComment.start >= node.start) {
+	        if (this.state.commentPreviousNode) {
+	          for (j = 0; j < this.state.leadingComments.length; j++) {
+	            if (this.state.leadingComments[j].end < this.state.commentPreviousNode.end) {
+	              this.state.leadingComments.splice(j, 1);
+	              j--;
+	            }
+	          }
+
+	          if (this.state.leadingComments.length > 0) {
+	            firstChild.trailingComments = this.state.leadingComments;
+	            this.state.leadingComments = [];
+	          }
+	        }
+	      }
+	    } else if (node.type === "CallExpression" && node.arguments && node.arguments.length) {
+	      var lastArg = last(node.arguments);
+
+	      if (lastArg && lastComment.start >= lastArg.start && lastComment.end <= node.end) {
+	        if (this.state.commentPreviousNode) {
+	          if (this.state.leadingComments.length > 0) {
+	            lastArg.trailingComments = this.state.leadingComments;
+	            this.state.leadingComments = [];
+	          }
 	        }
 	      }
 	    }
 	  }
 
 	  if (lastChild) {
 	    if (lastChild.leadingComments) {
 	      if (lastChild !== node && last(lastChild.leadingComments).end <= node.start) {
@@ -10522,26 +10536,24 @@ return /******/ (function(modules) { // 
 
 	      node.directives.reverse().forEach(function (directive) {
 	        node.body.unshift(_this2.directiveToStmt(directive));
 	      });
 	      delete node.directives;
 	    };
 	  });
 
-	  instance.extend("parseClassMethod", function (inner) {
-	    return function (classBody) {
-	      for (var _len3 = arguments.length, args = Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
-	        args[_key3 - 1] = arguments[_key3];
-	      }
-
-	      inner.call.apply(inner, [this, classBody].concat(args));
-
-	      var body = classBody.body;
-	      body[body.length - 1].type = "MethodDefinition";
+	  instance.extend("parseClassMethod", function () {
+	    return function (classBody, method, isGenerator, isAsync) {
+	      this.parseMethod(method, isGenerator, isAsync);
+	      if (method.typeParameters) {
+	        method.value.typeParameters = method.typeParameters;
+	        delete method.typeParameters;
+	      }
+	      classBody.body.push(this.finishNode(method, "MethodDefinition"));
 	    };
 	  });
 
 	  instance.extend("parseExprAtom", function (inner) {
 	    return function () {
 	      switch (this.state.type) {
 	        case types.regexp:
 	          return this.estreeParseRegExpLiteral(this.state.value);
@@ -10555,94 +10567,94 @@ return /******/ (function(modules) { // 
 
 	        case types._true:
 	          return this.estreeParseLiteral(true);
 
 	        case types._false:
 	          return this.estreeParseLiteral(false);
 
 	        default:
-	          for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
-	            args[_key4] = arguments[_key4];
+	          for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
+	            args[_key3] = arguments[_key3];
 	          }
 
 	          return inner.call.apply(inner, [this].concat(args));
 	      }
 	    };
 	  });
 
 	  instance.extend("parseLiteral", function (inner) {
 	    return function () {
-	      for (var _len5 = arguments.length, args = Array(_len5), _key5 = 0; _key5 < _len5; _key5++) {
-	        args[_key5] = arguments[_key5];
+	      for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
+	        args[_key4] = arguments[_key4];
 	      }
 
 	      var node = inner.call.apply(inner, [this].concat(args));
 	      node.raw = node.extra.raw;
 	      delete node.extra;
 
 	      return node;
 	    };
 	  });
 
 	  instance.extend("parseMethod", function (inner) {
 	    return function (node) {
 	      var funcNode = this.startNode();
 	      funcNode.kind = node.kind; // provide kind, so inner method correctly sets state
 
-	      for (var _len6 = arguments.length, args = Array(_len6 > 1 ? _len6 - 1 : 0), _key6 = 1; _key6 < _len6; _key6++) {
-	        args[_key6 - 1] = arguments[_key6];
+	      for (var _len5 = arguments.length, args = Array(_len5 > 1 ? _len5 - 1 : 0), _key5 = 1; _key5 < _len5; _key5++) {
+	        args[_key5 - 1] = arguments[_key5];
 	      }
 
 	      funcNode = inner.call.apply(inner, [this, funcNode].concat(args));
 	      delete funcNode.kind;
 	      node.value = this.finishNode(funcNode, "FunctionExpression");
 
 	      return node;
 	    };
 	  });
 
 	  instance.extend("parseObjectMethod", function (inner) {
 	    return function () {
-	      for (var _len7 = arguments.length, args = Array(_len7), _key7 = 0; _key7 < _len7; _key7++) {
-	        args[_key7] = arguments[_key7];
+	      for (var _len6 = arguments.length, args = Array(_len6), _key6 = 0; _key6 < _len6; _key6++) {
+	        args[_key6] = arguments[_key6];
 	      }
 
 	      var node = inner.call.apply(inner, [this].concat(args));
 
 	      if (node) {
 	        if (node.kind === "method") node.kind = "init";
 	        node.type = "Property";
 	      }
 
 	      return node;
 	    };
 	  });
 
 	  instance.extend("parseObjectProperty", function (inner) {
 	    return function () {
-	      for (var _len8 = arguments.length, args = Array(_len8), _key8 = 0; _key8 < _len8; _key8++) {
-	        args[_key8] = arguments[_key8];
+	      for (var _len7 = arguments.length, args = Array(_len7), _key7 = 0; _key7 < _len7; _key7++) {
+	        args[_key7] = arguments[_key7];
 	      }
 
 	      var node = inner.call.apply(inner, [this].concat(args));
 
 	      if (node) {
 	        node.kind = "init";
 	        node.type = "Property";
 	      }
 
 	      return node;
 	    };
 	  });
 
 	  instance.extend("toAssignable", function (inner) {
 	    return function (node, isBinding) {
-	      for (var _len9 = arguments.length, args = Array(_len9 > 2 ? _len9 - 2 : 0), _key9 = 2; _key9 < _len9; _key9++) {
-	        args[_key9 - 2] = arguments[_key9];
+	      for (var _len8 = arguments.length, args = Array(_len8 > 2 ? _len8 - 2 : 0), _key8 = 2; _key8 < _len8; _key8++) {
+	        args[_key8 - 2] = arguments[_key8];
 	      }
 
 	      if (isSimpleProperty(node)) {
 	        this.toAssignable.apply(this, [node.value, isBinding].concat(args));
 
 	        return node;
 	      } else if (node.type === "ObjectExpression") {
 	        node.type = "ObjectPattern";
@@ -23791,1310 +23803,17 @@ return /******/ (function(modules) { // 
 
 	module.exports = __webpack_require__(617);
 
 
 /***/ },
 /* 617 */
 /***/ function(module, exports) {
 
-	module.exports = {
-		"builtin": {
-			"Array": false,
-			"ArrayBuffer": false,
-			"Boolean": false,
-			"constructor": false,
-			"DataView": false,
-			"Date": false,
-			"decodeURI": false,
-			"decodeURIComponent": false,
-			"encodeURI": false,
-			"encodeURIComponent": false,
-			"Error": false,
-			"escape": false,
-			"eval": false,
-			"EvalError": false,
-			"Float32Array": false,
-			"Float64Array": false,
-			"Function": false,
-			"hasOwnProperty": false,
-			"Infinity": false,
-			"Int16Array": false,
-			"Int32Array": false,
-			"Int8Array": false,
-			"isFinite": false,
-			"isNaN": false,
-			"isPrototypeOf": false,
-			"JSON": false,
-			"Map": false,
-			"Math": false,
-			"NaN": false,
-			"Number": false,
-			"Object": false,
-			"parseFloat": false,
-			"parseInt": false,
-			"Promise": false,
-			"propertyIsEnumerable": false,
-			"Proxy": false,
-			"RangeError": false,
-			"ReferenceError": false,
-			"Reflect": false,
-			"RegExp": false,
-			"Set": false,
-			"String": false,
-			"Symbol": false,
-			"SyntaxError": false,
-			"System": false,
-			"toLocaleString": false,
-			"toString": false,
-			"TypeError": false,
-			"Uint16Array": false,
-			"Uint32Array": false,
-			"Uint8Array": false,
-			"Uint8ClampedArray": false,
-			"undefined": false,
-			"unescape": false,
-			"URIError": false,
-			"valueOf": false,
-			"WeakMap": false,
-			"WeakSet": false
-		},
-		"es5": {
-			"Array": false,
-			"Boolean": false,
-			"constructor": false,
-			"Date": false,
-			"decodeURI": false,
-			"decodeURIComponent": false,
-			"encodeURI": false,
-			"encodeURIComponent": false,
-			"Error": false,
-			"escape": false,
-			"eval": false,
-			"EvalError": false,
-			"Function": false,
-			"hasOwnProperty": false,
-			"Infinity": false,
-			"isFinite": false,
-			"isNaN": false,
-			"isPrototypeOf": false,
-			"JSON": false,
-			"Math": false,
-			"NaN": false,
-			"Number": false,
-			"Object": false,
-			"parseFloat": false,
-			"parseInt": false,
-			"propertyIsEnumerable": false,
-			"RangeError": false,
-			"ReferenceError": false,
-			"RegExp": false,
-			"String": false,
-			"SyntaxError": false,
-			"toLocaleString": false,
-			"toString": false,
-			"TypeError": false,
-			"undefined": false,
-			"unescape": false,
-			"URIError": false,
-			"valueOf": false
-		},
-		"es6": {
-			"Array": false,
-			"ArrayBuffer": false,
-			"Boolean": false,
-			"constructor": false,
-			"DataView": false,
-			"Date": false,
-			"decodeURI": false,
-			"decodeURIComponent": false,
-			"encodeURI": false,
-			"encodeURIComponent": false,
-			"Error": false,
-			"escape": false,
-			"eval": false,
-			"EvalError": false,
-			"Float32Array": false,
-			"Float64Array": false,
-			"Function": false,
-			"hasOwnProperty": false,
-			"Infinity": false,
-			"Int16Array": false,
-			"Int32Array": false,
-			"Int8Array": false,
-			"isFinite": false,
-			"isNaN": false,
-			"isPrototypeOf": false,
-			"JSON": false,
-			"Map": false,
-			"Math": false,
-			"NaN": false,
-			"Number": false,
-			"Object": false,
-			"parseFloat": false,
-			"parseInt": false,
-			"Promise": false,
-			"propertyIsEnumerable": false,
-			"Proxy": false,
-			"RangeError": false,
-			"ReferenceError": false,
-			"Reflect": false,
-			"RegExp": false,
-			"Set": false,
-			"String": false,
-			"Symbol": false,
-			"SyntaxError": false,
-			"System": false,
-			"toLocaleString": false,
-			"toString": false,
-			"TypeError": false,
-			"Uint16Array": false,
-			"Uint32Array": false,
-			"Uint8Array": false,
-			"Uint8ClampedArray": false,
-			"undefined": false,
-			"unescape": false,
-			"URIError": false,
-			"valueOf": false,
-			"WeakMap": false,
-			"WeakSet": false
-		},
-		"browser": {
-			"addEventListener": false,
-			"alert": false,
-			"AnalyserNode": false,
-			"Animation": false,
-			"AnimationEffectReadOnly": false,
-			"AnimationEffectTiming": false,
-			"AnimationEffectTimingReadOnly": false,
-			"AnimationEvent": false,
-			"AnimationPlaybackEvent": false,
-			"AnimationTimeline": false,
-			"applicationCache": false,
-			"ApplicationCache": false,
-			"ApplicationCacheErrorEvent": false,
-			"atob": false,
-			"Attr": false,
-			"Audio": false,
-			"AudioBuffer": false,
-			"AudioBufferSourceNode": false,
-			"AudioContext": false,
-			"AudioDestinationNode": false,
-			"AudioListener": false,
-			"AudioNode": false,
-			"AudioParam": false,
-			"AudioProcessingEvent": false,
-			"AutocompleteErrorEvent": false,
-			"BarProp": false,
-			"BatteryManager": false,
-			"BeforeUnloadEvent": false,
-			"BiquadFilterNode": false,
-			"Blob": false,
-			"blur": false,
-			"btoa": false,
-			"Cache": false,
-			"caches": false,
-			"CacheStorage": false,
-			"cancelAnimationFrame": false,
-			"cancelIdleCallback": false,
-			"CanvasGradient": false,
-			"CanvasPattern": false,
-			"CanvasRenderingContext2D": false,
-			"CDATASection": false,
-			"ChannelMergerNode": false,
-			"ChannelSplitterNode": false,
-			"CharacterData": false,
-			"clearInterval": false,
-			"clearTimeout": false,
-			"clientInformation": false,
-			"ClientRect": false,
-			"ClientRectList": false,
-			"ClipboardEvent": false,
-			"close": false,
-			"closed": false,
-			"CloseEvent": false,
-			"Comment": false,
-			"CompositionEvent": false,
-			"confirm": false,
-			"console": false,
-			"ConvolverNode": false,
-			"createImageBitmap": false,
-			"Credential": false,
-			"CredentialsContainer": false,
-			"crypto": false,
-			"Crypto": false,
-			"CryptoKey": false,
-			"CSS": false,
-			"CSSAnimation": false,
-			"CSSFontFaceRule": false,
-			"CSSImportRule": false,
-			"CSSKeyframeRule": false,
-			"CSSKeyframesRule": false,
-			"CSSMediaRule": false,
-			"CSSPageRule": false,
-			"CSSRule": false,
-			"CSSRuleList": false,
-			"CSSStyleDeclaration": false,
-			"CSSStyleRule": false,
-			"CSSStyleSheet": false,
-			"CSSSupportsRule": false,
-			"CSSTransition": false,
-			"CSSUnknownRule": false,
-			"CSSViewportRule": false,
-			"customElements": false,
-			"CustomEvent": false,
-			"DataTransfer": false,
-			"DataTransferItem": false,
-			"DataTransferItemList": false,
-			"Debug": false,
-			"defaultStatus": false,
-			"defaultstatus": false,
-			"DelayNode": false,
-			"DeviceMotionEvent": false,
-			"DeviceOrientationEvent": false,
-			"devicePixelRatio": false,
-			"dispatchEvent": false,
-			"document": false,
-			"Document": false,
-			"DocumentFragment": false,
-			"DocumentTimeline": false,
-			"DocumentType": false,
-			"DOMError": false,
-			"DOMException": false,
-			"DOMImplementation": false,
-			"DOMParser": false,
-			"DOMSettableTokenList": false,
-			"DOMStringList": false,
-			"DOMStringMap": false,
-			"DOMTokenList": false,
-			"DragEvent": false,
-			"DynamicsCompressorNode": false,
-			"Element": false,
-			"ElementTimeControl": false,
-			"ErrorEvent": false,
-			"event": false,
-			"Event": false,
-			"EventSource": false,
-			"EventTarget": false,
-			"external": false,
-			"FederatedCredential": false,
-			"fetch": false,
-			"File": false,
-			"FileError": false,
-			"FileList": false,
-			"FileReader": false,
-			"find": false,
-			"focus": false,
-			"FocusEvent": false,
-			"FontFace": false,
-			"FormData": false,
-			"frameElement": false,
-			"frames": false,
-			"GainNode": false,
-			"Gamepad": false,
-			"GamepadButton": false,
-			"GamepadEvent": false,
-			"getComputedStyle": false,
-			"getSelection": false,
-			"HashChangeEvent": false,
-			"Headers": false,
-			"history": false,
-			"History": false,
-			"HTMLAllCollection": false,
-			"HTMLAnchorElement": false,
-			"HTMLAppletElement": false,
-			"HTMLAreaElement": false,
-			"HTMLAudioElement": false,
-			"HTMLBaseElement": false,
-			"HTMLBlockquoteElement": false,
-			"HTMLBodyElement": false,
-			"HTMLBRElement": false,
-			"HTMLButtonElement": false,
-			"HTMLCanvasElement": false,
-			"HTMLCollection": false,
-			"HTMLContentElement": false,
-			"HTMLDataListElement": false,
-			"HTMLDetailsElement": false,
-			"HTMLDialogElement": false,
-			"HTMLDirectoryElement": false,
-			"HTMLDivElement": false,
-			"HTMLDListElement": false,
-			"HTMLDocument": false,
-			"HTMLElement": false,
-			"HTMLEmbedElement": false,
-			"HTMLFieldSetElement": false,
-			"HTMLFontElement": false,
-			"HTMLFormControlsCollection": false,
-			"HTMLFormElement": false,
-			"HTMLFrameElement": false,
-			"HTMLFrameSetElement": false,
-			"HTMLHeadElement": false,
-			"HTMLHeadingElement": false,
-			"HTMLHRElement": false,
-			"HTMLHtmlElement": false,
-			"HTMLIFrameElement": false,
-			"HTMLImageElement": false,
-			"HTMLInputElement": false,
-			"HTMLIsIndexElement": false,
-			"HTMLKeygenElement": false,
-			"HTMLLabelElement": false,
-			"HTMLLayerElement": false,
-			"HTMLLegendElement": false,
-			"HTMLLIElement": false,
-			"HTMLLinkElement": false,
-			"HTMLMapElement": false,
-			"HTMLMarqueeElement": false,
-			"HTMLMediaElement": false,
-			"HTMLMenuElement": false,
-			"HTMLMetaElement": false,
-			"HTMLMeterElement": false,
-			"HTMLModElement": false,
-			"HTMLObjectElement": false,
-			"HTMLOListElement": false,
-			"HTMLOptGroupElement": false,
-			"HTMLOptionElement": false,
-			"HTMLOptionsCollection": false,
-			"HTMLOutputElement": false,
-			"HTMLParagraphElement": false,
-			"HTMLParamElement": false,
-			"HTMLPictureElement": false,
-			"HTMLPreElement": false,
-			"HTMLProgressElement": false,
-			"HTMLQuoteElement": false,
-			"HTMLScriptElement": false,
-			"HTMLSelectElement": false,
-			"HTMLShadowElement": false,
-			"HTMLSourceElement": false,
-			"HTMLSpanElement": false,
-			"HTMLStyleElement": false,
-			"HTMLTableCaptionElement": false,
-			"HTMLTableCellElement": false,
-			"HTMLTableColElement": false,
-			"HTMLTableElement": false,
-			"HTMLTableRowElement": false,
-			"HTMLTableSectionElement": false,
-			"HTMLTemplateElement": false,
-			"HTMLTextAreaElement": false,
-			"HTMLTitleElement": false,
-			"HTMLTrackElement": false,
-			"HTMLUListElement": false,
-			"HTMLUnknownElement": false,
-			"HTMLVideoElement": false,
-			"IDBCursor": false,
-			"IDBCursorWithValue": false,
-			"IDBDatabase": false,
-			"IDBEnvironment": false,
-			"IDBFactory": false,
-			"IDBIndex": false,
-			"IDBKeyRange": false,
-			"IDBObjectStore": false,
-			"IDBOpenDBRequest": false,
-			"IDBRequest": false,
-			"IDBTransaction": false,
-			"IDBVersionChangeEvent": false,
-			"Image": false,
-			"ImageBitmap": false,
-			"ImageData": false,
-			"indexedDB": false,
-			"innerHeight": false,
-			"innerWidth": false,
-			"InputEvent": false,
-			"InputMethodContext": false,
-			"IntersectionObserver": false,
-			"IntersectionObserverEntry": false,
-			"Intl": false,
-			"KeyboardEvent": false,
-			"KeyframeEffect": false,
-			"KeyframeEffectReadOnly": false,
-			"length": false,
-			"localStorage": false,
-			"location": false,
-			"Location": false,
-			"locationbar": false,
-			"matchMedia": false,
-			"MediaElementAudioSourceNode": false,
-			"MediaEncryptedEvent": false,
-			"MediaError": false,
-			"MediaKeyError": false,
-			"MediaKeyEvent": false,
-			"MediaKeyMessageEvent": false,
-			"MediaKeys": false,
-			"MediaKeySession": false,
-			"MediaKeyStatusMap": false,
-			"MediaKeySystemAccess": false,
-			"MediaList": false,
-			"MediaQueryList": false,
-			"MediaQueryListEvent": false,
-			"MediaSource": false,
-			"MediaRecorder": false,
-			"MediaStream": false,
-			"MediaStreamAudioDestinationNode": false,
-			"MediaStreamAudioSourceNode": false,
-			"MediaStreamEvent": false,
-			"MediaStreamTrack": false,
-			"menubar": false,
-			"MessageChannel": false,
-			"MessageEvent": false,
-			"MessagePort": false,
-			"MIDIAccess": false,
-			"MIDIConnectionEvent": false,
-			"MIDIInput": false,
-			"MIDIInputMap": false,
-			"MIDIMessageEvent": false,
-			"MIDIOutput": false,
-			"MIDIOutputMap": false,
-			"MIDIPort": false,
-			"MimeType": false,
-			"MimeTypeArray": false,
-			"MouseEvent": false,
-			"moveBy": false,
-			"moveTo": false,
-			"MutationEvent": false,
-			"MutationObserver": false,
-			"MutationRecord": false,
-			"name": false,
-			"NamedNodeMap": false,
-			"navigator": false,
-			"Navigator": false,
-			"Node": false,
-			"NodeFilter": false,
-			"NodeIterator": false,
-			"NodeList": false,
-			"Notification": false,
-			"OfflineAudioCompletionEvent": false,
-			"OfflineAudioContext": false,
-			"offscreenBuffering": false,
-			"onbeforeunload": true,
-			"onblur": true,
-			"onerror": true,
-			"onfocus": true,
-			"onload": true,
-			"onresize": true,
-			"onunload": true,
-			"open": false,
-			"openDatabase": false,
-			"opener": false,
-			"opera": false,
-			"Option": false,
-			"OscillatorNode": false,
-			"outerHeight": false,
-			"outerWidth": false,
-			"PageTransitionEvent": false,
-			"pageXOffset": false,
-			"pageYOffset": false,
-			"parent": false,
-			"PasswordCredential": false,
-			"Path2D": false,
-			"performance": false,
-			"Performance": false,
-			"PerformanceEntry": false,
-			"PerformanceMark": false,
-			"PerformanceMeasure": false,
-			"PerformanceNavigation": false,
-			"PerformanceResourceTiming": false,
-			"PerformanceTiming": false,
-			"PeriodicWave": false,
-			"Permissions": false,
-			"PermissionStatus": false,
-			"personalbar": false,
-			"Plugin": false,
-			"PluginArray": false,
-			"PopStateEvent": false,
-			"postMessage": false,
-			"print": false,
-			"ProcessingInstruction": false,
-			"ProgressEvent": false,
-			"PromiseRejectionEvent": false,
-			"prompt": false,
-			"PushManager": false,
-			"PushSubscription": false,
-			"RadioNodeList": false,
-			"Range": false,
-			"ReadableByteStream": false,
-			"ReadableStream": false,
-			"removeEventListener": false,
-			"Request": false,
-			"requestAnimationFrame": false,
-			"requestIdleCallback": false,
-			"resizeBy": false,
-			"resizeTo": false,
-			"Response": false,
-			"RTCIceCandidate": false,
-			"RTCSessionDescription": false,
-			"RTCPeerConnection": false,
-			"screen": false,
-			"Screen": false,
-			"screenLeft": false,
-			"ScreenOrientation": false,
-			"screenTop": false,
-			"screenX": false,
-			"screenY": false,
-			"ScriptProcessorNode": false,
-			"scroll": false,
-			"scrollbars": false,
-			"scrollBy": false,
-			"scrollTo": false,
-			"scrollX": false,
-			"scrollY": false,
-			"SecurityPolicyViolationEvent": false,
-			"Selection": false,
-			"self": false,
-			"ServiceWorker": false,
-			"ServiceWorkerContainer": false,
-			"ServiceWorkerRegistration": false,
-			"sessionStorage": false,
-			"setInterval": false,
-			"setTimeout": false,
-			"ShadowRoot": false,
-			"SharedKeyframeList": false,
-			"SharedWorker": false,
-			"showModalDialog": false,
-			"SiteBoundCredential": false,
-			"speechSynthesis": false,
-			"SpeechSynthesisEvent": false,
-			"SpeechSynthesisUtterance": false,
-			"status": false,
-			"statusbar": false,
-			"stop": false,
-			"Storage": false,
-			"StorageEvent": false,
-			"styleMedia": false,
-			"StyleSheet": false,
-			"StyleSheetList": false,
-			"SubtleCrypto": false,
-			"SVGAElement": false,
-			"SVGAltGlyphDefElement": false,
-			"SVGAltGlyphElement": false,
-			"SVGAltGlyphItemElement": false,
-			"SVGAngle": false,
-			"SVGAnimateColorElement": false,
-			"SVGAnimatedAngle": false,
-			"SVGAnimatedBoolean": false,
-			"SVGAnimatedEnumeration": false,
-			"SVGAnimatedInteger": false,
-			"SVGAnimatedLength": false,
-			"SVGAnimatedLengthList": false,
-			"SVGAnimatedNumber": false,
-			"SVGAnimatedNumberList": false,
-			"SVGAnimatedPathData": false,
-			"SVGAnimatedPoints": false,
-			"SVGAnimatedPreserveAspectRatio": false,
-			"SVGAnimatedRect": false,
-			"SVGAnimatedString": false,
-			"SVGAnimatedTransformList": false,
-			"SVGAnimateElement": false,
-			"SVGAnimateMotionElement": false,
-			"SVGAnimateTransformElement": false,
-			"SVGAnimationElement": false,
-			"SVGCircleElement": false,
-			"SVGClipPathElement": false,
-			"SVGColor": false,
-			"SVGColorProfileElement": false,
-			"SVGColorProfileRule": false,
-			"SVGComponentTransferFunctionElement": false,
-			"SVGCSSRule": false,
-			"SVGCursorElement": false,
-			"SVGDefsElement": false,
-			"SVGDescElement": false,
-			"SVGDiscardElement": false,
-			"SVGDocument": false,
-			"SVGElement": false,
-			"SVGElementInstance": false,
-			"SVGElementInstanceList": false,
-			"SVGEllipseElement": false,
-			"SVGEvent": false,
-			"SVGExternalResourcesRequired": false,
-			"SVGFEBlendElement": false,
-			"SVGFEColorMatrixElement": false,
-			"SVGFEComponentTransferElement": false,
-			"SVGFECompositeElement": false,
-			"SVGFEConvolveMatrixElement": false,
-			"SVGFEDiffuseLightingElement": false,
-			"SVGFEDisplacementMapElement": false,
-			"SVGFEDistantLightElement": false,
-			"SVGFEDropShadowElement": false,
-			"SVGFEFloodElement": false,
-			"SVGFEFuncAElement": false,
-			"SVGFEFuncBElement": false,
-			"SVGFEFuncGElement": false,
-			"SVGFEFuncRElement": false,
-			"SVGFEGaussianBlurElement": false,
-			"SVGFEImageElement": false,
-			"SVGFEMergeElement": false,
-			"SVGFEMergeNodeElement": false,
-			"SVGFEMorphologyElement": false,
-			"SVGFEOffsetElement": false,
-			"SVGFEPointLightElement": false,
-			"SVGFESpecularLightingElement": false,
-			"SVGFESpotLightElement": false,
-			"SVGFETileElement": false,
-			"SVGFETurbulenceElement": false,
-			"SVGFilterElement": false,
-			"SVGFilterPrimitiveStandardAttributes": false,
-			"SVGFitToViewBox": false,
-			"SVGFontElement": false,
-			"SVGFontFaceElement": false,
-			"SVGFontFaceFormatElement": false,
-			"SVGFontFaceNameElement": false,
-			"SVGFontFaceSrcElement": false,
-			"SVGFontFaceUriElement": false,
-			"SVGForeignObjectElement": false,
-			"SVGGElement": false,
-			"SVGGeometryElement": false,
-			"SVGGlyphElement": false,
-			"SVGGlyphRefElement": false,
-			"SVGGradientElement": false,
-			"SVGGraphicsElement": false,
-			"SVGHKernElement": false,
-			"SVGICCColor": false,
-			"SVGImageElement": false,
-			"SVGLangSpace": false,
-			"SVGLength": false,
-			"SVGLengthList": false,
-			"SVGLinearGradientElement": false,
-			"SVGLineElement": false,
-			"SVGLocatable": false,
-			"SVGMarkerElement": false,
-			"SVGMaskElement": false,
-			"SVGMatrix": false,
-			"SVGMetadataElement": false,
-			"SVGMissingGlyphElement": false,
-			"SVGMPathElement": false,
-			"SVGNumber": false,
-			"SVGNumberList": false,
-			"SVGPaint": false,
-			"SVGPathElement": false,
-			"SVGPathSeg": false,
-			"SVGPathSegArcAbs": false,
-			"SVGPathSegArcRel": false,
-			"SVGPathSegClosePath": false,
-			"SVGPathSegCurvetoCubicAbs": false,
-			"SVGPathSegCurvetoCubicRel": false,
-			"SVGPathSegCurvetoCubicSmoothAbs": false,
-			"SVGPathSegCurvetoCubicSmoothRel": false,
-			"SVGPathSegCurvetoQuadraticAbs": false,
-			"SVGPathSegCurvetoQuadraticRel": false,
-			"SVGPathSegCurvetoQuadraticSmoothAbs": false,
-			"SVGPathSegCurvetoQuadraticSmoothRel": false,
-			"SVGPathSegLinetoAbs": false,
-			"SVGPathSegLinetoHorizontalAbs": false,
-			"SVGPathSegLinetoHorizontalRel": false,
-			"SVGPathSegLinetoRel": false,
-			"SVGPathSegLinetoVerticalAbs": false,
-			"SVGPathSegLinetoVerticalRel": false,
-			"SVGPathSegList": false,
-			"SVGPathSegMovetoAbs": false,
-			"SVGPathSegMovetoRel": false,
-			"SVGPatternElement": false,
-			"SVGPoint": false,
-			"SVGPointList": false,
-			"SVGPolygonElement": false,
-			"SVGPolylineElement": false,
-			"SVGPreserveAspectRatio": false,
-			"SVGRadialGradientElement": false,
-			"SVGRect": false,
-			"SVGRectElement": false,
-			"SVGRenderingIntent": false,
-			"SVGScriptElement": false,
-			"SVGSetElement": false,
-			"SVGStopElement": false,
-			"SVGStringList": false,
-			"SVGStylable": false,
-			"SVGStyleElement": false,
-			"SVGSVGElement": false,
-			"SVGSwitchElement": false,
-			"SVGSymbolElement": false,
-			"SVGTests": false,
-			"SVGTextContentElement": false,
-			"SVGTextElement": false,
-			"SVGTextPathElement": false,
-			"SVGTextPositioningElement": false,
-			"SVGTitleElement": false,
-			"SVGTransform": false,
-			"SVGTransformable": false,
-			"SVGTransformList": false,
-			"SVGTRefElement": false,
-			"SVGTSpanElement": false,
-			"SVGUnitTypes": false,
-			"SVGURIReference": false,
-			"SVGUseElement": false,
-			"SVGViewElement": false,
-			"SVGViewSpec": false,
-			"SVGVKernElement": false,
-			"SVGZoomAndPan": false,
-			"SVGZoomEvent": false,
-			"Text": false,
-			"TextDecoder": false,
-			"TextEncoder": false,
-			"TextEvent": false,
-			"TextMetrics": false,
-			"TextTrack": false,
-			"TextTrackCue": false,
-			"TextTrackCueList": false,
-			"TextTrackList": false,
-			"TimeEvent": false,
-			"TimeRanges": false,
-			"toolbar": false,
-			"top": false,
-			"Touch": false,
-			"TouchEvent": false,
-			"TouchList": false,
-			"TrackEvent": false,
-			"TransitionEvent": false,
-			"TreeWalker": false,
-			"UIEvent": false,
-			"URL": false,
-			"URLSearchParams": false,
-			"ValidityState": false,
-			"VTTCue": false,
-			"WaveShaperNode": false,
-			"WebGLActiveInfo": false,
-			"WebGLBuffer": false,
-			"WebGLContextEvent": false,
-			"WebGLFramebuffer": false,
-			"WebGLProgram": false,
-			"WebGLRenderbuffer": false,
-			"WebGLRenderingContext": false,
-			"WebGLShader": false,
-			"WebGLShaderPrecisionFormat": false,
-			"WebGLTexture": false,
-			"WebGLUniformLocation": false,
-			"WebSocket": false,
-			"WheelEvent": false,
-			"window": false,
-			"Window": false,
-			"Worker": false,
-			"XDomainRequest": false,
-			"XMLDocument": false,
-			"XMLHttpRequest": false,
-			"XMLHttpRequestEventTarget": false,
-			"XMLHttpRequestProgressEvent": false,
-			"XMLHttpRequestUpload": false,
-			"XMLSerializer": false,
-			"XPathEvaluator": false,
-			"XPathException": false,
-			"XPathExpression": false,
-			"XPathNamespace": false,
-			"XPathNSResolver": false,
-			"XPathResult": false,
-			"XSLTProcessor": false
-		},
-		"worker": {
-			"applicationCache": false,
-			"atob": false,
-			"Blob": false,
-			"BroadcastChannel": false,
-			"btoa": false,
-			"Cache": false,
-			"caches": false,
-			"clearInterval": false,
-			"clearTimeout": false,
-			"close": true,
-			"console": false,
-			"fetch": false,
-			"FileReaderSync": false,
-			"FormData": false,
-			"Headers": false,
-			"IDBCursor": false,
-			"IDBCursorWithValue": false,
-			"IDBDatabase": false,
-			"IDBFactory": false,
-			"IDBIndex": false,
-			"IDBKeyRange": false,
-			"IDBObjectStore": false,
-			"IDBOpenDBRequest": false,
-			"IDBRequest": false,
-			"IDBTransaction": false,
-			"IDBVersionChangeEvent": false,
-			"ImageData": false,
-			"importScripts": true,
-			"indexedDB": false,
-			"location": false,
-			"MessageChannel": false,
-			"MessagePort": false,
-			"name": false,
-			"navigator": false,
-			"Notification": false,
-			"onclose": true,
-			"onconnect": true,
-			"onerror": true,
-			"onlanguagechange": true,
-			"onmessage": true,
-			"onoffline": true,
-			"ononline": true,
-			"onrejectionhandled": true,
-			"onunhandledrejection": true,
-			"performance": false,
-			"Performance": false,
-			"PerformanceEntry": false,
-			"PerformanceMark": false,
-			"PerformanceMeasure": false,
-			"PerformanceNavigation": false,
-			"PerformanceResourceTiming": false,
-			"PerformanceTiming": false,
-			"postMessage": true,
-			"Promise": false,
-			"Request": false,
-			"Response": false,
-			"self": true,
-			"ServiceWorkerRegistration": false,
-			"setInterval": false,
-			"setTimeout": false,
-			"TextDecoder": false,
-			"TextEncoder": false,
-			"URL": false,
-			"URLSearchParams": false,
-			"WebSocket": false,
-			"Worker": false,
-			"XMLHttpRequest": false
-		},
-		"node": {
-			"__dirname": false,
-			"__filename": false,
-			"arguments": false,
-			"Buffer": false,
-			"clearImmediate": false,
-			"clearInterval": false,
-			"clearTimeout": false,
-			"console": false,
-			"exports": true,
-			"GLOBAL": false,
-			"global": false,
-			"Intl": false,
-			"module": false,
-			"process": false,
-			"require": false,
-			"root": false,
-			"setImmediate": false,
-			"setInterval": false,
-			"setTimeout": false
-		},
-		"commonjs": {
-			"exports": true,
-			"module": false,
-			"require": false,
-			"global": false
-		},
-		"amd": {
-			"define": false,
-			"require": false
-		},
-		"mocha": {
-			"after": false,
-			"afterEach": false,
-			"before": false,
-			"beforeEach": false,
-			"context": false,
-			"describe": false,
-			"it": false,
-			"mocha": false,
-			"run": false,
-			"setup": false,
-			"specify": false,
-			"suite": false,
-			"suiteSetup": false,
-			"suiteTeardown": false,
-			"teardown": false,
-			"test": false,
-			"xcontext": false,
-			"xdescribe": false,
-			"xit": false,
-			"xspecify": false
-		},
-		"jasmine": {
-			"afterAll": false,
-			"afterEach": false,
-			"beforeAll": false,
-			"beforeEach": false,
-			"describe": false,
-			"expect": false,
-			"fail": false,
-			"fdescribe": false,
-			"fit": false,
-			"it": false,
-			"jasmine": false,
-			"pending": false,
-			"runs": false,
-			"spyOn": false,
-			"spyOnProperty": false,
-			"waits": false,
-			"waitsFor": false,
-			"xdescribe": false,
-			"xit": false
-		},
-		"jest": {
-			"afterAll": false,
-			"afterEach": false,
-			"beforeAll": false,
-			"beforeEach": false,
-			"check": false,
-			"describe": false,
-			"expect": false,
-			"gen": false,
-			"it": false,
-			"fdescribe": false,
-			"fit": false,
-			"jest": false,
-			"pit": false,
-			"require": false,
-			"test": false,
-			"xdescribe": false,
-			"xit": false,
-			"xtest": false
-		},
-		"qunit": {
-			"asyncTest": false,
-			"deepEqual": false,
-			"equal": false,
-			"expect": false,
-			"module": false,
-			"notDeepEqual": false,
-			"notEqual": false,
-			"notOk": false,
-			"notPropEqual": false,
-			"notStrictEqual": false,
-			"ok": false,
-			"propEqual": false,
-			"QUnit": false,
-			"raises": false,
-			"start": false,
-			"stop": false,
-			"strictEqual": false,
-			"test": false,
-			"throws": false
-		},
-		"phantomjs": {
-			"console": true,
-			"exports": true,
-			"phantom": true,
-			"require": true,
-			"WebPage": true
-		},
-		"couch": {
-			"emit": false,
-			"exports": false,
-			"getRow": false,
-			"log": false,
-			"module": false,
-			"provides": false,
-			"require": false,
-			"respond": false,
-			"send": false,
-			"start": false,
-			"sum": false
-		},
-		"rhino": {
-			"defineClass": false,
-			"deserialize": false,
-			"gc": false,
-			"help": false,
-			"importClass": false,
-			"importPackage": false,
-			"java": false,
-			"load": false,
-			"loadClass": false,
-			"Packages": false,
-			"print": false,
-			"quit": false,
-			"readFile": false,
-			"readUrl": false,
-			"runCommand": false,
-			"seal": false,
-			"serialize": false,
-			"spawn": false,
-			"sync": false,
-			"toint32": false,
-			"version": false
-		},
-		"nashorn": {
-			"__DIR__": false,
-			"__FILE__": false,
-			"__LINE__": false,
-			"com": false,
-			"edu": false,
-			"exit": false,
-			"Java": false,
-			"java": false,
-			"javafx": false,
-			"JavaImporter": false,
-			"javax": false,
-			"JSAdapter": false,
-			"load": false,
-			"loadWithNewGlobal": false,
-			"org": false,
-			"Packages": false,
-			"print": false,
-			"quit": false
-		},
-		"wsh": {
-			"ActiveXObject": true,
-			"Enumerator": true,
-			"GetObject": true,
-			"ScriptEngine": true,
-			"ScriptEngineBuildVersion": true,
-			"ScriptEngineMajorVersion": true,
-			"ScriptEngineMinorVersion": true,
-			"VBArray": true,
-			"WScript": true,
-			"WSH": true,
-			"XDomainRequest": true
-		},
-		"jquery": {
-			"$": false,
-			"jQuery": false
-		},
-		"yui": {
-			"Y": false,
-			"YUI": false,
-			"YUI_config": false
-		},
-		"shelljs": {
-			"cat": false,
-			"cd": false,
-			"chmod": false,
-			"config": false,
-			"cp": false,
-			"dirs": false,
-			"echo": false,
-			"env": false,
-			"error": false,
-			"exec": false,
-			"exit": false,
-			"find": false,
-			"grep": false,
-			"ls": false,
-			"ln": false,
-			"mkdir": false,
-			"mv": false,
-			"popd": false,
-			"pushd": false,
-			"pwd": false,
-			"rm": false,
-			"sed": false,
-			"set": false,
-			"target": false,
-			"tempdir": false,
-			"test": false,
-			"touch": false,
-			"which": false
-		},
-		"prototypejs": {
-			"$": false,
-			"$$": false,
-			"$A": false,
-			"$break": false,
-			"$continue": false,
-			"$F": false,
-			"$H": false,
-			"$R": false,
-			"$w": false,
-			"Abstract": false,
-			"Ajax": false,
-			"Autocompleter": false,
-			"Builder": false,
-			"Class": false,
-			"Control": false,
-			"Draggable": false,
-			"Draggables": false,
-			"Droppables": false,
-			"Effect": false,
-			"Element": false,
-			"Enumerable": false,
-			"Event": false,
-			"Field": false,
-			"Form": false,
-			"Hash": false,
-			"Insertion": false,
-			"ObjectRange": false,
-			"PeriodicalExecuter": false,
-			"Position": false,
-			"Prototype": false,
-			"Scriptaculous": false,
-			"Selector": false,
-			"Sortable": false,
-			"SortableObserver": false,
-			"Sound": false,
-			"Template": false,
-			"Toggle": false,
-			"Try": false
-		},
-		"meteor": {
-			"$": false,
-			"_": false,
-			"Accounts": false,
-			"AccountsClient": false,
-			"AccountsServer": false,
-			"AccountsCommon": false,
-			"App": false,
-			"Assets": false,
-			"Blaze": false,
-			"check": false,
-			"Cordova": false,
-			"DDP": false,
-			"DDPServer": false,
-			"DDPRateLimiter": false,
-			"Deps": false,
-			"EJSON": false,
-			"Email": false,
-			"HTTP": false,
-			"Log": false,
-			"Match": false,
-			"Meteor": false,
-			"Mongo": false,
-			"MongoInternals": false,
-			"Npm": false,
-			"Package": false,
-			"Plugin": false,
-			"process": false,
-			"Random": false,
-			"ReactiveDict": false,
-			"ReactiveVar": false,
-			"Router": false,
-			"ServiceConfiguration": false,
-			"Session": false,
-			"share": false,
-			"Spacebars": false,
-			"Template": false,
-			"Tinytest": false,
-			"Tracker": false,
-			"UI": false,
-			"Utils": false,
-			"WebApp": false,
-			"WebAppInternals": false
-		},
-		"mongo": {
-			"_isWindows": false,
-			"_rand": false,
-			"BulkWriteResult": false,
-			"cat": false,
-			"cd": false,
-			"connect": false,
-			"db": false,
-			"getHostName": false,
-			"getMemInfo": false,
-			"hostname": false,
-			"ISODate": false,
-			"listFiles": false,
-			"load": false,
-			"ls": false,
-			"md5sumFile": false,
-			"mkdir": false,
-			"Mongo": false,
-			"NumberInt": false,
-			"NumberLong": false,
-			"ObjectId": false,
-			"PlanCache": false,
-			"print": false,
-			"printjson": false,
-			"pwd": false,
-			"quit": false,
-			"removeFile": false,
-			"rs": false,
-			"sh": false,
-			"UUID": false,
-			"version": false,
-			"WriteResult": false
-		},
-		"applescript": {
-			"$": false,
-			"Application": false,
-			"Automation": false,
-			"console": false,
-			"delay": false,
-			"Library": false,
-			"ObjC": false,
-			"ObjectSpecifier": false,
-			"Path": false,
-			"Progress": false,
-			"Ref": false
-		},
-		"serviceworker": {
-			"caches": false,
-			"Cache": false,
-			"CacheStorage": false,
-			"Client": false,
-			"clients": false,
-			"Clients": false,
-			"ExtendableEvent": false,
-			"ExtendableMessageEvent": false,
-			"FetchEvent": false,
-			"importScripts": false,
-			"registration": false,
-			"self": false,
-			"ServiceWorker": false,
-			"ServiceWorkerContainer": false,
-			"ServiceWorkerGlobalScope": false,
-			"ServiceWorkerMessageEvent": false,
-			"ServiceWorkerRegistration": false,
-			"skipWaiting": false,
-			"WindowClient": false
-		},
-		"atomtest": {
-			"advanceClock": false,
-			"fakeClearInterval": false,
-			"fakeClearTimeout": false,
-			"fakeSetInterval": false,
-			"fakeSetTimeout": false,
-			"resetTimeouts": false,
-			"waitsForPromise": false
-		},
-		"embertest": {
-			"andThen": false,
-			"click": false,
-			"currentPath": false,
-			"currentRouteName": false,
-			"currentURL": false,
-			"fillIn": false,
-			"find": false,
-			"findWithAssert": false,
-			"keyEvent": false,
-			"pauseTest": false,
-			"resumeTest": false,
-			"triggerEvent": false,
-			"visit": false
-		},
-		"protractor": {
-			"$": false,
-			"$$": false,
-			"browser": false,
-			"By": false,
-			"by": false,
-			"DartObject": false,
-			"element": false,
-			"protractor": false
-		},
-		"shared-node-browser": {
-			"clearInterval": false,
-			"clearTimeout": false,
-			"console": false,
-			"setInterval": false,
-			"setTimeout": false
-		},
-		"webextensions": {
-			"browser": false,
-			"chrome": false,
-			"opr": false
-		},
-		"greasemonkey": {
-			"GM_addStyle": false,
-			"GM_deleteValue": false,
-			"GM_getResourceText": false,
-			"GM_getResourceURL": false,
-			"GM_getValue": false,
-			"GM_info": false,
-			"GM_listValues": false,
-			"GM_log": false,
-			"GM_openInTab": false,
-			"GM_registerMenuCommand": false,
-			"GM_setClipboard": false,
-			"GM_setValue": false,
-			"GM_xmlhttpRequest": false,
-			"unsafeWindow": false
-		}
-	};
+	module.exports = {"builtin":{"Array":false,"ArrayBuffer":false,"Boolean":false,"constructor":false,"DataView":false,"Date":false,"decodeURI":false,"decodeURIComponent":false,"encodeURI":false,"encodeURIComponent":false,"Error":false,"escape":false,"eval":false,"EvalError":false,"Float32Array":false,"Float64Array":false,"Function":false,"hasOwnProperty":false,"Infinity":false,"Int16Array":false,"Int32Array":false,"Int8Array":false,"isFinite":false,"isNaN":false,"isPrototypeOf":false,"JSON":false,"Map":false,"Math":false,"NaN":false,"Number":false,"Object":false,"parseFloat":false,"parseInt":false,"Promise":false,"propertyIsEnumerable":false,"Proxy":false,"RangeError":false,"ReferenceError":false,"Reflect":false,"RegExp":false,"Set":false,"String":false,"Symbol":false,"SyntaxError":false,"System":false,"toLocaleString":false,"toString":false,"TypeError":false,"Uint16Array":false,"Uint32Array":false,"Uint8Array":false,"Uint8ClampedArray":false,"undefined":false,"unescape":false,"URIError":false,"valueOf":false,"WeakMap":false,"WeakSet":false},"es5":{"Array":false,"Boolean":false,"constructor":false,"Date":false,"decodeURI":false,"decodeURIComponent":false,"encodeURI":false,"encodeURIComponent":false,"Error":false,"escape":false,"eval":false,"EvalError":false,"Function":false,"hasOwnProperty":false,"Infinity":false,"isFinite":false,"isNaN":false,"isPrototypeOf":false,"JSON":false,"Math":false,"NaN":false,"Number":false,"Object":false,"parseFloat":false,"parseInt":false,"propertyIsEnumerable":false,"RangeError":false,"ReferenceError":false,"RegExp":false,"String":false,"SyntaxError":false,"toLocaleString":false,"toString":false,"TypeError":false,"undefined":false,"unescape":false,"URIError":false,"valueOf":false},"es6":{"Array":false,"ArrayBuffer":false,"Boolean":false,"constructor":false,"DataView":false,"Date":false,"decodeURI":false,"decodeURIComponent":false,"encodeURI":false,"encodeURIComponent":false,"Error":false,"escape":false,"eval":false,"EvalError":false,"Float32Array":false,"Float64Array":false,"Function":false,"hasOwnProperty":false,"Infinity":false,"Int16Array":false,"Int32Array":false,"Int8Array":false,"isFinite":false,"isNaN":false,"isPrototypeOf":false,"JSON":false,"Map":false,"Math":false,"NaN":false,"Number":false,"Object":false,"parseFloat":false,"parseInt":false,"Promise":false,"propertyIsEnumerable":false,"Proxy":false,"RangeError":false,"ReferenceError":false,"Reflect":false,"RegExp":false,"Set":false,"String":false,"Symbol":false,"SyntaxError":false,"System":false,"toLocaleString":false,"toString":false,"TypeError":false,"Uint16Array":false,"Uint32Array":false,"Uint8Array":false,"Uint8ClampedArray":false,"undefined":false,"unescape":false,"URIError":false,"valueOf":false,"WeakMap":false,"WeakSet":false},"browser":{"addEventListener":false,"alert":false,"AnalyserNode":false,"Animation":false,"AnimationEffectReadOnly":false,"AnimationEffectTiming":false,"AnimationEffectTimingReadOnly":false,"AnimationEvent":false,"AnimationPlaybackEvent":false,"AnimationTimeline":false,"applicationCache":false,"ApplicationCache":false,"ApplicationCacheErrorEvent":false,"atob":false,"Attr":false,"Audio":false,"AudioBuffer":false,"AudioBufferSourceNode":false,"AudioContext":false,"AudioDestinationNode":false,"AudioListener":false,"AudioNode":false,"AudioParam":false,"AudioProcessingEvent":false,"AutocompleteErrorEvent":false,"BarProp":false,"BatteryManager":false,"BeforeUnloadEvent":false,"BiquadFilterNode":false,"Blob":false,"blur":false,"btoa":false,"Cache":false,"caches":false,"CacheStorage":false,"cancelAnimationFrame":false,"cancelIdleCallback":false,"CanvasGradient":false,"CanvasPattern":false,"CanvasRenderingContext2D":false,"CDATASection":false,"ChannelMergerNode":false,"ChannelSplitterNode":false,"CharacterData":false,"clearInterval":false,"clearTimeout":false,"clientInformation":false,"ClientRect":false,"ClientRectList":false,"ClipboardEvent":false,"close":false,"closed":false,"CloseEvent":false,"Comment":false,"CompositionEvent":false,"confirm":false,"console":false,"ConvolverNode":false,"createImageBitmap":false,"Credential":false,"CredentialsContainer":false,"crypto":false,"Crypto":false,"CryptoKey":false,"CSS":false,"CSSAnimation":false,"CSSFontFaceRule":false,"CSSImportRule":false,"CSSKeyframeRule":false,"CSSKeyframesRule":false,"CSSMediaRule":false,"CSSPageRule":false,"CSSRule":false,"CSSRuleList":false,"CSSStyleDeclaration":false,"CSSStyleRule":false,"CSSStyleSheet":false,"CSSSupportsRule":false,"CSSTransition":false,"CSSUnknownRule":false,"CSSViewportRule":false,"customElements":false,"CustomEvent":false,"DataTransfer":false,"DataTransferItem":false,"DataTransferItemList":false,"Debug":false,"defaultStatus":false,"defaultstatus":false,"DelayNode":false,"DeviceMotionEvent":false,"DeviceOrientationEvent":false,"devicePixelRatio":false,"dispatchEvent":false,"document":false,"Document":false,"DocumentFragment":false,"DocumentTimeline":false,"DocumentType":false,"DOMError":false,"DOMException":false,"DOMImplementation":false,"DOMParser":false,"DOMSettableTokenList":false,"DOMStringList":false,"DOMStringMap":false,"DOMTokenList":false,"DragEvent":false,"DynamicsCompressorNode":false,"Element":false,"ElementTimeControl":false,"ErrorEvent":false,"event":false,"Event":false,"EventSource":false,"EventTarget":false,"external":false,"FederatedCredential":false,"fetch":false,"File":false,"FileError":false,"FileList":false,"FileReader":false,"find":false,"focus":false,"FocusEvent":false,"FontFace":false,"FormData":false,"frameElement":false,"frames":false,"GainNode":false,"Gamepad":false,"GamepadButton":false,"GamepadEvent":false,"getComputedStyle":false,"getSelection":false,"HashChangeEvent":false,"Headers":false,"history":false,"History":false,"HTMLAllCollection":false,"HTMLAnchorElement":false,"HTMLAppletElement":false,"HTMLAreaElement":false,"HTMLAudioElement":false,"HTMLBaseElement":false,"HTMLBlockquoteElement":false,"HTMLBodyElement":false,"HTMLBRElement":false,"HTMLButtonElement":false,"HTMLCanvasElement":false,"HTMLCollection":false,"HTMLContentElement":false,"HTMLDataListElement":false,"HTMLDetailsElement":false,"HTMLDialogElement":false,"HTMLDirectoryElement":false,"HTMLDivElement":false,"HTMLDListElement":false,"HTMLDocument":false,"HTMLElement":false,"HTMLEmbedElement":false,"HTMLFieldSetElement":false,"HTMLFontElement":false,"HTMLFormControlsCollection":false,"HTMLFormElement":false,"HTMLFrameElement":false,"HTMLFrameSetElement":false,"HTMLHeadElement":false,"HTMLHeadingElement":false,"HTMLHRElement":false,"HTMLHtmlElement":false,"HTMLIFrameElement":false,"HTMLImageElement":false,"HTMLInputElement":false,"HTMLIsIndexElement":false,"HTMLKeygenElement":false,"HTMLLabelElement":false,"HTMLLayerElement":false,"HTMLLegendElement":false,"HTMLLIElement":false,"HTMLLinkElement":false,"HTMLMapElement":false,"HTMLMarqueeElement":false,"HTMLMediaElement":false,"HTMLMenuElement":false,"HTMLMetaElement":false,"HTMLMeterElement":false,"HTMLModElement":false,"HTMLObjectElement":false,"HTMLOListElement":false,"HTMLOptGroupElement":false,"HTMLOptionElement":false,"HTMLOptionsCollection":false,"HTMLOutputElement":false,"HTMLParagraphElement":false,"HTMLParamElement":false,"HTMLPictureElement":false,"HTMLPreElement":false,"HTMLProgressElement":false,"HTMLQuoteElement":false,"HTMLScriptElement":false,"HTMLSelectElement":false,"HTMLShadowElement":false,"HTMLSourceElement":false,"HTMLSpanElement":false,"HTMLStyleElement":false,"HTMLTableCaptionElement":false,"HTMLTableCellElement":false,"HTMLTableColElement":false,"HTMLTableElement":false,"HTMLTableRowElement":false,"HTMLTableSectionElement":false,"HTMLTemplateElement":false,"HTMLTextAreaElement":false,"HTMLTitleElement":false,"HTMLTrackElement":false,"HTMLUListElement":false,"HTMLUnknownElement":false,"HTMLVideoElement":false,"IDBCursor":false,"IDBCursorWithValue":false,"IDBDatabase":false,"IDBEnvironment":false,"IDBFactory":false,"IDBIndex":false,"IDBKeyRange":false,"IDBObjectStore":false,"IDBOpenDBRequest":false,"IDBRequest":false,"IDBTransaction":false,"IDBVersionChangeEvent":false,"Image":false,"ImageBitmap":false,"ImageData":false,"indexedDB":false,"innerHeight":false,"innerWidth":false,"InputEvent":false,"InputMethodContext":false,"IntersectionObserver":false,"IntersectionObserverEntry":false,"Intl":false,"KeyboardEvent":false,"KeyframeEffect":false,"KeyframeEffectReadOnly":false,"length":false,"localStorage":false,"location":false,"Location":false,"locationbar":false,"matchMedia":false,"MediaElementAudioSourceNode":false,"MediaEncryptedEvent":false,"MediaError":false,"MediaKeyError":false,"MediaKeyEvent":false,"MediaKeyMessageEvent":false,"MediaKeys":false,"MediaKeySession":false,"MediaKeyStatusMap":false,"MediaKeySystemAccess":false,"MediaList":false,"MediaQueryList":false,"MediaQueryListEvent":false,"MediaSource":false,"MediaRecorder":false,"MediaStream":false,"MediaStreamAudioDestinationNode":false,"MediaStreamAudioSourceNode":false,"MediaStreamEvent":false,"MediaStreamTrack":false,"menubar":false,"MessageChannel":false,"MessageEvent":false,"MessagePort":false,"MIDIAccess":false,"MIDIConnectionEvent":false,"MIDIInput":false,"MIDIInputMap":false,"MIDIMessageEvent":false,"MIDIOutput":false,"MIDIOutputMap":false,"MIDIPort":false,"MimeType":false,"MimeTypeArray":false,"MouseEvent":false,"moveBy":false,"moveTo":false,"MutationEvent":false,"MutationObserver":false,"MutationRecord":false,"name":false,"NamedNodeMap":false,"navigator":false,"Navigator":false,"Node":false,"NodeFilter":false,"NodeIterator":false,"NodeList":false,"Notification":false,"OfflineAudioCompletionEvent":false,"OfflineAudioContext":false,"offscreenBuffering":false,"onbeforeunload":true,"onblur":true,"onerror":true,"onfocus":true,"onload":true,"onresize":true,"onunload":true,"open":false,"openDatabase":false,"opener":false,"opera":false,"Option":false,"OscillatorNode":false,"outerHeight":false,"outerWidth":false,"PageTransitionEvent":false,"pageXOffset":false,"pageYOffset":false,"parent":false,"PasswordCredential":false,"Path2D":false,"performance":false,"Performance":false,"PerformanceEntry":false,"PerformanceMark":false,"PerformanceMeasure":false,"PerformanceNavigation":false,"PerformanceResourceTiming":false,"PerformanceTiming":false,"PeriodicWave":false,"Permissions":false,"PermissionStatus":false,"personalbar":false,"Plugin":false,"PluginArray":false,"PopStateEvent":false,"postMessage":false,"print":false,"ProcessingInstruction":false,"ProgressEvent":false,"PromiseRejectionEvent":false,"prompt":false,"PushManager":false,"PushSubscription":false,"RadioNodeList":false,"Range":false,"ReadableByteStream":false,"ReadableStream":false,"removeEventListener":false,"Request":false,"requestAnimationFrame":false,"requestIdleCallback":false,"resizeBy":false,"resizeTo":false,"Response":false,"RTCIceCandidate":false,"RTCSessionDescription":false,"RTCPeerConnection":false,"screen":false,"Screen":false,"screenLeft":false,"ScreenOrientation":false,"screenTop":false,"screenX":false,"screenY":false,"ScriptProcessorNode":false,"scroll":false,"scrollbars":false,"scrollBy":false,"scrollTo":false,"scrollX":false,"scrollY":false,"SecurityPolicyViolationEvent":false,"Selection":false,"self":false,"ServiceWorker":false,"ServiceWorkerContainer":false,"ServiceWorkerRegistration":false,"sessionStorage":false,"setInterval":false,"setTimeout":false,"ShadowRoot":false,"SharedKeyframeList":false,"SharedWorker":false,"showModalDialog":false,"SiteBoundCredential":false,"speechSynthesis":false,"SpeechSynthesisEvent":false,"SpeechSynthesisUtterance":false,"status":false,"statusbar":false,"stop":false,"Storage":false,"StorageEvent":false,"styleMedia":false,"StyleSheet":false,"StyleSheetList":false,"SubtleCrypto":false,"SVGAElement":false,"SVGAltGlyphDefElement":false,"SVGAltGlyphElement":false,"SVGAltGlyphItemElement":false,"SVGAngle":false,"SVGAnimateColorElement":false,"SVGAnimatedAngle":false,"SVGAnimatedBoolean":false,"SVGAnimatedEnumeration":false,"SVGAnimatedInteger":false,"SVGAnimatedLength":false,"SVGAnimatedLengthList":false,"SVGAnimatedNumber":false,"SVGAnimatedNumberList":false,"SVGAnimatedPathData":false,"SVGAnimatedPoints":false,"SVGAnimatedPreserveAspectRatio":false,"SVGAnimatedRect":false,"SVGAnimatedString":false,"SVGAnimatedTransformList":false,"SVGAnimateElement":false,"SVGAnimateMotionElement":false,"SVGAnimateTransformElement":false,"SVGAnimationElement":false,"SVGCircleElement":false,"SVGClipPathElement":false,"SVGColor":false,"SVGColorProfileElement":false,"SVGColorProfileRule":false,"SVGComponentTransferFunctionElement":false,"SVGCSSRule":false,"SVGCursorElement":false,"SVGDefsElement":false,"SVGDescElement":false,"SVGDiscardElement":false,"SVGDocument":false,"SVGElement":false,"SVGElementInstance":false,"SVGElementInstanceList":false,"SVGEllipseElement":false,"SVGEvent":false,"SVGExternalResourcesRequired":false,"SVGFEBlendElement":false,"SVGFEColorMatrixElement":false,"SVGFEComponentTransferElement":false,"SVGFECompositeElement":false,"SVGFEConvolveMatrixElement":false,"SVGFEDiffuseLightingElement":false,"SVGFEDisplacementMapElement":false,"SVGFEDistantLightElement":false,"SVGFEDropShadowElement":false,"SVGFEFloodElement":false,"SVGFEFuncAElement":false,"SVGFEFuncBElement":false,"SVGFEFuncGElement":false,"SVGFEFuncRElement":false,"SVGFEGaussianBlurElement":false,"SVGFEImageElement":false,"SVGFEMergeElement":false,"SVGFEMergeNodeElement":false,"SVGFEMorphologyElement":false,"SVGFEOffsetElement":false,"SVGFEPointLightElement":false,"SVGFESpecularLightingElement":false,"SVGFESpotLightElement":false,"SVGFETileElement":false,"SVGFETurbulenceElement":false,"SVGFilterElement":false,"SVGFilterPrimitiveStandardAttributes":false,"SVGFitToViewBox":false,"SVGFontElement":false,"SVGFontFaceElement":false,"SVGFontFaceFormatElement":false,"SVGFontFaceNameElement":false,"SVGFontFaceSrcElement":false,"SVGFontFaceUriElement":false,"SVGForeignObjectElement":false,"SVGGElement":false,"SVGGeometryElement":false,"SVGGlyphElement":false,"SVGGlyphRefElement":false,"SVGGradientElement":false,"SVGGraphicsElement":false,"SVGHKernElement":false,"SVGICCColor":false,"SVGImageElement":false,"SVGLangSpace":false,"SVGLength":false,"SVGLengthList":false,"SVGLinearGradientElement":false,"SVGLineElement":false,"SVGLocatable":false,"SVGMarkerElement":false,"SVGMaskElement":false,"SVGMatrix":false,"SVGMetadataElement":false,"SVGMissingGlyphElement":false,"SVGMPathElement":false,"SVGNumber":false,"SVGNumberList":false,"SVGPaint":false,"SVGPathElement":false,"SVGPathSeg":false,"SVGPathSegArcAbs":false,"SVGPathSegArcRel":false,"SVGPathSegClosePath":false,"SVGPathSegCurvetoCubicAbs":false,"SVGPathSegCurvetoCubicRel":false,"SVGPathSegCurvetoCubicSmoothAbs":false,"SVGPathSegCurvetoCubicSmoothRel":false,"SVGPathSegCurvetoQuadraticAbs":false,"SVGPathSegCurvetoQuadraticRel":false,"SVGPathSegCurvetoQuadraticSmoothAbs":false,"SVGPathSegCurvetoQuadraticSmoothRel":false,"SVGPathSegLinetoAbs":false,"SVGPathSegLinetoHorizontalAbs":false,"SVGPathSegLinetoHorizontalRel":false,"SVGPathSegLinetoRel":false,"SVGPathSegLinetoVerticalAbs":false,"SVGPathSegLinetoVerticalRel":false,"SVGPathSegList":false,"SVGPathSegMovetoAbs":false,"SVGPathSegMovetoRel":false,"SVGPatternElement":false,"SVGPoint":false,"SVGPointList":false,"SVGPolygonElement":false,"SVGPolylineElement":false,"SVGPreserveAspectRatio":false,"SVGRadialGradientElement":false,"SVGRect":false,"SVGRectElement":false,"SVGRenderingIntent":false,"SVGScriptElement":false,"SVGSetElement":false,"SVGStopElement":false,"SVGStringList":false,"SVGStylable":false,"SVGStyleElement":false,"SVGSVGElement":false,"SVGSwitchElement":false,"SVGSymbolElement":false,"SVGTests":false,"SVGTextContentElement":false,"SVGTextElement":false,"SVGTextPathElement":false,"SVGTextPositioningElement":false,"SVGTitleElement":false,"SVGTransform":false,"SVGTransformable":false,"SVGTransformList":false,"SVGTRefElement":false,"SVGTSpanElement":false,"SVGUnitTypes":false,"SVGURIReference":false,"SVGUseElement":false,"SVGViewElement":false,"SVGViewSpec":false,"SVGVKernElement":false,"SVGZoomAndPan":false,"SVGZoomEvent":false,"Text":false,"TextDecoder":false,"TextEncoder":false,"TextEvent":false,"TextMetrics":false,"TextTrack":false,"TextTrackCue":false,"TextTrackCueList":false,"TextTrackList":false,"TimeEvent":false,"TimeRanges":false,"toolbar":false,"top":false,"Touch":false,"TouchEvent":false,"TouchList":false,"TrackEvent":false,"TransitionEvent":false,"TreeWalker":false,"UIEvent":false,"URL":false,"URLSearchParams":false,"ValidityState":false,"VTTCue":false,"WaveShaperNode":false,"WebGLActiveInfo":false,"WebGLBuffer":false,"WebGLContextEvent":false,"WebGLFramebuffer":false,"WebGLProgram":false,"WebGLRenderbuffer":false,"WebGLRenderingContext":false,"WebGLShader":false,"WebGLShaderPrecisionFormat":false,"WebGLTexture":false,"WebGLUniformLocation":false,"WebSocket":false,"WheelEvent":false,"window":false,"Window":false,"Worker":false,"XDomainRequest":false,"XMLDocument":false,"XMLHttpRequest":false,"XMLHttpRequestEventTarget":false,"XMLHttpRequestProgressEvent":false,"XMLHttpRequestUpload":false,"XMLSerializer":false,"XPathEvaluator":false,"XPathException":false,"XPathExpression":false,"XPathNamespace":false,"XPathNSResolver":false,"XPathResult":false,"XSLTProcessor":false},"worker":{"applicationCache":false,"atob":false,"Blob":false,"BroadcastChannel":false,"btoa":false,"Cache":false,"caches":false,"clearInterval":false,"clearTimeout":false,"close":true,"console":false,"fetch":false,"FileReaderSync":false,"FormData":false,"Headers":false,"IDBCursor":false,"IDBCursorWithValue":false,"IDBDatabase":false,"IDBFactory":false,"IDBIndex":false,"IDBKeyRange":false,"IDBObjectStore":false,"IDBOpenDBRequest":false,"IDBRequest":false,"IDBTransaction":false,"IDBVersionChangeEvent":false,"ImageData":false,"importScripts":true,"indexedDB":false,"location":false,"MessageChannel":false,"MessagePort":false,"name":false,"navigator":false,"Notification":false,"onclose":true,"onconnect":true,"onerror":true,"onlanguagechange":true,"onmessage":true,"onoffline":true,"ononline":true,"onrejectionhandled":true,"onunhandledrejection":true,"performance":false,"Performance":false,"PerformanceEntry":false,"PerformanceMark":false,"PerformanceMeasure":false,"PerformanceNavigation":false,"PerformanceResourceTiming":false,"PerformanceTiming":false,"postMessage":true,"Promise":false,"Request":false,"Response":false,"self":true,"ServiceWorkerRegistration":false,"setInterval":false,"setTimeout":false,"TextDecoder":false,"TextEncoder":false,"URL":false,"URLSearchParams":false,"WebSocket":false,"Worker":false,"XMLHttpRequest":false},"node":{"__dirname":false,"__filename":false,"arguments":false,"Buffer":false,"clearImmediate":false,"clearInterval":false,"clearTimeout":false,"console":false,"exports":true,"GLOBAL":false,"global":false,"Intl":false,"module":false,"process":false,"require":false,"root":false,"setImmediate":false,"setInterval":false,"setTimeout":false},"commonjs":{"exports":true,"module":false,"require":false,"global":false},"amd":{"define":false,"require":false},"mocha":{"after":false,"afterEach":false,"before":false,"beforeEach":false,"context":false,"describe":false,"it":false,"mocha":false,"run":false,"setup":false,"specify":false,"suite":false,"suiteSetup":false,"suiteTeardown":false,"teardown":false,"test":false,"xcontext":false,"xdescribe":false,"xit":false,"xspecify":false},"jasmine":{"afterAll":false,"afterEach":false,"beforeAll":false,"beforeEach":false,"describe":false,"expect":false,"fail":false,"fdescribe":false,"fit":false,"it":false,"jasmine":false,"pending":false,"runs":false,"spyOn":false,"spyOnProperty":false,"waits":false,"waitsFor":false,"xdescribe":false,"xit":false},"jest":{"afterAll":false,"afterEach":false,"beforeAll":false,"beforeEach":false,"check":false,"describe":false,"expect":false,"gen":false,"it":false,"fdescribe":false,"fit":false,"jest":false,"pit":false,"require":false,"test":false,"xdescribe":false,"xit":false,"xtest":false},"qunit":{"asyncTest":false,"deepEqual":false,"equal":false,"expect":false,"module":false,"notDeepEqual":false,"notEqual":false,"notOk":false,"notPropEqual":false,"notStrictEqual":false,"ok":false,"propEqual":false,"QUnit":false,"raises":false,"start":false,"stop":false,"strictEqual":false,"test":false,"throws":false},"phantomjs":{"console":true,"exports":true,"phantom":true,"require":true,"WebPage":true},"couch":{"emit":false,"exports":false,"getRow":false,"log":false,"module":false,"provides":false,"require":false,"respond":false,"send":false,"start":false,"sum":false},"rhino":{"defineClass":false,"deserialize":false,"gc":false,"help":false,"importClass":false,"importPackage":false,"java":false,"load":false,"loadClass":false,"Packages":false,"print":false,"quit":false,"readFile":false,"readUrl":false,"runCommand":false,"seal":false,"serialize":false,"spawn":false,"sync":false,"toint32":false,"version":false},"nashorn":{"__DIR__":false,"__FILE__":false,"__LINE__":false,"com":false,"edu":false,"exit":false,"Java":false,"java":false,"javafx":false,"JavaImporter":false,"javax":false,"JSAdapter":false,"load":false,"loadWithNewGlobal":false,"org":false,"Packages":false,"print":false,"quit":false},"wsh":{"ActiveXObject":true,"Enumerator":true,"GetObject":true,"ScriptEngine":true,"ScriptEngineBuildVersion":true,"ScriptEngineMajorVersion":true,"ScriptEngineMinorVersion":true,"VBArray":true,"WScript":true,"WSH":true,"XDomainRequest":true},"jquery":{"$":false,"jQuery":false},"yui":{"Y":false,"YUI":false,"YUI_config":false},"shelljs":{"cat":false,"cd":false,"chmod":false,"config":false,"cp":false,"dirs":false,"echo":false,"env":false,"error":false,"exec":false,"exit":false,"find":false,"grep":false,"ls":false,"ln":false,"mkdir":false,"mv":false,"popd":false,"pushd":false,"pwd":false,"rm":false,"sed":false,"set":false,"target":false,"tempdir":false,"test":false,"touch":false,"which":false},"prototypejs":{"$":false,"$$":false,"$A":false,"$break":false,"$continue":false,"$F":false,"$H":false,"$R":false,"$w":false,"Abstract":false,"Ajax":false,"Autocompleter":false,"Builder":false,"Class":false,"Control":false,"Draggable":false,"Draggables":false,"Droppables":false,"Effect":false,"Element":false,"Enumerable":false,"Event":false,"Field":false,"Form":false,"Hash":false,"Insertion":false,"ObjectRange":false,"PeriodicalExecuter":false,"Position":false,"Prototype":false,"Scriptaculous":false,"Selector":false,"Sortable":false,"SortableObserver":false,"Sound":false,"Template":false,"Toggle":false,"Try":false},"meteor":{"$":false,"_":false,"Accounts":false,"AccountsClient":false,"AccountsServer":false,"AccountsCommon":false,"App":false,"Assets":false,"Blaze":false,"check":false,"Cordova":false,"DDP":false,"DDPServer":false,"DDPRateLimiter":false,"Deps":false,"EJSON":false,"Email":false,"HTTP":false,"Log":false,"Match":false,"Meteor":false,"Mongo":false,"MongoInternals":false,"Npm":false,"Package":false,"Plugin":false,"process":false,"Random":false,"ReactiveDict":false,"ReactiveVar":false,"Router":false,"ServiceConfiguration":false,"Session":false,"share":false,"Spacebars":false,"Template":false,"Tinytest":false,"Tracker":false,"UI":false,"Utils":false,"WebApp":false,"WebAppInternals":false},"mongo":{"_isWindows":false,"_rand":false,"BulkWriteResult":false,"cat":false,"cd":false,"connect":false,"db":false,"getHostName":false,"getMemInfo":false,"hostname":false,"ISODate":false,"listFiles":false,"load":false,"ls":false,"md5sumFile":false,"mkdir":false,"Mongo":false,"NumberInt":false,"NumberLong":false,"ObjectId":false,"PlanCache":false,"print":false,"printjson":false,"pwd":false,"quit":false,"removeFile":false,"rs":false,"sh":false,"UUID":false,"version":false,"WriteResult":false},"applescript":{"$":false,"Application":false,"Automation":false,"console":false,"delay":false,"Library":false,"ObjC":false,"ObjectSpecifier":false,"Path":false,"Progress":false,"Ref":false},"serviceworker":{"caches":false,"Cache":false,"CacheStorage":false,"Client":false,"clients":false,"Clients":false,"ExtendableEvent":false,"ExtendableMessageEvent":false,"FetchEvent":false,"importScripts":false,"registration":false,"self":false,"ServiceWorker":false,"ServiceWorkerContainer":false,"ServiceWorkerGlobalScope":false,"ServiceWorkerMessageEvent":false,"ServiceWorkerRegistration":false,"skipWaiting":false,"WindowClient":false},"atomtest":{"advanceClock":false,"fakeClearInterval":false,"fakeClearTimeout":false,"fakeSetInterval":false,"fakeSetTimeout":false,"resetTimeouts":false,"waitsForPromise":false},"embertest":{"andThen":false,"click":false,"currentPath":false,"currentRouteName":false,"currentURL":false,"fillIn":false,"find":false,"findWithAssert":false,"keyEvent":false,"pauseTest":false,"resumeTest":false,"triggerEvent":false,"visit":false},"protractor":{"$":false,"$$":false,"browser":false,"By":false,"by":false,"DartObject":false,"element":false,"protractor":false},"shared-node-browser":{"clearInterval":false,"clearTimeout":false,"console":false,"setInterval":false,"setTimeout":false},"webextensions":{"browser":false,"chrome":false,"opr":false},"greasemonkey":{"GM_addStyle":false,"GM_deleteValue":false,"GM_getResourceText":false,"GM_getResourceURL":false,"GM_getValue":false,"GM_info":false,"GM_listValues":false,"GM_log":false,"GM_openInTab":false,"GM_registerMenuCommand":false,"GM_setClipboard":false,"GM_setValue":false,"GM_xmlhttpRequest":false,"unsafeWindow":false}}
 
 /***/ },
 /* 618 */
 /***/ function(module, exports, __webpack_require__) {
 
 	"use strict";
 
 	exports.__esModule = true;
--- a/devtools/client/debugger/new/search-worker.js
+++ b/devtools/client/debugger/new/search-worker.js
@@ -779,16 +779,19 @@ return /******/ (function(modules) { // 
 
 	var _buildQuery = __webpack_require__(1138);
 
 	var _buildQuery2 = _interopRequireDefault(_buildQuery);
 
 	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 	function getMatches(query, text, modifiers) {
+	  if (!query || !text || !modifiers) {
+	    return [];
+	  }
 	  var regexQuery = (0, _buildQuery2.default)(query, modifiers, {
 	    isGlobal: true
 	  });
 	  var matchedLocations = [];
 	  var lines = text.split("\n");
 	  for (var i = 0; i < lines.length; i++) {
 	    var singleMatch = void 0;
 	    while ((singleMatch = regexQuery.exec(lines[i])) !== null) {
--- a/devtools/client/preferences/debugger.js
+++ b/devtools/client/preferences/debugger.js
@@ -15,17 +15,17 @@ pref("devtools.debugger.ignore-caught-ex
 pref("devtools.debugger.source-maps-enabled", true);
 pref("devtools.debugger.client-source-maps-enabled", true);
 pref("devtools.debugger.pretty-print-enabled", true);
 pref("devtools.debugger.auto-pretty-print", false);
 pref("devtools.debugger.auto-black-box", true);
 pref("devtools.debugger.workers", false);
 
 // The default Debugger UI settings
-pref("devtools.debugger.prefs-schema-version", "1.0.0");
+pref("devtools.debugger.prefs-schema-version", "1.0.2");
 pref("devtools.debugger.ui.panes-workers-and-sources-width", 200);
 pref("devtools.debugger.ui.panes-instruments-width", 300);
 pref("devtools.debugger.ui.panes-visible-on-startup", false);
 pref("devtools.debugger.ui.variables-sorting-enabled", true);
 pref("devtools.debugger.ui.variables-only-enum-visible", false);
 pref("devtools.debugger.ui.variables-searchbox-visible", false);
 pref("devtools.debugger.ui.framework-grouping-on", true);
 pref("devtools.debugger.call-stack-visible", false);
--- a/devtools/client/webconsole/net/test/mochitest/browser_net_headers.js
+++ b/devtools/client/webconsole/net/test/mochitest/browser_net_headers.js
@@ -9,16 +9,19 @@ const TEST_PAGE_URL = URL_ROOT + "page_b
 const JSON_XHR_URL = URL_ROOT + "test.json";
 
 /**
  * This test generates XHR requests in the page, expands
  * networks details in the Console panel and checks that
  * HTTP headers are there.
  */
 add_task(function* () {
+  // Disable rcwn to make cache behavior deterministic.
+  yield pushPref("network.http.rcwn.enabled", false);
+
   info("Test XHR Spy headers started");
 
   let {hud} = yield addTestTab(TEST_PAGE_URL);
 
   let netInfoBody = yield executeAndInspectXhr(hud, {
     method: "GET",
     url: JSON_XHR_URL
   });
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -309,16 +309,18 @@ int32_t gTimeoutCnt                     
 #endif
 
 #if defined(DEBUG_bryner) || defined(DEBUG_chb)
 #define DEBUG_PAGE_CACHE
 #endif
 
 #define DOM_TOUCH_LISTENER_ADDED "dom-touch-listener-added"
 
+#define MEMORY_PRESSURE_OBSERVER_TOPIC "memory-pressure"
+
 // The interval at which we execute idle callbacks
 static uint32_t gThrottledIdlePeriodLength;
 
 #define DEFAULT_THROTTLED_IDLE_PERIOD_LENGTH 10000
 
 #define FORWARD_TO_OUTER(method, args, err_rval)                              \
   PR_BEGIN_MACRO                                                              \
   if (IsInnerWindow()) {                                                      \
@@ -1652,16 +1654,18 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
     mObserver = new nsGlobalWindowObserver(this);
     if (mObserver) {
       nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
       if (os) {
         // Watch for online/offline status changes so we can fire events. Use
         // a strong reference.
         os->AddObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
                         false);
+
+        os->AddObserver(mObserver, MEMORY_PRESSURE_OBSERVER_TOPIC, false);
       }
 
       Preferences::AddStrongObserver(mObserver, "intl.accept_languages");
 
       // Watch for storage notifications so we can fire storage events.
       RefPtr<StorageNotifierService> sns =
         StorageNotifierService::GetOrCreate();
       if (sns) {
@@ -1957,16 +1961,17 @@ nsGlobalWindow::CleanUp()
   StartDying();
 
   DisconnectEventTargetObjects();
 
   if (mObserver) {
     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     if (os) {
       os->RemoveObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC);
+      os->RemoveObserver(mObserver, MEMORY_PRESSURE_OBSERVER_TOPIC);
     }
 
     RefPtr<StorageNotifierService> sns = StorageNotifierService::GetOrCreate();
     if (sns) {
      sns->Unregister(mObserver);
     }
 
 #ifdef MOZ_B2G
@@ -12272,16 +12277,23 @@ nsGlobalWindow::Observe(nsISupports* aSu
   if (!nsCRT::strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC)) {
     if (!IsFrozen()) {
         // Fires an offline status event if the offline status has changed
         FireOfflineStatusEventIfChanged();
     }
     return NS_OK;
   }
 
+  if (!nsCRT::strcmp(aTopic, MEMORY_PRESSURE_OBSERVER_TOPIC)) {
+    if (mPerformance) {
+      mPerformance->MemoryPressure();
+    }
+    return NS_OK;
+  }
+
   if (!nsCRT::strcmp(aTopic, OBSERVER_TOPIC_IDLE)) {
     mCurrentlyIdle = true;
     if (IsFrozen()) {
       // need to fire only one idle event while the window is frozen.
       mNotifyIdleObserversIdleOnThaw = true;
       mNotifyIdleObserversActiveOnThaw = false;
     } else if (AsInner()->IsCurrentInnerWindow()) {
       HandleIdleActiveEvent();
@@ -13796,16 +13808,23 @@ nsGlobalWindow::AddSizeOfIncludingThis(n
       aWindowSizes->mDOMEventTargetsSize +=
         iSizeOf->SizeOfEventTargetIncludingThis(aWindowSizes->mMallocSizeOf);
     }
     if (EventListenerManager* elm = et->GetExistingListenerManager()) {
       aWindowSizes->mDOMEventListenersCount += elm->ListenerCount();
     }
     ++aWindowSizes->mDOMEventTargetsCount;
   }
+
+  if (IsInnerWindow() && mPerformance) {
+    aWindowSizes->mDOMPerformanceUserEntries =
+      mPerformance->SizeOfUserEntries(aWindowSizes->mMallocSizeOf);
+    aWindowSizes->mDOMPerformanceResourceEntries =
+      mPerformance->SizeOfResourceEntries(aWindowSizes->mMallocSizeOf);
+  }
 }
 
 void
 nsGlobalWindow::AddGamepad(uint32_t aIndex, Gamepad* aGamepad)
 {
   MOZ_ASSERT(IsInnerWindow());
   // Create the index we will present to content based on which indices are
   // already taken, as required by the spec.
--- a/dom/base/nsWindowMemoryReporter.cpp
+++ b/dom/base/nsWindowMemoryReporter.cpp
@@ -401,16 +401,28 @@ CollectWindowReports(nsGlobalWindow *aWi
     windowSizes.mLayoutPresContextSize;
 
   REPORT_SIZE("/layout/frame-properties", windowSizes.mLayoutFramePropertiesSize,
          "Memory used for frame properties attached to frames "
          "within a window.");
   aWindowTotalSizes->mLayoutFramePropertiesSize +=
     windowSizes.mLayoutFramePropertiesSize;
 
+  REPORT_SIZE("/dom/performance/user-entries",
+              windowSizes.mDOMPerformanceUserEntries,
+              "Memory used for performance user entries.");
+  aWindowTotalSizes->mDOMPerformanceUserEntries +=
+    windowSizes.mDOMPerformanceUserEntries;
+
+  REPORT_SIZE("/dom/performance/resource-entries",
+              windowSizes.mDOMPerformanceResourceEntries,
+              "Memory used for performance resource entries.");
+  aWindowTotalSizes->mDOMPerformanceResourceEntries +=
+    windowSizes.mDOMPerformanceResourceEntries;
+
   // 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, ...)                                        \
--- a/dom/base/nsWindowMemoryReporter.h
+++ b/dom/base/nsWindowMemoryReporter.h
@@ -22,16 +22,18 @@
 
 class nsWindowSizes {
 #define FOR_EACH_SIZE(macro) \
   macro(DOM,   mDOMElementNodesSize) \
   macro(DOM,   mDOMTextNodesSize) \
   macro(DOM,   mDOMCDATANodesSize) \
   macro(DOM,   mDOMCommentNodesSize) \
   macro(DOM,   mDOMEventTargetsSize) \
+  macro(DOM,   mDOMPerformanceUserEntries) \
+  macro(DOM,   mDOMPerformanceResourceEntries) \
   macro(DOM,   mDOMOtherSize) \
   macro(Style, mStyleSheetsSize) \
   macro(Other, mLayoutPresShellSize) \
   macro(Style, mLayoutStyleSetsSize) \
   macro(Other, mLayoutTextRunsSize) \
   macro(Other, mLayoutPresContextSize) \
   macro(Other, mLayoutFramePropertiesSize) \
   macro(Other, mPropertyTablesSize) \
--- a/dom/media/gmp/ChromiumCDMAdapter.cpp
+++ b/dom/media/gmp/ChromiumCDMAdapter.cpp
@@ -1,28 +1,42 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ChromiumCDMAdapter.h"
 #include "content_decryption_module.h"
+#include "content_decryption_module_ext.h"
 #include "VideoUtils.h"
 #include "gmp-api/gmp-entrypoints.h"
 #include "gmp-api/gmp-decryption.h"
 #include "gmp-api/gmp-video-codec.h"
 #include "gmp-api/gmp-platform.h"
 #include "WidevineUtils.h"
 #include "GMPLog.h"
+#include "mozilla/Move.h"
+
+#if defined(XP_MACOSX) || defined(XP_LINUX)
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#endif
 
 // Declared in WidevineAdapter.cpp.
 extern const GMPPlatformAPI* sPlatform;
 
 namespace mozilla {
 
+ChromiumCDMAdapter::ChromiumCDMAdapter(nsTArray<nsCString>&& aHostFilePaths)
+{
+  PopulateHostFiles(Move(aHostFilePaths));
+}
+
 void
 ChromiumCDMAdapter::SetAdaptee(PRLibrary* aLib)
 {
   mLib = aLib;
 }
 
 void*
 ChromiumCdmHost(int aHostInterfaceVersion, void* aUserData)
@@ -32,25 +46,46 @@ ChromiumCdmHost(int aHostInterfaceVersio
     return nullptr;
   }
   return static_cast<cdm::Host_8*>(aUserData);
 }
 
 #define STRINGIFY(s) _STRINGIFY(s)
 #define _STRINGIFY(s) #s
 
+static cdm::HostFile
+TakeToCDMHostFile(HostFileData& aHostFileData)
+{
+  return cdm::HostFile(aHostFileData.mBinary.Path().get(),
+                       aHostFileData.mBinary.TakePlatformFile(),
+                       aHostFileData.mSig.TakePlatformFile());
+}
+
 GMPErr
 ChromiumCDMAdapter::GMPInit(const GMPPlatformAPI* aPlatformAPI)
 {
   CDM_LOG("ChromiumCDMAdapter::GMPInit");
   sPlatform = aPlatformAPI;
   if (!mLib) {
     return GMPGenericErr;
   }
 
+  // Note: we must call the VerifyCdmHost_0 function if it's present before
+  // we call the initialize function.
+  auto verify = reinterpret_cast<decltype(::VerifyCdmHost_0)*>(
+    PR_FindFunctionSymbol(mLib, STRINGIFY(VerifyCdmHost_0)));
+  if (verify) {
+    nsTArray<cdm::HostFile> files;
+    for (HostFileData& hostFile : mHostFiles) {
+      files.AppendElement(TakeToCDMHostFile(hostFile));
+    }
+    bool result = verify(files.Elements(), files.Length());
+    GMP_LOG("%s VerifyCdmHost_0 returned %d", __func__, result);
+  }
+
   auto init = reinterpret_cast<decltype(::INITIALIZE_CDM_MODULE)*>(
     PR_FindFunctionSymbol(mLib, STRINGIFY(INITIALIZE_CDM_MODULE)));
   if (!init) {
     return GMPGenericErr;
   }
 
   CDM_LOG(STRINGIFY(INITIALIZE_CDM_MODULE)"()");
   init();
@@ -125,9 +160,69 @@ ChromiumCDMAdapter::Supports(int32_t aMo
                              int32_t aInterfaceVersion,
                              int32_t aHostVersion)
 {
   return aModuleVersion == CDM_MODULE_VERSION &&
          aInterfaceVersion == cdm::ContentDecryptionModule_8::kVersion &&
          aHostVersion == cdm::Host_8::kVersion;
 }
 
+HostFile::HostFile(HostFile&& aOther)
+  : mPath(aOther.mPath)
+  , mFile(aOther.TakePlatformFile())
+{
+}
+
+HostFile::~HostFile()
+{
+  if (mFile != cdm::kInvalidPlatformFile) {
+#ifdef XP_WIN
+    CloseHandle(mFile);
+#else
+    close(mFile);
+#endif
+    mFile = cdm::kInvalidPlatformFile;
+  }
+}
+
+#ifdef XP_WIN
+HostFile::HostFile(const nsCString& aPath)
+  : mPath(NS_ConvertUTF8toUTF16(aPath))
+{
+  HANDLE handle = CreateFileW(mPath.get(),
+                              GENERIC_READ,
+                              FILE_SHARE_READ | FILE_SHARE_WRITE,
+                              NULL,
+                              OPEN_EXISTING,
+                              0,
+                              NULL);
+  mFile = (handle == INVALID_HANDLE_VALUE) ? cdm::kInvalidPlatformFile : handle;
+}
+#endif
+
+#if defined(XP_MACOSX) || defined(XP_LINUX)
+HostFile::HostFile(const nsCString& aPath)
+  : mPath(aPath)
+{
+  // Note: open() returns -1 on failure; i.e. kInvalidPlatformFile.
+  mFile = open(aPath.get(), O_RDONLY);
+}
+#endif
+
+cdm::PlatformFile
+HostFile::TakePlatformFile()
+{
+  cdm::PlatformFile f = mFile;
+  mFile = cdm::kInvalidPlatformFile;
+  return f;
+}
+
+void
+ChromiumCDMAdapter::PopulateHostFiles(nsTArray<nsCString>&& aHostFilePaths)
+{
+  for (const nsCString& path : aHostFilePaths) {
+    mHostFiles.AppendElement(
+      HostFileData(mozilla::HostFile(path),
+                   mozilla::HostFile(path + NS_LITERAL_CSTRING(".sig"))));
+  }
+}
+
 } // namespace mozilla
--- a/dom/media/gmp/ChromiumCDMAdapter.h
+++ b/dom/media/gmp/ChromiumCDMAdapter.h
@@ -4,38 +4,86 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef ChromiumAdapter_h_
 #define ChromiumAdapter_h_
 
 #include "GMPLoader.h"
 #include "prlink.h"
 #include "GMPUtils.h"
+#include "nsTArray.h"
+#include "content_decryption_module_ext.h"
+#include "nsString.h"
 
 struct GMPPlatformAPI;
 
 namespace mozilla {
 
+#if defined(OS_WIN)
+typedef nsString HostFileString;
+#else
+typedef nsCString HostFileString;
+#endif
+
+class HostFile
+{
+public:
+  explicit HostFile(const nsCString& aPath);
+  HostFile(HostFile&& aOther);
+  ~HostFile();
+
+  const HostFileString& Path() const { return mPath; }
+  cdm::PlatformFile TakePlatformFile();
+
+private:
+  const HostFileString mPath;
+  cdm::PlatformFile mFile = cdm::kInvalidPlatformFile;
+};
+
+struct HostFileData
+{
+  HostFileData(HostFile&& aBinary, HostFile&& aSig)
+    : mBinary(Move(aBinary))
+    , mSig(Move(aSig))
+  {
+  }
+
+  HostFileData(HostFileData&& aOther)
+    : mBinary(Move(aOther.mBinary))
+    , mSig(Move(aOther.mSig))
+  {
+  }
+
+  ~HostFileData() {}
+
+  HostFile mBinary;
+  HostFile mSig;
+};
+
 class ChromiumCDMAdapter : public gmp::GMPAdapter
 {
 public:
+  explicit ChromiumCDMAdapter(nsTArray<nsCString>&& aHostFilePaths);
 
   void SetAdaptee(PRLibrary* aLib) override;
 
   // These are called in place of the corresponding GMP API functions.
   GMPErr GMPInit(const GMPPlatformAPI* aPlatformAPI) override;
   GMPErr GMPGetAPI(const char* aAPIName,
                    void* aHostAPI,
                    void** aPluginAPI,
                    uint32_t aDecryptorId) override;
   void GMPShutdown() override;
 
   static bool Supports(int32_t aModuleVersion,
                        int32_t aInterfaceVersion,
                        int32_t aHostVersion);
 
 private:
+  void PopulateHostFiles(nsTArray<nsCString>&& aHostFilePaths);
+
   PRLibrary* mLib = nullptr;
+  nsTArray<HostFileData> mHostFiles;
 };
 
 } // namespace mozilla
 
 #endif // ChromiumAdapter_h_
--- a/dom/media/gmp/GMPChild.cpp
+++ b/dom/media/gmp/GMPChild.cpp
@@ -17,23 +17,26 @@
 #include "gmp-video-decode.h"
 #include "gmp-video-encode.h"
 #include "GMPPlatform.h"
 #include "mozilla/ipc/CrashReporterClient.h"
 #include "mozilla/ipc/ProcessChild.h"
 #include "GMPUtils.h"
 #include "prio.h"
 #include "base/task.h"
+#include "base/command_line.h"
 #include "widevine-adapter/WidevineAdapter.h"
 #include "ChromiumCDMAdapter.h"
+#include "GMPLog.h"
 
 using namespace mozilla::ipc;
 
 #ifdef XP_WIN
 #include <stdlib.h> // for _exit()
+#include "WinUtils.h"
 #else
 #include <unistd.h> // for _exit()
 #endif
 
 #if defined(MOZ_GMP_SANDBOX)
 #if defined(XP_MACOSX)
 #include "mozilla/Sandbox.h"
 #endif
@@ -113,25 +116,23 @@ GetPluginFile(const nsAString& aPluginPa
   nsAutoString binaryName =                            baseName + NS_LITERAL_STRING(".dll");
 #else
 #error not defined
 #endif
   aLibFile->AppendRelativePath(binaryName);
   return true;
 }
 
-#if !defined(XP_MACOSX) || !defined(MOZ_GMP_SANDBOX)
 static bool
 GetPluginFile(const nsAString& aPluginPath,
               nsCOMPtr<nsIFile>& aLibFile)
 {
   nsCOMPtr<nsIFile> unusedlibDir;
   return GetPluginFile(aPluginPath, unusedlibDir, aLibFile);
 }
-#endif
 
 #if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
 static nsCString
 GetNativeTarget(nsIFile* aFile)
 {
   bool isLink;
   nsCString path;
   aFile->IsSymlink(&isLink);
@@ -292,16 +293,35 @@ GMPChild::RecvPreloadLibs(const nsCStrin
       }
     }
   }
 #endif
   return IPC_OK();
 }
 
 bool
+GMPChild::ResolveLinks(nsCOMPtr<nsIFile>& aPath)
+{
+#if defined(XP_WIN)
+  return widget::WinUtils::ResolveJunctionPointsAndSymLinks(aPath);
+#elif defined(XP_MACOSX)
+  nsCString targetPath = GetNativeTarget(aPath);
+  nsCOMPtr<nsIFile> newFile;
+  if (NS_FAILED(
+        NS_NewNativeLocalFile(targetPath, true, getter_AddRefs(newFile)))) {
+    return false;
+  }
+  aPath = newFile;
+  return true;
+#else
+  return true;
+#endif
+}
+
+bool
 GMPChild::GetUTF8LibPath(nsACString& aOutLibPath)
 {
 #if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
   nsAutoCString pluginDirectoryPath, pluginFilePath;
   if (!GetPluginPaths(mPluginPath, pluginDirectoryPath, pluginFilePath)) {
     MOZ_CRASH("Error scanning plugin path");
   }
   aOutLibPath.Assign(pluginFilePath);
@@ -320,16 +340,127 @@ GMPChild::GetUTF8LibPath(nsACString& aOu
   nsAutoString path;
   libFile->GetPath(path);
   aOutLibPath = NS_ConvertUTF16toUTF8(path);
 
   return true;
 #endif
 }
 
+#if defined(XP_MACOSX)
+#define FIREFOX_FILE NS_LITERAL_STRING("firefox")
+#define XUL_LIB_FILE NS_LITERAL_STRING("XUL")
+#elif defined(XP_LINUX)
+#define FIREFOX_FILE NS_LITERAL_STRING("firefox")
+#define XUL_LIB_FILE NS_LITERAL_STRING("libxul.so")
+#elif defined(OS_WIN)
+#define FIREFOX_FILE NS_LITERAL_STRING("firefox.exe")
+#define XUL_LIB_FILE NS_LITERAL_STRING("xul.dll")
+#endif
+
+#if defined(XP_MACOSX)
+static bool
+GetFirefoxAppPath(nsCOMPtr<nsIFile> aPluginContainerPath,
+                  nsCOMPtr<nsIFile>& aOutFirefoxAppPath)
+{
+  // aPluginContainerPath will end with something like:
+  // xxxx/NightlyDebug.app/Contents/MacOS/plugin-container.app/Contents/MacOS/plugin-container
+  MOZ_ASSERT(aPluginContainerPath);
+  nsCOMPtr<nsIFile> path = aPluginContainerPath;
+  for (int i = 0; i < 4; i++) {
+    nsCOMPtr<nsIFile> parent;
+    if (NS_FAILED(path->GetParent(getter_AddRefs(parent)))) {
+      return false;
+    }
+    path = parent;
+  }
+  MOZ_ASSERT(path);
+  aOutFirefoxAppPath = path;
+  return true;
+}
+#endif
+
+nsTArray<nsCString>
+GMPChild::MakeCDMHostVerificationPaths()
+{
+  nsTArray<nsCString> paths;
+
+  // Plugin binary path.
+  nsCOMPtr<nsIFile> path;
+  nsString str;
+  if (GetPluginFile(mPluginPath, path) && FileExists(path) &&
+      ResolveLinks(path) && NS_SUCCEEDED(path->GetPath(str))) {
+    paths.AppendElement(NS_ConvertUTF16toUTF8(str));
+  }
+
+  // Plugin-container binary path.
+  // Note: clang won't let us initialize an nsString from a wstring, so we
+  // need to go through UTF8 to get to an nsString.
+  const std::string pluginContainer =
+    WideToUTF8(CommandLine::ForCurrentProcess()->program());
+  path = nullptr;
+  str = NS_ConvertUTF8toUTF16(nsDependentCString(pluginContainer.c_str()));
+  if (NS_SUCCEEDED(NS_NewLocalFile(str,
+                                   true, /* aFollowLinks */
+                                   getter_AddRefs(path))) &&
+      FileExists(path) && ResolveLinks(path) &&
+      NS_SUCCEEDED(path->GetPath(str))) {
+    paths.AppendElement(nsCString(NS_ConvertUTF16toUTF8(str)));
+  } else {
+    // Without successfully determining plugin-container's path, we can't
+    // determine libxul's or Firefox's. So give up.
+    return paths;
+  }
+
+  // Firefox application binary path.
+  nsCOMPtr<nsIFile> appDir;
+#if defined(XP_WIN) || defined(XP_LINUX)
+  // Note: re-using 'path' var here, as on Windows/Linux we assume Firefox
+  // executable is in the same directory as plugin-container.
+  if (NS_SUCCEEDED(path->GetParent(getter_AddRefs(appDir))) &&
+      NS_SUCCEEDED(appDir->Clone(getter_AddRefs(path))) &&
+      NS_SUCCEEDED(path->Append(FIREFOX_FILE)) && FileExists(path) &&
+      ResolveLinks(path) && NS_SUCCEEDED(path->GetPath(str))) {
+    paths.AppendElement(NS_ConvertUTF16toUTF8(str));
+  }
+#elif defined(XP_MACOSX)
+  // On MacOS the firefox binary is a few parent directories up from
+  // plugin-container.
+  if (GetFirefoxAppPath(path, appDir) &&
+      NS_SUCCEEDED(appDir->Clone(getter_AddRefs(path))) &&
+      NS_SUCCEEDED(path->Append(FIREFOX_FILE)) && FileExists(path) &&
+      ResolveLinks(path) && NS_SUCCEEDED(path->GetPath(str))) {
+    paths.AppendElement(NS_ConvertUTF16toUTF8(str));
+  }
+#endif
+  // Libxul path. Note: re-using 'path' var here, as we assume libxul is in
+  // the same directory as Firefox executable.
+  appDir->GetPath(str);
+  if (NS_SUCCEEDED(appDir->Clone(getter_AddRefs(path))) &&
+      NS_SUCCEEDED(path->Append(XUL_LIB_FILE)) && FileExists(path) &&
+      ResolveLinks(path) && NS_SUCCEEDED(path->GetPath(str))) {
+    paths.AppendElement(NS_ConvertUTF16toUTF8(str));
+  }
+
+  return paths;
+}
+
+static nsCString
+ToCString(const nsTArray<nsCString>& aStrings)
+{
+  nsCString result;
+  for (const nsCString& s : aStrings) {
+    if (!result.IsEmpty()) {
+      result.AppendLiteral(",");
+    }
+    result.Append(s);
+  }
+  return result;
+}
+
 mozilla::ipc::IPCResult
 GMPChild::AnswerStartPlugin(const nsString& aAdapter)
 {
   LOGD("%s", __FUNCTION__);
 
   nsCString libPath;
   if (!GetUTF8LibPath(libPath)) {
     return IPC_FAIL_NO_REASON(this);
@@ -360,17 +491,19 @@ GMPChild::AnswerStartPlugin(const nsStri
     return IPC_FAIL_NO_REASON(this);
   }
 #endif
 
   GMPAdapter* adapter = nullptr;
   if (isWidevine) {
     adapter = new WidevineAdapter();
   } else if (isChromium) {
-    adapter = new ChromiumCDMAdapter();
+    nsTArray<nsCString> paths(MakeCDMHostVerificationPaths());
+    GMP_LOG("%s CDM host paths=%s", __func__, ToCString(paths).get());
+    adapter = new ChromiumCDMAdapter(Move(paths));
   }
 
   if (!mGMPLoader->Load(libPath.get(),
                         libPath.Length(),
                         platformAPI,
                         adapter)) {
     NS_WARNING("Failed to load GMP");
     delete platformAPI;
--- a/dom/media/gmp/GMPChild.h
+++ b/dom/media/gmp/GMPChild.h
@@ -36,16 +36,18 @@ public:
 
 #if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
   bool SetMacSandboxInfo(MacSandboxPluginType aPluginType);
 #endif
 
 private:
   friend class GMPContentChild;
 
+  bool ResolveLinks(nsCOMPtr<nsIFile>& aPath);
+
   bool GetUTF8LibPath(nsACString& aOutLibPath);
 
   mozilla::ipc::IPCResult AnswerStartPlugin(const nsString& aAdapter) override;
   mozilla::ipc::IPCResult RecvPreloadLibs(const nsCString& aLibs) override;
 
   PGMPTimerChild* AllocPGMPTimerChild() override;
   bool DeallocPGMPTimerChild(PGMPTimerChild* aActor) override;
 
@@ -59,16 +61,18 @@ private:
 
   mozilla::ipc::IPCResult RecvInitGMPContentChild(Endpoint<PGMPContentChild>&& aEndpoint) override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
   void ProcessingError(Result aCode, const char* aReason) override;
 
   GMPErr GetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI, uint32_t aDecryptorId = 0);
 
+  nsTArray<nsCString> MakeCDMHostVerificationPaths();
+
   nsTArray<UniquePtr<GMPContentChild>> mGMPContentChildren;
 
   RefPtr<GMPTimerChild> mTimerChild;
   RefPtr<GMPStorageChild> mStorage;
 
   MessageLoop* mGMPMessageLoop;
   nsString mPluginPath;
   UniquePtr<GMPLoader> mGMPLoader;
--- a/dom/performance/Performance.cpp
+++ b/dom/performance/Performance.cpp
@@ -272,28 +272,23 @@ Performance::RoundTime(double aTime) con
 void
 Performance::Mark(const nsAString& aName, ErrorResult& aRv)
 {
   // We add nothing when 'privacy.resistFingerprinting' is on.
   if (nsContentUtils::ShouldResistFingerprinting()) {
     return;
   }
 
-  // Don't add the entry if the buffer is full. XXX should be removed by bug 1159003.
-  if (mUserEntries.Length() >= mResourceTimingBufferSize) {
-    return;
-  }
-
   if (IsPerformanceTimingAttribute(aName)) {
     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     return;
   }
 
   RefPtr<PerformanceMark> performanceMark =
-    new PerformanceMark(GetAsISupports(), aName, Now());
+    new PerformanceMark(GetParentObject(), aName, Now());
   InsertUserEntry(performanceMark);
 
   if (profiler_is_active()) {
     profiler_add_marker(
       "UserTiming",
       MakeUnique<UserTimingMarkerPayload>(aName, TimeStamp::Now()));
   }
 }
@@ -339,22 +334,16 @@ Performance::Measure(const nsAString& aN
                      const Optional<nsAString>& aEndMark,
                      ErrorResult& aRv)
 {
   // We add nothing when 'privacy.resistFingerprinting' is on.
   if (nsContentUtils::ShouldResistFingerprinting()) {
     return;
   }
 
-  // Don't add the entry if the buffer is full. XXX should be removed by bug
-  // 1159003.
-  if (mUserEntries.Length() >= mResourceTimingBufferSize) {
-    return;
-  }
-
   DOMHighResTimeStamp startTime;
   DOMHighResTimeStamp endTime;
 
   if (IsPerformanceTimingAttribute(aName)) {
     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     return;
   }
 
@@ -375,17 +364,17 @@ Performance::Measure(const nsAString& aN
     if (NS_WARN_IF(aRv.Failed())) {
       return;
     }
   } else {
     endTime = Now();
   }
 
   RefPtr<PerformanceMeasure> performanceMeasure =
-    new PerformanceMeasure(GetAsISupports(), aName, startTime, endTime);
+    new PerformanceMeasure(GetParentObject(), aName, startTime, endTime);
   InsertUserEntry(performanceMeasure);
 
   if (profiler_is_active()) {
     TimeStamp startTimeStamp = CreationTimeStamp() +
                                TimeDuration::FromMilliseconds(startTime);
     TimeStamp endTimeStamp = CreationTimeStamp() +
                              TimeDuration::FromMilliseconds(endTime);
     profiler_add_marker(
@@ -573,10 +562,36 @@ Performance::IsObserverEnabled(JSContext
 
   RefPtr<PrefEnabledRunnable> runnable =
     new PrefEnabledRunnable(workerPrivate,
                             NS_LITERAL_CSTRING("dom.enable_performance_observer"));
 
   return runnable->Dispatch() && runnable->IsEnabled();
 }
 
+void
+Performance::MemoryPressure()
+{
+  mUserEntries.Clear();
+}
+
+size_t
+Performance::SizeOfUserEntries(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+  size_t userEntries = 0;
+  for (const PerformanceEntry* entry : mUserEntries) {
+    userEntries += entry->SizeOfIncludingThis(aMallocSizeOf);
+  }
+  return userEntries;
+}
+
+size_t
+Performance::SizeOfResourceEntries(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+  size_t resourceEntries = 0;
+  for (const PerformanceEntry* entry : mResourceEntries) {
+    resourceEntries += entry->SizeOfIncludingThis(aMallocSizeOf);
+  }
+  return resourceEntries;
+}
+
 } // dom namespace
 } // mozilla namespace
--- a/dom/performance/Performance.h
+++ b/dom/performance/Performance.h
@@ -96,33 +96,36 @@ public:
 
   virtual void GetMozMemory(JSContext *aCx,
                             JS::MutableHandle<JSObject*> aObj) = 0;
 
   virtual nsDOMNavigationTiming* GetDOMTiming() const = 0;
 
   virtual nsITimedChannel* GetChannel() const = 0;
 
+  void MemoryPressure();
+
+  size_t SizeOfUserEntries(mozilla::MallocSizeOf aMallocSizeOf) const;
+  size_t SizeOfResourceEntries(mozilla::MallocSizeOf aMallocSizeOf) const;
+
 protected:
   Performance();
   explicit Performance(nsPIDOMWindowInner* aWindow);
 
   virtual ~Performance();
 
   virtual void InsertUserEntry(PerformanceEntry* aEntry);
   void InsertResourceEntry(PerformanceEntry* aEntry);
 
   void ClearUserEntries(const Optional<nsAString>& aEntryName,
                         const nsAString& aEntryType);
 
   DOMHighResTimeStamp ResolveTimestampFromName(const nsAString& aName,
                                                ErrorResult& aRv);
 
-  virtual nsISupports* GetAsISupports() = 0;
-
   virtual void DispatchBufferFullEvent() = 0;
 
   virtual TimeStamp CreationTimeStamp() const = 0;
 
   virtual DOMHighResTimeStamp CreationTime() const = 0;
 
   virtual bool IsPerformanceTimingAttribute(const nsAString& aName)
   {
--- a/dom/performance/PerformanceEntry.cpp
+++ b/dom/performance/PerformanceEntry.cpp
@@ -36,8 +36,21 @@ PerformanceEntry::~PerformanceEntry()
 {
 }
 
 JSObject*
 PerformanceEntry::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return mozilla::dom::PerformanceEntryBinding::Wrap(aCx, this, aGivenProto);
 }
+
+size_t
+PerformanceEntry::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+  return mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
+         mEntryType.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+}
+
+size_t
+PerformanceEntry::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
--- a/dom/performance/PerformanceEntry.h
+++ b/dom/performance/PerformanceEntry.h
@@ -79,17 +79,21 @@ public:
     return 0;
   }
 
   virtual const PerformanceResourceTiming* ToResourceTiming() const
   {
     return nullptr;
   }
 
+  virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
 protected:
+  virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
   nsCOMPtr<nsISupports> mParent;
   nsString mName;
   nsString mEntryType;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/performance/PerformanceMainThread.h
+++ b/dom/performance/PerformanceMainThread.h
@@ -45,21 +45,16 @@ public:
   virtual nsITimedChannel* GetChannel() const override
   {
     return mChannel;
   }
 
 protected:
   ~PerformanceMainThread();
 
-  nsISupports* GetAsISupports() override
-  {
-    return this;
-  }
-
   void InsertUserEntry(PerformanceEntry* aEntry) override;
 
   bool IsPerformanceTimingAttribute(const nsAString& aName) override;
 
   DOMHighResTimeStamp
   GetPerformanceTimingFromString(const nsAString& aTimingName) override;
 
   void DispatchBufferFullEvent() override;
--- a/dom/performance/PerformanceMark.cpp
+++ b/dom/performance/PerformanceMark.cpp
@@ -24,8 +24,14 @@ PerformanceMark::~PerformanceMark()
 {
 }
 
 JSObject*
 PerformanceMark::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return PerformanceMarkBinding::Wrap(aCx, this, aGivenProto);
 }
+
+size_t
+PerformanceMark::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
--- a/dom/performance/PerformanceMark.h
+++ b/dom/performance/PerformanceMark.h
@@ -22,16 +22,18 @@ public:
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   virtual DOMHighResTimeStamp StartTime() const override
   {
     return mStartTime;
   }
 
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
+
 protected:
   virtual ~PerformanceMark();
   DOMHighResTimeStamp mStartTime;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/performance/PerformanceMeasure.cpp
+++ b/dom/performance/PerformanceMeasure.cpp
@@ -26,8 +26,14 @@ PerformanceMeasure::~PerformanceMeasure(
 {
 }
 
 JSObject*
 PerformanceMeasure::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return PerformanceMeasureBinding::Wrap(aCx, this, aGivenProto);
 }
+
+size_t
+PerformanceMeasure::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
--- a/dom/performance/PerformanceMeasure.h
+++ b/dom/performance/PerformanceMeasure.h
@@ -28,16 +28,18 @@ public:
     return mStartTime;
   }
 
   virtual DOMHighResTimeStamp Duration() const override
   {
     return mDuration;
   }
 
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
+
 protected:
   virtual ~PerformanceMeasure();
   DOMHighResTimeStamp mStartTime;
   DOMHighResTimeStamp mDuration;
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/performance/PerformanceResourceTiming.cpp
+++ b/dom/performance/PerformanceResourceTiming.cpp
@@ -21,17 +21,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_
 NS_INTERFACE_MAP_END_INHERITING(PerformanceEntry)
 
 NS_IMPL_ADDREF_INHERITED(PerformanceResourceTiming, PerformanceEntry)
 NS_IMPL_RELEASE_INHERITED(PerformanceResourceTiming, PerformanceEntry)
 
 PerformanceResourceTiming::PerformanceResourceTiming(PerformanceTiming* aPerformanceTiming,
                                                      Performance* aPerformance,
                                                      const nsAString& aName)
-: PerformanceEntry(aPerformance, aName, NS_LITERAL_STRING("resource")),
+: PerformanceEntry(aPerformance->GetParentObject(), aName, NS_LITERAL_STRING("resource")),
   mTiming(aPerformanceTiming),
   mEncodedBodySize(0),
   mTransferSize(0),
   mDecodedBodySize(0)
 {
   MOZ_ASSERT(aPerformance, "Parent performance object should be provided");
 }
 
@@ -46,8 +46,22 @@ PerformanceResourceTiming::StartTime() c
   return startTime ? startTime : mTiming->FetchStartHighRes();
 }
 
 JSObject*
 PerformanceResourceTiming::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return PerformanceResourceTimingBinding::Wrap(aCx, this, aGivenProto);
 }
+
+size_t
+PerformanceResourceTiming::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
+size_t
+PerformanceResourceTiming::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+  return PerformanceEntry::SizeOfExcludingThis(aMallocSizeOf) +
+         mInitiatorType.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
+         mNextHopProtocol.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+}
--- a/dom/performance/PerformanceResourceTiming.h
+++ b/dom/performance/PerformanceResourceTiming.h
@@ -163,19 +163,25 @@ public:
     mTransferSize = aTransferSize;
   }
 
   void SetDecodedBodySize(uint64_t aDecodedBodySize)
   {
     mDecodedBodySize = aDecodedBodySize;
   }
 
+  size_t
+  SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
+
 protected:
   virtual ~PerformanceResourceTiming();
 
+  size_t
+  SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
+
   nsString mInitiatorType;
   nsString mNextHopProtocol;
   RefPtr<PerformanceTiming> mTiming;
   uint64_t mEncodedBodySize;
   uint64_t mTransferSize;
   uint64_t mDecodedBodySize;
 };
 
--- a/dom/performance/PerformanceWorker.h
+++ b/dom/performance/PerformanceWorker.h
@@ -59,21 +59,16 @@ public:
   {
     MOZ_CRASH("This should not be called on workers.");
     return nullptr;
   }
 
 protected:
   ~PerformanceWorker();
 
-  nsISupports* GetAsISupports() override
-  {
-    return nullptr;
-  }
-
   void InsertUserEntry(PerformanceEntry* aEntry) override;
 
   void DispatchBufferFullEvent() override
   {
     MOZ_CRASH("This should not be called on workers.");
   }
 
 private:
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -2421,30 +2421,38 @@ private:
         workerPrivate->DispatchToMainThread(mFinishCollectRunnable.forget()));
     }
   };
 
   class FinishCollectRunnable final : public Runnable
   {
     nsCOMPtr<nsIHandleReportCallback> mHandleReport;
     nsCOMPtr<nsISupports> mHandlerData;
+    size_t mPerformanceUserEntries;
+    size_t mPerformanceResourceEntries;
     const bool mAnonymize;
     bool mSuccess;
 
   public:
     WorkerJSContextStats mCxStats;
 
     explicit FinishCollectRunnable(
       nsIHandleReportCallback* aHandleReport,
       nsISupports* aHandlerData,
       bool aAnonymize,
       const nsACString& aPath);
 
     NS_IMETHOD Run() override;
 
+    void SetPerformanceSizes(size_t userEntries, size_t resourceEntries)
+    {
+      mPerformanceUserEntries = userEntries;
+      mPerformanceResourceEntries = resourceEntries;
+    }
+
     void SetSuccess(bool success)
     {
       mSuccess = success;
     }
 
   private:
     ~FinishCollectRunnable()
     {
@@ -2585,31 +2593,42 @@ WorkerPrivate::MemoryReporter::CollectRe
 { }
 
 bool
 WorkerPrivate::MemoryReporter::CollectReportsRunnable::WorkerRun(JSContext* aCx,
                                                                  WorkerPrivate* aWorkerPrivate)
 {
   aWorkerPrivate->AssertIsOnWorkerThread();
 
+  RefPtr<Performance> performance =
+    aWorkerPrivate->GlobalScope()->GetPerformanceIfExists();
+  if (performance) {
+    size_t userEntries = performance->SizeOfUserEntries(JsWorkerMallocSizeOf);
+    size_t resourceEntries =
+      performance->SizeOfResourceEntries(JsWorkerMallocSizeOf);
+    mFinishCollectRunnable->SetPerformanceSizes(userEntries, resourceEntries);
+  }
+
   mFinishCollectRunnable->SetSuccess(
     aWorkerPrivate->CollectRuntimeStats(&mFinishCollectRunnable->mCxStats, mAnonymize));
 
   return true;
 }
 
 WorkerPrivate::MemoryReporter::FinishCollectRunnable::FinishCollectRunnable(
   nsIHandleReportCallback* aHandleReport,
   nsISupports* aHandlerData,
   bool aAnonymize,
   const nsACString& aPath)
   : mozilla::Runnable(
       "dom::workers::WorkerPrivate::MemoryReporter::FinishCollectRunnable")
   , mHandleReport(aHandleReport)
   , mHandlerData(aHandlerData)
+  , mPerformanceUserEntries(0)
+  , mPerformanceResourceEntries(0)
   , mAnonymize(aAnonymize)
   , mSuccess(false)
   , mCxStats(aPath)
 { }
 
 NS_IMETHODIMP
 WorkerPrivate::MemoryReporter::FinishCollectRunnable::Run()
 {
@@ -2620,16 +2639,38 @@ WorkerPrivate::MemoryReporter::FinishCol
 
   if (!manager)
     return NS_OK;
 
   if (mSuccess) {
     xpc::ReportJSRuntimeExplicitTreeStats(mCxStats, mCxStats.Path(),
                                           mHandleReport, mHandlerData,
                                           mAnonymize);
+
+    if (mPerformanceUserEntries) {
+      nsCString path = mCxStats.Path();
+      path.AppendLiteral("dom/performance/user-entries");
+      mHandleReport->Callback(EmptyCString(), path,
+                              nsIMemoryReporter::KIND_HEAP,
+                              nsIMemoryReporter::UNITS_BYTES,
+                              mPerformanceUserEntries,
+                              NS_LITERAL_CSTRING("Memory used for performance user entries."),
+                              mHandlerData);
+    }
+
+    if (mPerformanceResourceEntries) {
+      nsCString path = mCxStats.Path();
+      path.AppendLiteral("dom/performance/resource-entries");
+      mHandleReport->Callback(EmptyCString(), path,
+                              nsIMemoryReporter::KIND_HEAP,
+                              nsIMemoryReporter::UNITS_BYTES,
+                              mPerformanceResourceEntries,
+                              NS_LITERAL_CSTRING("Memory used for performance resource entries."),
+                              mHandlerData);
+    }
   }
 
   manager->EndReport();
 
   return NS_OK;
 }
 
 WorkerPrivate::SyncLoopInfo::SyncLoopInfo(EventTarget* aEventTarget)
@@ -6775,24 +6816,33 @@ WorkerPrivate::CycleCollectInternal(bool
   }
 }
 
 void
 WorkerPrivate::MemoryPressureInternal()
 {
   AssertIsOnWorkerThread();
 
-  RefPtr<Console> console = mScope ? mScope->GetConsoleIfExists() : nullptr;
-  if (console) {
-    console->ClearStorage();
-  }
-
-  console = mDebuggerScope ? mDebuggerScope->GetConsoleIfExists() : nullptr;
-  if (console) {
-    console->ClearStorage();
+  if (mScope) {
+    RefPtr<Console> console = mScope->GetConsoleIfExists();
+    if (console) {
+      console->ClearStorage();
+    }
+
+    RefPtr<Performance> performance = mScope->GetPerformanceIfExists();
+    if (performance) {
+      performance->MemoryPressure();
+    }
+  }
+
+  if (mDebuggerScope) {
+    RefPtr<Console> console = mDebuggerScope->GetConsoleIfExists();
+    if (console) {
+      console->ClearStorage();
+    }
   }
 
   for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
     mChildWorkers[index]->MemoryPressure(false);
   }
 }
 
 void
--- a/dom/workers/WorkerScope.h
+++ b/dom/workers/WorkerScope.h
@@ -151,16 +151,21 @@ public:
   IMPL_EVENT_HANDLER(online)
   IMPL_EVENT_HANDLER(offline)
 
   void
   Dump(const Optional<nsAString>& aString) const;
 
   Performance* GetPerformance();
 
+  Performance* GetPerformanceIfExists() const
+  {
+    return mPerformance;
+  }
+
   already_AddRefed<Promise>
   Fetch(const RequestOrUSVString& aInput, const RequestInit& aInit,
         CallerType aCallerType, ErrorResult& aRv);
 
   already_AddRefed<IDBFactory>
   GetIndexedDB(ErrorResult& aErrorResult);
 
   already_AddRefed<cache::CacheStorage>
--- a/extensions/spellcheck/locales/en-US/hunspell/en-US.dic
+++ b/extensions/spellcheck/locales/en-US/hunspell/en-US.dic
@@ -1,9 +1,9 @@
-52356
+52358
 0/nm
 0th/pt
 1/n1
 1st/p
 1th/tc
 2/nm
 2nd/p
 2th/tc
@@ -23903,16 +23903,18 @@ dyslexia/M
 dyslexic/SM
 dyspepsia/M
 dyspeptic/MS
 dysphagia
 dysphoria
 dysphoric
 dysprosium/M
 dystonia
+dystopia/S
+dystopian/S
 dz
 débridement
 débutante/SM
 décolletage/SM
 décolleté
 démodé
 dérailleur/MS
 déshabillé/M
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -1640,16 +1640,18 @@ public:
 #endif
 
 #ifdef MOZ_ENABLE_FREETYPE
   static void SetFTLibrary(FT_Library aFTLibrary);
   static FT_Library GetFTLibrary();
 
   static FT_Library NewFTLibrary();
   static void ReleaseFTLibrary(FT_Library aFTLibrary);
+  static void LockFTLibrary(FT_Library aFTLibrary);
+  static void UnlockFTLibrary(FT_Library aFTLibrary);
 
   static FT_Face NewFTFace(FT_Library aFTLibrary, const char* aFileName, int aFaceIndex);
   static FT_Face NewFTFaceFromData(FT_Library aFTLibrary, const uint8_t* aData, size_t aDataSize, int aFaceIndex);
   static void ReleaseFTFace(FT_Face aFace);
 
 private:
   static FT_Library mFTLibrary;
   static Mutex* mFTLock;
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -181,16 +181,28 @@ mozilla_NewFTFaceFromData(FT_Library aFT
 }
 
 void
 mozilla_ReleaseFTFace(FT_Face aFace)
 {
   mozilla::gfx::Factory::ReleaseFTFace(aFace);
 }
 
+void
+mozilla_LockFTLibrary(FT_Library aFTLibrary)
+{
+  mozilla::gfx::Factory::LockFTLibrary(aFTLibrary);
+}
+
+void
+mozilla_UnlockFTLibrary(FT_Library aFTLibrary)
+{
+  mozilla::gfx::Factory::UnlockFTLibrary(aFTLibrary);
+}
+
 }
 #endif
 
 namespace mozilla {
 namespace gfx {
 
 // In Gecko, this value is managed by gfx.logging.level in gfxPrefs.
 int32_t LoggingPrefs::sGfxLogLevel = LOG_DEFAULT;
@@ -662,16 +674,30 @@ Factory::NewFTLibrary()
 }
 
 void
 Factory::ReleaseFTLibrary(FT_Library aFTLibrary)
 {
   FT_Done_FreeType(aFTLibrary);
 }
 
+void
+Factory::LockFTLibrary(FT_Library aFTLibrary)
+{
+  MOZ_ASSERT(mFTLock);
+  mFTLock->Lock();
+}
+
+void
+Factory::UnlockFTLibrary(FT_Library aFTLibrary)
+{
+  MOZ_ASSERT(mFTLock);
+  mFTLock->Unlock();
+}
+
 FT_Face
 Factory::NewFTFace(FT_Library aFTLibrary, const char* aFileName, int aFaceIndex)
 {
   MOZ_ASSERT(mFTLock);
   MutexAutoLock lock(*mFTLock);
   if (!aFTLibrary) {
     aFTLibrary = mFTLibrary;
   }
--- a/gfx/cairo/cairo/src/cairo-ft-font.c
+++ b/gfx/cairo/cairo/src/cairo-ft-font.c
@@ -102,16 +102,18 @@ static setLcdFilterFunc setLcdFilter;
 #define MAX_OPEN_FACES 10
 /* This is the maximum font size we allow to be passed to FT_Set_Char_Size
  */
 #define MAX_FONT_SIZE 1000
 
 extern FT_Face mozilla_NewFTFace(FT_Library aFTLibrary, const char* aFileName, int aFaceIndex);
 extern FT_Face mozilla_NewFTFaceFromData(FT_Library aFTLibrary, const uint8_t* aData, size_t aDataSize, int aFaceIndex);
 extern void mozilla_ReleaseFTFace(FT_Face aFace);
+extern void mozilla_LockFTLibrary(FT_Library aFTLibrary);
+extern void mozilla_UnlockFTLibrary(FT_Library aFTLibrary);
 
 /**
  * SECTION:cairo-ft
  * @Title: FreeType Fonts
  * @Short_Description: Font support for FreeType
  * @See_Also: #cairo_font_face_t
  *
  * The FreeType font backend is primarily used to render text on GNU/Linux
@@ -1448,23 +1450,31 @@ static cairo_status_t
           initialized_setLcdFilter = 1;
 #ifdef HAVE_FT_LIBRARY_SETLCDFILTER
 	  setLcdFilter = &FT_Library_SetLcdFilter;
 #else
           setLcdFilter = (setLcdFilterFunc) dlsym(RTLD_DEFAULT, "FT_Library_SetLcdFilter");
 #endif
         }
 
-	if (setLcdFilter)
-          setLcdFilter (library, lcd_filter);
+	if (setLcdFilter &&
+	    (render_mode == FT_RENDER_MODE_LCD ||
+	     render_mode == FT_RENDER_MODE_LCD_V)) {
+	    mozilla_LockFTLibrary (library);
+	    setLcdFilter (library, lcd_filter);
+	}
 
 	fterror = FT_Render_Glyph (face->glyph, render_mode);
 
-	if (setLcdFilter)
-          setLcdFilter (library, FT_LCD_FILTER_NONE);
+	if (setLcdFilter &&
+	    (render_mode == FT_RENDER_MODE_LCD ||
+	     render_mode == FT_RENDER_MODE_LCD_V)) {
+	    setLcdFilter (library, FT_LCD_FILTER_NONE);
+	    mozilla_UnlockFTLibrary (library);
+	}
 
 	if (fterror != 0)
 		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
 	bitmap_size = _compute_xrender_bitmap_size (&bitmap,
 						    face->glyph,
 						    render_mode);
 	if (bitmap_size < 0)
@@ -2213,17 +2223,17 @@ static cairo_int_status_t
 _cairo_ft_scaled_glyph_init (void			*abstract_font,
 			     cairo_scaled_glyph_t	*scaled_glyph,
 			     cairo_scaled_glyph_info_t	 info)
 {
     cairo_text_extents_t    fs_metrics;
     cairo_ft_scaled_font_t *scaled_font = abstract_font;
     cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled;
     FT_GlyphSlot glyph;
-    FT_Face face;
+    FT_Face face = NULL;
     FT_Error error;
     int load_flags = scaled_font->ft_options.load_flags;
     FT_Glyph_Metrics *metrics;
     double x_factor, y_factor;
     cairo_bool_t vertical_layout = FALSE;
     cairo_status_t status;
 
     face = _cairo_ft_unscaled_font_lock_face (unscaled);
@@ -3271,17 +3281,18 @@ cairo_ft_scaled_font_lock_face (cairo_sc
     }
 
     /* Note: We deliberately release the unscaled font's mutex here,
      * so that we are not holding a lock across two separate calls to
      * cairo function, (which would give the application some
      * opportunity for creating deadlock. This is obviously unsafe,
      * but as documented, the user must add manual locking when using
      * this function. */
-     CAIRO_MUTEX_UNLOCK (scaled_font->unscaled->mutex);
+    // BEWARE: Mozilla's tree Cairo keeps the lock across calls for thread-safety.
+    // CAIRO_MUTEX_UNLOCK (scaled_font->unscaled->mutex);
 
     return face;
 }
 
 /**
  * cairo_ft_scaled_font_unlock_face:
  * @scaled_font: A #cairo_scaled_font_t from the FreeType font backend. Such an
  *   object can be created by calling cairo_scaled_font_create() on a
@@ -3302,17 +3313,18 @@ cairo_ft_scaled_font_unlock_face (cairo_
 
     if (scaled_font->base.status)
 	return;
 
     /* Note: We released the unscaled font's mutex at the end of
      * cairo_ft_scaled_font_lock_face, so we have to acquire it again
      * as _cairo_ft_unscaled_font_unlock_face expects it to be held
      * when we call into it. */
-    CAIRO_MUTEX_LOCK (scaled_font->unscaled->mutex);
+    // BEWARE: Mozilla's tree Cairo keeps the lock across calls for thread-safety.
+    //CAIRO_MUTEX_LOCK (scaled_font->unscaled->mutex);
 
     _cairo_ft_unscaled_font_unlock_face (scaled_font->unscaled);
 }
 
 /* We expose our unscaled font implementation internally for the the
  * PDF backend, which needs to keep track of the the different
  * fonts-on-disk used by a document, so it can embed them.
  */
--- a/gfx/ots/LICENSE
+++ b/gfx/ots/LICENSE
@@ -1,27 +1,27 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//    * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//    * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//    * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+   * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--- a/gfx/ots/README.mozilla
+++ b/gfx/ots/README.mozilla
@@ -1,11 +1,12 @@
 This is the Sanitiser for OpenType project, from http://code.google.com/p/ots/.
 
 Our reference repository is https://github.com/khaledhosny/ots/.
 
-Current revision: ba8417620956a920ed1f05a2f666fb6317fb10cb (5.1.0)
+Current revision: 5f685b8e1fce77347c87f6d98511d53debbe64b2 (5.2.0)
 
 Upstream files included: LICENSE, src/, include/, tests/*.cc
 
 Additional files: README.mozilla, src/moz.build
 
 Additional patch: ots-visibility.patch (bug 711079).
+Additional patch: ots-config.patch (config.h not needed in mozilla build)
--- a/gfx/ots/include/opentype-sanitiser.h
+++ b/gfx/ots/include/opentype-sanitiser.h
@@ -1,9 +1,9 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef OPENTYPE_SANITISER_H_
 #define OPENTYPE_SANITISER_H_
 
 #if defined(_WIN32) || defined(__CYGWIN__)
   #define OTS_DLL_IMPORT __declspec(dllimport)
@@ -47,17 +47,17 @@ typedef unsigned __int64 uint64_t;
 #include <sys/types.h>
 
 #include <algorithm>
 #include <cassert>
 #include <cstddef>
 #include <cstring>
 
 #define OTS_TAG(c1,c2,c3,c4) ((uint32_t)((((uint8_t)(c1))<<24)|(((uint8_t)(c2))<<16)|(((uint8_t)(c3))<<8)|((uint8_t)(c4))))
-#define OTS_UNTAG(tag)       ((uint8_t)((tag)>>24)), ((uint8_t)((tag)>>16)), ((uint8_t)((tag)>>8)), ((uint8_t)(tag))
+#define OTS_UNTAG(tag)       ((char)((tag)>>24)), ((char)((tag)>>16)), ((char)((tag)>>8)), ((char)(tag))
 
 namespace ots {
 
 // -----------------------------------------------------------------------------
 // This is an interface for an abstract stream class which is used for writing
 // the serialised results out.
 // -----------------------------------------------------------------------------
 class OTSStream {
@@ -181,17 +181,17 @@ enum TableAction {
   TABLE_ACTION_DROP      // Drop the table
 };
 
 class OTS_API OTSContext {
   public:
     OTSContext() {}
     virtual ~OTSContext() {}
 
-    // Process a given OpenType file and write out a sanitised version
+    // Process a given OpenType file and write out a sanitized version
     //   output: a pointer to an object implementing the OTSStream interface. The
     //     sanitisied output will be written to this. In the even of a failure,
     //     partial output may have been written.
     //   input: the OpenType file
     //   length: the size, in bytes, of |input|
     //   index: if the input is a font collection and index is specified, then
     //     the corresponding font will be returned, otherwise the whole
     //     collection. Ignored for non-collection fonts.
--- a/gfx/ots/include/ots-memory-stream.h
+++ b/gfx/ots/include/ots-memory-stream.h
@@ -1,9 +1,9 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef OTS_MEMORY_STREAM_H_
 #define OTS_MEMORY_STREAM_H_
 
 #include <cstring>
 #include <limits>
new file mode 100644
--- /dev/null
+++ b/gfx/ots/ots-config.patch
@@ -0,0 +1,22 @@
+diff --git a/gfx/ots/src/ots.h b/gfx/ots/src/ots.h
+--- a/gfx/ots/src/ots.h
++++ b/gfx/ots/src/ots.h
+@@ -1,16 +1,17 @@
+ // Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
+ // Use of this source code is governed by a BSD-style license that can be
+ // found in the LICENSE file.
+ 
+ #ifndef OTS_H_
+ #define OTS_H_
+ 
+-#include "config.h"
++// Not needed in the gecko build
++// #include "config.h"
+ 
+ #include <stddef.h>
+ #include <cstdarg>
+ #include <cstddef>
+ #include <cstdio>
+ #include <cstdlib>
+ #include <cstring>
+ #include <limits>
--- a/gfx/ots/ots-visibility.patch
+++ b/gfx/ots/ots-visibility.patch
@@ -47,12 +47,12 @@ diff --git a/gfx/ots/include/opentype-sa
  };
  
 -class OTSContext {
 +class OTS_API OTSContext {
    public:
      OTSContext() {}
      virtual ~OTSContext() {}
  
-     // Process a given OpenType file and write out a sanitised version
+     // Process a given OpenType file and write out a sanitized version
      //   output: a pointer to an object implementing the OTSStream interface. The
      //     sanitisied output will be written to this. In the even of a failure,
      //     partial output may have been written.
--- a/gfx/ots/src/cff.cc
+++ b/gfx/ots/src/cff.cc
@@ -1,9 +1,9 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "cff.h"
 
 #include <cstring>
 #include <utility>
 #include <vector>
@@ -899,24 +899,23 @@ bool ParseDictData(const uint8_t *data, 
   }
   return true;
 }
 
 }  // namespace
 
 namespace ots {
 
-bool ots_cff_parse(Font *font, const uint8_t *data, size_t length) {
+bool OpenTypeCFF::Parse(const uint8_t *data, size_t length) {
   Buffer table(data, length);
 
-  font->cff = new OpenTypeCFF;
-  font->cff->data = data;
-  font->cff->length = length;
-  font->cff->font_dict_length = 0;
-  font->cff->local_subrs = NULL;
+  Font *font = GetFont();
+
+  this->m_data = data;
+  this->m_length = length;
 
   // parse "6. Header" in the Adobe Compact Font Format Specification
   uint8_t major = 0;
   uint8_t minor = 0;
   uint8_t hdr_size = 0;
   uint8_t off_size = 0;
   if (!table.ReadU8(&major)) {
     return OTS_FAILURE();
@@ -944,17 +943,17 @@ bool ots_cff_parse(Font *font, const uin
   }
 
   // parse "7. Name INDEX"
   table.set_offset(hdr_size);
   CFFIndex name_index;
   if (!ParseIndex(&table, &name_index)) {
     return OTS_FAILURE();
   }
-  if (!ParseNameData(&table, name_index, &(font->cff->name))) {
+  if (!ParseNameData(&table, name_index, &(this->name))) {
     return OTS_FAILURE();
   }
 
   // parse "8. Top DICT INDEX"
   table.set_offset(name_index.offset_to_next);
   CFFIndex top_dict_index;
   if (!ParseIndex(&table, &top_dict_index)) {
     return OTS_FAILURE();
@@ -968,85 +967,76 @@ bool ots_cff_parse(Font *font, const uin
   CFFIndex string_index;
   if (!ParseIndex(&table, &string_index)) {
     return OTS_FAILURE();
   }
   if (string_index.count >= 65000 - kNStdString) {
     return OTS_FAILURE();
   }
 
-  const uint16_t num_glyphs = font->maxp->num_glyphs;
+  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
+    GetFont()->GetTypedTable(OTS_TAG_MAXP));
+  if (!maxp) {
+    return Error("Required maxp table missing");
+  }
+  const uint16_t num_glyphs = maxp->num_glyphs;
   const size_t sid_max = string_index.count + kNStdString;
   // string_index.count == 0 is allowed.
 
   // parse "9. Top DICT Data"
   if (!ParseDictData(data, length, top_dict_index,
                      num_glyphs, sid_max,
-                     DICT_DATA_TOPLEVEL, font->cff)) {
+                     DICT_DATA_TOPLEVEL, this)) {
     return OTS_FAILURE();
   }
 
   // parse "16. Global Subrs INDEX"
   table.set_offset(string_index.offset_to_next);
   CFFIndex global_subrs_index;
   if (!ParseIndex(&table, &global_subrs_index)) {
     return OTS_FAILURE();
   }
 
   // Check if all fd_index in FDSelect are valid.
   std::map<uint16_t, uint8_t>::const_iterator iter;
-  std::map<uint16_t, uint8_t>::const_iterator end = font->cff->fd_select.end();
-  for (iter = font->cff->fd_select.begin(); iter != end; ++iter) {
-    if (iter->second >= font->cff->font_dict_length) {
+  std::map<uint16_t, uint8_t>::const_iterator end = this->fd_select.end();
+  for (iter = this->fd_select.begin(); iter != end; ++iter) {
+    if (iter->second >= this->font_dict_length) {
       return OTS_FAILURE();
     }
   }
 
   // Check if all charstrings (font hinting code for each glyph) are valid.
-  for (size_t i = 0; i < font->cff->char_strings_array.size(); ++i) {
+  for (size_t i = 0; i < this->char_strings_array.size(); ++i) {
     if (!ValidateType2CharStringIndex(font,
-                                      *(font->cff->char_strings_array.at(i)),
+                                      *(this->char_strings_array.at(i)),
                                       global_subrs_index,
-                                      font->cff->fd_select,
-                                      font->cff->local_subrs_per_font,
-                                      font->cff->local_subrs,
+                                      this->fd_select,
+                                      this->local_subrs_per_font,
+                                      this->local_subrs,
                                       &table)) {
-      return OTS_FAILURE_MSG("Failed validating charstring set %d", (int) i);
+      return Error("Failed validating charstring set %d", (int) i);
     }
   }
 
   return true;
 }
 
-bool ots_cff_should_serialise(Font *font) {
-  return font->cff != NULL;
-}
-
-bool ots_cff_serialise(OTSStream *out, Font *font) {
-  // TODO(yusukes): would be better to transcode the data,
-  //                rather than simple memcpy.
-  if (!out->Write(font->cff->data, font->cff->length)) {
-    return OTS_FAILURE();
+bool OpenTypeCFF::Serialize(OTSStream *out) {
+  if (!out->Write(this->m_data, this->m_length)) {
+    return Error("Failed to write table");
   }
   return true;
 }
 
-void ots_cff_reuse(Font *font, Font *other) {
-  font->cff = other->cff;
-  font->cff_reused = true;
-}
-
-void ots_cff_free(Font *font) {
-  if (font->cff) {
-    for (size_t i = 0; i < font->cff->char_strings_array.size(); ++i) {
-      delete (font->cff->char_strings_array)[i];
-    }
-    for (size_t i = 0; i < font->cff->local_subrs_per_font.size(); ++i) {
-      delete (font->cff->local_subrs_per_font)[i];
-    }
-    delete font->cff->local_subrs;
-    delete font->cff;
+OpenTypeCFF::~OpenTypeCFF() {
+  for (size_t i = 0; i < this->char_strings_array.size(); ++i) {
+    delete (this->char_strings_array)[i];
   }
+  for (size_t i = 0; i < this->local_subrs_per_font.size(); ++i) {
+    delete (this->local_subrs_per_font)[i];
+  }
+  delete this->local_subrs;
 }
 
 }  // namespace ots
 
 #undef TABLE_NAME
--- a/gfx/ots/src/cff.h
+++ b/gfx/ots/src/cff.h
@@ -1,9 +1,9 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef OTS_CFF_H_
 #define OTS_CFF_H_
 
 #include "ots.h"
 
@@ -17,30 +17,46 @@ struct CFFIndex {
   CFFIndex()
       : count(0), off_size(0), offset_to_next(0) {}
   uint16_t count;
   uint8_t off_size;
   std::vector<uint32_t> offsets;
   uint32_t offset_to_next;
 };
 
-struct OpenTypeCFF {
-  const uint8_t *data;
-  size_t length;
+class OpenTypeCFF : public Table {
+ public:
+  explicit OpenTypeCFF(Font *font, uint32_t tag)
+      : Table(font, tag, tag),
+        font_dict_length(0),
+        local_subrs(NULL),
+        m_data(NULL),
+        m_length(0) {
+  }
+
+  ~OpenTypeCFF();
+
+  bool Parse(const uint8_t *data, size_t length);
+  bool Serialize(OTSStream *out);
+
   // Name INDEX. This name is used in name.cc as a postscript font name.
   std::string name;
 
   // The number of fonts the file has.
   size_t font_dict_length;
   // A map from glyph # to font #.
   std::map<uint16_t, uint8_t> fd_select;
 
   // A list of char strings.
   std::vector<CFFIndex *> char_strings_array;
   // A list of Local Subrs associated with FDArrays. Can be empty.
   std::vector<CFFIndex *> local_subrs_per_font;
   // A Local Subrs associated with Top DICT. Can be NULL.
   CFFIndex *local_subrs;
+
+ private:
+  const uint8_t *m_data;
+  size_t m_length;
 };
 
 }  // namespace ots
 
 #endif  // OTS_CFF_H_
--- a/gfx/ots/src/cff_type2_charstring.cc
+++ b/gfx/ots/src/cff_type2_charstring.cc
@@ -1,9 +1,9 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 // A parser for the Type 2 Charstring Format.
 // http://www.adobe.com/devnet/font/pdfs/5177.Type2.pdf
 
 #include "cff_type2_charstring.h"
 
--- a/gfx/ots/src/cff_type2_charstring.h
+++ b/gfx/ots/src/cff_type2_charstring.h
@@ -1,9 +1,9 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef OTS_CFF_TYPE2_CHARSTRING_H_
 #define OTS_CFF_TYPE2_CHARSTRING_H_
 
 #include "cff.h"
 #include "ots.h"
--- a/gfx/ots/src/cmap.cc
+++ b/gfx/ots/src/cmap.cc
@@ -1,27 +1,25 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "cmap.h"
 
 #include <algorithm>
 #include <set>
 #include <utility>
 #include <vector>
 
 #include "maxp.h"
 #include "os2.h"
 
 // cmap - Character To Glyph Index Mapping Table
 // http://www.microsoft.com/typography/otspec/cmap.htm
 
-#define TABLE_NAME "cmap"
-
 namespace {
 
 struct CMAPSubtableHeader {
   uint16_t platform;
   uint16_t encoding;
   uint32_t offset;
   uint16_t format;
   uint32_t length;
@@ -51,607 +49,607 @@ const uint32_t kMaxCMAPSelectorRecords =
 const uint32_t kMongolianVSStart = 0x180B;
 const uint32_t kMongolianVSEnd = 0x180D;
 const uint32_t kVSStart = 0xFE00;
 const uint32_t kVSEnd = 0xFE0F;
 const uint32_t kIVSStart = 0xE0100;
 const uint32_t kIVSEnd = 0xE01EF;
 const uint32_t kUVSUpperLimit = 0xFFFFFF;
 
+} // namespace
+
+namespace ots {
+
 // Parses Format 4 tables
-bool ParseFormat4(ots::Font *font, int platform, int encoding,
+bool OpenTypeCMAP::ParseFormat4(int platform, int encoding,
               const uint8_t *data, size_t length, uint16_t num_glyphs) {
   ots::Buffer subtable(data, length);
 
   // 0.3.4, 3.0.4 or 3.1.4 subtables are complex and, rather than expanding the
   // whole thing and recompacting it, we validate it and include it verbatim
   // in the output.
 
-  if (!font->os2) {
-    return OTS_FAILURE_MSG("Required OS/2 table missing");
+  OpenTypeOS2 *os2 = static_cast<OpenTypeOS2*>(
+      GetFont()->GetTypedTable(OTS_TAG_OS2));
+  if (!os2) {
+    return Error("Required OS/2 table missing");
   }
 
   if (!subtable.Skip(4)) {
-    return OTS_FAILURE_MSG("Can't read 4 bytes at start of cmap format 4 subtable");
+    return Error("Can't read 4 bytes at start of cmap format 4 subtable");
   }
   uint16_t language = 0;
   if (!subtable.ReadU16(&language)) {
-    return OTS_FAILURE_MSG("Can't read language");
+    return Error("Can't read language");
   }
   if (language) {
     // Platform ID 3 (windows) subtables should have language '0'.
-    return OTS_FAILURE_MSG("Languages should be 0 (%d)", language);
+    return Error("Languages should be 0 (%d)", language);
   }
 
   uint16_t segcountx2, search_range, entry_selector, range_shift;
   segcountx2 = search_range = entry_selector = range_shift = 0;
   if (!subtable.ReadU16(&segcountx2) ||
       !subtable.ReadU16(&search_range) ||
       !subtable.ReadU16(&entry_selector) ||
       !subtable.ReadU16(&range_shift)) {
-    return OTS_FAILURE_MSG("Failed to read subcmap structure");
+    return Error("Failed to read subcmap structure");
   }
 
   if (segcountx2 & 1 || search_range & 1) {
-    return OTS_FAILURE_MSG("Bad subcmap structure");
+    return Error("Bad subcmap structure");
   }
   const uint16_t segcount = segcountx2 >> 1;
   // There must be at least one segment according the spec.
   if (segcount < 1) {
-    return OTS_FAILURE_MSG("Segcount < 1 (%d)", segcount);
+    return Error("Segcount < 1 (%d)", segcount);
   }
 
   // log2segcount is the maximal x s.t. 2^x < segcount
   unsigned log2segcount = 0;
   while (1u << (log2segcount + 1) <= segcount) {
     log2segcount++;
   }
 
   const uint16_t expected_search_range = 2 * 1u << log2segcount;
   if (expected_search_range != search_range) {
-    return OTS_FAILURE_MSG("expected search range != search range (%d != %d)", expected_search_range, search_range);
+    return Error("expected search range != search range (%d != %d)", expected_search_range, search_range);
   }
 
   if (entry_selector != log2segcount) {
-    return OTS_FAILURE_MSG("entry selector != log2(segement count) (%d != %d)", entry_selector, log2segcount);
+    return Error("entry selector != log2(segement count) (%d != %d)", entry_selector, log2segcount);
   }
 
   const uint16_t expected_range_shift = segcountx2 - search_range;
   if (range_shift != expected_range_shift) {
-    return OTS_FAILURE_MSG("unexpected range shift (%d != %d)", range_shift, expected_range_shift);
+    return Error("unexpected range shift (%d != %d)", range_shift, expected_range_shift);
   }
 
   std::vector<Subtable314Range> ranges(segcount);
 
   for (unsigned i = 0; i < segcount; ++i) {
     if (!subtable.ReadU16(&ranges[i].end_range)) {
-      return OTS_FAILURE_MSG("Failed to read segment %d", i);
+      return Error("Failed to read segment %d", i);
     }
   }
 
   uint16_t padding;
   if (!subtable.ReadU16(&padding)) {
-    return OTS_FAILURE_MSG("Failed to read cmap subtable segment padding");
+    return Error("Failed to read cmap subtable segment padding");
   }
   if (padding) {
-    return OTS_FAILURE_MSG("Non zero cmap subtable segment padding (%d)", padding);
+    return Error("Non zero cmap subtable segment padding (%d)", padding);
   }
 
   for (unsigned i = 0; i < segcount; ++i) {
     if (!subtable.ReadU16(&ranges[i].start_range)) {
-      return OTS_FAILURE_MSG("Failed to read segment start range %d", i);
+      return Error("Failed to read segment start range %d", i);
     }
   }
   for (unsigned i = 0; i < segcount; ++i) {
     if (!subtable.ReadS16(&ranges[i].id_delta)) {
-      return OTS_FAILURE_MSG("Failed to read segment delta %d", i);
+      return Error("Failed to read segment delta %d", i);
     }
   }
   for (unsigned i = 0; i < segcount; ++i) {
     ranges[i].id_range_offset_offset = subtable.offset();
     if (!subtable.ReadU16(&ranges[i].id_range_offset)) {
-      return OTS_FAILURE_MSG("Failed to read segment range offset %d", i);
+      return Error("Failed to read segment range offset %d", i);
     }
 
     if (ranges[i].id_range_offset & 1) {
       // Some font generators seem to put 65535 on id_range_offset
       // for 0xFFFF-0xFFFF range.
       // (e.g., many fonts in http://www.princexml.com/fonts/)
       if (i == segcount - 1u) {
-        OTS_WARNING("bad id_range_offset");
+        Warning("bad id_range_offset");
         ranges[i].id_range_offset = 0;
         // The id_range_offset value in the transcoded font will not change
         // since this table is not actually "transcoded" yet.
       } else {
-        return OTS_FAILURE_MSG("Bad segment offset (%d)", ranges[i].id_range_offset);
+        return Error("Bad segment offset (%d)", ranges[i].id_range_offset);
       }
     }
   }
 
   // ranges must be ascending order, based on the end_code. Ranges may not
   // overlap.
   for (unsigned i = 1; i < segcount; ++i) {
     if ((i == segcount - 1u) &&
         (ranges[i - 1].start_range == 0xffff) &&
         (ranges[i - 1].end_range == 0xffff) &&
         (ranges[i].start_range == 0xffff) &&
         (ranges[i].end_range == 0xffff)) {
       // Some fonts (e.g., Germania.ttf) have multiple 0xffff terminators.
       // We'll accept them as an exception.
-      OTS_WARNING("multiple 0xffff terminators found");
+      Warning("multiple 0xffff terminators found");
       continue;
     }
 
     // Note: some Linux fonts (e.g., LucidaSansOblique.ttf, bsmi00lp.ttf) have
     // unsorted table...
     if (ranges[i].end_range <= ranges[i - 1].end_range) {
-      return OTS_FAILURE_MSG("Out of order end range (%d <= %d)", ranges[i].end_range, ranges[i-1].end_range);
+      return Error("Out of order end range (%d <= %d)", ranges[i].end_range, ranges[i-1].end_range);
     }
     if (ranges[i].start_range <= ranges[i - 1].end_range) {
-      return OTS_FAILURE_MSG("out of order start range (%d <= %d)", ranges[i].start_range, ranges[i-1].end_range);
+      return Error("out of order start range (%d <= %d)", ranges[i].start_range, ranges[i-1].end_range);
     }
 
     // On many fonts, the value of {first, last}_char_index are incorrect.
     // Fix them.
-    if (font->os2->first_char_index != 0xFFFF &&
+    if (os2->table.first_char_index != 0xFFFF &&
         ranges[i].start_range != 0xFFFF &&
-        font->os2->first_char_index > ranges[i].start_range) {
-      font->os2->first_char_index = ranges[i].start_range;
+        os2->table.first_char_index > ranges[i].start_range) {
+      os2->table.first_char_index = ranges[i].start_range;
     }
-    if (font->os2->last_char_index != 0xFFFF &&
+    if (os2->table.last_char_index != 0xFFFF &&
         ranges[i].end_range != 0xFFFF &&
-        font->os2->last_char_index < ranges[i].end_range) {
-      font->os2->last_char_index = ranges[i].end_range;
+        os2->table.last_char_index < ranges[i].end_range) {
+      os2->table.last_char_index = ranges[i].end_range;
     }
   }
 
   // The last range must end at 0xffff
   if (ranges[segcount - 1].start_range != 0xffff || ranges[segcount - 1].end_range != 0xffff) {
-    return OTS_FAILURE_MSG("Final segment start and end must be 0xFFFF (0x%04X-0x%04X)",
+    return Error("Final segment start and end must be 0xFFFF (0x%04X-0x%04X)",
                            ranges[segcount - 1].start_range, ranges[segcount - 1].end_range);
   }
 
   // A format 4 CMAP subtable is complex. To be safe we simulate a lookup of
   // each code-point defined in the table and make sure that they are all valid
   // glyphs and that we don't access anything out-of-bounds.
   for (unsigned i = 0; i < segcount; ++i) {
     for (unsigned cp = ranges[i].start_range; cp <= ranges[i].end_range; ++cp) {
       const uint16_t code_point = static_cast<uint16_t>(cp);
       if (ranges[i].id_range_offset == 0) {
         // this is explictly allowed to overflow in the spec
         const uint16_t glyph = code_point + ranges[i].id_delta;
         if (glyph >= num_glyphs) {
-          return OTS_FAILURE_MSG("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1);
+          return Error("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1);
         }
       } else {
         const uint16_t range_delta = code_point - ranges[i].start_range;
         // this might seem odd, but it's true. The offset is relative to the
         // location of the offset value itself.
         const uint32_t glyph_id_offset = ranges[i].id_range_offset_offset +
                                          ranges[i].id_range_offset +
                                          range_delta * 2;
         // We need to be able to access a 16-bit value from this offset
         if (glyph_id_offset + 1 >= length) {
-          return OTS_FAILURE_MSG("bad glyph id offset (%d > %ld)", glyph_id_offset, length);
+          return Error("bad glyph id offset (%d > %ld)", glyph_id_offset, length);
         }
         uint16_t glyph;
         std::memcpy(&glyph, data + glyph_id_offset, 2);
         glyph = ntohs(glyph);
         if (glyph >= num_glyphs) {
-          return OTS_FAILURE_MSG("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1);
+          return Error("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1);
         }
       }
     }
   }
 
   // We accept the table.
   // TODO(yusukes): transcode the subtable.
   if (platform == 3 && encoding == 0) {
-    font->cmap->subtable_3_0_4_data = data;
-    font->cmap->subtable_3_0_4_length = length;
+    this->subtable_3_0_4_data = data;
+    this->subtable_3_0_4_length = length;
   } else if (platform == 3 && encoding == 1) {
-    font->cmap->subtable_3_1_4_data = data;
-    font->cmap->subtable_3_1_4_length = length;
+    this->subtable_3_1_4_data = data;
+    this->subtable_3_1_4_length = length;
   } else if (platform == 0 && encoding == 3) {
-    font->cmap->subtable_0_3_4_data = data;
-    font->cmap->subtable_0_3_4_length = length;
+    this->subtable_0_3_4_data = data;
+    this->subtable_0_3_4_length = length;
   } else {
-    return OTS_FAILURE_MSG("Unknown cmap subtable type (platform=%d, encoding=%d)", platform, encoding);
+    return Error("Unknown cmap subtable type (platform=%d, encoding=%d)", platform, encoding);
   }
 
   return true;
 }
 
-bool Parse31012(ots::Font *font,
-                const uint8_t *data, size_t length, uint16_t num_glyphs) {
+bool OpenTypeCMAP::Parse31012(const uint8_t *data, size_t length,
+                              uint16_t num_glyphs) {
   ots::Buffer subtable(data, length);
 
   // Format 12 tables are simple. We parse these and fully serialise them
   // later.
 
   if (!subtable.Skip(8)) {
-    return OTS_FAILURE_MSG("failed to skip the first 8 bytes of format 12 subtable");
+    return Error("failed to skip the first 8 bytes of format 12 subtable");
   }
   uint32_t language = 0;
   if (!subtable.ReadU32(&language)) {
-    return OTS_FAILURE_MSG("can't read format 12 subtable language");
+    return Error("can't read format 12 subtable language");
   }
   if (language) {
-    return OTS_FAILURE_MSG("format 12 subtable language should be zero (%d)", language);
+    return Error("format 12 subtable language should be zero (%d)", language);
   }
 
   uint32_t num_groups = 0;
   if (!subtable.ReadU32(&num_groups)) {
-    return OTS_FAILURE_MSG("can't read number of format 12 subtable groups");
+    return Error("can't read number of format 12 subtable groups");
   }
   if (num_groups == 0 || subtable.remaining() / 12 < num_groups) {
-    return OTS_FAILURE_MSG("Bad format 12 subtable group count %d", num_groups);
+    return Error("Bad format 12 subtable group count %d", num_groups);
   }
 
   std::vector<ots::OpenTypeCMAPSubtableRange> &groups
-      = font->cmap->subtable_3_10_12;
+      = this->subtable_3_10_12;
   groups.resize(num_groups);
 
   for (unsigned i = 0; i < num_groups; ++i) {
     if (!subtable.ReadU32(&groups[i].start_range) ||
         !subtable.ReadU32(&groups[i].end_range) ||
         !subtable.ReadU32(&groups[i].start_glyph_id)) {
-      return OTS_FAILURE_MSG("can't read format 12 subtable group");
+      return Error("can't read format 12 subtable group");
     }
 
     if (groups[i].start_range > kUnicodeUpperLimit ||
         groups[i].end_range > kUnicodeUpperLimit ||
         groups[i].start_glyph_id > 0xFFFF) {
-      return OTS_FAILURE_MSG("bad format 12 subtable group (startCharCode=0x%4X, endCharCode=0x%4X, startGlyphID=%d)",
+      return Error("bad format 12 subtable group (startCharCode=0x%4X, endCharCode=0x%4X, startGlyphID=%d)",
                              groups[i].start_range, groups[i].end_range, groups[i].start_glyph_id);
     }
 
     // We assert that the glyph value is within range. Because of the range
     // limits, above, we don't need to worry about overflow.
     if (groups[i].end_range < groups[i].start_range) {
-      return OTS_FAILURE_MSG("format 12 subtable group endCharCode before startCharCode (0x%4X < 0x%4X)",
+      return Error("format 12 subtable group endCharCode before startCharCode (0x%4X < 0x%4X)",
                              groups[i].end_range, groups[i].start_range);
     }
     if ((groups[i].end_range - groups[i].start_range) +
         groups[i].start_glyph_id > num_glyphs) {
-      return OTS_FAILURE_MSG("bad format 12 subtable group startGlyphID (%d)", groups[i].start_glyph_id);
+      return Error("bad format 12 subtable group startGlyphID (%d)", groups[i].start_glyph_id);
     }
   }
 
   // the groups must be sorted by start code and may not overlap
   for (unsigned i = 1; i < num_groups; ++i) {
     if (groups[i].start_range <= groups[i - 1].start_range) {
-      return OTS_FAILURE_MSG("out of order format 12 subtable group (startCharCode=0x%4X <= startCharCode=0x%4X of previous group)",
+      return Error("out of order format 12 subtable group (startCharCode=0x%4X <= startCharCode=0x%4X of previous group)",
                              groups[i].start_range, groups[i-1].start_range);
     }
     if (groups[i].start_range <= groups[i - 1].end_range) {
-      return OTS_FAILURE_MSG("overlapping format 12 subtable groups (startCharCode=0x%4X <= endCharCode=0x%4X of previous group)",
+      return Error("overlapping format 12 subtable groups (startCharCode=0x%4X <= endCharCode=0x%4X of previous group)",
                              groups[i].start_range, groups[i-1].end_range);
     }
   }
 
   return true;
 }
 
-bool Parse31013(ots::Font *font,
-                const uint8_t *data, size_t length, uint16_t num_glyphs) {
+bool OpenTypeCMAP::Parse31013(const uint8_t *data, size_t length,
+                              uint16_t num_glyphs) {
   ots::Buffer subtable(data, length);
 
   // Format 13 tables are simple. We parse these and fully serialise them
   // later.
 
   if (!subtable.Skip(8)) {
-    return OTS_FAILURE_MSG("Bad cmap subtable length");
+    return Error("Bad cmap subtable length");
   }
   uint32_t language = 0;
   if (!subtable.ReadU32(&language)) {
-    return OTS_FAILURE_MSG("Can't read cmap subtable language");
+    return Error("Can't read cmap subtable language");
   }
   if (language) {
-    return OTS_FAILURE_MSG("Cmap subtable language should be zero but is %d", language);
+    return Error("Cmap subtable language should be zero but is %d", language);
   }
 
   uint32_t num_groups = 0;
   if (!subtable.ReadU32(&num_groups)) {
-    return OTS_FAILURE_MSG("Can't read number of groups in a cmap subtable");
+    return Error("Can't read number of groups in a cmap subtable");
   }
 
   // We limit the number of groups in the same way as in 3.10.12 tables. See
   // the comment there in
   if (num_groups == 0 || subtable.remaining() / 12 < num_groups) {
-    return OTS_FAILURE_MSG("Bad format 13 subtable group count %d", num_groups);
+    return Error("Bad format 13 subtable group count %d", num_groups);
   }
 
-  std::vector<ots::OpenTypeCMAPSubtableRange> &groups
-      = font->cmap->subtable_3_10_13;
+  std::vector<ots::OpenTypeCMAPSubtableRange> &groups = this->subtable_3_10_13;
   groups.resize(num_groups);
 
   for (unsigned i = 0; i < num_groups; ++i) {
     if (!subtable.ReadU32(&groups[i].start_range) ||
         !subtable.ReadU32(&groups[i].end_range) ||
         !subtable.ReadU32(&groups[i].start_glyph_id)) {
-      return OTS_FAILURE_MSG("Can't read subrange structure in a cmap subtable");
+      return Error("Can't read subrange structure in a cmap subtable");
     }
 
     // We conservatively limit all of the values to protect some parsers from
     // overflows
     if (groups[i].start_range > kUnicodeUpperLimit ||
         groups[i].end_range > kUnicodeUpperLimit ||
         groups[i].start_glyph_id > 0xFFFF) {
-      return OTS_FAILURE_MSG("Bad subrange with start_range=%d, end_range=%d, start_glyph_id=%d", groups[i].start_range, groups[i].end_range, groups[i].start_glyph_id);
+      return Error("Bad subrange with start_range=%d, end_range=%d, start_glyph_id=%d", groups[i].start_range, groups[i].end_range, groups[i].start_glyph_id);
     }
 
     if (groups[i].start_glyph_id >= num_glyphs) {
-      return OTS_FAILURE_MSG("Subrange starting glyph id too high (%d > %d)", groups[i].start_glyph_id, num_glyphs);
+      return Error("Subrange starting glyph id too high (%d > %d)", groups[i].start_glyph_id, num_glyphs);
     }
   }
 
   // the groups must be sorted by start code and may not overlap
   for (unsigned i = 1; i < num_groups; ++i) {
     if (groups[i].start_range <= groups[i - 1].start_range) {
-      return OTS_FAILURE_MSG("Overlapping subrange starts (%d >= %d)", groups[i]. start_range, groups[i-1].start_range);
+      return Error("Overlapping subrange starts (%d >= %d)", groups[i]. start_range, groups[i-1].start_range);
     }
     if (groups[i].start_range <= groups[i - 1].end_range) {
-      return OTS_FAILURE_MSG("Overlapping subranges (%d <= %d)", groups[i].start_range, groups[i-1].end_range);
+      return Error("Overlapping subranges (%d <= %d)", groups[i].start_range, groups[i-1].end_range);
     }
   }
 
   return true;
 }
 
-bool Parse0514(ots::Font *font,
-               const uint8_t *data, size_t length, uint16_t num_glyphs) {
+bool OpenTypeCMAP::Parse0514(const uint8_t *data, size_t length,
+                             uint16_t num_glyphs) {
   // Unicode Variation Selector table
   ots::Buffer subtable(data, length);
 
   // Format 14 tables are simple. We parse these and fully serialise them
   // later.
 
   // Skip format (USHORT) and length (ULONG)
   if (!subtable.Skip(6)) {
-    return OTS_FAILURE_MSG("Can't read start of cmap subtable");
+    return Error("Can't read start of cmap subtable");
   }
 
   uint32_t num_records = 0;
   if (!subtable.ReadU32(&num_records)) {
-    return OTS_FAILURE_MSG("Can't read number of records in cmap subtable");
+    return Error("Can't read number of records in cmap subtable");
   }
   if (num_records == 0 || num_records > kMaxCMAPSelectorRecords) {
-    return OTS_FAILURE_MSG("Bad format 14 subtable records count %d", num_records);
+    return Error("Bad format 14 subtable records count %d", num_records);
   }
 
   std::vector<ots::OpenTypeCMAPSubtableVSRecord>& records
-      = font->cmap->subtable_0_5_14;
+      = this->subtable_0_5_14;
   records.resize(num_records);
 
   for (unsigned i = 0; i < num_records; ++i) {
     if (!subtable.ReadU24(&records[i].var_selector) ||
         !subtable.ReadU32(&records[i].default_offset) ||
         !subtable.ReadU32(&records[i].non_default_offset)) {
-      return OTS_FAILURE_MSG("Can't read record structure of record %d in cmap subtale", i);
+      return Error("Can't read record structure of record %d in cmap subtale", i);
     }
     // Checks the value of variation selector
     if (!((records[i].var_selector >= kMongolianVSStart &&
            records[i].var_selector <= kMongolianVSEnd) ||
           (records[i].var_selector >= kVSStart &&
            records[i].var_selector <= kVSEnd) ||
           (records[i].var_selector >= kIVSStart &&
            records[i].var_selector <= kIVSEnd))) {
-      return OTS_FAILURE_MSG("Bad record variation selector (%04X) in record %i", records[i].var_selector, i);
+      return Error("Bad record variation selector (%04X) in record %i", records[i].var_selector, i);
     }
     if (i > 0 &&
         records[i-1].var_selector >= records[i].var_selector) {
-      return OTS_FAILURE_MSG("Out of order variation selector (%04X >= %04X) in record %d", records[i-1].var_selector, records[i].var_selector, i);
+      return Error("Out of order variation selector (%04X >= %04X) in record %d", records[i-1].var_selector, records[i].var_selector, i);
     }
 
     // Checks offsets
     if (!records[i].default_offset && !records[i].non_default_offset) {
-      return OTS_FAILURE_MSG("No default aoffset in variation selector record %d", i);
+      return Error("No default aoffset in variation selector record %d", i);
     }
     if (records[i].default_offset &&
         records[i].default_offset >= length) {
-      return OTS_FAILURE_MSG("Default offset too high (%d >= %ld) in record %d", records[i].default_offset, length, i);
+      return Error("Default offset too high (%d >= %ld) in record %d", records[i].default_offset, length, i);
     }
     if (records[i].non_default_offset &&
         records[i].non_default_offset >= length) {
-      return OTS_FAILURE_MSG("Non default offset too high (%d >= %ld) in record %d", records[i].non_default_offset, length, i);
+      return Error("Non default offset too high (%d >= %ld) in record %d", records[i].non_default_offset, length, i);
     }
   }
 
   for (unsigned i = 0; i < num_records; ++i) {
     // Checks default UVS table
     if (records[i].default_offset) {
       subtable.set_offset(records[i].default_offset);
       uint32_t num_ranges = 0;
       if (!subtable.ReadU32(&num_ranges)) {
-        return OTS_FAILURE_MSG("Can't read number of ranges in record %d", i);
+        return Error("Can't read number of ranges in record %d", i);
       }
       if (num_ranges == 0 || subtable.remaining() / 4 < num_ranges) {
-        return OTS_FAILURE_MSG("Bad number of ranges (%d) in record %d", num_ranges, i);
+        return Error("Bad number of ranges (%d) in record %d", num_ranges, i);
       }
 
       uint32_t last_unicode_value = 0;
       std::vector<ots::OpenTypeCMAPSubtableVSRange>& ranges
           = records[i].ranges;
       ranges.resize(num_ranges);
 
       for (unsigned j = 0; j < num_ranges; ++j) {
         if (!subtable.ReadU24(&ranges[j].unicode_value) ||
             !subtable.ReadU8(&ranges[j].additional_count)) {
-          return OTS_FAILURE_MSG("Can't read range info in variation selector record %d", i);
+          return Error("Can't read range info in variation selector record %d", i);
         }
         const uint32_t check_value =
             ranges[j].unicode_value + ranges[j].additional_count;
         if (ranges[j].unicode_value == 0 ||
             ranges[j].unicode_value > kUnicodeUpperLimit ||
             check_value > kUVSUpperLimit ||
             (last_unicode_value &&
              ranges[j].unicode_value <= last_unicode_value)) {
-          return OTS_FAILURE_MSG("Bad Unicode value *%04X) in variation selector range %d record %d", ranges[j].unicode_value, j, i);
+          return Error("Bad Unicode value *%04X) in variation selector range %d record %d", ranges[j].unicode_value, j, i);
         }
         last_unicode_value = check_value;
       }
     }
 
     // Checks non default UVS table
     if (records[i].non_default_offset) {
       subtable.set_offset(records[i].non_default_offset);
       uint32_t num_mappings = 0;
       if (!subtable.ReadU32(&num_mappings)) {
-        return OTS_FAILURE_MSG("Can't read number of mappings in variation selector record %d", i);
+        return Error("Can't read number of mappings in variation selector record %d", i);
       }
       if (num_mappings == 0 || subtable.remaining() / 5 < num_mappings) {
-        return OTS_FAILURE_MSG("Bad number of mappings (%d) in variation selector record %d", num_mappings, i);
+        return Error("Bad number of mappings (%d) in variation selector record %d", num_mappings, i);
       }
 
       uint32_t last_unicode_value = 0;
       std::vector<ots::OpenTypeCMAPSubtableVSMapping>& mappings
           = records[i].mappings;
       mappings.resize(num_mappings);
 
       for (unsigned j = 0; j < num_mappings; ++j) {
         if (!subtable.ReadU24(&mappings[j].unicode_value) ||
             !subtable.ReadU16(&mappings[j].glyph_id)) {
-          return OTS_FAILURE_MSG("Can't read mapping %d in variation selector record %d", j, i);
+          return Error("Can't read mapping %d in variation selector record %d", j, i);
         }
         if (mappings[j].glyph_id == 0 ||
             mappings[j].unicode_value == 0 ||
             mappings[j].unicode_value > kUnicodeUpperLimit ||
             (last_unicode_value &&
              mappings[j].unicode_value <= last_unicode_value)) {
-          return OTS_FAILURE_MSG("Bad mapping (%04X -> %d) in mapping %d of variation selector %d", mappings[j].unicode_value, mappings[j].glyph_id, j, i);
+          return Error("Bad mapping (%04X -> %d) in mapping %d of variation selector %d", mappings[j].unicode_value, mappings[j].glyph_id, j, i);
         }
         last_unicode_value = mappings[j].unicode_value;
       }
     }
   }
 
   if (subtable.offset() != length) {
-    return OTS_FAILURE_MSG("Bad subtable offset (%ld != %ld)", subtable.offset(), length);
+    return Error("Bad subtable offset (%ld != %ld)", subtable.offset(), length);
   }
-  font->cmap->subtable_0_5_14_length = subtable.offset();
+  this->subtable_0_5_14_length = subtable.offset();
   return true;
 }
 
-bool Parse100(ots::Font *font, const uint8_t *data, size_t length) {
+bool OpenTypeCMAP::Parse100(const uint8_t *data, size_t length) {
   // Mac Roman table
   ots::Buffer subtable(data, length);
 
   if (!subtable.Skip(4)) {
-    return OTS_FAILURE_MSG("Bad cmap subtable");
+    return Error("Bad cmap subtable");
   }
   uint16_t language = 0;
   if (!subtable.ReadU16(&language)) {
-    return OTS_FAILURE_MSG("Can't read language in cmap subtable");
+    return Error("Can't read language in cmap subtable");
   }
   if (language) {
     // simsun.ttf has non-zero language id.
-    OTS_WARNING("language id should be zero: %u", language);
+    Warning("language id should be zero: %u", language);
   }
 
-  font->cmap->subtable_1_0_0.reserve(kFormat0ArraySize);
+  this->subtable_1_0_0.reserve(kFormat0ArraySize);
   for (size_t i = 0; i < kFormat0ArraySize; ++i) {
     uint8_t glyph_id = 0;
     if (!subtable.ReadU8(&glyph_id)) {
-      return OTS_FAILURE_MSG("Can't read glyph id at array[%ld] in cmap subtable", i);
+      return Error("Can't read glyph id at array[%ld] in cmap subtable", i);
     }
-    font->cmap->subtable_1_0_0.push_back(glyph_id);
+    this->subtable_1_0_0.push_back(glyph_id);
   }
 
   return true;
 }
 
-}  // namespace
-
-namespace ots {
-
-bool ots_cmap_parse(Font *font, const uint8_t *data, size_t length) {
+bool OpenTypeCMAP::Parse(const uint8_t *data, size_t length) {
   Buffer table(data, length);
-  font->cmap = new OpenTypeCMAP;
 
   uint16_t version = 0;
   uint16_t num_tables = 0;
   if (!table.ReadU16(&version) ||
       !table.ReadU16(&num_tables)) {
-    return OTS_FAILURE_MSG("Can't read structure of cmap");
+    return Error("Can't read structure of cmap");
   }
 
   if (version != 0) {
-    return OTS_FAILURE_MSG("Non zero cmap version (%d)", version);
+    return Error("Non zero cmap version (%d)", version);
   }
   if (!num_tables) {
-    return OTS_FAILURE_MSG("No subtables in cmap!");
+    return Error("No subtables in cmap!");
   }
 
   std::vector<CMAPSubtableHeader> subtable_headers;
 
   // read the subtable headers
   subtable_headers.reserve(num_tables);
   for (unsigned i = 0; i < num_tables; ++i) {
     CMAPSubtableHeader subt;
 
     if (!table.ReadU16(&subt.platform) ||
         !table.ReadU16(&subt.encoding) ||
         !table.ReadU32(&subt.offset)) {
-      return OTS_FAILURE_MSG("Can't read subtable information cmap subtable %d", i);
+      return Error("Can't read subtable information cmap subtable %d", i);
     }
 
     subtable_headers.push_back(subt);
   }
 
   const size_t data_offset = table.offset();
 
   // make sure that all the offsets are valid.
   for (unsigned i = 0; i < num_tables; ++i) {
     if (subtable_headers[i].offset > 1024 * 1024 * 1024) {
-      return OTS_FAILURE_MSG("Bad subtable offset in cmap subtable %d", i);
+      return Error("Bad subtable offset in cmap subtable %d", i);
     }
     if (subtable_headers[i].offset < data_offset ||
         subtable_headers[i].offset >= length) {
-      return OTS_FAILURE_MSG("Bad subtable offset (%d) in cmap subtable %d", subtable_headers[i].offset, i);
+      return Error("Bad subtable offset (%d) in cmap subtable %d", subtable_headers[i].offset, i);
     }
   }
 
   // the format of the table is the first couple of bytes in the table. The
   // length of the table is stored in a format-specific way.
   for (unsigned i = 0; i < num_tables; ++i) {
     table.set_offset(subtable_headers[i].offset);
     if (!table.ReadU16(&subtable_headers[i].format)) {
-      return OTS_FAILURE_MSG("Can't read cmap subtable header format %d", i);
+      return Error("Can't read cmap subtable header format %d", i);
     }
 
     uint16_t len = 0;
     uint16_t lang = 0;
     switch (subtable_headers[i].format) {
       case 0:
       case 4:
         if (!table.ReadU16(&len)) {
-          return OTS_FAILURE_MSG("Can't read cmap subtable %d length", i);
+          return Error("Can't read cmap subtable %d length", i);
         }
         if (!table.ReadU16(&lang)) {
-          return OTS_FAILURE_MSG("Can't read cmap subtable %d language", i);
+          return Error("Can't read cmap subtable %d language", i);
         }
         subtable_headers[i].length = len;
         subtable_headers[i].language = lang;
         break;
       case 12:
       case 13:
         if (!table.Skip(2)) {
-          return OTS_FAILURE_MSG("Bad cmap subtable %d structure", i);
+          return Error("Bad cmap subtable %d structure", i);
         }
         if (!table.ReadU32(&subtable_headers[i].length)) {
-          return OTS_FAILURE_MSG("Can read cmap subtable %d length", i);
+          return Error("Can read cmap subtable %d length", i);
         }
         if (!table.ReadU32(&subtable_headers[i].language)) {
-          return OTS_FAILURE_MSG("Can't read cmap subtable %d language", i);
+          return Error("Can't read cmap subtable %d language", i);
         }
         break;
       case 14:
         if (!table.ReadU32(&subtable_headers[i].length)) {
-          return OTS_FAILURE_MSG("Can't read cmap subtable %d length", i);
+          return Error("Can't read cmap subtable %d length", i);
         }
         subtable_headers[i].language = 0;
         break;
       default:
         subtable_headers[i].length = 0;
         subtable_headers[i].language = 0;
         break;
     }
@@ -659,39 +657,39 @@ bool ots_cmap_parse(Font *font, const ui
 
   // check if the table is sorted first by platform ID, then by encoding ID.
   for (unsigned i = 1; i < num_tables; ++i) {
     if (subtable_headers[i - 1].platform > subtable_headers[i].platform ||
         (subtable_headers[i - 1].platform == subtable_headers[i].platform &&
          (subtable_headers[i - 1].encoding > subtable_headers[i].encoding ||
           (subtable_headers[i - 1].encoding == subtable_headers[i].encoding &&
            subtable_headers[i - 1].language > subtable_headers[i].language))))
-      OTS_WARNING("subtable %d with platform ID %d, encoding ID %d, language ID %d "
+      Warning("subtable %d with platform ID %d, encoding ID %d, language ID %d "
                   "following subtable with platform ID %d, encoding ID %d, language ID %d",
                   i,
                   subtable_headers[i].platform,
                   subtable_headers[i].encoding,
                   subtable_headers[i].language,
                   subtable_headers[i - 1].platform,
                   subtable_headers[i - 1].encoding,
                   subtable_headers[i - 1].language);
   }
 
   // Now, verify that all the lengths are sane
   for (unsigned i = 0; i < num_tables; ++i) {
     if (!subtable_headers[i].length) continue;
     if (subtable_headers[i].length > 1024 * 1024 * 1024) {
-      return OTS_FAILURE_MSG("Bad cmap subtable %d length", i);
+      return Error("Bad cmap subtable %d length", i);
     }
     // We know that both the offset and length are < 1GB, so the following
     // addition doesn't overflow
     const uint32_t end_byte
         = subtable_headers[i].offset + subtable_headers[i].length;
     if (end_byte > length) {
-      return OTS_FAILURE_MSG("Over long cmap subtable %d @ %d for %d", i, subtable_headers[i].offset, subtable_headers[i].length);
+      return Error("Over long cmap subtable %d @ %d for %d", i, subtable_headers[i].offset, subtable_headers[i].length);
     }
   }
 
   // check that the cmap subtables are not overlapping.
   std::set<std::pair<uint32_t, uint32_t> > uniq_checker;
   std::vector<std::pair<uint32_t, uint8_t> > overlap_checker;
   for (unsigned i = 0; i < num_tables; ++i) {
     const uint32_t end_byte
@@ -709,26 +707,28 @@ bool ots_cmap_parse(Font *font, const ui
     overlap_checker.push_back(
         std::make_pair(end_byte, static_cast<uint8_t>(0) /* end */));
   }
   std::sort(overlap_checker.begin(), overlap_checker.end());
   int overlap_count = 0;
   for (unsigned i = 0; i < overlap_checker.size(); ++i) {
     overlap_count += (overlap_checker[i].second ? 1 : -1);
     if (overlap_count > 1) {
-      return OTS_FAILURE_MSG("Excessive overlap count %d", overlap_count);
+      return Error("Excessive overlap count %d", overlap_count);
     }
   }
 
   // we grab the number of glyphs in the file from the maxp table to make sure
   // that the character map isn't referencing anything beyound this range.
-  if (!font->maxp) {
-    return OTS_FAILURE_MSG("No maxp table in font! Needed by cmap.");
+  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
+      GetFont()->GetTypedTable(OTS_TAG_MAXP));
+  if (!maxp) {
+    return Error("No maxp table in font! Needed by cmap.");
   }
-  const uint16_t num_glyphs = font->maxp->num_glyphs;
+  const uint16_t num_glyphs = maxp->num_glyphs;
 
   // We only support a subset of the possible character map tables. Microsoft
   // 'strongly recommends' that everyone supports the Unicode BMP table with
   // the UCS-4 table for non-BMP glyphs. We'll pass the following subtables:
   //   Platform ID   Encoding ID  Format
   //   0             0            4       (Unicode Default)
   //   0             1            4       (Unicode 1.1)
   //   0             3            4       (Unicode BMP)
@@ -755,144 +755,140 @@ bool ots_cmap_parse(Font *font, const ui
       // Unicode platform
 
       if ((subtable_headers[i].encoding == 0 || subtable_headers[i].encoding == 1) &&
           (subtable_headers[i].format == 4)) {
         // parse and output the 0-0-4 and 0-1-4 tables as 3-1-4 table. Sometimes the 0-0-4
         // table actually points to MS symbol data and thus should be parsed as
         // 3-0-4 table (e.g., marqueem.ttf and quixotic.ttf). This error will be
         // recovered in ots_cmap_serialise().
-        if (!ParseFormat4(font, 3, 1, data + subtable_headers[i].offset,
+        if (!ParseFormat4(3, 1, data + subtable_headers[i].offset,
                       subtable_headers[i].length, num_glyphs)) {
-          return OTS_FAILURE_MSG("Failed to parse format 4 cmap subtable %d", i);
+          return Error("Failed to parse format 4 cmap subtable %d", i);
         }
       } else if ((subtable_headers[i].encoding == 3) &&
                  (subtable_headers[i].format == 4)) {
         // parse and output the 0-3-4 table as 0-3-4 table.
-        if (!ParseFormat4(font, 0, 3, data + subtable_headers[i].offset,
+        if (!ParseFormat4(0, 3, data + subtable_headers[i].offset,
                       subtable_headers[i].length, num_glyphs)) {
-          return OTS_FAILURE_MSG("Failed to parse format 4 cmap subtable %d", i);
+          return Error("Failed to parse format 4 cmap subtable %d", i);
         }
       } else if ((subtable_headers[i].encoding == 3) &&
                  (subtable_headers[i].format == 12)) {
         // parse and output the 0-3-12 table as 3-10-12 table.
-        if (!Parse31012(font, data + subtable_headers[i].offset,
+        if (!Parse31012(data + subtable_headers[i].offset,
                         subtable_headers[i].length, num_glyphs)) {
-          return OTS_FAILURE_MSG("Failed to parse format 12 cmap subtable %d", i);
+          return Error("Failed to parse format 12 cmap subtable %d", i);
         }
       } else if ((subtable_headers[i].encoding == 5) &&
                  (subtable_headers[i].format == 14)) {
-        if (!Parse0514(font, data + subtable_headers[i].offset,
+        if (!Parse0514(data + subtable_headers[i].offset,
                        subtable_headers[i].length, num_glyphs)) {
-          return OTS_FAILURE_MSG("Failed to parse format 14 cmap subtable %d", i);
+          return Error("Failed to parse format 14 cmap subtable %d", i);
         }
       }
     } else if (subtable_headers[i].platform == 1) {
       // Mac platform
 
       if ((subtable_headers[i].encoding == 0) &&
           (subtable_headers[i].format == 0)) {
         // parse and output the 1-0-0 table.
-        if (!Parse100(font, data + subtable_headers[i].offset,
+        if (!Parse100(data + subtable_headers[i].offset,
                       subtable_headers[i].length)) {
           return OTS_FAILURE();
         }
       }
     } else if (subtable_headers[i].platform == 3) {
       // MS platform
 
       switch (subtable_headers[i].encoding) {
         case 0:
         case 1:
           if (subtable_headers[i].format == 4) {
             // parse 3-0-4 or 3-1-4 table.
-            if (!ParseFormat4(font, subtable_headers[i].platform,
+            if (!ParseFormat4(subtable_headers[i].platform,
                           subtable_headers[i].encoding,
                           data + subtable_headers[i].offset,
                           subtable_headers[i].length, num_glyphs)) {
               return OTS_FAILURE();
             }
           }
           break;
         case 10:
           if (subtable_headers[i].format == 12) {
-            font->cmap->subtable_3_10_12.clear();
-            if (!Parse31012(font, data + subtable_headers[i].offset,
+            this->subtable_3_10_12.clear();
+            if (!Parse31012(data + subtable_headers[i].offset,
                             subtable_headers[i].length, num_glyphs)) {
               return OTS_FAILURE();
             }
           } else if (subtable_headers[i].format == 13) {
-            font->cmap->subtable_3_10_13.clear();
-            if (!Parse31013(font, data + subtable_headers[i].offset,
+            this->subtable_3_10_13.clear();
+            if (!Parse31013(data + subtable_headers[i].offset,
                             subtable_headers[i].length, num_glyphs)) {
               return OTS_FAILURE();
             }
           }
           break;
       }
     }
   }
 
   return true;
 }
 
-bool ots_cmap_should_serialise(Font *font) {
-  return font->cmap != NULL;
-}
-
-bool ots_cmap_serialise(OTSStream *out, Font *font) {
-  const bool have_034 = font->cmap->subtable_0_3_4_data != NULL;
-  const bool have_0514 = font->cmap->subtable_0_5_14.size() != 0;
-  const bool have_100 = font->cmap->subtable_1_0_0.size() != 0;
-  const bool have_304 = font->cmap->subtable_3_0_4_data != NULL;
+bool OpenTypeCMAP::Serialize(OTSStream *out) {
+  const bool have_034 = this->subtable_0_3_4_data != NULL;
+  const bool have_0514 = this->subtable_0_5_14.size() != 0;
+  const bool have_100 = this->subtable_1_0_0.size() != 0;
+  const bool have_304 = this->subtable_3_0_4_data != NULL;
   // MS Symbol and MS Unicode tables should not co-exist.
   // See the comment above in 0-0-4 parser.
-  const bool have_314 = (!have_304) && font->cmap->subtable_3_1_4_data;
-  const bool have_31012 = font->cmap->subtable_3_10_12.size() != 0;
-  const bool have_31013 = font->cmap->subtable_3_10_13.size() != 0;
+  const bool have_314 = (!have_304) && this->subtable_3_1_4_data;
+  const bool have_31012 = this->subtable_3_10_12.size() != 0;
+  const bool have_31013 = this->subtable_3_10_13.size() != 0;
   const uint16_t num_subtables = static_cast<uint16_t>(have_034) +
                                  static_cast<uint16_t>(have_0514) +
                                  static_cast<uint16_t>(have_100) +
                                  static_cast<uint16_t>(have_304) +
                                  static_cast<uint16_t>(have_314) +
                                  static_cast<uint16_t>(have_31012) +
                                  static_cast<uint16_t>(have_31013);
   const off_t table_start = out->Tell();
 
   // Some fonts don't have 3-0-4 MS Symbol nor 3-1-4 Unicode BMP tables
   // (e.g., old fonts for Mac). We don't support them.
   if (!have_304 && !have_314 && !have_034 && !have_31012 && !have_31013) {
-    return OTS_FAILURE_MSG("no supported subtables were found");
+    return Error("no supported subtables were found");
   }
 
   if (!out->WriteU16(0) ||
       !out->WriteU16(num_subtables)) {
     return OTS_FAILURE();
   }
 
   const off_t record_offset = out->Tell();
   if (!out->Pad(num_subtables * 8)) {
     return OTS_FAILURE();
   }
 
   const off_t offset_034 = out->Tell();
   if (have_034) {
-    if (!out->Write(font->cmap->subtable_0_3_4_data,
-                    font->cmap->subtable_0_3_4_length)) {
+    if (!out->Write(this->subtable_0_3_4_data,
+                    this->subtable_0_3_4_length)) {
       return OTS_FAILURE();
     }
   }
 
   const off_t offset_0514 = out->Tell();
   if (have_0514) {
     const std::vector<ots::OpenTypeCMAPSubtableVSRecord> &records
-        = font->cmap->subtable_0_5_14;
+        = this->subtable_0_5_14;
     const unsigned num_records = records.size();
     if (!out->WriteU16(14) ||
-        !out->WriteU32(font->cmap->subtable_0_5_14_length) ||
+        !out->WriteU32(this->subtable_0_5_14_length) ||
         !out->WriteU32(num_records)) {
       return OTS_FAILURE();
     }
     for (unsigned i = 0; i < num_records; ++i) {
       if (!out->WriteU24(records[i].var_selector) ||
           !out->WriteU32(records[i].default_offset) ||
           !out->WriteU32(records[i].non_default_offset)) {
         return OTS_FAILURE();
@@ -934,41 +930,41 @@ bool ots_cmap_serialise(OTSStream *out, 
 
   const off_t offset_100 = out->Tell();
   if (have_100) {
     if (!out->WriteU16(0) ||  // format
         !out->WriteU16(6 + kFormat0ArraySize) ||  // length
         !out->WriteU16(0)) {  // language
       return OTS_FAILURE();
     }
-    if (!out->Write(&(font->cmap->subtable_1_0_0[0]), kFormat0ArraySize)) {
+    if (!out->Write(&(this->subtable_1_0_0[0]), kFormat0ArraySize)) {
       return OTS_FAILURE();
     }
   }
 
   const off_t offset_304 = out->Tell();
   if (have_304) {
-    if (!out->Write(font->cmap->subtable_3_0_4_data,
-                    font->cmap->subtable_3_0_4_length)) {
+    if (!out->Write(this->subtable_3_0_4_data,
+                    this->subtable_3_0_4_length)) {
       return OTS_FAILURE();
     }
   }
 
   const off_t offset_314 = out->Tell();
   if (have_314) {
-    if (!out->Write(font->cmap->subtable_3_1_4_data,
-                    font->cmap->subtable_3_1_4_length)) {
+    if (!out->Write(this->subtable_3_1_4_data,
+                    this->subtable_3_1_4_length)) {
       return OTS_FAILURE();
     }
   }
 
   const off_t offset_31012 = out->Tell();
   if (have_31012) {
     std::vector<OpenTypeCMAPSubtableRange> &groups
-        = font->cmap->subtable_3_10_12;
+        = this->subtable_3_10_12;
     const unsigned num_groups = groups.size();
     if (!out->WriteU16(12) ||
         !out->WriteU16(0) ||
         !out->WriteU32(num_groups * 12 + 16) ||
         !out->WriteU32(0) ||
         !out->WriteU32(num_groups)) {
       return OTS_FAILURE();
     }
@@ -980,17 +976,17 @@ bool ots_cmap_serialise(OTSStream *out, 
         return OTS_FAILURE();
       }
     }
   }
 
   const off_t offset_31013 = out->Tell();
   if (have_31013) {
     std::vector<OpenTypeCMAPSubtableRange> &groups
-        = font->cmap->subtable_3_10_13;
+        = this->subtable_3_10_13;
     const unsigned num_groups = groups.size();
     if (!out->WriteU16(13) ||
         !out->WriteU16(0) ||
         !out->WriteU32(num_groups * 12 + 16) ||
         !out->WriteU32(0) ||
         !out->WriteU32(num_groups)) {
       return OTS_FAILURE();
     }
@@ -1069,20 +1065,9 @@ bool ots_cmap_serialise(OTSStream *out, 
 
   if (!out->Seek(table_end)) {
     return OTS_FAILURE();
   }
 
   return true;
 }
 
-void ots_cmap_reuse(Font *font, Font *other) {
-  font->cmap = other->cmap;
-  font->cmap_reused = true;
-}
-
-void ots_cmap_free(Font *font) {
-  delete font->cmap;
-}
-
 }  // namespace ots
-
-#undef TABLE_NAME
--- a/gfx/ots/src/cmap.h
+++ b/gfx/ots/src/cmap.h
@@ -1,9 +1,9 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef OTS_CMAP_H_
 #define OTS_CMAP_H_
 
 #include <vector>
 
@@ -30,27 +30,33 @@ struct OpenTypeCMAPSubtableVSMapping {
 struct OpenTypeCMAPSubtableVSRecord {
   uint32_t var_selector;
   uint32_t default_offset;
   uint32_t non_default_offset;
   std::vector<OpenTypeCMAPSubtableVSRange> ranges;
   std::vector<OpenTypeCMAPSubtableVSMapping> mappings;
 };
 
-struct OpenTypeCMAP {
-  OpenTypeCMAP()
-      : subtable_0_3_4_data(NULL),
+class OpenTypeCMAP : public Table {
+ public:
+  explicit OpenTypeCMAP(Font *font, uint32_t tag)
+      : Table(font, tag, tag),
+        subtable_0_3_4_data(NULL),
         subtable_0_3_4_length(0),
         subtable_0_5_14_length(0),
         subtable_3_0_4_data(NULL),
         subtable_3_0_4_length(0),
         subtable_3_1_4_data(NULL),
         subtable_3_1_4_length(0) {
   }
 
+  bool Parse(const uint8_t *data, size_t length);
+  bool Serialize(OTSStream *out);
+
+ private:
   // Platform 0, Encoding 3, Format 4, Unicode BMP table.
   const uint8_t *subtable_0_3_4_data;
   size_t subtable_0_3_4_length;
 
   // Platform 0, Encoding 5, Format 14, Unicode Variation Sequence table.
   size_t subtable_0_5_14_length;
   std::vector<OpenTypeCMAPSubtableVSRecord> subtable_0_5_14;
 
@@ -62,13 +68,20 @@ struct OpenTypeCMAP {
   size_t subtable_3_1_4_length;
 
   // Platform 3, Encoding 10, Format 12, MS Unicode UCS-4 table.
   std::vector<OpenTypeCMAPSubtableRange> subtable_3_10_12;
   // Platform 3, Encoding 10, Format 13, MS UCS-4 Fallback table.
   std::vector<OpenTypeCMAPSubtableRange> subtable_3_10_13;
   // Platform 1, Encoding 0, Format 0, Mac Roman table.
   std::vector<uint8_t> subtable_1_0_0;
+
+  bool ParseFormat4(int platform, int encoding, const uint8_t *data,
+                    size_t length, uint16_t num_glyphs);
+  bool Parse31012(const uint8_t *data, size_t length, uint16_t num_glyphs);
+  bool Parse31013(const uint8_t *data, size_t length, uint16_t num_glyphs);
+  bool Parse0514(const uint8_t *data, size_t length, uint16_t num_glyphs);
+  bool Parse100(const uint8_t *data, size_t length);
 };
 
 }  // namespace ots
 
 #endif
--- a/gfx/ots/src/cvt.cc
+++ b/gfx/ots/src/cvt.cc
@@ -1,65 +1,46 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "cvt.h"
 
 // cvt - Control Value Table
 // http://www.microsoft.com/typography/otspec/cvt.htm
 
-#define TABLE_NAME "cvt"
-
 namespace ots {
 
-bool ots_cvt_parse(Font *font, const uint8_t *data, size_t length) {
+bool OpenTypeCVT::Parse(const uint8_t *data, size_t length) {
   Buffer table(data, length);
 
-  OpenTypeCVT *cvt = new OpenTypeCVT;
-  font->cvt = cvt;
-
   if (length >= 128 * 1024u) {
-    return OTS_FAILURE_MSG("Length (%d) > 120K");  // almost all cvt tables are less than 4k bytes.
+    return Error("Length (%d) > 120K");  // almost all cvt tables are less than 4k bytes.
   }
 
   if (length % 2 != 0) {
-    return OTS_FAILURE_MSG("Uneven cvt length (%d)", length);
+    return Error("Uneven table length (%d)", length);
   }
 
   if (!table.Skip(length)) {
-    return OTS_FAILURE_MSG("Length too high");
+    return Error("Table length too high");
   }
 
-  cvt->data = data;
-  cvt->length = length;
+  this->data = data;
+  this->length = length;
   return true;
 }
 
-bool ots_cvt_should_serialise(Font *font) {
-  if (!font->glyf) {
-    return false;  // this table is not for CFF fonts.
-  }
-  return font->cvt != NULL;
-}
-
-bool ots_cvt_serialise(OTSStream *out, Font *font) {
-  const OpenTypeCVT *cvt = font->cvt;
-
-  if (!out->Write(cvt->data, cvt->length)) {
-    return OTS_FAILURE_MSG("Failed to write CVT table");
+bool OpenTypeCVT::Serialize(OTSStream *out) {
+  if (!out->Write(this->data, this->length)) {
+    return Error("Failed to write cvt table");
   }
 
   return true;
 }
 
-void ots_cvt_reuse(Font *font, Font *other) {
-  font->cvt = other->cvt;
-  font->cvt_reused = true;
-}
-
-void ots_cvt_free(Font *font) {
-  delete font->cvt;
+bool OpenTypeCVT::ShouldSerialize() {
+  return Table::ShouldSerialize() &&
+         // this table is not for CFF fonts.
+         GetFont()->GetTable(OTS_TAG_GLYF) != NULL;
 }
 
 }  // namespace ots
-
-#undef TABLE_NAME
--- a/gfx/ots/src/cvt.h
+++ b/gfx/ots/src/cvt.h
@@ -1,19 +1,28 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef OTS_CVT_H_
 #define OTS_CVT_H_
 
 #include "ots.h"
 
 namespace ots {
 
-struct OpenTypeCVT {
+class OpenTypeCVT : public Table {
+ public:
+  explicit OpenTypeCVT(Font *font, uint32_t tag)
+      : Table(font, tag, tag) { }
+
+  bool Parse(const uint8_t *data, size_t length);
+  bool Serialize(OTSStream *out);
+  bool ShouldSerialize();
+
+ private:
   const uint8_t *data;
   uint32_t length;
 };
 
 }  // namespace ots
 
 #endif  // OTS_CVT_H_
--- a/gfx/ots/src/fpgm.cc
+++ b/gfx/ots/src/fpgm.cc
@@ -1,59 +1,42 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "fpgm.h"
 
 // fpgm - Font Program
 // http://www.microsoft.com/typography/otspec/fpgm.htm
 
-#define TABLE_NAME "fpgm"
-
 namespace ots {
 
-bool ots_fpgm_parse(Font *font, const uint8_t *data, size_t length) {
+bool OpenTypeFPGM::Parse(const uint8_t *data, size_t length) {
   Buffer table(data, length);
 
-  OpenTypeFPGM *fpgm = new OpenTypeFPGM;
-  font->fpgm = fpgm;
-
   if (length >= 128 * 1024u) {
-    return OTS_FAILURE_MSG("length (%ld) > 120", length);  // almost all fpgm tables are less than 5k bytes.
+    return Error("length (%ld) > 120", length);  // almost all fpgm tables are less than 5k bytes.
   }
 
   if (!table.Skip(length)) {
-    return OTS_FAILURE_MSG("Bad fpgm length");
+    return Error("Bad table length");
   }
 
-  fpgm->data = data;
-  fpgm->length = length;
+  this->data = data;
+  this->length = length;
   return true;
 }
 
-bool ots_fpgm_should_serialise(Font *font) {
-  if (!font->glyf) return false;  // this table is not for CFF fonts.
-  return font->fpgm != NULL;
-}
-
-bool ots_fpgm_serialise(OTSStream *out, Font *font) {
-  const OpenTypeFPGM *fpgm = font->fpgm;
-
-  if (!out->Write(fpgm->data, fpgm->length)) {
-    return OTS_FAILURE_MSG("Failed to write fpgm");
+bool OpenTypeFPGM::Serialize(OTSStream *out) {
+  if (!out->Write(this->data, this->length)) {
+    return Error("Failed to write fpgm table");
   }
 
   return true;
 }
 
-void ots_fpgm_reuse(Font *font, Font *other) {
-  font->fpgm = other->fpgm;
-  font->fpgm_reused = true;
-}
-
-void ots_fpgm_free(Font *font) {
-  delete font->fpgm;
+bool OpenTypeFPGM::ShouldSerialize() {
+  return Table::ShouldSerialize() &&
+         // this table is not for CFF fonts.
+         GetFont()->GetTable(OTS_TAG_GLYF) != NULL;
 }
 
 }  // namespace ots
-
-#undef TABLE_NAME
--- a/gfx/ots/src/fpgm.h
+++ b/gfx/ots/src/fpgm.h
@@ -1,19 +1,28 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef OTS_FPGM_H_
 #define OTS_FPGM_H_
 
 #include "ots.h"
 
 namespace ots {
 
-struct OpenTypeFPGM {
+class OpenTypeFPGM : public Table {
+ public:
+  explicit OpenTypeFPGM(Font *font, uint32_t tag)
+      : Table(font, tag, tag) { }
+
+  bool Parse(const uint8_t *data, size_t length);
+  bool Serialize(OTSStream *out);
+  bool ShouldSerialize();
+
+ private:
   const uint8_t *data;
   uint32_t length;
 };
 
 }  // namespace ots
 
 #endif  // OTS_FPGM_H_
--- a/gfx/ots/src/gasp.cc
+++ b/gfx/ots/src/gasp.cc
@@ -1,119 +1,84 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "gasp.h"
 
 // gasp - Grid-fitting And Scan-conversion Procedure
 // http://www.microsoft.com/typography/otspec/gasp.htm
 
-#define TABLE_NAME "gasp"
-
-#define DROP_THIS_TABLE(...) \
-  do { \
-    OTS_FAILURE_MSG_(font->file, TABLE_NAME ": " __VA_ARGS__); \
-    OTS_FAILURE_MSG("Table discarded"); \
-    delete font->gasp; \
-    font->gasp = 0; \
-  } while (0)
-
 namespace ots {
 
-bool ots_gasp_parse(Font *font, const uint8_t *data, size_t length) {
+bool OpenTypeGASP::Parse(const uint8_t *data, size_t length) {
   Buffer table(data, length);
 
-  OpenTypeGASP *gasp = new OpenTypeGASP;
-  font->gasp = gasp;
-
   uint16_t num_ranges = 0;
-  if (!table.ReadU16(&gasp->version) ||
+  if (!table.ReadU16(&this->version) ||
       !table.ReadU16(&num_ranges)) {
-    return OTS_FAILURE_MSG("Failed to read table header");
+    return Error("Failed to read table header");
   }
 
-  if (gasp->version > 1) {
+  if (this->version > 1) {
     // Lots of Linux fonts have bad version numbers...
-    DROP_THIS_TABLE("bad version: %u", gasp->version);
-    return true;
+    return Drop("Unsupported version: %u", this->version);
   }
 
   if (num_ranges == 0) {
-    DROP_THIS_TABLE("num_ranges is zero");
-    return true;
+    return Drop("numRanges is zero");
   }
 
-  gasp->gasp_ranges.reserve(num_ranges);
+  this->gasp_ranges.reserve(num_ranges);
   for (unsigned i = 0; i < num_ranges; ++i) {
     uint16_t max_ppem = 0;
     uint16_t behavior = 0;
     if (!table.ReadU16(&max_ppem) ||
         !table.ReadU16(&behavior)) {
-      return OTS_FAILURE_MSG("Failed to read subrange %d", i);
+      return Error("Failed to read GASPRANGE %d", i);
     }
-    if ((i > 0) && (gasp->gasp_ranges[i - 1].first >= max_ppem)) {
+    if ((i > 0) && (this->gasp_ranges[i - 1].first >= max_ppem)) {
       // The records in the gaspRange[] array must be sorted in order of
       // increasing rangeMaxPPEM value.
-      DROP_THIS_TABLE("ranges are not sorted");
-      return true;
+      return Drop("Ranges are not sorted");
     }
     if ((i == num_ranges - 1u) &&  // never underflow.
         (max_ppem != 0xffffu)) {
-      DROP_THIS_TABLE("The last record should be 0xFFFF as a sentinel value "
+      return Drop("The last record should be 0xFFFF as a sentinel value "
                   "for rangeMaxPPEM");
-      return true;
     }
 
     if (behavior >> 8) {
-      OTS_WARNING("undefined bits are used: %x", behavior);
+      Warning("Undefined bits are used: %x", behavior);
       // mask undefined bits.
       behavior &= 0x000fu;
     }
 
-    if (gasp->version == 0 && (behavior >> 2) != 0) {
-      OTS_WARNING("changed the version number to 1");
-      gasp->version = 1;
+    if (this->version == 0 && (behavior >> 2) != 0) {
+      Warning("Changed the version number to 1");
+      this->version = 1;
     }
 
-    gasp->gasp_ranges.push_back(std::make_pair(max_ppem, behavior));
+    this->gasp_ranges.push_back(std::make_pair(max_ppem, behavior));
   }
 
   return true;
 }
 
-bool ots_gasp_should_serialise(Font *font) {
-  return font->gasp != NULL;
-}
-
-bool ots_gasp_serialise(OTSStream *out, Font *font) {
-  const OpenTypeGASP *gasp = font->gasp;
-
-  const uint16_t num_ranges = static_cast<uint16_t>(gasp->gasp_ranges.size());
-  if (num_ranges != gasp->gasp_ranges.size() ||
-      !out->WriteU16(gasp->version) ||
+bool OpenTypeGASP::Serialize(OTSStream *out) {
+  const uint16_t num_ranges = static_cast<uint16_t>(this->gasp_ranges.size());
+  if (num_ranges != this->gasp_ranges.size() ||
+      !out->WriteU16(this->version) ||
       !out->WriteU16(num_ranges)) {
-    return OTS_FAILURE_MSG("failed to write gasp header");
+    return Error("Failed to write table header");
   }
 
   for (uint16_t i = 0; i < num_ranges; ++i) {
-    if (!out->WriteU16(gasp->gasp_ranges[i].first) ||
-        !out->WriteU16(gasp->gasp_ranges[i].second)) {
-      return OTS_FAILURE_MSG("Failed to write gasp subtable %d", i);
+    if (!out->WriteU16(this->gasp_ranges[i].first) ||
+        !out->WriteU16(this->gasp_ranges[i].second)) {
+      return Error("Failed to write GASPRANGE %d", i);
     }
   }
 
   return true;
 }
 
-void ots_gasp_reuse(Font *font, Font *other) {
-  font->gasp = other->gasp;
-  font->gasp_reused = true;
-}
-
-void ots_gasp_free(Font *font) {
-  delete font->gasp;
-}
-
 }  // namespace ots
-
-#undef TABLE_NAME
-#undef DROP_THIS_TABLE
--- a/gfx/ots/src/gasp.h
+++ b/gfx/ots/src/gasp.h
@@ -1,24 +1,32 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef OTS_GASP_H_
 #define OTS_GASP_H_
 
 #include <new>
 #include <utility>
 #include <vector>
 
 #include "ots.h"
 
 namespace ots {
 
-struct OpenTypeGASP {
+class OpenTypeGASP : public Table {
+ public:
+  explicit OpenTypeGASP(Font *font, uint32_t tag)
+      : Table(font, tag, tag) { }
+
+  bool Parse(const uint8_t *data, size_t length);
+  bool Serialize(OTSStream *out);
+
+ private:
   uint16_t version;
   // A array of (max PPEM, GASP behavior) pairs.
   std::vector<std::pair<uint16_t, uint16_t> > gasp_ranges;
 };
 
 }  // namespace ots
 
 #endif  // OTS_GASP_H_
--- a/gfx/ots/src/gdef.cc
+++ b/gfx/ots/src/gdef.cc
@@ -1,373 +1,338 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "gdef.h"
 
 #include <limits>
 #include <vector>
 
 #include "gpos.h"
 #include "gsub.h"
 #include "layout.h"
 #include "maxp.h"
 
 // GDEF - The Glyph Definition Table
 // http://www.microsoft.com/typography/otspec/gdef.htm
 
-#define TABLE_NAME "GDEF"
-
 namespace {
 
 // The maximum class value in class definition tables.
 const uint16_t kMaxClassDefValue = 0xFFFF;
 // The maximum class value in the glyph class definision table.
 const uint16_t kMaxGlyphClassDefValue = 4;
 // The maximum format number of caret value tables.
 // We don't support format 3 for now. See the comment in
 // ParseLigCaretListTable() for the reason.
 const uint16_t kMaxCaretValueFormat = 2;
 
-bool ParseGlyphClassDefTable(ots::Font *font, const uint8_t *data,
-                             size_t length, const uint16_t num_glyphs) {
-  return ots::ParseClassDefTable(font, data, length, num_glyphs,
-                                 kMaxGlyphClassDefValue);
-}
+}  // namespace
 
-bool ParseAttachListTable(ots::Font *font, const uint8_t *data,
-                          size_t length, const uint16_t num_glyphs) {
+namespace ots {
+
+bool OpenTypeGDEF::ParseAttachListTable(const uint8_t *data, size_t length) {
   ots::Buffer subtable(data, length);
 
   uint16_t offset_coverage = 0;
   uint16_t glyph_count = 0;
   if (!subtable.ReadU16(&offset_coverage) ||
       !subtable.ReadU16(&glyph_count)) {
-    return OTS_FAILURE_MSG("Failed to read gdef header");
+    return Error("Failed to read gdef header");
   }
   const unsigned attach_points_end =
       2 * static_cast<unsigned>(glyph_count) + 4;
   if (attach_points_end > std::numeric_limits<uint16_t>::max()) {
-    return OTS_FAILURE_MSG("Bad glyph count in gdef");
+    return Error("Bad glyph count in gdef");
   }
   if (offset_coverage == 0 || offset_coverage >= length ||
       offset_coverage < attach_points_end) {
-    return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage);
+    return Error("Bad coverage offset %d", offset_coverage);
   }
-  if (glyph_count > num_glyphs) {
-    return OTS_FAILURE_MSG("Bad glyph count %u", glyph_count);
+  if (glyph_count > this->m_num_glyphs) {
+    return Error("Bad glyph count %u", glyph_count);
   }
 
   std::vector<uint16_t> attach_points;
   attach_points.resize(glyph_count);
   for (unsigned i = 0; i < glyph_count; ++i) {
     if (!subtable.ReadU16(&attach_points[i])) {
-      return OTS_FAILURE_MSG("Can't read attachment point %d", i);
+      return Error("Can't read attachment point %d", i);
     }
     if (attach_points[i] >= length ||
         attach_points[i] < attach_points_end) {
-      return OTS_FAILURE_MSG("Bad attachment point %d of %d", i, attach_points[i]);
+      return Error("Bad attachment point %d of %d", i, attach_points[i]);
     }
   }
 
   // Parse coverage table
-  if (!ots::ParseCoverageTable(font, data + offset_coverage,
-                               length - offset_coverage, num_glyphs)) {
-    return OTS_FAILURE_MSG("Bad coverage table");
+  if (!ots::ParseCoverageTable(GetFont(), data + offset_coverage,
+                               length - offset_coverage, this->m_num_glyphs)) {
+    return Error("Bad coverage table");
   }
 
   // Parse attach point table
   for (unsigned i = 0; i < attach_points.size(); ++i) {
     subtable.set_offset(attach_points[i]);
     uint16_t point_count = 0;
     if (!subtable.ReadU16(&point_count)) {
-      return OTS_FAILURE_MSG("Can't read point count %d", i);
+      return Error("Can't read point count %d", i);
     }
     if (point_count == 0) {
-      return OTS_FAILURE_MSG("zero point count %d", i);
+      return Error("zero point count %d", i);
     }
     uint16_t last_point_index = 0;
     uint16_t point_index = 0;
     for (unsigned j = 0; j < point_count; ++j) {
       if (!subtable.ReadU16(&point_index)) {
-        return OTS_FAILURE_MSG("Can't read point index %d in point %d", j, i);
+        return Error("Can't read point index %d in point %d", j, i);
       }
       // Contour point indeces are in increasing numerical order
       if (last_point_index != 0 && last_point_index >= point_index) {
-        return OTS_FAILURE_MSG("bad contour indeces: %u >= %u",
+        return Error("bad contour indeces: %u >= %u",
                     last_point_index, point_index);
       }
       last_point_index = point_index;
     }
   }
   return true;
 }
 
-bool ParseLigCaretListTable(ots::Font *font, const uint8_t *data,
-                            size_t length, const uint16_t num_glyphs) {
+bool OpenTypeGDEF::ParseLigCaretListTable(const uint8_t *data, size_t length) {
   ots::Buffer subtable(data, length);
   uint16_t offset_coverage = 0;
   uint16_t lig_glyph_count = 0;
   if (!subtable.ReadU16(&offset_coverage) ||
       !subtable.ReadU16(&lig_glyph_count)) {
-    return OTS_FAILURE_MSG("Can't read caret structure");
+    return Error("Can't read caret structure");
   }
   const unsigned lig_glyphs_end =
       2 * static_cast<unsigned>(lig_glyph_count) + 4;
   if (lig_glyphs_end > std::numeric_limits<uint16_t>::max()) {
-    return OTS_FAILURE_MSG("Bad caret structure");
+    return Error("Bad caret structure");
   }
   if (offset_coverage == 0 || offset_coverage >= length ||
       offset_coverage < lig_glyphs_end) {
-    return OTS_FAILURE_MSG("Bad caret coverate offset %d", offset_coverage);
+    return Error("Bad caret coverate offset %d", offset_coverage);
   }
-  if (lig_glyph_count > num_glyphs) {
-    return OTS_FAILURE_MSG("bad ligature glyph count: %u", lig_glyph_count);
+  if (lig_glyph_count > this->m_num_glyphs) {
+    return Error("bad ligature glyph count: %u", lig_glyph_count);
   }
 
   std::vector<uint16_t> lig_glyphs;
   lig_glyphs.resize(lig_glyph_count);
   for (unsigned i = 0; i < lig_glyph_count; ++i) {
     if (!subtable.ReadU16(&lig_glyphs[i])) {
-      return OTS_FAILURE_MSG("Can't read ligature glyph location %d", i);
+      return Error("Can't read ligature glyph location %d", i);
     }
     if (lig_glyphs[i] >= length || lig_glyphs[i] < lig_glyphs_end) {
-      return OTS_FAILURE_MSG("Bad ligature glyph location %d in glyph %d", lig_glyphs[i], i);
+      return Error("Bad ligature glyph location %d in glyph %d", lig_glyphs[i], i);
     }
   }
 
   // Parse coverage table
-  if (!ots::ParseCoverageTable(font, data + offset_coverage,
-                               length - offset_coverage, num_glyphs)) {
-    return OTS_FAILURE_MSG("Can't parse caret coverage table");
+  if (!ots::ParseCoverageTable(GetFont(), data + offset_coverage,
+                               length - offset_coverage, this->m_num_glyphs)) {
+    return Error("Can't parse caret coverage table");
   }
 
   // Parse ligature glyph table
   for (unsigned i = 0; i < lig_glyphs.size(); ++i) {
     subtable.set_offset(lig_glyphs[i]);
     uint16_t caret_count = 0;
     if (!subtable.ReadU16(&caret_count)) {
-      return OTS_FAILURE_MSG("Can't read caret count for glyph %d", i);
+      return Error("Can't read caret count for glyph %d", i);
     }
     if (caret_count == 0) {
-      return OTS_FAILURE_MSG("bad caret value count: %u", caret_count);
+      return Error("bad caret value count: %u", caret_count);
     }
 
     std::vector<uint16_t> caret_value_offsets;
     caret_value_offsets.resize(caret_count);
     unsigned caret_value_offsets_end = 2 * static_cast<unsigned>(caret_count) + 2;
     for (unsigned j = 0; j < caret_count; ++j) {
       if (!subtable.ReadU16(&caret_value_offsets[j])) {
-        return OTS_FAILURE_MSG("Can't read caret offset %d for glyph %d", j, i);
+        return Error("Can't read caret offset %d for glyph %d", j, i);
       }
       if (caret_value_offsets[j] >= length || caret_value_offsets[j] < caret_value_offsets_end) {
-        return OTS_FAILURE_MSG("Bad caret offset %d for caret %d glyph %d", caret_value_offsets[j], j, i);
+        return Error("Bad caret offset %d for caret %d glyph %d", caret_value_offsets[j], j, i);
       }
     }
 
     // Parse caret values table
     for (unsigned j = 0; j < caret_count; ++j) {
       subtable.set_offset(lig_glyphs[i] + caret_value_offsets[j]);
       uint16_t caret_format = 0;
       if (!subtable.ReadU16(&caret_format)) {
-        return OTS_FAILURE_MSG("Can't read caret values table %d in glyph %d", j, i);
+        return Error("Can't read caret values table %d in glyph %d", j, i);
       }
       // TODO(bashi): We only support caret value format 1 and 2 for now
       // because there are no fonts which contain caret value format 3
       // as far as we investigated.
       if (caret_format == 0 || caret_format > kMaxCaretValueFormat) {
-        return OTS_FAILURE_MSG("bad caret value format: %u", caret_format);
+        return Error("bad caret value format: %u", caret_format);
       }
       // CaretValueFormats contain a 2-byte field which could be
       // arbitrary value.
       if (!subtable.Skip(2)) {
-        return OTS_FAILURE_MSG("Bad caret value table structure %d in glyph %d", j, i);
+        return Error("Bad caret value table structure %d in glyph %d", j, i);
       }
     }
   }
   return true;
 }
 
-bool ParseMarkAttachClassDefTable(ots::Font *font, const uint8_t *data,
-                                  size_t length, const uint16_t num_glyphs) {
-  return ots::ParseClassDefTable(font, data, length, num_glyphs, kMaxClassDefValue);
-}
-
-bool ParseMarkGlyphSetsDefTable(ots::Font *font, const uint8_t *data,
-                                size_t length, const uint16_t num_glyphs) {
+bool OpenTypeGDEF::ParseMarkGlyphSetsDefTable(const uint8_t *data, size_t length) {
   ots::Buffer subtable(data, length);
   uint16_t format = 0;
   uint16_t mark_set_count = 0;
   if (!subtable.ReadU16(&format) ||
       !subtable.ReadU16(&mark_set_count)) {
-    return OTS_FAILURE_MSG("Can' read mark glyph table structure");
+    return Error("Can' read mark glyph table structure");
   }
   if (format != 1) {
-    return OTS_FAILURE_MSG("bad mark glyph set table format: %u", format);
+    return Error("bad mark glyph set table format: %u", format);
   }
 
   const unsigned mark_sets_end = 2 * static_cast<unsigned>(mark_set_count) + 4;
   if (mark_sets_end > std::numeric_limits<uint16_t>::max()) {
-    return OTS_FAILURE_MSG("Bad mark_set %d", mark_sets_end);
+    return Error("Bad mark_set %d", mark_sets_end);
   }
   for (unsigned i = 0; i < mark_set_count; ++i) {
     uint32_t offset_coverage = 0;
     if (!subtable.ReadU32(&offset_coverage)) {
-      return OTS_FAILURE_MSG("Can't read covrage location for mark set %d", i);
+      return Error("Can't read covrage location for mark set %d", i);
     }
     if (offset_coverage >= length ||
         offset_coverage < mark_sets_end) {
-      return OTS_FAILURE_MSG("Bad coverage location %d for mark set %d", offset_coverage, i);
+      return Error("Bad coverage location %d for mark set %d", offset_coverage, i);
     }
-    if (!ots::ParseCoverageTable(font, data + offset_coverage,
-                                 length - offset_coverage, num_glyphs)) {
-      return OTS_FAILURE_MSG("Failed to parse coverage table for mark set %d", i);
+    if (!ots::ParseCoverageTable(GetFont(), data + offset_coverage,
+                                 length - offset_coverage, this->m_num_glyphs)) {
+      return Error("Failed to parse coverage table for mark set %d", i);
     }
   }
-  font->gdef->num_mark_glyph_sets = mark_set_count;
+  this->num_mark_glyph_sets = mark_set_count;
   return true;
 }
 
-}  // namespace
+bool OpenTypeGDEF::Parse(const uint8_t *data, size_t length) {
+  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
+      GetFont()->GetTypedTable(OTS_TAG_MAXP));
 
-namespace ots {
-
-bool ots_gdef_parse(Font *font, const uint8_t *data, size_t length) {
   // Grab the number of glyphs in the font from the maxp table to check
   // GlyphIDs in GDEF table.
-  if (!font->maxp) {
-    return OTS_FAILURE_MSG("No maxp table in font, needed by GDEF");
+  if (!maxp) {
+    return Error("No maxp table in font, needed by GDEF");
   }
-  const uint16_t num_glyphs = font->maxp->num_glyphs;
+  this->m_num_glyphs = maxp->num_glyphs;
 
   Buffer table(data, length);
 
-  OpenTypeGDEF *gdef = new OpenTypeGDEF;
-  font->gdef = gdef;
-
   uint32_t version = 0;
   if (!table.ReadU32(&version)) {
-    return OTS_FAILURE_MSG("Incomplete table");
+    return Error("Incomplete table");
   }
   if (version < 0x00010000 || version == 0x00010001) {
-    return OTS_FAILURE_MSG("Bad version");
+    return Error("Bad version");
   }
 
   if (version >= 0x00010002) {
-    gdef->version_2 = true;
+    this->version_2 = true;
   }
 
   uint16_t offset_glyph_class_def = 0;
   uint16_t offset_attach_list = 0;
   uint16_t offset_lig_caret_list = 0;
   uint16_t offset_mark_attach_class_def = 0;
   if (!table.ReadU16(&offset_glyph_class_def) ||
       !table.ReadU16(&offset_attach_list) ||
       !table.ReadU16(&offset_lig_caret_list) ||
       !table.ReadU16(&offset_mark_attach_class_def)) {
-    return OTS_FAILURE_MSG("Incomplete table");
+    return Error("Incomplete table");
   }
   uint16_t offset_mark_glyph_sets_def = 0;
-  if (gdef->version_2) {
+  if (this->version_2) {
     if (!table.ReadU16(&offset_mark_glyph_sets_def)) {
-      return OTS_FAILURE_MSG("Incomplete table");
+      return Error("Incomplete table");
     }
   }
 
   unsigned gdef_header_end = 4 + 4 * 2;
-  if (gdef->version_2)
+  if (this->version_2)
     gdef_header_end += 2;
 
   // Parse subtables
   if (offset_glyph_class_def) {
     if (offset_glyph_class_def >= length ||
         offset_glyph_class_def < gdef_header_end) {
-      return OTS_FAILURE_MSG("Invalid offset to glyph classes");
+      return Error("Invalid offset to glyph classes");
     }
-    if (!ParseGlyphClassDefTable(font, data + offset_glyph_class_def,
+    if (!ots::ParseClassDefTable(GetFont(), data + offset_glyph_class_def,
                                  length - offset_glyph_class_def,
-                                 num_glyphs)) {
-      return OTS_FAILURE_MSG("Invalid glyph classes");
+                                 this->m_num_glyphs, kMaxGlyphClassDefValue)) {
+      return Error("Invalid glyph classes");
     }
-    gdef->has_glyph_class_def = true;
+    this->has_glyph_class_def = true;
   }
 
   if (offset_attach_list) {
     if (offset_attach_list >= length ||
         offset_attach_list < gdef_header_end) {
-      return OTS_FAILURE_MSG("Invalid offset to attachment list");
+      return Error("Invalid offset to attachment list");
     }
-    if (!ParseAttachListTable(font, data + offset_attach_list,
-                              length - offset_attach_list,
-                              num_glyphs)) {
-      return OTS_FAILURE_MSG("Invalid attachment list");
+    if (!ParseAttachListTable(data + offset_attach_list,
+                              length - offset_attach_list)) {
+      return Error("Invalid attachment list");
     }
   }
 
   if (offset_lig_caret_list) {
     if (offset_lig_caret_list >= length ||
         offset_lig_caret_list < gdef_header_end) {
-      return OTS_FAILURE_MSG("Invalid offset to ligature caret list");
+      return Error("Invalid offset to ligature caret list");
     }
-    if (!ParseLigCaretListTable(font, data + offset_lig_caret_list,
-                              length - offset_lig_caret_list,
-                              num_glyphs)) {
-      return OTS_FAILURE_MSG("Invalid ligature caret list");
+    if (!ParseLigCaretListTable(data + offset_lig_caret_list,
+                                length - offset_lig_caret_list)) {
+      return Error("Invalid ligature caret list");
     }
   }
 
   if (offset_mark_attach_class_def) {
     if (offset_mark_attach_class_def >= length ||
         offset_mark_attach_class_def < gdef_header_end) {
-      return OTS_FAILURE_MSG("Invalid offset to mark attachment list");
+      return Error("Invalid offset to mark attachment list");
     }
-    if (!ParseMarkAttachClassDefTable(font,
-                                      data + offset_mark_attach_class_def,
-                                      length - offset_mark_attach_class_def,
-                                      num_glyphs)) {
-      return OTS_FAILURE_MSG("Invalid mark attachment list");
+    if (!ots::ParseClassDefTable(GetFont(),
+                                 data + offset_mark_attach_class_def,
+                                 length - offset_mark_attach_class_def,
+                                 this->m_num_glyphs, kMaxClassDefValue)) {
+      return Error("Invalid mark attachment list");
     }
-    gdef->has_mark_attachment_class_def = true;
+    this->has_mark_attachment_class_def = true;
   }
 
   if (offset_mark_glyph_sets_def) {
     if (offset_mark_glyph_sets_def >= length ||
         offset_mark_glyph_sets_def < gdef_header_end) {
-      return OTS_FAILURE_MSG("invalid offset to mark glyph sets");
+      return Error("invalid offset to mark glyph sets");
     }
-    if (!ParseMarkGlyphSetsDefTable(font,
-                                    data + offset_mark_glyph_sets_def,
-                                    length - offset_mark_glyph_sets_def,
-                                    num_glyphs)) {
-      return OTS_FAILURE_MSG("Invalid mark glyph sets");
+    if (!ParseMarkGlyphSetsDefTable(data + offset_mark_glyph_sets_def,
+                                    length - offset_mark_glyph_sets_def)) {
+      return Error("Invalid mark glyph sets");
     }
-    gdef->has_mark_glyph_sets_def = true;
+    this->has_mark_glyph_sets_def = true;
   }
-  gdef->data = data;
-  gdef->length = length;
+  this->m_data = data;
+  this->m_length = length;
   return true;
 }
 
-bool ots_gdef_should_serialise(Font *font) {
-  return font->gdef != NULL && font->gdef->data != NULL;
-}
-
-bool ots_gdef_serialise(OTSStream *out, Font *font) {
-  if (!out->Write(font->gdef->data, font->gdef->length)) {
-    return OTS_FAILURE_MSG("Failed to write GDEF table");
+bool OpenTypeGDEF::Serialize(OTSStream *out) {
+  if (!out->Write(this->m_data, this->m_length)) {
+    return Error("Failed to write table");
   }
 
   return true;
 }
 
-void ots_gdef_reuse(Font *font, Font *other) {
-  font->gdef = other->gdef;
-  font->gdef_reused = true;
-}
-
-void ots_gdef_free(Font *font) {
-  delete font->gdef;
-}
-
 }  // namespace ots
-
-#undef TABLE_NAME
--- a/gfx/ots/src/gdef.h
+++ b/gfx/ots/src/gdef.h
@@ -1,36 +1,48 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef OTS_GDEF_H_
 #define OTS_GDEF_H_
 
 #include "ots.h"
 
 namespace ots {
 
-struct OpenTypeGDEF {
-  OpenTypeGDEF()
-      : version_2(false),
+class OpenTypeGDEF : public Table {
+ public:
+  explicit OpenTypeGDEF(Font *font, uint32_t tag)
+      : Table(font, tag, tag),
+        version_2(false),
         has_glyph_class_def(false),
         has_mark_attachment_class_def(false),
         has_mark_glyph_sets_def(false),
         num_mark_glyph_sets(0),
-        data(NULL),
-        length(0) {
+        m_data(NULL),
+        m_length(0),
+        m_num_glyphs(0) {
   }
 
+  bool Parse(const uint8_t *data, size_t length);
+  bool Serialize(OTSStream *out);
+
   bool version_2;
   bool has_glyph_class_def;
   bool has_mark_attachment_class_def;
   bool has_mark_glyph_sets_def;
   uint16_t num_mark_glyph_sets;
 
-  const uint8_t *data;
-  size_t length;
+ private:
+  bool ParseAttachListTable(const uint8_t *data, size_t length);
+  bool ParseLigCaretListTable(const uint8_t *data, size_t length);
+  bool ParseMarkGlyphSetsDefTable(const uint8_t *data, size_t length);
+
+  const uint8_t *m_data;
+  size_t m_length;
+  uint16_t m_num_glyphs;
 };
 
 }  // namespace ots
 
 #endif
 
--- a/gfx/ots/src/glyf.cc
+++ b/gfx/ots/src/glyf.cc
@@ -1,303 +1,345 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "glyf.h"
 
 #include <algorithm>
 #include <limits>
 
 #include "head.h"
 #include "loca.h"
 #include "maxp.h"
 
 // glyf - Glyph Data
 // http://www.microsoft.com/typography/otspec/glyf.htm
 
-#define TABLE_NAME "glyf"
-
-namespace {
+namespace ots {
 
-bool ParseFlagsForSimpleGlyph(ots::Font *font,
-                              ots::Buffer *table,
-                              uint32_t gly_length,
-                              uint32_t num_flags,
-                              uint32_t *flags_count_logical,
-                              uint32_t *flags_count_physical,
-                              uint32_t *xy_coordinates_length) {
+bool OpenTypeGLYF::ParseFlagsForSimpleGlyph(Buffer &glyph,
+                                            uint32_t num_flags,
+                                            uint32_t *flag_index,
+                                            uint32_t *coordinates_length) {
   uint8_t flag = 0;
-  if (!table->ReadU8(&flag)) {
-    return OTS_FAILURE_MSG("Can't read flag");
+  if (!glyph.ReadU8(&flag)) {
+    return Error("Can't read flag");
   }
 
   uint32_t delta = 0;
   if (flag & (1u << 1)) {  // x-Short
     ++delta;
   } else if (!(flag & (1u << 4))) {
     delta += 2;
   }
 
   if (flag & (1u << 2)) {  // y-Short
     ++delta;
   } else if (!(flag & (1u << 5))) {
     delta += 2;
   }
 
   if (flag & (1u << 3)) {  // repeat
-    if (*flags_count_logical + 1 >= num_flags) {
-      return OTS_FAILURE_MSG("Count too high (%d + 1 >= %d)", *flags_count_logical, num_flags);
+    if (*flag_index + 1 >= num_flags) {
+      return Error("Count too high (%d + 1 >= %d)", *flag_index, num_flags);
     }
     uint8_t repeat = 0;
-    if (!table->ReadU8(&repeat)) {
-      return OTS_FAILURE_MSG("Can't read repeat value");
+    if (!glyph.ReadU8(&repeat)) {
+      return Error("Can't read repeat value");
     }
     if (repeat == 0) {
-      return OTS_FAILURE_MSG("Zero repeat");
+      return Error("Zero repeat");
     }
     delta += (delta * repeat);
 
-    *flags_count_logical += repeat;
-    if (*flags_count_logical >= num_flags) {
-      return OTS_FAILURE_MSG("Count too high (%d >= %d)", *flags_count_logical, num_flags);
+    *flag_index += repeat;
+    if (*flag_index >= num_flags) {
+      return Error("Count too high (%d >= %d)", *flag_index, num_flags);
     }
-    ++(*flags_count_physical);
   }
 
   if ((flag & (1u << 6)) || (flag & (1u << 7))) {  // reserved flags
-    return OTS_FAILURE_MSG("Bad glyph flag value (%d), reserved flags must be set to zero", flag);
+    return Error("Bad glyph flag value (%d), reserved flags must be set to zero", flag);
   }
 
-  *xy_coordinates_length += delta;
-  if (gly_length < *xy_coordinates_length) {
-    return OTS_FAILURE_MSG("Glyph coordinates length too low (%d < %d)", gly_length, *xy_coordinates_length);
+  *coordinates_length += delta;
+  if (glyph.length() < *coordinates_length) {
+    return Error("Glyph coordinates length bigger than glyph length (%d > %d)",
+                 *coordinates_length, glyph.length());
   }
 
   return true;
 }
 
-bool ParseSimpleGlyph(ots::Font *font, const uint8_t *data,
-                      ots::Buffer *table, int16_t num_contours,
-                      uint32_t gly_offset, uint32_t gly_length,
-                      uint32_t *new_size) {
-  ots::OpenTypeGLYF *glyf = font->glyf;
-
+bool OpenTypeGLYF::ParseSimpleGlyph(Buffer &glyph,
+                                    int16_t num_contours) {
   // read the end-points array
   uint16_t num_flags = 0;
   for (int i = 0; i < num_contours; ++i) {
     uint16_t tmp_index = 0;
-    if (!table->ReadU16(&tmp_index)) {
-      return OTS_FAILURE_MSG("Can't read contour index %d", i);
+    if (!glyph.ReadU16(&tmp_index)) {
+      return Error("Can't read contour index %d", i);
     }
     if (tmp_index == 0xffffu) {
-      return OTS_FAILURE_MSG("Bad contour index %d", i);
+      return Error("Bad contour index %d", i);
     }
     // check if the indices are monotonically increasing
     if (i && (tmp_index + 1 <= num_flags)) {
-      return OTS_FAILURE_MSG("Decreasing contour index %d + 1 <= %d", tmp_index, num_flags);
+      return Error("Decreasing contour index %d + 1 <= %d", tmp_index, num_flags);
     }
     num_flags = tmp_index + 1;
   }
 
   uint16_t bytecode_length = 0;
-  if (!table->ReadU16(&bytecode_length)) {
-    return OTS_FAILURE_MSG("Can't read bytecode length");
-  }
-  if ((font->maxp->version_1) &&
-      (font->maxp->max_size_glyf_instructions < bytecode_length)) {
-    return OTS_FAILURE_MSG("Bytecode length too high %d", bytecode_length);
-  }
-
-  const uint32_t gly_header_length = 10 + num_contours * 2 + 2;
-  if (gly_length < (gly_header_length + bytecode_length)) {
-    return OTS_FAILURE_MSG("Glyph header length too high %d", gly_header_length);
+  if (!glyph.ReadU16(&bytecode_length)) {
+    return Error("Can't read bytecode length");
   }
 
-  glyf->iov.push_back(std::make_pair(
-      data + gly_offset,
-      static_cast<size_t>(gly_header_length + bytecode_length)));
-
-  if (!table->Skip(bytecode_length)) {
-    return OTS_FAILURE_MSG("Can't skip bytecode of length %d", bytecode_length);
+  if (this->maxp->version_1 &&
+      this->maxp->max_size_glyf_instructions < bytecode_length) {
+    return Error("Bytecode length is bigger than maxp.maxSizeOfInstructions "
+        "%d: %d", this->maxp->max_size_glyf_instructions, bytecode_length);
   }
 
-  uint32_t flags_count_physical = 0;  // on memory
-  uint32_t xy_coordinates_length = 0;
-  for (uint32_t flags_count_logical = 0;
-       flags_count_logical < num_flags;
-       ++flags_count_logical, ++flags_count_physical) {
-    if (!ParseFlagsForSimpleGlyph(font,
-                                  table,
-                                  gly_length,
-                                  num_flags,
-                                  &flags_count_logical,
-                                  &flags_count_physical,
-                                  &xy_coordinates_length)) {
-      return OTS_FAILURE_MSG("Failed to parse glyph flags %d", flags_count_logical);
+  if (!glyph.Skip(bytecode_length)) {
+    return Error("Can't read bytecode of length %d", bytecode_length);
+  }
+
+  uint32_t coordinates_length = 0;
+  for (uint32_t i = 0; i < num_flags; ++i) {
+    if (!ParseFlagsForSimpleGlyph(glyph, num_flags, &i, &coordinates_length)) {
+      return Error("Failed to parse glyph flags %d", i);
     }
   }
 
-  if (gly_length < (gly_header_length + bytecode_length +
-                    flags_count_physical + xy_coordinates_length)) {
-    return OTS_FAILURE_MSG("Glyph too short %d", gly_length);
+  if (!glyph.Skip(coordinates_length)) {
+    return Error("Glyph too short %d", glyph.length());
   }
 
-  if (gly_length - (gly_header_length + bytecode_length +
-                    flags_count_physical + xy_coordinates_length) > 3) {
+  if (glyph.remaining() > 3) {
     // We allow 0-3 bytes difference since gly_length is 4-bytes aligned,
     // zero-padded length.
-    return OTS_FAILURE_MSG("Invalid glyph length %d", gly_length);
+    Warning("Extra bytes at end of the glyph: %d", glyph.remaining());
   }
 
-  glyf->iov.push_back(std::make_pair(
-      data + gly_offset + gly_header_length + bytecode_length,
-      static_cast<size_t>(flags_count_physical + xy_coordinates_length)));
-
-  *new_size
-      = gly_header_length + flags_count_physical + xy_coordinates_length + bytecode_length;
+  this->iov.push_back(std::make_pair(glyph.buffer(), glyph.offset()));
 
   return true;
 }
 
-}  // namespace
+#define ARG_1_AND_2_ARE_WORDS    (1u << 0)
+#define WE_HAVE_A_SCALE          (1u << 3)
+#define MORE_COMPONENTS          (1u << 5)
+#define WE_HAVE_AN_X_AND_Y_SCALE (1u << 6)
+#define WE_HAVE_A_TWO_BY_TWO     (1u << 7)
+#define WE_HAVE_INSTRUCTIONS     (1u << 8)
+
+bool OpenTypeGLYF::ParseCompositeGlyph(Buffer &glyph) {
+  uint16_t flags = 0;
+  uint16_t gid = 0;
+  do {
+    if (!glyph.ReadU16(&flags) || !glyph.ReadU16(&gid)) {
+      return Error("Can't read composite glyph flags or glyphIndex");
+    }
 
-namespace ots {
+    if (gid >= this->maxp->num_glyphs) {
+      return Error("Invalid glyph id used in composite glyph: %d", gid);
+    }
+
+    if (flags & ARG_1_AND_2_ARE_WORDS) {
+      int16_t argument1;
+      int16_t argument2;
+      if (!glyph.ReadS16(&argument1) || !glyph.ReadS16(&argument2)) {
+        return Error("Can't read argument1 or argument2");
+      }
+    } else {
+      uint8_t argument1;
+      uint8_t argument2;
+      if (!glyph.ReadU8(&argument1) || !glyph.ReadU8(&argument2)) {
+        return Error("Can't read argument1 or argument2");
+      }
+    }
 
-bool ots_glyf_parse(Font *font, const uint8_t *data, size_t length) {
-  Buffer table(data, length);
+    if (flags & WE_HAVE_A_SCALE) {
+      int16_t scale;
+      if (!glyph.ReadS16(&scale)) {
+        return Error("Can't read scale");
+      }
+    } else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) {
+      int16_t xscale;
+      int16_t yscale;
+      if (!glyph.ReadS16(&xscale) || !glyph.ReadS16(&yscale)) {
+        return Error("Can't read xscale or yscale");
+      }
+    } else if (flags & WE_HAVE_A_TWO_BY_TWO) {
+      int16_t xscale;
+      int16_t scale01;
+      int16_t scale10;
+      int16_t yscale;
+      if (!glyph.ReadS16(&xscale) ||
+          !glyph.ReadS16(&scale01) ||
+          !glyph.ReadS16(&scale10) ||
+          !glyph.ReadS16(&yscale)) {
+        return Error("Can't read transform");
+      }
+    }
+  } while (flags & MORE_COMPONENTS);
 
-  if (!font->maxp || !font->loca || !font->head) {
-    return OTS_FAILURE_MSG("Missing maxp or loca or head table needed by glyf table");
+  if (flags & WE_HAVE_INSTRUCTIONS) {
+    uint16_t bytecode_length;
+    if (!glyph.ReadU16(&bytecode_length)) {
+      return Error("Can't read instructions size");
+    }
+
+    if (this->maxp->version_1 &&
+        this->maxp->max_size_glyf_instructions < bytecode_length) {
+      return Error("Bytecode length is bigger than maxp.maxSizeOfInstructions "
+                   "%d: %d",
+                   this->maxp->max_size_glyf_instructions, bytecode_length);
+    }
+
+    if (!glyph.Skip(bytecode_length)) {
+      return Error("Can't read bytecode of length %d", bytecode_length);
+    }
   }
 
-  OpenTypeGLYF *glyf = new OpenTypeGLYF;
-  font->glyf = glyf;
+  this->iov.push_back(std::make_pair(glyph.buffer(), glyph.offset()));
+
+  return true;
+}
 
-  const unsigned num_glyphs = font->maxp->num_glyphs;
-  std::vector<uint32_t> &offsets = font->loca->offsets;
+bool OpenTypeGLYF::Parse(const uint8_t *data, size_t length) {
+  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
+      GetFont()->GetTypedTable(OTS_TAG_MAXP));
+  OpenTypeLOCA *loca = static_cast<OpenTypeLOCA*>(
+      GetFont()->GetTypedTable(OTS_TAG_LOCA));
+  OpenTypeHEAD *head = static_cast<OpenTypeHEAD*>(
+      GetFont()->GetTypedTable(OTS_TAG_HEAD));
+  if (!maxp || !loca || !head) {
+    return Error("Missing maxp or loca or head table needed by glyf table");
+  }
+
+  this->maxp = maxp;
+
+  const unsigned num_glyphs = maxp->num_glyphs;
+  std::vector<uint32_t> &offsets = loca->offsets;
 
   if (offsets.size() != num_glyphs + 1) {
-    return OTS_FAILURE_MSG("Invalide glyph offsets size %ld != %d", offsets.size(), num_glyphs + 1);
+    return Error("Invalide glyph offsets size %ld != %d", offsets.size(), num_glyphs + 1);
   }
 
   std::vector<uint32_t> resulting_offsets(num_glyphs + 1);
   uint32_t current_offset = 0;
 
   for (unsigned i = 0; i < num_glyphs; ++i) {
     const unsigned gly_offset = offsets[i];
     // The LOCA parser checks that these values are monotonic
     const unsigned gly_length = offsets[i + 1] - offsets[i];
     if (!gly_length) {
       // this glyph has no outline (e.g. the space charactor)
       resulting_offsets[i] = current_offset;
       continue;
     }
 
     if (gly_offset >= length) {
-      return OTS_FAILURE_MSG("Glyph %d offset %d too high %ld", i, gly_offset, length);
+      return Error("Glyph %d offset %d too high %ld", i, gly_offset, length);
     }
     // Since these are unsigned types, the compiler is not allowed to assume
     // that they never overflow.
     if (gly_offset + gly_length < gly_offset) {
-      return OTS_FAILURE_MSG("Glyph %d length (%d < 0)!", i, gly_length);
+      return Error("Glyph %d length (%d < 0)!", i, gly_length);
     }
     if (gly_offset + gly_length > length) {
-      return OTS_FAILURE_MSG("Glyph %d length %d too high", i, gly_length);
+      return Error("Glyph %d length %d too high", i, gly_length);
     }
 
-    table.set_offset(gly_offset);
+    Buffer glyph(data + gly_offset, gly_length);
+
     int16_t num_contours, xmin, ymin, xmax, ymax;
-    if (!table.ReadS16(&num_contours) ||
-        !table.ReadS16(&xmin) ||
-        !table.ReadS16(&ymin) ||
-        !table.ReadS16(&xmax) ||
-        !table.ReadS16(&ymax)) {
-      return OTS_FAILURE_MSG("Can't read glyph %d header", i);
+    if (!glyph.ReadS16(&num_contours) ||
+        !glyph.ReadS16(&xmin) ||
+        !glyph.ReadS16(&ymin) ||
+        !glyph.ReadS16(&xmax) ||
+        !glyph.ReadS16(&ymax)) {
+      return Error("Can't read glyph %d header", i);
     }
 
     if (num_contours <= -2) {
       // -2, -3, -4, ... are reserved for future use.
-      return OTS_FAILURE_MSG("Bad number of contours %d in glyph %d", num_contours, i);
+      return Error("Bad number of contours %d in glyph %d", num_contours, i);
     }
 
     // workaround for fonts in http://www.princexml.com/fonts/
     if ((xmin == 32767) &&
         (xmax == -32767) &&
         (ymin == 32767) &&
         (ymax == -32767)) {
-      OTS_WARNING("bad xmin/xmax/ymin/ymax values");
+      Warning("bad xmin/xmax/ymin/ymax values");
       xmin = xmax = ymin = ymax = 0;
     }
 
     if (xmin > xmax || ymin > ymax) {
-      return OTS_FAILURE_MSG("Bad bounding box values bl=(%d, %d), tr=(%d, %d) in glyph %d", xmin, ymin, xmax, ymax, i);
+      return Error("Bad bounding box values bl=(%d, %d), tr=(%d, %d) in glyph %d", xmin, ymin, xmax, ymax, i);
     }
 
-    unsigned new_size = 0;
-    if (num_contours >= 0) {
-      // this is a simple glyph and might contain bytecode
-      if (!ParseSimpleGlyph(font, data, &table,
-                            num_contours, gly_offset, gly_length, &new_size)) {
-        return OTS_FAILURE_MSG("Failed to parse glyph %d", i);
+    if (num_contours == 0) {
+      // This is an empty glyph and shouldn’t have any glyph data, but if it
+      // does we will simply ignore it.
+      glyph.set_offset(0);
+    } else if (num_contours > 0) {
+      if (!ParseSimpleGlyph(glyph, num_contours)) {
+        return Error("Failed to parse glyph %d", i);
       }
     } else {
-      // it's a composite glyph without any bytecode. Enqueue the whole thing
-      glyf->iov.push_back(std::make_pair(data + gly_offset,
-                                         static_cast<size_t>(gly_length)));
-      new_size = gly_length;
+      if (!ParseCompositeGlyph(glyph)) {
+        return Error("Failed to parse glyph %d", i);
+      }
     }
 
+    size_t new_size = glyph.offset();
     resulting_offsets[i] = current_offset;
     // glyphs must be four byte aligned
     // TODO(yusukes): investigate whether this padding is really necessary.
     //                Which part of the spec requires this?
     const unsigned padding = (4 - (new_size & 3)) % 4;
     if (padding) {
-      glyf->iov.push_back(std::make_pair(
+      this->iov.push_back(std::make_pair(
           reinterpret_cast<const uint8_t*>("\x00\x00\x00\x00"),
           static_cast<size_t>(padding)));
       new_size += padding;
     }
     current_offset += new_size;
   }
   resulting_offsets[num_glyphs] = current_offset;
 
   const uint16_t max16 = std::numeric_limits<uint16_t>::max();
   if ((*std::max_element(resulting_offsets.begin(),
                          resulting_offsets.end()) >= (max16 * 2u)) &&
-      (font->head->index_to_loc_format != 1)) {
-    OTS_WARNING("2-bytes indexing is not possible (due to the padding above)");
-    font->head->index_to_loc_format = 1;
+      (head->index_to_loc_format != 1)) {
+    head->index_to_loc_format = 1;
   }
 
-  font->loca->offsets = resulting_offsets;
+  loca->offsets = resulting_offsets;
+
+  if (this->iov.empty()) {
+    // As a special case when all glyph in the font are empty, add a zero byte
+    // to the table, so that we don’t reject it down the way, and to make the
+    // table work on Windows as well.
+    // See https://github.com/khaledhosny/ots/issues/52
+    static const uint8_t kZero = 0;
+    this->iov.push_back(std::make_pair(&kZero, 1));
+  }
+
   return true;
 }
 
-bool ots_glyf_should_serialise(Font *font) {
-  return font->glyf != NULL;
-}
-
-bool ots_glyf_serialise(OTSStream *out, Font *font) {
-  const OpenTypeGLYF *glyf = font->glyf;
-
-  for (unsigned i = 0; i < glyf->iov.size(); ++i) {
-    if (!out->Write(glyf->iov[i].first, glyf->iov[i].second)) {
-      return OTS_FAILURE_MSG("Falied to write glyph %d", i);
+bool OpenTypeGLYF::Serialize(OTSStream *out) {
+  for (unsigned i = 0; i < this->iov.size(); ++i) {
+    if (!out->Write(this->iov[i].first, this->iov[i].second)) {
+      return Error("Falied to write glyph %d", i);
     }
   }
 
   return true;
 }
 
-void ots_glyf_reuse(Font *font, Font *other) {
-  font->glyf = other->glyf;
-  font->glyf_reused = true;
-}
-
-void ots_glyf_free(Font *font) {
-  delete font->glyf;
-}
-
 }  // namespace ots
-
-#undef TABLE_NAME
--- a/gfx/ots/src/glyf.h
+++ b/gfx/ots/src/glyf.h
@@ -1,22 +1,40 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef OTS_GLYF_H_
 #define OTS_GLYF_H_
 
 #include <new>
 #include <utility>
 #include <vector>
 
 #include "ots.h"
 
 namespace ots {
+class OpenTypeMAXP;
 
-struct OpenTypeGLYF {
+class OpenTypeGLYF : public Table {
+ public:
+  explicit OpenTypeGLYF(Font *font, uint32_t tag)
+      : Table(font, tag, tag), maxp(NULL) { }
+
+  bool Parse(const uint8_t *data, size_t length);
+  bool Serialize(OTSStream *out);
+
+ private:
+  bool ParseFlagsForSimpleGlyph(Buffer &glyph,
+                                uint32_t num_flags,
+                                uint32_t *flag_index,
+                                uint32_t *coordinates_length);
+  bool ParseSimpleGlyph(Buffer &glyph, int16_t num_contours);
+  bool ParseCompositeGlyph(Buffer &glyph);
+
+  OpenTypeMAXP* maxp;
+
   std::vector<std::pair<const uint8_t*, size_t> > iov;
 };
 
 }  // namespace ots
 
 #endif  // OTS_GLYF_H_
--- a/gfx/ots/src/gpos.cc
+++ b/gfx/ots/src/gpos.cc
@@ -1,9 +1,9 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "gpos.h"
 
 #include <limits>
 #include <vector>
 
@@ -71,19 +71,33 @@ const ots::LookupSubtableParser::TypePar
 
 const ots::LookupSubtableParser kGposLookupSubtableParser = {
   arraysize(kGposTypeParsers),
   GPOS_TYPE_EXTENSION_POSITIONING, kGposTypeParsers
 };
 
 // Shared Tables: ValueRecord, Anchor Table, and MarkArray
 
+size_t CalcValueRecordSize(const uint16_t value_format) {
+  size_t size = 0;
+  for (unsigned i = 0; i < 8; ++i) {
+    if ((value_format >> i) & 0x1) {
+      size += 2;
+    }
+  }
+
+  return size;
+}
+
 bool ParseValueRecord(const ots::Font *font,
-                      ots::Buffer* subtable, const uint8_t *data,
-                      const size_t length, const uint16_t value_format) {
+                      ots::Buffer* subtable,
+                      const uint16_t value_format) {
+  const uint8_t *data = subtable->buffer();
+  const size_t length = subtable->length();
+
   // Check existence of adjustment fields.
   for (unsigned i = 0; i < 4; ++i) {
     if ((value_format >> i) & 0x1) {
       // Just read the field since these fileds could take an arbitrary values.
       if (!subtable->Skip(2)) {
         return OTS_FAILURE_MSG("Failed to read value reacord component");
       }
     }
@@ -202,51 +216,57 @@ bool ParseMarkArrayTable(const ots::Font
 }
 
 // Lookup Type 1:
 // Single Adjustment Positioning Subtable
 bool ParseSingleAdjustment(const ots::Font *font, const uint8_t *data,
                            const size_t length) {
   ots::Buffer subtable(data, length);
 
+  ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
+      font->GetTypedTable(OTS_TAG_MAXP));
+  if (!maxp) {
+    return OTS_FAILURE_MSG("Required maxp table missing");
+  }
+
   uint16_t format = 0;
   uint16_t offset_coverage = 0;
   uint16_t value_format = 0;
   if (!subtable.ReadU16(&format) ||
       !subtable.ReadU16(&offset_coverage) ||
       !subtable.ReadU16(&value_format)) {
     return OTS_FAILURE_MSG("Can't read single adjustment information");
   }
 
   if (format == 1) {
     // Format 1 exactly one value record.
-    if (!ParseValueRecord(font, &subtable, data, length, value_format)) {
+    if (!ParseValueRecord(font, &subtable, value_format)) {
       return OTS_FAILURE_MSG("Failed to parse format 1 single adjustment table");
     }
   } else if (format == 2) {
     uint16_t value_count = 0;
     if (!subtable.ReadU16(&value_count)) {
       return OTS_FAILURE_MSG("Failed to parse format 2 single adjustment table");
     }
     for (unsigned i = 0; i < value_count; ++i) {
-      if (!ParseValueRecord(font, &subtable, data, length, value_format)) {
+      if (!ParseValueRecord(font, &subtable, value_format)) {
         return OTS_FAILURE_MSG("Failed to parse value record %d in format 2 single adjustment table", i);
       }
     }
   } else {
     return OTS_FAILURE_MSG("Bad format %d in single adjustment table", format);
   }
 
   if (offset_coverage < subtable.offset() || offset_coverage >= length) {
     return OTS_FAILURE_MSG("Bad coverage offset %d in single adjustment table", offset_coverage);
   }
 
   if (!ots::ParseCoverageTable(font, data + offset_coverage,
                                length - offset_coverage,
-                               font->maxp->num_glyphs)) {
+                               maxp->num_glyphs)) {
     return OTS_FAILURE_MSG("Failed to parse coverage table in single adjustment table");
   }
 
   return true;
 }
 
 bool ParsePairSetTable(const ots::Font *font,
                        const uint8_t *data, const size_t length,
@@ -263,20 +283,20 @@ bool ParsePairSetTable(const ots::Font *
     // Check pair value record.
     uint16_t glyph_id = 0;
     if (!subtable.ReadU16(&glyph_id)) {
       return OTS_FAILURE_MSG("Failed to read glyph in pair value record %d", i);
     }
     if (glyph_id >= num_glyphs) {
       return OTS_FAILURE_MSG("glyph id %d too high >= %d", glyph_id, num_glyphs);
     }
-    if (!ParseValueRecord(font, &subtable, data, length, value_format1)) {
+    if (!ParseValueRecord(font, &subtable, value_format1)) {
       return OTS_FAILURE_MSG("Failed to parse value record in format 1 pair set table");
     }
-    if (!ParseValueRecord(font, &subtable, data, length, value_format2)) {
+    if (!ParseValueRecord(font, &subtable, value_format2)) {
       return OTS_FAILURE_MSG("Failed to parse value record in format 2 pair set table");
     }
   }
   return true;
 }
 
 bool ParsePairPosFormat1(const ots::Font *font,
                          const uint8_t *data, const size_t length,
@@ -336,36 +356,46 @@ bool ParsePairPosFormat2(const ots::Font
   uint16_t class2_count = 0;
   if (!subtable.ReadU16(&offset_class_def1) ||
       !subtable.ReadU16(&offset_class_def2) ||
       !subtable.ReadU16(&class1_count) ||
       !subtable.ReadU16(&class2_count)) {
     return OTS_FAILURE_MSG("Failed to read pair pos format 2 data");
   }
 
+  size_t value_record1_size = CalcValueRecordSize(value_format1);
+  size_t value_record2_size = CalcValueRecordSize(value_format2);
+  size_t value_records_size = size_t(class1_count) * size_t(class2_count) *
+    (value_record1_size + value_record2_size);
+
+  // Check the validity of class definition offsets.
+  if (offset_class_def1 < subtable.offset() + value_records_size ||
+      offset_class_def2 < subtable.offset() + value_records_size ||
+      offset_class_def1 >= length || offset_class_def2 >= length) {
+    return OTS_FAILURE_MSG("Bad ParsePairPosFormat2 class definition offsets %d or %d", offset_class_def1, offset_class_def2);
+  }
+
   // Check class 1 records.
-  for (unsigned i = 0; i < class1_count; ++i) {
-    // Check class 2 records.
-    for (unsigned j = 0; j < class2_count; ++j) {
-      if (value_format1 && !ParseValueRecord(font, &subtable, data, length,
-                                             value_format1)) {
-        return OTS_FAILURE_MSG("Failed to parse value record 1 %d and %d", j, i);
-      }
-      if (value_format2 && !ParseValueRecord(font, &subtable, data, length,
-                                             value_format2)) {
-        return OTS_FAILURE_MSG("Falied to parse value record 2 %d and %d", j, i);
+  if (value_record1_size || value_record2_size) {
+    for (unsigned i = 0; i < class1_count; ++i) {
+      // Check class 2 records.
+      for (unsigned j = 0; j < class2_count; ++j) {
+        if (value_format1 && value_record2_size &&
+            !ParseValueRecord(font, &subtable, value_format1)) {
+          return OTS_FAILURE_MSG("Failed to parse value record 1 %d and %d", j, i);
+        }
+        if (value_format2 && value_record2_size &&
+            !ParseValueRecord(font, &subtable, value_format2)) {
+          return OTS_FAILURE_MSG("Falied to parse value record 2 %d and %d", j, i);
+        }
       }
     }
   }
 
   // Check class definition tables.
-  if (offset_class_def1 < subtable.offset() || offset_class_def1 >= length ||
-      offset_class_def2 < subtable.offset() || offset_class_def2 >= length) {
-    return OTS_FAILURE_MSG("Bad class definition table offsets %d or %d", offset_class_def1, offset_class_def2);
-  }
   if (!ots::ParseClassDefTable(font, data + offset_class_def1,
                                length - offset_class_def1,
                                num_glyphs, kMaxClassDefValue)) {
     return OTS_FAILURE_MSG("Failed to parse class definition table 1");
   }
   if (!ots::ParseClassDefTable(font, data + offset_class_def2,
                                length - offset_class_def2,
                                num_glyphs, kMaxClassDefValue)) {
@@ -376,59 +406,71 @@ bool ParsePairPosFormat2(const ots::Font
 }
 
 // Lookup Type 2:
 // Pair Adjustment Positioning Subtable
 bool ParsePairAdjustment(const ots::Font *font, const uint8_t *data,
                          const size_t length) {
   ots::Buffer subtable(data, length);
 
+  ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
+      font->GetTypedTable(OTS_TAG_MAXP));
+  if (!maxp) {
+    return OTS_FAILURE_MSG("Required maxp table missing");
+  }
+
   uint16_t format = 0;
   uint16_t offset_coverage = 0;
   uint16_t value_format1 = 0;
   uint16_t value_format2 = 0;
   if (!subtable.ReadU16(&format) ||
       !subtable.ReadU16(&offset_coverage) ||
       !subtable.ReadU16(&value_format1) ||
       !subtable.ReadU16(&value_format2)) {
     return OTS_FAILURE_MSG("Failed to read pair adjustment structure");
   }
 
   if (format == 1) {
     if (!ParsePairPosFormat1(font, data, length, value_format1, value_format2,
-                             font->maxp->num_glyphs)) {
+                             maxp->num_glyphs)) {
       return OTS_FAILURE_MSG("Failed to parse pair pos format 1");
     }
   } else if (format == 2) {
     if (!ParsePairPosFormat2(font, data, length, value_format1, value_format2,
-                             font->maxp->num_glyphs)) {
+                             maxp->num_glyphs)) {
       return OTS_FAILURE_MSG("Failed to parse pair format 2");
     }
   } else {
     return OTS_FAILURE_MSG("Bad pos pair format %d", format);
   }
 
   if (offset_coverage < subtable.offset() || offset_coverage >= length) {
     return OTS_FAILURE_MSG("Bad pair pos offset coverage %d", offset_coverage);
   }
   if (!ots::ParseCoverageTable(font, data + offset_coverage,
                                length - offset_coverage,
-                               font->maxp->num_glyphs)) {
+                               maxp->num_glyphs)) {
     return OTS_FAILURE_MSG("Failed to parse coverage table");
   }
 
   return true;
 }
 
 // Lookup Type 3
 // Cursive Attachment Positioning Subtable
 bool ParseCursiveAttachment(const ots::Font *font, const uint8_t *data,
                             const size_t length) {
   ots::Buffer subtable(data, length);
 
+  ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
+      font->GetTypedTable(OTS_TAG_MAXP));
+  if (!maxp) {
+    return OTS_FAILURE_MSG("Required maxp table missing");
+  }
+
   uint16_t format = 0;
   uint16_t offset_coverage = 0;
   uint16_t entry_exit_count = 0;
   if (!subtable.ReadU16(&format) ||
       !subtable.ReadU16(&offset_coverage) ||
       !subtable.ReadU16(&entry_exit_count)) {
     return OTS_FAILURE_MSG("Failed to read cursive attachment structure");
   }
@@ -473,17 +515,17 @@ bool ParseCursiveAttachment(const ots::F
     }
   }
 
   if (offset_coverage < subtable.offset() || offset_coverage >= length) {
     return OTS_FAILURE_MSG("Bad coverage offset in cursive attachment %d", offset_coverage);
   }
   if (!ots::ParseCoverageTable(font, data + offset_coverage,
                                length - offset_coverage,
-                               font->maxp->num_glyphs)) {
+                               maxp->num_glyphs)) {
     return OTS_FAILURE_MSG("Failed to parse coverage table in cursive attachment");
   }
 
   return true;
 }
 
 bool ParseAnchorArrayTable(const ots::Font *font,
                            const uint8_t *data, const size_t length,
@@ -547,16 +589,22 @@ bool ParseLigatureArrayTable(const ots::
 }
 
 // Common parser for Lookup Type 4, 5 and 6.
 bool ParseMarkToAttachmentSubtables(const ots::Font *font,
                                     const uint8_t *data, const size_t length,
                                     const GPOS_TYPE type) {
   ots::Buffer subtable(data, length);
 
+  ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
+      font->GetTypedTable(OTS_TAG_MAXP));
+  if (!maxp) {
+    return OTS_FAILURE_MSG("Required maxp table missing");
+  }
+
   uint16_t format = 0;
   uint16_t offset_coverage1 = 0;
   uint16_t offset_coverage2 = 0;
   uint16_t class_count = 0;
   uint16_t offset_mark_array = 0;
   uint16_t offset_type_specific_array = 0;
   if (!subtable.ReadU16(&format) ||
       !subtable.ReadU16(&offset_coverage1) ||
@@ -575,25 +623,25 @@ bool ParseMarkToAttachmentSubtables(cons
   if (header_end > std::numeric_limits<uint16_t>::max()) {
     return OTS_FAILURE_MSG("Bad mark attachment subtable size ending at %d", header_end);
   }
   if (offset_coverage1 < header_end || offset_coverage1 >= length) {
     return OTS_FAILURE_MSG("Bad coverage 1 offset %d", offset_coverage1);
   }
   if (!ots::ParseCoverageTable(font, data + offset_coverage1,
                                length - offset_coverage1,
-                               font->maxp->num_glyphs)) {
+                               maxp->num_glyphs)) {
     return OTS_FAILURE_MSG("Failed to parse converge 1 table");
   }
   if (offset_coverage2 < header_end || offset_coverage2 >= length) {
     return OTS_FAILURE_MSG("Bad coverage 2 offset %d", offset_coverage2);
   }
   if (!ots::ParseCoverageTable(font, data + offset_coverage2,
                                length - offset_coverage2,
-                               font->maxp->num_glyphs)) {
+                               maxp->num_glyphs)) {
     return OTS_FAILURE_MSG("Failed to parse coverage table 2");
   }
 
   if (offset_mark_array < header_end || offset_mark_array >= length) {
     return OTS_FAILURE_MSG("Bad mark array offset %d", offset_mark_array);
   }
   if (!ParseMarkArrayTable(font, data + offset_mark_array,
                            length - offset_mark_array, class_count)) {
@@ -647,171 +695,125 @@ bool ParseMarkToMarkAttachment(const ots
   return ParseMarkToAttachmentSubtables(font, data, length,
                                         GPOS_TYPE_MARK_TO_MARK_ATTACHMENT);
 }
 
 // Lookup Type 7:
 // Contextual Positioning Subtables
 bool ParseContextPositioning(const ots::Font *font,
                              const uint8_t *data, const size_t length) {
-  return ots::ParseContextSubtable(font, data, length, font->maxp->num_glyphs,
-                                   font->gpos->num_lookups);
+  ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
+      font->GetTypedTable(OTS_TAG_MAXP));
+  if (!maxp) {
+    return OTS_FAILURE_MSG("Required maxp table missing");
+  }
+  ots::OpenTypeGPOS *gpos = static_cast<ots::OpenTypeGPOS*>(
+      font->GetTypedTable(OTS_TAG_GPOS));
+  if (!gpos) {
+    return OTS_FAILURE_MSG("Internal error!");
+  }
+  return ots::ParseContextSubtable(font, data, length, maxp->num_glyphs,
+                                   gpos->num_lookups);
 }
 
 // Lookup Type 8:
 // Chaining Contexual Positioning Subtable
 bool ParseChainedContextPositioning(const ots::Font *font,
                                     const uint8_t *data, const size_t length) {
+  ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
+      font->GetTypedTable(OTS_TAG_MAXP));
+  if (!maxp) {
+    return OTS_FAILURE_MSG("Required maxp table missing");
+  }
+  ots::OpenTypeGPOS *gpos = static_cast<ots::OpenTypeGPOS*>(
+      font->GetTypedTable(OTS_TAG_GPOS));
+  if (!gpos) {
+    return OTS_FAILURE_MSG("Internal error!");
+  }
   return ots::ParseChainingContextSubtable(font, data, length,
-                                           font->maxp->num_glyphs,
-                                           font->gpos->num_lookups);
+                                           maxp->num_glyphs,
+                                           gpos->num_lookups);
 }
 
 // Lookup Type 9:
 // Extension Positioning
 bool ParseExtensionPositioning(const ots::Font *font,
                                const uint8_t *data, const size_t length) {
   return ots::ParseExtensionSubtable(font, data, length,
                                      &kGposLookupSubtableParser);
 }
 
 }  // namespace
 
 namespace ots {
 
-// As far as I checked, following fonts contain invalid GPOS table and
-// OTS will drop their GPOS table.
-//
-// # invalid delta format in device table
-// samanata.ttf
-//
-// # bad size range in device table
-// Sarai_07.ttf
-//
-// # bad offset to PairSetTable
-// chandas1-2.ttf
-//
-// # bad offset to FeatureTable
-// glrso12.ttf
-// gllr12.ttf
-// glbo12.ttf
-// glb12.ttf
-// glro12.ttf
-// glbso12.ttf
-// glrc12.ttf
-// glrsc12.ttf
-// glbs12.ttf
-// glrs12.ttf
-// glr12.ttf
-//
-// # ScriptRecords aren't sorted by tag
-// Garogier_unhinted.otf
-//
-// # bad start coverage index in CoverageFormat2
-// AndBasR.ttf
-// CharisSILB.ttf
-// CharisSILBI.ttf
-// CharisSILI.ttf
-// CharisSILR.ttf
-// DoulosSILR.ttf
-// GenBasBI.ttf
-// GenBasI.ttf
-// GenBkBasI.ttf
-// GenBkBasB.ttf
-// GenBkBasR.ttf
-// Padauk-Bold.ttf
-// Padauk.ttf
-//
-// # Contour point indexes aren't sorted
-// Arial Unicode.ttf
-
-bool ots_gpos_parse(Font *font, const uint8_t *data, size_t length) {
-  // Parsing GPOS table requires num_glyphs which is contained in maxp table.
-  if (!font->maxp) {
-    return OTS_FAILURE_MSG("missing maxp table needed in GPOS");
-  }
-
+bool OpenTypeGPOS::Parse(const uint8_t *data, size_t length) {
+  Font *font = GetFont();
   Buffer table(data, length);
 
-  OpenTypeGPOS *gpos = new OpenTypeGPOS;
-  font->gpos = gpos;
-
   uint32_t version = 0;
   uint16_t offset_script_list = 0;
   uint16_t offset_feature_list = 0;
   uint16_t offset_lookup_list = 0;
   if (!table.ReadU32(&version) ||
       !table.ReadU16(&offset_script_list) ||
       !table.ReadU16(&offset_feature_list) ||
       !table.ReadU16(&offset_lookup_list)) {
-    return OTS_FAILURE_MSG("Incomplete table");
+    return Error("Incomplete table");
   }
 
   if (version != 0x00010000) {
-    return OTS_FAILURE_MSG("Bad version");
+    return Error("Bad version");
   }
 
   if (offset_lookup_list) {
     if (offset_lookup_list < kGposHeaderSize || offset_lookup_list >= length) {
-      return OTS_FAILURE_MSG("Bad lookup list offset in table header");
+      return Error("Bad lookup list offset in table header");
     }
 
     if (!ParseLookupListTable(font, data + offset_lookup_list,
                               length - offset_lookup_list,
                               &kGposLookupSubtableParser,
-                              &gpos->num_lookups)) {
-      return OTS_FAILURE_MSG("Failed to parse lookup list table");
+                              &this->num_lookups)) {
+      return Error("Failed to parse lookup list table");
     }
   }
 
   uint16_t num_features = 0;
   if (offset_feature_list) {
     if (offset_feature_list < kGposHeaderSize || offset_feature_list >= length) {
-      return OTS_FAILURE_MSG("Bad feature list offset in table header");
+      return Error("Bad feature list offset in table header");
     }
 
     if (!ParseFeatureListTable(font, data + offset_feature_list,
-                               length - offset_feature_list, gpos->num_lookups,
+                               length - offset_feature_list, this->num_lookups,
                                &num_features)) {
-      return OTS_FAILURE_MSG("Failed to parse feature list table");
+      return Error("Failed to parse feature list table");
     }
   }
 
   if (offset_script_list) {
     if (offset_script_list < kGposHeaderSize || offset_script_list >= length) {
-      return OTS_FAILURE_MSG("Bad script list offset in table header");
+      return Error("Bad script list offset in table header");
     }
 
     if (!ParseScriptListTable(font, data + offset_script_list,
                               length - offset_script_list, num_features)) {
-      return OTS_FAILURE_MSG("Failed to parse script list table");
+      return Error("Failed to parse script list table");
     }
   }
 
-  gpos->data = data;
-  gpos->length = length;
+  this->m_data = data;
+  this->m_length = length;
   return true;
 }
 
-bool ots_gpos_should_serialise(Font *font) {
-  return font->gpos != NULL && font->gpos->data != NULL;
-}
-
-bool ots_gpos_serialise(OTSStream *out, Font *font) {
-  if (!out->Write(font->gpos->data, font->gpos->length)) {
-    return OTS_FAILURE_MSG("Failed to write GPOS table");
+bool OpenTypeGPOS::Serialize(OTSStream *out) {
+  if (!out->Write(this->m_data, this->m_length)) {
+    return Error("Failed to write GPOS table");
   }
 
   return true;
 }
 
-void ots_gpos_reuse(Font *font, Font *other) {
-  font->gpos = other->gpos;
-  font->gpos_reused = true;
-}
-
-void ots_gpos_free(Font *font) {
-  delete font->gpos;
-}
-
 }  // namespace ots
 
 #undef TABLE_NAME
--- a/gfx/ots/src/gpos.h
+++ b/gfx/ots/src/gpos.h
@@ -1,29 +1,35 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef OTS_GPOS_H_
 #define OTS_GPOS_H_
 
 #include "ots.h"
 
 namespace ots {
 
-struct OpenTypeGPOS {
-  OpenTypeGPOS()
-      : num_lookups(0),
-        data(NULL),
-        length(0) {
+class OpenTypeGPOS : public Table {
+ public:
+  explicit OpenTypeGPOS(Font *font, uint32_t tag)
+      : Table(font, tag, tag),
+        num_lookups(0),
+        m_data(NULL),
+        m_length(0) {
   }
 
+  bool Parse(const uint8_t *data, size_t length);
+  bool Serialize(OTSStream *out);
+
   // Number of lookups in GPOS table
   uint16_t num_lookups;
 
-  const uint8_t *data;
-  size_t length;
+ private:
+  const uint8_t *m_data;
+  size_t m_length;
 };
 
 }  // namespace ots
 
 #endif
 
--- a/gfx/ots/src/gsub.cc
+++ b/gfx/ots/src/gsub.cc
@@ -1,9 +1,9 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "gsub.h"
 
 #include <limits>
 #include <vector>
 
@@ -77,17 +77,22 @@ bool ParseSingleSubstitution(const ots::
   uint16_t format = 0;
   uint16_t offset_coverage = 0;
 
   if (!subtable.ReadU16(&format) ||
       !subtable.ReadU16(&offset_coverage)) {
     return OTS_FAILURE_MSG("Failed to read single subst table header");
   }
 
-  const uint16_t num_glyphs = font->maxp->num_glyphs;
+  ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
+      font->GetTypedTable(OTS_TAG_MAXP));
+  if (!maxp) {
+    return OTS_FAILURE_MSG("Required maxp table missing");
+  }
+  const uint16_t num_glyphs = maxp->num_glyphs;
   if (format == 1) {
     // Parse SingleSubstFormat1
     int16_t delta_glyph_id = 0;
     if (!subtable.ReadS16(&delta_glyph_id)) {
       return OTS_FAILURE_MSG("Failed to read glyph shift from format 1 single subst table");
     }
     if (std::abs(delta_glyph_id) >= num_glyphs) {
       return OTS_FAILURE_MSG("bad glyph shift of %d in format 1 single subst table", delta_glyph_id);
@@ -165,17 +170,22 @@ bool ParseMutipleSubstitution(const ots:
       !subtable.ReadU16(&sequence_count)) {
     return OTS_FAILURE_MSG("Can't read header of multiple subst table");
   }
 
   if (format != 1) {
     return OTS_FAILURE_MSG("Bad multiple subst table format %d", format);
   }
 
-  const uint16_t num_glyphs = font->maxp->num_glyphs;
+  ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
+      font->GetTypedTable(OTS_TAG_MAXP));
+  if (!maxp) {
+    return OTS_FAILURE_MSG("Required maxp table missing");
+  }
+  const uint16_t num_glyphs = maxp->num_glyphs;
   const unsigned sequence_end = static_cast<unsigned>(6) +
       sequence_count * 2;
   if (sequence_end > std::numeric_limits<uint16_t>::max()) {
     return OTS_FAILURE_MSG("Bad segence end %d, in multiple subst", sequence_end);
   }
   for (unsigned i = 0; i < sequence_count; ++i) {
     uint16_t offset_sequence = 0;
     if (!subtable.ReadU16(&offset_sequence)) {
@@ -240,17 +250,22 @@ bool ParseAlternateSubstitution(const ot
       !subtable.ReadU16(&alternate_set_count)) {
     return OTS_FAILURE_MSG("Can't read alternate subst header");
   }
 
   if (format != 1) {
     return OTS_FAILURE_MSG("Bad alternate subst table format %d", format);
   }
 
-  const uint16_t num_glyphs = font->maxp->num_glyphs;
+  ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
+      font->GetTypedTable(OTS_TAG_MAXP));
+  if (!maxp) {
+    return OTS_FAILURE_MSG("Required maxp table missing");
+  }
+  const uint16_t num_glyphs = maxp->num_glyphs;
   const unsigned alternate_set_end = static_cast<unsigned>(6) +
       alternate_set_count * 2;
   if (alternate_set_end > std::numeric_limits<uint16_t>::max()) {
     return OTS_FAILURE_MSG("Bad end of alternate set %d", alternate_set_end);
   }
   for (unsigned i = 0; i < alternate_set_count; ++i) {
     uint16_t offset_alternate_set = 0;
     if (!subtable.ReadU16(&offset_alternate_set)) {
@@ -357,17 +372,22 @@ bool ParseLigatureSubstitution(const ots
       !subtable.ReadU16(&lig_set_count)) {
     return OTS_FAILURE_MSG("Failed to read ligature substitution header");
   }
 
   if (format != 1) {
     return OTS_FAILURE_MSG("Bad ligature substitution table format %d", format);
   }
 
-  const uint16_t num_glyphs = font->maxp->num_glyphs;
+  ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
+      font->GetTypedTable(OTS_TAG_MAXP));
+  if (!maxp) {
+    return OTS_FAILURE_MSG("Required maxp table missing");
+  }
+  const uint16_t num_glyphs = maxp->num_glyphs;
   const unsigned ligature_set_end = static_cast<unsigned>(6) +
       lig_set_count * 2;
   if (ligature_set_end > std::numeric_limits<uint16_t>::max()) {
     return OTS_FAILURE_MSG("Bad end of ligature set %d in ligature substitution table", ligature_set_end);
   }
   for (unsigned i = 0; i < lig_set_count; ++i) {
     uint16_t offset_ligature_set = 0;
     if (!subtable.ReadU16(&offset_ligature_set)) {
@@ -393,28 +413,48 @@ bool ParseLigatureSubstitution(const ots
 
   return true;
 }
 
 // Lookup Type 5:
 // Contextual Substitution Subtable
 bool ParseContextSubstitution(const ots::Font *font,
                               const uint8_t *data, const size_t length) {
-  return ots::ParseContextSubtable(font, data, length, font->maxp->num_glyphs,
-                                   font->gsub->num_lookups);
+  ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
+      font->GetTypedTable(OTS_TAG_MAXP));
+  if (!maxp) {
+    return OTS_FAILURE_MSG("Required maxp table missing");
+  }
+  ots::OpenTypeGSUB *gsub = static_cast<ots::OpenTypeGSUB*>(
+      font->GetTypedTable(OTS_TAG_GSUB));
+  if (!gsub) {
+    return OTS_FAILURE_MSG("Internal error!");
+  }
+  return ots::ParseContextSubtable(font, data, length, maxp->num_glyphs,
+                                   gsub->num_lookups);
 }
 
 // Lookup Type 6:
 // Chaining Contextual Substitution Subtable
 bool ParseChainingContextSubstitution(const ots::Font *font,
                                       const uint8_t *data,
                                       const size_t length) {
+  ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
+      font->GetTypedTable(OTS_TAG_MAXP));
+  if (!maxp) {
+    return OTS_FAILURE_MSG("Required maxp table missing");
+  }
+  ots::OpenTypeGSUB *gsub = static_cast<ots::OpenTypeGSUB*>(
+      font->GetTypedTable(OTS_TAG_GSUB));
+  if (!gsub) {
+    return OTS_FAILURE_MSG("Internal error!");
+  }
   return ots::ParseChainingContextSubtable(font, data, length,
-                                           font->maxp->num_glyphs,
-                                           font->gsub->num_lookups);
+                                           maxp->num_glyphs,
+                                           gsub->num_lookups);
 }
 
 // Lookup Type 7:
 // Extension Substition
 bool ParseExtensionSubstitution(const ots::Font *font,
                                 const uint8_t *data, const size_t length) {
   return ots::ParseExtensionSubtable(font, data, length,
                                      &kGsubLookupSubtableParser);
@@ -429,17 +469,22 @@ bool ParseReverseChainingContextSingleSu
   uint16_t format = 0;
   uint16_t offset_coverage = 0;
 
   if (!subtable.ReadU16(&format) ||
       !subtable.ReadU16(&offset_coverage)) {
     return OTS_FAILURE_MSG("Failed to read reverse chaining header");
   }
 
-  const uint16_t num_glyphs = font->maxp->num_glyphs;
+  ots::OpenTypeMAXP *maxp = static_cast<ots::OpenTypeMAXP*>(
+      font->GetTypedTable(OTS_TAG_MAXP));
+  if (!maxp) {
+    return OTS_FAILURE_MSG("Required maxp table missing");
+  }
+  const uint16_t num_glyphs = maxp->num_glyphs;
 
   uint16_t backtrack_glyph_count = 0;
   if (!subtable.ReadU16(&backtrack_glyph_count)) {
     return OTS_FAILURE_MSG("Failed to read backtrack glyph count in reverse chaining table");
   }
   if (backtrack_glyph_count > num_glyphs) {
     return OTS_FAILURE_MSG("Bad backtrack glyph count of %d", backtrack_glyph_count);
   }
@@ -525,150 +570,81 @@ bool ParseReverseChainingContextSingleSu
 
   return true;
 }
 
 }  // namespace
 
 namespace ots {
 
-// As far as I checked, following fonts contain invalid values in GSUB table.
-// OTS will drop their GSUB table.
-//
-// # too large substitute (value is 0xFFFF)
-// kaiu.ttf
-// mingliub2.ttf
-// mingliub1.ttf
-// mingliub0.ttf
-// GraublauWeb.otf
-// GraublauWebBold.otf
-//
-// # too large alternate (value is 0xFFFF)
-// ManchuFont.ttf
-//
-// # bad offset to lang sys table (NULL offset)
-// DejaVuMonoSansBold.ttf
-// DejaVuMonoSansBoldOblique.ttf
-// DejaVuMonoSansOblique.ttf
-// DejaVuSansMono-BoldOblique.ttf
-// DejaVuSansMono-Oblique.ttf
-// DejaVuSansMono-Bold.ttf
-//
-// # bad start coverage index
-// GenBasBI.ttf
-// GenBasI.ttf
-// AndBasR.ttf
-// GenBkBasI.ttf
-// CharisSILR.ttf
-// CharisSILBI.ttf
-// CharisSILI.ttf
-// CharisSILB.ttf
-// DoulosSILR.ttf
-// CharisSILBI.ttf
-// GenBkBasB.ttf
-// GenBkBasR.ttf
-// GenBkBasBI.ttf
-// GenBasB.ttf
-// GenBasR.ttf
-//
-// # glyph range is overlapping
-// KacstTitleL.ttf
-// KacstDecorative.ttf
-// KacstTitle.ttf
-// KacstArt.ttf
-// KacstPoster.ttf
-// KacstQurn.ttf
-// KacstDigital.ttf
-// KacstBook.ttf
-// KacstFarsi.ttf
-
-bool ots_gsub_parse(Font *font, const uint8_t *data, size_t length) {
-  // Parsing gsub table requires |font->maxp->num_glyphs|
-  if (!font->maxp) {
-    return OTS_FAILURE_MSG("Missing maxp table in font, needed by GSUB");
-  }
-
+bool OpenTypeGSUB::Parse(const uint8_t *data, size_t length) {
+  // Parsing gsub table requires |maxp->num_glyphs|
+  Font *font = GetFont();
   Buffer table(data, length);
 
-  OpenTypeGSUB *gsub = new OpenTypeGSUB;
-  font->gsub = gsub;
-
   uint32_t version = 0;
   uint16_t offset_script_list = 0;
   uint16_t offset_feature_list = 0;
   uint16_t offset_lookup_list = 0;
   if (!table.ReadU32(&version) ||
       !table.ReadU16(&offset_script_list) ||
       !table.ReadU16(&offset_feature_list) ||
       !table.ReadU16(&offset_lookup_list)) {
-    return OTS_FAILURE_MSG("Incomplete table");
+    return Error("Incomplete table");
   }
 
   if (version != 0x00010000) {
-    return OTS_FAILURE_MSG("Bad version");
+    return Error("Bad version");
   }
 
   if (offset_lookup_list) {
     if (offset_lookup_list < kGsubHeaderSize || offset_lookup_list >= length) {
-      return OTS_FAILURE_MSG("Bad lookup list offset in table header");
+      return Error("Bad lookup list offset in table header");
     }
 
     if (!ParseLookupListTable(font, data + offset_lookup_list,
                               length - offset_lookup_list,
                               &kGsubLookupSubtableParser,
-                              &gsub->num_lookups)) {
-      return OTS_FAILURE_MSG("Failed to parse lookup list table");
+                              &this->num_lookups)) {
+      return Error("Failed to parse lookup list table");
     }
   }
 
   uint16_t num_features = 0;
   if (offset_feature_list) {
     if (offset_feature_list < kGsubHeaderSize || offset_feature_list >= length) {
-      return OTS_FAILURE_MSG("Bad feature list offset in table header");
+      return Error("Bad feature list offset in table header");
     }
 
     if (!ParseFeatureListTable(font, data + offset_feature_list,
-                               length - offset_feature_list, gsub->num_lookups,
+                               length - offset_feature_list, this->num_lookups,
                                &num_features)) {
-      return OTS_FAILURE_MSG("Failed to parse feature list table");
+      return Error("Failed to parse feature list table");
     }
   }
 
   if (offset_script_list) {
     if (offset_script_list < kGsubHeaderSize || offset_script_list >= length) {
-      return OTS_FAILURE_MSG("Bad script list offset in table header");
+      return Error("Bad script list offset in table header");
     }
 
     if (!ParseScriptListTable(font, data + offset_script_list,
                               length - offset_script_list, num_features)) {
-      return OTS_FAILURE_MSG("Failed to parse script list table");
+      return Error("Failed to parse script list table");
     }
   }
 
-  gsub->data = data;
-  gsub->length = length;
+  this->m_data = data;
+  this->m_length = length;
   return true;
 }
 
-bool ots_gsub_should_serialise(Font *font) {
-  return font->gsub != NULL && font->gsub->data != NULL;
-}
-
-bool ots_gsub_serialise(OTSStream *out, Font *font) {
-  if (!out->Write(font->gsub->data, font->gsub->length)) {
-    return OTS_FAILURE_MSG("Failed to write GSUB table");
+bool OpenTypeGSUB::Serialize(OTSStream *out) {
+  if (!out->Write(this->m_data, this->m_length)) {
+    return Error("Failed to write GSUB table");
   }
 
   return true;
 }
 
-void ots_gsub_reuse(Font *font, Font *other) {
-  font->gsub = other->gsub;
-  font->gsub_reused = true;
-}
-
-void ots_gsub_free(Font *font) {
-  delete font->gsub;
-}
-
 }  // namespace ots
 
 #undef TABLE_NAME
--- a/gfx/ots/src/gsub.h
+++ b/gfx/ots/src/gsub.h
@@ -1,29 +1,35 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef OTS_GSUB_H_
 #define OTS_GSUB_H_
 
 #include "ots.h"
 
 namespace ots {
 
-struct OpenTypeGSUB {
-  OpenTypeGSUB()
-      : num_lookups(0),
-        data(NULL),
-        length(0) {
+class OpenTypeGSUB : public Table {
+ public:
+  explicit OpenTypeGSUB(Font *font, uint32_t tag)
+      : Table(font, tag, tag),
+        num_lookups(0),
+        m_data(NULL),
+        m_length(0) {
   }
 
+  bool Parse(const uint8_t *data, size_t length);
+  bool Serialize(OTSStream *out);
+
   // Number of lookups in GPSUB table
   uint16_t num_lookups;
 
-  const uint8_t *data;
-  size_t length;
+ //private:
+  const uint8_t *m_data;
+  size_t m_length;
 };
 
 }  // namespace ots
 
 #endif  // OTS_GSUB_H_
 
--- a/gfx/ots/src/hdmx.cc
+++ b/gfx/ots/src/hdmx.cc
@@ -1,147 +1,120 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "hdmx.h"
 #include "head.h"
 #include "maxp.h"
 
 // hdmx - Horizontal Device Metrics
 // http://www.microsoft.com/typography/otspec/hdmx.htm
 
-#define TABLE_NAME "hdmx"
-
-#define DROP_THIS_TABLE(...) \
-  do { \
-    OTS_FAILURE_MSG_(font->file, TABLE_NAME ": " __VA_ARGS__); \
-    OTS_FAILURE_MSG("Table discarded"); \
-    delete font->hdmx; \
-    font->hdmx = 0; \
-  } while (0)
-
 namespace ots {
 
-bool ots_hdmx_parse(Font *font, const uint8_t *data, size_t length) {
+bool OpenTypeHDMX::Parse(const uint8_t *data, size_t length) {
   Buffer table(data, length);
-  font->hdmx = new OpenTypeHDMX;
-  OpenTypeHDMX * const hdmx = font->hdmx;
 
-  if (!font->head || !font->maxp) {
-    return OTS_FAILURE_MSG("Missing maxp or head tables in font, needed by hdmx");
+  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
+      GetFont()->GetTypedTable(OTS_TAG_MAXP));
+  OpenTypeHEAD *head = static_cast<OpenTypeHEAD*>(
+      GetFont()->GetTypedTable(OTS_TAG_HEAD));
+  if (!head || !maxp) {
+    return Error("Missing maxp or head tables in font, needed by hdmx");
   }
 
-  if ((font->head->flags & 0x14) == 0) {
-    // http://www.microsoft.com/typography/otspec/recom.htm
-    DROP_THIS_TABLE("the table should not be present when bit 2 and 4 of the "
-                    "head->flags are not set");
-    return true;
+  if ((head->flags & 0x14) == 0) {
+    // http://www.microsoft.com/typography/otspec/recom.htm#hdmx
+    return Drop("the table should not be present when bit 2 and 4 of the "
+                "head->flags are not set");
   }
 
   int16_t num_recs;
-  if (!table.ReadU16(&hdmx->version) ||
+  if (!table.ReadU16(&this->version) ||
       !table.ReadS16(&num_recs) ||
-      !table.ReadS32(&hdmx->size_device_record)) {
-    return OTS_FAILURE_MSG("Failed to read hdmx header");
+      !table.ReadS32(&this->size_device_record)) {
+    return Error("Failed to read table header");
   }
-  if (hdmx->version != 0) {
-    DROP_THIS_TABLE("bad version: %u", hdmx->version);
-    return true;
+  if (this->version != 0) {
+    return Drop("Unsupported version: %u", this->version);
   }
   if (num_recs <= 0) {
-    DROP_THIS_TABLE("bad num_recs: %d", num_recs);
-    return true;
+    return Drop("Bad numRecords: %d", num_recs);
   }
-  const int32_t actual_size_device_record = font->maxp->num_glyphs + 2;
-  if (hdmx->size_device_record < actual_size_device_record) {
-    DROP_THIS_TABLE("bad hdmx->size_device_record: %d", hdmx->size_device_record);
-    return true;
+  const int32_t actual_size_device_record = maxp->num_glyphs + 2;
+  if (this->size_device_record < actual_size_device_record) {
+    return Drop("Bad sizeDeviceRecord: %d", this->size_device_record);
   }
 
-  hdmx->pad_len = hdmx->size_device_record - actual_size_device_record;
-  if (hdmx->pad_len > 3) {
-    return OTS_FAILURE_MSG("Bad padding %d", hdmx->pad_len);
+  this->pad_len = this->size_device_record - actual_size_device_record;
+  if (this->pad_len > 3) {
+    return Error("Bad DeviceRecord padding %d", this->pad_len);
   }
 
   uint8_t last_pixel_size = 0;
-  hdmx->records.reserve(num_recs);
+  this->records.reserve(num_recs);
   for (int i = 0; i < num_recs; ++i) {
     OpenTypeHDMXDeviceRecord rec;
 
     if (!table.ReadU8(&rec.pixel_size) ||
         !table.ReadU8(&rec.max_width)) {
-      return OTS_FAILURE_MSG("Failed to read hdmx record %d", i);
+      return Error("Failed to read DeviceRecord %d", i);
     }
     if ((i != 0) &&
         (rec.pixel_size <= last_pixel_size)) {
-      DROP_THIS_TABLE("records are not sorted");
-      return true;
+      return Drop("DeviceRecord's are not sorted");
     }
     last_pixel_size = rec.pixel_size;
 
-    rec.widths.reserve(font->maxp->num_glyphs);
-    for (unsigned j = 0; j < font->maxp->num_glyphs; ++j) {
+    rec.widths.reserve(maxp->num_glyphs);
+    for (unsigned j = 0; j < maxp->num_glyphs; ++j) {
       uint8_t width;
       if (!table.ReadU8(&width)) {
-        return OTS_FAILURE_MSG("Failed to read glyph width %d in record %d", j, i);
+        return Error("Failed to read glyph width %d in DeviceRecord %d", j, i);
       }
       rec.widths.push_back(width);
     }
 
-    if ((hdmx->pad_len > 0) &&
-        !table.Skip(hdmx->pad_len)) {
-      return OTS_FAILURE_MSG("Failed to skip padding %d", hdmx->pad_len);
+    if ((this->pad_len > 0) &&
+        !table.Skip(this->pad_len)) {
+      return Error("DeviceRecord %d should be padded by %d", i, this->pad_len);
     }
 
-    hdmx->records.push_back(rec);
+    this->records.push_back(rec);
   }
 
   return true;
 }
 
-bool ots_hdmx_should_serialise(Font *font) {
-  if (!font->hdmx) return false;
-  if (!font->glyf) return false;  // this table is not for CFF fonts.
-  return true;
+bool OpenTypeHDMX::ShouldSerialize() {
+  return Table::ShouldSerialize() &&
+         // this table is not for CFF fonts.
+         GetFont()->GetTable(OTS_TAG_GLYF) != NULL;
 }
 
-bool ots_hdmx_serialise(OTSStream *out, Font *font) {
-  OpenTypeHDMX * const hdmx = font->hdmx;
-
-  const int16_t num_recs = static_cast<int16_t>(hdmx->records.size());
-  if (hdmx->records.size() >
+bool OpenTypeHDMX::Serialize(OTSStream *out) {
+  const int16_t num_recs = static_cast<int16_t>(this->records.size());
+  if (this->records.size() >
           static_cast<size_t>(std::numeric_limits<int16_t>::max()) ||
-      !out->WriteU16(hdmx->version) ||
+      !out->WriteU16(this->version) ||
       !out->WriteS16(num_recs) ||
-      !out->WriteS32(hdmx->size_device_record)) {
-    return OTS_FAILURE_MSG("Failed to write hdmx header");
+      !out->WriteS32(this->size_device_record)) {
+    return Error("Failed to write table header");
   }
 
   for (int16_t i = 0; i < num_recs; ++i) {
-    const OpenTypeHDMXDeviceRecord& rec = hdmx->records[i];
+    const OpenTypeHDMXDeviceRecord& rec = this->records[i];
     if (!out->Write(&rec.pixel_size, 1) ||
         !out->Write(&rec.max_width, 1) ||
         !out->Write(&rec.widths[0], rec.widths.size())) {
-      return OTS_FAILURE_MSG("Failed to write hdmx record %d", i);
+      return Error("Failed to write DeviceRecord %d", i);
     }
-    if ((hdmx->pad_len > 0) &&
-        !out->Write((const uint8_t *)"\x00\x00\x00", hdmx->pad_len)) {
-      return OTS_FAILURE_MSG("Failed to write hdmx padding of length %d", hdmx->pad_len);
+    if ((this->pad_len > 0) &&
+        !out->Write((const uint8_t *)"\x00\x00\x00", this->pad_len)) {
+      return Error("Failed to write padding of length %d", this->pad_len);
     }
   }
 
   return true;
 }
 
-void ots_hdmx_reuse(Font *font, Font *other) {
-  font->hdmx = other->hdmx;
-  font->hdmx_reused = true;
-}
-
-void ots_hdmx_free(Font *font) {
-  delete font->hdmx;
-}
-
 }  // namespace ots
-
-#undef TABLE_NAME
-#undef DROP_THIS_TABLE
--- a/gfx/ots/src/hdmx.h
+++ b/gfx/ots/src/hdmx.h
@@ -1,9 +1,9 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef OTS_HDMX_H_
 #define OTS_HDMX_H_
 
 #include <vector>
 
@@ -12,17 +12,26 @@
 namespace ots {
 
 struct OpenTypeHDMXDeviceRecord {
   uint8_t pixel_size;
   uint8_t max_width;
   std::vector<uint8_t> widths;
 };
 
-struct OpenTypeHDMX {
+class OpenTypeHDMX : public Table {
+ public:
+  explicit OpenTypeHDMX(Font *font, uint32_t tag)
+      : Table(font, tag, tag) { }
+
+  bool Parse(const uint8_t *data, size_t length);
+  bool Serialize(OTSStream *out);
+  bool ShouldSerialize();
+
+ private:
   uint16_t version;
   int32_t size_device_record;
   int32_t pad_len;
   std::vector<OpenTypeHDMXDeviceRecord> records;
 };
 
 }  // namespace ots
 
--- a/gfx/ots/src/head.cc
+++ b/gfx/ots/src/head.cc
@@ -1,159 +1,132 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "head.h"
 
 #include <cstring>
 
 // head - Font Header
 // http://www.microsoft.com/typography/otspec/head.htm
 
-#define TABLE_NAME "head"
-
 namespace ots {
 
-bool ots_head_parse(Font* font, const uint8_t *data, size_t length) {
+bool OpenTypeHEAD::Parse(const uint8_t *data, size_t length) {
   Buffer table(data, length);
-  OpenTypeHEAD *head = new OpenTypeHEAD;
-  font->head = head;
 
   uint32_t version = 0;
   if (!table.ReadU32(&version) ||
-      !table.ReadU32(&head->revision)) {
-    return OTS_FAILURE_MSG("Failed to read head header");
+      !table.ReadU32(&this->revision)) {
+    return Error("Failed to read table header");
   }
 
   if (version >> 16 != 1) {
-    return OTS_FAILURE_MSG("Bad head table version of %d", version);
+    return Error("Unsupported majorVersion: %d", version >> 16);
   }
 
   // Skip the checksum adjustment
   if (!table.Skip(4)) {
-    return OTS_FAILURE_MSG("Failed to read checksum");
+    return Error("Failed to read checksum");
   }
 
   uint32_t magic;
   if (!table.ReadU32(&magic) || magic != 0x5F0F3CF5) {
-    return OTS_FAILURE_MSG("Failed to read font magic number");
+    return Error("Failed to read or incorrect magicNumber");
   }
 
-  if (!table.ReadU16(&head->flags)) {
-    return OTS_FAILURE_MSG("Failed to read head flags");
+  if (!table.ReadU16(&this->flags)) {
+    return Error("Failed to read flags");
   }
 
   // We allow bits 0..4, 11..13
-  head->flags &= 0x381f;
+  this->flags &= 0x381f;
 
-  if (!table.ReadU16(&head->ppem)) {
-    return OTS_FAILURE_MSG("Failed to read pixels per em");
+  if (!table.ReadU16(&this->upem)) {
+    return Error("Failed to read unitsPerEm");
   }
 
-  // ppem must be in range
-  if (head->ppem < 16 ||
-      head->ppem > 16384) {
-    return OTS_FAILURE_MSG("Bad ppm of %d", head->ppem);
+  // upem must be in range
+  if (this->upem < 16 ||
+      this->upem > 16384) {
+    return Error("unitsPerEm on in the range [16, 16384]: %d", this->upem);
+  }
+
+  if (!table.ReadR64(&this->created) ||
+      !table.ReadR64(&this->modified)) {
+    return Error("Can't read font dates");
   }
 
-  // ppem must be a power of two
-#if 0
-  // We don't call ots_failure() for now since lots of TrueType fonts are
-  // not following this rule. Putting OTS_WARNING here is too noisy.
-  if ((head->ppem - 1) & head->ppem) {
-    return OTS_FAILURE_MSG("ppm not a power of two: %d", head->ppem);
-  }
-#endif
-
-  if (!table.ReadR64(&head->created) ||
-      !table.ReadR64(&head->modified)) {
-    return OTS_FAILURE_MSG("Can't read font dates");
+  if (!table.ReadS16(&this->xmin) ||
+      !table.ReadS16(&this->ymin) ||
+      !table.ReadS16(&this->xmax) ||
+      !table.ReadS16(&this->ymax)) {
+    return Error("Failed to read font bounding box");
   }
 
-  if (!table.ReadS16(&head->xmin) ||
-      !table.ReadS16(&head->ymin) ||
-      !table.ReadS16(&head->xmax) ||
-      !table.ReadS16(&head->ymax)) {
-    return OTS_FAILURE_MSG("Failed to read font bounding box");
+  if (this->xmin > this->xmax) {
+    return Error("Bad x dimension in the font bounding box (%d, %d)",
+                  this->xmin, this->xmax);
+  }
+  if (this->ymin > this->ymax) {
+    return Error("Bad y dimension in the font bounding box (%d, %d)",
+                  this->ymin, this->ymax);
   }
 
-  if (head->xmin > head->xmax) {
-    return OTS_FAILURE_MSG("Bad x dimension in the font bounding box (%d, %d)", head->xmin, head->xmax);
-  }
-  if (head->ymin > head->ymax) {
-    return OTS_FAILURE_MSG("Bad y dimension in the font bounding box (%d, %d)", head->ymin, head->ymax);
-  }
-
-  if (!table.ReadU16(&head->mac_style)) {
-    return OTS_FAILURE_MSG("Failed to read font style");
+  if (!table.ReadU16(&this->mac_style)) {
+    return Error("Failed to read macStyle");
   }
 
   // We allow bits 0..6
-  head->mac_style &= 0x7f;
+  this->mac_style &= 0x7f;
 
-  if (!table.ReadU16(&head->min_ppem)) {
-    return OTS_FAILURE_MSG("Failed to read font minimum ppm");
+  if (!table.ReadU16(&this->min_ppem)) {
+    return Error("Failed to read lowestRecPPEM");
   }
 
   // We don't care about the font direction hint
   if (!table.Skip(2)) {
-    return OTS_FAILURE_MSG("Failed to skip font direction hint");
+    return Error("Failed to read fontDirectionHint");
   }
 
-  if (!table.ReadS16(&head->index_to_loc_format)) {
-    return OTS_FAILURE_MSG("Failed to read index to loc format");
+  if (!table.ReadS16(&this->index_to_loc_format)) {
+    return Error("Failed to read indexToLocFormat");
   }
-  if (head->index_to_loc_format < 0 ||
-      head->index_to_loc_format > 1) {
-    return OTS_FAILURE_MSG("Bad index to loc format %d", head->index_to_loc_format);
+  if (this->index_to_loc_format < 0 ||
+      this->index_to_loc_format > 1) {
+    return Error("Bad indexToLocFormat %d", this->index_to_loc_format);
   }
 
   int16_t glyph_data_format;
   if (!table.ReadS16(&glyph_data_format) ||
       glyph_data_format) {
-    return OTS_FAILURE_MSG("Failed to read glyph data format");
+    return Error("Failed to read or bad glyphDataFormat");
   }
 
   return true;
 }
 
-bool ots_head_should_serialise(Font *font) {
-  return font->head != NULL;
-}
-
-bool ots_head_serialise(OTSStream *out, Font *font) {
-  const OpenTypeHEAD *head = font->head;
+bool OpenTypeHEAD::Serialize(OTSStream *out) {
   if (!out->WriteU32(0x00010000) ||
-      !out->WriteU32(head->revision) ||
+      !out->WriteU32(this->revision) ||
       !out->WriteU32(0) ||  // check sum not filled in yet
       !out->WriteU32(0x5F0F3CF5) ||
-      !out->WriteU16(head->flags) ||
-      !out->WriteU16(head->ppem) ||
-      !out->WriteR64(head->created) ||
-      !out->WriteR64(head->modified) ||
-      !out->WriteS16(head->xmin) ||
-      !out->WriteS16(head->ymin) ||
-      !out->WriteS16(head->xmax) ||
-      !out->WriteS16(head->ymax) ||
-      !out->WriteU16(head->mac_style) ||
-      !out->WriteU16(head->min_ppem) ||
+      !out->WriteU16(this->flags) ||
+      !out->WriteU16(this->upem) ||
+      !out->WriteR64(this->created) ||
+      !out->WriteR64(this->modified) ||
+      !out->WriteS16(this->xmin) ||
+      !out->WriteS16(this->ymin) ||
+      !out->WriteS16(this->xmax) ||
+      !out->WriteS16(this->ymax) ||
+      !out->WriteU16(this->mac_style) ||
+      !out->WriteU16(this->min_ppem) ||
       !out->WriteS16(2) ||
-      !out->WriteS16(head->index_to_loc_format) ||
+      !out->WriteS16(this->index_to_loc_format) ||
       !out->WriteS16(0)) {
-    return OTS_FAILURE_MSG("Failed to write head table");
+    return Error("Failed to write table");
   }
 
   return true;
 }
 
-void ots_head_reuse(Font *font, Font *other) {
-  font->head = other->head;
-  font->head_reused = true;
-}
-
-void ots_head_free(Font *font) {
-  delete font->head;
-}
-
 }  // namespace
-
-#undef TABLE_NAME
--- a/gfx/ots/src/head.h
+++ b/gfx/ots/src/head.h
@@ -1,23 +1,30 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef OTS_HEAD_H_
 #define OTS_HEAD_H_
 
 #include "ots.h"
 
 namespace ots {
 
-struct OpenTypeHEAD {
+class OpenTypeHEAD : public Table {
+ public:
+  explicit OpenTypeHEAD(Font *font, uint32_t tag)
+      : Table(font, tag, tag) { }
+
+  bool Parse(const uint8_t *data, size_t length);
+  bool Serialize(OTSStream *out);
+
   uint32_t revision;
   uint16_t flags;
-  uint16_t ppem;
+  uint16_t upem;
   uint64_t created;
   uint64_t modified;
 
   int16_t xmin, xmax;
   int16_t ymin, ymax;
 
   uint16_t mac_style;
   uint16_t min_ppem;
--- a/gfx/ots/src/hhea.cc
+++ b/gfx/ots/src/hhea.cc
@@ -1,58 +1,32 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "hhea.h"
 
 #include "head.h"
 #include "maxp.h"
 
 // hhea - Horizontal Header
 // http://www.microsoft.com/typography/otspec/hhea.htm
 
-#define TABLE_NAME "hhea"
-
 namespace ots {
 
-bool ots_hhea_parse(Font *font, const uint8_t *data, size_t length) {
+bool OpenTypeHHEA::Parse(const uint8_t *data, size_t length) {
   Buffer table(data, length);
-  OpenTypeHHEA *hhea = new OpenTypeHHEA;
-  font->hhea = hhea;
 
-  if (!table.ReadU32(&hhea->header.version)) {
-    return OTS_FAILURE_MSG("Failed to read hhea version");
+  if (!table.ReadU32(&this->version)) {
+    return Error("Failed to read table version");
   }
-  if (hhea->header.version >> 16 != 1) {
-    return OTS_FAILURE_MSG("Bad hhea version of %d", hhea->header.version);
-  }
-
-  if (!ParseMetricsHeader(font, &table, &hhea->header)) {
-    return OTS_FAILURE_MSG("Failed to parse horizontal metrics");
+  if (this->version >> 16 != 1) {
+    return Error("Unsupported majorVersion: %d", this->version >> 16);
   }
 
-  return true;
-}
-
-bool ots_hhea_should_serialise(Font *font) {
-  return font->hhea != NULL;
+  return OpenTypeMetricsHeader::Parse(data, length);
 }
 
-bool ots_hhea_serialise(OTSStream *out, Font *font) {
-  if (!SerialiseMetricsHeader(font, out, &font->hhea->header)) {
-    return OTS_FAILURE_MSG("Failed to serialise horizontal metrics");
-  }
-  return true;
-}
-
-void ots_hhea_reuse(Font *font, Font *other) {
-  font->hhea = other->hhea;
-  font->hhea_reused = true;
-}
-
-void ots_hhea_free(Font *font) {
-  delete font->hhea;
+bool OpenTypeHHEA::Serialize(OTSStream *out) {
+  return OpenTypeMetricsHeader::Serialize(out);
 }
 
 }  // namespace ots
-
-#undef TABLE_NAME
--- a/gfx/ots/src/hhea.h
+++ b/gfx/ots/src/hhea.h
@@ -1,19 +1,24 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef OTS_HHEA_H_
 #define OTS_HHEA_H_
 
 #include "metrics.h"
 #include "ots.h"
 
 namespace ots {
 
-struct OpenTypeHHEA {
-  OpenTypeMetricsHeader header;
+class OpenTypeHHEA : public OpenTypeMetricsHeader {
+ public:
+  explicit OpenTypeHHEA(Font *font, uint32_t tag)
+      : OpenTypeMetricsHeader(font, tag, tag) { }
+
+  bool Parse(const uint8_t *data, size_t length);
+  bool Serialize(OTSStream *out);
 };
 
 }  // namespace ots
 
 #endif  // OTS_HHEA_H_
--- a/gfx/ots/src/hmtx.cc
+++ b/gfx/ots/src/hmtx.cc
@@ -1,56 +1,23 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "hmtx.h"
 
 #include "hhea.h"
 #include "maxp.h"
 
 // hmtx - Horizontal Metrics
 // http://www.microsoft.com/typography/otspec/hmtx.htm
 
-#define TABLE_NAME "hmtx"
-
 namespace ots {
 
-bool ots_hmtx_parse(Font *font, const uint8_t *data, size_t length) {
-  Buffer table(data, length);
-  OpenTypeHMTX *hmtx = new OpenTypeHMTX;
-  font->hmtx = hmtx;
-
-  if (!font->hhea || !font->maxp) {
-    return OTS_FAILURE_MSG("Missing hhea or maxp tables in font, needed by hmtx");
-  }
-
-  if (!ParseMetricsTable(font, &table, font->maxp->num_glyphs,
-                         &font->hhea->header, &hmtx->metrics)) {
-    return OTS_FAILURE_MSG("Failed to parse hmtx metrics");
-  }
-
-  return true;
+bool OpenTypeHMTX::Parse(const uint8_t *data, size_t length) {
+  return OpenTypeMetricsTable::Parse(data, length);
 }
 
-bool ots_hmtx_should_serialise(Font *font) {
-  return font->hmtx != NULL;
-}
-
-bool ots_hmtx_serialise(OTSStream *out, Font *font) {
-  if (!SerialiseMetricsTable(font, out, &font->hmtx->metrics)) {
-    return OTS_FAILURE_MSG("Failed to serialise htmx metrics");
-  }
-  return true;
-}
-
-void ots_hmtx_reuse(Font *font, Font *other) {
-  font->hmtx = other->hmtx;
-  font->hmtx_reused = true;
-}
-
-void ots_hmtx_free(Font *font) {
-  delete font->hmtx;
+bool OpenTypeHMTX::Serialize(OTSStream *out) {
+  return OpenTypeMetricsTable::Serialize(out);
 }
 
 }  // namespace ots
-
-#undef TABLE_NAME
--- a/gfx/ots/src/hmtx.h
+++ b/gfx/ots/src/hmtx.h
@@ -1,19 +1,25 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef OTS_HMTX_H_
 #define OTS_HMTX_H_
 
 #include "metrics.h"
+#include "hhea.h"
 #include "ots.h"
 
 namespace ots {
 
-struct OpenTypeHMTX {
-  OpenTypeMetricsTable metrics;
+class OpenTypeHMTX : public OpenTypeMetricsTable {
+ public:
+  explicit OpenTypeHMTX(Font *font, uint32_t tag)
+      : OpenTypeMetricsTable(font, tag, tag, OTS_TAG_HHEA) { }
+
+  bool Parse(const uint8_t *data, size_t length);
+  bool Serialize(OTSStream *out);
 };
 
 }  // namespace ots
 
 #endif  // OTS_HMTX_H_
--- a/gfx/ots/src/kern.cc
+++ b/gfx/ots/src/kern.cc
@@ -1,208 +1,177 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "kern.h"
 
 // kern - Kerning
 // http://www.microsoft.com/typography/otspec/kern.htm
 
-#define TABLE_NAME "kern"
-
-#define DROP_THIS_TABLE(msg_) \
-  do { \
-    OTS_FAILURE_MSG(msg_ ", table discarded"); \
-    delete font->kern; \
-    font->kern = 0; \
-  } while (0)
-
 namespace ots {
 
-bool ots_kern_parse(Font *font, const uint8_t *data, size_t length) {
+bool OpenTypeKERN::Parse(const uint8_t *data, size_t length) {
   Buffer table(data, length);
 
-  OpenTypeKERN *kern = new OpenTypeKERN;
-  font->kern = kern;
-
   uint16_t num_tables = 0;
-  if (!table.ReadU16(&kern->version) ||
+  if (!table.ReadU16(&this->version) ||
       !table.ReadU16(&num_tables)) {
-    return OTS_FAILURE_MSG("Failed to read kern header");
+    return Error("Failed to read table header");
   }
 
-  if (kern->version > 0) {
-    DROP_THIS_TABLE("bad table version");
-    return true;
+  if (this->version > 0) {
+    return Drop("Unsupported table version: %d", this->version);
   }
 
   if (num_tables == 0) {
-    DROP_THIS_TABLE("num_tables is zero");
-    return true;
+    return Drop("nTables is zero");
   }
 
-  kern->subtables.reserve(num_tables);
+  this->subtables.reserve(num_tables);
   for (unsigned i = 0; i < num_tables; ++i) {
     OpenTypeKERNFormat0 subtable;
     uint16_t sub_length = 0;
 
     if (!table.ReadU16(&subtable.version) ||
         !table.ReadU16(&sub_length)) {
-      return OTS_FAILURE_MSG("Failed to read kern subtable %d header", i);
+      return Error("Failed to read subtable %d header", i);
     }
 
     if (subtable.version > 0) {
-      OTS_WARNING("Bad subtable version: %d", subtable.version);
+      Warning("Ignoring subtable %d with unsupported version: %d",
+              i, subtable.version);
       continue;
     }
 
     const size_t current_offset = table.offset();
     if (current_offset - 4 + sub_length > length) {
-      return OTS_FAILURE_MSG("Bad kern subtable %d offset %ld", i, current_offset);
+      return Error("Bad subtable %d offset %ld", i, current_offset);
     }
 
     if (!table.ReadU16(&subtable.coverage)) {
-      return OTS_FAILURE_MSG("Cailed to read kern subtable %d coverage", i);
+      return Error("Failed to read subtable %d coverage", i);
     }
 
     if (!(subtable.coverage & 0x1)) {
-      OTS_WARNING(
+      Warning(
           "We don't support vertical data as the renderer doesn't support it.");
       continue;
     }
     if (subtable.coverage & 0xF0) {
-      DROP_THIS_TABLE("Reserved fields should zero-filled");
-      return true;
+      return Drop("Reserved fields should be zero");
     }
     const uint32_t format = (subtable.coverage & 0xFF00) >> 8;
     if (format != 0) {
-      OTS_WARNING("Format %d is not supported.", format);
+      Warning("Ignoring subtable %d with unsupported format: %d", i, format);
       continue;
     }
 
     // Parse the format 0 field.
     uint16_t num_pairs = 0;
     if (!table.ReadU16(&num_pairs) ||
         !table.ReadU16(&subtable.search_range) ||
         !table.ReadU16(&subtable.entry_selector) ||
         !table.ReadU16(&subtable.range_shift)) {
-      return OTS_FAILURE_MSG("Failed to read kern subtable %d format 0 fields", i);
+      return Error("Failed to read subtable %d format 0 fields", i);
     }
 
     if (!num_pairs) {
-      DROP_THIS_TABLE("Zero length subtable is found");
-      return true;
+      return Drop("Zero length subtable is found");
     }
 
     // Sanity checks for search_range, entry_selector, and range_shift. See the
     // comment in ots.cc for details.
     const size_t kFormat0PairSize = 6;  // left, right, and value. 2 bytes each.
     if (num_pairs > (65536 / kFormat0PairSize)) {
       // Some fonts (e.g. calibri.ttf, pykes_peak_zero.ttf) have pairs >= 10923.
-      DROP_THIS_TABLE("Too large subtable");
-      return true;
+      return Drop("Too large subtable");
     }
     unsigned max_pow2 = 0;
     while (1u << (max_pow2 + 1) <= num_pairs) {
       ++max_pow2;
     }
     const uint16_t expected_search_range = (1u << max_pow2) * kFormat0PairSize;
     if (subtable.search_range != expected_search_range) {
-      OTS_WARNING("bad search range");
+      Warning("bad search range");
       subtable.search_range = expected_search_range;
     }
     if (subtable.entry_selector != max_pow2) {
-      return OTS_FAILURE_MSG("Bad subtable %d entry selector %d", i, subtable.entry_selector);
+      return Error("Bad subtable %d entry selector %d", i, subtable.entry_selector);
     }
     const uint16_t expected_range_shift =
         kFormat0PairSize * num_pairs - subtable.search_range;
     if (subtable.range_shift != expected_range_shift) {
-      OTS_WARNING("bad range shift");
+      Warning("bad range shift");
       subtable.range_shift = expected_range_shift;
     }
 
     // Read kerning pairs.
     subtable.pairs.reserve(num_pairs);
     uint32_t last_pair = 0;
     for (unsigned j = 0; j < num_pairs; ++j) {
       OpenTypeKERNFormat0Pair kerning_pair;
       if (!table.ReadU16(&kerning_pair.left) ||
           !table.ReadU16(&kerning_pair.right) ||
           !table.ReadS16(&kerning_pair.value)) {
-        return OTS_FAILURE_MSG("Failed to read subtable %d kerning pair %d", i, j);
+        return Error("Failed to read subtable %d kerning pair %d", i, j);
       }
       const uint32_t current_pair
           = (kerning_pair.left << 16) + kerning_pair.right;
       if (j != 0 && current_pair <= last_pair) {
         // Many free fonts don't follow this rule, so we don't call OTS_FAILURE
         // in order to support these fonts.
-        DROP_THIS_TABLE("Kerning pairs are not sorted");
-        return true;
+        return Drop("Kerning pairs are not sorted");
       }
       last_pair = current_pair;
       subtable.pairs.push_back(kerning_pair);
     }
 
-    kern->subtables.push_back(subtable);
+    this->subtables.push_back(subtable);
   }
 
-  if (!kern->subtables.size()) {
-    DROP_THIS_TABLE("All subtables are removed");
-    return true;
+  if (!this->subtables.size()) {
+    return Drop("All subtables were removed");
   }
 
   return true;
 }
 
-bool ots_kern_should_serialise(Font *font) {
-  if (!font->glyf) return false;  // this table is not for CFF fonts.
-  return font->kern != NULL;
-}
-
-bool ots_kern_serialise(OTSStream *out, Font *font) {
-  const OpenTypeKERN *kern = font->kern;
-
-  const uint16_t num_subtables = static_cast<uint16_t>(kern->subtables.size());
-  if (num_subtables != kern->subtables.size() ||
-      !out->WriteU16(kern->version) ||
+bool OpenTypeKERN::Serialize(OTSStream *out) {
+  const uint16_t num_subtables = static_cast<uint16_t>(this->subtables.size());
+  if (num_subtables != this->subtables.size() ||
+      !out->WriteU16(this->version) ||
       !out->WriteU16(num_subtables)) {
-    return OTS_FAILURE_MSG("Can't write kern table header");
+    return Error("Failed to write kern table header");
   }
 
   for (uint16_t i = 0; i < num_subtables; ++i) {
-    const size_t length = 14 + (6 * kern->subtables[i].pairs.size());
+    const size_t length = 14 + (6 * this->subtables[i].pairs.size());
     if (length > std::numeric_limits<uint16_t>::max() ||
-        !out->WriteU16(kern->subtables[i].version) ||
+        !out->WriteU16(this->subtables[i].version) ||
         !out->WriteU16(static_cast<uint16_t>(length)) ||
-        !out->WriteU16(kern->subtables[i].coverage) ||
+        !out->WriteU16(this->subtables[i].coverage) ||
         !out->WriteU16(
-            static_cast<uint16_t>(kern->subtables[i].pairs.size())) ||
-        !out->WriteU16(kern->subtables[i].search_range) ||
-        !out->WriteU16(kern->subtables[i].entry_selector) ||
-        !out->WriteU16(kern->subtables[i].range_shift)) {
-      return OTS_FAILURE_MSG("Failed to write kern subtable %d", i);
+            static_cast<uint16_t>(this->subtables[i].pairs.size())) ||
+        !out->WriteU16(this->subtables[i].search_range) ||
+        !out->WriteU16(this->subtables[i].entry_selector) ||
+        !out->WriteU16(this->subtables[i].range_shift)) {
+      return Error("Failed to write kern subtable %d", i);
     }
-    for (unsigned j = 0; j < kern->subtables[i].pairs.size(); ++j) {
-      if (!out->WriteU16(kern->subtables[i].pairs[j].left) ||
-          !out->WriteU16(kern->subtables[i].pairs[j].right) ||
-          !out->WriteS16(kern->subtables[i].pairs[j].value)) {
-        return OTS_FAILURE_MSG("Failed to write kern pair %d for subtable %d", j, i);
+    for (unsigned j = 0; j < this->subtables[i].pairs.size(); ++j) {
+      if (!out->WriteU16(this->subtables[i].pairs[j].left) ||
+          !out->WriteU16(this->subtables[i].pairs[j].right) ||
+          !out->WriteS16(this->subtables[i].pairs[j].value)) {
+        return Error("Failed to write kern pair %d for subtable %d", j, i);
       }
     }
   }
 
   return true;
 }
 
-void ots_kern_reuse(Font *font, Font *other) {
-  font->kern = other->kern;
-  font->kern_reused = true;
-}
-
-void ots_kern_free(Font *font) {
-  delete font->kern;
+bool OpenTypeKERN::ShouldSerialize() {
+  return Table::ShouldSerialize() &&
+         // this table is not for CFF fonts.
+         GetFont()->GetTable(OTS_TAG_GLYF) != NULL;
 }
 
 }  // namespace ots
-
-#undef TABLE_NAME
-#undef DROP_THIS_TABLE
--- a/gfx/ots/src/kern.h
+++ b/gfx/ots/src/kern.h
@@ -1,9 +1,9 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef OTS_KERN_H_
 #define OTS_KERN_H_
 
 #include <vector>
 
@@ -25,16 +25,25 @@ struct OpenTypeKERNFormat0 {
   uint16_t range_shift;
   std::vector<OpenTypeKERNFormat0Pair> pairs;
 };
 
 // Format 2 is not supported. Since the format is not supported by Windows,
 // WebFonts unlikely use it. I've checked thousands of proprietary fonts and
 // free fonts, and found no font uses the format.
 
-struct OpenTypeKERN {
+class OpenTypeKERN : public Table {
+ public:
+  explicit OpenTypeKERN(Font *font, uint32_t tag)
+      : Table(font, tag, tag) { }
+
+  bool Parse(const uint8_t *data, size_t length);
+  bool Serialize(OTSStream *out);
+  bool ShouldSerialize();
+
+ private:
   uint16_t version;
   std::vector<OpenTypeKERNFormat0> subtables;
 };
 
 }  // namespace ots
 
 #endif  // OTS_KERN_H_
--- a/gfx/ots/src/layout.cc
+++ b/gfx/ots/src/layout.cc
@@ -1,9 +1,9 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "layout.h"
 
 #include <limits>
 #include <vector>
 
@@ -89,25 +89,22 @@ bool ParseScriptTable(const ots::Font *f
   uint16_t offset_default_lang_sys = 0;
   uint16_t lang_sys_count = 0;
   if (!subtable.ReadU16(&offset_default_lang_sys) ||
       !subtable.ReadU16(&lang_sys_count)) {
     return OTS_FAILURE_MSG("Failed to read script header for script tag %c%c%c%c", OTS_UNTAG(tag));
   }
 
   // The spec requires a script table for 'DFLT' tag must contain non-NULL
-  // |offset_default_lang_sys| and |lang_sys_count| == 0
+  // |offset_default_lang_sys|.
   // https://www.microsoft.com/typography/otspec/chapter2.htm
   if (tag == kScriptTableTagDflt) {
     if (offset_default_lang_sys == 0) {
       return OTS_FAILURE_MSG("DFLT script doesn't satisfy the spec. DefaultLangSys is NULL");
     }
-    if (lang_sys_count != 0) {
-      return OTS_FAILURE_MSG("DFLT script doesn't satisfy the spec. LangSysCount is not zero: %d", lang_sys_count);
-    }
   }
 
   const unsigned lang_sys_record_end =
       6 * static_cast<unsigned>(lang_sys_count) + 4;
   if (lang_sys_record_end > std::numeric_limits<uint16_t>::max()) {
     return OTS_FAILURE_MSG("Bad end of langsys record %d for script tag %c%c%c%c", lang_sys_record_end, OTS_UNTAG(tag));
   }
 
@@ -192,31 +189,34 @@ bool ParseLookupTable(ots::Font *font, c
       !subtable.ReadU16(&subtable_count)) {
     return OTS_FAILURE_MSG("Failed to read lookup table header");
   }
 
   if (lookup_type == 0 || lookup_type > parser->num_types) {
     return OTS_FAILURE_MSG("Bad lookup type %d", lookup_type);
   }
 
+  ots::OpenTypeGDEF *gdef = static_cast<ots::OpenTypeGDEF*>(
+      font->GetTypedTable(OTS_TAG_GDEF));
+
   // Check lookup flags.
   if ((lookup_flag & kGdefRequiredFlags) &&
-      (!font->gdef || !font->gdef->has_glyph_class_def)) {
+      (!gdef || !gdef->has_glyph_class_def)) {
     return OTS_FAILURE_MSG("Lookup flags require GDEF table, "
                            "but none was found: %d", lookup_flag);
   }
   if ((lookup_flag & kMarkAttachmentTypeMask) &&
-      (!font->gdef || !font->gdef->has_mark_attachment_class_def)) {
+      (!gdef || !gdef->has_mark_attachment_class_def)) {
     return OTS_FAILURE_MSG("Lookup flags ask for mark attachment, "
                            "but there is no GDEF table or it has no "
                            "mark attachment classes: %d", lookup_flag);
   }
   bool use_mark_filtering_set = false;
   if (lookup_flag & kUseMarkFilteringSetBit) {
-    if (!font->gdef || !font->gdef->has_mark_glyph_sets_def) {
+    if (!gdef || !gdef->has_mark_glyph_sets_def) {
       return OTS_FAILURE_MSG("Lookup flags ask for mark filtering, "
                              "but there is no GDEF table or it has no "
                              "mark filtering sets: %d", lookup_flag);
     }
     use_mark_filtering_set = true;
   }
 
   std::vector<uint16_t> subtables;
@@ -243,18 +243,18 @@ bool ParseLookupTable(ots::Font *font, c
     return OTS_FAILURE_MSG("Bad subtable size %ld", subtables.size());
   }
 
   if (use_mark_filtering_set) {
     uint16_t mark_filtering_set = 0;
     if (!subtable.ReadU16(&mark_filtering_set)) {
       return OTS_FAILURE_MSG("Failed to read mark filtering set");
     }
-    if (font->gdef->num_mark_glyph_sets == 0 ||
-        mark_filtering_set >= font->gdef->num_mark_glyph_sets) {
+    if (gdef->num_mark_glyph_sets == 0 ||
+        mark_filtering_set >= gdef->num_mark_glyph_sets) {
       return OTS_FAILURE_MSG("Bad mark filtering set %d", mark_filtering_set);
     }
   }
 
   // Parse lookup subtables for this lookup type.
   for (unsigned i = 0; i < subtable_count; ++i) {
     if (!parser->Parse(font, data + subtables[i], length - subtables[i],
                        lookup_type)) {
--- a/gfx/ots/src/layout.h
+++ b/gfx/ots/src/layout.h
@@ -1,9 +1,9 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef OTS_LAYOUT_H_
 #define OTS_LAYOUT_H_
 
 #include "ots.h"
 
--- a/gfx/ots/src/loca.cc
+++ b/gfx/ots/src/loca.cc
@@ -1,109 +1,93 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "loca.h"
 
 #include "head.h"
 #include "maxp.h"
 
 // loca - Index to Location
 // http://www.microsoft.com/typography/otspec/loca.htm
 
-#define TABLE_NAME "loca"
-
 namespace ots {
 
-bool ots_loca_parse(Font *font, const uint8_t *data, size_t length) {
+bool OpenTypeLOCA::Parse(const uint8_t *data, size_t length) {
   Buffer table(data, length);
 
   // We can't do anything useful in validating this data except to ensure that
   // the values are monotonically increasing.
 
-  OpenTypeLOCA *loca = new OpenTypeLOCA;
-  font->loca = loca;
-
-  if (!font->maxp || !font->head) {
-    return OTS_FAILURE_MSG("maxp or head tables missing from font, needed by loca");
+  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
+      GetFont()->GetTypedTable(OTS_TAG_MAXP));
+  OpenTypeHEAD *head = static_cast<OpenTypeHEAD*>(
+      GetFont()->GetTypedTable(OTS_TAG_HEAD));
+  if (!maxp || !head) {
+    return Error("Required maxp or head tables are missing");
   }
 
-  const unsigned num_glyphs = font->maxp->num_glyphs;
+  const unsigned num_glyphs = maxp->num_glyphs;
   unsigned last_offset = 0;
-  loca->offsets.resize(num_glyphs + 1);
+  this->offsets.resize(num_glyphs + 1);
   // maxp->num_glyphs is uint16_t, thus the addition never overflows.
 
-  if (font->head->index_to_loc_format == 0) {
+  if (head->index_to_loc_format == 0) {
     // Note that the <= here (and below) is correct. There is one more offset
     // than the number of glyphs in order to give the length of the final
     // glyph.
     for (unsigned i = 0; i <= num_glyphs; ++i) {
       uint16_t offset = 0;
       if (!table.ReadU16(&offset)) {
-        return OTS_FAILURE_MSG("Failed to read offset for glyph %d", i);
+        return Error("Failed to read offset for glyph %d", i);
       }
       if (offset < last_offset) {
-        return OTS_FAILURE_MSG("Out of order offset %d < %d for glyph %d", offset, last_offset, i);
+        return Error("Out of order offset %d < %d for glyph %d", offset, last_offset, i);
       }
       last_offset = offset;
-      loca->offsets[i] = offset * 2;
+      this->offsets[i] = offset * 2;
     }
   } else {
     for (unsigned i = 0; i <= num_glyphs; ++i) {
       uint32_t offset = 0;
       if (!table.ReadU32(&offset)) {
-        return OTS_FAILURE_MSG("Failed to read offset for glyph %d", i);
+        return Error("Failed to read offset for glyph %d", i);
       }
       if (offset < last_offset) {
-        return OTS_FAILURE_MSG("Out of order offset %d < %d for glyph %d", offset, last_offset, i);
+        return Error("Out of order offset %d < %d for glyph %d", offset, last_offset, i);
       }
       last_offset = offset;
-      loca->offsets[i] = offset;
+      this->offsets[i] = offset;
     }
   }
 
   return true;
 }
 
-bool ots_loca_should_serialise(Font *font) {
-  return font->loca != NULL;
-}
-
-bool ots_loca_serialise(OTSStream *out, Font *font) {
-  const OpenTypeLOCA *loca = font->loca;
-  const OpenTypeHEAD *head = font->head;
+bool OpenTypeLOCA::Serialize(OTSStream *out) {
+  OpenTypeHEAD *head = static_cast<OpenTypeHEAD*>(
+      GetFont()->GetTypedTable(OTS_TAG_HEAD));
 
   if (!head) {
-    return OTS_FAILURE_MSG("Missing head table in font needed by loca");
+    return Error("Required head table is missing");
   }
 
   if (head->index_to_loc_format == 0) {
-    for (unsigned i = 0; i < loca->offsets.size(); ++i) {
-      const uint16_t offset = static_cast<uint16_t>(loca->offsets[i] >> 1);
-      if ((offset != (loca->offsets[i] >> 1)) ||
+    for (unsigned i = 0; i < this->offsets.size(); ++i) {
+      const uint16_t offset = static_cast<uint16_t>(this->offsets[i] >> 1);
+      if ((offset != (this->offsets[i] >> 1)) ||
           !out->WriteU16(offset)) {
-        return OTS_FAILURE_MSG("Failed to write glyph offset for glyph %d", i);
+        return Error("Failed to write glyph offset for glyph %d", i);
       }
     }
   } else {
-    for (unsigned i = 0; i < loca->offsets.size(); ++i) {
-      if (!out->WriteU32(loca->offsets[i])) {
-        return OTS_FAILURE_MSG("Failed to write glyph offset for glyph %d", i);
+    for (unsigned i = 0; i < this->offsets.size(); ++i) {
+      if (!out->WriteU32(this->offsets[i])) {
+        return Error("Failed to write glyph offset for glyph %d", i);
       }
     }
   }
 
   return true;
 }
 
-void ots_loca_reuse(Font *font, Font *other) {
-  font->loca = other->loca;
-  font->loca_reused = true;
-}
-
-void ots_loca_free(Font *font) {
-  delete font->loca;
-}
-
 }  // namespace ots
-
-#undef TABLE_NAME
--- a/gfx/ots/src/loca.h
+++ b/gfx/ots/src/loca.h
@@ -1,20 +1,27 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef OTS_LOCA_H_
 #define OTS_LOCA_H_
 
 #include <vector>
 
 #include "ots.h"
 
 namespace ots {
 
-struct OpenTypeLOCA {
+class OpenTypeLOCA : public Table {
+ public:
+  explicit OpenTypeLOCA(Font *font, uint32_t tag)
+      : Table(font, tag, tag) { }
+
+  bool Parse(const uint8_t *data, size_t length);
+  bool Serialize(OTSStream *out);
+
   std::vector<uint32_t> offsets;
 };
 
 }  // namespace ots
 
 #endif  // OTS_LOCA_H_
--- a/gfx/ots/src/ltsh.cc
+++ b/gfx/ots/src/ltsh.cc
@@ -1,97 +1,71 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "ltsh.h"
 
 #include "maxp.h"
 
 // LTSH - Linear Threshold
 // http://www.microsoft.com/typography/otspec/ltsh.htm
 
-#define TABLE_NAME "LTSH"
-
-#define DROP_THIS_TABLE(...) \
-  do { \
-    OTS_FAILURE_MSG_(font->file, TABLE_NAME ": " __VA_ARGS__); \
-    OTS_FAILURE_MSG("Table discarded"); \
-    delete font->ltsh; \
-    font->ltsh = 0; \
-  } while (0)
-
 namespace ots {
 
-bool ots_ltsh_parse(Font *font, const uint8_t *data, size_t length) {
+bool OpenTypeLTSH::Parse(const uint8_t *data, size_t length) {
   Buffer table(data, length);
 
-  if (!font->maxp) {
-    return OTS_FAILURE_MSG("Missing maxp table from font needed by ltsh");
+  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
+      GetFont()->GetTypedTable(OTS_TAG_MAXP));
+  if (!maxp) {
+    return Error("Required maxp table is missing");
   }
 
-  OpenTypeLTSH *ltsh = new OpenTypeLTSH;
-  font->ltsh = ltsh;
-
   uint16_t num_glyphs = 0;
-  if (!table.ReadU16(&ltsh->version) ||
+  if (!table.ReadU16(&this->version) ||
       !table.ReadU16(&num_glyphs)) {
-    return OTS_FAILURE_MSG("Failed to read ltsh header");
+    return Error("Failed to read table header");
   }
 
-  if (ltsh->version != 0) {
-    DROP_THIS_TABLE("bad version: %u", ltsh->version);
-    return true;
+  if (this->version != 0) {
+    return Drop("Unsupported version: %u", this->version);
   }
 
-  if (num_glyphs != font->maxp->num_glyphs) {
-    DROP_THIS_TABLE("bad num_glyphs: %u", num_glyphs);
-    return true;
+  if (num_glyphs != maxp->num_glyphs) {
+    return Drop("Bad numGlyphs: %u", num_glyphs);
   }
 
-  ltsh->ypels.reserve(num_glyphs);
+  this->ypels.reserve(num_glyphs);
   for (unsigned i = 0; i < num_glyphs; ++i) {
     uint8_t pel = 0;
     if (!table.ReadU8(&pel)) {
-      return OTS_FAILURE_MSG("Failed to read pixels for glyph %d", i);
+      return Error("Failed to read pixels for glyph %d", i);
     }
-    ltsh->ypels.push_back(pel);
+    this->ypels.push_back(pel);
   }
 
   return true;
 }
 
-bool ots_ltsh_should_serialise(Font *font) {
-  if (!font->glyf) return false;  // this table is not for CFF fonts.
-  return font->ltsh != NULL;
-}
-
-bool ots_ltsh_serialise(OTSStream *out, Font *font) {
-  const OpenTypeLTSH *ltsh = font->ltsh;
-
-  const uint16_t num_ypels = static_cast<uint16_t>(ltsh->ypels.size());
-  if (num_ypels != ltsh->ypels.size() ||
-      !out->WriteU16(ltsh->version) ||
+bool OpenTypeLTSH::Serialize(OTSStream *out) {
+  const uint16_t num_ypels = static_cast<uint16_t>(this->ypels.size());
+  if (num_ypels != this->ypels.size() ||
+      !out->WriteU16(this->version) ||
       !out->WriteU16(num_ypels)) {
-    return OTS_FAILURE_MSG("Failed to write pels size");
+    return Error("Failed to write table header");
   }
   for (uint16_t i = 0; i < num_ypels; ++i) {
-    if (!out->Write(&(ltsh->ypels[i]), 1)) {
-      return OTS_FAILURE_MSG("Failed to write pixel size for glyph %d", i);
+    if (!out->Write(&(this->ypels[i]), 1)) {
+      return Error("Failed to write pixel size for glyph %d", i);
     }
   }
 
   return true;
 }
 
-void ots_ltsh_reuse(Font *font, Font *other) {
-  font->ltsh = other->ltsh;
-  font->ltsh_reused = true;
-}
-
-void ots_ltsh_free(Font *font) {
-  delete font->ltsh;
+bool OpenTypeLTSH::ShouldSerialize() {
+  return Table::ShouldSerialize() &&
+         // this table is not for CFF fonts.
+         GetFont()->GetTable(OTS_TAG_GLYF) != NULL;
 }
 
 }  // namespace ots
-
-#undef TABLE_NAME
-#undef DROP_THIS_TABLE
--- a/gfx/ots/src/ltsh.h
+++ b/gfx/ots/src/ltsh.h
@@ -1,21 +1,30 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef OTS_LTSH_H_
 #define OTS_LTSH_H_
 
 #include <vector>
 
 #include "ots.h"
 
 namespace ots {
 
-struct OpenTypeLTSH {
+class OpenTypeLTSH : public Table {
+ public:
+  explicit OpenTypeLTSH(Font *font, uint32_t tag)
+      : Table(font, tag, tag) { }
+
+  bool Parse(const uint8_t *data, size_t length);
+  bool Serialize(OTSStream *out);
+  bool ShouldSerialize();
+
+ private:
   uint16_t version;
   std::vector<uint8_t> ypels;
 };
 
 }  // namespace ots
 
 #endif  // OTS_LTSH_H_
--- a/gfx/ots/src/math.cc
+++ b/gfx/ots/src/math.cc
@@ -1,28 +1,23 @@
-// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Copyright (c) 2014-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 // We use an underscore to avoid confusion with the standard math.h library.
 #include "math_.h"
 
 #include <limits>
 #include <vector>
 
 #include "layout.h"
 #include "maxp.h"
 
 // MATH - The MATH Table
-// The specification is not yet public but has been submitted to the MPEG group
-// in response to the 'Call for Proposals for ISO/IEC 14496-22 "Open Font
-// Format" Color Font Technology and MATH layout support'. Meanwhile, you can
-// contact Microsoft's engineer Murray Sargent to obtain a copy.
-
-#define TABLE_NAME "MATH"
+// http://www.microsoft.com/typography/otspec/math.htm
 
 namespace {
 
 // The size of MATH header.
 // Version
 // MathConstants
 // MathGlyphInfo
 // MathVariants
@@ -43,45 +38,49 @@ const unsigned kMathValueRecordSize = 2 
 // The size of the GlyphPartRecord.
 // glyph
 // StartConnectorLength
 // EndConnectorLength
 // FullAdvance
 // PartFlags
 const unsigned kGlyphPartRecordSize = 5 * 2;
 
+}  // namespace
+
+namespace ots {
+
 // Shared Table: MathValueRecord
 
-bool ParseMathValueRecord(const ots::Font *font,
-                          ots::Buffer* subtable, const uint8_t *data,
-                          const size_t length) {
+bool OpenTypeMATH::ParseMathValueRecord(ots::Buffer* subtable,
+                                        const uint8_t *data,
+                                        const size_t length) {
   // Check the Value field.
   if (!subtable->Skip(2)) {
     return OTS_FAILURE();
   }
 
   // Check the offset to device table.
   uint16_t offset = 0;
   if (!subtable->ReadU16(&offset)) {
     return OTS_FAILURE();
   }
   if (offset) {
     if (offset >= length) {
       return OTS_FAILURE();
     }
-    if (!ots::ParseDeviceTable(font, data + offset, length - offset)) {
+    if (!ots::ParseDeviceTable(GetFont(), data + offset, length - offset)) {
       return OTS_FAILURE();
     }
   }
 
   return true;
 }
 
-bool ParseMathConstantsTable(const ots::Font *font,
-                             const uint8_t *data, size_t length) {
+bool OpenTypeMATH::ParseMathConstantsTable(const uint8_t *data,
+                                           size_t length) {
   ots::Buffer subtable(data, length);
 
   // Part 1: int16 or uint16 constants.
   //  ScriptPercentScaleDown
   //  ScriptScriptPercentScaleDown
   //  DelimitedSubFormulaMinHeight
   //  DisplayOperatorMinHeight
   if (!subtable.Skip(4 * 2)) {
@@ -141,35 +140,34 @@ bool ParseMathConstantsTable(const ots::
   // RadicalVerticalGap
   // RadicalDisplayStyleVerticalGap
   // RadicalRuleThickness
   // RadicalExtraAscender
   // RadicalKernBeforeDegree
   //
   // RadicalKernAfterDegree
   for (unsigned i = 0; i < static_cast<unsigned>(51); ++i) {
-    if (!ParseMathValueRecord(font, &subtable, data, length)) {
+    if (!ParseMathValueRecord(&subtable, data, length)) {
       return OTS_FAILURE();
     }
   }
 
   // Part 3: uint16 constant
   // RadicalDegreeBottomRaisePercent
   if (!subtable.Skip(2)) {
     return OTS_FAILURE();
   }
 
   return true;
 }
 
-bool ParseMathValueRecordSequenceForGlyphs(const ots::Font *font,
-                                           ots::Buffer* subtable,
-                                           const uint8_t *data,
-                                           const size_t length,
-                                           const uint16_t num_glyphs) {
+bool OpenTypeMATH::ParseMathValueRecordSequenceForGlyphs(ots::Buffer* subtable,
+                                                         const uint8_t *data,
+                                                         const size_t length,
+                                                         const uint16_t num_glyphs) {
   // Check the header.
   uint16_t offset_coverage = 0;
   uint16_t sequence_count = 0;
   if (!subtable->ReadU16(&offset_coverage) ||
       !subtable->ReadU16(&sequence_count)) {
     return OTS_FAILURE();
   }
 
@@ -178,80 +176,77 @@ bool ParseMathValueRecordSequenceForGlyp
   if (sequence_end > std::numeric_limits<uint16_t>::max()) {
     return OTS_FAILURE();
   }
 
   // Check coverage table.
   if (offset_coverage < sequence_end || offset_coverage >= length) {
     return OTS_FAILURE();
   }
-  if (!ots::ParseCoverageTable(font, data + offset_coverage,
+  if (!ots::ParseCoverageTable(GetFont(), data + offset_coverage,
                                length - offset_coverage,
                                num_glyphs, sequence_count)) {
     return OTS_FAILURE();
   }
 
   // Check sequence.
   for (unsigned i = 0; i < sequence_count; ++i) {
-    if (!ParseMathValueRecord(font, subtable, data, length)) {
+    if (!ParseMathValueRecord(subtable, data, length)) {
       return OTS_FAILURE();
     }
   }
 
   return true;
 }
 
-bool ParseMathItalicsCorrectionInfoTable(const ots::Font *font,
-                                         const uint8_t *data,
-                                         size_t length,
-                                         const uint16_t num_glyphs) {
+bool OpenTypeMATH::ParseMathItalicsCorrectionInfoTable(const uint8_t *data,
+                                                       size_t length,
+                                                       const uint16_t num_glyphs) {
   ots::Buffer subtable(data, length);
-  return ParseMathValueRecordSequenceForGlyphs(font, &subtable, data, length,
+  return ParseMathValueRecordSequenceForGlyphs(&subtable, data, length,
                                                num_glyphs);
 }
 
-bool ParseMathTopAccentAttachmentTable(const ots::Font *font,
-                                       const uint8_t *data,
-                                       size_t length,
-                                       const uint16_t num_glyphs) {
+bool OpenTypeMATH::ParseMathTopAccentAttachmentTable(const uint8_t *data,
+                                                     size_t length,
+                                                     const uint16_t num_glyphs) {
   ots::Buffer subtable(data, length);
-  return ParseMathValueRecordSequenceForGlyphs(font, &subtable, data, length,
+  return ParseMathValueRecordSequenceForGlyphs(&subtable, data, length,
                                                num_glyphs);
 }
 
-bool ParseMathKernTable(const ots::Font *font,
-                        const uint8_t *data, size_t length) {
+bool OpenTypeMATH::ParseMathKernTable(const uint8_t *data, size_t length) {
   ots::Buffer subtable(data, length);
 
   // Check the Height count.
   uint16_t height_count = 0;
   if (!subtable.ReadU16(&height_count)) {
     return OTS_FAILURE();
   }
 
   // Check the Correction Heights.
   for (unsigned i = 0; i < height_count; ++i) {
-    if (!ParseMathValueRecord(font, &subtable, data, length)) {
+    if (!ParseMathValueRecord(&subtable, data, length)) {
       return OTS_FAILURE();
     }
   }
 
   // Check the Kern Values.
   for (unsigned i = 0; i <= height_count; ++i) {
-    if (!ParseMathValueRecord(font, &subtable, data, length)) {
+    if (!ParseMathValueRecord(&subtable, data, length)) {
       return OTS_FAILURE();
     }
   }
 
   return true;
 }
 
-bool ParseMathKernInfoTable(const ots::Font *font,
-                            const uint8_t *data, size_t length,
-                            const uint16_t num_glyphs) {
+bool OpenTypeMATH::ParseMathKernInfoTable(const uint8_t *data,
+                                          size_t length,
+                                          const uint16_t num_glyphs) {
   ots::Buffer subtable(data, length);
 
   // Check the header.
   uint16_t offset_coverage = 0;
   uint16_t sequence_count = 0;
   if (!subtable.ReadU16(&offset_coverage) ||
       !subtable.ReadU16(&sequence_count)) {
     return OTS_FAILURE();
@@ -262,45 +257,45 @@ bool ParseMathKernInfoTable(const ots::F
   if (sequence_end > std::numeric_limits<uint16_t>::max()) {
     return OTS_FAILURE();
   }
 
   // Check coverage table.
   if (offset_coverage < sequence_end || offset_coverage >= length) {
     return OTS_FAILURE();
   }
-  if (!ots::ParseCoverageTable(font, data + offset_coverage, length - offset_coverage,
+  if (!ots::ParseCoverageTable(GetFont(), data + offset_coverage, length - offset_coverage,
                                num_glyphs, sequence_count)) {
     return OTS_FAILURE();
   }
 
   // Check sequence of MathKernInfoRecord
   for (unsigned i = 0; i < sequence_count; ++i) {
     // Check TopRight, TopLeft, BottomRight and BottomLeft Math Kern.
     for (unsigned j = 0; j < 4; ++j) {
       uint16_t offset_math_kern = 0;
       if (!subtable.ReadU16(&offset_math_kern)) {
         return OTS_FAILURE();
       }
       if (offset_math_kern) {
         if (offset_math_kern < sequence_end || offset_math_kern >= length ||
-            !ParseMathKernTable(font, data + offset_math_kern,
+            !ParseMathKernTable(data + offset_math_kern,
                                 length - offset_math_kern)) {
           return OTS_FAILURE();
         }
       }
     }
   }
 
   return true;
 }
 
-bool ParseMathGlyphInfoTable(const ots::Font *font,
-                             const uint8_t *data, size_t length,
-                             const uint16_t num_glyphs) {
+bool OpenTypeMATH::ParseMathGlyphInfoTable(const uint8_t *data,
+                                           size_t length,
+                                           const uint16_t num_glyphs) {
   ots::Buffer subtable(data, length);
 
   // Check Header.
   uint16_t offset_math_italics_correction_info = 0;
   uint16_t offset_math_top_accent_attachment = 0;
   uint16_t offset_extended_shaped_coverage = 0;
   uint16_t offset_math_kern_info = 0;
   if (!subtable.ReadU16(&offset_math_italics_correction_info) ||
@@ -313,62 +308,62 @@ bool ParseMathGlyphInfoTable(const ots::
   // Check subtables.
   // The specification does not say whether the offsets for
   // MathItalicsCorrectionInfo, MathTopAccentAttachment and MathKernInfo may
   // be NULL, but that's the case in some fonts (e.g STIX) so we accept that.
   if (offset_math_italics_correction_info) {
     if (offset_math_italics_correction_info >= length ||
         offset_math_italics_correction_info < kMathGlyphInfoHeaderSize ||
         !ParseMathItalicsCorrectionInfoTable(
-            font, data + offset_math_italics_correction_info,
+            data + offset_math_italics_correction_info,
             length - offset_math_italics_correction_info,
             num_glyphs)) {
       return OTS_FAILURE();
     }
   }
   if (offset_math_top_accent_attachment) {
     if (offset_math_top_accent_attachment >= length ||
         offset_math_top_accent_attachment < kMathGlyphInfoHeaderSize ||
-        !ParseMathTopAccentAttachmentTable(font, data +
+        !ParseMathTopAccentAttachmentTable(data +
                                            offset_math_top_accent_attachment,
                                            length -
                                            offset_math_top_accent_attachment,
                                            num_glyphs)) {
       return OTS_FAILURE();
     }
   }
   if (offset_extended_shaped_coverage) {
     if (offset_extended_shaped_coverage >= length ||
         offset_extended_shaped_coverage < kMathGlyphInfoHeaderSize ||
-        !ots::ParseCoverageTable(font, data + offset_extended_shaped_coverage,
+        !ots::ParseCoverageTable(GetFont(), data + offset_extended_shaped_coverage,
                                  length - offset_extended_shaped_coverage,
                                  num_glyphs)) {
       return OTS_FAILURE();
     }
   }
   if (offset_math_kern_info) {
     if (offset_math_kern_info >= length ||
         offset_math_kern_info < kMathGlyphInfoHeaderSize ||
-        !ParseMathKernInfoTable(font, data + offset_math_kern_info,
+        !ParseMathKernInfoTable(data + offset_math_kern_info,
                                 length - offset_math_kern_info, num_glyphs)) {
       return OTS_FAILURE();
     }
   }
 
   return true;
 }
 
-bool ParseGlyphAssemblyTable(const ots::Font *font,
-                             const uint8_t *data,
-                             size_t length, const uint16_t num_glyphs) {
+bool OpenTypeMATH::ParseGlyphAssemblyTable(const uint8_t *data,
+                                           size_t length,
+                                           const uint16_t num_glyphs) {
   ots::Buffer subtable(data, length);
 
   // Check the header.
   uint16_t part_count = 0;
-  if (!ParseMathValueRecord(font, &subtable, data, length) ||
+  if (!ParseMathValueRecord(&subtable, data, length) ||
       !subtable.ReadU16(&part_count)) {
     return OTS_FAILURE();
   }
 
   const unsigned sequence_end = kMathValueRecordSize +
     static_cast<unsigned>(2) + part_count * kGlyphPartRecordSize;
   if (sequence_end > std::numeric_limits<uint16_t>::max()) {
     return OTS_FAILURE();
@@ -379,29 +374,29 @@ bool ParseGlyphAssemblyTable(const ots::
     uint16_t glyph = 0;
     uint16_t part_flags = 0;
     if (!subtable.ReadU16(&glyph) ||
         !subtable.Skip(2 * 3) ||
         !subtable.ReadU16(&part_flags)) {
       return OTS_FAILURE();
     }
     if (glyph >= num_glyphs) {
-      return OTS_FAILURE_MSG("bad glyph ID: %u", glyph);
+      return Error("bad glyph ID: %u", glyph);
     }
     if (part_flags & ~0x00000001) {
-      return OTS_FAILURE_MSG("unknown part flag: %u", part_flags);
+      return Error("unknown part flag: %u", part_flags);
     }
   }
 
   return true;
 }
 
-bool ParseMathGlyphConstructionTable(const ots::Font *font,
-                                     const uint8_t *data,
-                                     size_t length, const uint16_t num_glyphs) {
+bool OpenTypeMATH::ParseMathGlyphConstructionTable(const uint8_t *data,
+                                                   size_t length,
+                                                   const uint16_t num_glyphs) {
   ots::Buffer subtable(data, length);
 
   // Check the header.
   uint16_t offset_glyph_assembly = 0;
   uint16_t variant_count = 0;
   if (!subtable.ReadU16(&offset_glyph_assembly) ||
       !subtable.ReadU16(&variant_count)) {
     return OTS_FAILURE();
@@ -414,81 +409,80 @@ bool ParseMathGlyphConstructionTable(con
   }
 
   // Check the GlyphAssembly offset.
   if (offset_glyph_assembly) {
     if (offset_glyph_assembly >= length ||
         offset_glyph_assembly < sequence_end) {
       return OTS_FAILURE();
     }
-    if (!ParseGlyphAssemblyTable(font, data + offset_glyph_assembly,
+    if (!ParseGlyphAssemblyTable(data + offset_glyph_assembly,
                                  length - offset_glyph_assembly, num_glyphs)) {
       return OTS_FAILURE();
     }
   }
 
   // Check the sequence of MathGlyphVariantRecord.
   for (unsigned i = 0; i < variant_count; ++i) {
     uint16_t glyph = 0;
     if (!subtable.ReadU16(&glyph) ||
         !subtable.Skip(2)) {
       return OTS_FAILURE();
     }
     if (glyph >= num_glyphs) {
-      return OTS_FAILURE_MSG("bad glyph ID: %u", glyph);
+      return Error("bad glyph ID: %u", glyph);
     }
   }
 
   return true;
 }
 
-bool ParseMathGlyphConstructionSequence(const ots::Font *font,
-                                        ots::Buffer* subtable,
-                                        const uint8_t *data,
-                                        size_t length,
-                                        const uint16_t num_glyphs,
-                                        uint16_t offset_coverage,
-                                        uint16_t glyph_count,
-                                        const unsigned sequence_end) {
+bool OpenTypeMATH::ParseMathGlyphConstructionSequence(ots::Buffer* subtable,
+                                                      const uint8_t *data,
+                                                      size_t length,
+                                                      const uint16_t num_glyphs,
+                                                      uint16_t offset_coverage,
+                                                      uint16_t glyph_count,
+                                                      const unsigned sequence_end) {
   // Zero glyph count, nothing to parse.
   if (!glyph_count) {
     return true;
   }
 
   // Check coverage table.
   if (offset_coverage < sequence_end || offset_coverage >= length) {
     return OTS_FAILURE();
   }
-  if (!ots::ParseCoverageTable(font, data + offset_coverage,
+  if (!ots::ParseCoverageTable(GetFont(), data + offset_coverage,
                                length - offset_coverage,
                                num_glyphs, glyph_count)) {
     return OTS_FAILURE();
   }
 
   // Check sequence of MathGlyphConstruction.
   for (unsigned i = 0; i < glyph_count; ++i) {
       uint16_t offset_glyph_construction = 0;
       if (!subtable->ReadU16(&offset_glyph_construction)) {
         return OTS_FAILURE();
       }
       if (offset_glyph_construction < sequence_end ||
           offset_glyph_construction >= length ||
-          !ParseMathGlyphConstructionTable(font, data + offset_glyph_construction,
+          !ParseMathGlyphConstructionTable(data + offset_glyph_construction,
                                            length - offset_glyph_construction,
                                            num_glyphs)) {
         return OTS_FAILURE();
       }
   }
 
   return true;
 }
 
-bool ParseMathVariantsTable(const ots::Font *font,
-                            const uint8_t *data,
-                            size_t length, const uint16_t num_glyphs) {
+bool OpenTypeMATH::ParseMathVariantsTable(const uint8_t *data,
+                                          size_t length,
+                                          const uint16_t num_glyphs) {
   ots::Buffer subtable(data, length);
 
   // Check the header.
   uint16_t offset_vert_glyph_coverage = 0;
   uint16_t offset_horiz_glyph_coverage = 0;
   uint16_t vert_glyph_count = 0;
   uint16_t horiz_glyph_count = 0;
   if (!subtable.Skip(2) ||  // MinConnectorOverlap
@@ -500,61 +494,48 @@ bool ParseMathVariantsTable(const ots::F
   }
 
   const unsigned sequence_end = 5 * 2 + vert_glyph_count * 2 +
     horiz_glyph_count * 2;
   if (sequence_end > std::numeric_limits<uint16_t>::max()) {
     return OTS_FAILURE();
   }
 
-  if (!ParseMathGlyphConstructionSequence(font, &subtable, data, length, num_glyphs,
+  if (!ParseMathGlyphConstructionSequence(&subtable, data, length, num_glyphs,
                                           offset_vert_glyph_coverage,
                                           vert_glyph_count,
                                           sequence_end) ||
-      !ParseMathGlyphConstructionSequence(font, &subtable, data, length, num_glyphs,
+      !ParseMathGlyphConstructionSequence(&subtable, data, length, num_glyphs,
                                           offset_horiz_glyph_coverage,
                                           horiz_glyph_count,
                                           sequence_end)) {
     return OTS_FAILURE();
   }
 
   return true;
 }
 
-}  // namespace
-
-#define DROP_THIS_TABLE(msg_) \
-  do { \
-    OTS_FAILURE_MSG(msg_ ", table discarded"); \
-    font->math->data = 0; \
-    font->math->length = 0; \
-  } while (0)
-
-namespace ots {
-
-bool ots_math_parse(Font *font, const uint8_t *data, size_t length) {
+bool OpenTypeMATH::Parse(const uint8_t *data, size_t length) {
   // Grab the number of glyphs in the font from the maxp table to check
   // GlyphIDs in MATH table.
-  if (!font->maxp) {
-    return OTS_FAILURE();
+  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
+      GetFont()->GetTypedTable(OTS_TAG_MAXP));
+  if (!maxp) {
+    return Error("Required maxp table missing");
   }
-  const uint16_t num_glyphs = font->maxp->num_glyphs;
+  const uint16_t num_glyphs = maxp->num_glyphs;
 
   Buffer table(data, length);
 
-  OpenTypeMATH* math = new OpenTypeMATH;
-  font->math = math;
-
   uint32_t version = 0;
   if (!table.ReadU32(&version)) {
     return OTS_FAILURE();
   }
   if (version != 0x00010000) {
-    DROP_THIS_TABLE("bad MATH version");
-    return true;
+    return Drop("bad MATH version");
   }
 
   uint16_t offset_math_constants = 0;
   uint16_t offset_math_glyph_info = 0;
   uint16_t offset_math_variants = 0;
   if (!table.ReadU16(&offset_math_constants) ||
       !table.ReadU16(&offset_math_glyph_info) ||
       !table.ReadU16(&offset_math_variants)) {
@@ -562,58 +543,42 @@ bool ots_math_parse(Font *font, const ui
   }
 
   if (offset_math_constants >= length ||
       offset_math_constants < kMathHeaderSize ||
       offset_math_glyph_info >= length ||
       offset_math_glyph_info < kMathHeaderSize ||
       offset_math_variants >= length ||
       offset_math_variants < kMathHeaderSize) {
-    DROP_THIS_TABLE("bad offset in MATH header");
-    return true;
+    return Drop("bad offset in MATH header");
   }
 
-  if (!ParseMathConstantsTable(font, data + offset_math_constants,
+  if (!ParseMathConstantsTable(data + offset_math_constants,
                                length - offset_math_constants)) {
-    DROP_THIS_TABLE("failed to parse MathConstants table");
-    return true;
+    return Drop("failed to parse MathConstants table");
   }
-  if (!ParseMathGlyphInfoTable(font, data + offset_math_glyph_info,
+  if (!ParseMathGlyphInfoTable(data + offset_math_glyph_info,
                                length - offset_math_glyph_info, num_glyphs)) {
-    DROP_THIS_TABLE("failed to parse MathGlyphInfo table");
-    return true;
+    return Drop("failed to parse MathGlyphInfo table");
   }
-  if (!ParseMathVariantsTable(font, data + offset_math_variants,
+  if (!ParseMathVariantsTable(data + offset_math_variants,
                               length - offset_math_variants, num_glyphs)) {
-    DROP_THIS_TABLE("failed to parse MathVariants table");
-    return true;
+    return Drop("failed to parse MathVariants table");
   }
 
-  math->data = data;
-  math->length = length;
+  this->m_data = data;
+  this->m_length = length;
   return true;
 }
 
-bool ots_math_should_serialise(Font *font) {
-  return font->math != NULL && font->math->data != NULL;
-}
-
-bool ots_math_serialise(OTSStream *out, Font *font) {
-  if (!out->Write(font->math->data, font->math->length)) {
+bool OpenTypeMATH::Serialize(OTSStream *out) {
+  if (!out->Write(this->m_data, this->m_length)) {
     return OTS_FAILURE();
   }
 
   return true;
 }
 
-void ots_math_reuse(Font *font, Font *other) {
-  font->math = other->math;
-  font->math_reused = true;
-}
-
-void ots_math_free(Font *font) {
-  delete font->math;
+bool OpenTypeMATH::ShouldSerialize() {
+  return Table::ShouldSerialize() && this->m_data != NULL;
 }
 
 }  // namespace ots
-
-#undef TABLE_NAME
-#undef DROP_THIS_TABLE
--- a/gfx/ots/src/math_.h
+++ b/gfx/ots/src/math_.h
@@ -1,25 +1,69 @@
-// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Copyright (c) 2014-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef OTS_MATH_H_
 #define OTS_MATH_H_
 
 #include "ots.h"
 
 namespace ots {
 
-struct OpenTypeMATH {
-  OpenTypeMATH()
-      : data(NULL),
-        length(0) {
-  }
+class OpenTypeMATH : public Table {
+ public:
+  explicit OpenTypeMATH(Font *font, uint32_t tag)
+      : Table(font, tag, tag),
+        m_data(NULL),
+        m_length(0) { }
+
+  bool Parse(const uint8_t *data, size_t length);
+  bool Serialize(OTSStream *out);
+  bool ShouldSerialize();
 
-  const uint8_t *data;
-  size_t length;
+ private:
+  bool ParseMathValueRecord(ots::Buffer* subtable,
+                            const uint8_t *data,
+                            const size_t length);
+  bool ParseMathConstantsTable(const uint8_t *data, size_t length);
+  bool ParseMathValueRecordSequenceForGlyphs(ots::Buffer* subtable,
+                                             const uint8_t *data,
+                                             const size_t length,
+                                             const uint16_t num_glyphs);
+  bool ParseMathItalicsCorrectionInfoTable(const uint8_t *data,
+                                           size_t length,
+                                           const uint16_t num_glyphs);
+  bool ParseMathTopAccentAttachmentTable(const uint8_t *data,
+                                         size_t length,
+                                         const uint16_t num_glyphs);
+  bool ParseMathKernTable(const uint8_t *data, size_t length);
+  bool ParseMathKernInfoTable(const uint8_t *data,
+                              size_t length,
+                              const uint16_t num_glyphs);
+  bool ParseMathGlyphInfoTable(const uint8_t *data,
+                               size_t length,
+                               const uint16_t num_glyphs);
+  bool ParseGlyphAssemblyTable(const uint8_t *data,
+                               size_t length,
+                               const uint16_t num_glyphs);
+  bool ParseMathGlyphConstructionTable(const uint8_t *data,
+                                       size_t length,
+                                       const uint16_t num_glyphs);
+  bool ParseMathGlyphConstructionSequence(ots::Buffer* subtable,
+                                          const uint8_t *data,
+                                          size_t length,
+                                          const uint16_t num_glyphs,
+                                          uint16_t offset_coverage,
+                                          uint16_t glyph_count,
+                                          const unsigned sequence_end);
+  bool ParseMathVariantsTable(const uint8_t *data,
+                              size_t length,
+                              const uint16_t num_glyphs);
+
+  const uint8_t *m_data;
+  size_t m_length;
 };
 
 }  // namespace ots
 
 #endif
 
--- a/gfx/ots/src/maxp.cc
+++ b/gfx/ots/src/maxp.cc
@@ -1,125 +1,103 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "maxp.h"
 
 // maxp - Maximum Profile
 // http://www.microsoft.com/typography/otspec/maxp.htm
 
-#define TABLE_NAME "maxp"
-
 namespace ots {
 
-bool ots_maxp_parse(Font *font, const uint8_t *data, size_t length) {
+bool OpenTypeMAXP::Parse(const uint8_t *data, size_t length) {
   Buffer table(data, length);
 
-  OpenTypeMAXP *maxp = new OpenTypeMAXP;
-  font->maxp = maxp;
-
   uint32_t version = 0;
   if (!table.ReadU32(&version)) {
-    return OTS_FAILURE_MSG("Failed to read version of maxp table");
+    return Error("Failed to read table version");
   }
 
   if (version >> 16 > 1) {
-    return OTS_FAILURE_MSG("Bad maxp version %d", version);
+    return Error("Unsupported table version 0x%x", version);
   }
 
-  if (!table.ReadU16(&maxp->num_glyphs)) {
-    return OTS_FAILURE_MSG("Failed to read number of glyphs from maxp table");
+  if (!table.ReadU16(&this->num_glyphs)) {
+    return Error("Failed to read numGlyphs");
   }
 
-  if (!maxp->num_glyphs) {
-    return OTS_FAILURE_MSG("Bad number of glyphs 0 in maxp table");
+  if (!this->num_glyphs) {
+    return Error("numGlyphs is 0");
   }
 
   if (version >> 16 == 1) {
-    maxp->version_1 = true;
-    if (!table.ReadU16(&maxp->max_points) ||
-        !table.ReadU16(&maxp->max_contours) ||
-        !table.ReadU16(&maxp->max_c_points) ||
-        !table.ReadU16(&maxp->max_c_contours) ||
-        !table.ReadU16(&maxp->max_zones) ||
-        !table.ReadU16(&maxp->max_t_points) ||
-        !table.ReadU16(&maxp->max_storage) ||
-        !table.ReadU16(&maxp->max_fdefs) ||
-        !table.ReadU16(&maxp->max_idefs) ||
-        !table.ReadU16(&maxp->max_stack) ||
-        !table.ReadU16(&maxp->max_size_glyf_instructions) ||
-        !table.ReadU16(&maxp->max_c_components) ||
-        !table.ReadU16(&maxp->max_c_depth)) {
-      return OTS_FAILURE_MSG("Failed to read maxp table");
+    this->version_1 = true;
+    if (!table.ReadU16(&this->max_points) ||
+        !table.ReadU16(&this->max_contours) ||
+        !table.ReadU16(&this->max_c_points) ||
+        !table.ReadU16(&this->max_c_contours) ||
+        !table.ReadU16(&this->max_zones) ||
+        !table.ReadU16(&this->max_t_points) ||
+        !table.ReadU16(&this->max_storage) ||
+        !table.ReadU16(&this->max_fdefs) ||
+        !table.ReadU16(&this->max_idefs) ||
+        !table.ReadU16(&this->max_stack) ||
+        !table.ReadU16(&this->max_size_glyf_instructions) ||
+        !table.ReadU16(&this->max_c_components) ||
+        !table.ReadU16(&this->max_c_depth)) {
+      return Error("Failed to read version 1 table data");
     }
 
-    if (maxp->max_zones == 0) {
+    if (this->max_zones == 0) {
       // workaround for ipa*.ttf Japanese fonts.
-      OTS_WARNING("bad max_zones: %u", maxp->max_zones);
-      maxp->max_zones = 1;
-    } else if (maxp->max_zones == 3) {
+      Warning("Bad maxZones: %u", this->max_zones);
+      this->max_zones = 1;
+    } else if (this->max_zones == 3) {
       // workaround for Ecolier-*.ttf fonts.
-      OTS_WARNING("bad max_zones: %u", maxp->max_zones);
-      maxp->max_zones = 2;
+      Warning("Bad maxZones: %u", this->max_zones);
+      this->max_zones = 2;
     }
 
-    if ((maxp->max_zones != 1) && (maxp->max_zones != 2)) {
-      return OTS_FAILURE_MSG("Bad max zones %d in maxp", maxp->max_zones);
+    if ((this->max_zones != 1) && (this->max_zones != 2)) {
+      return Error("Bad maxZones: %u", this->max_zones);
     }
   } else {
-    maxp->version_1 = false;
+    this->version_1 = false;
   }
 
   return true;
 }
 
-bool ots_maxp_should_serialise(Font *font) {
-  return font->maxp != NULL;
-}
-
-bool ots_maxp_serialise(OTSStream *out, Font *font) {
-  const OpenTypeMAXP *maxp = font->maxp;
-
-  if (!out->WriteU32(maxp->version_1 ? 0x00010000 : 0x00005000) ||
-      !out->WriteU16(maxp->num_glyphs)) {
-    return OTS_FAILURE_MSG("Failed to write maxp version or number of glyphs");
+bool OpenTypeMAXP::Serialize(OTSStream *out) {
+  if (!out->WriteU32(this->version_1 ? 0x00010000 : 0x00005000) ||
+      !out->WriteU16(this->num_glyphs)) {
+    return Error("Failed to write version or numGlyphs");
   }
 
-  if (!maxp->version_1) return true;
+  if (!this->version_1) return true;
 
-  if (!out->WriteU16(maxp->max_points) ||
-      !out->WriteU16(maxp->max_contours) ||
-      !out->WriteU16(maxp->max_c_points) ||
-      !out->WriteU16(maxp->max_c_contours)) {
-    return OTS_FAILURE_MSG("Failed to write maxp");
+  if (!out->WriteU16(this->max_points) ||
+      !out->WriteU16(this->max_contours) ||
+      !out->WriteU16(this->max_c_points) ||
+      !out->WriteU16(this->max_c_contours)) {
+    return Error("Failed to write maxp");
   }
 
-  if (!out->WriteU16(maxp->max_zones) ||
-      !out->WriteU16(maxp->max_t_points) ||
-      !out->WriteU16(maxp->max_storage) ||
-      !out->WriteU16(maxp->max_fdefs) ||
-      !out->WriteU16(maxp->max_idefs) ||
-      !out->WriteU16(maxp->max_stack) ||
-      !out->WriteU16(maxp->max_size_glyf_instructions)) {
-    return OTS_FAILURE_MSG("Failed to write more maxp");
+  if (!out->WriteU16(this->max_zones) ||
+      !out->WriteU16(this->max_t_points) ||
+      !out->WriteU16(this->max_storage) ||
+      !out->WriteU16(this->max_fdefs) ||
+      !out->WriteU16(this->max_idefs) ||
+      !out->WriteU16(this->max_stack) ||
+      !out->WriteU16(this->max_size_glyf_instructions)) {
+    return Error("Failed to write more maxp");
   }
 
-  if (!out->WriteU16(maxp->max_c_components) ||
-      !out->WriteU16(maxp->max_c_depth)) {
-    return OTS_FAILURE_MSG("Failed to write yet more maxp");
+  if (!out->WriteU16(this->max_c_components) ||
+      !out->WriteU16(this->max_c_depth)) {
+    return Error("Failed to write yet more maxp");
   }
 
   return true;
 }
 
-void ots_maxp_reuse(Font *font, Font *other) {
-  font->maxp = other->maxp;
-  font->maxp_reused = true;
-}
-
-void ots_maxp_free(Font *font) {
-  delete font->maxp;
-}
-
 }  // namespace ots
-
-#undef TABLE_NAME
--- a/gfx/ots/src/maxp.h
+++ b/gfx/ots/src/maxp.h
@@ -1,20 +1,27 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef OTS_MAXP_H_
 #define OTS_MAXP_H_
 
 #include "ots.h"
 
 namespace ots {
 
-struct OpenTypeMAXP {
+class OpenTypeMAXP : public Table {
+ public:
+  explicit OpenTypeMAXP(Font *font, uint32_t tag)
+      : Table(font, tag, tag) { }
+
+  bool Parse(const uint8_t *data, size_t length);
+  bool Serialize(OTSStream *out);
+
   uint16_t num_glyphs;
   bool version_1;
 
   uint16_t max_points;
   uint16_t max_contours;
   uint16_t max_c_points;
   uint16_t max_c_contours;
 
--- a/gfx/ots/src/metrics.cc
+++ b/gfx/ots/src/metrics.cc
@@ -1,165 +1,177 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011-2017 The OTS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "metrics.h"
 
 #include "head.h"
 #include "maxp.h"
 
 // OpenType horizontal and vertical common header format
 // http://www.microsoft.com/typography/otspec/hhea.htm
 // http://www.microsoft.com/typography/otspec/vhea.htm
 
-#define TABLE_NAME "metrics" // XXX: use individual table names
-
 namespace ots {
 
-bool ParseMetricsHeader(Font *font, Buffer *table,
-                        OpenTypeMetricsHeader *header) {
-  if (!table->ReadS16(&header->ascent) ||
-      !table->ReadS16(&header->descent) ||
-      !table->ReadS16(&header->linegap) ||
-      !table->ReadU16(&header->adv_width_max) ||
-      !table->ReadS16(&header->min_sb1) ||
-      !table->ReadS16(&header->min_sb2) ||
-      !table->ReadS16(&header->max_extent) ||
-      !table->ReadS16(&header->caret_slope_rise) ||
-      !table->ReadS16(&header->caret_slope_run) ||
-      !table->ReadS16(&header->caret_offset)) {
-    return OTS_FAILURE_MSG("Failed to read metrics header");
+bool OpenTypeMetricsHeader::Parse(const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  // Skip already read version.
+  if (!table.Skip(4)) {
+    return false;
   }
 
-  if (header->ascent < 0) {
-    OTS_WARNING("bad ascent: %d", header->ascent);
-    header->ascent = 0;
-  }
-  if (header->linegap < 0) {
-    OTS_WARNING("bad linegap: %d", header->linegap);
-    header->linegap = 0;
+  if (!table.ReadS16(&this->ascent) ||
+      !table.ReadS16(&this->descent) ||
+      !table.ReadS16(&this->linegap) ||
+      !table.ReadU16(&this->adv_width_max) ||
+      !table.ReadS16(&this->min_sb1) ||
+      !table.ReadS16(&this->min_sb2) ||
+      !table.ReadS16(&this->max_extent) ||
+      !table.ReadS16(&this->caret_slope_rise) ||
+      !table.ReadS16(&this->caret_slope_run) ||
+      !table.ReadS16(&this->caret_offset)) {
+    return Error("Failed to read table");
   }
 
-  if (!font->head) {
-    return OTS_FAILURE_MSG("Missing head font table");
+  if (this->ascent < 0) {
+    Warning("bad ascent: %d", this->ascent);
+    this->ascent = 0;
+  }
+  if (this->linegap < 0) {
+    Warning("bad linegap: %d", this->linegap);
+    this->linegap = 0;
+  }
+
+  OpenTypeHEAD *head = static_cast<OpenTypeHEAD*>(
+      GetFont()->GetTypedTable(OTS_TAG_HEAD));
+  if (!head) {
+    return Error("Missing head font table");
   }
 
   // if the font is non-slanted, caret_offset should be zero.
-  if (!(font->head->mac_style & 2) &&
-      (header->caret_offset != 0)) {
-    OTS_WARNING("bad caret offset: %d", header->caret_offset);
-    header->caret_offset = 0;
+  if (!(head->mac_style & 2) &&
+      (this->caret_offset != 0)) {
+    Warning("bad caret offset: %d", this->caret_offset);
+    this->caret_offset = 0;
   }
 
   // skip the reserved bytes
-  if (!table->Skip(8)) {
-    return OTS_FAILURE_MSG("Failed to skip reserverd bytes");
+  if (!table.Skip(8)) {
+    return Error("Failed to read reserverd bytes");
   }
 
   int16_t data_format;
-  if (!table->ReadS16(&data_format)) {
-    return OTS_FAILURE_MSG("Failed to read data format");
+  if (!table.ReadS16(&data_format)) {
+    return Error("Failed to read metricDataFormat");
   }
   if (data_format) {
-    return OTS_FAILURE_MSG("Bad data format %d", data_format);
+    return Error("Bad metricDataFormat: %d", data_format);
   }
 
-  if (!table->ReadU16(&header->num_metrics)) {
-    return OTS_FAILURE_MSG("Failed to read number of metrics");
+  if (!table.ReadU16(&this->num_metrics)) {
+    return Error("Failed to read number of metrics");
   }
 
-  if (!font->maxp) {
-    return OTS_FAILURE_MSG("Missing maxp font table");
+  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
+      GetFont()->GetTypedTable(OTS_TAG_MAXP));
+  if (!maxp) {
+    return Error("Missing maxp font table");
   }
 
-  if (header->num_metrics > font->maxp->num_glyphs) {
-    return OTS_FAILURE_MSG("Bad number of metrics %d", header->num_metrics);
+  if (this->num_metrics > maxp->num_glyphs) {
+    return Error("Bad number of metrics %d", this->num_metrics);
   }
 
   return true;
 }
 
-bool SerialiseMetricsHeader(const ots::Font *font,
-                            OTSStream *out,
-                            const OpenTypeMetricsHeader *header) {
-  if (!out->WriteU32(header->version) ||
-      !out->WriteS16(header->ascent) ||
-      !out->WriteS16(header->descent) ||
-      !out->WriteS16(header->linegap) ||
-      !out->WriteU16(header->adv_width_max) ||
-      !out->WriteS16(header->min_sb1) ||
-      !out->WriteS16(header->min_sb2) ||
-      !out->WriteS16(header->max_extent) ||
-      !out->WriteS16(header->caret_slope_rise) ||
-      !out->WriteS16(header->caret_slope_run) ||
-      !out->WriteS16(header->caret_offset) ||
+bool OpenTypeMetricsHeader::Serialize(OTSStream *out) {
+  if (!