Import Cobalt 8.21796
diff --git a/src/base/bind_helpers.h b/src/base/bind_helpers.h
index b60a56e..17b2f55 100644
--- a/src/base/bind_helpers.h
+++ b/src/base/bind_helpers.h
@@ -255,7 +255,7 @@
   static Yes& Check(...);
 
  public:
-  static const bool value = sizeof(Check<Base>(0)) == sizeof(Yes);
+  enum { value = sizeof(Check<Base>(0)) == sizeof(Yes) };
 };
 
 // Helpers to assert that arguments of a recounted type are bound with a
diff --git a/src/base/file_util_unittest.cc b/src/base/file_util_unittest.cc
index 81fdcc7..66595fc 100644
--- a/src/base/file_util_unittest.cc
+++ b/src/base/file_util_unittest.cc
@@ -33,6 +33,9 @@
 #if defined(OS_WIN)
 #include "base/win/scoped_handle.h"
 #endif
+#if defined(OS_STARBOARD)
+#include "starboard/string.h"
+#endif
 
 // This macro helps avoid wrapped lines in the test structs.
 #define FPL(x) FILE_PATH_LITERAL(x)
diff --git a/src/base/string_number_conversions.cc b/src/base/string_number_conversions.cc
index 1aafc4e..18afd5e 100644
--- a/src/base/string_number_conversions.cc
+++ b/src/base/string_number_conversions.cc
@@ -300,6 +300,13 @@
 typedef BaseHexIteratorRangeToIntTraits<StringPiece::const_iterator>
     HexIteratorRangeToIntTraits;
 
+template <typename ITERATOR>
+class BaseHexIteratorRangeToUIntTraits
+    : public BaseIteratorRangeToNumberTraits<ITERATOR, unsigned int, 16> {};
+
+typedef BaseHexIteratorRangeToUIntTraits<StringPiece::const_iterator>
+    HexIteratorRangeToUIntTraits;
+
 template<typename STR>
 bool HexStringToBytesT(const STR& input, std::vector<uint8>* output) {
   DCHECK_EQ(output->size(), 0u);
@@ -419,6 +426,11 @@
     input.begin(), input.end(), output);
 }
 
+bool HexStringToUInt(const StringPiece& input, unsigned int* output) {
+  return IteratorRangeToNumber<HexIteratorRangeToUIntTraits>::Invoke(
+      input.begin(), input.end(), output);
+}
+
 bool HexStringToBytes(const std::string& input, std::vector<uint8>* output) {
   return HexStringToBytesT(input, output);
 }
diff --git a/src/base/string_number_conversions.h b/src/base/string_number_conversions.h
index 7b7cfe1..e1cd1e6 100644
--- a/src/base/string_number_conversions.h
+++ b/src/base/string_number_conversions.h
@@ -96,6 +96,8 @@
 
 // Best effort conversion, see StringToInt above for restrictions.
 BASE_EXPORT bool HexStringToInt(const StringPiece& input, int* output);
+BASE_EXPORT bool HexStringToUInt(const StringPiece& input,
+                                 unsigned int* output);
 
 // Similar to the previous functions, except that output is a vector of bytes.
 // |*output| will contain as many bytes as were successfully parsed prior to the
diff --git a/src/base/string_util.cc b/src/base/string_util.cc
index 1acf1be..0f7f151 100644
--- a/src/base/string_util.cc
+++ b/src/base/string_util.cc
@@ -754,13 +754,26 @@
   if (parts.empty())
     return STR();
 
-  STR result(parts[0]);
+  std::size_t expected_result_length = 0;
+
   typename std::vector<STR>::const_iterator iter = parts.begin();
-  ++iter;
 
   for (; iter != parts.end(); ++iter) {
+    expected_result_length += iter->size();
+  }
+  expected_result_length += sep.size() * (parts.size() - 1);
+
+  STR result;
+  result.reserve(expected_result_length);
+
+  iter = parts.begin();
+  result += *iter;
+  ++iter;
+
+  while(iter != parts.end()) {
     result += sep;
     result += *iter;
+    ++iter;
   }
 
   return result;
diff --git a/src/base/time.h b/src/base/time.h
index 9fde0e4..5640a78 100644
--- a/src/base/time.h
+++ b/src/base/time.h
@@ -25,6 +25,8 @@
 
 #include <time.h>
 
+#include <ostream>
+
 #include "base/atomicops.h"
 #include "base/base_export.h"
 #include "base/basictypes.h"
@@ -98,6 +100,18 @@
     return delta_;
   }
 
+  // Returns the magnitude (absolute value) of this TimeDelta.
+  TimeDelta magnitude() const {
+    // Some toolchains provide an incomplete C++11 implementation and lack an
+    // int64_t overload for std::abs().  The following is a simple branchless
+    // implementation:
+    const int64_t mask = delta_ >> (sizeof(delta_) * 8 - 1);
+    return TimeDelta((delta_ + mask) ^ mask);
+  }
+
+  // Returns true if the time delta is zero.
+  bool is_zero() const { return delta_ == 0; }
+
   // Returns true if the time delta is the maximum time delta.
   bool is_max() const {
     return delta_ == std::numeric_limits<int64>::max();
@@ -214,6 +228,10 @@
   return TimeDelta(a * td.delta_);
 }
 
+inline std::ostream& operator<<(std::ostream& os, TimeDelta time_delta) {
+  return os << time_delta.InSecondsF() << " s";
+}
+
 // Time -----------------------------------------------------------------------
 
 // Represents a wall clock time.
diff --git a/src/cobalt/accessibility/accessibility.gyp b/src/cobalt/accessibility/accessibility.gyp
index c73c56e..149d26f 100644
--- a/src/cobalt/accessibility/accessibility.gyp
+++ b/src/cobalt/accessibility/accessibility.gyp
@@ -17,6 +17,8 @@
       'target_name': 'accessibility',
       'type': 'static_library',
       'sources': [
+        'internal/live_region.cc',
+        'internal/live_region.h',
         'internal/text_alternative_helper.cc',
         'internal/text_alternative_helper.h',
         'focus_observer.cc',
diff --git a/src/cobalt/accessibility/accessibility_test.gyp b/src/cobalt/accessibility/accessibility_test.gyp
index 2c4c7b8..e28a8f1 100644
--- a/src/cobalt/accessibility/accessibility_test.gyp
+++ b/src/cobalt/accessibility/accessibility_test.gyp
@@ -18,6 +18,7 @@
       'type': '<(gtest_target_type)',
       'sources': [
         'text_alternative_tests.cc',
+        'internal/live_region_test.cc',
         'internal/text_alternative_helper_test.cc',
       ],
       'dependencies': [
diff --git a/src/cobalt/accessibility/internal/live_region.cc b/src/cobalt/accessibility/internal/live_region.cc
new file mode 100644
index 0000000..fb9cb68
--- /dev/null
+++ b/src/cobalt/accessibility/internal/live_region.cc
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2017 Google Inc. 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/accessibility/internal/live_region.h"
+
+#include <bitset>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/string_split.h"
+#include "cobalt/base/tokens.h"
+
+namespace cobalt {
+namespace accessibility {
+namespace internal {
+namespace {
+typedef std::bitset<8> RelevantMutationsBitset;
+
+// Default value for the aria-relevant property per the spec.
+// https://www.w3.org/TR/wai-aria/states_and_properties#aria-relevant
+const char kDefaultAriaRelevantValue[] = "additions text";
+
+bool HasValidLiveRegionProperty(const scoped_refptr<dom::Element>& element) {
+  std::string aria_live_attribute =
+      element->GetAttribute(base::Tokens::aria_live().c_str()).value_or("");
+
+  return aria_live_attribute == base::Tokens::assertive() ||
+         aria_live_attribute == base::Tokens::polite();
+}
+
+RelevantMutationsBitset GetRelevantMutations(
+    const scoped_refptr<dom::Element>& element) {
+  RelevantMutationsBitset bitset;
+
+  std::string aria_relevant_attribute =
+      element->GetAttribute(base::Tokens::aria_relevant().c_str())
+          .value_or(kDefaultAriaRelevantValue);
+  std::vector<std::string> tokens;
+  base::SplitStringAlongWhitespace(aria_relevant_attribute, &tokens);
+  for (size_t i = 0; i < tokens.size(); ++i) {
+    if (tokens[i] == base::Tokens::additions()) {
+      bitset.set(LiveRegion::kMutationTypeAddition);
+    } else if (tokens[i] == base::Tokens::removals()) {
+      bitset.set(LiveRegion::kMutationTypeRemoval);
+    } else if (tokens[i] == base::Tokens::text()) {
+      bitset.set(LiveRegion::kMutationTypeText);
+    } else if (tokens[i] == base::Tokens::all()) {
+      bitset.set();
+    } else {
+      DLOG(WARNING) << "Unexpected value for aria-relevant attribute: "
+                    << tokens[i];
+    }
+  }
+  return bitset;
+}
+}  // namespace
+
+// static
+scoped_ptr<LiveRegion> LiveRegion::GetLiveRegionForElement(
+    const scoped_refptr<dom::Element>& element) {
+  if (!element) {
+    return make_scoped_ptr<LiveRegion>(NULL);
+  }
+  if (HasValidLiveRegionProperty(element)) {
+    return make_scoped_ptr(new LiveRegion(element));
+  }
+  return GetLiveRegionForElement(element->parent_element());
+}
+
+bool LiveRegion::IsAssertive() const {
+  base::optional<std::string> aria_live_attribute =
+      root_->GetAttribute(base::Tokens::aria_live().c_str());
+  if (!aria_live_attribute) {
+    NOTREACHED();
+    return false;
+  }
+  return *aria_live_attribute == base::Tokens::assertive();
+}
+
+bool LiveRegion::IsMutationRelevant(MutationType mutation_type) const {
+  RelevantMutationsBitset bitset = GetRelevantMutations(root_);
+  return bitset.test(mutation_type);
+}
+
+bool LiveRegion::IsAtomic(const scoped_refptr<dom::Element>& element) const {
+  // Stop searching if we go past the live region's root node. The default is
+  // non-atomic.
+  if (!element || element == root_->parent_element()) {
+    return false;
+  }
+
+  // Search ancestors of the changed element to determine if this change is
+  // atomic or not, per the algorithm described in the spec.
+  // https://www.w3.org/TR/wai-aria/states_and_properties#aria-atomic
+  base::optional<std::string> aria_atomic_attribute =
+      element->GetAttribute(base::Tokens::aria_atomic().c_str());
+  if (aria_atomic_attribute) {
+    if (*aria_atomic_attribute == base::Tokens::true_token()) {
+      return true;
+    } else if (*aria_atomic_attribute == base::Tokens::false_token()) {
+      return false;
+    } else {
+      DLOG(WARNING) << "Unexpected token for aria-atomic: "
+                    << *aria_atomic_attribute;
+    }
+  }
+  return IsAtomic(element->parent_element());
+}
+
+}  // namespace internal
+}  // namespace accessibility
+}  // namespace cobalt
diff --git a/src/cobalt/accessibility/internal/live_region.h b/src/cobalt/accessibility/internal/live_region.h
new file mode 100644
index 0000000..d39ca2b
--- /dev/null
+++ b/src/cobalt/accessibility/internal/live_region.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2017 Google Inc. 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_ACCESSIBILITY_INTERNAL_LIVE_REGION_H_
+#define COBALT_ACCESSIBILITY_INTERNAL_LIVE_REGION_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/dom/element.h"
+
+namespace cobalt {
+namespace accessibility {
+namespace internal {
+// Collection of functions to support the implementation of wai-aria Live
+// Regions:
+// https://www.w3.org/TR/2010/WD-wai-aria-20100916/terms#def_liveregion
+class LiveRegion {
+ public:
+  // Types of mutation to the DOM subtree that might cause a live region to be
+  // updated, corresponding to the possible values of aria-relevant as
+  // described in:
+  // https://www.w3.org/TR/2011/CR-wai-aria-20110118/states_and_properties#aria-relevant
+  enum MutationType {
+    kMutationTypeAddition = 1,  // corresponds to "additions"
+    kMutationTypeRemoval,   // corresponds to "removals"
+    kMutationTypeText,      // corresponds to "text"
+  };
+
+  // Returns a LiveRegion instance describing the live region that this element
+  // is a member of, or NULL if this element is not part of a live region.
+  //
+  // Searches the element's ancestors looking for the first element with a
+  // valid aria-live attribute.
+  static scoped_ptr<LiveRegion> GetLiveRegionForElement(
+      const scoped_refptr<dom::Element>& element);
+
+  // Returns true if the value of aria-live is "assertive".
+  // https://www.w3.org/TR/2011/CR-wai-aria-20110118/states_and_properties#aria-live
+  bool IsAssertive() const;
+
+  // Returns true if this live region should be updated in response to the
+  // specified type of change, based on the value of this element's
+  // aria-relevant property.
+  // https://www.w3.org/TR/2011/CR-wai-aria-20110118/states_and_properties#aria-relevant
+  bool IsMutationRelevant(MutationType mutation_type) const;
+
+  // Returns true if changes to this element should result in an atomic update
+  // to its live region, according to the value of the aria-atomic property set
+  // on this element or its ancestors, up to and including the live region root.
+  //
+  // https://www.w3.org/TR/2011/CR-wai-aria-20110118/states_and_properties#aria-atomic
+  bool IsAtomic(const scoped_refptr<dom::Element>& element) const;
+
+  // Returns the root of the live region (i.e.) the element that has the
+  // aria-live property set on it.
+  const scoped_refptr<dom::Element>& root() { return root_; }
+
+ private:
+  explicit LiveRegion(const scoped_refptr<dom::Element>& root) : root_(root) {}
+
+  // The root element of the live region. That is, the element with a valid
+  // aria-live attribute.
+  scoped_refptr<dom::Element> root_;
+};
+
+}  // namespace internal
+}  // namespace accessibility
+}  // namespace cobalt
+
+#endif  // COBALT_ACCESSIBILITY_INTERNAL_LIVE_REGION_H_
diff --git a/src/cobalt/accessibility/internal/live_region_test.cc b/src/cobalt/accessibility/internal/live_region_test.cc
new file mode 100644
index 0000000..cfb9230
--- /dev/null
+++ b/src/cobalt/accessibility/internal/live_region_test.cc
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2017 Google Inc. 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 "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/accessibility/internal/live_region.h"
+#include "cobalt/test/empty_document.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace accessibility {
+namespace internal {
+class LiveRegionTest : public ::testing::Test {
+ protected:
+  dom::Document* document() { return empty_document_.document(); }
+
+  scoped_refptr<dom::Element> CreateLiveRegion(const char* type) {
+    scoped_refptr<dom::Element> live_region = document()->CreateElement("div");
+    live_region->SetAttribute(base::Tokens::aria_live().c_str(), type);
+    return live_region;
+  }
+
+ private:
+  test::EmptyDocument empty_document_;
+};
+
+TEST_F(LiveRegionTest, GetLiveRegionRoot) {
+  scoped_ptr<LiveRegion> live_region;
+
+  scoped_refptr<dom::Element> live_region_element =
+      CreateLiveRegion(base::Tokens::polite().c_str());
+  ASSERT_TRUE(live_region_element);
+
+  // GetLiveRegionForElement with the live region root.
+  live_region = LiveRegion::GetLiveRegionForElement(live_region_element);
+  ASSERT_TRUE(live_region);
+  EXPECT_EQ(live_region->root(), live_region_element);
+
+  // GetLiveRegionForElement with a descendent node of a live region.
+  scoped_refptr<dom::Element> child = document()->CreateElement("div");
+  live_region_element->AppendChild(child);
+  live_region = LiveRegion::GetLiveRegionForElement(child);
+  ASSERT_TRUE(live_region);
+  EXPECT_EQ(live_region->root(), live_region_element);
+
+  // Element that is not in a live region.
+  scoped_refptr<dom::Element> not_live = document()->CreateElement("div");
+  live_region = LiveRegion::GetLiveRegionForElement(not_live);
+  EXPECT_FALSE(live_region);
+}
+
+TEST_F(LiveRegionTest, NestedLiveRegion) {
+  scoped_refptr<dom::Element> live_region_element =
+      CreateLiveRegion(base::Tokens::polite().c_str());
+  ASSERT_TRUE(live_region_element);
+
+  scoped_refptr<dom::Element> nested_region_element =
+      CreateLiveRegion(base::Tokens::polite().c_str());
+  ASSERT_TRUE(nested_region_element);
+  live_region_element->AppendChild(nested_region_element);
+
+  // The closest ancestor live region is this element's live region.
+  scoped_refptr<dom::Element> child = document()->CreateElement("div");
+  nested_region_element->AppendChild(child);
+  scoped_ptr<LiveRegion> live_region =
+      LiveRegion::GetLiveRegionForElement(child);
+  ASSERT_TRUE(live_region);
+  EXPECT_EQ(live_region->root(), nested_region_element);
+
+  // Ignore live regions that are not active.
+  nested_region_element->SetAttribute(base::Tokens::aria_live().c_str(),
+                                      base::Tokens::off().c_str());
+  live_region = LiveRegion::GetLiveRegionForElement(child);
+  ASSERT_TRUE(live_region);
+  EXPECT_EQ(live_region->root(), live_region_element);
+}
+
+TEST_F(LiveRegionTest, LiveRegionType) {
+  scoped_ptr<LiveRegion> live_region;
+
+  // aria-live="polite"
+  scoped_refptr<dom::Element> polite_element =
+      CreateLiveRegion(base::Tokens::polite().c_str());
+  ASSERT_TRUE(polite_element);
+  live_region = LiveRegion::GetLiveRegionForElement(polite_element);
+  ASSERT_TRUE(live_region);
+  EXPECT_FALSE(live_region->IsAssertive());
+
+  // aria-live="assertive"
+  scoped_refptr<dom::Element> assertive_element =
+      CreateLiveRegion(base::Tokens::assertive().c_str());
+  ASSERT_TRUE(assertive_element);
+  live_region = LiveRegion::GetLiveRegionForElement(assertive_element);
+  ASSERT_TRUE(live_region);
+  EXPECT_TRUE(live_region->IsAssertive());
+
+  // aria-live="off"
+  scoped_refptr<dom::Element> off_element =
+      CreateLiveRegion(base::Tokens::off().c_str());
+  ASSERT_TRUE(off_element);
+  live_region = LiveRegion::GetLiveRegionForElement(off_element);
+  EXPECT_FALSE(live_region);
+
+  // aria-live=<invalid token>
+  scoped_refptr<dom::Element> invalid_element = CreateLiveRegion("banana");
+  ASSERT_TRUE(invalid_element);
+  live_region = LiveRegion::GetLiveRegionForElement(invalid_element);
+  EXPECT_FALSE(live_region);
+}
+
+TEST_F(LiveRegionTest, IsMutationRelevant) {
+  scoped_refptr<dom::Element> live_region_element =
+      CreateLiveRegion(base::Tokens::polite().c_str());
+  ASSERT_TRUE(live_region_element);
+
+  // GetLiveRegionForElement with the live region root.
+  scoped_ptr<LiveRegion> live_region =
+      LiveRegion::GetLiveRegionForElement(live_region_element);
+  ASSERT_TRUE(live_region);
+  // Default is that additions and text are relevant.
+  EXPECT_TRUE(
+      live_region->IsMutationRelevant(LiveRegion::kMutationTypeAddition));
+  EXPECT_TRUE(live_region->IsMutationRelevant(LiveRegion::kMutationTypeText));
+  EXPECT_FALSE(
+      live_region->IsMutationRelevant(LiveRegion::kMutationTypeRemoval));
+
+  // Only removals are relevant.
+  live_region_element->SetAttribute(base::Tokens::aria_relevant().c_str(),
+                                    "removals");
+  live_region = LiveRegion::GetLiveRegionForElement(live_region_element);
+  ASSERT_TRUE(live_region);
+  EXPECT_FALSE(
+      live_region->IsMutationRelevant(LiveRegion::kMutationTypeAddition));
+  EXPECT_FALSE(live_region->IsMutationRelevant(LiveRegion::kMutationTypeText));
+  EXPECT_TRUE(
+      live_region->IsMutationRelevant(LiveRegion::kMutationTypeRemoval));
+
+  // Removals and additions are relevant.
+  live_region_element->SetAttribute(base::Tokens::aria_relevant().c_str(),
+                                    "removals additions");
+  live_region = LiveRegion::GetLiveRegionForElement(live_region_element);
+  ASSERT_TRUE(live_region);
+  EXPECT_TRUE(
+      live_region->IsMutationRelevant(LiveRegion::kMutationTypeAddition));
+  EXPECT_FALSE(live_region->IsMutationRelevant(LiveRegion::kMutationTypeText));
+  EXPECT_TRUE(
+      live_region->IsMutationRelevant(LiveRegion::kMutationTypeRemoval));
+
+  // An invalid token.
+  live_region_element->SetAttribute(base::Tokens::aria_relevant().c_str(),
+                                    "text dog additions");
+  live_region = LiveRegion::GetLiveRegionForElement(live_region_element);
+  ASSERT_TRUE(live_region);
+  EXPECT_TRUE(
+      live_region->IsMutationRelevant(LiveRegion::kMutationTypeAddition));
+  EXPECT_TRUE(live_region->IsMutationRelevant(LiveRegion::kMutationTypeText));
+  EXPECT_FALSE(
+      live_region->IsMutationRelevant(LiveRegion::kMutationTypeRemoval));
+
+  // "all" token.
+  live_region_element->SetAttribute(base::Tokens::aria_relevant().c_str(),
+                                    "all");
+  live_region = LiveRegion::GetLiveRegionForElement(live_region_element);
+  ASSERT_TRUE(live_region);
+  EXPECT_TRUE(
+      live_region->IsMutationRelevant(LiveRegion::kMutationTypeAddition));
+  EXPECT_TRUE(live_region->IsMutationRelevant(LiveRegion::kMutationTypeText));
+  EXPECT_TRUE(
+      live_region->IsMutationRelevant(LiveRegion::kMutationTypeRemoval));
+}
+
+TEST_F(LiveRegionTest, IsAtomic) {
+  scoped_ptr<LiveRegion> live_region;
+
+  scoped_refptr<dom::Element> live_region_element =
+      CreateLiveRegion(base::Tokens::polite().c_str());
+  ASSERT_TRUE(live_region_element);
+
+  // Default is non-atomic.
+  live_region = LiveRegion::GetLiveRegionForElement(live_region_element);
+  ASSERT_TRUE(live_region);
+  EXPECT_FALSE(live_region->IsAtomic(live_region_element));
+
+  // aria-atomic=true
+  live_region_element->SetAttribute(base::Tokens::aria_atomic().c_str(),
+                                    "true");
+  live_region = LiveRegion::GetLiveRegionForElement(live_region_element);
+  ASSERT_TRUE(live_region);
+  EXPECT_TRUE(live_region->IsAtomic(live_region_element));
+
+  // aria-atomic=false
+  live_region_element->SetAttribute(base::Tokens::aria_atomic().c_str(),
+                                    "false");
+  live_region = LiveRegion::GetLiveRegionForElement(live_region_element);
+  ASSERT_TRUE(live_region);
+  EXPECT_FALSE(live_region->IsAtomic(live_region_element));
+
+  // Get the value of aria-atomic from ancestor elements.
+  live_region_element->SetAttribute(base::Tokens::aria_atomic().c_str(),
+                                    "true");
+  scoped_refptr<dom::Element> child = document()->CreateElement("div");
+  live_region_element->AppendChild(child);
+  live_region = LiveRegion::GetLiveRegionForElement(live_region_element);
+  ASSERT_TRUE(live_region);
+  EXPECT_TRUE(live_region->IsAtomic(child));
+  EXPECT_TRUE(live_region->IsAtomic(live_region_element));
+
+  // Stop checking ancestors on the first aria-atomic attribute.
+  live_region_element->SetAttribute(base::Tokens::aria_atomic().c_str(),
+                                    "true");
+  child->SetAttribute(base::Tokens::aria_atomic().c_str(), "false");
+  live_region = LiveRegion::GetLiveRegionForElement(live_region_element);
+  ASSERT_TRUE(live_region);
+  EXPECT_FALSE(live_region->IsAtomic(child));
+  EXPECT_TRUE(live_region->IsAtomic(live_region_element));
+}
+}  // namespace internal
+}  // namespace accessibility
+}  // namespace cobalt
diff --git a/src/cobalt/accessibility/internal/text_alternative_helper_test.cc b/src/cobalt/accessibility/internal/text_alternative_helper_test.cc
index 528efa5..1686df8 100644
--- a/src/cobalt/accessibility/internal/text_alternative_helper_test.cc
+++ b/src/cobalt/accessibility/internal/text_alternative_helper_test.cc
@@ -17,11 +17,9 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
 #include "cobalt/accessibility/internal/text_alternative_helper.h"
-#include "cobalt/css_parser/parser.h"
-#include "cobalt/dom/document.h"
-#include "cobalt/dom/dom_stat_tracker.h"
 #include "cobalt/dom/html_image_element.h"
 #include "cobalt/dom/text.h"
+#include "cobalt/test/empty_document.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -30,20 +28,10 @@
 namespace internal {
 class TextAlternativeHelperTest : public ::testing::Test {
  protected:
-  TextAlternativeHelperTest()
-      : css_parser_(css_parser::Parser::Create()),
-        dom_stat_tracker_(new dom::DomStatTracker("TextAlternativeHelperTest")),
-        html_element_context_(NULL, css_parser_.get(), NULL, NULL, NULL, NULL,
-                              NULL, NULL, NULL, NULL, NULL,
-                              dom_stat_tracker_.get(), ""),
-        document_(new dom::Document(&html_element_context_)) {}
+  dom::Document* document() { return empty_document_.document(); }
 
-  scoped_ptr<css_parser::Parser> css_parser_;
-  scoped_ptr<dom::DomStatTracker> dom_stat_tracker_;
-  dom::HTMLElementContext html_element_context_;
-  scoped_refptr<dom::Document> document_;
-  scoped_refptr<dom::Element> element_;
   TextAlternativeHelper text_alternative_helper_;
+  test::EmptyDocument empty_document_;
 };
 
 TEST_F(TextAlternativeHelperTest, ConcatenatesAlternatives) {
@@ -70,7 +58,7 @@
 }
 
 TEST_F(TextAlternativeHelperTest, HiddenOnlyIfAriaHiddenSet) {
-  scoped_refptr<dom::Element> element = document_->CreateElement("div");
+  scoped_refptr<dom::Element> element = document()->CreateElement("div");
   // No aria-hidden attribute set.
   EXPECT_FALSE(TextAlternativeHelper::IsAriaHidden(element));
 
@@ -90,8 +78,8 @@
 }
 
 TEST_F(TextAlternativeHelperTest, GetTextAlternativeFromTextNode) {
-  scoped_refptr<dom::Element> element = document_->CreateElement("div");
-  element->AppendChild(document_->CreateTextNode("text node contents"));
+  scoped_refptr<dom::Element> element = document()->CreateElement("div");
+  element->AppendChild(document()->CreateTextNode("text node contents"));
   text_alternative_helper_.AppendTextAlternative(element);
   EXPECT_STREQ("text node contents",
                text_alternative_helper_.GetTextAlternative().c_str());
@@ -99,7 +87,7 @@
 
 TEST_F(TextAlternativeHelperTest, GetAltAttributeFromHTMLImage) {
   scoped_refptr<dom::Element> element =
-      document_->CreateElement(dom::HTMLImageElement::kTagName);
+      document()->CreateElement(dom::HTMLImageElement::kTagName);
   element->SetAttribute(base::Tokens::alt().c_str(), "image alt text");
   EXPECT_TRUE(text_alternative_helper_.TryAppendFromAltProperty(element));
   EXPECT_STREQ("image alt text",
@@ -108,7 +96,7 @@
 
 TEST_F(TextAlternativeHelperTest, EmptyAltAttribute) {
   scoped_refptr<dom::Element> element =
-      document_->CreateElement(dom::HTMLImageElement::kTagName);
+      document()->CreateElement(dom::HTMLImageElement::kTagName);
   // No alt attribute.
   EXPECT_FALSE(text_alternative_helper_.TryAppendFromAltProperty(element));
   EXPECT_TRUE(text_alternative_helper_.GetTextAlternative().empty());
@@ -120,14 +108,14 @@
 }
 
 TEST_F(TextAlternativeHelperTest, AltAttributeOnArbitraryElement) {
-  scoped_refptr<dom::Element> element = document_->CreateElement("div");
+  scoped_refptr<dom::Element> element = document()->CreateElement("div");
   element->SetAttribute(base::Tokens::alt().c_str(), "alt text");
   EXPECT_FALSE(text_alternative_helper_.TryAppendFromAltProperty(element));
   EXPECT_TRUE(text_alternative_helper_.GetTextAlternative().empty());
 }
 
 TEST_F(TextAlternativeHelperTest, GetTextFromAriaLabel) {
-  scoped_refptr<dom::Element> element = document_->CreateElement("div");
+  scoped_refptr<dom::Element> element = document()->CreateElement("div");
   element->SetAttribute(base::Tokens::aria_label().c_str(), "label text");
   EXPECT_TRUE(text_alternative_helper_.TryAppendFromLabel(element));
   EXPECT_STREQ("label text",
@@ -135,23 +123,23 @@
 }
 
 TEST_F(TextAlternativeHelperTest, IgnoreEmptyAriaLabel) {
-  scoped_refptr<dom::Element> element = document_->CreateElement("test");
+  scoped_refptr<dom::Element> element = document()->CreateElement("test");
   element->SetAttribute(base::Tokens::aria_label().c_str(), "   ");
   EXPECT_FALSE(text_alternative_helper_.TryAppendFromLabel(element));
   EXPECT_TRUE(text_alternative_helper_.GetTextAlternative().empty());
 }
 
 TEST_F(TextAlternativeHelperTest, GetTextFromAriaLabelledBy) {
-  scoped_refptr<dom::Element> target_element = document_->CreateElement("div");
-  target_element->AppendChild(document_->CreateTextNode("labelledby target"));
+  scoped_refptr<dom::Element> target_element = document()->CreateElement("div");
+  target_element->AppendChild(document()->CreateTextNode("labelledby target"));
   target_element->set_id("target_element");
-  document_->AppendChild(target_element);
+  document()->AppendChild(target_element);
 
   scoped_refptr<dom::Element> labelledby_element =
-      document_->CreateElement("div");
+      document()->CreateElement("div");
   labelledby_element->SetAttribute(base::Tokens::aria_labelledby().c_str(),
                                    "target_element");
-  document_->AppendChild(labelledby_element);
+  document()->AppendChild(labelledby_element);
 
   EXPECT_TRUE(
       text_alternative_helper_.TryAppendFromLabelledBy(labelledby_element));
@@ -160,16 +148,16 @@
 }
 
 TEST_F(TextAlternativeHelperTest, InvalidLabelledByReference) {
-  scoped_refptr<dom::Element> target_element = document_->CreateElement("div");
-  target_element->AppendChild(document_->CreateTextNode("labelledby target"));
+  scoped_refptr<dom::Element> target_element = document()->CreateElement("div");
+  target_element->AppendChild(document()->CreateTextNode("labelledby target"));
   target_element->set_id("target_element");
-  document_->AppendChild(target_element);
+  document()->AppendChild(target_element);
 
   scoped_refptr<dom::Element> labelledby_element =
-      document_->CreateElement("div");
+      document()->CreateElement("div");
   labelledby_element->SetAttribute(base::Tokens::aria_labelledby().c_str(),
                                    "bad_reference");
-  document_->AppendChild(labelledby_element);
+  document()->AppendChild(labelledby_element);
 
   EXPECT_FALSE(
       text_alternative_helper_.TryAppendFromLabelledBy(labelledby_element));
@@ -177,25 +165,25 @@
 }
 
 TEST_F(TextAlternativeHelperTest, MultipleLabelledByReferences) {
-  scoped_refptr<dom::Element> target_element = document_->CreateElement("div");
-  target_element->AppendChild(document_->CreateTextNode("target1"));
+  scoped_refptr<dom::Element> target_element = document()->CreateElement("div");
+  target_element->AppendChild(document()->CreateTextNode("target1"));
   target_element->set_id("target_element1");
-  document_->AppendChild(target_element);
-  target_element = document_->CreateElement("div");
-  target_element->AppendChild(document_->CreateTextNode("target2"));
+  document()->AppendChild(target_element);
+  target_element = document()->CreateElement("div");
+  target_element->AppendChild(document()->CreateTextNode("target2"));
   target_element->set_id("target_element2");
-  document_->AppendChild(target_element);
-  target_element = document_->CreateElement("div");
-  target_element->AppendChild(document_->CreateTextNode("target3"));
+  document()->AppendChild(target_element);
+  target_element = document()->CreateElement("div");
+  target_element->AppendChild(document()->CreateTextNode("target3"));
   target_element->set_id("target_element3");
-  document_->AppendChild(target_element);
+  document()->AppendChild(target_element);
 
   scoped_refptr<dom::Element> labelledby_element =
-      document_->CreateElement("div");
+      document()->CreateElement("div");
   labelledby_element->SetAttribute(
       base::Tokens::aria_labelledby().c_str(),
       "target_element1 target_element2 bad_reference target_element3");
-  document_->AppendChild(labelledby_element);
+  document()->AppendChild(labelledby_element);
 
   EXPECT_TRUE(
       text_alternative_helper_.TryAppendFromLabelledBy(labelledby_element));
@@ -204,19 +192,19 @@
 }
 
 TEST_F(TextAlternativeHelperTest, LabelledByReferencesSelf) {
-  scoped_refptr<dom::Element> target_element = document_->CreateElement("div");
-  target_element->AppendChild(document_->CreateTextNode("other-text"));
+  scoped_refptr<dom::Element> target_element = document()->CreateElement("div");
+  target_element->AppendChild(document()->CreateTextNode("other-text"));
   target_element->set_id("other_id");
-  document_->AppendChild(target_element);
+  document()->AppendChild(target_element);
 
   scoped_refptr<dom::Element> labelledby_element =
-      document_->CreateElement("div");
+      document()->CreateElement("div");
   labelledby_element->set_id("self_id");
   labelledby_element->SetAttribute(base::Tokens::aria_label().c_str(),
                                    "self-label");
   labelledby_element->SetAttribute(base::Tokens::aria_labelledby().c_str(),
                                    "other_id self_id");
-  document_->AppendChild(labelledby_element);
+  document()->AppendChild(labelledby_element);
 
   EXPECT_TRUE(
       text_alternative_helper_.TryAppendFromLabelledBy(labelledby_element));
@@ -225,26 +213,26 @@
 }
 
 TEST_F(TextAlternativeHelperTest, NoRecursiveLabelledBy) {
-  scoped_refptr<dom::Element> element = document_->CreateElement("div");
+  scoped_refptr<dom::Element> element = document()->CreateElement("div");
   element->SetAttribute(base::Tokens::aria_labelledby().c_str(),
                         "first_labelled_by_id");
-  document_->AppendChild(element);
+  document()->AppendChild(element);
 
   scoped_refptr<dom::Element> first_labelledby_element =
-      document_->CreateElement("div");
+      document()->CreateElement("div");
   first_labelledby_element->AppendChild(
-      document_->CreateTextNode("first-labelledby-text-contents"));
+      document()->CreateTextNode("first-labelledby-text-contents"));
   first_labelledby_element->set_id("first_labelled_by_id");
   first_labelledby_element->SetAttribute(
       base::Tokens::aria_labelledby().c_str(), "second_labelled_by_id");
-  document_->AppendChild(first_labelledby_element);
+  document()->AppendChild(first_labelledby_element);
 
   scoped_refptr<dom::Element> second_labelledby_element =
-      document_->CreateElement("div");
+      document()->CreateElement("div");
   second_labelledby_element->AppendChild(
-      document_->CreateTextNode("second-labelledby-text-contents"));
+      document()->CreateTextNode("second-labelledby-text-contents"));
   second_labelledby_element->set_id("second_labelled_by_id");
-  document_->AppendChild(second_labelledby_element);
+  document()->AppendChild(second_labelledby_element);
 
   // In the non-recursive case, we should follow the labelledby attribute.
   text_alternative_helper_.AppendTextAlternative(first_labelledby_element);
@@ -260,22 +248,22 @@
 }
 
 TEST_F(TextAlternativeHelperTest, GetTextFromSubtree) {
-  scoped_refptr<dom::Element> element = document_->CreateElement("div");
-  document_->AppendChild(element);
+  scoped_refptr<dom::Element> element = document()->CreateElement("div");
+  document()->AppendChild(element);
 
-  scoped_refptr<dom::Element> child = document_->CreateElement("div");
-  child->AppendChild(document_->CreateTextNode("child1-text"));
+  scoped_refptr<dom::Element> child = document()->CreateElement("div");
+  child->AppendChild(document()->CreateTextNode("child1-text"));
   element->AppendChild(child);
 
-  scoped_refptr<dom::Element> child_element = document_->CreateElement("div");
+  scoped_refptr<dom::Element> child_element = document()->CreateElement("div");
   element->AppendChild(child_element);
 
-  child = document_->CreateElement("div");
-  child->AppendChild(document_->CreateTextNode("child2-text"));
+  child = document()->CreateElement("div");
+  child->AppendChild(document()->CreateTextNode("child2-text"));
   child_element->AppendChild(child);
 
-  child = document_->CreateElement("div");
-  child->AppendChild(document_->CreateTextNode("child3-text"));
+  child = document()->CreateElement("div");
+  child->AppendChild(document()->CreateTextNode("child3-text"));
   child_element->AppendChild(child);
 
   text_alternative_helper_.AppendTextAlternative(element);
@@ -284,19 +272,19 @@
 }
 
 TEST_F(TextAlternativeHelperTest, DontFollowReferenceLoops) {
-  scoped_refptr<dom::Element> element = document_->CreateElement("div");
+  scoped_refptr<dom::Element> element = document()->CreateElement("div");
   element->set_id("root_element_id");
-  document_->AppendChild(element);
+  document()->AppendChild(element);
 
-  scoped_refptr<dom::Element> child_element1 = document_->CreateElement("div");
+  scoped_refptr<dom::Element> child_element1 = document()->CreateElement("div");
   child_element1->SetAttribute(base::Tokens::aria_labelledby().c_str(),
                                "root_element_id");
   element->AppendChild(child_element1);
 
-  scoped_refptr<dom::Element> child_element2 = document_->CreateElement("div");
+  scoped_refptr<dom::Element> child_element2 = document()->CreateElement("div");
   child_element1->AppendChild(child_element2);
 
-  child_element2->AppendChild(document_->CreateTextNode("child2-text"));
+  child_element2->AppendChild(document()->CreateTextNode("child2-text"));
 
   text_alternative_helper_.AppendTextAlternative(element);
   EXPECT_STREQ("child2-text",
diff --git a/src/cobalt/base/c_val.h b/src/cobalt/base/c_val.h
index 4f7529c..e07b543 100644
--- a/src/cobalt/base/c_val.h
+++ b/src/cobalt/base/c_val.h
@@ -211,6 +211,11 @@
 }
 
 template <>
+inline std::string ValToString<bool>(const bool& value) {
+  return value ? "true" : "false";
+}
+
+template <>
 inline std::string ValToString<std::string>(const std::string& value) {
   return value;
 }
diff --git a/src/cobalt/base/c_val_collection_entry_stats.h b/src/cobalt/base/c_val_collection_entry_stats.h
index ad4831e..c1dd807 100644
--- a/src/cobalt/base/c_val_collection_entry_stats.h
+++ b/src/cobalt/base/c_val_collection_entry_stats.h
@@ -19,6 +19,7 @@
 #include <algorithm>
 #include <cmath>
 #include <numeric>
+#include <sstream>
 #include <string>
 #include <vector>
 
@@ -43,13 +44,17 @@
 //        entries is extremely large, |CValTimeIntervalEntryStats| is more
 //        appropriate, as it does its tracking without keeping entries in memory
 //        (at the cost of not being able to provide percentiles);
+// NOTE3: This class provides the ability to record all entries within a single
+//        string CVal when |enable_entry_list_c_val| is set to true in the
+//        constructor. By default, this CVal is not used.
 template <typename EntryType, typename Visibility = CValDebug>
 class CValCollectionEntryStatsImpl {
  public:
   static const size_t kNoMaxSize = 0;
 
   CValCollectionEntryStatsImpl(const std::string& name,
-                               size_t max_size = kNoMaxSize);
+                               size_t max_size = kNoMaxSize,
+                               bool enable_entry_list_c_val = false);
 
   // Add an entry to the collection. This may trigger a Flush() if adding the
   // entry causes the max size to be reached.
@@ -66,6 +71,11 @@
   static double CalculateStandardDeviation(const CollectionType& collection,
                                            double mean);
 
+  // Constructs a string representing the entries within the collection and
+  // populates |entry_list_| with it if the entry list CVal was enabled during
+  // construction.
+  void PopulateEntryList();
+
   // The maximum size of the collection before Flush() is automatically called.
   const size_t max_size_;
   // The current collection of entries. These will be used to generate the cval
@@ -82,12 +92,15 @@
   base::CVal<EntryType, Visibility> percentile_75th_;
   base::CVal<EntryType, Visibility> percentile_95th_;
   base::CVal<EntryType, Visibility> standard_deviation_;
+  // |entry_list_| is only non-NULL when it is enabled.
+  scoped_ptr<base::CVal<std::string, Visibility> > entry_list_;
 };
 
 template <typename EntryType, typename Visibility>
 CValCollectionEntryStatsImpl<EntryType, Visibility>::
     CValCollectionEntryStatsImpl(const std::string& name,
-                                 size_t max_size /*=kNoMaxSize*/)
+                                 size_t max_size /*=kNoMaxSize*/,
+                                 bool enable_entry_list_c_val /*=false*/)
     : max_size_(max_size),
       count_(StringPrintf("%s.Cnt", name.c_str()), 0, "Total entries."),
       average_(StringPrintf("%s.Avg", name.c_str()), EntryType(),
@@ -105,7 +118,13 @@
       percentile_95th_(StringPrintf("%s.Pct.95th", name.c_str()), EntryType(),
                        "95th percentile value."),
       standard_deviation_(StringPrintf("%s.Std", name.c_str()), EntryType(),
-                          "Standard deviation of values.") {}
+                          "Standard deviation of values.") {
+  if (enable_entry_list_c_val) {
+    entry_list_.reset(new base::CVal<std::string, Visibility>(
+        StringPrintf("%s.EntryList", name.c_str()), "[]",
+        "The list of entries in the collection."));
+  }
+}
 
 template <typename EntryType, typename Visibility>
 void CValCollectionEntryStatsImpl<EntryType, Visibility>::AddEntry(
@@ -122,6 +141,9 @@
     return;
   }
 
+  // Populate the entry list cval before the collection is sorted.
+  PopulateEntryList();
+
   // Sort the collection. This allows min, max, and percentiles to be easily
   // determined.
   std::sort(collection_.begin(), collection_.end());
@@ -204,6 +226,28 @@
   return std::sqrt(variance);
 }
 
+template <typename EntryType, typename Visibility>
+void CValCollectionEntryStatsImpl<EntryType, Visibility>::PopulateEntryList() {
+  // If the entry list was not enabled, then |entry_list_| will be NULL and
+  // there is nothing to do. Simply return.
+  if (!entry_list_) {
+    return;
+  }
+
+  // Construct a string containing a list representation of the collection
+  // entries and set the entry list CVal to it.
+  std::ostringstream oss;
+  oss << "[";
+  for (size_t i = 0; i < collection_.size(); ++i) {
+    if (i > 0) {
+      oss << ", ";
+    }
+    oss << CValDetail::ValToString<EntryType>(collection_[i]);
+  }
+  oss << "]";
+  *entry_list_ = oss.str();
+}
+
 #if !defined(ENABLE_DEBUG_C_VAL)
 // This is a stub class that disables CValCollectionEntryStats when
 // ENABLE_DEBUG_C_VAL is not defined.
@@ -213,9 +257,11 @@
   explicit CValCollectionEntryStatsStub(const std::string& name) {
     UNREFERENCED_PARAMETER(name);
   }
-  CValCollectionEntryStatsStub(const std::string& name, size_t max_size) {
+  CValCollectionEntryStatsStub(const std::string& name, size_t max_size,
+                               bool enable_entry_list_c_val) {
     UNREFERENCED_PARAMETER(name);
     UNREFERENCED_PARAMETER(max_size);
+    UNREFERENCED_PARAMETER(enable_entry_list_c_val);
   }
 
   void AddEntry(const EntryType& value) { UNREFERENCED_PARAMETER(value); }
@@ -248,8 +294,9 @@
  public:
   explicit CValCollectionEntryStats(const std::string& name)
       : CValParent(name) {}
-  CValCollectionEntryStats(const std::string& name, size_t max_size)
-      : CValParent(name, max_size) {}
+  CValCollectionEntryStats(const std::string& name, size_t max_size,
+                           bool enable_entry_list_c_val)
+      : CValParent(name, max_size, enable_entry_list_c_val) {}
 };
 
 // CVals with visibility set to CValPublic are always tracked though the CVal
@@ -263,8 +310,9 @@
  public:
   explicit CValCollectionEntryStats(const std::string& name)
       : CValParent(name) {}
-  CValCollectionEntryStats(const std::string& name, size_t max_size)
-      : CValParent(name, max_size) {}
+  CValCollectionEntryStats(const std::string& name, size_t max_size,
+                           bool enable_entry_list_c_val)
+      : CValParent(name, max_size, enable_entry_list_c_val) {}
 };
 
 }  // namespace base
diff --git a/src/cobalt/base/c_val_collection_entry_stats_test.cc b/src/cobalt/base/c_val_collection_entry_stats_test.cc
index 794e27c..3a17189 100644
--- a/src/cobalt/base/c_val_collection_entry_stats_test.cc
+++ b/src/cobalt/base/c_val_collection_entry_stats_test.cc
@@ -30,8 +30,10 @@
 TEST(CValCollectionEntryStatsTest, DefaultValues) {
   const std::string name = "CollectionEntry";
   const size_t max_size = 5;
+  bool enable_entry_list_c_val = false;
 
-  base::CValCollectionEntryStats<int> cval(name, max_size);
+  base::CValCollectionEntryStats<int> cval(name, max_size,
+                                           enable_entry_list_c_val);
 
   base::CValManager* cvm = base::CValManager::GetInstance();
   base::optional<std::string> count =
@@ -52,6 +54,8 @@
       cvm->GetValueAsPrettyString(StringPrintf("%s.Pct.95th", name.c_str()));
   base::optional<std::string> std =
       cvm->GetValueAsPrettyString(StringPrintf("%s.Std", name.c_str()));
+  base::optional<std::string> entry_list =
+      cvm->GetValueAsPrettyString(StringPrintf("%s.EntryList", name.c_str()));
 
   EXPECT_TRUE(count);
   EXPECT_EQ(*count, "0");
@@ -71,13 +75,16 @@
   EXPECT_EQ(*pct95, "0");
   EXPECT_TRUE(std);
   EXPECT_EQ(*std, "0");
+  EXPECT_FALSE(entry_list);
 }
 
 TEST(CValCollectionEntryStatsTest, NoFlush) {
   const std::string name = "CollectionEntryStats";
   const size_t max_size = 5;
+  bool enable_entry_list_c_val = true;
 
-  base::CValCollectionEntryStats<int> cval(name, max_size);
+  base::CValCollectionEntryStats<int> cval(name, max_size,
+                                           enable_entry_list_c_val);
   cval.AddEntry(3);
   cval.AddEntry(9);
   cval.AddEntry(1);
@@ -102,6 +109,8 @@
       cvm->GetValueAsPrettyString(StringPrintf("%s.Pct.95th", name.c_str()));
   base::optional<std::string> std =
       cvm->GetValueAsPrettyString(StringPrintf("%s.Std", name.c_str()));
+  base::optional<std::string> entry_list =
+      cvm->GetValueAsPrettyString(StringPrintf("%s.EntryList", name.c_str()));
 
   EXPECT_TRUE(count);
   EXPECT_EQ(*count, "0");
@@ -121,13 +130,17 @@
   EXPECT_EQ(*pct95, "0");
   EXPECT_TRUE(std);
   EXPECT_EQ(*std, "0");
+  EXPECT_TRUE(entry_list);
+  EXPECT_EQ(*entry_list, "[]");
 }
 
 TEST(CValCollectionEntryStatsTest, MaxSizeFlush) {
   const std::string name = "CollectionEntryStats";
   const size_t max_size = 5;
+  bool enable_entry_list_c_val = true;
 
-  base::CValCollectionEntryStats<int> cval(name, max_size);
+  base::CValCollectionEntryStats<int> cval(name, max_size,
+                                           enable_entry_list_c_val);
   cval.AddEntry(3);
   cval.AddEntry(9);
   cval.AddEntry(1);
@@ -153,6 +166,8 @@
       cvm->GetValueAsPrettyString(StringPrintf("%s.Pct.95th", name.c_str()));
   base::optional<std::string> std =
       cvm->GetValueAsPrettyString(StringPrintf("%s.Std", name.c_str()));
+  base::optional<std::string> entry_list =
+      cvm->GetValueAsPrettyString(StringPrintf("%s.EntryList", name.c_str()));
 
   EXPECT_TRUE(count);
   EXPECT_EQ(*count, "5");
@@ -172,13 +187,17 @@
   EXPECT_EQ(*pct95, "8");
   EXPECT_TRUE(std);
   EXPECT_EQ(*std, "3");
+  EXPECT_TRUE(entry_list);
+  EXPECT_EQ(*entry_list, "[3, 9, 1, 7, 5]");
 }
 
 TEST(CValCollectionEntryStatsTest, ManualFlush) {
   const std::string name = "CollectionEntryStats";
   const size_t max_size = 5;
+  bool enable_entry_list_c_val = true;
 
-  base::CValCollectionEntryStats<int, CValPublic> cval(name, max_size);
+  base::CValCollectionEntryStats<int> cval(name, max_size,
+                                           enable_entry_list_c_val);
   cval.AddEntry(3);
   cval.AddEntry(9);
   cval.AddEntry(1);
@@ -205,6 +224,8 @@
       cvm->GetValueAsPrettyString(StringPrintf("%s.Pct.95th", name.c_str()));
   base::optional<std::string> std =
       cvm->GetValueAsPrettyString(StringPrintf("%s.Std", name.c_str()));
+  base::optional<std::string> entry_list =
+      cvm->GetValueAsPrettyString(StringPrintf("%s.EntryList", name.c_str()));
 
   EXPECT_TRUE(count);
   EXPECT_EQ(*count, "4");
@@ -224,13 +245,17 @@
   EXPECT_EQ(*pct95, "8");
   EXPECT_TRUE(std);
   EXPECT_EQ(*std, "3");
+  EXPECT_TRUE(entry_list);
+  EXPECT_EQ(*entry_list, "[3, 9, 1, 7]");
 }
 
 TEST(CValCollectionEntryStatsTest, TwoManualFlushes) {
   const std::string name = "CollectionEntryStats";
   const size_t max_size = 5;
+  bool enable_entry_list_c_val = true;
 
-  base::CValCollectionEntryStats<int, CValDebug> cval(name, max_size);
+  base::CValCollectionEntryStats<int> cval(name, max_size,
+                                           enable_entry_list_c_val);
   cval.AddEntry(3);
   cval.AddEntry(9);
   cval.AddEntry(1);
@@ -258,6 +283,8 @@
       cvm->GetValueAsPrettyString(StringPrintf("%s.Pct.95th", name.c_str()));
   base::optional<std::string> std =
       cvm->GetValueAsPrettyString(StringPrintf("%s.Std", name.c_str()));
+  base::optional<std::string> entry_list =
+      cvm->GetValueAsPrettyString(StringPrintf("%s.EntryList", name.c_str()));
 
   EXPECT_TRUE(count);
   EXPECT_EQ(*count, "1");
@@ -277,6 +304,8 @@
   EXPECT_EQ(*pct95, "5");
   EXPECT_TRUE(std);
   EXPECT_EQ(*std, "0");
+  EXPECT_TRUE(entry_list);
+  EXPECT_EQ(*entry_list, "[5]");
 }
 
 }  // namespace base
diff --git a/src/cobalt/base/c_val_collection_timer_stats.h b/src/cobalt/base/c_val_collection_timer_stats.h
index 0454cd1..aff2c02 100644
--- a/src/cobalt/base/c_val_collection_timer_stats.h
+++ b/src/cobalt/base/c_val_collection_timer_stats.h
@@ -35,8 +35,9 @@
  public:
   explicit CValCollectionTimerStats(const std::string& name)
       : entry_stats_(name) {}
-  CValCollectionTimerStats(const std::string& name, size_t max_size)
-      : entry_stats_(name, max_size) {}
+  CValCollectionTimerStats(const std::string& name, size_t max_size,
+                           bool enable_entry_list_c_val)
+      : entry_stats_(name, max_size, enable_entry_list_c_val) {}
 
   // Start the timer. If the timer is currently running, it is stopped and
   // re-started. If no time is provided, then |base::TimeTicks::Now()| is used.
diff --git a/src/cobalt/base/c_val_collection_timer_stats_test.cc b/src/cobalt/base/c_val_collection_timer_stats_test.cc
index 596d9cc..8b0f6f3 100644
--- a/src/cobalt/base/c_val_collection_timer_stats_test.cc
+++ b/src/cobalt/base/c_val_collection_timer_stats_test.cc
@@ -31,8 +31,10 @@
 TEST(CValCollectionTimerStatsTest, DefaultValues) {
   const std::string name = "CollectionTimerStats";
   const size_t max_size = 5;
+  bool enable_entry_list_c_val = false;
 
-  base::CValCollectionTimerStats<> cval(name, max_size);
+  base::CValCollectionTimerStats<> cval(name, max_size,
+                                        enable_entry_list_c_val);
 
   base::CValManager* cvm = base::CValManager::GetInstance();
   base::optional<std::string> count =
@@ -53,6 +55,8 @@
       cvm->GetValueAsPrettyString(StringPrintf("%s.Pct.95th", name.c_str()));
   base::optional<std::string> std =
       cvm->GetValueAsPrettyString(StringPrintf("%s.Std", name.c_str()));
+  base::optional<std::string> entry_list =
+      cvm->GetValueAsPrettyString(StringPrintf("%s.EntryList", name.c_str()));
 
   EXPECT_TRUE(count);
   EXPECT_EQ(*count, "0");
@@ -72,13 +76,16 @@
   EXPECT_EQ(*pct95, "0us");
   EXPECT_TRUE(std);
   EXPECT_EQ(*std, "0us");
+  EXPECT_FALSE(entry_list);
 }
 
 TEST(CValCollectionTimerStatsTest, NoFlush) {
   const std::string name = "CollectionTimerStats";
   const size_t max_size = 5;
+  bool enable_entry_list_c_val = true;
 
-  base::CValCollectionTimerStats<> cval(name, max_size);
+  base::CValCollectionTimerStats<> cval(name, max_size,
+                                        enable_entry_list_c_val);
   cval.Start(base::TimeTicks::FromInternalValue(1000));
   cval.Stop(base::TimeTicks::FromInternalValue(4000));
   cval.Start(base::TimeTicks::FromInternalValue(4000));
@@ -107,6 +114,8 @@
       cvm->GetValueAsPrettyString(StringPrintf("%s.Pct.95th", name.c_str()));
   base::optional<std::string> std =
       cvm->GetValueAsPrettyString(StringPrintf("%s.Std", name.c_str()));
+  base::optional<std::string> entry_list =
+      cvm->GetValueAsPrettyString(StringPrintf("%s.EntryList", name.c_str()));
 
   EXPECT_TRUE(count);
   EXPECT_EQ(*count, "0");
@@ -126,13 +135,17 @@
   EXPECT_EQ(*pct95, "0us");
   EXPECT_TRUE(std);
   EXPECT_EQ(*std, "0us");
+  EXPECT_TRUE(entry_list);
+  EXPECT_EQ(*entry_list, "[]");
 }
 
 TEST(CValCollectionTimerStatsTest, MaxSizeFlush) {
   const std::string name = "CollectionTimerStats";
   const size_t max_size = 5;
+  bool enable_entry_list_c_val = true;
 
-  base::CValCollectionTimerStats<> cval(name, max_size);
+  base::CValCollectionTimerStats<> cval(name, max_size,
+                                        enable_entry_list_c_val);
   cval.Start(base::TimeTicks::FromInternalValue(1000));
   cval.Stop(base::TimeTicks::FromInternalValue(4000));
   cval.Start(base::TimeTicks::FromInternalValue(4000));
@@ -164,6 +177,8 @@
       cvm->GetValueAsPrettyString(StringPrintf("%s.Pct.95th", name.c_str()));
   base::optional<std::string> std =
       cvm->GetValueAsPrettyString(StringPrintf("%s.Std", name.c_str()));
+  base::optional<std::string> entry_list =
+      cvm->GetValueAsPrettyString(StringPrintf("%s.EntryList", name.c_str()));
 
   EXPECT_TRUE(count);
   EXPECT_EQ(*count, "5");
@@ -183,13 +198,17 @@
   EXPECT_EQ(*pct95, "8.6ms");
   EXPECT_TRUE(std);
   EXPECT_EQ(*std, "3.2ms");
+  EXPECT_TRUE(entry_list);
+  EXPECT_EQ(*entry_list, "[3000, 9000, 1000, 7000, 5000]");
 }
 
 TEST(CValCollectionTimerStatsTest, ManualFlush) {
   const std::string name = "CollectionTimerStats";
   const size_t max_size = 5;
+  bool enable_entry_list_c_val = true;
 
-  base::CValCollectionTimerStats<CValPublic> cval(name, max_size);
+  base::CValCollectionTimerStats<CValPublic> cval(name, max_size,
+                                                  enable_entry_list_c_val);
   cval.Start(base::TimeTicks::FromInternalValue(1000));
   cval.Stop(base::TimeTicks::FromInternalValue(4000));
   cval.Start(base::TimeTicks::FromInternalValue(4000));
@@ -221,6 +240,8 @@
       cvm->GetValueAsPrettyString(StringPrintf("%s.Pct.95th", name.c_str()));
   base::optional<std::string> std =
       cvm->GetValueAsPrettyString(StringPrintf("%s.Std", name.c_str()));
+  base::optional<std::string> entry_list =
+      cvm->GetValueAsPrettyString(StringPrintf("%s.EntryList", name.c_str()));
 
   EXPECT_TRUE(count);
   EXPECT_EQ(*count, "4");
@@ -240,13 +261,17 @@
   EXPECT_EQ(*pct95, "8.7ms");
   EXPECT_TRUE(std);
   EXPECT_EQ(*std, "3.7ms");
+  EXPECT_TRUE(entry_list);
+  EXPECT_EQ(*entry_list, "[3000, 9000, 1000, 7000]");
 }
 
 TEST(CValCollectionTimerStatsTest, TwoManualFlushes) {
   const std::string name = "CollectionTimer";
   const size_t max_size = 5;
+  bool enable_entry_list_c_val = true;
 
-  base::CValCollectionTimerStats<CValDebug> cval(name, max_size);
+  base::CValCollectionTimerStats<CValDebug> cval(name, max_size,
+                                                 enable_entry_list_c_val);
   cval.Start(base::TimeTicks::FromInternalValue(1000));
   cval.Stop(base::TimeTicks::FromInternalValue(4000));
   cval.Start(base::TimeTicks::FromInternalValue(4000));
@@ -279,6 +304,8 @@
       cvm->GetValueAsPrettyString(StringPrintf("%s.Pct.95th", name.c_str()));
   base::optional<std::string> std =
       cvm->GetValueAsPrettyString(StringPrintf("%s.Std", name.c_str()));
+  base::optional<std::string> entry_list =
+      cvm->GetValueAsPrettyString(StringPrintf("%s.EntryList", name.c_str()));
 
   EXPECT_TRUE(count);
   EXPECT_EQ(*count, "1");
@@ -298,6 +325,8 @@
   EXPECT_EQ(*pct95, "5.0ms");
   EXPECT_TRUE(std);
   EXPECT_EQ(*std, "0us");
+  EXPECT_TRUE(entry_list);
+  EXPECT_EQ(*entry_list, "[5000]");
 }
 
 }  // namespace base
diff --git a/src/cobalt/base/c_val_test.cc b/src/cobalt/base/c_val_test.cc
index ad60807..adfbb61 100644
--- a/src/cobalt/base/c_val_test.cc
+++ b/src/cobalt/base/c_val_test.cc
@@ -65,7 +65,7 @@
   base::CValManager* cvm = base::CValManager::GetInstance();
   base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
   EXPECT_TRUE(result);
-  EXPECT_EQ(*result, "1");
+  EXPECT_EQ(*result, "true");
 }
 
 TEST(CValTest, RegisterAndPrintBoolFalse) {
@@ -75,7 +75,7 @@
   base::CValManager* cvm = base::CValManager::GetInstance();
   base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
   EXPECT_TRUE(result);
-  EXPECT_EQ(*result, "0");
+  EXPECT_EQ(*result, "false");
 }
 
 TEST(CValTest, RegisterAndPrintU32) {
diff --git a/src/cobalt/base/tokens.h b/src/cobalt/base/tokens.h
index 6885dfc..5c42945 100644
--- a/src/cobalt/base/tokens.h
+++ b/src/cobalt/base/tokens.h
@@ -28,6 +28,7 @@
     MacroOpWithNameOnly(abort)                                \
     MacroOpWithNameOnly(additions)                            \
     MacroOpWithNameOnly(alt)                                  \
+    MacroOpWithNameOnly(all)                                  \
     MacroOpWithNameOnly(animationend)                         \
     MacroOpWithNameOnly(assertive)                            \
     MacroOpWithNameOnly(blur)                                 \
diff --git a/src/cobalt/bindings/testing/global_constructors_idls_idl_files_list.tmp b/src/cobalt/bindings/testing/global_constructors_idls_idl_files_list.tmp
new file mode 100644
index 0000000..bb7227e
--- /dev/null
+++ b/src/cobalt/bindings/testing/global_constructors_idls_idl_files_list.tmp
@@ -0,0 +1,46 @@
+AnonymousIndexedGetterInterface.idl
+AnonymousNamedGetterInterface.idl
+AnonymousNamedIndexedGetterInterface.idl
+ArbitraryInterface.idl
+BaseInterface.idl
+BooleanTypeTestInterface.idl
+CallbackFunctionInterface.idl
+CallbackInterfaceInterface.idl
+ConditionalInterface.idl
+ConstantsInterface.idl
+ConstructorInterface.idl
+ConstructorWithArgumentsInterface.idl
+DerivedGetterSetterInterface.idl
+DerivedInterface.idl
+DisabledInterface.idl
+DOMStringTestInterface.idl
+EnumerationInterface.idl
+ExceptionObjectInterface.idl
+ExceptionsInterface.idl
+ExtendedIDLAttributesInterface.idl
+GarbageCollectionTestInterface.idl
+GetOpaqueRootInterface.idl
+GlobalInterfaceParent.idl
+IndexedGetterInterface.idl
+InterfaceWithUnsupportedProperties.idl
+NamedConstructorInterface.idl
+NamedGetterInterface.idl
+NamedIndexedGetterInterface.idl
+NestedPutForwardsInterface.idl
+NoConstructorInterface.idl
+NoInterfaceObjectInterface.idl
+NullableTypesTestInterface.idl
+NumericTypesTestInterface.idl
+ObjectTypeBindingsInterface.idl
+OperationsTestInterface.idl
+PutForwardsInterface.idl
+SequenceUser.idl
+SingleOperationInterface.idl
+StaticPropertiesInterface.idl
+StringifierAnonymousOperationInterface.idl
+StringifierAttributeInterface.idl
+StringifierOperationInterface.idl
+TargetInterface.idl
+UnionTypesInterface.idl
+Window.idl
+UnsupportedInterface.idl
diff --git a/src/cobalt/bindings/testing/global_objects_idl_files_list.tmp b/src/cobalt/bindings/testing/global_objects_idl_files_list.tmp
new file mode 100644
index 0000000..3b0f3da
--- /dev/null
+++ b/src/cobalt/bindings/testing/global_objects_idl_files_list.tmp
@@ -0,0 +1,45 @@
+AnonymousIndexedGetterInterface.idl
+AnonymousNamedGetterInterface.idl
+AnonymousNamedIndexedGetterInterface.idl
+ArbitraryInterface.idl
+BaseInterface.idl
+BooleanTypeTestInterface.idl
+CallbackFunctionInterface.idl
+CallbackInterfaceInterface.idl
+ConditionalInterface.idl
+ConstantsInterface.idl
+ConstructorInterface.idl
+ConstructorWithArgumentsInterface.idl
+DerivedGetterSetterInterface.idl
+DerivedInterface.idl
+DisabledInterface.idl
+DOMStringTestInterface.idl
+EnumerationInterface.idl
+ExceptionObjectInterface.idl
+ExceptionsInterface.idl
+ExtendedIDLAttributesInterface.idl
+GarbageCollectionTestInterface.idl
+GetOpaqueRootInterface.idl
+GlobalInterfaceParent.idl
+IndexedGetterInterface.idl
+InterfaceWithUnsupportedProperties.idl
+NamedConstructorInterface.idl
+NamedGetterInterface.idl
+NamedIndexedGetterInterface.idl
+NestedPutForwardsInterface.idl
+NoConstructorInterface.idl
+NoInterfaceObjectInterface.idl
+NullableTypesTestInterface.idl
+NumericTypesTestInterface.idl
+ObjectTypeBindingsInterface.idl
+OperationsTestInterface.idl
+PutForwardsInterface.idl
+SequenceUser.idl
+SingleOperationInterface.idl
+StaticPropertiesInterface.idl
+StringifierAnonymousOperationInterface.idl
+StringifierAttributeInterface.idl
+StringifierOperationInterface.idl
+TargetInterface.idl
+UnionTypesInterface.idl
+Window.idl
diff --git a/src/cobalt/bindings/testing/interfaces_info_individual_static_idl_files_list.tmp b/src/cobalt/bindings/testing/interfaces_info_individual_static_idl_files_list.tmp
new file mode 100644
index 0000000..ea6cdb9
--- /dev/null
+++ b/src/cobalt/bindings/testing/interfaces_info_individual_static_idl_files_list.tmp
@@ -0,0 +1,49 @@
+AnonymousIndexedGetterInterface.idl
+AnonymousNamedGetterInterface.idl
+AnonymousNamedIndexedGetterInterface.idl
+ArbitraryInterface.idl
+BaseInterface.idl
+BooleanTypeTestInterface.idl
+CallbackFunctionInterface.idl
+CallbackInterfaceInterface.idl
+ConditionalInterface.idl
+ConstantsInterface.idl
+ConstructorInterface.idl
+ConstructorWithArgumentsInterface.idl
+DerivedGetterSetterInterface.idl
+DerivedInterface.idl
+DisabledInterface.idl
+DOMStringTestInterface.idl
+EnumerationInterface.idl
+ExceptionObjectInterface.idl
+ExceptionsInterface.idl
+ExtendedIDLAttributesInterface.idl
+GarbageCollectionTestInterface.idl
+GetOpaqueRootInterface.idl
+GlobalInterfaceParent.idl
+IndexedGetterInterface.idl
+InterfaceWithUnsupportedProperties.idl
+NamedConstructorInterface.idl
+NamedGetterInterface.idl
+NamedIndexedGetterInterface.idl
+NestedPutForwardsInterface.idl
+NoConstructorInterface.idl
+NoInterfaceObjectInterface.idl
+NullableTypesTestInterface.idl
+NumericTypesTestInterface.idl
+ObjectTypeBindingsInterface.idl
+OperationsTestInterface.idl
+PutForwardsInterface.idl
+SequenceUser.idl
+SingleOperationInterface.idl
+StaticPropertiesInterface.idl
+StringifierAnonymousOperationInterface.idl
+StringifierAttributeInterface.idl
+StringifierOperationInterface.idl
+TargetInterface.idl
+UnionTypesInterface.idl
+Window.idl
+ImplementedInterface.idl
+PartialInterface.idl
+InterfaceWithUnsupportedProperties_partial.idl
+UnsupportedInterface.idl
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc
index 671baa7..765ee14 100644
--- a/src/cobalt/browser/application.cc
+++ b/src/cobalt/browser/application.cc
@@ -101,7 +101,11 @@
 int GetWebDriverPort() {
   // The default port on which the webdriver server should listen for incoming
   // connections.
+#if defined(SB_OVERRIDE_DEFAULT_WEBDRIVER_PORT)
+  const int kDefaultWebDriverPort = SB_OVERRIDE_DEFAULT_WEBDRIVER_PORT;
+#else
   const int kDefaultWebDriverPort = 9515;
+#endif  // defined(SB_OVERRIDE_DEFAULT_WEBDRIVER_PORT)
   int webdriver_port = kDefaultWebDriverPort;
   CommandLine* command_line = CommandLine::ForCurrentProcess();
   if (command_line->HasSwitch(switches::kWebDriverPort)) {
diff --git a/src/cobalt/browser/browser.gyp b/src/cobalt/browser/browser.gyp
index 3bdd680..c544756 100644
--- a/src/cobalt/browser/browser.gyp
+++ b/src/cobalt/browser/browser.gyp
@@ -91,6 +91,9 @@
         ['enable_about_scheme == 1', {
           'defines': [ 'ENABLE_ABOUT_SCHEME' ],
         }],
+        ['enable_mtm == 1', {
+          'defines' : ['ENABLE_MTM'],
+        }],
       ],
     },
 
diff --git a/src/cobalt/browser/global_constructors_idls_idl_files_list.tmp b/src/cobalt/browser/global_constructors_idls_idl_files_list.tmp
new file mode 100644
index 0000000..b50619f
--- /dev/null
+++ b/src/cobalt/browser/global_constructors_idls_idl_files_list.tmp
@@ -0,0 +1,149 @@
+../audio/AudioBuffer.idl
+../audio/AudioBufferSourceNode.idl
+../audio/AudioContext.idl
+../audio/AudioDestinationNode.idl
+../audio/AudioNode.idl
+../cssom/CSS.idl
+../cssom/CSSConditionRule.idl
+../cssom/CSSGroupingRule.idl
+../cssom/CSSFontFaceRule.idl
+../cssom/CSSMediaRule.idl
+../cssom/CSSKeyframeRule.idl
+../cssom/CSSKeyframesRule.idl
+../cssom/CSSRule.idl
+../cssom/CSSRuleList.idl
+../cssom/CSSStyleDeclaration.idl
+../cssom/CSSStyleRule.idl
+../cssom/CSSStyleSheet.idl
+../cssom/MediaList.idl
+../cssom/StyleSheet.idl
+../cssom/StyleSheetList.idl
+../debug/DebugHub.idl
+../debug/Debugger.idl
+../debug/DebuggerEventTarget.idl
+../debug/DebugScriptRunner.idl
+../dom/AnimationEvent.idl
+../dom/ArrayBuffer.idl
+../dom/ArrayBufferView.idl
+../dom/Attr.idl
+../dom/Blob.idl
+../dom/CDATASection.idl
+../dom/CharacterData.idl
+../dom/Comment.idl
+../dom/Console.idl
+../dom/Crypto.idl
+../dom/DataView.idl
+../dom/Document.idl
+../dom/DocumentTimeline.idl
+../dom/DocumentType.idl
+../dom/DOMException.idl
+../dom/DOMImplementation.idl
+../dom/DOMParser.idl
+../dom/DOMRect.idl
+../dom/DOMRectList.idl
+../dom/DOMRectReadOnly.idl
+../dom/DOMStringMap.idl
+../dom/DOMTokenList.idl
+../dom/Element.idl
+../dom/Event.idl
+../dom/EventListener.idl
+../dom/EventTarget.idl
+../dom/Float32Array.idl
+../dom/Float64Array.idl
+../dom/FocusEvent.idl
+../dom/History.idl
+../dom/HTMLAnchorElement.idl
+../dom/HTMLBodyElement.idl
+../dom/HTMLBRElement.idl
+../dom/HTMLCollection.idl
+../dom/HTMLDivElement.idl
+../dom/HTMLElement.idl
+../dom/HTMLHeadElement.idl
+../dom/HTMLHeadingElement.idl
+../dom/HTMLHtmlElement.idl
+../dom/HTMLImageElement.idl
+../dom/HTMLLinkElement.idl
+../dom/HTMLMediaElement.idl
+../dom/HTMLMetaElement.idl
+../dom/HTMLParagraphElement.idl
+../dom/HTMLScriptElement.idl
+../dom/HTMLSpanElement.idl
+../dom/HTMLStyleElement.idl
+../dom/HTMLTitleElement.idl
+../dom/HTMLUnknownElement.idl
+../dom/HTMLVideoElement.idl
+../dom/KeyboardEvent.idl
+../dom/Location.idl
+../dom/MediaError.idl
+../dom/MediaKeyCompleteEvent.idl
+../dom/MediaKeyError.idl
+../dom/MediaKeyErrorEvent.idl
+../dom/MediaKeyMessageEvent.idl
+../dom/MediaKeyNeededEvent.idl
+../dom/MediaQueryList.idl
+../dom/MediaSource.idl
+../dom/MemoryInfo.idl
+../dom/MimeTypeArray.idl
+../dom/NamedNodeMap.idl
+../dom/Navigator.idl
+../dom/Node.idl
+../dom/NodeList.idl
+../dom/Performance.idl
+../dom/PerformanceTiming.idl
+../dom/PluginArray.idl
+../dom/ProgressEvent.idl
+../dom/Screen.idl
+../dom/SecurityPolicyViolationEvent.idl
+../dom/SourceBuffer.idl
+../dom/SourceBufferList.idl
+../dom/Storage.idl
+../dom/StorageEvent.idl
+../dom/TestRunner.idl
+../dom/Text.idl
+../dom/TimeRanges.idl
+../dom/TransitionEvent.idl
+../dom/UIEvent.idl
+../dom/Uint16Array.idl
+../dom/Uint32Array.idl
+../dom/Uint8Array.idl
+../dom/URL.idl
+../dom/VideoPlaybackQuality.idl
+../dom/Window.idl
+../dom/XMLDocument.idl
+../dom/XMLSerializer.idl
+../h5vcc/dial/DialHttpRequest.idl
+../h5vcc/dial/DialHttpResponse.idl
+../h5vcc/dial/DialServer.idl
+../h5vcc/H5vcc.idl
+../h5vcc/H5vccAccountInfo.idl
+../h5vcc/H5vccAccountManager.idl
+../h5vcc/H5vccAudioConfig.idl
+../h5vcc/H5vccAudioConfigArray.idl
+../h5vcc/H5vccCVal.idl
+../h5vcc/H5vccCValKeyList.idl
+../h5vcc/H5vccDeepLinkEventTarget.idl
+../h5vcc/H5vccRuntime.idl
+../h5vcc/H5vccRuntimeEventTarget.idl
+../h5vcc/H5vccSettings.idl
+../h5vcc/H5vccStorage.idl
+../h5vcc/H5vccSystem.idl
+../speech/SpeechRecognition.idl
+../speech/SpeechRecognitionAlternative.idl
+../speech/SpeechRecognitionError.idl
+../speech/SpeechRecognitionEvent.idl
+../speech/SpeechRecognitionResult.idl
+../speech/SpeechRecognitionResultList.idl
+../web_animations/Animatable.idl
+../web_animations/Animation.idl
+../web_animations/AnimationEffectReadOnly.idl
+../web_animations/AnimationEffectTimingReadOnly.idl
+../web_animations/AnimationTimeline.idl
+../web_animations/Keyframe.idl
+../web_animations/KeyframeEffectReadOnly.idl
+../webdriver/ScriptExecutor.idl
+../webdriver/ScriptExecutorParams.idl
+../webdriver/ScriptExecutorResult.idl
+../websocket/WebSocket.idl
+../xhr/XMLHttpRequest.idl
+../xhr/XMLHttpRequestEventTarget.idl
+../xhr/XMLHttpRequestUpload.idl
diff --git a/src/cobalt/browser/global_objects_idl_files_list.tmp b/src/cobalt/browser/global_objects_idl_files_list.tmp
new file mode 100644
index 0000000..b50619f
--- /dev/null
+++ b/src/cobalt/browser/global_objects_idl_files_list.tmp
@@ -0,0 +1,149 @@
+../audio/AudioBuffer.idl
+../audio/AudioBufferSourceNode.idl
+../audio/AudioContext.idl
+../audio/AudioDestinationNode.idl
+../audio/AudioNode.idl
+../cssom/CSS.idl
+../cssom/CSSConditionRule.idl
+../cssom/CSSGroupingRule.idl
+../cssom/CSSFontFaceRule.idl
+../cssom/CSSMediaRule.idl
+../cssom/CSSKeyframeRule.idl
+../cssom/CSSKeyframesRule.idl
+../cssom/CSSRule.idl
+../cssom/CSSRuleList.idl
+../cssom/CSSStyleDeclaration.idl
+../cssom/CSSStyleRule.idl
+../cssom/CSSStyleSheet.idl
+../cssom/MediaList.idl
+../cssom/StyleSheet.idl
+../cssom/StyleSheetList.idl
+../debug/DebugHub.idl
+../debug/Debugger.idl
+../debug/DebuggerEventTarget.idl
+../debug/DebugScriptRunner.idl
+../dom/AnimationEvent.idl
+../dom/ArrayBuffer.idl
+../dom/ArrayBufferView.idl
+../dom/Attr.idl
+../dom/Blob.idl
+../dom/CDATASection.idl
+../dom/CharacterData.idl
+../dom/Comment.idl
+../dom/Console.idl
+../dom/Crypto.idl
+../dom/DataView.idl
+../dom/Document.idl
+../dom/DocumentTimeline.idl
+../dom/DocumentType.idl
+../dom/DOMException.idl
+../dom/DOMImplementation.idl
+../dom/DOMParser.idl
+../dom/DOMRect.idl
+../dom/DOMRectList.idl
+../dom/DOMRectReadOnly.idl
+../dom/DOMStringMap.idl
+../dom/DOMTokenList.idl
+../dom/Element.idl
+../dom/Event.idl
+../dom/EventListener.idl
+../dom/EventTarget.idl
+../dom/Float32Array.idl
+../dom/Float64Array.idl
+../dom/FocusEvent.idl
+../dom/History.idl
+../dom/HTMLAnchorElement.idl
+../dom/HTMLBodyElement.idl
+../dom/HTMLBRElement.idl
+../dom/HTMLCollection.idl
+../dom/HTMLDivElement.idl
+../dom/HTMLElement.idl
+../dom/HTMLHeadElement.idl
+../dom/HTMLHeadingElement.idl
+../dom/HTMLHtmlElement.idl
+../dom/HTMLImageElement.idl
+../dom/HTMLLinkElement.idl
+../dom/HTMLMediaElement.idl
+../dom/HTMLMetaElement.idl
+../dom/HTMLParagraphElement.idl
+../dom/HTMLScriptElement.idl
+../dom/HTMLSpanElement.idl
+../dom/HTMLStyleElement.idl
+../dom/HTMLTitleElement.idl
+../dom/HTMLUnknownElement.idl
+../dom/HTMLVideoElement.idl
+../dom/KeyboardEvent.idl
+../dom/Location.idl
+../dom/MediaError.idl
+../dom/MediaKeyCompleteEvent.idl
+../dom/MediaKeyError.idl
+../dom/MediaKeyErrorEvent.idl
+../dom/MediaKeyMessageEvent.idl
+../dom/MediaKeyNeededEvent.idl
+../dom/MediaQueryList.idl
+../dom/MediaSource.idl
+../dom/MemoryInfo.idl
+../dom/MimeTypeArray.idl
+../dom/NamedNodeMap.idl
+../dom/Navigator.idl
+../dom/Node.idl
+../dom/NodeList.idl
+../dom/Performance.idl
+../dom/PerformanceTiming.idl
+../dom/PluginArray.idl
+../dom/ProgressEvent.idl
+../dom/Screen.idl
+../dom/SecurityPolicyViolationEvent.idl
+../dom/SourceBuffer.idl
+../dom/SourceBufferList.idl
+../dom/Storage.idl
+../dom/StorageEvent.idl
+../dom/TestRunner.idl
+../dom/Text.idl
+../dom/TimeRanges.idl
+../dom/TransitionEvent.idl
+../dom/UIEvent.idl
+../dom/Uint16Array.idl
+../dom/Uint32Array.idl
+../dom/Uint8Array.idl
+../dom/URL.idl
+../dom/VideoPlaybackQuality.idl
+../dom/Window.idl
+../dom/XMLDocument.idl
+../dom/XMLSerializer.idl
+../h5vcc/dial/DialHttpRequest.idl
+../h5vcc/dial/DialHttpResponse.idl
+../h5vcc/dial/DialServer.idl
+../h5vcc/H5vcc.idl
+../h5vcc/H5vccAccountInfo.idl
+../h5vcc/H5vccAccountManager.idl
+../h5vcc/H5vccAudioConfig.idl
+../h5vcc/H5vccAudioConfigArray.idl
+../h5vcc/H5vccCVal.idl
+../h5vcc/H5vccCValKeyList.idl
+../h5vcc/H5vccDeepLinkEventTarget.idl
+../h5vcc/H5vccRuntime.idl
+../h5vcc/H5vccRuntimeEventTarget.idl
+../h5vcc/H5vccSettings.idl
+../h5vcc/H5vccStorage.idl
+../h5vcc/H5vccSystem.idl
+../speech/SpeechRecognition.idl
+../speech/SpeechRecognitionAlternative.idl
+../speech/SpeechRecognitionError.idl
+../speech/SpeechRecognitionEvent.idl
+../speech/SpeechRecognitionResult.idl
+../speech/SpeechRecognitionResultList.idl
+../web_animations/Animatable.idl
+../web_animations/Animation.idl
+../web_animations/AnimationEffectReadOnly.idl
+../web_animations/AnimationEffectTimingReadOnly.idl
+../web_animations/AnimationTimeline.idl
+../web_animations/Keyframe.idl
+../web_animations/KeyframeEffectReadOnly.idl
+../webdriver/ScriptExecutor.idl
+../webdriver/ScriptExecutorParams.idl
+../webdriver/ScriptExecutorResult.idl
+../websocket/WebSocket.idl
+../xhr/XMLHttpRequest.idl
+../xhr/XMLHttpRequestEventTarget.idl
+../xhr/XMLHttpRequestUpload.idl
diff --git a/src/cobalt/browser/interfaces_info_individual_static_idl_files_list.tmp b/src/cobalt/browser/interfaces_info_individual_static_idl_files_list.tmp
new file mode 100644
index 0000000..a9c6e86
--- /dev/null
+++ b/src/cobalt/browser/interfaces_info_individual_static_idl_files_list.tmp
@@ -0,0 +1,176 @@
+../audio/AudioBuffer.idl
+../audio/AudioBufferSourceNode.idl
+../audio/AudioContext.idl
+../audio/AudioDestinationNode.idl
+../audio/AudioNode.idl
+../cssom/CSS.idl
+../cssom/CSSConditionRule.idl
+../cssom/CSSGroupingRule.idl
+../cssom/CSSFontFaceRule.idl
+../cssom/CSSMediaRule.idl
+../cssom/CSSKeyframeRule.idl
+../cssom/CSSKeyframesRule.idl
+../cssom/CSSRule.idl
+../cssom/CSSRuleList.idl
+../cssom/CSSStyleDeclaration.idl
+../cssom/CSSStyleRule.idl
+../cssom/CSSStyleSheet.idl
+../cssom/MediaList.idl
+../cssom/StyleSheet.idl
+../cssom/StyleSheetList.idl
+../debug/DebugHub.idl
+../debug/Debugger.idl
+../debug/DebuggerEventTarget.idl
+../debug/DebugScriptRunner.idl
+../dom/AnimationEvent.idl
+../dom/ArrayBuffer.idl
+../dom/ArrayBufferView.idl
+../dom/Attr.idl
+../dom/Blob.idl
+../dom/CDATASection.idl
+../dom/CharacterData.idl
+../dom/Comment.idl
+../dom/Console.idl
+../dom/Crypto.idl
+../dom/DataView.idl
+../dom/Document.idl
+../dom/DocumentTimeline.idl
+../dom/DocumentType.idl
+../dom/DOMException.idl
+../dom/DOMImplementation.idl
+../dom/DOMParser.idl
+../dom/DOMRect.idl
+../dom/DOMRectList.idl
+../dom/DOMRectReadOnly.idl
+../dom/DOMStringMap.idl
+../dom/DOMTokenList.idl
+../dom/Element.idl
+../dom/Event.idl
+../dom/EventListener.idl
+../dom/EventTarget.idl
+../dom/Float32Array.idl
+../dom/Float64Array.idl
+../dom/FocusEvent.idl
+../dom/History.idl
+../dom/HTMLAnchorElement.idl
+../dom/HTMLBodyElement.idl
+../dom/HTMLBRElement.idl
+../dom/HTMLCollection.idl
+../dom/HTMLDivElement.idl
+../dom/HTMLElement.idl
+../dom/HTMLHeadElement.idl
+../dom/HTMLHeadingElement.idl
+../dom/HTMLHtmlElement.idl
+../dom/HTMLImageElement.idl
+../dom/HTMLLinkElement.idl
+../dom/HTMLMediaElement.idl
+../dom/HTMLMetaElement.idl
+../dom/HTMLParagraphElement.idl
+../dom/HTMLScriptElement.idl
+../dom/HTMLSpanElement.idl
+../dom/HTMLStyleElement.idl
+../dom/HTMLTitleElement.idl
+../dom/HTMLUnknownElement.idl
+../dom/HTMLVideoElement.idl
+../dom/KeyboardEvent.idl
+../dom/Location.idl
+../dom/MediaError.idl
+../dom/MediaKeyCompleteEvent.idl
+../dom/MediaKeyError.idl
+../dom/MediaKeyErrorEvent.idl
+../dom/MediaKeyMessageEvent.idl
+../dom/MediaKeyNeededEvent.idl
+../dom/MediaQueryList.idl
+../dom/MediaSource.idl
+../dom/MemoryInfo.idl
+../dom/MimeTypeArray.idl
+../dom/NamedNodeMap.idl
+../dom/Navigator.idl
+../dom/Node.idl
+../dom/NodeList.idl
+../dom/Performance.idl
+../dom/PerformanceTiming.idl
+../dom/PluginArray.idl
+../dom/ProgressEvent.idl
+../dom/Screen.idl
+../dom/SecurityPolicyViolationEvent.idl
+../dom/SourceBuffer.idl
+../dom/SourceBufferList.idl
+../dom/Storage.idl
+../dom/StorageEvent.idl
+../dom/TestRunner.idl
+../dom/Text.idl
+../dom/TimeRanges.idl
+../dom/TransitionEvent.idl
+../dom/UIEvent.idl
+../dom/Uint16Array.idl
+../dom/Uint32Array.idl
+../dom/Uint8Array.idl
+../dom/URL.idl
+../dom/VideoPlaybackQuality.idl
+../dom/Window.idl
+../dom/XMLDocument.idl
+../dom/XMLSerializer.idl
+../h5vcc/dial/DialHttpRequest.idl
+../h5vcc/dial/DialHttpResponse.idl
+../h5vcc/dial/DialServer.idl
+../h5vcc/H5vcc.idl
+../h5vcc/H5vccAccountInfo.idl
+../h5vcc/H5vccAccountManager.idl
+../h5vcc/H5vccAudioConfig.idl
+../h5vcc/H5vccAudioConfigArray.idl
+../h5vcc/H5vccCVal.idl
+../h5vcc/H5vccCValKeyList.idl
+../h5vcc/H5vccDeepLinkEventTarget.idl
+../h5vcc/H5vccRuntime.idl
+../h5vcc/H5vccRuntimeEventTarget.idl
+../h5vcc/H5vccSettings.idl
+../h5vcc/H5vccStorage.idl
+../h5vcc/H5vccSystem.idl
+../speech/SpeechRecognition.idl
+../speech/SpeechRecognitionAlternative.idl
+../speech/SpeechRecognitionError.idl
+../speech/SpeechRecognitionEvent.idl
+../speech/SpeechRecognitionResult.idl
+../speech/SpeechRecognitionResultList.idl
+../web_animations/Animatable.idl
+../web_animations/Animation.idl
+../web_animations/AnimationEffectReadOnly.idl
+../web_animations/AnimationEffectTimingReadOnly.idl
+../web_animations/AnimationTimeline.idl
+../web_animations/Keyframe.idl
+../web_animations/KeyframeEffectReadOnly.idl
+../webdriver/ScriptExecutor.idl
+../webdriver/ScriptExecutorParams.idl
+../webdriver/ScriptExecutorResult.idl
+../websocket/WebSocket.idl
+../xhr/XMLHttpRequest.idl
+../xhr/XMLHttpRequestEventTarget.idl
+../xhr/XMLHttpRequestUpload.idl
+../cssom/LinkStyle.idl
+../dom/Document_CSSOM.idl
+../dom/Document_HTML5.idl
+../dom/Document_WebAnimationsAPI.idl
+../dom/Element_CSSOMView.idl
+../dom/Element_DOMParsingAndSerialization.idl
+../dom/ElementCSSInlineStyle.idl
+../dom/GlobalCrypto.idl
+../dom/GlobalEventHandlers.idl
+../dom/HTMLElement_CSSOMView.idl
+../dom/NavigatorID.idl
+../dom/NavigatorLanguage.idl
+../dom/NavigatorPlugins.idl
+../dom/NavigatorStorageUtils.idl
+../dom/NonDocumentTypeChildNode.idl
+../dom/NonElementParentNode.idl
+../dom/ParentNode.idl
+../dom/Performance_HighResolutionTime.idl
+../dom/URLUtils.idl
+../dom/Window_AnimationTiming.idl
+../dom/Window_CSSOM.idl
+../dom/Window_CSSOMView.idl
+../dom/Window_Performance.idl
+../dom/WindowEventHandlers.idl
+../dom/WindowLocalStorage.idl
+../dom/WindowSessionStorage.idl
+../dom/WindowTimers.idl
diff --git a/src/cobalt/browser/memory_tracker/memory_tracker_tool_impl.cc b/src/cobalt/browser/memory_tracker/memory_tracker_tool_impl.cc
index 5b82542..cc317ea 100644
--- a/src/cobalt/browser/memory_tracker/memory_tracker_tool_impl.cc
+++ b/src/cobalt/browser/memory_tracker/memory_tracker_tool_impl.cc
@@ -655,12 +655,14 @@
   // Begin output to CSV.
 
   // Preamble
-  ss << kNewLine << "//////////////////////////////////////////////" << kNewLine
-     << "// CSV of bytes allocated per region (MB's)." << kNewLine
+  ss << kNewLine
+     << "//////////////////////////////////////////////" << kNewLine
+     << "// CSV of BYTES allocated per region (MB's)." << kNewLine
      << "// Units are in Megabytes. This is designed" << kNewLine
      << "// to be used in a stacked graph." << kNewLine;
 
   // HEADER.
+  ss << "Time(mins),";
   for (MapIt it = samples.begin(); it != samples.end(); ++it) {
     const std::string& name = it->first;
     ss << kQuote << SanitizeCSVKey(name) << kQuote << kDelimiter;
@@ -669,6 +671,9 @@
 
   // Print out the values of each of the samples.
   for (size_t i = 0; i < smallest_sample_size; ++i) {
+    // Output time first so that it can be used as an x-axis.
+    const double time_mins = timeseries.time_stamps_[i].InSeconds()/60.f;
+    ss << time_mins << ",";
     for (MapIt it = samples.begin(); it != samples.end(); ++it) {
       const int64 alloc_bytes = it->second.allocated_bytes_[i];
       // Convert to float megabytes with decimals of precision.
@@ -678,26 +683,34 @@
     }
     ss << kNewLine;
   }
+  ss << "// END CSV of BYTES allocated per region." << kNewLine;
+  ss << "//////////////////////////////////////////////";
 
   ss << kNewLine;
   // Preamble
   ss << kNewLine << "//////////////////////////////////////////////";
-  ss << kNewLine << "// CSV of number of allocations per region." << kNewLine;
+  ss << kNewLine << "// CSV of COUNT of allocations per region." << kNewLine;
 
   // HEADER
+  ss << "Time(mins),";
   for (MapIt it = samples.begin(); it != samples.end(); ++it) {
     const std::string& name = it->first;
     ss << kQuote << SanitizeCSVKey(name) << kQuote << kDelimiter;
   }
   ss << kNewLine;
   for (size_t i = 0; i < smallest_sample_size; ++i) {
+    // Output time first so that it can be used as an x-axis.
+    const double time_mins = timeseries.time_stamps_[i].InSeconds() / 60.f;
+    ss << time_mins << ",";
     for (MapIt it = samples.begin(); it != samples.end(); ++it) {
       const int64 n_allocs = it->second.number_allocations_[i];
       ss << n_allocs << kDelimiter;
     }
     ss << kNewLine;
   }
-  ss << kNewLine;
+  ss << "// END CSV of COUNT of allocations per region." << kNewLine;
+  ss << "//////////////////////////////////////////////";
+
   std::string output = ss.str();
   return output;
 }
@@ -736,6 +749,19 @@
     new_entry.allocated_bytes_.push_back(allocation_bytes);
     new_entry.number_allocations_.push_back(num_allocs);
   }
+
+  // Sample the in use memory bytes reported by malloc.
+  MemoryStats memory_stats = GetProcessMemoryStats();
+  AllocationSamples& process_mem_in_use = map_samples["ProcessMemoryInUse"];
+  process_mem_in_use.allocated_bytes_.push_back(memory_stats.used_cpu_memory);
+  process_mem_in_use.number_allocations_.push_back(0);
+
+  // Sample the reserved memory bytes reported by malloc.
+  AllocationSamples& process_mem_reserved
+      = map_samples["ProcessMemoryReserved"];
+  process_mem_reserved.allocated_bytes_
+      .push_back(memory_stats.used_cpu_memory);
+  process_mem_reserved.number_allocations_.push_back(0);
 }
 
 bool MemoryTrackerCompressedTimeSeries::IsFull(const TimeSeries& timeseries,
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc
index dd689c9..572200d 100644
--- a/src/cobalt/browser/web_module.cc
+++ b/src/cobalt/browser/web_module.cc
@@ -326,7 +326,27 @@
     : name_(data.options.name), is_running_(false) {
   resource_provider_ = data.resource_provider;
 
-  css_parser_ = css_parser::Parser::Create();
+  // Currently we rely on a platform to explicitly specify that it supports
+  // the map-to-mesh filter via the ENABLE_MTM define (and the 'enable_mtm' gyp
+  // variable).  When we have better support for checking for decode to texture
+  // support, it would be nice to switch this logic to something like:
+  //
+  //   supports_map_to_mesh =
+  //      (resource_provider_->Supports3D() && SbPlayerSupportsDecodeToTexture()
+  //           ? css_parser::Parser::kSupportsMapToMesh
+  //           : css_parser::Parser::kDoesNotSupportMapToMesh);
+  //
+  // Note that it is important that we do not parse map-to-mesh filters if we
+  // cannot render them, since web apps may check for map-to-mesh support by
+  // testing whether it parses or not via the CSS.supports() Web API.
+  css_parser::Parser::SupportsMapToMeshFlag supports_map_to_mesh =
+#if defined(ENABLE_MTM)
+      css_parser::Parser::kSupportsMapToMesh;
+#else
+      css_parser::Parser::kDoesNotSupportMapToMesh;
+#endif
+
+  css_parser_ = css_parser::Parser::Create(supports_map_to_mesh);
   DCHECK(css_parser_);
 
   dom_parser_.reset(new dom_parser::Parser(
diff --git a/src/cobalt/browser/web_module_stat_tracker.cc b/src/cobalt/browser/web_module_stat_tracker.cc
index 61b0d92..06323fa 100644
--- a/src/cobalt/browser/web_module_stat_tracker.cc
+++ b/src/cobalt/browser/web_module_stat_tracker.cc
@@ -16,9 +16,16 @@
 
 #include "cobalt/browser/web_module_stat_tracker.h"
 
+#if defined(ENABLE_WEBDRIVER)
+#include <sstream>
+#endif  // ENABLE_WEBDRIVER
+
 #include "base/stringprintf.h"
 #include "cobalt/base/tokens.h"
 #include "cobalt/dom/event.h"
+#if defined(ENABLE_WEBDRIVER)
+#include "cobalt/dom/global_stats.h"
+#endif  // ENABLE_WEBDRIVER
 
 namespace cobalt {
 namespace browser {
@@ -162,7 +169,15 @@
           StringPrintf("Event.Duration.%s.Layout.RenderAndAnimate",
                        name.c_str()),
           base::TimeDelta(),
-          "RenderAndAnimate duration for event (in microseconds).") {}
+          "RenderAndAnimate duration for event (in microseconds).")
+#if defined(ENABLE_WEBDRIVER)
+      ,
+      value_dictionary(
+          StringPrintf("Event.%s.ValueDictionary", name.c_str()),
+          "All event values represented as a dictionary in a string.")
+#endif  // ENABLE_WEBDRIVER
+{
+}
 
 bool WebModuleStatTracker::IsStopWatchEnabled(int /*id*/) const { return true; }
 
@@ -217,6 +232,70 @@
       layout_stat_tracker_->GetStopWatchTypeDuration(
           layout::LayoutStatTracker::kStopWatchTypeRenderAndAnimate);
 
+#if defined(ENABLE_WEBDRIVER)
+  // When the Webdriver is enabled, all of the event's values are stored within
+  // a single string representing a dictionary of key-value pairs. This allows
+  // the Webdriver to query a single CVal to retrieve all of the event's values.
+  std::ostringstream oss;
+  oss << "{"
+      << "\"ProducedRenderTree\":" << was_render_tree_produced << ", "
+      << "\"CntDomEventListeners\":"
+      << dom::GlobalStats::GetInstance()->GetNumEventListeners() << ", "
+      << "\"CntDomNodes\":" << dom::GlobalStats::GetInstance()->GetNumNodes()
+      << ", "
+      << "\"CntDomHtmlElements\":" << dom_stat_tracker_->total_html_elements()
+      << ", "
+      << "\"CntDomHtmlElementsCreated\":"
+      << dom_stat_tracker_->html_elements_created_count() << ", "
+      << "\"CntDomHtmlElementsDestroyed\":"
+      << dom_stat_tracker_->html_elements_destroyed_count() << ", "
+      << "\"CntDomUpdateMatchingRuleCalls\":"
+      << dom_stat_tracker_->update_matching_rules_count() << ", "
+      << "\"CntDomUpdateComputedStyleCalls\":"
+      << dom_stat_tracker_->update_computed_style_count() << ", "
+      << "\"CntLayoutBoxes\":" << layout_stat_tracker_->total_boxes() << ", "
+      << "\"CntLayoutBoxesCreated\":"
+      << layout_stat_tracker_->boxes_created_count() << ", "
+      << "\"CntLayoutBoxesDestroyed\":"
+      << layout_stat_tracker_->boxes_destroyed_count() << ", "
+      << "\"DurTotalUs\":"
+      << stop_watch_durations_[kStopWatchTypeEvent].InMicroseconds() << ", "
+      << "\"DurDomInjectEventUs\":"
+      << stop_watch_durations_[kStopWatchTypeInjectEvent].InMicroseconds()
+      << ", "
+      << "\"DurDomUpdateComputedStyleUs\":"
+      << dom_stat_tracker_
+             ->GetStopWatchTypeDuration(
+                 dom::DomStatTracker::kStopWatchTypeUpdateComputedStyle)
+             .InMicroseconds()
+      << ", "
+      << "\"DurLayoutBoxTreeUs\":"
+      << layout_stat_tracker_
+             ->GetStopWatchTypeDuration(
+                 layout::LayoutStatTracker::kStopWatchTypeLayoutBoxTree)
+             .InMicroseconds()
+      << ", "
+      << "\"DurLayoutBoxTreeBoxGenerationUs\":"
+      << layout_stat_tracker_
+             ->GetStopWatchTypeDuration(
+                 layout::LayoutStatTracker::kStopWatchTypeBoxGeneration)
+             .InMicroseconds()
+      << ", "
+      << "\"DurLayoutBoxTreeUpdateUsedSizesUs\":"
+      << layout_stat_tracker_
+             ->GetStopWatchTypeDuration(
+                 layout::LayoutStatTracker::kStopWatchTypeUpdateUsedSizes)
+             .InMicroseconds()
+      << ", "
+      << "\"DurLayoutRenderAndAnimateUs\":"
+      << layout_stat_tracker_
+             ->GetStopWatchTypeDuration(
+                 layout::LayoutStatTracker::kStopWatchTypeRenderAndAnimate)
+             .InMicroseconds()
+      << "}";
+  event_stats->value_dictionary = oss.str();
+#endif  // ENABLE_WEBDRIVER
+
   current_event_type_ = kEventTypeInvalid;
 
   dom_stat_tracker_->OnEndEvent();
diff --git a/src/cobalt/browser/web_module_stat_tracker.h b/src/cobalt/browser/web_module_stat_tracker.h
index ef5d751..95efb4a 100644
--- a/src/cobalt/browser/web_module_stat_tracker.h
+++ b/src/cobalt/browser/web_module_stat_tracker.h
@@ -97,6 +97,13 @@
         duration_layout_update_used_sizes;
     base::CVal<base::TimeDelta, base::CValPublic>
         duration_layout_render_and_animate;
+
+#if defined(ENABLE_WEBDRIVER)
+    // A string containing all of the event's values as a dictionary of
+    // key-value pairs. This is used by the Webdriver and is only enabled with
+    // it.
+    base::CVal<std::string> value_dictionary;
+#endif  // ENABLE_WEBDRIVER
   };
 
   // From base::StopWatchOwner
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index 615df59..b679c25 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-20698
\ No newline at end of file
+21796
\ No newline at end of file
diff --git a/src/cobalt/build/gyp_utils.py b/src/cobalt/build/gyp_utils.py
index e1f3791..c39ed35 100644
--- a/src/cobalt/build/gyp_utils.py
+++ b/src/cobalt/build/gyp_utils.py
@@ -94,7 +94,10 @@
 
   if os.path.isfile(BUILD_ID_PATH):
     with open(BUILD_ID_PATH, 'r') as build_id_file:
-      return build_id_file.read().replace('\n', '')
+      build_number = int(build_id_file.read().replace('\n', ''))
+      logging.info('Retrieving build number from %s', BUILD_ID_PATH)
+      logging.info('Build Number: %d', build_number)
+      return build_number
 
   revinfo = GetRevinfo()
   json_deps = json.dumps(revinfo)
diff --git a/src/cobalt/content/ssl/certs/578d5c04.0 b/src/cobalt/content/ssl/certs/578d5c04.0
deleted file mode 100644
index 676db97..0000000
--- a/src/cobalt/content/ssl/certs/578d5c04.0
+++ /dev/null
@@ -1,19 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV
-UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy
-dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1
-MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx
-dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B
-AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f
-BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A
-cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC
-AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ
-MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm
-aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw
-ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj
-IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF
-MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA
-A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y
-7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh
-1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4
------END CERTIFICATE-----
diff --git a/src/cobalt/content/ssl/certs/c01cdfa2.0 b/src/cobalt/content/ssl/certs/c01cdfa2.0
index b5f1875..27778ab 100644
--- a/src/cobalt/content/ssl/certs/c01cdfa2.0
+++ b/src/cobalt/content/ssl/certs/c01cdfa2.0
@@ -25,4 +25,4 @@
 BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+PwGZsY6rp2aQW9IHR
 lRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4mJO3
 7M2CYfE45k+XmCpajQ==
------END CERTIFICATE-----
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/src/cobalt/css_parser/grammar.y b/src/cobalt/css_parser/grammar.y
index 50f29c5..3342a9e 100644
--- a/src/cobalt/css_parser/grammar.y
+++ b/src/cobalt/css_parser/grammar.y
@@ -198,6 +198,7 @@
 %token kEllipseToken                    // ellipse
 %token kEllipsisToken                   // ellipsis
 %token kEndToken                        // end
+%token kEquirectangularToken            // equirectangular
 %token kFantasyToken                    // fantasy
 %token kFarthestCornerToken             // farthest-corner
 %token kFarthestSideToken               // farthest-side
@@ -793,6 +794,11 @@
 %destructor { delete $$; } <cobalt_mtm_filter_functions>
 
 %union {
+  cssom::MTMFunction::MeshSpec* cobalt_map_to_mesh_spec; }
+%type <cobalt_map_to_mesh_spec> cobalt_map_to_mesh_spec
+%destructor { delete $$; } <cobalt_map_to_mesh_spec>
+
+%union {
   cssom::MTMFunction::ResolutionMatchedMeshListBuilder* cobalt_mtm_resolution_matched_meshes; }
 %type <cobalt_mtm_resolution_matched_meshes> cobalt_mtm_resolution_matched_mesh_list
 %destructor { delete $$; } <cobalt_mtm_resolution_matched_meshes>
@@ -6542,21 +6548,42 @@
 
 cobalt_mtm_filter_function:
   // Encodes an mtm filter. Currently the only type of filter function supported.
-    cobalt_mtm_function_name maybe_whitespace url
-        cobalt_mtm_resolution_matched_mesh_list comma angle angle comma
-        cobalt_mtm_transform_function maybe_cobalt_mtm_stereo_mode
+    cobalt_mtm_function_name maybe_whitespace cobalt_map_to_mesh_spec comma angle
+        angle comma cobalt_mtm_transform_function maybe_cobalt_mtm_stereo_mode
         ')' maybe_whitespace {
-    scoped_ptr<cssom::MTMFunction::ResolutionMatchedMeshListBuilder>
-        resolution_matched_mesh_urls($4);
-    scoped_ptr<glm::mat4> transform($9);
+    scoped_ptr<cssom::MTMFunction::MeshSpec>
+        mesh_spec($3);
+    scoped_ptr<glm::mat4> transform($8);
+    scoped_refptr<cssom::KeywordValue> stereo_mode =
+        MakeScopedRefPtrAndRelease($9);
 
-    $$ = new cssom::MTMFunction(
-        MakeScopedRefPtrAndRelease($3),
-        resolution_matched_mesh_urls->Pass(),
-        $6,
-        $7,
-        *transform,
-        MakeScopedRefPtrAndRelease($10));
+    if (!parser_impl->supports_map_to_mesh()) {
+      YYERROR;
+    } else {
+      $$ = new cssom::MTMFunction(
+          mesh_spec.Pass(),
+          $5,
+          $6,
+          *transform,
+          stereo_mode);
+    }
+  }
+  ;
+
+cobalt_map_to_mesh_spec:
+    kEquirectangularToken {
+    $$ = new cssom::MTMFunction::MeshSpec(
+        cssom::MTMFunction::kEquirectangular);
+  }
+  | url cobalt_mtm_resolution_matched_mesh_list {
+    scoped_refptr<cssom::PropertyValue> url = MakeScopedRefPtrAndRelease($1);
+    scoped_ptr<cssom::MTMFunction::ResolutionMatchedMeshListBuilder>
+        resolution_matched_mesh_urls($2);
+
+    $$ = new cssom::MTMFunction::MeshSpec(
+        cssom::MTMFunction::kUrls,
+        url,
+        resolution_matched_mesh_urls->Pass());
   }
   ;
 
diff --git a/src/cobalt/css_parser/parser.cc b/src/cobalt/css_parser/parser.cc
index ca538a5..c632354 100644
--- a/src/cobalt/css_parser/parser.cc
+++ b/src/cobalt/css_parser/parser.cc
@@ -119,7 +119,8 @@
              cssom::CSSParser* css_parser,
              const Parser::OnMessageCallback& on_warning_callback,
              const Parser::OnMessageCallback& on_error_callback,
-             Parser::MessageVerbosity message_verbosity);
+             Parser::MessageVerbosity message_verbosity,
+             Parser::SupportsMapToMeshFlag supports_map_to_mesh);
 
   cssom::CSSParser* css_parser() { return css_parser_; }
 
@@ -176,6 +177,8 @@
     return into_declaration_data_;
   }
 
+  bool supports_map_to_mesh() const { return supports_map_to_mesh_; }
+
  private:
   bool Parse();
 
@@ -215,6 +218,9 @@
   // ParsePropertyIntoDeclarationData() is called.
   cssom::CSSDeclarationData* into_declaration_data_;
 
+  // Whether or not we support parsing "filter: map-to-mesh(...)".
+  bool supports_map_to_mesh_;
+
   static void IncludeInputWithMessage(const std::string& input,
                                       const Parser::OnMessageCallback& callback,
                                       const std::string& message) {
@@ -245,7 +251,8 @@
                        cssom::CSSParser* css_parser,
                        const Parser::OnMessageCallback& on_warning_callback,
                        const Parser::OnMessageCallback& on_error_callback,
-                       Parser::MessageVerbosity message_verbosity)
+                       Parser::MessageVerbosity message_verbosity,
+                       Parser::SupportsMapToMeshFlag supports_map_to_mesh)
     : input_(input),
       input_location_(input_location),
       on_warning_callback_(on_warning_callback),
@@ -253,7 +260,9 @@
       message_verbosity_(message_verbosity),
       css_parser_(css_parser),
       scanner_(input_.c_str(), &string_pool_),
-      into_declaration_data_(NULL) {}
+      into_declaration_data_(NULL),
+      supports_map_to_mesh_(supports_map_to_mesh ==
+                            Parser::kSupportsMapToMesh) {}
 
 scoped_refptr<cssom::CSSStyleSheet> ParserImpl::ParseStyleSheet() {
   TRACK_MEMORY_SCOPE("CSS");
@@ -531,39 +540,44 @@
 
 }  // namespace
 
-scoped_ptr<Parser> Parser::Create() {
+scoped_ptr<Parser> Parser::Create(SupportsMapToMeshFlag supports_map_to_mesh) {
   return make_scoped_ptr(new Parser(base::Bind(&LogWarningCallback),
                                     base::Bind(&LogErrorCallback),
-                                    Parser::kVerbose));
+                                    Parser::kVerbose, supports_map_to_mesh));
 }
 
 Parser::Parser(const OnMessageCallback& on_warning_callback,
                const OnMessageCallback& on_error_callback,
-               MessageVerbosity message_verbosity)
+               MessageVerbosity message_verbosity,
+               SupportsMapToMeshFlag supports_map_to_mesh)
     : on_warning_callback_(on_warning_callback),
       on_error_callback_(on_error_callback),
-      message_verbosity_(message_verbosity) {}
+      message_verbosity_(message_verbosity),
+      supports_map_to_mesh_(supports_map_to_mesh) {}
 
 Parser::~Parser() {}
 
 scoped_refptr<cssom::CSSStyleSheet> Parser::ParseStyleSheet(
     const std::string& input, const base::SourceLocation& input_location) {
   ParserImpl parser_impl(input, input_location, this, on_warning_callback_,
-                         on_error_callback_, message_verbosity_);
+                         on_error_callback_, message_verbosity_,
+                         supports_map_to_mesh_);
   return parser_impl.ParseStyleSheet();
 }
 
 scoped_refptr<cssom::CSSRule> Parser::ParseRule(
     const std::string& input, const base::SourceLocation& input_location) {
   ParserImpl parser_impl(input, input_location, this, on_warning_callback_,
-                         on_error_callback_, message_verbosity_);
+                         on_error_callback_, message_verbosity_,
+                         supports_map_to_mesh_);
   return parser_impl.ParseRule();
 }
 
 scoped_refptr<cssom::CSSDeclaredStyleData> Parser::ParseStyleDeclarationList(
     const std::string& input, const base::SourceLocation& input_location) {
   ParserImpl parser_impl(input, input_location, this, on_warning_callback_,
-                         on_error_callback_, message_verbosity_);
+                         on_error_callback_, message_verbosity_,
+                         supports_map_to_mesh_);
   return parser_impl.ParseStyleDeclarationList();
 }
 
@@ -571,7 +585,8 @@
 Parser::ParseFontFaceDeclarationList(
     const std::string& input, const base::SourceLocation& input_location) {
   ParserImpl parser_impl(input, input_location, this, on_warning_callback_,
-                         on_error_callback_, message_verbosity_);
+                         on_error_callback_, message_verbosity_,
+                         supports_map_to_mesh_);
   return parser_impl.ParseFontFaceDeclarationList();
 }
 
@@ -580,7 +595,7 @@
     const base::SourceLocation& property_location) {
   ParserImpl parser_impl(property_value, property_location, this,
                          on_warning_callback_, on_error_callback_,
-                         message_verbosity_);
+                         message_verbosity_, supports_map_to_mesh_);
   return parser_impl.ParsePropertyValue(property_name);
 }
 
@@ -590,7 +605,7 @@
     cssom::CSSDeclarationData* declaration_data) {
   ParserImpl parser_impl(property_value, property_location, this,
                          on_warning_callback_, on_error_callback_,
-                         message_verbosity_);
+                         message_verbosity_, supports_map_to_mesh_);
   return parser_impl.ParsePropertyIntoDeclarationData(property_name,
                                                       declaration_data);
 }
@@ -598,7 +613,8 @@
 scoped_refptr<cssom::MediaList> Parser::ParseMediaList(
     const std::string& media_list, const base::SourceLocation& input_location) {
   ParserImpl parser_impl(media_list, input_location, this, on_warning_callback_,
-                         on_error_callback_, message_verbosity_);
+                         on_error_callback_, message_verbosity_,
+                         supports_map_to_mesh_);
   return parser_impl.ParseMediaList();
 }
 
@@ -607,7 +623,7 @@
     const base::SourceLocation& input_location) {
   ParserImpl parser_impl(media_query, input_location, this,
                          on_warning_callback_, on_error_callback_,
-                         message_verbosity_);
+                         message_verbosity_, supports_map_to_mesh_);
   return parser_impl.ParseMediaQuery();
 }
 
diff --git a/src/cobalt/css_parser/parser.h b/src/cobalt/css_parser/parser.h
index 116f1e2..e7ecb23 100644
--- a/src/cobalt/css_parser/parser.h
+++ b/src/cobalt/css_parser/parser.h
@@ -27,7 +27,16 @@
 
 class Parser : public cssom::CSSParser {
  public:
-  static scoped_ptr<Parser> Create();
+  enum SupportsMapToMeshFlag {
+    kSupportsMapToMesh,
+    kDoesNotSupportMapToMesh,
+  };
+
+  // The parameter |supports_map_to_mesh| can be used to toggle parser support
+  // for the map-to-mesh CSS filter.  If disabled, "filter: map-to-mesh(...)"
+  // will result in a parse error.
+  static scoped_ptr<Parser> Create(
+      SupportsMapToMeshFlag supports_map_to_mesh = kSupportsMapToMesh);
   ~Parser();
 
   scoped_refptr<cssom::CSSStyleSheet> ParseStyleSheet(
@@ -79,14 +88,16 @@
 
   Parser(const OnMessageCallback& on_warning_callback,
          const OnMessageCallback& on_error_callback,
-         MessageVerbosity message_verbosity);
+         MessageVerbosity message_verbosity,
+         SupportsMapToMeshFlag supports_map_to_mesh);
 
   const OnMessageCallback on_warning_callback_;
   const OnMessageCallback on_error_callback_;
   MessageVerbosity message_verbosity_;
+  SupportsMapToMeshFlag supports_map_to_mesh_;
 
   friend class ParserImpl;
-  friend class ParserTest;
+  friend class ParserTestBase;
   DISALLOW_COPY_AND_ASSIGN(Parser);
 };
 
diff --git a/src/cobalt/css_parser/parser_test.cc b/src/cobalt/css_parser/parser_test.cc
index e786724..a428d6b 100644
--- a/src/cobalt/css_parser/parser_test.cc
+++ b/src/cobalt/css_parser/parser_test.cc
@@ -95,10 +95,10 @@
   MOCK_METHOD1(OnError, void(const std::string& message));
 };
 
-class ParserTest : public ::testing::Test {
+class ParserTestBase : public ::testing::Test {
  public:
-  ParserTest();
-  ~ParserTest() OVERRIDE {}
+  explicit ParserTestBase(Parser::SupportsMapToMeshFlag supports_map_to_mesh);
+  ~ParserTestBase() OVERRIDE {}
 
  protected:
   ::testing::StrictMock<MockParserObserver> parser_observer_;
@@ -106,14 +106,25 @@
   const base::SourceLocation source_location_;
 };
 
-ParserTest::ParserTest()
+ParserTestBase::ParserTestBase(
+    Parser::SupportsMapToMeshFlag supports_map_to_mesh)
     : parser_(base::Bind(&MockParserObserver::OnWarning,
                          base::Unretained(&parser_observer_)),
               base::Bind(&MockParserObserver::OnError,
                          base::Unretained(&parser_observer_)),
-              Parser::kShort),
+              Parser::kShort, supports_map_to_mesh),
       source_location_("[object ParserTest]", 1, 1) {}
 
+class ParserTest : public ParserTestBase {
+ public:
+  ParserTest() : ParserTestBase(Parser::kSupportsMapToMesh) {}
+};
+
+class ParserNoMapToMeshTest : public ParserTestBase {
+ public:
+  ParserNoMapToMeshTest() : ParserTestBase(Parser::kDoesNotSupportMapToMesh) {}
+};
+
 // TODO: Test every reduction that has semantic action.
 
 TEST_F(ParserTest, ParsesEmptyInput) {
@@ -8373,11 +8384,11 @@
   scoped_refptr<cssom::CSSDeclaredStyleData> style =
       parser_.ParseStyleDeclarationList(
           "filter: map-to-mesh(url(projection.msh),"
-          "                        180deg 1.5rad,"
-          "                        matrix3d(1, 0, 0, 0,"
-          "                                 0, 1, 0, 0,"
-          "                                 0, 0, 1, 0,"
-          "                                 0, 0, 0, 1));",
+          "                    180deg 1.5rad,"
+          "                    matrix3d(1, 0, 0, 0,"
+          "                             0, 1, 0, 0,"
+          "                             0, 0, 1, 0,"
+          "                             0, 0, 0, 1));",
           source_location_);
   scoped_refptr<cssom::FilterFunctionListValue> filter_list =
       dynamic_cast<cssom::FilterFunctionListValue*>(
@@ -8390,6 +8401,40 @@
       dynamic_cast<const cssom::MTMFunction*>(filter_list->value()[0]);
   ASSERT_TRUE(mtm_function);
 
+  EXPECT_EQ(cssom::MTMFunction::kUrls, mtm_function->mesh_spec().mesh_type());
+
+  EXPECT_EQ(static_cast<float>(M_PI), mtm_function->horizontal_fov());
+  EXPECT_EQ(1.5f, mtm_function->vertical_fov());
+
+  EXPECT_EQ(mtm_function->stereo_mode()->value(),
+            cssom::KeywordValue::kMonoscopic);
+}
+
+TEST_F(ParserTest, ParsesMtmEquirectangularFilter) {
+  scoped_refptr<cssom::CSSDeclaredStyleData> style =
+      parser_.ParseStyleDeclarationList(
+          "filter: map-to-mesh(equirectangular,"
+          "                    180deg 1.5rad,"
+          "                    matrix3d(1, 0, 0, 0,"
+          "                             0, 1, 0, 0,"
+          "                             0, 0, 1, 0,"
+          "                             0, 0, 0, 1),"
+          "                             monoscopic);",
+          source_location_);
+  scoped_refptr<cssom::FilterFunctionListValue> filter_list =
+      dynamic_cast<cssom::FilterFunctionListValue*>(
+          style->GetPropertyValue(cssom::kFilterProperty).get());
+
+  ASSERT_TRUE(filter_list);
+  ASSERT_EQ(1, filter_list->value().size());
+
+  const cssom::MTMFunction* mtm_function =
+      dynamic_cast<const cssom::MTMFunction*>(filter_list->value()[0]);
+  ASSERT_TRUE(mtm_function);
+
+  EXPECT_EQ(cssom::MTMFunction::kEquirectangular,
+            mtm_function->mesh_spec().mesh_type());
+
   EXPECT_EQ(static_cast<float>(M_PI), mtm_function->horizontal_fov());
   EXPECT_EQ(1.5f, mtm_function->vertical_fov());
 
@@ -8425,6 +8470,46 @@
             cssom::KeywordValue::kMonoscopic);
 }
 
+TEST_F(ParserNoMapToMeshTest, DoesNotParseMapToMeshFilter) {
+  EXPECT_CALL(parser_observer_,
+              OnWarning("[object ParserTest]:1:9: warning: unsupported value"));
+
+  scoped_refptr<cssom::CSSDeclaredStyleData> style =
+      parser_.ParseStyleDeclarationList(
+          "filter: map-to-mesh(url(projection.msh),"
+          "                    180deg 1.5rad,"
+          "                    matrix3d(1, 0, 0, 0,"
+          "                             0, 1, 0, 0,"
+          "                             0, 0, 1, 0,"
+          "                             0, 0, 0, 1));",
+          source_location_);
+  scoped_refptr<cssom::FilterFunctionListValue> filter_list =
+      dynamic_cast<cssom::FilterFunctionListValue*>(
+          style->GetPropertyValue(cssom::kFilterProperty).get());
+
+  EXPECT_FALSE(filter_list);
+}
+
+TEST_F(ParserNoMapToMeshTest, DoesNotParseMtmFilter) {
+  EXPECT_CALL(parser_observer_,
+              OnWarning("[object ParserTest]:1:9: warning: unsupported value"));
+
+  scoped_refptr<cssom::CSSDeclaredStyleData> style =
+      parser_.ParseStyleDeclarationList(
+          "filter: -cobalt-mtm(url(projection.msh),"
+          "                    180deg 1.5rad,"
+          "                    matrix3d(1, 0, 0, 0,"
+          "                             0, 1, 0, 0,"
+          "                             0, 0, 1, 0,"
+          "                             0, 0, 0, 1));",
+          source_location_);
+  scoped_refptr<cssom::FilterFunctionListValue> filter_list =
+      dynamic_cast<cssom::FilterFunctionListValue*>(
+          style->GetPropertyValue(cssom::kFilterProperty).get());
+
+  EXPECT_FALSE(filter_list);
+}
+
 TEST_F(ParserTest, ParsesMtmResolutionMatchedUrlsFilter) {
   scoped_refptr<cssom::CSSDeclaredStyleData> style =
       parser_.ParseStyleDeclarationList(
@@ -8448,7 +8533,7 @@
       dynamic_cast<const cssom::MTMFunction*>(filter_list->value()[0]);
 
   const cssom::MTMFunction::ResolutionMatchedMeshListBuilder& meshes =
-      mtm_function->resolution_matched_meshes();
+      mtm_function->mesh_spec().resolution_matched_meshes();
 
   ASSERT_EQ(3, meshes.size());
   EXPECT_EQ(640, meshes[0]->width_match());
diff --git a/src/cobalt/css_parser/scanner.cc b/src/cobalt/css_parser/scanner.cc
index 3be1ffb..3d3ce5e 100644
--- a/src/cobalt/css_parser/scanner.cc
+++ b/src/cobalt/css_parser/scanner.cc
@@ -2257,6 +2257,11 @@
         *property_value_token = kFarthestCornerToken;
         return true;
       }
+      if (IsEqualToCssIdentifier(name.begin,
+                                 cssom::kEquirectangularKeywordName)) {
+        *property_value_token = kEquirectangularToken;
+        return true;
+      }
       return false;
 
     case 17:
diff --git a/src/cobalt/cssom/computed_style.cc b/src/cobalt/cssom/computed_style.cc
index 5fa9201..3e13d7d 100644
--- a/src/cobalt/cssom/computed_style.cc
+++ b/src/cobalt/cssom/computed_style.cc
@@ -351,6 +351,7 @@
     case KeywordValue::kCursive:
     case KeywordValue::kEllipsis:
     case KeywordValue::kEnd:
+    case KeywordValue::kEquirectangular:
     case KeywordValue::kFantasy:
     case KeywordValue::kForwards:
     case KeywordValue::kFixed:
@@ -449,6 +450,7 @@
     case KeywordValue::kCursive:
     case KeywordValue::kEllipsis:
     case KeywordValue::kEnd:
+    case KeywordValue::kEquirectangular:
     case KeywordValue::kFantasy:
     case KeywordValue::kFixed:
     case KeywordValue::kForwards:
@@ -555,6 +557,7 @@
     case KeywordValue::kCursive:
     case KeywordValue::kEllipsis:
     case KeywordValue::kEnd:
+    case KeywordValue::kEquirectangular:
     case KeywordValue::kFantasy:
     case KeywordValue::kFixed:
     case KeywordValue::kForwards:
@@ -676,6 +679,7 @@
     case KeywordValue::kCursive:
     case KeywordValue::kEllipsis:
     case KeywordValue::kEnd:
+    case KeywordValue::kEquirectangular:
     case KeywordValue::kFantasy:
     case KeywordValue::kFixed:
     case KeywordValue::kForwards:
@@ -798,6 +802,7 @@
     case KeywordValue::kCurrentColor:
     case KeywordValue::kEllipsis:
     case KeywordValue::kEnd:
+    case KeywordValue::kEquirectangular:
     case KeywordValue::kFantasy:
     case KeywordValue::kFixed:
     case KeywordValue::kForwards:
@@ -918,6 +923,7 @@
     case KeywordValue::kCurrentColor:
     case KeywordValue::kCursive:
     case KeywordValue::kEllipsis:
+    case KeywordValue::kEquirectangular:
     case KeywordValue::kEnd:
     case KeywordValue::kFantasy:
     case KeywordValue::kFixed:
@@ -1035,6 +1041,7 @@
     case KeywordValue::kCursive:
     case KeywordValue::kEllipsis:
     case KeywordValue::kEnd:
+    case KeywordValue::kEquirectangular:
     case KeywordValue::kFantasy:
     case KeywordValue::kFixed:
     case KeywordValue::kForwards:
@@ -1146,6 +1153,7 @@
     case KeywordValue::kCursive:
     case KeywordValue::kEllipsis:
     case KeywordValue::kEnd:
+    case KeywordValue::kEquirectangular:
     case KeywordValue::kFantasy:
     case KeywordValue::kFixed:
     case KeywordValue::kForwards:
@@ -1563,6 +1571,7 @@
     case KeywordValue::kCursive:
     case KeywordValue::kEllipsis:
     case KeywordValue::kEnd:
+    case KeywordValue::kEquirectangular:
     case KeywordValue::kFantasy:
     case KeywordValue::kFixed:
     case KeywordValue::kForwards:
@@ -1839,6 +1848,7 @@
     case KeywordValue::kCursive:
     case KeywordValue::kEllipsis:
     case KeywordValue::kEnd:
+    case KeywordValue::kEquirectangular:
     case KeywordValue::kFantasy:
     case KeywordValue::kFixed:
     case KeywordValue::kForwards:
@@ -2089,6 +2099,7 @@
     case KeywordValue::kCursive:
     case KeywordValue::kEllipsis:
     case KeywordValue::kEnd:
+    case KeywordValue::kEquirectangular:
     case KeywordValue::kFantasy:
     case KeywordValue::kForwards:
     case KeywordValue::kFixed:
@@ -2476,6 +2487,7 @@
     case KeywordValue::kCursive:
     case KeywordValue::kEllipsis:
     case KeywordValue::kEnd:
+    case KeywordValue::kEquirectangular:
     case KeywordValue::kFantasy:
     case KeywordValue::kFixed:
     case KeywordValue::kForwards:
diff --git a/src/cobalt/cssom/keyword_names.cc b/src/cobalt/cssom/keyword_names.cc
index 34fde90..dce8cd0 100644
--- a/src/cobalt/cssom/keyword_names.cc
+++ b/src/cobalt/cssom/keyword_names.cc
@@ -50,6 +50,7 @@
 const char kEllipseKeywordName[] = "ellipse";
 const char kEllipsisKeywordName[] = "ellipsis";
 const char kEndKeywordName[] = "end";
+const char kEquirectangularKeywordName[] = "equirectangular";
 const char kFantasyKeywordName[] = "fantasy";
 const char kFarthestCornerKeywordName[] = "farthest-corner";
 const char kFarthestSideKeywordName[] = "farthest-side";
diff --git a/src/cobalt/cssom/keyword_names.h b/src/cobalt/cssom/keyword_names.h
index 86f0eda..7cd5852 100644
--- a/src/cobalt/cssom/keyword_names.h
+++ b/src/cobalt/cssom/keyword_names.h
@@ -52,6 +52,7 @@
 extern const char kEllipseKeywordName[];
 extern const char kEllipsisKeywordName[];
 extern const char kEndKeywordName[];
+extern const char kEquirectangularKeywordName[];
 extern const char kFantasyKeywordName[];
 extern const char kFarthestCornerKeywordName[];
 extern const char kFarthestSideKeywordName[];
diff --git a/src/cobalt/cssom/keyword_value.cc b/src/cobalt/cssom/keyword_value.cc
index 0d90535..ac2c21e 100644
--- a/src/cobalt/cssom/keyword_value.cc
+++ b/src/cobalt/cssom/keyword_value.cc
@@ -44,6 +44,7 @@
         cursive_value(new KeywordValue(KeywordValue::kCursive)),
         ellipsis_value(new KeywordValue(KeywordValue::kEllipsis)),
         end_value(new KeywordValue(KeywordValue::kEnd)),
+        equirectangular_value(new KeywordValue(KeywordValue::kEquirectangular)),
         fantasy_value(new KeywordValue(KeywordValue::kFantasy)),
         forwards_value(new KeywordValue(KeywordValue::kForwards)),
         fixed_value(new KeywordValue(KeywordValue::kFixed)),
@@ -100,6 +101,7 @@
   const scoped_refptr<KeywordValue> cursive_value;
   const scoped_refptr<KeywordValue> ellipsis_value;
   const scoped_refptr<KeywordValue> end_value;
+  const scoped_refptr<KeywordValue> equirectangular_value;
   const scoped_refptr<KeywordValue> fantasy_value;
   const scoped_refptr<KeywordValue> forwards_value;
   const scoped_refptr<KeywordValue> fixed_value;
@@ -219,6 +221,10 @@
   return non_trivial_static_fields.Get().end_value;
 }
 
+const scoped_refptr<KeywordValue>& KeywordValue::GetEquirectangular() {
+  return non_trivial_static_fields.Get().equirectangular_value;
+}
+
 const scoped_refptr<KeywordValue>& KeywordValue::GetFantasy() {
   return non_trivial_static_fields.Get().fantasy_value;
 }
@@ -401,6 +407,8 @@
       return kEllipsisKeywordName;
     case kEnd:
       return kEndKeywordName;
+    case kEquirectangular:
+      return kEquirectangularKeywordName;
     case kFantasy:
       return kFantasyKeywordName;
     case kForwards:
diff --git a/src/cobalt/cssom/keyword_value.h b/src/cobalt/cssom/keyword_value.h
index bed4ba0..35659b6 100644
--- a/src/cobalt/cssom/keyword_value.h
+++ b/src/cobalt/cssom/keyword_value.h
@@ -131,6 +131,11 @@
     //   https://www.w3.org/TR/css-text-3/#text-align
     kEnd,
 
+    // "equirectangular" is a value of a parameter of the "map-to-mesh"
+    // filter function which indicates that the built-in equirectangular mesh
+    // should be used.
+    kEquirectangular,
+
     // "fantasy" is a value of "font_family" property which indicates a generic
     // font family using decorative or expressive representations of characters.
     //   https://www.w3.org/TR/css3-fonts/#generic-font-families
@@ -346,6 +351,7 @@
   static const scoped_refptr<KeywordValue>& GetCursive();
   static const scoped_refptr<KeywordValue>& GetEllipsis();
   static const scoped_refptr<KeywordValue>& GetEnd();
+  static const scoped_refptr<KeywordValue>& GetEquirectangular();
   static const scoped_refptr<KeywordValue>& GetFantasy();
   static const scoped_refptr<KeywordValue>& GetForwards();
   static const scoped_refptr<KeywordValue>& GetFixed();
diff --git a/src/cobalt/cssom/mtm_function.cc b/src/cobalt/cssom/mtm_function.cc
index 661060b..f973bf6 100644
--- a/src/cobalt/cssom/mtm_function.cc
+++ b/src/cobalt/cssom/mtm_function.cc
@@ -26,34 +26,24 @@
 namespace cobalt {
 namespace cssom {
 
-MTMFunction::MTMFunction(
-    const scoped_refptr<PropertyValue>& mesh_url,
-    ResolutionMatchedMeshListBuilder resolution_matched_meshes,
-    float horizontal_fov, float vertical_fov, const glm::mat4& transform,
-    const scoped_refptr<KeywordValue>& stereo_mode)
-    : mesh_url_(mesh_url),
-      resolution_matched_meshes_(resolution_matched_meshes.Pass()),
-      horizontal_fov_(horizontal_fov),
-      vertical_fov_(vertical_fov),
-      transform_(transform),
-      stereo_mode_(stereo_mode) {
-  DCHECK(mesh_url_);
-  DCHECK(stereo_mode_);
-}
-
 std::string MTMFunction::ToString() const {
-  std::string result = "-cobalt-mtm(";
+  std::string result = "map-to-mesh(";
 
-  result.append(mesh_url()->ToString());
+  if (mesh_spec().mesh_type() == kEquirectangular) {
+    result.append("equirectangular");
+  } else if (mesh_spec().mesh_type() == kUrls) {
+    result.append(mesh_spec().mesh_url()->ToString());
 
-  const ResolutionMatchedMeshListBuilder& meshes = resolution_matched_meshes();
-  for (size_t mesh_index = 0; mesh_index < meshes.size(); ++mesh_index) {
-    result.push_back(' ');
-    result.append(base::IntToString(meshes[mesh_index]->width_match()));
-    result.push_back(' ');
-    result.append(base::IntToString(meshes[mesh_index]->height_match()));
-    result.push_back(' ');
-    result.append(meshes[mesh_index]->mesh_url()->ToString());
+    const ResolutionMatchedMeshListBuilder& meshes =
+        mesh_spec().resolution_matched_meshes();
+    for (size_t mesh_index = 0; mesh_index < meshes.size(); ++mesh_index) {
+      result.push_back(' ');
+      result.append(base::IntToString(meshes[mesh_index]->width_match()));
+      result.push_back(' ');
+      result.append(base::IntToString(meshes[mesh_index]->height_match()));
+      result.push_back(' ');
+      result.append(meshes[mesh_index]->mesh_url()->ToString());
+    }
   }
 
   result.append(base::StringPrintf(", %.7grad", horizontal_fov()));
@@ -118,31 +108,38 @@
 }
 
 bool MTMFunction::operator==(const MTMFunction& rhs) const {
-  if (!mesh_url()->Equals(*rhs.mesh_url()) ||
+  if (mesh_spec().mesh_type() != rhs.mesh_spec().mesh_type() ||
       horizontal_fov() != rhs.horizontal_fov() ||
       horizontal_fov() != rhs.horizontal_fov() ||
       !stereo_mode()->Equals(*rhs.stereo_mode())) {
     return false;
   }
-  const ResolutionMatchedMeshListBuilder& lhs_meshes =
-      resolution_matched_meshes();
-  const ResolutionMatchedMeshListBuilder& rhs_meshes =
-      rhs.resolution_matched_meshes();
 
-  if (lhs_meshes.size() != rhs_meshes.size()) {
-    return false;
-  }
-
-  for (size_t i = 0; i < lhs_meshes.size(); ++i) {
-    if (*lhs_meshes[i] != *rhs_meshes[i]) {
+  if (mesh_spec().mesh_type() == kUrls) {
+    if (!mesh_spec().mesh_url()->Equals(*rhs.mesh_spec().mesh_url())) {
       return false;
     }
-  }
 
-  for (int col = 0; col <= 3; ++col) {
-    if (!glm::all(glm::equal(transform()[col], rhs.transform()[col]))) {
+    const ResolutionMatchedMeshListBuilder& lhs_meshes =
+        mesh_spec().resolution_matched_meshes();
+    const ResolutionMatchedMeshListBuilder& rhs_meshes =
+        rhs.mesh_spec().resolution_matched_meshes();
+
+    if (lhs_meshes.size() != rhs_meshes.size()) {
       return false;
     }
+
+    for (size_t i = 0; i < lhs_meshes.size(); ++i) {
+      if (*lhs_meshes[i] != *rhs_meshes[i]) {
+        return false;
+      }
+    }
+
+    for (int col = 0; col <= 3; ++col) {
+      if (!glm::all(glm::equal(transform()[col], rhs.transform()[col]))) {
+        return false;
+      }
+    }
   }
 
   return true;
diff --git a/src/cobalt/cssom/mtm_function.h b/src/cobalt/cssom/mtm_function.h
index 764c0cb..00a1a17 100644
--- a/src/cobalt/cssom/mtm_function.h
+++ b/src/cobalt/cssom/mtm_function.h
@@ -22,6 +22,7 @@
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
 #include "base/memory/scoped_vector.h"
 #include "cobalt/base/polymorphic_equatable.h"
 #include "cobalt/cssom/filter_function.h"
@@ -33,7 +34,7 @@
 namespace cobalt {
 namespace cssom {
 
-// Represent an MTM filter.
+// Represent a map-to-mesh filter.
 class MTMFunction : public FilterFunction {
  public:
   // A resolution-matched mesh specifier.
@@ -52,21 +53,95 @@
     const int height_match_;
     const scoped_refptr<PropertyValue> mesh_url_;
   };
+
+  // Type of the source of the mesh: either a built-in mesh type or a custom
+  // mesh.
+  enum MeshSpecType {
+    // Built-in equirectangular mesh.
+    kEquirectangular,
+    // List of custom binary mesh URLs.
+    kUrls
+  };
   typedef ScopedVector<ResolutionMatchedMesh> ResolutionMatchedMeshListBuilder;
 
+  // Contains the specification of the mesh source.
+  class MeshSpec {
+   public:
+    explicit MeshSpec(MeshSpecType mesh_type) : mesh_type_(mesh_type) {
+      // Check that this is a built-in mesh type.
+      DCHECK_EQ(mesh_type, kEquirectangular);
+    }
+
+    MeshSpec(MeshSpecType mesh_type,
+             const scoped_refptr<PropertyValue>& mesh_url,
+             ResolutionMatchedMeshListBuilder resolution_matched_meshes)
+        : mesh_type_(mesh_type),
+          mesh_url_(mesh_url),
+          resolution_matched_meshes_(resolution_matched_meshes.Pass()) {
+      DCHECK_EQ(mesh_type_, kUrls);
+      DCHECK(mesh_url);
+    }
+
+    const scoped_refptr<PropertyValue>& mesh_url() const {
+      DCHECK_EQ(mesh_type_, kUrls);
+      return mesh_url_;
+    }
+    const ResolutionMatchedMeshListBuilder& resolution_matched_meshes() const {
+      DCHECK_EQ(mesh_type_, kUrls);
+      return resolution_matched_meshes_;
+    }
+    MeshSpecType mesh_type() const { return mesh_type_; }
+
+   private:
+    MeshSpecType mesh_type_;
+    scoped_refptr<PropertyValue> mesh_url_;
+    ResolutionMatchedMeshListBuilder resolution_matched_meshes_;
+
+    DISALLOW_COPY_AND_ASSIGN(MeshSpec);
+  };
+
+  MTMFunction(scoped_ptr<MeshSpec> mesh_spec, float horizontal_fov,
+              float vertical_fov, const glm::mat4& transform,
+              const scoped_refptr<KeywordValue>& stereo_mode)
+      : mesh_spec_(mesh_spec.Pass()),
+        horizontal_fov_(horizontal_fov),
+        vertical_fov_(vertical_fov),
+        transform_(transform),
+        stereo_mode_(stereo_mode) {
+    DCHECK(mesh_spec_);
+    DCHECK(stereo_mode_);
+  }
+
+  // Alternate constructor for mesh URL lists.
   MTMFunction(const scoped_refptr<PropertyValue>& mesh_url,
               ResolutionMatchedMeshListBuilder resolution_matched_meshes,
               float horizontal_fov, float vertical_fov,
               const glm::mat4& transform,
-              const scoped_refptr<KeywordValue>& stereo_mode);
+              const scoped_refptr<KeywordValue>& stereo_mode)
+      : mesh_spec_(
+            new MeshSpec(kUrls, mesh_url, resolution_matched_meshes.Pass())),
+        horizontal_fov_(horizontal_fov),
+        vertical_fov_(vertical_fov),
+        transform_(transform),
+        stereo_mode_(stereo_mode) {
+    DCHECK(stereo_mode_);
+  }
+
+  // Alternate constructor for built-in meshes.
+  MTMFunction(MeshSpecType spec_type, float horizontal_fov, float vertical_fov,
+              const glm::mat4& transform,
+              const scoped_refptr<KeywordValue>& stereo_mode)
+      : mesh_spec_(new MeshSpec(spec_type)),
+        horizontal_fov_(horizontal_fov),
+        vertical_fov_(vertical_fov),
+        transform_(transform),
+        stereo_mode_(stereo_mode) {
+    DCHECK(stereo_mode_);
+  }
 
   ~MTMFunction() OVERRIDE {}
 
-  const scoped_refptr<PropertyValue>& mesh_url() const { return mesh_url_; }
-  const ResolutionMatchedMeshListBuilder& resolution_matched_meshes() const {
-    return resolution_matched_meshes_;
-  }
-
+  const MeshSpec& mesh_spec() const { return *mesh_spec_; }
   float horizontal_fov() const { return horizontal_fov_; }
   float vertical_fov() const { return vertical_fov_; }
   const glm::mat4& transform() const { return transform_; }
@@ -83,8 +158,7 @@
   DEFINE_POLYMORPHIC_EQUATABLE_TYPE(MTMFunction);
 
  private:
-  const scoped_refptr<PropertyValue> mesh_url_;
-  const ResolutionMatchedMeshListBuilder resolution_matched_meshes_;
+  const scoped_ptr<MeshSpec> mesh_spec_;
   const float horizontal_fov_;
   const float vertical_fov_;
   const glm::mat4 transform_;
diff --git a/src/cobalt/cssom/property_value_is_equal_test.cc b/src/cobalt/cssom/property_value_is_equal_test.cc
index 263634b..d619871 100644
--- a/src/cobalt/cssom/property_value_is_equal_test.cc
+++ b/src/cobalt/cssom/property_value_is_equal_test.cc
@@ -612,6 +612,11 @@
       glm::mat4(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
                 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
       KeywordValue::GetStereoscopicLeftRight()));
+  filter_list_a.push_back(
+      new MTMFunction(MTMFunction::kEquirectangular, 0.676f, 6.28f,
+                      glm::mat4(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
+                                0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
+                      KeywordValue::GetStereoscopicLeftRight()));
   scoped_refptr<FilterFunctionListValue> value_a(
       new FilterFunctionListValue(filter_list_a.Pass()));
 
@@ -628,6 +633,11 @@
       glm::mat4(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
                 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
       KeywordValue::GetStereoscopicLeftRight()));
+  filter_list_b.push_back(
+      new MTMFunction(MTMFunction::kEquirectangular, 0.676f, 6.28f,
+                      glm::mat4(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
+                                0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
+                      KeywordValue::GetStereoscopicLeftRight()));
   scoped_refptr<FilterFunctionListValue> value_b(
       new FilterFunctionListValue(filter_list_b.Pass()));
 
@@ -656,6 +666,27 @@
       new FilterFunctionListValue(filter_list_b.Pass()));
 
   EXPECT_FALSE(value_a->Equals(*value_b));
+
+  FilterFunctionListValue::Builder filter_list_c;
+  filter_list_c.push_back(
+      new MTMFunction(MTMFunction::kEquirectangular, 8.5f, 3.14f,
+                      glm::mat4(1.0f, 0.0f, 0.0f, 2.0f, 0.0f, 1.0f, 0.0f, 0.0f,
+                                0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
+                      KeywordValue::GetMonoscopic()));
+  scoped_refptr<FilterFunctionListValue> value_c(
+      new FilterFunctionListValue(filter_list_c.Pass()));
+
+  FilterFunctionListValue::Builder filter_list_d;
+  filter_list_d.push_back(new MTMFunction(
+      new URLValue("format.msh"),
+      MTMFunction::ResolutionMatchedMeshListBuilder().Pass(), 8.5f, 3.14f,
+      glm::mat4(1.0f, 0.0f, 0.0f, 2.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+                1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
+      KeywordValue::GetMonoscopic()));
+  scoped_refptr<FilterFunctionListValue> value_d(
+      new FilterFunctionListValue(filter_list_d.Pass()));
+
+  EXPECT_FALSE(value_c->Equals(*value_d));
 }
 
 }  // namespace cssom
diff --git a/src/cobalt/cssom/property_value_to_string_test.cc b/src/cobalt/cssom/property_value_to_string_test.cc
index 7d18941..ea7cc8e 100644
--- a/src/cobalt/cssom/property_value_to_string_test.cc
+++ b/src/cobalt/cssom/property_value_to_string_test.cc
@@ -356,7 +356,21 @@
                 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
       KeywordValue::GetMonoscopic()));
   EXPECT_EQ(
-      "-cobalt-mtm(url(-.msh), "
+      "map-to-mesh(url(-.msh), "
+      "2.5rad 3.14rad, "
+      "matrix3d(1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1), "
+      "monoscopic)",
+      function->ToString());
+}
+
+TEST(PropertyValueToStringTest, MTMFunctionEquirectangular) {
+  scoped_ptr<MTMFunction> function(
+      new MTMFunction(MTMFunction::kEquirectangular, 2.5f, 3.14f,
+                      glm::mat4(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
+                                0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
+                      KeywordValue::GetMonoscopic()));
+  EXPECT_EQ(
+      "map-to-mesh(equirectangular, "
       "2.5rad 3.14rad, "
       "matrix3d(1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1), "
       "monoscopic)",
@@ -375,7 +389,7 @@
                                 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
                       KeywordValue::GetStereoscopicLeftRight()));
   EXPECT_EQ(
-      "-cobalt-mtm(url(-.msh) 1920 2000000 url(a.msh) 640 5 url(b.msh), "
+      "map-to-mesh(url(-.msh) 1920 2000000 url(a.msh) 640 5 url(b.msh), "
       "28.5rad 3.14rad, "
       "matrix3d(1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1), "
       "stereoscopic-left-right)",
@@ -413,19 +427,19 @@
       new FilterFunctionListValue(filter_list.Pass()));
 
   EXPECT_EQ(
-      "-cobalt-mtm(url(-.msh), "
+      "map-to-mesh(url(-.msh), "
       "8.5rad 3.14rad, "
       "matrix3d(1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1), "
       "monoscopic) "
-      "-cobalt-mtm(url(world.msh), "
+      "map-to-mesh(url(world.msh), "
       "8.5rad 39rad, "
       "matrix3d(1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1), "
       "monoscopic) "
-      "-cobalt-mtm(url(stereoscopic-world.msh), "
+      "map-to-mesh(url(stereoscopic-world.msh), "
       "8.5rad 39rad, "
       "matrix3d(1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1), "
       "stereoscopic-left-right) "
-      "-cobalt-mtm(url(stereoscopic-top-bottom-world.msh), "
+      "map-to-mesh(url(stereoscopic-top-bottom-world.msh), "
       "8.5rad 39rad, "
       "matrix3d(1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1), "
       "stereoscopic-top-bottom)",
diff --git a/src/cobalt/dom/dom.gyp b/src/cobalt/dom/dom.gyp
index 71ab518..02775e4 100644
--- a/src/cobalt/dom/dom.gyp
+++ b/src/cobalt/dom/dom.gyp
@@ -179,6 +179,10 @@
         'memory_info.h',
         'mime_type_array.cc',
         'mime_type_array.h',
+        'mutation_observer.cc',
+        'mutation_observer.h',
+        'mutation_observer_init.h',
+        'mutation_record.h',
         'named_node_map.cc',
         'named_node_map.h',
         'navigator.cc',
diff --git a/src/cobalt/dom/dom_stat_tracker.h b/src/cobalt/dom/dom_stat_tracker.h
index 03ad867..80e1208 100644
--- a/src/cobalt/dom/dom_stat_tracker.h
+++ b/src/cobalt/dom/dom_stat_tracker.h
@@ -50,6 +50,8 @@
   void OnUpdateMatchingRules();
   void OnUpdateComputedStyle();
 
+  int total_html_elements() const { return total_html_elements_; }
+
   int html_elements_created_count() const {
     return html_elements_created_count_;
   }
diff --git a/src/cobalt/dom/global_stats.h b/src/cobalt/dom/global_stats.h
index 67ff3d3..0fc2b08 100644
--- a/src/cobalt/dom/global_stats.h
+++ b/src/cobalt/dom/global_stats.h
@@ -61,6 +61,9 @@
   void Remove(NodeList* object);
   void RemoveEventListener();
 
+  int GetNumEventListeners() const { return num_event_listeners_; }
+  int GetNumNodes() const { return num_nodes_; }
+
   void StartDispatchEvent();
   void StopDispatchEvent();
 
diff --git a/src/cobalt/dom/mutation_observer.cc b/src/cobalt/dom/mutation_observer.cc
new file mode 100644
index 0000000..3cfa7a0
--- /dev/null
+++ b/src/cobalt/dom/mutation_observer.cc
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2017 Google Inc. 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/dom/mutation_observer.h"
+
+namespace cobalt {
+namespace dom {
+// Abstract base class for a MutationCallback.
+class MutationObserver::CallbackInternal {
+ public:
+  virtual void RunCallback(const MutationRecordSequence& mutations,
+                           const scoped_refptr<MutationObserver>& observer) = 0;
+  virtual ~CallbackInternal() {}
+};
+
+namespace {
+// Implement the CallbackInternal interface for a JavaScript callback.
+class ScriptCallback : public MutationObserver::CallbackInternal {
+ public:
+  ScriptCallback(const MutationObserver::MutationCallbackArg& callback,
+                 MutationObserver* owner)
+      : callback_(owner, callback) {}
+  void RunCallback(const MutationObserver::MutationRecordSequence& mutations,
+                   const scoped_refptr<MutationObserver>& observer) OVERRIDE {
+    callback_.value().Run(mutations, observer);
+  }
+
+ private:
+  MutationObserver::MutationCallbackArg::Reference callback_;
+};
+
+// Implement the CallbackInternal interface for a native callback.
+class NativeCallback : public MutationObserver::CallbackInternal {
+ public:
+  explicit NativeCallback(
+      const MutationObserver::NativeMutationCallback& callback)
+      : callback_(callback) {}
+  void RunCallback(const MutationObserver::MutationRecordSequence& mutations,
+                   const scoped_refptr<MutationObserver>& observer) OVERRIDE {
+    callback_.Run(mutations, observer);
+  }
+
+ private:
+  MutationObserver::NativeMutationCallback callback_;
+};
+}  // namespace
+
+MutationObserver::MutationObserver(
+    const NativeMutationCallback& native_callback) {
+  callback_.reset(new NativeCallback(native_callback));
+}
+
+MutationObserver::MutationObserver(const MutationCallbackArg& callback) {
+  callback_.reset(new ScriptCallback(callback, this));
+}
+
+void MutationObserver::Observe(const scoped_refptr<Node>& target,
+                               const MutationObserverInit& options) {
+  UNREFERENCED_PARAMETER(target);
+  UNREFERENCED_PARAMETER(options);
+  NOTIMPLEMENTED();
+}
+
+void MutationObserver::Disconnect() { NOTIMPLEMENTED(); }
+
+MutationObserver::MutationRecordSequence MutationObserver::TakeRecords() {
+  NOTIMPLEMENTED();
+  return MutationRecordSequence();
+}
+
+}  // namespace dom
+}  // namespace cobalt
diff --git a/src/cobalt/dom/mutation_observer.h b/src/cobalt/dom/mutation_observer.h
new file mode 100644
index 0000000..a78030a
--- /dev/null
+++ b/src/cobalt/dom/mutation_observer.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2017 Google Inc. 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_DOM_MUTATION_OBSERVER_H_
+#define COBALT_DOM_MUTATION_OBSERVER_H_
+
+#include <vector>
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/dom/mutation_observer_init.h"
+#include "cobalt/dom/mutation_record.h"
+#include "cobalt/dom/node.h"
+#include "cobalt/script/callback_function.h"
+#include "cobalt/script/sequence.h"
+#include "cobalt/script/wrappable.h"
+
+namespace cobalt {
+namespace dom {
+
+// A MutationObserver object can be used to observe mutations to the tree of
+// nodes.
+// https://www.w3.org/TR/dom/#mutationobserver
+class MutationObserver : public script::Wrappable {
+ public:
+  typedef script::Sequence<scoped_refptr<MutationRecord> >
+      MutationRecordSequence;
+
+  typedef script::CallbackFunction<void(const MutationRecordSequence&,
+                                        const scoped_refptr<MutationObserver>&)>
+      MutationCallback;
+  typedef script::ScriptObject<MutationCallback> MutationCallbackArg;
+
+  typedef base::Callback<void(const MutationRecordSequence&,
+                              const scoped_refptr<MutationObserver>&)>
+      NativeMutationCallback;
+
+  // Not part of the spec. Support creating MutationObservers from native Cobalt
+  // code.
+  explicit MutationObserver(const NativeMutationCallback& native_callback);
+
+  // Web Api: MutationObserver
+  explicit MutationObserver(const MutationCallbackArg& callback);
+  void Observe(const scoped_refptr<Node>& target,
+               const MutationObserverInit& options);
+  void Disconnect();
+  MutationRecordSequence TakeRecords();
+
+  // Internal helper class to allow creation of a MutationObserver with either a
+  // native or script callback. Must be public so it can be inherited from in
+  // the .cc file.
+  class CallbackInternal;
+
+ private:
+  scoped_ptr<CallbackInternal> callback_;
+};
+}  // namespace dom
+}  // namespace cobalt
+#endif  // COBALT_DOM_MUTATION_OBSERVER_H_
diff --git a/src/cobalt/dom/mutation_observer_init.h b/src/cobalt/dom/mutation_observer_init.h
new file mode 100644
index 0000000..3e3c0ea
--- /dev/null
+++ b/src/cobalt/dom/mutation_observer_init.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2017 Google Inc. 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_DOM_MUTATION_OBSERVER_INIT_H_
+#define COBALT_DOM_MUTATION_OBSERVER_INIT_H_
+
+#include <string>
+#include <vector>
+
+#include "base/optional.h"
+
+namespace cobalt {
+namespace dom {
+
+// Represents the MutationObserverInit dictionary type that is used to
+// initialize the MutationObserver interface.
+// https://www.w3.org/TR/dom/#mutationobserver
+struct MutationObserverInit {
+  typedef std::vector<std::string> StringSequence;
+
+  // Default values as specified in the IDL.
+  MutationObserverInit() : child_list(false), subtree(false) {}
+
+  // Dictionary members:
+  bool child_list;
+  base::optional<bool> attributes;
+  base::optional<bool> character_data;
+  bool subtree;
+  base::optional<bool> attribute_old_value;
+  base::optional<bool> character_data_old_value;
+  base::optional<StringSequence> attribute_filter;
+};
+}  // namespace dom
+}  // namespace cobalt
+
+#endif  // COBALT_DOM_MUTATION_OBSERVER_INIT_H_
diff --git a/src/cobalt/dom/mutation_record.h b/src/cobalt/dom/mutation_record.h
new file mode 100644
index 0000000..cd43589
--- /dev/null
+++ b/src/cobalt/dom/mutation_record.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017 Google Inc. 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_DOM_MUTATION_RECORD_H_
+#define COBALT_DOM_MUTATION_RECORD_H_
+
+#include <string>
+
+#include "base/optional.h"
+#include "cobalt/dom/node.h"
+#include "cobalt/dom/node_list.h"
+#include "cobalt/script/wrappable.h"
+
+namespace cobalt {
+namespace dom {
+
+// MutationRecords are used with the MutationObserver interface to describe a
+// mutation on the documnet.
+// https://www.w3.org/TR/dom/#mutationrecord
+class MutationRecord : public script::Wrappable {
+ public:
+  // Web API: MutationRecord
+  std::string type() { return ""; }
+  scoped_refptr<dom::Node> target() { return NULL; }
+  scoped_refptr<dom::NodeList> added_nodes() { return NULL; }
+  scoped_refptr<dom::NodeList> removed_nodes() { return NULL; }
+  scoped_refptr<dom::Node> previous_sibling() { return NULL; }
+  scoped_refptr<dom::Node> next_sibling() { return NULL; }
+  base::optional<std::string> attribute_name() { return base::nullopt; }
+  base::optional<std::string> attribute_namespace() { return base::nullopt; }
+  base::optional<std::string> old_value() { return base::nullopt; }
+
+  DEFINE_WRAPPABLE_TYPE(MutationRecord);
+};
+}  // namespace dom
+}  // namespace cobalt
+#endif  // COBALT_DOM_MUTATION_RECORD_H_
diff --git a/src/cobalt/layout/layout_stat_tracker.h b/src/cobalt/layout/layout_stat_tracker.h
index 247aadd..195cce9 100644
--- a/src/cobalt/layout/layout_stat_tracker.h
+++ b/src/cobalt/layout/layout_stat_tracker.h
@@ -49,6 +49,8 @@
   void OnBoxCreated();
   void OnBoxDestroyed();
 
+  int total_boxes() const { return total_boxes_; }
+
   int boxes_created_count() const { return boxes_created_count_; }
   int boxes_destroyed_count() const { return boxes_destroyed_count_; }
 
diff --git a/src/cobalt/media/fetcher_buffered_data_source.cc b/src/cobalt/media/fetcher_buffered_data_source.cc
index fd8511b..3306431 100644
--- a/src/cobalt/media/fetcher_buffered_data_source.cc
+++ b/src/cobalt/media/fetcher_buffered_data_source.cc
@@ -59,7 +59,9 @@
   DCHECK(network_module);
 }
 
-FetcherBufferedDataSource::~FetcherBufferedDataSource() { DCHECK(!fetcher_); }
+FetcherBufferedDataSource::~FetcherBufferedDataSource() {
+  DCHECK(message_loop_->BelongsToCurrentThread());
+}
 
 void FetcherBufferedDataSource::Read(int64 position, int size, uint8* data,
                                      const ReadCB& read_cb) {
@@ -84,8 +86,15 @@
       base::ResetAndReturn(&pending_read_cb_).Run(0);
     }
     // From this moment on, any call to Read() should be treated as an error.
+    // Note that we cannot reset |fetcher_| here because of:
+    // 1. Fetcher has to be destroyed on the thread that it is created, however
+    //    Stop() is usually called from the pipeline thread where |fetcher_| is
+    //    created on the web thread.
+    // 2. We cannot post a task to the web thread to destroy |fetcher_| as the
+    //    web thread is blocked by WMPI::Destroy().
+    // Once error_occured_ is set to true, the fetcher callbacks return
+    // immediately so it is safe to destroy |fetcher_| inside the dtor.
     error_occured_ = true;
-    DestroyFetcher(fetcher_.Pass());
   }
 
   // We cannot post the callback using the MessageLoop as we share the
@@ -112,12 +121,11 @@
   DCHECK(message_loop_->BelongsToCurrentThread());
 
   base::AutoLock auto_lock(lock_);
-  if (fetcher_.get() != source) {
+
+  if (fetcher_.get() != source || error_occured_) {
     return;
   }
-  if (error_occured_) {
-    return;
-  }
+
   if (!source->GetStatus().is_success()) {
     // The error will be handled on OnURLFetchComplete()
     error_occured_ = true;
@@ -181,10 +189,8 @@
   }
   const uint8* data = reinterpret_cast<const uint8*>(download_data->data());
   base::AutoLock auto_lock(lock_);
-  if (fetcher_.get() != source) {
-    return;
-  }
-  if (error_occured_) {
+
+  if (fetcher_.get() != source || error_occured_) {
     return;
   }
 
@@ -193,7 +199,7 @@
   if (size == 0) {
     // The server side doesn't support range request.  Delete the fetcher to
     // stop the current request.
-    DestroyFetcher(fetcher_.Pass());
+    fetcher_.reset();
     ProcessPendingRead_Locked();
     return;
   }
@@ -245,9 +251,11 @@
   DCHECK(message_loop_->BelongsToCurrentThread());
 
   base::AutoLock auto_lock(lock_);
-  if (fetcher_.get() != source) {
+
+  if (fetcher_.get() != source || error_occured_) {
     return;
   }
+
   const net::URLRequestStatus& status = source->GetStatus();
   if (status.is_success()) {
     if (total_size_of_resource_ && last_request_size_ != 0) {
@@ -266,21 +274,13 @@
   ProcessPendingRead_Locked();
 }
 
-void FetcherBufferedDataSource::DestroyFetcher(
-    scoped_ptr<net::URLFetcher> fetcher) {
-  if (fetcher && !message_loop_->BelongsToCurrentThread()) {
-    message_loop_->PostTask(
-        FROM_HERE, base::Bind(&FetcherBufferedDataSource::DestroyFetcher, this,
-                              base::Passed(&fetcher)));
-    return;
-  }
-  // Do nothing as |fetcher| will destroy itself.
-}
-
 void FetcherBufferedDataSource::CreateNewFetcher() {
   DCHECK(message_loop_->BelongsToCurrentThread());
 
   base::AutoLock auto_lock(lock_);
+
+  fetcher_.reset();
+
   DCHECK_GE(static_cast<int64>(last_request_offset_), 0);
   DCHECK_GE(static_cast<int64>(last_request_size_), 0);
 
@@ -405,7 +405,6 @@
     last_request_offset_ = buffer_offset_ + buffer_.GetLength();
   }
 
-  DestroyFetcher(fetcher_.Pass());
   message_loop_->PostTask(
       FROM_HERE,
       base::Bind(&FetcherBufferedDataSource::CreateNewFetcher, this));
diff --git a/src/cobalt/media/fetcher_buffered_data_source.h b/src/cobalt/media/fetcher_buffered_data_source.h
index 9c64a7f..f8e56ae 100644
--- a/src/cobalt/media/fetcher_buffered_data_source.h
+++ b/src/cobalt/media/fetcher_buffered_data_source.h
@@ -81,8 +81,6 @@
                               scoped_ptr<std::string> download_data) OVERRIDE;
   void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
 
-  // This function can be posted to |message_loop_| to destroy a fetcher on it.
-  void DestroyFetcher(scoped_ptr<net::URLFetcher> fetcher);
   void CreateNewFetcher();
   void Read_Locked(uint64 position, size_t size, uint8* data,
                    const ReadCB& read_cb);
diff --git a/src/cobalt/media/media2.gyp b/src/cobalt/media/media2.gyp
index 7c53993..74ad424 100644
--- a/src/cobalt/media/media2.gyp
+++ b/src/cobalt/media/media2.gyp
@@ -181,7 +181,9 @@
       ],
     },
     {
-      'target_name': 'media_unittests',
+      # Rename 'media2_unittests' to 'media_unittests' once the original media
+      # is removed.
+      'target_name': 'media2_unittests',
       'type': '<(gtest_target_type)',
       'dependencies': [
         'media2',
@@ -291,14 +293,18 @@
       ],
     },
     {
-      'target_name': 'qt_faststart',
+      # Rename 'media2_qt_faststart' to 'qt_faststart' once the original media
+      # is removed.
+      'target_name': 'media2_qt_faststart',
       'type': 'executable',
       'sources': [
         'tools/qt_faststart/qt_faststart.c'
       ],
     },
     {
-      'target_name': 'seek_tester',
+      # Rename 'media2_seek_tester' to 'seek_tester' once the original media is
+      # removed.
+      'target_name': 'media2_seek_tester',
       'type': 'executable',
       'dependencies': [
         'media2',
@@ -309,7 +315,9 @@
       ],
     },
     {
-      'target_name': 'demuxer_bench',
+      # Rename 'media2_demuxer_bench' to 'demuxer_bench' once the original media
+      # is removed.
+      'target_name': 'media2_demuxer_bench',
       'type': 'executable',
       'dependencies': [
         'media2',
diff --git a/src/cobalt/media/sandbox/media2_sandbox.cc b/src/cobalt/media/sandbox/media2_sandbox.cc
new file mode 100644
index 0000000..585ba73
--- /dev/null
+++ b/src/cobalt/media/sandbox/media2_sandbox.cc
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2017 Google Inc. 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 "base/bind.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "cobalt/base/wrap_main.h"
+#include "cobalt/media/base/media_log.h"
+#include "cobalt/media/filters/chunk_demuxer.h"
+#include "starboard/event.h"
+
+namespace cobalt {
+namespace media {
+namespace sandbox {
+
+using ::media::ChunkDemuxer;
+using ::media::DecoderBuffer;
+using ::media::DemuxerStream;
+using ::media::MediaTracks;
+
+namespace {
+
+class DemuxerHostStub : public ::media::DemuxerHost {
+  void OnBufferedTimeRangesChanged(
+      const ::media::Ranges<base::TimeDelta>& ranges) OVERRIDE {}
+
+  void SetDuration(base::TimeDelta duration) OVERRIDE {}
+
+  void OnDemuxerError(::media::PipelineStatus error) OVERRIDE {}
+
+  void AddTextStream(::media::DemuxerStream* text_stream,
+                     const ::media::TextTrackConfig& config) OVERRIDE {}
+
+  void RemoveTextStream(::media::DemuxerStream* text_stream) OVERRIDE {}
+};
+
+void OnDemuxerOpen() {}
+
+void OnEncryptedMediaInitData(::media::EmeInitDataType type,
+                              const std::vector<uint8_t>& init_data) {}
+
+void OnInitSegmentReceived(scoped_ptr<MediaTracks> tracks) {}
+
+void OnDemuxerStatus(::media::PipelineStatus status) {
+  status = ::media::PIPELINE_OK;
+}
+
+std::string LoadFile(const std::string& file_name) {
+  FilePath file_path(file_name);
+  if (!file_path.IsAbsolute()) {
+    FilePath content_path;
+    PathService::Get(base::DIR_SOURCE_ROOT, &content_path);
+    DCHECK(content_path.IsAbsolute());
+    file_path = content_path.Append(file_path);
+  }
+
+  std::string content;
+  if (!file_util::ReadFileToString(file_path, &content)) {
+    LOG(ERROR) << "Failed to load file " << file_path.value();
+    return "";
+  }
+  return content;
+}
+
+const char* GetDemuxerStreamType(DemuxerStream* demuxer_stream) {
+  return demuxer_stream->type() == DemuxerStream::AUDIO ? "audio" : "video";
+}
+
+void ReadDemuxerStream(DemuxerStream* demuxer_stream);
+
+void OnDemuxerStreamRead(DemuxerStream* demuxer_stream,
+                         DemuxerStream::Status status,
+                         const scoped_refptr<DecoderBuffer>& decoder_buffer) {
+  if (!decoder_buffer->end_of_stream()) {
+    LOG(INFO) << "Reading " << GetDemuxerStreamType(demuxer_stream)
+              << " buffer at " << decoder_buffer->timestamp();
+    ReadDemuxerStream(demuxer_stream);
+  } else {
+    LOG(INFO) << "Received " << GetDemuxerStreamType(demuxer_stream) << " EOS";
+  }
+}
+
+void ReadDemuxerStream(DemuxerStream* demuxer_stream) {
+  DCHECK(demuxer_stream);
+  demuxer_stream->Read(
+      base::Bind(OnDemuxerStreamRead, base::Unretained(demuxer_stream)));
+}
+
+}  // namespace
+
+int SandboxMain(int argc, char** argv) {
+  if (argc != 3) {
+    // Path should be in the form of
+    //     "cobalt/browser/testdata/media-element-demo/dash-video-240p.mp4".
+    LOG(ERROR) << "Usage: " << argv[0] << " <audio_path> <video_path>";
+    return 1;
+  }
+
+  MessageLoop message_loop;
+
+  DemuxerHostStub demuxer_host;
+  scoped_ptr<ChunkDemuxer> demuxer(new ChunkDemuxer(
+      base::Bind(OnDemuxerOpen), base::Bind(OnEncryptedMediaInitData),
+      new ::media::MediaLog, false));
+  demuxer->Initialize(&demuxer_host, base::Bind(OnDemuxerStatus), false);
+
+  ChunkDemuxer::Status status =
+      demuxer->AddId("audio", "audio/mp4", "mp4a.40.2");
+  DCHECK_EQ(status, ChunkDemuxer::kOk);
+  status = demuxer->AddId("video", "video/mp4", "avc1.640028");
+  DCHECK_EQ(status, ChunkDemuxer::kOk);
+  base::TimeDelta timestamp_offset;
+
+  std::string audio_content = LoadFile(argv[1]);
+  std::string video_content = LoadFile(argv[2]);
+  DCHECK(!audio_content.empty());
+  DCHECK(!video_content.empty());
+
+  demuxer->SetTracksWatcher("audio", base::Bind(OnInitSegmentReceived));
+  demuxer->SetTracksWatcher("video", base::Bind(OnInitSegmentReceived));
+  demuxer->AppendData("audio", reinterpret_cast<uint8*>(&audio_content[0]),
+                      audio_content.size(), base::TimeDelta(),
+                      base::TimeDelta::Max(), &timestamp_offset);
+  demuxer->AppendData("video", reinterpret_cast<uint8*>(&video_content[0]),
+                      video_content.size(), base::TimeDelta(),
+                      base::TimeDelta::Max(), &timestamp_offset);
+  demuxer->MarkEndOfStream(::media::PIPELINE_OK);
+
+  DemuxerStream* audio_stream = demuxer->GetStream(DemuxerStream::AUDIO);
+  DemuxerStream* video_stream = demuxer->GetStream(DemuxerStream::VIDEO);
+
+  ReadDemuxerStream(audio_stream);
+  ReadDemuxerStream(video_stream);
+
+  message_loop.RunUntilIdle();
+
+  demuxer->Stop();
+
+  return 0;
+}
+
+}  // namespace sandbox
+}  // namespace media
+}  // namespace cobalt
+
+COBALT_WRAP_SIMPLE_MAIN(cobalt::media::sandbox::SandboxMain);
diff --git a/src/cobalt/media/sandbox/sandbox.gyp b/src/cobalt/media/sandbox/sandbox.gyp
index 3290d35..1e1783e 100644
--- a/src/cobalt/media/sandbox/sandbox.gyp
+++ b/src/cobalt/media/sandbox/sandbox.gyp
@@ -99,6 +99,22 @@
     },
   ],
   'conditions': [
+    ['OS=="starboard" and sb_media_platform == "starboard"', {
+      'targets': [
+        {
+          'target_name': 'media2_sandbox',
+          'type': '<(final_executable_type)',
+          'sources': [
+            'media2_sandbox.cc',
+          ],
+          'dependencies': [
+            '<(DEPTH)/cobalt/base/base.gyp:base',
+            '<(DEPTH)/cobalt/math/math.gyp:math',
+            '<(DEPTH)/cobalt/media/media2.gyp:media2',
+          ],
+        },
+      ],
+    }],
     ['OS == "starboard" and has_zzuf == "True"', {
       'targets': [
         # This target will build a sandbox application that allows for fuzzing
diff --git a/src/cobalt/renderer/pipeline.cc b/src/cobalt/renderer/pipeline.cc
index e19c59c..568b2b2 100644
--- a/src/cobalt/renderer/pipeline.cc
+++ b/src/cobalt/renderer/pipeline.cc
@@ -80,9 +80,11 @@
           submit_even_if_render_tree_is_unchanged),
       last_render_animations_active_(false),
       rasterize_periodic_timer_("Renderer.Rasterize.Duration",
-                                kRasterizePeriodicTimerEntriesPerUpdate),
+                                kRasterizePeriodicTimerEntriesPerUpdate,
+                                false /*enable_entry_list_c_val*/),
       rasterize_animations_timer_("Renderer.Rasterize.Animations",
-                                  kRasterizeAnimationsTimerMaxEntries),
+                                  kRasterizeAnimationsTimerMaxEntries,
+                                  true /*enable_entry_list_c_val*/),
       has_active_animations_c_val_(
           "Renderer.HasActiveAnimations", false,
           "Is non-zero if the current render tree has active animations.")
diff --git a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
index 5e64ba7..b76eee8 100644
--- a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
+++ b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
@@ -306,6 +306,15 @@
   eval_enabled_ = true;
 }
 
+void MozjsGlobalEnvironment::DisableJit() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  JS::RuntimeOptionsRef(context_)
+      .setBaseline(false)
+      .setIon(false)
+      .setAsmJS(false)
+      .setNativeRegExp(false);
+}
+
 void MozjsGlobalEnvironment::SetReportEvalCallback(
     const base::Closure& report_eval) {
   DCHECK(thread_checker_.CalledOnValidThread());
diff --git a/src/cobalt/script/mozjs-45/mozjs_global_environment.h b/src/cobalt/script/mozjs-45/mozjs_global_environment.h
index dcee058..ce17451 100644
--- a/src/cobalt/script/mozjs-45/mozjs_global_environment.h
+++ b/src/cobalt/script/mozjs-45/mozjs_global_environment.h
@@ -71,6 +71,8 @@
 
   void EnableEval() OVERRIDE;
 
+  void DisableJit() OVERRIDE;
+
   void SetReportEvalCallback(const base::Closure& report_eval) OVERRIDE;
 
   void Bind(const std::string& identifier,
diff --git a/src/cobalt/speech/microphone_fake.cc b/src/cobalt/speech/microphone_fake.cc
index 1ac01bd..31d372b 100644
--- a/src/cobalt/speech/microphone_fake.cc
+++ b/src/cobalt/speech/microphone_fake.cc
@@ -25,6 +25,7 @@
 #include "base/rand_util.h"
 #include "cobalt/audio/audio_file_reader.h"
 #include "starboard/file.h"
+#include "starboard/log.h"
 #include "starboard/memory.h"
 #include "starboard/time.h"
 
diff --git a/src/cobalt/speech/speech_recognition_manager.cc b/src/cobalt/speech/speech_recognition_manager.cc
index 4eab7de..5e40ee2 100644
--- a/src/cobalt/speech/speech_recognition_manager.cc
+++ b/src/cobalt/speech/speech_recognition_manager.cc
@@ -111,7 +111,7 @@
       microphone_creator));
 }
 
-SpeechRecognitionManager::~SpeechRecognitionManager() { Stop(); }
+SpeechRecognitionManager::~SpeechRecognitionManager() { Stop(false); }
 
 void SpeechRecognitionManager::Start(const SpeechRecognitionConfig& config,
                                      script::ExceptionState* exception_state) {
@@ -131,7 +131,7 @@
   state_ = kStarted;
 }
 
-void SpeechRecognitionManager::Stop() {
+void SpeechRecognitionManager::Stop(bool run_callback) {
   DCHECK(main_message_loop_->BelongsToCurrentThread());
 
   // If the stop method is called on an object which is already stopped or being
@@ -144,7 +144,9 @@
   microphone_manager_->Close();
   recognizer_->Stop();
   state_ = kStopped;
-  event_callback_.Run(new dom::Event(base::Tokens::soundend()));
+  if (run_callback) {
+    event_callback_.Run(new dom::Event(base::Tokens::soundend()));
+  }
 }
 
 void SpeechRecognitionManager::Abort() {
diff --git a/src/cobalt/speech/speech_recognition_manager.h b/src/cobalt/speech/speech_recognition_manager.h
index 2d622dd..323c389 100644
--- a/src/cobalt/speech/speech_recognition_manager.h
+++ b/src/cobalt/speech/speech_recognition_manager.h
@@ -54,7 +54,7 @@
   // managed by their own class.
   void Start(const SpeechRecognitionConfig& config,
              script::ExceptionState* exception_state);
-  void Stop();
+  void Stop(bool run_callback = true);
   void Abort();
 
  private:
diff --git a/src/cobalt/test/empty_document.h b/src/cobalt/test/empty_document.h
new file mode 100644
index 0000000..9fae1b9
--- /dev/null
+++ b/src/cobalt/test/empty_document.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017 Google Inc. 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_TEST_EMPTY_DOCUMENT_H_
+#define COBALT_TEST_EMPTY_DOCUMENT_H_
+
+#include "cobalt/css_parser/parser.h"
+#include "cobalt/dom/document.h"
+#include "cobalt/dom/dom_stat_tracker.h"
+#include "cobalt/dom/html_element_context.h"
+
+namespace cobalt {
+namespace test {
+
+// This is a helper class for tests that want an empty dom::Document, which can
+// be used to create other dom::Element instances.
+class EmptyDocument {
+ public:
+  EmptyDocument()
+      : css_parser_(css_parser::Parser::Create()),
+        dom_stat_tracker_(new dom::DomStatTracker("EmptyDocument")),
+        html_element_context_(NULL, css_parser_.get(), NULL, NULL, NULL, NULL,
+                              NULL, NULL, NULL, NULL, NULL,
+                              dom_stat_tracker_.get(), ""),
+        document_(new dom::Document(&html_element_context_)) {}
+
+  dom::Document* document() { return document_.get(); }
+
+ private:
+  scoped_ptr<css_parser::Parser> css_parser_;
+  scoped_ptr<dom::DomStatTracker> dom_stat_tracker_;
+  dom::HTMLElementContext html_element_context_;
+  scoped_refptr<dom::Document> document_;
+};
+}  // namespace test
+}  // namespace cobalt
+#endif  // COBALT_TEST_EMPTY_DOCUMENT_H_
diff --git a/src/cobalt/tools/binary_to_callgraph.sh b/src/cobalt/tools/binary_to_callgraph.sh
new file mode 100755
index 0000000..375dc7e
--- /dev/null
+++ b/src/cobalt/tools/binary_to_callgraph.sh
@@ -0,0 +1,65 @@
+#!/bin/bash
+#
+# Copyright 2017 Google Inc. 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.
+#
+# Create a callgraph from an x86 or x86-64 binary.
+#
+# Example Usage:
+#     cobalt/tools/binary_to_callgraph.sh out/linux-x64x11_debug/cobalt > callgraph.txt
+#
+# TODO: It'd be cool to include "...->PostTask(...)" stuff in here too.
+# TODO: It'd be cool to output to a sqlite database instead of text, and then
+# have a command line program to search through it.
+# TODO: It'd be cool to support ARM and MIPS too.
+
+set -e
+
+objdump -d "$1" |
+python3 -c '
+import collections
+import re
+import sys
+
+caller = None
+callee = None
+graph = collections.defaultdict(set)
+for line in sys.stdin:
+  m = re.match(r"\w+ <(.*?)>:", line)
+  if m:
+    caller = m.group(1)
+    continue
+
+  m = re.match(".*callq?.*?<(.*?)>", line)
+  if m:
+    callee = m.group(1)
+    graph[caller].add(callee)
+
+for key, value in graph.items():
+  for item in value:
+    print(key)
+    print(item)
+' | c++filt |
+python3 -c '
+import sys
+
+buffer = []
+for line in sys.stdin:
+  buffer.append(line.rstrip())
+  if len(buffer) == 2:
+    print("{} @@ {}".format(*buffer))
+    buffer = []
+'
+
+
diff --git a/src/cobalt/webdriver_benchmarks/c_val_names.py b/src/cobalt/webdriver_benchmarks/c_val_names.py
index d1fdfa0..9e2a1aa 100644
--- a/src/cobalt/webdriver_benchmarks/c_val_names.py
+++ b/src/cobalt/webdriver_benchmarks/c_val_names.py
@@ -4,140 +4,34 @@
 from __future__ import division
 from __future__ import print_function
 
-EVENT_COUNT_STRING = "Event.Count.MainWebModule"
-EVENT_DURATION_STRING = "Event.Duration.MainWebModule"
-
 
 def count_dom_active_dispatch_events():
   return "Count.DOM.ActiveDispatchEvents"
 
 
-def count_dom_event_listeners():
-  return "Count.DOM.EventListeners"
-
-
-def count_dom_nodes():
-  return "Count.DOM.Nodes"
-
-
-def count_dom_html_elements():
-  return "Count.MainWebModule.DOM.HtmlElement"
-
-
 def count_image_cache_loading_resources():
   return "Count.MainWebModule.ImageCache.LoadingResources"
 
 
-def count_image_cache_pending_callbacks():
-  return "Count.MainWebModule.ImageCache.PendingCallbacks"
-
-
-def count_layout_boxes():
-  return "Count.MainWebModule.Layout.Box"
-
-
 def count_font_files_loaded():
   return "Count.Font.FilesLoaded"
 
 
-def count_remote_typeface_cache_loading_resources():
-  return "Count.MainWebModule.RemoteTypefaceCache.LoadingResources"
-
-
-def count_remote_typeface_cache_pending_callbacks():
-  return "Count.MainWebModule.RemoteTypefaceCache.PendingCallbacks"
-
-
-def event_produced_render_tree(event_type):
-  return "Event.MainWebModule.{}.ProducedRenderTree".format(event_type)
-
-
-def event_count_dom_html_elements_created(event_type):
-  return "{}.{}.DOM.HtmlElement.Created".format(EVENT_COUNT_STRING, event_type)
-
-
-def event_count_dom_html_elements_destroyed(event_type):
-  return "{}.{}.DOM.HtmlElement.Destroyed".format(EVENT_COUNT_STRING,
-                                                  event_type)
-
-
-def event_count_dom_update_matching_rules(event_type):
-  return "{}.{}.DOM.HtmlElement.UpdateMatchingRules".format(EVENT_COUNT_STRING,
-                                                            event_type)
-
-
-def event_count_dom_update_computed_style(event_type):
-  return "{}.{}.DOM.HtmlElement.UpdateComputedStyle".format(EVENT_COUNT_STRING,
-                                                            event_type)
-
-
-def event_count_layout_boxes_created(event_type):
-  return "{}.{}.Layout.Box.Created".format(EVENT_COUNT_STRING, event_type)
-
-
-def event_count_layout_boxes_destroyed(event_type):
-  return "{}.{}.Layout.Box.Destroyed".format(EVENT_COUNT_STRING, event_type)
-
-
-def event_duration_total(event_type):
-  return "{}.{}".format(EVENT_DURATION_STRING, event_type)
-
-
-def event_duration_dom_inject_event(event_type):
-  return "{}.{}.DOM.InjectEvent".format(EVENT_DURATION_STRING, event_type)
-
-
-def event_duration_dom_update_computed_style(event_type):
-  return "{}.{}.DOM.UpdateComputedStyle".format(EVENT_DURATION_STRING,
-                                                event_type)
-
-
 def event_duration_dom_video_start_delay():
-  return "{}.DOM.VideoStartDelay".format(EVENT_DURATION_STRING)
+  return "Event.Duration.MainWebModule.DOM.VideoStartDelay"
 
 
-def event_duration_layout_box_tree(event_type):
-  return "{}.{}.Layout.BoxTree".format(EVENT_DURATION_STRING, event_type)
-
-
-def event_duration_layout_box_tree_box_generation(event_type):
-  return "{}.{}.Layout.BoxTree.BoxGeneration".format(EVENT_DURATION_STRING,
-                                                     event_type)
-
-
-def event_duration_layout_box_tree_update_used_sizes(event_type):
-  return "{}.{}.Layout.BoxTree.UpdateUsedSizes".format(EVENT_DURATION_STRING,
-                                                       event_type)
-
-
-def event_duration_layout_render_and_animate(event_type):
-  return "{}.{}.Layout.RenderAndAnimate".format(EVENT_DURATION_STRING,
-                                                event_type)
+def event_value_dictionary(event_type):
+  return "Event.MainWebModule.{}.ValueDictionary".format(event_type)
 
 
 def layout_is_dirty():
   return "MainWebModule.Layout.IsDirty"
 
 
+def rasterize_animations_entry_list():
+  return "Renderer.Rasterize.Animations.EntryList"
+
+
 def renderer_has_active_animations():
   return "Renderer.HasActiveAnimations"
-
-
-def rasterize_animations_average():
-  return "Renderer.Rasterize.Animations.Avg"
-
-
-def rasterize_animations_percentile_25():
-  return "Renderer.Rasterize.Animations.Pct.25th"
-
-
-def rasterize_animations_percentile_50():
-  return "Renderer.Rasterize.Animations.Pct.50th"
-
-
-def rasterize_animations_percentile_75():
-  return "Renderer.Rasterize.Animations.Pct.75th"
-
-
-def rasterize_animations_percentile_95():
-  return "Renderer.Rasterize.Animations.Pct.95th"
diff --git a/src/cobalt/webdriver_benchmarks/tests/time_to_shelf.py b/src/cobalt/webdriver_benchmarks/tests/time_to_shelf.py
index e829a80..9551d9d 100755
--- a/src/cobalt/webdriver_benchmarks/tests/time_to_shelf.py
+++ b/src/cobalt/webdriver_benchmarks/tests/time_to_shelf.py
@@ -19,6 +19,7 @@
 from __future__ import division
 from __future__ import print_function
 
+import datetime
 import os
 import sys
 
@@ -36,20 +37,23 @@
     """This test tries to measure the startup time for the YouTube TV page.
 
     Specifically, this test uses the Cobalt CVal Cobalt.Lifetime, which gets
-    updated ~60Hz on a best effort basis.  This value is in microseconds.
+    updated ~60Hz on a best effort basis and is in microseconds, to determine
+    "timeToShelfBlankStartupTimeUs" and uses Python's datetime module to
+    determine "timeToShelfTestTimeShelfDisplayMedianUs".
 
     Note: t0 is defined after Cobalt starts up, but has not navigated to a page.
     If that true startup time metric is desired, perhaps a separate should be
     used.
     """
     metrics_array = []
-    blank_startup_time_microseconds = self.get_int_cval('Cobalt.Lifetime')
+    blank_startup_time_microseconds = self.get_cval('Cobalt.Lifetime')
     for _ in range(10):
-      t0 = self.get_int_cval('Cobalt.Lifetime')
+      t0 = datetime.datetime.now()
       self.load_tv()
       self.wait_for_processing_complete_after_focused_shelf()
-      t1 = self.get_int_cval('Cobalt.Lifetime')
-      startup_time_microseconds = t1 - t0
+      t1 = datetime.datetime.now()
+      delta = t1 - t0
+      startup_time_microseconds = delta.seconds * 1000000 + delta.microseconds
       metrics_array.append(startup_time_microseconds)
 
     tv_testcase_util.record_test_result_median(
@@ -57,5 +61,6 @@
     tv_testcase_util.record_test_result('timeToShelfBlankStartupTimeUs',
                                         blank_startup_time_microseconds)
 
+
 if __name__ == '__main__':
   tv_testcase.main()
diff --git a/src/cobalt/webdriver_benchmarks/tv_testcase.py b/src/cobalt/webdriver_benchmarks/tv_testcase.py
index 19c70ab..796c860 100644
--- a/src/cobalt/webdriver_benchmarks/tv_testcase.py
+++ b/src/cobalt/webdriver_benchmarks/tv_testcase.py
@@ -4,6 +4,7 @@
 from __future__ import division
 from __future__ import print_function
 
+import json
 import logging
 import os
 import sys
@@ -75,33 +76,19 @@
     return tv_testcase_runner.GetWebDriver()
 
   def get_cval(self, cval_name):
-    """Returns value of a cval.
+    """Returns the Python object represented by a JSON cval string.
 
     Args:
       cval_name: Name of the cval.
     Returns:
-      Value of the cval.
+      Python object represented by the JSON cval string
     """
     javascript_code = "return h5vcc.cVal.getValue('{}')".format(cval_name)
-    return self.get_webdriver().execute_script(javascript_code)
-
-  def get_int_cval(self, cval_name):
-    """Returns int value of a cval.
-
-    The cval value must be an integer, if it is a float, then this function will
-    throw a ValueError.
-
-    Args:
-      cval_name: Name of the cval.
-    Returns:
-      Value of the cval.
-    Raises:
-      ValueError if the cval is cannot be converted to int.
-    """
-    answer = self.get_cval(cval_name)
-    if answer is None:
-      return answer
-    return int(answer)
+    json_result = self.get_webdriver().execute_script(javascript_code)
+    if json_result is None:
+      return None
+    else:
+      return json.loads(json_result)
 
   def goto(self, path, query_params=None):
     """Goes to a path off of BASE_URL.
@@ -267,17 +254,11 @@
 
   def is_processing(self, check_animations):
     """Checks to see if Cobalt is currently processing."""
-    return (
-        self.get_int_cval(c_val_names.count_dom_active_dispatch_events()) or
-        self.get_int_cval(c_val_names.layout_is_dirty()) or
-        (check_animations and
-         self.get_int_cval(c_val_names.renderer_has_active_animations())) or
-        self.get_int_cval(c_val_names.count_image_cache_loading_resources()) or
-        self.get_int_cval(c_val_names.count_image_cache_pending_callbacks()) or
-        self.get_int_cval(
-            c_val_names.count_remote_typeface_cache_loading_resources()) or
-        self.get_int_cval(
-            c_val_names.count_remote_typeface_cache_pending_callbacks()))
+    return (self.get_cval(c_val_names.count_dom_active_dispatch_events()) or
+            self.get_cval(c_val_names.layout_is_dirty()) or
+            (check_animations and
+             self.get_cval(c_val_names.renderer_has_active_animations())) or
+            self.get_cval(c_val_names.count_image_cache_loading_resources()))
 
   def wait_for_media_element_playing(self):
     """Waits for a video to begin playing.
@@ -287,8 +268,8 @@
       required time.
     """
     start_time = time.time()
-    while self.get_int_cval(c_val_names.event_duration_dom_video_start_delay(
-    )) == 0:
+    while self.get_cval(
+        c_val_names.event_duration_dom_video_start_delay()) == 0:
       if time.time() - start_time > MEDIA_TIMEOUT_SECONDS:
         raise TvTestCase.MediaTimeoutException()
 
diff --git a/src/cobalt/webdriver_benchmarks/tv_testcase_c_val_recorder.py b/src/cobalt/webdriver_benchmarks/tv_testcase_c_val_recorder.py
deleted file mode 100644
index 4ab6a10..0000000
--- a/src/cobalt/webdriver_benchmarks/tv_testcase_c_val_recorder.py
+++ /dev/null
@@ -1,97 +0,0 @@
-#!/usr/bin/python2
-""""Records stats on the collected values of a CVal during a benchmark test."""
-
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
-import tv_testcase_util
-
-
-class CValRecorderBase(object):
-  """"Records stats on the collected values of a CVal during a benchmark test.
-
-  Handles collecting values for a specific CVal during a benchmark test and
-  records statistics on those values when the test ends. The base class does not
-  record any statistics and subclasses determine which statistics to record.
-  """
-
-  def __init__(self, test, c_val_name, record_name):
-    """Initializes the event benchmark recorder.
-
-    Args:
-      test: specific benchmark test being run
-      c_val_name: the lookup name of the CVal
-      record_name: the name to use when recording CVal stats for the test
-    """
-    self.test = test
-    self.c_val_name = c_val_name
-    self.record_name = record_name
-    self.values = []
-
-  def collect_current_value(self):
-    self.values.append(self.test.get_int_cval(self.c_val_name))
-
-  def on_end_test(self):
-    """Handles logic related to the end of the benchmark test."""
-
-    # Only record the test results when values have been collected.
-    if self.values:
-      self._record_test_results()
-
-  def _record_test_results(self):
-    """Records test results for the collected CVal values.
-
-    This function is expected to be overridden by subclasses, which will use
-    it to record select statistics for the test results.
-    """
-    pass
-
-  def _record_test_result_mean(self):
-    tv_testcase_util.record_test_result_mean("{}Mean".format(self.record_name),
-                                             self.values)
-
-  def _record_test_result_percentile(self, percentile):
-    tv_testcase_util.record_test_result_percentile("{}Pct{}".format(
-        self.record_name, percentile), self.values, percentile)
-
-
-class FullCValRecorder(CValRecorderBase):
-  """"Subclass that records full statistics of the collected values.
-
-  This subclass records the mean, 25th, 50th, 75th, and 95th percentiles of the
-  collected values when the test ends.
-  """
-
-  def _record_test_results(self):
-    self._record_test_result_mean()
-    self._record_test_result_percentile(25)
-    self._record_test_result_percentile(50)
-    self._record_test_result_percentile(75)
-    self._record_test_result_percentile(95)
-
-
-class MeanCValRecorder(CValRecorderBase):
-  """"Subclass that records the mean of the collected values.
-
-  This subclass only records the mean of the collected values when the test
-  ends.
-  """
-
-  def _record_test_results(self):
-    self._record_test_result_mean()
-
-
-class PercentileCValRecorder(CValRecorderBase):
-  """"Subclass that records a percentile of the collected values.
-
-  This subclass records only records a single percentile of the collected values
-  when the test ends.
-  """
-
-  def __init__(self, test, c_val_name, output_name, percentile_to_record):
-    super(PercentileCValRecorder, self).__init__(test, c_val_name, output_name)
-    self.percentile_to_record = percentile_to_record
-
-  def _record_test_results(self):
-    self._record_test_result_percentile(self.percentile_to_record)
diff --git a/src/cobalt/webdriver_benchmarks/tv_testcase_event_recorder.py b/src/cobalt/webdriver_benchmarks/tv_testcase_event_recorder.py
index 3a33150..50acc3c 100644
--- a/src/cobalt/webdriver_benchmarks/tv_testcase_event_recorder.py
+++ b/src/cobalt/webdriver_benchmarks/tv_testcase_event_recorder.py
@@ -6,7 +6,7 @@
 from __future__ import print_function
 
 import c_val_names
-import tv_testcase_c_val_recorder
+import tv_testcase_value_recorder
 
 
 class EventRecorderOptions(object):
@@ -32,9 +32,9 @@
 class EventRecorder(object):
   """Records stats on an event injected by a benchmark test.
 
-  Handles collecting CVal event data over all of the instances of a specific
-  event injected by a benchmark test and records statistics on the data when the
-  test ends. The specific event types that can be injected are listed within
+  Handles collecting event data over all of the instances of a specific event
+  injected by a benchmark test and records statistics on the data when the test
+  ends. The specific event types that can be injected are listed within
   tv_testcase_util.py.
 
   Both rasterize animations and video start delay data can potentially be
@@ -59,181 +59,111 @@
     self.event_type = options.event_type
     self.skip_font_file_load_events = options.skip_font_file_load_events
 
+    if self.skip_font_file_load_events:
+      self.font_files_loaded_count = self.test.get_cval(
+          c_val_names.count_font_files_loaded())
+
     self.render_tree_failure_count = 0
     self.font_file_load_skip_count = 0
 
-    self.c_val_recorders = []
+    # Each entry in the list contains a tuple with a key and value recorder.
+    self.value_dictionary_recorders = []
 
-    # Count cval recorders
-    self.c_val_recorders.append(
-        tv_testcase_c_val_recorder.FullCValRecorder(
-            self.test,
-            c_val_names.count_dom_event_listeners(),
-            self.event_name + "CntDomEventListeners"))
-    self.c_val_recorders.append(
-        tv_testcase_c_val_recorder.FullCValRecorder(
-            self.test,
-            c_val_names.count_dom_nodes(),
-            self.event_name + "CntDomNodes"))
-    self.c_val_recorders.append(
-        tv_testcase_c_val_recorder.FullCValRecorder(
-            self.test,
-            c_val_names.count_dom_html_elements(),
-            self.event_name + "CntDomHtmlElements"))
-    self.c_val_recorders.append(
-        tv_testcase_c_val_recorder.FullCValRecorder(
-            self.test,
-            c_val_names.event_count_dom_html_elements_created(self.event_type),
-            self.event_name + "CntDomHtmlElementsCreated"))
-    self.c_val_recorders.append(
-        tv_testcase_c_val_recorder.FullCValRecorder(
-            self.test,
-            c_val_names.event_count_dom_html_elements_destroyed(
-                self.event_type),
-            self.event_name + "CntDomHtmlElementsDestroyed"))
-    self.c_val_recorders.append(
-        tv_testcase_c_val_recorder.FullCValRecorder(
-            self.test,
-            c_val_names.event_count_dom_update_matching_rules(self.event_type),
-            self.event_name + "CntDomUpdateMatchingRuleCalls"))
-    self.c_val_recorders.append(
-        tv_testcase_c_val_recorder.FullCValRecorder(
-            self.test,
-            c_val_names.event_count_dom_update_computed_style(self.event_type),
-            self.event_name + "CntDomUpdateComputedStyleCalls"))
-    self.c_val_recorders.append(
-        tv_testcase_c_val_recorder.FullCValRecorder(
-            self.test,
-            c_val_names.count_layout_boxes(),
-            self.event_name + "CntLayoutBoxes"))
-    self.c_val_recorders.append(
-        tv_testcase_c_val_recorder.FullCValRecorder(
-            self.test,
-            c_val_names.event_count_layout_boxes_created(self.event_type),
-            self.event_name + "CntLayoutBoxesCreated"))
-    self.c_val_recorders.append(
-        tv_testcase_c_val_recorder.FullCValRecorder(
-            self.test,
-            c_val_names.event_count_layout_boxes_destroyed(self.event_type),
-            self.event_name + "CntLayoutBoxesDestroyed"))
+    self.animations_recorder = None
+    self.video_delay_recorder = None
 
-    # Duration cval recorders
-    self.c_val_recorders.append(
-        tv_testcase_c_val_recorder.FullCValRecorder(
-            self.test,
-            c_val_names.event_duration_total(self.event_type), self.event_name +
-            "DurTotalUs"))
-    self.c_val_recorders.append(
-        tv_testcase_c_val_recorder.FullCValRecorder(
-            self.test,
-            c_val_names.event_duration_dom_inject_event(self.event_type),
-            self.event_name + "DurDomInjectEventUs"))
-    self.c_val_recorders.append(
-        tv_testcase_c_val_recorder.FullCValRecorder(
-            self.test,
-            c_val_names.event_duration_dom_update_computed_style(
-                self.event_type),
-            self.event_name + "DurDomUpdateComputedStyleUs"))
-    self.c_val_recorders.append(
-        tv_testcase_c_val_recorder.FullCValRecorder(
-            self.test,
-            c_val_names.event_duration_layout_box_tree(self.event_type),
-            self.event_name + "DurLayoutBoxTreeUs"))
-    self.c_val_recorders.append(
-        tv_testcase_c_val_recorder.FullCValRecorder(
-            self.test,
-            c_val_names.event_duration_layout_box_tree_box_generation(
-                self.event_type), self.event_name +
-            "DurLayoutBoxTreeBoxGenerationUs"))
-    self.c_val_recorders.append(
-        tv_testcase_c_val_recorder.FullCValRecorder(
-            self.test,
-            c_val_names.event_duration_layout_box_tree_update_used_sizes(
-                self.event_type), self.event_name +
-            "DurLayoutBoxTreeUpdateUsedSizesUs"))
-    self.c_val_recorders.append(
-        tv_testcase_c_val_recorder.FullCValRecorder(
-            self.test,
-            c_val_names.event_duration_layout_render_and_animate(
-                self.event_type), self.event_name +
-            "DurLayoutRenderAndAnimateUs"))
+    # Count recorders
+    self._add_value_dictionary_recorder("CntDomEventListeners")
+    self._add_value_dictionary_recorder("CntDomNodes")
+    self._add_value_dictionary_recorder("CntDomHtmlElements")
+    self._add_value_dictionary_recorder("CntDomHtmlElementsCreated")
+    self._add_value_dictionary_recorder("CntDomHtmlElementsDestroyed")
+    self._add_value_dictionary_recorder("CntDomUpdateMatchingRuleCalls")
+    self._add_value_dictionary_recorder("CntDomUpdateComputedStyleCalls")
+    self._add_value_dictionary_recorder("CntLayoutBoxes")
+    self._add_value_dictionary_recorder("CntLayoutBoxesCreated")
+    self._add_value_dictionary_recorder("CntLayoutBoxesDestroyed")
 
-    # Optional rasterize animations cval recorders
+    # Duration recorders
+    self._add_value_dictionary_recorder("DurTotalUs")
+    self._add_value_dictionary_recorder("DurDomInjectEventUs")
+    self._add_value_dictionary_recorder("DurDomUpdateComputedStyleUs")
+    self._add_value_dictionary_recorder("DurLayoutBoxTreeUs")
+    self._add_value_dictionary_recorder("DurLayoutBoxTreeBoxGenerationUs")
+    self._add_value_dictionary_recorder("DurLayoutBoxTreeUpdateUsedSizesUs")
+    self._add_value_dictionary_recorder("DurLayoutRenderAndAnimateUs")
+
+    # Optional rasterize animations recorders
     if options.record_rasterize_animations:
-      self.c_val_recorders.append(
-          tv_testcase_c_val_recorder.MeanCValRecorder(
-              self.test,
-              c_val_names.rasterize_animations_average(), self.event_name +
-              "DurRasterizeAnimationsUs"))
-      self.c_val_recorders.append(
-          tv_testcase_c_val_recorder.PercentileCValRecorder(
-              self.test,
-              c_val_names.rasterize_animations_percentile_25(), self.event_name
-              + "DurRasterizeAnimationsUs", 25))
-      self.c_val_recorders.append(
-          tv_testcase_c_val_recorder.PercentileCValRecorder(
-              self.test,
-              c_val_names.rasterize_animations_percentile_50(), self.event_name
-              + "DurRasterizeAnimationsUs", 50))
-      self.c_val_recorders.append(
-          tv_testcase_c_val_recorder.PercentileCValRecorder(
-              self.test,
-              c_val_names.rasterize_animations_percentile_75(), self.event_name
-              + "DurRasterizeAnimationsUs", 75))
-      self.c_val_recorders.append(
-          tv_testcase_c_val_recorder.PercentileCValRecorder(
-              self.test,
-              c_val_names.rasterize_animations_percentile_95(), self.event_name
-              + "DurRasterizeAnimationsUs", 95))
+      self.animations_recorder = tv_testcase_value_recorder.ValueRecorder(
+          self.event_name + "DurRasterizeAnimationsUs")
 
-    # Optional video start delay cval recorder
+    # Optional video start delay recorder
     if options.record_video_start_delay:
-      self.c_val_recorders.append(
-          tv_testcase_c_val_recorder.FullCValRecorder(
-              self.test,
-              c_val_names.event_duration_dom_video_start_delay(),
-              self.event_name + "DurVideoStartDelayUs"))
+      self.video_delay_recorder = tv_testcase_value_recorder.ValueRecorder(
+          self.event_name + "DurVideoStartDelayUs")
+
+  def _add_value_dictionary_recorder(self, key):
+    recorder = tv_testcase_value_recorder.ValueRecorder(self.event_name + key)
+    self.value_dictionary_recorders.append((key, recorder))
 
   def on_start_event(self):
-    """Handles logic related to the start of a new instance of the event."""
-
-    # If the recorder is set to skip events with font file loads, then the font
-    # files loaded count needs to be set prior to the event. That'll allow the
-    # recorder to know whether or not font files were loaded during the event.
-    if self.skip_font_file_load_events:
-      self.start_event_font_files_loaded_count = self.test.get_int_cval(
-          c_val_names.count_font_files_loaded())
+    """Handles logic related to the start of the event instance."""
+    pass
 
   def on_end_event(self):
     """Handles logic related to the end of the event instance."""
 
+    # If the event is set to skip events with font file loads and a font file
+    # loaded during the event, then its data is not collected. Log that it was
+    # skipped and return.
+    if self.skip_font_file_load_events:
+      current_font_files_loaded_count = self.test.get_cval(
+          c_val_names.count_font_files_loaded())
+      if self.font_files_loaded_count != current_font_files_loaded_count:
+        self.font_file_load_skip_count += 1
+        print("{} event skipped because a font file loaded during it! {} "
+              "events skipped.".format(self.event_name,
+                                       self.font_file_load_skip_count))
+        self.font_files_loaded_count = current_font_files_loaded_count
+        return
+
+    value_dictionary = self.test.get_cval(
+        c_val_names.event_value_dictionary(self.event_type))
+
     # If the event failed to produce a render tree, then its data is not
     # collected. Log the failure and return.
-    if self.test.get_int_cval(
-        c_val_names.event_produced_render_tree(self.event_type)) == 0:
+    if not value_dictionary.get("ProducedRenderTree"):
       self.render_tree_failure_count += 1
       print("{} event failed to produce render tree! {} events failed.".format(
           self.event_name, self.render_tree_failure_count))
       return
 
-    # If the event is set to skip events with font file loads and a font file
-    # loaded during the event, then its data is not collected. Log that it was
-    # skipped and return.
-    if (self.skip_font_file_load_events and
-        self.start_event_font_files_loaded_count !=
-        self.test.get_int_cval(c_val_names.count_font_files_loaded())):
-      self.font_file_load_skip_count += 1
-      print("{} event skipped because a font file loaded during it! {} events "
-            "skipped.".format(self.event_name, self.font_file_load_skip_count))
-      return
+    # Record all of the values from the event.
+    for value_dictionary_recorder in self.value_dictionary_recorders:
+      value = value_dictionary.get(value_dictionary_recorder[0])
+      if value is not None:
+        value_dictionary_recorder[1].collect_value(value)
 
-    # Collect the current value of all of the CVal recorders.
-    for c_val_recorder in self.c_val_recorders:
-      c_val_recorder.collect_current_value()
+    if self.animations_recorder:
+      animation_entries = self.test.get_cval(
+          c_val_names.rasterize_animations_entry_list())
+      for value in animation_entries:
+        self.animations_recorder.collect_value(value)
+
+    if self.video_delay_recorder:
+      self.video_delay_recorder.collect_value(
+          self.test.get_cval(
+              c_val_names.event_duration_dom_video_start_delay()))
 
   def on_end_test(self):
     """Handles logic related to the end of the test."""
 
-    # Notify all of the CVal recorders that the test has ended.
-    for c_val_recorder_entry in self.c_val_recorders:
-      c_val_recorder_entry.on_end_test()
+    for value_dictionary_recorder in self.value_dictionary_recorders:
+      value_dictionary_recorder[1].on_end_test()
+
+    if self.animations_recorder:
+      self.animations_recorder.on_end_test()
+
+    if self.video_delay_recorder:
+      self.video_delay_recorder.on_end_test()
diff --git a/src/cobalt/webdriver_benchmarks/tv_testcase_runner.py b/src/cobalt/webdriver_benchmarks/tv_testcase_runner.py
index 0f89a62..ab69ff6 100755
--- a/src/cobalt/webdriver_benchmarks/tv_testcase_runner.py
+++ b/src/cobalt/webdriver_benchmarks/tv_testcase_runner.py
@@ -141,12 +141,12 @@
     self.should_exit.set()
     self.launcher.SendKill()
 
-  def _GetIPAddress(self):
-    return self.launcher.GetIPAddress()
+  def _GetProcessIPAddress(self):
+    return self.launcher.GetProcessIPAddress()
 
   def _StartWebdriver(self, port):
     global _webdriver
-    url = "http://{}:{}/".format(self._GetIPAddress(), port)
+    url = "http://{}:{}/".format(self._GetProcessIPAddress(), port)
     self.webdriver = self.selenium_webdriver_module.Remote(
         url, COBALT_WEBDRIVER_CAPABILITIES)
     self.webdriver.command_executor.set_timeout(WEBDRIVER_HTTP_TIMEOUT_SECS)
diff --git a/src/cobalt/webdriver_benchmarks/tv_testcase_value_recorder.py b/src/cobalt/webdriver_benchmarks/tv_testcase_value_recorder.py
new file mode 100644
index 0000000..0c48fd0
--- /dev/null
+++ b/src/cobalt/webdriver_benchmarks/tv_testcase_value_recorder.py
@@ -0,0 +1,55 @@
+#!/usr/bin/python2
+""""Records stats on collected values during a benchmark test."""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import tv_testcase_util
+
+
+class ValueRecorder(object):
+  """"Records stats on collected values during a benchmark test.
+
+  Handles collecting values and records statistics on those values when the
+  test ends.
+  """
+
+  def __init__(self, name):
+    """Initializes the event benchmark recorder.
+
+    Args:
+      name: the name to use when recording test results
+    """
+    self.name = name
+    self.values = []
+
+  def collect_value(self, value):
+    self.values.append(value)
+
+  def on_end_test(self):
+    """Handles logic related to the end of the benchmark test."""
+
+    # Only record the test results when values have been collected.
+    if self.values:
+      self._record_test_results()
+
+  def _record_test_result_mean(self):
+    tv_testcase_util.record_test_result_mean("{}Mean".format(self.name),
+                                             self.values)
+
+  def _record_test_result_percentile(self, percentile):
+    tv_testcase_util.record_test_result_percentile("{}Pct{}".format(
+        self.name, percentile), self.values, percentile)
+
+  def _record_test_results(self):
+    """Records test results for the collected values.
+
+    This subclass records the mean, 25th, 50th, 75th, and 95th percentiles of
+    the collected values when the test ends.
+    """
+    self._record_test_result_mean()
+    self._record_test_result_percentile(25)
+    self._record_test_result_percentile(50)
+    self._record_test_result_percentile(75)
+    self._record_test_result_percentile(95)
diff --git a/src/media/base/starboard_player.cc b/src/media/base/starboard_player.cc
index d5c173a..32b73d4 100644
--- a/src/media/base/starboard_player.cc
+++ b/src/media/base/starboard_player.cc
@@ -117,10 +117,9 @@
   video_info.frame_height = frame_height_;
 
 #if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
-  SbMediaHdrMetadataColorSpace sb_media_hdr_metadata_color_space =
-      MediaToSbMediaHdrMetadataColorSpace(video_config_.hdr_metadata(),
-                                          video_config_.color_space_info());
-  video_info.hdr_metadata_color_space = &sb_media_hdr_metadata_color_space;
+  SbMediaColorMetadata sb_media_color_metadata =
+      MediaToSbMediaColorMetadata(video_config_.webm_color_metadata());
+  video_info.color_metadata = &sb_media_color_metadata;
 #endif
   if (is_encrypted) {
     FillDrmSampleInfo(buffer, &drm_info, &subsample_mapping);
diff --git a/src/media/base/starboard_utils.cc b/src/media/base/starboard_utils.cc
index f0e103f..4f60ec9 100644
--- a/src/media/base/starboard_utils.cc
+++ b/src/media/base/starboard_utils.cc
@@ -207,59 +207,74 @@
 ENUM_EQ(kSbMediaRangeIdDerived, gfx::ColorSpace::kRangeIdDerived);
 ENUM_EQ(kSbMediaRangeIdLast, gfx::ColorSpace::kRangeIdLast);
 
-SbMediaHdrMetadataColorSpace MediaToSbMediaHdrMetadataColorSpace(
-    const HDRMetadata& hdr_metadata,
-    const gfx::ColorSpace& color_space_info) {
+SbMediaColorMetadata MediaToSbMediaColorMetadata(
+    const WebMColorMetadata& webm_color_metadata) {
+  SbMediaColorMetadata sb_media_color_metadata;
+
+  // Copy the other color metadata below.
+  sb_media_color_metadata.bits_per_channel = webm_color_metadata.BitsPerChannel;
+  sb_media_color_metadata.chroma_subsampling_horizontal =
+      webm_color_metadata.ChromaSubsamplingHorz;
+  sb_media_color_metadata.chroma_subsampling_vertical =
+      webm_color_metadata.ChromaSubsamplingVert;
+  sb_media_color_metadata.cb_subsampling_horizontal =
+      webm_color_metadata.CbSubsamplingHorz;
+  sb_media_color_metadata.cb_subsampling_vertical =
+      webm_color_metadata.CbSubsamplingVert;
+  sb_media_color_metadata.chroma_siting_horizontal =
+      webm_color_metadata.ChromaSitingHorz;
+  sb_media_color_metadata.chroma_siting_vertical =
+      webm_color_metadata.ChromaSitingVert;
+
   // Copy the HDR Metadata below.
   SbMediaMasteringMetadata sb_media_mastering_metadata;
+  HDRMetadata hdr_metadata = webm_color_metadata.hdr_metadata;
+  MasteringMetadata mastering_metadata = hdr_metadata.mastering_metadata;
 
   sb_media_mastering_metadata.primary_r_chromaticity_x =
-      hdr_metadata.mastering_metadata.primary_r_chromaticity_x;
+      mastering_metadata.primary_r_chromaticity_x;
   sb_media_mastering_metadata.primary_r_chromaticity_y =
-      hdr_metadata.mastering_metadata.primary_r_chromaticity_y;
+      mastering_metadata.primary_r_chromaticity_y;
 
   sb_media_mastering_metadata.primary_g_chromaticity_x =
-      hdr_metadata.mastering_metadata.primary_g_chromaticity_x;
+      mastering_metadata.primary_g_chromaticity_x;
   sb_media_mastering_metadata.primary_g_chromaticity_y =
-      hdr_metadata.mastering_metadata.primary_g_chromaticity_y;
+      mastering_metadata.primary_g_chromaticity_y;
 
   sb_media_mastering_metadata.primary_b_chromaticity_x =
-      hdr_metadata.mastering_metadata.primary_b_chromaticity_x;
+      mastering_metadata.primary_b_chromaticity_x;
   sb_media_mastering_metadata.primary_b_chromaticity_y =
-      hdr_metadata.mastering_metadata.primary_b_chromaticity_y;
+      mastering_metadata.primary_b_chromaticity_y;
 
   sb_media_mastering_metadata.white_point_chromaticity_x =
-      hdr_metadata.mastering_metadata.white_point_chromaticity_x;
+      mastering_metadata.white_point_chromaticity_x;
   sb_media_mastering_metadata.white_point_chromaticity_y =
-      hdr_metadata.mastering_metadata.white_point_chromaticity_y;
+      mastering_metadata.white_point_chromaticity_y;
 
-  sb_media_mastering_metadata.luminance_max =
-      hdr_metadata.mastering_metadata.luminance_max;
-  sb_media_mastering_metadata.luminance_min =
-      hdr_metadata.mastering_metadata.luminance_min;
+  sb_media_mastering_metadata.luminance_max = mastering_metadata.luminance_max;
+  sb_media_mastering_metadata.luminance_min = mastering_metadata.luminance_min;
 
-  SbMediaHdrMetadataColorSpace sb_media_hdr_metadata_color_space;
-  sb_media_hdr_metadata_color_space.mastering_metadata =
-      sb_media_mastering_metadata;
-  sb_media_hdr_metadata_color_space.max_cll = hdr_metadata.max_cll;
-  sb_media_hdr_metadata_color_space.max_fall = hdr_metadata.max_fall;
+  sb_media_color_metadata.mastering_metadata = sb_media_mastering_metadata;
+  sb_media_color_metadata.max_cll = hdr_metadata.max_cll;
+  sb_media_color_metadata.max_fall = hdr_metadata.max_fall;
 
   // Copy the color space below.
-  sb_media_hdr_metadata_color_space.primaries =
-      static_cast<SbMediaPrimaryId>(color_space_info.primaries());
-  sb_media_hdr_metadata_color_space.transfer =
-      static_cast<SbMediaTransferId>(color_space_info.transfer());
-  sb_media_hdr_metadata_color_space.matrix =
-      static_cast<SbMediaMatrixId>(color_space_info.matrix());
-  sb_media_hdr_metadata_color_space.range =
-      static_cast<SbMediaRangeId>(color_space_info.range());
-  if (sb_media_hdr_metadata_color_space.primaries == kSbMediaPrimaryIdCustom) {
-    const float* custom_primary_matrix =
-        color_space_info.custom_primary_matrix();
-    SbMemoryCopy(sb_media_hdr_metadata_color_space.custom_primary_matrix,
+  gfx::ColorSpace color_space = webm_color_metadata.color_space;
+  sb_media_color_metadata.primaries =
+      static_cast<SbMediaPrimaryId>(color_space.primaries());
+  sb_media_color_metadata.transfer =
+      static_cast<SbMediaTransferId>(color_space.transfer());
+  sb_media_color_metadata.matrix =
+      static_cast<SbMediaMatrixId>(color_space.matrix());
+  sb_media_color_metadata.range =
+      static_cast<SbMediaRangeId>(color_space.range());
+  if (sb_media_color_metadata.primaries == kSbMediaPrimaryIdCustom) {
+    const float* custom_primary_matrix = color_space.custom_primary_matrix();
+    SbMemoryCopy(sb_media_color_metadata.custom_primary_matrix,
                  custom_primary_matrix, sizeof(custom_primary_matrix));
   }
-  return sb_media_hdr_metadata_color_space;
+
+  return sb_media_color_metadata;
 }
 #endif  // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
 
diff --git a/src/media/base/starboard_utils.h b/src/media/base/starboard_utils.h
index d2b3c22..ef03e27 100644
--- a/src/media/base/starboard_utils.h
+++ b/src/media/base/starboard_utils.h
@@ -38,9 +38,8 @@
                        SbDrmSubSampleMapping* subsample_mapping);
 
 #if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
-SbMediaHdrMetadataColorSpace MediaToSbMediaHdrMetadataColorSpace(
-    const HDRMetadata& hdr_metadata,
-    const gfx::ColorSpace& color_space_info);
+SbMediaColorMetadata MediaToSbMediaColorMetadata(
+    const WebMColorMetadata& webm_color_metadata);
 #endif
 
 }  // namespace media
diff --git a/src/media/base/video_decoder_config.cc b/src/media/base/video_decoder_config.cc
index e7e2dc7..7d0055f 100644
--- a/src/media/base/video_decoder_config.cc
+++ b/src/media/base/video_decoder_config.cc
@@ -15,8 +15,8 @@
       profile_(VIDEO_CODEC_PROFILE_UNKNOWN),
       format_(VideoFrame::INVALID),
       extra_data_size_(0),
-      is_encrypted_(false) {
-}
+      is_encrypted_(false),
+      color_space_(COLOR_SPACE_UNSPECIFIED) {}
 
 VideoDecoderConfig::VideoDecoderConfig(VideoCodec codec,
                                        VideoCodecProfile profile,
@@ -102,13 +102,13 @@
 
   switch (color_space) {
     case COLOR_SPACE_JPEG:
-      color_space_info_ = gfx::ColorSpace::CreateJpeg();
+      webm_color_metadata_.color_space = gfx::ColorSpace::CreateJpeg();
       break;
     case COLOR_SPACE_HD_REC709:
-      color_space_info_ = gfx::ColorSpace::CreateREC709();
+      webm_color_metadata_.color_space = gfx::ColorSpace::CreateREC709();
       break;
     case COLOR_SPACE_SD_REC601:
-      color_space_info_ = gfx::ColorSpace::CreateREC601();
+      webm_color_metadata_.color_space = gfx::ColorSpace::CreateREC601();
       break;
     case COLOR_SPACE_UNSPECIFIED:
       break;
@@ -119,17 +119,13 @@
 }
 
 void VideoDecoderConfig::CopyFrom(const VideoDecoderConfig& video_config) {
-  Initialize(video_config.codec(),
-             video_config.profile(),
-             video_config.format(),
-             video_config.color_space(),
-             video_config.coded_size(),
-             video_config.visible_rect(),
-             video_config.natural_size(),
-             video_config.extra_data(),
-             video_config.extra_data_size(),
-             video_config.is_encrypted(),
+  Initialize(video_config.codec(), video_config.profile(),
+             video_config.format(), video_config.color_space_,
+             video_config.coded_size(), video_config.visible_rect(),
+             video_config.natural_size(), video_config.extra_data(),
+             video_config.extra_data_size(), video_config.is_encrypted(),
              false);
+  webm_color_metadata_ = video_config.webm_color_metadata_;
 }
 
 bool VideoDecoderConfig::IsValidConfig() const {
@@ -141,16 +137,15 @@
 }
 
 bool VideoDecoderConfig::Matches(const VideoDecoderConfig& config) const {
-  return ((codec() == config.codec()) &&
-          (format() == config.format()) &&
-          (color_space_info() == config.color_space_info()) &&
+  return ((codec() == config.codec()) && (format() == config.format()) &&
+          (webm_color_metadata_ == config.webm_color_metadata()) &&
           (profile() == config.profile()) &&
           (coded_size() == config.coded_size()) &&
           (visible_rect() == config.visible_rect()) &&
           (natural_size() == config.natural_size()) &&
           (extra_data_size() == config.extra_data_size()) &&
-          (!extra_data() || !memcmp(extra_data(), config.extra_data(),
-                                    extra_data_size())) &&
+          (!extra_data() ||
+           !memcmp(extra_data(), config.extra_data(), extra_data_size())) &&
           (is_encrypted() == config.is_encrypted()));
 }
 
@@ -208,25 +203,4 @@
   return is_encrypted_;
 }
 
-void VideoDecoderConfig::set_color_space_info(
-    const gfx::ColorSpace& color_space_info) {
-  color_space_info_ = color_space_info;
-}
-
-gfx::ColorSpace VideoDecoderConfig::color_space_info() const {
-  return color_space_info_;
-}
-
-ColorSpace VideoDecoderConfig::color_space() const {
-  return color_space_;
-}
-
-void VideoDecoderConfig::set_hdr_metadata(const HDRMetadata& hdr_metadata) {
-  hdr_metadata_ = hdr_metadata;
-}
-
-HDRMetadata VideoDecoderConfig::hdr_metadata() const {
-  return hdr_metadata_;
-}
-
 }  // namespace media
diff --git a/src/media/base/video_decoder_config.h b/src/media/base/video_decoder_config.h
index acd8762..d828420 100644
--- a/src/media/base/video_decoder_config.h
+++ b/src/media/base/video_decoder_config.h
@@ -14,6 +14,7 @@
 #include "media/base/media_export.h"
 #include "media/base/video_frame.h"
 #include "media/base/video_types.h"
+#include "media/webm/webm_colour_parser.h"
 #include "ui/gfx/rect.h"
 #include "ui/gfx/size.h"
 
@@ -83,7 +84,8 @@
                      const gfx::Size& coded_size,
                      const gfx::Rect& visible_rect,
                      const gfx::Size& natural_size,
-                     const uint8* extra_data, size_t extra_data_size,
+                     const uint8* extra_data,
+                     size_t extra_data_size,
                      bool is_encrypted);
 
   ~VideoDecoderConfig();
@@ -96,7 +98,8 @@
                   const gfx::Size& coded_size,
                   const gfx::Rect& visible_rect,
                   const gfx::Size& natural_size,
-                  const uint8* extra_data, size_t extra_data_size,
+                  const uint8* extra_data,
+                  size_t extra_data_size,
                   bool is_encrypted,
                   bool record_stats);
 
@@ -142,13 +145,13 @@
   // can be encrypted or not encrypted.
   bool is_encrypted() const;
 
-  void set_color_space_info(const gfx::ColorSpace& color_space_info);
-  gfx::ColorSpace color_space_info() const;
+  void set_webm_color_metadata(const WebMColorMetadata& webm_color_metadata) {
+    webm_color_metadata_ = webm_color_metadata;
+  }
 
-  ColorSpace color_space() const;
-
-  void set_hdr_metadata(const HDRMetadata& hdr_metadata);
-  HDRMetadata hdr_metadata() const;
+  const WebMColorMetadata& webm_color_metadata() const {
+    return webm_color_metadata_;
+  }
 
  private:
   VideoCodec codec_;
@@ -168,9 +171,7 @@
 
   bool is_encrypted_;
 
-  gfx::ColorSpace color_space_info_;
-  HDRMetadata hdr_metadata_;
-
+  WebMColorMetadata webm_color_metadata_;
   DISALLOW_COPY_AND_ASSIGN(VideoDecoderConfig);
 };
 
diff --git a/src/media/filters/decrypting_demuxer_stream.cc b/src/media/filters/decrypting_demuxer_stream.cc
index 2c0e799..cdf3c26 100644
--- a/src/media/filters/decrypting_demuxer_stream.cc
+++ b/src/media/filters/decrypting_demuxer_stream.cc
@@ -358,17 +358,7 @@
       const VideoDecoderConfig& input_video_config =
           stream->video_decoder_config();
       video_config_.reset(new VideoDecoderConfig());
-      video_config_->Initialize(input_video_config.codec(),
-                                input_video_config.profile(),
-                                input_video_config.format(),
-                                input_video_config.color_space(),
-                                input_video_config.coded_size(),
-                                input_video_config.visible_rect(),
-                                input_video_config.natural_size(),
-                                input_video_config.extra_data(),
-                                input_video_config.extra_data_size(),
-                                false,  // Output video is not encrypted.
-                                false);
+      video_config_->CopyFrom(input_video_config);
       break;
     }
 
diff --git a/src/media/webm/webm_colour_parser.cc b/src/media/webm/webm_colour_parser.cc
index 616cc6c..0b12c52 100644
--- a/src/media/webm/webm_colour_parser.cc
+++ b/src/media/webm/webm_colour_parser.cc
@@ -343,6 +343,17 @@
   ChromaSitingVert = 0;
 }
 
+bool WebMColorMetadata::operator==(const WebMColorMetadata& rhs) const {
+  return (BitsPerChannel == rhs.BitsPerChannel &&
+          ChromaSubsamplingHorz == rhs.ChromaSubsamplingHorz &&
+          ChromaSubsamplingVert == rhs.ChromaSubsamplingVert &&
+          CbSubsamplingHorz == rhs.CbSubsamplingHorz &&
+          CbSubsamplingVert == rhs.CbSubsamplingVert &&
+          ChromaSitingHorz == rhs.ChromaSitingHorz &&
+          ChromaSitingVert == rhs.ChromaSitingVert &&
+          color_space == rhs.color_space && hdr_metadata == rhs.hdr_metadata);
+}
+
 WebMMasteringMetadataParser::WebMMasteringMetadataParser() {}
 WebMMasteringMetadataParser::~WebMMasteringMetadataParser() {}
 
diff --git a/src/media/webm/webm_colour_parser.h b/src/media/webm/webm_colour_parser.h
index 76d1c85..acf9a3d 100644
--- a/src/media/webm/webm_colour_parser.h
+++ b/src/media/webm/webm_colour_parser.h
@@ -29,6 +29,7 @@
 
   WebMColorMetadata();
   WebMColorMetadata(const WebMColorMetadata& rhs);
+  bool operator==(const WebMColorMetadata& rhs) const;
 };
 
 // Parser for WebM MasteringMetadata within Colour element:
diff --git a/src/media/webm/webm_video_client.cc b/src/media/webm/webm_video_client.cc
index aa3efed..bcd3b87 100644
--- a/src/media/webm/webm_video_client.cc
+++ b/src/media/webm/webm_video_client.cc
@@ -107,8 +107,7 @@
       true);
   if (colour_parsed_) {
     WebMColorMetadata color_metadata = colour_parser_.GetWebMColorMetadata();
-    config->set_color_space_info(color_metadata.color_space);
-    config->set_hdr_metadata(color_metadata.hdr_metadata);
+    config->set_webm_color_metadata(color_metadata);
   }
   return config->IsValidConfig();
 }
diff --git a/src/nb/atomic.h b/src/nb/atomic.h
index 9bd5c61..8c3c999 100644
--- a/src/nb/atomic.h
+++ b/src/nb/atomic.h
@@ -17,6 +17,7 @@
 #ifndef NB_ATOMIC_H_
 #define NB_ATOMIC_H_
 
+#include "starboard/atomic.h"
 #include "starboard/mutex.h"
 #include "starboard/types.h"
 
@@ -64,7 +65,7 @@
 
 // Base functionality for atomic types. Defines exchange(), load(),
 // store(), compare_exhange_weak(), compare_exchange_strong()
-template <typename T>

+template <typename T>
 class atomic;
 
 // Subtype of atomic<T> for numbers likes float and integer types but not bool.
@@ -244,13 +245,80 @@
   explicit atomic_bool(bool initial_val) : Super(initial_val) {}
 };
 
-// Simple atomic int class. This could be optimized for speed using
-// compiler intrinsics for concurrent integer modification.
-class atomic_int32_t : public atomic_integral<int32_t> {
+// Lockfree atomic int class.
+class atomic_int32_t {
  public:
-  typedef atomic_integral<int32_t> Super;
-  atomic_int32_t() : Super() {}
-  explicit atomic_int32_t(int32_t initial_val) : Super(initial_val) {}
+  typedef int32_t ValueType;
+  atomic_int32_t() : value_(0) {}
+  atomic_int32_t(SbAtomic32 value) : value_(value) {}
+
+  bool is_lock_free() const { return true; }
+  bool is_lock_free() const volatile { return true; }
+
+  int32_t increment() {
+    return fetch_add(1);
+  }
+  int32_t decrement() {
+    return fetch_add(-1);
+  }
+
+  int32_t fetch_add(int32_t val) {
+    // fetch_add is a post-increment operation, while SbAtomicBarrier_Increment
+    // is a pre-increment operation. Therefore subtract the value to match
+    // the expected interface.
+    return SbAtomicBarrier_Increment(volatile_ptr(), val) - val;
+  }
+
+ int32_t fetch_sub(int32_t val) {
+   return fetch_add(-val);
+  }
+
+  // Atomically replaces the value of the atomic object
+  // and returns the value held previously.
+  // See also std::atomic<T>::exchange().
+  int32_t exchange(int32_t new_val) {
+    return SbAtomicNoBarrier_Exchange(volatile_ptr(), new_val);
+  }
+
+  // Atomically obtains the value of the atomic object.
+  // See also std::atomic<T>::load().
+  int32_t load() const {
+    return SbAtomicAcquire_Load(volatile_const_ptr());
+  }
+
+  // Stores the value. See std::atomic<T>::store(...)
+  void store(int32_t val) {
+    SbAtomicRelease_Store(volatile_ptr(), val);
+  }
+
+  bool compare_exchange_strong(int32_t* expected_value, int32_t new_value) {
+    int32_t prev_value = *expected_value;
+    SbAtomicMemoryBarrier();
+    int32_t value_written = SbAtomicRelease_CompareAndSwap(volatile_ptr(),
+                                                           prev_value,
+                                                           new_value);
+    const bool write_ok = (prev_value == value_written);
+    if (!write_ok) {
+      *expected_value = value_written;
+    }
+    return write_ok;
+  }
+
+  // Weak version of this function is documented to be faster, but has allows
+  // weaker memory ordering and therefore will sometimes have a false negative:
+  // The value compared will actually be equal but the return value from this
+  // function indicates otherwise.
+  // By default, the function delegates to compare_exchange_strong(...).
+  //
+  // See also std::atomic<T>::compare_exchange_weak(...).
+  bool compare_exchange_weak(int32_t* expected_value, int32_t new_value) {
+    return compare_exchange_strong(expected_value, new_value);
+  }
+
+ private:
+  volatile int32_t* volatile_ptr() { return &value_; }
+  volatile const int32_t* volatile_const_ptr() const { return &value_; }
+  int32_t value_;
 };
 
 // Simple atomic int class. This could be optimized for speed using
diff --git a/src/nb/atomic_test.cc b/src/nb/atomic_test.cc
index fc76703..222965d 100644
--- a/src/nb/atomic_test.cc
+++ b/src/nb/atomic_test.cc
@@ -17,6 +17,7 @@
 #include "nb/atomic.h"
 
 #include <algorithm>
+#include <numeric>
 #include <vector>
 
 #include "nb/test_thread.h"
@@ -24,6 +25,8 @@
 #include "starboard/mutex.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#define NUM_THREADS 4
+
 namespace nb {
 namespace {
 
@@ -41,18 +44,25 @@
 typedef ::testing::Types<atomic_int32_t, atomic_int64_t,
                          atomic_float, atomic_double> AtomicNumberTypes;
 
+// Defines a typelist for just atomic number types.
+typedef ::testing::Types<atomic_int32_t, atomic_int64_t> AtomicIntegralTypes;
+
 // Defines test type that will be instantiated using each type in
 // AllAtomicTypes type list.
-template <typename T>

+template <typename T>
 class AtomicTest : public ::testing::Test {};
 TYPED_TEST_CASE(AtomicTest, AllAtomicTypes);  // Registration.
 
 // Defines test type that will be instantiated using each type in
 // AtomicNumberTypes type list.
-template <typename T>

+template <typename T>
 class AtomicNumberTest : public ::testing::Test {};
 TYPED_TEST_CASE(AtomicNumberTest, AtomicNumberTypes);  // Registration.
 
+template <typename T>
+class AtomicIntegralTest : public ::testing::Test {};
+TYPED_TEST_CASE(AtomicIntegralTest, AtomicIntegralTypes);  // Registration.
+
 
 ///////////////////////////////////////////////////////////////////////////////
 // Singlethreaded tests.
@@ -163,6 +173,19 @@
   ASSERT_EQ(neg_two, atomic.load());         // 0-2 = -2
 }
 
+TYPED_TEST(AtomicIntegralTest, IncrementAndDecrement_SingleThread) {
+  typedef TypeParam AtomicT;
+  typedef typename AtomicT::ValueType T;
+
+  const T zero(0);
+  const T one = zero + 1;  // Allows AtomicPointer<T*>.
+
+  AtomicT atomic;
+  ASSERT_EQ(atomic.load(), zero);       // Default is 0.
+  ASSERT_EQ(zero, atomic.increment());  // Tests for post-increment operation.
+  ASSERT_EQ(one, atomic.decrement());   // Tests for post-decrement operation.
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // Multithreaded tests.
 ///////////////////////////////////////////////////////////////////////////////
@@ -190,7 +213,7 @@
       T new_value = T(i);
       while (true) {
         if (std::rand() % 3 == 0) {
-          // 1 in 3 chance of yeilding.
+          // 1 in 3 chance of yielding.
           // Attempt to cause more contention by giving other threads a chance
           // to run.
           SbThreadYield();
@@ -227,17 +250,17 @@
 // The numbers are sequentially written to the shared Atomic type and then
 // exposed to other threads:
 //
-//    Generates [0,1,2,...,n/2)

-//   +------+ Thread A <--------+        (Write Exchanged Value)

-//   |                          |

-//   |    compare_exchange()    +---> exchanged? ---+

-//   +----> +------------+ +----+                   v

-//          | AtomicType |                   Output vector

-//   +----> +------------+ +----+                   ^

-//   |    compare_exchange()    +---> exchanged? ---+

-//   |                          |

-//   +------+ Thread B <--------+

-//    Generates [n/2,n/2+1,...,n)

+//    Generates [0,1,2,...,n/2)
+//   +------+ Thread A <--------+        (Write Exchanged Value)
+//   |                          |
+//   |    compare_exchange()    +---> exchanged? ---+
+//   +----> +------------+ +----+                   v
+//          | AtomicType |                   Output vector
+//   +----> +------------+ +----+                   ^
+//   |    compare_exchange()    +---> exchanged? ---+
+//   |                          |
+//   +------+ Thread B <--------+
+//    Generates [n/2,n/2+1,...,n)
 //
 // By repeatedly calling atomic<T>::compare_exchange_strong() by each of the
 // threads, each will see the previous value of the shared variable when their
@@ -245,8 +268,8 @@
 // values are recombined then it will form the original generated sequence from
 // all threads.
 //
-//            TEST PHASE

-//  sort( output vector ) AND TEST THAT

+//            TEST PHASE
+//  sort( output vector ) AND TEST THAT
 //  output vector Contains [0,1,2,...,n)
 //
 // The test passes when the output array is tested that it contains every
@@ -258,16 +281,15 @@
   typedef typename AtomicT::ValueType T;
 
   static const int kNumElements = 1000;
-  static const int kNumThreads = 4;
 
   AtomicT atomic_value(T(-1));
   std::vector<TestThread*> threads;
   std::vector<T> output_values;
   starboard::Mutex output_mutex;
 
-  for (int i = 0; i < kNumThreads; ++i) {
-    const int start_num = (kNumElements * i) / kNumThreads;
-    const int end_num = (kNumElements * (i + 1)) / kNumThreads;
+  for (int i = 0; i < NUM_THREADS; ++i) {
+    const int start_num = (kNumElements * i) / NUM_THREADS;
+    const int end_num = (kNumElements * (i + 1)) / NUM_THREADS;
     threads.push_back(
         new CompareExchangeThread<AtomicT>(
             start_num,  // defines the number range to generate.
@@ -279,15 +301,15 @@
 
   // These threads will generate unique numbers in their range and then
   // write them to the output array.
-  for (int i = 0; i < kNumThreads; ++i) {
+  for (int i = 0; i < NUM_THREADS; ++i) {
     threads[i]->Start();
   }
 
-  for (int i = 0; i < kNumThreads; ++i) {
+  for (int i = 0; i < NUM_THREADS; ++i) {
     threads[i]->Join();
   }
   // Cleanup threads.
-  for (int i = 0; i < kNumThreads; ++i) {
+  for (int i = 0; i < NUM_THREADS; ++i) {
     delete threads[i];
   }
   threads.clear();
@@ -313,5 +335,153 @@
   }
 }
 
+// A thread that will invoke increment() and decrement() and equal number
+// of times to atomic_value. The value after this is done should be equal to
+// 0.
+template <typename AtomicT>
+class IncrementAndDecrementThread : public TestThread {
+ public:
+  typedef typename AtomicT::ValueType T;
+  IncrementAndDecrementThread(size_t half_number_of_operations,
+                              AtomicT* atomic_value)
+      : atomic_value_(atomic_value) {
+    for (size_t i = 0; i < half_number_of_operations; ++i) {
+      operation_sequence_.push_back(true);
+    }
+    for (size_t i = 0; i < half_number_of_operations; ++i) {
+      operation_sequence_.push_back(false);
+    }
+    std::random_shuffle(operation_sequence_.begin(),
+                        operation_sequence_.end());
+  }
+
+  virtual void Run() {
+    for (size_t i = 0; i < operation_sequence_.size(); ++i) {
+      if (std::rand() % 3 == 0) {
+        // 1 in 3 chance of yielding.
+        // Attempt to cause more contention by giving other threads a chance
+        // to run.
+        SbThreadYield();
+      }
+      T prev_value = 0;
+      if (operation_sequence_[i]) {
+        prev_value = atomic_value_->increment();
+      } else {
+        prev_value = atomic_value_->decrement();
+      }
+    }
+  }
+ private:
+  // Used purely for true/false values. Note that we don't
+  // use std::vector<bool> because some platforms won't support
+  // swapping elements of std::vector<bool>, which is required for
+  // std::random_shuffle().
+  std::vector<uint8_t> operation_sequence_;
+  AtomicT*const atomic_value_;
+};
+
+TYPED_TEST(AtomicIntegralTest, Test_IncrementAndDecrement_MultiThreaded) {
+  typedef TypeParam AtomicT;
+  typedef typename AtomicT::ValueType T;
+
+  static const int kNumOperations = 10000;
+
+  AtomicT atomic_value(T(0));
+  std::vector<TestThread*> threads;
+
+  for (int i = 0; i < NUM_THREADS; ++i) {
+    threads.push_back(
+        new IncrementAndDecrementThread<AtomicT>(
+            kNumOperations,
+            &atomic_value));
+  }
+
+  for (int i = 0; i < NUM_THREADS; ++i) {
+    threads[i]->Start();
+  }
+
+  for (int i = 0; i < NUM_THREADS; ++i) {
+    threads[i]->Join();
+  }
+  // Cleanup threads.
+  for (int i = 0; i < NUM_THREADS; ++i) {
+    delete threads[i];
+  }
+  threads.clear();
+
+  // After an equal number of decrements and increments, the final value should
+  // be 0.
+  ASSERT_EQ(0, atomic_value.load());
+}
+
+template <typename AtomicT>
+class FetchAddSubThread : public TestThread {
+ public:
+  typedef typename AtomicT::ValueType T;
+  FetchAddSubThread(const int32_t start_value,
+                    const int32_t end_value,
+                    AtomicT* atomic_value)
+      : start_value_(start_value),
+        end_value_(end_value),
+        atomic_value_(atomic_value) {
+  }
+
+  virtual void Run() {
+    for (int32_t i = start_value_; i < end_value_; ++i) {
+      if (std::rand() % 3 == 0) {
+        // 1 in 3 chance of yielding.
+        // Attempt to cause more contention by giving other threads a chance
+        // to run.s
+        SbThreadYield();
+      }
+
+      if (std::rand() % 2 == 0) {
+        atomic_value_->fetch_add(i);
+      } else {
+        atomic_value_->fetch_sub(-i);
+      }
+    }
+  }
+ private:
+  int32_t start_value_;
+  int32_t end_value_;
+  AtomicT*const atomic_value_;
+};
+
+TYPED_TEST(AtomicIntegralTest, Test_FetchAdd_MultiThreaded) {
+  typedef TypeParam AtomicT;
+  typedef typename AtomicT::ValueType T;
+
+  static const int kNumOperations = 10000;
+
+  AtomicT atomic_value(T(0));
+  std::vector<TestThread*> threads;
+
+  // First value is inclusive, second is exclusive.
+  threads.push_back(
+      new FetchAddSubThread<AtomicT>(-kNumOperations, 0, &atomic_value));
+
+  threads.push_back(
+      new FetchAddSubThread<AtomicT>(1, kNumOperations+1, &atomic_value));
+
+  for (int i = 0; i < threads.size(); ++i) {
+    threads[i]->Start();
+  }
+
+  for (int i = 0; i < threads.size(); ++i) {
+    threads[i]->Join();
+  }
+  // Cleanup threads.
+  for (int i = 0; i < threads.size(); ++i) {
+    delete threads[i];
+  }
+  threads.clear();
+
+  // After an equal number of decrements and increments, the final value should
+  // be 0.
+  ASSERT_EQ(0, atomic_value.load());
+}
+
+
 }  // namespace
 }  // namespace nb
diff --git a/src/nb/lexical_cast.h b/src/nb/lexical_cast.h
new file mode 100644
index 0000000..0ce3a20
--- /dev/null
+++ b/src/nb/lexical_cast.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2017 Google Inc. 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 NB_LEXICAL_CAST_H_
+#define NB_LEXICAL_CAST_H_
+
+#include <limits>
+#include <sstream>
+
+#include "starboard/types.h"
+
+namespace nb {
+
+// Converts a string into a value. This function should not be used in
+// performance sensitive code.
+//
+// Note:
+//  * All strings are assumed to represent numbers in base 10.
+//  * Casting will parse until a non-numerical character is encountered:
+//    * Numbers like "128M" will drop "M" and cast to a value of 128.
+//    * Numbers like "12M8" will cast to value 12.
+//    * Numbers like "M128" will fail to cast.
+//
+// Returns the value of the result after the lexical cast. If the lexical
+// cast fails then the default value of the parameterized type is returned.
+// |cast_ok| is an optional parameter which will be |true| if the cast
+// succeeds, otherwise |false|.
+// Example:
+//   int value = lexical_cast<int>("1234");
+//   EXPECT_EQ(value, 1234);
+//   bool ok = true;
+//   value = lexical_cast<int>("not a number", &ok);
+//   EXPECT_FALSE(ok);
+//   EXPECT_EQ(0, value);
+template <typename T>
+T lexical_cast(const char* s, bool* cast_ok = NULL) {
+  if (!s) {  // Handle NULL case of input string.
+    if (cast_ok) {
+      *cast_ok = false;
+    }
+    return T();
+  }
+  std::stringstream ss;
+  ss << s;
+  T value;
+  ss >> value;
+  if (cast_ok) {
+    *cast_ok = !ss.fail();
+  }
+  if (ss.fail()) {
+    value = T();
+  }
+  return value;
+}
+
+// int8_t and uint8_t will normally be interpreted as a char, which will
+// result in only the first character being parsed. This is obviously not
+// what we want. Therefore we provide specializations for lexical_cast for
+// these types.
+template <>
+int8_t lexical_cast<int8_t>(const char* s, bool* cast_ok) {
+  int16_t value_i16 = lexical_cast<int16_t>(s, cast_ok);
+  if (value_i16 < std::numeric_limits<int8_t>::min() ||
+      value_i16 > std::numeric_limits<int8_t>::max()) {
+    value_i16 = 0;
+    if (cast_ok) {
+      *cast_ok = false;
+    }
+  }
+  return static_cast<int8_t>(value_i16);
+}
+
+template <>
+uint8_t lexical_cast<uint8_t>(const char* s, bool* cast_ok) {
+  uint16_t value_i16 = lexical_cast<uint16_t>(s, cast_ok);
+  if (value_i16 > std::numeric_limits<uint8_t>::max()) {
+    value_i16 = 0;
+    if (cast_ok) {
+      *cast_ok = false;
+    }
+  }
+  return static_cast<uint8_t>(value_i16);
+}
+
+}  // namespace nb
+
+#endif  // NB_LEXICAL_CAST_H_
\ No newline at end of file
diff --git a/src/nb/lexical_cast_test.cc b/src/nb/lexical_cast_test.cc
new file mode 100644
index 0000000..fb7e12d
--- /dev/null
+++ b/src/nb/lexical_cast_test.cc
@@ -0,0 +1,193 @@
+/*

+ * Copyright 2017 Google Inc. 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 "nb/lexical_cast.h"

+#include "starboard/types.h"

+#include "testing/gtest/include/gtest/gtest.h"

+

+namespace nb {

+namespace {

+

+TEST(lexical_cast, OptionalParameterOmitted) {

+  EXPECT_EQ(123, lexical_cast<int>("123"));

+  EXPECT_EQ(0, lexical_cast<int>("not a number"));

+  EXPECT_EQ(-123, lexical_cast<int8_t>("-123"));

+  EXPECT_EQ(123, lexical_cast<uint8_t>("123"));

+}

+

+TEST(lexical_cast, PositiveBasicTypes) {

+  bool cast_ok = false;

+  EXPECT_EQ(123, lexical_cast<int>("123", &cast_ok));

+  EXPECT_TRUE(cast_ok);

+  EXPECT_EQ(123, lexical_cast<int8_t>("123", &cast_ok));

+  EXPECT_TRUE(cast_ok);

+  EXPECT_EQ(123, lexical_cast<int16_t>("123", &cast_ok));

+  EXPECT_TRUE(cast_ok);

+  EXPECT_EQ(123, lexical_cast<int32_t>("123", &cast_ok));

+  EXPECT_TRUE(cast_ok);

+  EXPECT_EQ(123, lexical_cast<int64_t>("123", &cast_ok));

+  EXPECT_TRUE(cast_ok);

+

+  EXPECT_EQ(123, lexical_cast<uint8_t>("123", &cast_ok));

+  EXPECT_TRUE(cast_ok);

+  EXPECT_EQ(123, lexical_cast<uint16_t>("123", &cast_ok));

+  EXPECT_TRUE(cast_ok);

+  EXPECT_EQ(123, lexical_cast<uint32_t>("123", &cast_ok));

+  EXPECT_TRUE(cast_ok);

+  EXPECT_EQ(123, lexical_cast<uint64_t>("123", &cast_ok));

+  EXPECT_TRUE(cast_ok);

+

+  EXPECT_FLOAT_EQ(1234.5f, lexical_cast<float>("1234.5", &cast_ok));

+  EXPECT_TRUE(cast_ok);

+  EXPECT_FLOAT_EQ(1234.5f, lexical_cast<float>("1234.5f", &cast_ok));

+  EXPECT_TRUE(cast_ok);

+

+  EXPECT_FLOAT_EQ(1234.5f, lexical_cast<double>("1234.5", &cast_ok));

+  EXPECT_TRUE(cast_ok);

+  EXPECT_FLOAT_EQ(1234.5f, lexical_cast<double>("1234.5f", &cast_ok));

+  EXPECT_TRUE(cast_ok);

+}

+

+TEST(lexical_cast, NegativeBasicTypes) {

+  bool cast_ok = false;

+  EXPECT_EQ(-123, lexical_cast<int>("-123", &cast_ok));

+  EXPECT_TRUE(cast_ok);

+  EXPECT_EQ(-123, lexical_cast<int8_t>("-123", &cast_ok));

+  EXPECT_TRUE(cast_ok);

+  EXPECT_EQ(-123, lexical_cast<int16_t>("-123", &cast_ok));

+  EXPECT_TRUE(cast_ok);

+  EXPECT_EQ(-123, lexical_cast<int32_t>("-123", &cast_ok));

+  EXPECT_TRUE(cast_ok);

+  EXPECT_EQ(-123, lexical_cast<int64_t>("-123", &cast_ok));

+  EXPECT_TRUE(cast_ok);

+

+  EXPECT_EQ(0, lexical_cast<uint8_t>("-123", &cast_ok));

+  EXPECT_FALSE(cast_ok);

+  EXPECT_EQ(0, lexical_cast<uint16_t>("-123", &cast_ok));

+  EXPECT_FALSE(cast_ok);

+  EXPECT_EQ(0, lexical_cast<uint32_t>("-123", &cast_ok));

+  EXPECT_FALSE(cast_ok);

+  EXPECT_EQ(0, lexical_cast<uint64_t>("-123", &cast_ok));

+  EXPECT_FALSE(cast_ok);

+

+  EXPECT_FLOAT_EQ(-1234.5f, lexical_cast<float>("-1234.5", &cast_ok));

+  EXPECT_TRUE(cast_ok);

+  EXPECT_FLOAT_EQ(-1234.5f, lexical_cast<float>("-1234.5f", &cast_ok));

+  EXPECT_TRUE(cast_ok);

+

+  EXPECT_FLOAT_EQ(-1234.5f, lexical_cast<double>("-1234.5", &cast_ok));

+  EXPECT_TRUE(cast_ok);

+  EXPECT_FLOAT_EQ(-1234.5f, lexical_cast<double>("-1234.5f", &cast_ok));

+  EXPECT_TRUE(cast_ok);

+}

+

+TEST(lexical_cast, StringIsNonNumerical) {

+  bool cast_ok = false;

+  EXPECT_EQ(0, lexical_cast<int>("not a number", &cast_ok));

+  EXPECT_FALSE(cast_ok);

+  EXPECT_EQ(0, lexical_cast<int8_t>("not a number", &cast_ok));

+  EXPECT_FALSE(cast_ok);

+  EXPECT_EQ(0, lexical_cast<int16_t>("not a number", &cast_ok));

+  EXPECT_FALSE(cast_ok);

+  EXPECT_EQ(0, lexical_cast<int32_t>("not a number", &cast_ok));

+  EXPECT_FALSE(cast_ok);

+  EXPECT_EQ(0, lexical_cast<int64_t>("not a number", &cast_ok));

+  EXPECT_FALSE(cast_ok);

+

+  EXPECT_FLOAT_EQ(0.f, lexical_cast<float>("not a number", &cast_ok));

+  EXPECT_FALSE(cast_ok);

+  EXPECT_FLOAT_EQ(0.0, lexical_cast<double>("not a number", &cast_ok));

+  EXPECT_FALSE(cast_ok);

+}

+

+TEST(lexical_cast, StringIsEmpty) {

+  bool cast_ok = false;

+  int value = lexical_cast<int>("");

+  EXPECT_EQ(value, 0);

+  EXPECT_FALSE(cast_ok);

+}

+

+TEST(lexical_cast, StringIsNull) {

+  bool cast_ok = false;

+  int value = lexical_cast<int>(NULL);

+  EXPECT_EQ(value, 0);

+  EXPECT_FALSE(cast_ok);

+}

+

+TEST(lexical_cast, IntegerOverflow_int8) {

+  bool cast_ok = false;

+  int8_t value = lexical_cast<int8_t>("128", &cast_ok);

+

+  EXPECT_FALSE(cast_ok);

+  EXPECT_EQ(0, value);

+}

+

+TEST(lexical_cast, IntegerOverflow_uint8) {

+  bool cast_ok = false;

+  uint8_t value = lexical_cast<uint8_t>("256", &cast_ok);

+

+  EXPECT_FALSE(cast_ok);

+  EXPECT_EQ(0, value);

+}

+

+TEST(lexical_cast, IntegerOverflow_int16) {

+  bool cast_ok = false;

+  int16_t value = lexical_cast<int16_t>("65535", &cast_ok);

+

+  EXPECT_FALSE(cast_ok);

+  EXPECT_EQ(0, value);

+}

+

+TEST(lexical_cast, IntegerOverflow_uint16) {

+  bool cast_ok = false;

+  int16_t value = lexical_cast<int16_t>("65536", &cast_ok);

+

+  EXPECT_FALSE(cast_ok);

+  EXPECT_EQ(0, value);

+}

+

+TEST(lexical_cast, LetterBeforeNumber) {

+  bool cast_ok = false;

+  int value = lexical_cast<int>("M128", &cast_ok);

+  EXPECT_FALSE(cast_ok);

+  EXPECT_EQ(0, value);

+}

+

+TEST(lexical_cast, LetterInNumber) {

+  bool cast_ok = false;

+  int value = lexical_cast<int>("12M8", &cast_ok);

+  EXPECT_TRUE(cast_ok);

+  EXPECT_EQ(12, value);

+}

+

+TEST(lexical_cast, LetterAfterNumber) {

+  bool cast_ok = false;

+  int value = lexical_cast<int>("128M", &cast_ok);

+  EXPECT_TRUE(cast_ok);

+  EXPECT_EQ(128, value);

+}

+

+TEST(lexical_cast, OnlyBase10) {

+  bool cast_ok = false;

+  // Expect that the "100000" part of "100000ff" will be parsed. The "ff" part

+  // is dropped.

+  uint32_t value = lexical_cast<uint32_t>("100000ff", &cast_ok);

+  EXPECT_TRUE(cast_ok);

+  EXPECT_EQ(100000, value);  //

+}

+

+}  // namespace

+}  // namespace nb

diff --git a/src/nb/nb.gyp b/src/nb/nb.gyp
index 30179ea..d9ea369 100644
--- a/src/nb/nb.gyp
+++ b/src/nb/nb.gyp
@@ -37,6 +37,7 @@
             'fixed_no_free_allocator.h',
             'hash.cc',
             'hash.h',
+            'lexical_cast.h',
             'memory_pool.cc',
             'memory_pool.h',
             'memory_scope.cc',
@@ -83,6 +84,7 @@
             'analytics/memory_tracker_test.cc',
             'atomic_test.cc',
             'fixed_no_free_allocator_test.cc',
+            'lexical_cast_test.cc',
             'memory_scope_test.cc',
             'reuse_allocator_test.cc',
             'run_all_unittests.cc',
diff --git a/src/starboard/common/flat_map.h b/src/starboard/common/flat_map.h
index 02130cb..4f4d39c 100644
--- a/src/starboard/common/flat_map.h
+++ b/src/starboard/common/flat_map.h
@@ -1,431 +1,431 @@
-// Copyright 2017 Google Inc. 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_COMMON_FLAT_MAP_H_

-#define STARBOARD_COMMON_FLAT_MAP_H_

-

-#include <algorithm>

-#include <functional>

-#include <utility>

-#include <vector>

-

-#include "starboard/log.h"

-#include "starboard/types.h"

-

-namespace starboard {

-namespace flat_map_detail {

-// IsPod<> is a white list of common types that are "plain-old-data'.

-// Types not registered with IsPod<> will default to non-pod.

-// Usage: IsPod<int>::value == true;

-//        IsPod<std::string>::value == false

-// See specializations at the bottom of this file for what has been defined

-// as pod.

-template <typename T>

-struct IsPod {

-  enum { value = false };

-};

-}  // namespace flat_map_detail.

-

-// FlatMap<key_type, mapped_type> is a sorted vector map of key-value pairs.

-// This map has an interface that is largely compatible with std::map<> and

-// is designed to be a near drop in replacement for std::map<>.

-//

-// A main usage difference between FlatMap<> and std::map<> is that FlatMap<>

-// will invalidate it's iterators on insert() and erase().

-//

-// Reasons to use this map include low-level operations which require that

-// the map does not allocate memory during runtime (this is achievable by

-// invoking FlatMap::reserve()), or to speed up a lot of find() operations

-// where the FlatMap() is mutated to very occasionally.

-//

-// PERFORMANCE

-//   where n is the number of items in flatmap

-//   and m is the input size for the operation.

-//

-// bulk insert |  O(n+m + m*log(m))

-// insert      |  O(n)

-// erase       |  O(n)

-// bulk erase  |  O(n*m)     TODO: Make faster - O(n+m + log(m))

-// find        |  O(log(n))

-// clear       |  O(n)

-//

-// Performance of FlatMap::find() tends to be about +50% faster than that

-// of std::map<> for pod types in optimized builds. However this is balanced

-// by the fact that FlatMap::insert() and FlatMap::erase() both operate at

-// O(n), where std::map will operate at O(log n) for both operations.

-//

-// FlatMap<int,int>::find() Performance

-// NUMBER OF ELEMENTS | SPEED COMPARSION vs std::map

-// -------------------------------------

-//                  5 | 220.37%

-//                 10 | 158.602%

-//                 25 | 87.7049%

-//                 50 | 91.0873%

-//                100 | 96.1367%

-//               1000 | 120.588%

-//              10000 | 156.969%

-//             100000 | 179.55%

-//

-// When in doubt, use std::map. If you need to use FlatMap<>, then make sure

-// that insertion() is done in bulk, and that delete are infrequent, or that

-// the maps are small.

-//

-// Please see unit tests for additional usage.

-

-template <typename Key,

-          typename Value,

-          typename LessThan = std::less<Key>,

-          typename VectorType = std::vector<std::pair<Key, Value> > >

-class FlatMap {

- public:

-  // Most typedefs here are to make FlatMap<> compatible with std::map<>.

-  typedef Key key_type;

-  typedef Value mapped_type;

-  typedef LessThan key_compare;

-  typedef std::pair<key_type, mapped_type> value_type;

-  typedef typename VectorType::size_type size_type;

-  typedef typename VectorType::difference_type difference_type;

-  typedef typename VectorType::iterator iterator;

-  typedef typename VectorType::const_iterator const_iterator;

-

-  FlatMap() {}

-  FlatMap(const FlatMap& other) : vector_(other.vector_) {}

-

-  template <typename ITERATOR>

-  FlatMap(ITERATOR start, ITERATOR end) {

-    insert(start, end);

-  }

-

-  void reserve(size_t size) { vector_.reserve(size); }

-

-  // Takes the incoming elements and only adds the elements that don't already

-  // exist in this map.

-  // Returns the number of elements that were added.

-  template <typename Iterator>

-  size_t insert(Iterator begin_it, Iterator end_it) {

-    const size_t partition_idx = vector_.size();

-

-    // PART 1 - Elements are added unsorted into array as a new partition.

-    //

-    // Only add elements that don't exist

-    for (Iterator it = begin_it; it != end_it; ++it) {

-      // These have to be recomputed every loop iteration because

-      // vector_.push_back() will invalidate iterators.

-      const_iterator sorted_begin = vector_.begin();

-      const_iterator sorted_end = sorted_begin + partition_idx;

-

-      const bool already_exists_sorted_part =

-          exists_in_range(sorted_begin, sorted_end, it->first);

-      // Okay so it doesn't exist yet so place it in the vector.

-      if (!already_exists_sorted_part) {

-        vector_.push_back(*it);

-      }

-    }

-

-    // No elements added.

-    if (vector_.size() == partition_idx) {

-      return 0;

-    }

-

-    iterator unsorted_begin = vector_.begin() + partition_idx;

-    // std::sort(...) will not maintain the order of values which have

-    // keys. Therefore InplaceMergeSort(...) is used instead, which has

-    // the same guarantees as std::stable_sort but with the added addition

-    // that no memory will be allocated during the operation of

-    // InplaceMergeSort(...). This is important because when bulk inserting

-    // elements, the first key-value pair (of duplicate keys) will be the one

-    // that gets inserted, and the second one is ignored.

-    InplaceMergeSort(unsorted_begin, vector_.end());

-

-    // Corner-case: remove duplicates in the input.

-    iterator new_end = std::unique(unsorted_begin, vector_.end(), EqualTo);

-    std::inplace_merge(vector_.begin(), unsorted_begin, new_end, LessThanValue);

-

-    vector_.erase(new_end, vector_.end());

-

-    return std::distance(vector_.begin() + partition_idx, new_end);

-  }

-

-  std::pair<iterator, bool> insert(const value_type& entry) {

-    iterator insertion_it =

-        std::upper_bound(begin(), end(), entry, LessThanValue);

-

-    // DUPLICATE CHECK - If the key already exists then return it.

-    if (insertion_it != begin()) {

-      // Check for a duplicate value, which will be the preceding value, if

-      // it exits.

-      iterator previous_it = (insertion_it - 1);

-      const key_type& previous_key = previous_it->first;

-      if (EqualKeyTo(previous_key, entry.first)) {

-        // Value already exists.

-        return std::pair<iterator, bool>(previous_it, false);

-      }

-    }

-

-    iterator inserted_it = vector_.insert(insertion_it, entry);

-    return std::pair<iterator, bool>(inserted_it, true);

-  }

-

-  iterator find(const key_type& key) {

-    return find_in_range(vector_.begin(), vector_.end(), key);

-  }

-

-  const_iterator find(const key_type& key) const {

-    return find_in_range_const(vector_.begin(), vector_.end(), key);

-  }

-

-  bool erase(const key_type& key) {

-    iterator found_it = find(key);

-    if (found_it != vector_.end()) {

-      vector_.erase(found_it);  // no resorting necessary.

-      return true;

-    } else {

-      return false;

-    }

-  }

-

-  void erase(iterator begin_it, iterator end_it) {

-    vector_.erase(begin_it, end_it);

-  }

-

-  bool empty() const { return vector_.empty(); }

-  size_t size() const { return vector_.size(); }

-  iterator begin() { return vector_.begin(); }

-  const_iterator begin() const { return vector_.begin(); }

-  const_iterator cbegin() const { return vector_.begin(); }

-  iterator end() { return vector_.end(); }

-  const_iterator end() const { return vector_.end(); }

-  const_iterator cend() const { return vector_.end(); }

-  void clear() { vector_.clear(); }

-

-  size_t count(const key_type& key) const {

-    if (cend() != find(key)) {

-      return 1;

-    } else {

-      return 0;

-    }

-  }

-

-  size_type max_size() const { return vector_.max_size(); }

-  void swap(FlatMap& other) { vector_.swap(other.vector_); }

-

-  iterator lower_bound(const key_type& key) {

-    mapped_type dummy;

-    value_type key_data(key, dummy);  // Second value is ignored.

-    return std::lower_bound(begin(), end(), key_data, LessThanValue);

-  }

-

-  const_iterator lower_bound(const key_type& key) const {

-    mapped_type dummy;

-    value_type key_data(key, dummy);  // Second value is ignored.

-    return std::lower_bound(cbegin(), cend(), key_data, LessThanValue);

-  }

-

-  iterator upper_bound(const key_type& key) {

-    mapped_type dummy;

-    value_type key_data(key, dummy);  // Second value is ignored.

-    return std::upper_bound(begin(), end(), key_data, LessThanValue);

-  }

-

-  const_iterator upper_bound(const key_type& key) const {

-    mapped_type dummy;

-    value_type key_data(key, dummy);  // Second value is ignored.

-    return std::upper_bound(cbegin(), cend(), key_data, LessThanValue);

-  }

-

-  std::pair<iterator, iterator> equal_range(const key_type& key) {

-    iterator found = find(key);

-    if (found == end()) {

-      return std::pair<iterator, iterator>(end(), end());

-    }

-    iterator found_end = found;

-    ++found_end;

-    return std::pair<iterator, iterator>(found, found_end);

-  }

-

-  std::pair<const_iterator, const_iterator> equal_range(

-      const key_type& key) const {

-    const_iterator found = find(key);

-    if (found == end()) {

-      return std::pair<const_iterator, const_iterator>(cend(), cend());

-    }

-    const_iterator found_end = found;

-    ++found_end;

-    return std::pair<const_iterator, const_iterator>(found, found_end);

-  }

-

-  key_compare key_comp() const {

-    key_compare return_value;

-    return return_value;

-  }

-

-  mapped_type& operator[](const key_type& key) {

-    std::pair<key_type, mapped_type> entry;

-    entry.first = key;

-    std::pair<iterator, bool> result = insert(entry);

-    iterator found = result.first;

-    mapped_type& value = found->second;

-    return value;

-  }

-

-  bool operator==(const FlatMap& other) const {

-    return vector_ == other.vector_;

-  }

-

-  bool operator!=(const FlatMap& other) const {

-    return vector_ != other.vector_;

-  }

-

- private:

-  static bool LessThanValue(const std::pair<key_type, mapped_type>& a,

-                            const std::pair<key_type, mapped_type>& b) {

-    return LessThanKey(a.first, b.first);

-  }

-

-  static bool LessThanKey(const key_type& a, const key_type& b) {

-    key_compare less_than;

-    return less_than(a, b);

-  }

-

-  static bool NotEqualKeyTo(const key_type& a, const key_type& b) {

-    key_compare less_than;

-    return less_than(a, b) || less_than(b, a);

-  }

-

-  static bool EqualKeyTo(const key_type& a, const key_type& b) {

-    return !NotEqualKeyTo(a, b);

-  }

-

-  static bool EqualTo(const std::pair<key_type, mapped_type>& a,

-                      const std::pair<key_type, mapped_type>& b) {

-    return EqualKeyTo(a.first, b.first);

-  }

-

-  static iterator find_in_range(iterator begin_it,

-                                iterator end_it,

-                                const key_type& key) {

-    // Delegate to find_in_range_const().

-    const_iterator begin_it_const = begin_it;

-    const_iterator end_it_const = end_it;

-    const_iterator found_it_const =

-        find_in_range_const(begin_it_const, end_it_const, key);

-    const size_t diff = std::distance(begin_it_const, found_it_const);

-    return begin_it + diff;

-  }

-

-  static inline const_iterator find_in_range_const_linear(

-      const_iterator begin_it,

-      const_iterator end_it,

-      const value_type& key_data) {

-    SB_DCHECK(end_it >= begin_it);

-    for (const_iterator it = begin_it; it != end_it; ++it) {

-      if (LessThanValue(key_data, *it)) {

-        continue;

-      }

-      if (!LessThanValue(*it, key_data)) {

-        return it;

-      }

-    }

-    return end_it;

-  }

-

-  static inline const_iterator find_in_range_const(const_iterator begin_it,

-                                                   const_iterator end_it,

-                                                   const key_type& key) {

-    // This was tested and found to have a very positive effect on

-    // performance. The threshold could be a lot higher (~20 elements) but is

-    // kept at 11 elements to be on the conservative side.

-    static const difference_type kLinearSearchThreshold = 11;

-

-    mapped_type dummy;

-    value_type key_data(key, dummy);  // Second value is ignored.

-

-    // Speedup for small maps of pod type: just do a linear search. This was

-    // tested to be significantly faster - 2x speedup. The conditions for this

-    // search are very specific so that in many situations the fast path won't

-    // be taken.

-    if (flat_map_detail::IsPod<key_type>::value) {

-      const difference_type range_distance = std::distance(begin_it, end_it);

-

-      // Linear search.

-      if (range_distance < kLinearSearchThreshold) {

-        return find_in_range_const_linear(begin_it, end_it, key_data);

-      }

-    }

-

-    const_iterator found_it =

-        std::lower_bound(begin_it, end_it, key_data, LessThanValue);

-    if (found_it == end_it) {

-      return end_it;

-    }

-    if (NotEqualKeyTo(found_it->first, key)) {  // different keys.

-      return end_it;

-    }

-    size_t dist = std::distance(begin_it, found_it);

-    return begin_it + dist;

-  }

-

-  static bool exists_in_range(const_iterator begin_it,

-                              const_iterator end_it,

-                              const key_type& key) {

-    const_iterator result_iterator = find_in_range_const(begin_it, end_it, key);

-    return result_iterator != end_it;

-  }

-

-  // This is needed as a stable sorting algorithm, which leaves duplicates in

-  // relative order from which they appeared in the original container.

-  // Unlike std::stable_sort(...) this function will not allocate memory.

-  void InplaceMergeSort(iterator begin_it, iterator end_it) {

-    if (end_it - begin_it > 1) {

-      iterator middle_it = begin_it + (end_it - begin_it) / 2;

-      InplaceMergeSort(begin_it, middle_it);

-      InplaceMergeSort(middle_it, end_it);

-      std::inplace_merge(begin_it, middle_it, end_it, LessThanValue);

-    }

-  }

-

-  VectorType vector_;

-};

-

-namespace flat_map_detail {

-#define STARBOARD_FLATMAP_DEFINE_IS_POD(TYPE) \

-  template <>                                 \

-  struct IsPod<TYPE> {                        \

-    enum { value = true };                    \

-  }

-

-STARBOARD_FLATMAP_DEFINE_IS_POD(bool);

-STARBOARD_FLATMAP_DEFINE_IS_POD(float);

-STARBOARD_FLATMAP_DEFINE_IS_POD(double);

-STARBOARD_FLATMAP_DEFINE_IS_POD(int8_t);

-STARBOARD_FLATMAP_DEFINE_IS_POD(uint8_t);

-STARBOARD_FLATMAP_DEFINE_IS_POD(int16_t);

-STARBOARD_FLATMAP_DEFINE_IS_POD(uint16_t);

-STARBOARD_FLATMAP_DEFINE_IS_POD(int32_t);

-STARBOARD_FLATMAP_DEFINE_IS_POD(uint32_t);

-STARBOARD_FLATMAP_DEFINE_IS_POD(int64_t);

-STARBOARD_FLATMAP_DEFINE_IS_POD(uint64_t);

-

-#undef STARBOARD_FLATMAP_DEFINE_IS_POD

-

-// Specialization - all pointer types are treated as pod.

-template <typename T>

-struct IsPod<T*> {

-  enum { value = true };

-};

-

-}  // namespace flat_map_detail.

-}  // namespace starboard.

-

-#endif  // STARBOARD_COMMON_FLAT_MAP_H_

+// Copyright 2017 Google Inc. 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_COMMON_FLAT_MAP_H_
+#define STARBOARD_COMMON_FLAT_MAP_H_
+
+#include <algorithm>
+#include <functional>
+#include <utility>
+#include <vector>
+
+#include "starboard/log.h"
+#include "starboard/types.h"
+
+namespace starboard {
+namespace flat_map_detail {
+// IsPod<> is a white list of common types that are "plain-old-data'.
+// Types not registered with IsPod<> will default to non-pod.
+// Usage: IsPod<int>::value == true;
+//        IsPod<std::string>::value == false
+// See specializations at the bottom of this file for what has been defined
+// as pod.
+template <typename T>
+struct IsPod {
+  enum { value = false };
+};
+}  // namespace flat_map_detail.
+
+// FlatMap<key_type, mapped_type> is a sorted vector map of key-value pairs.
+// This map has an interface that is largely compatible with std::map<> and
+// is designed to be a near drop in replacement for std::map<>.
+//
+// A main usage difference between FlatMap<> and std::map<> is that FlatMap<>
+// will invalidate it's iterators on insert() and erase().
+//
+// Reasons to use this map include low-level operations which require that
+// the map does not allocate memory during runtime (this is achievable by
+// invoking FlatMap::reserve()), or to speed up a lot of find() operations
+// where the FlatMap() is mutated to very occasionally.
+//
+// PERFORMANCE
+//   where n is the number of items in flatmap
+//   and m is the input size for the operation.
+//
+// bulk insert |  O(n+m + m*log(m))
+// insert      |  O(n)
+// erase       |  O(n)
+// bulk erase  |  O(n*m)     TODO: Make faster - O(n+m + log(m))
+// find        |  O(log(n))
+// clear       |  O(n)
+//
+// Performance of FlatMap::find() tends to be about +50% faster than that
+// of std::map<> for pod types in optimized builds. However this is balanced
+// by the fact that FlatMap::insert() and FlatMap::erase() both operate at
+// O(n), where std::map will operate at O(log n) for both operations.
+//
+// FlatMap<int,int>::find() Performance
+// NUMBER OF ELEMENTS | SPEED COMPARSION vs std::map
+// -------------------------------------
+//                  5 | 220.37%
+//                 10 | 158.602%
+//                 25 | 87.7049%
+//                 50 | 91.0873%
+//                100 | 96.1367%
+//               1000 | 120.588%
+//              10000 | 156.969%
+//             100000 | 179.55%
+//
+// When in doubt, use std::map. If you need to use FlatMap<>, then make sure
+// that insertion() is done in bulk, and that delete are infrequent, or that
+// the maps are small.
+//
+// Please see unit tests for additional usage.
+
+template <typename Key,
+          typename Value,
+          typename LessThan = std::less<Key>,
+          typename VectorType = std::vector<std::pair<Key, Value> > >
+class FlatMap {
+ public:
+  // Most typedefs here are to make FlatMap<> compatible with std::map<>.
+  typedef Key key_type;
+  typedef Value mapped_type;
+  typedef LessThan key_compare;
+  typedef std::pair<key_type, mapped_type> value_type;
+  typedef typename VectorType::size_type size_type;
+  typedef typename VectorType::difference_type difference_type;
+  typedef typename VectorType::iterator iterator;
+  typedef typename VectorType::const_iterator const_iterator;
+
+  FlatMap() {}
+  FlatMap(const FlatMap& other) : vector_(other.vector_) {}
+
+  template <typename ITERATOR>
+  FlatMap(ITERATOR start, ITERATOR end) {
+    insert(start, end);
+  }
+
+  void reserve(size_t size) { vector_.reserve(size); }
+
+  // Takes the incoming elements and only adds the elements that don't already
+  // exist in this map.
+  // Returns the number of elements that were added.
+  template <typename Iterator>
+  size_t insert(Iterator begin_it, Iterator end_it) {
+    const size_t partition_idx = vector_.size();
+
+    // PART 1 - Elements are added unsorted into array as a new partition.
+    //
+    // Only add elements that don't exist
+    for (Iterator it = begin_it; it != end_it; ++it) {
+      // These have to be recomputed every loop iteration because
+      // vector_.push_back() will invalidate iterators.
+      const_iterator sorted_begin = vector_.begin();
+      const_iterator sorted_end = sorted_begin + partition_idx;
+
+      const bool already_exists_sorted_part =
+          exists_in_range(sorted_begin, sorted_end, it->first);
+      // Okay so it doesn't exist yet so place it in the vector.
+      if (!already_exists_sorted_part) {
+        vector_.push_back(*it);
+      }
+    }
+
+    // No elements added.
+    if (vector_.size() == partition_idx) {
+      return 0;
+    }
+
+    iterator unsorted_begin = vector_.begin() + partition_idx;
+    // std::sort(...) will not maintain the order of values which have
+    // keys. Therefore InplaceMergeSort(...) is used instead, which has
+    // the same guarantees as std::stable_sort but with the added addition
+    // that no memory will be allocated during the operation of
+    // InplaceMergeSort(...). This is important because when bulk inserting
+    // elements, the first key-value pair (of duplicate keys) will be the one
+    // that gets inserted, and the second one is ignored.
+    InplaceMergeSort(unsorted_begin, vector_.end());
+
+    // Corner-case: remove duplicates in the input.
+    iterator new_end = std::unique(unsorted_begin, vector_.end(), EqualTo);
+    std::inplace_merge(vector_.begin(), unsorted_begin, new_end, LessThanValue);
+
+    vector_.erase(new_end, vector_.end());
+
+    return std::distance(vector_.begin() + partition_idx, new_end);
+  }
+
+  std::pair<iterator, bool> insert(const value_type& entry) {
+    iterator insertion_it =
+        std::upper_bound(begin(), end(), entry, LessThanValue);
+
+    // DUPLICATE CHECK - If the key already exists then return it.
+    if (insertion_it != begin()) {
+      // Check for a duplicate value, which will be the preceding value, if
+      // it exits.
+      iterator previous_it = (insertion_it - 1);
+      const key_type& previous_key = previous_it->first;
+      if (EqualKeyTo(previous_key, entry.first)) {
+        // Value already exists.
+        return std::pair<iterator, bool>(previous_it, false);
+      }
+    }
+
+    iterator inserted_it = vector_.insert(insertion_it, entry);
+    return std::pair<iterator, bool>(inserted_it, true);
+  }
+
+  iterator find(const key_type& key) {
+    return find_in_range(vector_.begin(), vector_.end(), key);
+  }
+
+  const_iterator find(const key_type& key) const {
+    return find_in_range_const(vector_.begin(), vector_.end(), key);
+  }
+
+  bool erase(const key_type& key) {
+    iterator found_it = find(key);
+    if (found_it != vector_.end()) {
+      vector_.erase(found_it);  // no resorting necessary.
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  void erase(iterator begin_it, iterator end_it) {
+    vector_.erase(begin_it, end_it);
+  }
+
+  bool empty() const { return vector_.empty(); }
+  size_t size() const { return vector_.size(); }
+  iterator begin() { return vector_.begin(); }
+  const_iterator begin() const { return vector_.begin(); }
+  const_iterator cbegin() const { return vector_.begin(); }
+  iterator end() { return vector_.end(); }
+  const_iterator end() const { return vector_.end(); }
+  const_iterator cend() const { return vector_.end(); }
+  void clear() { vector_.clear(); }
+
+  size_t count(const key_type& key) const {
+    if (cend() != find(key)) {
+      return 1;
+    } else {
+      return 0;
+    }
+  }
+
+  size_type max_size() const { return vector_.max_size(); }
+  void swap(FlatMap& other) { vector_.swap(other.vector_); }
+
+  iterator lower_bound(const key_type& key) {
+    mapped_type dummy;
+    value_type key_data(key, dummy);  // Second value is ignored.
+    return std::lower_bound(begin(), end(), key_data, LessThanValue);
+  }
+
+  const_iterator lower_bound(const key_type& key) const {
+    mapped_type dummy;
+    value_type key_data(key, dummy);  // Second value is ignored.
+    return std::lower_bound(cbegin(), cend(), key_data, LessThanValue);
+  }
+
+  iterator upper_bound(const key_type& key) {
+    mapped_type dummy;
+    value_type key_data(key, dummy);  // Second value is ignored.
+    return std::upper_bound(begin(), end(), key_data, LessThanValue);
+  }
+
+  const_iterator upper_bound(const key_type& key) const {
+    mapped_type dummy;
+    value_type key_data(key, dummy);  // Second value is ignored.
+    return std::upper_bound(cbegin(), cend(), key_data, LessThanValue);
+  }
+
+  std::pair<iterator, iterator> equal_range(const key_type& key) {
+    iterator found = find(key);
+    if (found == end()) {
+      return std::pair<iterator, iterator>(end(), end());
+    }
+    iterator found_end = found;
+    ++found_end;
+    return std::pair<iterator, iterator>(found, found_end);
+  }
+
+  std::pair<const_iterator, const_iterator> equal_range(
+      const key_type& key) const {
+    const_iterator found = find(key);
+    if (found == end()) {
+      return std::pair<const_iterator, const_iterator>(cend(), cend());
+    }
+    const_iterator found_end = found;
+    ++found_end;
+    return std::pair<const_iterator, const_iterator>(found, found_end);
+  }
+
+  key_compare key_comp() const {
+    key_compare return_value;
+    return return_value;
+  }
+
+  mapped_type& operator[](const key_type& key) {
+    std::pair<key_type, mapped_type> entry;
+    entry.first = key;
+    std::pair<iterator, bool> result = insert(entry);
+    iterator found = result.first;
+    mapped_type& value = found->second;
+    return value;
+  }
+
+  bool operator==(const FlatMap& other) const {
+    return vector_ == other.vector_;
+  }
+
+  bool operator!=(const FlatMap& other) const {
+    return vector_ != other.vector_;
+  }
+
+ private:
+  static bool LessThanValue(const std::pair<key_type, mapped_type>& a,
+                            const std::pair<key_type, mapped_type>& b) {
+    return LessThanKey(a.first, b.first);
+  }
+
+  static bool LessThanKey(const key_type& a, const key_type& b) {
+    key_compare less_than;
+    return less_than(a, b);
+  }
+
+  static bool NotEqualKeyTo(const key_type& a, const key_type& b) {
+    key_compare less_than;
+    return less_than(a, b) || less_than(b, a);
+  }
+
+  static bool EqualKeyTo(const key_type& a, const key_type& b) {
+    return !NotEqualKeyTo(a, b);
+  }
+
+  static bool EqualTo(const std::pair<key_type, mapped_type>& a,
+                      const std::pair<key_type, mapped_type>& b) {
+    return EqualKeyTo(a.first, b.first);
+  }
+
+  static iterator find_in_range(iterator begin_it,
+                                iterator end_it,
+                                const key_type& key) {
+    // Delegate to find_in_range_const().
+    const_iterator begin_it_const = begin_it;
+    const_iterator end_it_const = end_it;
+    const_iterator found_it_const =
+        find_in_range_const(begin_it_const, end_it_const, key);
+    const size_t diff = std::distance(begin_it_const, found_it_const);
+    return begin_it + diff;
+  }
+
+  static inline const_iterator find_in_range_const_linear(
+      const_iterator begin_it,
+      const_iterator end_it,
+      const value_type& key_data) {
+    SB_DCHECK(end_it >= begin_it);
+    for (const_iterator it = begin_it; it != end_it; ++it) {
+      if (LessThanValue(key_data, *it)) {
+        continue;
+      }
+      if (!LessThanValue(*it, key_data)) {
+        return it;
+      }
+    }
+    return end_it;
+  }
+
+  static inline const_iterator find_in_range_const(const_iterator begin_it,
+                                                   const_iterator end_it,
+                                                   const key_type& key) {
+    // This was tested and found to have a very positive effect on
+    // performance. The threshold could be a lot higher (~20 elements) but is
+    // kept at 11 elements to be on the conservative side.
+    static const difference_type kLinearSearchThreshold = 11;
+
+    mapped_type dummy;
+    value_type key_data(key, dummy);  // Second value is ignored.
+
+    // Speedup for small maps of pod type: just do a linear search. This was
+    // tested to be significantly faster - 2x speedup. The conditions for this
+    // search are very specific so that in many situations the fast path won't
+    // be taken.
+    if (flat_map_detail::IsPod<key_type>::value) {
+      const difference_type range_distance = std::distance(begin_it, end_it);
+
+      // Linear search.
+      if (range_distance < kLinearSearchThreshold) {
+        return find_in_range_const_linear(begin_it, end_it, key_data);
+      }
+    }
+
+    const_iterator found_it =
+        std::lower_bound(begin_it, end_it, key_data, LessThanValue);
+    if (found_it == end_it) {
+      return end_it;
+    }
+    if (NotEqualKeyTo(found_it->first, key)) {  // different keys.
+      return end_it;
+    }
+    size_t dist = std::distance(begin_it, found_it);
+    return begin_it + dist;
+  }
+
+  static bool exists_in_range(const_iterator begin_it,
+                              const_iterator end_it,
+                              const key_type& key) {
+    const_iterator result_iterator = find_in_range_const(begin_it, end_it, key);
+    return result_iterator != end_it;
+  }
+
+  // This is needed as a stable sorting algorithm, which leaves duplicates in
+  // relative order from which they appeared in the original container.
+  // Unlike std::stable_sort(...) this function will not allocate memory.
+  void InplaceMergeSort(iterator begin_it, iterator end_it) {
+    if (end_it - begin_it > 1) {
+      iterator middle_it = begin_it + (end_it - begin_it) / 2;
+      InplaceMergeSort(begin_it, middle_it);
+      InplaceMergeSort(middle_it, end_it);
+      std::inplace_merge(begin_it, middle_it, end_it, LessThanValue);
+    }
+  }
+
+  VectorType vector_;
+};
+
+namespace flat_map_detail {
+#define STARBOARD_FLATMAP_DEFINE_IS_POD(TYPE) \
+  template <>                                 \
+  struct IsPod<TYPE> {                        \
+    enum { value = true };                    \
+  }
+
+STARBOARD_FLATMAP_DEFINE_IS_POD(bool);
+STARBOARD_FLATMAP_DEFINE_IS_POD(float);
+STARBOARD_FLATMAP_DEFINE_IS_POD(double);
+STARBOARD_FLATMAP_DEFINE_IS_POD(int8_t);
+STARBOARD_FLATMAP_DEFINE_IS_POD(uint8_t);
+STARBOARD_FLATMAP_DEFINE_IS_POD(int16_t);
+STARBOARD_FLATMAP_DEFINE_IS_POD(uint16_t);
+STARBOARD_FLATMAP_DEFINE_IS_POD(int32_t);
+STARBOARD_FLATMAP_DEFINE_IS_POD(uint32_t);
+STARBOARD_FLATMAP_DEFINE_IS_POD(int64_t);
+STARBOARD_FLATMAP_DEFINE_IS_POD(uint64_t);
+
+#undef STARBOARD_FLATMAP_DEFINE_IS_POD
+
+// Specialization - all pointer types are treated as pod.
+template <typename T>
+struct IsPod<T*> {
+  enum { value = true };
+};
+
+}  // namespace flat_map_detail.
+}  // namespace starboard.
+
+#endif  // STARBOARD_COMMON_FLAT_MAP_H_
diff --git a/src/starboard/creator/ci20directfb/starboard_platform.gyp b/src/starboard/creator/ci20directfb/starboard_platform.gyp
index d5566bf..d897ef9 100644
--- a/src/starboard/creator/ci20directfb/starboard_platform.gyp
+++ b/src/starboard/creator/ci20directfb/starboard_platform.gyp
@@ -241,6 +241,8 @@
         '<(DEPTH)/starboard/shared/signal/suspend_signals.cc',
         '<(DEPTH)/starboard/shared/signal/suspend_signals.h',
         '<(DEPTH)/starboard/shared/starboard/application.cc',
+        '<(DEPTH)/starboard/shared/starboard/command_line.cc',
+        '<(DEPTH)/starboard/shared/starboard/command_line.h',
         '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_create.cc',
         '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_destroy.cc',
         '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_internal.cc',
diff --git a/src/starboard/creator/ci20x11/starboard_platform.gyp b/src/starboard/creator/ci20x11/starboard_platform.gyp
index b694772..8105b97 100644
--- a/src/starboard/creator/ci20x11/starboard_platform.gyp
+++ b/src/starboard/creator/ci20x11/starboard_platform.gyp
@@ -206,6 +206,8 @@
         '<(DEPTH)/starboard/shared/signal/suspend_signals.cc',
         '<(DEPTH)/starboard/shared/signal/suspend_signals.h',
         '<(DEPTH)/starboard/shared/starboard/application.cc',
+        '<(DEPTH)/starboard/shared/starboard/command_line.cc',
+        '<(DEPTH)/starboard/shared/starboard/command_line.h',
         '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_create.cc',
         '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_destroy.cc',
         '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_internal.cc',
diff --git a/src/starboard/doc/principles.md b/src/starboard/doc/principles.md
new file mode 100644
index 0000000..a58fba7
--- /dev/null
+++ b/src/starboard/doc/principles.md
@@ -0,0 +1,153 @@
+# Starboard Design Principles
+
+An overview of the goals and design principles of Starboard with the perspective
+of hindsight.
+
+**Status:** REVIEWED\
+**Created:** 2016-11-12
+
+Starboard is a porting abstraction and a collection of implementation fragments
+used to abstract operating system facilities and platform quirks from C or C++
+applications. It occupies a similar space to SDL, DirectFB, Marmalade, and
+various others.
+
+## Background
+
+Starboard was created as a response to the significant effort it has
+historically taken to port desktop-oriented web browsers to non-traditional
+device platforms like game consoles. Chromium in particular mixes
+platform-specific code with common code throughout the technology stack, making
+it very difficult to know what has to be done for a new platform or how much
+work it is going to be.
+
+## Goals
+
+Here are the main goals of Starboard, stack-ranked from most-to-least important.
+
+  * **G1** Minimize the total time and effort required to port Starboard Client
+    Applications to new platforms.
+  * **G2** Minimize the incremental time and effort required to rebase Starboard
+    Client Applications across platforms.
+  * **G3** Enable third parties to port Starboard to their platform without
+    significant engineering support from the Starboard team.
+  * **G4** Ensure support for low-profile platforms that are not geared towards
+    broad native C/C++ development access.
+  * **G5** Provide an organization framework for platform-specific code, clearly
+    delineating what is common and what is platform-specific, consolidating
+    platform-specific code into a single location, and enumerating all the APIs
+    needed to provide full functionality.
+  * **G6** Encapsulate all platform-provided services needed to build graphical
+    media applications into a single API.
+  * **G7** Reduce the total surface area needed to port to new platforms.
+  * **G8** Improve and encourage sharing of platform-specific implementation
+    components between platforms.
+  * **G9** Maximize the amount of common (platform-independent) code, to avoid
+    variances between platforms, and to increase the leverage of testing,
+    including fuzz testing which must often be done on particular platforms.
+  * **G10** Maintain a loose binding between a Starboard Platform Implementation
+    and a Starboard Client Application, such that they can be updated
+    independently at a source level.
+  * **G11** Avoid the pitfalls of trying to emulate POSIX, including, but not
+    limited to: auto-included headers, global defines of short and common
+    symbols, wrapping misbehaving or misprototyped system APIs, using custom
+    toolchain facilities, and conflicts with platform headers.
+
+## Principles
+
+### Make APIs sufficient for their purpose, but minimally so.
+
+APIs can generally be augmented without serious backwards-compatibility
+consequences, but they can not be changed or pruned so easily, so it is better
+to **err on the side of providing less**.
+
+#### Corollary: Implementation details should be as hidden as possible.
+
+#### Corollary: Anything that could be implemented purely on top of Starboard APIs should be implemented purely on top of Starboard APIs.
+
+#### Exception: If there are good reasons why an API may need to be implemented in a platform-specific manner on one or more platforms, but can be commonly implemented on other platforms, it should be part of the API, with a shared Starboard-based implementation.
+
+#### Exception: For the select few cases where Starboard implementations also need to use it, it should be included in the Starboard API so that can happen without creating circular dependencies.
+
+### Specify public APIs concretely and narrowly.
+
+A broader specification of the behavior of an API function makes life easier for
+the implementor, but more difficult for anyone attempting to use the API. An API
+can be so weakly specified that it is not usable across platforms. It can also
+be so strictly specified that it is not implementable across platforms. **Err on
+the side of narrower specifications**, requiring generality only when
+necessitated by one or more platforms.
+
+#### Corollary: Documentation should be exhaustive and clear.
+
+#### Corollary: Avoid overly-flexible convention-based interfaces.
+
+For example, passing in a set of string-string name-value pairs. This takes the
+compiler out of any kind of validation, and can encourage mismatches of
+understanding between Clients and Platforms.
+
+### Minimize the burden on the porter.
+
+Whenever adding or changing an API, or specifying a contract, consider whether
+this places a large burden on some platform implementers. This burden could be
+because of a wide porting surface, or complex requirements that are difficult to
+implement. It could be caused by a fundamental piece of infrastructure that
+isn't provided by a particular platform.
+
+We can always make APIs that are burdensome to use easier with more common code.
+
+### Be consistent and predictable.
+
+Consistency, not just in formatting, but in semantics, leads to
+predictability. Some people just won't read the documentation, even if it's
+thorough. Perhaps especially if it's thorough. The names of API entities should
+convey the intention as completely as possible.
+
+Yet, overly-verbose naming will make the API difficult to remember, read, and
+use.
+
+### Assume the porter knows nothing.
+
+Engineers from a broad set of backgrounds and environments will end up being
+dropped into porting Starboard. They may not have knowledge of any particular
+technologies, best practices, or design patterns. They may be under an
+aggressive deadline.
+
+### Always consider threading (and document it).
+
+Each function and module should have a strategy for dealing with multiple
+threads. It should make sense for the expected use cases of the interface
+entities in question. As the interface designer, it is most clear to you how the
+API should be used with respect to threads.
+
+Some may be "thread-safe," such that any functions can be called from any thread
+without much concern. Note that this places the burden on the implementer to
+ensure that an implementation meets that specification, and they MAY not know
+how to do that. This can also be more complex than just acquiring a mutex inside
+every function implementation, as there may be inherent race conditions between
+function calls even when using a synchronization mechanism.
+
+The path of least burden to the porter is to say that an interface is not
+thread-safe at all, which means applications will have to take care how the API
+is used. But, sometimes even this requires clarification as to what modes of
+access are dangerous and how to use the API safely.
+
+### Don't expose Informational-Only result codes, but do DLOG them.
+
+"Informational-Only" is defined by a result code that doesn't change the
+behavior of the caller. Many times, why something failed does not matter when
+the product is already in the hands of the user. We often want diagnostic
+logging during development
+
+### Trust but Verify. Whenever possible, write NPLB tests for all contracts declared by the interface.
+
+#### Corollary: For hard-to-test features (like Input) add an example sandbox app for testing.
+
+### We will get it wrong the first time, so plan for some kind of change mechanism.
+
+Starboard has a [versioning mechanism](versioning.md) to manage change.
+
+## References
+  * [Joshua Bloch's presentation about API design](https://www.youtube.com/watch?v=aAb7hSCtvGw)
+  * [Joshua Bloch's bumper sticker API design rules](http://www.infoq.com/articles/API-Design-Joshua-Bloch)
+  * [digithead's collection of API design links (I didn't read them all)](http://digitheadslabnotebook.blogspot.com/2010/07/how-to-design-good-apis.html)
+
diff --git a/src/starboard/doc/style.md b/src/starboard/doc/style.md
new file mode 100644
index 0000000..7345067
--- /dev/null
+++ b/src/starboard/doc/style.md
@@ -0,0 +1,271 @@
+# Starboard C and C++ Style Guide
+
+A description of the coding conventions for Starboard code and API headers.
+
+**Status:** REVIEWED\
+**Created:** 2016-11-08
+
+Starboard generally tries to follow the coding conventions of Cobalt, which
+itself mostly follows the conventions of Chromium, which mostly follows the
+externally-published Google C++ coding conventions. But, Starboard has some
+special requirements due to its unusual constraints, so it must add a few new
+conventions and loosen some of the existing style prescriptions.
+
+## Background
+
+Before looking at this document, bear in mind that it is not intending to
+completely describe all conventions that apply to Starboard. You probably want
+to take some time to familiarize yourself with these documents first, probably
+in this order:
+
+  * [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html)
+  * [Chromium C++ Style Guide](https://chromium.googlesource.com/chromium/src/+/master/styleguide/c++/c++.md)
+  * [Cobalt Style Guide](http://cobalt.foo/broken)
+
+The main additional constraints that Starboard has to deal with are:
+
+  * The Starboard API is defined in straight-C. It must be able to interface
+    with all possible third-party components, many of which are in C and not
+    C++.
+  * Starboard is a public API. Starboard platform implementations and
+    applications written on top of Starboard will change independently. This
+    means there are intense requirements for API stability, usage
+    predictability, searchability, and documentation.
+  * Note that even though it is presented as a "style guide," the conventions
+    presented here are required to be approved for check-in unless otherwise
+    noted.
+
+
+## Definitions
+
+### snake-case
+
+Words separated with underscores.
+
+    lower_snake_case
+    ALL_CAPS_SNAKE_CASE
+
+### camel-case
+
+Words separated by letter capitalization.
+
+    camelCase
+    CapitalizedCamelCase
+
+## C++ Guidelines
+
+What follows are hereby the guidelines for Starboard C and C++ code. Heretofore
+the guidelines follow thusly as follows.
+
+### API Definitions
+
+  * Starboard API definitions must always be compatible with straight-C99 compilers.
+  * All public API declarations must be specified in headers in
+    `src/starboard/*.h`, not in any subdirectories.
+  * Non-public declarations must NOT be specified in headers in
+    `src/starboard/*.h`.
+  * C++ inline helper definitions may be included inside an `#if
+    defined(__cplusplus)` preprocessor block. They must only provide
+    convenience, and must NOT be required for any API functionality.
+  * All public API functions should be exported symbols with the SB_EXPORT
+    attribute.
+  * No non-const variables shall be exposed as part of the public API.
+  * All APIs should be implemented in C++ source files, not straight-C source files.
+
+### Modules
+
+  * Each module header must be contained with a single header file.
+
+  * The name of the module must be the singular form of the noun being
+    interfaced with by the module, without any "sb" or "starboard".
+      * `file.h`
+      * `directory.h`
+      * `window.h`
+  * Module interfaces should not have circular dependencies.
+
+### File Names
+
+  * Like in the other conventions (e.g. Google, Chromium), file names must be in
+    `lower_snake_case`.
+  * File names must not contain `sb_` or `starboard_`.
+  * The name of a module header file must be the `lower_snake_case` form of the
+    module name.
+      * `SbConditionVariable` âž¡ `starboard/condition_variable.h`
+  * A header that is intended to be an internal implementation detail of one or
+    more platform implementations should have the suffix `_internal.h`, and
+    include the header `starboard/shared/internal_only.h`.
+  * See "Implementations" for conventions about where to place implementation files.
+
+### Types
+
+  * Like in the other conventions, types should be `CapitalizedCamelCase`.
+  * Every public Starboard type must start with `Sb`. There are no namespaces in
+    C, so `Sb` is the Starboard namespace.
+  * Every public Starboard type must be declared by a module, and must have the
+    name of the module following the `Sb`.
+      * `file.h` contains `SbFile`, `SbFileInfo`, `SbFileWhence`, etc...
+  * Every seemingly-allocatable, platform-specific Starboard type should be
+    defined as an opaque handle to a publically undefined struct with the
+    `Private` suffix. Follow this pattern for all such type declarations.
+      * `struct SbFilePrivate` is declared, but not defined in the public header.
+      * `SbFilePrivate` is `typedef`'d to `struct SbFilePrivate`. This is a C
+        thing where types are defined as names with the "`struct`" keyword
+        prepended unless `typedef`'d.
+      * `SbFile` is defined as a `typedef` of `struct SbFilePrivate*`.
+  * C structs may be defined internally to have functions and visibility. It is
+    allowed for such structs to have constructors, destructors, methods,
+    members, and public members.
+  * It is also considered idiomatic to never define the private struct but to
+    just treat it like a handle into some other method of object tracking,
+    casting the handle back and forth to the pointer type.
+  * If a word in the name of a type is redundant with the module name, it is
+    omitted.
+        * A monotonic time type in the Time module is `SbTimeMonotonic`, not
+          ~~`SbMonotonicTime`, `SbTimeMonotonicTime`, or
+          `SbTimeMonotonicSbTime`~~.
+
+### Functions
+
+  * Like in the other conventions, functions should be `CapitalizedCamelCase`.
+  * Every public Starboard function must start with `Sb`. There are no namespaces
+    in C, so `Sb` is the Starboard namespace.
+  * Every public Starboard function must be declared by a module, and must have
+    the name of the module following the `Sb`.
+      * `system.h` contains `SbSystemGetPath()`
+      * `file.h` contains `SbFileOpen()`
+  * After the Starboard and Module prefix, functions should start with an
+    imperative verb indicating what the function does.
+      * The Thread module defines `SbThreadCreateLocalKey()` to create a key for
+        thread local storage.
+  * If a word in the name of a function is redundant with the module name, it is
+    omitted.
+      * The `File` module as the function `SbFileOpen`, not ~~`SbOpenFile`,
+        `SbFileOpenFile` or `SbFileOpenSbFile`~~.
+      * If this gets awkward, it may indicate a need to split into a different
+        module.
+
+### Variables, Parameters, Fields
+
+  * Like in the other conventions, variable, function parameter, and field names
+    must be in `lower_snake_case`.
+  * Private member fields end in an underscore.
+  * Public member fields do not end in an underscore.
+
+### Namespaces
+
+Most Starboard API headers are straight-C compatible, so cannot live inside a
+namespace. Implementations, since they implement straight-C interface functions,
+also cannot live inside a namespace.
+
+But, in all other cases, Starboard C++ code should follow the inherited
+conventions and use a namespace for each directory starting with a "starboard"
+namespace at the starboard repository root.
+
+### Preprocessor Macros
+
+  * Like in the other conventions, variable, function parameter, and field names
+    must be in `ALL_CAPS_SNAKE_CASE`.
+  * Macros may be used as compile-time constants because straight-C does not
+    have a proper facility for typed constants. This is as opposed to macros
+    used primarily at preprocessor-time to filter or modify what gets sent to
+    the compiler. Macros used as compile-time constants and that are not
+    configuration parameters should be explicitly-typed with a c-style cast, and
+    should follow the Constants naming conventions.
+  * Macros must start with `SB_`, and then must further be namespaced with the
+    module name, with the exception of configuration definitions.
+  * Configuration definitions should be namespaced with the module name that
+    they primarily affect, if applicable, or a scope that generally indicates
+    its domain.
+      * `SB_FILE_MAX_NAME`
+      * `SB_MEMORY_PAGE_SIZE`
+  * Always use `#if defined(MACRO)` over `#ifdef MACRO`.
+
+### Constants
+
+  * Constants (including enum entries) are named using the Google constant
+    naming convention, `CapitalizedCamelCase`d, but starting with a lower-case
+    `k`.
+  * After the `k`, all constants have `Sb`, the Starboard namespace.
+      * `kSb`
+  * After `kSb`, all constants then have the module name.
+      * `kSbTime`
+      * `kSbFile`
+  * After `kSb<module>` comes the rest of the name of the constant.
+      * `kSbTimeMillisecond`
+      * `kSbFileInvalid`
+  * Enum entries are prefixed with the full name of the enum.
+      * The enum `SbSystemDeviceType` contains entries like
+        `kSbSystemDeviceTypeBlueRayDiskPlayer`.
+
+### Comments
+
+  * All files must have a license and copyright comment.
+  * It is expected that the straight-C compiler supports C99 single-line
+    comments. Block comments should be avoided whenever possible, even in
+    license and copyright headers.
+  * Each public API module file should have a Module Overview documentation
+    comment below the license explaining what the module is for, and how to use
+    it effectively.
+  * The Module Overview must be separated by a completely blank line from the
+    license comment.
+  * The first line of the Module Overview documentation comment must say
+    "`Module Overview: Starboard <module-name> module`", followed by a blank
+    comment line (i.e. a line that contains a `//`, but nothing else).
+  * The first sentence of a documentation comment describing any entity (module,
+    type, function, variable, etc...) should assume "This module," "This type,"
+    "This function," or "This variable" at the beginning of the sentence, and
+    not include it.
+  * The first sentence of a documentation comment describing any entity should
+    be a single-sentence summary description of the entire entity.
+  * The first paragraph of a documentation comment should describe the overall
+    behavior of the entity.
+  * Paragraphs in comments should be separated by a blank comment line.
+  * All public entities must have documentation comments, including enum
+    entries.
+  * Documentation comments should be formatted with Markdown.
+  * Variables, constants, literals, and expressions should be referenced in
+    comments with pipes around them.
+  * All comments must be full grammatically-correct English sentences with
+    proper punctuation.
+
+### Implementations
+
+  * Each API implementation should attempt to minimize other platform
+    assumptions, and should therefore use Starboard APIs to accomplish
+    platform-specific work unless directly related to the platform functionality
+    being implemented.
+        * For example, `SbFile` can use POSIX file I/O, because that what it is
+          abstracting, but it should use `SbMemoryAllocate` for any memory
+          allocations, because it might be used with a variety of `SbMemory`
+          implementations.
+  * Whenever possible, each shared function implementation should be implemented
+    in an individual file so as to maximize the chances of reuse between
+    implementations.
+  * This does not apply to platform-specific functions that have no chance of
+    being reusable on other platforms.
+  * Implementation files that can conceivably be shared between one or more
+    implementations should be placed in a `starboard/shared/<dependency>/`
+    directory, where `<dependency>` is the primary platform dependency of that
+    implementation. (e.g. `libevent`, `posix`, `c++11`, etc.)
+  * Implementation files that don't have a specific platform dependency, but
+    whether to use them should be a platform decision should be placed in
+    `starboard/shared/starboard/`, and must only have dependencies on other
+    Starboard APIs.
+  * Implementation files that definitely can be common to ALL implementations
+    should be placed in `starboard/common/`.
+
+### Language Features
+
+  * In public headers, particularly in inline functions and macros, only C-Style
+    casts may be used, though they are forbidden everywhere else.
+  * It is expected that the C compiler supports inline functions. They must be
+    declared `static`, and they must use the `SB_C_INLINE` or
+    `SB_C_FORCE_INLINE` attribute. In straight-C code, there is no anonymous
+    namespace, so `static` is allowed and required for inline functions.
+  * No straight-C ISO or POSIX headers should be assumed to exist. Basic C++03
+    headers may be assumed to exist in C++ code. The ISO C standards have grown
+    up over a long period of time and have historically been implemented with
+    quirks, missing pieces, and so on. Support for the core C++ standard library
+    is much more consistent on those platforms that do support it.
+  * It is idiomatic to include thin C++ inline wrappers inside public API
+    headers, gated by an `#if defined(cplusplus__)` check.
diff --git a/src/starboard/file.h b/src/starboard/file.h
index 0e92907..4e80e92 100644
--- a/src/starboard/file.h
+++ b/src/starboard/file.h
@@ -20,7 +20,6 @@
 #define STARBOARD_FILE_H_
 
 #include "starboard/export.h"
-#include "starboard/log.h"
 #include "starboard/time.h"
 #include "starboard/types.h"
 
diff --git a/src/starboard/linux/shared/gyp_configuration.gypi b/src/starboard/linux/shared/gyp_configuration.gypi
index 1fd243d..f053843 100644
--- a/src/starboard/linux/shared/gyp_configuration.gypi
+++ b/src/starboard/linux/shared/gyp_configuration.gypi
@@ -24,11 +24,6 @@
     'in_app_dial%': 1,
     'gl_type%': 'system_gles3',
 
-    # This should have a default value in cobalt/base.gypi. See the comment
-    # there for acceptable values for this variable.
-    'javascript_engine': 'mozjs',
-    'cobalt_enable_jit': 1,
-
     'platform_libraries': [
       '-lasound',
       '-lavcodec',
diff --git a/src/starboard/linux/shared/starboard_platform.gypi b/src/starboard/linux/shared/starboard_platform.gypi
index 6700e41..281a28d 100644
--- a/src/starboard/linux/shared/starboard_platform.gypi
+++ b/src/starboard/linux/shared/starboard_platform.gypi
@@ -192,6 +192,8 @@
       '<(DEPTH)/starboard/shared/signal/suspend_signals.cc',
       '<(DEPTH)/starboard/shared/signal/suspend_signals.h',
       '<(DEPTH)/starboard/shared/starboard/application.cc',
+      '<(DEPTH)/starboard/shared/starboard/command_line.cc',
+      '<(DEPTH)/starboard/shared/starboard/command_line.h',
       '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_create.cc',
       '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_destroy.cc',
       '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_internal.cc',
@@ -218,6 +220,8 @@
       '<(DEPTH)/starboard/shared/starboard/media/mime_type.cc',
       '<(DEPTH)/starboard/shared/starboard/media/mime_type.h',
       '<(DEPTH)/starboard/shared/starboard/new.cc',
+      '<(DEPTH)/starboard/shared/starboard/player/decoded_audio_internal.cc',
+      '<(DEPTH)/starboard/shared/starboard/player/decoded_audio_internal.h',
       '<(DEPTH)/starboard/shared/starboard/player/filter/audio_decoder_internal.h',
       '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.cc',
       '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.h',
@@ -228,6 +232,8 @@
       '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.h',
       '<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.cc',
       '<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.h',
+      '<(DEPTH)/starboard/shared/starboard/player/job_queue.cc',
+      '<(DEPTH)/starboard/shared/starboard/player/job_queue.h',
       '<(DEPTH)/starboard/shared/starboard/player/player_create.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_destroy.cc',
       '<(DEPTH)/starboard/shared/starboard/player/player_get_info.cc',
diff --git a/src/starboard/linux/x64directfb/README.md b/src/starboard/linux/x64directfb/README.md
index 3d1a187..b8fd196 100644
--- a/src/starboard/linux/x64directfb/README.md
+++ b/src/starboard/linux/x64directfb/README.md
@@ -50,7 +50,7 @@
 which will automatically run an executable (and its parameters) within a
 Xephyr window.  For example, to run the nplb executable under Xephyr:
 
-    $ starboard/linux/x64/directfb/xephyr_run.sh out/linux-x64directfb_debug/nplb
+    $ starboard/linux/x64directfb/xephyr_run.sh out/linux-x64directfb_debug/nplb
 
 Note that the script will start an instance of Xephyr, run the application
 within it, and then shutdown Xephyr when the application terminates.
diff --git a/src/starboard/linux/x64directfb/gyp_configuration.gypi b/src/starboard/linux/x64directfb/gyp_configuration.gypi
index 7005f6f..1fbc03e 100644
--- a/src/starboard/linux/x64directfb/gyp_configuration.gypi
+++ b/src/starboard/linux/x64directfb/gyp_configuration.gypi
@@ -24,6 +24,11 @@
     ],
 
     'gl_type': 'none',
+
+    # This should have a default value in cobalt/base.gypi. See the comment
+    # there for acceptable values for this variable.
+    'javascript_engine': 'mozjs',
+    'cobalt_enable_jit': 1,
   },
 
   'target_defaults': {
diff --git a/src/starboard/linux/x64x11/clang/gyp_configuration.gypi b/src/starboard/linux/x64x11/clang/gyp_configuration.gypi
index 2ede477..0df5d5f 100644
--- a/src/starboard/linux/x64x11/clang/gyp_configuration.gypi
+++ b/src/starboard/linux/x64x11/clang/gyp_configuration.gypi
@@ -13,6 +13,12 @@
 # limitations under the License.
 
 {
+  'variables': {
+    # This should have a default value in cobalt/base.gypi. See the comment
+    # there for acceptable values for this variable.
+    'javascript_engine': 'mozjs',
+    'cobalt_enable_jit': 1,
+  },
   'includes': [
     '../libraries.gypi',
     '../../shared/gyp_configuration.gypi',
diff --git a/src/starboard/linux/x64x11/cpp11/atomic_public.h b/src/starboard/linux/x64x11/cpp11/atomic_public.h
new file mode 100644
index 0000000..d74094e
--- /dev/null
+++ b/src/starboard/linux/x64x11/cpp11/atomic_public.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2017 Google Inc. 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_X64X11_CPP11_ATOMIC_PUBLIC_H_
+#define STARBOARD_LINUX_X64X11_CPP11_ATOMIC_PUBLIC_H_
+
+#include "starboard/linux/shared/atomic_public.h"
+
+#endif  // STARBOARD_LINUX_X64X11_CPP11_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/cpp11/compiler_flags.gypi b/src/starboard/linux/x64x11/cpp11/compiler_flags.gypi
new file mode 100644
index 0000000..1210a68
--- /dev/null
+++ b/src/starboard/linux/x64x11/cpp11/compiler_flags.gypi
@@ -0,0 +1,161 @@
+# Copyright 2017 Google Inc. 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.
+
+# Platform specific compiler flags for Linux on Starboard. Included from
+# gyp_configuration.gypi.
+#
+{
+  'variables': {
+    'compiler_flags_host': [
+      '-O2',
+    ],
+    'linker_flags': [
+    ],
+    'compiler_flags_debug': [
+      '-frtti',
+      '-O0',
+    ],
+    'compiler_flags_devel': [
+      '-frtti',
+      '-O2',
+    ],
+    'compiler_flags_qa': [
+      '-fno-rtti',
+      '-O2',
+      '-gline-tables-only',
+    ],
+    'compiler_flags_gold': [
+      '-fno-rtti',
+      '-O2',
+      '-gline-tables-only',
+    ],
+    'conditions': [
+      ['clang==1', {
+        'common_clang_flags': [
+          '-Werror',
+          '-fcolor-diagnostics',
+          # Default visibility to hidden, to enable dead stripping.
+          '-fvisibility=hidden',
+          # Warn for implicit type conversions that may change a value.
+          '-Wconversion',
+          '-Wno-c++11-compat',
+          # This (rightfully) complains about 'override', which we use
+          # heavily.
+          '-Wno-c++11-extensions',
+          # Warns on switches on enums that cover all enum values but
+          # also contain a default: branch. Chrome is full of that.
+          '-Wno-covered-switch-default',
+          # protobuf uses hash_map.
+          '-Wno-deprecated',
+          '-fno-exceptions',
+          # Don't warn about the "struct foo f = {0};" initialization pattern.
+          '-Wno-missing-field-initializers',
+          # Do not warn for implicit sign conversions.
+          '-Wno-sign-conversion',
+          '-fno-strict-aliasing',  # See http://crbug.com/32204
+          '-Wno-unnamed-type-template-args',
+          # Triggered by the COMPILE_ASSERT macro.
+          '-Wno-unused-local-typedef',
+        ],
+      }],
+      ['cobalt_fastbuild==0', {
+        'compiler_flags_debug': [
+          '-g',
+        ],
+        'compiler_flags_devel': [
+          '-g',
+        ],
+        'compiler_flags_qa': [
+          '-gline-tables-only',
+        ],
+        'compiler_flags_gold': [
+          '-gline-tables-only',
+        ],
+      }],
+    ],
+  },
+
+  'target_defaults': {
+    'defines': [
+      # By default, <EGL/eglplatform.h> pulls in some X11 headers that have some
+      # nasty macros (|Status|, for example) that conflict with Chromium base.
+      'MESA_EGL_NO_X11_HEADERS'
+    ],
+    'cflags_c': [
+      # Limit to C99. This allows Linux to be a canary build for any
+      # C11 features that are not supported on some platforms' compilers.
+      '-std=c99',
+    ],
+    'cflags_cc': [
+      '-std=gnu++11',
+      # Don't warn about an implicit exception spec mismatch
+      '-Wno-implicit-exception-spec-mismatch',
+      # Don't warn about inline new and delete
+      '-Wno-inline-new-delete',
+    ],
+    'target_conditions': [
+      ['cobalt_code==1', {
+        'cflags': [
+          '-Wall',
+          '-Wextra',
+          '-Wunreachable-code',
+          '<@(common_clang_flags)',
+        ],
+      },{
+        'cflags': [
+          '<@(common_clang_flags)',
+          # 'this' pointer cannot be NULL...pointer may be assumed
+          # to always convert to true.
+          '-Wno-undefined-bool-conversion',
+          # Skia doesn't use overrides.
+          '-Wno-inconsistent-missing-override',
+          # Do not warn about unused function params.
+          '-Wno-unused-parameter',
+          # Do not warn for implicit type conversions that may change a value.
+          '-Wno-conversion',
+          # shifting a negative signed value is undefined
+          '-Wno-shift-negative-value',
+          # Width of bit-field exceeds width of its type- value will be truncated
+          '-Wno-bitfield-width',
+        ],
+      }],
+      ['use_asan==1', {
+        'cflags': [
+          '-fsanitize=address',
+          '-fno-omit-frame-pointer',
+        ],
+        'ldflags': [
+          '-fsanitize=address',
+          # Force linking of the helpers in sanitizer_options.cc
+          '-Wl,-u_sanitizer_options_link_helper',
+        ],
+        'defines': [
+          'ADDRESS_SANITIZER',
+        ],
+      }],
+      ['use_tsan==1', {
+        'cflags': [
+          '-fsanitize=thread',
+          '-fno-omit-frame-pointer',
+        ],
+        'ldflags': [
+          '-fsanitize=thread',
+        ],
+        'defines': [
+          'THREAD_SANITIZER',
+        ],
+      }],
+    ],
+  }, # end of target_defaults
+}
diff --git a/src/starboard/linux/x64x11/cpp11/configuration_public.h b/src/starboard/linux/x64x11/cpp11/configuration_public.h
new file mode 100644
index 0000000..1c14924
--- /dev/null
+++ b/src/starboard/linux/x64x11/cpp11/configuration_public.h
@@ -0,0 +1,27 @@
+// Copyright 2017 Google Inc. 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 X86 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_X64X11_CPP11_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_LINUX_X64X11_CPP11_CONFIGURATION_PUBLIC_H_
+
+#include "starboard/linux/x64x11/configuration_public.h"
+
+#endif  // STARBOARD_LINUX_X64X11_CPP11_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/cpp11/gyp_configuration.gypi b/src/starboard/linux/x64x11/cpp11/gyp_configuration.gypi
new file mode 100644
index 0000000..fcc5747
--- /dev/null
+++ b/src/starboard/linux/x64x11/cpp11/gyp_configuration.gypi
@@ -0,0 +1,45 @@
+# Copyright 2017 Google Inc. 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': {
+    # This should have a default value in cobalt/base.gypi. See the comment
+    # there for acceptable values for this variable.
+    'javascript_engine': 'mozjs-45',
+    'cobalt_enable_jit': 1,
+  },
+  'target_defaults': {
+    'default_configuration': 'linux-x64x11-cpp11_debug',
+    'configurations': {
+      'linux-x64x11-cpp11_debug': {
+        'inherit_from': ['debug_base'],
+      },
+      'linux-x64x11-cpp11_devel': {
+        'inherit_from': ['devel_base'],
+      },
+      'linux-x64x11-cpp11_qa': {
+        'inherit_from': ['qa_base'],
+      },
+      'linux-x64x11-cpp11_gold': {
+        'inherit_from': ['gold_base'],
+      },
+    }, # end of configurations
+  },
+
+  'includes': [
+    'compiler_flags.gypi',
+    '../libraries.gypi',
+    '../../shared/gyp_configuration.gypi',
+  ],
+}
diff --git a/src/starboard/linux/x64x11/cpp11/gyp_configuration.py b/src/starboard/linux/x64x11/cpp11/gyp_configuration.py
new file mode 100644
index 0000000..c5dab76
--- /dev/null
+++ b/src/starboard/linux/x64x11/cpp11/gyp_configuration.py
@@ -0,0 +1,34 @@
+# Copyright 2017 Google Inc. 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 X64 X11 platform configuration for gyp_cobalt."""
+
+import logging
+import os
+import sys
+
+# Import the shared Linux platform configuration.
+sys.path.append(
+    os.path.realpath(
+        os.path.join(os.path.dirname(__file__), os.pardir, os.pardir,
+                     'shared')))
+# pylint: disable=import-self,g-import-not-at-top
+import gyp_configuration
+
+
+def CreatePlatformConfig():
+  try:
+    return gyp_configuration.PlatformConfig('linux-x64x11-cpp11')
+  except RuntimeError as e:
+    logging.critical(e)
+    return None
diff --git a/src/starboard/linux/x64x11/cpp11/starboard_platform.gyp b/src/starboard/linux/x64x11/cpp11/starboard_platform.gyp
new file mode 100644
index 0000000..42b0bf5
--- /dev/null
+++ b/src/starboard/linux/x64x11/cpp11/starboard_platform.gyp
@@ -0,0 +1,33 @@
+# Copyright 2017 Google Inc. 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': [
+    '../starboard_platform.gypi'
+  ],
+  'targets': [
+    {
+      'target_name': 'starboard_platform',
+      'type': 'static_library',
+      'sources': ['<@(starboard_platform_sources)'],
+      'defines': [
+        # This must be defined when building Starboard, and must not when
+        # building Starboard client code.
+        'STARBOARD_IMPLEMENTATION',
+      ],
+      'dependencies': [
+        '<@(starboard_platform_dependencies)',
+      ],
+    },
+  ],
+}
diff --git a/src/starboard/linux/x64x11/cpp11/thread_types_public.h b/src/starboard/linux/x64x11/cpp11/thread_types_public.h
new file mode 100644
index 0000000..9676b99
--- /dev/null
+++ b/src/starboard/linux/x64x11/cpp11/thread_types_public.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2017 Google Inc. 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_X64X11_CPP11_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_LINUX_X64X11_CPP11_THREAD_TYPES_PUBLIC_H_
+
+#include "starboard/linux/shared/thread_types_public.h"
+
+#endif  // STARBOARD_LINUX_X64X11_CPP11_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/gcc/gyp_configuration.gypi b/src/starboard/linux/x64x11/gcc/gyp_configuration.gypi
index 2ede477..0df5d5f 100644
--- a/src/starboard/linux/x64x11/gcc/gyp_configuration.gypi
+++ b/src/starboard/linux/x64x11/gcc/gyp_configuration.gypi
@@ -13,6 +13,12 @@
 # limitations under the License.
 
 {
+  'variables': {
+    # This should have a default value in cobalt/base.gypi. See the comment
+    # there for acceptable values for this variable.
+    'javascript_engine': 'mozjs',
+    'cobalt_enable_jit': 1,
+  },
   'includes': [
     '../libraries.gypi',
     '../../shared/gyp_configuration.gypi',
diff --git a/src/starboard/linux/x64x11/gyp_configuration.gypi b/src/starboard/linux/x64x11/gyp_configuration.gypi
index 78216dd..1203c9d 100644
--- a/src/starboard/linux/x64x11/gyp_configuration.gypi
+++ b/src/starboard/linux/x64x11/gyp_configuration.gypi
@@ -13,6 +13,12 @@
 # limitations under the License.
 
 {
+  'variables': {
+    # This should have a default value in cobalt/base.gypi. See the comment
+    # there for acceptable values for this variable.
+    'javascript_engine': 'mozjs',
+    'cobalt_enable_jit': 1,
+  },
   'target_defaults': {
     'default_configuration': 'linux-x64x11_debug',
     'configurations': {
diff --git a/src/starboard/media.h b/src/starboard/media.h
index 3e82d17..1b11b8f 100644
--- a/src/starboard/media.h
+++ b/src/starboard/media.h
@@ -289,7 +289,43 @@
 // with standard digital imaging. See the Consumer Electronics
 // Association press release:
 // https://www.cta.tech/News/Press-Releases/2015/August/CEA-Defines-%E2%80%98HDR-Compatible%E2%80%99-Displays.aspx
-typedef struct SbMediaHdrMetadataColorSpace {
+typedef struct SbMediaColorMetadata {
+  // Number of decoded bits per channel. A value of 0 indicates that
+  // the BitsPerChannel is unspecified.
+  unsigned int bits_per_channel;
+
+  // The amount of pixels to remove in the Cr and Cb channels for
+  // every pixel not removed horizontally. Example: For video with
+  // 4:2:0 chroma subsampling, the |chroma_subsampling_horizontal| should be set
+  // to 1.
+  unsigned int chroma_subsampling_horizontal;
+
+  // The amount of pixels to remove in the Cr and Cb channels for
+  // every pixel not removed vertically. Example: For video with
+  // 4:2:0 chroma subsampling, the |chroma_subsampling_vertical| should be set
+  // to 1.
+  unsigned int chroma_subsampling_vertical;
+
+  // The amount of pixels to remove in the Cb channel for every pixel
+  // not removed horizontally. This is additive with
+  // ChromaSubsamplingHorz. Example: For video with 4:2:1 chroma
+  // subsampling, the |chroma_subsampling_horizontal| should be set to 1 and
+  // |cb_subsampling_horizontal| should be set to 1.
+  unsigned int cb_subsampling_horizontal;
+
+  // The amount of pixels to remove in the Cb channel for every pixel
+  // not removed vertically. This is additive with
+  // |chroma_subsampling_vertical|.
+  unsigned int cb_subsampling_vertical;
+
+  // How chroma is subsampled horizontally. (0: Unspecified, 1: Left
+  // Collocated, 2: Half)
+  unsigned int chroma_siting_horizontal;
+
+  // How chroma is subsampled vertically. (0: Unspecified, 1: Top
+  // Collocated, 2: Half)
+  unsigned int chroma_siting_vertical;
+
   // [HDR Metadata field] SMPTE 2086 mastering data.
   SbMediaMasteringMetadata mastering_metadata;
 
@@ -347,7 +383,7 @@
   // submatrix of the 4 x 4 transform matrix.  The 4th row is
   // completed as (0, 0, 0, 1).
   float custom_primary_matrix[12];
-} SbMediaHdrMetadataColorSpace;
+} SbMediaColorMetadata;
 #endif  // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
 
 // The set of information required by the decoder or player for each video
@@ -368,13 +404,15 @@
   int frame_height;
 
 #if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
-  // HDR metadata common for HDR10 and WeB/VP9-based HDR formats as
-  // well as the Color Space: MatrixCoefficients, Range,
-  // TansferCharacteristics, and Primaries described here:
-  // https://matroska.org/technical/specs/index.html
-  // This will only be specified on frames where the HDR metadata and color
-  // space might have changed (e.g. keyframes).
-  SbMediaHdrMetadataColorSpace* hdr_metadata_color_space;
+  // HDR metadata common for HDR10 and WebM/VP9-based HDR formats as
+  // well as the Color Space, and Color elements: MatrixCoefficients,
+  // BitsPerChannel, ChromaSubsamplingHorz, ChromaSubsamplingVert,
+  // CbSubsamplingHorz, CbSubsamplingVert, ChromaSitingHorz,
+  // ChromaSitingVert, Range, TransferCharacteristics, and Primaries
+  // described here: https://matroska.org/technical/specs/index.html .
+  // This will only be specified on frames where the HDR metadata and
+  // color / color space might have changed (e.g. keyframes).
+  SbMediaColorMetadata* color_metadata;
 #endif
 } SbMediaVideoSampleInfo;
 
diff --git a/src/starboard/nplb/audio_sink_create_test.cc b/src/starboard/nplb/audio_sink_create_test.cc
index 22a6d7e..d4702cf 100644
--- a/src/starboard/nplb/audio_sink_create_test.cc
+++ b/src/starboard/nplb/audio_sink_create_test.cc
@@ -1,229 +1,229 @@
-// Copyright 2016 Google Inc. 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/audio_sink.h"

-

-#include <vector>

-

-#include "starboard/nplb/audio_sink_helpers.h"

-#include "testing/gtest/include/gtest/gtest.h"

-

-namespace starboard {

-namespace nplb {

-

-namespace {

-

-void UpdateSourceStatusFuncStub(int* frames_in_buffer,

-                                int* offset_in_frames,

-                                bool* is_playing,

-                                bool* is_eos_reached,

-                                void* context) {}

-void ConsumeFramesFuncStub(int frames_consumed, void* context) {}

-

-}  // namespace

-

-TEST(SbAudioSinkCreateTest, SunnyDay) {

-  ASSERT_GE(SbAudioSinkGetMaxChannels(), 1);

-

-  AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());

-

-  SbAudioSink audio_sink = SbAudioSinkCreate(

-      frame_buffers.channels(),

-      SbAudioSinkGetNearestSupportedSampleFrequency(44100),

-      frame_buffers.sample_type(), frame_buffers.storage_type(),

-      frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),

-      UpdateSourceStatusFuncStub, ConsumeFramesFuncStub,

-      reinterpret_cast<void*>(1));

-  EXPECT_TRUE(SbAudioSinkIsValid(audio_sink));

-  SbAudioSinkDestroy(audio_sink);

-}

-

-TEST(SbAudioSinkCreateTest, SunnyDayAllCombinations) {

-  std::vector<SbMediaAudioSampleType> sample_types;

-  if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeInt16)) {

-    sample_types.push_back(kSbMediaAudioSampleTypeInt16);

-  }

-  if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)) {

-    sample_types.push_back(kSbMediaAudioSampleTypeFloat32);

-  }

-

-  std::vector<SbMediaAudioFrameStorageType> storage_types;

-  if (SbAudioSinkIsAudioFrameStorageTypeSupported(

-          kSbMediaAudioFrameStorageTypeInterleaved)) {

-    storage_types.push_back(kSbMediaAudioFrameStorageTypeInterleaved);

-  }

-  if (SbAudioSinkIsAudioFrameStorageTypeSupported(

-          kSbMediaAudioFrameStorageTypePlanar)) {

-    storage_types.push_back(kSbMediaAudioFrameStorageTypePlanar);

-  }

-

-  for (size_t sample_type = 0; sample_type < sample_types.size();

-       ++sample_type) {

-    for (size_t storage_type = 0; storage_type < storage_types.size();

-         ++storage_type) {

-      AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels(),

-                                              sample_types[sample_type],

-                                              storage_types[storage_type]);

-      // It should at least support stereo and the claimed max channels.

-      SbAudioSink audio_sink = SbAudioSinkCreate(

-          frame_buffers.channels(),

-          SbAudioSinkGetNearestSupportedSampleFrequency(44100),

-          frame_buffers.sample_type(), frame_buffers.storage_type(),

-          frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),

-          UpdateSourceStatusFuncStub, ConsumeFramesFuncStub, NULL);

-      EXPECT_TRUE(SbAudioSinkIsValid(audio_sink));

-      SbAudioSinkDestroy(audio_sink);

-

-      if (SbAudioSinkGetMaxChannels() > 2) {

-        AudioSinkTestFrameBuffers frame_buffers(2, sample_types[sample_type],

-                                                storage_types[storage_type]);

-        audio_sink = SbAudioSinkCreate(

-            frame_buffers.channels(),

-            SbAudioSinkGetNearestSupportedSampleFrequency(44100),

-            frame_buffers.sample_type(), frame_buffers.storage_type(),

-            frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),

-            UpdateSourceStatusFuncStub, ConsumeFramesFuncStub, NULL);

-        EXPECT_TRUE(SbAudioSinkIsValid(audio_sink));

-        SbAudioSinkDestroy(audio_sink);

-      }

-    }

-  }

-}

-

-TEST(SbAudioSinkCreateTest, RainyDayInvalidChannels) {

-  AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());

-  SbAudioSink audio_sink = SbAudioSinkCreate(

-      0,  // |channels| is 0

-      SbAudioSinkGetNearestSupportedSampleFrequency(44100),

-      frame_buffers.sample_type(), frame_buffers.storage_type(),

-      frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),

-      UpdateSourceStatusFuncStub, ConsumeFramesFuncStub, NULL);

-  EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));

-}

-

-TEST(SbAudioSinkCreateTest, RainyDayInvalidFrequency) {

-  AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());

-  SbAudioSink audio_sink = SbAudioSinkCreate(

-      frame_buffers.channels(), 0,  // |sampling_frequency_hz| is 0

-      frame_buffers.sample_type(), frame_buffers.storage_type(),

-      frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),

-      UpdateSourceStatusFuncStub, ConsumeFramesFuncStub, NULL);

-  EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));

-

-  const int kOddFrequency = 12345;

-  if (SbAudioSinkGetNearestSupportedSampleFrequency(kOddFrequency) !=

-      kOddFrequency) {

-    audio_sink = SbAudioSinkCreate(

-        frame_buffers.channels(),

-        kOddFrequency,  // |sampling_frequency_hz| is unsupported

-        frame_buffers.sample_type(), frame_buffers.storage_type(),

-        frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),

-        UpdateSourceStatusFuncStub, ConsumeFramesFuncStub, NULL);

-    EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));

-  }

-}

-

-TEST(SbAudioSinkCreateTest, RainyDayInvalidSampleType) {

-  SbMediaAudioSampleType invalid_sample_type;

-  if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeInt16)) {

-    if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)) {

-      return;

-    }

-    invalid_sample_type = kSbMediaAudioSampleTypeFloat32;

-  } else {

-    invalid_sample_type = kSbMediaAudioSampleTypeInt16;

-  }

-

-  AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels(),

-                                          invalid_sample_type);

-  SbAudioSink audio_sink = SbAudioSinkCreate(

-      SbAudioSinkGetMaxChannels(),

-      SbAudioSinkGetNearestSupportedSampleFrequency(44100),

-      invalid_sample_type,  // |sample_type| is invalid

-      frame_buffers.storage_type(), frame_buffers.frame_buffers(),

-      frame_buffers.frames_per_channel(), UpdateSourceStatusFuncStub,

-      ConsumeFramesFuncStub, NULL);

-  EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));

-}

-

-TEST(SbAudioSinkCreateTest, RainyDayInvalidFrameStorageType) {

-  SbMediaAudioFrameStorageType invalid_storage_type;

-  if (SbAudioSinkIsAudioFrameStorageTypeSupported(

-          kSbMediaAudioFrameStorageTypeInterleaved)) {

-    if (SbAudioSinkIsAudioFrameStorageTypeSupported(

-            kSbMediaAudioFrameStorageTypePlanar)) {

-      return;

-    }

-    invalid_storage_type = kSbMediaAudioFrameStorageTypePlanar;

-  } else {

-    invalid_storage_type = kSbMediaAudioFrameStorageTypeInterleaved;

-  }

-

-  AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels(),

-                                          invalid_storage_type);

-  SbAudioSink audio_sink = SbAudioSinkCreate(

-      SbAudioSinkGetMaxChannels(),

-      SbAudioSinkGetNearestSupportedSampleFrequency(44100),

-      frame_buffers.sample_type(),

-      invalid_storage_type,  // |storage_type| is invalid

-      frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),

-      UpdateSourceStatusFuncStub, ConsumeFramesFuncStub, NULL);

-  EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));

-}

-

-TEST(SbAudioSinkCreateTest, RainyDayInvalidFrameBuffers) {

-  AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());

-  SbAudioSink audio_sink = SbAudioSinkCreate(

-      frame_buffers.channels(),

-      SbAudioSinkGetNearestSupportedSampleFrequency(44100),

-      frame_buffers.sample_type(), frame_buffers.storage_type(),

-      NULL,  // |frame_buffers| is NULL

-      frame_buffers.frames_per_channel(), UpdateSourceStatusFuncStub,

-      ConsumeFramesFuncStub, NULL);

-  EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));

-

-  audio_sink = SbAudioSinkCreate(

-      frame_buffers.channels(),

-      SbAudioSinkGetNearestSupportedSampleFrequency(44100),

-      frame_buffers.sample_type(), frame_buffers.storage_type(),

-      frame_buffers.frame_buffers(),

-      0,  // |frames_per_channel| is 0

-      UpdateSourceStatusFuncStub, ConsumeFramesFuncStub, NULL);

-  EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));

-}

-

-TEST(SbAudioSinkCreateTest, RainyDayInvalidCallback) {

-  AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());

-  SbAudioSink audio_sink = SbAudioSinkCreate(

-      frame_buffers.channels(),

-      SbAudioSinkGetNearestSupportedSampleFrequency(44100),

-      frame_buffers.sample_type(), frame_buffers.storage_type(),

-      frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),

-      NULL,  // |update_source_status_func| is NULL

-      ConsumeFramesFuncStub, NULL);

-  EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));

-

-  audio_sink = SbAudioSinkCreate(

-      frame_buffers.channels(),

-      SbAudioSinkGetNearestSupportedSampleFrequency(44100),

-      frame_buffers.sample_type(), frame_buffers.storage_type(),

-      frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),

-      UpdateSourceStatusFuncStub,

-      NULL,  // |consume_frames_func| is NULL

-      NULL);

-  EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));

-}

-

-}  // namespace nplb

-}  // namespace starboard

+// Copyright 2016 Google Inc. 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/audio_sink.h"
+
+#include <vector>
+
+#include "starboard/nplb/audio_sink_helpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace nplb {
+
+namespace {
+
+void UpdateSourceStatusFuncStub(int* frames_in_buffer,
+                                int* offset_in_frames,
+                                bool* is_playing,
+                                bool* is_eos_reached,
+                                void* context) {}
+void ConsumeFramesFuncStub(int frames_consumed, void* context) {}
+
+}  // namespace
+
+TEST(SbAudioSinkCreateTest, SunnyDay) {
+  ASSERT_GE(SbAudioSinkGetMaxChannels(), 1);
+
+  AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
+
+  SbAudioSink audio_sink = SbAudioSinkCreate(
+      frame_buffers.channels(),
+      SbAudioSinkGetNearestSupportedSampleFrequency(44100),
+      frame_buffers.sample_type(), frame_buffers.storage_type(),
+      frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),
+      UpdateSourceStatusFuncStub, ConsumeFramesFuncStub,
+      reinterpret_cast<void*>(1));
+  EXPECT_TRUE(SbAudioSinkIsValid(audio_sink));
+  SbAudioSinkDestroy(audio_sink);
+}
+
+TEST(SbAudioSinkCreateTest, SunnyDayAllCombinations) {
+  std::vector<SbMediaAudioSampleType> sample_types;
+  if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeInt16)) {
+    sample_types.push_back(kSbMediaAudioSampleTypeInt16);
+  }
+  if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)) {
+    sample_types.push_back(kSbMediaAudioSampleTypeFloat32);
+  }
+
+  std::vector<SbMediaAudioFrameStorageType> storage_types;
+  if (SbAudioSinkIsAudioFrameStorageTypeSupported(
+          kSbMediaAudioFrameStorageTypeInterleaved)) {
+    storage_types.push_back(kSbMediaAudioFrameStorageTypeInterleaved);
+  }
+  if (SbAudioSinkIsAudioFrameStorageTypeSupported(
+          kSbMediaAudioFrameStorageTypePlanar)) {
+    storage_types.push_back(kSbMediaAudioFrameStorageTypePlanar);
+  }
+
+  for (size_t sample_type = 0; sample_type < sample_types.size();
+       ++sample_type) {
+    for (size_t storage_type = 0; storage_type < storage_types.size();
+         ++storage_type) {
+      AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels(),
+                                              sample_types[sample_type],
+                                              storage_types[storage_type]);
+      // It should at least support stereo and the claimed max channels.
+      SbAudioSink audio_sink = SbAudioSinkCreate(
+          frame_buffers.channels(),
+          SbAudioSinkGetNearestSupportedSampleFrequency(44100),
+          frame_buffers.sample_type(), frame_buffers.storage_type(),
+          frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),
+          UpdateSourceStatusFuncStub, ConsumeFramesFuncStub, NULL);
+      EXPECT_TRUE(SbAudioSinkIsValid(audio_sink));
+      SbAudioSinkDestroy(audio_sink);
+
+      if (SbAudioSinkGetMaxChannels() > 2) {
+        AudioSinkTestFrameBuffers frame_buffers(2, sample_types[sample_type],
+                                                storage_types[storage_type]);
+        audio_sink = SbAudioSinkCreate(
+            frame_buffers.channels(),
+            SbAudioSinkGetNearestSupportedSampleFrequency(44100),
+            frame_buffers.sample_type(), frame_buffers.storage_type(),
+            frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),
+            UpdateSourceStatusFuncStub, ConsumeFramesFuncStub, NULL);
+        EXPECT_TRUE(SbAudioSinkIsValid(audio_sink));
+        SbAudioSinkDestroy(audio_sink);
+      }
+    }
+  }
+}
+
+TEST(SbAudioSinkCreateTest, RainyDayInvalidChannels) {
+  AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
+  SbAudioSink audio_sink = SbAudioSinkCreate(
+      0,  // |channels| is 0
+      SbAudioSinkGetNearestSupportedSampleFrequency(44100),
+      frame_buffers.sample_type(), frame_buffers.storage_type(),
+      frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),
+      UpdateSourceStatusFuncStub, ConsumeFramesFuncStub, NULL);
+  EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));
+}
+
+TEST(SbAudioSinkCreateTest, RainyDayInvalidFrequency) {
+  AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
+  SbAudioSink audio_sink = SbAudioSinkCreate(
+      frame_buffers.channels(), 0,  // |sampling_frequency_hz| is 0
+      frame_buffers.sample_type(), frame_buffers.storage_type(),
+      frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),
+      UpdateSourceStatusFuncStub, ConsumeFramesFuncStub, NULL);
+  EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));
+
+  const int kOddFrequency = 12345;
+  if (SbAudioSinkGetNearestSupportedSampleFrequency(kOddFrequency) !=
+      kOddFrequency) {
+    audio_sink = SbAudioSinkCreate(
+        frame_buffers.channels(),
+        kOddFrequency,  // |sampling_frequency_hz| is unsupported
+        frame_buffers.sample_type(), frame_buffers.storage_type(),
+        frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),
+        UpdateSourceStatusFuncStub, ConsumeFramesFuncStub, NULL);
+    EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));
+  }
+}
+
+TEST(SbAudioSinkCreateTest, RainyDayInvalidSampleType) {
+  SbMediaAudioSampleType invalid_sample_type;
+  if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeInt16)) {
+    if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)) {
+      return;
+    }
+    invalid_sample_type = kSbMediaAudioSampleTypeFloat32;
+  } else {
+    invalid_sample_type = kSbMediaAudioSampleTypeInt16;
+  }
+
+  AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels(),
+                                          invalid_sample_type);
+  SbAudioSink audio_sink = SbAudioSinkCreate(
+      SbAudioSinkGetMaxChannels(),
+      SbAudioSinkGetNearestSupportedSampleFrequency(44100),
+      invalid_sample_type,  // |sample_type| is invalid
+      frame_buffers.storage_type(), frame_buffers.frame_buffers(),
+      frame_buffers.frames_per_channel(), UpdateSourceStatusFuncStub,
+      ConsumeFramesFuncStub, NULL);
+  EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));
+}
+
+TEST(SbAudioSinkCreateTest, RainyDayInvalidFrameStorageType) {
+  SbMediaAudioFrameStorageType invalid_storage_type;
+  if (SbAudioSinkIsAudioFrameStorageTypeSupported(
+          kSbMediaAudioFrameStorageTypeInterleaved)) {
+    if (SbAudioSinkIsAudioFrameStorageTypeSupported(
+            kSbMediaAudioFrameStorageTypePlanar)) {
+      return;
+    }
+    invalid_storage_type = kSbMediaAudioFrameStorageTypePlanar;
+  } else {
+    invalid_storage_type = kSbMediaAudioFrameStorageTypeInterleaved;
+  }
+
+  AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels(),
+                                          invalid_storage_type);
+  SbAudioSink audio_sink = SbAudioSinkCreate(
+      SbAudioSinkGetMaxChannels(),
+      SbAudioSinkGetNearestSupportedSampleFrequency(44100),
+      frame_buffers.sample_type(),
+      invalid_storage_type,  // |storage_type| is invalid
+      frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),
+      UpdateSourceStatusFuncStub, ConsumeFramesFuncStub, NULL);
+  EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));
+}
+
+TEST(SbAudioSinkCreateTest, RainyDayInvalidFrameBuffers) {
+  AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
+  SbAudioSink audio_sink = SbAudioSinkCreate(
+      frame_buffers.channels(),
+      SbAudioSinkGetNearestSupportedSampleFrequency(44100),
+      frame_buffers.sample_type(), frame_buffers.storage_type(),
+      NULL,  // |frame_buffers| is NULL
+      frame_buffers.frames_per_channel(), UpdateSourceStatusFuncStub,
+      ConsumeFramesFuncStub, NULL);
+  EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));
+
+  audio_sink = SbAudioSinkCreate(
+      frame_buffers.channels(),
+      SbAudioSinkGetNearestSupportedSampleFrequency(44100),
+      frame_buffers.sample_type(), frame_buffers.storage_type(),
+      frame_buffers.frame_buffers(),
+      0,  // |frames_per_channel| is 0
+      UpdateSourceStatusFuncStub, ConsumeFramesFuncStub, NULL);
+  EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));
+}
+
+TEST(SbAudioSinkCreateTest, RainyDayInvalidCallback) {
+  AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
+  SbAudioSink audio_sink = SbAudioSinkCreate(
+      frame_buffers.channels(),
+      SbAudioSinkGetNearestSupportedSampleFrequency(44100),
+      frame_buffers.sample_type(), frame_buffers.storage_type(),
+      frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),
+      NULL,  // |update_source_status_func| is NULL
+      ConsumeFramesFuncStub, NULL);
+  EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));
+
+  audio_sink = SbAudioSinkCreate(
+      frame_buffers.channels(),
+      SbAudioSinkGetNearestSupportedSampleFrequency(44100),
+      frame_buffers.sample_type(), frame_buffers.storage_type(),
+      frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),
+      UpdateSourceStatusFuncStub,
+      NULL,  // |consume_frames_func| is NULL
+      NULL);
+  EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));
+}
+
+}  // namespace nplb
+}  // namespace starboard
diff --git a/src/starboard/nplb/audio_sink_destroy_test.cc b/src/starboard/nplb/audio_sink_destroy_test.cc
index 3f93a3e..cc461ed 100644
--- a/src/starboard/nplb/audio_sink_destroy_test.cc
+++ b/src/starboard/nplb/audio_sink_destroy_test.cc
@@ -1,26 +1,26 @@
-// Copyright 2016 Google Inc. 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/audio_sink.h"

-#include "testing/gtest/include/gtest/gtest.h"

-

-namespace starboard {

-namespace nplb {

-

-TEST(SbAudioSinkCreateTest, DestroyInvalidAudioSink) {

-  SbAudioSinkDestroy(kSbAudioSinkInvalid);

-}

-

-}  // namespace nplb

-}  // namespace starboard

+// Copyright 2016 Google Inc. 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/audio_sink.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace nplb {
+
+TEST(SbAudioSinkCreateTest, DestroyInvalidAudioSink) {
+  SbAudioSinkDestroy(kSbAudioSinkInvalid);
+}
+
+}  // namespace nplb
+}  // namespace starboard
diff --git a/src/starboard/nplb/audio_sink_get_max_channels_test.cc b/src/starboard/nplb/audio_sink_get_max_channels_test.cc
index a61af9a..ea88467 100644
--- a/src/starboard/nplb/audio_sink_get_max_channels_test.cc
+++ b/src/starboard/nplb/audio_sink_get_max_channels_test.cc
@@ -1,29 +1,29 @@
-// Copyright 2016 Google Inc. 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/audio_sink.h"

-#include "testing/gtest/include/gtest/gtest.h"

-

-namespace starboard {

-namespace nplb {

-

-TEST(SbAudioSinkGetMaxChannelsTest, SunnyDay) {

-  int max_channels = SbAudioSinkGetMaxChannels();

-  EXPECT_GT(max_channels, 0);

-  // See if it reports anything ridiculous.

-  EXPECT_LT(max_channels, 99);

-}

-

-}  // namespace nplb

-}  // namespace starboard

+// Copyright 2016 Google Inc. 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/audio_sink.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace nplb {
+
+TEST(SbAudioSinkGetMaxChannelsTest, SunnyDay) {
+  int max_channels = SbAudioSinkGetMaxChannels();
+  EXPECT_GT(max_channels, 0);
+  // See if it reports anything ridiculous.
+  EXPECT_LT(max_channels, 99);
+}
+
+}  // namespace nplb
+}  // namespace starboard
diff --git a/src/starboard/nplb/audio_sink_get_nearest_supported_sample_frequency_test.cc b/src/starboard/nplb/audio_sink_get_nearest_supported_sample_frequency_test.cc
index 0fd816f..5f8d0d8 100644
--- a/src/starboard/nplb/audio_sink_get_nearest_supported_sample_frequency_test.cc
+++ b/src/starboard/nplb/audio_sink_get_nearest_supported_sample_frequency_test.cc
@@ -1,60 +1,60 @@
-// Copyright 2016 Google Inc. 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/audio_sink.h"

-

-#include <limits>

-

-#include "testing/gtest/include/gtest/gtest.h"

-

-namespace starboard {

-namespace nplb {

-

-namespace {

-const int kFrequencyCD = 44100;

-}  // namespace

-

-TEST(SbAudioSinkGetNearestSupportedSampleFrequencyTest, SunnyDay) {

-  int nearest_frequency =

-      SbAudioSinkGetNearestSupportedSampleFrequency(kFrequencyCD);

-  EXPECT_GT(nearest_frequency, 0);

-  EXPECT_EQ(nearest_frequency,

-            SbAudioSinkGetNearestSupportedSampleFrequency(nearest_frequency));

-}

-

-TEST(SbAudioSinkGetNearestSupportedSampleFrequencyTest, ZeroFrequency) {

-  int nearest_frequency = SbAudioSinkGetNearestSupportedSampleFrequency(0);

-  EXPECT_GT(nearest_frequency, 0);

-}

-

-TEST(SbAudioSinkGetNearestSupportedSampleFrequencyTest, SnapUp) {

-  int nearest_frequency = SbAudioSinkGetNearestSupportedSampleFrequency(1);

-  EXPECT_GT(nearest_frequency, 0);

-  if (nearest_frequency > 1) {

-    EXPECT_EQ(nearest_frequency,

-              SbAudioSinkGetNearestSupportedSampleFrequency(2));

-  }

-}

-

-TEST(SbAudioSinkGetNearestSupportedSampleFrequencyTest, Snap) {

-  int nearest_frequency = SbAudioSinkGetNearestSupportedSampleFrequency(

-      std::numeric_limits<int>::max());

-  if (nearest_frequency < std::numeric_limits<int>::max()) {

-    EXPECT_EQ(nearest_frequency, SbAudioSinkGetNearestSupportedSampleFrequency(

-                                     std::numeric_limits<int>::max() - 1));

-  }

-}

-

-}  // namespace nplb

-}  // namespace starboard

+// Copyright 2016 Google Inc. 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/audio_sink.h"
+
+#include <limits>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace nplb {
+
+namespace {
+const int kFrequencyCD = 44100;
+}  // namespace
+
+TEST(SbAudioSinkGetNearestSupportedSampleFrequencyTest, SunnyDay) {
+  int nearest_frequency =
+      SbAudioSinkGetNearestSupportedSampleFrequency(kFrequencyCD);
+  EXPECT_GT(nearest_frequency, 0);
+  EXPECT_EQ(nearest_frequency,
+            SbAudioSinkGetNearestSupportedSampleFrequency(nearest_frequency));
+}
+
+TEST(SbAudioSinkGetNearestSupportedSampleFrequencyTest, ZeroFrequency) {
+  int nearest_frequency = SbAudioSinkGetNearestSupportedSampleFrequency(0);
+  EXPECT_GT(nearest_frequency, 0);
+}
+
+TEST(SbAudioSinkGetNearestSupportedSampleFrequencyTest, SnapUp) {
+  int nearest_frequency = SbAudioSinkGetNearestSupportedSampleFrequency(1);
+  EXPECT_GT(nearest_frequency, 0);
+  if (nearest_frequency > 1) {
+    EXPECT_EQ(nearest_frequency,
+              SbAudioSinkGetNearestSupportedSampleFrequency(2));
+  }
+}
+
+TEST(SbAudioSinkGetNearestSupportedSampleFrequencyTest, Snap) {
+  int nearest_frequency = SbAudioSinkGetNearestSupportedSampleFrequency(
+      std::numeric_limits<int>::max());
+  if (nearest_frequency < std::numeric_limits<int>::max()) {
+    EXPECT_EQ(nearest_frequency, SbAudioSinkGetNearestSupportedSampleFrequency(
+                                     std::numeric_limits<int>::max() - 1));
+  }
+}
+
+}  // namespace nplb
+}  // namespace starboard
diff --git a/src/starboard/nplb/audio_sink_is_audio_frame_storage_type_supported_test.cc b/src/starboard/nplb/audio_sink_is_audio_frame_storage_type_supported_test.cc
index 372f970..ce49962 100644
--- a/src/starboard/nplb/audio_sink_is_audio_frame_storage_type_supported_test.cc
+++ b/src/starboard/nplb/audio_sink_is_audio_frame_storage_type_supported_test.cc
@@ -1,31 +1,31 @@
-// Copyright 2016 Google Inc. 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/audio_sink.h"

-#include "testing/gtest/include/gtest/gtest.h"

-

-namespace starboard {

-namespace nplb {

-

-TEST(SbAudioSinkIsAudioFrameStorageTypeSupportedTest, SunnyDay) {

-  bool interleaved_supported = SbAudioSinkIsAudioFrameStorageTypeSupported(

-      kSbMediaAudioFrameStorageTypeInterleaved);

-  bool planar_supported = SbAudioSinkIsAudioFrameStorageTypeSupported(

-      kSbMediaAudioFrameStorageTypePlanar);

-  // A platform must support at least one of the frame storage types.

-  EXPECT_TRUE(interleaved_supported || planar_supported);

-}

-

-}  // namespace nplb

-}  // namespace starboard

+// Copyright 2016 Google Inc. 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/audio_sink.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace nplb {
+
+TEST(SbAudioSinkIsAudioFrameStorageTypeSupportedTest, SunnyDay) {
+  bool interleaved_supported = SbAudioSinkIsAudioFrameStorageTypeSupported(
+      kSbMediaAudioFrameStorageTypeInterleaved);
+  bool planar_supported = SbAudioSinkIsAudioFrameStorageTypeSupported(
+      kSbMediaAudioFrameStorageTypePlanar);
+  // A platform must support at least one of the frame storage types.
+  EXPECT_TRUE(interleaved_supported || planar_supported);
+}
+
+}  // namespace nplb
+}  // namespace starboard
diff --git a/src/starboard/nplb/audio_sink_is_audio_sample_type_supported_test.cc b/src/starboard/nplb/audio_sink_is_audio_sample_type_supported_test.cc
index 2498eec..b1adfbf 100644
--- a/src/starboard/nplb/audio_sink_is_audio_sample_type_supported_test.cc
+++ b/src/starboard/nplb/audio_sink_is_audio_sample_type_supported_test.cc
@@ -1,31 +1,31 @@
-// Copyright 2016 Google Inc. 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/audio_sink.h"

-#include "testing/gtest/include/gtest/gtest.h"

-

-namespace starboard {

-namespace nplb {

-

-TEST(SbAudioSinkIsAudioSampleTypeSupportedTest, SunnyDay) {

-  bool int16_supported =

-      SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeInt16);

-  bool float32_supported =

-      SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32);

-  // A platform must support at least one of the sample types.

-  EXPECT_TRUE(int16_supported || float32_supported);

-}

-

-}  // namespace nplb

-}  // namespace starboard

+// Copyright 2016 Google Inc. 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/audio_sink.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace nplb {
+
+TEST(SbAudioSinkIsAudioSampleTypeSupportedTest, SunnyDay) {
+  bool int16_supported =
+      SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeInt16);
+  bool float32_supported =
+      SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32);
+  // A platform must support at least one of the sample types.
+  EXPECT_TRUE(int16_supported || float32_supported);
+}
+
+}  // namespace nplb
+}  // namespace starboard
diff --git a/src/starboard/nplb/audio_sink_test.cc b/src/starboard/nplb/audio_sink_test.cc
index a40cf5e..a48011c 100644
--- a/src/starboard/nplb/audio_sink_test.cc
+++ b/src/starboard/nplb/audio_sink_test.cc
@@ -1,95 +1,95 @@
-// Copyright 2016 Google Inc. 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/audio_sink.h"

-

-#include <algorithm>

-

-#include "starboard/nplb/audio_sink_helpers.h"

-#include "testing/gtest/include/gtest/gtest.h"

-

-namespace starboard {

-namespace nplb {

-

-TEST(SbAudioSinkTest, UpdateStatusCalled) {

-  AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());

-  AudioSinkTestEnvironment environment(frame_buffers);

-

-  EXPECT_TRUE(environment.WaitUntilUpdateStatusCalled());

-  EXPECT_TRUE(environment.WaitUntilUpdateStatusCalled());

-}

-

-TEST(SbAudioSinkTest, SomeFramesConsumed) {

-  AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());

-  AudioSinkTestEnvironment environment(frame_buffers);

-

-  environment.AppendFrame(1);

-  EXPECT_TRUE(environment.WaitUntilSomeFramesAreConsumed());

-}

-

-TEST(SbAudioSinkTest, AllFramesConsumed) {

-  AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());

-  AudioSinkTestEnvironment environment(frame_buffers);

-

-  environment.AppendFrame(1024);

-  EXPECT_TRUE(environment.WaitUntilAllFramesAreConsumed());

-}

-

-TEST(SbAudioSinkTest, MultipleAppendAndConsume) {

-  AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());

-  AudioSinkTestEnvironment environment(frame_buffers);

-

-  int frames_to_append = frame_buffers.frames_per_channel();

-

-  environment.AppendFrame(frames_to_append / 2);

-  EXPECT_TRUE(environment.WaitUntilSomeFramesAreConsumed());

-  environment.AppendFrame(frames_to_append / 2);

-  EXPECT_TRUE(environment.WaitUntilAllFramesAreConsumed());

-}

-

-TEST(SbAudioSinkTest, Pause) {

-  AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());

-  AudioSinkTestEnvironment environment(frame_buffers);

-  environment.SetIsPlaying(false);

-

-  int frames_to_append = frame_buffers.frames_per_channel();

-

-  environment.AppendFrame(frames_to_append / 2);

-  int free_space = environment.GetFrameBufferFreeSpaceAmount();

-  EXPECT_TRUE(environment.WaitUntilUpdateStatusCalled());

-  EXPECT_TRUE(environment.WaitUntilUpdateStatusCalled());

-  EXPECT_EQ(free_space, environment.GetFrameBufferFreeSpaceAmount());

-  environment.SetIsPlaying(true);

-  EXPECT_TRUE(environment.WaitUntilSomeFramesAreConsumed());

-}

-

-TEST(SbAudioSinkTest, ContinuousAppend) {

-  AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());

-

-  AudioSinkTestEnvironment environment(frame_buffers);

-  int sample_rate = environment.sample_rate();

-  // We are trying to send 1/4s worth of audio samples.

-  int frames_to_append = sample_rate / 4;

-

-  while (frames_to_append > 0) {

-    int free_space = environment.GetFrameBufferFreeSpaceAmount();

-    environment.AppendFrame(std::min(free_space, frames_to_append));

-    frames_to_append -= std::min(free_space, frames_to_append);

-    ASSERT_TRUE(environment.WaitUntilSomeFramesAreConsumed());

-  }

-  EXPECT_TRUE(environment.WaitUntilAllFramesAreConsumed());

-}

-

-}  // namespace nplb

-}  // namespace starboard

+// Copyright 2016 Google Inc. 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/audio_sink.h"
+
+#include <algorithm>
+
+#include "starboard/nplb/audio_sink_helpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace nplb {
+
+TEST(SbAudioSinkTest, UpdateStatusCalled) {
+  AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
+  AudioSinkTestEnvironment environment(frame_buffers);
+
+  EXPECT_TRUE(environment.WaitUntilUpdateStatusCalled());
+  EXPECT_TRUE(environment.WaitUntilUpdateStatusCalled());
+}
+
+TEST(SbAudioSinkTest, SomeFramesConsumed) {
+  AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
+  AudioSinkTestEnvironment environment(frame_buffers);
+
+  environment.AppendFrame(1);
+  EXPECT_TRUE(environment.WaitUntilSomeFramesAreConsumed());
+}
+
+TEST(SbAudioSinkTest, AllFramesConsumed) {
+  AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
+  AudioSinkTestEnvironment environment(frame_buffers);
+
+  environment.AppendFrame(1024);
+  EXPECT_TRUE(environment.WaitUntilAllFramesAreConsumed());
+}
+
+TEST(SbAudioSinkTest, MultipleAppendAndConsume) {
+  AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
+  AudioSinkTestEnvironment environment(frame_buffers);
+
+  int frames_to_append = frame_buffers.frames_per_channel();
+
+  environment.AppendFrame(frames_to_append / 2);
+  EXPECT_TRUE(environment.WaitUntilSomeFramesAreConsumed());
+  environment.AppendFrame(frames_to_append / 2);
+  EXPECT_TRUE(environment.WaitUntilAllFramesAreConsumed());
+}
+
+TEST(SbAudioSinkTest, Pause) {
+  AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
+  AudioSinkTestEnvironment environment(frame_buffers);
+  environment.SetIsPlaying(false);
+
+  int frames_to_append = frame_buffers.frames_per_channel();
+
+  environment.AppendFrame(frames_to_append / 2);
+  int free_space = environment.GetFrameBufferFreeSpaceAmount();
+  EXPECT_TRUE(environment.WaitUntilUpdateStatusCalled());
+  EXPECT_TRUE(environment.WaitUntilUpdateStatusCalled());
+  EXPECT_EQ(free_space, environment.GetFrameBufferFreeSpaceAmount());
+  environment.SetIsPlaying(true);
+  EXPECT_TRUE(environment.WaitUntilSomeFramesAreConsumed());
+}
+
+TEST(SbAudioSinkTest, ContinuousAppend) {
+  AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
+
+  AudioSinkTestEnvironment environment(frame_buffers);
+  int sample_rate = environment.sample_rate();
+  // We are trying to send 1/4s worth of audio samples.
+  int frames_to_append = sample_rate / 4;
+
+  while (frames_to_append > 0) {
+    int free_space = environment.GetFrameBufferFreeSpaceAmount();
+    environment.AppendFrame(std::min(free_space, frames_to_append));
+    frames_to_append -= std::min(free_space, frames_to_append);
+    ASSERT_TRUE(environment.WaitUntilSomeFramesAreConsumed());
+  }
+  EXPECT_TRUE(environment.WaitUntilAllFramesAreConsumed());
+}
+
+}  // namespace nplb
+}  // namespace starboard
diff --git a/src/starboard/nplb/memory_align_to_page_size_test.cc b/src/starboard/nplb/memory_align_to_page_size_test.cc
index 78b3be8..8412399 100644
--- a/src/starboard/nplb/memory_align_to_page_size_test.cc
+++ b/src/starboard/nplb/memory_align_to_page_size_test.cc
@@ -1,34 +1,34 @@
-// Copyright 2016 Google Inc. 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/memory.h"

-#include "testing/gtest/include/gtest/gtest.h"

-

-namespace starboard {

-namespace nplb {

-namespace {

-

-TEST(SbMemoryAlignToPageSizeTest, AlignsVariousSizes) {

-  EXPECT_EQ(0, SbMemoryAlignToPageSize(0));

-  EXPECT_EQ(SB_MEMORY_PAGE_SIZE, SbMemoryAlignToPageSize(1));

-  EXPECT_EQ(SB_MEMORY_PAGE_SIZE,

-            SbMemoryAlignToPageSize(SB_MEMORY_PAGE_SIZE - 1));

-  EXPECT_EQ(SB_MEMORY_PAGE_SIZE, SbMemoryAlignToPageSize(SB_MEMORY_PAGE_SIZE));

-  EXPECT_EQ(100 * SB_MEMORY_PAGE_SIZE,

-            SbMemoryAlignToPageSize(100 * SB_MEMORY_PAGE_SIZE));

-}

-

-}  // namespace

-}  // namespace nplb

-}  // namespace starboard

+// Copyright 2016 Google Inc. 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/memory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace nplb {
+namespace {
+
+TEST(SbMemoryAlignToPageSizeTest, AlignsVariousSizes) {
+  EXPECT_EQ(0, SbMemoryAlignToPageSize(0));
+  EXPECT_EQ(SB_MEMORY_PAGE_SIZE, SbMemoryAlignToPageSize(1));
+  EXPECT_EQ(SB_MEMORY_PAGE_SIZE,
+            SbMemoryAlignToPageSize(SB_MEMORY_PAGE_SIZE - 1));
+  EXPECT_EQ(SB_MEMORY_PAGE_SIZE, SbMemoryAlignToPageSize(SB_MEMORY_PAGE_SIZE));
+  EXPECT_EQ(100 * SB_MEMORY_PAGE_SIZE,
+            SbMemoryAlignToPageSize(100 * SB_MEMORY_PAGE_SIZE));
+}
+
+}  // namespace
+}  // namespace nplb
+}  // namespace starboard
diff --git a/src/starboard/nplb/memory_map_test.cc b/src/starboard/nplb/memory_map_test.cc
index a17bbdc..05ad242 100644
--- a/src/starboard/nplb/memory_map_test.cc
+++ b/src/starboard/nplb/memory_map_test.cc
@@ -101,6 +101,9 @@
 static int sum(int x, int y) {
   return x + y;
 }
+static int sum2(int x, int y) {
+  return x + y + x;
+}
 
 // This test is known to run on x64, ARM, and MIPS32 with MIPS32 and MIPS16
 // instructions.
@@ -109,17 +112,31 @@
       kSize, kSbMemoryMapProtectReadWrite | kSbMemoryMapProtectExec, "test");
   ASSERT_NE(kFailed, memory);
 
-  // There's no reliable way to determine the size of the 'sum' function. We can
+  // There's no reliable way to determine the size of the 'sum' function. If we
+  // assume the function is at most a certain size, then we might try to read
+  // beyond mapped memory when copying it to the destination address. We can
   // get a reasonable upper bound by assuming that the function's implementation
   // does not cross a page boundary, and copy the memory from the beginning of
   // the function to the end of the page that it is mapped to.
-  // Note that this will fail if the function implementation crosses a page
-  // boundary, which seems pretty unlikely, especially given the size of the
-  // function.
+  //
+  // However, since it's possible the function may cross the page boundary, we
+  // define two functions and use the one closest to the start of a page. There
+  // is no guarantee that the linker will place these definitions sequentially
+  // (although it likely will), so we can't use the address of 'sum2' as the
+  // end of 'sum'.
+  //
+  // To avoid the possibility that COMDAT folding will combine these two
+  // definitions into one, make sure they are different.
 
   // A function pointer can't be cast to void*, but uint8* seems to be okay. So
   // cast to a uint* which will be implicitly casted to a void* below.
-  uint8_t* sum_function_start = reinterpret_cast<uint8_t*>(&sum);
+  SumFunction original_function = &sum;
+  if (reinterpret_cast<uintptr_t>(&sum2) % SB_MEMORY_PAGE_SIZE <
+      reinterpret_cast<uintptr_t>(&sum) % SB_MEMORY_PAGE_SIZE) {
+    original_function = &sum2;
+  }
+
+  uint8_t* sum_function_start = reinterpret_cast<uint8_t*>(original_function);
 
   // MIPS16 instruction are kept odd addresses to differentiate between that and
   // MIPS32 instructions. Most other instructions are aligned to at least even
@@ -148,8 +165,8 @@
   SumFunction mapped_function = reinterpret_cast<SumFunction>(
       reinterpret_cast<uint8_t*>(memory) + sum_function_offset);
 
-  EXPECT_EQ(4, (*mapped_function)(1, 3));
-  EXPECT_EQ(5, (*mapped_function)(10, -5));
+  EXPECT_EQ((*original_function)(1, 3), (*mapped_function)(1, 3));
+  EXPECT_EQ((*original_function)(10, -5), (*mapped_function)(10, -5));
 
   EXPECT_TRUE(SbMemoryUnmap(memory, kSize));
 }
diff --git a/src/starboard/nplb/once_test.cc b/src/starboard/nplb/once_test.cc
index a504fc2..c94993d 100644
--- a/src/starboard/nplb/once_test.cc
+++ b/src/starboard/nplb/once_test.cc
@@ -1,163 +1,163 @@
-// Copyright 2015 Google Inc. 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.

-

-// Broadcast is Sunny Day tested in most of the other SbConditionVariable tests.

-

-#include "starboard/nplb/thread_helpers.h"

-#include "starboard/once.h"

-#include "starboard/thread.h"

-#include "starboard/thread_types.h"

-#include "testing/gtest/include/gtest/gtest.h"

-

-namespace starboard {

-namespace nplb {

-namespace {

-

-int s_global_value;

-

-void IncrementGlobalValue() {

-  ++s_global_value;

-}

-

-TEST(SbOnceTest, SunnyDaySingleInit) {

-  SbOnceControl once_control = SB_ONCE_INITIALIZER;

-

-  s_global_value = 0;

-  EXPECT_TRUE(SbOnce(&once_control, &IncrementGlobalValue));

-

-  EXPECT_EQ(1, s_global_value);

-}

-

-TEST(SbOnceTest, SunnyDayMultipleInit) {

-  SbOnceControl once_control = SB_ONCE_INITIALIZER;

-

-  s_global_value = 0;

-  EXPECT_TRUE(SbOnce(&once_control, &IncrementGlobalValue));

-  EXPECT_EQ(1, s_global_value);

-

-  s_global_value = 0;

-  EXPECT_TRUE(SbOnce(&once_control, &IncrementGlobalValue));

-  EXPECT_EQ(0, s_global_value);

-

-  s_global_value = 0;

-  EXPECT_TRUE(SbOnce(&once_control, &IncrementGlobalValue));

-  EXPECT_EQ(0, s_global_value);

-}

-

-struct RunSbOnceContext {

-  RunSbOnceContext() : once_control(SB_ONCE_INITIALIZER) {

-    SbMutexCreate(&mutex);

-    SbConditionVariableCreate(&condition, &mutex);

-  }

-  ~RunSbOnceContext() {

-    SbConditionVariableDestroy(&condition);

-    SbMutexDestroy(&mutex);

-  }

-

-  Semaphore semaphore;

-  SbMutex mutex;

-  SbConditionVariable condition;

-

-  SbOnceControl once_control;

-};

-

-void* RunSbOnceEntryPoint(void* context) {

-  RunSbOnceContext* run_sbonce_context =

-      reinterpret_cast<RunSbOnceContext*>(context);

-

-  {

-    SbMutexAcquire(&run_sbonce_context->mutex);

-    run_sbonce_context->semaphore.Put();

-    SbConditionVariableWait(&run_sbonce_context->condition,

-                            &run_sbonce_context->mutex);

-    SbMutexRelease(&run_sbonce_context->mutex);

-  }

-

-  SbThreadYield();

-  static const int kIterationCount = 3;

-  for (int i = 0; i < kIterationCount; ++i) {

-    SbOnce(&run_sbonce_context->once_control, &IncrementGlobalValue);

-  }

-

-  return NULL;

-}

-

-// Here we spawn many threads each of which will call SbOnce multiple times

-// using a shared SbOnceControl object.  We then test that the initialization

-// routine got called exactly one time.

-TEST(SbOnceTest, SunnyDayMultipleThreadsInit) {

-  const int kMany = SB_MAX_THREADS;

-  SbThread threads[kMany];

-

-  const int kIterationCount = 10;

-  for (int i = 0; i < kIterationCount; ++i) {

-    SbOnceControl once_control = SB_ONCE_INITIALIZER;

-    RunSbOnceContext context;

-

-    s_global_value = 0;

-    for (int i = 0; i < kMany; ++i) {

-      threads[i] =

-          SbThreadCreate(0, kSbThreadNoPriority, kSbThreadNoAffinity, true,

-                         kThreadName, RunSbOnceEntryPoint, &context);

-    }

-

-    // Wait for all threads to finish initializing and become ready, then

-    // broadcast the signal to begin.  We do this to increase the chances that

-    // threads will call SbOnce() at the same time as each other.

-    for (int i = 0; i < kMany; ++i) {

-      context.semaphore.Take();

-    }

-    {

-      SbMutexAcquire(&context.mutex);

-      SbConditionVariableBroadcast(&context.condition);

-      SbMutexRelease(&context.mutex);

-    }

-

-    // Signal threads to beginWait for all threads to complete.

-    for (int i = 0; i < kMany; ++i) {

-      void* result;

-      SbThreadJoin(threads[i], &result);

-    }

-

-    EXPECT_EQ(1, s_global_value);

-  }

-}

-

-TEST(SbOnceTest, RainyDayBadOnceControl) {

-  s_global_value = 0;

-  EXPECT_FALSE(SbOnce(NULL, &IncrementGlobalValue));

-

-  EXPECT_EQ(0, s_global_value);

-}

-

-TEST(SbOnceTest, RainyDayBadInitRoutine) {

-  SbOnceControl once_control = SB_ONCE_INITIALIZER;

-

-  s_global_value = 0;

-  EXPECT_FALSE(SbOnce(&once_control, NULL));

-

-  EXPECT_EQ(0, s_global_value);

-}

-

-SB_ONCE_INITIALIZE_FUNCTION(int, GetIntSingleton);

-TEST(SbOnceTest, InitializeOnceMacroFunction) {

-  int* int_singelton = GetIntSingleton();

-  ASSERT_TRUE(int_singelton);

-  EXPECT_EQ(0, *int_singelton)

-      << "Singleton Macro does not default initialize.";

-}

-

-}  // namespace.

-}  // namespace nplb.

-}  // namespace starboard.

+// Copyright 2015 Google Inc. 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.
+
+// Broadcast is Sunny Day tested in most of the other SbConditionVariable tests.
+
+#include "starboard/nplb/thread_helpers.h"
+#include "starboard/once.h"
+#include "starboard/thread.h"
+#include "starboard/thread_types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace nplb {
+namespace {
+
+int s_global_value;
+
+void IncrementGlobalValue() {
+  ++s_global_value;
+}
+
+TEST(SbOnceTest, SunnyDaySingleInit) {
+  SbOnceControl once_control = SB_ONCE_INITIALIZER;
+
+  s_global_value = 0;
+  EXPECT_TRUE(SbOnce(&once_control, &IncrementGlobalValue));
+
+  EXPECT_EQ(1, s_global_value);
+}
+
+TEST(SbOnceTest, SunnyDayMultipleInit) {
+  SbOnceControl once_control = SB_ONCE_INITIALIZER;
+
+  s_global_value = 0;
+  EXPECT_TRUE(SbOnce(&once_control, &IncrementGlobalValue));
+  EXPECT_EQ(1, s_global_value);
+
+  s_global_value = 0;
+  EXPECT_TRUE(SbOnce(&once_control, &IncrementGlobalValue));
+  EXPECT_EQ(0, s_global_value);
+
+  s_global_value = 0;
+  EXPECT_TRUE(SbOnce(&once_control, &IncrementGlobalValue));
+  EXPECT_EQ(0, s_global_value);
+}
+
+struct RunSbOnceContext {
+  RunSbOnceContext() : once_control(SB_ONCE_INITIALIZER) {
+    SbMutexCreate(&mutex);
+    SbConditionVariableCreate(&condition, &mutex);
+  }
+  ~RunSbOnceContext() {
+    SbConditionVariableDestroy(&condition);
+    SbMutexDestroy(&mutex);
+  }
+
+  Semaphore semaphore;
+  SbMutex mutex;
+  SbConditionVariable condition;
+
+  SbOnceControl once_control;
+};
+
+void* RunSbOnceEntryPoint(void* context) {
+  RunSbOnceContext* run_sbonce_context =
+      reinterpret_cast<RunSbOnceContext*>(context);
+
+  {
+    SbMutexAcquire(&run_sbonce_context->mutex);
+    run_sbonce_context->semaphore.Put();
+    SbConditionVariableWait(&run_sbonce_context->condition,
+                            &run_sbonce_context->mutex);
+    SbMutexRelease(&run_sbonce_context->mutex);
+  }
+
+  SbThreadYield();
+  static const int kIterationCount = 3;
+  for (int i = 0; i < kIterationCount; ++i) {
+    SbOnce(&run_sbonce_context->once_control, &IncrementGlobalValue);
+  }
+
+  return NULL;
+}
+
+// Here we spawn many threads each of which will call SbOnce multiple times
+// using a shared SbOnceControl object.  We then test that the initialization
+// routine got called exactly one time.
+TEST(SbOnceTest, SunnyDayMultipleThreadsInit) {
+  const int kMany = SB_MAX_THREADS;
+  SbThread threads[kMany];
+
+  const int kIterationCount = 10;
+  for (int i = 0; i < kIterationCount; ++i) {
+    SbOnceControl once_control = SB_ONCE_INITIALIZER;
+    RunSbOnceContext context;
+
+    s_global_value = 0;
+    for (int i = 0; i < kMany; ++i) {
+      threads[i] =
+          SbThreadCreate(0, kSbThreadNoPriority, kSbThreadNoAffinity, true,
+                         kThreadName, RunSbOnceEntryPoint, &context);
+    }
+
+    // Wait for all threads to finish initializing and become ready, then
+    // broadcast the signal to begin.  We do this to increase the chances that
+    // threads will call SbOnce() at the same time as each other.
+    for (int i = 0; i < kMany; ++i) {
+      context.semaphore.Take();
+    }
+    {
+      SbMutexAcquire(&context.mutex);
+      SbConditionVariableBroadcast(&context.condition);
+      SbMutexRelease(&context.mutex);
+    }
+
+    // Signal threads to beginWait for all threads to complete.
+    for (int i = 0; i < kMany; ++i) {
+      void* result;
+      SbThreadJoin(threads[i], &result);
+    }
+
+    EXPECT_EQ(1, s_global_value);
+  }
+}
+
+TEST(SbOnceTest, RainyDayBadOnceControl) {
+  s_global_value = 0;
+  EXPECT_FALSE(SbOnce(NULL, &IncrementGlobalValue));
+
+  EXPECT_EQ(0, s_global_value);
+}
+
+TEST(SbOnceTest, RainyDayBadInitRoutine) {
+  SbOnceControl once_control = SB_ONCE_INITIALIZER;
+
+  s_global_value = 0;
+  EXPECT_FALSE(SbOnce(&once_control, NULL));
+
+  EXPECT_EQ(0, s_global_value);
+}
+
+SB_ONCE_INITIALIZE_FUNCTION(int, GetIntSingleton);
+TEST(SbOnceTest, InitializeOnceMacroFunction) {
+  int* int_singelton = GetIntSingleton();
+  ASSERT_TRUE(int_singelton);
+  EXPECT_EQ(0, *int_singelton)
+      << "Singleton Macro does not default initialize.";
+}
+
+}  // namespace.
+}  // namespace nplb.
+}  // namespace starboard.
diff --git a/src/starboard/nplb/player_create_test.cc b/src/starboard/nplb/player_create_test.cc
index dffa92c..e463b90 100644
--- a/src/starboard/nplb/player_create_test.cc
+++ b/src/starboard/nplb/player_create_test.cc
@@ -1,61 +1,61 @@
-// Copyright 2016 Google Inc. 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/player.h"

-

-#include "starboard/window.h"

-#include "testing/gtest/include/gtest/gtest.h"

-

-#if SB_HAS(PLAYER)

-

-namespace starboard {

-namespace nplb {

-

-TEST(SbPlayerTest, SunnyDay) {

-  SbWindowOptions window_options;

-  SbWindowSetDefaultOptions(&window_options);

-

-  SbWindow window = SbWindowCreate(&window_options);

-  EXPECT_TRUE(SbWindowIsValid(window));

-

-  SbMediaAudioHeader audio_header;

-

-  audio_header.format_tag = 0xff;

-  audio_header.number_of_channels = 2;

-  audio_header.samples_per_second = 22050;

-  audio_header.block_alignment = 4;

-  audio_header.bits_per_sample = 32;

-  audio_header.audio_specific_config_size = 0;

-  audio_header.average_bytes_per_second = audio_header.samples_per_second *

-                                          audio_header.number_of_channels *

-                                          audio_header.bits_per_sample / 8;

-

-  SbPlayer player =

-      SbPlayerCreate(window, kSbMediaVideoCodecH264, kSbMediaAudioCodecAac,

-                     SB_PLAYER_NO_DURATION, kSbDrmSystemInvalid, &audio_header,

-                     NULL, NULL, NULL, NULL

-#if SB_VERSION(3)

-                     ,

-                     NULL

-#endif

-                     );  // NOLINT

-  EXPECT_TRUE(SbPlayerIsValid(player));

-  SbPlayerDestroy(player);

-  SbWindowDestroy(window);

-}

-

-}  // namespace nplb

-}  // namespace starboard

-

-#endif  // SB_HAS(PLAYER)

+// Copyright 2016 Google Inc. 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/player.h"
+
+#include "starboard/window.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if SB_HAS(PLAYER)
+
+namespace starboard {
+namespace nplb {
+
+TEST(SbPlayerTest, SunnyDay) {
+  SbWindowOptions window_options;
+  SbWindowSetDefaultOptions(&window_options);
+
+  SbWindow window = SbWindowCreate(&window_options);
+  EXPECT_TRUE(SbWindowIsValid(window));
+
+  SbMediaAudioHeader audio_header;
+
+  audio_header.format_tag = 0xff;
+  audio_header.number_of_channels = 2;
+  audio_header.samples_per_second = 22050;
+  audio_header.block_alignment = 4;
+  audio_header.bits_per_sample = 32;
+  audio_header.audio_specific_config_size = 0;
+  audio_header.average_bytes_per_second = audio_header.samples_per_second *
+                                          audio_header.number_of_channels *
+                                          audio_header.bits_per_sample / 8;
+
+  SbPlayer player =
+      SbPlayerCreate(window, kSbMediaVideoCodecH264, kSbMediaAudioCodecAac,
+                     SB_PLAYER_NO_DURATION, kSbDrmSystemInvalid, &audio_header,
+                     NULL, NULL, NULL, NULL
+#if SB_VERSION(3)
+                     ,
+                     NULL
+#endif
+                     );  // NOLINT
+  EXPECT_TRUE(SbPlayerIsValid(player));
+  SbPlayerDestroy(player);
+  SbWindowDestroy(window);
+}
+
+}  // namespace nplb
+}  // namespace starboard
+
+#endif  // SB_HAS(PLAYER)
diff --git a/src/starboard/nplb/speech_synthesis_basic_test.cc b/src/starboard/nplb/speech_synthesis_basic_test.cc
index 4e01d35..d2e67c7 100644
--- a/src/starboard/nplb/speech_synthesis_basic_test.cc
+++ b/src/starboard/nplb/speech_synthesis_basic_test.cc
@@ -18,11 +18,8 @@
 #if SB_HAS(SPEECH_SYNTHESIS) && SB_VERSION(3)
 
 TEST(SbSpeechSynthesisBasicTest, Basic) {
-  // An implementation must at least support US English
-  EXPECT_TRUE(SbSpeechSynthesisSetLanguage("en-US"));
   SbSpeechSynthesisSpeak("Hello");
   SbSpeechSynthesisCancel();
 }
 
 #endif  // SB_HAS(SPEECH_SYNTHESIS) && SB_VERSION(3)
-
diff --git a/src/starboard/nplb/system_get_total_cpu_memory_test.cc b/src/starboard/nplb/system_get_total_cpu_memory_test.cc
index 591fd6f..1fbe72e 100644
--- a/src/starboard/nplb/system_get_total_cpu_memory_test.cc
+++ b/src/starboard/nplb/system_get_total_cpu_memory_test.cc
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "starboard/log.h"
 #include "starboard/system.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -24,6 +25,18 @@
   EXPECT_LE(SB_INT64_C(80) * 1024 * 1024, SbSystemGetTotalCPUMemory());
 }
 
+TEST(SbSystemGetTotalCPUMemoryTest, PrintValues) {
+  int64_t bytes_reserved = SbSystemGetTotalCPUMemory();
+  int64_t bytes_in_use = SbSystemGetUsedCPUMemory();
+
+  std::stringstream ss;
+  ss << "\n"
+     << "SbSystemGetTotalCPUMemory() = " << SbSystemGetTotalCPUMemory() << "\n"
+     << "SbSystemGetUsedCPUMemory()  = " << SbSystemGetUsedCPUMemory()
+     << "\n\n";
+  SbLogRaw(ss.str().c_str());
+}
+
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/queue.h b/src/starboard/queue.h
index 53f7633..e333ef3 100644
--- a/src/starboard/queue.h
+++ b/src/starboard/queue.h
@@ -21,6 +21,7 @@
 #ifndef STARBOARD_QUEUE_H_
 #define STARBOARD_QUEUE_H_
 
+#include <algorithm>
 #include <deque>
 
 #include "starboard/condition_variable.h"
@@ -115,6 +116,20 @@
     condition_.Signal();
   }
 
+  // It is guaranteed that after this function returns there is no element in
+  // the queue equals to |value|.  This is useful to remove un-processed values
+  // before destroying the owning object.
+  // Note that it is the responsibility of the caller to free any resources
+  // associated with |value| after this function returns.  It is possible that
+  // another thread is still using |value|, the caller should make sure that
+  // this is properly coordinated with the free of resources.  Usually this can
+  // be solved by calling Remove() on the same thread that calls Get().
+  void Remove(T value) {
+    ScopedLock lock(mutex_);
+    queue_.erase(std::remove(queue_.begin(), queue_.end(), value),
+                 queue_.end());
+  }
+
  private:
   Mutex mutex_;
   ConditionVariable condition_;
diff --git a/src/starboard/raspi/shared/open_max/video_decoder.cc b/src/starboard/raspi/shared/open_max/video_decoder.cc
index 3311a06..9d3e6b2 100644
--- a/src/starboard/raspi/shared/open_max/video_decoder.cc
+++ b/src/starboard/raspi/shared/open_max/video_decoder.cc
@@ -149,7 +149,7 @@
       filled_buffers_.push(buffer);
     }
 
-    if (current_buffer.valid()) {
+    if (current_buffer.is_valid()) {
       int size = static_cast<int>(current_buffer.size());
       while (offset < size) {
         int written = component.WriteData(
diff --git a/src/starboard/raspi/shared/starboard_platform.gypi b/src/starboard/raspi/shared/starboard_platform.gypi
index 9d44f1e..a096687 100644
--- a/src/starboard/raspi/shared/starboard_platform.gypi
+++ b/src/starboard/raspi/shared/starboard_platform.gypi
@@ -236,6 +236,8 @@
         '<(DEPTH)/starboard/shared/signal/suspend_signals.cc',
         '<(DEPTH)/starboard/shared/signal/suspend_signals.h',
         '<(DEPTH)/starboard/shared/starboard/application.cc',
+        '<(DEPTH)/starboard/shared/starboard/command_line.cc',
+        '<(DEPTH)/starboard/shared/starboard/command_line.h',
         '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_create.cc',
         '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_destroy.cc',
         '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_internal.cc',
@@ -262,6 +264,8 @@
         '<(DEPTH)/starboard/shared/starboard/media/mime_type.cc',
         '<(DEPTH)/starboard/shared/starboard/media/mime_type.h',
         '<(DEPTH)/starboard/shared/starboard/new.cc',
+        '<(DEPTH)/starboard/shared/starboard/player/decoded_audio_internal.cc',
+        '<(DEPTH)/starboard/shared/starboard/player/decoded_audio_internal.h',
         '<(DEPTH)/starboard/shared/starboard/player/filter/audio_decoder_internal.h',
         '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.cc',
         '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.h',
@@ -272,6 +276,8 @@
         '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.h',
         '<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.cc',
         '<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.h',
+        '<(DEPTH)/starboard/shared/starboard/player/job_queue.cc',
+        '<(DEPTH)/starboard/shared/starboard/player/job_queue.h',
         '<(DEPTH)/starboard/shared/starboard/player/player_create.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_destroy.cc',
         '<(DEPTH)/starboard/shared/starboard/player/player_get_info.cc',
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc
index 1444180..4b73ed7 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc
+++ b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc
@@ -16,6 +16,7 @@
 
 #include "starboard/audio_sink.h"
 #include "starboard/log.h"
+#include "starboard/memory.h"
 
 namespace starboard {
 namespace shared {
@@ -81,9 +82,7 @@
   TeardownCodec();
 }
 
-void AudioDecoder::Decode(const InputBuffer& input_buffer,
-                          std::vector<uint8_t>* output) {
-  SB_CHECK(output != NULL);
+void AudioDecoder::Decode(const InputBuffer& input_buffer) {
   SB_CHECK(codec_context_ != NULL);
 
   if (stream_ended_) {
@@ -105,7 +104,6 @@
     SB_DLOG(WARNING) << "avcodec_decode_audio4() failed with result: " << result
                      << " with input buffer size: " << input_buffer.size()
                      << " and frame decoded: " << frame_decoded;
-    output->clear();
     return;
   }
 
@@ -115,20 +113,24 @@
   audio_header_.samples_per_second = codec_context_->sample_rate;
 
   if (decoded_audio_size > 0) {
-    output->resize(codec_context_->channels * av_frame_->nb_samples *
-                   (sample_type_ == kSbMediaAudioSampleTypeInt16 ? 2 : 4));
+    scoped_refptr<DecodedAudio> decoded_audio = new DecodedAudio(
+        input_buffer.pts(),
+        codec_context_->channels * av_frame_->nb_samples *
+            (sample_type_ == kSbMediaAudioSampleTypeInt16 ? 2 : 4));
     if (codec_context_->sample_fmt == codec_context_->request_sample_fmt) {
-      memcpy(&(*output)[0], av_frame_->extended_data, output->size());
+      SbMemoryCopy(decoded_audio->buffer(), av_frame_->extended_data,
+                   decoded_audio->size());
     } else {
-      ConvertSamples(
-          codec_context_->sample_fmt, codec_context_->request_sample_fmt,
-          codec_context_->channel_layout, audio_header_.samples_per_second,
-          av_frame_->nb_samples, av_frame_->extended_data, &(*output)[0]);
+      ConvertSamples(codec_context_->sample_fmt,
+                     codec_context_->request_sample_fmt,
+                     codec_context_->channel_layout,
+                     audio_header_.samples_per_second, av_frame_->nb_samples,
+                     av_frame_->extended_data, decoded_audio->buffer());
     }
+    decoded_audios_.push(decoded_audio);
   } else {
     // TODO: Consider fill it with silence.
     SB_LOG(ERROR) << "Decoded audio frame is empty.";
-    output->clear();
   }
 }
 
@@ -136,10 +138,24 @@
   // AAC has no dependent frames so we needn't flush the decoder.  Set the flag
   // to ensure that Decode() is not called when the stream is ended.
   stream_ended_ = true;
+  // Put EOS into the queue.
+  decoded_audios_.push(new DecodedAudio);
+}
+
+scoped_refptr<AudioDecoder::DecodedAudio> AudioDecoder::Read() {
+  scoped_refptr<DecodedAudio> result;
+  if (!decoded_audios_.empty()) {
+    result = decoded_audios_.front();
+    decoded_audios_.pop();
+  }
+  return result;
 }
 
 void AudioDecoder::Reset() {
   stream_ended_ = false;
+  while (!decoded_audios_.empty()) {
+    decoded_audios_.pop();
+  }
 }
 
 SbMediaAudioSampleType AudioDecoder::GetSampleType() const {
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h
index c2a78db..4d6ba4c 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h
+++ b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h
@@ -15,11 +15,12 @@
 #ifndef STARBOARD_SHARED_FFMPEG_FFMPEG_AUDIO_DECODER_H_
 #define STARBOARD_SHARED_FFMPEG_FFMPEG_AUDIO_DECODER_H_
 
-#include <vector>
+#include <queue>
 
 #include "starboard/media.h"
 #include "starboard/shared/ffmpeg/ffmpeg_common.h"
 #include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/player/decoded_audio_internal.h"
 #include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
 
 namespace starboard {
@@ -28,15 +29,16 @@
 
 class AudioDecoder : public starboard::player::filter::AudioDecoder {
  public:
+  typedef starboard::player::DecodedAudio DecodedAudio;
   typedef starboard::player::InputBuffer InputBuffer;
 
   AudioDecoder(SbMediaAudioCodec audio_codec,
                const SbMediaAudioHeader& audio_header);
   ~AudioDecoder() SB_OVERRIDE;
 
-  void Decode(const InputBuffer& input_buffer,
-              std::vector<uint8_t>* output) SB_OVERRIDE;
+  void Decode(const InputBuffer& input_buffer) SB_OVERRIDE;
   void WriteEndOfStream() SB_OVERRIDE;
+  scoped_refptr<DecodedAudio> Read() SB_OVERRIDE;
   void Reset() SB_OVERRIDE;
   SbMediaAudioSampleType GetSampleType() const SB_OVERRIDE;
   int GetSamplesPerSecond() const SB_OVERRIDE;
@@ -52,6 +54,7 @@
   AVFrame* av_frame_;
 
   bool stream_ended_;
+  std::queue<scoped_refptr<DecodedAudio> > decoded_audios_;
   SbMediaAudioHeader audio_header_;
 };
 
diff --git a/src/starboard/shared/iso/directory_close.cc b/src/starboard/shared/iso/directory_close.cc
index a4fb9b5..93f50c2 100644
--- a/src/starboard/shared/iso/directory_close.cc
+++ b/src/starboard/shared/iso/directory_close.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. 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.
@@ -12,20 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/directory.h"
-
-#include <dirent.h>
-#include <errno.h>
-
-#include "starboard/file.h"
 #include "starboard/shared/iso/directory_internal.h"
 
-bool SbDirectoryClose(SbDirectory directory) {
-  if (!directory || !directory->directory) {
-    return false;
-  }
+#include "starboard/shared/iso/impl/directory_close.h"
 
-  bool result = !closedir(directory->directory);
-  delete directory;
-  return result;
+bool SbDirectoryClose(SbDirectory directory) {
+  return ::starboard::shared::iso::impl::SbDirectoryClose(directory);
 }
diff --git a/src/starboard/shared/iso/directory_get_next.cc b/src/starboard/shared/iso/directory_get_next.cc
index c60375f..bef30b6 100644
--- a/src/starboard/shared/iso/directory_get_next.cc
+++ b/src/starboard/shared/iso/directory_get_next.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. 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.
@@ -12,28 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/directory.h"
-
-#include <dirent.h>
-#include <errno.h>
-
-#include "starboard/file.h"
 #include "starboard/shared/iso/directory_internal.h"
-#include "starboard/string.h"
+
+#include "starboard/shared/iso/impl/directory_get_next.h"
 
 bool SbDirectoryGetNext(SbDirectory directory, SbDirectoryEntry* out_entry) {
-  if (!directory || !directory->directory || !out_entry) {
-    return false;
-  }
-
-  struct dirent dirent_buffer;
-  struct dirent* dirent;
-  int result = readdir_r(directory->directory, &dirent_buffer, &dirent);
-  if (result || !dirent) {
-    return false;
-  }
-
-  SbStringCopy(out_entry->name, dirent->d_name,
-               SB_ARRAY_SIZE_INT(out_entry->name));
-  return true;
+  return ::starboard::shared::iso::impl::SbDirectoryGetNext(directory,
+                                                            out_entry);
 }
diff --git a/src/starboard/shared/iso/directory_open.cc b/src/starboard/shared/iso/directory_open.cc
index e1fe59b..2604aea 100644
--- a/src/starboard/shared/iso/directory_open.cc
+++ b/src/starboard/shared/iso/directory_open.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. 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.
@@ -12,63 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/directory.h"
-
-#include <dirent.h>
-#include <errno.h>
-
-#include "starboard/file.h"
 #include "starboard/shared/iso/directory_internal.h"
 
+#include "starboard/shared/iso/impl/directory_open.h"
+
 SbDirectory SbDirectoryOpen(const char* path, SbFileError* out_error) {
-  if (!path) {
-    if (out_error) {
-      *out_error = kSbFileErrorNotFound;
-    }
-    return kSbDirectoryInvalid;
-  }
-
-  DIR* dir = opendir(path);
-  if (!dir) {
-    if (out_error) {
-      switch (errno) {
-        case EACCES:
-          *out_error = kSbFileErrorAccessDenied;
-          break;
-        case ELOOP:
-          *out_error = kSbFileErrorFailed;
-          break;
-        case ENAMETOOLONG:
-          *out_error = kSbFileErrorInvalidOperation;
-          break;
-        case ENOENT:
-          *out_error = kSbFileErrorNotFound;
-          break;
-        case ENOTDIR:
-          *out_error = kSbFileErrorNotADirectory;
-          break;
-        case EMFILE:
-        case ENFILE:
-          *out_error = kSbFileErrorTooManyOpened;
-          break;
-      }
-    }
-
-    return kSbDirectoryInvalid;
-  }
-
-  SbDirectory result = new SbDirectoryPrivate();
-  if (!result) {
-    closedir(dir);
-    if (out_error) {
-      *out_error = kSbFileErrorNoMemory;
-    }
-    return kSbDirectoryInvalid;
-  }
-
-  result->directory = dir;
-  if (out_error) {
-    *out_error = kSbFileOk;
-  }
-  return result;
+  return ::starboard::shared::iso::impl::SbDirectoryOpen(path, out_error);
 }
diff --git a/src/starboard/shared/iso/impl/directory_close.h b/src/starboard/shared/iso/impl/directory_close.h
new file mode 100644
index 0000000..f9760a9
--- /dev/null
+++ b/src/starboard/shared/iso/impl/directory_close.h
@@ -0,0 +1,48 @@
+// Copyright 2017 Google Inc. 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_SHARED_ISO_IMPL_DIRECTORY_CLOSE_H_
+#define STARBOARD_SHARED_ISO_IMPL_DIRECTORY_CLOSE_H_
+
+#include "starboard/directory.h"
+
+#include <dirent.h>
+#include <errno.h>
+
+#include "starboard/file.h"
+
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/iso/impl/directory_impl.h"
+
+namespace starboard {
+namespace shared {
+namespace iso {
+namespace impl {
+
+bool SbDirectoryClose(SbDirectory directory) {
+  if (!directory || !directory->directory) {
+    return false;
+  }
+
+  bool result = !closedir(directory->directory);
+  delete directory;
+  return result;
+}
+
+}  // namespace impl
+}  // namespace iso
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_SHARED_ISO_IMPL_DIRECTORY_CLOSE_H_
diff --git a/src/starboard/shared/iso/impl/directory_get_next.h b/src/starboard/shared/iso/impl/directory_get_next.h
new file mode 100644
index 0000000..be4ca1b
--- /dev/null
+++ b/src/starboard/shared/iso/impl/directory_get_next.h
@@ -0,0 +1,56 @@
+// Copyright 2017 Google Inc. 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_SHARED_ISO_IMPL_DIRECTORY_GET_NEXT_H_
+#define STARBOARD_SHARED_ISO_IMPL_DIRECTORY_GET_NEXT_H_
+
+#include "starboard/directory.h"
+
+#include <dirent.h>
+#include <errno.h>
+
+#include "starboard/file.h"
+#include "starboard/string.h"
+
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/iso/impl/directory_impl.h"
+
+namespace starboard {
+namespace shared {
+namespace iso {
+namespace impl {
+
+bool SbDirectoryGetNext(SbDirectory directory, SbDirectoryEntry* out_entry) {
+  if (!directory || !directory->directory || !out_entry) {
+    return false;
+  }
+
+  struct dirent dirent_buffer;
+  struct dirent* dirent;
+  int result = readdir_r(directory->directory, &dirent_buffer, &dirent);
+  if (result || !dirent) {
+    return false;
+  }
+
+  SbStringCopy(out_entry->name, dirent->d_name,
+               SB_ARRAY_SIZE_INT(out_entry->name));
+  return true;
+}
+
+}  // namespace impl
+}  // namespace iso
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_SHARED_ISO_IMPL_DIRECTORY_GET_NEXT_H_
diff --git a/src/starboard/shared/iso/impl/directory_impl.h b/src/starboard/shared/iso/impl/directory_impl.h
new file mode 100644
index 0000000..99c9cfe
--- /dev/null
+++ b/src/starboard/shared/iso/impl/directory_impl.h
@@ -0,0 +1,28 @@
+// Copyright 2017 Google Inc. 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_SHARED_ISO_IMPL_DIRECTORY_IMPL_H_
+#define STARBOARD_SHARED_ISO_IMPL_DIRECTORY_IMPL_H_
+
+#include "starboard/configuration.h"
+#include "starboard/directory.h"
+
+#include "starboard/shared/internal_only.h"
+
+// Ensure SbDirectory is typedef'd to a SbDirectoryPrivate* that has a directory
+// field.
+SB_COMPILE_ASSERT(sizeof(((SbDirectory)0)->directory), \
+                  SbDirectoryPrivate_must_have_directory);
+
+#endif  // STARBOARD_SHARED_ISO_IMPL_DIRECTORY_IMPL_H_
diff --git a/src/starboard/shared/iso/impl/directory_open.h b/src/starboard/shared/iso/impl/directory_open.h
new file mode 100644
index 0000000..9594035
--- /dev/null
+++ b/src/starboard/shared/iso/impl/directory_open.h
@@ -0,0 +1,91 @@
+// Copyright 2017 Google Inc. 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_SHARED_ISO_IMPL_DIRECTORY_OPEN_H_
+#define STARBOARD_SHARED_ISO_IMPL_DIRECTORY_OPEN_H_
+
+#include "starboard/directory.h"
+
+#include <dirent.h>
+#include <errno.h>
+
+#include "starboard/file.h"
+
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/iso/impl/directory_impl.h"
+
+namespace starboard {
+namespace shared {
+namespace iso {
+namespace impl {
+
+SbDirectory SbDirectoryOpen(const char* path, SbFileError* out_error) {
+  if (!path) {
+    if (out_error) {
+      *out_error = kSbFileErrorNotFound;
+    }
+    return kSbDirectoryInvalid;
+  }
+
+  DIR* dir = opendir(path);
+  if (!dir) {
+    if (out_error) {
+      switch (errno) {
+        case EACCES:
+          *out_error = kSbFileErrorAccessDenied;
+          break;
+        case ELOOP:
+          *out_error = kSbFileErrorFailed;
+          break;
+        case ENAMETOOLONG:
+          *out_error = kSbFileErrorInvalidOperation;
+          break;
+        case ENOENT:
+          *out_error = kSbFileErrorNotFound;
+          break;
+        case ENOTDIR:
+          *out_error = kSbFileErrorNotADirectory;
+          break;
+        case EMFILE:
+        case ENFILE:
+          *out_error = kSbFileErrorTooManyOpened;
+          break;
+      }
+    }
+
+    return kSbDirectoryInvalid;
+  }
+
+  SbDirectory result = new SbDirectoryPrivate();
+  if (!result) {
+    closedir(dir);
+    if (out_error) {
+      *out_error = kSbFileErrorNoMemory;
+    }
+    return kSbDirectoryInvalid;
+  }
+
+  result->directory = dir;
+  if (out_error) {
+    *out_error = kSbFileOk;
+  }
+  return result;
+}
+
+}  // namespace impl
+}  // namespace iso
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_SHARED_ISO_IMPL_DIRECTORY_OPEN_H_
diff --git a/src/starboard/shared/pthread/once.cc b/src/starboard/shared/pthread/once.cc
index 74395f8..1fe6b82 100644
--- a/src/starboard/shared/pthread/once.cc
+++ b/src/starboard/shared/pthread/once.cc
@@ -1,28 +1,28 @@
-// Copyright 2015 Google Inc. 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/once.h"

-

-#include <pthread.h>

-

-bool SbOnce(SbOnceControl* once_control, SbOnceInitRoutine init_routine) {

-  if (once_control == NULL) {

-    return false;

-  }

-  if (init_routine == NULL) {

-    return false;

-  }

-

-  return pthread_once(once_control, init_routine) == 0;

-}

+// Copyright 2015 Google Inc. 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/once.h"
+
+#include <pthread.h>
+
+bool SbOnce(SbOnceControl* once_control, SbOnceInitRoutine init_routine) {
+  if (once_control == NULL) {
+    return false;
+  }
+  if (init_routine == NULL) {
+    return false;
+  }
+
+  return pthread_once(once_control, init_routine) == 0;
+}
diff --git a/src/starboard/shared/speechd/speech_synthesis_set_language.cc b/src/starboard/shared/speechd/speech_synthesis_set_language.cc
index 1e8080f..cbd2c81 100644
--- a/src/starboard/shared/speechd/speech_synthesis_set_language.cc
+++ b/src/starboard/shared/speechd/speech_synthesis_set_language.cc
@@ -12,6 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#if SB_API_VERSION < SB_EXPERIMENTAL_API_VERSION
+// DEPRECATED IN API VERSION 4
+
 #include "starboard/speech_synthesis.h"
 
 #include "starboard/shared/speechd/speechd_internal.h"
@@ -25,3 +28,5 @@
   }
   return speech_dispatcher->SetLanguage(lang);
 }
+
+#endif
diff --git a/src/starboard/shared/speechd/speechd_internal.cc b/src/starboard/shared/speechd/speechd_internal.cc
index 12efab4..ee30ff9 100644
--- a/src/starboard/shared/speechd/speechd_internal.cc
+++ b/src/starboard/shared/speechd/speechd_internal.cc
@@ -40,6 +40,9 @@
   if (!connection_) {
     SB_DLOG(ERROR) << "Failed to initialize SpeechDispatcher.";
   }
+  if (!SetLanguage(SbSystemGetLocaleId())) {
+    SB_DLOG(ERROR) << "Unable to set language to current locale.";
+  }
 }
 
 SpeechDispatcher::~SpeechDispatcher() {
diff --git a/src/starboard/shared/starboard/application.cc b/src/starboard/shared/starboard/application.cc
index 61fc960..f79f5dd 100644
--- a/src/starboard/shared/starboard/application.cc
+++ b/src/starboard/shared/starboard/application.cc
@@ -20,6 +20,8 @@
 #include "starboard/memory.h"
 #include "starboard/string.h"
 
+#include "starboard/shared/starboard/command_line.h"
+
 namespace starboard {
 namespace shared {
 namespace starboard {
@@ -49,8 +51,6 @@
 Application::Application()
     : error_level_(0),
       thread_(SbThreadGetCurrent()),
-      argument_count_(0),
-      argument_values_(NULL),
       start_link_(NULL),
       state_(kStateUnstarted) {
   Application* old_instance =
@@ -74,8 +74,7 @@
 
 int Application::Run(int argc, char** argv) {
   Initialize();
-  argument_count_ = argc;
-  argument_values_ = argv;
+  command_line_.reset(new CommandLine(argc, argv));
   if (IsStartImmediate()) {
     DispatchStart();
   }
@@ -91,6 +90,10 @@
   return error_level_;
 }
 
+CommandLine* Application::GetCommandLine() {
+  return command_line_.get();
+}
+
 void Application::Pause(void* context, EventHandledCallback callback) {
   Inject(new Event(kSbEventTypePause, context, callback));
 }
@@ -148,8 +151,9 @@
 void Application::DispatchStart() {
   SB_DCHECK(state_ == kStateUnstarted);
   SbEventStartData start_data;
-  start_data.argument_values = argument_values_;
-  start_data.argument_count = argument_count_;
+  start_data.argument_values =
+      const_cast<char**>(command_line_->GetOriginalArgv());
+  start_data.argument_count = command_line_->GetOriginalArgc();
   start_data.link = start_link_;
   Dispatch(kSbEventTypeStart, &start_data, NULL);
   state_ = kStateStarted;
diff --git a/src/starboard/shared/starboard/application.h b/src/starboard/shared/starboard/application.h
index 50dfe9f..99b4061 100644
--- a/src/starboard/shared/starboard/application.h
+++ b/src/starboard/shared/starboard/application.h
@@ -26,6 +26,7 @@
 #include "starboard/log.h"
 #include "starboard/player.h"
 #include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/command_line.h"
 #include "starboard/shared/starboard/player/video_frame_internal.h"
 #include "starboard/thread.h"
 #include "starboard/time.h"
@@ -151,6 +152,10 @@
   // initialization and teardown events. Returns the resulting error level.
   int Run(int argc, char** argv);
 
+  // Retrieves the CommandLine for the application.
+  // NULL until Run() is called.
+  CommandLine* GetCommandLine();
+
   // Signals that the application should transition from STARTED to PAUSED as
   // soon as possible. Does nothing if already PAUSED or SUSPENDED. May be
   // called from an external thread.
@@ -317,9 +322,8 @@
   // main thread.
   SbThread thread_;
 
-  // The command line arguments passed to |Run|.
-  int argument_count_;
-  char** argument_values_;
+  // CommandLine instance initialized in |Run|.
+  scoped_ptr<CommandLine> command_line_;
 
   // The deep link included in the Start event sent to Cobalt. Initially NULL,
   // derived classes may set it during initialization using |SetStartLink|.
diff --git a/src/starboard/shared/starboard/command_line.cc b/src/starboard/shared/starboard/command_line.cc
new file mode 100644
index 0000000..f4a8459
--- /dev/null
+++ b/src/starboard/shared/starboard/command_line.cc
@@ -0,0 +1,178 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "starboard/shared/starboard/command_line.h"
+
+#include <algorithm>
+#include <ostream>
+
+#include "starboard/configuration.h"
+#include "starboard/log.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+
+namespace {
+// From base/string_util.cc
+const char kWhitespaceASCII[] = {
+  0x09,    // <control-0009> to <control-000D>
+  0x0A,
+  0x0B,
+  0x0C,
+  0x0D,
+  0x20,    // Space
+  0
+};
+
+const CommandLine::CharType kSwitchTerminator[] = "--";
+const CommandLine::CharType kSwitchValueSeparator[] = "=";
+// Since we use a lazy match, make sure that longer versions (like "--") are
+// listed before shorter versions (like "-") of similar prefixes.
+// Unixes don't use slash as a switch.
+const CommandLine::CharType* const kSwitchPrefixes[] = {"--", "-"};
+
+size_t GetSwitchPrefixLength(const CommandLine::StringType& string) {
+  for (size_t i = 0; i < SB_ARRAY_SIZE_INT(kSwitchPrefixes); ++i) {
+    CommandLine::StringType prefix(kSwitchPrefixes[i]);
+    if (string.compare(0, prefix.length(), prefix) == 0)
+      return prefix.length();
+  }
+  return 0;
+}
+
+// Fills in |switch_string| and |switch_value| if |string| is a switch.
+// This will preserve the input switch prefix in the output |switch_string|.
+bool IsSwitch(const CommandLine::StringType& string,
+              CommandLine::StringType* switch_string,
+              CommandLine::StringType* switch_value) {
+  switch_string->clear();
+  switch_value->clear();
+  size_t prefix_length = GetSwitchPrefixLength(string);
+  if (prefix_length == 0 || prefix_length == string.length())
+    return false;
+
+  const size_t equals_position = string.find(kSwitchValueSeparator);
+  *switch_string = string.substr(0, equals_position);
+  if (equals_position != CommandLine::StringType::npos)
+    *switch_value = string.substr(equals_position + 1);
+  return true;
+}
+
+// Append switches and arguments, keeping switches before arguments.
+void AppendSwitchesAndArguments(CommandLine& command_line,
+                                const CommandLine::StringVector& argv) {
+  bool parse_switches = true;
+  for (size_t i = 1; i < argv.size(); ++i) {
+    CommandLine::StringType arg = argv[i];
+
+    // Begin inlined TrimWhitespace TRIM_ALL from base/string_util.cc
+    CommandLine::StringType::size_type last_char = arg.length() - 1;
+    CommandLine::StringType::size_type first_good_char =
+        arg.find_first_not_of(kWhitespaceASCII);
+    CommandLine::StringType::size_type last_good_char =
+        arg.find_last_not_of(kWhitespaceASCII);
+
+    if (arg.empty() ||
+      (first_good_char == CommandLine::StringType::npos) ||
+        (last_good_char == CommandLine::StringType::npos)) {
+      arg.clear();
+    } else {
+      // Trim the whitespace.
+      arg = arg.substr(first_good_char, last_good_char - first_good_char + 1);
+    }
+    // End inlined TrimWhitespace TRIM_ALL from base/string_util.cc
+
+    CommandLine::StringType switch_string;
+    CommandLine::StringType switch_value;
+    parse_switches &= (arg != kSwitchTerminator);
+    if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) {
+      command_line.AppendSwitch(switch_string, switch_value);
+    } else {
+      command_line.AppendArg(arg);
+    }
+  }
+}
+
+// Lowercase switches for backwards compatiblity *on Windows*.
+std::string LowerASCIIOnWindows(const std::string& string) {
+  return string;
+}
+
+}  // namespace
+
+CommandLine::CommandLine(int argc, const CommandLine::CharType* const* argv)
+    : argv_(1),
+      begin_args_(1),
+      original_argument_count_(argc),
+      original_argument_vector_(argv) {
+  InitFromArgv(argc, argv);
+}
+
+CommandLine::~CommandLine() {
+}
+
+void CommandLine::InitFromArgv(int argc,
+                               const CommandLine::CharType* const* argv) {
+  StringVector new_argv;
+  for (int i = 0; i < argc; ++i)
+    new_argv.push_back(argv[i]);
+  InitFromArgv(new_argv);
+}
+
+void CommandLine::InitFromArgv(const StringVector& argv) {
+  argv_ = StringVector(1);
+  begin_args_ = 1;
+  AppendSwitchesAndArguments(*this, argv);
+}
+
+bool CommandLine::HasSwitch(const std::string& switch_string) const {
+  return switches_.find(LowerASCIIOnWindows(switch_string)) != switches_.end();
+}
+
+CommandLine::StringType CommandLine::GetSwitchValue(
+    const std::string& switch_string) const {
+  SwitchMap::const_iterator result = switches_.end();
+  result = switches_.find(LowerASCIIOnWindows(switch_string));
+  return result == switches_.end() ? StringType() : result->second;
+}
+
+void CommandLine::AppendSwitch(const std::string& switch_string,
+                               const CommandLine::StringType& value) {
+  std::string switch_key(LowerASCIIOnWindows(switch_string));
+  StringType combined_switch_string(switch_string);
+  size_t prefix_length = GetSwitchPrefixLength(combined_switch_string);
+  switches_[switch_key.substr(prefix_length)] = value;
+  // Preserve existing switch prefixes in |argv_|; only append one if necessary.
+  if (prefix_length == 0)
+    combined_switch_string = kSwitchPrefixes[0] + combined_switch_string;
+  if (!value.empty())
+    combined_switch_string += kSwitchValueSeparator + value;
+  // Append the switch and update the switches/arguments divider |begin_args_|.
+  argv_.insert(argv_.begin() + begin_args_++, combined_switch_string);
+}
+
+CommandLine::StringVector CommandLine::GetArgs() const {
+  // Gather all arguments after the last switch (may include kSwitchTerminator).
+  StringVector args(argv_.begin() + begin_args_, argv_.end());
+  // Erase only the first kSwitchTerminator (maybe "--" is a legitimate page?)
+  StringVector::iterator switch_terminator =
+      std::find(args.begin(), args.end(), kSwitchTerminator);
+  if (switch_terminator != args.end())
+    args.erase(switch_terminator);
+  return args;
+}
+
+void CommandLine::AppendArg(const CommandLine::StringType& value) {
+  argv_.push_back(value);
+}
+
+void CommandLine::AppendArguments(const CommandLine& other,
+                                  bool include_program) {
+  AppendSwitchesAndArguments(*this, other.argv());
+}
+
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
diff --git a/src/starboard/shared/starboard/command_line.h b/src/starboard/shared/starboard/command_line.h
new file mode 100644
index 0000000..9baf916
--- /dev/null
+++ b/src/starboard/shared/starboard/command_line.h
@@ -0,0 +1,103 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This class works with command lines: building and parsing.
+// Arguments with prefixes ('--', '-') are switches.
+// Switches will precede all other arguments without switch prefixes.
+// Switches can optionally have values, delimited by '=', e.g., "-switch=value".
+// An argument of "--" will terminate switch parsing during initialization,
+// interpreting subsequent tokens as non-switch arguments, regardless of prefix.
+
+#ifndef STARBOARD_SHARED_STARBOARD_COMMAND_LINE_H_
+#define STARBOARD_SHARED_STARBOARD_COMMAND_LINE_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "starboard/types.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+
+class CommandLine {
+ public:
+  typedef std::string StringType;
+
+  typedef StringType::value_type CharType;
+  typedef std::vector<StringType> StringVector;
+  typedef std::map<std::string, StringType> SwitchMap;
+
+  // Construct a new command line from an argument list.
+  CommandLine(int argc, const CharType* const* argv);
+
+  ~CommandLine();
+
+  // Initialize from an argv vector.
+  void InitFromArgv(int argc, const CharType* const* argv);
+
+  // Returns the original command line string as a vector of strings.
+  const StringVector& argv() const { return argv_; }
+
+  int GetOriginalArgc() const { return original_argument_count_; }
+  const CharType* const* GetOriginalArgv() const {
+    return original_argument_vector_;
+  }
+
+  // Returns true if this command line contains the given switch.
+  // (Switch names are case-insensitive).
+  bool HasSwitch(const std::string& switch_string) const;
+
+  // Append a switch [with optional value] to the command line.
+  // Note: Switches will precede arguments regardless of appending order.
+  void AppendSwitch(const std::string& switch_string,
+                    const StringType& value);
+
+  // Returns the value associated with the given switch. If the switch has no
+  // value or isn't present, this method returns the empty string.
+  StringType GetSwitchValue(const std::string& switch_string) const;
+
+  // Get a copy of all switches, along with their values.
+  const SwitchMap& GetSwitches() const { return switches_; }
+
+  // Get the remaining arguments to the command.
+  StringVector GetArgs() const;
+
+  // Append an argument to the command line. Note that the argument is quoted
+  // properly such that it is interpreted as one argument to the target command.
+  // AppendArg is primarily for ASCII; non-ASCII input is interpreted as UTF-8.
+  // Note: Switches will precede arguments regardless of appending order.
+  void AppendArg(const std::string& value);
+
+  // Append the switches and arguments from another command line to this one.
+  // If |include_program| is true, include |other|'s program as well.
+  void AppendArguments(const CommandLine& other, bool include_program);
+
+ private:
+  void InitFromArgv(const StringVector& argv);
+
+  // Disallow default constructor; a program name must be explicitly specified.
+  CommandLine();
+  // Allow the copy constructor. A common pattern is to copy of the current
+  // process's command line and then add some flags to it.
+
+  // The argv array: { program, [(--|-|/)switch[=value]]*, [--], [argument]* }
+  StringVector argv_;
+
+  // Parsed-out switch keys and values.
+  SwitchMap switches_;
+
+  // The index after the program and switches, any arguments start here.
+  size_t begin_args_;
+
+  int original_argument_count_;
+  const CharType* const* original_argument_vector_;
+};
+
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_SHARED_STARBOARD_COMMAND_LINE_H_
diff --git a/src/starboard/shared/starboard/lazy_initialization_internal.cc b/src/starboard/shared/starboard/lazy_initialization_internal.cc
index 6d2cdaf..f108899 100644
--- a/src/starboard/shared/starboard/lazy_initialization_internal.cc
+++ b/src/starboard/shared/starboard/lazy_initialization_internal.cc
@@ -1,61 +1,61 @@
-// Copyright 2015 Google Inc. 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/shared/starboard/lazy_initialization_internal.h"

-

-#include "starboard/log.h"

-#include "starboard/thread.h"

-

-// INITIALIZED_STATE_UNINITIALIZED is defined in the header.

-#define INITIALIZED_STATE_INITIALIZING 2

-#define INITIALIZED_STATE_INITIALIZED 3

-

-namespace starboard {

-namespace shared {

-namespace starboard {

-

-bool EnsureInitialized(InitializedState* state) {

-  // Check what state we're in, and if we find that we are uninitialized,

-  // simultaneously mark us as initializing and return to the caller.

-  InitializedState original = SbAtomicNoBarrier_CompareAndSwap(

-      state, INITIALIZED_STATE_UNINITIALIZED, INITIALIZED_STATE_INITIALIZING);

-  if (original == INITIALIZED_STATE_UNINITIALIZED) {

-    // If we were uninitialized, we are now marked as initializing and so

-    // we relay this information to the caller, so that they may initialize.

-    return false;

-  } else if (original == INITIALIZED_STATE_INITIALIZING) {

-    // If the current state is that we are being initialized, spin until

-    // initialization is complete, then return.

-    do {

-      SbThreadYield();

-    } while (SbAtomicAcquire_Load(state) != INITIALIZED_STATE_INITIALIZED);

-  } else {

-    SB_DCHECK(original == INITIALIZED_STATE_INITIALIZED);

-  }

-

-  return true;

-}

-

-bool IsInitialized(InitializedState* state) {

-  return SbAtomicNoBarrier_Load(state) == INITIALIZED_STATE_INITIALIZED;

-}

-

-void SetInitialized(InitializedState* state) {

-  // Mark that we are initialized now.

-  SbAtomicRelease_Store(state, INITIALIZED_STATE_INITIALIZED);

-}

-

-}  // namespace starboard

-}  // namespace shared

-}  // namespace starboard

+// Copyright 2015 Google Inc. 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/shared/starboard/lazy_initialization_internal.h"
+
+#include "starboard/log.h"
+#include "starboard/thread.h"
+
+// INITIALIZED_STATE_UNINITIALIZED is defined in the header.
+#define INITIALIZED_STATE_INITIALIZING 2
+#define INITIALIZED_STATE_INITIALIZED 3
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+
+bool EnsureInitialized(InitializedState* state) {
+  // Check what state we're in, and if we find that we are uninitialized,
+  // simultaneously mark us as initializing and return to the caller.
+  InitializedState original = SbAtomicNoBarrier_CompareAndSwap(
+      state, INITIALIZED_STATE_UNINITIALIZED, INITIALIZED_STATE_INITIALIZING);
+  if (original == INITIALIZED_STATE_UNINITIALIZED) {
+    // If we were uninitialized, we are now marked as initializing and so
+    // we relay this information to the caller, so that they may initialize.
+    return false;
+  } else if (original == INITIALIZED_STATE_INITIALIZING) {
+    // If the current state is that we are being initialized, spin until
+    // initialization is complete, then return.
+    do {
+      SbThreadYield();
+    } while (SbAtomicAcquire_Load(state) != INITIALIZED_STATE_INITIALIZED);
+  } else {
+    SB_DCHECK(original == INITIALIZED_STATE_INITIALIZED);
+  }
+
+  return true;
+}
+
+bool IsInitialized(InitializedState* state) {
+  return SbAtomicNoBarrier_Load(state) == INITIALIZED_STATE_INITIALIZED;
+}
+
+void SetInitialized(InitializedState* state) {
+  // Mark that we are initialized now.
+  SbAtomicRelease_Store(state, INITIALIZED_STATE_INITIALIZED);
+}
+
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
diff --git a/src/starboard/shared/starboard/lazy_initialization_internal.h b/src/starboard/shared/starboard/lazy_initialization_internal.h
index eb7028a..f1cd603 100644
--- a/src/starboard/shared/starboard/lazy_initialization_internal.h
+++ b/src/starboard/shared/starboard/lazy_initialization_internal.h
@@ -1,47 +1,47 @@
-// Copyright 2015 Google Inc. 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_SHARED_STARBOARD_LAZY_INITIALIZATION_INTERNAL_H_

-#define STARBOARD_SHARED_STARBOARD_LAZY_INITIALIZATION_INTERNAL_H_

-

-#include "starboard/atomic.h"

-#include "starboard/shared/internal_only.h"

-#include "starboard/shared/starboard/lazy_initialization_public.h"

-

-namespace starboard {

-namespace shared {

-namespace starboard {

-

-// The utility functions defined here use atomics and spin-locks to allow for

-// easy lazy initialization in a thread-safe way.

-

-// Returns false if initialization is necessary, otherwise returns true.

-// If false is returned, you must initialize the state (e.g. by eventually

-// calling SetInitialized() or else other threads waiting for initialization

-// to complete will wait forever.)

-bool EnsureInitialized(InitializedState* state);

-

-// Returns true if the state is initialized, false otherwise.  Do not

-// use the outcome of this function to make a decision on whether to initialize

-// or not, use EnsureInitialized() for that.

-bool IsInitialized(InitializedState* state);

-

-// Sets the state as being initialized.

-void SetInitialized(InitializedState* state);

-

-}  // namespace starboard

-}  // namespace shared

-}  // namespace starboard

-

-#endif  // STARBOARD_SHARED_STARBOARD_LAZY_INITIALIZATION_INTERNAL_H_

+// Copyright 2015 Google Inc. 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_SHARED_STARBOARD_LAZY_INITIALIZATION_INTERNAL_H_
+#define STARBOARD_SHARED_STARBOARD_LAZY_INITIALIZATION_INTERNAL_H_
+
+#include "starboard/atomic.h"
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/lazy_initialization_public.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+
+// The utility functions defined here use atomics and spin-locks to allow for
+// easy lazy initialization in a thread-safe way.
+
+// Returns false if initialization is necessary, otherwise returns true.
+// If false is returned, you must initialize the state (e.g. by eventually
+// calling SetInitialized() or else other threads waiting for initialization
+// to complete will wait forever.)
+bool EnsureInitialized(InitializedState* state);
+
+// Returns true if the state is initialized, false otherwise.  Do not
+// use the outcome of this function to make a decision on whether to initialize
+// or not, use EnsureInitialized() for that.
+bool IsInitialized(InitializedState* state);
+
+// Sets the state as being initialized.
+void SetInitialized(InitializedState* state);
+
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_SHARED_STARBOARD_LAZY_INITIALIZATION_INTERNAL_H_
diff --git a/src/starboard/shared/starboard/lazy_initialization_public.h b/src/starboard/shared/starboard/lazy_initialization_public.h
index 73d0726..c4d7f8f 100644
--- a/src/starboard/shared/starboard/lazy_initialization_public.h
+++ b/src/starboard/shared/starboard/lazy_initialization_public.h
@@ -1,30 +1,30 @@
-// Copyright 2015 Google Inc. 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_SHARED_STARBOARD_LAZY_INITIALIZATION_PUBLIC_H_

-#define STARBOARD_SHARED_STARBOARD_LAZY_INITIALIZATION_PUBLIC_H_

-

-#include "starboard/atomic.h"

-

-// The utility functions defined here use atomics and spin-locks to allow for

-// easy lazy initialization in a thread-safe way.

-

-// An InitializedState should be treated as an opaque type that can be

-// initialized to kInitializedStateUninitialized and then passed into the

-// functions in this file to transition it first to a "initializing" state and

-// then an "initialized" state.

-typedef SbAtomic32 InitializedState;

-#define INITIALIZED_STATE_UNINITIALIZED 1

-

-#endif  // STARBOARD_SHARED_STARBOARD_LAZY_INITIALIZATION_PUBLIC_H_

+// Copyright 2015 Google Inc. 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_SHARED_STARBOARD_LAZY_INITIALIZATION_PUBLIC_H_
+#define STARBOARD_SHARED_STARBOARD_LAZY_INITIALIZATION_PUBLIC_H_
+
+#include "starboard/atomic.h"
+
+// The utility functions defined here use atomics and spin-locks to allow for
+// easy lazy initialization in a thread-safe way.
+
+// An InitializedState should be treated as an opaque type that can be
+// initialized to kInitializedStateUninitialized and then passed into the
+// functions in this file to transition it first to a "initializing" state and
+// then an "initialized" state.
+typedef SbAtomic32 InitializedState;
+#define INITIALIZED_STATE_UNINITIALIZED 1
+
+#endif  // STARBOARD_SHARED_STARBOARD_LAZY_INITIALIZATION_PUBLIC_H_
diff --git a/src/starboard/shared/starboard/once.cc b/src/starboard/shared/starboard/once.cc
index e472279..ace0197 100644
--- a/src/starboard/shared/starboard/once.cc
+++ b/src/starboard/shared/starboard/once.cc
@@ -1,38 +1,38 @@
-// Copyright 2015 Google Inc. 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/once.h"

-#include "starboard/shared/starboard/lazy_initialization_internal.h"

-

-// Platform-independent SbOnce() implementation based on the functionality

-// declared by starboard/shared/lazy_initialization_internal.h which internally

-// uses atomics.

-using starboard::shared::starboard::EnsureInitialized;

-using starboard::shared::starboard::SetInitialized;

-

-bool SbOnce(SbOnceControl* once_control, SbOnceInitRoutine init_routine) {

-  if (once_control == NULL) {

-    return false;

-  }

-  if (init_routine == NULL) {

-    return false;

-  }

-

-  if (!EnsureInitialized(once_control)) {

-    init_routine();

-    SetInitialized(once_control);

-  }

-

-  return true;

-}

+// Copyright 2015 Google Inc. 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/once.h"
+#include "starboard/shared/starboard/lazy_initialization_internal.h"
+
+// Platform-independent SbOnce() implementation based on the functionality
+// declared by starboard/shared/lazy_initialization_internal.h which internally
+// uses atomics.
+using starboard::shared::starboard::EnsureInitialized;
+using starboard::shared::starboard::SetInitialized;
+
+bool SbOnce(SbOnceControl* once_control, SbOnceInitRoutine init_routine) {
+  if (once_control == NULL) {
+    return false;
+  }
+  if (init_routine == NULL) {
+    return false;
+  }
+
+  if (!EnsureInitialized(once_control)) {
+    init_routine();
+    SetInitialized(once_control);
+  }
+
+  return true;
+}
diff --git a/src/starboard/shared/starboard/once_types_public.h b/src/starboard/shared/starboard/once_types_public.h
index 692e944..835c030 100644
--- a/src/starboard/shared/starboard/once_types_public.h
+++ b/src/starboard/shared/starboard/once_types_public.h
@@ -1,34 +1,34 @@
-// Copyright 2015 Google Inc. 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.

-

-// Provides definitions useful for working with SbOnce implemented using

-// SbAtomic32.

-

-#ifndef STARBOARD_SHARED_STARBOARD_ONCE_TYPES_PUBLIC_H_

-#define STARBOARD_SHARED_STARBOARD_ONCE_TYPES_PUBLIC_H_

-

-#include "starboard/atomic.h"

-#include "starboard/shared/starboard/lazy_initialization_public.h"

-

-// Defines once types for the platform-independent atomics-based implementation

-// of SbOnce() defined in shared/once.cc.

-

-// Transparent Once control handle.

-typedef InitializedState SbOnceControl;

-

-// Once static initializer.

-#define SB_ONCE_INITIALIZER \

-  { INITIALIZED_STATE_UNINITIALIZED }

-

-#endif  // STARBOARD_SHARED_STARBOARD_ONCE_TYPES_PUBLIC_H_

+// Copyright 2015 Google Inc. 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.
+
+// Provides definitions useful for working with SbOnce implemented using
+// SbAtomic32.
+
+#ifndef STARBOARD_SHARED_STARBOARD_ONCE_TYPES_PUBLIC_H_
+#define STARBOARD_SHARED_STARBOARD_ONCE_TYPES_PUBLIC_H_
+
+#include "starboard/atomic.h"
+#include "starboard/shared/starboard/lazy_initialization_public.h"
+
+// Defines once types for the platform-independent atomics-based implementation
+// of SbOnce() defined in shared/once.cc.
+
+// Transparent Once control handle.
+typedef InitializedState SbOnceControl;
+
+// Once static initializer.
+#define SB_ONCE_INITIALIZER \
+  { INITIALIZED_STATE_UNINITIALIZED }
+
+#endif  // STARBOARD_SHARED_STARBOARD_ONCE_TYPES_PUBLIC_H_
diff --git a/src/starboard/shared/starboard/player/closure.h b/src/starboard/shared/starboard/player/closure.h
new file mode 100644
index 0000000..96e2b4e
--- /dev/null
+++ b/src/starboard/shared/starboard/player/closure.h
@@ -0,0 +1,149 @@
+// Copyright 2017 Google Inc. 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_SHARED_STARBOARD_PLAYER_CLOSURE_H_
+#define STARBOARD_SHARED_STARBOARD_PLAYER_CLOSURE_H_
+
+#include "starboard/common/ref_counted.h"
+#include "starboard/log.h"
+#include "starboard/shared/internal_only.h"
+
+#ifndef __cplusplus
+#error "Only C++ files can include this header."
+#endif
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+
+class Closure {
+ public:
+  class Functor : public RefCountedThreadSafe<Functor> {
+   public:
+    virtual ~Functor() {}
+    virtual void Run() = 0;
+  };
+
+  explicit Closure(const scoped_refptr<Functor> functor = NULL)
+      : functor_(functor) {}
+
+  bool operator==(const Closure& that) const {
+    return functor_ == that.functor_;
+  }
+
+  bool is_valid() const { return functor_ != NULL; }
+  void reset() { functor_ = NULL; }
+  void Run() {
+    SB_DCHECK(functor_);
+    if (functor_) {
+      functor_->Run();
+    }
+  }
+
+ private:
+  scoped_refptr<Functor> functor_;
+};
+
+inline Closure Bind(void (*closure)()) {
+  class FunctorImpl : public Closure::Functor {
+   public:
+    explicit FunctorImpl(void (*closure)()) : closure_(closure) {}
+
+    void Run() SB_OVERRIDE { closure_(); }
+
+   private:
+    void (*closure_)();
+  };
+  return Closure(new FunctorImpl(closure));
+}
+
+template <typename Param>
+inline Closure Bind(void (*func)(Param), Param param) {
+  class FunctorImpl : public Closure::Functor {
+   public:
+    FunctorImpl(void (*func)(Param), Param param)
+        : func_(func), param_(param) {}
+
+    void Run() SB_OVERRIDE { func_(param_); }
+
+   private:
+    void (*func_)(Param);
+    Param param_;
+  };
+  return Closure(new FunctorImpl(func, param));
+}
+
+template <typename C>
+inline Closure Bind(void (C::*func)(), C* obj) {
+  class FunctorImpl : public Closure::Functor {
+   public:
+    FunctorImpl(void (C::*func)(), C* obj) : func_(func), obj_(obj) {}
+
+    void Run() SB_OVERRIDE { ((*obj_).*func_)(); }
+
+   private:
+    void (C::*func_)();
+    C* obj_;
+  };
+  return Closure(new FunctorImpl(func, obj));
+}
+
+template <typename C, typename Param>
+inline Closure Bind(void (C::*func)(Param), C* obj, Param param) {
+  class FunctorImpl : public Closure::Functor {
+   public:
+    FunctorImpl(void (C::*func)(Param), C* obj, Param param)
+        : func_(func), obj_(obj), param_(param) {}
+
+    void Run() SB_OVERRIDE { ((*obj_).*func_)(param_); }
+
+   private:
+    void (C::*func_)(Param);
+    C* obj_;
+    Param param_;
+  };
+  return Closure(new FunctorImpl(func, obj, param));
+}
+
+template <typename C, typename Param1, typename Param2>
+inline Closure Bind(void (C::*func)(Param1, Param2),
+                    C* obj,
+                    Param1 param1,
+                    Param2 param2) {
+  class FunctorImpl : public Closure::Functor {
+   public:
+    FunctorImpl(void (C::*func)(Param1, Param2),
+                C* obj,
+                Param1 param1,
+                Param2 param2)
+        : func_(func), obj_(obj), param1_(param1), param2_(param2) {}
+
+    void Run() SB_OVERRIDE { ((*obj_).*func_)(param1_, param2_); }
+
+   private:
+    void (C::*func_)(Param1, Param2);
+    C* obj_;
+    Param1 param1_;
+    Param2 param2_;
+  };
+  return Closure(new FunctorImpl(func, obj, param1, param2));
+}
+
+}  // namespace player
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_SHARED_STARBOARD_PLAYER_CLOSURE_H_
diff --git a/src/starboard/shared/starboard/player/decoded_audio_internal.cc b/src/starboard/shared/starboard/player/decoded_audio_internal.cc
new file mode 100644
index 0000000..1d53da4
--- /dev/null
+++ b/src/starboard/shared/starboard/player/decoded_audio_internal.cc
@@ -0,0 +1,34 @@
+// Copyright 2017 Google Inc. 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/shared/starboard/player/decoded_audio_internal.h"
+
+#include "starboard/log.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+
+DecodedAudio::DecodedAudio() : pts_(0), size_(0) {}
+
+DecodedAudio::DecodedAudio(SbMediaTime pts, size_t size)
+    : pts_(pts), buffer_(new uint8_t[size]), size_(size) {
+  SB_DCHECK(size > 0) << size;
+}
+
+}  // namespace player
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
diff --git a/src/starboard/shared/starboard/player/decoded_audio_internal.h b/src/starboard/shared/starboard/player/decoded_audio_internal.h
new file mode 100644
index 0000000..40a12fe
--- /dev/null
+++ b/src/starboard/shared/starboard/player/decoded_audio_internal.h
@@ -0,0 +1,61 @@
+// Copyright 2017 Google Inc. 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_SHARED_STARBOARD_PLAYER_DECODED_AUDIO_INTERNAL_H_
+#define STARBOARD_SHARED_STARBOARD_PLAYER_DECODED_AUDIO_INTERNAL_H_
+
+#include "starboard/common/ref_counted.h"
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/media.h"
+#include "starboard/shared/internal_only.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+
+// Decoded audio frames produced by an audio decoder.  It can contain multiple
+// frames with continuous timestamps.
+// It doesn't have a specific storage type and sample type.  The decoder and the
+// renderer will determinate the proper storage type and sample type in their
+// own way.
+class DecodedAudio : public RefCountedThreadSafe<DecodedAudio> {
+ public:
+  DecodedAudio();  // Signal an EOS.
+  DecodedAudio(SbMediaTime pts, size_t size);
+
+  bool is_end_of_stream() const { return buffer_ == NULL; }
+  SbMediaTime pts() const { return pts_; }
+  const uint8_t* buffer() const { return buffer_.get(); }
+  size_t size() const { return size_; }
+
+  uint8_t* buffer() { return buffer_.get(); }
+
+ private:
+  // The timestamp of the first audio frame.
+  SbMediaTime pts_;
+  // Use scoped_array<uint8_t> instead of std::vector<uint8_t> to avoid wasting
+  // time on setting content to 0.
+  scoped_array<uint8_t> buffer_;
+  size_t size_;
+
+  SB_DISALLOW_COPY_AND_ASSIGN(DecodedAudio);
+};
+
+}  // namespace player
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_SHARED_STARBOARD_PLAYER_DECODED_AUDIO_INTERNAL_H_
diff --git a/src/starboard/shared/starboard/player/filter/audio_decoder_internal.h b/src/starboard/shared/starboard/player/filter/audio_decoder_internal.h
index cb1850c..6fb5acc 100644
--- a/src/starboard/shared/starboard/player/filter/audio_decoder_internal.h
+++ b/src/starboard/shared/starboard/player/filter/audio_decoder_internal.h
@@ -19,6 +19,7 @@
 
 #include "starboard/media.h"
 #include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/player/decoded_audio_internal.h"
 #include "starboard/shared/starboard/player/input_buffer_internal.h"
 #include "starboard/types.h"
 
@@ -33,12 +34,17 @@
  public:
   virtual ~AudioDecoder() {}
 
-  // Decode the encoded audio data stored in |input_buffer| and store the
-  // result in |output|.
-  virtual void Decode(const InputBuffer& input_buffer,
-                      std::vector<uint8_t>* output) = 0;
+  // Decode the encoded audio data stored in |input_buffer|.
+  virtual void Decode(const InputBuffer& input_buffer) = 0;
+
   // Note that there won't be more input data unless Reset() is called.
   virtual void WriteEndOfStream() = 0;
+
+  // Try to read the next decoded audio buffer.  If there is no decoded audio
+  // available currently, it returns NULL.  If the audio stream reaches EOS and
+  // there is no more decoded audio available, it returns an EOS buffer.
+  virtual scoped_refptr<DecodedAudio> Read() = 0;
+
   // Clear any cached buffer of the codec and reset the state of the codec.
   // This function will be called during seek to ensure that the left over
   // data from previous buffers are cleared.
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc
index 105e23f..4c3ea65 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc
@@ -17,6 +17,7 @@
 #include <algorithm>
 
 #include "starboard/memory.h"
+#include "starboard/shared/starboard/player/closure.h"
 
 namespace starboard {
 namespace shared {
@@ -24,15 +25,11 @@
 namespace player {
 namespace filter {
 
-namespace {
-// TODO: This should be retrieved from the decoder.
-// TODO: Make it not dependent on the frame size of AAC and HE-AAC.
-const int kMaxFramesPerAccessUnit = 1024 * 2;
-}  // namespace
-
-AudioRenderer::AudioRenderer(scoped_ptr<AudioDecoder> decoder,
+AudioRenderer::AudioRenderer(JobQueue* job_queue,
+                             scoped_ptr<AudioDecoder> decoder,
                              const SbMediaAudioHeader& audio_header)
-    : channels_(audio_header.number_of_channels),
+    : job_queue_(job_queue),
+      channels_(audio_header.number_of_channels),
       bytes_per_frame_(
           (decoder->GetSampleType() == kSbMediaAudioSampleTypeInt16 ? 2 : 4) *
           channels_),
@@ -43,75 +40,59 @@
       frames_in_buffer_(0),
       offset_in_frames_(0),
       frames_consumed_(0),
-      end_of_stream_reached_(false),
+      end_of_stream_written_(false),
+      end_of_stream_decoded_(false),
       decoder_(decoder.Pass()),
       audio_sink_(kSbAudioSinkInvalid) {
+  SB_DCHECK(job_queue != NULL);
   SB_DCHECK(decoder_ != NULL);
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
   frame_buffers_[0] = &frame_buffer_[0];
 }
 
 AudioRenderer::~AudioRenderer() {
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
   if (audio_sink_ != kSbAudioSinkInvalid) {
     SbAudioSinkDestroy(audio_sink_);
   }
+
+  if (read_from_decoder_closure_.is_valid()) {
+    job_queue_->Remove(read_from_decoder_closure_);
+  }
 }
 
 void AudioRenderer::WriteSample(const InputBuffer& input_buffer) {
-  if (end_of_stream_reached_) {
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
+  if (end_of_stream_written_) {
     SB_LOG(ERROR) << "Appending audio sample at " << input_buffer.pts()
                   << " after EOS reached.";
     return;
   }
 
-  SbMediaTime input_pts = input_buffer.pts();
-  std::vector<uint8_t> decoded_audio;
-  decoder_->Decode(input_buffer, &decoded_audio);
-  if (decoded_audio.empty()) {
-    SB_DLOG(ERROR) << "decoded_audio contains no frames.";
-    return;
-  }
+  decoder_->Decode(input_buffer);
 
-  {
-    ScopedLock lock(mutex_);
-    if (seeking_) {
-      if (input_pts < seeking_to_pts_) {
-        return;
-      }
-    }
-
-    AppendFrames(&decoded_audio[0], decoded_audio.size() / bytes_per_frame_);
-
-    if (seeking_ && frame_buffer_.size() > kPrerollFrames * bytes_per_frame_) {
-      seeking_ = false;
-    }
-  }
-
-  // Create the audio sink if it is the first incoming AU after seeking.
-  if (audio_sink_ == kSbAudioSinkInvalid) {
-    int sample_rate = decoder_->GetSamplesPerSecond();
-    // TODO: Implement resampler.
-    SB_DCHECK(sample_rate ==
-              SbAudioSinkGetNearestSupportedSampleFrequency(sample_rate));
-    // TODO: Handle sink creation failure.
-    audio_sink_ = SbAudioSinkCreate(
-        channels_, sample_rate, decoder_->GetSampleType(),
-        kSbMediaAudioFrameStorageTypeInterleaved,
-        reinterpret_cast<SbAudioSinkFrameBuffers>(frame_buffers_),
-        kMaxCachedFrames, &AudioRenderer::UpdateSourceStatusFunc,
-        &AudioRenderer::ConsumeFramesFunc, this);
+  ScopedLock lock(mutex_);
+  if (!read_from_decoder_closure_.is_valid()) {
+    read_from_decoder_closure_ = Bind(&AudioRenderer::ReadFromDecoder, this);
+    job_queue_->Schedule(read_from_decoder_closure_);
   }
 }
 
 void AudioRenderer::WriteEndOfStream() {
-  SB_LOG_IF(WARNING, end_of_stream_reached_)
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
+  SB_LOG_IF(WARNING, end_of_stream_written_)
       << "Try to write EOS after EOS is reached";
-  if (end_of_stream_reached_) {
+  if (end_of_stream_written_) {
     return;
   }
-  end_of_stream_reached_ = true;
   decoder_->WriteEndOfStream();
 
   ScopedLock lock(mutex_);
+  end_of_stream_written_ = true;
   // If we are seeking, we consider the seek is finished if end of stream is
   // reached as there won't be any audio data in future.
   if (seeking_) {
@@ -120,19 +101,26 @@
 }
 
 void AudioRenderer::Play() {
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
   ScopedLock lock(mutex_);
   paused_ = false;
 }
 
 void AudioRenderer::Pause() {
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
   ScopedLock lock(mutex_);
   paused_ = true;
 }
 
 void AudioRenderer::Seek(SbMediaTime seek_to_pts) {
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
   SB_DCHECK(seek_to_pts >= 0);
 
   SbAudioSinkDestroy(audio_sink_);
+  // Now the sink is destroyed and the callbacks will no longer be called, so
+  // the following modifications are safe without lock.
   audio_sink_ = kSbAudioSinkInvalid;
 
   seeking_to_pts_ = std::max<SbMediaTime>(seek_to_pts, 0);
@@ -140,27 +128,38 @@
   frames_in_buffer_ = 0;
   offset_in_frames_ = 0;
   frames_consumed_ = 0;
-  end_of_stream_reached_ = false;
+  end_of_stream_written_ = false;
+  end_of_stream_decoded_ = false;
+  pending_decoded_audio_ = NULL;
 
   decoder_->Reset();
-  return;
 }
 
 bool AudioRenderer::IsEndOfStreamPlayed() const {
-  return end_of_stream_reached_ && frames_in_buffer_ == 0;
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
+  ScopedLock lock(mutex_);
+  return end_of_stream_decoded_ && frames_in_buffer_ == 0;
 }
 
 bool AudioRenderer::CanAcceptMoreData() const {
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
   ScopedLock lock(mutex_);
-  return frames_in_buffer_ <= kMaxCachedFrames - kMaxFramesPerAccessUnit &&
-         !end_of_stream_reached_;
+  if (end_of_stream_written_) {
+    return false;
+  }
+  return pending_decoded_audio_ == NULL;
 }
 
 bool AudioRenderer::IsSeekingInProgress() const {
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
   return seeking_;
 }
 
 SbMediaTime AudioRenderer::GetCurrentTime() {
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
   if (seeking_) {
     return seeking_to_pts_;
   }
@@ -169,38 +168,13 @@
              decoder_->GetSamplesPerSecond();
 }
 
-// static
-void AudioRenderer::UpdateSourceStatusFunc(int* frames_in_buffer,
-                                           int* offset_in_frames,
-                                           bool* is_playing,
-                                           bool* is_eos_reached,
-                                           void* context) {
-  AudioRenderer* audio_renderer = reinterpret_cast<AudioRenderer*>(context);
-  SB_DCHECK(audio_renderer);
-  SB_DCHECK(frames_in_buffer);
-  SB_DCHECK(offset_in_frames);
-  SB_DCHECK(is_playing);
-  SB_DCHECK(is_eos_reached);
-
-  audio_renderer->UpdateSourceStatus(frames_in_buffer, offset_in_frames,
-                                     is_playing, is_eos_reached);
-}
-
-// static
-void AudioRenderer::ConsumeFramesFunc(int frames_consumed, void* context) {
-  AudioRenderer* audio_renderer = reinterpret_cast<AudioRenderer*>(context);
-  SB_DCHECK(audio_renderer);
-
-  audio_renderer->ConsumeFrames(frames_consumed);
-}
-
 void AudioRenderer::UpdateSourceStatus(int* frames_in_buffer,
                                        int* offset_in_frames,
                                        bool* is_playing,
                                        bool* is_eos_reached) {
   ScopedLock lock(mutex_);
 
-  *is_eos_reached = end_of_stream_reached_;
+  *is_eos_reached = end_of_stream_decoded_;
 
   if (paused_ || seeking_) {
     *is_playing = false;
@@ -221,11 +195,84 @@
   offset_in_frames_ %= kMaxCachedFrames;
   frames_in_buffer_ -= frames_consumed;
   frames_consumed_ += frames_consumed;
+
+  bool decoded_audio_available =
+      pending_decoded_audio_ ||
+      (end_of_stream_written_ && !end_of_stream_decoded_);
+  if (decoded_audio_available && !read_from_decoder_closure_.is_valid()) {
+    read_from_decoder_closure_ = Bind(&AudioRenderer::ReadFromDecoder, this);
+    job_queue_->Schedule(read_from_decoder_closure_);
+  }
 }
 
-void AudioRenderer::AppendFrames(const uint8_t* source_buffer,
-                                 int frames_to_append) {
-  SB_DCHECK(frames_in_buffer_ + frames_to_append <= kMaxCachedFrames);
+// Try to read some audio data from the decoder.  Note that this operation is
+// valid across seeking.  If a seek happens immediately after a ReadFromDecoder
+// request is scheduled, the seek will reset the decoder.  So the
+// ReadFromDecoder request will not read stale data.
+void AudioRenderer::ReadFromDecoder() {
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
+  ScopedLock lock(mutex_);
+  SB_DCHECK(read_from_decoder_closure_.is_valid());
+  read_from_decoder_closure_.reset();
+
+  scoped_refptr<DecodedAudio> decoded_audio =
+      pending_decoded_audio_ ? pending_decoded_audio_ : decoder_->Read();
+  pending_decoded_audio_ = NULL;
+  if (!decoded_audio) {
+    return;
+  }
+
+  if (decoded_audio->is_end_of_stream()) {
+    SB_DCHECK(end_of_stream_written_);
+    end_of_stream_decoded_ = true;
+    return;
+  }
+
+  if (seeking_) {
+    if (decoded_audio->pts() < seeking_to_pts_) {
+      // Discard any audio data before the seeking target.
+      return;
+    }
+  }
+
+  if (!AppendDecodedAudio_Locked(decoded_audio)) {
+    pending_decoded_audio_ = decoded_audio;
+    return;
+  }
+
+  if (seeking_ && frame_buffer_.size() > kPrerollFrames * bytes_per_frame_) {
+    seeking_ = false;
+  }
+
+  // Create the audio sink if it is the first incoming AU after seeking.
+  if (audio_sink_ == kSbAudioSinkInvalid) {
+    int sample_rate = decoder_->GetSamplesPerSecond();
+    // TODO: Implement resampler.
+    SB_DCHECK(sample_rate ==
+              SbAudioSinkGetNearestSupportedSampleFrequency(sample_rate));
+    // TODO: Handle sink creation failure.
+    audio_sink_ = SbAudioSinkCreate(
+        channels_, sample_rate, decoder_->GetSampleType(),
+        kSbMediaAudioFrameStorageTypeInterleaved,
+        reinterpret_cast<SbAudioSinkFrameBuffers>(frame_buffers_),
+        kMaxCachedFrames, &AudioRenderer::UpdateSourceStatusFunc,
+        &AudioRenderer::ConsumeFramesFunc, this);
+  }
+}
+
+// TODO: This function should be executed when lock is not acquired as it copies
+// relatively large amount of data.
+bool AudioRenderer::AppendDecodedAudio_Locked(
+    const scoped_refptr<DecodedAudio>& decoded_audio) {
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
+  const uint8_t* source_buffer = decoded_audio->buffer();
+  int frames_to_append = decoded_audio->size() / bytes_per_frame_;
+
+  if (frames_in_buffer_ + frames_to_append > kMaxCachedFrames) {
+    return false;
+  }
 
   int offset_to_append =
       (offset_in_frames_ + frames_in_buffer_) % kMaxCachedFrames;
@@ -241,6 +288,33 @@
   SbMemoryCopy(&frame_buffer_[offset_to_append * bytes_per_frame_],
                source_buffer, frames_to_append * bytes_per_frame_);
   frames_in_buffer_ += frames_to_append;
+
+  return true;
+}
+
+// static
+void AudioRenderer::UpdateSourceStatusFunc(int* frames_in_buffer,
+                                           int* offset_in_frames,
+                                           bool* is_playing,
+                                           bool* is_eos_reached,
+                                           void* context) {
+  AudioRenderer* audio_renderer = static_cast<AudioRenderer*>(context);
+  SB_DCHECK(audio_renderer);
+  SB_DCHECK(frames_in_buffer);
+  SB_DCHECK(offset_in_frames);
+  SB_DCHECK(is_playing);
+  SB_DCHECK(is_eos_reached);
+
+  audio_renderer->UpdateSourceStatus(frames_in_buffer, offset_in_frames,
+                                     is_playing, is_eos_reached);
+}
+
+// static
+void AudioRenderer::ConsumeFramesFunc(int frames_consumed, void* context) {
+  AudioRenderer* audio_renderer = static_cast<AudioRenderer*>(context);
+  SB_DCHECK(audio_renderer);
+
+  audio_renderer->ConsumeFrames(frames_consumed);
 }
 
 }  // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
index e14457c..745c4f0 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
@@ -23,8 +23,11 @@
 #include "starboard/media.h"
 #include "starboard/mutex.h"
 #include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/player/closure.h"
+#include "starboard/shared/starboard/player/decoded_audio_internal.h"
 #include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
 #include "starboard/shared/starboard/player/input_buffer_internal.h"
+#include "starboard/shared/starboard/player/job_queue.h"
 #include "starboard/types.h"
 
 namespace starboard {
@@ -35,7 +38,8 @@
 
 class AudioRenderer {
  public:
-  AudioRenderer(scoped_ptr<AudioDecoder> decoder,
+  AudioRenderer(JobQueue* job_queue,
+                scoped_ptr<AudioDecoder> decoder,
                 const SbMediaAudioHeader& audio_header);
   ~AudioRenderer();
 
@@ -48,7 +52,7 @@
   void Pause();
   void Seek(SbMediaTime seek_to_pts);
 
-  bool IsEndOfStreamWritten() const { return end_of_stream_reached_; }
+  bool IsEndOfStreamWritten() const { return end_of_stream_written_; }
   bool IsEndOfStreamPlayed() const;
   bool CanAcceptMoreData() const;
   bool IsSeekingInProgress() const;
@@ -64,6 +68,16 @@
   //    no longer accept more data.
   static const size_t kMaxCachedFrames = 256 * 1024;
 
+  void UpdateSourceStatus(int* frames_in_buffer,
+                          int* offset_in_frames,
+                          bool* is_playing,
+                          bool* is_eos_reached);
+  void ConsumeFrames(int frames_consumed);
+
+  void ReadFromDecoder();
+  bool AppendDecodedAudio_Locked(
+      const scoped_refptr<DecodedAudio>& decoded_audio);
+
   // SbAudioSink callbacks
   static void UpdateSourceStatusFunc(int* frames_in_buffer,
                                      int* offset_in_frames,
@@ -71,14 +85,8 @@
                                      bool* is_eos_reached,
                                      void* context);
   static void ConsumeFramesFunc(int frames_consumed, void* context);
-  void UpdateSourceStatus(int* frames_in_buffer,
-                          int* offset_in_frames,
-                          bool* is_playing,
-                          bool* is_eos_reached);
-  void ConsumeFrames(int frames_consumed);
 
-  void AppendFrames(const uint8_t* source_buffer, int frames_to_append);
-
+  JobQueue* job_queue_;
   const int channels_;
   const int bytes_per_frame_;
 
@@ -93,10 +101,13 @@
   int offset_in_frames_;
 
   int frames_consumed_;
-  bool end_of_stream_reached_;
+  bool end_of_stream_written_;
+  bool end_of_stream_decoded_;
 
   scoped_ptr<AudioDecoder> decoder_;
   SbAudioSink audio_sink_;
+  scoped_refptr<DecodedAudio> pending_decoded_audio_;
+  Closure read_from_decoder_closure_;
 };
 
 }  // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
index 973ed57..447b416 100644
--- a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
+++ b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
@@ -29,12 +29,20 @@
 namespace player {
 namespace filter {
 
+namespace {
+
+// TODO: Make this configurable inside SbPlayerCreate().
+const SbTimeMonotonic kUpdateInterval = 5 * kSbTimeMillisecond;
+
+}  // namespace
+
 FilterBasedPlayerWorkerHandler::FilterBasedPlayerWorkerHandler(
     SbMediaVideoCodec video_codec,
     SbMediaAudioCodec audio_codec,
     SbDrmSystem drm_system,
     const SbMediaAudioHeader& audio_header)
     : player_worker_(NULL),
+      job_queue_(NULL),
       player_(kSbPlayerInvalid),
       update_media_time_cb_(NULL),
       get_player_state_cb_(NULL),
@@ -43,10 +51,15 @@
       audio_codec_(audio_codec),
       drm_system_(drm_system),
       audio_header_(audio_header),
-      paused_(false) {}
+      paused_(false) {
+#if SB_IS(PLAYER_PUNCHED_OUT)
+  bounds_ = PlayerWorker::Bounds();
+#endif  // SB_IS(PLAYER_PUNCHED_OUT)
+}
 
-void FilterBasedPlayerWorkerHandler::Setup(
+bool FilterBasedPlayerWorkerHandler::Init(
     PlayerWorker* player_worker,
+    JobQueue* job_queue,
     SbPlayer player,
     UpdateMediaTimeCB update_media_time_cb,
     GetPlayerStateCB get_player_state_cb,
@@ -56,19 +69,20 @@
 
   // All parameters has to be valid.
   SB_DCHECK(player_worker);
+  SB_DCHECK(job_queue);
+  SB_DCHECK(job_queue->BelongsToCurrentThread());
   SB_DCHECK(SbPlayerIsValid(player));
   SB_DCHECK(update_media_time_cb);
   SB_DCHECK(get_player_state_cb);
   SB_DCHECK(update_player_state_cb);
 
   player_worker_ = player_worker;
+  job_queue_ = job_queue;
   player_ = player;
   update_media_time_cb_ = update_media_time_cb;
   get_player_state_cb_ = get_player_state_cb;
   update_player_state_cb_ = update_player_state_cb;
-}
 
-bool FilterBasedPlayerWorkerHandler::ProcessInitEvent() {
   scoped_ptr<AudioDecoder> audio_decoder(
       AudioDecoder::Create(audio_codec_, audio_header_));
   scoped_ptr<VideoDecoder> video_decoder(VideoDecoder::Create(video_codec_));
@@ -77,9 +91,12 @@
     return false;
   }
 
-  audio_renderer_.reset(new AudioRenderer(audio_decoder.Pass(), audio_header_));
+  audio_renderer_.reset(
+      new AudioRenderer(job_queue, audio_decoder.Pass(), audio_header_));
   video_renderer_.reset(new VideoRenderer(video_decoder.Pass()));
   if (audio_renderer_->is_valid() && video_renderer_->is_valid()) {
+    update_closure_ = Bind(&FilterBasedPlayerWorkerHandler::Update, this);
+    job_queue_->Schedule(update_closure_, kUpdateInterval);
     return true;
   }
 
@@ -88,9 +105,9 @@
   return false;
 }
 
-bool FilterBasedPlayerWorkerHandler::ProcessSeekEvent(
-    const SeekEventData& data) {
-  SbMediaTime seek_to_pts = data.seek_to_pts;
+bool FilterBasedPlayerWorkerHandler::Seek(SbMediaTime seek_to_pts, int ticket) {
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
   if (seek_to_pts < 0) {
     SB_DLOG(ERROR) << "Try to seek to negative timestamp " << seek_to_pts;
     seek_to_pts = 0;
@@ -102,14 +119,14 @@
   return true;
 }
 
-bool FilterBasedPlayerWorkerHandler::ProcessWriteSampleEvent(
-    const WriteSampleEventData& data,
-    bool* written) {
+bool FilterBasedPlayerWorkerHandler::WriteSample(InputBuffer input_buffer,
+                                                 bool* written) {
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
   SB_DCHECK(written != NULL);
 
   *written = true;
 
-  if (data.sample_type == kSbMediaTypeAudio) {
+  if (input_buffer.sample_type() == kSbMediaTypeAudio) {
     if (audio_renderer_->IsEndOfStreamWritten()) {
       SB_LOG(WARNING) << "Try to write audio sample after EOS is reached";
     } else {
@@ -118,20 +135,19 @@
         return true;
       }
 
-      if (data.input_buffer->drm_info()) {
+      if (input_buffer.drm_info()) {
         if (!SbDrmSystemIsValid(drm_system_)) {
           return false;
         }
-        if (drm_system_->Decrypt(data.input_buffer) ==
-            SbDrmSystemPrivate::kRetry) {
+        if (drm_system_->Decrypt(&input_buffer) == SbDrmSystemPrivate::kRetry) {
           *written = false;
           return true;
         }
       }
-      audio_renderer_->WriteSample(*data.input_buffer);
+      audio_renderer_->WriteSample(input_buffer);
     }
   } else {
-    SB_DCHECK(data.sample_type == kSbMediaTypeVideo);
+    SB_DCHECK(input_buffer.sample_type() == kSbMediaTypeVideo);
     if (video_renderer_->IsEndOfStreamWritten()) {
       SB_LOG(WARNING) << "Try to write video sample after EOS is reached";
     } else {
@@ -139,26 +155,26 @@
         *written = false;
         return true;
       }
-      if (data.input_buffer->drm_info()) {
+      if (input_buffer.drm_info()) {
         if (!SbDrmSystemIsValid(drm_system_)) {
           return false;
         }
-        if (drm_system_->Decrypt(data.input_buffer) ==
-            SbDrmSystemPrivate::kRetry) {
+        if (drm_system_->Decrypt(&input_buffer) == SbDrmSystemPrivate::kRetry) {
           *written = false;
           return true;
         }
       }
-      video_renderer_->WriteSample(*data.input_buffer);
+      video_renderer_->WriteSample(input_buffer);
     }
   }
 
   return true;
 }
 
-bool FilterBasedPlayerWorkerHandler::ProcessWriteEndOfStreamEvent(
-    const WriteEndOfStreamEventData& data) {
-  if (data.stream_type == kSbMediaTypeAudio) {
+bool FilterBasedPlayerWorkerHandler::WriteEndOfStream(SbMediaType sample_type) {
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
+  if (sample_type == kSbMediaTypeAudio) {
     if (audio_renderer_->IsEndOfStreamWritten()) {
       SB_LOG(WARNING) << "Try to write audio EOS after EOS is enqueued";
     } else {
@@ -177,11 +193,12 @@
   return true;
 }
 
-bool FilterBasedPlayerWorkerHandler::ProcessSetPauseEvent(
-    const SetPauseEventData& data) {
-  paused_ = data.pause;
+bool FilterBasedPlayerWorkerHandler::SetPause(bool pause) {
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
 
-  if (data.pause) {
+  paused_ = pause;
+
+  if (pause) {
     audio_renderer_->Pause();
     SB_DLOG(INFO) << "Playback paused.";
   } else {
@@ -192,8 +209,26 @@
   return true;
 }
 
-bool FilterBasedPlayerWorkerHandler::ProcessUpdateEvent(
-    const SetBoundsEventData& data) {
+#if SB_IS(PLAYER_PUNCHED_OUT)
+bool FilterBasedPlayerWorkerHandler::SetBounds(
+    const PlayerWorker::Bounds& bounds) {
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
+  if (SbMemoryCompare(&bounds_, &bounds, sizeof(bounds_)) != 0) {
+    bounds_ = bounds;
+    // Force an update
+    job_queue_->Remove(update_closure_);
+    Update();
+  }
+
+  return true;
+}
+#endif  // SB_IS(PLAYER_PUNCHED_OUT)
+
+// TODO: This should be driven by callbacks instead polling.
+void FilterBasedPlayerWorkerHandler::Update() {
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
   if ((*player_worker_.*get_player_state_cb_)() == kSbPlayerStatePrerolling) {
     if (!audio_renderer_->IsSeekingInProgress() &&
         !video_renderer_->IsSeekingInProgress()) {
@@ -217,7 +252,7 @@
 
 #if SB_IS(PLAYER_PUNCHED_OUT)
     shared::starboard::Application::Get()->HandleFrame(
-        player_, frame, data.x, data.y, data.width, data.height);
+        player_, frame, bounds_.x, bounds_.y, bounds_.width, bounds_.height);
 #endif  // SB_IS(PLAYER_PUNCHED_OUT)
 
     (*player_worker_.*update_media_time_cb_)(audio_renderer_->GetCurrentTime());
@@ -227,10 +262,12 @@
     video_renderer_->Update();
   }
 
-  return true;
+  job_queue_->Schedule(update_closure_, kUpdateInterval);
 }
 
-void FilterBasedPlayerWorkerHandler::ProcessStopEvent() {
+void FilterBasedPlayerWorkerHandler::Stop() {
+  job_queue_->Remove(update_closure_);
+
   audio_renderer_.reset();
   video_renderer_.reset();
 
diff --git a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
index cde0391..370178d 100644
--- a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
+++ b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
@@ -19,9 +19,11 @@
 #include "starboard/configuration.h"
 #include "starboard/media.h"
 #include "starboard/player.h"
+#include "starboard/shared/starboard/player/closure.h"
 #include "starboard/shared/starboard/player/filter/audio_renderer_internal.h"
 #include "starboard/shared/starboard/player/filter/video_renderer_internal.h"
 #include "starboard/shared/starboard/player/input_buffer_internal.h"
+#include "starboard/shared/starboard/player/job_queue.h"
 #include "starboard/shared/starboard/player/player_worker.h"
 #include "starboard/time.h"
 
@@ -39,22 +41,25 @@
                                  const SbMediaAudioHeader& audio_header);
 
  private:
-  void Setup(PlayerWorker* player_worker,
-             SbPlayer player,
-             UpdateMediaTimeCB update_media_time_cb,
-             GetPlayerStateCB get_player_state_cb,
-             UpdatePlayerStateCB update_player_state_cb) SB_OVERRIDE;
-  bool ProcessInitEvent() SB_OVERRIDE;
-  bool ProcessSeekEvent(const SeekEventData& data) SB_OVERRIDE;
-  bool ProcessWriteSampleEvent(const WriteSampleEventData& data,
-                               bool* written) SB_OVERRIDE;
-  bool ProcessWriteEndOfStreamEvent(const WriteEndOfStreamEventData& data)
-      SB_OVERRIDE;
-  bool ProcessSetPauseEvent(const SetPauseEventData& data) SB_OVERRIDE;
-  bool ProcessUpdateEvent(const SetBoundsEventData& data) SB_OVERRIDE;
-  void ProcessStopEvent() SB_OVERRIDE;
+  bool Init(PlayerWorker* player_worker,
+            JobQueue* job_queue,
+            SbPlayer player,
+            UpdateMediaTimeCB update_media_time_cb,
+            GetPlayerStateCB get_player_state_cb,
+            UpdatePlayerStateCB update_player_state_cb) SB_OVERRIDE;
+  bool Seek(SbMediaTime seek_to_pts, int ticket) SB_OVERRIDE;
+  bool WriteSample(InputBuffer input_buffer, bool* written) SB_OVERRIDE;
+  bool WriteEndOfStream(SbMediaType sample_type) SB_OVERRIDE;
+  bool SetPause(bool pause) SB_OVERRIDE;
+#if SB_IS(PLAYER_PUNCHED_OUT)
+  bool SetBounds(const PlayerWorker::Bounds& bounds) SB_OVERRIDE;
+#endif  // SB_IS(PLAYER_PUNCHED_OUT)
+  void Stop() SB_OVERRIDE;
+
+  void Update();
 
   PlayerWorker* player_worker_;
+  JobQueue* job_queue_;
   SbPlayer player_;
   UpdateMediaTimeCB update_media_time_cb_;
   GetPlayerStateCB get_player_state_cb_;
@@ -69,6 +74,10 @@
   scoped_ptr<VideoRenderer> video_renderer_;
 
   bool paused_;
+#if SB_IS(PLAYER_PUNCHED_OUT)
+  PlayerWorker::Bounds bounds_;
+#endif  // SB_IS(PLAYER_PUNCHED_OUT)
+  Closure update_closure_;
 };
 
 }  // namespace filter
diff --git a/src/starboard/shared/starboard/player/input_buffer_internal.cc b/src/starboard/shared/starboard/player/input_buffer_internal.cc
index 8431fc2..9794c14 100644
--- a/src/starboard/shared/starboard/player/input_buffer_internal.cc
+++ b/src/starboard/shared/starboard/player/input_buffer_internal.cc
@@ -1,196 +1,211 @@
-// Copyright 2016 Google Inc. 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/shared/starboard/player/input_buffer_internal.h"

-

-#include <vector>

-

-#include "starboard/atomic.h"

-#include "starboard/log.h"

-#include "starboard/memory.h"

-

-namespace starboard {

-namespace shared {

-namespace starboard {

-namespace player {

-

-class InputBuffer::ReferenceCountedBuffer {

- public:

-  ReferenceCountedBuffer(SbPlayerDeallocateSampleFunc deallocate_sample_func,

-                         SbPlayer player,

-                         void* context,

-                         const void* sample_buffer,

-                         int sample_buffer_size,

-                         SbMediaTime sample_pts,

-                         const SbMediaVideoSampleInfo* video_sample_info,

-                         const SbDrmSampleInfo* sample_drm_info)

-      : ref_count_(0),

-        deallocate_sample_func_(deallocate_sample_func),

-        player_(player),

-        context_(context),

-        data_(reinterpret_cast<const uint8_t*>(sample_buffer)),

-        size_(sample_buffer_size),

-        pts_(sample_pts),

-        has_video_sample_info_(video_sample_info != NULL),

-        has_drm_info_(sample_drm_info != NULL) {

-    SB_DCHECK(deallocate_sample_func);

-    if (has_video_sample_info_) {

-      video_sample_info_ = *video_sample_info;

-    }

-    if (has_drm_info_) {

-      SB_DCHECK(sample_drm_info->subsample_count > 0);

-

-      subsamples_.assign(sample_drm_info->subsample_mapping,

-                         sample_drm_info->subsample_mapping +

-                             sample_drm_info->subsample_count);

-      drm_info_ = *sample_drm_info;

-      drm_info_.subsample_mapping =

-          subsamples_.empty() ? NULL : &subsamples_[0];

-    }

-  }

-

-  void AddRef() const { SbAtomicBarrier_Increment(&ref_count_, 1); }

-  void Release() const {

-    if (SbAtomicBarrier_Increment(&ref_count_, -1) == 0) {

-      delete this;

-    }

-  }

-

-  const uint8_t* data() const { return data_; }

-  int size() const { return size_; }

-  SbMediaTime pts() const { return pts_; }

-  const SbMediaVideoSampleInfo* video_sample_info() const {

-    return has_video_sample_info_ ? &video_sample_info_ : NULL;

-  }

-  const SbDrmSampleInfo* drm_info() const {

-    return has_drm_info_ ? &drm_info_ : NULL;

-  }

-  void SetDecryptedContent(const void* buffer, int size) {

-    SB_DCHECK(size == size_);

-    SB_DCHECK(deallocate_sample_func_);

-    DeallocateSampleBuffer();

-

-    if (size > 0) {

-      decrypted_data_.resize(size);

-      SbMemoryCopy(&decrypted_data_[0], buffer, size);

-      data_ = &decrypted_data_[0];

-    } else {

-      data_ = NULL;

-    }

-    size_ = size;

-    has_drm_info_ = false;

-  }

-

- private:

-  ~ReferenceCountedBuffer() { DeallocateSampleBuffer(); }

-

-  void DeallocateSampleBuffer() {

-    if (deallocate_sample_func_) {

-      deallocate_sample_func_(player_, context_, const_cast<uint8_t*>(data_));

-      deallocate_sample_func_ = NULL;

-    }

-  }

-

-  mutable SbAtomic32 ref_count_;

-  SbPlayerDeallocateSampleFunc deallocate_sample_func_;

-  SbPlayer player_;

-  void* context_;

-  const uint8_t* data_;

-  int size_;

-  SbMediaTime pts_;

-  bool has_video_sample_info_;

-  SbMediaVideoSampleInfo video_sample_info_;

-  bool has_drm_info_;

-  SbDrmSampleInfo drm_info_;

-  std::vector<uint8_t> decrypted_data_;

-  std::vector<SbDrmSubSampleMapping> subsamples_;

-

-  SB_DISALLOW_COPY_AND_ASSIGN(ReferenceCountedBuffer);

-};

-

-InputBuffer::InputBuffer() : buffer_(NULL) {}

-

-InputBuffer::InputBuffer(SbPlayerDeallocateSampleFunc deallocate_sample_func,

-                         SbPlayer player,

-                         void* context,

-                         const void* sample_buffer,

-                         int sample_buffer_size,

-                         SbMediaTime sample_pts,

-                         const SbMediaVideoSampleInfo* video_sample_info,

-                         const SbDrmSampleInfo* sample_drm_info) {

-  buffer_ = new ReferenceCountedBuffer(

-      deallocate_sample_func, player, context, sample_buffer,

-      sample_buffer_size, sample_pts, video_sample_info, sample_drm_info);

-  buffer_->AddRef();

-}

-

-InputBuffer::InputBuffer(const InputBuffer& that) {

-  buffer_ = that.buffer_;

-  if (buffer_) {

-    buffer_->AddRef();

-  }

-}

-

-InputBuffer::~InputBuffer() {

-  if (buffer_) {

-    buffer_->Release();

-  }

-}

-

-InputBuffer& InputBuffer::operator=(const InputBuffer& that) {

-  if (that.buffer_) {

-    that.buffer_->AddRef();

-  }

-  if (buffer_) {

-    buffer_->Release();

-  }

-  buffer_ = that.buffer_;

-

-  return *this;

-}

-

-const uint8_t* InputBuffer::data() const {

-  SB_DCHECK(buffer_);

-  return buffer_->data();

-}

-

-int InputBuffer::size() const {

-  SB_DCHECK(buffer_);

-  return buffer_->size();

-}

-

-SbMediaTime InputBuffer::pts() const {

-  SB_DCHECK(buffer_);

-  return buffer_->pts();

-}

-

-const SbMediaVideoSampleInfo* InputBuffer::video_sample_info() const {

-  SB_DCHECK(buffer_);

-  return buffer_->video_sample_info();

-}

-

-const SbDrmSampleInfo* InputBuffer::drm_info() const {

-  SB_DCHECK(buffer_);

-  return buffer_->drm_info();

-}

-

-void InputBuffer::SetDecryptedContent(const void* buffer, int size) {

-  SB_DCHECK(buffer_);

-  buffer_->SetDecryptedContent(buffer, size);

-}

-

-}  // namespace player

-}  // namespace starboard

-}  // namespace shared

-}  // namespace starboard

+// Copyright 2016 Google Inc. 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/shared/starboard/player/input_buffer_internal.h"
+
+#include <vector>
+
+#include "starboard/atomic.h"
+#include "starboard/log.h"
+#include "starboard/memory.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+
+class InputBuffer::ReferenceCountedBuffer {
+ public:
+  ReferenceCountedBuffer(SbMediaType sample_type,
+                         SbPlayerDeallocateSampleFunc deallocate_sample_func,
+                         SbPlayer player,
+                         void* context,
+                         const void* sample_buffer,
+                         int sample_buffer_size,
+                         SbMediaTime sample_pts,
+                         const SbMediaVideoSampleInfo* video_sample_info,
+                         const SbDrmSampleInfo* sample_drm_info)
+      : ref_count_(0),
+        sample_type_(sample_type),
+        deallocate_sample_func_(deallocate_sample_func),
+        player_(player),
+        context_(context),
+        data_(static_cast<const uint8_t*>(sample_buffer)),
+        size_(sample_buffer_size),
+        pts_(sample_pts),
+        has_video_sample_info_(video_sample_info != NULL),
+        has_drm_info_(sample_drm_info != NULL) {
+    SB_DCHECK(deallocate_sample_func);
+    if (has_video_sample_info_) {
+      video_sample_info_ = *video_sample_info;
+    }
+    if (has_drm_info_) {
+      SB_DCHECK(sample_drm_info->subsample_count > 0);
+
+      subsamples_.assign(sample_drm_info->subsample_mapping,
+                         sample_drm_info->subsample_mapping +
+                             sample_drm_info->subsample_count);
+      drm_info_ = *sample_drm_info;
+      drm_info_.subsample_mapping =
+          subsamples_.empty() ? NULL : &subsamples_[0];
+    }
+  }
+
+  void AddRef() const { SbAtomicBarrier_Increment(&ref_count_, 1); }
+  void Release() const {
+    if (SbAtomicBarrier_Increment(&ref_count_, -1) == 0) {
+      delete this;
+    }
+  }
+
+  SbMediaType sample_type() const { return sample_type_; }
+  const uint8_t* data() const { return data_; }
+  int size() const { return size_; }
+  SbMediaTime pts() const { return pts_; }
+  const SbMediaVideoSampleInfo* video_sample_info() const {
+    return has_video_sample_info_ ? &video_sample_info_ : NULL;
+  }
+  const SbDrmSampleInfo* drm_info() const {
+    return has_drm_info_ ? &drm_info_ : NULL;
+  }
+  void SetDecryptedContent(const void* buffer, int size) {
+    SB_DCHECK(size == size_);
+    SB_DCHECK(deallocate_sample_func_);
+    DeallocateSampleBuffer();
+
+    if (size > 0) {
+      decrypted_data_.resize(size);
+      SbMemoryCopy(&decrypted_data_[0], buffer, size);
+      data_ = &decrypted_data_[0];
+    } else {
+      data_ = NULL;
+    }
+    size_ = size;
+    has_drm_info_ = false;
+  }
+
+ private:
+  ~ReferenceCountedBuffer() { DeallocateSampleBuffer(); }
+
+  void DeallocateSampleBuffer() {
+    if (deallocate_sample_func_) {
+      deallocate_sample_func_(player_, context_, const_cast<uint8_t*>(data_));
+      deallocate_sample_func_ = NULL;
+    }
+  }
+
+  mutable SbAtomic32 ref_count_;
+  SbMediaType sample_type_;
+  SbPlayerDeallocateSampleFunc deallocate_sample_func_;
+  SbPlayer player_;
+  void* context_;
+  const uint8_t* data_;
+  int size_;
+  SbMediaTime pts_;
+  bool has_video_sample_info_;
+  SbMediaVideoSampleInfo video_sample_info_;
+  bool has_drm_info_;
+  SbDrmSampleInfo drm_info_;
+  std::vector<uint8_t> decrypted_data_;
+  std::vector<SbDrmSubSampleMapping> subsamples_;
+
+  SB_DISALLOW_COPY_AND_ASSIGN(ReferenceCountedBuffer);
+};
+
+InputBuffer::InputBuffer() : buffer_(NULL) {}
+
+InputBuffer::InputBuffer(SbMediaType sample_type,
+                         SbPlayerDeallocateSampleFunc deallocate_sample_func,
+                         SbPlayer player,
+                         void* context,
+                         const void* sample_buffer,
+                         int sample_buffer_size,
+                         SbMediaTime sample_pts,
+                         const SbMediaVideoSampleInfo* video_sample_info,
+                         const SbDrmSampleInfo* sample_drm_info) {
+  buffer_ = new ReferenceCountedBuffer(
+      sample_type, deallocate_sample_func, player, context, sample_buffer,
+      sample_buffer_size, sample_pts, video_sample_info, sample_drm_info);
+  buffer_->AddRef();
+}
+
+InputBuffer::InputBuffer(const InputBuffer& that) {
+  buffer_ = that.buffer_;
+  if (buffer_) {
+    buffer_->AddRef();
+  }
+}
+
+InputBuffer::~InputBuffer() {
+  reset();
+}
+
+InputBuffer& InputBuffer::operator=(const InputBuffer& that) {
+  if (that.buffer_) {
+    that.buffer_->AddRef();
+  }
+  if (buffer_) {
+    buffer_->Release();
+  }
+  buffer_ = that.buffer_;
+
+  return *this;
+}
+
+void InputBuffer::reset() {
+  if (buffer_) {
+    buffer_->Release();
+    buffer_ = NULL;
+  }
+}
+
+SbMediaType InputBuffer::sample_type() const {
+  SB_DCHECK(buffer_);
+  return buffer_->sample_type();
+}
+
+const uint8_t* InputBuffer::data() const {
+  SB_DCHECK(buffer_);
+  return buffer_->data();
+}
+
+int InputBuffer::size() const {
+  SB_DCHECK(buffer_);
+  return buffer_->size();
+}
+
+SbMediaTime InputBuffer::pts() const {
+  SB_DCHECK(buffer_);
+  return buffer_->pts();
+}
+
+const SbMediaVideoSampleInfo* InputBuffer::video_sample_info() const {
+  SB_DCHECK(buffer_);
+  return buffer_->video_sample_info();
+}
+
+const SbDrmSampleInfo* InputBuffer::drm_info() const {
+  SB_DCHECK(buffer_);
+  return buffer_->drm_info();
+}
+
+void InputBuffer::SetDecryptedContent(const void* buffer, int size) {
+  SB_DCHECK(buffer_);
+  buffer_->SetDecryptedContent(buffer, size);
+}
+
+}  // namespace player
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
diff --git a/src/starboard/shared/starboard/player/input_buffer_internal.h b/src/starboard/shared/starboard/player/input_buffer_internal.h
index 4a056b9..8f4e0ce 100644
--- a/src/starboard/shared/starboard/player/input_buffer_internal.h
+++ b/src/starboard/shared/starboard/player/input_buffer_internal.h
@@ -1,69 +1,72 @@
-// Copyright 2016 Google Inc. 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_SHARED_STARBOARD_PLAYER_INPUT_BUFFER_INTERNAL_H_

-#define STARBOARD_SHARED_STARBOARD_PLAYER_INPUT_BUFFER_INTERNAL_H_

-

-#include "starboard/drm.h"

-#include "starboard/media.h"

-#include "starboard/player.h"

-#include "starboard/shared/internal_only.h"

-

-namespace starboard {

-namespace shared {

-namespace starboard {

-namespace player {

-

-// This class encapsulate a media buffer.  It holds a reference-counted object

-// internally to ensure that the object of this class can be copied while the

-// underlying resources allocated is properly managed.

-// Note that pass this class as const reference doesn't guarantee that the

-// content of the internal buffer won't be changed.

-class InputBuffer {

- public:

-  InputBuffer();

-  InputBuffer(SbPlayerDeallocateSampleFunc deallocate_sample_func,

-              SbPlayer player,

-              void* context,

-              const void* sample_buffer,

-              int sample_buffer_size,

-              SbMediaTime sample_pts,

-              const SbMediaVideoSampleInfo* video_sample_info,

-              const SbDrmSampleInfo* sample_drm_info);

-  InputBuffer(const InputBuffer& that);

-  ~InputBuffer();

-

-  InputBuffer& operator=(const InputBuffer& that);

-

-  bool valid() const { return buffer_ != NULL; }

-

-  const uint8_t* data() const;

-  int size() const;

-  SbMediaTime pts() const;

-  const SbMediaVideoSampleInfo* video_sample_info() const;

-  const SbDrmSampleInfo* drm_info() const;

-  void SetDecryptedContent(const void* buffer, int size);

-

- private:

-  class ReferenceCountedBuffer;

-

-  ReferenceCountedBuffer* buffer_;

-};

-

-}  // namespace player

-}  // namespace starboard

-}  // namespace shared

-}  // namespace starboard

-

-#endif  // STARBOARD_SHARED_STARBOARD_PLAYER_INPUT_BUFFER_INTERNAL_H_

+// Copyright 2016 Google Inc. 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_SHARED_STARBOARD_PLAYER_INPUT_BUFFER_INTERNAL_H_
+#define STARBOARD_SHARED_STARBOARD_PLAYER_INPUT_BUFFER_INTERNAL_H_
+
+#include "starboard/drm.h"
+#include "starboard/media.h"
+#include "starboard/player.h"
+#include "starboard/shared/internal_only.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+
+// This class encapsulate a media buffer.  It holds a reference-counted object
+// internally to ensure that the object of this class can be copied while the
+// underlying resources allocated is properly managed.
+// Note that pass this class as const reference doesn't guarantee that the
+// content of the internal buffer won't be changed.
+class InputBuffer {
+ public:
+  InputBuffer();
+  InputBuffer(SbMediaType sample_type,
+              SbPlayerDeallocateSampleFunc deallocate_sample_func,
+              SbPlayer player,
+              void* context,
+              const void* sample_buffer,
+              int sample_buffer_size,
+              SbMediaTime sample_pts,
+              const SbMediaVideoSampleInfo* video_sample_info,
+              const SbDrmSampleInfo* sample_drm_info);
+  InputBuffer(const InputBuffer& that);
+  ~InputBuffer();
+
+  InputBuffer& operator=(const InputBuffer& that);
+
+  bool is_valid() const { return buffer_ != NULL; }
+  void reset();
+
+  SbMediaType sample_type() const;
+  const uint8_t* data() const;
+  int size() const;
+  SbMediaTime pts() const;
+  const SbMediaVideoSampleInfo* video_sample_info() const;
+  const SbDrmSampleInfo* drm_info() const;
+  void SetDecryptedContent(const void* buffer, int size);
+
+ private:
+  class ReferenceCountedBuffer;
+
+  ReferenceCountedBuffer* buffer_;
+};
+
+}  // namespace player
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_SHARED_STARBOARD_PLAYER_INPUT_BUFFER_INTERNAL_H_
diff --git a/src/starboard/shared/starboard/player/job_queue.cc b/src/starboard/shared/starboard/player/job_queue.cc
new file mode 100644
index 0000000..254378d
--- /dev/null
+++ b/src/starboard/shared/starboard/player/job_queue.cc
@@ -0,0 +1,208 @@
+// Copyright 2017 Google Inc. 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/shared/starboard/player/job_queue.h"
+
+#include <utility>
+
+#include "starboard/log.h"
+#include "starboard/once.h"
+#include "starboard/thread.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+
+namespace {
+
+SbOnceControl s_once_flag = SB_ONCE_INITIALIZER;
+SbThreadLocalKey s_thread_local_key = kSbThreadLocalKeyInvalid;
+
+void InitThreadLocalKey() {
+  s_thread_local_key = SbThreadCreateLocalKey(NULL);
+  SB_DCHECK(SbThreadIsValidLocalKey(s_thread_local_key));
+}
+
+void EnsureThreadLocalKeyInited() {
+  SbOnce(&s_once_flag, InitThreadLocalKey);
+  SB_DCHECK(SbThreadIsValidLocalKey(s_thread_local_key));
+}
+
+JobQueue* GetCurrentThreadJobQueue() {
+  EnsureThreadLocalKeyInited();
+  return static_cast<JobQueue*>(SbThreadGetLocalValue(s_thread_local_key));
+}
+
+void SetCurrentThreadJobQueue(JobQueue* job_queue) {
+  SB_DCHECK(job_queue != NULL);
+  SB_DCHECK(GetCurrentThreadJobQueue() == NULL);
+
+  EnsureThreadLocalKeyInited();
+  SbThreadSetLocalValue(s_thread_local_key, job_queue);
+}
+
+void ResetCurrentThreadJobQueue() {
+  SB_DCHECK(GetCurrentThreadJobQueue());
+
+  EnsureThreadLocalKeyInited();
+  SbThreadSetLocalValue(s_thread_local_key, NULL);
+}
+
+}  // namespace
+
+JobQueue::JobQueue() : thread_id_(SbThreadGetId()), stopped_(false) {
+  SB_DCHECK(SbThreadIsValidId(thread_id_));
+  SetCurrentThreadJobQueue(this);
+}
+
+JobQueue::~JobQueue() {
+  SB_DCHECK(BelongsToCurrentThread());
+  StopSoon();
+  ResetCurrentThreadJobQueue();
+}
+
+void JobQueue::Schedule(Closure job, SbTimeMonotonic delay /*= 0*/) {
+  SB_DCHECK(job.is_valid());
+  SB_DCHECK(delay >= 0) << delay;
+
+  ScopedLock scoped_lock(mutex_);
+  if (stopped_) {
+    return;
+  }
+  if (delay <= 0) {
+    queue_.Put(job);
+    return;
+  }
+  SbTimeMonotonic time_to_run_job = SbTimeGetMonotonicNow() + delay;
+  time_to_job_map_.insert(std::make_pair(time_to_run_job, job));
+  if (time_to_job_map_.begin()->second == job) {
+    queue_.Wake();
+  }
+}
+
+void JobQueue::Remove(Closure job) {
+  SB_DCHECK(BelongsToCurrentThread());
+  SB_DCHECK(job.is_valid());
+
+  ScopedLock scoped_lock(mutex_);
+  queue_.Remove(job);
+  // std::multimap::erase() doesn't return an iterator until C++11.  So this has
+  // to be done in a nested loop to delete multiple occurrences of |job|.
+  bool should_keep_running = true;
+  while (should_keep_running) {
+    should_keep_running = false;
+    for (TimeToJobMap::iterator iter = time_to_job_map_.begin();
+         iter != time_to_job_map_.end(); ++iter) {
+      if (iter->second == job) {
+        time_to_job_map_.erase(iter);
+        should_keep_running = true;
+        break;
+      }
+    }
+  }
+}
+
+void JobQueue::StopSoon() {
+  {
+    ScopedLock scoped_lock(mutex_);
+    stopped_ = true;
+  }
+
+  queue_.Wake();
+
+  for (;;) {
+    Closure job = queue_.Poll();
+    if (!job.is_valid()) {
+      break;
+    }
+  }
+  time_to_job_map_.clear();
+}
+
+void JobQueue::RunUntilStopped() {
+  SB_DCHECK(BelongsToCurrentThread());
+
+  for (;;) {
+    {
+      ScopedLock scoped_lock(mutex_);
+      if (stopped_) {
+        return;
+      }
+    }
+    TryToRunOneJob(/*wait_for_next_job =*/true);
+  }
+}
+
+void JobQueue::RunUntilIdle() {
+  SB_DCHECK(BelongsToCurrentThread());
+
+  while (TryToRunOneJob(/*wait_for_next_job =*/false)) {
+  }
+}
+
+bool JobQueue::BelongsToCurrentThread() const {
+  // The ctor already ensures that the current JobQueue is the only JobQueue of
+  // the thread, checking for thread id is more light-weighted then calling
+  // JobQueue::current() and compare the result with |this|.
+  return thread_id_ == SbThreadGetId();
+}
+
+// static
+JobQueue* JobQueue::current() {
+  return GetCurrentThreadJobQueue();
+}
+
+bool JobQueue::TryToRunOneJob(bool wait_for_next_job) {
+  SB_DCHECK(BelongsToCurrentThread());
+
+  Closure job;
+  // |kSbTimeMax| makes more sense here, but |kSbTimeDay| is much safer.
+  SbTimeMonotonic delay = kSbTimeDay;
+
+  {
+    ScopedLock scoped_lock(mutex_);
+    if (stopped_) {
+      return false;
+    }
+    if (!time_to_job_map_.empty()) {
+      TimeToJobMap::iterator first_delayed_job = time_to_job_map_.begin();
+      delay = first_delayed_job->first - SbTimeGetMonotonicNow();
+      if (delay <= 0) {
+        job = first_delayed_job->second;
+        time_to_job_map_.erase(first_delayed_job);
+      }
+    }
+  }
+
+  if (!job.is_valid()) {
+    if (wait_for_next_job) {
+      job = queue_.GetTimed(delay);
+    } else {
+      job = queue_.Poll();
+    }
+  }
+
+  if (!job.is_valid()) {
+    return false;
+  }
+
+  job.Run();
+  return true;
+}
+
+}  // namespace player
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
diff --git a/src/starboard/shared/starboard/player/job_queue.h b/src/starboard/shared/starboard/player/job_queue.h
new file mode 100644
index 0000000..36100ab
--- /dev/null
+++ b/src/starboard/shared/starboard/player/job_queue.h
@@ -0,0 +1,81 @@
+// Copyright 2017 Google Inc. 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_SHARED_STARBOARD_PLAYER_JOB_QUEUE_H_
+#define STARBOARD_SHARED_STARBOARD_PLAYER_JOB_QUEUE_H_
+
+#include <map>
+
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/condition_variable.h"
+#include "starboard/mutex.h"
+#include "starboard/queue.h"
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/player/closure.h"
+#include "starboard/time.h"
+
+#ifndef __cplusplus
+#error "Only C++ files can include this header."
+#endif
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+
+// This class implements a job queue where closures can be posted to it on any
+// thread and will be processed on one thread that this job queue is linked to.
+// A thread can only have one job queue.
+class JobQueue {
+ public:
+  JobQueue();
+  ~JobQueue();
+
+  void Schedule(Closure job, SbTimeMonotonic delay = 0);
+  void Remove(Closure job);
+
+  // The processing of jobs may not be stopped when this function returns, but
+  // it is guaranteed that the processing will be stopped very soon.  So it is
+  // safe to join the thread after this call returns.
+  void StopSoon();
+
+  void RunUntilStopped();
+  void RunUntilIdle();
+
+  bool BelongsToCurrentThread() const;
+  static JobQueue* current();
+
+ private:
+  typedef std::multimap<SbTimeMonotonic, Closure> TimeToJobMap;
+
+  // Return true if a job is run, otherwise return false.  When there is no job
+  // ready to run currently and |wait_for_next_job| is true, the function will
+  // wait to until a job is available or if the |queue_| is woken up.  Note that
+  // set |wait_for_next_job| to true doesn't guarantee that one job will always
+  // be run.
+  bool TryToRunOneJob(bool wait_for_next_job);
+
+  SbThreadId thread_id_;
+  Mutex mutex_;
+  Queue<Closure> queue_;
+  TimeToJobMap time_to_job_map_;
+  bool stopped_;
+};
+
+}  // namespace player
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_SHARED_STARBOARD_PLAYER_JOB_QUEUE_H_
diff --git a/src/starboard/shared/starboard/player/player_internal.cc b/src/starboard/shared/starboard/player/player_internal.cc
index 35726f1..9c8a25b 100644
--- a/src/starboard/shared/starboard/player/player_internal.cc
+++ b/src/starboard/shared/starboard/player/player_internal.cc
@@ -20,8 +20,6 @@
 
 namespace {
 
-const SbTime kUpdateInterval = kSbTimeMillisecond;
-
 SbMediaTime GetMediaTime(SbMediaTime media_pts,
                          SbTimeMonotonic media_pts_update_time) {
   SbTimeMonotonic elapsed = SbTimeGetMonotonicNow() - media_pts_update_time;
@@ -54,7 +52,6 @@
                                decoder_status_func,
                                player_status_func,
                                this,
-                               kUpdateInterval,
                                context)) {}
 
 void SbPlayerPrivate::Seek(SbMediaTime seek_to_pts, int ticket) {
@@ -66,8 +63,7 @@
     ticket_ = ticket;
   }
 
-  PlayerWorker::SeekEventData data = {seek_to_pts, ticket};
-  worker_->EnqueueEvent(data);
+  worker_->Seek(seek_to_pts, ticket);
 }
 
 void SbPlayerPrivate::WriteSample(
@@ -80,22 +76,20 @@
   if (sample_type == kSbMediaTypeVideo) {
     ++total_video_frames_;
   }
-  InputBuffer* input_buffer = new InputBuffer(
-      sample_deallocate_func_, this, context_, sample_buffer,
-      sample_buffer_size, sample_pts, video_sample_info, sample_drm_info);
-  PlayerWorker::WriteSampleEventData data = {sample_type, input_buffer};
-  worker_->EnqueueEvent(data);
+  InputBuffer input_buffer(sample_type, sample_deallocate_func_, this, context_,
+                           sample_buffer, sample_buffer_size, sample_pts,
+                           video_sample_info, sample_drm_info);
+  worker_->WriteSample(input_buffer);
 }
 
 void SbPlayerPrivate::WriteEndOfStream(SbMediaType stream_type) {
-  PlayerWorker::WriteEndOfStreamEventData data = {stream_type};
-  worker_->EnqueueEvent(data);
+  worker_->WriteEndOfStream(stream_type);
 }
 
 #if SB_IS(PLAYER_PUNCHED_OUT)
 void SbPlayerPrivate::SetBounds(int x, int y, int width, int height) {
-  PlayerWorker::SetBoundsEventData data = {x, y, width, height};
-  worker_->EnqueueEvent(data);
+  PlayerWorker::Bounds bounds = {x, y, width, height};
+  worker_->SetBounds(bounds);
   // TODO: Wait until a frame is rendered with the updated bounds.
 }
 #endif
@@ -121,8 +115,7 @@
 }
 
 void SbPlayerPrivate::SetPause(bool pause) {
-  PlayerWorker::SetPauseEventData data = {pause};
-  worker_->EnqueueEvent(data);
+  worker_->SetPause(pause);
 }
 
 void SbPlayerPrivate::SetVolume(double volume) {
diff --git a/src/starboard/shared/starboard/player/player_worker.cc b/src/starboard/shared/starboard/player/player_worker.cc
index 5b36cb6..61155d6 100644
--- a/src/starboard/shared/starboard/player/player_worker.cc
+++ b/src/starboard/shared/starboard/player/player_worker.cc
@@ -15,19 +15,32 @@
 #include "starboard/shared/starboard/player/player_worker.h"
 
 #include "starboard/common/reset_and_return.h"
+#include "starboard/condition_variable.h"
 #include "starboard/memory.h"
+#include "starboard/mutex.h"
 
 namespace starboard {
 namespace shared {
 namespace starboard {
 namespace player {
 
+namespace {
+
+struct ThreadParam {
+  explicit ThreadParam(PlayerWorker* player_worker)
+      : condition_variable(mutex), player_worker(player_worker) {}
+  Mutex mutex;
+  ConditionVariable condition_variable;
+  PlayerWorker* player_worker;
+};
+
+}  // namespace
+
 PlayerWorker::PlayerWorker(Host* host,
                            scoped_ptr<Handler> handler,
                            SbPlayerDecoderStatusFunc decoder_status_func,
                            SbPlayerStatusFunc player_status_func,
                            SbPlayer player,
-                           SbTime update_interval,
                            void* context)
     : thread_(kSbThreadInvalid),
       host_(host),
@@ -37,51 +50,30 @@
       player_(player),
       context_(context),
       ticket_(SB_PLAYER_INITIAL_TICKET),
-      player_state_(kSbPlayerStateInitialized),
-      update_interval_(update_interval) {
+      player_state_(kSbPlayerStateInitialized) {
   SB_DCHECK(host_ != NULL);
   SB_DCHECK(handler_ != NULL);
-  SB_DCHECK(update_interval_ > 0);
 
-  handler_->Setup(this, player_, &PlayerWorker::UpdateMediaTime,
-                  &PlayerWorker::player_state,
-                  &PlayerWorker::UpdatePlayerState);
-
-  pending_audio_sample_.input_buffer = NULL;
-  pending_video_sample_.input_buffer = NULL;
-
-  thread_ =
-      SbThreadCreate(0, kSbThreadPriorityHigh, kSbThreadNoAffinity, true,
-                     "player_worker", &PlayerWorker::ThreadEntryPoint, this);
+  ThreadParam thread_param(this);
+  thread_ = SbThreadCreate(0, kSbThreadPriorityHigh, kSbThreadNoAffinity, true,
+                           "player_worker", &PlayerWorker::ThreadEntryPoint,
+                           &thread_param);
   SB_DCHECK(SbThreadIsValid(thread_));
-
-  queue_.Put(new Event(Event::kInit));
+  ScopedLock scoped_lock(thread_param.mutex);
+  while (!job_queue_) {
+    thread_param.condition_variable.Wait();
+  }
+  SB_DCHECK(job_queue_);
 }
 
 PlayerWorker::~PlayerWorker() {
-  queue_.Put(new Event(Event::kStop));
+  job_queue_->Schedule(Bind(&PlayerWorker::DoStop, this));
   SbThreadJoin(thread_, NULL);
   thread_ = kSbThreadInvalid;
 
   // Now the whole pipeline has been torn down and no callback will be called.
   // The caller can ensure that upon the return of SbPlayerDestroy() all side
   // effects are gone.
-
-  // There can be events inside the queue that is not processed.  Clean them up.
-  while (Event* event = queue_.GetTimed(0)) {
-    if (event->type == Event::kWriteSample) {
-      delete event->data.write_sample.input_buffer;
-    }
-    delete event;
-  }
-
-  if (pending_audio_sample_.input_buffer != NULL) {
-    delete pending_audio_sample_.input_buffer;
-  }
-
-  if (pending_video_sample_.input_buffer != NULL) {
-    delete pending_video_sample_.input_buffer;
-  }
 }
 
 void PlayerWorker::UpdateMediaTime(SbMediaTime time) {
@@ -89,6 +81,8 @@
 }
 
 void PlayerWorker::UpdatePlayerState(SbPlayerState player_state) {
+  SB_DLOG_IF(WARNING, player_state == kSbPlayerStateError)
+      << "encountered kSbPlayerStateError";
   player_state_ = player_state;
 
   if (!player_status_func_) {
@@ -100,130 +94,121 @@
 
 // static
 void* PlayerWorker::ThreadEntryPoint(void* context) {
-  PlayerWorker* player_worker = reinterpret_cast<PlayerWorker*>(context);
-  player_worker->RunLoop();
+  ThreadParam* param = static_cast<ThreadParam*>(context);
+  SB_DCHECK(param != NULL);
+  {
+    ScopedLock scoped_lock(param->mutex);
+    param->player_worker->job_queue_.reset(new JobQueue);
+    param->condition_variable.Signal();
+  }
+  param->player_worker->RunLoop();
   return NULL;
 }
 
 void PlayerWorker::RunLoop() {
-  Event* event = queue_.Get();
-  SB_DCHECK(event != NULL);
-  SB_DCHECK(event->type == Event::kInit);
-  delete event;
-  bool running = DoInit();
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
 
-  SetBoundsEventData bounds = {0, 0, 0, 0};
-
-  while (running) {
-    Event* event = queue_.GetTimed(update_interval_);
-    if (event == NULL) {
-      running &= DoUpdate(bounds);
-      continue;
-    }
-
-    SB_DCHECK(event->type != Event::kInit);
-
-    if (event->type == Event::kSeek) {
-      running &= DoSeek(event->data.seek);
-    } else if (event->type == Event::kWriteSample) {
-      running &= DoWriteSample(event->data.write_sample);
-    } else if (event->type == Event::kWriteEndOfStream) {
-      running &= DoWriteEndOfStream(event->data.write_end_of_stream);
-    } else if (event->type == Event::kSetPause) {
-      running &= handler_->ProcessSetPauseEvent(event->data.set_pause);
-    } else if (event->type == Event::kSetBounds) {
-      if (SbMemoryCompare(&bounds, &event->data.set_bounds, sizeof(bounds)) !=
-          0) {
-        bounds = event->data.set_bounds;
-        running &= DoUpdate(bounds);
-      }
-    } else if (event->type == Event::kStop) {
-      DoStop();
-      running = false;
-    } else {
-      SB_NOTREACHED() << "event type " << event->type;
-    }
-    delete event;
-    running &= DoUpdate(bounds);
-  }
+  DoInit();
+  job_queue_->RunUntilStopped();
+  job_queue_.reset();
 }
 
-bool PlayerWorker::DoInit() {
-  if (handler_->ProcessInitEvent()) {
+void PlayerWorker::DoInit() {
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
+  if (handler_->Init(
+          this, job_queue_.get(), player_, &PlayerWorker::UpdateMediaTime,
+          &PlayerWorker::player_state, &PlayerWorker::UpdatePlayerState)) {
     UpdatePlayerState(kSbPlayerStateInitialized);
-    return true;
+  } else {
+    UpdatePlayerState(kSbPlayerStateError);
   }
-
-  UpdatePlayerState(kSbPlayerStateError);
-  return false;
 }
 
-bool PlayerWorker::DoSeek(const SeekEventData& data) {
+void PlayerWorker::DoSeek(SbMediaTime seek_to_pts, int ticket) {
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
   SB_DCHECK(player_state_ != kSbPlayerStateDestroyed);
   SB_DCHECK(player_state_ != kSbPlayerStateError);
-  SB_DCHECK(ticket_ != data.ticket);
+  SB_DCHECK(ticket_ != ticket);
 
   SB_DLOG(INFO) << "Try to seek to timestamp "
-                << data.seek_to_pts / kSbMediaTimeSecond;
+                << seek_to_pts / kSbMediaTimeSecond;
 
-  if (pending_audio_sample_.input_buffer != NULL) {
-    delete pending_audio_sample_.input_buffer;
-    pending_audio_sample_.input_buffer = NULL;
+  if (write_pending_sample_closure_.is_valid()) {
+    job_queue_->Remove(write_pending_sample_closure_);
+    write_pending_sample_closure_.reset();
   }
-  if (pending_video_sample_.input_buffer != NULL) {
-    delete pending_video_sample_.input_buffer;
-    pending_video_sample_.input_buffer = NULL;
+  pending_audio_buffer_.reset();
+  pending_video_buffer_.reset();
+
+  if (!handler_->Seek(seek_to_pts, ticket)) {
+    UpdatePlayerState(kSbPlayerStateError);
+    return;
   }
 
-  if (!handler_->ProcessSeekEvent(data)) {
-    return false;
-  }
-
-  ticket_ = data.ticket;
+  ticket_ = ticket;
 
   UpdatePlayerState(kSbPlayerStatePrerolling);
   UpdateDecoderState(kSbMediaTypeAudio, kSbPlayerDecoderStateNeedsData);
   UpdateDecoderState(kSbMediaTypeVideo, kSbPlayerDecoderStateNeedsData);
-
-  return true;
 }
 
-bool PlayerWorker::DoWriteSample(const WriteSampleEventData& data) {
+void PlayerWorker::DoWriteSample(InputBuffer input_buffer) {
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
   if (player_state_ == kSbPlayerStateInitialized ||
       player_state_ == kSbPlayerStateEndOfStream ||
       player_state_ == kSbPlayerStateDestroyed ||
       player_state_ == kSbPlayerStateError) {
     SB_LOG(ERROR) << "Try to write sample when |player_state_| is "
                   << player_state_;
-    delete data.input_buffer;
-    // Return true so the pipeline will continue running with the particular
-    // call ignored.
-    return true;
+    return;
   }
 
-  if (data.sample_type == kSbMediaTypeAudio) {
-    SB_DCHECK(pending_audio_sample_.input_buffer == NULL);
+  if (input_buffer.sample_type() == kSbMediaTypeAudio) {
+    SB_DCHECK(!pending_audio_buffer_.is_valid());
   } else {
-    SB_DCHECK(pending_video_sample_.input_buffer == NULL);
+    SB_DCHECK(!pending_video_buffer_.is_valid());
   }
   bool written;
-  bool result = handler_->ProcessWriteSampleEvent(data, &written);
-  if (!written && result) {
-    if (data.sample_type == kSbMediaTypeAudio) {
-      pending_audio_sample_ = data;
-    } else {
-      pending_video_sample_ = data;
-    }
+  bool result = handler_->WriteSample(input_buffer, &written);
+  if (!result) {
+    UpdatePlayerState(kSbPlayerStateError);
+    return;
+  }
+  if (written) {
+    UpdateDecoderState(input_buffer.sample_type(),
+                       kSbPlayerDecoderStateNeedsData);
   } else {
-    delete data.input_buffer;
-    if (result) {
-      UpdateDecoderState(data.sample_type, kSbPlayerDecoderStateNeedsData);
+    if (input_buffer.sample_type() == kSbMediaTypeAudio) {
+      pending_audio_buffer_ = input_buffer;
+    } else {
+      pending_video_buffer_ = input_buffer;
+    }
+    if (!write_pending_sample_closure_.is_valid()) {
+      write_pending_sample_closure_ =
+          Bind(&PlayerWorker::DoWritePendingSamples, this);
+      job_queue_->Schedule(write_pending_sample_closure_);
     }
   }
-  return result;
 }
 
-bool PlayerWorker::DoWriteEndOfStream(const WriteEndOfStreamEventData& data) {
+void PlayerWorker::DoWritePendingSamples() {
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
+  SB_DCHECK(write_pending_sample_closure_.is_valid());
+  write_pending_sample_closure_.reset();
+
+  if (pending_audio_buffer_.is_valid()) {
+    DoWriteSample(common::ResetAndReturn(&pending_audio_buffer_));
+  }
+  if (pending_video_buffer_.is_valid()) {
+    DoWriteSample(common::ResetAndReturn(&pending_video_buffer_));
+  }
+}
+
+void PlayerWorker::DoWriteEndOfStream(SbMediaType sample_type) {
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
   SB_DCHECK(player_state_ != kSbPlayerStateDestroyed);
 
   if (player_state_ == kSbPlayerStateInitialized ||
@@ -233,40 +218,43 @@
                   << player_state_;
     // Return true so the pipeline will continue running with the particular
     // call ignored.
-    return true;
+    return;
   }
 
-  if (data.stream_type == kSbMediaTypeAudio) {
-    SB_DCHECK(pending_audio_sample_.input_buffer == NULL);
+  if (sample_type == kSbMediaTypeAudio) {
+    SB_DCHECK(!pending_audio_buffer_.is_valid());
   } else {
-    SB_DCHECK(pending_video_sample_.input_buffer == NULL);
+    SB_DCHECK(!pending_video_buffer_.is_valid());
   }
 
-  if (!handler_->ProcessWriteEndOfStreamEvent(data)) {
-    return false;
+  if (!handler_->WriteEndOfStream(sample_type)) {
+    UpdatePlayerState(kSbPlayerStateError);
   }
-
-  return true;
 }
 
-bool PlayerWorker::DoUpdate(const SetBoundsEventData& data) {
-  if (pending_audio_sample_.input_buffer != NULL) {
-    if (!DoWriteSample(common::ResetAndReturn(&pending_audio_sample_))) {
-      return false;
-    }
+#if SB_IS(PLAYER_PUNCHED_OUT)
+void PlayerWorker::DoSetBounds(Bounds bounds) {
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
+  if (!handler_->SetBounds(bounds)) {
+    UpdatePlayerState(kSbPlayerStateError);
   }
-  if (pending_video_sample_.input_buffer != NULL) {
-    if (!DoWriteSample(common::ResetAndReturn(&pending_video_sample_))) {
-      return false;
-    }
+}
+#endif  // SB_IS(PLAYER_PUNCHED_OUT)
+
+void PlayerWorker::DoSetPause(bool pause) {
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
+  if (!handler_->SetPause(pause)) {
+    UpdatePlayerState(kSbPlayerStateError);
   }
-  return handler_->ProcessUpdateEvent(data);
 }
 
 void PlayerWorker::DoStop() {
-  handler_->ProcessStopEvent();
+  SB_DCHECK(job_queue_->BelongsToCurrentThread());
 
+  handler_->Stop();
   UpdatePlayerState(kSbPlayerStateDestroyed);
+  job_queue_->StopSoon();
 }
 
 void PlayerWorker::UpdateDecoderState(SbMediaType type,
diff --git a/src/starboard/shared/starboard/player/player_worker.h b/src/starboard/shared/starboard/player/player_worker.h
index 95ca389..ccddf39 100644
--- a/src/starboard/shared/starboard/player/player_worker.h
+++ b/src/starboard/shared/starboard/player/player_worker.h
@@ -19,9 +19,10 @@
 #include "starboard/log.h"
 #include "starboard/media.h"
 #include "starboard/player.h"
-#include "starboard/queue.h"
 #include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/player/closure.h"
 #include "starboard/shared/starboard/player/input_buffer_internal.h"
+#include "starboard/shared/starboard/player/job_queue.h"
 #include "starboard/thread.h"
 #include "starboard/time.h"
 #include "starboard/window.h"
@@ -48,84 +49,14 @@
     ~Host() {}
   };
 
-  struct SeekEventData {
-    SbMediaTime seek_to_pts;
-    int ticket;
-  };
-
-  struct WriteSampleEventData {
-    SbMediaType sample_type;
-    InputBuffer* input_buffer;
-  };
-
-  struct WriteEndOfStreamEventData {
-    SbMediaType stream_type;
-  };
-
-  struct SetPauseEventData {
-    bool pause;
-  };
-
-  struct SetBoundsEventData {
+  struct Bounds {
     int x;
     int y;
     int width;
     int height;
   };
 
-  struct Event {
-   public:
-    enum Type {
-      kInit,
-      kSeek,
-      kWriteSample,
-      kWriteEndOfStream,
-      kSetPause,
-      kSetBounds,
-      kStop,
-    };
-
-    // TODO: No longer use a union so individual members can have non trivial
-    // ctor and WriteSampleEventData::input_buffer no longer has to be a
-    // pointer.
-    union Data {
-      SeekEventData seek;
-      WriteSampleEventData write_sample;
-      WriteEndOfStreamEventData write_end_of_stream;
-      SetPauseEventData set_pause;
-      SetBoundsEventData set_bounds;
-    };
-
-    explicit Event(const SeekEventData& seek) : type(kSeek) {
-      data.seek = seek;
-    }
-
-    explicit Event(const WriteSampleEventData& write_sample)
-        : type(kWriteSample) {
-      data.write_sample = write_sample;
-    }
-
-    explicit Event(const WriteEndOfStreamEventData& write_end_of_stream)
-        : type(kWriteEndOfStream) {
-      data.write_end_of_stream = write_end_of_stream;
-    }
-
-    explicit Event(const SetPauseEventData& set_pause) : type(kSetPause) {
-      data.set_pause = set_pause;
-    }
-
-    explicit Event(const SetBoundsEventData& set_bounds) : type(kSetBounds) {
-      data.set_bounds = set_bounds;
-    }
-
-    explicit Event(Type type) : type(type) {
-      SB_DCHECK(type == kInit || type == kStop);
-    }
-
-    Type type;
-    Data data;
-  };
-
+  // All functions of this class will be called from the JobQueue thread.
   class Handler {
    public:
     typedef void (PlayerWorker::*UpdateMediaTimeCB)(SbMediaTime media_time);
@@ -133,33 +64,27 @@
     typedef void (PlayerWorker::*UpdatePlayerStateCB)(
         SbPlayerState player_state);
 
-    typedef PlayerWorker::SeekEventData SeekEventData;
-    typedef PlayerWorker::WriteSampleEventData WriteSampleEventData;
-    typedef PlayerWorker::WriteEndOfStreamEventData WriteEndOfStreamEventData;
-    typedef PlayerWorker::SetPauseEventData SetPauseEventData;
-    typedef PlayerWorker::SetBoundsEventData SetBoundsEventData;
-
     virtual ~Handler() {}
 
-    // This function will be called once inside PlayerWorker's ctor to setup the
-    // callbacks required by the Handler.
-    virtual void Setup(PlayerWorker* player_worker,
-                       SbPlayer player,
-                       UpdateMediaTimeCB update_media_time_cb,
-                       GetPlayerStateCB get_player_state_cb,
-                       UpdatePlayerStateCB update_player_state_cb) = 0;
-
     // All the Process* functions return false to signal a fatal error.  The
     // event processing loop in PlayerWorker will termimate in this case.
-    virtual bool ProcessInitEvent() = 0;
-    virtual bool ProcessSeekEvent(const SeekEventData& data) = 0;
-    virtual bool ProcessWriteSampleEvent(const WriteSampleEventData& data,
-                                         bool* written) = 0;
-    virtual bool ProcessWriteEndOfStreamEvent(
-        const WriteEndOfStreamEventData& data) = 0;
-    virtual bool ProcessSetPauseEvent(const SetPauseEventData& data) = 0;
-    virtual bool ProcessUpdateEvent(const SetBoundsEventData& data) = 0;
-    virtual void ProcessStopEvent() = 0;
+    // All function returns false on error.
+    virtual bool Init(PlayerWorker* player_worker,
+                      JobQueue* job_queue,
+                      SbPlayer player,
+                      UpdateMediaTimeCB update_media_time_cb,
+                      GetPlayerStateCB get_player_state_cb,
+                      UpdatePlayerStateCB update_player_state_cb) = 0;
+    virtual bool Seek(SbMediaTime seek_to_pts, int ticket) = 0;
+    virtual bool WriteSample(InputBuffer input_buffer, bool* written) = 0;
+    virtual bool WriteEndOfStream(SbMediaType sample_type) = 0;
+    virtual bool SetPause(bool pause) = 0;
+    virtual bool SetBounds(const Bounds& bounds) = 0;
+
+    // Once this function returns, all processing on the Handler and related
+    // objects has to be stopped.  The JobQueue will be destroyed immediately
+    // after and is no longer safe to access.
+    virtual void Stop() = 0;
   };
 
   PlayerWorker(Host* host,
@@ -167,14 +92,32 @@
                SbPlayerDecoderStatusFunc decoder_status_func,
                SbPlayerStatusFunc player_status_func,
                SbPlayer player,
-               SbTime update_interval,
                void* context);
-  virtual ~PlayerWorker();
+  ~PlayerWorker();
 
-  template <typename EventData>
-  void EnqueueEvent(const EventData& event_data) {
-    SB_DCHECK(SbThreadIsValid(thread_));
-    queue_.Put(new Event(event_data));
+  void Seek(SbMediaTime seek_to_pts, int ticket) {
+    job_queue_->Schedule(
+        Bind(&PlayerWorker::DoSeek, this, seek_to_pts, ticket));
+  }
+
+  void WriteSample(InputBuffer input_buffer) {
+    job_queue_->Schedule(
+        Bind(&PlayerWorker::DoWriteSample, this, input_buffer));
+  }
+
+  void WriteEndOfStream(SbMediaType sample_type) {
+    job_queue_->Schedule(
+        Bind(&PlayerWorker::DoWriteEndOfStream, this, sample_type));
+  }
+
+#if SB_IS(PLAYER_PUNCHED_OUT)
+  void SetBounds(Bounds bounds) {
+    job_queue_->Schedule(Bind(&PlayerWorker::DoSetBounds, this, bounds));
+  }
+#endif  // SB_IS(PLAYER_PUNCHED_OUT)
+
+  void SetPause(bool pause) {
+    job_queue_->Schedule(Bind(&PlayerWorker::DoSetPause, this, pause));
   }
 
   void UpdateDroppedVideoFrames(int dropped_video_frames) {
@@ -189,17 +132,21 @@
 
   static void* ThreadEntryPoint(void* context);
   void RunLoop();
-  bool DoInit();
-  bool DoSeek(const SeekEventData& data);
-  bool DoWriteSample(const WriteSampleEventData& data);
-  bool DoWriteEndOfStream(const WriteEndOfStreamEventData& data);
-  bool DoUpdate(const SetBoundsEventData& data);
+  void DoInit();
+  void DoSeek(SbMediaTime seek_to_pts, int ticket);
+  void DoWriteSample(InputBuffer input_buffer);
+  void DoWritePendingSamples();
+  void DoWriteEndOfStream(SbMediaType sample_type);
+#if SB_IS(PLAYER_PUNCHED_OUT)
+  void DoSetBounds(Bounds bounds);
+#endif  // SB_IS(PLAYER_PUNCHED_OUT)
+  void DoSetPause(bool pause);
   void DoStop();
 
   void UpdateDecoderState(SbMediaType type, SbPlayerDecoderState state);
 
   SbThread thread_;
-  Queue<Event*> queue_;
+  scoped_ptr<JobQueue> job_queue_;
 
   Host* host_;
   scoped_ptr<Handler> handler_;
@@ -211,9 +158,9 @@
   int ticket_;
 
   SbPlayerState player_state_;
-  const SbTime update_interval_;
-  WriteSampleEventData pending_audio_sample_;
-  WriteSampleEventData pending_video_sample_;
+  InputBuffer pending_audio_buffer_;
+  InputBuffer pending_video_buffer_;
+  Closure write_pending_sample_closure_;
 };
 
 }  // namespace player
diff --git a/src/starboard/shared/starboard/player/video_frame_internal.cc b/src/starboard/shared/starboard/player/video_frame_internal.cc
index d8dd571..69818c1 100644
--- a/src/starboard/shared/starboard/player/video_frame_internal.cc
+++ b/src/starboard/shared/starboard/player/video_frame_internal.cc
@@ -1,254 +1,254 @@
-// Copyright 2016 Google Inc. 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/shared/starboard/player/video_frame_internal.h"

-

-#include "starboard/log.h"

-#include "starboard/memory.h"

-

-namespace starboard {

-namespace shared {

-namespace starboard {

-namespace player {

-

-namespace {

-

-bool s_yuv_to_rgb_lookup_table_initialized = false;

-int s_y_to_rgb[256];

-int s_v_to_r[256];

-int s_u_to_g[256];

-int s_v_to_g[256];

-int s_u_to_b[256];

-uint8_t s_clamp_table[256 * 5];

-

-void EnsureYUVToRGBLookupTableInitialized() {

-  if (s_yuv_to_rgb_lookup_table_initialized) {

-    return;

-  }

-

-  // The YUV to RGBA conversion is based on

-  //   http://www.equasys.de/colorconversion.html.

-  // The formula is:

-  // r = 1.164f * y                      + 1.793f * (v - 128);

-  // g = 1.164f * y - 0.213f * (u - 128) - 0.533f * (v - 128);

-  // b = 1.164f * y + 2.112f * (u - 128);

-  // And r/g/b has to be clamped to [0, 255].

-  //

-  // We optimize the conversion algorithm by creating two kinds of lookup

-  // tables.  The color component table contains pre-calculated color component

-  // values.  The clamp table contains a map between |v| + 512 to the clamped

-  // |v| to avoid conditional operation.

-  // The minimum value of |v| can be 2.112f * (-128) = -271, the maximum value

-  // of |v| can be 1.164f * 255 + 2.112f * 127 = 565.  So we need 512 bytes at

-  // each side of the clamp buffer.

-  SbMemorySet(s_clamp_table, 0, 512);

-  SbMemorySet(s_clamp_table + 768, 0xff, 512);

-

-  for (int i = 0; i < 256; ++i) {

-    s_y_to_rgb[i] = (static_cast<uint8_t>(i) - 16) * 1.164f;

-    s_v_to_r[i] = (static_cast<uint8_t>(i) - 128) * 1.793f;

-    s_u_to_g[i] = (static_cast<uint8_t>(i) - 128) * -0.213;

-    s_v_to_g[i] = (static_cast<uint8_t>(i) - 128) * -0.533f;

-    s_u_to_b[i] = (static_cast<uint8_t>(i) - 128) * 2.112f;

-    s_clamp_table[512 + i] = i;

-  }

-

-  s_yuv_to_rgb_lookup_table_initialized = true;

-}

-

-uint8_t ClampColorComponent(int component) {

-  return s_clamp_table[component + 512];

-}

-

-}  // namespace

-

-VideoFrame::VideoFrame() {

-  InitializeToInvalidFrame();

-}

-

-VideoFrame::VideoFrame(int width,

-                       int height,

-                       SbMediaTime pts,

-                       void* native_texture,

-                       void* native_texture_context,

-                       FreeNativeTextureFunc free_native_texture_func) {

-  SB_DCHECK(native_texture != NULL);

-  SB_DCHECK(free_native_texture_func != NULL);

-

-  InitializeToInvalidFrame();

-

-  format_ = kNativeTexture;

-  width_ = width;

-  height_ = height;

-  pts_ = pts;

-  native_texture_ = native_texture;

-  native_texture_context_ = native_texture_context;

-  free_native_texture_func_ = free_native_texture_func;

-}

-

-VideoFrame::~VideoFrame() {

-  if (format_ == kNativeTexture) {

-    free_native_texture_func_(native_texture_context_, native_texture_);

-  }

-}

-

-int VideoFrame::GetPlaneCount() const {

-  SB_DCHECK(format_ != kInvalid);

-  SB_DCHECK(format_ != kNativeTexture);

-

-  return static_cast<int>(planes_.size());

-}

-

-const VideoFrame::Plane& VideoFrame::GetPlane(int index) const {

-  SB_DCHECK(format_ != kInvalid);

-  SB_DCHECK(format_ != kNativeTexture);

-  SB_DCHECK(index >= 0 && index < GetPlaneCount()) << "Invalid index: "

-                                                   << index;

-  return planes_[index];

-}

-

-void* VideoFrame::native_texture() const {

-  SB_DCHECK(format_ == kNativeTexture);

-  return native_texture_;

-}

-

-scoped_refptr<VideoFrame> VideoFrame::ConvertTo(Format target_format) const {

-  SB_DCHECK(format_ == kYV12);

-  SB_DCHECK(target_format == kBGRA32);

-

-  EnsureYUVToRGBLookupTableInitialized();

-

-  scoped_refptr<VideoFrame> target_frame(new VideoFrame);

-

-  target_frame->format_ = target_format;

-  target_frame->width_ = width();

-  target_frame->height_ = height();

-  target_frame->pts_ = pts_;

-  target_frame->pixel_buffer_.reset(new uint8_t[width() * height() * 4]);

-  target_frame->planes_.push_back(

-      Plane(width(), height(), width() * 4, target_frame->pixel_buffer_.get()));

-

-  const uint8_t* y_data = GetPlane(0).data;

-  const uint8_t* u_data = GetPlane(1).data;

-  const uint8_t* v_data = GetPlane(2).data;

-  uint8_t* bgra_data = target_frame->pixel_buffer_.get();

-

-  int height = this->height();

-  int width = this->width();

-

-  for (int row = 0; row < height; ++row) {

-    const uint8_t* y = &y_data[row * GetPlane(0).pitch_in_bytes];

-    const uint8_t* u = &u_data[row / 2 * GetPlane(1).pitch_in_bytes];

-    const uint8_t* v = &v_data[row / 2 * GetPlane(2).pitch_in_bytes];

-    int v_to_r, u_to_g, v_to_g, u_to_b;

-

-    for (int column = 0; column < width; ++column) {

-      if (column % 2 == 0) {

-        v_to_r = s_v_to_r[*v];

-        u_to_g = s_u_to_g[*u];

-        v_to_g = s_v_to_g[*v];

-        u_to_b = s_u_to_b[*u];

-      } else {

-        ++u, ++v;

-      }

-

-      int y_to_rgb = s_y_to_rgb[*y];

-      int r = y_to_rgb + v_to_r;

-      int g = y_to_rgb + u_to_g + v_to_g;

-      int b = y_to_rgb + u_to_b;

-

-      *bgra_data++ = ClampColorComponent(b);

-      *bgra_data++ = ClampColorComponent(g);

-      *bgra_data++ = ClampColorComponent(r);

-      *bgra_data++ = 0xff;

-

-      ++y;

-    }

-  }

-

-  return target_frame;

-}

-

-// static

-scoped_refptr<VideoFrame> VideoFrame::CreateEOSFrame() {

-  return new VideoFrame;

-}

-

-// static

-scoped_refptr<VideoFrame> VideoFrame::CreateYV12Frame(int width,

-                                                      int height,

-                                                      int pitch_in_bytes,

-                                                      SbMediaTime pts,

-                                                      const uint8_t* y,

-                                                      const uint8_t* u,

-                                                      const uint8_t* v) {

-  scoped_refptr<VideoFrame> frame(new VideoFrame);

-  frame->format_ = kYV12;

-  frame->width_ = width;

-  frame->height_ = height;

-  frame->pts_ = pts;

-

-  // U/V planes generally have half resolution of the Y plane.  However, in the

-  // extreme case that any dimension of Y plane is odd, we want to have an

-  // extra pixel on U/V planes.

-  int uv_height = height / 2 + height % 2;

-  int uv_width = width / 2 + width % 2;

-  int uv_pitch_in_bytes = pitch_in_bytes / 2 + pitch_in_bytes % 2;

-

-  int y_plane_size_in_bytes = height * pitch_in_bytes;

-  int uv_plane_size_in_bytes = uv_height * uv_pitch_in_bytes;

-  frame->pixel_buffer_.reset(

-      new uint8_t[y_plane_size_in_bytes + uv_plane_size_in_bytes * 2]);

-  SbMemoryCopy(frame->pixel_buffer_.get(), y, y_plane_size_in_bytes);

-  SbMemoryCopy(frame->pixel_buffer_.get() + y_plane_size_in_bytes, u,

-               uv_plane_size_in_bytes);

-  SbMemoryCopy(frame->pixel_buffer_.get() + y_plane_size_in_bytes +

-                   uv_plane_size_in_bytes,

-               v, uv_plane_size_in_bytes);

-

-  frame->planes_.push_back(

-      Plane(width, height, pitch_in_bytes, frame->pixel_buffer_.get()));

-  frame->planes_.push_back(

-      Plane(uv_width, uv_height, uv_pitch_in_bytes,

-            frame->pixel_buffer_.get() + y_plane_size_in_bytes));

-  frame->planes_.push_back(Plane(uv_width, uv_height, uv_pitch_in_bytes,

-                                 frame->pixel_buffer_.get() +

-                                     y_plane_size_in_bytes +

-                                     uv_plane_size_in_bytes));

-  return frame;

-}

-