bug 811382 - [esr10] update OTS library to r.95. r=me,jdaggett,emk a=akeybl
authorJonathan Kew <jkew@mozilla.com>
Fri, 07 Dec 2012 08:46:23 +0000
changeset 82095 164e6a42a414299dd8f94507b1c1caec418f71a0
parent 82094 ed34973f6e186e7f5612fec8dad12a807f3ee94c
child 82096 61cab3c822ff1452ba7e14483069b6ea1810c822
push id346
push userjkew@mozilla.com
push dateFri, 07 Dec 2012 08:51:00 +0000
reviewersme, jdaggett, emk, akeybl
bugs811382
milestone10.0.11esrpre
bug 811382 - [esr10] update OTS library to r.95. r=me,jdaggett,emk a=akeybl
gfx/ots/README.mozilla
gfx/ots/include/opentype-sanitiser.h
gfx/ots/src/cff.cc
gfx/ots/src/cmap.cc
gfx/ots/src/layout.cc
gfx/ots/src/ots.cc
toolkit/content/tests/chrome/window_largemenu.xul
--- a/gfx/ots/README.mozilla
+++ b/gfx/ots/README.mozilla
@@ -1,8 +1,8 @@
 This is the Sanitiser for OpenType project, from http://code.google.com/p/ots/.
 
-Current revision: r74
+Current revision: r95
 
 Applied local patches:
     ots-fix-vc10.patch - workaround for VS10 STL wrappers (bug 602558)
 
     ots-fix-sparc64.patch - fix alignment error on sparc64 (bug 643137)
--- a/gfx/ots/include/opentype-sanitiser.h
+++ b/gfx/ots/include/opentype-sanitiser.h
@@ -1,25 +1,29 @@
 // Copyright (c) 2009 The Chromium 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)
+#include <stdlib.h>
 typedef signed char int8_t;
 typedef unsigned char uint8_t;
 typedef short int16_t;
 typedef unsigned short uint16_t;
 typedef int int32_t;
 typedef unsigned int uint32_t;
 typedef __int64 int64_t;
 typedef unsigned __int64 uint64_t;
-#include <winsock2.h>  // for htons/ntohs
+#define ntohl(x) _byteswap_ulong (x)
+#define ntohs(x) _byteswap_ushort (x)
+#define htonl(x) _byteswap_ulong (x)
+#define htons(x) _byteswap_ushort (x)
 #else
 #include <arpa/inet.h>
 #include <stdint.h>
 #endif
 
 #include <algorithm>  // for std::min
 #include <cassert>
 #include <cstddef>
