Import Cobalt 21.master.0.272474
diff --git a/src/cobalt/base/tokens.h b/src/cobalt/base/tokens.h
index c460d60..bfe8fee 100644
--- a/src/cobalt/base/tokens.h
+++ b/src/cobalt/base/tokens.h
@@ -56,6 +56,7 @@
     MacroOpWithNameOnly(focus)                                       \
     MacroOpWithNameOnly(focusin)                                     \
     MacroOpWithNameOnly(focusout)                                    \
+    MacroOpWithNameOnly(freeze)                                      \
     MacroOpWithNameOnly(gotpointercapture)                           \
     MacroOpWithNameOnly(hashchange)                                  \
     MacroOpWithNameOnly(hide)                                        \
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc
index e478a82..de5abe0 100644
--- a/src/cobalt/browser/application.cc
+++ b/src/cobalt/browser/application.cc
@@ -487,15 +487,6 @@
 
 Application::AppStatus Application::app_status_ =
     Application::kUninitializedAppStatus;
-int Application::app_pause_count_ = 0;
-int Application::app_unpause_count_ = 0;
-int Application::app_suspend_count_ = 0;
-int Application::app_resume_count_ = 0;
-
-Application::NetworkStatus Application::network_status_ =
-    Application::kDisconnectedNetworkStatus;
-int Application::network_connect_count_ = 0;
-int Application::network_disconnect_count_ = 0;
 
 Application::Application(const base::Closure& quit_closure, bool should_preload)
     : message_loop_(base::MessageLoop::current()),
@@ -1125,21 +1116,18 @@
     case kSbEventTypePause:
       DLOG(INFO) << "Got pause event.";
       app_status_ = kPausedAppStatus;
-      ++app_pause_count_;
       browser_module_->Pause();
       DLOG(INFO) << "Finished pausing.";
       break;
     case kSbEventTypeUnpause:
       DLOG(INFO) << "Got unpause event.";
       app_status_ = kRunningAppStatus;
-      ++app_unpause_count_;
       browser_module_->Unpause();
       DLOG(INFO) << "Finished unpausing.";
       break;
     case kSbEventTypeSuspend:
       DLOG(INFO) << "Got suspend event.";
       app_status_ = kSuspendedAppStatus;
-      ++app_suspend_count_;
       browser_module_->Suspend();
 #if SB_IS(EVERGREEN)
       updater_module_->Suspend();
@@ -1150,7 +1138,6 @@
       DCHECK(SbSystemSupportsResume());
       DLOG(INFO) << "Got resume event.";
       app_status_ = kPausedAppStatus;
-      ++app_resume_count_;
       browser_module_->Resume();
 #if SB_IS(EVERGREEN)
       updater_module_->Resume();
diff --git a/src/cobalt/browser/application.h b/src/cobalt/browser/application.h
index afc8c34..599ceb3 100644
--- a/src/cobalt/browser/application.h
+++ b/src/cobalt/browser/application.h
@@ -210,14 +210,6 @@
   static int64 lifetime_in_ms_;
 
   static AppStatus app_status_;
-  static int app_suspend_count_;
-  static int app_resume_count_;
-  static int app_pause_count_;
-  static int app_unpause_count_;
-
-  static NetworkStatus network_status_;
-  static int network_connect_count_;
-  static int network_disconnect_count_;
 
   CValStats c_val_stats_;
 
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index 0c285df..e3ab124 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -1314,8 +1314,7 @@
 
 bool BrowserModule::FilterKeyEventForHotkeys(
     base::Token type, const dom::KeyboardEventInit& event) {
-#if !defined(ENABLE_DEBUGGER)
-#else
+#if defined(ENABLE_DEBUGGER)
   if (event.key_code() == dom::keycode::kF1 ||
       (event.ctrl_key() && event.key_code() == dom::keycode::kO)) {
     if (type == base::Tokens::keydown()) {
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index b1d084f..9eaba4f 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-272029
\ No newline at end of file
+272474
\ No newline at end of file
diff --git a/src/cobalt/dom/lottie_player.cc b/src/cobalt/dom/lottie_player.cc
index efd6ebb..5992e3c 100644
--- a/src/cobalt/dom/lottie_player.cc
+++ b/src/cobalt/dom/lottie_player.cc
@@ -435,6 +435,14 @@
   properties_.onenterframe_callback = base::Bind(
       &LottiePlayer::CallOnEnterFrame, callback_task_runner_,
       base::Bind(&LottiePlayer::OnEnterFrame, base::AsWeakPtr(this)));
+  properties_.onfreeze_callback =
+      base::Bind(base::IgnoreResult(&base::SingleThreadTaskRunner::PostTask),
+                 callback_task_runner_, FROM_HERE,
+                 base::Bind(&LottiePlayer::OnFreeze, base::AsWeakPtr(this)));
+  properties_.onunfreeze_callback =
+      base::Bind(base::IgnoreResult(&base::SingleThreadTaskRunner::PostTask),
+                 callback_task_runner_, FROM_HERE,
+                 base::Bind(&LottiePlayer::OnUnfreeze, base::AsWeakPtr(this)));
 }
 
 void LottiePlayer::OnPlay() { ScheduleEvent(base::Tokens::play()); }
@@ -467,5 +475,19 @@
       FROM_HERE, base::Bind(enter_frame_callback, frame, seeker));
 }
 
+void LottiePlayer::OnFreeze() {
+  if (properties_.FreezeAnimationState()) {
+    ScheduleEvent(base::Tokens::freeze());
+    UpdateLottieObjects();
+  }
+}
+
+void LottiePlayer::OnUnfreeze() {
+  if (properties_.UnfreezeAnimationState()) {
+    ScheduleEvent(base::Tokens::play());
+    UpdateLottieObjects();
+  }
+}
+
 }  // namespace dom
 }  // namespace cobalt
diff --git a/src/cobalt/dom/lottie_player.h b/src/cobalt/dom/lottie_player.h
index bd3358b..569bee7 100644
--- a/src/cobalt/dom/lottie_player.h
+++ b/src/cobalt/dom/lottie_player.h
@@ -137,6 +137,8 @@
       scoped_refptr<base::SingleThreadTaskRunner> callback_task_runner,
       base::Callback<void(double, double)> enter_frame_callback, double frame,
       double seeker);
+  void OnFreeze();
+  void OnUnfreeze();
 
   scoped_refptr<loader::image::CachedImage> cached_image_;
   std::unique_ptr<loader::image::CachedImage::OnLoadedCallbackHandler>
diff --git a/src/cobalt/layout/box_generator.cc b/src/cobalt/layout/box_generator.cc
index c72355d..c622e99 100644
--- a/src/cobalt/layout/box_generator.cc
+++ b/src/cobalt/layout/box_generator.cc
@@ -451,6 +451,12 @@
     // This behavior cannot be overridden by setting the "display" property on
     // the descendants.
     //   https://www.w3.org/TR/CSS21/visuren.html#display-prop
+
+    // A LottiePlayer element with "display: none" should potentially trigger
+    // a freeze event.
+    if (!lottie_player->GetProperties().onfreeze_callback.is_null()) {
+      lottie_player->GetProperties().onfreeze_callback.Run();
+    }
     return;
   }
 
diff --git a/src/cobalt/layout/intersection_observer_target.cc b/src/cobalt/layout/intersection_observer_target.cc
index a82d1f2..26d863d 100644
--- a/src/cobalt/layout/intersection_observer_target.cc
+++ b/src/cobalt/layout/intersection_observer_target.cc
@@ -69,17 +69,17 @@
   // Let intersectionArea be intersectionRect's area.
   float intersection_area = intersection_rect.size().GetArea();
 
+  // Let isIntersecting be true if targetRect and rootBounds intersect or are
+  // edge-adjacent, even if the intersection has zero area (because rootBounds
+  // or targetRect have zero area); otherwise, let isIntersecting be false.
+  bool is_intersecting =
+      intersection_rect.width() != 0 || intersection_rect.height() != 0;
+
   // If targetArea is non-zero, let intersectionRatio be intersectionArea
   // divided by targetArea. Otherwise, let intersectionRatio be 1 if
