Import Cobalt 21.lts.5.304347
diff --git a/src/CONTRIBUTING.md b/src/CONTRIBUTING.md
index 71f6b83..332d6ab 100644
--- a/src/CONTRIBUTING.md
+++ b/src/CONTRIBUTING.md
@@ -53,7 +53,7 @@
   1. Run `git clang-format HEAD~` to apply default C++ formatting rules,
      followed by `git commit -a --amend` to squash any formatting changes
      into your commit.
-  1. Run `git cl upload` to upload the review to
+  1. Run `git push origin HEAD:refs/for/master` to upload the review to
      [Cobalt's Gerrit instance](https://cobalt-review.googlesource.com/).
   1. Someone from the maintainers team will review the code, putting up comments
      on any things that need to change for submission.
@@ -62,4 +62,3 @@
   1. If you do not need to make any more changes, a maintainer will integrate
      the change into our private repository, and it will get pushed out to the
      public repository after some time.
-
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index 87efaac..bd3b8b4 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-302899
\ No newline at end of file
+304347
\ No newline at end of file
diff --git a/src/cobalt/build/save_build_id.py b/src/cobalt/build/save_build_id.py
index 014e80d..fc81ab9 100755
--- a/src/cobalt/build/save_build_id.py
+++ b/src/cobalt/build/save_build_id.py
@@ -63,7 +63,12 @@
     return 0
 
   if not options.build_id:
-    options.build_id = gyp_utils.GetBuildNumber()
+    build_id_server_url = os.environ.get('BUILD_ID_SERVER_URL')
+    if build_id_server_url:
+      options.build_id = gyp_utils.GetBuildNumber(
+          version_server=build_id_server_url)
+    else:
+      options.build_id = gyp_utils.GetBuildNumber()
 
   if not options.build_id:
     logging.error('Unable to retrieve build id.')
diff --git a/src/cobalt/media/base/sbplayer_pipeline.cc b/src/cobalt/media/base/sbplayer_pipeline.cc
index 92979dd..a393489 100644
--- a/src/cobalt/media/base/sbplayer_pipeline.cc
+++ b/src/cobalt/media/base/sbplayer_pipeline.cc
@@ -852,11 +852,15 @@
         task_runner_, source_url, window_, this, set_bounds_helper_.get(),
         allow_resume_after_suspend_, *decode_to_texture_output_mode_,
         on_encrypted_media_init_data_encountered_cb_, video_frame_provider_));
-    SetPlaybackRateTask(playback_rate_);
-    SetVolumeTask(volume_);
+    if (player_->IsValid()) {
+      SetPlaybackRateTask(playback_rate_);
+      SetVolumeTask(volume_);
+    } else {
+      player_.reset();
+    }
   }
 
-  if (player_->IsValid()) {
+  if (player_ && player_->IsValid()) {
     base::Closure output_mode_change_cb;
     {
       base::AutoLock auto_lock(lock_);
@@ -867,7 +871,8 @@
     return;
   }
 
-  player_.reset();
+  DLOG(ERROR) << "SbPlayerPipeline::CreateUrlPlayer failed: "
+                 "player_->IsValid() is false.";
   CallSeekCB(DECODER_ERROR_NOT_SUPPORTED);
 }
 
@@ -937,33 +942,35 @@
         set_bounds_helper_.get(), allow_resume_after_suspend_,
         *decode_to_texture_output_mode_, video_frame_provider_,
         max_video_capabilities_));
-
-    if (!player_->IsValid()) {
+    if (player_->IsValid()) {
+      SetPlaybackRateTask(playback_rate_);
+      SetVolumeTask(volume_);
+    } else {
       player_.reset();
-      DLOG(ERROR) << "SbPlayerPipeline::CreatePlayer failed: "
-                     "player_->IsValid() is false.";
-      CallSeekCB(DECODER_ERROR_NOT_SUPPORTED);
-      return;
     }
-
-    SetPlaybackRateTask(playback_rate_);
-    SetVolumeTask(volume_);
   }
 
-  base::Closure output_mode_change_cb;
-  {
-    base::AutoLock auto_lock(lock_);
-    DCHECK(!output_mode_change_cb_.is_null());
-    output_mode_change_cb = std::move(output_mode_change_cb_);
-  }
-  output_mode_change_cb.Run();
+  if (player_ && player_->IsValid()) {
+    base::Closure output_mode_change_cb;
+    {
+      base::AutoLock auto_lock(lock_);
+      DCHECK(!output_mode_change_cb_.is_null());
+      output_mode_change_cb = std::move(output_mode_change_cb_);
+    }
+    output_mode_change_cb.Run();
 
-  if (audio_stream_) {
-    UpdateDecoderConfig(audio_stream_);
+    if (audio_stream_) {
+      UpdateDecoderConfig(audio_stream_);
+    }
+    if (video_stream_) {
+      UpdateDecoderConfig(video_stream_);
+    }
+    return;
   }
-  if (video_stream_) {
-    UpdateDecoderConfig(video_stream_);
-  }
+
+  DLOG(ERROR) << "SbPlayerPipeline::CreatePlayer failed: "
+                 "player_->IsValid() is false.";
+  CallSeekCB(DECODER_ERROR_NOT_SUPPORTED);
 }
 
 void SbPlayerPipeline::OnDemuxerInitialized(PipelineStatus status) {
@@ -1353,10 +1360,17 @@
   if (player_) {
     player_->Resume();
     if (!player_->IsValid()) {
-      player_.reset();
-      DLOG(ERROR) << "SbPlayerPipeline::ResumeTask failed: "
-                     "player_->IsValid() is false.";
-      CallSeekCB(DECODER_ERROR_NOT_SUPPORTED);
+      {
+        base::AutoLock auto_lock(lock_);
+        player_.reset();
+      }
+      // TODO: Determine if CallSeekCB() may be used here, as |seek_cb_| may be
+      // available if the app is suspended before a seek is completed.
+      if (!error_cb_.is_null()) {
+        ResetAndRunIfNotNull(&error_cb_, DECODER_ERROR_NOT_SUPPORTED,
+                             "SbPlayerPipeline::ResumeTask failed: "
+                             "player_->IsValid() is false.");
+      }
       return;
     }
   }
diff --git a/src/cobalt/media/formats/mp4/box_definitions.cc b/src/cobalt/media/formats/mp4/box_definitions.cc
index 5d30892..2ff0303 100644
--- a/src/cobalt/media/formats/mp4/box_definitions.cc
+++ b/src/cobalt/media/formats/mp4/box_definitions.cc
@@ -1308,7 +1308,8 @@
          reader->ReadChild(&decode_time) && reader->MaybeReadChildren(&runs) &&
          reader->MaybeReadChild(&auxiliary_offset) &&
          reader->MaybeReadChild(&auxiliary_size) &&
-         reader->MaybeReadChild(&sdtp));
+         reader->MaybeReadChild(&sdtp) &&
+         reader->MaybeReadChild(&sample_encryption));
 
   // There could be multiple SampleGroupDescription and SampleToGroup boxes with
   // different grouping types. For common encryption, the relevant grouping type
diff --git a/src/cobalt/media/player/web_media_player_impl.cc b/src/cobalt/media/player/web_media_player_impl.cc
index d5cfa6f..c8ae297 100644
--- a/src/cobalt/media/player/web_media_player_impl.cc
+++ b/src/cobalt/media/player/web_media_player_impl.cc
@@ -231,7 +231,7 @@
   UMA_HISTOGRAM_ENUMERATION("Media.URLScheme", URLScheme(url), kMaxURLScheme);
   DLOG(INFO) << "Start URL playback";
 
-  // Handle any volume changes that occured before load().
+  // Handle any volume changes that occurred before load().
   SetVolume(GetClient()->Volume());
 
   // TODO: Set networkState to WebMediaPlayer::kNetworkStateIdle on stop.
@@ -251,7 +251,7 @@
 
   DLOG(INFO) << "Start MEDIASOURCE playback";
 
-  // Handle any volume changes that occured before load().
+  // Handle any volume changes that occurred before load().
   SetVolume(GetClient()->Volume());
 
   SetNetworkState(WebMediaPlayer::kNetworkStateLoading);
@@ -278,7 +278,7 @@
   UMA_HISTOGRAM_ENUMERATION("Media.URLScheme", URLScheme(url), kMaxURLScheme);
   DLOG(INFO) << "Start PROGRESSIVE playback";
 
-  // Handle any volume changes that occured before load().
+  // Handle any volume changes that occurred before load().
   SetVolume(GetClient()->Volume());
 
   SetNetworkState(WebMediaPlayer::kNetworkStateLoading);
@@ -307,9 +307,6 @@
   TRACE_EVENT0("cobalt::media", "WebMediaPlayerImpl::Play");
 
   DCHECK_EQ(main_loop_, base::MessageLoop::current());
-#if defined(__LB_ANDROID__)
-  audio_focus_bridge_.RequestAudioFocus();
-#endif  // defined(__LB_ANDROID__)
 
   state_.paused = false;
   pipeline_->SetPlaybackRate(state_.playback_rate);
@@ -319,9 +316,6 @@
 
 void WebMediaPlayerImpl::Pause() {
   DCHECK_EQ(main_loop_, base::MessageLoop::current());
-#if defined(__LB_ANDROID__)
-  audio_focus_bridge_.AbandonAudioFocus();
-#endif  // defined(__LB_ANDROID__)
 
   state_.paused = true;
   pipeline_->SetPlaybackRate(0.0f);
diff --git a/src/cobalt/media/player/web_media_player_impl.h b/src/cobalt/media/player/web_media_player_impl.h
index a1a51be..9aa48c1 100644
--- a/src/cobalt/media/player/web_media_player_impl.h
+++ b/src/cobalt/media/player/web_media_player_impl.h
@@ -313,10 +313,6 @@
   std::unique_ptr<Demuxer> progressive_demuxer_;
   std::unique_ptr<ChunkDemuxer> chunk_demuxer_;
 
-#if defined(__LB_ANDROID__)
-  AudioFocusBridge audio_focus_bridge_;
-#endif  // defined(__LB_ANDROID__)
-
   // Suppresses calls to OnPipelineError() after destruction / shutdown has been
   // started; prevents us from spuriously logging errors that are transient or
   // unimportant.
diff --git a/src/cobalt/renderer/egl_and_gles.h b/src/cobalt/renderer/egl_and_gles.h
index 3dac4b2..6666f1b 100644
--- a/src/cobalt/renderer/egl_and_gles.h
+++ b/src/cobalt/renderer/egl_and_gles.h
@@ -50,6 +50,7 @@
 #define GL_DCHECK_MAYBE_LOG(x)
 #endif  // defined(COBALT_EGL_AND_GLES_LOGGING)
 
+#if SB_DCHECK_ENABLED
 #define EGL_DCHECK(x)                                                 \
   do {                                                                \
     EGL_DCHECK_MAYBE_LOG(x);                                          \
@@ -64,6 +65,10 @@
     SB_DCHECK(COBALT_GL_ERRNO == GL_NO_ERROR)                      \
         << #x << " exited with code: " << COBALT_GL_ERRNO;         \
   } while (false)
+#else
+#define EGL_DCHECK(x)
+#define GL_DCHECK(x)
+#endif  // SB_DCHECK_ENABLED
 
 #if SB_API_VERSION >= 11
 namespace cobalt {
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_threshold_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_threshold_2.glsl
index 1380682..1be401c 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_threshold_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_threshold_2.glsl
@@ -14,7 +14,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_thresholds.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_thresholds.glsl
index 63ff1ed..11db997 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_thresholds.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_thresholds.glsl
@@ -19,7 +19,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_threshold_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_threshold_2.glsl
index 9c5c1f3..e7447a2 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_threshold_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_threshold_2.glsl
@@ -14,7 +14,7 @@
 varying mediump float vinCoverage_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds.glsl
index cbcba0c..7bf9ef3 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds.glsl
@@ -19,7 +19,7 @@
 varying highp float vcoverage_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds_2.glsl
index 083f5e4..e6a6613 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds_2.glsl
@@ -14,7 +14,7 @@
 varying highp float vcoverage_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds_3.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds_3.glsl
index e737a75..a5049c7 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds_3.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds_3.glsl
@@ -21,7 +21,7 @@
 varying mediump float vinCoverage_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_decal_scaletranslate_texdom_texturew_threshold.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_decal_scaletranslate_texdom_texturew_threshold.glsl
index d3a03a4..e338bcf 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_decal_scaletranslate_texdom_texturew_threshold.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_decal_scaletranslate_texdom_texturew_threshold.glsl
@@ -18,7 +18,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold.glsl
index 5f2b1d3..667a564 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold.glsl
@@ -15,7 +15,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold_2.glsl
index 084c066..35a3010 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold_2.glsl
@@ -15,7 +15,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold_3.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold_3.glsl
index cb27f80..1f47fae 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold_3.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold_3.glsl
@@ -15,7 +15,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_thresholds.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_thresholds.glsl
index 1ba24f6..b7223a3 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_thresholds.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_thresholds.glsl
@@ -18,7 +18,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_thresholds_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_thresholds_2.glsl
index da56a36..ba5cadd 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_thresholds_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_thresholds_2.glsl
@@ -20,7 +20,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold.glsl
index e2ec055..8bcc27a 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold.glsl
@@ -16,7 +16,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold_2.glsl
index fe3a8ae..2ac7fcc 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold_2.glsl
@@ -16,7 +16,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold_3.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold_3.glsl
index f1bbe9d..1cb8137 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold_3.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold_3.glsl
@@ -16,7 +16,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_thresholds.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_thresholds.glsl
index b073544..ba8e732 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_thresholds.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_thresholds.glsl
@@ -19,7 +19,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_threshold.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_threshold.glsl
index 3657419..2cec90c 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_threshold.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_threshold.glsl
@@ -13,7 +13,7 @@
 varying mediump vec4 vcolor_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds.glsl
index 6e1c100..7685597 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds.glsl
@@ -18,7 +18,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_2.glsl
index e055c58..a1f1060 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_2.glsl
@@ -18,7 +18,7 @@
 varying mediump vec4 vcolor_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_coverage_texture.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_coverage_texture.glsl
index 224674d..07a5ac3 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_coverage_texture.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_coverage_texture.glsl
@@ -10,7 +10,7 @@
 varying highp float vcoverage_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_quad_texture.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_quad_texture.glsl
index 1ea6bd8..3c26f70 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_quad_texture.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_quad_texture.glsl
@@ -11,7 +11,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_quad_texture_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_quad_texture_2.glsl
index d8c1e31..298ccc0 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_quad_texture_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_quad_texture_2.glsl
@@ -11,7 +11,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texindex_texturew.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texindex_texturew.glsl
index 4e004af..92cba89 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texindex_texturew.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texindex_texturew.glsl
@@ -12,7 +12,7 @@
 varying highp vec2 vTransformedCoords_0_Stage0;
 mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
     mediump vec4 _sample1099_c0_c0;
-    mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+    mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
     _sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
     return _sample1099_c0_c0;
 }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_edges_texindex_texturew_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_edges_texindex_texturew_2.glsl
index e7cd090..fda3d24 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_edges_texindex_texturew_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_edges_texindex_texturew_2.glsl
@@ -21,9 +21,9 @@
         {
             texColor = texture2D(uTextureSampler_0_Stage0, uv).wwww;
         }
-        mediump float distance = 7.96875 * (texColor.x - 0.50196081399917603);
+        mediump float distance = 7.96875 * (texColor.x - 0.501960813999176);
         mediump float afwidth;
-        afwidth = abs(0.64999997615814209 * -dFdy(vIntTextureCoords_Stage0.y));
+        afwidth = abs(0.6499999761581421 * -dFdy(vIntTextureCoords_Stage0.y));
         mediump float val = smoothstep(-afwidth, afwidth, distance);
         outputCoverage_Stage0 = vec4(val);
     }
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse.glsl
index ee26f8b..8b55db4 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse.glsl
@@ -14,7 +14,7 @@
         highp float test = dot(offset, offset) - 1.0;
         highp vec2 grad = (2.0 * offset) * (vEllipseOffsets_Stage0.z * vEllipseRadii_Stage0.xy);
         highp float grad_dot = dot(grad, grad);
-        grad_dot = max(grad_dot, 6.1036000261083245e-05);
+        grad_dot = max(grad_dot, 6.103600026108325e-05);
         highp float invlen = vEllipseOffsets_Stage0.z * inversesqrt(grad_dot);
         highp float edgeAlpha = clamp(0.5 - test * invlen, 0.0, 1.0);
         outputCoverage_Stage0 = vec4(edgeAlpha);
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_2.glsl
index 0cb3d54..dd2801e 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_2.glsl
@@ -18,7 +18,7 @@
         highp vec2 grad = vec2(vEllipseOffsets0_Stage0.x * duvdx.x + vEllipseOffsets0_Stage0.y * duvdx.y, vEllipseOffsets0_Stage0.x * duvdy.x + vEllipseOffsets0_Stage0.y * duvdy.y);
         grad *= vEllipseOffsets0_Stage0.z;
         highp float grad_dot = 4.0 * dot(grad, grad);
-        grad_dot = max(grad_dot, 6.1036000261083245e-05);
+        grad_dot = max(grad_dot, 6.103600026108325e-05);
         highp float invlen = inversesqrt(grad_dot);
         invlen *= vEllipseOffsets0_Stage0.z;
         highp float edgeAlpha = clamp(0.5 - test * invlen, 0.0, 1.0);
@@ -29,7 +29,7 @@
         grad = vec2(vEllipseOffsets1_Stage0.x * duvdx.x + vEllipseOffsets1_Stage0.y * duvdx.y, vEllipseOffsets1_Stage0.x * duvdy.x + vEllipseOffsets1_Stage0.y * duvdy.y);
         grad *= vEllipseOffsets0_Stage0.z;
         grad_dot = 4.0 * dot(grad, grad);
-        grad_dot = max(grad_dot, 6.1036000261083245e-05);
+        grad_dot = max(grad_dot, 6.103600026108325e-05);
         invlen = inversesqrt(grad_dot);
         invlen *= vEllipseOffsets0_Stage0.z;
         edgeAlpha *= clamp(0.5 + test * invlen, 0.0, 1.0);
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_3.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_3.glsl
index 6148054..ff7654e 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_3.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_3.glsl
@@ -18,7 +18,7 @@
         highp vec2 grad = vec2(vEllipseOffsets0_Stage0.x * duvdx.x + vEllipseOffsets0_Stage0.y * duvdx.y, vEllipseOffsets0_Stage0.x * duvdy.x + vEllipseOffsets0_Stage0.y * duvdy.y);
         grad *= vEllipseOffsets0_Stage0.z;
         highp float grad_dot = 4.0 * dot(grad, grad);
-        grad_dot = max(grad_dot, 6.1036000261083245e-05);
+        grad_dot = max(grad_dot, 6.103600026108325e-05);
         highp float invlen = inversesqrt(grad_dot);
         invlen *= vEllipseOffsets0_Stage0.z;
         highp float edgeAlpha = clamp(0.5 - test * invlen, 0.0, 1.0);
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_scale_texture.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_scale_texture.glsl
index 0b4a811..4676e51 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_scale_texture.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_scale_texture.glsl
@@ -27,7 +27,7 @@
         highp float implicit = dot(Z, d) - 1.0;

         highp float grad_dot = 4.0 * dot(Z, Z);

         {

-            grad_dot = max(grad_dot, 6.1036000261083245e-05);

+            grad_dot = max(grad_dot, 6.103600026108325e-05);

         }

         highp float approx_dist = implicit * inversesqrt(grad_dot);

         {

diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew.glsl
index 53a0a74..32a6af2 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew.glsl
@@ -18,9 +18,9 @@
         {

             texColor = texture2D(uTextureSampler_0_Stage0, uv).wwww;

         }

-        mediump float distance = 7.96875 * (texColor.x - 0.50196081399917603);

+        mediump float distance = 7.96875 * (texColor.x - 0.501960813999176);

         mediump float afwidth;

-        afwidth = abs(0.64999997615814209 * -dFdy(vIntTextureCoords_Stage0.y));

+        afwidth = abs(0.6499999761581421 * -dFdy(vIntTextureCoords_Stage0.y));

         mediump float val = smoothstep(-afwidth, afwidth, distance);

         outputCoverage_Stage0 = vec4(val);

     }

diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_2.glsl
index 1b61907..7c4db08 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_2.glsl
@@ -18,9 +18,9 @@
         {

             texColor = texture2D(uTextureSampler_0_Stage0, uv).wwww;

         }

-        mediump float distance = 7.96875 * (texColor.x - 0.50196081399917603);

+        mediump float distance = 7.96875 * (texColor.x - 0.501960813999176);

         mediump float afwidth;

-        afwidth = abs(0.64999997615814209 * -dFdy(vIntTextureCoords_Stage0.y));

+        afwidth = abs(0.6499999761581421 * -dFdy(vIntTextureCoords_Stage0.y));

         mediump float val = smoothstep(-afwidth, afwidth, distance);

         outputCoverage_Stage0 = vec4(val);

     }

diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_3.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_3.glsl
index f4db076..421494c 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_3.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_3.glsl
@@ -18,19 +18,19 @@
         {

             texColor = texture2D(uTextureSampler_0_Stage0, uv).wwww;

         }

-        mediump float distance = 7.96875 * (texColor.x - 0.50196081399917603);

+        mediump float distance = 7.96875 * (texColor.x - 0.501960813999176);

         mediump float afwidth;

         mediump vec2 dist_grad = vec2(dFdx(distance), -dFdy(distance));

         mediump float dg_len2 = dot(dist_grad, dist_grad);

