Import Cobalt 21.lts.4.301658
diff --git a/src/cobalt/CHANGELOG.md b/src/cobalt/CHANGELOG.md
index dc2d88d..9c41986 100644
--- a/src/cobalt/CHANGELOG.md
+++ b/src/cobalt/CHANGELOG.md
@@ -142,6 +142,12 @@
    Platforms can provide javascript code caching by implementing
    CobaltExtensionJavaScriptCacheApi.
 
+
+ - **Added support for UrlFetcher observer.**
+
+   Platforms can implement UrlFetcher observer for performance tracing by
+   implementing CobaltExtensionUrlFetcherObserverApi.
+
 ## Version 20
 
  - **Support for QUIC and SPDY is now enabled.**
diff --git a/src/cobalt/audio/audio_context.cc b/src/cobalt/audio/audio_context.cc
index 6c4808e..4ea6847 100644
--- a/src/cobalt/audio/audio_context.cc
+++ b/src/cobalt/audio/audio_context.cc
@@ -15,6 +15,7 @@
 #include "cobalt/audio/audio_context.h"
 
 #include <memory>
+#include <utility>
 
 #include "base/callback.h"
 #include "cobalt/base/polymorphic_downcast.h"
@@ -109,25 +110,23 @@
 }
 
 void AudioContext::DecodeAudioData(
-    script::EnvironmentSettings* settings,
     const script::Handle<script::ArrayBuffer>& audio_data,
     const DecodeSuccessCallbackArg& success_handler) {
   DCHECK(main_message_loop_->BelongsToCurrentThread());
 
   std::unique_ptr<DecodeCallbackInfo> info(
-      new DecodeCallbackInfo(settings, audio_data, this, success_handler));
+      new DecodeCallbackInfo(audio_data, this, success_handler));
   DecodeAudioDataInternal(std::move(info));
 }
 
 void AudioContext::DecodeAudioData(
-    script::EnvironmentSettings* settings,
     const script::Handle<script::ArrayBuffer>& audio_data,
     const DecodeSuccessCallbackArg& success_handler,
     const DecodeErrorCallbackArg& error_handler) {
   DCHECK(main_message_loop_->BelongsToCurrentThread());
 
-  std::unique_ptr<DecodeCallbackInfo> info(new DecodeCallbackInfo(
-      settings, audio_data, this, success_handler, error_handler));
+  std::unique_ptr<DecodeCallbackInfo> info(
+      new DecodeCallbackInfo(audio_data, this, success_handler, error_handler));
   DecodeAudioDataInternal(std::move(info));
 }
 
@@ -138,14 +137,13 @@
   const int callback_id = next_callback_id_++;
   CHECK(pending_decode_callbacks_.find(callback_id) ==
         pending_decode_callbacks_.end());
-  script::Handle<script::ArrayBuffer> audio_data =
-      script::Handle<script::ArrayBuffer>(info->audio_data_reference);
+  const std::string& audio_data = info->audio_data;
   pending_decode_callbacks_[callback_id] = info.release();
 
   AsyncAudioDecoder::DecodeFinishCallback decode_callback = base::Bind(
       &AudioContext::DecodeFinish, base::Unretained(this), callback_id);
-  audio_decoder_.AsyncDecode(static_cast<const uint8*>(audio_data->Data()),
-                             audio_data->ByteLength(), decode_callback);
+  audio_decoder_.AsyncDecode(reinterpret_cast<const uint8*>(audio_data.data()),
+                             audio_data.size(), decode_callback);
 }
 
 // Success callback and error callback should be scheduled to run on the main
diff --git a/src/cobalt/audio/audio_context.h b/src/cobalt/audio/audio_context.h
index 536b2bd..a334ea0 100644
--- a/src/cobalt/audio/audio_context.h
+++ b/src/cobalt/audio/audio_context.h
@@ -124,11 +124,9 @@
   // ArrayBuffer can, for example, be loaded from an XMLHttpRequest's response
   // attribute after setting the responseType to "arraybuffer". Audio file data
   // can be in any of the formats supported by the audio element.