-  // targetRect and rootBounds are edge-adjacent (in the case that targetRect or
-  // rootbounds have zero area), and 0 otherwise.
-  float intersection_ratio =
-      (intersection_rect.width() != 0 || intersection_rect.height() != 0)
-          ? 1.0f
-          : 0.0f;
-  if (target_area != 0) {
-    intersection_ratio = intersection_area / target_area;
-  }
+  // isIntersecting is true, or 0 if isIntersecting is false.
+  float intersection_ratio = target_area > 0 ? intersection_area / target_area
+                                             : is_intersecting ? 1.0f : 0.0f;
 
   // Let thresholdIndex be the index of the first entry in observer.thresholds
   // whose value is greater than intersectionRatio, or the length of
@@ -91,13 +91,15 @@
   for (threshold_index = 0; threshold_index < thresholds.size();
        ++threshold_index) {
     if (thresholds.at(threshold_index) > intersection_ratio) {
+      // isIntersecting is false if intersectionRatio is less than all
+      // thresholds, sorted ascending. Not in spec but follows Chrome behavior.
+      if (threshold_index == 0) {
+        is_intersecting = false;
+      }
       break;
     }
   }
 
-  // Set isIntersecting to true if |threshold_index| > 0, and false otherwise.
-  bool is_intersecting = threshold_index > 0;
-
   // If thresholdIndex does not equal previousThresholdIndex or if
   // isIntersecting does not equal previousIsIntersecting, queue an
   // IntersectionObserverEntry, passing in observer, time, rootBounds,
diff --git a/src/cobalt/layout/replaced_box.cc b/src/cobalt/layout/replaced_box.cc
index 6a8850d..c420851 100644
--- a/src/cobalt/layout/replaced_box.cc
+++ b/src/cobalt/layout/replaced_box.cc
@@ -297,7 +297,7 @@
   render_tree::LottieAnimation* lottie =
       base::polymorphic_downcast<render_tree::LottieAnimation*>(
           animation.get());
-  lottie->SetProperties(lottie_properties);
+  lottie->BeginRenderFrame(lottie_properties);
   node_builder->animation = lottie;
   node_builder->destination_rect = destination_rect;
   node_builder->animation_time = time_elapsed;
diff --git a/src/cobalt/layout_tests/testdata/intersection-observer/layout_tests.txt b/src/cobalt/layout_tests/testdata/intersection-observer/layout_tests.txt
index 6f3f547..1236809 100644
--- a/src/cobalt/layout_tests/testdata/intersection-observer/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/intersection-observer/layout_tests.txt
@@ -5,7 +5,8 @@
 intersection-ratio-is-nonzero-but-is-intersecting-is-false
 multiple-observers-with-different-roots-and-targets
 no-intersection-when-root-is-not-in-containing-block-chain-of-target
-observers-should-update-when-elements-move
+observers-should-update-when-elements-move-with-threshold
+observers-should-update-when-elements-move-without-threshold
 previous-threshold-index-and-is-intersecting-fields-should-be-updated
 root-has-nonzero-padding-and-border
 root-has-nonzero-padding-and-border-and-overflow-clip
diff --git a/src/cobalt/layout_tests/testdata/intersection-observer/observers-should-update-when-elements-move-expected.png b/src/cobalt/layout_tests/testdata/intersection-observer/observers-should-update-when-elements-move-with-threshold-expected.png
similarity index 100%
rename from src/cobalt/layout_tests/testdata/intersection-observer/observers-should-update-when-elements-move-expected.png
rename to src/cobalt/layout_tests/testdata/intersection-observer/observers-should-update-when-elements-move-with-threshold-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/intersection-observer/observers-should-update-when-elements-move.html b/src/cobalt/layout_tests/testdata/intersection-observer/observers-should-update-when-elements-move-with-threshold.html
similarity index 66%
rename from src/cobalt/layout_tests/testdata/intersection-observer/observers-should-update-when-elements-move.html
rename to src/cobalt/layout_tests/testdata/intersection-observer/observers-should-update-when-elements-move-with-threshold.html
index eb74022..6e72540 100644
--- a/src/cobalt/layout_tests/testdata/intersection-observer/observers-should-update-when-elements-move.html
+++ b/src/cobalt/layout_tests/testdata/intersection-observer/observers-should-update-when-elements-move-with-threshold.html
@@ -50,6 +50,9 @@
             entry.target.style.backgroundColor = "green";
           }
         });
+        if (window.testRunner) {
+          window.testRunner.DoNonMeasuredLayout();
+        }
       }
 
       var options = {
@@ -65,15 +68,25 @@
         window.testRunner.DoNonMeasuredLayout();
       }
 
-      // Move the target element so that the intersection ratio now crosses
-      // the threshold value. An intersection observer update should be
-      // triggered and the callback should run.
-      targetElement.style.top = "-30px";
+      // Without waiting for requestAnimationFrame, only the movement is observed.
+      requestAnimationFrame(() => {
+        window.setTimeout(() => {
+          // Move the target element so that the intersection ratio now crosses
+          // the threshold value. An intersection observer update should be
+          // triggered and the callback should run.
+          targetElement.style.top = "-30px";
 
-      if (window.testRunner) {
-        window.testRunner.DoNonMeasuredLayout();
-        window.setTimeout(function() { window.testRunner.notifyDone(); }, 0);
-      }
+          if (window.testRunner) {
+            window.testRunner.DoNonMeasuredLayout();
+            window.setTimeout(function() { window.testRunner.notifyDone(); }, 0);
+          }
+        }, 0);
+      });
+
+      // Fail after 3 seconds if requestAnimationFrame never triggered.
+      window.setTimeout(function() {
+        window.testRunner.notifyDone();
+      }, 3000);
     });
   </script>
 
diff --git a/src/cobalt/layout_tests/testdata/intersection-observer/observers-should-update-when-elements-move-without-threshold-expected.png b/src/cobalt/layout_tests/testdata/intersection-observer/observers-should-update-when-elements-move-without-threshold-expected.png
new file mode 100644
index 0000000..40b311f
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/intersection-observer/observers-should-update-when-elements-move-without-threshold-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/intersection-observer/observers-should-update-when-elements-move-without-threshold.html b/src/cobalt/layout_tests/testdata/intersection-observer/observers-should-update-when-elements-move-without-threshold.html
new file mode 100644
index 0000000..7d982cf
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/intersection-observer/observers-should-update-when-elements-move-without-threshold.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<!--
+ | This test checks that the observer objects get updated when the elements
+ | themselves move around.
+ | The color of the target element is blue if there is an intersection and
+ | green if there is not. In this test, the initial observation turns the
+ | target blue, but when the target moves above the root, it turns green.
+ |   https://www.w3.org/TR/intersection-observer/
+ -->
+<html>
+<head>
+  <style>
+    div {
+      position: absolute;
+    }
+    #root {
+      margin: 100px;
+      background-color: red;
+      width: 250px;
+      height: 150px;
+    }
+    #target {
+      width: 150px;
+      height: 50px;
+      top: 50px;
+      left: 50px;
+    }
+  </style>
+</head>
+<body>
+  <div id="root">
+    <div id="target"></div>
+  </div>
+
+  <script>
+    if (window.testRunner) {
+      window.testRunner.waitUntilDone();
+    }
+
+    window.addEventListener("load", function() {
+      var rootElement = document.querySelector('#root');
+      var targetElement = document.querySelector('#target');
+
+      function handleIntersect(entries, observer) {
+        entries.forEach(function(entry) {
+          if (entry.isIntersecting) {
+            entry.target.style.backgroundColor = "blue";
+          } else {
+            entry.target.style.backgroundColor = "green";
+          }
+        });
+        if (window.testRunner) {
+            window.testRunner.DoNonMeasuredLayout();
+        }
+      }
+
+      var options = {
+        root: rootElement,
+        rootMargin: "0px",
+      };
+
+      var observer = new IntersectionObserver(handleIntersect, options);
+      observer.observe(targetElement);
+
+      if (window.testRunner) {
+        window.testRunner.DoNonMeasuredLayout();
+      }
+
+      // Without waiting for requestAnimationFrame, only the movement is observed. 
+      requestAnimationFrame(() => {
+        window.setTimeout(() => {
+          // Move the target element so that it is clearly no longer
+          // intersecting. An intersection observer update should be
+          // triggered and the callback should run.
+          targetElement.style.top = "-60px";
+
+          if (window.testRunner) {
+            window.testRunner.DoNonMeasuredLayout();
+            window.setTimeout(function() { window.testRunner.notifyDone(); }, 0);
+          }
+
+        }, 0);
+      });
+
+      // Fail after 3 seconds if requestAnimationFrame never triggered.
+      window.setTimeout(function() {
+        window.testRunner.notifyDone();
+      }, 3000);
+    });
+  </script>
+
+</body>
+</html>
diff --git a/src/cobalt/render_tree/lottie_animation.h b/src/cobalt/render_tree/lottie_animation.h
index 9cab513..39cefb9 100644
--- a/src/cobalt/render_tree/lottie_animation.h
+++ b/src/cobalt/render_tree/lottie_animation.h
@@ -30,7 +30,7 @@
 // LottieAnimation object.
 class LottieAnimation : public Image {
  public:
-  enum class LottieState { kPlaying, kPaused, kStopped };
+  enum class LottieState { kPlaying, kPaused, kStopped, kFrozen };
 
   enum class LottieMode { kNormal, kBounce };
 
@@ -69,6 +69,26 @@
       return true;
     }
 
