Import Cobalt 22.lts.1.304997
diff --git a/src/cobalt/audio/audio_destination_node.cc b/src/cobalt/audio/audio_destination_node.cc
index 6f82ba8..3f0505f 100644
--- a/src/cobalt/audio/audio_destination_node.cc
+++ b/src/cobalt/audio/audio_destination_node.cc
@@ -62,11 +62,15 @@
     SB_LOG(INFO) << "Created audio device " << audio_device_.get() << '.';
     context()->PreventGarbageCollection();
   }
-  audio_device_to_delete_ = NULL;
+  delete_audio_device_ = false;
 }
 
 void AudioDestinationNode::FillAudioBus(bool all_consumed, AudioBus* audio_bus,
                                         bool* silence) {
+  if (delete_audio_device_) {
+    return;
+  }
+
   // This is called on Audio thread.
   AudioLock::AutoLock lock(audio_lock());
 
@@ -74,11 +78,10 @@
   DCHECK_EQ(number_of_inputs(), 1u);
   bool all_finished = true;
   Input(0)->FillAudioBus(audio_bus, silence, &all_finished);
-  if (all_consumed && all_finished &&
-      audio_device_to_delete_ != audio_device_.get()) {
+  if (all_consumed && all_finished) {
     SB_LOG(INFO) << "Schedule to destroy audio device " << audio_device_.get()
                  << '.';
-    audio_device_to_delete_ = audio_device_.get();
+    delete_audio_device_ = true;
     message_loop_->task_runner()->PostTask(
         FROM_HERE, base::Bind(&AudioDestinationNode::DestroyAudioDevice,
                               base::Unretained(this)));
@@ -90,10 +93,11 @@
   if (!audio_device_.get()) {
     return;
   }
-  if (audio_device_.get() == audio_device_to_delete_) {
+  if (delete_audio_device_) {
     SB_LOG(INFO) << "Destroying audio device " << audio_device_.get() << '.';
     audio_device_.reset();
     context()->AllowGarbageCollection();
+    delete_audio_device_ = false;
   }
 }
 
diff --git a/src/cobalt/audio/audio_destination_node.h b/src/cobalt/audio/audio_destination_node.h
index b758d92..77cee04 100644
--- a/src/cobalt/audio/audio_destination_node.h
+++ b/src/cobalt/audio/audio_destination_node.h
@@ -74,7 +74,7 @@
   uint32 max_channel_count_;
 
   std::unique_ptr<AudioDevice> audio_device_;
-  AudioDevice* audio_device_to_delete_ = NULL;
+  std::atomic_bool delete_audio_device_ = {false};
 
   DISALLOW_COPY_AND_ASSIGN(AudioDestinationNode);
 };
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index 1f6e058..f66ed8b 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -1461,8 +1461,7 @@
   DCHECK_EQ(base::MessageLoop::current(), self_message_loop_);
   DCHECK(application_state_ == base::kApplicationStateStarted);
   application_state_ = base::kApplicationStateBlurred;
-  FOR_EACH_OBSERVER(LifecycleObserver,
-                    lifecycle_observers_, Blur(timestamp));
+  FOR_EACH_OBSERVER(LifecycleObserver, lifecycle_observers_, Blur(timestamp));
 }
 
 void BrowserModule::Conceal(SbTimeMonotonic timestamp) {
@@ -1477,8 +1476,7 @@
   TRACE_EVENT0("cobalt::browser", "BrowserModule::Focus()");
   DCHECK_EQ(base::MessageLoop::current(), self_message_loop_);
   DCHECK(application_state_ == base::kApplicationStateBlurred);
-  FOR_EACH_OBSERVER(LifecycleObserver,
-                    lifecycle_observers_, Focus(timestamp));
+  FOR_EACH_OBSERVER(LifecycleObserver, lifecycle_observers_, Focus(timestamp));
   application_state_ = base::kApplicationStateStarted;
 }
 
@@ -1629,12 +1627,12 @@
                                                options_.media_module_options));
 
     if (web_module_) {
-      web_module_->SetCamera3D(input_device_manager_->camera_3d());
       web_module_->SetMediaModule(media_module_.get());
     }
   }
 
   if (web_module_) {
+    web_module_->UpdateCamera3D(input_device_manager_->camera_3d());
     web_module_->GetUiNavRoot()->SetContainerWindow(
         system_window_->GetSbWindow());
   }
@@ -1782,8 +1780,7 @@
   FreezeMediaModule();
   // First freeze all our web modules which implies that they will release
   // their resource provider and all resources created through it.
-  FOR_EACH_OBSERVER(LifecycleObserver,
-                    lifecycle_observers_, Freeze(timestamp));
+  FOR_EACH_OBSERVER(LifecycleObserver, lifecycle_observers_, Freeze(timestamp));
 }
 
 void BrowserModule::RevealInternal(SbTimeMonotonic timestamp) {
@@ -2095,14 +2092,12 @@
 }
 
 void BrowserModule::SetApplicationStartOrPreloadTimestamp(
-  bool is_preload, SbTimeMonotonic timestamp) {
+    bool is_preload, SbTimeMonotonic timestamp) {
   DCHECK(web_module_);
-  web_module_->SetApplicationStartOrPreloadTimestamp(
-      is_preload, timestamp);
+  web_module_->SetApplicationStartOrPreloadTimestamp(is_preload, timestamp);
 }
 
-void BrowserModule::SetDeepLinkTimestamp(
-    SbTimeMonotonic timestamp) {
+void BrowserModule::SetDeepLinkTimestamp(SbTimeMonotonic timestamp) {
   DCHECK(web_module_);
   web_module_->SetDeepLinkTimestamp(timestamp);
 }
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc
index ef84f3b..06600b8 100644
--- a/src/cobalt/browser/web_module.cc
+++ b/src/cobalt/browser/web_module.cc
@@ -217,7 +217,7 @@
 #endif  // defined(ENABLE_DEBUGGER)
 
   void SetSize(cssom::ViewportSize viewport_size);
-  void SetCamera3D(const scoped_refptr<input::Camera3D>& camera_3d);
+  void UpdateCamera3D(const scoped_refptr<input::Camera3D>& camera_3d);
   void SetMediaModule(media::MediaModule* media_module);
   void SetImageCacheCapacity(int64_t bytes);
   void SetRemoteTypefaceCacheCapacity(int64_t bytes);
@@ -1123,9 +1123,9 @@
   window_->SetSize(viewport_size);
 }
 
-void WebModule::Impl::SetCamera3D(
+void WebModule::Impl::UpdateCamera3D(
     const scoped_refptr<input::Camera3D>& camera_3d) {
-  window_->SetCamera3D(camera_3d);
+  window_->UpdateCamera3D(camera_3d);
 }
 
 void WebModule::Impl::SetMediaModule(media::MediaModule* media_module) {
@@ -1666,9 +1666,10 @@
                             base::Unretained(impl_.get()), viewport_size));
 }
 
-void WebModule::SetCamera3D(const scoped_refptr<input::Camera3D>& camera_3d) {
+void WebModule::UpdateCamera3D(
+    const scoped_refptr<input::Camera3D>& camera_3d) {
   message_loop()->task_runner()->PostTask(
-      FROM_HERE, base::Bind(&WebModule::Impl::SetCamera3D,
+      FROM_HERE, base::Bind(&WebModule::Impl::UpdateCamera3D,
                             base::Unretained(impl_.get()), camera_3d));
 }
 
diff --git a/src/cobalt/browser/web_module.h b/src/cobalt/browser/web_module.h
index 16c830d..20b39bb 100644
--- a/src/cobalt/browser/web_module.h
+++ b/src/cobalt/browser/web_module.h
@@ -370,7 +370,7 @@
   // from the current parameters.
   void SetSize(const cssom::ViewportSize& viewport_size);
 
-  void SetCamera3D(const scoped_refptr<input::Camera3D>& camera_3d);
+  void UpdateCamera3D(const scoped_refptr<input::Camera3D>& camera_3d);
   void SetMediaModule(media::MediaModule* media_module);
   void SetImageCacheCapacity(int64_t bytes);
   void SetRemoteTypefaceCacheCapacity(int64_t bytes);
@@ -412,6 +412,7 @@
   void SetApplicationStartOrPreloadTimestamp(bool is_preload,
                                              SbTimeMonotonic timestamp);
   void SetDeepLinkTimestamp(SbTimeMonotonic timestamp);
+
  private:
   // Data required to construct a WebModule, initialized in the constructor and
   // passed to |Initialize|.
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index ba0b6d4..ce625a1 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-304707
\ No newline at end of file
+304997
\ No newline at end of file
diff --git a/src/cobalt/doc/gyp_gn_files.md b/src/cobalt/doc/gyp_gn_files.md
deleted file mode 100644
index 452bfa7..0000000
--- a/src/cobalt/doc/gyp_gn_files.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# GYP files and their corresponding GN files
-
-Generally, `foo/bar/bar.gyp` corresponds to `foo/bar/BUILD.gn`, while
-`foo/bar/baz.gyp` corresponds to either `foo/bar/baz/BUILD.gn` or
-`foo/bar/BUILD.gn` (depending on whether it made sense to combine `baz.gyp` with
-`bar.gyp`). `foo/bar/quux.gypi` typically corresponds to
-`foo/bar/quux.gni`. Here is a table of irregular correspondences:
-
-GYP File                                            | GN File                                                                   | Notes
---------------------------------------------------- | ------------------------------------------------------------------------- |------
-build/common.gypi                                   | cobalt/build/config/base.gni, starboard/build/config/base.gni (variables) | A few variables have been omitted, moved to `BUILDCONFIG.gn` instead, or refactored into configs. See the GYP -> GN cookbook for more info.
-build/common.gypi                                   | cobalt/build/config/BUILD.gn (target defaults)
-cobalt/build/all.gyp                                | BUILD.gn (in root directory)                                              | GN requires this location to be used
-cobalt/build/config/base.gypi                       | cobalt/build/config/base.gni, starboard/build/config/base.gni (variables) | See comments for `build/common.gypi`
-starboard/linux/shared/compiler_flags.gypi          | starboard/linux/shared/BUILD.gn                                           | "Compiler Defaults" section
-starboard/linux/shared/starboard_base_symbolize.gyp | starboard/linux/shared/BUILD.gn                                           | "starboard_platform Target" section
-starboard/linux/shared/starboard_platform.gypi      | starboard/linux/shared/BUILD.gn                                           | "starboard_platform Target" section
-starboard/linux/x64x11/libraries.gypi               | starboard/linux/x64x11/BUILD.gn                                           | `libs` variable of the `compiler_defaults` config
-starboard/starboard_base_target.gypi                | starboard/build/config/BUILD.gn                                           | "Compiler Defaults" section
diff --git a/src/cobalt/dom/font_cache.cc b/src/cobalt/dom/font_cache.cc
index ce58fe0..93e64d6 100644
--- a/src/cobalt/dom/font_cache.cc
+++ b/src/cobalt/dom/font_cache.cc
@@ -159,34 +159,21 @@
   return cached_font_info.font;
 }
 
-std::vector<FontFace*> FontCache::GetFacesForFamilyAndStyle(
-    const std::string& family, render_tree::FontStyle style) {
-  std::vector<FontFace*> faces;
-  FontFaceMap::iterator font_face_map_iterator = font_face_map_->find(family);
-  if (font_face_map_iterator != font_face_map_->end()) {
-    // Add all font-face entries that match the family.
-    std::vector<const FontFaceStyleSet::Entry*> entries =
-        font_face_map_iterator->second.GetEntriesThatMatchStyle(style);
-    for (auto entry : entries) {
-      FontFace* face = new FontFace();
-      face->entry = entry;
-      faces.push_back(face);
-    }
-  } else {
-    // This is a local font. One face can represent it.
-    FontFace* face = new FontFace();
-    faces.push_back(face);
-  }
-  return faces;
-}
-
 scoped_refptr<render_tree::Font> FontCache::TryGetFont(
     const std::string& family, render_tree::FontStyle style, float size,
-    FontFace::State* state,
-    const FontFaceStyleSet::Entry* maybe_style_set_entry) {
+    FontListFont::State* state) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  FontFaceMap::iterator font_face_map_iterator = font_face_map_->find(family);
   int64 request_time_start = base::TimeTicks::Now().ToInternalValue();
-  if (maybe_style_set_entry) {
+  if (font_face_map_iterator != font_face_map_->end()) {
+    // Retrieve the font face style set entry that most closely matches the
+    // desired style. Given that a font face was found for this family, it
+    // should never be NULL.
+    // https://www.w3.org/TR/css3-fonts/#font-prop-desc
+    const FontFaceStyleSet::Entry* style_set_entry =
+        font_face_map_iterator->second.MatchStyle(style);
+    DCHECK(style_set_entry != NULL);
+
     // Walk the entry's sources:
     // - If a remote source is encountered, always return the results of its
     //   attempted retrieval, regardless of its success.
@@ -196,9 +183,8 @@
     //   instead.
     // https://www.w3.org/TR/css3-fonts/#src-desc
     for (FontFaceSources::const_iterator source_iterator =
-             maybe_style_set_entry->sources.begin();
-         source_iterator != maybe_style_set_entry->sources.end();
-         ++source_iterator) {
+             style_set_entry->sources.begin();
+         source_iterator != style_set_entry->sources.end(); ++source_iterator) {
       if (source_iterator->IsUrlSource()) {
         auto font = TryGetRemoteFont(source_iterator->GetUrl(), size, state);
         GlobalStats::GetInstance()->OnFontRequestComplete(request_time_start);
@@ -213,7 +199,7 @@
       }
     }
 
-    *state = FontFace::kUnavailableState;
+    *state = FontListFont::kUnavailableState;
     return NULL;
   } else {
     auto font = TryGetLocalFont(family, style, size, state);
@@ -349,7 +335,7 @@
 }
 
 scoped_refptr<render_tree::Font> FontCache::TryGetRemoteFont(
-    const GURL& url, float size, FontFace::State* state) {
+    const GURL& url, float size, FontListFont::State* state) {
   // Retrieve the font from the remote typeface cache, potentially triggering a
   // load.
   scoped_refptr<loader::font::CachedRemoteTypeface> cached_remote_typeface =
@@ -384,16 +370,16 @@
   scoped_refptr<render_tree::Typeface> typeface =
       cached_remote_typeface->TryGetResource();
   if (typeface.get() != NULL) {
-    *state = FontFace::kLoadedState;
+    *state = FontListFont::kLoadedState;
     return GetFontFromTypefaceAndSize(typeface, size);
   } else {
     if (cached_remote_typeface->IsLoadingComplete()) {
-      *state = FontFace::kUnavailableState;
+      *state = FontListFont::kUnavailableState;
     } else if (requested_remote_typeface_iterator->second
                    ->HasActiveRequestTimer()) {
-      *state = FontFace::kLoadingWithTimerActiveState;
+      *state = FontListFont::kLoadingWithTimerActiveState;
     } else {
-      *state = FontFace::kLoadingWithTimerExpiredState;
+      *state = FontListFont::kLoadingWithTimerExpiredState;
     }
     return NULL;
   }
@@ -401,7 +387,7 @@
 
 scoped_refptr<render_tree::Font> FontCache::TryGetLocalFont(
     const std::string& family, render_tree::FontStyle style, float size,
-    FontFace::State* state) {
+    FontListFont::State* state) {
   DCHECK(resource_provider());
   DCHECK(resource_provider() != NULL);
   // Only request the local font from the resource provider if the family is
@@ -412,10 +398,10 @@
   // signifies using the default font.
   if (!family.empty() &&
       !resource_provider()->HasLocalFontFamily(family.c_str())) {
-    *state = FontFace::kUnavailableState;
+    *state = FontListFont::kUnavailableState;
     return NULL;
   } else {
-    *state = FontFace::kLoadedState;
+    *state = FontListFont::kLoadedState;
     return GetFontFromTypefaceAndSize(
         GetCachedLocalTypeface(
             resource_provider()->GetLocalTypeface(family.c_str(), style)),
@@ -424,7 +410,7 @@
 }
 
 scoped_refptr<render_tree::Font> FontCache::TryGetLocalFontByFaceName(
-    const std::string& font_face, float size, FontFace::State* state) {
+    const std::string& font_face, float size, FontListFont::State* state) {
   do {
     if (font_face.empty()) {
       break;
@@ -438,11 +424,11 @@
     const scoped_refptr<render_tree::Typeface>& typeface_cached(
         GetCachedLocalTypeface(typeface));
 
-    *state = FontFace::kLoadedState;
+    *state = FontListFont::kLoadedState;
     return GetFontFromTypefaceAndSize(typeface_cached, size);
   } while (false);
 
-  *state = FontFace::kUnavailableState;
+  *state = FontListFont::kUnavailableState;
   return NULL;
 }
 
diff --git a/src/cobalt/dom/font_cache.h b/src/cobalt/dom/font_cache.h
index 932743c..44d10a9 100644
--- a/src/cobalt/dom/font_cache.h
+++ b/src/cobalt/dom/font_cache.h
@@ -201,19 +201,13 @@
   const scoped_refptr<render_tree::Font>& GetFontFromTypefaceAndSize(
       const scoped_refptr<render_tree::Typeface>& typeface, float size);
 
-
-  // Retrieves a list of the font faces that match the given family name and
-  // style. If no explicit font faces exist, creates a faux face for
-  // representing a local font.
-  std::vector<FontFace*> GetFacesForFamilyAndStyle(
-      const std::string& family, render_tree::FontStyle style);
-
   // Attempts to retrieve a font. If the family maps to a font face, then this
   // makes a request to |TryGetRemoteFont()|; otherwise, it makes a request
   // to |TryGetLocalFont()|. This function may return NULL.
-  scoped_refptr<render_tree::Font> TryGetFont(
-      const std::string& family, render_tree::FontStyle style, float size,
-      FontFace::State* state, const FontFaceStyleSet::Entry* maybe_entry);
+  scoped_refptr<render_tree::Font> TryGetFont(const std::string& family,
+                                              render_tree::FontStyle style,
+                                              float size,
+                                              FontListFont::State* state);
 
   // Returns the character fallback typeface map associated with the specified
   // style. Each unique style has its own exclusive map. If it doesn't already
@@ -265,20 +259,20 @@
   // font are registered with the remote typeface cache to be called when the
   // load finishes.
   scoped_refptr<render_tree::Font> TryGetRemoteFont(const GURL& url, float size,
-                                                    FontFace::State* state);
+                                                    FontListFont::State* state);
 
   // Returns NULL if the requested family is not empty and is not available in
   // the resource provider. Otherwise, returns the best matching local font.
   scoped_refptr<render_tree::Font> TryGetLocalFont(const std::string& family,
                                                    render_tree::FontStyle style,
                                                    float size,
-                                                   FontFace::State* state);
+                                                   FontListFont::State* state);
 
   // Lookup by a typeface (aka font_face), typeface is defined as font family +
   // style (weight, width, and style).
   // Returns NULL if the requested font face is not found.
   scoped_refptr<render_tree::Font> TryGetLocalFontByFaceName(
-      const std::string& font_face, float size, FontFace::State* state);
+      const std::string& font_face, float size, FontListFont::State* state);
 
   // Called when a remote typeface either successfully loads or fails to load.
   // In either case, the event can impact the fonts contained within the font
diff --git a/src/cobalt/dom/font_cache_test.cc b/src/cobalt/dom/font_cache_test.cc
index 1c22311..49656eb 100644
--- a/src/cobalt/dom/font_cache_test.cc
+++ b/src/cobalt/dom/font_cache_test.cc
@@ -100,13 +100,6 @@
   const std::string postscript_font_name("DancingScript");
   std::unique_ptr<FontCache::FontFaceMap> ffm =
       CreateFontFaceMapHelper(family_name, postscript_font_name);
-
-  const FontFaceStyleSet::Entry* entry = nullptr;
-  FontCache::FontFaceMap::iterator ffm_iterator = ffm->find(family_name);
-  if (ffm_iterator != ffm->end()) {
-    entry = ffm_iterator->second.GetEntriesThatMatchStyle(kNormalUpright)[0];
-  }
-  EXPECT_TRUE(entry);
   font_cache_->SetFontFaceMap(std::move(ffm));
 
   EXPECT_CALL(loader_factory_, CreateTypefaceLoaderMock(_, _, _, _, _))
@@ -116,9 +109,9 @@
               GetLocalTypefaceIfAvailableMock(postscript_font_name))
       .WillOnce(Return(sample_typeface_));
 
-  FontFace::State state;
+  FontListFont::State state;
   scoped_refptr<render_tree::Font> f =
-      font_cache_->TryGetFont(family_name, kNormalUpright, 12.0, &state, entry);
+      font_cache_->TryGetFont(family_name, kNormalUpright, 12.0, &state);
 
   EXPECT_TRUE(f);
 }
@@ -128,13 +121,6 @@
   const std::string family_name("Dancing Script");
   std::unique_ptr<FontCache::FontFaceMap> ffm =
       CreateFontFaceMapHelper(family_name, invalid_postscript_font_name);
-
-  const FontFaceStyleSet::Entry* entry = nullptr;
-  FontCache::FontFaceMap::iterator ffm_iterator = ffm->find(family_name);
-  if (ffm_iterator != ffm->end()) {
-    entry = ffm_iterator->second.GetEntriesThatMatchStyle(kNormalUpright)[0];
-  }
-  EXPECT_TRUE(entry);
   font_cache_->SetFontFaceMap(std::move(ffm));
 
   EXPECT_CALL(mock_resource_provider_,
@@ -142,9 +128,9 @@
       .Times(1);
   EXPECT_CALL(loader_factory_, CreateTypefaceLoaderMock(_, _, _, _, _));
 
-  FontFace::State state;
+  FontListFont::State state;
   scoped_refptr<render_tree::Font> f =
-      font_cache_->TryGetFont(family_name, kNormalUpright, 12.0, &state, entry);
+      font_cache_->TryGetFont(family_name, kNormalUpright, 12.0, &state);
   EXPECT_FALSE(f);
 }
 
diff --git a/src/cobalt/dom/font_face.cc b/src/cobalt/dom/font_face.cc
index 6c44d75..5f6fdb5 100644
--- a/src/cobalt/dom/font_face.cc
+++ b/src/cobalt/dom/font_face.cc
@@ -39,22 +39,25 @@
   }
 }
 