-  void DecodeAudioData(script::EnvironmentSettings* settings,
-                       const script::Handle<script::ArrayBuffer>& audio_data,
+  void DecodeAudioData(const script::Handle<script::ArrayBuffer>& audio_data,
                        const DecodeSuccessCallbackArg& success_handler);
-  void DecodeAudioData(script::EnvironmentSettings* settings,
-                       const script::Handle<script::ArrayBuffer>& audio_data,
+  void DecodeAudioData(const script::Handle<script::ArrayBuffer>& audio_data,
                        const DecodeSuccessCallbackArg& success_handler,
                        const DecodeErrorCallbackArg& error_handler);
 
@@ -155,27 +153,26 @@
 
  private:
   struct DecodeCallbackInfo {
-    DecodeCallbackInfo(script::EnvironmentSettings* settings,
-                       const script::Handle<script::ArrayBuffer>& data_handle,
+    DecodeCallbackInfo(const script::Handle<script::ArrayBuffer>& data_handle,
                        AudioContext* const audio_context,
                        const DecodeSuccessCallbackArg& success_handler)
-        : env_settings(settings),
-          audio_data_reference(audio_context, data_handle),
+        : audio_data(reinterpret_cast<const char*>(data_handle->Data()),
+                     data_handle->ByteLength()),
           success_callback(audio_context, success_handler) {}
 
-    DecodeCallbackInfo(script::EnvironmentSettings* settings,
-                       const script::Handle<script::ArrayBuffer>& data_handle,
+    DecodeCallbackInfo(const script::Handle<script::ArrayBuffer>& data_handle,
                        AudioContext* const audio_context,
                        const DecodeSuccessCallbackArg& success_handler,
                        const DecodeErrorCallbackArg& error_handler)
-        : env_settings(settings),
-          audio_data_reference(audio_context, data_handle),
+        : audio_data(reinterpret_cast<const char*>(data_handle->Data()),
+                     data_handle->ByteLength()),
           success_callback(audio_context, success_handler) {
       error_callback.emplace(audio_context, error_handler);
     }
 
-    script::EnvironmentSettings* env_settings;
-    script::ScriptValue<script::ArrayBuffer>::Reference audio_data_reference;
+    DecodeCallbackInfo(const DecodeCallbackInfo&) = delete;
+
+    std::string audio_data;
     DecodeSuccessCallbackReference success_callback;
     base::Optional<DecodeErrorCallbackReference> error_callback;
   };
diff --git a/src/cobalt/audio/audio_context.idl b/src/cobalt/audio/audio_context.idl
index f62770a..4069974 100644
--- a/src/cobalt/audio/audio_context.idl
+++ b/src/cobalt/audio/audio_context.idl
@@ -23,9 +23,9 @@
   readonly attribute float sampleRate;
   readonly attribute double currentTime;
 
-  [CallWith=EnvironmentSettings] void decodeAudioData(
-      ArrayBuffer audioData, DecodeSuccessCallback successCallback,
-      optional DecodeErrorCallback errorCallback);
+  void decodeAudioData(ArrayBuffer audioData,
+                       DecodeSuccessCallback successCallback,
+                       optional DecodeErrorCallback errorCallback);
 
   // AudioNode creation
   [CallWith=EnvironmentSettings] AudioBufferSourceNode createBufferSource();
diff --git a/src/cobalt/audio/audio_file_reader_wav.cc b/src/cobalt/audio/audio_file_reader_wav.cc
index f92b8bd..bb9a740 100644
--- a/src/cobalt/audio/audio_file_reader_wav.cc
+++ b/src/cobalt/audio/audio_file_reader_wav.cc
@@ -199,9 +199,12 @@
 #if SB_IS(LITTLE_ENDIAN)
   if ((!is_src_sample_in_float && sample_type_ == kSampleTypeInt16) ||
       (is_src_sample_in_float && sample_type_ == kSampleTypeFloat32)) {
+    SB_LOG(INFO) << "Copying " << size << " bytes of wav data.";
     SbMemoryCopy(audio_bus_->interleaved_data(), data + offset, size);
   } else if (!is_src_sample_in_float && sample_type_ == kSampleTypeFloat32) {
     // Convert from int16 to float32
+    SB_LOG(INFO) << "Converting " << number_of_frames_ * number_of_channels_
+                 << " samples from int16 to float32.";
     const int16* src_samples = reinterpret_cast<const int16*>(data + offset);
     float* dest_samples =
         reinterpret_cast<float*>(audio_bus_->interleaved_data());
@@ -213,6 +216,8 @@
     }
   } else {
     // Convert from float32 to int16
+    SB_LOG(INFO) << "Converting " << number_of_frames_ * number_of_channels_
+                 << " samples from float32 to int16.";
     const float* src_samples = reinterpret_cast<const float*>(data + offset);
     int16* dest_samples =
         reinterpret_cast<int16*>(audio_bus_->interleaved_data());
@@ -225,6 +230,8 @@
   }
 #else   // SB_IS(LITTLE_ENDIAN)
   if (!is_src_sample_in_float && sample_type_ == kSampleTypeInt16) {
+    SB_LOG(INFO) << "Converting " << number_of_frames_ * number_of_channels_
+                 << " int16 samples from little endian to big endian.";
     const uint8_t* src_samples = data + offset;
     int16* dest_samples =
         reinterpret_cast<int16*>(audio_bus_->interleaved_data());
@@ -234,6 +241,8 @@
       ++dest_samples;
     }
   } else if (is_src_sample_in_float && sample_type_ == kSampleTypeFloat32) {
+    SB_LOG(INFO) << "Converting " << number_of_frames_ * number_of_channels_
+                 << " float32 samples from little endian to big endian.";
     const uint8_t* src_samples = data + offset;
     float* dest_samples =
         reinterpret_cast<float*>(audio_bus_->interleaved_data());
@@ -245,6 +254,8 @@
     }
   } else if (!is_src_sample_in_float && sample_type_ == kSampleTypeFloat32) {
     // Convert from int16 to float32
+    SB_LOG(INFO) << "Converting " << number_of_frames_ * number_of_channels_
+                 << " int16 samples in little endian to float32 in big endian.";
     const uint8_t* src_samples = data + offset;
     float* dest_samples =
         reinterpret_cast<float*>(audio_bus_->interleaved_data());
@@ -256,6 +267,8 @@
     }
   } else {
     // Convert from float32 to int16
+    SB_LOG(INFO) << "Converting " << number_of_frames_ * number_of_channels_
+                 << " float32 samples in little endian to int16 in big endian.";
     const uint8_t* src_samples = data + offset;
     int16* dest_samples =
         reinterpret_cast<int16*>(audio_bus_->interleaved_data());
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index 189d70d..ef7e632 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-301329
\ No newline at end of file
+301658
\ No newline at end of file
diff --git a/src/cobalt/build/cobalt_configuration.py b/src/cobalt/build/cobalt_configuration.py
index 4815b88..6d70969 100644
--- a/src/cobalt/build/cobalt_configuration.py
+++ b/src/cobalt/build/cobalt_configuration.py
@@ -46,10 +46,17 @@
     except (ValueError, TypeError):
       use_fastbuild = 0
 
+    try:
+      build_in_docker = int(os.environ.get('IS_DOCKER', 0))
+    except (ValueError, TypeError):
+      build_in_docker = 0
+
     variables = {
         # This is used to omit large debuginfo in files on CI environment
         'cobalt_fastbuild': use_fastbuild,
 
+        'cobalt_docker_build': build_in_docker,
+
         # This is here rather than cobalt_configuration.gypi so that it's
         # available for browser_bindings_gen.gyp.
         'enable_debugger': 0 if config_name == Config.GOLD else 1,
diff --git a/src/cobalt/content/fonts/config/empty/fonts.xml b/src/cobalt/content/fonts/config/empty/fonts.xml
new file mode 100644
index 0000000..62a2891
--- /dev/null
+++ b/src/cobalt/content/fonts/config/empty/fonts.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" ?>
+<familyset version="1">
+</familyset>
diff --git a/src/cobalt/content/fonts/fonts.gyp b/src/cobalt/content/fonts/fonts.gyp
index a47c417..edf844e 100644
--- a/src/cobalt/content/fonts/fonts.gyp
+++ b/src/cobalt/content/fonts/fonts.gyp
@@ -85,6 +85,22 @@
             'package_fallback_symbols': 0,
           }],
 
+          [ 'cobalt_font_package == "empty"', {
+            'source_font_config_dir': 'config/empty',
+
+            'package_named_sans_serif': 0,
+            'package_named_serif': 0,
+            'package_named_fcc_fonts': 0,
+            'package_fallback_lang_non_cjk': 0,
+            'package_fallback_lang_cjk': 0,
+            'package_fallback_lang_cjk_low_quality': 0,
+            'package_fallback_historic': 0,
+            'package_fallback_color_emoji': 0,
+            'package_fallback_emoji': 0,
+            'package_fallback_symbols': 0,
+          }],
+
+
           [ 'cobalt_font_package == "android_system"', {
             # fonts.xml contains a superset of what we expect to find on Android
             # devices. The Android SbFile implementation falls back to system font
@@ -140,37 +156,47 @@
         ],
       },
 