+    // Return true if we can freeze the animation, i.e. the animation is
+    // currently playing.
+    bool FreezeAnimationState() {
+      if (state == LottieState::kPlaying) {
+        state = LottieState::kFrozen;
+        return true;
+      }
+      return false;
+    }
+
+    // Return true if we can unfreeze the animation, i.e. the animation is
+    // currently frozen.
+    bool UnfreezeAnimationState() {
+      if (state == LottieState::kFrozen) {
+        state = LottieState::kPlaying;
+        return true;
+      }
+      return false;
+    }
+
     // Return true if |count| is updated to a new & valid number.
     bool UpdateCount(int new_count) {
       // |count| must be positive.
@@ -199,17 +219,44 @@
     base::Closure oncomplete_callback;
     base::Closure onloop_callback;
     base::Callback<void(double, double)> onenterframe_callback;
+    base::Closure onfreeze_callback;
+    base::Closure onunfreeze_callback;
   };
 
-  virtual void SetProperties(LottieProperties properties) = 0;
+  void BeginRenderFrame(const LottieProperties& properties) {
+    // Trigger callback if playback state changed.
+    if (played_this_frame_ && properties.state == LottieState::kFrozen &&
+        !properties_.onunfreeze_callback.is_null()) {
+      properties_.onunfreeze_callback.Run();
+    }
+    if (!played_this_frame_ && properties.state == LottieState::kPlaying &&
+        !properties_.onfreeze_callback.is_null()) {
+      properties_.onfreeze_callback.Run();
+    }
+    played_this_frame_ = false;
+    properties_ = properties;
+  }
 
-  virtual void SetAnimationTime(base::TimeDelta animate_function_time) = 0;
+  void SetAnimationTime(base::TimeDelta animate_function_time) {
+    played_this_frame_ = true;
+    SetAnimationTimeInternal(animate_function_time);
+  }
 
  protected:
   virtual ~LottieAnimation() {}
 
   // Allow the reference counting system access to our destructor.
   friend class base::RefCountedThreadSafe<LottieAnimation>;
+
+  virtual void SetAnimationTimeInternal(
+      base::TimeDelta animate_function_time) = 0;
+
+  LottieProperties properties_;
+
+ private:
+  // A flag that will be set to true only if the animation is visible onscreen
+  // and is rendered.
+  bool played_this_frame_ = false;
 };
 
 }  // namespace render_tree
diff --git a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
index 07b7cea..3301a4b 100644
--- a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
@@ -613,6 +613,10 @@
 }
 
 void RenderTreeNodeVisitor::Visit(render_tree::LottieNode* lottie_node) {
+  if (!IsVisible(lottie_node->GetBounds())) {
+    return;
+  }
+
   // Use Skottie to render Lottie animations.
   FallbackRasterize(lottie_node);
 }
diff --git a/src/cobalt/renderer/rasterizer/pixel_test.cc b/src/cobalt/renderer/rasterizer/pixel_test.cc
index 55ab832..ae27062 100644
--- a/src/cobalt/renderer/rasterizer/pixel_test.cc
+++ b/src/cobalt/renderer/rasterizer/pixel_test.cc
@@ -4137,7 +4137,7 @@
           reinterpret_cast<char*>(&animation_data[0]), animation_data.size());
   LottieAnimation::LottieProperties lottie_properties;
   lottie_properties.UpdateState(LottieAnimation::LottieState::kPlaying);
-  animation->SetProperties(lottie_properties);
+  animation->BeginRenderFrame(lottie_properties);
 
   LottieNode::Builder node_builder =
       LottieNode::Builder(animation, RectF(output_surface_size()));
@@ -4154,7 +4154,7 @@
           reinterpret_cast<char*>(&animation_data[0]), animation_data.size());
   LottieAnimation::LottieProperties lottie_properties;
   lottie_properties.UpdateState(LottieAnimation::LottieState::kPlaying);
-  animation->SetProperties(lottie_properties);
+  animation->BeginRenderFrame(lottie_properties);
 
   LottieNode::Builder node_builder =
       LottieNode::Builder(animation, RectF(output_surface_size()));
@@ -4171,7 +4171,7 @@
           reinterpret_cast<char*>(&animation_data[0]), animation_data.size());
   LottieAnimation::LottieProperties lottie_properties;
   lottie_properties.UpdateState(LottieAnimation::LottieState::kPlaying);
-  animation->SetProperties(lottie_properties);
+  animation->BeginRenderFrame(lottie_properties);
 
   LottieNode::Builder node_builder =
       LottieNode::Builder(animation, RectF(output_surface_size()));
@@ -4190,7 +4190,7 @@
   // An animation must start playing before it can be paused.
   lottie_properties.UpdateState(LottieAnimation::LottieState::kPlaying);
   lottie_properties.UpdateState(LottieAnimation::LottieState::kPaused);
-  animation->SetProperties(lottie_properties);
+  animation->BeginRenderFrame(lottie_properties);
 
   LottieNode::Builder node_builder =
       LottieNode::Builder(animation, RectF(output_surface_size()));
@@ -4207,7 +4207,7 @@
           reinterpret_cast<char*>(&animation_data[0]), animation_data.size());
   LottieAnimation::LottieProperties lottie_properties;
   lottie_properties.UpdateState(LottieAnimation::LottieState::kStopped);
-  animation->SetProperties(lottie_properties);
+  animation->BeginRenderFrame(lottie_properties);
 
   LottieNode::Builder node_builder =
       LottieNode::Builder(animation, RectF(output_surface_size()));
@@ -4224,7 +4224,7 @@
           reinterpret_cast<char*>(&animation_data[0]), animation_data.size());
   LottieAnimation::LottieProperties lottie_properties;
   lottie_properties.SeekFrame(10);
-  animation->SetProperties(lottie_properties);
+  animation->BeginRenderFrame(lottie_properties);
 
   LottieNode::Builder node_builder =
       LottieNode::Builder(animation, RectF(output_surface_size()));
@@ -4241,7 +4241,7 @@
           reinterpret_cast<char*>(&animation_data[0]), animation_data.size());
   LottieAnimation::LottieProperties lottie_properties;
   lottie_properties.SeekPercent(50);
-  animation->SetProperties(lottie_properties);
+  animation->BeginRenderFrame(lottie_properties);
 
   LottieNode::Builder node_builder =
       LottieNode::Builder(animation, RectF(output_surface_size()));
@@ -4259,7 +4259,7 @@
   LottieAnimation::LottieProperties lottie_properties;
   lottie_properties.UpdateState(LottieAnimation::LottieState::kPlaying);
   lottie_properties.UpdateDirection(-1);
-  animation->SetProperties(lottie_properties);
+  animation->BeginRenderFrame(lottie_properties);
 
   LottieNode::Builder node_builder =
       LottieNode::Builder(animation, RectF(output_surface_size()));
@@ -4277,7 +4277,7 @@
   LottieAnimation::LottieProperties lottie_properties;
   lottie_properties.UpdateState(LottieAnimation::LottieState::kPlaying);
   lottie_properties.UpdateLoop(true);
-  animation->SetProperties(lottie_properties);
+  animation->BeginRenderFrame(lottie_properties);
 
   LottieNode::Builder node_builder =
       LottieNode::Builder(animation, RectF(output_surface_size()));
@@ -4297,7 +4297,7 @@
   LottieAnimation::LottieProperties lottie_properties;
   lottie_properties.UpdateState(LottieAnimation::LottieState::kPlaying);
   lottie_properties.UpdateLoop(false);