-        if (dg_len2 < 9.9999997473787516e-05) {

-            dist_grad = vec2(0.70709997415542603, 0.70709997415542603);

+        if (dg_len2 < 9.999999747378752e-05) {

+            dist_grad = vec2(0.707099974155426, 0.707099974155426);

         } else {

             dist_grad = dist_grad * inversesqrt(dg_len2);

         }

         mediump vec2 Jdx = dFdx(vIntTextureCoords_Stage0);

         mediump vec2 Jdy = -dFdy(vIntTextureCoords_Stage0);

         mediump vec2 grad = vec2(dist_grad.x * Jdx.x + dist_grad.y * Jdy.x, dist_grad.x * Jdx.y + dist_grad.y * Jdy.y);

-        afwidth = 0.64999997615814209 * length(grad);

+        afwidth = 0.6499999761581421 * length(grad);

         mediump float val = smoothstep(-afwidth, afwidth, distance);

         outputCoverage_Stage0 = vec4(val);

     }

diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_4.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_4.glsl
index 20eef11..663553b 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_4.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_4.glsl
@@ -18,10 +18,10 @@
         {

             texColor = texture2D(uTextureSampler_0_Stage0, uv).wwww;

         }

-        mediump float distance = 7.96875 * (texColor.x - 0.50196081399917603);

+        mediump float distance = 7.96875 * (texColor.x - 0.501960813999176);

         mediump float afwidth;

         mediump float st_grad_len = length(-dFdy(vIntTextureCoords_Stage0));

-        afwidth = abs(0.64999997615814209 * st_grad_len);

+        afwidth = abs(0.6499999761581421 * st_grad_len);

         mediump float val = smoothstep(-afwidth, afwidth, distance);

         outputCoverage_Stage0 = vec4(val);

     }

diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_5.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_5.glsl
index ac92c03..80cd4a3 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_5.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_5.glsl
@@ -18,10 +18,10 @@
         {
             texColor = texture2D(uTextureSampler_0_Stage0, uv).wwww;
         }
-        mediump float distance = 7.96875 * (texColor.x - 0.50196081399917603);
+        mediump float distance = 7.96875 * (texColor.x - 0.501960813999176);
         mediump float afwidth;
         mediump float st_grad_len = length(-dFdy(vIntTextureCoords_Stage0));
-        afwidth = abs(0.64999997615814209 * st_grad_len);
+        afwidth = abs(0.6499999761581421 * st_grad_len);
         mediump float val = smoothstep(-afwidth, afwidth, distance);
         outputCoverage_Stage0 = vec4(val);
     }
diff --git a/src/cobalt/renderer/rasterizer/egl/textured_mesh_renderer.cc b/src/cobalt/renderer/rasterizer/egl/textured_mesh_renderer.cc
index ac3cb80..3c20715 100644
--- a/src/cobalt/renderer/rasterizer/egl/textured_mesh_renderer.cc
+++ b/src/cobalt/renderer/rasterizer/egl/textured_mesh_renderer.cc
@@ -20,6 +20,7 @@
 #include <string>
 #include <vector>
 
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "cobalt/extension/graphics.h"
 #include "cobalt/math/size.h"