-      'actions': [
-        {
-          'action_name': 'fonts_xml',
-          'inputs': [
-              'scripts/filter_fonts.py',
-              '<(source_font_config_dir)/fonts.xml',
-          ],
-          'outputs': [
-            '<(sb_static_contents_output_data_dir)/fonts/fonts.xml',
-          ],
-          'action': [
-            'python2', 'scripts/filter_fonts.py',
-            '-i', '<(source_font_config_dir)/fonts.xml',
-            '-o', '<(sb_static_contents_output_data_dir)/fonts/fonts.xml',
-            '<@(package_categories)',
-          ],
-        },
-      ],
       'conditions': [
-        [ 'copy_font_files == 0', {
+        [ 'cobalt_font_package == "empty"', {
           'copies': [{
-            # Copy at least the fallback Roboto Subsetted font.
-            'files': [ '<(source_font_files_dir)/Roboto-Regular-Subsetted.woff2' ],
+            'files': [ 'config/empty/fonts.xml' ],
             'destination': '<(sb_static_contents_output_data_dir)/fonts/',
           }],
         }, {
-          'copies': [{
-            # Late expansion so <@(package_categories) is resolved.
-            'files': [ '>!@pymod_do_main(cobalt.content.fonts.scripts.filter_fonts -i <(source_font_config_dir)/fonts.xml -f <(source_font_files_dir) <@(package_categories))' ],
-            'destination': '<(sb_static_contents_output_data_dir)/fonts/',
-          }],
+          'actions': [
+            {
+              'action_name': 'fonts_xml',
+              'inputs': [
+                  'scripts/filter_fonts.py',
+                  '<(source_font_config_dir)/fonts.xml',
+              ],
+              'outputs': [
+                '<(sb_static_contents_output_data_dir)/fonts/fonts.xml',
+              ],
+              'action': [
+                'python2', 'scripts/filter_fonts.py',
+                '-i', '<(source_font_config_dir)/fonts.xml',
+                '-o', '<(sb_static_contents_output_data_dir)/fonts/fonts.xml',
+                '<@(package_categories)',
+              ],
+            },
+          ],
+
+          'conditions': [
+            [ 'copy_font_files == 0', {
+              'copies': [{
+                # Copy at least the fallback Roboto Subsetted font.
+                'files': [ '<(source_font_files_dir)/Roboto-Regular-Subsetted.woff2' ],
+                'destination': '<(sb_static_contents_output_data_dir)/fonts/',
+              }],
+            }, {
+              'copies': [{
+                # Late expansion so <@(package_categories) is resolved.
+                'files': [ '>!@pymod_do_main(cobalt.content.fonts.scripts.filter_fonts -i <(source_font_config_dir)/fonts.xml -f <(source_font_files_dir) <@(package_categories))' ],
+                'destination': '<(sb_static_contents_output_data_dir)/fonts/',
+              }],
+            }],
+          ],
         }],
       ],
 
diff --git a/src/cobalt/cssom/viewport_size.h b/src/cobalt/cssom/viewport_size.h
index 040d414..84de9bd 100644
--- a/src/cobalt/cssom/viewport_size.h
+++ b/src/cobalt/cssom/viewport_size.h
@@ -53,7 +53,7 @@
 
   // Ratio of CSS pixels per device pixel, matching the devicePixelRatio
   // attribute.
-  //   https://www.w3.org/TR/cssom-view-1/#dom-window-devicepixelratio
+  //   https://www.w3.org/TR/2016/WD-cssom-view-1-20160317/#dom-window-devicepixelratio
   float device_pixel_ratio_ = 1.0f;
 };
 
diff --git a/src/cobalt/extension/extension_test.cc b/src/cobalt/extension/extension_test.cc
index 989ccac..d57b5ed 100644
--- a/src/cobalt/extension/extension_test.cc
+++ b/src/cobalt/extension/extension_test.cc
@@ -21,6 +21,7 @@
 #include "cobalt/extension/installation_manager.h"
 #include "cobalt/extension/javascript_cache.h"
 #include "cobalt/extension/platform_service.h"
+#include "cobalt/extension/url_fetcher_observer.h"
 #include "starboard/system.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -243,6 +244,28 @@
       << "Extension struct should be a singleton";
 }
 
+TEST(ExtensionTest, UrlFetcherObserver) {
+  typedef CobaltExtensionUrlFetcherObserverApi ExtensionApi;
+  const char* kExtensionName = kCobaltExtensionUrlFetcherObserverName;
+
+  const ExtensionApi* extension_api =
+      static_cast<const ExtensionApi*>(SbSystemGetExtension(kExtensionName));
+  if (!extension_api) {
+    return;
+  }
+
+  EXPECT_STREQ(extension_api->name, kExtensionName);
+  EXPECT_EQ(extension_api->version, 1u);
+  EXPECT_NE(extension_api->FetcherCreated, nullptr);
+  EXPECT_NE(extension_api->FetcherDestroyed, nullptr);
+  EXPECT_NE(extension_api->StartURLRequest, nullptr);
+
+  const ExtensionApi* second_extension_api =
+      static_cast<const ExtensionApi*>(SbSystemGetExtension(kExtensionName));
+  EXPECT_EQ(second_extension_api, extension_api)
+      << "Extension struct should be a singleton";
+}
+
 }  // namespace extension
 }  // namespace cobalt
 #endif  // SB_API_VERSION >= 11
diff --git a/src/cobalt/extension/url_fetcher_observer.h b/src/cobalt/extension/url_fetcher_observer.h
new file mode 100644
index 0000000..7795689
--- /dev/null
+++ b/src/cobalt/extension/url_fetcher_observer.h
@@ -0,0 +1,51 @@
+// 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.
+
+#ifndef COBALT_EXTENSION_URL_FETCHER_OBSERVER_H_
+#define COBALT_EXTENSION_URL_FETCHER_OBSERVER_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define kCobaltExtensionUrlFetcherObserverName \
+  "dev.cobalt.extension.UrlFetcherObserver"
+
+typedef struct CobaltExtensionUrlFetcherObserverApi {
+  // Name should be the string |kCobaltExtensionUrlFetcherObserverName|.
+  // This helps to validate that the extension API is correct.
+  const char* name;
+
+  // This specifies the version of the API that is implemented.
+  uint32_t version;
+
+  // The fields below this point were added in version 1 or later.
+
+  // The UrlFetcher for the specified |url| was created.
+  void (*FetcherCreated)(const char* url);
+
+  // The UrlFetcher for the specified |url| was destroyed.
+  void (*FetcherDestroyed)(const char* url);
+
+  // The URL request started for the specified |url|.
+  void (*StartURLRequest)(const char* url);
+} CobaltExtensionUrlFetcherObserverApi;
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // COBALT_EXTENSION_URL_FETCHER_OBSERVER_H_
diff --git a/src/cobalt/media/base/sbplayer_pipeline.cc b/src/cobalt/media/base/sbplayer_pipeline.cc
index 3f5b78d..92979dd 100644
--- a/src/cobalt/media/base/sbplayer_pipeline.cc
+++ b/src/cobalt/media/base/sbplayer_pipeline.cc
@@ -937,30 +937,33 @@
         set_bounds_helper_.get(), allow_resume_after_suspend_,
         *decode_to_texture_output_mode_, video_frame_provider_,
         max_video_capabilities_));
+
+    if (!player_->IsValid()) {
+      player_.reset();
+      DLOG(ERROR) << "SbPlayerPipeline::CreatePlayer failed: "
+                     "player_->IsValid() is false.";
+      CallSeekCB(DECODER_ERROR_NOT_SUPPORTED);
+      return;
+    }
+
     SetPlaybackRateTask(playback_rate_);
     SetVolumeTask(volume_);
   }
 