-  animation->SetProperties(lottie_properties);
+  animation->BeginRenderFrame(lottie_properties);
 
   LottieNode::Builder node_builder =
       LottieNode::Builder(animation, RectF(output_surface_size()));
@@ -4318,7 +4318,7 @@
   lottie_properties.UpdateState(LottieAnimation::LottieState::kPlaying);
   lottie_properties.UpdateLoop(true);
   lottie_properties.UpdateCount(2);
-  animation->SetProperties(lottie_properties);
+  animation->BeginRenderFrame(lottie_properties);
 
   LottieNode::Builder node_builder =
       LottieNode::Builder(animation, RectF(output_surface_size()));
@@ -4339,7 +4339,7 @@
   lottie_properties.UpdateState(LottieAnimation::LottieState::kPlaying);
   lottie_properties.UpdateLoop(true);
   lottie_properties.UpdateCount(2);
-  animation->SetProperties(lottie_properties);
+  animation->BeginRenderFrame(lottie_properties);
 
   LottieNode::Builder node_builder =
       LottieNode::Builder(animation, RectF(output_surface_size()));
@@ -4360,7 +4360,7 @@
   lottie_properties.UpdateState(LottieAnimation::LottieState::kPlaying);
   lottie_properties.UpdateLoop(true);
   lottie_properties.UpdateMode(LottieAnimation::LottieMode::kBounce);
-  animation->SetProperties(lottie_properties);
+  animation->BeginRenderFrame(lottie_properties);
 
   LottieNode::Builder node_builder =
       LottieNode::Builder(animation, RectF(output_surface_size()));
@@ -4380,7 +4380,7 @@
   LottieAnimation::LottieProperties lottie_properties;
   lottie_properties.UpdateState(LottieAnimation::LottieState::kPlaying);
   lottie_properties.UpdateSpeed(2);
-  animation->SetProperties(lottie_properties);
+  animation->BeginRenderFrame(lottie_properties);
 
   LottieNode::Builder node_builder =
       LottieNode::Builder(animation, RectF(output_surface_size()));
@@ -4399,7 +4399,7 @@
   lottie_properties.UpdateState(LottieAnimation::LottieState::kPlaying);
   lottie_properties.UpdateLoop(false);
   lottie_properties.ToggleLooping();
-  animation->SetProperties(lottie_properties);
+  animation->BeginRenderFrame(lottie_properties);
 
   LottieNode::Builder node_builder =
       LottieNode::Builder(animation, RectF(output_surface_size()));
@@ -4420,7 +4420,7 @@
   lottie_properties.UpdateState(LottieAnimation::LottieState::kPlaying);
   lottie_properties.UpdateLoop(true);
   lottie_properties.ToggleLooping();
-  animation->SetProperties(lottie_properties);
+  animation->BeginRenderFrame(lottie_properties);
 
   LottieNode::Builder node_builder =
       LottieNode::Builder(animation, RectF(output_surface_size()));
@@ -4440,7 +4440,7 @@
   LottieAnimation::LottieProperties lottie_properties;
   lottie_properties.UpdateState(LottieAnimation::LottieState::kStopped);
   lottie_properties.TogglePlay();
-  animation->SetProperties(lottie_properties);
+  animation->BeginRenderFrame(lottie_properties);
 
   LottieNode::Builder node_builder =
       LottieNode::Builder(animation, RectF(output_surface_size()));
@@ -4459,7 +4459,7 @@
   LottieAnimation::LottieProperties lottie_properties;
   lottie_properties.UpdateState(LottieAnimation::LottieState::kPaused);
   lottie_properties.TogglePlay();
-  animation->SetProperties(lottie_properties);
+  animation->BeginRenderFrame(lottie_properties);
 
   LottieNode::Builder node_builder =
       LottieNode::Builder(animation, RectF(output_surface_size()));
@@ -4478,7 +4478,7 @@
   LottieAnimation::LottieProperties lottie_properties;
   lottie_properties.UpdateState(LottieAnimation::LottieState::kPlaying);
   lottie_properties.TogglePlay();
-  animation->SetProperties(lottie_properties);
+  animation->BeginRenderFrame(lottie_properties);
 
   LottieNode::Builder node_builder =
       LottieNode::Builder(animation, RectF(output_surface_size()));
@@ -4497,7 +4497,7 @@
   LottieAnimation::LottieProperties lottie_properties;
   lottie_properties.UpdateState(LottieAnimation::LottieState::kPlaying);
   lottie_properties.UpdateLoop(true);
-  animation->SetProperties(lottie_properties);
+  animation->BeginRenderFrame(lottie_properties);
 
   LottieNode::Builder node_builder =
       LottieNode::Builder(animation, RectF(output_surface_size()));
@@ -4515,7 +4515,7 @@
   LottieAnimation::LottieProperties lottie_properties;
   lottie_properties.UpdateState(LottieAnimation::LottieState::kPlaying);
   lottie_properties.UpdateLoop(true);
-  animation->SetProperties(lottie_properties);
+  animation->BeginRenderFrame(lottie_properties);
 
   LottieNode::Builder node_builder =
       LottieNode::Builder(animation, RectF(output_surface_size()));
diff --git a/src/cobalt/renderer/rasterizer/skia/skottie_animation.cc b/src/cobalt/renderer/rasterizer/skia/skottie_animation.cc
index 71af29e..679c4af 100644
--- a/src/cobalt/renderer/rasterizer/skia/skottie_animation.cc
+++ b/src/cobalt/renderer/rasterizer/skia/skottie_animation.cc
@@ -30,11 +30,8 @@
   json_size_in_bytes_ = builder.getStats().fJsonSize;
 }
 