@@ -52,18 +56,19 @@ class OTSStream {
         std::min(length, static_cast<size_t>(4) - chksum_buffer_offset_);
       std::memcpy(chksum_buffer_ + chksum_buffer_offset_, data, l);
       chksum_buffer_offset_ += l;
       offset += l;
       length -= l;
     }
 
     if (chksum_buffer_offset_ == 4) {
-      // TODO(yusukes): This cast breaks the strict-aliasing rule.
-      chksum_ += ntohl(*reinterpret_cast<const uint32_t*>(chksum_buffer_));
+      uint32_t chksum;
+      std::memcpy(&chksum, chksum_buffer_, 4);
+      chksum_ += ntohl(chksum);
       chksum_buffer_offset_ = 0;
     }
 
     while (length >= 4) {
       uint32_t tmp;
       std::memcpy(&tmp, reinterpret_cast<const uint8_t *>(data) + offset,
         sizeof(uint32_t));
       chksum_ += ntohl(tmp);
--- a/gfx/ots/src/cff.cc
+++ b/gfx/ots/src/cff.cc
@@ -1,37 +1,43 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium 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>  // std::pair
 #include <vector>
 
 #include "cff_type2_charstring.h"
 
 // CFF - PostScript font program (Compact Font Format) table
-// http://www.microsoft.com/opentype/otspec/cff.htm
-// http://www.microsoft.com/opentype/otspec/5176.CFF.pdf
+// http://www.microsoft.com/typography/otspec/cff.htm
+// http://www.microsoft.com/typography/otspec/cffspec.htm
 
 namespace {
 
 enum DICT_OPERAND_TYPE {
   DICT_OPERAND_INTEGER,
   DICT_OPERAND_REAL,
   DICT_OPERATOR,
 };
 
 enum DICT_DATA_TYPE {
   DICT_DATA_TOPLEVEL,
   DICT_DATA_FDARRAY,
 };
 
+enum FONT_FORMAT {
+  FORMAT_UNKNOWN,
+  FORMAT_CID_KEYED,
+  FORMAT_OTHER,  // Including synthetic fonts
+};
+
 // see Appendix. A
 const size_t kNStdString = 390;
 
 bool ReadOffset(ots::Buffer *table, uint8_t off_size, uint32_t *offset) {
   if (off_size > 4) {
     return OTS_FAILURE();
   }
 
@@ -52,17 +58,17 @@ bool ParseIndex(ots::Buffer *table, ots:
   index->off_size = 0;
   index->offsets.clear();
 
   if (!table->ReadU16(&(index->count))) {
     return OTS_FAILURE();
   }
   if (index->count == 0) {
     // An empty INDEX.
-    index->offset_to_next = table->offset() + sizeof(index->count);
+    index->offset_to_next = table->offset();
     return true;
   }
 
   if (!table->ReadU8(&(index->off_size))) {
     return OTS_FAILURE();
   }
   if ((index->off_size == 0) ||
       (index->off_size > 4)) {
@@ -412,17 +418,17 @@ bool ParsePrivateDictData(
         ots::Buffer table(data, table_length);
         table.set_offset(operands.back().first + offset);
         ots::CFFIndex *local_subrs_index = NULL;
         if (type == DICT_DATA_FDARRAY) {
           if (out_cff->local_subrs_per_font.empty()) {
             return OTS_FAILURE();  // not reached.
           }
           local_subrs_index = out_cff->local_subrs_per_font.back();
-        } else if (type == DICT_DATA_TOPLEVEL) {
+        } else { // type == DICT_DATA_TOPLEVEL
           if (out_cff->local_subrs) {
             return OTS_FAILURE();  // two or more local_subrs?
           }
           local_subrs_index = new ots::CFFIndex;
           out_cff->local_subrs = local_subrs_index;
         }
         if (!ParseIndex(&table, local_subrs_index)) {
           return OTS_FAILURE();
@@ -459,16 +465,17 @@ bool ParseDictData(const uint8_t *data, 
     if (type == DICT_DATA_TOPLEVEL) {
       out_cff->char_strings_array.push_back(new ots::CFFIndex);
     }
     size_t dict_length = index.offsets[i] - index.offsets[i - 1];
     ots::Buffer table(data + index.offsets[i - 1], dict_length);
 
     std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > operands;
 
+    FONT_FORMAT font_format = FORMAT_UNKNOWN;
     bool have_ros = false;
     size_t glyphs = 0;
     size_t charset_offset = 0;
 
     while (table.offset() < dict_length) {
       if (!ParseDictDataReadNext(&table, &operands)) {
         return OTS_FAILURE();
       }
@@ -517,24 +524,31 @@ bool ParseDictData(const uint8_t *data, 
         // number
         case 13:  // UniqueID
         case (12U << 8) + 2:   // ItalicAngle
         case (12U << 8) + 3:   // UnderlinePosition
         case (12U << 8) + 4:   // UnderlineThickness
         case (12U << 8) + 5:   // PaintType
         case (12U << 8) + 8:   // StrokeWidth
         case (12U << 8) + 20:  // SyntheticBase
+          if (operands.size() != 1) {
+            return OTS_FAILURE();
+          }
+          break;
         case (12U << 8) + 31:  // CIDFontVersion
         case (12U << 8) + 32:  // CIDFontRevision
         case (12U << 8) + 33:  // CIDFontType
         case (12U << 8) + 34:  // CIDCount
         case (12U << 8) + 35:  // UIDBase
           if (operands.size() != 1) {
             return OTS_FAILURE();
           }
+          if (font_format != FORMAT_CID_KEYED) {
+            return OTS_FAILURE();
+          }
           break;
         case (12U << 8) + 6:   // CharstringType
           if (operands.size() != 1) {
             return OTS_FAILURE();
           }
           if(operands.back().second != DICT_OPERAND_INTEGER) {
             return OTS_FAILURE();
           }
@@ -755,40 +769,40 @@ bool ParseDictData(const uint8_t *data, 
             return OTS_FAILURE();
           }
           const uint32_t private_offset = operands.back().first;
           operands.pop_back();
           if (operands.back().second != DICT_OPERAND_INTEGER) {
             return OTS_FAILURE();
           }
           const uint32_t private_length = operands.back().first;
-          if (private_offset >= table_length) {
+          if (private_offset > table_length) {
             return OTS_FAILURE();
           }
           if (private_length >= table_length) {
             return OTS_FAILURE();
           }
           if (private_length + private_offset > table_length) {
-            // does not overflow since table_length < 1GB
             return OTS_FAILURE();
           }
           // parse "15. Private DICT Data"
           if (!ParsePrivateDictData(data, table_length,
                                     private_offset, private_length,
                                     type, out_cff)) {
             return OTS_FAILURE();
           }
           break;
         }
 
         // ROS
         case (12U << 8) + 30:
-          if (type != DICT_DATA_TOPLEVEL) {
+          if (font_format != FORMAT_UNKNOWN) {
             return OTS_FAILURE();
           }
+          font_format = FORMAT_CID_KEYED;
           if (operands.size() != 3) {
             return OTS_FAILURE();
           }
           // check SIDs
           operands.pop_back();  // ignore the first number.
           if (!CheckSid(operands.back(), sid_max)) {
             return OTS_FAILURE();
           }
@@ -801,16 +815,20 @@ bool ParseDictData(const uint8_t *data, 
           }
           have_ros = true;
           break;
 
         default:
           return OTS_FAILURE();
       }
       operands.clear();
+
+      if (font_format == FORMAT_UNKNOWN) {
+        font_format = FORMAT_OTHER;
+      }
     }
 
     // parse "13. Charsets"
     if (charset_offset) {
       ots::Buffer table(data, table_length);
       table.set_offset(charset_offset);
       uint8_t format = 0;
       if (!table.ReadU8(&format)) {
--- a/gfx/ots/src/cmap.cc
+++ b/gfx/ots/src/cmap.cc
@@ -207,17 +207,17 @@ bool ParseFormat4(ots::OpenTypeFile *fil
   // The last range must end at 0xffff
   if (ranges[segcount - 1].end_range != 0xffff) {
     return OTS_FAILURE();
   }
 
   // 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 = 1; i < segcount; ++i) {
+  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 = 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();
         }
--- a/gfx/ots/src/layout.cc
+++ b/gfx/ots/src/layout.cc
@@ -142,17 +142,17 @@ bool ParseFeatureTable(const uint8_t *da
   uint16_t offset_feature_params = 0;
   uint16_t lookup_count = 0;
   if (!subtable.ReadU16(&offset_feature_params) ||
       !subtable.ReadU16(&lookup_count)) {
     return OTS_FAILURE();
   }
 
   const unsigned feature_table_end =
-      2 * static_cast<unsigned>(num_lookups) + 4;
+      2 * static_cast<unsigned>(lookup_count) + 4;
   if (feature_table_end > std::numeric_limits<uint16_t>::max()) {
     return OTS_FAILURE();
   }
   // |offset_feature_params| is generally set to NULL.
   if (offset_feature_params != 0 &&
       (offset_feature_params < feature_table_end ||
        offset_feature_params >= length)) {
     return OTS_FAILURE();
@@ -389,17 +389,22 @@ bool ParseCoverageFormat2(const uint8_t 
     uint16_t start = 0;
     uint16_t end = 0;
     uint16_t start_coverage_index = 0;
     if (!subtable.ReadU16(&start) ||
         !subtable.ReadU16(&end) ||
         !subtable.ReadU16(&start_coverage_index)) {
       return OTS_FAILURE();
     }
-    if (start > end || (last_end && start <= last_end)) {
+
+    // Some of the Adobe Pro fonts have ranges that overlap by one element: the
+    // start of one range is equal to the end of the previous range. Therefore
+    // the < in the following condition should be <= were it not for this.
+    // See crbug.com/134135.
+    if (start > end || (last_end && start < last_end)) {
       OTS_WARNING("glyph range is overlapping.");
       return OTS_FAILURE();
     }
     if (start_coverage_index != last_start_coverage_index) {
       OTS_WARNING("bad start coverage index.");
       return OTS_FAILURE();
     }
     last_end = end;
--- a/gfx/ots/src/ots.cc
+++ b/gfx/ots/src/ots.cc
@@ -5,16 +5,17 @@
 #include "ots.h"
 
 #include <sys/types.h>
 #include <zlib.h>
 
 #include <algorithm>
 #include <cstdlib>
 #include <cstring>
+#include <limits>
 #include <map>
 #include <vector>
 
 // The OpenType Font File
 // http://www.microsoft.com/typography/otspec/cmap.htm
 
 namespace {
 
@@ -23,39 +24,42 @@ bool g_debug_output = true;
 struct OpenTypeTable {
   uint32_t tag;
   uint32_t chksum;
   uint32_t offset;
   uint32_t length;
   uint32_t uncompressed_length;
 };
 
-// Round a value up to the nearest multiple of 4. Note that this can overflow
-// and return zero.
+// Round a value up to the nearest multiple of 4. Don't round the value in the
+// case that rounding up overflows.
 template<typename T> T Round4(T value) {
+  if (std::numeric_limits<T>::max() - value < 3) {
+    return value;
+  }
   return (value + 3) & ~3;
 }
 
-uint32_t Tag(const char *tag_str) {
-  uint32_t ret;
-  std::memcpy(&ret, tag_str, 4);
-  return ret;
-}
-
 bool CheckTag(uint32_t tag_value) {
   for (unsigned i = 0; i < 4; ++i) {
     const uint32_t check = tag_value & 0xff;
     if (check < 32 || check > 126) {
       return false;  // non-ASCII character found.
     }
     tag_value >>= 8;
   }
   return true;
 }
 
+uint32_t Tag(const char *tag_str) {	
+  uint32_t ret;	
+  std::memcpy(&ret, tag_str, 4);	
+  return ret;	
+}
+
 struct OutputTable {
   uint32_t tag;
   size_t offset;
   size_t length;
   uint32_t chksum;
 
   static bool SortByTag(const OutputTable& a, const OutputTable& b) {
     const uint32_t atag = ntohl(a.tag);
@@ -79,89 +83,91 @@ struct Arena {
     return p;
   }
 
  private:
   std::vector<uint8_t*> hunks_;
 };
 
 const struct {
-  uint32_t tag;
+  const char* tag;
   bool (*parse)(ots::OpenTypeFile *otf, const uint8_t *data, size_t length);
   bool (*serialise)(ots::OTSStream *out, ots::OpenTypeFile *file);
   bool (*should_serialise)(ots::OpenTypeFile *file);
   void (*free)(ots::OpenTypeFile *file);
   bool required;
 } table_parsers[] = {
-  { Tag("maxp"), ots::ots_maxp_parse, ots::ots_maxp_serialise,
+  { "maxp", ots::ots_maxp_parse, ots::ots_maxp_serialise,
     ots::ots_maxp_should_serialise, ots::ots_maxp_free, true },
-  { Tag("head"), ots::ots_head_parse, ots::ots_head_serialise,
+  { "head", ots::ots_head_parse, ots::ots_head_serialise,
     ots::ots_head_should_serialise, ots::ots_head_free, true },
-  { Tag("OS/2"), ots::ots_os2_parse, ots::ots_os2_serialise,
+  { "OS/2", ots::ots_os2_parse, ots::ots_os2_serialise,
     ots::ots_os2_should_serialise, ots::ots_os2_free, true },
-  { Tag("cmap"), ots::ots_cmap_parse, ots::ots_cmap_serialise,
+  { "cmap", ots::ots_cmap_parse, ots::ots_cmap_serialise,
     ots::ots_cmap_should_serialise, ots::ots_cmap_free, true },
-  { Tag("hhea"), ots::ots_hhea_parse, ots::ots_hhea_serialise,
+  { "hhea", ots::ots_hhea_parse, ots::ots_hhea_serialise,
     ots::ots_hhea_should_serialise, ots::ots_hhea_free, true },
-  { Tag("hmtx"), ots::ots_hmtx_parse, ots::ots_hmtx_serialise,
+  { "hmtx", ots::ots_hmtx_parse, ots::ots_hmtx_serialise,
     ots::ots_hmtx_should_serialise, ots::ots_hmtx_free, true },
-  { Tag("name"), ots::ots_name_parse, ots::ots_name_serialise,
+  { "name", ots::ots_name_parse, ots::ots_name_serialise,
     ots::ots_name_should_serialise, ots::ots_name_free, true },
-  { Tag("post"), ots::ots_post_parse, ots::ots_post_serialise,
+  { "post", ots::ots_post_parse, ots::ots_post_serialise,
     ots::ots_post_should_serialise, ots::ots_post_free, true },
-  { Tag("loca"), ots::ots_loca_parse, ots::ots_loca_serialise,
+  { "loca", ots::ots_loca_parse, ots::ots_loca_serialise,
     ots::ots_loca_should_serialise, ots::ots_loca_free, false },
-  { Tag("glyf"), ots::ots_glyf_parse, ots::ots_glyf_serialise,
+  { "glyf", ots::ots_glyf_parse, ots::ots_glyf_serialise,
     ots::ots_glyf_should_serialise, ots::ots_glyf_free, false },
-  { Tag("CFF "), ots::ots_cff_parse, ots::ots_cff_serialise,
+  { "CFF ", ots::ots_cff_parse, ots::ots_cff_serialise,
     ots::ots_cff_should_serialise, ots::ots_cff_free, false },
-  { Tag("VDMX"), ots::ots_vdmx_parse, ots::ots_vdmx_serialise,
+  { "VDMX", ots::ots_vdmx_parse, ots::ots_vdmx_serialise,
     ots::ots_vdmx_should_serialise, ots::ots_vdmx_free, false },
-  { Tag("hdmx"), ots::ots_hdmx_parse, ots::ots_hdmx_serialise,
+  { "hdmx", ots::ots_hdmx_parse, ots::ots_hdmx_serialise,
     ots::ots_hdmx_should_serialise, ots::ots_hdmx_free, false },
-  { Tag("gasp"), ots::ots_gasp_parse, ots::ots_gasp_serialise,
+  { "gasp", ots::ots_gasp_parse, ots::ots_gasp_serialise,
     ots::ots_gasp_should_serialise, ots::ots_gasp_free, false },
-  { Tag("cvt "), ots::ots_cvt_parse, ots::ots_cvt_serialise,
+  { "cvt ", ots::ots_cvt_parse, ots::ots_cvt_serialise,
     ots::ots_cvt_should_serialise, ots::ots_cvt_free, false },
-  { Tag("fpgm"), ots::ots_fpgm_parse, ots::ots_fpgm_serialise,
+  { "fpgm", ots::ots_fpgm_parse, ots::ots_fpgm_serialise,
     ots::ots_fpgm_should_serialise, ots::ots_fpgm_free, false },
-  { Tag("prep"), ots::ots_prep_parse, ots::ots_prep_serialise,
+  { "prep", ots::ots_prep_parse, ots::ots_prep_serialise,
     ots::ots_prep_should_serialise, ots::ots_prep_free, false },
-  { Tag("LTSH"), ots::ots_ltsh_parse, ots::ots_ltsh_serialise,
+  { "LTSH", ots::ots_ltsh_parse, ots::ots_ltsh_serialise,
     ots::ots_ltsh_should_serialise, ots::ots_ltsh_free, false },
-  { Tag("VORG"), ots::ots_vorg_parse, ots::ots_vorg_serialise,
+  { "VORG", ots::ots_vorg_parse, ots::ots_vorg_serialise,
     ots::ots_vorg_should_serialise, ots::ots_vorg_free, false },
-  { Tag("kern"), ots::ots_kern_parse, ots::ots_kern_serialise,
+  { "kern", ots::ots_kern_parse, ots::ots_kern_serialise,
     ots::ots_kern_should_serialise, ots::ots_kern_free, false },
   // We need to parse GDEF table in advance of parsing GSUB/GPOS tables
   // because they could refer GDEF table.
-  { Tag("GDEF"), ots::ots_gdef_parse, ots::ots_gdef_serialise,
+  { "GDEF", ots::ots_gdef_parse, ots::ots_gdef_serialise,
     ots::ots_gdef_should_serialise, ots::ots_gdef_free, false },
-  { Tag("GPOS"), ots::ots_gpos_parse, ots::ots_gpos_serialise,
+  { "GPOS", ots::ots_gpos_parse, ots::ots_gpos_serialise,
     ots::ots_gpos_should_serialise, ots::ots_gpos_free, false },
-  { Tag("GSUB"), ots::ots_gsub_parse, ots::ots_gsub_serialise,
+  { "GSUB", ots::ots_gsub_parse, ots::ots_gsub_serialise,
     ots::ots_gsub_should_serialise, ots::ots_gsub_free, false },
-  { Tag("vhea"), ots::ots_vhea_parse, ots::ots_vhea_serialise,
+  { "vhea", ots::ots_vhea_parse, ots::ots_vhea_serialise,
     ots::ots_vhea_should_serialise, ots::ots_vhea_free, false },
-  { Tag("vmtx"), ots::ots_vmtx_parse, ots::ots_vmtx_serialise,
+  { "vmtx", ots::ots_vmtx_parse, ots::ots_vmtx_serialise,
     ots::ots_vmtx_should_serialise, ots::ots_vmtx_free, false },
   // TODO(bashi): Support mort, base, and jstf tables.
   { 0, NULL, NULL, NULL, NULL, false },
 };
 
 bool IsValidVersionTag(uint32_t tag) {
   return tag == Tag("\x00\x01\x00\x00") ||
          // OpenType fonts with CFF data have 'OTTO' tag.
          tag == Tag("OTTO") ||
          // Older Mac fonts might have 'true' or 'typ1' tag.
          tag == Tag("true") ||
          tag == Tag("typ1");
 }
 
-bool ProcessGeneric(ots::OpenTypeFile *header, ots::OTSStream *output,
+bool ProcessGeneric(ots::OpenTypeFile *header,
+                    uint32_t signature,
+                    ots::OTSStream *output,
                     const uint8_t *data, size_t length,
                     const std::vector<OpenTypeTable>& tables,
                     ots::Buffer& file);
 
 bool ProcessTTF(ots::OpenTypeFile *header,
                 ots::OTSStream *output, const uint8_t *data, size_t length) {
   ots::Buffer file(data, length);
 
@@ -229,17 +235,18 @@ bool ProcessTTF(ots::OpenTypeFile *heade
         !file.ReadU32(&table.length)) {
       return OTS_FAILURE();
     }
 
     table.uncompressed_length = table.length;
     tables.push_back(table);
   }
 
-  return ProcessGeneric(header, output, data, length, tables, file);
+  return ProcessGeneric(header, header->version, output, data, length,
+                        tables, file);
 }
 
 bool ProcessWOFF(ots::OpenTypeFile *header,
                  ots::OTSStream *output, const uint8_t *data, size_t length) {
   ots::Buffer file(data, length);
 
   // we disallow all files > 1GB in size for sanity.
   if (length > 1024 * 1024 * 1024) {
@@ -266,54 +273,142 @@ bool ProcessWOFF(ots::OpenTypeFile *head
   header->entry_selector = 0;
   header->range_shift = 0;
 
   uint32_t reported_length;
   if (!file.ReadU32(&reported_length) || length != reported_length) {
     return OTS_FAILURE();
   }
 
-  if (!file.ReadU16(&header->num_tables)) {
+  if (!file.ReadU16(&header->num_tables) || !header->num_tables) {
     return OTS_FAILURE();
   }
 
   uint16_t reserved_value;
   if (!file.ReadU16(&reserved_value) || reserved_value) {
     return OTS_FAILURE();
   }
 
+  uint32_t reported_total_sfnt_size;
+  if (!file.ReadU32(&reported_total_sfnt_size)) {
+    return OTS_FAILURE();
+  }
+
   // We don't care about these fields of the header:
-  //   uint32_t uncompressed_size;
   //   uint16_t major_version, minor_version
-  //   uint32_t meta_offset, meta_length, meta_length_orig
-  //   uint32_t priv_offset, priv_length
-  if (!file.Skip(6 * 4 + 2 * 2)) {
+  if (!file.Skip(2 * 2)) {
+    return OTS_FAILURE();
+  }
+
+  // Checks metadata block size.
+  uint32_t meta_offset;
+  uint32_t meta_length;
+  uint32_t meta_length_orig;
+  if (!file.ReadU32(&meta_offset) ||
+      !file.ReadU32(&meta_length) ||
+      !file.ReadU32(&meta_length_orig)) {
     return OTS_FAILURE();
   }
+  if (meta_offset) {
+    if (meta_offset >= length || length - meta_offset < meta_length) {
+      return OTS_FAILURE();
+    }
+  }
+
+  // Checks private data block size.
+  uint32_t priv_offset;
+  uint32_t priv_length;
+  if (!file.ReadU32(&priv_offset) ||
+      !file.ReadU32(&priv_length)) {
+    return OTS_FAILURE();
+  }
+  if (priv_offset) {
+    if (priv_offset >= length || length - priv_offset < priv_length) {
+      return OTS_FAILURE();
+    }
+  }
 
   // Next up is the list of tables.
   std::vector<OpenTypeTable> tables;
 
+  uint32_t first_index = 0;
+  uint32_t last_index = 0;
+  // Size of sfnt header plus size of table records.
+  uint64_t total_sfnt_size = 12 + 16 * header->num_tables;
   for (unsigned i = 0; i < header->num_tables; ++i) {
     OpenTypeTable table;
     if (!file.ReadTag(&table.tag) ||
         !file.ReadU32(&table.offset) ||
         !file.ReadU32(&table.length) ||
         !file.ReadU32(&table.uncompressed_length) ||
         !file.ReadU32(&table.chksum)) {
       return OTS_FAILURE();
     }
 
+    total_sfnt_size += Round4(table.uncompressed_length);
+    if (total_sfnt_size > std::numeric_limits<uint32_t>::max()) {
+      return OTS_FAILURE();
+    }
     tables.push_back(table);
+    if (i == 0 || tables[first_index].offset > table.offset)
+      first_index = i;
+    if (i == 0 || tables[last_index].offset < table.offset)
+      last_index = i;
+  }
+
+  if (reported_total_sfnt_size != total_sfnt_size) {
+    return OTS_FAILURE();
+  }
+
+  // Table data must follow immediately after the header.
+  if (tables[first_index].offset != Round4(file.offset())) {
+    return OTS_FAILURE();
   }
 
-  return ProcessGeneric(header, output, data, length, tables, file);
+  if (tables[last_index].offset >= length ||
+      length - tables[last_index].offset < tables[last_index].length) {
+    return OTS_FAILURE();
+  }
+  // Blocks must follow immediately after the previous block.
+  // (Except for padding with a maximum of three null bytes)
+  uint64_t block_end = Round4(
+      static_cast<uint64_t>(tables[last_index].offset) +
+      static_cast<uint64_t>(tables[last_index].length));
+  if (block_end > std::numeric_limits<uint32_t>::max()) {
+    return OTS_FAILURE();
+  }
+  if (meta_offset) {
+    if (block_end != meta_offset) {
+      return OTS_FAILURE();
+    }
+    block_end = Round4(static_cast<uint64_t>(meta_offset) +
+                       static_cast<uint64_t>(meta_length));
+    if (block_end > std::numeric_limits<uint32_t>::max()) {
+      return OTS_FAILURE();
+    }
+  }
+  if (priv_offset) {
+    if (block_end != priv_offset) {
+      return OTS_FAILURE();
+    }
+    block_end = Round4(static_cast<uint64_t>(priv_offset) +
+                       static_cast<uint64_t>(priv_length));
+    if (block_end > std::numeric_limits<uint32_t>::max()) {
+      return OTS_FAILURE();
+    }
+  }
+  if (block_end != Round4(length)) {
+    return OTS_FAILURE();
+  }
+
+  return ProcessGeneric(header, woff_tag, output, data, length, tables, file);
 }
 
-bool ProcessGeneric(ots::OpenTypeFile *header, ots::OTSStream *output,
+bool ProcessGeneric(ots::OpenTypeFile *header, uint32_t signature,
+                    ots::OTSStream *output,
                     const uint8_t *data, size_t length,
                     const std::vector<OpenTypeTable>& tables,
                     ots::Buffer& file) {
   const size_t data_offset = file.offset();
 
   uint32_t uncompressed_sum = 0;
 
   for (unsigned i = 0; i < header->num_tables; ++i) {
@@ -364,21 +459,22 @@ bool ProcessGeneric(ots::OpenTypeFile *h
       if (uncompressed_sum + tables[i].uncompressed_length < uncompressed_sum) {
         return OTS_FAILURE();
       }
 
       uncompressed_sum += tables[i].uncompressed_length;
     }
     // since we required that the file be < 1GB in length, and that the table
     // length is < 1GB, the following addtion doesn't overflow
-    const uint32_t end_byte = Round4(tables[i].offset + tables[i].length);
-    // Some fonts which are automatically generated by a font generator
-    // called TTX seems not to add 0-padding to the final table. It might be
-    // ok to accept these fonts so we round up the length of the font file.
-    if (!end_byte || end_byte > Round4(length)) {
+    uint32_t end_byte = tables[i].offset + tables[i].length;
+    // Tables in the WOFF file must be aligned 4-byte boundary.
+    if (signature == Tag("wOFF")) {
+        end_byte = Round4(end_byte);
+    }
+    if (!end_byte || end_byte > length) {
       return OTS_FAILURE();
     }
   }
 
   // All decompressed tables uncompressed must be <= 30MB.
   if (uncompressed_sum > 30 * 1024 * 1024) {
     return OTS_FAILURE();
   }
@@ -407,17 +503,17 @@ bool ProcessGeneric(ots::OpenTypeFile *h
   }
 
   Arena arena;
 
   for (unsigned i = 0; ; ++i) {
     if (table_parsers[i].parse == NULL) break;
 
     const std::map<uint32_t, OpenTypeTable>::const_iterator it
-        = table_map.find(table_parsers[i].tag);
+        = table_map.find(Tag(table_parsers[i].tag));
 
     if (it == table_map.end()) {
       if (table_parsers[i].required) {
         return OTS_FAILURE();
       }
       continue;
     }
 
@@ -502,21 +598,22 @@ bool ProcessGeneric(ots::OpenTypeFile *h
       break;
     }
 
     if (!table_parsers[i].should_serialise(header)) {
       continue;
     }
 
     OutputTable out;
-    out.tag = table_parsers[i].tag;
+    uint32_t tag = Tag(table_parsers[i].tag);
+    out.tag = tag;
     out.offset = output->Tell();
 
     output->ResetChecksum();
-    if (table_parsers[i].tag == Tag("head")) {
+    if (tag == Tag("head")) {
       head_table_offset = out.offset;
     }
     if (!table_parsers[i].serialise(output, header)) {
       return OTS_FAILURE();
     }
 
     const size_t end_offset = output->Tell();
     if (end_offset <= out.offset) {
--- a/toolkit/content/tests/chrome/window_largemenu.xul
+++ b/toolkit/content/tests/chrome/window_largemenu.xul
@@ -15,22 +15,22 @@
 <script>
 <![CDATA[
 
 var gOverflowed = false, gUnderflowed = false;
 var gContextMenuTests = false;
 var gScreenY = -1;
 var gTestIndex = 0;
 var gTests = ["open normal", "open flipped position", "open with scrolling",
-              "open after scrolling", "open small again",
+              "open after scrolling" /* , "open small again",
               "menu movement", "panel movement",
               "context menu enough space below",
               "context menu more space above",
               "context menu too big either side",
-              "context menu larger than screen"];
+              "context menu larger than screen" */ ];
 
 function getScreenXY(element)
 {
   var screenX, screenY;
   var mouseFn = function(event) {
     screenX = event.screenX - 1;
     screenY = event.screenY - 1;
   }
@@ -46,16 +46,21 @@ function getScreenXY(element)
 function runTests()
 {
   [, gScreenY] = getScreenXY(document.documentElement);
   nextTest();
 }
 
 function nextTest()
 {
+  setTimeout("doNextTest()", 2000);
+}
+
+function doNextTest()
+{
   gOverflowed = false, gUnderflowed = false;
 
   var y = screen.height;
   if (gTestIndex == 1) // open flipped position test:
     y -= 100;
   else
     y /= 2;
 
@@ -73,16 +78,21 @@ function nextTest()
       popup.removeChild(popup.lastChild);
   }
 
   popup.openPopupAtScreen(100, y, false);
 }
 
 function popupShown()
 {
+  setTimeout("doPopupShown()", 2000);
+}
+
+function doPopupShown()
+{
   if (gTests[gTestIndex] == "menu movement")
     return testPopupMovement();
 
   if (gContextMenuTests)
     return contextMenuPopupShown();
 
   var popup = document.getElementById("popup");
   var rect = popup.getBoundingClientRect();
@@ -109,37 +119,37 @@ function popupShown()
     ok(!gOverflowed && !gUnderflowed, gTests[gTestIndex] + " overflow")
   }
   else if (gTestIndex == 2) {
     // the popup is too large so ensure that it is on screen
     ok(Math.round(rect.top) + gScreenY >= screen.top, gTests[gTestIndex] + " top");
     ok(Math.round(rect.bottom) + gScreenY < screen.height, gTests[gTestIndex] + " bottom");
     ok(gOverflowed && !gUnderflowed, gTests[gTestIndex] + " overflow")
 
-    sbo.scrollTo(0, 40);
-    expectedScrollPos = 40;
+    sbo.scrollTo(0, 15);
+    expectedScrollPos = 15;
   }
   else if (gTestIndex == 3) {
-    expectedScrollPos = 40;
+    expectedScrollPos = 15;
   }
   else if (gTestIndex == 4) {
     // note that if the height is odd, the y-offset will have been rounded
     // down when we pass the fractional value to openPopupAtScreen above.
     is(Math.round(rect.top) + gScreenY, Math.floor(screen.height / 2),
                               gTests[gTestIndex] + " top");
     ok(Math.round(rect.bottom) + gScreenY < screen.height,
                                 gTests[gTestIndex] + " bottom");
     ok(!gOverflowed && gUnderflowed, gTests[gTestIndex] + " overflow")
   }
 
   var sx = { }, sy = { };
   sbo.getPosition(sx, sy);
   is(sy.value, expectedScrollPos, "menu scroll position");
 
-  popup.hidePopup();
+  hideThePopup(popup);
 }
 
 function is(l, r, n) { window.opener.wrappedJSObject.SimpleTest.is(l,r,n); }
 function ok(v, n) { window.opener.wrappedJSObject.SimpleTest.ok(v,n); }
 
 var oldx, oldy, waitSteps = 0;
 function moveWindowTo(x, y, callback, arg)
 {
@@ -163,16 +173,28 @@ function moveWindowTo(x, y, callback, ar
     setTimeout(moveWindowTo, 100, x, y, callback, arg);
   }
   else {
     waitSteps = 0;
     callback(arg);
   }
 }
 
+var thePopup;
+function hideThePopup(p)
+{
+  thePopup = p;
+  setTimeout("doHidePopup()", 2000);
+}
+
+function doHidePopup(p)
+{
+  thePopup.hidePopup();
+}
+
 function popupHidden()
 {
   gTestIndex++;
   if (gTestIndex == gTests.length) {
     window.opener.wrappedJSObject.SimpleTest.finish();
     window.close();
   }
   else if (gTests[gTestIndex] == "context menu enough space below") {