-  if (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 (video_stream_) {
-      UpdateDecoderConfig(video_stream_);
-    }
-    return;
+  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();
 
-  player_.reset();
-  CallSeekCB(DECODER_ERROR_NOT_SUPPORTED);
+  if (audio_stream_) {
+    UpdateDecoderConfig(audio_stream_);
+  }
+  if (video_stream_) {
+    UpdateDecoderConfig(video_stream_);
+  }
 }
 
 void SbPlayerPipeline::OnDemuxerInitialized(PipelineStatus status) {
@@ -1349,6 +1352,13 @@
 
   if (player_) {
     player_->Resume();
+    if (!player_->IsValid()) {
+      player_.reset();
+      DLOG(ERROR) << "SbPlayerPipeline::ResumeTask failed: "
+                     "player_->IsValid() is false.";
+      CallSeekCB(DECODER_ERROR_NOT_SUPPORTED);
+      return;
+    }
   }
 
   suspended_ = false;
diff --git a/src/cobalt/media/base/starboard_player.cc b/src/cobalt/media/base/starboard_player.cc
index 7014fd0..28d9896 100644
--- a/src/cobalt/media/base/starboard_player.cc
+++ b/src/cobalt/media/base/starboard_player.cc
@@ -165,10 +165,12 @@
 
   CreatePlayer();
 
-  task_runner->PostTask(
-      FROM_HERE,
-      base::Bind(&StarboardPlayer::CallbackHelper::ClearDecoderBufferCache,
-                 callback_helper_));
+  if (SbPlayerIsValid(player_)) {
+    task_runner->PostTask(
+        FROM_HERE,
+        base::Bind(&StarboardPlayer::CallbackHelper::ClearDecoderBufferCache,
+                   callback_helper_));
+  }
 }
 
 StarboardPlayer::~StarboardPlayer() {
@@ -479,9 +481,11 @@
   CreatePlayer();
 #endif  // SB_HAS(PLAYER_WITH_URL)
 
-  base::AutoLock auto_lock(lock_);
-  state_ = kResuming;
-  UpdateBounds_Locked();
+  if (SbPlayerIsValid(player_)) {
+    base::AutoLock auto_lock(lock_);
+    state_ = kResuming;
+    UpdateBounds_Locked();
+  }
 }
 
 namespace {
@@ -591,7 +595,9 @@
 
 #endif  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
 
-  DCHECK(SbPlayerIsValid(player_));
+  if (!SbPlayerIsValid(player_)) {
+    return;
+  }
 
   if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) {
     // If the player is setup to decode to texture, then provide Cobalt with
@@ -944,7 +950,7 @@
   auto output_mode = SbPlayerGetPreferredOutputMode(&creation_param);
   CHECK_NE(kSbPlayerOutputModeInvalid, output_mode);
   return output_mode;
-#else  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
+#else   // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
   SbMediaVideoCodec video_codec = kSbMediaVideoCodecNone;
 
 #if SB_API_VERSION >= 11
diff --git a/src/cobalt/script/v8c/v8c_global_environment.cc b/src/cobalt/script/v8c/v8c_global_environment.cc
index 044ea64..52c2d6e 100644
--- a/src/cobalt/script/v8c/v8c_global_environment.cc
+++ b/src/cobalt/script/v8c/v8c_global_environment.cc
@@ -244,11 +244,7 @@
 }
 
 void V8cGlobalEnvironment::RemoveRoot(Traceable* traceable) {
-  CHECK(isolate_);
-  V8cEngine* v8c_engine = V8cEngine::GetFromIsolate(isolate_);
-  CHECK(v8c_engine);
-  CHECK(v8c_engine->heap_tracer());
-  v8c_engine->heap_tracer()->RemoveRoot(traceable);
+  V8cEngine::GetFromIsolate(isolate_)->heap_tracer()->RemoveRoot(traceable);
 }
 
 void V8cGlobalEnvironment::PreventGarbageCollection(
diff --git a/src/net/url_request/url_fetcher_core.cc b/src/net/url_request/url_fetcher_core.cc
index 48c99c0..2979a78 100644
--- a/src/net/url_request/url_fetcher_core.cc
+++ b/src/net/url_request/url_fetcher_core.cc
@@ -137,6 +137,23 @@
       total_response_bytes_(-1),
       traffic_annotation_(traffic_annotation) {
   CHECK(original_url_.is_valid());
+
+#if SB_API_VERSION >= 11
+  const CobaltExtensionUrlFetcherObserverApi* observer_extension =
+      static_cast<const CobaltExtensionUrlFetcherObserverApi*>(
+          SbSystemGetExtension(kCobaltExtensionUrlFetcherObserverName));
+  if (observer_extension &&
+      SbStringCompareAll(observer_extension->name,
+                         kCobaltExtensionUrlFetcherObserverName) == 0 &&
+      observer_extension->version >= 1) {
+    observer_extension_ = observer_extension;
+    observer_extension_->FetcherCreated(original_url_.spec().c_str());
+  } else {
+    observer_extension_ = nullptr;
+  }
+#else
+  observer_extension_ = nullptr;
+#endif
 }
 
 void URLFetcherCore::Start() {
@@ -598,6 +615,9 @@
 }
 
 URLFetcherCore::~URLFetcherCore() {
+  if (observer_extension_ != nullptr) {
+    observer_extension_->FetcherDestroyed(original_url_.spec().c_str());
+  }
   // |request_| should be NULL. If not, it's unsafe to delete it here since we
   // may not be on the IO thread.
   DCHECK(!request_.get());
@@ -626,6 +646,9 @@
 void URLFetcherCore::StartURLRequest() {
   DCHECK(network_task_runner_->BelongsToCurrentThread());
 
+  if (observer_extension_ != nullptr) {
+    observer_extension_->StartURLRequest(original_url_.spec().c_str());
+  }
   if (was_cancelled_) {
     // Since StartURLRequest() is posted as a *delayed* task, it may
     // run after the URLFetcher was already stopped.
diff --git a/src/net/url_request/url_fetcher_core.h b/src/net/url_request/url_fetcher_core.h
index 3a18e18..5ea695c 100644
--- a/src/net/url_request/url_fetcher_core.h
+++ b/src/net/url_request/url_fetcher_core.h
@@ -15,6 +15,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/timer/timer.h"
+#include "cobalt/extension/url_fetcher_observer.h"
 #include "net/base/chunked_upload_data_stream.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/proxy_server.h"
@@ -370,6 +371,8 @@
 
   static base::LazyInstance<Registry>::DestructorAtExit g_registry;
 
+  const CobaltExtensionUrlFetcherObserverApi* observer_extension_;
+
   DISALLOW_COPY_AND_ASSIGN(URLFetcherCore);
 };
 
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
index fc4907a..e580c5c 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
@@ -18,6 +18,7 @@
 import static android.media.AudioManager.GET_DEVICES_INPUTS;
 import static dev.cobalt.util.Log.TAG;
 
+import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
@@ -33,6 +34,7 @@
 import android.view.Display;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.CaptioningManager;
+import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import dev.cobalt.account.UserAuthorizer;
 import dev.cobalt.libraries.services.clientloginfo.ClientLogInfo;
@@ -320,6 +322,43 @@
     return DisplayUtil.getSystemDisplaySize();
   }
 
+  @Nullable
+  private static String getSystemProperty(String name) {
+    try {
+      @SuppressLint("PrivateApi")
+      Class<?> systemProperties = Class.forName("android.os.SystemProperties");
+      Method getMethod = systemProperties.getMethod("get", String.class);
+      return (String) getMethod.invoke(systemProperties, name);
+    } catch (Exception e) {
+      Log.e(TAG, "Failed to read system property " + name, e);
+      return null;
+    }
+  }
+
+  @SuppressWarnings("unused")
+  @UsedByNative
+  Size getDeviceResolution() {
+    String displaySize =
+        android.os.Build.VERSION.SDK_INT < 28
+            ? getSystemProperty("sys.display-size")
+            : getSystemProperty("vendor.display-size");
+
+    if (displaySize == null) {
+      return getDisplaySize();
+    }
+
+    String[] sizes = displaySize.split("x");
+    if (sizes.length != 2) {
+      return getDisplaySize();
+    }
+
+    try {
+      return new Size(Integer.parseInt(sizes[0]), Integer.parseInt(sizes[1]));
+    } catch (NumberFormatException e) {
+      return getDisplaySize();
+    }
+  }
+
   /**
    * Checks if there is no microphone connected to the system.
    *
diff --git a/src/starboard/android/apk/build.id b/src/starboard/android/apk/build.id
deleted file mode 100644
index 13984bc..0000000
--- a/src/starboard/android/apk/build.id
+++ /dev/null
@@ -1 +0,0 @@
-301279
\ No newline at end of file
diff --git a/src/starboard/android/shared/window_get_size.cc b/src/starboard/android/shared/window_get_size.cc
index 05bcced..a6182c4 100644
--- a/src/starboard/android/shared/window_get_size.cc
+++ b/src/starboard/android/shared/window_get_size.cc
@@ -34,8 +34,8 @@
 
   JniEnvExt* env = JniEnvExt::Get();
   ScopedLocalJavaRef<jobject> display_size(
-      env->CallStarboardObjectMethodOrAbort("getDisplaySize",
-                                           "()Landroid/util/Size;"));
+      env->CallStarboardObjectMethodOrAbort("getDeviceResolution",
+                                            "()Landroid/util/Size;"));
   int display_width =
       env->CallIntMethodOrAbort(display_size.Get(), "getWidth", "()I");
   int display_height =
@@ -43,9 +43,9 @@
 
   // In the off chance we have non-square pixels, use the max ratio so the
   // highest quality video suitable to the device gets selected.
-  size->video_pixel_ratio = std::max(
-      static_cast<float>(display_width) / size->width,
-      static_cast<float>(display_height) / size->height);
+  size->video_pixel_ratio =
+      std::max(static_cast<float>(display_width) / size->width,
+               static_cast<float>(display_height) / size->height);
 
   return true;
 }
diff --git a/src/starboard/build/platform_configuration.py b/src/starboard/build/platform_configuration.py
index ebb054e..eaf4088 100644
--- a/src/starboard/build/platform_configuration.py
+++ b/src/starboard/build/platform_configuration.py
@@ -22,7 +22,7 @@
 from starboard.build.application_configuration import ApplicationConfiguration
 from starboard.optional import get_optional_tests
 from starboard.sabi import sabi
-from starboard.tools import ccache
+from starboard.tools import cache
 from starboard.tools import environment
 from starboard.tools import paths
 from starboard.tools import platform
@@ -70,15 +70,19 @@
       self._directory = os.path.realpath(os.path.dirname(__file__))
     self._application_configuration = None
     self._application_configuration_search_path = [self._directory]
+    # Default build accelerator is ccache.
+    self.build_accelerator = self.GetBuildAccelerator(cache.Accelerator.CCACHE)
 
-    # Specifies the build accelerator to be used. Default is ccache.
-    build_accelerator = ccache.Ccache()
+  def GetBuildAccelerator(self, accelerator):
+    """Returns the build accelerator name."""
+    build_accelerator = cache.Cache(accelerator)
+    name = build_accelerator.GetName()
     if build_accelerator.Use():
-      self.build_accelerator = build_accelerator.GetName()
-      logging.info('Using %sbuild accelerator.', self.build_accelerator)
+      logging.info('Using %s build accelerator.', name)
+      return name
     else:
-      self.build_accelerator = ''
-      logging.info('Not using a build accelerator.')
+      logging.info('Not using %s build accelerator.', name)
+      return ''
 
   def GetBuildFormat(self):
     """Returns the desired build format."""
@@ -354,7 +358,8 @@
     raise NotImplementedError()
 
   def GetPathToSabiJsonFile(self):
-    """Gets the path to the JSON file with Starboard ABI information for the build.
+    """Gets the path to the JSON file with Starboard ABI information for the
+       build.
 
     Examples:
         'starboard/sabi/arm64/sabi-v12.json'
diff --git a/src/starboard/evergreen/shared/gyp_configuration.gypi b/src/starboard/evergreen/shared/gyp_configuration.gypi
index 40f4071..7cd89b5 100644
--- a/src/starboard/evergreen/shared/gyp_configuration.gypi
+++ b/src/starboard/evergreen/shared/gyp_configuration.gypi
@@ -25,7 +25,7 @@
     'javascript_engine': 'v8',
     'cobalt_v8_enable_embedded_builtins': 1,
 
-    'cobalt_font_package': 'minimal',
+    'cobalt_font_package': 'empty',
 
     # Override that omits the "data" subdirectory.
     # TODO: Remove when omitted for all platforms in base_configuration.gypi.
diff --git a/src/starboard/nplb/memory_reporter_test.cc b/src/starboard/nplb/memory_reporter_test.cc
index e327035..6dde02b 100644
--- a/src/starboard/nplb/memory_reporter_test.cc
+++ b/src/starboard/nplb/memory_reporter_test.cc
@@ -355,6 +355,9 @@
   SbMemoryUnmap(mem_chunk, kMemSize);
   EXPECT_EQ_NO_TRACKING(mem_chunk, mem_reporter()->last_mem_unmap());
   EXPECT_EQ_NO_TRACKING(0, mem_reporter()->number_map_mem());
+  // On some platforms, bookkeeping for memory mapping can cost allocations.
+  // Call Clear() explicitly before TearDown() checks number_allocs_;
+  mem_reporter()->Clear();
 }
 #endif  // SB_API_VERSION >= 12 || SB_HAS(MMAP)
 
diff --git a/src/starboard/tools/cache.py b/src/starboard/tools/cache.py
new file mode 100644
index 0000000..4839ffc
--- /dev/null
+++ b/src/starboard/tools/cache.py
@@ -0,0 +1,64 @@
+#
+# Copyright 2020 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.
+#
+"""Cache specific definitions and helper functions.
+
+Override settings to enable/disable cache using environment variables. Default
+is None.
+"""
+
+import logging
+import os
+
+from starboard.tools import build_accelerator
+from starboard.tools import util
+
+
+class Accelerator:
+  CCACHE = ('ccache', 'USE_CCACHE')
+  # Windows compatible ccache.
+  SCCACHE = ('sccache.exe', 'USE_SCCACHE')
+
+
+class Cache(build_accelerator.BuildAccelerator):
+  """Cache based build accelerator."""
+
+  def __init__(self, accelerator):
+    self.binary, self.env_var = accelerator
+
+  def GetName(self):
+    return self.binary
+
+  def Use(self):
+    return self.EnvOverride() and self.BinaryInstalled()
+
+  def EnvOverride(self):
+    """Checks env_var to enable/disable cache.
+
+    Returns:
+      env_var boolean if exists, defaults to True.
+    """
+    if self.env_var in os.environ:
+      return os.environ[self.env_var] == '1'
+    return True
+
+  def BinaryInstalled(self):
+    """Returns True if binary is installed, otherwise is False."""
+    binary_path = util.Which(self.binary)
+    if binary_path is not None:
+      logging.info('Using %s installed at: %s', self.binary, binary_path)
+      return True
+    logging.info('Unable to find %s.', self.binary)
+    return False
diff --git a/src/starboard/tools/ccache.py b/src/starboard/tools/ccache.py
deleted file mode 100644
index 5f1928c..0000000
--- a/src/starboard/tools/ccache.py
+++ /dev/null
@@ -1,57 +0,0 @@
-#
-# Copyright 2020 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.
-#
-"""Ccache specific definitions and helper functions.
-
-Override settings using environment variables.
-USE_CCACHE  To enable/disable ccache. Default is None.
-"""
-
-import logging
-import os
-import util
-
-from starboard.tools import build_accelerator
-
-
-class Ccache(build_accelerator.BuildAccelerator):
-  """Ccache is a cache based build accelerator."""
-
-  def GetName(self):
-    return 'ccache'
-
-  def Use(self):
-    return CcacheEnvOverride() and CcacheInstalled()
-
-
-def CcacheEnvOverride():
-  """Checks USE_CCACHE to enable/disable ccache.
-
-  Returns:
-    USE_CCACHE boolean if exists, defaults to True.
-  """
-  if 'USE_CCACHE' in os.environ:
-    return os.environ['USE_CCACHE'] == '1'
-  return True
-
-
-def CcacheInstalled():
-  """Returns True if ccache is installed, otherwise is False."""
-  ccache_path = util.Which('ccache')
-  if ccache_path is not None:
-    logging.info('Using ccache installed at: %s', ccache_path)
-    return True
-  logging.error('Unable to find ccache.')
-  return False
diff --git a/src/starboard/tools/toolchain/msvc.py b/src/starboard/tools/toolchain/msvc.py
index 401ba5b..3d43367 100644
--- a/src/starboard/tools/toolchain/msvc.py
+++ b/src/starboard/tools/toolchain/msvc.py
@@ -50,24 +50,21 @@
     return 'msvc'
 
   def GetRspFilePath(self):
-    return '$out.rsp'
+    return None
 
   def GetRspFileContent(self):
-    return self._command_flags
+    return None
 
   def GetCommand(self, path, extra_flags, flags, shell):
     del extra_flags  # Not used.
     del shell  # Not used.
-    self._command_flags = flags
-    return ('{path} /nologo /showIncludes /FC @$out.rsp /c $in /Fo$out '
-            '/Fd$pdbname'.format(path=path))
+    return ('{path} /nologo /showIncludes {flags} /c $in /Fo$out /Fd$out.pdb'
+            .format(path=path, flags=flags))
 
   def GetFlags(self, defines, include_dirs, cflags):
     defines = defines + self._gyp_defines
     quoted_defines = QuoteArguments(defines)
-    define_flags = [
-        '/D{0}'.format(define) for define in quoted_defines
-    ]
+    define_flags = ['/D{0}'.format(define) for define in quoted_defines]
     include_dirs = include_dirs + self._gyp_include_dirs
     quoted_include_dirs = QuoteArguments(include_dirs)
     include_dir_flags = [
@@ -128,8 +125,8 @@
     del extra_flags  # Not used.
     del shell  # Not used.
     return ('{python} gyp-win-tool asm-wrapper {arch} {path} {flags} /c /Fo '
-            '$out $in'.format(python=sys.executable, arch=self._arch,
-                              path=path, flags=flags))
+            '$out $in'.format(
+                python=sys.executable, arch=self._arch, path=path, flags=flags))
 
   def GetDescription(self):
     return 'ASM $in'
@@ -138,9 +135,7 @@
     del cflags  # Not used.
     defines = defines + self._gyp_defines
     quoted_defines = QuoteArguments(defines)
-    define_flags = [
-        '/D{0}'.format(define) for define in quoted_defines
-    ]
+    define_flags = ['/D{0}'.format(define) for define in quoted_defines]
     include_dirs = include_dirs + self._gyp_include_dirs
     quoted_include_dirs = QuoteArguments(include_dirs)
     include_dir_flags = [
@@ -156,6 +151,8 @@
     self._path = common.GetPath('ar', **kwargs)
     self._arch = kwargs.get('arch', 'environment.x64')
     self._gyp_libflags = kwargs.get('gyp_libflags', [])
+    self._max_concurrent_processes = kwargs.get('max_concurrent_processes',
+                                                None)
 
   def GetPath(self):
     return self._path
@@ -165,8 +162,7 @@
     return []
 
   def GetMaxConcurrentProcesses(self):
-    # Run as much concurrent processes as possible.
-    return None
+    return self._max_concurrent_processes
 
   def GetDescription(self):
     return 'LIB $out'
@@ -182,9 +178,8 @@
     del shell  # Not used.
     self._command_flags = flags
     return ('{python} gyp-win-tool link-wrapper {arch} {path} /nologo '
-            '/ignore:4221 /OUT:$out @$out.rsp'.format(python=sys.executable,
-                                                      arch=self._arch,
-                                                      path=path))
+            '/ignore:4221 /OUT:$out @$out.rsp'.format(
+                python=sys.executable, arch=self._arch, path=path))
 
   def GetFlags(self):
     return self._gyp_libflags
@@ -249,9 +244,8 @@
     del shell  # Not used.
     self._command_flags = flags
     return ('{python} gyp-win-tool link-wrapper {arch} {path} /nologo '
-            '/OUT:$out /PDB:$out.pdb @$out.rsp'.format(python=sys.executable,
-                                                       arch=self._arch,
-                                                       path=path))
+            '/OUT:$out /PDB:$out.pdb @$out.rsp'.format(
+                python=sys.executable, arch=self._arch, path=path))
 
 
 class SharedLibraryLinker(DynamicLinkerBase, abstract.SharedLibraryLinker):
@@ -274,9 +268,8 @@
     del shell  # Not used.
     self._command_flags = flags
     return ('{python} gyp-win-tool link-wrapper {arch} {path} /nologo '
-            '$implibflag /DLL /OUT:$dll /PDB:$dll.pdb @$dll.rsp'.format(python=sys.executable,
-                                                      arch=self._arch,
-                                                      path=path))
+            '$implibflag /DLL /OUT:$dll /PDB:$dll.pdb @$dll.rsp'.format(
+                python=sys.executable, arch=self._arch, path=path))
 
   def GetRestat(self):
     return True
diff --git a/src/third_party/angle/angle.gyp b/src/third_party/angle/angle.gyp
index 8bb6ad8..b34eea7 100644
--- a/src/third_party/angle/angle.gyp
+++ b/src/third_party/angle/angle.gyp
@@ -55,6 +55,13 @@
                 './src',
                 './src/common/third_party/base',
             ],
+            'msvs_settings':
+            {
+                  'VCCLCompilerTool':
+                  {
+                        'WarnAsError': 'false',
+                  },
+            },
             'direct_dependent_settings':
             {
                 'include_dirs':
diff --git a/src/third_party/angle/src/compiler.gypi b/src/third_party/angle/src/compiler.gypi
index 7be0ac3..4c87123 100644
--- a/src/third_party/angle/src/compiler.gypi
+++ b/src/third_party/angle/src/compiler.gypi
@@ -353,6 +353,13 @@
             'dependencies': [ 'angle_common' ],
             'includes': [ '../gyp/common_defines.gypi', ],
             'sources': [ '<@(angle_preprocessor_sources)', ],
+            'msvs_settings':
+            {
+                  'VCCLCompilerTool':
+                  {
+                        'WarnAsError': 'false',
+                  },
+            },
         },
         {
             'target_name': 'translator',
@@ -370,10 +377,14 @@
             ],
             'msvs_settings':
             {
-              'VCLibrarianTool':
-              {
-                'AdditionalOptions': ['/ignore:4221']
-              },
+                  'VCCLCompilerTool':
+                  {
+                        'WarnAsError': 'false',
+                  },
+                  'VCLibrarianTool':
+                  {
+                        'AdditionalOptions': ['/ignore:4221'],
+                  },
             },
             'conditions':
             [
diff --git a/src/tools/gyp/pylib/gyp/MSVSSettings.py b/src/tools/gyp/pylib/gyp/MSVSSettings.py
index d0d4990..8526389 100644
--- a/src/tools/gyp/pylib/gyp/MSVSSettings.py
+++ b/src/tools/gyp/pylib/gyp/MSVSSettings.py
@@ -532,7 +532,6 @@
 _Same(_compile, 'ForcedUsingFiles', _file_list)  # /FU
 _Same(_compile, 'GenerateXMLDocumentationFiles', _boolean)  # /doc
 _Same(_compile, 'IgnoreStandardIncludePath', _boolean)  # /X
-_Same(_compile, 'MinimalRebuild', _boolean)  # /Gm
 _Same(_compile, 'OmitDefaultLibName', _boolean)  # /Zl
 _Same(_compile, 'OmitFramePointers', _boolean)  # /Oy
 _Same(_compile, 'PreprocessorDefinitions', _string_list)  # /D
diff --git a/src/tools/gyp/pylib/gyp/MSVSSettings_test.py b/src/tools/gyp/pylib/gyp/MSVSSettings_test.py
index 4e06da3..52ff7c3 100755
--- a/src/tools/gyp/pylib/gyp/MSVSSettings_test.py
+++ b/src/tools/gyp/pylib/gyp/MSVSSettings_test.py
@@ -79,7 +79,6 @@
             'IgnoreStandardIncludePath': 'true',
             'InlineFunctionExpansion': '1',
             'KeepComments': 'true',
-            'MinimalRebuild': 'true',
             'ObjectFile': 'a_file_name',
             'OmitDefaultLibName': 'true',
             'OmitFramePointers': 'true',
@@ -318,7 +317,6 @@
             'IgnoreStandardIncludePath': 'true',
             'InlineFunctionExpansion': 'OnlyExplicitInline',
             'IntrinsicFunctions': 'false',
-            'MinimalRebuild': 'true',
             'MultiProcessorCompilation': 'true',
             'ObjectFileName': 'a_file_name',
             'OmitDefaultLibName': 'true',
@@ -690,7 +688,6 @@
             'IgnoreStandardIncludePath': 'true',
             'InlineFunctionExpansion': '2',
             'KeepComments': 'true',
-            'MinimalRebuild': 'true',
             'ObjectFile': 'a_file_name',
             'OmitDefaultLibName': 'true',
             'OmitFramePointers': 'true',
diff --git a/src/tools/gyp/pylib/gyp/generator/ninja.py b/src/tools/gyp/pylib/gyp/generator/ninja.py
index 7a5a8ca..1b8a661 100755
--- a/src/tools/gyp/pylib/gyp/generator/ninja.py
+++ b/src/tools/gyp/pylib/gyp/generator/ninja.py
@@ -82,15 +82,17 @@
     'win', 'win-win32', 'win-win32-lib',
 ]
 sony_flavors = []
+nintendo_flavors = []
 
 try:
   import private_ninja_flavors
   microsoft_flavors += private_ninja_flavors.PrivateMicrosoftFlavors()
   sony_flavors += private_ninja_flavors.PrivateSonyFlavors()
+  nintendo_flavors += private_ninja_flavors.PrivateNintendoFlavors()
 except ImportError:
   pass
 
-windows_host_flavors = microsoft_flavors + sony_flavors
+windows_host_flavors = microsoft_flavors + sony_flavors + nintendo_flavors
 
 
 def GetToolchainOrNone(flavor):
@@ -157,7 +159,7 @@
     return arg  # No quoting necessary.
   if GetToolchainOrNone(flavor):
     return GetToolchainOrNone(flavor).QuoteForRspFile(arg)
-  elif flavor in sony_flavors:
+  elif flavor in sony_flavors or flavor in nintendo_flavors:
     # Escape double quotes.
     return '"' + arg.replace('\"', '\\\"') + '"'
   return "'" + arg.replace("'", "'" + '"\'"' + "'") + "'"
@@ -165,17 +167,21 @@
 
 def Define(d, flavor):
   """Takes a preprocessor define and returns a -D parameter that's ninja- and
-  shell-escaped."""
+
+  shell-escaped.
+  """
 
   return QuoteShellArgument(ninja_syntax.escape('-D' + d), flavor)
 
 
 def InvertRelativePath(path):
   """Given a relative path like foo/bar, return the inverse relative path:
+
   the path from the relative path back to the origin dir.
 
   E.g. os.path.normpath(os.path.join(path, InvertRelativePath(path)))
-  should always produce the empty string."""
+  should always produce the empty string.
+  """
 
   if not path:
     return path
@@ -264,12 +270,16 @@
 
   def PreCompileInput(self):
     """Return the path, if any, that should be used as a dependency of
-    any dependent compile step."""
+
+    any dependent compile step.
+    """
     return self.actions_stamp or self.precompile_stamp
 
   def FinalOutput(self):
     """Return the last output of the target, which depends on all prior
-    steps."""
+
+    steps.
+    """
     return self.bundle or self.binary or self.actions_stamp
 
 
@@ -393,9 +403,11 @@
 
   def GypPathToNinja(self, path, env=None):
     """Translate a gyp path to a ninja path, optionally expanding environment
+
     variable references in |path| with |env|.
 
-    See the above discourse on path conversions."""
+    See the above discourse on path conversions.
+    """
     if env:
       if self.flavor == 'mac':
         path = gyp.xcode_emulation.ExpandEnvVars(path, env)
@@ -427,7 +439,8 @@
     of the target.  This is necessary when e.g. compiling the same
     path twice for two separate output targets.
 
-    See the above discourse on path conversions."""
+    See the above discourse on path conversions.
+    """
 
     path = self.ExpandSpecial(path)
     assert not path.startswith('$'), path
@@ -457,9 +470,11 @@
 
   def WriteCollapsedDependencies(self, name, targets):
     """Given a list of targets, return a path for a single file
+
     representing the result of building all the targets or None.
 
-    Uses a stamp file if necessary."""
+    Uses a stamp file if necessary.
+    """
 
     assert targets == filter(None, targets), targets
     if len(targets) == 0:
@@ -484,7 +499,8 @@
 
     Returns a Target object, which represents the output paths for this spec.
     Returns None if there are no outputs (e.g. a settings-only 'none' type
-    target)."""
+    target).
+    """
 
     self.config_name = config_name
     self.name = spec['target_name']
@@ -499,7 +515,7 @@
     if self.flavor == 'mac':
       self.xcode_settings = gyp.xcode_emulation.XcodeSettings(spec)
     if (self.flavor in windows_host_flavors and is_windows):
-      if self.flavor in sony_flavors:
+      if self.flavor in sony_flavors or self.flavor in nintendo_flavors:
         self.msvs_settings = gyp.msvs_emulation.MsvsSettings(
             spec, generator_flags)
         arch = self.msvs_settings.GetArch(config_name)
@@ -608,8 +624,11 @@
     return self.target
 
   def _WinIdlRule(self, source, prebuild, outputs):
-    """Handle the implicit VS .idl rule for one source file. Fills |outputs|
-    with files that are generated."""
+    """Handle the implicit VS .idl rule for one source file.
+
+    Fills |outputs|
+    with files that are generated.
+    """
     outdir, output, vars, flags = GetToolchainOrNone(
         self.flavor).GetCompilerSettings().GetIdlBuildData(
             source, self.config_name)
@@ -646,8 +665,11 @@
 
   def WriteActionsRulesCopies(self, spec, extra_sources, prebuild,
                               mac_bundle_depends):
-    """Write out the Actions, Rules, and Copies steps.  Return a path
-    representing the outputs of these steps."""
+    """Write out the Actions, Rules, and Copies steps.
+
+    Return a path
+    representing the outputs of these steps.
+    """
     outputs = []
     extra_mac_bundle_resources = []
 
@@ -685,10 +707,10 @@
       return '%s %s: %s' % (verb, self.name, fallback)
 
   def IsCygwinRule(self, action):
-    if self.flavor in sony_flavors:
+    if self.flavor in sony_flavors or self.flavor in nintendo_flavors:
       value = str(action.get('msvs_cygwin_shell', 0)) != '0'
       if value:
-        raise Exception("Cygwin usage is no longer allowed in Cobalt Gyp")
+        raise Exception('Cygwin usage is no longer allowed in Cobalt Gyp')
     return False
 
   def WriteActions(self, actions, extra_sources, prebuild,
@@ -1611,8 +1633,10 @@
                           output_binary,
                           is_command_start=False):
     """Returns a shell command that runs all the postbuilds, and removes
+
     |output| if any of them fails. If |is_command_start| is False, then the
-    returned string will start with ' && '."""
+    returned string will start with ' && '.
+    """
     if not self.xcode_settings or spec['type'] == 'none' or not output:
       return ''
     output = QuoteShellArgument(output, self.flavor)
@@ -1648,8 +1672,10 @@
 
   def ComputeExportEnvString(self, env):
     """Given an environment, returns a string looking like
+
         'export FOO=foo; export BAR="${FOO} bar;'
-    that exports |env| to the shell."""
+    that exports |env| to the shell.
+    """
     export_str = []
     for k, v in env:
       export_str.append(
@@ -1777,7 +1803,8 @@
     """Write out a new ninja "rule" statement for a given command.
 
     Returns the name of the new rule, and a copy of |args| with variables
-    expanded."""
+    expanded.
+    """
 
     if self.flavor == 'win':
       args = [
@@ -1818,7 +1845,7 @@
       rspfile = rule_name + '.$unique_name.rsp'
       # The cygwin case handles this inside the bash sub-shell.
       run_in = ' ' + self.build_to_base
-      if self.flavor in sony_flavors:
+      if self.flavor in sony_flavors or self.flavor in nintendo_flavors:
         rspfile_content = gyp.msvs_emulation.EncodeRspFileList(args)
       else:
         rspfile_content = GetToolchainOrNone(
@@ -1869,19 +1896,13 @@
     global generator_extra_sources_for_rules
     generator_extra_sources_for_rules = getattr(
         xcode_generator, 'generator_extra_sources_for_rules', [])
-  elif flavor in sony_flavors:
-    if is_windows:
-      # This is required for BuildCygwinBashCommandLine() to work.
-      import gyp.generator.msvs as msvs_generator
-      generator_additional_non_configuration_keys = getattr(
-          msvs_generator, 'generator_additional_non_configuration_keys', [])
-      generator_additional_path_sections = getattr(
-          msvs_generator, 'generator_additional_path_sections', [])
-
-    default_variables['EXECUTABLE_SUFFIX'] = '.elf'
+  elif flavor in sony_flavors or flavor in nintendo_flavors:
+    if flavor in sony_flavors:
+      default_variables['EXECUTABLE_SUFFIX'] = '.elf'
+    else:
+      default_variables['EXECUTABLE_SUFFIX'] = '.exe'
     default_variables['SHARED_LIB_PREFIX'] = 'lib'
     default_variables['SHARED_LIB_SUFFIX'] = '.so'
-
     # Copy additional generator configuration data from VS, which is shared
     # by the Windows Ninja generator.
     import gyp.generator.msvs as msvs_generator
diff --git a/src/v8/gypfiles/standalone.gypi b/src/v8/gypfiles/standalone.gypi
index 8f893ce..a2955f8 100644
--- a/src/v8/gypfiles/standalone.gypi
+++ b/src/v8/gypfiles/standalone.gypi
@@ -944,7 +944,6 @@
         ],
         'msvs_settings': {
           'VCCLCompilerTool': {
-            'MinimalRebuild': 'false',
             'BufferSecurityCheck': 'true',
             'EnableFunctionLevelLinking': 'true',
             'RuntimeTypeInfo': 'false',
diff --git a/src/v8/src/codegen/arm64/macro-assembler-arm64-inl.h b/src/v8/src/codegen/arm64/macro-assembler-arm64-inl.h
index 62bd9c2..928e257 100644
--- a/src/v8/src/codegen/arm64/macro-assembler-arm64-inl.h
+++ b/src/v8/src/codegen/arm64/macro-assembler-arm64-inl.h
@@ -1069,7 +1069,7 @@
     return;
   }
   DCHECK_EQ(size % 16, 0);
-#if V8_OS_WIN
+#if V8_TARGET_OS_WIN
   while (size > kStackPageSize) {
     Sub(sp, sp, kStackPageSize);
     Str(xzr, MemOperand(sp));
@@ -1091,7 +1091,7 @@
   }
   AssertPositiveOrZero(count);
 
-#if V8_OS_WIN
+#if V8_TARGET_OS_WIN
   // "Functions that allocate 4k or more worth of stack must ensure that each
   // page prior to the final page is touched in order." Source:
   // https://docs.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions?view=vs-2019#stack
diff --git a/src/v8/src/v8.gyp b/src/v8/src/v8.gyp
index 588e049..d4e3a8e 100644
--- a/src/v8/src/v8.gyp
+++ b/src/v8/src/v8.gyp
@@ -1758,27 +1758,6 @@
           '<!@pymod_do_main(v8.gypfiles.GN-scraper "<(DEPTH)/v8/BUILD.gn"  "\\"postmortem-metadata.*?sources = ")',
         ],
       },
-      'actions': [
-        {
-          'action_name': 'gen-postmortem-metadata',
-          'inputs': [
-            '<(DEPTH)/v8/tools/gen-postmortem-metadata.py',
-            '<@(heapobject_files)',
-          ],
-          'outputs': [
-            '<(SHARED_INTERMEDIATE_DIR)/debug-support.cc',
-          ],
-          'action': [
-            'python2',
-            '<(DEPTH)/v8/tools/gen-postmortem-metadata.py',
-            '<@(_outputs)',
-            '<@(heapobject_files)'
-          ],
-        },
-      ],
-      'direct_dependent_settings': {
-        'sources': ['<(SHARED_INTERMEDIATE_DIR)/debug-support.cc', ],
-      },
     },  # postmortem-metadata
   ],
-}
+}
\ No newline at end of file