@@ -364,28 +365,31 @@
       graphics_extension->GetMapToMeshColorAdjustments(&color_adjustment)) {
     // Setup vectors for the color adjustments. Ensure they use dot for decimal
     // point regardless of locale.
-    std::stringstream buffer;
-    buffer.imbue(std::locale::classic());
-    buffer << "  vec4 scale0 = vec4(" << color_adjustment.rgba0_scale[0] << ","
-           << color_adjustment.rgba0_scale[1] << ","
-           << color_adjustment.rgba0_scale[2] << ","
-           << color_adjustment.rgba0_scale[3] << ");";
-    buffer << "  vec4 scale1 = vec4(" << color_adjustment.rgba1_scale[0] << ","
-           << color_adjustment.rgba1_scale[1] << ","
-           << color_adjustment.rgba1_scale[2] << ","
-           << color_adjustment.rgba1_scale[3] << ");";
-    buffer << "  vec4 scale2 = vec4(" << color_adjustment.rgba2_scale[0] << ","
-           << color_adjustment.rgba2_scale[1] << ","
-           << color_adjustment.rgba2_scale[2] << ","
-           << color_adjustment.rgba2_scale[3] << ");";
-    buffer << "  vec4 scale3 = vec4(" << color_adjustment.rgba3_scale[0] << ","
-           << color_adjustment.rgba3_scale[1] << ","
-           << color_adjustment.rgba3_scale[2] << ","
-           << color_adjustment.rgba3_scale[3] << ");";
+    std::string buffer;
+    buffer = "  vec4 scale0 = vec4(" +
+             base::NumberToString(color_adjustment.rgba0_scale[0]) + "," +
+             base::NumberToString(color_adjustment.rgba0_scale[1]) + "," +
+             base::NumberToString(color_adjustment.rgba0_scale[2]) + "," +
+             base::NumberToString(color_adjustment.rgba0_scale[3]) + ");" +
+             "  vec4 scale1 = vec4(" +
+             base::NumberToString(color_adjustment.rgba1_scale[0]) + "," +
+             base::NumberToString(color_adjustment.rgba1_scale[1]) + "," +
+             base::NumberToString(color_adjustment.rgba1_scale[2]) + "," +
+             base::NumberToString(color_adjustment.rgba1_scale[3]) + ");" +
+             "  vec4 scale2 = vec4(" +
+             base::NumberToString(color_adjustment.rgba2_scale[0]) + "," +
+             base::NumberToString(color_adjustment.rgba2_scale[1]) + "," +
+             base::NumberToString(color_adjustment.rgba2_scale[2]) + "," +
+             base::NumberToString(color_adjustment.rgba2_scale[3]) + ");" +
+             "  vec4 scale3 = vec4(" +
+             base::NumberToString(color_adjustment.rgba3_scale[0]) + "," +
+             base::NumberToString(color_adjustment.rgba3_scale[1]) + "," +
+             base::NumberToString(color_adjustment.rgba3_scale[2]) + "," +
+             base::NumberToString(color_adjustment.rgba3_scale[3]) + ");";
     blit_fragment_shader_source +=
         "  vec4 color2 = color * color;"
         "  vec4 color3 = color2 * color;" +
-        buffer.str() +
+        buffer +
         "  color = scale0 + scale1*color + scale2*color2 + scale3*color3;"
         "  gl_FragColor = clamp(color, vec4(0.0), vec4(1.0));"
         "}";
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/skia.gyp b/src/cobalt/renderer/rasterizer/skia/skia/skia.gyp
index dfefca6..5fe1b23 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/skia.gyp
+++ b/src/cobalt/renderer/rasterizer/skia/skia/skia.gyp
@@ -28,6 +28,7 @@
       'target_name': 'skia_library',
       'type': 'static_library',
       'dependencies': [
+        '<(DEPTH)/base/base.gyp:base',
         'skia_library_no_asan',
       ],
       'includes': [
@@ -65,7 +66,6 @@
       'target_name': 'filter_fuzz_stub',
       'type': 'executable',
       'dependencies': [
-        '<(DEPTH)/base/base.gyp:base',
         'skia',
       ],
       'sources': [
diff --git a/src/cobalt/site/docs/contributors/index.md b/src/cobalt/site/docs/contributors/index.md
index ea93cc6..77811fa 100644
--- a/src/cobalt/site/docs/contributors/index.md
+++ b/src/cobalt/site/docs/contributors/index.md
@@ -52,7 +52,7 @@
 *  Ensure you or your company have signed the appropriate CLA as discussed
    in the [Before You Contribute](#before-you-contribute) section above.
 *  Rebase your changes down into a single git commit.
-*  Run `git cl upload` to upload the review to
+*  Run `git push origin HEAD:refs/for/master` to upload the review to
    [Cobalt's Gerrit instance](https://cobalt-review.googlesource.com/).
 *  Someone from the maintainers team reviews the code, adding comments on
    any things that need to change before the code can be submitted.
diff --git a/src/cobalt/updater/network_fetcher.cc b/src/cobalt/updater/network_fetcher.cc
index fb7ea98..1dfbc7c 100644
--- a/src/cobalt/updater/network_fetcher.cc
+++ b/src/cobalt/updater/network_fetcher.cc
@@ -129,6 +129,13 @@
   url_fetcher_->Start();
 }
 
+void NetworkFetcher::CancelDownloadToFile() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  SB_LOG(INFO) << "Canceling DownloadToFile";
+  url_fetcher_.reset();
+}
+
 void NetworkFetcher::OnURLFetchResponseStarted(const net::URLFetcher* source) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   std::move(response_started_callback_)
diff --git a/src/cobalt/updater/network_fetcher.h b/src/cobalt/updater/network_fetcher.h
index 1ff2675..17b8909 100644
--- a/src/cobalt/updater/network_fetcher.h
+++ b/src/cobalt/updater/network_fetcher.h
@@ -69,6 +69,7 @@
                       ProgressCallback progress_callback,
                       DownloadToFileCompleteCallback
                           download_to_file_complete_callback) override;
+  void CancelDownloadToFile() override;
 
   // net::URLFetcherDelegate interface.
   void OnURLFetchResponseStarted(const net::URLFetcher* source) override;
diff --git a/src/cobalt/updater/updater_module.cc b/src/cobalt/updater/updater_module.cc
index 8f5663f..3941cb6 100644
--- a/src/cobalt/updater/updater_module.cc
+++ b/src/cobalt/updater/updater_module.cc
@@ -174,6 +174,7 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   update_client_->RemoveObserver(updater_observer_.get());
   updater_observer_.reset();
+  update_client_->Stop();
   update_client_ = nullptr;
 
   if (updater_configurator_ != nullptr) {
diff --git a/src/cobalt/version.h b/src/cobalt/version.h
index 9e20fe2..e014a72 100644
--- a/src/cobalt/version.h
+++ b/src/cobalt/version.h
@@ -35,6 +35,6 @@
 //                  release is cut.
 //.
 
-#define COBALT_VERSION "21.lts.4"
+#define COBALT_VERSION "21.lts.5"
 
 #endif  // COBALT_VERSION_H_
diff --git a/src/cobalt/webdriver/protocol/moveto.cc b/src/cobalt/webdriver/protocol/moveto.cc
index a8a10f9..d9eb35e 100644
--- a/src/cobalt/webdriver/protocol/moveto.cc
+++ b/src/cobalt/webdriver/protocol/moveto.cc
@@ -12,10 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "cobalt/webdriver/protocol/moveto.h"
+
 #include <memory>
 #include <string>
-
-#include "cobalt/webdriver/protocol/moveto.h"
+#include <utility>
 
 namespace cobalt {
 namespace webdriver {
@@ -43,7 +44,7 @@
 }
 
 base::Optional<Moveto> Moveto::FromValue(const base::Value* value) {
-  const base::DictionaryValue* dictionary_value;
+  const base::DictionaryValue* dictionary_value = nullptr;
   if (!value->GetAsDictionary(&dictionary_value)) {
     return base::nullopt;
   }
@@ -53,6 +54,12 @@
   if (dictionary_value->GetString(kElementKey, &element_id) &&
       !element_id.empty()) {
     element = ElementId(element_id);
+  } else {
+    const base::Value* element_value =
+        value->FindKeyOfType(kElementKey, base::Value::Type::DICTIONARY);
+    if (element_value) {
+      element = ElementId::FromValue(element_value);
+    }
   }
 
   int xoffset_value = 0;
diff --git a/src/cobalt/webdriver/testdata/simple_test.py b/src/cobalt/webdriver/testdata/simple_test.py
index d2cea99..bf532b0 100755
--- a/src/cobalt/webdriver/testdata/simple_test.py
+++ b/src/cobalt/webdriver/testdata/simple_test.py
@@ -29,6 +29,37 @@
 POST = 'POST'
 DELETE = 'DELETE'
 
+# WebDriver Response Status Codes:
+# https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#Response-Status-Codes
+
+RESPONSE_STATUS_CODES = {
+    0: 'Success',
+    6: 'No Such Driver',
+    7: 'No Such Element',
+    8: 'No Such Frame',
+    9: 'Unknown Command',
+    10: 'Stale Element Reference',
+    11: 'Element Not Visible',
+    12: 'Invalid Element State',
+    13: 'Unknown Error',
+    15: 'Element Is Not Selectable',
+    17: 'JavaScript Error',
+    19: 'XPath Lookup Error',
+    21: 'Timeout',
+    23: 'No Such Window',
+    24: 'Invalid Cookie Domain',
+    25: 'Unable To Set Cookie',
+    26: 'Unexpected Alert Open',
+    27: 'No Alert Open Error',
+    28: 'Script Timeout',
+    29: 'Invalid Element Coordinates',
+    30: 'IME Not Available',
+    31: 'IME Engine Activation Failed',
+    32: 'Invalid Selector',
+    33: 'Session Not Created Exception',
+    34: 'Move Target Out Of Bounds'
+}
+
 
 def Request(request_type, path='', parameters=None):
   """Perform a WebDriver JSON Wire Protocol Request.
@@ -54,8 +85,11 @@
   if request.status_code == 200:
     return result
   else:
-    print '*** Error %d: %s' % (request.status_code, result['value']['message']
-                                if isinstance(result, dict) else result)
+    print('*** Error %d %s: \"%s\"' %
+          (request.status_code, RESPONSE_STATUS_CODES[result['status']]
+           if isinstance(result, dict) else 'unknown',
+           result['value']['message'] if isinstance(result, dict) else result))
+    print('*** Error %d: %s' % (request.status_code, result))
   return None
 
 
@@ -82,8 +116,9 @@
                    request_type,
                    path=None,
                    parameters=None):
-  return SessionRequest(session_id, request_type, 'element/%s/%s' %
-                        (element_id[u'ELEMENT'], path), parameters)
+  return SessionRequest(session_id, request_type,
+                        'element/%s/%s' % (element_id[u'ELEMENT'], path),
+                        parameters)
 
 
 def GetSessionID():
@@ -129,6 +164,20 @@
       f.close()
 
 
+def GetElementScreenShot(session_id, element_id, filename):
+  """Retrieve a Screenshot.
+
+  Args:
+    session_id: Value for ':sessionId' for the request
+    filename: The filename to write the PNG screenshot to
+  """
+  request = ElementRequest(session_id, element_id, GET, 'screenshot')
+  if request:
+    with open(filename, 'w') as f:
+      f.write(binascii.a2b_base64(request['value']))
+      f.close()
+
+
 def GetActiveElement(session_id):
   return SessionRequest(session_id, POST, 'element/active')['value']
 
@@ -171,32 +220,97 @@
 
 
 def ElementFind(session_id, using, value):
-  return SessionRequest(session_id, POST, 'element',
-                        {u'using': using,
-                         u'value': value})['value']
+  result = SessionRequest(session_id, POST, 'element', {
+      u'using': using,
+      u'value': value
+  })
+  return None if result is None else result['value']
 
 
 def MouseTest():
   # Do a simple test that hovers the mouse to the right from the active
   # element, then clicks on the element of class 'trending'.
   session_id = GetSessionID()
-  active_element = GetActiveElement(session_id)
-  print 'active_element : %s' % active_element
+  try:
+    active_element = GetActiveElement(session_id)
+    print('active_element : %s' % active_element)
 
-  for xoffset in range(0, 1900, 20):
-    print 'Moveto: %s' % Moveto(session_id, active_element, xoffset, 200)
-    time.sleep(0.05)
+    for xoffset in range(0, 1900, 20):
+      print('Moveto: %s' % Moveto(session_id, active_element, xoffset, 200))
+      time.sleep(0.05)
 
-  trending_element = ElementFind(session_id, 'class name', 'trending')
-  print 'trending_element : %s' % trending_element
+    selected_element = ElementFind(session_id, 'class name',
+                                   'ytlr-tile-renderer--focused')
+    print('selected_element : %s' % selected_element)
 
-  print 'ElementClick: %s' % ElementClick(session_id, trending_element, 0)
+    print('ElementClick: %s' % ElementClick(session_id, selected_element, 0))
+
+  except KeyboardInterrupt:
+    print('Bye')
 
   DeleteSession(session_id)
 
 
+def ElementScreenShotTest():
+  # Do a simple test that hovers the mouse to the right from the active
+  # element, then clicks on the element of class 'trending'.
+  session_id = GetSessionID()
+  try:
+    selected_element = ElementFind(session_id, 'class name',
+                                   'ytlr-tile-renderer--focused')
+    print('Selected List element : %s' % selected_element)
+
+    # Write screenshots for the selected element, until interrupted.
+    while True:
+      selected_element = ElementFind(session_id, 'class name',
+                                     'ytlr-tile-renderer--focused')
+      print('Selected List element : %s' % selected_element)
+      if selected_element is not None:
+        print('GetElementScreenShot: %s' % GetElementScreenShot(
+            session_id, selected_element,
+            'element-' + selected_element['ELEMENT'] + '.png'))
+
+  except KeyboardInterrupt:
+    print('Bye')
+
+  DeleteSession(session_id)
+
+
+def ElementUniqueTest():
+  # Do a simple test that keeps running as long as the element IDs stay the
+  # same.
+  session_id = GetSessionID()
+  try:
+    initial_active_element = GetActiveElement(session_id)
+    print('initial active_element : %s' % initial_active_element)
+
+    initial_selected_element = ElementFind(session_id, 'class name',
+                                           'ytlr-tile-renderer--focused')
+    print('Selected List element : %s' % initial_selected_element)
+    while True:
+      active_element = GetActiveElement(session_id)
+      print('active_element : %s' % active_element)
+
+      if initial_active_element != active_element:
+        break
+
+      selected_element = ElementFind(session_id, 'class name',
+                                     'ytlr-tile-renderer--focused')
+      print('Selected List element : %s' % selected_element)
+
+      if initial_selected_element != selected_element:
+        break
+
+      time.sleep(1)
+
+  except KeyboardInterrupt:
+    print('Bye')
+
+
 def main():
   MouseTest()
+  ElementScreenShotTest()
+  # ElementUniqueTest()
 
 
 if __name__ == '__main__':
diff --git a/src/cobalt/webdriver/window_driver.cc b/src/cobalt/webdriver/window_driver.cc
index 483b6d2..1cb4432 100644
--- a/src/cobalt/webdriver/window_driver.cc
+++ b/src/cobalt/webdriver/window_driver.cc
@@ -399,6 +399,14 @@
 protocol::ElementId WindowDriver::ElementToId(
     const scoped_refptr<dom::Element>& element) {
   DCHECK_EQ(base::MessageLoop::current()->task_runner(), window_task_runner_);
+  for (auto i : element_drivers_) {
+    // Note: The element_task_runner_ is the same as the window_task_runner_.
+    auto weak_element = i.second->GetWeakElement();
+    if (element == weak_element) {
+      return i.second->element_id();
+    }
+  }
+
   return CreateNewElementDriver(base::AsWeakPtr(element.get()));
 }
 
diff --git a/src/components/update_client/component.cc b/src/components/update_client/component.cc
index 3e23074..21a74f0 100644
--- a/src/components/update_client/component.cc
+++ b/src/components/update_client/component.cc
@@ -300,6 +300,13 @@
       base::BindOnce(&Component::ChangeState, base::Unretained(this)));
 }
 
+#if defined(STARBOARD)
+void Component::Cancel() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  state_->Cancel();
+}
+#endif
+
 void Component::ChangeState(std::unique_ptr<State> next_state) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -532,6 +539,14 @@
   DoHandle();
 }
 
+#if defined(STARBOARD)
+void Component::State::Cancel() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  // Further work may be needed to ensure cancelation during any state results
+  // in a clear result and no memory leaks.
+}
+#endif
+
 void Component::State::TransitionState(std::unique_ptr<State> next_state) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(next_state);
@@ -726,6 +741,13 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 }
 
+#if defined(STARBOARD)
+void Component::StateDownloadingDiff::Cancel() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  crx_downloader_->CancelDownload();
+}
+#endif
+
 void Component::StateDownloadingDiff::DoHandle() {
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -800,6 +822,13 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 }
 
+#if defined(STARBOARD)
+void Component::StateDownloading::Cancel() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  crx_downloader_->CancelDownload();
+}
+#endif
+
 void Component::StateDownloading::DoHandle() {
   DCHECK(thread_checker_.CalledOnValidThread());
 
diff --git a/src/components/update_client/component.h b/src/components/update_client/component.h
index 0f9b973..229e80e 100644
--- a/src/components/update_client/component.h
+++ b/src/components/update_client/component.h
@@ -55,6 +55,12 @@
   // to the next component state before |callback_handle_complete_| is invoked.
   void Handle(CallbackHandleComplete callback_handle_complete);
 
+#if defined(STARBOARD)
+  // Stops update progress for the component and may clean resources used in its
+  // current state.
+  void Cancel();
+#endif
+
   CrxUpdateItem GetCrxUpdateItem() const;
 
   // Sets the uninstall state for this component.
@@ -156,6 +162,11 @@
     // by the outer component, after the current state is fully handled.
     void Handle(CallbackNextState callback);
 
+#if defined(STARBOARD)
+    // Stops update progress and may clean resources used in the current state.
+    virtual void Cancel();
+#endif
+
     ComponentState state() const { return state_; }
 
    protected:
@@ -247,6 +258,9 @@
    public:
     explicit StateDownloadingDiff(Component* component);
     ~StateDownloadingDiff() override;
+#if defined(STARBOARD)
+    void Cancel() override;
+#endif
 
    private:
     // State overrides.
@@ -270,6 +284,9 @@
    public:
     explicit StateDownloading(Component* component);
     ~StateDownloading() override;
+#if defined(STARBOARD)
+    void Cancel() override;
+#endif
 
    private:
     // State overrides.
diff --git a/src/components/update_client/crx_downloader.cc b/src/components/update_client/crx_downloader.cc
index bac3857..1e347d6 100644
--- a/src/components/update_client/crx_downloader.cc
+++ b/src/components/update_client/crx_downloader.cc
@@ -120,6 +120,12 @@
   DoStartDownload(*current_url_);
 }
 
+#if defined(STARBOARD)
+void CrxDownloader::CancelDownload() {
+  DoCancelDownload();
+}
+#endif
+
 void CrxDownloader::OnDownloadComplete(
     bool is_handled,
     const Result& result,
diff --git a/src/components/update_client/crx_downloader.h b/src/components/update_client/crx_downloader.h
index 3b46799..2db575a 100644
--- a/src/components/update_client/crx_downloader.h
+++ b/src/components/update_client/crx_downloader.h
@@ -122,6 +122,10 @@
                      const std::string& expected_hash,
                      DownloadCallback download_callback);
 
+#if defined(STARBOARD)
+  void CancelDownload();
+#endif
+
   const std::vector<DownloadMetrics> download_metrics() const;
 
  protected:
@@ -151,6 +155,9 @@
 
  private:
   virtual void DoStartDownload(const GURL& url) = 0;
+#if defined(STARBOARD)
+  virtual void DoCancelDownload() = 0;
+#endif
 
   void VerifyResponse(bool is_handled,
                       Result result,
diff --git a/src/components/update_client/network.h b/src/components/update_client/network.h
index daed162..dec108f 100644
--- a/src/components/update_client/network.h
+++ b/src/components/update_client/network.h
@@ -63,6 +63,9 @@
       ResponseStartedCallback response_started_callback,
       ProgressCallback progress_callback,
       DownloadToFileCompleteCallback download_to_file_complete_callback) = 0;
+#if defined(STARBOARD)
+  virtual void CancelDownloadToFile() = 0;
+#endif
 
  protected:
   NetworkFetcher() = default;
diff --git a/src/components/update_client/task_update.cc b/src/components/update_client/task_update.cc
index ab445b5..0129e4c 100644
--- a/src/components/update_client/task_update.cc
+++ b/src/components/update_client/task_update.cc
@@ -36,13 +36,25 @@
     return;
   }
 
+#if defined(STARBOARD)
+  update_engine_->Update(is_foreground_, ids_, std::move(crx_data_callback_),
+                         base::BindOnce(&TaskUpdate::TaskComplete, this),
+                         cancelation_closure_);
+#else
   update_engine_->Update(is_foreground_, ids_, std::move(crx_data_callback_),
                          base::BindOnce(&TaskUpdate::TaskComplete, this));
+#endif
 }
 
 void TaskUpdate::Cancel() {
   DCHECK(thread_checker_.CalledOnValidThread());
 
+#if defined(STARBOARD)
+  if (cancelation_closure_) {  // The engine's picked up the task.
+    std::move(cancelation_closure_).Run();
+  }
+#endif
+
   TaskComplete(Error::UPDATE_CANCELED);
 }
 
diff --git a/src/components/update_client/task_update.h b/src/components/update_client/task_update.h
index 4df796b..74c0a6a 100644
--- a/src/components/update_client/task_update.h
+++ b/src/components/update_client/task_update.h
@@ -57,6 +57,9 @@
   const std::vector<std::string> ids_;
   UpdateClient::CrxDataCallback crx_data_callback_;
   Callback callback_;
+#if defined(STARBOARD)
+  base::OnceClosure cancelation_closure_;
+#endif
 
   DISALLOW_COPY_AND_ASSIGN(TaskUpdate);
 };
diff --git a/src/components/update_client/update_client.cc b/src/components/update_client/update_client.cc
index 369c6d3..6ce65bf 100644
--- a/src/components/update_client/update_client.cc
+++ b/src/components/update_client/update_client.cc
@@ -201,6 +201,16 @@
 
   is_stopped_ = true;
 
+  // Cancel the pending tasks. These tasks are safe to cancel and delete since
+  // they have not picked up by the update engine, and not shared with any
+  // task runner yet.
+  while (!task_queue_.empty()) {
+    auto task = task_queue_.front();
+    task_queue_.pop_front();
+    task->Cancel();
+  }
+
+#if !defined(STARBOARD)
   // In the current implementation it is sufficient to cancel the pending
   // tasks only. The tasks that are run by the update engine will stop
   // making progress naturally, as the main task runner stops running task
@@ -210,15 +220,15 @@
   // area, to cancel the running tasks by canceling the current action update.
   // This behavior would be expected, correct, and result in no resource leaks
   // in all cases, in shutdown or not.
-  //
-  // Cancel the pending tasks. These tasks are safe to cancel and delete since
-  // they have not picked up by the update engine, and not shared with any
-  // task runner yet.
-  while (!task_queue_.empty()) {
-    auto task = task_queue_.front();
-    task_queue_.pop_front();
+#else
+  // For Cobalt it's not sufficient to just let the tasks already picked up by
+  // the update engine stop naturally, as this can result in resource leaks and
+  // crashes. These tasks are also canceled so that any necessary cleanup can be
+  // done.
+  for (auto task : tasks_) {
     task->Cancel();
   }
+#endif
 }
 
 void UpdateClientImpl::SendUninstallPing(const std::string& id,
diff --git a/src/components/update_client/update_engine.cc b/src/components/update_client/update_engine.cc
index a6d6920..29c260b 100644
--- a/src/components/update_client/update_engine.cc
+++ b/src/components/update_client/update_engine.cc
@@ -72,10 +72,19 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 }
 
+#if !defined(STARBOARD)
 void UpdateEngine::Update(bool is_foreground,
                           const std::vector<std::string>& ids,
                           UpdateClient::CrxDataCallback crx_data_callback,
                           Callback callback) {
+#else
+void UpdateEngine::Update(bool is_foreground,
+                          const std::vector<std::string>& ids,
+                          UpdateClient::CrxDataCallback crx_data_callback,
+                          Callback callback,
+                          base::OnceClosure& cancelation_closure) {
+
+#endif
   DCHECK(thread_checker_.CalledOnValidThread());
 
   if (ids.empty()) {
@@ -100,6 +109,10 @@
       config_, is_foreground, ids, std::move(crx_data_callback),
       notify_observers_callback_, std::move(callback), crx_downloader_factory_);
   DCHECK(!update_context->session_id.empty());
+#if defined(STARBOARD)
+  cancelation_closure = base::BindOnce(&UpdateEngine::Cancel, this,
+                                       update_context->session_id, ids);
+#endif
 
   const auto result = update_contexts_.insert(
       std::make_pair(update_context->session_id, update_context));
@@ -405,6 +418,17 @@
          now < throttle_updates_until_;
 }
 
+#if defined(STARBOARD)
+void UpdateEngine::Cancel(const std::string& update_context_session_id,
+                          const std::vector<std::string>& crx_component_ids) {
+  const auto& context = update_contexts_.at(update_context_session_id);
+  for (const auto& crx_component_id : crx_component_ids) {
+    auto& component = context->components.at(crx_component_id);
+    component->Cancel();
+  }
+}
+#endif
+
 void UpdateEngine::SendUninstallPing(const std::string& id,
                                      const base::Version& version,
                                      int reason,
diff --git a/src/components/update_client/update_engine.h b/src/components/update_client/update_engine.h
index 9601873..4384fa8 100644
--- a/src/components/update_client/update_engine.h
+++ b/src/components/update_client/update_engine.h
@@ -56,10 +56,20 @@
   // is not found.
   bool GetUpdateState(const std::string& id, CrxUpdateItem* update_state);
 
+#if !defined(STARBOARD)
   void Update(bool is_foreground,
               const std::vector<std::string>& ids,
               UpdateClient::CrxDataCallback crx_data_callback,
               Callback update_callback);
+#else
+  // |cancelation_closure| is populated with a closure that can be run to cancel
+  // the update requested by the caller.
+  void Update(bool is_foreground,
+              const std::vector<std::string>& ids,
+              UpdateClient::CrxDataCallback crx_data_callback,
+              Callback update_callback,
+              base::OnceClosure& cancelation_closure);
+#endif
 
   void SendUninstallPing(const std::string& id,
                          const base::Version& version,
@@ -96,6 +106,14 @@
   // occurs too soon.
   bool IsThrottled(bool is_foreground) const;
 
+#if defined(STARBOARD)
+  // Cancels updates currently handled by the engine for each component
+  // identified by one of |crx_component_ids| for the update context identified
+  // by the |update_context_session_id|.
+  void Cancel(const std::string& update_context_session_id,
+              const std::vector<std::string>& crx_component_ids);
+#endif
+
   base::ThreadChecker thread_checker_;
   scoped_refptr<Configurator> config_;
   UpdateChecker::Factory update_checker_factory_;
diff --git a/src/components/update_client/url_fetcher_downloader.cc b/src/components/update_client/url_fetcher_downloader.cc
index f94b010..f940e2e 100644
--- a/src/components/update_client/url_fetcher_downloader.cc
+++ b/src/components/update_client/url_fetcher_downloader.cc
@@ -144,6 +144,13 @@
 #endif
 }
 
+#if defined(STARBOARD)
+void UrlFetcherDownloader::DoCancelDownload() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  network_fetcher_->CancelDownloadToFile();
+}
+#endif
+
 void UrlFetcherDownloader::CreateDownloadDir() {
   base::CreateNewTempDirectory(FILE_PATH_LITERAL("chrome_url_fetcher_"),
                                &download_dir_);
diff --git a/src/components/update_client/url_fetcher_downloader.h b/src/components/update_client/url_fetcher_downloader.h
index fc32732..ea7f29d 100644
--- a/src/components/update_client/url_fetcher_downloader.h
+++ b/src/components/update_client/url_fetcher_downloader.h
@@ -42,6 +42,9 @@
  private:
   // Overrides for CrxDownloader.
   void DoStartDownload(const GURL& url) override;
+#if defined(STARBOARD)
+  void DoCancelDownload() override;
+#endif
 
   void CreateDownloadDir();
   void StartURLFetch(const GURL& url);
diff --git a/src/docker/linux/base/build/Dockerfile b/src/docker/linux/base/build/Dockerfile
index 545908d..8d60ad9 100644
--- a/src/docker/linux/base/build/Dockerfile
+++ b/src/docker/linux/base/build/Dockerfile
@@ -2,10 +2,15 @@
 FROM ${FROM_IMAGE:-cobalt-base}
 
 # === Get Nodejs pinned LTS version via NVM
+ARG NVM_SHA256SUM="f068e17dacb88f73302790cc076956c7a0d459ce9b01df842ff3e75744f9e2fe /tmp/install.sh"
+ARG NVM_URL="https://cobalt.googlesource.com/third_party/nvm/+/refs/tags/v0.35.3/install.sh?format=TEXT"
 ENV NVM_DIR /root/.nvm
 ENV NODE_VERSION 12.17.0
 
-RUN curl --silent -o- https://raw.githubusercontent.com/creationix/nvm/v0.35.3/install.sh | bash
+RUN curl --silent -o- ${NVM_URL} \
+  | base64 -d > /tmp/install.sh \
+   && echo ${NVM_SHA256SUM} | sha256sum --check \
+   && . /tmp/install.sh
 
 RUN . $NVM_DIR/nvm.sh \
    && nvm install --lts \
diff --git a/src/requirements.txt b/src/requirements.txt
index e9215ea..b666306 100644
--- a/src/requirements.txt
+++ b/src/requirements.txt
@@ -2,3 +2,4 @@
 pre-commit==2.6.0
 pylint==2.6.0
 yapf==0.30.0
+requests==2.25.1
diff --git a/src/starboard/android/apk/build.id b/src/starboard/android/apk/build.id
deleted file mode 100644
index 772184d..0000000
--- a/src/starboard/android/apk/build.id
+++ /dev/null
@@ -1 +0,0 @@
-302879
\ No newline at end of file
diff --git a/src/starboard/build/collect_deploy_content.gypi b/src/starboard/build/collect_deploy_content.gypi
index 6bee14a..a9a5064 100644
--- a/src/starboard/build/collect_deploy_content.gypi
+++ b/src/starboard/build/collect_deploy_content.gypi
@@ -46,6 +46,11 @@
         'collect_deploy_content_extra_args': [ '--use_absolute_symlinks' ],
       }
     }],
+    ['cobalt_docker_build == 1 and host_os == "win"', {
+      'variables': {
+        'collect_deploy_content_extra_args': [ '--copy_override' ],
+      }
+    }],
   ],
   'actions': [{
     'action_name': 'collect_deploy_content',
diff --git a/src/starboard/build/collect_deploy_content.py b/src/starboard/build/collect_deploy_content.py
index 8d3fffd..49bf9af 100644
--- a/src/starboard/build/collect_deploy_content.py
+++ b/src/starboard/build/collect_deploy_content.py
@@ -17,6 +17,7 @@
 import argparse
 import logging
 import os
+import shutil
 import sys
 
 import _env  # pylint: disable=unused-import
@@ -39,6 +40,21 @@
   port_symlink.Rmtree(path)
 
 
+def _CopyTree(src_path, dst_path):
+  """
+  Copy tree with a safeguard for windows long path (>260).
+  On Windows Python is facing long path limitation, for more details see
+  https://bugs.python.org/issue27730
+  """
+  if os.sep == r'\\':
+    prefix = r'\\\\?\\'
+    if prefix not in src_path:
+      src_path = prefix + src_path
+    if prefix not in dst_path:
+      dst_path = prefix + dst_path
+  shutil.copytree(src_path, dst_path)
+
+
 def main(argv):
   parser = argparse.ArgumentParser()
   parser.add_argument(
@@ -59,6 +75,11 @@
       metavar='subdirs',
       nargs='*',
       help='subdirectories within both the input and output directories')
+  parser.add_argument(
+      '--copy_override',
+      action='store_true',
+      help='Overrides the behavior of collect_deploy_content to copy files, '
+      'instead of symlinking them.')
   options = parser.parse_args(argv[1:])
 
   if os.environ.get(_SHOULD_LOG_ENV_KEY, None) == '1':
@@ -107,7 +128,9 @@
           msg += ' path points to an unknown type'
         logging.error(msg)
 
-    if options.use_absolute_symlinks:
+    if options.copy_override:
+      _CopyTree(src_path, dst_path)
+    elif options.use_absolute_symlinks:
       port_symlink.MakeSymLink(
           target_path=os.path.abspath(src_path),
           link_path=os.path.abspath(dst_path))
diff --git a/src/starboard/common/log.h b/src/starboard/common/log.h
index f1ec6b9..596dbc6 100644
--- a/src/starboard/common/log.h
+++ b/src/starboard/common/log.h
@@ -168,8 +168,10 @@
 #if SB_LOGGING_IS_OFFICIAL_BUILD || \
     (defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON))
 #define SB_DCHECK(condition) SB_EAT_STREAM_PARAMETERS
+#define SB_DCHECK_ENABLED 0
 #else
 #define SB_DCHECK(condition) SB_CHECK(condition)
+#define SB_DCHECK_ENABLED 1
 #endif
 
 #define SB_DLOG(severity) SB_DLOG_IF(severity, SB_DLOG_IS_ON(severity))
@@ -224,8 +226,10 @@
 #if SB_LOGGING_IS_OFFICIAL_BUILD
 #define SB_NOTIMPLEMENTED()
 #define SB_DCHECK(condition)
+#define SB_DCHECK_ENABLED 0
 #else
 #define SB_DCHECK(condition) SB_CHECK(condition)
+#define SB_DCHECK_ENABLED 1
 #define SB_NOTIMPLEMENTED()                              \
   do {                                                   \
     static int count = 0;                                \
diff --git a/src/starboard/linux/shared/gyp_configuration.py b/src/starboard/linux/shared/gyp_configuration.py
index b8f0949..4e4a96c 100644
--- a/src/starboard/linux/shared/gyp_configuration.py
+++ b/src/starboard/linux/shared/gyp_configuration.py
@@ -121,3 +121,10 @@
           'VideoDecoderTests/VideoDecoderTest.*Invalid*',
       ],
   }
+  # Conditionally disables tests that require ipv6
+  if os.getenv('IPV6_AVAILABLE', 1) == '0':
+    __FILTERED_TESTS['nplb'] = [
+        'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.SunnyDayDestination/1',
+        'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.SunnyDaySourceForDestination/1',
+        'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.SunnyDaySourceNotLoopback/1',
+    ]
diff --git a/src/starboard/nplb/nplb_evergreen_compat_tests/crashpad_config_test.cc b/src/starboard/nplb/nplb_evergreen_compat_tests/crashpad_config_test.cc
new file mode 100644
index 0000000..ca4ee47
--- /dev/null
+++ b/src/starboard/nplb/nplb_evergreen_compat_tests/crashpad_config_test.cc
@@ -0,0 +1,61 @@
+// Copyright 2021 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <string>
+#include <vector>
+
+#include "cobalt/extension/crash_handler.h"
+#include "starboard/nplb/nplb_evergreen_compat_tests/checks.h"
+#include "starboard/system.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if SB_IS(EVERGREEN_COMPATIBLE)
+
+namespace starboard {
+namespace nplb {
+namespace nplb_evergreen_compat_tests {
+
+namespace {
+
+class CrashpadConfigTest : public ::testing::Test {
+ protected:
+  CrashpadConfigTest() {}
+  ~CrashpadConfigTest() {}
+};
+
+TEST_F(CrashpadConfigTest, VerifyUploadCert) {
+  std::vector<char> buffer(kSbFileMaxPath);
+  ASSERT_TRUE(SbSystemGetPath(kSbSystemPathContentDirectory, buffer.data(),
+                              buffer.size()));
+
+  std::string cert_location(buffer.data());
+  cert_location.append(std::string(kSbFileSepString) + "app" +
+                       kSbFileSepString + "cobalt" + kSbFileSepString +
+                       "content" + kSbFileSepString + "ssl" + kSbFileSepString +
+                       "certs");
+  ASSERT_TRUE(SbFileExists(cert_location.c_str()));
+}
+
+TEST_F(CrashpadConfigTest, VerifyCrashHandlerExtension) {
+  auto crash_handler_extension =
+      SbSystemGetExtension(kCobaltExtensionCrashHandlerName);
+  ASSERT_TRUE(crash_handler_extension != nullptr);
+}
+
+}  // namespace
+}  // namespace nplb_evergreen_compat_tests
+}  // namespace nplb
+}  // namespace starboard
+
+#endif  // SB_IS(EVERGREEN_COMPATIBLE)
diff --git a/src/starboard/nplb/nplb_evergreen_compat_tests/nplb_evergreen_compat_tests.gyp b/src/starboard/nplb/nplb_evergreen_compat_tests/nplb_evergreen_compat_tests.gyp
index 4e292f8..1f2b9a73 100644
--- a/src/starboard/nplb/nplb_evergreen_compat_tests/nplb_evergreen_compat_tests.gyp
+++ b/src/starboard/nplb/nplb_evergreen_compat_tests/nplb_evergreen_compat_tests.gyp
@@ -19,6 +19,7 @@
       'type': '<(gtest_target_type)',
       'sources': [
         'checks.h',
+        'crashpad_config_test.cc',
         'executable_memory_test.cc',
         'fonts_test.cc',
         'sabi_test.cc',
diff --git a/src/third_party/crashpad/handler/handler.gyp b/src/third_party/crashpad/handler/handler.gyp
index effe9e1..1275a4d 100644
--- a/src/third_party/crashpad/handler/handler.gyp
+++ b/src/third_party/crashpad/handler/handler.gyp
@@ -89,8 +89,21 @@
       'sources': [
         'main.cc',
       ],
-
       'conditions': [
+        # Help platforms cross compiled on linux to reduce
+        # the memory footprint of crashpad_handler by eliminating
+        # unused code and unused shared libraries.
+        # The flags assume gcc/clang toolchain.
+        ['host_os=="linux"',  {
+          'cflags': [
+            '-ffunction-sections',
+            '-fdata-sections',
+          ],
+          'ldflags': [
+            '-Wl,--as-needed',
+            '-Wl,-gc-sections',
+          ],
+        }],
         ['OS=="win"',  {
           'msvs_settings': {
             'VCLinkerTool': {
diff --git a/src/third_party/freetype2/src/tools/Jamfile b/src/third_party/freetype2/src/tools/Jamfile
new file mode 100644
index 0000000..475161e
--- /dev/null
+++ b/src/third_party/freetype2/src/tools/Jamfile
@@ -0,0 +1,5 @@
+# Jamfile for src/tools
+#
+SubDir FT2_TOP src tools ;
+
+Main  apinames : apinames.c ;
diff --git a/src/third_party/freetype2/src/tools/afblue.pl b/src/third_party/freetype2/src/tools/afblue.pl
new file mode 100644
index 0000000..bbc4f47
--- /dev/null
+++ b/src/third_party/freetype2/src/tools/afblue.pl
@@ -0,0 +1,551 @@
+#! /usr/bin/perl -w
+# -*- Perl -*-
+#
+# afblue.pl
+#
+# Process a blue zone character data file.
+#
+# Copyright (C) 2013-2020 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used,
+# modified, and distributed under the terms of the FreeType project
+# license, LICENSE.TXT.  By continuing to use, modify, or distribute
+# this file you indicate that you have read the license and
+# understand and accept it fully.
+
+use strict;
+use warnings;
+use English '-no_match_vars';
+use open ':std', ':encoding(UTF-8)';
+
+
+my $prog = $PROGRAM_NAME;
+$prog =~ s| .* / ||x;      # Remove path.
+
+die "usage: $prog datafile < infile > outfile\n" if $#ARGV != 0;
+
+
+my $datafile = $ARGV[0];
+
+my %diversions;        # The extracted and massaged data from `datafile'.
+my @else_stack;        # Booleans to track else-clauses.
+my @name_stack;        # Stack of integers used for names of aux. variables.
+
+my $curr_enum;         # Name of the current enumeration.
+my $curr_array;        # Name of the current array.
+my $curr_max;          # Name of the current maximum value.
+
+my $curr_enum_element; # Name of the current enumeration element.
+my $curr_offset;       # The offset relative to current aux. variable.
+my $curr_elem_size;    # The number of non-space characters in the current string or
+                       # the number of elements in the current block.
+
+my $have_sections = 0; # Boolean; set if start of a section has been seen.
+my $have_strings;      # Boolean; set if current section contains strings.
+my $have_blocks;       # Boolean; set if current section contains blocks.
+
+my $have_enum_element; # Boolean; set if we have an enumeration element.
+my $in_string;         # Boolean; set if a string has been parsed.
+
+my $num_sections = 0;  # Number of sections seen so far.
+
+my $last_aux;          # Name of last auxiliary variable.
+
+
+# Regular expressions.
+
+# [<ws>] <enum_name> <ws> <array_name> <ws> <max_name> [<ws>] ':' [<ws>] '\n'
+my $section_re = qr/ ^ \s* (\S+) \s+ (\S+) \s+ (\S+) \s* : \s* $ /x;
+
+# [<ws>] <enum_element_name> [<ws>] '\n'
+my $enum_element_re = qr/ ^ \s* ( [A-Za-z0-9_]+ ) \s* $ /x;
+
+# '#' <preprocessor directive> '\n'
+my $preprocessor_re = qr/ ^ \# /x;
+
+# [<ws>] '/' '/' <comment> '\n'
+my $comment_re = qr| ^ \s* // |x;
+
+# empty line
+my $whitespace_only_re = qr/ ^ \s* $ /x;
+
+# [<ws>] '"' <string> '"' [<ws>] '\n'  (<string> doesn't contain newlines)
+my $string_re = qr/ ^ \s*
+                       " ( (?> (?: (?> [^"\\]+ ) | \\. )* ) ) "
+                       \s* $ /x;
+
+# [<ws>] '{' <block> '}' [<ws>] '\n'  (<block> can contain newlines)
+my $block_start_re = qr/ ^ \s* \{ /x;
+
+# We need the capturing group for `split' to make it return the separator
+# tokens (i.e., the opening and closing brace) also.
+my $brace_re = qr/ ( [{}] ) /x;
+
+
+sub Warn
+{
+  my $message = shift;
+  warn "$datafile:$INPUT_LINE_NUMBER: warning: $message\n";
+}
+
+
+sub Die
+{
+  my $message = shift;
+  die "$datafile:$INPUT_LINE_NUMBER: error: $message\n";
+}
+
+
+my $warned_before = 0;
+
+sub warn_before
+{
+  Warn("data before first section gets ignored") unless $warned_before;
+  $warned_before = 1;
+}
+
+
+sub strip_newline
+{
+  chomp;
+  s/ \x0D $ //x;
+}
+
+
+sub end_curr_string
+{
+  # Append final null byte to string.
+  if ($have_strings)
+  {
+    push @{$diversions{$curr_array}}, "    '\\0',\n" if $in_string;
+
+    $curr_offset++;
+    $in_string = 0;
+  }
+}
+
+
+sub update_max_elem_size
+{
+  if ($curr_elem_size)
+  {
+    my $max = pop @{$diversions{$curr_max}};
+    $max = $curr_elem_size if $curr_elem_size > $max;
+    push @{$diversions{$curr_max}}, $max;
+  }
+}
+
+
+sub convert_non_ascii_char
+{
+  # A UTF-8 character outside of the printable ASCII range, with possibly a
+  # leading backslash character.
+  my $s = shift;
+
+  # Here we count characters, not bytes.
+  $curr_elem_size += length $s;
+
+  utf8::encode($s);
+  $s = uc unpack 'H*', $s;
+
+  $curr_offset += $s =~ s/\G(..)/'\\x$1', /sg;
+
+  return $s;
+}
+
+
+sub convert_ascii_chars
+{
+  # A series of ASCII characters in the printable range.
+  my $s = shift;
+
+  # We reduce multiple space characters to a single one.
+  $s =~ s/ +/ /g;
+
+  # Count all non-space characters.  Note that `()' applies a list context
+  # to the capture that is used to count the elements.
+  $curr_elem_size += () = $s =~ /[^ ]/g;
+
+  $curr_offset += $s =~ s/\G(.)/'$1', /g;
+
+  return $s;
+}
+
+
+sub convert_literal
+{
+  my $s = shift;
+  my $orig = $s;
+
+  # ASCII printables and space
+  my $safe_re = '\x20-\x7E';
+  # ASCII printables and space, no backslash
+  my $safe_no_backslash_re = '\x20-\x5B\x5D-\x7E';
+
+  $s =~ s{
+           (?: \\? ( [^$safe_re] )
+               | ( (?: [$safe_no_backslash_re]
+                       | \\ [$safe_re] )+ ) )
+         }
+         {
+           defined($1) ? convert_non_ascii_char($1)
+                       : convert_ascii_chars($2)
+         }egx;
+
+   # We assume that `$orig' doesn't contain `*/'
+   return $s . " /* $orig */";
+}
+
+
+sub aux_name
+{
+  return "af_blue_" . $num_sections. "_" . join('_', @name_stack);
+}
+
+
+sub aux_name_next
+{
+  $name_stack[$#name_stack]++;
+  my $name = aux_name();
+  $name_stack[$#name_stack]--;
+
+  return $name;
+}
+
+
+sub enum_val_string
+{
+  # Build string that holds code to save the current offset in an
+  # enumeration element.
+  my $aux = shift;
+
+  my $add = ($last_aux eq "af_blue_" . $num_sections . "_0" )
+              ? ""
+              : "$last_aux + ";
+
+  return "    $aux = $add$curr_offset,\n";
+}
+
+
+
+# Process data file.
+
+open(DATA, $datafile) || die "$prog: can't open \`$datafile': $OS_ERROR\n";
+
+while (<DATA>)
+{
+  strip_newline();
+
+  next if /$comment_re/;
+  next if /$whitespace_only_re/;
+
+  if (/$section_re/)
+  {
+    Warn("previous section is empty") if ($have_sections
+                                          && !$have_strings
+                                          && !$have_blocks);
+
+    end_curr_string();
+    update_max_elem_size();
+
+    # Save captured groups from `section_re'.
+    $curr_enum = $1;
+    $curr_array = $2;
+    $curr_max = $3;
+
+    $curr_enum_element = "";
+    $curr_offset = 0;
+
+    Warn("overwriting already defined enumeration \`$curr_enum'")
+      if exists($diversions{$curr_enum});
+    Warn("overwriting already defined array \`$curr_array'")
+      if exists($diversions{$curr_array});
+    Warn("overwriting already defined maximum value \`$curr_max'")
+      if exists($diversions{$curr_max});
+
+    $diversions{$curr_enum} = [];
+    $diversions{$curr_array} = [];
+    $diversions{$curr_max} = [];
+
+    push @{$diversions{$curr_max}}, 0;
+
+    @name_stack = ();
+    push @name_stack, 0;
+
+    $have_sections = 1;
+    $have_strings = 0;
+    $have_blocks = 0;
+
+    $have_enum_element = 0;
+    $in_string = 0;
+
+    $num_sections++;
+    $curr_elem_size = 0;
+
+    $last_aux = aux_name();
+
+    next;
+  }
+
+  if (/$preprocessor_re/)
+  {
+    if ($have_sections)
+    {
+      # Having preprocessor conditionals complicates the computation of
+      # correct offset values.  We have to introduce auxiliary enumeration
+      # elements with the name `af_blue_<s>_<n1>_<n2>_...' that store
+      # offsets to be used in conditional clauses.  `<s>' is the number of
+      # sections seen so far, `<n1>' is the number of `#if' and `#endif'
+      # conditionals seen so far in the topmost level, `<n2>' the number of
+      # `#if' and `#endif' conditionals seen so far one level deeper, etc.
+      # As a consequence, uneven values are used within a clause, and even
+      # values after a clause, since the C standard doesn't allow the
+      # redefinition of an enumeration value.  For example, the name
+      # `af_blue_5_1_6' is used to construct enumeration values in the fifth
+      # section after the third (second-level) if-clause within the first
+      # (top-level) if-clause.  After the first top-level clause has
+      # finished, `af_blue_5_2' is used.  The current offset is then
+      # relative to the value stored in the current auxiliary element.
+
+      if (/ ^ \# \s* if /x)
+      {
+        push @else_stack, 0;
+
+        $name_stack[$#name_stack]++;
+
+        push @{$diversions{$curr_enum}}, enum_val_string(aux_name());
+        $last_aux = aux_name();
+
+        push @name_stack, 0;
+
+        $curr_offset = 0;
+      }
+      elsif (/ ^ \# \s* elif /x)
+      {
+        Die("unbalanced #elif") unless @else_stack;
+
+        pop @name_stack;
+
+        push @{$diversions{$curr_enum}}, enum_val_string(aux_name_next());
+        $last_aux = aux_name();
+
+        push @name_stack, 0;
+
+        $curr_offset = 0;
+      }
+      elsif (/ ^ \# \s* else /x)
+      {
+        my $prev_else = pop @else_stack;
+        Die("unbalanced #else") unless defined($prev_else);
+        Die("#else already seen") if $prev_else;
+        push @else_stack, 1;
+
+        pop @name_stack;
+
+        push @{$diversions{$curr_enum}}, enum_val_string(aux_name_next());
+        $last_aux = aux_name();
+
+        push @name_stack, 0;
+
+        $curr_offset = 0;
+      }
+      elsif (/ ^ (\# \s*) endif /x)
+      {
+        my $prev_else = pop @else_stack;
+        Die("unbalanced #endif") unless defined($prev_else);
+
+        pop @name_stack;
+
+        # If there is no else-clause for an if-clause, we add one.  This is
+        # necessary to have correct offsets.
+        if (!$prev_else)
+        {
+          # Use amount of whitespace from `endif'.
+          push @{$diversions{$curr_enum}}, enum_val_string(aux_name_next())
+                                           . $1 . "else\n";
+          $last_aux = aux_name();
+
+          $curr_offset = 0;
+        }
+
+        $name_stack[$#name_stack]++;
+
+        push @{$diversions{$curr_enum}}, enum_val_string(aux_name());
+        $last_aux = aux_name();
+
+        $curr_offset = 0;
+      }
+
+      # Handle (probably continued) preprocessor lines.
+    CONTINUED_LOOP:
+      {
+        do
+        {
+          strip_newline();
+
+          push @{$diversions{$curr_enum}}, $ARG . "\n";
+          push @{$diversions{$curr_array}}, $ARG . "\n";
+
+          last CONTINUED_LOOP unless / \\ $ /x;
+
+        } while (<DATA>);
+      }
+    }
+    else
+    {
+      warn_before();
+    }
+
+    next;
+  }
+
+  if (/$enum_element_re/)
+  {
+    end_curr_string();
+    update_max_elem_size();
+
+    $curr_enum_element = $1;
+    $have_enum_element = 1;
+    $curr_elem_size = 0;
+
+    next;
+  }
+
+  if (/$string_re/)
+  {
+    if ($have_sections)
+    {
+      Die("strings and blocks can't be mixed in a section") if $have_blocks;
+
+      # Save captured group from `string_re'.
+      my $string = $1;
+
+      if ($have_enum_element)
+      {
+        push @{$diversions{$curr_enum}}, enum_val_string($curr_enum_element);
+        $have_enum_element = 0;
+      }
+
+      $string = convert_literal($string);
+
+      push @{$diversions{$curr_array}}, "    $string\n";
+
+      $have_strings = 1;
+      $in_string = 1;
+    }
+    else
+    {
+      warn_before();
+    }
+
+    next;
+  }
+
+  if (/$block_start_re/)
+  {
+    if ($have_sections)
+    {
+      Die("strings and blocks can't be mixed in a section") if $have_strings;
+
+      my $depth = 0;
+      my $block = "";
+      my $block_end = 0;
+
+      # Count braces while getting the block.
+    BRACE_LOOP:
+      {
+        do
+        {
+          strip_newline();
+
+          foreach my $substring (split(/$brace_re/))
+          {
+            if ($block_end)
+            {
+              Die("invalid data after last matching closing brace")
+                if $substring !~ /$whitespace_only_re/;
+            }
+
+            $block .= $substring;
+
+            if ($substring eq '{')
+            {
+              $depth++;
+            }
+            elsif ($substring eq '}')
+            {
+              $depth--;
+
+              $block_end = 1 if $depth == 0;
+            }
+          }
+
+          # If we are here, we have run out of substrings, so get next line
+          # or exit.
+          last BRACE_LOOP if $block_end;
+
+          $block .= "\n";
+
+        } while (<DATA>);
+      }
+
+      if ($have_enum_element)
+      {
+        push @{$diversions{$curr_enum}}, enum_val_string($curr_enum_element);
+        $have_enum_element = 0;
+      }
+
+      push @{$diversions{$curr_array}}, $block . ",\n";
+
+      $curr_offset++;
+      $curr_elem_size++;
+
+      $have_blocks = 1;
+    }
+    else
+    {
+      warn_before();
+    }
+
+    next;
+  }
+
+  # Garbage.  We weren't able to parse the data.
+  Die("syntax error");
+}
+
+# Finalize data.
+end_curr_string();
+update_max_elem_size();
+
+
+# Filter stdin to stdout, replacing `@...@' templates.
+
+sub emit_diversion
+{
+  my $diversion_name = shift;
+  return (exists($diversions{$1})) ? "@{$diversions{$1}}"
+                                   : "@" . $diversion_name . "@";
+}
+
+
+$LIST_SEPARATOR = '';
+
+my $s1 = "This file has been generated by the Perl script \`$prog',";
+my $s1len = length $s1;
+my $s2 = "using data from file \`$datafile'.";
+my $s2len = length $s2;
+my $slen = ($s1len > $s2len) ? $s1len : $s2len;
+
+print "/* " . $s1 . " " x ($slen - $s1len) . " */\n"
+      . "/* " . $s2 . " " x ($slen - $s2len) . " */\n"
+      . "\n";
+
+while (<STDIN>)
+{
+  s/ @ ( [A-Za-z0-9_]+? ) @ / emit_diversion($1) /egx;
+  print;
+}
+
+# EOF
diff --git a/src/third_party/freetype2/src/tools/apinames.c b/src/third_party/freetype2/src/tools/apinames.c
new file mode 100644
index 0000000..aeecf88
--- /dev/null
+++ b/src/third_party/freetype2/src/tools/apinames.c
@@ -0,0 +1,514 @@
+/*
+ * This little program is used to parse the FreeType headers and
+ * find the declaration of all public APIs.  This is easy, because
+ * they all look like the following:
+ *
+ *   FT_EXPORT( return_type )
+ *   function_name( function arguments );
+ *
+ * You must pass the list of header files as arguments.  Wildcards are
+ * accepted if you are using GCC for compilation (and probably by
+ * other compilers too).
+ *
+ * Author: FreeType team, 2005-2019
+ *
+ * This code is explicitly placed into the public domain.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#define  PROGRAM_NAME     "apinames"
+#define  PROGRAM_VERSION  "0.3"
+
+#define  LINEBUFF_SIZE  1024
+
+
+typedef enum  OutputFormat_
+{
+  OUTPUT_LIST = 0,      /* output the list of names, one per line             */
+  OUTPUT_WINDOWS_DEF,   /* output a Windows .DEF file for Visual C++ or Mingw */
+  OUTPUT_BORLAND_DEF,   /* output a Windows .DEF file for Borland C++         */
+  OUTPUT_WATCOM_LBC,    /* output a Watcom Linker Command File                */
+  OUTPUT_NETWARE_IMP,   /* output a NetWare ImportFile                        */
+  OUTPUT_GNU_VERMAP     /* output a version map for GNU or Solaris linker     */
+
+} OutputFormat;
+
+
+static void
+panic( const char*  message )
+{
+  fprintf( stderr, "PANIC: %s\n", message );
+  exit(2);
+}
+
+
+typedef struct  NameRec_
+{
+  char*         name;
+  unsigned int  hash;
+
+} NameRec, *Name;
+
+
+static Name  the_names;
+static int   num_names;
+static int   max_names;
+
+
+static void
+names_add( const char*  name,
+           const char*  end )
+{
+  unsigned int  h;
+  int           nn, len;
+  Name          nm;
+
+
+  if ( end <= name )
+    return;
+
+  /* compute hash value */
+  len = (int)( end - name );
+  h   = 0;
+
+  for ( nn = 0; nn < len; nn++ )
+    h = h * 33 + name[nn];
+
+  /* check for an pre-existing name */
+  for ( nn = 0; nn < num_names; nn++ )
+  {
+    nm = the_names + nn;
+
+    if ( (int)nm->hash                 == h &&
+         memcmp( name, nm->name, len ) == 0 &&
+         nm->name[len]                 == 0 )
+      return;
+  }
+
+  /* add new name */
+  if ( num_names >= max_names )
+  {
+    max_names += ( max_names >> 1 ) + 4;
+    the_names  = (NameRec*)realloc( the_names,
+                                    sizeof ( the_names[0] ) * max_names );
+    if ( !the_names )
+      panic( "not enough memory" );
+  }
+  nm = &the_names[num_names++];
+
+  nm->hash = h;
+  nm->name = (char*)malloc( len + 1 );
+  if ( !nm->name )
+    panic( "not enough memory" );
+
+  memcpy( nm->name, name, len );
+  nm->name[len] = 0;
+}
+
+
+static int
+name_compare( const void*  name1,
+              const void*  name2 )
+{
+  Name  n1 = (Name)name1;
+  Name  n2 = (Name)name2;
+
+  return strcmp( n1->name, n2->name );
+}
+
+
+static void
+names_sort( void )
+{
+  qsort( the_names, (size_t)num_names,
+         sizeof ( the_names[0] ), name_compare );
+}
+
+
+static void
+names_dump( FILE*         out,
+            OutputFormat  format,
+            const char*   dll_name )
+{
+  int  nn;
+
+
+  switch ( format )
+  {
+  case OUTPUT_WINDOWS_DEF:
+    if ( dll_name )
+      fprintf( out, "LIBRARY %s\n", dll_name );
+
+    fprintf( out, "DESCRIPTION  FreeType 2 DLL\n" );
+    fprintf( out, "EXPORTS\n" );
+
+    for ( nn = 0; nn < num_names; nn++ )
+      fprintf( out, "  %s\n", the_names[nn].name );
+
+    break;
+
+  case OUTPUT_BORLAND_DEF:
+    if ( dll_name )
+      fprintf( out, "LIBRARY %s\n", dll_name );
+
+    fprintf( out, "DESCRIPTION  FreeType 2 DLL\n" );
+    fprintf( out, "EXPORTS\n" );
+
+    for ( nn = 0; nn < num_names; nn++ )
+      fprintf( out, "  _%s\n", the_names[nn].name );
+
+    break;
+
+  case OUTPUT_WATCOM_LBC:
+    {
+      const char*  dot;
+      char         temp[512];
+
+
+      if ( !dll_name )
+      {
+        fprintf( stderr,
+                 "you must provide a DLL name with the -d option!\n" );
+        exit( 4 );
+      }
+
+      /* we must omit the `.dll' suffix from the library name */
+      dot = strchr( dll_name, '.' );
+      if ( dot )
+      {
+        int  len = dot - dll_name;
+
+
+        if ( len > (int)( sizeof ( temp ) - 1 ) )
+          len = sizeof ( temp ) - 1;
+
+        memcpy( temp, dll_name, len );
+        temp[len] = 0;
+
+        dll_name = (const char*)temp;
+      }
+
+      for ( nn = 0; nn < num_names; nn++ )
+        fprintf( out, "++_%s.%s.%s\n",
+                      the_names[nn].name, dll_name, the_names[nn].name );
+    }
+
+    break;
+
+  case OUTPUT_NETWARE_IMP:
+    if ( dll_name )
+      fprintf( out, "  (%s)\n", dll_name );
+
+    for ( nn = 0; nn < num_names - 1; nn++ )
+      fprintf( out, "  %s,\n", the_names[nn].name );
+    fprintf( out, "  %s\n", the_names[num_names - 1].name );
+
+    break;
+
+  case OUTPUT_GNU_VERMAP:
+    fprintf( out, "{\n\tglobal:\n" );
+
+    for ( nn = 0; nn < num_names; nn++ )
+      fprintf( out, "\t\t%s;\n", the_names[nn].name );
+
+    fprintf( out, "\tlocal:\n\t\t*;\n};\n" );
+
+    break;
+
+  default:  /* LIST */
+    for ( nn = 0; nn < num_names; nn++ )
+      fprintf( out, "%s\n", the_names[nn].name );
+
+    break;
+  }
+}
+
+
+/* states of the line parser */
+
+typedef enum  State_
+{
+  STATE_START = 0,  /* waiting for FT_EXPORT keyword and return type */
+  STATE_TYPE        /* type was read, waiting for function name      */
+
+} State;
+
+
+static int
+read_header_file( FILE*  file,
+                  int    verbose )
+{
+  static char  buff[LINEBUFF_SIZE + 1];
+  State        state = STATE_START;
+
+
+  while ( !feof( file ) )
+  {
+    char*  p;
+
+
+    if ( !fgets( buff, LINEBUFF_SIZE, file ) )
+      break;
+
+    p = buff;
+
+    /* skip leading whitespace */
+    while ( *p && ( *p == ' ' || *p == '\\' ) )
+      p++;
+
+    /* skip empty lines */
+    if ( *p == '\n' || *p == '\r' )
+      continue;
+
+    switch ( state )
+    {
+    case STATE_START:
+      if ( memcmp( p, "FT_EXPORT(", 10 ) != 0 )
+        break;
+
+      p += 10;
+      for (;;)
+      {
+        if ( *p == 0 || *p == '\n' || *p == '\r' )
+          goto NextLine;
+
+        if ( *p == ')' )
+        {
+          p++;
+          break;
+        }
+
+        p++;
+      }
+
+      state = STATE_TYPE;
+
+      /*
+       * Sometimes, the name is just after `FT_EXPORT(...)', so skip
+       * whitespace and fall-through if we find an alphanumeric character.
+       */
+      while ( *p == ' ' || *p == '\t' )
+        p++;
+
+      if ( !isalpha( *p ) )
+        break;
+
+      /* fall-through */
+
+    case STATE_TYPE:
+      {
+        char*   name = p;
+
+
+        while ( isalnum( *p ) || *p == '_' )
+          p++;
+
+        if ( p > name )
+        {
+          if ( verbose )
+            fprintf( stderr, ">>> %.*s\n", (int)( p - name ), name );
+
+          names_add( name, p );
+        }
+
+        state = STATE_START;
+      }
+
+      break;
+
+    default:
+      ;
+    }
+
+NextLine:
+    ;
+  } /* end of while loop */
+
+  return 0;
+}
+
+
+static void
+usage( void )
+{
+  static const char* const  format =
+    "%s %s: extract FreeType API names from header files\n"
+    "\n"
+    "This program extracts the list of public FreeType API functions.\n"
+    "It receives a list of header files as an argument and\n"
+    "generates a sorted list of unique identifiers in various formats.\n"
+    "\n"
+    "usage: %s header1 [options] [header2 ...]\n"
+    "\n"
+    "options:   -       parse the contents of stdin, ignore arguments\n"
+    "           -v      verbose mode, output sent to standard error\n"
+    "           -oFILE  write output to FILE instead of standard output\n"
+    "           -dNAME  indicate DLL file name, 'freetype.dll' by default\n"
+    "           -w      output .DEF file for Visual C++ and Mingw\n"
+    "           -wB     output .DEF file for Borland C++\n"
+    "           -wW     output Watcom Linker Response File\n"
+    "           -wN     output NetWare Import File\n"
+    "           -wL     output version map for GNU or Solaris linker\n"
+    "\n";
+
+  fprintf( stderr,
+           format,
+           PROGRAM_NAME,
+           PROGRAM_VERSION,
+           PROGRAM_NAME );
+
+  exit( 1 );
+}
+
+
+int
+main( int                 argc,
+      const char* const*  argv )
+{
+  int           from_stdin   = 0;
+  int           verbose      = 0;
+  OutputFormat  format       = OUTPUT_LIST;  /* the default */
+  FILE*         out          = stdout;
+  const char*   library_name = NULL;
+
+
+  if ( argc < 2 )
+    usage();
+
+  /* `-' used as a single argument means read source file from stdin */
+  while ( argc > 1 && argv[1][0] == '-' )
+  {
+    const char*  arg = argv[1];
+
+
+    switch ( arg[1] )
+    {
+    case 'v':
+      verbose = 1;
+
+      break;
+
+    case 'o':
+      if ( arg[2] == 0 )
+      {
+        if ( argc < 2 )
+          usage();
+
+        arg = argv[2];
+        argv++;
+        argc--;
+      }
+      else
+        arg += 2;
+
+      out = fopen( arg, "wt" );
+      if ( !out )
+      {
+        fprintf( stderr, "could not open '%s' for writing\n", arg );
+        exit( 3 );
+      }
+
+      break;
+
+    case 'd':
+      if ( arg[2] == 0 )
+      {
+        if ( argc < 2 )
+          usage();
+
+        arg = argv[2];
+        argv++;
+        argc--;
+      }
+      else
+        arg += 2;
+
+      library_name = arg;
+
+      break;
+
+    case 'w':
+      format = OUTPUT_WINDOWS_DEF;
+
+      switch ( arg[2] )
+      {
+      case 'B':
+        format = OUTPUT_BORLAND_DEF;
+        break;
+
+      case 'W':
+        format = OUTPUT_WATCOM_LBC;
+        break;
+
+      case 'N':
+        format = OUTPUT_NETWARE_IMP;
+        break;
+
+      case 'L':
+        format = OUTPUT_GNU_VERMAP;
+        break;
+
+      case 0:
+        break;
+
+      default:
+        usage();
+      }
+
+      break;
+
+    case 0:
+      from_stdin = 1;
+
+      break;
+
+    default:
+      usage();
+    }
+
+    argc--;
+    argv++;
+
+  } /* end of while loop */
+
+  if ( from_stdin )
+    read_header_file( stdin, verbose );
+  else
+  {
+    for ( --argc, argv++; argc > 0; argc--, argv++ )
+    {
+      FILE*  file = fopen( argv[0], "rb" );
+
+
+      if ( !file )
+        fprintf( stderr, "unable to open '%s'\n", argv[0] );
+      else
+      {
+        if ( verbose )
+          fprintf( stderr, "opening '%s'\n", argv[0] );
+
+        read_header_file( file, verbose );
+        fclose( file );
+      }
+    }
+  }
+
+  if ( num_names == 0 )
+    panic( "could not find exported functions\n" );
+
+  names_sort();
+  names_dump( out, format, library_name );
+
+  if ( out != stdout )
+    fclose( out );
+
+  return 0;
+}
+
+
+/* END */
diff --git a/src/third_party/freetype2/src/tools/ftrandom/Makefile b/src/third_party/freetype2/src/tools/ftrandom/Makefile
new file mode 100644
index 0000000..24dc49c
--- /dev/null
+++ b/src/third_party/freetype2/src/tools/ftrandom/Makefile
@@ -0,0 +1,45 @@
+# TOP_DIR and OBJ_DIR should be set by the user to the right directories,
+# if necessary.
+
+TOP_DIR ?= ../../..
+OBJ_DIR ?= $(TOP_DIR)/objs
+
+
+# The setup below is for gcc on a Unix-like platform,
+# where FreeType has been set up to create a static library
+# (which is the default).
+
+VPATH = $(OBJ_DIR) \
+        $(OBJ_DIR)/.libs
+
+SRC_DIR = $(TOP_DIR)/src/tools/ftrandom
+
+CC = gcc
+WFLAGS = -Wmissing-prototypes \
+         -Wunused \
+         -Wimplicit \
+         -Wreturn-type \
+         -Wparentheses \
+         -pedantic \
+         -Wformat \
+         -Wchar-subscripts \
+         -Wsequence-point
+CFLAGS = $(WFLAGS) \
+         -g
+INCLUDES = -I $(TOP_DIR)/include
+LDFLAGS =
+LIBS = -lm \
+       -lz \
+       -lpng \
+       -lbz2 \
+       -lharfbuzz
+
+all: $(OBJ_DIR)/ftrandom
+
+$(OBJ_DIR)/ftrandom.o: $(SRC_DIR)/ftrandom.c
+	$(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $<
+
+$(OBJ_DIR)/ftrandom: $(OBJ_DIR)/ftrandom.o libfreetype.a
+	$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
+
+# EOF
diff --git a/src/third_party/freetype2/src/tools/ftrandom/README b/src/third_party/freetype2/src/tools/ftrandom/README
new file mode 100644
index 0000000..7c61086
--- /dev/null
+++ b/src/third_party/freetype2/src/tools/ftrandom/README
@@ -0,0 +1,69 @@
+ftrandom
+========
+
+This program expects a set of directories containing good fonts, and a set
+of extensions of fonts to be tested.  It will randomly pick a font, copy it,
+introduce an error and then test it.
+
+The FreeType tests are quite basic; for each erroneous font ftrandom
+
+  . forks off a new tester,
+  . initializes the library,
+  . opens each font in the file,
+  . loads each glyph,
+  . optionally reviews the contours of the glyph,
+  . optionally rasterizes the glyph, and
+  . closes the face.
+
+If a tester takes longer than 20 seconds, ftrandom saves the erroneous font
+and continues.  If the tester exits normally or with an error, then the
+superstructure removes the test font and continues.
+
+
+Command line options
+--------------------
+
+  --all                    Test every font in the directory(ies) no matter
+                           what its extension.
+  --check-outlines         Call `FT_Outline_Decompose' on each glyph.
+  --dir <dir>              Append <dir> to the list of directories to search
+                           for good fonts.  No recursive search.
+  --error-count <cnt>      Introduce <cnt> single-byte errors into the
+                           erroneous fonts (default: 1).
+  --error-fraction <frac>  Multiply the file size of the font by <frac> and
+                           introduce that many errors into the erroneous
+                           font file.  <frac> should be in the range [0;1]
+                           (default: 0.0).
+  --ext <ext>              Add <ext> to the set of font types tested.
+  --help                   Print out this list of options.
+  --nohints                Specify FT_LOAD_NO_HINTING when loading glyphs.
+  --rasterize              Call `FT_Render_Glyph' as well as loading it.
+  --result <dir>           This is the directory in which test files are
+                           placed.
+  --test <file>            Run a single test on a pre-generated testcase.
+                           This is done in the current process so it can be
+                           debugged more easily.
+
+The default font extensions tested by ftrandom are
+
+  .ttf .otf .ttc .cid .pfb .pfa .bdf .pcf .pfr .fon .otb .cff
+
+The default font directory is controlled by the macro `GOOD_FONTS_DIR' in
+the source code (and can be thus specified during compilation); its default
+value is
+
+  /usr/local/share/fonts
+
+The default result directory is `results' (in the current directory).
+
+
+Compilation
+-----------
+
+Two possible solutions.
+
+. Run ftrandom within a debugging tool like `valgrind' to catch various
+  memory issues.
+
+. Compile FreeType with sanitizer flags as provided by gcc or clang, for
+  example, then link it with ftrandom.
diff --git a/src/third_party/freetype2/src/tools/ftrandom/ftrandom.c b/src/third_party/freetype2/src/tools/ftrandom/ftrandom.c
new file mode 100644
index 0000000..ab5cfc9
--- /dev/null
+++ b/src/third_party/freetype2/src/tools/ftrandom/ftrandom.c
@@ -0,0 +1,720 @@
+/* Copyright (C) 2005, 2007, 2008, 2013 by George Williams */
+/*
+ * 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.
+
+ * The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+/* modified by Werner Lemberg <wl@gnu.org>       */
+/* This file is now part of the FreeType library */
+
+
+#define _XOPEN_SOURCE 500 /* for `kill', `strdup', `random', and `srandom' */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <signal.h>
+#include <time.h>
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_OUTLINE_H
+
+#define true     1
+#define false    0
+#define forever  for (;;)
+
+
+  static int    check_outlines = false;
+  static int    nohints        = false;
+  static int    rasterize      = false;
+  static char*  results_dir    = "results";
+
+#define GOOD_FONTS_DIR  "/usr/local/share/fonts"
+
+  static char*  default_dir_list[] =
+  {
+    GOOD_FONTS_DIR,
+    NULL
+  };
+
+  static char*  default_ext_list[] =
+  {
+    "ttf",
+    "otf",
+    "ttc",
+    "cid",
+    "pfb",
+    "pfa",
+    "bdf",
+    "pcf",
+    "pfr",
+    "fon",
+    "otb",
+    "cff",
+    NULL
+  };
+
+  static unsigned int  error_count    = 1;
+  static double        error_fraction = 0.0;
+
+  static FT_F26Dot6  font_size = 12 * 64;
+
+  static struct fontlist
+  {
+    char*         name;
+    long          len;
+    unsigned int  isbinary: 1;
+    unsigned int  isascii: 1;
+    unsigned int  ishex: 1;
+
+  } *fontlist;
+
+  static unsigned int  fcnt;
+
+
+  static int
+  FT_MoveTo( const FT_Vector  *to,
+             void             *user )
+  {
+    FT_UNUSED( to );
+    FT_UNUSED( user );
+
+    return 0;
+  }
+
+
+  static int
+  FT_LineTo( const FT_Vector  *to,
+             void             *user )
+  {
+    FT_UNUSED( to );
+    FT_UNUSED( user );
+
+    return 0;
+  }
+
+
+  static int
+  FT_ConicTo( const FT_Vector  *_cp,
+              const FT_Vector  *to,
+              void             *user )
+  {
+    FT_UNUSED( _cp );
+    FT_UNUSED( to );
+    FT_UNUSED( user );
+
+    return 0;
+  }
+
+
+  static int
+  FT_CubicTo( const FT_Vector  *cp1,
+              const FT_Vector  *cp2,
+              const FT_Vector  *to,
+              void             *user )
+  {
+    FT_UNUSED( cp1 );
+    FT_UNUSED( cp2 );
+    FT_UNUSED( to );
+    FT_UNUSED( user );
+
+    return 0;
+  }
+
+
+  static FT_Outline_Funcs outlinefuncs =
+  {
+    FT_MoveTo,
+    FT_LineTo,
+    FT_ConicTo,
+    FT_CubicTo,
+    0, 0          /* No shift, no delta */
+  };
+
+
+  static void
+  TestFace( FT_Face  face )
+  {
+    unsigned int  gid;
+    int           load_flags = FT_LOAD_DEFAULT;
+
+
+    if ( check_outlines         &&
+         FT_IS_SCALABLE( face ) )
+      load_flags = FT_LOAD_NO_BITMAP;
+
+    if ( nohints )
+      load_flags |= FT_LOAD_NO_HINTING;
+
+    FT_Set_Char_Size( face, 0, font_size, 72, 72 );
+
+    for ( gid = 0; gid < face->num_glyphs; gid++ )
+    {
+      if ( check_outlines         &&
+           FT_IS_SCALABLE( face ) )
+      {
+        if ( !FT_Load_Glyph( face, gid, load_flags ) )
+          FT_Outline_Decompose( &face->glyph->outline, &outlinefuncs, NULL );
+      }
+      else
+        FT_Load_Glyph( face, gid, load_flags );
+
+      if ( rasterize )
+        FT_Render_Glyph( face->glyph, ft_render_mode_normal );
+    }
+
+    FT_Done_Face( face );
+  }
+
+
+  static void
+  ExecuteTest( char*  testfont )
+  {
+    FT_Library  context;
+    FT_Face     face;
+
+
+    if ( FT_Init_FreeType( &context ) )
+    {
+      fprintf( stderr, "Can't initialize FreeType.\n" );
+      exit( 1 );
+    }
+
+    if ( FT_New_Face( context, testfont, 0, &face ) )
+    {
+      /* The font is erroneous, so if this fails that's ok. */
+      exit( 0 );
+    }
+
+    if ( face->num_faces == 1 )
+      TestFace( face );
+    else
+    {
+      long  i, num;
+
+
+      num = face->num_faces;
+      FT_Done_Face( face );
+
+      for ( i = 0; i < num; i++ )
+      {
+        if ( !FT_New_Face( context, testfont, i, &face ) )
+          TestFace( face );
+      }
+    }
+
+    FT_Done_FreeType( context );
+
+    exit( 0 );
+  }
+
+
+  static int
+  extmatch( char*   filename,
+            char**  extensions )
+  {
+    int    i;
+    char*  pt;
+
+
+    if ( !extensions )
+      return true;
+
+    pt = strrchr( filename, '.' );
+    if ( !pt )
+      return false;
+    if ( pt < strrchr( filename, '/' ) )
+      return false;
+
+    for ( i = 0; extensions[i] != NULL; i++ )
+      if ( strcasecmp( pt + 1, extensions[i] ) == 0 ||
+           strcasecmp( pt,     extensions[i] ) == 0 )
+        return true;
+
+    return false;
+  }
+
+
+  static void
+  figurefiletype( struct fontlist*  item )
+  {
+    FILE*  foo;
+
+
+    item->isbinary = item->isascii = item->ishex = false;
+
+    foo = fopen( item->name, "rb" );
+    if ( foo )
+    {
+      /* Try to guess the file type from the first few characters... */
+      int  ch1 = getc( foo );
+      int  ch2 = getc( foo );
+      int  ch3 = getc( foo );
+      int  ch4 = getc( foo );
+
+
+      fclose( foo );
+
+      if ( ( ch1 == 0   && ch2 == 1   && ch3 == 0   && ch4 == 0   ) ||
+           ( ch1 == 'O' && ch2 == 'T' && ch3 == 'T' && ch4 == 'O' ) ||
+           ( ch1 == 't' && ch2 == 'r' && ch3 == 'u' && ch4 == 'e' ) ||
+           ( ch1 == 't' && ch2 == 't' && ch3 == 'c' && ch4 == 'f' ) )
+      {
+        /* ttf, otf, ttc files */
+        item->isbinary = true;
+      }
+      else if ( ch1 == 0x80 && ch2 == '\01' )
+      {
+        /* PFB header */
+        item->isbinary = true;
+      }
+      else if ( ch1 == '%' && ch2 == '!' )
+      {
+        /* Random PostScript */
+        if ( strstr( item->name, ".pfa" ) ||
+             strstr( item->name, ".PFA" ) )
+          item->ishex = true;
+        else
+          item->isascii = true;
+      }
+      else if ( ch1 == 1 && ch2 == 0 && ch3 == 4 )
+      {
+        /* Bare CFF */
+        item->isbinary = true;
+      }
+      else if ( ch1 == 'S' && ch2 == 'T' && ch3 == 'A' && ch4 == 'R' )
+      {
+        /* BDF */
+        item->ishex = true;
+      }
+      else if ( ch1 == 'P' && ch2 == 'F' && ch3 == 'R' && ch4 == '0' )
+      {
+        /* PFR */
+        item->isbinary = true;
+      }
+      else if ( ( ch1 == '\1' && ch2 == 'f' && ch3 == 'c' && ch4 == 'p' ) ||
+                ( ch1 == 'M'  && ch2 == 'Z' )                             )
+      {
+        /* Windows FON */
+        item->isbinary = true;
+      }
+      else
+      {
+        fprintf( stderr,
+                 "Can't recognize file type of `%s', assuming binary\n",
+                 item->name );
+        item->isbinary = true;
+      }
+    }
+    else
+    {
+      fprintf( stderr, "Can't open `%s' for typing the file.\n",
+               item->name );
+      item->isbinary = true;
+    }
+  }
+
+
+  static void
+  FindFonts( char**  fontdirs,
+             char**  extensions )
+  {
+    int           i;
+    unsigned int  max;
+    char          buffer[1025];
+    struct stat   statb;
+
+
+    max  = 0;
+    fcnt = 0;
+
+    for ( i = 0; fontdirs[i] != NULL; i++ )
+    {
+      DIR*            examples;
+      struct dirent*  ent;
+
+
+      examples = opendir( fontdirs[i] );
+      if ( !examples )
+      {
+        fprintf( stderr,
+                 "Can't open example font directory `%s'\n",
+                 fontdirs[i] );
+        exit( 1 );
+      }
+
+      while ( ( ent = readdir( examples ) ) != NULL )
+      {
+        snprintf( buffer, sizeof ( buffer ),
+                  "%s/%s", fontdirs[i], ent->d_name );
+        if ( stat( buffer, &statb ) == -1 || S_ISDIR( statb.st_mode ) )
+          continue;
+        if ( !extensions || extmatch( buffer, extensions ) )
+        {
+          if ( fcnt >= max )
+          {
+            max += 100;
+            fontlist = realloc( fontlist, max * sizeof ( struct fontlist ) );
+            if ( !fontlist )
+            {
+              fprintf( stderr, "Can't allocate memory\n" );
+              exit( 1 );
+            }
+          }
+
+          fontlist[fcnt].name = strdup( buffer );
+          fontlist[fcnt].len  = statb.st_size;
+
+          figurefiletype( &fontlist[fcnt] );
+          fcnt++;
+        }
+      }
+
+      closedir( examples );
+    }
+
+    if ( fcnt == 0 )
+    {
+      fprintf( stderr, "Can't find matching font files.\n" );
+      exit( 1 );
+    }
+
+    fontlist[fcnt].name = NULL;
+  }
+
+
+  static unsigned int
+  getErrorCnt( struct fontlist*  item )
+  {
+    if ( error_count == 0 && error_fraction == 0.0 )
+      return 0;
+
+    return error_count + (unsigned int)( error_fraction * item->len );
+  }
+
+
+  static int
+  getRandom( int  low,
+             int  high )
+  {
+    if ( low - high < 0x10000L )
+      return low + ( ( random() >> 8 ) % ( high + 1 - low ) );
+
+    return low + ( random() % ( high + 1 - low ) );
+  }
+
+
+  static int
+  copyfont( struct fontlist*  item,
+            char*             newfont )
+  {
+    static char   buffer[8096];
+    FILE          *good, *newf;
+    size_t        len;
+    unsigned int  i, err_cnt;
+
+
+    good = fopen( item->name, "r" );
+    if ( !good )
+    {
+      fprintf( stderr, "Can't open `%s'\n", item->name );
+      return false;
+    }
+
+    newf = fopen( newfont, "w+" );
+    if ( !newf )
+    {
+      fprintf( stderr, "Can't create temporary output file `%s'\n",
+               newfont );
+      exit( 1 );
+    }
+
+    while ( ( len = fread( buffer, 1, sizeof ( buffer ), good ) ) > 0 )
+      fwrite( buffer, 1, len, newf );
+
+    fclose( good );
+
+    err_cnt = getErrorCnt( item );
+    for ( i = 0; i < err_cnt; i++ )
+    {
+      fseek( newf, getRandom( 0, (int)( item->len - 1 ) ), SEEK_SET );
+
+      if ( item->isbinary )
+        putc( getRandom( 0, 0xFF ), newf );
+      else if ( item->isascii )
+        putc( getRandom( 0x20, 0x7E ), newf );
+      else
+      {
+        int  hex = getRandom( 0, 15 );
+
+
+        if ( hex < 10 )
+          hex += '0';
+        else
+          hex += 'A' - 10;
+
+        putc( hex, newf );
+      }
+    }
+
+    if ( ferror( newf ) )
+    {
+      fclose( newf );
+      unlink( newfont );
+      return false;
+    }
+
+    fclose( newf );
+
+    return true;
+  }
+
+
+  static int  child_pid;
+
+  static void
+  abort_test( int  sig )
+  {
+    FT_UNUSED( sig );
+
+    /* If a time-out happens, then kill the child */
+    kill( child_pid, SIGFPE );
+    write( 2, "Timeout... ", 11 );
+  }
+
+
+  static void
+  do_test( void )
+  {
+    int         i        = getRandom( 0, (int)( fcnt - 1 ) );
+    static int  test_num = 0;
+    char        buffer[1024];
+
+
+    sprintf( buffer, "%s/test%d", results_dir, test_num++ );
+
+    if ( copyfont ( &fontlist[i], buffer ) )
+    {
+      signal( SIGALRM, abort_test );
+      /* Anything that takes more than 20 seconds */
+      /* to parse and/or rasterize is an error.   */
+      alarm( 20 );
+      if ( ( child_pid = fork() ) == 0 )
+        ExecuteTest( buffer );
+      else if ( child_pid != -1 )
+      {
+        int  status;
+
+
+        waitpid( child_pid, &status, 0 );
+        alarm( 0 );
+        if ( WIFSIGNALED ( status ) )
+          printf( "Error found in file `%s'\n", buffer );
+        else
+          unlink( buffer );
+      }
+      else
+      {
+        fprintf( stderr, "Can't fork test case.\n" );
+        exit( 1 );
+      }
+      alarm( 0 );
+    }
+  }
+
+
+  static void
+  usage( FILE*  out,
+         char*  name )
+  {
+    char**  d = default_dir_list;
+    char**  e = default_ext_list;
+
+
+    fprintf( out, "%s [options] -- Generate random erroneous fonts\n"
+                  "  and attempt to parse them with FreeType.\n\n", name );
+
+    fprintf( out, "  --all                    All non-directory files are assumed to be fonts.\n" );
+    fprintf( out, "  --check-outlines         Make sure we can parse the outlines of each glyph.\n" );
+    fprintf( out, "  --dir <path>             Append <path> to list of font search directories\n"
+                  "                           (no recursive search).\n" );
+    fprintf( out, "  --error-count <cnt>      Introduce <cnt> single byte errors into each font\n"
+                  "                           (default: 1)\n" );
+    fprintf( out, "  --error-fraction <frac>  Introduce <frac>*filesize single byte errors\n"
+                  "                           into each font (default: 0.0).\n" );
+    fprintf( out, "  --ext <ext>              Add <ext> to list of extensions indicating fonts.\n" );
+    fprintf( out, "  --help                   Print this.\n" );
+    fprintf( out, "  --nohints                Turn off hinting.\n" );
+    fprintf( out, "  --rasterize              Attempt to rasterize each glyph.\n" );
+    fprintf( out, "  --results <path>         Place the created test fonts into <path>\n"
+                  "                           (default: `results')\n" );
+    fprintf( out, "  --size <float>           Use the given font size for the tests.\n" );
+    fprintf( out, "  --test <file>            Run a single test on an already existing file.\n" );
+    fprintf( out, "\n" );
+
+    fprintf( out, "Default font extensions:\n" );
+    fprintf( out, " " );
+    while ( *e )
+      fprintf( out, " .%s", *e++ );
+    fprintf( out, "\n" );
+
+    fprintf( out, "Default font directories:\n" );
+    fprintf( out, " " );
+    while ( *d )
+      fprintf( out, " %s", *d++ );
+    fprintf( out, "\n" );
+  }
+
+
+  int
+  main( int     argc,
+        char**  argv )
+  {
+    char    **dirs, **exts;
+    int     dcnt = 0, ecnt = 0, rset = false, allexts = false;
+    int     i;
+    time_t  now;
+    char*   testfile = NULL;
+
+
+    dirs = calloc( (size_t)( argc + 1 ), sizeof ( char ** ) );
+    exts = calloc( (size_t)( argc + 1 ), sizeof ( char ** ) );
+
+    for ( i = 1; i < argc; i++ )
+    {
+      char*  pt = argv[i];
+      char*  end;
+
+
+      if ( pt[0] == '-' && pt[1] == '-' )
+        pt++;
+
+      if ( strcmp( pt, "-all" ) == 0 )
+        allexts = true;
+      else if ( strcmp( pt, "-check-outlines" ) == 0 )
+        check_outlines = true;
+      else if ( strcmp( pt, "-dir" ) == 0 )
+        dirs[dcnt++] = argv[++i];
+      else if ( strcmp( pt, "-error-count" ) == 0 )
+      {
+        if ( !rset )
+          error_fraction = 0.0;
+        rset = true;
+        error_count = (unsigned int)strtoul( argv[++i], &end, 10 );
+        if ( *end != '\0' )
+        {
+          fprintf( stderr, "Bad value for error-count: %s\n", argv[i] );
+          exit( 1 );
+        }
+      }
+      else if ( strcmp( pt, "-error-fraction" ) == 0 )
+      {
+        if ( !rset )
+          error_count = 0;
+        rset = true;
+        error_fraction = strtod( argv[++i], &end );
+        if ( *end != '\0' )
+        {
+          fprintf( stderr, "Bad value for error-fraction: %s\n", argv[i] );
+          exit( 1 );
+        }
+        if ( error_fraction < 0.0 || error_fraction > 1.0 )
+        {
+          fprintf( stderr, "error-fraction must be in the range [0;1]\n" );
+          exit( 1 );
+        }
+      }
+      else if ( strcmp( pt, "-ext" ) == 0 )
+        exts[ecnt++] = argv[++i];
+      else if ( strcmp( pt, "-help" ) == 0 )
+      {
+        usage( stdout, argv[0] );
+        exit( 0 );
+      }
+      else if ( strcmp( pt, "-nohints" ) == 0 )
+        nohints = true;
+      else if ( strcmp( pt, "-rasterize" ) == 0 )
+        rasterize = true;
+      else if ( strcmp( pt, "-results" ) == 0 )
+        results_dir = argv[++i];
+      else if ( strcmp( pt, "-size" ) == 0 )
+      {
+        font_size = (FT_F26Dot6)( strtod( argv[++i], &end ) * 64 );
+        if ( *end != '\0' || font_size < 64 )
+        {
+          fprintf( stderr, "Bad value for size: %s\n", argv[i] );
+          exit( 1 );
+        }
+      }
+      else if ( strcmp( pt, "-test" ) == 0 )
+        testfile = argv[++i];
+      else
+      {
+        usage( stderr, argv[0] );
+        exit( 1 );
+      }
+    }
+
+    if ( allexts )
+    {
+      free( exts );
+      exts = NULL;
+    }
+    else if ( ecnt == 0 )
+    {
+      free( exts );
+      exts = default_ext_list;
+    }
+
+    if ( dcnt == 0 )
+    {
+      free( dirs );
+      dirs = default_dir_list;
+    }
+
+    if ( testfile )
+      ExecuteTest( testfile );         /* This should never return */
+
+    time( &now );
+    srandom( (unsigned int)now );
+
+    FindFonts( dirs, exts );
+    mkdir( results_dir, 0755 );
+
+    forever
+      do_test();
+
+    return 0;
+  }
+
+
+/* EOF */
diff --git a/src/third_party/freetype2/src/tools/no-copyright b/src/third_party/freetype2/src/tools/no-copyright
new file mode 100644
index 0000000..d639aa4
--- /dev/null
+++ b/src/third_party/freetype2/src/tools/no-copyright
@@ -0,0 +1,65 @@
+# Files that don't get a copyright, or which are taken from elsewhere.
+#
+# All lines in this file are patterns, including the comment lines; this
+# means that e.g. `FTL.TXT' matches all files that have this string in
+# the file name (including the path relative to the current directory,
+# always starting with `./').
+#
+# Don't put empty lines into this file!
+#
+.gitignore
+#
+builds/unix/pkg.m4
+#
+docs/FTL.TXT
+docs/GPLv2.TXT
+#
+include/freetype/internal/fthash.h
+#
+src/base/fthash.c
+src/base/md5.c
+src/base/md5.h
+#
+src/bdf/bdf.c
+src/bdf/bdf.h
+src/bdf/bdfdrivr.c
+src/bdf/bdfdrivr.h
+src/bdf/bdferror.h
+src/bdf/bdflib.c
+src/bdf/module.mk
+src/bdf/README
+src/bdf/rules.mk
+#
+src/pcf/module.mk
+src/pcf/pcf.c
+src/pcf/pcf.h
+src/pcf/pcfdrivr.c
+src/pcf/pcfdrivr.h
+src/pcf/pcferror.h
+src/pcf/pcfread.c
+src/pcf/pcfread.h
+src/pcf/pcfutil.c
+src/pcf/pcfutil.h
+src/pcf/README
+src/pcf/rules.mk
+#
+src/gzip/adler32.c
+src/gzip/infblock.c
+src/gzip/infblock.h
+src/gzip/infcodes.c
+src/gzip/infcodes.h
+src/gzip/inffixed.h
+src/gzip/inflate.c
+src/gzip/inftrees.c
+src/gzip/inftrees.h
+src/gzip/infutil.c
+src/gzip/infutil.h
+src/gzip/zconf.h
+src/gzip/zlib.h
+src/gzip/zutil.c
+src/gzip/zutil.h
+#
+src/tools/apinames.c
+src/tools/ftrandom/ftrandom.c
+#
+# EOF
diff --git a/src/third_party/freetype2/src/tools/test_afm.c b/src/third_party/freetype2/src/tools/test_afm.c
new file mode 100644
index 0000000..8de619b
--- /dev/null
+++ b/src/third_party/freetype2/src/tools/test_afm.c
@@ -0,0 +1,157 @@
+/*
+ * gcc -DFT2_BUILD_LIBRARY -I../../include -o test_afm test_afm.c \
+ *     -L../../objs/.libs -lfreetype -lz -static
+ */
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
+
+  void dump_fontinfo( AFM_FontInfo  fi )
+  {
+    FT_UInt  i;
+
+
+    printf( "This AFM is for %sCID font.\n\n",
+            ( fi->IsCIDFont ) ? "" : "non-" );
+
+    printf( "FontBBox: %.2f %.2f %.2f %.2f\n", fi->FontBBox.xMin / 65536.,
+                                               fi->FontBBox.yMin / 65536.,
+                                               fi->FontBBox.xMax / 65536.,
+                                               fi->FontBBox.yMax / 65536. );
+    printf( "Ascender: %.2f\n", fi->Ascender / 65536. );
+    printf( "Descender: %.2f\n\n", fi->Descender / 65536. );
+
+    if ( fi->NumTrackKern )
+      printf( "There are %d sets of track kernings:\n",
+              fi->NumTrackKern );
+    else
+      printf( "There is no track kerning.\n" );
+
+    for ( i = 0; i < fi->NumTrackKern; i++ )
+    {
+      AFM_TrackKern  tk = fi->TrackKerns + i;
+
+
+      printf( "\t%2d: %5.2f %5.2f %5.2f %5.2f\n", tk->degree,
+                                                  tk->min_ptsize / 65536.,
+                                                  tk->min_kern / 65536.,
+                                                  tk->max_ptsize / 65536.,
+                                                  tk->max_kern / 65536. );
+    }
+
+    printf( "\n" );
+
+    if ( fi->NumKernPair )
+      printf( "There are %d kerning pairs:\n",
+              fi->NumKernPair );
+    else
+      printf( "There is no kerning pair.\n" );
+
+    for ( i = 0; i < fi->NumKernPair; i++ )
+    {
+      AFM_KernPair  kp = fi->KernPairs + i;
+
+
+      printf( "\t%3d + %3d => (%4d, %4d)\n", kp->index1,
+                                             kp->index2,
+                                             kp->x,
+                                             kp->y );
+    }
+
+  }
+
+  int
+  dummy_get_index( const char*  name,
+                   FT_Offset    len,
+                   void*        user_data )
+  {
+    if ( len )
+      return name[0];
+    else
+      return 0;
+  }
+
+  FT_Error
+  parse_afm( FT_Library    library,
+             FT_Stream     stream,
+             AFM_FontInfo  fi )
+  {
+    PSAux_Service  psaux;
+    AFM_ParserRec  parser;
+    FT_Error       error = FT_Err_Ok;
+
+
+    psaux = (PSAux_Service)FT_Get_Module_Interface( library, "psaux" );
+    if ( !psaux || !psaux->afm_parser_funcs )
+      return -1;
+
+    error = FT_Stream_EnterFrame( stream, stream->size );
+    if ( error )
+      return error;
+
+    error = psaux->afm_parser_funcs->init( &parser,
+                                           library->memory,
+                                           stream->cursor,
+                                           stream->limit );
+    if ( error )
+      return error;
+
+    parser.FontInfo = fi;
+    parser.get_index = dummy_get_index;
+
+    error = psaux->afm_parser_funcs->parse( &parser );
+
+    psaux->afm_parser_funcs->done( &parser );
+
+    return error;
+  }
+
+
+  int main( int    argc,
+            char** argv )
+  {
+    FT_Library       library;
+    FT_StreamRec     stream;
+    FT_Error         error = FT_Err_Ok;
+    AFM_FontInfoRec  fi;
+
+
+    if ( argc < 2 )
+      return FT_ERR( Invalid_Argument );
+
+    error = FT_Init_FreeType( &library );
+    if ( error )
+      return error;
+
+    FT_ZERO( &stream );
+    error = FT_Stream_Open( &stream, argv[1] );
+    if ( error )
+      goto Exit;
+    stream.memory = library->memory;
+
+    FT_ZERO( &fi );
+    error = parse_afm( library, &stream, &fi );
+
+    if ( !error )
+    {
+      FT_Memory  memory = library->memory;
+
+
+      dump_fontinfo( &fi );
+
+      if ( fi.KernPairs )
+        FT_FREE( fi.KernPairs );
+      if ( fi.TrackKerns )
+        FT_FREE( fi.TrackKerns );
+    }
+    else
+      printf( "parse error\n" );
+
+    FT_Stream_Close( &stream );
+
+  Exit:
+    FT_Done_FreeType( library );
+
+    return error;
+  }
diff --git a/src/third_party/freetype2/src/tools/test_bbox.c b/src/third_party/freetype2/src/tools/test_bbox.c
new file mode 100644
index 0000000..64b82c3
--- /dev/null
+++ b/src/third_party/freetype2/src/tools/test_bbox.c
@@ -0,0 +1,188 @@
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_BBOX_H
+
+
+#include <time.h>    /* for clock() */
+
+/* SunOS 4.1.* does not define CLOCKS_PER_SEC, so include <sys/param.h> */
+/* to get the HZ macro which is the equivalent.                         */
+#if defined(__sun__) && !defined(SVR4) && !defined(__SVR4)
+#include <sys/param.h>
+#define CLOCKS_PER_SEC HZ
+#endif
+
+  static long
+  get_time( void )
+  {
+    return clock() * 10000L / CLOCKS_PER_SEC;
+  }
+
+
+
+
+  /* test bbox computations */
+
+#define  XSCALE    65536
+#define  XX(x)     ((FT_Pos)(x*XSCALE))
+#define  XVEC(x,y)  { XX(x), XX(y) }
+#define  XVAL(x)   ((x)/(1.0*XSCALE))
+
+  /* dummy outline #1 */
+  static FT_Vector  dummy_vec_1[4] =
+  {
+#if 1
+    XVEC( 408.9111, 535.3164 ),
+    XVEC( 455.8887, 634.396  ),
+    XVEC( -37.8765, 786.2207 ),
+    XVEC( 164.6074, 535.3164 )
+#else
+    { (FT_Int32)0x0198E93DL , (FT_Int32)0x021750FFL },  /* 408.9111, 535.3164 */
+    { (FT_Int32)0x01C7E312L , (FT_Int32)0x027A6560L },  /* 455.8887, 634.3960 */
+    { (FT_Int32)0xFFDA1F9EL , (FT_Int32)0x0312387FL },  /* -37.8765, 786.2207 */
+    { (FT_Int32)0x00A49B7EL , (FT_Int32)0x021750FFL }   /* 164.6074, 535.3164 */
+#endif
+   };
+
+  static char  dummy_tag_1[4] =
+  {
+    FT_CURVE_TAG_ON,
+    FT_CURVE_TAG_CUBIC,
+    FT_CURVE_TAG_CUBIC,
+    FT_CURVE_TAG_ON
+  };
+
+  static short  dummy_contour_1[1] =
+  {
+    3
+  };
+
+  static FT_Outline  dummy_outline_1 =
+  {
+    1,
+    4,
+    dummy_vec_1,
+    dummy_tag_1,
+    dummy_contour_1,
+    0
+  };
+
+
+  /* dummy outline #2 */
+  static FT_Vector  dummy_vec_2[4] =
+  {
+    XVEC( 100.0, 100.0 ),
+    XVEC( 100.0, 200.0 ),
+    XVEC( 200.0, 200.0 ),
+    XVEC( 200.0, 133.0 )
+  };
+
+  static FT_Outline  dummy_outline_2 =
+  {
+    1,
+    4,
+    dummy_vec_2,
+    dummy_tag_1,
+    dummy_contour_1,
+    0
+  };
+
+
+  /* dummy outline #3 with bbox of [0 100 128 128] precisely */
+  static FT_Vector  dummy_vec_3[4] =
+  {
+    XVEC( 100.0, 127.0 ),
+    XVEC( 200.0, 127.0 ),
+    XVEC(   0.0, 136.0 ),
+    XVEC(   0.0, 100.0 )
+  };
+
+  static FT_Outline  dummy_outline_3 =
+  {
+    1,
+    4,
+    dummy_vec_3,
+    dummy_tag_1,
+    dummy_contour_1,
+    0
+  };
+
+
+  static void
+  dump_outline( FT_Outline*  outline )
+  {
+    FT_BBox  bbox;
+
+    /* compute and display cbox */
+    FT_Outline_Get_CBox( outline, &bbox );
+    printf( "cbox = [%.2f %.2f %.2f %.2f]\n",
+             XVAL( bbox.xMin ),
+             XVAL( bbox.yMin ),
+             XVAL( bbox.xMax ),
+             XVAL( bbox.yMax ) );
+
+    /* compute and display bbox */
+    FT_Outline_Get_BBox( outline, &bbox );
+    printf( "bbox = [%.2f %.2f %.2f %.2f]\n",
+             XVAL( bbox.xMin ),
+             XVAL( bbox.yMin ),
+             XVAL( bbox.xMax ),
+             XVAL( bbox.yMax ) );
+  }
+
+
+
+  static void
+  profile_outline( FT_Outline*   outline,
+                   long          repeat )
+  {
+    FT_BBox  bbox;
+    long     count;
+    long     time0;
+
+    time0 = get_time();
+    for ( count = repeat; count > 0; count-- )
+      FT_Outline_Get_CBox( outline, &bbox );
+
+    time0 = get_time() - time0;
+    printf( "time = %6.3f cbox = [%8.4f %8.4f %8.4f %8.4f]\n",
+             ((double)time0/10000.0),
+             XVAL( bbox.xMin ),
+             XVAL( bbox.yMin ),
+             XVAL( bbox.xMax ),
+             XVAL( bbox.yMax ) );
+    printf( "cbox_hex = [%08X %08X %08X %08X]\n",
+             bbox.xMin, bbox.yMin, bbox.xMax, bbox.yMax );
+
+
+    time0 = get_time();
+    for ( count = repeat; count > 0; count-- )
+      FT_Outline_Get_BBox( outline, &bbox );
+
+    time0 = get_time() - time0;
+    printf( "time = %6.3f bbox = [%8.4f %8.4f %8.4f %8.4f]\n",
+             ((double)time0/10000.0),
+             XVAL( bbox.xMin ),
+             XVAL( bbox.yMin ),
+             XVAL( bbox.xMax ),
+             XVAL( bbox.yMax ) );
+    printf( "bbox_hex = [%08X %08X %08X %08X]\n",
+             bbox.xMin, bbox.yMin, bbox.xMax, bbox.yMax );
+  }
+
+#define REPEAT  1000000L
+
+  int  main( int  argc, char**  argv )
+  {
+    printf( "outline #1\n" );
+    profile_outline( &dummy_outline_1, REPEAT );
+
+    printf( "outline #2\n" );
+    profile_outline( &dummy_outline_2, REPEAT );
+
+    printf( "outline #3\n" );
+    profile_outline( &dummy_outline_3, REPEAT );
+
+    return 0;
+  }
+
diff --git a/src/third_party/freetype2/src/tools/test_trig.c b/src/third_party/freetype2/src/tools/test_trig.c
new file mode 100644
index 0000000..99ac1cf
--- /dev/null
+++ b/src/third_party/freetype2/src/tools/test_trig.c
@@ -0,0 +1,258 @@
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_TRIGONOMETRY_H
+
+#include <math.h>
+#include <stdio.h>
+
+#define  PI   3.14159265358979323846
+#define  SPI  (PI/FT_ANGLE_PI)
+
+/* the precision in 16.16 fixed-point checks. Expect between 2 and 5 */
+/* noise LSB bits during operations, due to rounding errors..        */
+#define  THRESHOLD  64
+
+  static  error = 0;
+
+  static void
+  test_cos( void )
+  {
+    int  i;
+
+
+    for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000L )
+    {
+      FT_Fixed  f1, f2;
+      double    d2;
+
+
+      f1 = FT_Cos(i);
+      d2 = cos( i*SPI );
+      f2 = (FT_Fixed)(d2*65536.0);
+
+      if ( abs( f2-f1 ) > THRESHOLD )
+      {
+        error = 1;
+        printf( "FT_Cos[%3d] = %.7f  cos[%3d] = %.7f\n",
+                (i >> 16), f1/65536.0, (i >> 16), d2 );
+      }
+    }
+  }
+
+
+  static void
+  test_sin( void )
+  {
+    int  i;
+
+
+    for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000L )
+    {
+      FT_Fixed  f1, f2;
+      double    d2;
+
+
+      f1 = FT_Sin(i);
+      d2 = sin( i*SPI );
+      f2 = (FT_Fixed)(d2*65536.0);
+
+      if ( abs( f2-f1 ) > THRESHOLD )
+      {
+        error = 1;
+        printf( "FT_Sin[%3d] = %.7f  sin[%3d] = %.7f\n",
+                (i >> 16), f1/65536.0, (i >> 16), d2 );
+      }
+    }
+  }
+
+
+  static void
+  test_tan( void )
+  {
+    int  i;
+
+
+    for ( i = 0; i < FT_ANGLE_PI2 - 0x2000000L; i += 0x10000L )
+    {
+      FT_Fixed  f1, f2;
+      double    d2;
+
+
+      f1 = FT_Tan(i);
+      d2 = tan( i*SPI );
+      f2 = (FT_Fixed)(d2*65536.0);
+
+      if ( abs( f2-f1 ) > THRESHOLD )
+      {
+        error = 1;
+        printf( "FT_Tan[%3d] = %.7f  tan[%3d] = %.7f\n",
+                (i >> 16), f1/65536.0, (i >> 16), d2 );
+      }
+    }
+  }
+
+
+  static void
+  test_atan2( void )
+  {
+    int  i;
+
+
+    for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000L )
+    {
+      FT_Fixed  c2, s2;
+      double    l, a, c1, s1;
+      int       j;
+
+
+      l  = 5.0;
+      a  = i*SPI;
+
+      c1 = l * cos(a);
+      s1 = l * sin(a);
+
+      c2 = (FT_Fixed)(c1*65536.0);
+      s2 = (FT_Fixed)(s1*65536.0);
+
+      j  = FT_Atan2( c2, s2 );
+      if ( j < 0 )
+        j += FT_ANGLE_2PI;
+
+      if ( abs( i - j ) > 1 )
+      {
+        printf( "FT_Atan2( %.7f, %.7f ) = %.5f, atan = %.5f\n",
+                c2/65536.0, s2/65536.0, j/65536.0, i/65536.0 );
+      }
+    }
+  }
+
+
+  static void
+  test_unit( void )
+  {
+    int  i;
+
+
+    for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000L )
+    {
+      FT_Vector  v;
+      double     a, c1, s1;
+      FT_Fixed   c2, s2;
+
+
+      FT_Vector_Unit( &v, i );
+      a  = ( i*SPI );
+      c1 = cos(a);
+      s1 = sin(a);
+      c2 = (FT_Fixed)(c1*65536.0);
+      s2 = (FT_Fixed)(s1*65536.0);
+
+      if ( abs( v.x-c2 ) > THRESHOLD ||
+           abs( v.y-s2 ) > THRESHOLD )
+      {
+        error = 1;
+        printf( "FT_Vector_Unit[%3d] = ( %.7f, %.7f )  vec = ( %.7f, %.7f )\n",
+                (i >> 16),
+                v.x/65536.0, v.y/65536.0,
+                c1, s1 );
+      }
+    }
+  }
+
+
+  static void
+  test_length( void )
+  {
+    int  i;
+
+
+    for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000L )
+    {
+      FT_Vector  v;
+      FT_Fixed   l, l2;
+
+
+      l   = (FT_Fixed)(500.0*65536.0);
+      v.x = (FT_Fixed)( l * cos( i*SPI ) );
+      v.y = (FT_Fixed)( l * sin( i*SPI ) );
+      l2  = FT_Vector_Length( &v );
+
+      if ( abs( l2-l ) > THRESHOLD )
+      {
+        error = 1;
+        printf( "FT_Length( %.7f, %.7f ) = %.5f, length = %.5f\n",
+                v.x/65536.0, v.y/65536.0, l2/65536.0, l/65536.0 );
+      }
+    }
+  }
+
+
+  static void
+  test_rotate( void )
+  {
+    int  rotate;
+
+
+    for ( rotate = 0; rotate < FT_ANGLE_2PI; rotate += 0x10000L )
+    {
+      double  ra, cra, sra;
+      int     i;
+
+
+      ra  = rotate*SPI;
+      cra = cos( ra );
+      sra = sin( ra );
+
+      for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000L )
+      {
+        FT_Fixed   c2, s2, c4, s4;
+        FT_Vector  v;
+        double     l, a, c1, s1, c3, s3;
+
+
+        l  = 500.0;
+        a  = i*SPI;
+
+        c1 = l * cos(a);
+        s1 = l * sin(a);
+
+        v.x = c2 = (FT_Fixed)(c1*65536.0);
+        v.y = s2 = (FT_Fixed)(s1*65536.0);
+
+        FT_Vector_Rotate( &v, rotate );
+
+        c3 = c1 * cra - s1 * sra;
+        s3 = c1 * sra + s1 * cra;
+
+        c4 = (FT_Fixed)(c3*65536.0);
+        s4 = (FT_Fixed)(s3*65536.0);
+
+        if ( abs( c4 - v.x ) > THRESHOLD ||
+             abs( s4 - v.y ) > THRESHOLD )
+        {
+          error = 1;
+          printf( "FT_Rotate( (%.7f,%.7f), %.5f ) = ( %.7f, %.7f ), rot = ( %.7f, %.7f )\n",
+                  c1, s1, ra,
+                  c2/65536.0, s2/65536.0,
+                  c4/65536.0, s4/65536.0 );
+        }
+      }
+    }
+  }
+
+
+  int main( void )
+  {
+    test_cos();
+    test_sin();
+    test_tan();
+    test_atan2();
+    test_unit();
+    test_length();
+    test_rotate();
+
+    if (!error)
+      printf( "trigonometry test ok !\n" );
+
+    return !error;
+  }
diff --git a/src/third_party/freetype2/src/tools/update-copyright b/src/third_party/freetype2/src/tools/update-copyright
new file mode 100755
index 0000000..4a8bf9b
--- /dev/null
+++ b/src/third_party/freetype2/src/tools/update-copyright
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+# Run the `update-copyright-year' script on all files in the git repository,
+# taking care of exceptions stored in file `no-copyright'.
+
+topdir=`git rev-parse --show-toplevel`
+toolsdir=$topdir/src/tools
+
+git ls-files --full-name $topdir        \
+| sed 's|^|../../|'                     \
+| grep -vFf $toolsdir/no-copyright      \
+| xargs $toolsdir/update-copyright-year
+
+# EOF
diff --git a/src/third_party/freetype2/src/tools/update-copyright-year b/src/third_party/freetype2/src/tools/update-copyright-year
new file mode 100755
index 0000000..c659bba
--- /dev/null
+++ b/src/third_party/freetype2/src/tools/update-copyright-year
@@ -0,0 +1,138 @@
+eval '(exit $?0)' && eval 'exec perl -wS -i "$0" ${1+"$@"}'
+  & eval 'exec perl -wS -i "$0" $argv:q'
+    if 0;
+
+# Copyright (C) 2015-2020 by
+# Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT.  By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+# [Note: This script is expected to be called by the shell, which in turn
+#  calls perl automatically.  The nifty start-up code above is based on
+#  gnulib's `update-copyright' script; it is a more portable replacement for
+#  the shebang, using the first `perl' program in the shell's path instead.]
+
+# Usage:
+#
+#   update-copyright-year file1 [file2 ...]
+
+
+# This script handles copyright entries like
+#
+#   Copyright  2000   by
+#   foobar
+#
+# or
+#
+#   /* Copyright 2000,  2001, 2004-2007 by    */
+#   /* foobar                                 */
+#
+# and replaces them uniformly with
+#
+#   Copyright 2000-2015
+#   foobar
+#
+# and
+#
+#   /* Copyright 2000-2015 by                 */
+#   /* foobar                                 */
+#
+# (assuming that the current year is 2015).  As can be seen, the line length
+# is retained if there is non-whitespace after the word `by' on the same
+# line.
+
+use strict;
+
+
+my (undef, undef, undef,
+    undef, undef, $year,
+    undef, undef, undef) = localtime(time);
+$year += 1900;
+
+my $replaced = 0;
+
+
+# Loop over all input files; option `-i' (issued at the very beginning of
+# this script) makes perl edit them in-place.
+while (<>)
+{
+  # Only handle the first copyright notice in a file.
+  if (!$replaced)
+  {
+    # First try: Search multiple copyright years.
+    s {
+        (?<begin>.*)
+        Copyright
+        (?<space1>(\ +
+                   | \ +\(C\)\ +))
+        (?<first>[12][0-9][0-9][0-9])
+        (?<middle>.+)
+        (?<last>[12][0-9][0-9][0-9])
+        (?<space2>\ +)
+        by
+        (?<space3>\ *)
+        (?<end>.*)
+      }
+      {
+        # Fill line to the same length (if appropriate); we skip the middle
+        # part but insert `(C)', three spaces, and `-'.
+        my $space = length($+{space1}) - 1
+                    + length($+{middle}) - 1
+                    + length($+{space2}) - 1
+                    + length($+{space3})
+                    - (length("(C)") + 1);
+
+        print "$+{begin}";
+        print "Copyright\ (C)\ $+{first}-$year\ by";
+        print ' ' x $space if length($+{end});
+        print "$+{end}\n";
+        $replaced = 1;
+      }ex
+    ||
+    # Second try: Search a single copyright year.
+    s {
+        (?<begin>.*)
+        Copyright
+        (?<space1>(\ +
+                   | \ +\(C\)\ +))
+        (?<first>[12][0-9][0-9][0-9])
+        (?<space2>\ +)
+        by
+        (?<space3>\ *)
+        (?<end>.*)
+      }
+      {
+        # Fill line to the same length (if appropriate); we insert three
+        # spaces, a `-', and the current year.
+        my $space = length($+{space1}) - 1
+                    + length($+{space2}) - 1
+                    + length($+{space3})
+                    - (length($year) + 1);
+
+        print "$+{begin}";
+        print "Copyright\ (C)\ $+{first}-$year\ by";
+        # If $space is negative this inserts nothing.
+        print ' ' x $space if length($+{end});
+        print "$+{end}\n";
+        $replaced = 1;
+      }ex
+    ||
+    # Otherwise print line unaltered.
+    print;
+  }
+  else
+  {
+    print;
+  }
+}
+continue
+{
+  # Reset $replaced before processing the next file.
+  $replaced = 0 if eof;
+}
+
+# EOF
diff --git a/src/third_party/inspector_protocol/crdtp/json_platform.cc b/src/third_party/inspector_protocol/crdtp/json_platform.cc
index 59c8deb..21e26ce 100644
--- a/src/third_party/inspector_protocol/crdtp/json_platform.cc
+++ b/src/third_party/inspector_protocol/crdtp/json_platform.cc
@@ -16,6 +16,9 @@
 namespace json {
 namespace platform {
 bool StrToD(const char* str, double* result) {
+#if SB_IS(EVERGREEN)
+#error "The std::locale::classic() is not supported for Evergreen. Please use base::StringToDouble()."
+#endif
   std::istringstream is(str);
   is.imbue(std::locale::classic());
   is >> *result;
@@ -23,6 +26,9 @@
 }
 
 std::string DToStr(double value) {
+#if SB_IS(EVERGREEN)
+#error "The std::locale::classic() is not supported for Evergreen. Please use base::NumberToString()."
+#endif
   std::stringstream ss;
   ss.imbue(std::locale::classic());
   ss << value;
diff --git a/src/third_party/libevent/evdns.c b/src/third_party/libevent/evdns.c
index 05fe594..d72fdaa 100644
--- a/src/third_party/libevent/evdns.c
+++ b/src/third_party/libevent/evdns.c
@@ -783,7 +783,6 @@
 
 	for(;;) {
 		u8 label_len;
-		if (j >= length) return -1;
 		GET8(label_len);
 		if (!label_len) break;
 		if (label_len & 0xc0) {
@@ -804,6 +803,7 @@
 			*cp++ = '.';
 		}
 		if (cp + label_len >= end) return -1;
+		if (j + label_len > length) return -1;
 		memcpy(cp, packet + j, label_len);
 		cp += label_len;
 		j += label_len;
diff --git a/src/third_party/libxml/src/entities.c b/src/third_party/libxml/src/entities.c
index ea54cc3..4b41fe9 100644
--- a/src/third_party/libxml/src/entities.c
+++ b/src/third_party/libxml/src/entities.c
@@ -667,11 +667,25 @@
 	    } else {
 		/*
 		 * We assume we have UTF-8 input.
+		 * It must match either:
+		 *   110xxxxx 10xxxxxx
+		 *   1110xxxx 10xxxxxx 10xxxxxx
+		 *   11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+		 * That is:
+		 *   cur[0] is 11xxxxxx
+		 *   cur[1] is 10xxxxxx
+		 *   cur[2] is 10xxxxxx if cur[0] is 111xxxxx
+		 *   cur[3] is 10xxxxxx if cur[0] is 1111xxxx
+		 *   cur[0] is not 11111xxx
 		 */
 		char buf[11], *ptr;
 		int val = 0, l = 1;
 
-		if (*cur < 0xC0) {
+		if (((cur[0] & 0xC0) != 0xC0) ||
+		    ((cur[1] & 0xC0) != 0x80) ||
+		    (((cur[0] & 0xE0) == 0xE0) && ((cur[2] & 0xC0) != 0x80)) ||
+		    (((cur[0] & 0xF0) == 0xF0) && ((cur[3] & 0xC0) != 0x80)) ||
+		    (((cur[0] & 0xF8) == 0xF8))) {
 		    xmlEntitiesErr(XML_CHECK_NOT_UTF8,
 			    "xmlEncodeEntities: input not UTF-8");
 		    if (doc != NULL)
diff --git a/src/third_party/skia/src/sksl/SkSLString.cpp b/src/third_party/skia/src/sksl/SkSLString.cpp
index 433744a..c8124da 100644
--- a/src/third_party/skia/src/sksl/SkSLString.cpp
+++ b/src/third_party/skia/src/sksl/SkSLString.cpp
@@ -15,6 +15,8 @@
 #include <sstream>
 #include <string>
 
+#include "base/strings/string_number_conversions.h"
+
 #if defined(STARBOARD)
 #include "starboard/client_porting/poem/stdio_leaks_poem.h"
 #include "starboard/client_porting/poem/stdlib_poem.h"
@@ -229,6 +231,24 @@
 }
 
 String to_string(double value) {
+#if defined(STARBOARD)
+    std::string s = base::NumberToString(value);
+    bool needsDotZero = true;
+    for (int i = s.size() - 1; i >= 0; --i) {
+        char c = s[i];
+        if (c == '.' || c == 'e') {
+            needsDotZero = false;
+            break;
+        }
+    }
+    if (needsDotZero) {
+        s += ".0";
+    }
+    if (s.size() > 0 && s[0] == '.') {
+      s = "0" + s;
+    }
+    return String(s.c_str());
+#else
     std::stringstream buffer;
     buffer.imbue(std::locale::classic());
     buffer.precision(17);
@@ -246,6 +266,7 @@
         buffer << ".0";
     }
     return String(buffer.str().c_str());
+#endif
 }
 
 SKSL_INT stoi(const String& s) {
@@ -258,6 +279,12 @@
 }
 
 SKSL_FLOAT stod(const String& s) {
+#if defined(STARBOARD)
+    double d;
+    bool res= base::StringToDouble(s.c_str(), &d);
+    SkASSERT(res);
+    return d;
+#else
     double result;
     std::string str(s.c_str(), s.size());
     std::stringstream buffer(str);
@@ -265,6 +292,7 @@
     buffer >> result;
     SkASSERT(!buffer.fail());
     return result;
+#endif
 }
 
 long stol(const String& s) {
diff --git a/src/tools/__init__.py b/src/tools/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/tools/__init__.py
diff --git a/src/tools/download_from_gcs.py b/src/tools/download_from_gcs.py
index fc7c8ee..9c79dfb 100755
--- a/src/tools/download_from_gcs.py
+++ b/src/tools/download_from_gcs.py
@@ -56,7 +56,13 @@
   url = '{}/{}/{}'.format(_BASE_GCS_URL, bucket, sha1)
   context = create_default_context()
 
-  res = urllib.urlopen(url, context=context) if context else urllib.urlopen(url)
+  try:
+    res = urllib.urlopen(url, context=context) if context else urllib.urlopen(url)
+  except urllib.URLError:
+    from ssl import _create_unverified_context
+    context = _create_unverified_context()
+    res = urllib.urlopen(url, context=context) if context else urllib.urlopen(url)
+
   if not res:
     logging.error('Could not reach %s', url)
     return None
diff --git a/src/tools/gyp/pylib/gyp/MSVSVersion.py b/src/tools/gyp/pylib/gyp/MSVSVersion.py
index 0dacf85..cae2c88 100644
--- a/src/tools/gyp/pylib/gyp/MSVSVersion.py
+++ b/src/tools/gyp/pylib/gyp/MSVSVersion.py
@@ -324,10 +324,13 @@
         if not os.path.exists(path):
           path = r'C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE'
       path = _ConvertToCygpath(path)
-      full_path = os.path.join(path, 'devenv.exe')
-      if os.path.exists(full_path) and version in version_to_year:
+      devenv_path = os.path.join(path, 'devenv.exe')
+      devenv_ini_path = os.path.join(path, 'devenv.isolation.ini')
+      if (os.path.exists(devenv_path) or os.path.exists(devenv_ini_path)) and version in version_to_year:
         versions.append(_CreateVersion(version_to_year[version],
             os.path.join(path, '..', '..')))
+      else:
+          print('_DetectVisualStudioVersion() did not find Visual Studio 2017 (v15.0)')
       continue
     # Old method of searching for which VS version is installed
     # We don't use the 2010-encouraged-way because we also want to get the
diff --git a/src/tools/gyp/pylib/gyp/msvs_emulation.py b/src/tools/gyp/pylib/gyp/msvs_emulation.py
index 068ad1e..3951c19 100755
--- a/src/tools/gyp/pylib/gyp/msvs_emulation.py
+++ b/src/tools/gyp/pylib/gyp/msvs_emulation.py
@@ -747,6 +747,7 @@
       'cell_.*',
       'sn_.*',
       'sce_.*',
+      'is_docker',
   )
   env = {}
   for line in output_of_set.splitlines():
diff --git a/src/tools/imagediff/image_diff.cc b/src/tools/imagediff/image_diff.cc
deleted file mode 100644
index 19d5df9..0000000
--- a/src/tools/imagediff/image_diff.cc
+++ /dev/null
@@ -1,382 +0,0 @@
-// Copyright (c) 2011 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.
-
-// This file input format is based loosely on
-// Tools/DumpRenderTree/ImageDiff.m
-
-// The exact format of this tool's output to stdout is important, to match
-// what the run-webkit-tests script expects.
-
-#include <algorithm>
-#include <vector>
-#include <string>
-#include <iostream>
-
-#include "base/basictypes.h"
-#include "base/command_line.h"
-#include "base/file_path.h"
-#include "base/file_util.h"
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/process_util.h"
-#include "base/string_util.h"
-#include "base/utf_string_conversions.h"
-#include "ui/gfx/codec/png_codec.h"
-#include "ui/gfx/size.h"
-
-#if defined(OS_WIN)
-#include "windows.h"
-#endif
-
-// Causes the app to remain open, waiting for pairs of filenames on stdin.
-// The caller is then responsible for terminating this app.
-static const char kOptionPollStdin[] = "use-stdin";
-static const char kOptionGenerateDiff[] = "diff";
-
-// Return codes used by this utility.
-static const int kStatusSame = 0;
-static const int kStatusDifferent = 1;
-static const int kStatusError = 2;
-
-// Color codes.
-static const uint32 RGBA_RED = 0x000000ff;
-static const uint32 RGBA_ALPHA = 0xff000000;
-
-class Image {
- public:
-  Image() : w_(0), h_(0) {
-  }
-
-  Image(const Image& image)
-      : w_(image.w_),
-        h_(image.h_),
-        data_(image.data_) {
-  }
-
-  bool has_image() const {
-    return w_ > 0 && h_ > 0;
-  }
-
-  int w() const {
-    return w_;
-  }
-
-  int h() const {
-    return h_;
-  }
-
-  const unsigned char* data() const {
-    return &data_.front();
-  }
-
-  // Creates the image from stdin with the given data length. On success, it
-  // will return true. On failure, no other methods should be accessed.
-  bool CreateFromStdin(size_t byte_length) {
-    if (byte_length == 0)
-      return false;
-
-    scoped_array<unsigned char> source(new unsigned char[byte_length]);
-    if (fread(source.get(), 1, byte_length, stdin) != byte_length)
-      return false;
-
-    if (!gfx::PNGCodec::Decode(source.get(), byte_length,
-                               gfx::PNGCodec::FORMAT_RGBA,
-                               &data_, &w_, &h_)) {
-      Clear();
-      return false;
-    }
-    return true;
-  }
-
-  // Creates the image from the given filename on disk, and returns true on
-  // success.
-  bool CreateFromFilename(const FilePath& path) {
-    FILE* f = file_util::OpenFile(path, "rb");
-    if (!f)
-      return false;
-
-    std::vector<unsigned char> compressed;
-    const int buf_size = 1024;
-    unsigned char buf[buf_size];
-    size_t num_read = 0;
-    while ((num_read = fread(buf, 1, buf_size, f)) > 0) {
-      compressed.insert(compressed.end(), buf, buf + num_read);
-    }
-
-    file_util::CloseFile(f);
-
-    if (!gfx::PNGCodec::Decode(&compressed[0], compressed.size(),
-                               gfx::PNGCodec::FORMAT_RGBA, &data_, &w_, &h_)) {
-      Clear();
-      return false;
-    }
-    return true;
-  }
-
-  void Clear() {
-    w_ = h_ = 0;
-    data_.clear();
-  }
-
-  // Returns the RGBA value of the pixel at the given location
-  uint32 pixel_at(int x, int y) const {
-    DCHECK(x >= 0 && x < w_);
-    DCHECK(y >= 0 && y < h_);
-    return *reinterpret_cast<const uint32*>(&(data_[(y * w_ + x) * 4]));
-  }
-
-  void set_pixel_at(int x, int y, uint32 color) const {
-    DCHECK(x >= 0 && x < w_);
-    DCHECK(y >= 0 && y < h_);
-    void* addr = &const_cast<unsigned char*>(&data_.front())[(y * w_ + x) * 4];
-    *reinterpret_cast<uint32*>(addr) = color;
-  }
-
- private:
-  // pixel dimensions of the image
-  int w_, h_;
-
-  std::vector<unsigned char> data_;
-};
-
-float PercentageDifferent(const Image& baseline, const Image& actual) {
-  int w = std::min(baseline.w(), actual.w());
-  int h = std::min(baseline.h(), actual.h());
-
-  // compute pixels different in the overlap
-  int pixels_different = 0;
-  for (int y = 0; y < h; y++) {
-    for (int x = 0; x < w; x++) {
-      if (baseline.pixel_at(x, y) != actual.pixel_at(x, y))
-        pixels_different++;
-    }
-  }
-
-  // count pixels that are a difference in size as also being different
-  int max_w = std::max(baseline.w(), actual.w());
-  int max_h = std::max(baseline.h(), actual.h());
-
-  // ...pixels off the right side, but not including the lower right corner
-  pixels_different += (max_w - w) * h;
-
-  // ...pixels along the bottom, including the lower right corner
-  pixels_different += (max_h - h) * max_w;
-
-  // Like the WebKit ImageDiff tool, we define percentage different in terms
-  // of the size of the 'actual' bitmap.
-  float total_pixels = static_cast<float>(actual.w()) *
-                       static_cast<float>(actual.h());
-  if (total_pixels == 0)
-    return 100.0f;  // when the bitmap is empty, they are 100% different
-  return static_cast<float>(pixels_different) / total_pixels * 100;
-}
-
-void PrintHelp() {
-  fprintf(stderr,
-    "Usage:\n"
-    "  image_diff <compare file> <reference file>\n"
-    "    Compares two files on disk, returning 0 when they are the same\n"
-    "  image_diff --use-stdin\n"
-    "    Stays open reading pairs of filenames from stdin, comparing them,\n"
-    "    and sending 0 to stdout when they are the same\n"
-    "  image_diff --diff <compare file> <reference file> <output file>\n"
-    "    Compares two files on disk, outputs an image that visualizes the"
-    "    difference to <output file>\n");
-  /* For unfinished webkit-like-mode (see below)
-    "\n"
-    "  image_diff -s\n"
-    "    Reads stream input from stdin, should be EXACTLY of the format\n"
-    "    \"Content-length: <byte length> <data>Content-length: ...\n"
-    "    it will take as many file pairs as given, and will compare them as\n"
-    "    (cmp_file, reference_file) pairs\n");
-  */
-}
-
-int CompareImages(const FilePath& file1, const FilePath& file2) {
-  Image actual_image;
-  Image baseline_image;
-
-  if (!actual_image.CreateFromFilename(file1)) {
-    fprintf(stderr, "image_diff: Unable to open file \"%" PRFilePath "\"\n",
-            file1.value().c_str());
-    return kStatusError;
-  }
-  if (!baseline_image.CreateFromFilename(file2)) {
-    fprintf(stderr, "image_diff: Unable to open file \"%" PRFilePath "\"\n",
-            file2.value().c_str());
-    return kStatusError;
-  }
-
-  float percent = PercentageDifferent(actual_image, baseline_image);
-  if (percent > 0.0) {
-    // failure: The WebKit version also writes the difference image to
-    // stdout, which seems excessive for our needs.
-    printf("diff: %01.2f%% failed\n", percent);
-    return kStatusDifferent;
-  }
-
-  // success
-  printf("diff: %01.2f%% passed\n", percent);
-  return kStatusSame;
-
-/* Untested mode that acts like WebKit's image comparator. I wrote this but
-   decided it's too complicated. We may use it in the future if it looks useful
-
-  char buffer[2048];
-  while (fgets(buffer, sizeof(buffer), stdin)) {
-
-    if (strncmp("Content-length: ", buffer, 16) == 0) {
-      char* context;
-      strtok_s(buffer, " ", &context);
-      int image_size = strtol(strtok_s(NULL, " ", &context), NULL, 10);
-
-      bool success = false;
-      if (image_size > 0 && actual_image.has_image() == 0) {
-        if (!actual_image.CreateFromStdin(image_size)) {
-          fputs("Error, input image can't be decoded.\n", stderr);
-          return 1;
-        }
-      } else if (image_size > 0 && baseline_image.has_image() == 0) {
-        if (!baseline_image.CreateFromStdin(image_size)) {
-          fputs("Error, baseline image can't be decoded.\n", stderr);
-          return 1;
-        }
-      } else {
-        fputs("Error, image size must be specified.\n", stderr);
-        return 1;
-      }
-    }
-
-    if (actual_image.has_image() && baseline_image.has_image()) {
-      float percent = PercentageDifferent(actual_image, baseline_image);
-      if (percent > 0.0) {
-        // failure: The WebKit version also writes the difference image to
-        // stdout, which seems excessive for our needs.
-        printf("diff: %01.2f%% failed\n", percent);
-      } else {
-        // success
-        printf("diff: %01.2f%% passed\n", percent);
-      }
-      actual_image.Clear();
-      baseline_image.Clear();
-    }
-
-    fflush(stdout);
-  }
-*/
-}
-
-bool CreateImageDiff(const Image& image1, const Image& image2, Image* out) {
-  int w = std::min(image1.w(), image2.w());
-  int h = std::min(image1.h(), image2.h());
-  *out = Image(image1);
-  bool same = (image1.w() == image2.w()) && (image1.h() == image2.h());
-
-  // TODO(estade): do something with the extra pixels if the image sizes
-  // are different.
-  for (int y = 0; y < h; y++) {
-    for (int x = 0; x < w; x++) {
-      uint32 base_pixel = image1.pixel_at(x, y);
-      if (base_pixel != image2.pixel_at(x, y)) {
-        // Set differing pixels red.
-        out->set_pixel_at(x, y, RGBA_RED | RGBA_ALPHA);
-        same = false;
-      } else {
-        // Set same pixels as faded.
-        uint32 alpha = base_pixel & RGBA_ALPHA;
-        uint32 new_pixel = base_pixel - ((alpha / 2) & RGBA_ALPHA);
-        out->set_pixel_at(x, y, new_pixel);
-      }
-    }
-  }
-
-  return same;
-}
-
-int DiffImages(const FilePath& file1, const FilePath& file2,
-               const FilePath& out_file) {
-  Image actual_image;
-  Image baseline_image;
-
-  if (!actual_image.CreateFromFilename(file1)) {
-    fprintf(stderr, "image_diff: Unable to open file \"%" PRFilePath "\"\n",
-            file1.value().c_str());
-    return kStatusError;
-  }
-  if (!baseline_image.CreateFromFilename(file2)) {
-    fprintf(stderr, "image_diff: Unable to open file \"%" PRFilePath "\"\n",
-            file2.value().c_str());
-    return kStatusError;
-  }
-
-  Image diff_image;
-  bool same = CreateImageDiff(baseline_image, actual_image, &diff_image);
-  if (same)
-    return kStatusSame;
-
-  std::vector<unsigned char> png_encoding;
-  gfx::PNGCodec::Encode(diff_image.data(), gfx::PNGCodec::FORMAT_RGBA,
-                        gfx::Size(diff_image.w(), diff_image.h()),
-                        diff_image.w() * 4, false,
-                        std::vector<gfx::PNGCodec::Comment>(), &png_encoding);
-  if (file_util::WriteFile(out_file,
-      reinterpret_cast<char*>(&png_encoding.front()), png_encoding.size()) < 0)
-    return kStatusError;
-
-  return kStatusDifferent;
-}
-
-// It isn't strictly correct to only support ASCII paths, but this
-// program reads paths on stdin and the program that spawns it outputs
-// paths as non-wide strings anyway.
-FilePath FilePathFromASCII(const std::string& str) {
-#if defined(OS_WIN)
-  return FilePath(ASCIIToWide(str));
-#else
-  return FilePath(str);
-#endif
-}
-
-int main(int argc, const char* argv[]) {
-  base::EnableTerminationOnHeapCorruption();
-  CommandLine::Init(argc, argv);
-  const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
-  if (parsed_command_line.HasSwitch(kOptionPollStdin)) {
-    // Watch stdin for filenames.
-    std::string stdin_buffer;
-    FilePath filename1;
-    while (std::getline(std::cin, stdin_buffer)) {
-      if (stdin_buffer.empty())
-        continue;
-
-      if (!filename1.empty()) {
-        // CompareImages writes results to stdout unless an error occurred.
-        FilePath filename2 = FilePathFromASCII(stdin_buffer);
-        if (CompareImages(filename1, filename2) == kStatusError)
-          printf("error\n");
-        fflush(stdout);
-        filename1 = FilePath();
-      } else {
-        // Save the first filename in another buffer and wait for the second
-        // filename to arrive via stdin.
-        filename1 = FilePathFromASCII(stdin_buffer);
-      }
-    }
-    return 0;
-  }
-
-  const CommandLine::StringVector& args = parsed_command_line.GetArgs();
-  if (parsed_command_line.HasSwitch(kOptionGenerateDiff)) {
-    if (args.size() == 3) {
-      return DiffImages(FilePath(args[0]),
-                        FilePath(args[1]),
-                        FilePath(args[2]));
-    }
-  } else if (args.size() == 2) {
-    return CompareImages(FilePath(args[0]), FilePath(args[1]));
-  }
-
-  PrintHelp();
-  return kStatusError;
-}
diff --git a/src/tools/imagediff/image_diff.gyp b/src/tools/imagediff/image_diff.gyp
deleted file mode 100644
index 6d4a620..0000000
--- a/src/tools/imagediff/image_diff.gyp
+++ /dev/null
@@ -1,22 +0,0 @@
-# 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.
-
-{
-  'variables': {
-    'chromium_code': 1,
-  },
-  'targets' : [
-    {
-      'target_name': 'image_diff',
-      'type': 'executable',
-      'dependencies': [
-        '../../base/base.gyp:base',
-        '../../ui/ui.gyp:ui',
-      ],
-      'sources': [
-        'image_diff.cc',
-      ],
-    },
-  ],
-}
diff --git a/src/v8/regress-1051017.js b/src/v8/regress-1051017.js
new file mode 100644
index 0000000..142b563
--- /dev/null
+++ b/src/v8/regress-1051017.js
@@ -0,0 +1,48 @@
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --allow-natives-syntax
+
+
+function foo1() {
+  var x = -Infinity;
+  var i = 0;
+  for (; i < 1; i += x) {
+    if (i == -Infinity) x = +Infinity;
+  }
+  return i;
+}
+
+%PrepareFunctionForOptimization(foo1);
+assertEquals(NaN, foo1());
+assertEquals(NaN, foo1());
+%OptimizeFunctionOnNextCall(foo1);
+assertEquals(NaN, foo1());
+
+
+function foo2() {
+  var i = -Infinity;
+  for (; i <= 42; i += Infinity) { }
+  return i;
+}
+
+%PrepareFunctionForOptimization(foo2);
+assertEquals(NaN, foo2());
+assertEquals(NaN, foo2());
+%OptimizeFunctionOnNextCall(foo2);
+assertEquals(NaN, foo2());
+
+
+function foo3(b) {
+  var k = 0;
+  let str = b ? "42" : "0";
+  for (var i = str; i < 1 && k++ < 1; i -= 0) { }
+  return i;
+}
+
+%PrepareFunctionForOptimization(foo3);
+assertEquals(0, foo3());
+assertEquals(0, foo3());
+%OptimizeFunctionOnNextCall(foo3);
+assertEquals(0, foo3());
diff --git a/src/v8/src/compiler/js-create-lowering.cc b/src/v8/src/compiler/js-create-lowering.cc
index 4e69db6..fe60eaa 100644
--- a/src/v8/src/compiler/js-create-lowering.cc
+++ b/src/v8/src/compiler/js-create-lowering.cc
@@ -667,6 +667,9 @@
         length_type.Max() <= kElementLoopUnrollLimit &&
         length_type.Min() == length_type.Max()) {
       int capacity = static_cast<int>(length_type.Max());
+      // Replace length with a constant in order to protect against a potential
+      // typer bug leading to length > capacity.
+      length = jsgraph()->Constant(capacity);
       return ReduceNewArray(node, length, capacity, *initial_map, elements_kind,
                             allocation, slack_tracking_prediction);
     }
diff --git a/src/v8/src/compiler/typer.cc b/src/v8/src/compiler/typer.cc
index ae1590a..956f0ec 100644
--- a/src/v8/src/compiler/typer.cc
+++ b/src/v8/src/compiler/typer.cc
@@ -211,6 +211,9 @@
 
   Type TypeConstant(Handle<Object> value);
 
+  bool InductionVariablePhiTypeIsPrefixedPoint(
+      InductionVariable* induction_var);
+
  private:
   Typer* typer_;
   LoopVariableOptimizer* induction_vars_;
@@ -251,7 +254,8 @@
   using BinaryTyperFun = Type (*)(Type, Type, Typer* t);
 
   Type TypeUnaryOp(Node* node, UnaryTyperFun);
-  Type TypeBinaryOp(Node* node, BinaryTyperFun);
+  inline Type TypeBinaryOp(Node* node, BinaryTyperFun);
+  inline Type TypeBinaryOp(Type left, Type right, BinaryTyperFun);
 
   static Type BinaryNumberOpTyper(Type lhs, Type rhs, Typer* t,
                                   BinaryTyperFun f);
@@ -299,7 +303,21 @@
   SIMPLIFIED_SPECULATIVE_NUMBER_BINOP_LIST(DECLARE_METHOD)
   SIMPLIFIED_SPECULATIVE_BIGINT_BINOP_LIST(DECLARE_METHOD)
 #undef DECLARE_METHOD
-
+#define DECLARE_METHOD(Name)                       \
+  inline Type Type##Name(Type left, Type right) {  \
+    return TypeBinaryOp(left, right, Name##Typer); \
+  }
+  JS_SIMPLE_BINOP_LIST(DECLARE_METHOD)
+#undef DECLARE_METHOD
+#define DECLARE_METHOD(Name)                      \
+  inline Type Type##Name(Type left, Type right) { \
+    return TypeBinaryOp(left, right, Name);       \
+  }
+  SIMPLIFIED_NUMBER_BINOP_LIST(DECLARE_METHOD)
+  SIMPLIFIED_BIGINT_BINOP_LIST(DECLARE_METHOD)
+  SIMPLIFIED_SPECULATIVE_NUMBER_BINOP_LIST(DECLARE_METHOD)
+  SIMPLIFIED_SPECULATIVE_BIGINT_BINOP_LIST(DECLARE_METHOD)
+#undef DECLARE_METHOD
   static Type ObjectIsArrayBufferView(Type, Typer*);
   static Type ObjectIsBigInt(Type, Typer*);
   static Type ObjectIsCallable(Type, Typer*);
@@ -430,6 +448,14 @@
   graph_reducer.ReduceGraph();
 
   if (induction_vars != nullptr) {
+    // Validate the types computed by TypeInductionVariablePhi.
+    for (auto entry : induction_vars->induction_variables()) {
+      InductionVariable* induction_var = entry.second;
+      if (induction_var->phi()->opcode() == IrOpcode::kInductionVariablePhi) {
+        CHECK(visitor.InductionVariablePhiTypeIsPrefixedPoint(induction_var));
+      }
+    }
+
     induction_vars->ChangeToPhisAndInsertGuards();
   }
 }
@@ -466,6 +492,10 @@
 Type Typer::Visitor::TypeBinaryOp(Node* node, BinaryTyperFun f) {
   Type left = Operand(node, 0);
   Type right = Operand(node, 1);
+  return TypeBinaryOp(left, right, f);
+}
+
+Type Typer::Visitor::TypeBinaryOp(Type left, Type right, BinaryTyperFun f) {
   return left.IsNone() || right.IsNone() ? Type::None()
                                          : f(left, right, typer_);
 }
@@ -843,15 +873,23 @@
   Type initial_type = Operand(node, 0);
   Type increment_type = Operand(node, 2);
 
-  // We only handle integer induction variables (otherwise ranges
-  // do not apply and we cannot do anything).
-  if (!initial_type.Is(typer_->cache_->kInteger) ||
-      !increment_type.Is(typer_->cache_->kInteger)) {
-    // Fallback to normal phi typing, but ensure monotonicity.
-    // (Unfortunately, without baking in the previous type, monotonicity might
-    // be violated because we might not yet have retyped the incrementing
-    // operation even though the increment's type might been already reflected
-    // in the induction variable phi.)
+  // Fallback to normal phi typing in a variety of cases:
+  // - when the induction variable is not initially of type Integer, because we
+  //   want to work with ranges in the algorithm below.
+  // - when the increment is zero, because in that case normal phi typing will
+  //   generally yield a more precise type.
+  // - when the induction variable can become NaN (through addition/subtraction
+  //   of opposing infinities), because the code below can't handle that case.
+  if (initial_type.IsNone() ||
+      increment_type.Is(typer_->cache_->kSingletonZero) ||
+      !initial_type.Is(typer_->cache_->kInteger) ||
+      !increment_type.Is(typer_->cache_->kInteger) ||
+      increment_type.Min() == -V8_INFINITY ||
+      increment_type.Max() == +V8_INFINITY) {
+    // Unfortunately, without baking in the previous type, monotonicity might be
+    // violated because we might not yet have retyped the incrementing operation
+    // even though the increment's type might been already reflected in the
+    // induction variable phi.
     Type type = NodeProperties::IsTyped(node) ? NodeProperties::GetType(node)
                                               : Type::None();
     for (int i = 0; i < arity; ++i) {
@@ -859,18 +897,10 @@
     }
     return type;
   }
-  // If we do not have enough type information for the initial value or
-  // the increment, just return the initial value's type.
-  if (initial_type.IsNone() ||
-      increment_type.Is(typer_->cache_->kSingletonZero)) {
-    return initial_type;
-  }
 
-  // Now process the bounds.
   auto res = induction_vars_->induction_variables().find(node->id());
-  DCHECK(res != induction_vars_->induction_variables().end());
+  DCHECK_NE(res, induction_vars_->induction_variables().end());
   InductionVariable* induction_var = res->second;
-
   InductionVariable::ArithmeticType arithmetic_type = induction_var->Type();
 
   double min = -V8_INFINITY;
@@ -882,13 +912,13 @@
     increment_min = increment_type.Min();
     increment_max = increment_type.Max();
   } else {
-    DCHECK_EQ(InductionVariable::ArithmeticType::kSubtraction, arithmetic_type);
+    DCHECK_EQ(arithmetic_type, InductionVariable::ArithmeticType::kSubtraction);
     increment_min = -increment_type.Max();
     increment_max = -increment_type.Min();
   }
 
   if (increment_min >= 0) {
-    // increasing sequence
+    // Increasing sequence.
     min = initial_type.Min();
     for (auto bound : induction_var->upper_bounds()) {
       Type bound_type = TypeOrNone(bound.bound);
@@ -908,7 +938,7 @@
     // The upper bound must be at least the initial value's upper bound.
     max = std::max(max, initial_type.Max());
   } else if (increment_max <= 0) {
-    // decreasing sequence
+    // Decreasing sequence.
     max = initial_type.Max();
     for (auto bound : induction_var->lower_bounds()) {
       Type bound_type = TypeOrNone(bound.bound);
@@ -928,9 +958,11 @@
     // The lower bound must be at most the initial value's lower bound.
     min = std::min(min, initial_type.Min());
   } else {
-    // Shortcut: If the increment can be both positive and negative,
-    // the variable can go arbitrarily far, so just return integer.
-    return typer_->cache_->kInteger;
+    // If the increment can be both positive and negative, the variable can go
+    // arbitrarily far. Use the maximal range in that case. Note that this may
+    // be less precise than what ordinary typing would produce.
+    min = -V8_INFINITY;
+    max = +V8_INFINITY;
   }
 #ifndef V8_OS_STARBOARD
   if (FLAG_trace_turbo_loop) {
@@ -945,9 +977,68 @@
                    << ")\n";
   }
 #endif
+
   return Type::Range(min, max, typer_->zone());
 }
 
+bool Typer::Visitor::InductionVariablePhiTypeIsPrefixedPoint(
+    InductionVariable* induction_var) {
+  Node* node = induction_var->phi();
+  DCHECK_EQ(node->opcode(), IrOpcode::kInductionVariablePhi);
+  Type type = NodeProperties::GetType(node);
+  Type initial_type = Operand(node, 0);
+  Node* arith = node->InputAt(1);
+  Type increment_type = Operand(node, 2);
+
+  // Intersect {type} with useful bounds.
+  for (auto bound : induction_var->upper_bounds()) {
+    Type bound_type = TypeOrNone(bound.bound);
+    if (!bound_type.Is(typer_->cache_->kInteger)) continue;
+    if (!bound_type.IsNone()) {
+      bound_type = Type::Range(
+          -V8_INFINITY,
+          bound_type.Max() - (bound.kind == InductionVariable::kStrict),
+          zone());
+    }
+    type = Type::Intersect(type, bound_type, typer_->zone());
+  }
+  for (auto bound : induction_var->lower_bounds()) {
+    Type bound_type = TypeOrNone(bound.bound);
+    if (!bound_type.Is(typer_->cache_->kInteger)) continue;
+    if (!bound_type.IsNone()) {
+      bound_type = Type::Range(
+          bound_type.Min() + (bound.kind == InductionVariable::kStrict),
+          +V8_INFINITY, typer_->zone());
+    }
+    type = Type::Intersect(type, bound_type, typer_->zone());
+  }
+
+  // Apply ordinary typing to the "increment" operation.
+  // clang-format off
+  switch (arith->opcode()) {
+#define CASE(x)                             \
+    case IrOpcode::k##x:                    \
+      type = Type##x(type, increment_type); \
+      break;
+    CASE(JSAdd)
+    CASE(JSSubtract)
+    CASE(NumberAdd)
+    CASE(NumberSubtract)
+    CASE(SpeculativeNumberAdd)
+    CASE(SpeculativeNumberSubtract)
+    CASE(SpeculativeSafeIntegerAdd)
+    CASE(SpeculativeSafeIntegerSubtract)
+#undef CASE
+    default:
+      UNREACHABLE();
+  }
+  // clang-format on
+
+  type = Type::Union(initial_type, type, typer_->zone());
+
+  return type.Is(NodeProperties::GetType(node));
+}
+
 Type Typer::Visitor::TypeEffectPhi(Node* node) { UNREACHABLE(); }
 
 Type Typer::Visitor::TypeLoopExit(Node* node) { UNREACHABLE(); }