-void SkottieAnimation::SetProperties(LottieProperties properties) {
-  properties_ = properties;
-}
-
-void SkottieAnimation::SetAnimationTime(base::TimeDelta animate_function_time) {
+void SkottieAnimation::SetAnimationTimeInternal(
+    base::TimeDelta animate_function_time) {
   // Seeking to a particular frame takes precedence over normal playback.
   // Check whether "seek()" has been called but has yet to occur.
   if (seek_counter_ != properties_.seek_counter) {
@@ -75,7 +72,6 @@
     return;
   }
 
-  DCHECK(properties_.state == LottieState::kPlaying);
   base::TimeDelta current_animation_time = last_updated_animation_time_;
   base::TimeDelta time_elapsed =
       animate_function_time - last_updated_animate_function_time_;
diff --git a/src/cobalt/renderer/rasterizer/skia/skottie_animation.h b/src/cobalt/renderer/rasterizer/skia/skottie_animation.h
index f6cd6eb..3d5531c 100644
--- a/src/cobalt/renderer/rasterizer/skia/skottie_animation.h
+++ b/src/cobalt/renderer/rasterizer/skia/skottie_animation.h
@@ -37,9 +37,7 @@
 
   bool IsOpaque() const override { return false; }
 
-  void SetProperties(LottieProperties properties) override;
-
-  void SetAnimationTime(base::TimeDelta animate_function_time) override;
+  void SetAnimationTimeInternal(base::TimeDelta animate_function_time) override;
 
   sk_sp<skottie::Animation> GetSkottieAnimation() { return skottie_animation_; }
 
@@ -52,7 +50,6 @@
   math::Size animation_size_;
   uint32 json_size_in_bytes_;
 
-  LottieProperties properties_;
   // |seek_counter_| is used to indicate whether a particular seek has already
   // been processed. When |LottieProperties::seek_counter| is different, then
   // the requested seek should be performed and |seek_counter_| updated to match
diff --git a/src/starboard/CHANGELOG.md b/src/starboard/CHANGELOG.md
index 34bdb7c..cdd706c 100644
--- a/src/starboard/CHANGELOG.md
+++ b/src/starboard/CHANGELOG.md
@@ -232,7 +232,12 @@
 ### Deprecated unused enums |kSbPlayerDecoderStateBufferFull| and
 |kSbPlayerDecoderStateDestroyed|.
 
-### Deprecate the |SB_HAS_ASYNC_AUDIO_FRAMES_REPORTING| macro.
+### Deprecated the usage of |SbMediaIsOutputProtected()| and
+|SbMediaSetOutputProtection()|.
+
+### Deprecated the |SB_HAS_QUIRK_SEEK_TO_KEYFRAME| macro.
+
+### Deprecated the |SB_HAS_ASYNC_AUDIO_FRAMES_REPORTING| macro.
 
 ### Deprecated 'cobalt_minimum_frame_time_in_milliseconds'.
 
diff --git a/src/starboard/android/shared/media_is_output_protected.cc b/src/starboard/android/shared/media_is_output_protected.cc
deleted file mode 100644
index aff256b..0000000
--- a/src/starboard/android/shared/media_is_output_protected.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// 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/media.h"
-
-#include "starboard/common/log.h"
-
-bool SbMediaIsOutputProtected() {
-  // Protected output is expected to be properly enforced by the system level
-  // DRM system, and also does not provide a convenient way to query its
-  // state.  Thus, from a starboard application perspective, we always return
-  // |true|, and rely on playback of protected content failing at a later
-  // point if the output was not actually secure.
-  return true;
-}
diff --git a/src/starboard/android/shared/media_set_output_protection.cc b/src/starboard/android/shared/media_set_output_protection.cc
deleted file mode 100644
index 627407b..0000000
--- a/src/starboard/android/shared/media_set_output_protection.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// 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/media.h"
-
-#include "starboard/common/log.h"
-
-bool SbMediaSetOutputProtection(bool enabled) {
-  // Output is always protected from a starboard application perspective, so
-  // return a redundant true.  See documentation in |SbMediaIsOutputProtected|
-  // for further details.
-  return true;
-}
diff --git a/src/starboard/android/shared/starboard_platform.gypi b/src/starboard/android/shared/starboard_platform.gypi
index 2f94003..f6a01f5 100644
--- a/src/starboard/android/shared/starboard_platform.gypi
+++ b/src/starboard/android/shared/starboard_platform.gypi
@@ -132,9 +132,7 @@
         'media_get_initial_buffer_capacity.cc',
         'media_get_max_buffer_capacity.cc',
         'media_is_audio_supported.cc',
-        'media_is_output_protected.cc',
         'media_is_video_supported.cc',
-        'media_set_output_protection.cc',
         'microphone_impl.cc',
         'player_create.cc',
         'player_destroy.cc',
diff --git a/src/starboard/build/base_configuration.gypi b/src/starboard/build/base_configuration.gypi
index 74bd2eb..b8f6d94 100644
--- a/src/starboard/build/base_configuration.gypi
+++ b/src/starboard/build/base_configuration.gypi
@@ -56,6 +56,9 @@
     # requires a 'lib' starboard implementation for the corresponding platform.
     'sb_enable_lib%': '<(sb_enable_lib)',
 
+    # Disables an NPLB audit of C++14 support.
+    'sb_disable_cpp14_audit': 0,
+
     # When this is set to true, the web bindings for the microphone
     # are disabled
     'sb_disable_microphone_idl%': 0,
diff --git a/src/starboard/elf_loader/exported_symbols.cc b/src/starboard/elf_loader/exported_symbols.cc
index 49bbb9b..b07c021 100644
--- a/src/starboard/elf_loader/exported_symbols.cc
+++ b/src/starboard/elf_loader/exported_symbols.cc
@@ -125,9 +125,7 @@
   REGISTER_SYMBOL(SbFileWrite);
   REGISTER_SYMBOL(SbMediaGetAudioConfiguration);
   REGISTER_SYMBOL(SbMediaGetAudioOutputCount);
-  REGISTER_SYMBOL(SbMediaIsOutputProtected);
   REGISTER_SYMBOL(SbMediaIsSupported);
-  REGISTER_SYMBOL(SbMediaSetOutputProtection);
   REGISTER_SYMBOL(SbMemoryAllocate);
   REGISTER_SYMBOL(SbMemoryAllocateAligned);
   REGISTER_SYMBOL(SbMemoryAllocateNoReport);
diff --git a/src/starboard/evergreen/arm/shared/cobalt/configuration.py b/src/starboard/evergreen/arm/shared/cobalt/configuration.py
index 9f4af82..c2776d3 100644
--- a/src/starboard/evergreen/arm/shared/cobalt/configuration.py
+++ b/src/starboard/evergreen/arm/shared/cobalt/configuration.py
@@ -47,8 +47,16 @@
 
   def GetTestFilters(self):
     filters = super(CobaltARMConfiguration, self).GetTestFilters()
-    for target, tests in self.__FILTERED_TESTS.iteritems():
-      filters.extend(test_filter.TestFilter(target, test) for test in tests)
+    filters.extend([
+        test_filter.TestFilter('bindings_test', 'DateBindingsTest.PosixEpoch'),
+        # TODO: Remove this filter once the layout_tests slowdown in the debug
+        # configuration is resolved.
+        test_filter.TestFilter('layout_tests', test_filter.FILTER_ALL, 'debug'),
+        test_filter.TestFilter('renderer_test',
+                               'PixelTest.CircularSubPixelBorder'),
+        test_filter.TestFilter('renderer_test',
+                               'PixelTest.FilterBlurred100PxText'),
+    ])
     return filters
 
   def GetWebPlatformTestFilters(self):
@@ -72,10 +80,3 @@
             'ASAN_OPTIONS': 'detect_leaks=0'
         }
     }
-
-  __FILTERED_TESTS = {
-      'bindings_test': ['DateBindingsTest.PosixEpoch'],
-      'renderer_test': [
-          'PixelTest.CircularSubPixelBorder', 'PixelTest.FilterBlurred100PxText'
-      ],
-  }
diff --git a/src/starboard/linux/shared/BUILD.gn b/src/starboard/linux/shared/BUILD.gn
index 8c26a02..f5ed21b 100644
--- a/src/starboard/linux/shared/BUILD.gn
+++ b/src/starboard/linux/shared/BUILD.gn
@@ -483,9 +483,7 @@
     "//starboard/shared/starboard/media/media_get_audio_configuration_stereo_only.cc",
     "//starboard/shared/starboard/media/media_get_audio_output_count_stereo_only.cc",
     "//starboard/shared/starboard/media/media_is_audio_supported_aac_and_opus.cc",
-    "//starboard/shared/starboard/media/media_is_output_protected.cc",
     "//starboard/shared/starboard/media/media_is_transfer_characteristics_supported.cc",
-    "//starboard/shared/starboard/media/media_set_output_protection.cc",
     "//starboard/shared/starboard/media/media_util.cc",
     "//starboard/shared/starboard/media/media_util.h",
     "//starboard/shared/starboard/media/mime_type.cc",
@@ -574,7 +572,9 @@
     "//starboard/shared/stub/drm_update_session.cc",
     "//starboard/shared/stub/image_decode.cc",
     "//starboard/shared/stub/image_is_decode_supported.cc",
+    "//starboard/shared/stub/media_is_output_protected.cc",
     "//starboard/shared/stub/media_is_supported.cc",
+    "//starboard/shared/stub/media_set_output_protection.cc",
     "//starboard/shared/stub/microphone_close.cc",
     "//starboard/shared/stub/microphone_create.cc",
     "//starboard/shared/stub/microphone_destroy.cc",
diff --git a/src/starboard/linux/shared/media_is_output_protected.cc b/src/starboard/linux/shared/media_is_output_protected.cc
deleted file mode 100644
index 7a64b05..0000000
--- a/src/starboard/linux/shared/media_is_output_protected.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// 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/media.h"
-
-bool SbMediaIsOutputProtected() {
-  // Pretend that HDCP is always on because Linux is only able to play SD
-  // protected content.
-  return true;
-}
diff --git a/src/starboard/linux/shared/starboard_platform.gypi b/src/starboard/linux/shared/starboard_platform.gypi
index 2167157..64f6c7e 100644
--- a/src/starboard/linux/shared/starboard_platform.gypi
+++ b/src/starboard/linux/shared/starboard_platform.gypi
@@ -284,7 +284,6 @@
       '<(DEPTH)/starboard/shared/starboard/media/media_is_buffer_pool_allocate_on_demand.cc',
       '<(DEPTH)/starboard/shared/starboard/media/media_is_buffer_using_memory_pool.cc',
       '<(DEPTH)/starboard/shared/starboard/media/media_is_transfer_characteristics_supported.cc',
-      '<(DEPTH)/starboard/shared/starboard/media/media_set_output_protection.cc',
       '<(DEPTH)/starboard/shared/starboard/media/mime_type.cc',
       '<(DEPTH)/starboard/shared/starboard/media/mime_type.h',
       '<(DEPTH)/starboard/shared/starboard/memory.cc',