-std::vector<const FontFaceStyleSet::Entry*>
-FontFaceStyleSet::GetEntriesThatMatchStyle(
+const FontFaceStyleSet::Entry* FontFaceStyleSet::MatchStyle(
     const render_tree::FontStyle& pattern) const {
-  std::vector<const FontFaceStyleSet::Entry*> entries;
+  return entries_.empty() ? NULL
+                          : &entries_[GetClosestStyleEntryIndex(pattern)];
+}
+
+size_t FontFaceStyleSet::GetClosestStyleEntryIndex(
+    const render_tree::FontStyle& pattern) const {
+  size_t closest_index = 0;
   int max_score = std::numeric_limits<int>::min();
-  for (const auto& entry : entries_) {
-    int score = MatchScore(pattern, entry.style);
-    if (score >= max_score) {
-      if (score > max_score) {
-        max_score = score;
-        entries.clear();
-      }
-      entries.push_back(&entry);
+  for (size_t i = 0; i < entries_.size(); ++i) {
+    int score = MatchScore(pattern, entries_[i].style);
+    if (score > max_score) {
+      closest_index = i;
+      max_score = score;
     }
   }
-  return entries;
+
+  return closest_index;
 }
 
 int FontFaceStyleSet::MatchScore(
diff --git a/src/cobalt/dom/font_face.h b/src/cobalt/dom/font_face.h
index a5f80cc..1bfb40a 100644
--- a/src/cobalt/dom/font_face.h
+++ b/src/cobalt/dom/font_face.h
@@ -66,19 +66,7 @@
       return style.weight == other.style.weight &&
              style.slant == other.style.slant && sources == other.sources;
     }
-    struct UnicodeRange {
-      // Sorts ranges primarily based on start and secondarily based on end.
-      bool operator<(const UnicodeRange& range) const {
-        if (start == range.start) {
-          return end < range.end;
-        }
-        return start < range.start;
-      }
-      uint32 start;
-      uint32 end;
-    };
 
-    std::set<UnicodeRange> unicode_range;
     render_tree::FontStyle style;
     FontFaceSources sources;
   };
@@ -89,11 +77,10 @@
   // into the set. All pre-existing url entries within the set are retained.
   void CollectUrlSources(std::set<GURL>* urls) const;
 
-
-  // Returns a list of entries with the style that most closesly matches the
-  // pattern.
-  std::vector<const Entry*> GetEntriesThatMatchStyle(
-      const render_tree::FontStyle& pattern) const;
+  // Returns the style set entry with the style most closely matching the
+  // requested pattern. If the style set contains any entries, it is guaranteed
+  // to not return NULL.
+  const Entry* MatchStyle(const render_tree::FontStyle& pattern) const;
 
   bool operator==(const FontFaceStyleSet& other) const {
     return entries_ == other.entries_;
@@ -102,6 +89,11 @@
  private:
   typedef std::vector<Entry> Entries;
 
+  // Returns the index of the entry with a style most closely matching the
+  // pattern (the lower the score, the more closely it matches). In the case of
+  // a tie, the earliest encountered entry is given priority.
+  size_t GetClosestStyleEntryIndex(const render_tree::FontStyle& pattern) const;
+
   // Returns the match score between two patterns. The score logic matches that
   // within SkFontStyleSet_Cobalt::match_score().
   int MatchScore(const render_tree::FontStyle& pattern,
diff --git a/src/cobalt/dom/font_face_updater.cc b/src/cobalt/dom/font_face_updater.cc
index 940eaaa..2a486c6 100644
--- a/src/cobalt/dom/font_face_updater.cc
+++ b/src/cobalt/dom/font_face_updater.cc
@@ -29,7 +29,6 @@
 #include "cobalt/cssom/property_value_visitor.h"
 #include "cobalt/cssom/string_value.h"
 #include "cobalt/cssom/style_sheet_list.h"
-#include "cobalt/cssom/unicode_range_value.h"
 #include "cobalt/cssom/url_src_value.h"
 #include "cobalt/cssom/url_value.h"
 
@@ -215,10 +214,8 @@
 
 void FontFaceProvider::VisitUnicodeRange(
     cssom::UnicodeRangeValue* unicode_range) {
-  FontFaceStyleSet::Entry::UnicodeRange range = {
-      static_cast<uint32>(unicode_range->start()),
-      static_cast<uint32>(unicode_range->end())};
-  style_set_entry_.unicode_range.insert(range);
+  NOTIMPLEMENTED()
+      << "FontFaceProvider::UnicodeRange support not implemented yet.";
 }
 
 // Check for a supported format. If no format hints are supplied, then the user
@@ -310,9 +307,6 @@
   if (css_font_face_rule->data()->weight()) {
     css_font_face_rule->data()->weight()->Accept(&font_face_provider);
   }
-  if (css_font_face_rule->data()->unicode_range()) {
-    css_font_face_rule->data()->unicode_range()->Accept(&font_face_provider);
-  }
 
   if (font_face_provider.IsFontFaceValid()) {
     (*font_face_map_)[font_face_provider.font_family()].AddEntry(
diff --git a/src/cobalt/dom/font_list.cc b/src/cobalt/dom/font_list.cc
index 45e8d5c..ae4cbb4 100644
--- a/src/cobalt/dom/font_list.cc
+++ b/src/cobalt/dom/font_list.cc
@@ -15,7 +15,6 @@
 #include "cobalt/dom/font_list.h"
 
 #include "base/i18n/char_iterator.h"
-#include "cobalt/base/unicode/character_values.h"
 #include "cobalt/dom/font_cache.h"
 
 namespace cobalt {
@@ -25,19 +24,6 @@
 
 const base::char16 kHorizontalEllipsisValue = 0x2026;
 
-bool CharInRange(
-    const std::set<FontFaceStyleSet::Entry::UnicodeRange>& unicode_range,
-    uint32 utf32_character) {
-  if (unicode_range.empty()) return true;
-  for (const auto& range : unicode_range) {
-    if (range.start > utf32_character) break;
-    if ((range.start <= utf32_character) && (utf32_character <= range.end)) {
-      return true;
-    }
-  }
-  return false;
-}
-
 }  // namespace
 
 FontList::FontList(FontCache* font_cache, const FontListKey& font_list_key)
@@ -54,34 +40,18 @@
           font_cache_->GetCharacterFallbackTypefaceMap(style_)) {
   // Add all of the family names to the font list fonts.
   for (size_t i = 0; i < font_list_key.family_names.size(); ++i) {
-    FontListFont font = FontListFont(font_list_key.family_names[i]);
-    font.faces =
-        font_cache_->GetFacesForFamilyAndStyle(font.family_name, style_);
-    fonts_.push_back(font);
+    fonts_.push_back(FontListFont(font_list_key.family_names[i]));
   }
 
   // Add an empty font at the end in order to fall back to the default typeface.
-  FontListFont default_font = FontListFont("");
-  FontFace* default_face = new FontFace();
-  default_font.faces = std::vector<FontFace*>{default_face};
-  fonts_.push_back(default_font);
-}
-
-FontList::~FontList() {
-  for (FontListFont font : fonts_) {
-    for (FontFace* face : font.faces) {
-      delete face;
-    }
-  }
+  fonts_.push_back(FontListFont(""));
 }
 
 void FontList::Reset() {
   for (size_t i = 0; i < fonts_.size(); ++i) {
     FontListFont& font_list_font = fonts_[i];
-    for (auto face : font_list_font.faces) {
-      face->state = FontFace::kUnrequestedState;
-      face->font = NULL;
-    }
+    font_list_font.set_state(FontListFont::kUnrequestedState);
+    font_list_font.set_font(NULL);
   }
 
   primary_font_ = NULL;
@@ -99,22 +69,20 @@
   for (size_t i = 0; i < fonts_.size(); ++i) {
     FontListFont& font_list_font = fonts_[i];
 
-    for (auto face : font_list_font.faces) {
-      if (face->state == FontFace::kLoadingWithTimerActiveState ||
-          face->state == FontFace::kLoadingWithTimerExpiredState) {
-        face->state = FontFace::kUnrequestedState;
-        // If a loaded font hasn't been found yet, then the cached values need
-        // to be reset. It'll potentially change the primary font.
-        if (!found_loaded_font) {
-          primary_font_ = NULL;
-          is_font_metrics_set_ = false;
-          is_space_width_set_ = false;
-          is_ellipsis_info_set_ = false;
-          ellipsis_font_ = NULL;
-        }
-      } else if (face->state == FontFace::kLoadedState) {
-        found_loaded_font = true;
+    if (font_list_font.state() == FontListFont::kLoadingWithTimerActiveState ||
+        font_list_font.state() == FontListFont::kLoadingWithTimerExpiredState) {
+      font_list_font.set_state(FontListFont::kUnrequestedState);
+      // If a loaded font hasn't been found yet, then the cached values need to
+      // be reset. It'll potentially change the primary font.
+      if (!found_loaded_font) {
+        primary_font_ = NULL;
+        is_font_metrics_set_ = false;
+        is_space_width_set_ = false;
+        is_ellipsis_info_set_ = false;
+        ellipsis_font_ = NULL;
       }
+    } else if (font_list_font.state() == FontListFont::kLoadedState) {
+      found_loaded_font = true;
     }
   }
 }
@@ -129,10 +97,8 @@
     // display text, simply leaving transparent text is considered
     // non-conformant behavior."
     //   https://www.w3.org/TR/css3-fonts/#font-face-loading
-    for (auto face : fonts_[i].faces) {
-      if (face->state == FontFace::kLoadingWithTimerActiveState) {
-        return false;
-      }
+    if (fonts_[i].state() == FontListFont::kLoadingWithTimerActiveState) {
+      return false;
     }
   }
 
@@ -231,20 +197,15 @@
   for (size_t i = 0; i < fonts_.size(); ++i) {
     FontListFont& font_list_font = fonts_[i];
 
-    for (FontFace* face : font_list_font.faces) {
-      const FontFaceStyleSet::Entry* entry = face->entry;
-      if (entry && !CharInRange(entry->unicode_range, utf32_character)) {
-        continue;
-      }
-      if (face->state == FontFace::kUnrequestedState) {
-        RequestFont(i, face);
-      }
+    if (font_list_font.state() == FontListFont::kUnrequestedState) {
+      RequestFont(i);
+    }
 
-      if (face->state == FontFace::kLoadedState) {
-        *glyph_index = face->font->GetGlyphForCharacter(utf32_character);
-        if (*glyph_index != render_tree::kInvalidGlyphIndex) {
-          return face->font;
-        }
+    if (font_list_font.state() == FontListFont::kLoadedState) {
+      *glyph_index =
+          font_list_font.font()->GetGlyphForCharacter(utf32_character);
+      if (*glyph_index != render_tree::kInvalidGlyphIndex) {
+        return font_list_font.font();
       }
     }
   }
@@ -280,27 +241,18 @@
   // time to do it now.
   if (!primary_font_) {
     // Walk the list of fonts, requesting any encountered that are in an
-    // unrequested state. The first font encountered that is loaded and whose
-    // unicode range includes the space character is the primary font.
-    // https://www.w3.org/TR/css-fonts-4/#first-available-font
+    // unrequested state. The first font encountered that is loaded is
+    // the primary font.
     for (size_t i = 0; i < fonts_.size(); ++i) {
       FontListFont& font_list_font = fonts_[i];
 
-      for (FontFace* face : font_list_font.faces) {
-        const FontFaceStyleSet::Entry* entry = face->entry;
-        if (entry && !CharInRange(entry->unicode_range,
-                                  base::unicode::kSpaceCharacter)) {
-          continue;
-        }
-        if (face->state == FontFace::kUnrequestedState) {
-          RequestFont(i, face);
-        }
+      if (font_list_font.state() == FontListFont::kUnrequestedState) {
+        RequestFont(i);
+      }
 
-        if (face->state == FontFace::kLoadedState) {
-          primary_font_ = face->font;
-          DCHECK(primary_font_);
-          return primary_font_;
-        }
+      if (font_list_font.state() == FontListFont::kLoadedState) {
+        primary_font_ = font_list_font.font();
+        break;
       }
     }
   }
@@ -309,41 +261,40 @@
   return primary_font_;
 }
 
-void FontList::RequestFont(size_t index, FontFace* used_face) {
+void FontList::RequestFont(size_t index) {
   FontListFont& font_list_font = fonts_[index];
-  FontFace::State state;
+  FontListFont::State state;
 
   // Request the font from the font cache; the state of the font will be set
   // during the call.
   scoped_refptr<render_tree::Font> render_tree_font = font_cache_->TryGetFont(
-      font_list_font.family_name, style_, size_, &state, used_face->entry);
+      font_list_font.family_name(), style_, size_, &state);
 
-  if (state == FontFace::kLoadedState) {
+  if (state == FontListFont::kLoadedState) {
     DCHECK(render_tree_font.get() != NULL);
 
     // Walk all of the fonts in the list preceding the loaded font. If they have
-    // the same typeface as the loaded font, then do not create a new face.
-    // There's no reason to have multiple fonts in the list with the same
-    // typeface.
+    // the same typeface as the loaded font, then set the font list font as a
+    // duplicate. There's no reason to have multiple fonts in the list with the
+    // same typeface.
     render_tree::TypefaceId typeface_id = render_tree_font->GetTypefaceId();
     for (size_t i = 0; i < index; ++i) {
       FontListFont& check_font = fonts_[i];
-      for (auto face : check_font.faces) {
-        if (face->state == FontFace::kLoadedState &&
-            face->font->GetTypefaceId() == typeface_id) {
-          used_face->state = FontFace::kDuplicateState;
-        }
+      if (check_font.state() == FontListFont::kLoadedState &&
+          check_font.font()->GetTypefaceId() == typeface_id) {
+        font_list_font.set_state(FontListFont::kDuplicateState);
+        break;
       }
     }
 
     // If this font wasn't a duplicate, then its time to initialize its font
     // data. This font is now available to use.
-    if (used_face->state != FontFace::kDuplicateState) {
-      used_face->state = FontFace::kLoadedState;
-      used_face->font = render_tree_font;
+    if (font_list_font.state() != FontListFont::kDuplicateState) {
+      font_list_font.set_state(FontListFont::kLoadedState);
+      font_list_font.set_font(render_tree_font);
     }
   } else {
-    used_face->state = state;
+    font_list_font.set_state(state);
   }
 }
 
@@ -359,10 +310,10 @@
 
 void FontList::GenerateSpaceWidth() {
   if (!is_space_width_set_) {
-    render_tree::GlyphIndex space_glyph = render_tree::kInvalidGlyphIndex;
-    space_width_ =
-        GetCharacterFont(base::unicode::kSpaceCharacter, &space_glyph)
-            ->GetGlyphWidth(space_glyph);
+    const scoped_refptr<render_tree::Font>& primary_font = GetPrimaryFont();
+    render_tree::GlyphIndex space_glyph =
+        primary_font->GetGlyphForCharacter(' ');
+    space_width_ = primary_font->GetGlyphWidth(space_glyph);
     if (space_width_ == 0) {
       DLOG(WARNING) << "Font being used with space width of 0!";
     }
diff --git a/src/cobalt/dom/font_list.h b/src/cobalt/dom/font_list.h
index 6676b1f..8c95c94 100644
--- a/src/cobalt/dom/font_list.h
+++ b/src/cobalt/dom/font_list.h
@@ -22,7 +22,6 @@
 #include "base/containers/hash_tables.h"
 #include "base/containers/small_map.h"
 #include "base/memory/ref_counted.h"
-#include "cobalt/dom/font_face.h"
 #include "cobalt/math/rect_f.h"
 #include "cobalt/render_tree/font.h"
 #include "cobalt/render_tree/font_provider.h"
@@ -34,11 +33,12 @@
 
 class FontCache;
 
-// A font-face for a font-family. It has an internal state, which lets the font
-// list know whether or not the font has already been requested, and if so,
-// whether or not it was available. |font_| will only be non-NULL in the case
-// where |state_| is set to |kLoadedState|.
-struct FontFace {
+// A specific font family within a font list. It has an internal state, which
+// lets the font list know whether or not the font has already been requested,
+// and if so, whether or not it was available. |font_| and |character_map_|
+// will only be non-NULL in the case where |state_| is set to |kLoadedState|.
+class FontListFont {
+ public:
   enum State {
     kUnrequestedState,
     kLoadingWithTimerActiveState,
@@ -47,29 +47,27 @@
     kUnavailableState,
     kDuplicateState,
   };
-  State state = kUnrequestedState;
-  const FontFaceStyleSet::Entry* entry = nullptr;
 
-  // The render_tree::Font obtained via the font cache using |family_name_| in
-  // font list font, along with |style_| and |size_| from the containing font
-  // list, and the unicode range needed for the requested character. It is only
-  // non-NULL in the case where |state_| is set to |kLoadedState|.
-  scoped_refptr<render_tree::Font> font;
-};
-
-// A specific font family within a font list. A family may have more than one
-// FontFace if the FontFaces have different unicode ranges specified.
-struct FontListFont {
   explicit FontListFont(const std::string& family_name)
-      : family_name(family_name) {}
+      : family_name_(family_name), state_(kUnrequestedState) {}
 
-  std::string family_name;
+  const std::string& family_name() const { return family_name_; }
+
+  State state() const { return state_; }
+  void set_state(State state) { state_ = state; }
+
+  const scoped_refptr<render_tree::Font>& font() const { return font_; }
+  void set_font(const scoped_refptr<render_tree::Font>& font) { font_ = font; }
+
+ private:
+  std::string family_name_;
+  State state_;
 
   // The render_tree::Font obtained via the font cache using |family_name_| in
   // font list font, along with |style_| and |size_| from the containing font
   // list. It is only non-NULL in the case where |state_| is set to
   // |kLoadedState|.
-  std::vector<FontFace*> faces;
+  scoped_refptr<render_tree::Font> font_;
 };
 
 // The key used for maps with a |FontList| value. It is also used for
@@ -180,8 +178,6 @@
       int32 utf32_character, render_tree::GlyphIndex* glyph_index) override;
 
  private:
-  ~FontList() override;
-
   const scoped_refptr<render_tree::Font>& GetFallbackCharacterFont(
       int32 utf32_character, render_tree::GlyphIndex* glyph_index);
 
@@ -195,10 +191,9 @@
   const scoped_refptr<render_tree::Font>& GetPrimaryFont();
 
   // Request a font from the font cache and update its state depending on the
-  // results of the request. If the font is successfully set, then its |font_|
-  // is non-NULL after this call.
-  void RequestFont(size_t index, FontFace* face);
-
+  // results of the request. If the font is successfully set, then both its
+  // |font_| and |character_map_| are non-NULL after this call.
+  void RequestFont(size_t index);
 
   // Lazily generates the ellipsis font and ellipsis width. If it is already
   // generated then it immediately returns.
diff --git a/src/cobalt/dom/window.cc b/src/cobalt/dom/window.cc
index 34e0c86..6fde6ea 100644
--- a/src/cobalt/dom/window.cc
+++ b/src/cobalt/dom/window.cc
@@ -198,7 +198,7 @@
 #endif
   document_->AddObserver(relay_on_load_event_.get());
   html_element_context_->application_lifecycle_state()->AddObserver(this);
-  SetCamera3D(camera_3d);
+  UpdateCamera3D(camera_3d);
 
   // Document load start is deferred from this constructor so that we can be
   // guaranteed that this Window object is fully constructed before document
@@ -393,7 +393,7 @@
 
 int Window::SetTimeout(const WindowTimers::TimerCallbackArg& handler,
                        int timeout) {
-  DLOG_IF(WARNING, timeout < 0)
+  LOG_IF(WARNING, timeout < 0)
       << "Window::SetTimeout received negative timeout: " << timeout;
   timeout = std::max(timeout, 0);
 
@@ -401,7 +401,7 @@
   if (window_timers_) {
     return_value = window_timers_->SetTimeout(handler, timeout);
   } else {
-    DLOG(WARNING) << "window_timers_ does not exist.  Already destroyed?";
+    LOG(WARNING) << "window_timers_ does not exist.  Already destroyed?";
   }
 
   return return_value;
@@ -411,13 +411,13 @@
   if (window_timers_) {
     window_timers_->ClearTimeout(handle);
   } else {
-    DLOG(WARNING) << "window_timers_ does not exist.  Already destroyed?";
+    LOG(WARNING) << "window_timers_ does not exist.  Already destroyed?";
   }
 }
 
 int Window::SetInterval(const WindowTimers::TimerCallbackArg& handler,
                         int timeout) {
-  DLOG_IF(WARNING, timeout < 0)
+  LOG_IF(WARNING, timeout < 0)
       << "Window::SetInterval received negative timeout: " << timeout;
   timeout = std::max(timeout, 0);
 
@@ -425,7 +425,7 @@
   if (window_timers_) {
     return_value = window_timers_->SetInterval(handler, timeout);
   } else {
-    DLOG(WARNING) << "window_timers_ does not exist.  Already destroyed?";
+    LOG(WARNING) << "window_timers_ does not exist.  Already destroyed?";
   }
 
   return return_value;
@@ -629,9 +629,15 @@
   }
 }
 
-void Window::SetCamera3D(const scoped_refptr<input::Camera3D>& camera_3d) {
-  camera_3d_ = new Camera3D(camera_3d);
-  camera_3d_->StartOrientationEvents(base::AsWeakPtr(this));
+void Window::UpdateCamera3D(const scoped_refptr<input::Camera3D>& camera_3d) {
+  if (camera_3d_ && camera_3d_->impl()) {
+    // Update input object for existing camera.
+    camera_3d_->impl()->SetInput(camera_3d);
+  } else {
+    // Create a new camera which uses the given input camera object.
+    camera_3d_ = new Camera3D(camera_3d);
+    camera_3d_->StartOrientationEvents(base::AsWeakPtr(this));
+  }
 }
 
 void Window::OnWindowFocusChanged(bool has_focus) {
diff --git a/src/cobalt/dom/window.h b/src/cobalt/dom/window.h
index 81467b4..d6bf1ab 100644
--- a/src/cobalt/dom/window.h
+++ b/src/cobalt/dom/window.h
@@ -347,7 +347,7 @@
 
   void SetSize(cssom::ViewportSize size);
 
-  void SetCamera3D(const scoped_refptr<input::Camera3D>& camera_3d);
+  void UpdateCamera3D(const scoped_refptr<input::Camera3D>& camera_3d);
 
   void set_web_media_player_factory(
       media::WebMediaPlayerFactory* web_media_player_factory) {
diff --git a/src/cobalt/input/camera_3d.h b/src/cobalt/input/camera_3d.h
index d282e4d..3fa1fb2 100644
--- a/src/cobalt/input/camera_3d.h
+++ b/src/cobalt/input/camera_3d.h
@@ -70,6 +70,9 @@
   // Resets camera to default orientation.
   virtual void Reset() {}
 
+  // Adopt input object from the given Camera3D.
+  virtual void SetInput(const scoped_refptr<Camera3D>& other) {}
+
   virtual ~Camera3D() {}
 
   template <typename FloatType>
diff --git a/src/cobalt/input/camera_3d_input_poller.cc b/src/cobalt/input/camera_3d_input_poller.cc
index 2101015..5b7a53c 100644
--- a/src/cobalt/input/camera_3d_input_poller.cc
+++ b/src/cobalt/input/camera_3d_input_poller.cc
@@ -19,6 +19,7 @@
 #include <algorithm>
 #include <cmath>
 
+#include "cobalt/base/polymorphic_downcast.h"
 #include "cobalt/input/create_default_camera_3d.h"
 #include "third_party/glm/glm/gtc/matrix_transform.hpp"
 #include "third_party/glm/glm/gtc/quaternion.hpp"
@@ -91,6 +92,13 @@
   yaw_in_radians_ = 0.0f;
 }
 
+void Camera3DInputPoller::SetInput(const scoped_refptr<Camera3D>& other) {
+  base::AutoLock lock(mutex_);
+  Camera3DInputPoller* other_camera =
+      base::polymorphic_downcast<Camera3DInputPoller*>(other.get());
+  input_poller_ = other_camera ? other_camera->input_poller_ : nullptr;
+}
+
 void Camera3DInputPoller::AccumulateOrientation() {
   if (!input_poller_) {
     // Nothing to do if no input poller was provided.
diff --git a/src/cobalt/input/camera_3d_input_poller.h b/src/cobalt/input/camera_3d_input_poller.h
index ceff058..c6050ab 100644
--- a/src/cobalt/input/camera_3d_input_poller.h
+++ b/src/cobalt/input/camera_3d_input_poller.h
@@ -52,6 +52,8 @@
 
   void Reset() override;
 
+  void SetInput(const scoped_refptr<Camera3D>& other) override;
+
  private:
   struct KeycodeMappingInfo {
     KeycodeMappingInfo() : axis(0), degrees_per_second(0.0f) {}
diff --git a/src/cobalt/layout_tests/layout_tests.cc b/src/cobalt/layout_tests/layout_tests.cc
index 028de65..1f25893 100644
--- a/src/cobalt/layout_tests/layout_tests.cc
+++ b/src/cobalt/layout_tests/layout_tests.cc
@@ -128,9 +128,8 @@
   // room for tests to maneuver within and speed at which pixel tests can be
   // done.
   const ViewportSize kDefaultViewportSize(640, 360);
-  ViewportSize viewport_size = test_info.viewport_size
-                                   ? *test_info.viewport_size
-                                   : kDefaultViewportSize;
+  ViewportSize viewport_size =
+      test_info.viewport_size ? *test_info.viewport_size : kDefaultViewportSize;
 
   renderer::RenderTreePixelTester pixel_tester(
       viewport_size.width_height(), GetTestInputRootDirectory(),
@@ -222,30 +221,26 @@
 }
 
 // Cobalt-specific test cases.
-INSTANTIATE_TEST_CASE_P(
-    CobaltSpecificLayoutTests, Layout,
-    ::testing::ValuesIn(EnumerateLayoutTests("cobalt")),
-    GetTestName());
+INSTANTIATE_TEST_CASE_P(CobaltSpecificLayoutTests, Layout,
+                        ::testing::ValuesIn(EnumerateLayoutTests("cobalt")),
+                        GetTestName());
 // Custom CSS 2.1 (https://www.w3.org/TR/CSS21/) test cases.
-INSTANTIATE_TEST_CASE_P(
-    CSS21LayoutTests, Layout,
-    ::testing::ValuesIn(EnumerateLayoutTests("css-2-1")),
-    GetTestName());
+INSTANTIATE_TEST_CASE_P(CSS21LayoutTests, Layout,
+                        ::testing::ValuesIn(EnumerateLayoutTests("css-2-1")),
+                        GetTestName());
 // Custom CSS Background (https://www.w3.org/TR/css3-background/) test cases.
 INSTANTIATE_TEST_CASE_P(
     CSSBackground3LayoutTests, Layout,
     ::testing::ValuesIn(EnumerateLayoutTests("css3-background")),
     GetTestName());
 // Custom CSS Color (https://www.w3.org/TR/css3-color/) test cases.
-INSTANTIATE_TEST_CASE_P(
-    CSSColor3LayoutTests, Layout,
-    ::testing::ValuesIn(EnumerateLayoutTests("css3-color")),
-    GetTestName());
+INSTANTIATE_TEST_CASE_P(CSSColor3LayoutTests, Layout,
+                        ::testing::ValuesIn(EnumerateLayoutTests("css3-color")),
+                        GetTestName());
 // Custom CSS Images (https://www.w3.org/TR/css3-images/) test cases.
 INSTANTIATE_TEST_CASE_P(
     CSSImages3LayoutTests, Layout,
-    ::testing::ValuesIn(EnumerateLayoutTests("css3-images")),
-    GetTestName());
+    ::testing::ValuesIn(EnumerateLayoutTests("css3-images")), GetTestName());
 // Custom CSS Media Queries (https://www.w3.org/TR/css3-mediaqueries/) test
 // cases.
 INSTANTIATE_TEST_CASE_P(
@@ -253,16 +248,14 @@
     ::testing::ValuesIn(EnumerateLayoutTests("css3-mediaqueries")),
     GetTestName());
 // Custom CSS Text (https://www.w3.org/TR/css-text-3/) test cases.
-INSTANTIATE_TEST_CASE_P(
-    CSSText3LayoutTests, Layout,
-    ::testing::ValuesIn(EnumerateLayoutTests("css-text-3")),
-    GetTestName());
+INSTANTIATE_TEST_CASE_P(CSSText3LayoutTests, Layout,
+                        ::testing::ValuesIn(EnumerateLayoutTests("css-text-3")),
+                        GetTestName());
 // Custom CSS Transform (http://https://www.w3.org/TR/css-transforms/)
 // test cases.
 INSTANTIATE_TEST_CASE_P(
     CSSTransformsLayoutTests, Layout,
-    ::testing::ValuesIn(EnumerateLayoutTests("css-transforms")),
-    GetTestName());
+    ::testing::ValuesIn(EnumerateLayoutTests("css-transforms")), GetTestName());
 // Custom CSS Transition
 // (https://www.w3.org/TR/2013/WD-css3-transitions-20131119/)
 // test cases.
@@ -279,15 +272,13 @@
     GetTestName());
 // Custom bidi text (http://www.unicode.org/reports/tr9/)
 // (https://www.w3.org/TR/CSS21/visuren.html#direction) test cases.
-INSTANTIATE_TEST_CASE_P(
-    BidiLayoutTests, Layout,
-    ::testing::ValuesIn(EnumerateLayoutTests("bidi")),
-    GetTestName());
+INSTANTIATE_TEST_CASE_P(BidiLayoutTests, Layout,
+                        ::testing::ValuesIn(EnumerateLayoutTests("bidi")),
+                        GetTestName());
 // Custom text shaping test cases.
 INSTANTIATE_TEST_CASE_P(
     TextShapingLayoutTests, Layout,
-    ::testing::ValuesIn(EnumerateLayoutTests("text-shaping")),
-    GetTestName());
+    ::testing::ValuesIn(EnumerateLayoutTests("text-shaping")), GetTestName());
 // Custom CSS Conditional (https://www.w3.org/TR/css3-conditional/) test cases.
 INSTANTIATE_TEST_CASE_P(
     CSSConditional3LayoutTests, Layout,
@@ -298,25 +289,22 @@
     CSSFlexbox3LayoutTests, Layout,
     ::testing::ValuesIn(EnumerateLayoutTests("css3-flexbox")), GetTestName());
 // Custom CSS Font (https://www.w3.org/TR/css3-fonts/) test cases.
-INSTANTIATE_TEST_CASE_P(
-    CSS3FontsLayoutTests, Layout,
-    ::testing::ValuesIn(EnumerateLayoutTests("css3-fonts")),
-    GetTestName());
+INSTANTIATE_TEST_CASE_P(CSS3FontsLayoutTests, Layout,
+                        ::testing::ValuesIn(EnumerateLayoutTests("css3-fonts")),
+                        GetTestName());
 // Custom CSS Text Decor (https://www.w3.org/TR/css-text-decor-3/) test cases.
 INSTANTIATE_TEST_CASE_P(
     CSS3TextDecorLayoutTests, Layout,
     ::testing::ValuesIn(EnumerateLayoutTests("css3-text-decor")),
     GetTestName());
 // Custom CSS UI (https://www.w3.org/TR/css3-ui/) test cases.
-INSTANTIATE_TEST_CASE_P(
-    CSS3UILayoutTests, Layout,
-    ::testing::ValuesIn(EnumerateLayoutTests("css3-ui")),
-    GetTestName());
+INSTANTIATE_TEST_CASE_P(CSS3UILayoutTests, Layout,
+                        ::testing::ValuesIn(EnumerateLayoutTests("css3-ui")),
+                        GetTestName());
 // Custom CSS Value (https://www.w3.org/TR/css3-values/) test cases.
 INSTANTIATE_TEST_CASE_P(
     CSS3ValuesLayoutTests, Layout,
-    ::testing::ValuesIn(EnumerateLayoutTests("css3-values")),
-    GetTestName());
+    ::testing::ValuesIn(EnumerateLayoutTests("css3-values")), GetTestName());
 // Custom incremental layout test cases.
 INSTANTIATE_TEST_CASE_P(
     IncrementalLayoutLayoutTests, Layout,
@@ -324,10 +312,9 @@
     GetTestName());
 // Custom CSSOM view (https://www.w3.org/TR/2013/WD-cssom-view-20131217/)
 // test cases.
-INSTANTIATE_TEST_CASE_P(
-    CSSOMViewLayoutTests, Layout,
-    ::testing::ValuesIn(EnumerateLayoutTests("cssom-view")),
-    GetTestName());
+INSTANTIATE_TEST_CASE_P(CSSOMViewLayoutTests, Layout,
+                        ::testing::ValuesIn(EnumerateLayoutTests("cssom-view")),
+                        GetTestName());
 // Custom DOM (https://dom.spec.whatwg.org/) test cases.
 INSTANTIATE_TEST_CASE_P(DOMLayoutTests, Layout,
                         ::testing::ValuesIn(EnumerateLayoutTests("dom")),
@@ -341,11 +328,9 @@
 
 // JavaScript HTML5 WebAPIs (https://www.w3.org/TR/html50/webappapis.html) test
 // cases.
-INSTANTIATE_TEST_CASE_P(
-    WebAppAPIsLayoutTests, Layout,
-    ::testing::ValuesIn(EnumerateLayoutTests("webappapis")),
-    GetTestName());
-
+INSTANTIATE_TEST_CASE_P(WebAppAPIsLayoutTests, Layout,
+                        ::testing::ValuesIn(EnumerateLayoutTests("webappapis")),
+                        GetTestName());
 // JavaScript HTML5 APIs that describe requestAnimationFrame().
 //   https://www.w3.org/TR/animation-timing/
 INSTANTIATE_TEST_CASE_P(
@@ -356,8 +341,7 @@
 // Problematic test cases found through cluster-fuzz.
 INSTANTIATE_TEST_CASE_P(
     ClusterFuzzLayoutTests, Layout,
-    ::testing::ValuesIn(EnumerateLayoutTests("cluster-fuzz")),
-    GetTestName());
+    ::testing::ValuesIn(EnumerateLayoutTests("cluster-fuzz")), GetTestName());
 
 // Intersection Observer API (https://www.w3.org/TR/intersection-observer/) test
 // cases
@@ -366,6 +350,11 @@
     ::testing::ValuesIn(EnumerateLayoutTests("intersection-observer")),
     GetTestName());
 
+// webp background image test cases.
+INSTANTIATE_TEST_CASE_P(WebPLayoutTests, Layout,
+                        ::testing::ValuesIn(EnumerateLayoutTests("webp")),
+                        GetTestName());
+
 // Blitter does not support Skottie.
 #if !SB_HAS(BLITTER)
 // Lottie (https://github.com/LottieFiles/lottie-player) test cases
@@ -377,17 +366,14 @@
 // Disable on Windows until network stack is implemented.
 #if !defined(COBALT_WIN)
 // Content Security Policy test cases.
-INSTANTIATE_TEST_CASE_P(
-    ContentSecurityPolicyTests, Layout,
-    ::testing::ValuesIn(EnumerateLayoutTests("csp")),
-    GetTestName());
+INSTANTIATE_TEST_CASE_P(ContentSecurityPolicyTests, Layout,
+                        ::testing::ValuesIn(EnumerateLayoutTests("csp")),
+                        GetTestName());
 #endif  // !defined(COBALT_WIN)
 
 // Pixel-perfect tests.
 INSTANTIATE_TEST_CASE_P(
     CobaltPixelTests, LayoutExact,
-    ::testing::ValuesIn(EnumerateLayoutTests("cobalt-pixel")),
-    GetTestName());
-
+    ::testing::ValuesIn(EnumerateLayoutTests("cobalt-pixel")), GetTestName());
 }  // namespace layout_tests
 }  // namespace cobalt
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/4-5-prefer-later-face-with-close-style-over-earlier-face-with-unicode-range-expected.png b/src/cobalt/layout_tests/testdata/css3-fonts/4-5-prefer-later-face-with-close-style-over-earlier-face-with-unicode-range-expected.png
deleted file mode 100644
index 2cd7bd0..0000000
--- a/src/cobalt/layout_tests/testdata/css3-fonts/4-5-prefer-later-face-with-close-style-over-earlier-face-with-unicode-range-expected.png
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/4-5-prefer-later-face-with-close-style-over-earlier-face-with-unicode-range.html b/src/cobalt/layout_tests/testdata/css3-fonts/4-5-prefer-later-face-with-close-style-over-earlier-face-with-unicode-range.html
deleted file mode 100644
index c257b7e..0000000
--- a/src/cobalt/layout_tests/testdata/css3-fonts/4-5-prefer-later-face-with-close-style-over-earlier-face-with-unicode-range.html
+++ /dev/null
@@ -1,37 +0,0 @@
-<!DOCTYPE html>
-<!--
- | Unicode range test. Tests that font style takes precedence over a matching unicode-range.
- | https://www.w3.org/TR/css3-fonts/#font-prop-desc
- -->
-<html>
-<head>
-  <style>
-    @font-face {
-        font-family: 'TestFont';
-        src: local('Noto Serif');
-        unicode-range: U+4d-5a; /* =, M-Z */
-        font-weight: bold;
-    }
-    @font-face {
-        font-family: 'TestFont';
-        src: local('Roboto');
-        font-weight: normal;
-    }
-    @font-face {
-        font-family: 'TestFont2';
-        src: local('Dancing Script');
-        font-weight: bold;
-    }
-    body {
-      background-color: black;
-      font-size: 75px;
-      color: #fff;
-      font-weight: bold;
-      font-family: 'TestFont', 'TestFont2'; /*TestFont2 should be used. */
-    }
-  </style>
-</head>
-<body>
-    <div>Hello</div>
-</body>
-</html>
\ No newline at end of file
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/4-5-use-correct-font-with-unicode-range-expected.png b/src/cobalt/layout_tests/testdata/css3-fonts/4-5-use-correct-font-with-unicode-range-expected.png
deleted file mode 100644
index c32526d..0000000
--- a/src/cobalt/layout_tests/testdata/css3-fonts/4-5-use-correct-font-with-unicode-range-expected.png
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/4-5-use-correct-font-with-unicode-range.html b/src/cobalt/layout_tests/testdata/css3-fonts/4-5-use-correct-font-with-unicode-range.html
deleted file mode 100644
index 3ff6b53..0000000
--- a/src/cobalt/layout_tests/testdata/css3-fonts/4-5-use-correct-font-with-unicode-range.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!DOCTYPE html>
-<!--
- | Unicode range test. Tests multiple unicode-range values and multiple
- | font-families with the same style but different unicode ranges and sources.
- | https://www.w3.org/TR/css3-fonts/#font-prop-desc
- -->
-<html>
-<head>
-  <style>
-    @font-face {
-        font-family: 'TestFont';
-        src: local('Dancing Script');
-        unicode-range: U+3d, U+4d-5a; /* =, M-Z */
-    }
-    @font-face {
-        font-family: 'TestFont';
-        src: local('Noto Serif');
-        unicode-range: U+26; /* & */
-    }
-    body {
-      background-color: black;
-      font-size: 75px;
-      color: #fff;
-      font-family: 'TestFont', 'Roboto';
-    }
-  </style>
-</head>
-<body>
-    <div>Me & You = Us</div>
-</body>
-</html>
\ No newline at end of file
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/layout_tests.txt b/src/cobalt/layout_tests/testdata/css3-fonts/layout_tests.txt
index 8c922d4..aac78cf 100644
--- a/src/cobalt/layout_tests/testdata/css3-fonts/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/layout_tests.txt
@@ -4,8 +4,6 @@
 4-3-use-first-available-local-font-face
 4-3-use-next-font-family-if-font-face-sources-unavailable
 4-4-use-correct-style-in-font-face-set
-4-5-use-correct-font-with-unicode-range
-4-5-prefer-later-face-with-close-style-over-earlier-face-with-unicode-range
 5-2-use-correct-style-in-font-family
 5-2-use-first-available-listed-font-family
 5-2-use-numerical-font-weights-in-family-face-matching
diff --git a/src/cobalt/layout_tests/testdata/webp/layout_tests.txt b/src/cobalt/layout_tests/testdata/webp/layout_tests.txt
new file mode 100644
index 0000000..a7b4e0c
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/webp/layout_tests.txt
@@ -0,0 +1 @@
+static-webp-background-image
diff --git a/src/cobalt/layout_tests/testdata/webp/static-webp-background-image-expected.png b/src/cobalt/layout_tests/testdata/webp/static-webp-background-image-expected.png
new file mode 100644
index 0000000..edcc28c
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/webp/static-webp-background-image-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/webp/static-webp-background-image.html b/src/cobalt/layout_tests/testdata/webp/static-webp-background-image.html
new file mode 100644
index 0000000..92d307b
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/webp/static-webp-background-image.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!--
+ | This test checks if a static webp background image is rendered as expected.
+ -->
+<html>
+
+<head>
+    <style>
+        div {
+            height: 360px;
+            width: 480px;
+        }
+    </style>
+    <script>
+        if (window.testRunner) {
+            window.testRunner.waitUntilDone();
+        }
+
+        var image = new Image();
+        var image_name = 'static-webp-image.webp';
+
+        image.onload = function () {
+            var cobalt = document.getElementById('image');
+            cobalt.style.background = 'url(' + image_name + ')';
+
+            if (window.testRunner) {
+                window.testRunner.notifyDone();
+            }
+        }
+
+        image.src = image_name;
+
+    </script>
+</head>
+
+<body>
+    <div id='image'></div>
+</body>
+
+</html>
diff --git a/src/cobalt/layout_tests/testdata/webp/static-webp-image.webp b/src/cobalt/layout_tests/testdata/webp/static-webp-image.webp
new file mode 100644
index 0000000..cca0a37
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/webp/static-webp-image.webp
Binary files differ
diff --git a/src/cobalt/layout_tests/web_platform_tests.cc b/src/cobalt/layout_tests/web_platform_tests.cc
index 5edd114..be7348d 100644
--- a/src/cobalt/layout_tests/web_platform_tests.cc
+++ b/src/cobalt/layout_tests/web_platform_tests.cc
@@ -79,6 +79,15 @@
   }
 };
 
+// FakeResourceProviderStub has the identical behavior as ResourceProviderStub,
+// except the GetTypeId, which makes the ImageDecode not to create the
+// FailureImageDecoder based on the TypeId Check.
+class FakeResourceProviderStub : public render_tree::ResourceProviderStub {
+  base::TypeId GetTypeId() const override {
+    return base::GetTypeId<FakeResourceProviderStub>();
+  }
+};
+
 // Match the enums from testharness.js.
 enum TestStatus {
   kPass = 0,
@@ -190,7 +199,7 @@
   network::NetworkModule network_module(net_options);
 
   // Media module
-  render_tree::ResourceProviderStub resource_provider;
+  FakeResourceProviderStub resource_provider;
   std::unique_ptr<media::MediaModule> media_module(
       new media::MediaModule(NULL, &resource_provider));
   std::unique_ptr<media::CanPlayTypeHandler> can_play_type_handler(
diff --git a/src/cobalt/loader/image/animated_webp_image.cc b/src/cobalt/loader/image/animated_webp_image.cc
index 7b8dffd..ed0b959 100644
--- a/src/cobalt/loader/image/animated_webp_image.cc
+++ b/src/cobalt/loader/image/animated_webp_image.cc
@@ -238,13 +238,11 @@
 void DecodeError(const base::Optional<std::string>& error) {
   if (error) LOG(ERROR) << *error;
 }
-
 }  // namespace
 
 bool AnimatedWebPImage::DecodeOneFrame(int frame_index) {
   TRACE_EVENT0("cobalt::loader::image", "AnimatedWebPImage::DecodeOneFrame()");
   TRACK_MEMORY_SCOPE("Rendering");
-  DCHECK(task_runner_->BelongsToCurrentThread());
   lock_.AssertAcquired();
 
   WebPIterator webp_iterator;
@@ -407,6 +405,32 @@
   return loop_count_ == 1 && current_frame_index_ == frame_count_;
 }
 
+scoped_refptr<render_tree::Image> AnimatedWebPImage::GetFrameForDebugging(
+    int target_frame) {
+  TRACE_EVENT0("cobalt::loader::image",
+               "AnimatedWebPImage::GetFrameForDebugging()");
+
+  base::AutoLock lock(lock_);
+  DCHECK(!should_dispose_previous_frame_to_background_ && !current_canvas_);
+  DCHECK(current_frame_index_ == 0 && !is_playing_);
+
+  if (target_frame <= 0 || target_frame > frame_count_) {
+    LOG(WARNING) << "Invalid frame index: " << target_frame;
+    return nullptr;
+  }
+
+  for (int frame = 1; frame <= target_frame; ++frame) {
+    DecodeOneFrame(frame);
+  }
+
+  // Reset states when GetFrameForDebugging finishes
+  should_dispose_previous_frame_to_background_ = false;
+  scoped_refptr<render_tree::Image> target_canvas = current_canvas_;
+  current_canvas_ = nullptr;
+
+  return target_canvas;
+}
+
 }  // namespace image
 }  // namespace loader
 }  // namespace cobalt
diff --git a/src/cobalt/loader/image/animated_webp_image.h b/src/cobalt/loader/image/animated_webp_image.h
index c0fd222..135bf6a 100644
--- a/src/cobalt/loader/image/animated_webp_image.h
+++ b/src/cobalt/loader/image/animated_webp_image.h
@@ -63,6 +63,9 @@
 
   void AppendChunk(const uint8* data, size_t input_byte);
 
+  // Returns the render image of the frame for debugging
+  scoped_refptr<render_tree::Image> GetFrameForDebugging(int target_frame);
+
  private:
   ~AnimatedWebPImage() override;
 
diff --git a/src/cobalt/loader/image/image_decoder.cc b/src/cobalt/loader/image/image_decoder.cc
index 68782a3..be9856e 100644
--- a/src/cobalt/loader/image/image_decoder.cc
+++ b/src/cobalt/loader/image/image_decoder.cc
@@ -56,6 +56,9 @@
 
 // Returns true if the ResourceProvider is ResourceProviderStub.
 bool IsResourceProviderStub(render_tree::ResourceProvider* resource_provider) {
+  if (resource_provider == nullptr) {
+    return true;
+  }
   return resource_provider->GetTypeId() ==
          base::GetTypeId<render_tree::ResourceProviderStub>();
 }
@@ -108,6 +111,7 @@
       state_(resource_provider_ ? kWaitingForHeader : kSuspended),
       is_deletion_pending_(false) {
   signature_cache_.position = 0;
+  use_failure_image_decoder_ = IsResourceProviderStub(resource_provider);
 }
 
 ImageDecoder::ImageDecoder(
@@ -124,6 +128,7 @@
       state_(resource_provider_ ? kWaitingForHeader : kSuspended),
       is_deletion_pending_(false) {
   signature_cache_.position = 0;
+  use_failure_image_decoder_ = IsResourceProviderStub(resource_provider);
 }
 
 LoadResponseType ImageDecoder::OnResponseStarted(
@@ -241,11 +246,7 @@
   DCHECK_EQ(state_, kSuspended);
   DCHECK(!resource_provider_);
   DCHECK(resource_provider);
-  if (IsResourceProviderStub(resource_provider)) {
-    use_failure_image_decoder_ = true;
-  } else {
-    use_failure_image_decoder_ = false;
-  }
+  use_failure_image_decoder_ = IsResourceProviderStub(resource_provider);
   state_ = kWaitingForHeader;
   resource_provider_ = resource_provider;
 }
diff --git a/src/cobalt/loader/image/image_decoder_mock.h b/src/cobalt/loader/image/image_decoder_mock.h
new file mode 100644
index 0000000..ad8ff3a
--- /dev/null
+++ b/src/cobalt/loader/image/image_decoder_mock.h
@@ -0,0 +1,119 @@
+// 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_LOADER_IMAGE_IMAGE_DECODER_MOCK_H_
+#define COBALT_LOADER_IMAGE_IMAGE_DECODER_MOCK_H_
+
+#include <memory>
+#include <string>
+
+#include "cobalt/loader/image/image_decoder.h"
+#include "cobalt/render_tree/resource_provider_stub.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace loader {
+namespace image {
+
+struct MockImageDecoderCallback {
+  void SuccessCallback(const scoped_refptr<Image>& value) { image = value; }
+
+  MOCK_METHOD1(LoadCompleteCallback,
+               void(const base::Optional<std::string>& message));
+
+  scoped_refptr<Image> image;
+};
+
+class MockImageDecoder : public Decoder {
+ public:
+  MockImageDecoder();
+  explicit MockImageDecoder(render_tree::ResourceProvider* resource_provider);
+  ~MockImageDecoder() override {}
+
+  LoadResponseType OnResponseStarted(
+      Fetcher* fetcher,
+      const scoped_refptr<net::HttpResponseHeaders>& headers) override;
+
+  void DecodeChunk(const char* data, size_t size) override;
+
+  void Finish() override;
+  bool Suspend() override;
+  void Resume(render_tree::ResourceProvider* resource_provider) override;
+
+  scoped_refptr<Image> image();
+
+  void ExpectCallWithError(const base::Optional<std::string>& error);
+
+ protected:
+  std::unique_ptr<render_tree::ResourceProviderStub> resource_provider_stub_;
+  render_tree::ResourceProvider* resource_provider_;
+  base::NullDebuggerHooks debugger_hooks_;
+  ::testing::StrictMock<MockImageDecoderCallback> image_decoder_callback_;
+  std::unique_ptr<Decoder> image_decoder_;
+};
+
+MockImageDecoder::MockImageDecoder()
+    : resource_provider_stub_(new render_tree::ResourceProviderStub()),
+      resource_provider_(resource_provider_stub_.get()) {
+  image_decoder_.reset(new ImageDecoder(
+      resource_provider_, debugger_hooks_,
+      base::Bind(&MockImageDecoderCallback::SuccessCallback,
+                 base::Unretained(&image_decoder_callback_)),
+      base::Bind(&MockImageDecoderCallback::LoadCompleteCallback,
+                 base::Unretained(&image_decoder_callback_))));
+}
+
+MockImageDecoder::MockImageDecoder(
+    render_tree::ResourceProvider* resource_provider)
+    : resource_provider_(resource_provider) {
+  image_decoder_.reset(new ImageDecoder(
+      resource_provider_, debugger_hooks_,
+      base::Bind(&MockImageDecoderCallback::SuccessCallback,
+                 base::Unretained(&image_decoder_callback_)),
+      base::Bind(&MockImageDecoderCallback::LoadCompleteCallback,
+                 base::Unretained(&image_decoder_callback_))));
+}
+
+LoadResponseType MockImageDecoder::OnResponseStarted(
+    Fetcher* fetcher, const scoped_refptr<net::HttpResponseHeaders>& headers) {
+  return image_decoder_->OnResponseStarted(fetcher, headers);
+}
+
+void MockImageDecoder::DecodeChunk(const char* data, size_t size) {
+  image_decoder_->DecodeChunk(data, size);
+}
+
+void MockImageDecoder::Finish() { image_decoder_->Finish(); }
+
+bool MockImageDecoder::Suspend() { return image_decoder_->Suspend(); }
+
+void MockImageDecoder::Resume(
+    render_tree::ResourceProvider* resource_provider) {
+  image_decoder_->Resume(resource_provider);
+}
+
+scoped_refptr<Image> MockImageDecoder::image() {
+  return image_decoder_callback_.image;
+}
+
+void MockImageDecoder::ExpectCallWithError(
+    const base::Optional<std::string>& error) {
+  EXPECT_CALL(image_decoder_callback_, LoadCompleteCallback(error));
+}
+}  // namespace image
+}  // namespace loader
+};  // namespace cobalt
+
+#endif  // COBALT_LOADER_IMAGE_IMAGE_DECODER_MOCK_H_
diff --git a/src/cobalt/loader/image/image_decoder_test.cc b/src/cobalt/loader/image/image_decoder_test.cc
index 5aaf617..b893b23 100644
--- a/src/cobalt/loader/image/image_decoder_test.cc
+++ b/src/cobalt/loader/image/image_decoder_test.cc
@@ -12,8 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/loader/image/image_decoder.h"
-
 #include <memory>
 #include <string>
 #include <vector>
@@ -27,89 +25,14 @@
 #include "cobalt/base/cobalt_paths.h"
 #include "cobalt/base/polymorphic_downcast.h"
 #include "cobalt/loader/image/animated_webp_image.h"
+#include "cobalt/loader/image/image_decoder_mock.h"
 #include "cobalt/loader/image/jpeg_image_decoder.h"
-#include "cobalt/render_tree/resource_provider_stub.h"
 #include "starboard/configuration.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
 
 namespace cobalt {
 namespace loader {
 namespace image {
-
 namespace {
-
-struct MockImageDecoderCallback {
-  void SuccessCallback(const scoped_refptr<Image>& value) { image = value; }
-
-  MOCK_METHOD1(LoadCompleteCallback,
-               void(const base::Optional<std::string>& message));
-
-  scoped_refptr<Image> image;
-};
-
-class MockImageDecoder : public Decoder {
- public:
-  MockImageDecoder();
-  ~MockImageDecoder() override {}
-
-  LoadResponseType OnResponseStarted(
-      Fetcher* fetcher,
-      const scoped_refptr<net::HttpResponseHeaders>& headers) override;
-
-  void DecodeChunk(const char* data, size_t size) override;
-
-  void Finish() override;
-  bool Suspend() override;
-  void Resume(render_tree::ResourceProvider* resource_provider) override;
-
-  scoped_refptr<Image> image();
-
-  void ExpectCallWithError(const base::Optional<std::string>& error);
-
- protected:
-  render_tree::ResourceProviderStub resource_provider_;
-  base::NullDebuggerHooks debugger_hooks_;
-  ::testing::StrictMock<MockImageDecoderCallback> image_decoder_callback_;
-  std::unique_ptr<Decoder> image_decoder_;
-};
-
-MockImageDecoder::MockImageDecoder() {
-  image_decoder_.reset(new ImageDecoder(
-      &resource_provider_, debugger_hooks_,
-      base::Bind(&MockImageDecoderCallback::SuccessCallback,
-                 base::Unretained(&image_decoder_callback_)),
-      base::Bind(&MockImageDecoderCallback::LoadCompleteCallback,
-                 base::Unretained(&image_decoder_callback_))));
-}
-
-LoadResponseType MockImageDecoder::OnResponseStarted(
-    Fetcher* fetcher, const scoped_refptr<net::HttpResponseHeaders>& headers) {
-  return image_decoder_->OnResponseStarted(fetcher, headers);
-}
-
-void MockImageDecoder::DecodeChunk(const char* data, size_t size) {
-  image_decoder_->DecodeChunk(data, size);
-}
-
-void MockImageDecoder::Finish() { image_decoder_->Finish(); }
-
-bool MockImageDecoder::Suspend() { return image_decoder_->Suspend(); }
-
-void MockImageDecoder::Resume(
-    render_tree::ResourceProvider* resource_provider) {
-  image_decoder_->Resume(resource_provider);
-}
-
-scoped_refptr<Image> MockImageDecoder::image() {
-  return image_decoder_callback_.image;
-}
-
-void MockImageDecoder::ExpectCallWithError(
-    const base::Optional<std::string>& error) {
-  EXPECT_CALL(image_decoder_callback_, LoadCompleteCallback(error));
-}
-
 base::FilePath GetTestImagePath(const char* file_name) {
   base::FilePath data_directory;
   CHECK(base::PathService::Get(base::DIR_TEST_DATA, &data_directory));
@@ -227,13 +150,24 @@
   return CheckSameColor(pixels, size.width(), size.height(), test_color);
 }
 
+// FakeResourceProviderStub has the identical behavior as ResourceProviderStub,
+// except the GetTypeId, which makes the ImageDecode not to create the
+// FailureImageDecoder based on the TypeId Check.
+class FakeResourceProviderStub : public render_tree::ResourceProviderStub {
+  base::TypeId GetTypeId() const override {
+    return base::GetTypeId<FakeResourceProviderStub>();
+  }
+};
+
 }  // namespace
 
 // TODO: Test special images like the image has gAMA chunk information,
 // pngs with 16 bit depth, and large pngs.
 
 TEST(ImageDecoderTest, DecodeImageWithContentLength0) {
-  MockImageDecoder image_decoder;
+  std::unique_ptr<FakeResourceProviderStub> resource_provider(
+      new FakeResourceProviderStub());
+  MockImageDecoder image_decoder(resource_provider.get());
   image_decoder.ExpectCallWithError(
       std::string("No content returned, but expected some."));
 
@@ -257,7 +191,9 @@
 }
 
 TEST(ImageDecoderTest, DecodeNonImageTypeWithContentLength0) {
-  MockImageDecoder image_decoder;
+  std::unique_ptr<FakeResourceProviderStub> resource_provider(
+      new FakeResourceProviderStub());
+  MockImageDecoder image_decoder(resource_provider.get());
   image_decoder.ExpectCallWithError(std::string(
       "No content returned, but expected some. Not an image mime type."));
 
@@ -281,7 +217,9 @@
 }
 
 TEST(ImageDecoderTest, DecodeNonImageType) {
-  MockImageDecoder image_decoder;
+  std::unique_ptr<FakeResourceProviderStub> resource_provider(
+      new FakeResourceProviderStub());
+  MockImageDecoder image_decoder(resource_provider.get());
   image_decoder.ExpectCallWithError(std::string("Not an image mime type."));
 
   const char kHTMLHeaders[] = {
@@ -306,7 +244,9 @@
 }
 
 TEST(ImageDecoderTest, DecodeNoContentType) {
-  MockImageDecoder image_decoder;
+  std::unique_ptr<FakeResourceProviderStub> resource_provider(
+      new FakeResourceProviderStub());
+  MockImageDecoder image_decoder(resource_provider.get());
   image_decoder.ExpectCallWithError(std::string("Not an image mime type."));
 
   const char kHTMLHeaders[] = {
@@ -330,7 +270,9 @@
 }
 
 TEST(ImageDecoderTest, DecodeImageWithNoContent) {
-  MockImageDecoder image_decoder;
+  std::unique_ptr<FakeResourceProviderStub> resource_provider(
+      new FakeResourceProviderStub());
+  MockImageDecoder image_decoder(resource_provider.get());
   image_decoder.ExpectCallWithError(
       std::string("No content returned. Not an image mime type."));
 
@@ -354,7 +296,9 @@
 }
 
 TEST(ImageDecoderTest, DecodeImageWithLessThanHeaderBytes) {
-  MockImageDecoder image_decoder;
+  std::unique_ptr<FakeResourceProviderStub> resource_provider(
+      new FakeResourceProviderStub());
+  MockImageDecoder image_decoder(resource_provider.get());
   image_decoder.ExpectCallWithError(
       std::string("No enough image data for header."));
 
@@ -366,7 +310,9 @@
 }
 
 TEST(ImageDecoderTest, FailedToDecodeImage) {
-  MockImageDecoder image_decoder;
+  std::unique_ptr<FakeResourceProviderStub> resource_provider(
+      new FakeResourceProviderStub());
+  MockImageDecoder image_decoder(resource_provider.get());
   image_decoder.ExpectCallWithError(
       std::string("PNGImageDecoder failed to decode image."));
 
@@ -380,7 +326,9 @@
 }
 
 TEST(ImageDecoderTest, UnsupportedImageFormat) {
-  MockImageDecoder image_decoder;
+  std::unique_ptr<FakeResourceProviderStub> resource_provider(
+      new FakeResourceProviderStub());
+  MockImageDecoder image_decoder(resource_provider.get());
   image_decoder.ExpectCallWithError(std::string("Unsupported image format."));
 
   const char kPartialICOImage[] = {
@@ -393,7 +341,9 @@
 
 // Test that we can properly decode the PNG image.
 TEST(ImageDecoderTest, DecodePNGImage) {
-  MockImageDecoder image_decoder;
+  std::unique_ptr<FakeResourceProviderStub> resource_provider(
+      new FakeResourceProviderStub());
+  MockImageDecoder image_decoder(resource_provider.get());
   image_decoder.ExpectCallWithError(base::nullopt);
 
   std::vector<uint8> image_data =
@@ -427,7 +377,9 @@
 
 // Test that we can properly decode the PNG image with multiple chunks.
 TEST(ImageDecoderTest, DecodePNGImageWithMultipleChunks) {
-  MockImageDecoder image_decoder;
+  std::unique_ptr<FakeResourceProviderStub> resource_provider(
+      new FakeResourceProviderStub());
+  MockImageDecoder image_decoder(resource_provider.get());
   image_decoder.ExpectCallWithError(base::nullopt);
 
   std::vector<uint8> image_data =
@@ -465,7 +417,9 @@
 
 // Test that we can properly decode the the interlaced PNG.
 TEST(ImageDecoderTest, DecodeInterlacedPNGImage) {
-  MockImageDecoder image_decoder;
+  std::unique_ptr<FakeResourceProviderStub> resource_provider(
+      new FakeResourceProviderStub());
+  MockImageDecoder image_decoder(resource_provider.get());
   image_decoder.ExpectCallWithError(base::nullopt);
 
   std::vector<uint8> image_data =
@@ -499,7 +453,9 @@
 
 // Test that we can properly decode the interlaced PNG with multiple chunks.
 TEST(ImageDecoderTest, DecodeInterlacedPNGImageWithMultipleChunks) {
-  MockImageDecoder image_decoder;
+  std::unique_ptr<FakeResourceProviderStub> resource_provider(
+      new FakeResourceProviderStub());
+  MockImageDecoder image_decoder(resource_provider.get());
   image_decoder.ExpectCallWithError(base::nullopt);
 
   std::vector<uint8> image_data =
@@ -537,7 +493,9 @@
 
 // Test that we can properly decode the JPEG image.
 TEST(ImageDecoderTest, DecodeJPEGImage) {
-  MockImageDecoder image_decoder;
+  std::unique_ptr<FakeResourceProviderStub> resource_provider(
+      new FakeResourceProviderStub());
+  MockImageDecoder image_decoder(resource_provider.get());
   image_decoder.ExpectCallWithError(base::nullopt);
 
   std::vector<uint8> image_data =
@@ -563,7 +521,9 @@
 
 // Test that we can properly decode the JPEG image with multiple chunks.
 TEST(ImageDecoderTest, DecodeJPEGImageWithMultipleChunks) {
-  MockImageDecoder image_decoder;
+  std::unique_ptr<FakeResourceProviderStub> resource_provider(
+      new FakeResourceProviderStub());
+  MockImageDecoder image_decoder(resource_provider.get());
   image_decoder.ExpectCallWithError(base::nullopt);
 
   std::vector<uint8> image_data =
@@ -593,7 +553,9 @@
 
 // Test that we can properly decode the progressive JPEG image.
 TEST(ImageDecoderTest, DecodeProgressiveJPEGImage) {
-  MockImageDecoder image_decoder;
+  std::unique_ptr<FakeResourceProviderStub> resource_provider(
+      new FakeResourceProviderStub());
+  MockImageDecoder image_decoder(resource_provider.get());
   image_decoder.ExpectCallWithError(base::nullopt);
 
   std::vector<uint8> image_data =
@@ -620,7 +582,9 @@
 
 // Test that we can properly decode the progressive JPEG with multiple chunks.
 TEST(ImageDecoderTest, DecodeProgressiveJPEGImageWithMultipleChunks) {
-  MockImageDecoder image_decoder;
+  std::unique_ptr<FakeResourceProviderStub> resource_provider(
+      new FakeResourceProviderStub());
+  MockImageDecoder image_decoder(resource_provider.get());
   image_decoder.ExpectCallWithError(base::nullopt);
 
   std::vector<uint8> image_data =
@@ -651,7 +615,7 @@
 // Test that we can properly decode the progressive JPEG image while forcing the
 // output to be single plane.
 TEST(ImageDecoderTest, DecodeProgressiveJPEGImageToSinglePlane) {
-  render_tree::ResourceProviderStub resource_provider;
+  FakeResourceProviderStub resource_provider;
   base::NullDebuggerHooks debugger_hooks;
   const bool kAllowImageDecodingToMultiPlane = false;
   JPEGImageDecoder jpeg_image_decoder(&resource_provider, debugger_hooks,
@@ -684,7 +648,7 @@
 // while forcing the output to be single plane.
 TEST(ImageDecoderTest,
      DecodeProgressiveJPEGImageWithMultipleChunksToSinglePlane) {
-  render_tree::ResourceProviderStub resource_provider;
+  FakeResourceProviderStub resource_provider;
   base::NullDebuggerHooks debugger_hooks;
   const bool kAllowImageDecodingToMultiPlane = false;
   JPEGImageDecoder jpeg_image_decoder(&resource_provider, debugger_hooks,
@@ -719,7 +683,9 @@
 
 // Test that we can properly decode the WEBP image.
 TEST(ImageDecoderTest, DecodeWEBPImage) {
-  MockImageDecoder image_decoder;
+  std::unique_ptr<FakeResourceProviderStub> resource_provider(
+      new FakeResourceProviderStub());
+  MockImageDecoder image_decoder(resource_provider.get());
   image_decoder.ExpectCallWithError(base::nullopt);
 
   std::vector<uint8> image_data =
@@ -752,7 +718,9 @@
 
 // Test that we can properly decode the WEBP image with multiple chunks.
 TEST(ImageDecoderTest, DecodeWEBPImageWithMultipleChunks) {
-  MockImageDecoder image_decoder;
+  std::unique_ptr<FakeResourceProviderStub> resource_provider(
+      new FakeResourceProviderStub());
+  MockImageDecoder image_decoder(resource_provider.get());
   image_decoder.ExpectCallWithError(base::nullopt);
 
   std::vector<uint8> image_data =
@@ -791,7 +759,9 @@
   base::Thread thread("AnimatedWebP");
   thread.Start();
 
-  MockImageDecoder image_decoder;
+  std::unique_ptr<FakeResourceProviderStub> resource_provider(
+      new FakeResourceProviderStub());
+  MockImageDecoder image_decoder(resource_provider.get());
   image_decoder.ExpectCallWithError(base::nullopt);
 
   std::vector<uint8> image_data =
@@ -820,7 +790,9 @@
   base::Thread thread("AnimatedWebP");
   thread.Start();
 
-  MockImageDecoder image_decoder;
+  std::unique_ptr<FakeResourceProviderStub> resource_provider(
+      new FakeResourceProviderStub());
+  MockImageDecoder image_decoder(resource_provider.get());
   image_decoder.ExpectCallWithError(base::nullopt);
 
   std::vector<uint8> image_data =
@@ -846,7 +818,6 @@
   EXPECT_EQ(math::Size(480, 270), animated_webp_image->GetSize());
   EXPECT_TRUE(animated_webp_image->IsOpaque());
 }
-
 }  // namespace image
 }  // namespace loader
 }  // namespace cobalt
diff --git a/src/cobalt/media_session/media_session_client.cc b/src/cobalt/media_session/media_session_client.cc
index 7dd8853..09a5424 100644
--- a/src/cobalt/media_session/media_session_client.cc
+++ b/src/cobalt/media_session/media_session_client.cc
@@ -176,8 +176,6 @@
 }
 
 void MediaSessionClient::PostDelayedTaskForMaybeFreezeCallback() {
-  if (is_active()) return;
-
   media_session_->task_runner_->PostDelayedTask(
       FROM_HERE, base::Bind(&MediaSessionClient::RunMaybeFreezeCallback,
                             base::Unretained(this), ++sequence_number_),
diff --git a/src/cobalt/renderer/pipeline.cc b/src/cobalt/renderer/pipeline.cc
index bb9954c..c49e72d 100644
--- a/src/cobalt/renderer/pipeline.cc
+++ b/src/cobalt/renderer/pipeline.cc
@@ -26,10 +26,9 @@
 #include "cobalt/base/polymorphic_downcast.h"
 #include "cobalt/extension/graphics.h"
 #include "cobalt/math/rect_f.h"
-#include "cobalt/render_tree/brush.h"
+#include "cobalt/render_tree/clear_rect_node.h"
 #include "cobalt/render_tree/composition_node.h"
 #include "cobalt/render_tree/dump_render_tree_to_string.h"
-#include "cobalt/render_tree/rect_node.h"
 #include "nb/memory_scope.h"
 #include "starboard/system.h"
 
@@ -66,7 +65,7 @@
 // How many entries the rasterize periodic timer will contain before updating.
 const size_t kRasterizePeriodicTimerEntriesPerUpdate = 60;
 
-// The maximum numer of entries that the rasterize animations timer can contain
+// The maximum number of entries that the rasterize animations timer can contain
 // before automatically updating. In the typical use case, the update will
 // occur manually when the animations expire.
 const size_t kRasterizeAnimationsTimerMaxEntries = 60;
@@ -658,11 +657,10 @@
   render_tree::ColorRGBA clear_color;
   if (render_target_ && clear_on_shutdown_mode_ == kClearAccordingToPlatform &&
       ShouldClearFrameOnShutdown(&clear_color)) {
-    rasterizer_->Submit(new render_tree::RectNode(
-                            math::RectF(render_target_->GetSize()),
-                            std::unique_ptr<render_tree::Brush>(
-                                new render_tree::SolidColorBrush(clear_color))),
-                        render_target_);
+    rasterizer_->Submit(
+        new render_tree::ClearRectNode(math::RectF(render_target_->GetSize()),
+                                       clear_color),
+        render_target_);
   }
 
   // This potential reference to a render tree whose animations may have ended
diff --git a/src/cobalt/renderer/rasterizer/pixel_test.cc b/src/cobalt/renderer/rasterizer/pixel_test.cc
index 6ee0ccb..8bc8886 100644
--- a/src/cobalt/renderer/rasterizer/pixel_test.cc
+++ b/src/cobalt/renderer/rasterizer/pixel_test.cc
@@ -19,6 +19,8 @@
 #include "base/files/file_path.h"
 #include "base/memory/ptr_util.h"
 #include "base/path_service.h"
+#include "cobalt/loader/image/animated_webp_image.h"
+#include "cobalt/loader/image/image_decoder_mock.h"
 #include "cobalt/math/matrix3_f.h"
 #include "cobalt/math/rect_f.h"
 #include "cobalt/math/size_f.h"
@@ -68,6 +70,9 @@
 #endif
 #endif
 
+using cobalt::loader::image::AnimatedWebPImage;
+using cobalt::loader::image::MockImageDecoder;
+using cobalt::loader::image::MockImageDecoderCallback;
 using cobalt::math::Matrix3F;
 using cobalt::math::PointF;
 using cobalt::math::RectF;
@@ -4566,6 +4571,38 @@
           TranslateMatrix(output_surface_size().width() * -0.5f, 0.0f)));
 }
 
+TEST_F(PixelTest, DebugAnimatedWebPFrame) {
+  MockImageDecoder image_decoder(GetResourceProvider());
+  image_decoder.ExpectCallWithError(base::nullopt);
+
+  std::vector<uint8> image_data =
+      GetFileData(GetTestFilePath("loading-spinner-opaque.webp"));
+  image_decoder.DecodeChunk(reinterpret_cast<char*>(&image_data[0]),
+                            image_data.size());
+  image_decoder.Finish();
+
+  scoped_refptr<AnimatedWebPImage> animated_webp_image =
+      base::polymorphic_downcast<AnimatedWebPImage*>(
+          image_decoder.image().get());
+
+  scoped_refptr<Image> frame_image =
+      animated_webp_image->GetFrameForDebugging(20);
+
+  scoped_refptr<ImageNode> frame_image_node = new ImageNode(frame_image);
+
+  CompositionNode::Builder builder;
+
+  // Test opaque background animated webp image on top of an opaque canvas
+  // webp image's background should be ignored and only shows underlying
+  // canvas's color.
+  builder.AddChild(new ClearRectNode(math::RectF(output_surface_size()),
+                                     ColorRGBA(0.0f, 0.0f, 0.0f, 1.0f)));
+  builder.AddChild(frame_image_node);
+
+  scoped_refptr<Node> root = new CompositionNode(builder);
+  TestTree(root);
+}
+
 #endif  // !SB_HAS(BLITTER)
 
 }  // namespace rasterizer
diff --git a/src/cobalt/renderer/rasterizer/testdata/DebugAnimatedWebPFrame-expected.png b/src/cobalt/renderer/rasterizer/testdata/DebugAnimatedWebPFrame-expected.png
new file mode 100644
index 0000000..c7ba0fe
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/DebugAnimatedWebPFrame-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/loading-spinner-opaque.webp b/src/cobalt/renderer/rasterizer/testdata/loading-spinner-opaque.webp
new file mode 100644
index 0000000..6cee3dd
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/loading-spinner-opaque.webp
Binary files differ
diff --git a/src/cobalt/renderer/renderer.gyp b/src/cobalt/renderer/renderer.gyp
index 5904624..f63ce0b 100644
--- a/src/cobalt/renderer/renderer.gyp
+++ b/src/cobalt/renderer/renderer.gyp
@@ -102,6 +102,7 @@
       ],
       'dependencies': [
         '<(DEPTH)/cobalt/base/base.gyp:base',
+        '<(DEPTH)/cobalt/loader/loader.gyp:loader',
         '<(DEPTH)/testing/gmock.gyp:gmock',
         '<(DEPTH)/testing/gtest.gyp:gtest',
         'render_tree_pixel_tester',
diff --git a/src/docker/linux/raspi/gn/Dockerfile b/src/docker/linux/raspi/gn/Dockerfile
index 2952fef..3339455 100644
--- a/src/docker/linux/raspi/gn/Dockerfile
+++ b/src/docker/linux/raspi/gn/Dockerfile
@@ -14,5 +14,5 @@
 
 FROM cobalt-build-raspi
 
-CMD gn gen ${OUTDIR}/${PLATFORM}_${CONFIG} --args="target_platform=\"${PLATFORM}\" build_type=\"${CONFIG}\" target_cpu=\"arm\" treat_warnings_as_errors=false" && \
+CMD gn gen ${OUTDIR}/${PLATFORM}_${CONFIG} --args="target_platform=\"${PLATFORM}\" build_type=\"${CONFIG}\" target_cpu=\"arm\" is_clang=false can_build_evergreen_loader_apps=false " && \
     ninja -j ${NINJA_PARALLEL} -C ${OUTDIR}/${PLATFORM}_${CONFIG}
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/CobaltMediaSession.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/CobaltMediaSession.java
index c31e017..ba0329d 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/CobaltMediaSession.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/CobaltMediaSession.java
@@ -521,6 +521,10 @@
   }
 
   private void updateMetadata(boolean resetMetadataWithEmptyBuilder) {
+    if (mediaSession == null) {
+      return;
+    }
+
     MediaMetadataCompat.Builder metadataBuilder = new MediaMetadataCompat.Builder();
     // Reset the metadata to make sure the artwork update correctly.
     if (resetMetadataWithEmptyBuilder) mediaSession.setMetadata(metadataBuilder.build());
diff --git a/src/starboard/android/shared/BUILD.gn b/src/starboard/android/shared/BUILD.gn
index a84353e..3cf4157 100644
--- a/src/starboard/android/shared/BUILD.gn
+++ b/src/starboard/android/shared/BUILD.gn
@@ -284,6 +284,9 @@
     "atomic_public.h",
     "audio_decoder.cc",
     "audio_decoder.h",
+    "audio_decoder_passthrough.h",
+    "audio_renderer_passthrough.cc",
+    "audio_renderer_passthrough.h",
     "audio_sink_get_max_channels.cc",
     "audio_sink_get_min_buffer_size_in_frames.cc",
     "audio_sink_get_nearest_supported_sample_frequency.cc",
@@ -293,6 +296,8 @@
     "audio_sink_min_required_frames_tester.h",
     "audio_track_audio_sink_type.cc",
     "audio_track_audio_sink_type.h",
+    "audio_track_bridge.cc",
+    "audio_track_bridge.h",
     "bionic/bionic_netlink.cpp",
     "bionic/bionic_netlink.h",
     "bionic/ifaddrs.cpp",
diff --git a/src/starboard/android/shared/starboard_platform.gypi b/src/starboard/android/shared/starboard_platform.gypi
index 6e61339..e8579cb 100644
--- a/src/starboard/android/shared/starboard_platform.gypi
+++ b/src/starboard/android/shared/starboard_platform.gypi
@@ -164,6 +164,7 @@
         'system_has_capability.cc',
         'system_network_is_disconnected.cc',
         'system_platform_error.cc',
+        'system_request_conceal.cc',
         'system_request_freeze_no_freezedone_callback.cc',
         'system_request_stop.cc',
         'system_request_suspend.cc',
@@ -357,7 +358,6 @@
         '<(DEPTH)/starboard/shared/signal/crash_signals_sigaction.cc',
         '<(DEPTH)/starboard/shared/signal/suspend_signals.cc',
         '<(DEPTH)/starboard/shared/signal/suspend_signals.h',
-        '<(DEPTH)/starboard/shared/signal/system_request_conceal.cc',
         '<(DEPTH)/starboard/shared/starboard/application.cc',
         '<(DEPTH)/starboard/shared/starboard/application.h',
         '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_create.cc',
diff --git a/src/starboard/android/shared/system_request_conceal.cc b/src/starboard/android/shared/system_request_conceal.cc
new file mode 100644
index 0000000..1406923
--- /dev/null
+++ b/src/starboard/android/shared/system_request_conceal.cc
@@ -0,0 +1,24 @@
+// Copyright 2017 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 "starboard/system.h"
+
+#include "starboard/android/shared/jni_env_ext.h"
+
+using starboard::android::shared::JniEnvExt;
+
+void SbSystemRequestConceal() {
+  JniEnvExt* env = JniEnvExt::Get();
+  env->CallStarboardVoidMethodOrAbort("requestSuspend", "()V");
+}
diff --git a/src/starboard/android/shared/system_request_freeze_no_freezedone_callback.cc b/src/starboard/android/shared/system_request_freeze_no_freezedone_callback.cc
index d245e9e..7a4942b 100644
--- a/src/starboard/android/shared/system_request_freeze_no_freezedone_callback.cc
+++ b/src/starboard/android/shared/system_request_freeze_no_freezedone_callback.cc
@@ -14,12 +14,9 @@
 
 #include "starboard/system.h"
 
-#include "starboard/android/shared/jni_env_ext.h"
 #include "starboard/shared/signal/signal_internal.h"
 #include "starboard/shared/starboard/application.h"
 
-using starboard::android::shared::JniEnvExt;
-
 #if SB_IS(EVERGREEN_COMPATIBLE) && !SB_IS(EVERGREEN_COMPATIBLE_LITE)
 #include "starboard/loader_app/pending_restart.h"
 #endif  // SB_IS(EVERGREEN_COMPATIBLE) && !SB_IS(EVERGREEN_COMPATIBLE_LITE)
@@ -35,19 +32,11 @@
     // There is no FreezeDone callback for stopping all thread execution
     // after fully transitioning into Frozen.
     starboard::shared::starboard::Application::Get()->Freeze(NULL, NULL);
-
-    // Let Android platform directly transit into Frozen.
-    JniEnvExt* env = JniEnvExt::Get();
-    env->CallStarboardVoidMethodOrAbort("requestSuspend", "()V");
   }
 #else
   // There is no FreezeDone callback for stopping all thread execution
   // after fully transitioning into Frozen.
   starboard::shared::starboard::Application::Get()->Freeze(NULL, NULL);
-
-  // Let Android platform directly transit into Frozen.
-  JniEnvExt* env = JniEnvExt::Get();
-  env->CallStarboardVoidMethodOrAbort("requestSuspend", "()V");
 #endif  // SB_IS(EVERGREEN_COMPATIBLE) && !SB_IS(EVERGREEN_COMPATIBLE_LITE)
 }
-#endif  // SB_API_VERSION >= 13
+#endif  // SB_API_VERSION >= 13
\ No newline at end of file
diff --git a/src/starboard/build/config/BUILDCONFIG.gn b/src/starboard/build/config/BUILDCONFIG.gn
index 38169b8..b3356ca 100644
--- a/src/starboard/build/config/BUILDCONFIG.gn
+++ b/src/starboard/build/config/BUILDCONFIG.gn
@@ -26,6 +26,9 @@
   cobalt_fastbuild = getenv("IS_CI") == 1
 
   is_internal_build = false
+
+  # TODO: Remove this flag when the affected targets can be built.
+  can_build_evergreen_loader_apps = true
 }
 
 is_debug = build_type == "debug"
diff --git a/src/starboard/build/config/base_configuration.gni b/src/starboard/build/config/base_configuration.gni
index f2ea066..293751b 100644
--- a/src/starboard/build/config/base_configuration.gni
+++ b/src/starboard/build/config/base_configuration.gni
@@ -98,4 +98,7 @@
   static_library_configs = []
   source_set_configs = []
   loadable_module_configs = []
+
+  # Whether or not to build drm test suites.
+  has_drm_support = true
 }
diff --git a/src/starboard/build/config/sabi/BUILD.gn b/src/starboard/build/config/sabi/BUILD.gn
index 081e595..96b4185 100644
--- a/src/starboard/build/config/sabi/BUILD.gn
+++ b/src/starboard/build/config/sabi/BUILD.gn
@@ -49,6 +49,9 @@
     "SB_SABI_JSON_ID=R\"($sabi_id)\"",
     "SB_API_VERSION=$sb_api_version",
 
+    "SB_SABI_TARGET_ARCH=\"${target_cpu}\"",
+    "SB_SABI_WORD_SIZE=\"${word_size}\"",
+
     "SB_IS_ARCH_${arch_uppercase}=1",
     "SB_HAS_${calling_convention_uppercase}_CALLING=1",
     "SB_HAS_${floating_point_abi_uppercase}_FLOATS=1",
diff --git a/src/starboard/build/doc/migrating_gyp_to_gn.md b/src/starboard/build/doc/migrating_gyp_to_gn.md
index 1a3d4bb..c6d06b6 100644
--- a/src/starboard/build/doc/migrating_gyp_to_gn.md
+++ b/src/starboard/build/doc/migrating_gyp_to_gn.md
@@ -203,20 +203,25 @@
 comparison tool, i.e. [meld](https://meldmerge.org/). This will allow you to see
 any changes in commands, i.e. with flags or otherwise.
 
-The name of the intermediate .o, .d files is different in both cases: this
-doesn't cause any issues. Keep this in mind while comparing the ninja flags for
-GYP vs GN. Here is an example for raspi2 while compiling the same source file
-```
-starboard/common/new.cc
-```
-GYP generates:
-```
-obj/starboard/common/common.new.cc.o
-```
-GN generates:
-```
-obj/starboard/common/common/new.o
-```
+The following differences for ninja flags between GYP and GN don't cause any
+issues:
+
+1. The name of the intermediate .o, .d files is different in both cases: Here is
+   an example while compiling the same source file
+   ```
+   starboard/common/new.cc
+   ```
+   GYP generates:
+   ```
+   obj/starboard/common/common.new.cc.o
+   ```
+   GN generates:
+   ```
+   obj/starboard/common/common/new.o
+   ```
+2. The `-x` flag for specifying language is not present in GN migration.
+   For example GYP specifies `-x c` flag while building c language files for
+   certain targets. This flag is not specified while building any GN targets.
 
 ### Validating a Platform
 
diff --git a/src/starboard/contrib/linux/README.md b/src/starboard/contrib/linux/README.md
new file mode 100644
index 0000000..8c3fd94
--- /dev/null
+++ b/src/starboard/contrib/linux/README.md
@@ -0,0 +1,18 @@
+### Example Linux variant for Stadia
+
+## ./linux/stadia
+  This directory must be copied into starboard/linux/ in order to be built. Once this directory is copied into starboard/linux, you will be able to run the build commands for a Stadia configuration.
+  Sample commands:
+
+```bash
+$ cp starboard/contrib/linux/stadia starboard/linux/
+$ ./cobalt/build/gyp_cobalt linux-stadia
+$ ninja -C out/linux-stadia_devel cobalt
+```
+
+**NOTE: This target will not be able to successfully link without a shared library that implements the shared interface defined in ./clients**
+
+This build configuration was based completely on the linux-x64x11 configuration,
+and the only differences are that this configuration extends ApplicationX11 in
+order to initialize Stadia and that this configuration registers the Stadia
+extension API in SbSystemGetExtension.
diff --git a/src/starboard/contrib/linux/stadia/__init__.py b/src/starboard/contrib/linux/stadia/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/starboard/contrib/linux/stadia/__init__.py
diff --git a/src/starboard/contrib/linux/stadia/atomic_public.h b/src/starboard/contrib/linux/stadia/atomic_public.h
new file mode 100644
index 0000000..6c07e70
--- /dev/null
+++ b/src/starboard/contrib/linux/stadia/atomic_public.h
@@ -0,0 +1,20 @@
+// 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 STARBOARD_LINUX_STADIA_ATOMIC_PUBLIC_H_
+#define STARBOARD_LINUX_STADIA_ATOMIC_PUBLIC_H_
+
+#include "starboard/linux/shared/atomic_public.h"
+
+#endif  // STARBOARD_LINUX_STADIA_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/contrib/linux/stadia/configuration_public.h b/src/starboard/contrib/linux/stadia/configuration_public.h
new file mode 100644
index 0000000..8fb5278
--- /dev/null
+++ b/src/starboard/contrib/linux/stadia/configuration_public.h
@@ -0,0 +1,28 @@
+// 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.
+
+// The Starboard configuration for Desktop x64 Linux. Other devices will have
+// specific Starboard implementations, even if they ultimately are running some
+// version of Linux.
+
+// Other source files should never include this header directly, but should
+// include the generic "starboard/configuration.h" instead.
+
+#ifndef STARBOARD_LINUX_STADIA_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_LINUX_STADIA_CONFIGURATION_PUBLIC_H_
+
+// Include the x64x11 Linux configuration on which this config is based.
+#include "starboard/linux/x64x11/configuration_public.h"
+
+#endif  // STARBOARD_LINUX_STADIA_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/contrib/linux/stadia/gyp_configuration.gypi b/src/starboard/contrib/linux/stadia/gyp_configuration.gypi
new file mode 100644
index 0000000..14694c7
--- /dev/null
+++ b/src/starboard/contrib/linux/stadia/gyp_configuration.gypi
@@ -0,0 +1,43 @@
+# 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.
+
+{
+  'variables': {
+    'variables': {
+      'sb_evergreen_compatible': 1,
+      'sb_evergreen_compatible_libunwind': 1,
+    }
+  },
+  'target_defaults': {
+    'default_configuration': 'linux-stadia_debug',
+    'configurations': {
+      'linux-stadia_debug': {
+        'inherit_from': ['debug_base'],
+      },
+      'linux-stadia_devel': {
+        'inherit_from': ['devel_base'],
+      },
+      'linux-stadia_qa': {
+        'inherit_from': ['qa_base'],
+      },
+      'linux-stadia_gold': {
+        'inherit_from': ['gold_base'],
+      },
+    }, # end of configurations
+  },
+
+  'includes': [
+    '<(DEPTH)/starboard/linux/x64x11/gyp_configuration.gypi',
+  ],
+}
diff --git a/src/starboard/contrib/linux/stadia/gyp_configuration.py b/src/starboard/contrib/linux/stadia/gyp_configuration.py
new file mode 100644
index 0000000..c75a68c
--- /dev/null
+++ b/src/starboard/contrib/linux/stadia/gyp_configuration.py
@@ -0,0 +1,59 @@
+# 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.
+"""Starboard Linux Stadia platform configuration."""
+
+from starboard.linux.shared import gyp_configuration as shared_configuration
+from starboard.tools.toolchain import ar
+from starboard.tools.toolchain import bash
+from starboard.tools.toolchain import clang
+from starboard.tools.toolchain import clangxx
+from starboard.tools.toolchain import cp
+from starboard.tools.toolchain import touch
+
+
+class LinuxStadiaConfiguration(shared_configuration.LinuxConfiguration):
+  """Starboard Linux Stadia platform configuration."""
+
+  def __init__(self,
+               platform='linux-stadia',
+               asan_enabled_by_default=True,
+               sabi_json_path='starboard/sabi/default/sabi.json'):
+    super(LinuxStadiaConfiguration,
+          self).__init__(platform, asan_enabled_by_default, sabi_json_path)
+
+  def GetTargetToolchain(self, **kwargs):
+    return self.GetHostToolchain(**kwargs)
+
+  def GetHostToolchain(self, **kwargs):
+    environment_variables = self.GetEnvironmentVariables()
+    cc_path = environment_variables['CC']
+    cxx_path = environment_variables['CXX']
+
+    return [
+        clang.CCompiler(path=cc_path),
+        clang.CxxCompiler(path=cxx_path),
+        clang.AssemblerWithCPreprocessor(path=cc_path),
+        ar.StaticThinLinker(),
+        ar.StaticLinker(),
+        clangxx.ExecutableLinker(path=cxx_path),
+        clangxx.SharedLibraryLinker(path=cxx_path),
+        cp.Copy(),
+        touch.Stamp(),
+        bash.Shell(),
+    ]
+
+
+def CreatePlatformConfig():
+  return LinuxStadiaConfiguration(
+      sabi_json_path='starboard/sabi/x64/sysv/sabi-v{sb_api_version}.json')
diff --git a/src/starboard/contrib/linux/stadia/libraries.gypi b/src/starboard/contrib/linux/stadia/libraries.gypi
new file mode 100644
index 0000000..fbf7bd1
--- /dev/null
+++ b/src/starboard/contrib/linux/stadia/libraries.gypi
@@ -0,0 +1,19 @@
+# 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.
+
+{
+  'includes': [
+    '<(DEPTH)/starboard/linux/x64x11/shared/libraries.gypi',
+  ],
+}
diff --git a/src/starboard/contrib/linux/stadia/main.cc b/src/starboard/contrib/linux/stadia/main.cc
new file mode 100644
index 0000000..61c90a1
--- /dev/null
+++ b/src/starboard/contrib/linux/stadia/main.cc
@@ -0,0 +1,49 @@
+// 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 <time.h>
+
+#include "starboard/configuration.h"
+#include "starboard/contrib/stadia/x11/application_stadia_x11.h"
+#include "starboard/shared/signal/crash_signals.h"
+#include "starboard/shared/signal/suspend_signals.h"
+#include "starboard/shared/starboard/link_receiver.h"
+
+#include "third_party/crashpad/wrapper/wrapper.h"
+
+extern "C" SB_EXPORT_PLATFORM int main(int argc, char** argv) {
+  tzset();
+  starboard::shared::signal::InstallCrashSignalHandlers();
+  starboard::shared::signal::InstallSuspendSignalHandlers();
+
+#if SB_IS(EVERGREEN_COMPATIBLE)
+  third_party::crashpad::wrapper::InstallCrashpadHandler();
+#endif
+
+#if SB_HAS_QUIRK(BACKTRACE_DLOPEN_BUG)
+  // Call backtrace() once to work around potential
+  // crash bugs in glibc, in dlopen()
+  SbLogRawDumpStack(3);
+#endif
+
+  starboard::contrib::stadia::x11::ApplicationStadiaX11 application;
+  int result = 0;
+  {
+    starboard::shared::starboard::LinkReceiver receiver(&application);
+    result = application.Run(argc, argv);
+  }
+  starboard::shared::signal::UninstallSuspendSignalHandlers();
+  starboard::shared::signal::UninstallCrashSignalHandlers();
+  return result;
+}
diff --git a/src/starboard/contrib/linux/stadia/starboard_platform.gyp b/src/starboard/contrib/linux/stadia/starboard_platform.gyp
new file mode 100644
index 0000000..ed04709
--- /dev/null
+++ b/src/starboard/contrib/linux/stadia/starboard_platform.gyp
@@ -0,0 +1,44 @@
+# 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.
+
+# Note, that despite the file extension ".gyp", this file is included by several
+# platform variants of linux-x64x11, like a ".gypi" file, since those platforms
+# have no need to modify this code.
+{
+  'includes': ['<(DEPTH)/starboard/linux/x64x11/shared/starboard_platform_target.gypi'],
+
+  'variables': {
+    'starboard_platform_sources': [
+      '<(DEPTH)/starboard/linux/stadia/main.cc',
+      '<(DEPTH)/starboard/linux/x64x11/sanitizer_options.cc',
+      '<(DEPTH)/starboard/linux/x64x11/system_get_property.cc',
+      '<(DEPTH)/starboard/linux/x64x11/system_get_property_impl.cc',
+      '<(DEPTH)/starboard/shared/libjpeg/image_decode.cc',
+      '<(DEPTH)/starboard/shared/libjpeg/image_is_decode_supported.cc',
+      '<(DEPTH)/starboard/shared/libjpeg/jpeg_image_decoder.cc',
+      '<(DEPTH)/starboard/shared/libjpeg/jpeg_image_decoder.h',
+      '<(DEPTH)/starboard/shared/starboard/link_receiver.cc',
+      '<(DEPTH)/starboard/contrib/stadia/x11/application_stadia_x11.cc',
+      '<(DEPTH)/starboard/shared/x11/egl_swap_buffers.cc',
+      '<(DEPTH)/starboard/contrib/stadia/get_platform_service_api.cc',
+      '<(DEPTH)/starboard/contrib/stadia/get_platform_service_api.h',
+      '<(DEPTH)/starboard/shared/x11/player_set_bounds.cc',
+      '<(DEPTH)/starboard/shared/x11/window_create.cc',
+      '<(DEPTH)/starboard/shared/x11/window_destroy.cc',
+      '<(DEPTH)/starboard/shared/x11/window_get_platform_handle.cc',
+      '<(DEPTH)/starboard/shared/x11/window_get_size.cc',
+      '<(DEPTH)/starboard/shared/x11/window_internal.cc',
+    ],
+  },
+}
diff --git a/src/starboard/contrib/linux/stadia/system_get_extensions.cc b/src/starboard/contrib/linux/stadia/system_get_extensions.cc
new file mode 100644
index 0000000..f5db101
--- /dev/null
+++ b/src/starboard/contrib/linux/stadia/system_get_extensions.cc
@@ -0,0 +1,47 @@
+// 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 "cobalt/extension/configuration.h"
+#include "cobalt/extension/crash_handler.h"
+#include "starboard/common/string.h"
+#include "starboard/contrib/stadia/get_platform_service_api.h"
+#include "starboard/shared/starboard/crash_handler.h"
+#include "starboard/system.h"
+#if SB_IS(EVERGREEN_COMPATIBLE)
+#include "starboard/elf_loader/evergreen_config.h"
+#endif
+#include "starboard/linux/shared/configuration.h"
+
+const void* SbSystemGetExtension(const char* name) {
+#if SB_IS(EVERGREEN_COMPATIBLE)
+  const starboard::elf_loader::EvergreenConfig* evergreen_config =
+      starboard::elf_loader::EvergreenConfig::GetInstance();
+  if (evergreen_config != NULL &&
+      evergreen_config->custom_get_extension_ != NULL) {
+    const void* ext = evergreen_config->custom_get_extension_(name);
+    if (ext != NULL) {
+      return ext;
+    }
+  }
+#endif
+  if (strcmp(name, kCobaltExtensionConfigurationName) == 0) {
+    return starboard::shared::GetConfigurationApi();
+  }
+  if (strcmp(name, kCobaltExtensionCrashHandlerName) == 0) {
+    return starboard::common::GetCrashHandlerApi();
+  }
+  if (strcmp(name, kCobaltExtensionPlatformServiceName) == 0) {
+    return starboard::contrib::stadia::GetPlatformServiceApi();
+  }
+  return NULL;
+}
diff --git a/src/starboard/contrib/linux/stadia/thread_types_public.h b/src/starboard/contrib/linux/stadia/thread_types_public.h
new file mode 100644
index 0000000..96dc485
--- /dev/null
+++ b/src/starboard/contrib/linux/stadia/thread_types_public.h
@@ -0,0 +1,20 @@
+// 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 STARBOARD_LINUX_STADIA_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_LINUX_STADIA_THREAD_TYPES_PUBLIC_H_
+
+#include "starboard/linux/shared/thread_types_public.h"
+
+#endif  // STARBOARD_LINUX_STADIA_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/contrib/stadia/README.md b/src/starboard/contrib/stadia/README.md
new file mode 100644
index 0000000..1063f30
--- /dev/null
+++ b/src/starboard/contrib/stadia/README.md
@@ -0,0 +1,13 @@
+### Reference implementation for Stadia
+
+ This directory contains patches for Starboard required in order to use the Stadia web app from Cobalt.
+
+## ./
+
+  Files in the top-level directory offer the ability to pass binary messages back and forth between the Stadia web app and native Stadia services.
+
+## ./clients
+  Files in the clients/ subdirectory mimics the public interface of an underlying services library.
+
+## ./x11
+  This directory contains a fork of the starboard/shared/x11 code that integrates the Stadia-required changes in order to enable communication with an underlying shared library.
diff --git a/src/starboard/contrib/stadia/clients/vendor/public/stadia_export.h b/src/starboard/contrib/stadia/clients/vendor/public/stadia_export.h
new file mode 100644
index 0000000..bac4582
--- /dev/null
+++ b/src/starboard/contrib/stadia/clients/vendor/public/stadia_export.h
@@ -0,0 +1,30 @@
+// 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 STARBOARD_CONTRIB_STADIA_CLIENTS_VENDOR_PUBLIC_STADIA_EXPORT_H_
+#define STARBOARD_CONTRIB_STADIA_CLIENTS_VENDOR_PUBLIC_STADIA_EXPORT_H_
+
+// Generates a forward declaration, a string with the function name, and a
+// typedef for the given return_type, function_name, and parameters. This macro
+// ensures consistency between the symbol that can be loaded dynamincally for
+// this function and the underlying implementation. e.g.
+// STADIA_EXPORT_FUNCTION(bool, GreaterThan, (int a, int b)) will produce the
+// following statements:
+//     * bool GreaterThan(int a, int b);
+//     * const char* kGreaterThan = "_GreaterThan";
+//     * typedef bool (*GreaterThanFunction)(int a, int b);
+#define STADIA_EXPORT_FUNCTION(return_type, name, parameters) \
+  return_type name parameters;                                \
+  const char* k##name = "_" #name;                            \
+  typedef return_type(*name##Function) parameters
+#endif  // STARBOARD_CONTRIB_STADIA_CLIENTS_VENDOR_PUBLIC_STADIA_EXPORT_H_
diff --git a/src/starboard/contrib/stadia/clients/vendor/public/stadia_lifecycle.h b/src/starboard/contrib/stadia/clients/vendor/public/stadia_lifecycle.h
new file mode 100644
index 0000000..efdd0b7
--- /dev/null
+++ b/src/starboard/contrib/stadia/clients/vendor/public/stadia_lifecycle.h
@@ -0,0 +1,26 @@
+// 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 STARBOARD_CONTRIB_STADIA_CLIENTS_VENDOR_PUBLIC_STADIA_LIFECYCLE_H_
+#define STARBOARD_CONTRIB_STADIA_CLIENTS_VENDOR_PUBLIC_STADIA_LIFECYCLE_H_
+
+#include "starboard/contrib/stadia/clients/vendor/public/stadia_export.h"
+
+extern "C" {
+
+// An initialization call for Stadia Plugins that should be called when the
+// application window is created.
+STADIA_EXPORT_FUNCTION(void, StadiaInitialize, ());
+}
+#endif  // STARBOARD_CONTRIB_STADIA_CLIENTS_VENDOR_PUBLIC_STADIA_LIFECYCLE_H_
diff --git a/src/starboard/contrib/stadia/clients/vendor/public/stadia_plugin.h b/src/starboard/contrib/stadia/clients/vendor/public/stadia_plugin.h
new file mode 100644
index 0000000..4b5e699
--- /dev/null
+++ b/src/starboard/contrib/stadia/clients/vendor/public/stadia_plugin.h
@@ -0,0 +1,50 @@
+// 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 STARBOARD_CONTRIB_STADIA_CLIENTS_VENDOR_PUBLIC_STADIA_PLUGIN_H_
+#define STARBOARD_CONTRIB_STADIA_CLIENTS_VENDOR_PUBLIC_STADIA_PLUGIN_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "starboard/contrib/stadia/clients/vendor/public/stadia_export.h"
+
+// An interface header that allows to interact with Stadia plugins.
+
+extern "C" {
+typedef struct StadiaPlugin StadiaPlugin;
+
+// Callback that the StadiaPlugin will invoke with data that it needs to pass
+// back to the client.
+typedef void (*StadiaPluginReceiveFromCallback)(const uint8_t* message,
+                                                size_t length,
+                                                void* user_data);
+
+STADIA_EXPORT_FUNCTION(bool, StadiaPluginHas, (const char* channel));
+
+STADIA_EXPORT_FUNCTION(StadiaPlugin*,
+                       StadiaPluginOpen,
+                       (const char* channel,
+                        StadiaPluginReceiveFromCallback callback,
+                        void* user_data));
+
+STADIA_EXPORT_FUNCTION(void,
+                       StadiaPluginSendTo,
+                       (StadiaPlugin* plugin,
+                        const char* message,
+                        size_t length));
+
+STADIA_EXPORT_FUNCTION(void, StadiaPluginClose, (StadiaPlugin* plugin));
+}
+#endif  // STARBOARD_CONTRIB_STADIA_CLIENTS_VENDOR_PUBLIC_STADIA_PLUGIN_H_
diff --git a/src/starboard/contrib/stadia/get_platform_service_api.cc b/src/starboard/contrib/stadia/get_platform_service_api.cc
new file mode 100644
index 0000000..d4dc0ac
--- /dev/null
+++ b/src/starboard/contrib/stadia/get_platform_service_api.cc
@@ -0,0 +1,144 @@
+// 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 "starboard/contrib/stadia/get_platform_service_api.h"
+
+#include <algorithm>
+#include <memory>
+#include <vector>
+
+#include "cobalt/extension/platform_service.h"
+#include "starboard/common/log.h"
+#include "starboard/common/mutex.h"
+#include "starboard/common/string.h"
+#include "starboard/contrib/stadia/clients/vendor/public/stadia_plugin.h"
+#include "starboard/event.h"
+#include "starboard/memory.h"
+#include "starboard/window.h"
+
+namespace starboard {
+namespace contrib {
+namespace stadia {
+
+namespace {
+
+namespace {
+// Encapsulates a channel name and data to be sent on that channel.
+struct StadiaPluginSendToData {
+  StadiaPlugin* plugin;
+  std::vector<uint8_t> data;
+  StadiaPluginSendToData(StadiaPlugin* plugin, const std::vector<uint8_t>& data)
+      : plugin(plugin), data(data) {}
+};
+}  // namespace
+
+bool HasPlatformService(const char* name) {
+  return StadiaPluginHas(name);
+}
+
+CobaltExtensionPlatformService OpenPlatformService(
+    void* context,
+    const char* name_c_str,
+    ReceiveMessageCallback receive_callback) {
+  // name_c_str is allocated by Cobalt, but must be freed here.
+  std::unique_ptr<const char[]> service_name(name_c_str);
+
+  SB_DCHECK(context);
+  SB_LOG(INFO) << "Open " << service_name.get();
+
+  if (!StadiaPluginHas(&service_name[0])) {
+    SB_LOG(ERROR) << "Cannot open service. Service not found. "
+                  << service_name.get();
+    return kCobaltExtensionPlatformServiceInvalid;
+  }
+
+  auto std_callback = std::make_unique<
+      std::function<void(const std::vector<uint8_t>& message)>>(
+      [receive_callback, context](const std::vector<uint8_t>& message) -> void {
+
+        receive_callback(context, message.data(), message.size());
+
+      });
+
+  StadiaPlugin* plugin = StadiaPluginOpen(
+      name_c_str,
+
+      [](const uint8_t* const message, size_t length, void* user_data) -> void {
+
+        auto callback = static_cast<
+            const std::function<void(const std::vector<uint8_t>& message)>*>(
+            user_data);
+        std::vector<uint8_t> data(message, message + length);
+        (*callback)(data);
+      },
+      std_callback.release());
+
+  return reinterpret_cast<CobaltExtensionPlatformService>(plugin);
+}
+
+void ClosePlatformService(CobaltExtensionPlatformService service) {
+  SB_DCHECK(service);
+  auto plugin = reinterpret_cast<StadiaPlugin*>(service);
+  StadiaPluginClose(plugin);
+}
+
+void* SendToPlatformService(CobaltExtensionPlatformService service,
+                            void* data,
+                            uint64_t length,
+                            uint64_t* output_length,
+                            bool* invalid_state) {
+  SB_DCHECK(service);
+  SB_DCHECK(data);
+  SB_DCHECK(output_length);
+  SB_DCHECK(invalid_state);
+
+  auto plugin = reinterpret_cast<StadiaPlugin*>(service);
+  std::vector<uint8_t> buffer(static_cast<uint8_t*>(data),
+                              static_cast<uint8_t*>(data) + length);
+
+  auto send_to_plugin_data =
+      std::make_unique<StadiaPluginSendToData>(plugin, buffer);
+
+  // Use the main thread.
+  SbEventSchedule(
+      [](void* context) -> void {
+        auto internal_data = std::unique_ptr<StadiaPluginSendToData>(
+            static_cast<StadiaPluginSendToData*>(context));
+
+        std::vector<uint8_t> plugin_data(internal_data->data);
+        StadiaPluginSendTo(internal_data->plugin,
+                           reinterpret_cast<const char*>(plugin_data.data()),
+                           plugin_data.size());
+      },
+      send_to_plugin_data.release(), 0);
+
+  std::vector<uint8_t> response = std::vector<uint8_t>();
+  return response.data();
+}
+
+const CobaltExtensionPlatformServiceApi kPlatformServiceApi = {
+    kCobaltExtensionPlatformServiceName,
+    // API version that's implemented.
+    1, &HasPlatformService, &OpenPlatformService, &ClosePlatformService,
+    &SendToPlatformService,
+};
+}  // namespace
+
+const void* GetPlatformServiceApi() {
+  return &kPlatformServiceApi;
+}
+
+}  // namespace stadia
+}  // namespace contrib
+}  // namespace starboard
diff --git a/src/starboard/contrib/stadia/get_platform_service_api.h b/src/starboard/contrib/stadia/get_platform_service_api.h
new file mode 100644
index 0000000..5382ad1
--- /dev/null
+++ b/src/starboard/contrib/stadia/get_platform_service_api.h
@@ -0,0 +1,27 @@
+// 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 STARBOARD_CONTRIB_STADIA_GET_PLATFORM_SERVICE_API_H_
+#define STARBOARD_CONTRIB_STADIA_GET_PLATFORM_SERVICE_API_H_
+
+namespace starboard {
+namespace contrib {
+namespace stadia {
+
+const void* GetPlatformServiceApi();
+
+}  // namespace stadia
+}  // namespace contrib
+}  // namespace starboard
+#endif  // STARBOARD_CONTRIB_STADIA_GET_PLATFORM_SERVICE_API_H_
diff --git a/src/starboard/contrib/stadia/x11/application_stadia_x11.cc b/src/starboard/contrib/stadia/x11/application_stadia_x11.cc
new file mode 100644
index 0000000..fb2fc76
--- /dev/null
+++ b/src/starboard/contrib/stadia/x11/application_stadia_x11.cc
@@ -0,0 +1,40 @@
+// 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 "starboard/contrib/stadia/x11/application_stadia_x11.h"
+
+#include "starboard/contrib/stadia/services/vendor/linux/public/stadia_lifecycle.h"
+#include "starboard/shared/x11/application_x11.cc"
+#include "starboard/shared/x11/window_internal.h"
+
+namespace starboard {
+namespace contrib {
+namespace stadia {
+namespace x11 {
+
+using ::starboard::shared::dev_input::DevInput;
+
+constexpr char kAppId[] = "com.google.stadia.linux";
+
+SbWindow ApplicationStadiaX11::CreateWindow(const SbWindowOptions* options) {
+  SbWindow window =
+      starboard::shared::x11::ApplicationX11::CreateWindow(options);
+  StadiaInitialize();
+  return window;
+}
+
+}  // namespace x11
+}  // namespace stadia
+}  // namespace contrib
+}  // namespace starboard
diff --git a/src/starboard/contrib/stadia/x11/application_stadia_x11.h b/src/starboard/contrib/stadia/x11/application_stadia_x11.h
new file mode 100644
index 0000000..3d40274
--- /dev/null
+++ b/src/starboard/contrib/stadia/x11/application_stadia_x11.h
@@ -0,0 +1,40 @@
+// 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 STARBOARD_CONTRIB_STADIA_X11_APPLICATION_STADIA_X11_H_
+#define STARBOARD_CONTRIB_STADIA_X11_APPLICATION_STADIA_X11_H_
+
+#include "starboard/shared/x11/application_x11.h"
+#include "starboard/window.h"
+
+namespace starboard {
+namespace contrib {
+namespace stadia {
+namespace x11 {
+
+// This application engine combines the generic queue with the X11 event queue.
+class ApplicationStadiaX11 : public starboard::shared::x11::ApplicationX11 {
+ public:
+  ApplicationStadiaX11();
+  ~ApplicationStadiaX11();
+
+  SbWindow CreateWindow(const SbWindowOptions* options) override;
+};
+
+}  // namespace x11
+}  // namespace stadia
+}  // namespace contrib
+}  // namespace starboard
+
+#endif  // STARBOARD_CONRTIB_STADIA_X11_APPLICATION_STADIA_X11_H_
diff --git a/src/starboard/elf_loader/BUILD.gn b/src/starboard/elf_loader/BUILD.gn
index 9ce393b..eff2b9e 100644
--- a/src/starboard/elf_loader/BUILD.gn
+++ b/src/starboard/elf_loader/BUILD.gn
@@ -97,31 +97,33 @@
   ]
 }
 
-target(final_executable_type, "elf_loader_sys_sandbox") {
-  # To properly function the system loader requires the starboard
-  # symbols to be exported from the binary.
-  # To allow symbols to be exported remove the '-fvisibility=hidden' flag
-  # from your compiler_flags.gypi. For Linux this would be:
-  #   starboard/linux/shared/compiler_flags.gypi
-  # Example run:
-  # export LD_LIBRARY_PATH=.
-  # ./elf_loader_sys_sandbox --evergreen_library=app/cobalt/lib/libcobalt.so --evergreen_content=app/cobalt/content
-  sources = [ "sandbox.cc" ]
-  configs += [ ":elf_loader_config" ]
+if (can_build_evergreen_loader_apps) {
+  target(final_executable_type, "elf_loader_sys_sandbox") {
+    # To properly function the system loader requires the starboard
+    # symbols to be exported from the binary.
+    # To allow symbols to be exported remove the '-fvisibility=hidden' flag
+    # from your compiler_flags.gypi. For Linux this would be:
+    #   starboard/linux/shared/compiler_flags.gypi
+    # Example run:
+    # export LD_LIBRARY_PATH=.
+    # ./elf_loader_sys_sandbox --evergreen_library=app/cobalt/lib/libcobalt.so --evergreen_content=app/cobalt/content
+    sources = [ "sandbox.cc" ]
+    configs += [ ":elf_loader_config" ]
 
-  starboard_syms_path =
-      rebase_path("//starboard/starboard.syms", root_build_dir)
-  ldflags = [
-    "-Wl,--dynamic-list=$starboard_syms_path",
-    "-ldl",
-  ]
+    starboard_syms_path =
+        rebase_path("//starboard/starboard.syms", root_build_dir)
+    ldflags = [
+      "-Wl,--dynamic-list=$starboard_syms_path",
+      "-ldl",
+    ]
 
-  deps = [
-    ":elf_loader_sys",
-    ":evergreen_info",
-    ":sabi_string",
-    "//starboard",
-  ]
+    deps = [
+      ":elf_loader_sys",
+      ":evergreen_info",
+      ":sabi_string",
+      "//starboard",
+    ]
+  }
 }
 
 target(gtest_target_type, "elf_loader_test") {
diff --git a/src/starboard/linux/shared/BUILD.gn b/src/starboard/linux/shared/BUILD.gn
index 9ef03b9..b9d5b3a 100644
--- a/src/starboard/linux/shared/BUILD.gn
+++ b/src/starboard/linux/shared/BUILD.gn
@@ -366,7 +366,6 @@
     "//starboard/shared/stub/system_get_total_gpu_memory.cc",
     "//starboard/shared/stub/system_get_used_gpu_memory.cc",
     "//starboard/shared/stub/system_hide_splash_screen.cc",
-    "//starboard/shared/stub/system_network_is_disconnected.cc",
     "//starboard/shared/stub/system_raise_platform_error.cc",
     "//starboard/shared/stub/system_sign_with_certification_secret_key.cc",
     "//starboard/shared/stub/thread_create_priority.cc",
diff --git a/src/starboard/linux/shared/platform_configuration/configuration.gni b/src/starboard/linux/shared/platform_configuration/configuration.gni
index eda9b87..8b40276 100644
--- a/src/starboard/linux/shared/platform_configuration/configuration.gni
+++ b/src/starboard/linux/shared/platform_configuration/configuration.gni
@@ -33,3 +33,5 @@
     "//starboard/linux/shared/platform_configuration:no_pedantic_warnings"
 
 sb_widevine_platform = "linux"
+
+has_drm_support = is_internal_build
diff --git a/src/starboard/loader_app/BUILD.gn b/src/starboard/loader_app/BUILD.gn
index 481ccc8..cc35c66 100644
--- a/src/starboard/loader_app/BUILD.gn
+++ b/src/starboard/loader_app/BUILD.gn
@@ -42,22 +42,24 @@
   }
 }
 
-target(final_executable_type, "loader_app_sys") {
-  if (target_cpu == "x86" || target_cpu == "x64" || target_cpu == "arm" ||
-      target_cpu == "arm64") {
-    sources = _common_loader_app_sources
+if (can_build_evergreen_loader_apps) {
+  target(final_executable_type, "loader_app_sys") {
+    if (target_cpu == "x86" || target_cpu == "x64" || target_cpu == "arm" ||
+        target_cpu == "arm64") {
+      sources = _common_loader_app_sources
 
-    starboard_syms_path =
-        rebase_path("//starboard/starboard.syms", root_build_dir)
-    ldflags = [
-      "-Wl,--dynamic-list=$starboard_syms_path",
-      "-ldl",
-    ]
-    deps = [
-      ":common_loader_app_dependencies",
-      "//cobalt/content/fonts:copy_font_data",
-      "//starboard/elf_loader:elf_loader_sys",
-    ]
+      starboard_syms_path =
+          rebase_path("//starboard/starboard.syms", root_build_dir)
+      ldflags = [
+        "-Wl,--dynamic-list=$starboard_syms_path",
+        "-ldl",
+      ]
+      deps = [
+        ":common_loader_app_dependencies",
+        "//cobalt/content/fonts:copy_font_data",
+        "//starboard/elf_loader:elf_loader_sys",
+      ]
+    }
   }
 }
 
diff --git a/src/starboard/nplb/BUILD.gn b/src/starboard/nplb/BUILD.gn
index a53df6a..20e502d 100644
--- a/src/starboard/nplb/BUILD.gn
+++ b/src/starboard/nplb/BUILD.gn
@@ -107,6 +107,7 @@
     "drm_helpers.cc",
     "drm_helpers.h",
     "drm_is_server_certificate_updatable_test.cc",
+    "drm_session_test.cc",
     "drm_update_server_certificate_test.cc",
     "egl_test.cc",
     "extern_c_test.cc",
@@ -294,7 +295,7 @@
     "window_get_size_test.cc",
   ]
 
-  if (is_internal_build) {
+  if (has_drm_support) {
     sources += [
       "drm_create_system_test.cc",
       "media_can_play_mime_and_key_system_test.cc",
@@ -303,6 +304,10 @@
 
   deps = [ "//starboard/nplb/testdata/file_tests:nplb_file_tests_data" ]
 
+  if (is_internal_build) {
+    deps += [ "//starboard/private/nplb:nplb_private" ]
+  }
+
   public_deps = [
     "//starboard",
     "//starboard/common",
diff --git a/src/starboard/raspi/shared/platform_configuration/BUILD.gn b/src/starboard/raspi/shared/platform_configuration/BUILD.gn
index 98d7b28..fe181d1 100644
--- a/src/starboard/raspi/shared/platform_configuration/BUILD.gn
+++ b/src/starboard/raspi/shared/platform_configuration/BUILD.gn
@@ -182,8 +182,7 @@
     # This decision should be revisited after raspi toolchain is upgraded.
     "-Wno-maybe-uninitialized",
 
-    #TODO: Renable -Werror after fixing all warnings.
-    #"-Werror",
+    "-Werror",
     "-Wno-expansion-to-defined",
     "-Wno-implicit-fallthrough",
   ]
diff --git a/src/starboard/raspi/shared/platform_configuration/configuration.gni b/src/starboard/raspi/shared/platform_configuration/configuration.gni
index fd4a3ce..4ceaa77 100644
--- a/src/starboard/raspi/shared/platform_configuration/configuration.gni
+++ b/src/starboard/raspi/shared/platform_configuration/configuration.gni
@@ -15,12 +15,12 @@
 import("//starboard/build/config/base_configuration.gni")
 
 arm_float_abi = "hard"
+has_drm_support = false
 sb_pedantic_warnings = true
 sb_static_contents_output_data_dir = "$root_out_dir/content"
 
-pedantic_warnings_config_path =
-    "//starboard/raspi/shared/platform_configuration:pedantic_warnings"
 no_pedantic_warnings_config_path =
     "//starboard/raspi/shared/platform_configuration:no_pedantic_warnings"
-
+pedantic_warnings_config_path =
+    "//starboard/raspi/shared/platform_configuration:pedantic_warnings"
 sabi_path = "//starboard/sabi/arm/hardfp/sabi-v$sb_api_version.json"
diff --git a/src/third_party/crashpad/handler/BUILD.gn b/src/third_party/crashpad/handler/BUILD.gn
index 8823beb..91a0f06 100644
--- a/src/third_party/crashpad/handler/BUILD.gn
+++ b/src/third_party/crashpad/handler/BUILD.gn
@@ -152,6 +152,20 @@
 }
 
 if (!crashpad_is_ios) {
+  if (crashpad_is_in_starboard) {
+    config("crashpad_handler_starboard_config") {
+      cflags = [
+        "-ffunction-sections",
+        "-fdata-sections",
+      ]
+      ldflags = [
+        "-Wl,--as-needed",
+        "-Wl,-gc-sections",
+        "-Wl,-z,noexecstack",
+      ]
+    }
+  }
+
   crashpad_executable("crashpad_handler") {
     if (crashpad_is_in_starboard) {
       check_includes = false
@@ -166,17 +180,22 @@
       "../third_party/mini_chromium:base",
     ]
 
+    configs = []
     if (crashpad_is_win) {
       if (crashpad_is_in_chromium || crashpad_is_in_dart) {
         remove_configs = [ "//build/config/win:console" ]
-        configs = [ "//build/config/win:windowed" ]
+        configs += [ "//build/config/win:windowed" ]
       } else {
         remove_configs =
             [ "//third_party/mini_chromium/mini_chromium/build:win_console" ]
-        configs =
+        configs +=
             [ "//third_party/mini_chromium/mini_chromium/build:win_windowed" ]
       }
     }
+
+    if (crashpad_is_in_starboard) {
+      configs += [ ":crashpad_handler_starboard_config" ]
+    }
   }
 }
 
diff --git a/src/tools/format_ninja.py b/src/tools/format_ninja.py
index f18e622..3793f80 100644
--- a/src/tools/format_ninja.py
+++ b/src/tools/format_ninja.py
@@ -18,12 +18,14 @@
 Primarily used for looking at the differences between GYP and GN builds during
 the GN migration.
 
-To test, first generate a the ninja build files, then use
+To test, first generate the ".ninja" build files, then use
 
 ninja -t compdb > out.json
 
-in the build directory to generate a JSON file containing all actions ninja
-would run. The run this script on that file and diff it with another.
+in the build directory to generate a JSON file (out.json) containing all actions
+ninja would run. Then, run this script on the out.json file to generate the
+normalized_out.json file. Diff this normalized_out.json file with another
+generated from GYP/GN to see differences in actions, build flags, etc.
 """
 
 import argparse