@@ -342,7 +341,9 @@
       '<(DEPTH)/starboard/shared/stub/cryptography_transform.cc',
       '<(DEPTH)/starboard/shared/stub/image_decode.cc',
       '<(DEPTH)/starboard/shared/stub/image_is_decode_supported.cc',
+      '<(DEPTH)/starboard/shared/stub/media_is_output_protected.cc',
       '<(DEPTH)/starboard/shared/stub/media_set_audio_write_duration.cc',
+      '<(DEPTH)/starboard/shared/stub/media_set_output_protection.cc',
       '<(DEPTH)/starboard/shared/stub/microphone_close.cc',
       '<(DEPTH)/starboard/shared/stub/microphone_create.cc',
       '<(DEPTH)/starboard/shared/stub/microphone_destroy.cc',
@@ -419,7 +420,6 @@
         ],
         'starboard_platform_sources': [
           '<(DEPTH)/starboard/linux/shared/drm_create_system.cc',
-          '<(DEPTH)/starboard/linux/shared/media_is_output_protected.cc',
           '<(DEPTH)/starboard/linux/shared/oemcrypto_engine_device_properties_linux.cc',
 
           '<(DEPTH)/starboard/shared/starboard/drm/drm_close_session.cc',
@@ -441,7 +441,6 @@
         ],
       }, {
         'starboard_platform_sources': [
-          '<(DEPTH)/starboard/shared/starboard/media/media_is_output_protected.cc',
           '<(DEPTH)/starboard/shared/stub/media_is_supported.cc',
           '<(DEPTH)/starboard/shared/stub/drm_close_session.cc',
           '<(DEPTH)/starboard/shared/stub/drm_create_system.cc',
diff --git a/src/starboard/media.h b/src/starboard/media.h
index bfc5864..df9b45f 100644
--- a/src/starboard/media.h
+++ b/src/starboard/media.h
@@ -571,6 +571,7 @@
     int output_index,
     SbMediaAudioConfiguration* out_configuration);
 
+#if SB_API_VERSION < 12
 // Indicates whether output copy protection is currently enabled on all capable
 // outputs. If |true|, then non-protection-capable outputs are expected to be
 // blanked.
@@ -586,6 +587,7 @@
 // |enabled|: Indicates whether output protection is enabled (|true|) or
 //   disabled.
 SB_EXPORT bool SbMediaSetOutputProtection(bool enabled);
+#endif  // SB_API_VERSION < 12
 
 // Value used when a video's resolution is not known.
 #define kSbMediaVideoResolutionDimensionInvalid 0
diff --git a/src/starboard/nplb/compiler_compliance/compiler_compliance.gyp b/src/starboard/nplb/compiler_compliance/compiler_compliance.gyp
new file mode 100644
index 0000000..12d941c
--- /dev/null
+++ b/src/starboard/nplb/compiler_compliance/compiler_compliance.gyp
@@ -0,0 +1,31 @@
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+  'targets': [
+    {
+      'target_name': 'cpp14_supported',
+      'type': 'static_library',
+      'cflags_cc': [
+        '-std=c++14',
+      ],
+      'sources': [
+        'cpp14_constexpr.cc',
+      ],
+      'dependencies': [
+        '<(DEPTH)/starboard/starboard.gyp:starboard',
+      ],
+    },
+  ],
+}
diff --git a/src/starboard/nplb/compiler_compliance/cpp14_constexpr.cc b/src/starboard/nplb/compiler_compliance/cpp14_constexpr.cc
new file mode 100644
index 0000000..3a07f42
--- /dev/null
+++ b/src/starboard/nplb/compiler_compliance/cpp14_constexpr.cc
@@ -0,0 +1,38 @@
+// Copyright 2020 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/configuration.h"
+#include "starboard/types.h"
+
+namespace starboard {
+namespace nplb {
+namespace {
+
+constexpr const char kPangram[] = "The quick brown fox jumps over a lazy dog";
+
+// The C++14 constexpr permits allocations and branches.
+constexpr size_t CompileTimeStringLength(const char* str) {
+  size_t length = 0;
+  while (*(str + length)) {
+    ++length;
+  }
+  return length + 1;
+}
+
+SB_COMPILE_ASSERT(CompileTimeStringLength(kPangram) == sizeof(kPangram),
+                  CompileTimeStringLength_does_not_return_correct_length);
+
+}  // namespace
+}  // namespace nplb
+}  // namespace starboard
diff --git a/src/starboard/nplb/nplb.gyp b/src/starboard/nplb/nplb.gyp
index e34ed0c..d4e249c 100644
--- a/src/starboard/nplb/nplb.gyp
+++ b/src/starboard/nplb/nplb.gyp
@@ -316,14 +316,19 @@
       'dependencies': [
         '<@(cobalt_platform_dependencies)',
         '<(DEPTH)/starboard/shared/starboard/media/media.gyp:media_util',
-        '<(DEPTH)/starboard/shared/starboard/player/player.gyp:video_dmp',
         '<(DEPTH)/starboard/shared/starboard/player/player.gyp:player_copy_test_data',
+        '<(DEPTH)/starboard/shared/starboard/player/player.gyp:video_dmp',
         '<(DEPTH)/starboard/starboard.gyp:starboard',
         '<(DEPTH)/testing/gmock.gyp:gmock',
         '<(DEPTH)/testing/gtest.gyp:gtest',
         'copy_nplb_file_tests_data',
       ],
       'conditions': [
+        ['sb_disable_cpp14_audit != 1', {
+          'dependencies': [
+            '<(DEPTH)/starboard/nplb/compiler_compliance/compiler_compliance.gyp:cpp14_supported',
+          ],
+        }],
         ['sb_evergreen != 1', {
           'sources': [
             # Segfaults or causes unresolved symbols for Cobalt Evergreen.
diff --git a/src/starboard/raspi/shared/cobalt/configuration.py b/src/starboard/raspi/shared/cobalt/configuration.py
index 7c7d912..26f981d 100644
--- a/src/starboard/raspi/shared/cobalt/configuration.py
+++ b/src/starboard/raspi/shared/cobalt/configuration.py
@@ -24,8 +24,9 @@
 
   def __init__(self, platform_configuration, application_name,
                application_directory):
-    super(CobaltRaspiConfiguration, self).__init__(
-        platform_configuration, application_name, application_directory)
+    super(CobaltRaspiConfiguration,
+          self).__init__(platform_configuration, application_name,
+                         application_directory)
 
   def GetPostIncludes(self):
     # If there isn't a configuration.gypi found in the usual place, we'll
@@ -47,6 +48,9 @@
   def GetTestFilters(self):
     filters = super(CobaltRaspiConfiguration, self).GetTestFilters()
     filters.extend([
+        # TODO: Remove this filter once the layout_tests slowdown in the debug
+        # configuration is resolved.
+        test_filter.TestFilter('layout_tests', test_filter.FILTER_ALL, 'debug'),
         # These tests are currently producing slightly different images on the
         # RasPi.
         test_filter.TestFilter('renderer_test',
diff --git a/src/starboard/raspi/shared/gyp_configuration.gypi b/src/starboard/raspi/shared/gyp_configuration.gypi
index ea69e9f..deb191c 100644
--- a/src/starboard/raspi/shared/gyp_configuration.gypi
+++ b/src/starboard/raspi/shared/gyp_configuration.gypi
@@ -18,6 +18,9 @@
     # TODO: Remove when omitted for all platforms in base_configuration.gypi.
     'sb_static_contents_output_data_dir': '<(PRODUCT_DIR)/content',
 
+    # The Raspberry Pi compiler does not have support for C++14.
+    'sb_disable_cpp14_audit': 1,
+
     'target_os': 'linux',
 
     'sysroot%': '/',
diff --git a/src/starboard/raspi/shared/starboard_platform.gypi b/src/starboard/raspi/shared/starboard_platform.gypi
index f1215a6..352a3cb 100644
--- a/src/starboard/raspi/shared/starboard_platform.gypi
+++ b/src/starboard/raspi/shared/starboard_platform.gypi
@@ -314,9 +314,7 @@
         '<(DEPTH)/starboard/shared/starboard/media/media_is_audio_supported_aac_only.cc',
         '<(DEPTH)/starboard/shared/starboard/media/media_is_buffer_pool_allocate_on_demand.cc',
         '<(DEPTH)/starboard/shared/starboard/media/media_is_buffer_using_memory_pool.cc',
-        '<(DEPTH)/starboard/shared/starboard/media/media_is_output_protected.cc',
         '<(DEPTH)/starboard/shared/starboard/media/media_is_transfer_characteristics_supported.cc',
-        '<(DEPTH)/starboard/shared/starboard/media/media_set_output_protection.cc',
         '<(DEPTH)/starboard/shared/starboard/media/mime_type.cc',
         '<(DEPTH)/starboard/shared/starboard/media/mime_type.h',
         '<(DEPTH)/starboard/shared/starboard/memory.cc',
diff --git a/src/starboard/shared/starboard/media/media_is_output_protected.cc b/src/starboard/shared/starboard/media/media_is_output_protected.cc
deleted file mode 100644
index a8dca1b..0000000
--- a/src/starboard/shared/starboard/media/media_is_output_protected.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2016 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/media.h"
-
-#include "starboard/common/log.h"
-
-bool SbMediaIsOutputProtected() {
-  SB_NOTIMPLEMENTED();
-  return false;
-}
diff --git a/src/starboard/shared/starboard/media/media_set_output_protection.cc b/src/starboard/shared/starboard/media/media_set_output_protection.cc
deleted file mode 100644
index 6b4953e..0000000
--- a/src/starboard/shared/starboard/media/media_set_output_protection.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2016 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/media.h"
-
-#include "starboard/common/log.h"
-
-bool SbMediaSetOutputProtection(bool enabled) {
-  SB_NOTIMPLEMENTED();
-  return false;
-}
diff --git a/src/starboard/shared/stub/media_is_output_protected.cc b/src/starboard/shared/stub/media_is_output_protected.cc
index b5280d3..3d117a5 100644
--- a/src/starboard/shared/stub/media_is_output_protected.cc
+++ b/src/starboard/shared/stub/media_is_output_protected.cc
@@ -16,6 +16,8 @@
 
 #include "starboard/common/log.h"
 
+#if SB_API_VERSION < 12
 bool SbMediaIsOutputProtected() {
   return false;
 }
+#endif  // SB_API_VERSION < 12
diff --git a/src/starboard/shared/stub/media_set_output_protection.cc b/src/starboard/shared/stub/media_set_output_protection.cc
index 19a886b..219237d 100644
--- a/src/starboard/shared/stub/media_set_output_protection.cc
+++ b/src/starboard/shared/stub/media_set_output_protection.cc
@@ -16,6 +16,8 @@
 
 #include "starboard/common/log.h"
 
+#if SB_API_VERSION < 12
 bool SbMediaSetOutputProtection(bool enabled) {
   return false;
 }
+#endif  // SB_API_VERSION < 12
diff --git a/src/third_party/boringssl/boringssl.gyp b/src/third_party/boringssl/boringssl.gyp
index 6485284..585fd34 100644
--- a/src/third_party/boringssl/boringssl.gyp
+++ b/src/third_party/boringssl/boringssl.gyp
@@ -40,15 +40,7 @@
           '<(DEPTH)/starboard/client_porting/eztime/eztime.gyp:eztime',
           '<(DEPTH)/starboard/starboard_headers_only.gyp:starboard_headers_only',
         ],
-        'sources': [
-          '<(boringssl_root)/crypto/rand_extra/starboard.c',
-          '<(boringssl_root)/crypto/cpu-starboard.c',
-        ],
         'sources!': [
-          '<(boringssl_root)/crypto/bio/connect.c',
-          '<(boringssl_root)/crypto/bio/fd.c',
-          '<(boringssl_root)/crypto/bio/socket.c',
-          '<(boringssl_root)/crypto/bio/socket_helper.c',
           '<(boringssl_root)/crypto/rand_extra/deterministic.c',
           '<(boringssl_root)/crypto/rand_extra/fuchsia.c',
           '<(boringssl_root)/crypto/rand_extra/windows.c',
@@ -94,7 +86,7 @@
   },
   'targets': [
     {
-      'target_name': 'crypto',
+      'target_name': 'crypto_full',
 
       # Include the source file list generated by BoringSSL
       'includes': ['boringssl.gypi',],
@@ -242,7 +234,23 @@
             }],
           ],
         }],
+        ['OS=="starboard"', {
+          'sources': [
+            '<(boringssl_root)/crypto/rand_extra/starboard.c',
+            '<(boringssl_root)/crypto/cpu-starboard.c',
+          ],
+        }],
       ],
     },
+    {
+      'target_name': 'crypto',
+      'sources!': [
+        '<(boringssl_root)/crypto/bio/connect.c',
+        '<(boringssl_root)/crypto/bio/fd.c',
+        '<(boringssl_root)/crypto/bio/socket.c',
+        '<(boringssl_root)/crypto/bio/socket_helper.c',
+      ],
+      'dependencies': [ 'crypto_full' ],
+    },
   ],
 }
diff --git a/src/third_party/crashpad/build/crashpad_dependencies.gypi b/src/third_party/crashpad/build/crashpad_dependencies.gypi
index 7fd1cf6..1e04abd 100644
--- a/src/third_party/crashpad/build/crashpad_dependencies.gypi
+++ b/src/third_party/crashpad/build/crashpad_dependencies.gypi
@@ -32,9 +32,6 @@
   # and its copy of the gtest library.
 
   'variables': {
-    # When with external dependencies, build/gyp_crashpad.py sets
-    # crashpad_dependencies to "external", and this % assignment will not
-    # override it.
-    'crashpad_dependencies%': 'standalone',
+    'crashpad_dependencies%': 'external',
   },
 }
diff --git a/src/third_party/crashpad/crashpad.gyp b/src/third_party/crashpad/crashpad.gyp
index f30a9df..cbbf283 100644
--- a/src/third_party/crashpad/crashpad.gyp
+++ b/src/third_party/crashpad/crashpad.gyp
@@ -19,19 +19,19 @@
       'type': 'none',
       'dependencies': [
         'client/client.gyp:*',
-        'client/client_test.gyp:*',
+        # 'client/client_test.gyp:*',
         'compat/compat.gyp:*',
         'handler/handler.gyp:*',
-        'handler/handler_test.gyp:*',
+        # 'handler/handler_test.gyp:*',
         'minidump/minidump.gyp:*',
-        'minidump/minidump_test.gyp:*',
+        # 'minidump/minidump_test.gyp:*',
         'snapshot/snapshot.gyp:*',
-        'snapshot/snapshot_test.gyp:*',
-        'test/test.gyp:*',
-        'test/test_test.gyp:*',
+        # 'snapshot/snapshot_test.gyp:*',
+        # 'test/test.gyp:*',
+        # 'test/test_test.gyp:*',
         'tools/tools.gyp:*',
         'util/util.gyp:*',
-        'util/util_test.gyp:*',
+        # 'util/util_test.gyp:*',
       ],
       'sources': [
         'doc/support/crashpad.doxy.h',
diff --git a/src/third_party/crashpad/handler/handler.gyp b/src/third_party/crashpad/handler/handler.gyp
index f8313a5..2080d97 100644
--- a/src/third_party/crashpad/handler/handler.gyp
+++ b/src/third_party/crashpad/handler/handler.gyp
@@ -31,11 +31,17 @@
         '../third_party/zlib/zlib.gyp:zlib',
         '../tools/tools.gyp:crashpad_tool_support',
         '../util/util.gyp:crashpad_util',
+        '<(DEPTH)/third_party/boringssl/boringssl.gyp:crypto_full',
+        '<(DEPTH)/starboard/starboard.gyp:starboard',
       ],
       'include_dirs': [
         '..',
       ],
+      'defines': [
+        'CRASHPAD_USE_BORINGSSL',
+      ],
       'sources': [
+        '../util/net/http_transport_socket.cc',
         'crash_report_upload_thread.cc',
         'crash_report_upload_thread.h',
         'handler_main.cc',
diff --git a/src/third_party/crashpad/handler/handler_main.cc b/src/third_party/crashpad/handler/handler_main.cc
index e0a262c..be73916 100644
--- a/src/third_party/crashpad/handler/handler_main.cc
+++ b/src/third_party/crashpad/handler/handler_main.cc
@@ -100,6 +100,12 @@
 #include "handler/linux/exception_handler_server.h"
 #endif  // OS_MACOSX
 
+#if defined(STARBOARD)
+// Stub out required starboard implementation as this is built in parallel.
+#include "starboard/event.h"
+void SbEventHandle(const SbEvent* event) {}
+#endif  // defined(STARBOARD)
+
 namespace crashpad {
 
 namespace {
diff --git a/src/third_party/crashpad/third_party/lss/lss.h b/src/third_party/crashpad/third_party/lss/lss.h
index 11209ff..8cd9293 100644
--- a/src/third_party/crashpad/third_party/lss/lss.h
+++ b/src/third_party/crashpad/third_party/lss/lss.h
@@ -15,7 +15,9 @@
 #ifndef CRASHPAD_THIRD_PARTY_LSS_LSS_H_
 #define CRASHPAD_THIRD_PARTY_LSS_LSS_H_
 
-#if defined(CRASHPAD_LSS_SOURCE_EXTERNAL)
+#if defined(STARBOARD)
+#include "third_party/linux-syscall-support/linux_syscall_support.h"
+#elif defined(CRASHPAD_LSS_SOURCE_EXTERNAL)
 #include "third_party/lss/linux_syscall_support.h"
 #elif defined(CRASHPAD_LSS_SOURCE_EMBEDDED)
 #include "third_party/lss/lss/linux_syscall_support.h"
diff --git a/src/third_party/crashpad/third_party/mini_chromium/mini_chromium.gyp b/src/third_party/crashpad/third_party/mini_chromium/mini_chromium.gyp
index e14a1fa..e4e9357 100644
--- a/src/third_party/crashpad/third_party/mini_chromium/mini_chromium.gyp
+++ b/src/third_party/crashpad/third_party/mini_chromium/mini_chromium.gyp
@@ -35,10 +35,10 @@
         }],
         ['crashpad_dependencies=="external"', {
           'dependencies': [
-            '../../../../mini_chromium/mini_chromium/base/base.gyp:base',
+            '<(DEPTH)/third_party/mini_chromium/base/base.gyp:base',
           ],
           'export_dependent_settings': [
-            '../../../../mini_chromium/mini_chromium/base/base.gyp:base',
+            '<(DEPTH)/third_party/mini_chromium/base/base.gyp:base',
           ],
         }],
       ],
diff --git a/src/third_party/crashpad/third_party/zlib/zlib.gyp b/src/third_party/crashpad/third_party/zlib/zlib.gyp
index b5fbc83..09c68b0 100644
--- a/src/third_party/crashpad/third_party/zlib/zlib.gyp
+++ b/src/third_party/crashpad/third_party/zlib/zlib.gyp
@@ -163,7 +163,7 @@
             ],
           },
           'dependencies': [
-            '../../../../zlib/zlib.gyp:zlib',
+            '<(DEPTH)/third_party/zlib/zlib.gyp:zlib',
           ],
         }],
       ],
diff --git a/src/third_party/crashpad/third_party/zlib/zlib_crashpad.h b/src/third_party/crashpad/third_party/zlib/zlib_crashpad.h
index fd497a8..f8f9f75 100644
--- a/src/third_party/crashpad/third_party/zlib/zlib_crashpad.h
+++ b/src/third_party/crashpad/third_party/zlib/zlib_crashpad.h
@@ -19,7 +19,9 @@
 // available at any other location in the source tree. It will #include the
 // proper <zlib.h> depending on how the build has been configured.
 
-#if defined(CRASHPAD_ZLIB_SOURCE_SYSTEM) || \
+#if defined(STARBOARD)
+#include "third_party/zlib/zlib.h"
+#elif defined(CRASHPAD_ZLIB_SOURCE_SYSTEM) || \
     defined(CRASHPAD_ZLIB_SOURCE_EXTERNAL)
 #include <zlib.h>
 #elif defined(CRASHPAD_ZLIB_SOURCE_EMBEDDED)
diff --git a/src/third_party/crashpad/util/file/file_io_posix.cc b/src/third_party/crashpad/util/file/file_io_posix.cc
index 91b252a..e0d4eb3 100644
--- a/src/third_party/crashpad/util/file/file_io_posix.cc
+++ b/src/third_party/crashpad/util/file/file_io_posix.cc
@@ -156,11 +156,14 @@
 FileHandle LoggingOpenMemoryFileForReadAndWrite(const base::FilePath& name) {
   DCHECK(name.value().find('/') == std::string::npos);
 
-  int result = HANDLE_EINTR(memfd_create(name.value().c_str(), 0));
+  int result;
+#if !defined(STARBOARD)
+  result = HANDLE_EINTR(memfd_create(name.value().c_str(), 0));
   if (result >= 0 || errno != ENOSYS) {
     PLOG_IF(ERROR, result < 0) << "memfd_create";
     return result;
   }
+#endif
 
   const char* tmp = getenv("TMPDIR");
   tmp = tmp ? tmp : "/tmp";
diff --git a/src/third_party/crashpad/util/linux/socket.cc b/src/third_party/crashpad/util/linux/socket.cc
index f56eacf..a0fcbf0 100644
--- a/src/third_party/crashpad/util/linux/socket.cc
+++ b/src/third_party/crashpad/util/linux/socket.cc
@@ -48,7 +48,7 @@
   return true;
 }
 
-constexpr size_t UnixCredentialSocket::kMaxSendRecvMsgFDs = 4;
+const size_t UnixCredentialSocket::kMaxSendRecvMsgFDs = 4;
 
 // static
 int UnixCredentialSocket::SendMsg(int fd,
diff --git a/src/third_party/crashpad/util/stdlib/aligned_allocator.cc b/src/third_party/crashpad/util/stdlib/aligned_allocator.cc
index 797a3ac..ae14a3a 100644
--- a/src/third_party/crashpad/util/stdlib/aligned_allocator.cc
+++ b/src/third_party/crashpad/util/stdlib/aligned_allocator.cc
@@ -25,6 +25,10 @@
 #include <xutility>
 #endif  // OS_POSIX
 
+#if defined(STARBOARD)
+#include "starboard/memory.h"
+#endif
+
 namespace {
 
 // Throws std::bad_alloc() by calling an internal function provided by the C++
@@ -66,7 +70,9 @@
 }
 
 void AlignedFree(void* pointer) {
-#if defined(OS_POSIX)
+#if defined(STARBOARD)
+  SbMemoryDeallocate(pointer);
+#elif defined(OS_POSIX)
   free(pointer);
 #elif defined(OS_WIN)
   _aligned_free(pointer);
diff --git a/src/third_party/crashpad/util/stdlib/map_insert.h b/src/third_party/crashpad/util/stdlib/map_insert.h
index d2c3b84..ea8fc03 100644
--- a/src/third_party/crashpad/util/stdlib/map_insert.h
+++ b/src/third_party/crashpad/util/stdlib/map_insert.h
@@ -51,6 +51,18 @@
   return result.second;
 }
 
+template <typename T>
+bool MapInsertOrReplace(T* map,
+                        const typename T::key_type& key,
+                        const typename T::mapped_type& value,
+                        std::nullptr_t) {
+  const auto result = map->insert(std::make_pair(key, value));
+  if (!result.second) {
+    result.first->second = value;
+  }
+  return result.second;
+}
+
 }  // namespace crashpad
 
 #endif  // CRASHPAD_UTIL_STDLIB_MAP_INSERT_H_
diff --git a/src/third_party/crashpad/util/util.gyp b/src/third_party/crashpad/util/util.gyp
index 8a33e83..2440d7b 100644
--- a/src/third_party/crashpad/util/util.gyp
+++ b/src/third_party/crashpad/util/util.gyp
@@ -23,10 +23,12 @@
       'dependencies': [
         '../compat/compat.gyp:crashpad_compat',
         '../third_party/mini_chromium/mini_chromium.gyp:base',
-        '../third_party/zlib/zlib.gyp:zlib',
         '../third_party/lss/lss.gyp:lss',
       ],
-      'defines': [ 'ZLIB_CONST' ],
+      'defines': [
+        'ZLIB_CONST',
+        'CRASHPAD_USE_BORINGSSL',
+      ],
       'include_dirs': [
         '..',
         '<(INTERMEDIATE_DIR)',
@@ -416,7 +418,6 @@
         }],
         ['target_os=="linux" or target_os=="android"', {
           'sources': [
-            'net/http_transport_socket.cc',
             'process/process_memory_sanitized.cc',
             'process/process_memory_sanitized.h',
           ],