Import Cobalt 13.111497

Change-Id: Id1b3ec264d4706a3f717f9f72d656b51bd62d168
diff --git a/src/_env.py b/src/_env.py
new file mode 100644
index 0000000..d398413
--- /dev/null
+++ b/src/_env.py
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+"""Load the project environment."""
+
+from imp import load_source
+from os import path
+
+_ENVIRONMENT_PATH = path.abspath(path.join(path.dirname(__file__), 'starboard',
+                                           'tools', 'environment.py'))
+load_source('environment', _ENVIRONMENT_PATH)
diff --git a/src/cobalt/_env.py b/src/cobalt/_env.py
new file mode 100644
index 0000000..6188bb7
--- /dev/null
+++ b/src/cobalt/_env.py
@@ -0,0 +1,26 @@
+#
+# 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.
+#
+"""Ask the parent directory to load the project environment."""
+
+from imp import load_source
+from os import path
+import sys
+
+_ENV = path.abspath(path.join(path.dirname(__file__), path.pardir, '_env.py'))
+if not path.exists(_ENV):
+  print '%s: Can\'t find repo root.\nMissing parent: %s' % (__file__, _ENV)
+  sys.exit(1)
+load_source('', _ENV)
diff --git a/src/cobalt/base/base.gyp b/src/cobalt/base/base.gyp
index ccbcc85..ee51357 100644
--- a/src/cobalt/base/base.gyp
+++ b/src/cobalt/base/base.gyp
@@ -69,6 +69,7 @@
         'tokens.cc',
         'tokens.h',
         'type_id.h',
+        'window_size_changed_event.h',
         'unicode/character.cc',
         'unicode/character.h',
         'unicode/character_values.h',
diff --git a/src/cobalt/base/window_size_changed_event.h b/src/cobalt/base/window_size_changed_event.h
new file mode 100644
index 0000000..50034c8
--- /dev/null
+++ b/src/cobalt/base/window_size_changed_event.h
@@ -0,0 +1,40 @@
+// 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_BASE_WINDOW_SIZE_CHANGED_EVENT_H_
+#define COBALT_BASE_WINDOW_SIZE_CHANGED_EVENT_H_
+
+#include "cobalt/base/event.h"
+#include "starboard/event.h"
+
+namespace base {
+
+class WindowSizeChangedEvent : public Event {
+ public:
+  WindowSizeChangedEvent(SbWindow window, SbWindowSize size)
+      : window_(window), size_(size) {}
+
+  SbWindow window() const { return window_; }
+  SbWindowSize size() const { return size_; }
+
+  BASE_EVENT_SUBCLASS(WindowSizeChangedEvent);
+
+ private:
+  SbWindow window_;
+  SbWindowSize size_;
+};
+
+}  // namespace base
+
+#endif  // COBALT_BASE_WINDOW_SIZE_CHANGED_EVENT_H_
diff --git a/src/cobalt/bindings/_env.py b/src/cobalt/bindings/_env.py
new file mode 100644
index 0000000..8c282d9
--- /dev/null
+++ b/src/cobalt/bindings/_env.py
@@ -0,0 +1,41 @@
+#
+# 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.
+#
+"""Ask the parent directory to load the project environment."""
+
+from imp import load_source
+import os
+from os import path
+import sys
+
+_ENV = path.abspath(path.join(path.dirname(__file__), path.pardir, '_env.py'))
+if not path.exists(_ENV):
+  print '%s: Can\'t find repo root.\nMissing parent: %s' % (__file__, _ENV)
+  sys.exit(1)
+load_source('', _ENV)
+
+# Add blink's Python tools to the path.
+
+from cobalt.tools import paths  # pylint: disable=g-import-not-at-top
+
+sys.path.append(
+    os.path.normpath(
+        os.path.join(paths.REPOSITORY_ROOT, 'third_party', 'blink', 'Tools',
+                     'Scripts')))
+
+sys.path.append(
+    os.path.normpath(
+        os.path.join(paths.REPOSITORY_ROOT, 'third_party', 'blink', 'Source',
+                     'bindings', 'scripts')))
diff --git a/src/cobalt/bindings/bootstrap_path.py b/src/cobalt/bindings/bootstrap_path.py
deleted file mode 100644
index 6882bed..0000000
--- a/src/cobalt/bindings/bootstrap_path.py
+++ /dev/null
@@ -1,49 +0,0 @@
-#
-# 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.
-#
-"""Utility to prepend the top-level source directory to sys.path.
-
-Since this may be used outside of gclient or git environment (such as part of a
-tarball), the path to the root must be hardcoded.
-"""
-
-import os
-import sys
-
-
-def _GetSrcRoot():
-  """Finds the first directory named 'src' that this module is in."""
-  current_path = os.path.normpath(__file__)
-  while os.path.basename(current_path) != 'src':
-    next_path = os.path.dirname(current_path)
-    if next_path == current_path:
-      raise RuntimeError('Could not find src directory.')
-    current_path = next_path
-  return os.path.abspath(current_path)
-
-
-sys.path.insert(0, _GetSrcRoot())
-
-# Add blink's python tools to the path.
-
-sys.path.append(
-    os.path.normpath(
-        os.path.join(_GetSrcRoot(), 'third_party', 'blink', 'Tools',
-                     'Scripts')))
-
-sys.path.append(
-    os.path.normpath(
-        os.path.join(_GetSrcRoot(), 'third_party', 'blink', 'Source',
-                     'bindings', 'scripts')))
diff --git a/src/cobalt/bindings/code_generator_cobalt.py b/src/cobalt/bindings/code_generator_cobalt.py
index fb43c76..a16a417 100644
--- a/src/cobalt/bindings/code_generator_cobalt.py
+++ b/src/cobalt/bindings/code_generator_cobalt.py
@@ -22,8 +22,7 @@
 import os
 import sys
 
-import bootstrap_path  # pylint: disable=unused-import
-
+import _env  # pylint: disable=unused-import
 from cobalt.bindings import path_generator
 from cobalt.bindings.contexts import ContextBuilder
 from cobalt.bindings.name_conversion import get_interface_name
diff --git a/src/cobalt/bindings/contexts.py b/src/cobalt/bindings/contexts.py
index d779ec5..4bebf81 100644
--- a/src/cobalt/bindings/contexts.py
+++ b/src/cobalt/bindings/contexts.py
@@ -18,6 +18,7 @@
 dicts that will be used by Jinja in JS bindings generation.
 """
 
+import _env  # pylint: disable=unused-import
 from idl_definitions import IdlTypedef
 from idl_types import IdlPromiseType
 from idl_types import IdlSequenceType
diff --git a/src/cobalt/bindings/flatten_idls_test.py b/src/cobalt/bindings/flatten_idls_test.py
index f637f22..3eb18f9 100644
--- a/src/cobalt/bindings/flatten_idls_test.py
+++ b/src/cobalt/bindings/flatten_idls_test.py
@@ -18,17 +18,11 @@
 
 import os
 import platform
-import sys
 import unittest
 
+import _env  # pylint: disable=unused-import
 import flatten_idls
 
-# This needs to be set before calling flatten_idls._FlattenInterfaces
-script_dir = os.path.dirname(__file__)
-sys.path.append(os.path.join(script_dir, os.pardir, os.pardir, os.pardir,
-                             'third_party', 'blink', 'Source', 'bindings',
-                             'scripts'))
-
 
 def _TestDataPath():
   return os.path.join(os.path.dirname(__file__), 'testdata')
@@ -40,6 +34,7 @@
     yield os.path.join(test_data_path, f)
 
 
+@unittest.skip('Test has bitrotted.')
 class FlattenedInterfacesTest(unittest.TestCase):
 
   def testFlattenIdl(self):
diff --git a/src/cobalt/bindings/idl_compiler_cobalt.py b/src/cobalt/bindings/idl_compiler_cobalt.py
index ac25c86..f5c4d74 100644
--- a/src/cobalt/bindings/idl_compiler_cobalt.py
+++ b/src/cobalt/bindings/idl_compiler_cobalt.py
@@ -21,8 +21,7 @@
 import os
 import pickle
 
-import bootstrap_path  # pylint: disable=g-bad-import-order,unused-import
-
+import _env  # pylint: disable=unused-import
 from idl_compiler import IdlCompiler
 from utilities import ComponentInfoProviderCobalt
 from utilities import idl_filename_to_interface_name
diff --git a/src/cobalt/bindings/mozjs45/_env.py b/src/cobalt/bindings/mozjs45/_env.py
new file mode 100644
index 0000000..6188bb7
--- /dev/null
+++ b/src/cobalt/bindings/mozjs45/_env.py
@@ -0,0 +1,26 @@
+#
+# 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.
+#
+"""Ask the parent directory to load the project environment."""
+
+from imp import load_source
+from os import path
+import sys
+
+_ENV = path.abspath(path.join(path.dirname(__file__), path.pardir, '_env.py'))
+if not path.exists(_ENV):
+  print '%s: Can\'t find repo root.\nMissing parent: %s' % (__file__, _ENV)
+  sys.exit(1)
+load_source('', _ENV)
diff --git a/src/cobalt/bindings/mozjs45/bootstrap_path.py b/src/cobalt/bindings/mozjs45/bootstrap_path.py
deleted file mode 100644
index 6882bed..0000000
--- a/src/cobalt/bindings/mozjs45/bootstrap_path.py
+++ /dev/null
@@ -1,49 +0,0 @@
-#
-# 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.
-#
-"""Utility to prepend the top-level source directory to sys.path.
-
-Since this may be used outside of gclient or git environment (such as part of a
-tarball), the path to the root must be hardcoded.
-"""
-
-import os
-import sys
-
-
-def _GetSrcRoot():
-  """Finds the first directory named 'src' that this module is in."""
-  current_path = os.path.normpath(__file__)
-  while os.path.basename(current_path) != 'src':
-    next_path = os.path.dirname(current_path)
-    if next_path == current_path:
-      raise RuntimeError('Could not find src directory.')
-    current_path = next_path
-  return os.path.abspath(current_path)
-
-
-sys.path.insert(0, _GetSrcRoot())
-
-# Add blink's python tools to the path.
-
-sys.path.append(
-    os.path.normpath(
-        os.path.join(_GetSrcRoot(), 'third_party', 'blink', 'Tools',
-                     'Scripts')))
-
-sys.path.append(
-    os.path.normpath(
-        os.path.join(_GetSrcRoot(), 'third_party', 'blink', 'Source',
-                     'bindings', 'scripts')))
diff --git a/src/cobalt/bindings/mozjs45/generate_conversion_header_mozjs45.py b/src/cobalt/bindings/mozjs45/generate_conversion_header_mozjs45.py
index d149eee..eb4fe5d 100644
--- a/src/cobalt/bindings/mozjs45/generate_conversion_header_mozjs45.py
+++ b/src/cobalt/bindings/mozjs45/generate_conversion_header_mozjs45.py
@@ -15,8 +15,7 @@
 
 import sys
 
-import bootstrap_path  # pylint: disable=g-bad-import-order,unused-import
-
+import _env  # pylint: disable=unused-import
 from cobalt.bindings.generate_conversion_header import generate_header
 from cobalt.bindings.mozjs45.code_generator_mozjs45 import CodeGeneratorMozjs45
 
diff --git a/src/cobalt/bindings/mozjs45/idl_compiler_mozjs45.py b/src/cobalt/bindings/mozjs45/idl_compiler_mozjs45.py
index cec76da..3b2bf36 100644
--- a/src/cobalt/bindings/mozjs45/idl_compiler_mozjs45.py
+++ b/src/cobalt/bindings/mozjs45/idl_compiler_mozjs45.py
@@ -19,8 +19,7 @@
 
 import sys
 
-import bootstrap_path  # pylint: disable=unused-import
-
+import _env  # pylint: disable=unused-import
 from cobalt.bindings.idl_compiler_cobalt import generate_bindings
 from cobalt.bindings.mozjs45.code_generator_mozjs45 import CodeGeneratorMozjs45
 
diff --git a/src/cobalt/bindings/path_generator_test.py b/src/cobalt/bindings/path_generator_test.py
index 5e0bbb8..f8f0f5b 100644
--- a/src/cobalt/bindings/path_generator_test.py
+++ b/src/cobalt/bindings/path_generator_test.py
@@ -15,10 +15,11 @@
 
 import unittest
 
-import bootstrap_path  # pylint: disable=unused-import
+import _env  # pylint: disable=unused-import
 from cobalt.bindings.path_generator import PathBuilder
 
 
+@unittest.skip('Test has bitrotted.')
 class PathBuilderTest(unittest.TestCase):
 
   def setUp(self):
@@ -36,56 +37,56 @@
         ['root', 'this', 'is', 'a'])
 
   def testBindingsClass(self):
-    self.assertEquals(
+    self.assertEqual(
         self.path_builder.BindingsClass('TestInterface'), 'PreTestInterface')
 
   def testFullBindingsClass(self):
-    self.assertEquals(
+    self.assertEqual(
         self.path_builder.FullBindingsClassName('TestInterface'),
         'root::this::is::a::PreTestInterface')
 
   def testFullClass(self):
-    self.assertEquals(
+    self.assertEqual(
         self.path_builder.FullClassName('TestInterface'),
         'root::this::is::a::TestInterface')
 
   def testImplementationHeaderIncludePath(self):
-    self.assertEquals(
+    self.assertEqual(
         self.path_builder.ImplementationHeaderPath('TestInterface'),
         'root/this/is/a/test_interface.h')
 
   def testBindingsHeaderIncludePath(self):
-    self.assertEquals(
+    self.assertEqual(
         self.path_builder.BindingsHeaderIncludePath('TestInterface'),
         'root/this/is/a/pre_test_interface.h')
 
   def testBindingsHeaderFullPath(self):
-    self.assertEquals(
+    self.assertEqual(
         self.path_builder.BindingsHeaderFullPath('TestInterface'),
         '/path/to/generated/root/this/is/a/pre_test_interface.h')
 
   def testBindingsImplementationPath(self):
-    self.assertEquals(
+    self.assertEqual(
         self.path_builder.BindingsImplementationPath('TestInterface'),
         '/path/to/generated/root/this/is/a/pre_test_interface.cc')
 
   def testDictionaryHeaderIncludePath(self):
-    self.assertEquals(
+    self.assertEqual(
         self.path_builder.DictionaryHeaderIncludePath('TestInterface'),
         'root/this/is/a/test_interface.h')
 
   def testDictionaryHeaderFullPath(self):
-    self.assertEquals(
+    self.assertEqual(
         self.path_builder.DictionaryHeaderFullPath('TestInterface'),
         '/path/to/generated/root/this/is/a/test_interface.h')
 
   def testDictionaryConversionHeaderIncludePath(self):
-    self.assertEquals(
+    self.assertEqual(
         self.path_builder.BindingsHeaderIncludePath('TestInterface'),
         'root/this/is/a/pre_test_interface.h')
 
   def testDictionaryConversionHeaderFullPath(self):
-    self.assertEquals(
+    self.assertEqual(
         self.path_builder.BindingsHeaderFullPath('TestInterface'),
         '/path/to/generated/root/this/is/a/pre_test_interface.h')
 
diff --git a/src/cobalt/bindings/run_cobalt_bindings_tests.py b/src/cobalt/bindings/run_cobalt_bindings_tests.py
index ffb3911..acec6d9 100644
--- a/src/cobalt/bindings/run_cobalt_bindings_tests.py
+++ b/src/cobalt/bindings/run_cobalt_bindings_tests.py
@@ -26,8 +26,7 @@
 import os
 import sys
 
-import bootstrap_path  # pylint: disable=unused-import
-
+import _env  # pylint: disable=unused-import
 from cobalt.bindings.idl_compiler_cobalt import IdlCompilerCobalt
 from cobalt.bindings.mozjs45.code_generator_mozjs45 import CodeGeneratorMozjs45
 from webkitpy.bindings.bindings_tests import run_bindings_tests
diff --git a/src/cobalt/bindings/v8c/_env.py b/src/cobalt/bindings/v8c/_env.py
new file mode 100644
index 0000000..6188bb7
--- /dev/null
+++ b/src/cobalt/bindings/v8c/_env.py
@@ -0,0 +1,26 @@
+#
+# 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.
+#
+"""Ask the parent directory to load the project environment."""
+
+from imp import load_source
+from os import path
+import sys
+
+_ENV = path.abspath(path.join(path.dirname(__file__), path.pardir, '_env.py'))
+if not path.exists(_ENV):
+  print '%s: Can\'t find repo root.\nMissing parent: %s' % (__file__, _ENV)
+  sys.exit(1)
+load_source('', _ENV)
diff --git a/src/cobalt/bindings/v8c/bootstrap_path.py b/src/cobalt/bindings/v8c/bootstrap_path.py
deleted file mode 100644
index 69a1b65..0000000
--- a/src/cobalt/bindings/v8c/bootstrap_path.py
+++ /dev/null
@@ -1,49 +0,0 @@
-#
-# 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.
-#
-"""Utility to prepend the top-level source directory to sys.path.
-
-Since this may be used outside of gclient or git environment (such as part of a
-tarball), the path to the root must be hardcoded.
-"""
-
-import os
-import sys
-
-
-def _GetSrcRoot():
-  """Finds the first directory named 'src' that this module is in."""
-  current_path = os.path.normpath(__file__)
-  while os.path.basename(current_path) != 'src':
-    next_path = os.path.dirname(current_path)
-    if next_path == current_path:
-      raise RuntimeError('Could not find src directory.')
-    current_path = next_path
-  return os.path.abspath(current_path)
-
-
-sys.path.insert(0, _GetSrcRoot())
-
-# Add blink's python tools to the path.
-
-sys.path.append(
-    os.path.normpath(
-        os.path.join(_GetSrcRoot(), 'third_party', 'blink', 'Tools',
-                     'Scripts')))
-
-sys.path.append(
-    os.path.normpath(
-        os.path.join(_GetSrcRoot(), 'third_party', 'blink', 'Source',
-                     'bindings', 'scripts')))
diff --git a/src/cobalt/bindings/v8c/generate_conversion_header_v8c.py b/src/cobalt/bindings/v8c/generate_conversion_header_v8c.py
index 8901cc2..d1bfe8f 100644
--- a/src/cobalt/bindings/v8c/generate_conversion_header_v8c.py
+++ b/src/cobalt/bindings/v8c/generate_conversion_header_v8c.py
@@ -15,8 +15,7 @@
 
 import sys
 
-import bootstrap_path  # pylint: disable=g-bad-import-order,unused-import
-
+import _env  # pylint: disable=unused-import
 from cobalt.bindings.generate_conversion_header import generate_header
 from cobalt.bindings.v8c.code_generator_v8c import CodeGeneratorV8c
 
diff --git a/src/cobalt/bindings/v8c/idl_compiler_v8c.py b/src/cobalt/bindings/v8c/idl_compiler_v8c.py
index 6c8c92e..f23c63d 100644
--- a/src/cobalt/bindings/v8c/idl_compiler_v8c.py
+++ b/src/cobalt/bindings/v8c/idl_compiler_v8c.py
@@ -19,8 +19,7 @@
 
 import sys
 
-import bootstrap_path  # pylint: disable=unused-import
-
+import _env  # pylint: disable=unused-import
 from cobalt.bindings.idl_compiler_cobalt import generate_bindings
 from cobalt.bindings.v8c.code_generator_v8c import CodeGeneratorV8c
 
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc
index 81c407e..3091f7c 100644
--- a/src/cobalt/browser/application.cc
+++ b/src/cobalt/browser/application.cc
@@ -39,6 +39,7 @@
 #include "cobalt/base/localized_strings.h"
 #include "cobalt/base/startup_timer.h"
 #include "cobalt/base/user_log.h"
+#include "cobalt/base/window_size_changed_event.h"
 #include "cobalt/browser/memory_settings/auto_mem_settings.h"
 #include "cobalt/browser/memory_tracker/tool.h"
 #include "cobalt/browser/switches.h"
@@ -568,8 +569,7 @@
   if (command_line->HasSwitch(switches::kMemoryTracker)) {
     std::string command_arg =
         command_line->GetSwitchValueASCII(switches::kMemoryTracker);
-    memory_tracker_tool_ =
-        memory_tracker::CreateMemoryTrackerTool(command_arg);
+    memory_tracker_tool_ = memory_tracker::CreateMemoryTrackerTool(command_arg);
   }
 
   if (command_line->HasSwitch(switches::kDisableImageAnimations)) {
@@ -614,7 +614,12 @@
       base::Bind(&Application::OnDeepLinkEvent, base::Unretained(this));
   event_dispatcher_.AddEventCallback(base::DeepLinkEvent::TypeId(),
                                      deep_link_event_callback_);
-
+#if SB_API_VERSION >= SB_WINDOW_SIZE_CHANGED_API_VERSION
+  window_size_change_event_callback_ = base::Bind(
+      &Application::OnWindowSizeChangedEvent, base::Unretained(this));
+  event_dispatcher_.AddEventCallback(base::WindowSizeChangedEvent::TypeId(),
+                                     window_size_change_event_callback_);
+#endif  // SB_API_VERSION >= SB_WINDOW_SIZE_CHANGED_API_VERSION
 #if defined(ENABLE_WEBDRIVER)
 #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
   bool create_webdriver_module =
@@ -669,9 +674,12 @@
   // Unregister event callbacks.
   event_dispatcher_.RemoveEventCallback(network::NetworkEvent::TypeId(),
                                         network_event_callback_);
-  event_dispatcher_.RemoveEventCallback(
-      base::DeepLinkEvent::TypeId(), deep_link_event_callback_);
-
+  event_dispatcher_.RemoveEventCallback(base::DeepLinkEvent::TypeId(),
+                                        deep_link_event_callback_);
+#if SB_API_VERSION >= SB_WINDOW_SIZE_CHANGED_API_VERSION
+  event_dispatcher_.RemoveEventCallback(base::WindowSizeChangedEvent::TypeId(),
+                                        window_size_change_event_callback_);
+#endif  // SB_API_VERSION >= SB_WINDOW_SIZE_CHANGED_API_VERSION
   app_status_ = kShutDownAppStatus;
 }
 
@@ -725,6 +733,15 @@
 #endif  // SB_API_VERSION >= 6
       OnApplicationEvent(starboard_event->type);
       break;
+#if SB_API_VERSION >= SB_WINDOW_SIZE_CHANGED_API_VERSION
+    case kSbEventTypeWindowSizeChanged:
+      DispatchEventInternal(new base::WindowSizeChangedEvent(
+          static_cast<SbEventWindowSizeChangedData*>(starboard_event->data)
+              ->window,
+          static_cast<SbEventWindowSizeChangedData*>(starboard_event->data)
+              ->size));
+      break;
+#endif  // SB_API_VERSION >= SB_WINDOW_SIZE_CHANGED_API_VERSION
     case kSbEventTypeNetworkConnect:
       DispatchEventInternal(
           new network::NetworkEvent(network::NetworkEvent::kConnection));
@@ -835,6 +852,15 @@
   }
 }
 
+#if SB_API_VERSION >= SB_WINDOW_SIZE_CHANGED_API_VERSION
+void Application::OnWindowSizeChangedEvent(const base::Event* event) {
+  TRACE_EVENT0("cobalt::browser", "Application::OnWindowSizeChangedEvent()");
+  const base::WindowSizeChangedEvent* window_size_change_event =
+      base::polymorphic_downcast<const base::WindowSizeChangedEvent*>(event);
+  browser_module_->OnWindowSizeChanged(window_size_change_event->size());
+}
+#endif  // SB_API_VERSION >= SB_WINDOW_SIZE_CHANGED_API_VERSION
+
 void Application::WebModuleRecreated() {
   TRACE_EVENT0("cobalt::browser", "Application::WebModuleRecreated()");
 #if defined(ENABLE_WEBDRIVER)
@@ -888,9 +914,8 @@
                             &app_suspend_count_, sizeof(app_suspend_count_));
     base::UserLog::Register(base::UserLog::kAppResumeCountIndex, "ResumeCnt",
                             &app_resume_count_, sizeof(app_resume_count_));
-    base::UserLog::Register(base::UserLog::kNetworkStatusIndex,
-                            "NetworkStatus", &network_status_,
-                            sizeof(network_status_));
+    base::UserLog::Register(base::UserLog::kNetworkStatusIndex, "NetworkStatus",
+                            &network_status_, sizeof(network_status_));
     base::UserLog::Register(base::UserLog::kNetworkConnectCountIndex,
                             "ConnectCnt", &network_connect_count_,
                             sizeof(network_connect_count_));
diff --git a/src/cobalt/browser/application.h b/src/cobalt/browser/application.h
index ab3ce08..9000883 100644
--- a/src/cobalt/browser/application.h
+++ b/src/cobalt/browser/application.h
@@ -70,6 +70,11 @@
   // Called to handle a deep link event.
   void OnDeepLinkEvent(const base::Event* event);
 
+#if SB_API_VERSION >= SB_WINDOW_SIZE_CHANGED_API_VERSION
+  // Called to handle a window size change event.
+  void OnWindowSizeChangedEvent(const base::Event* event);
+#endif  // SB_API_VERSION >= SB_WINDOW_SIZE_CHANGED_API_VERSION
+
   // Called when a navigation occurs in the BrowserModule.
   void WebModuleRecreated();
 
@@ -85,6 +90,9 @@
   // Event callbacks.
   base::EventCallback network_event_callback_;
   base::EventCallback deep_link_event_callback_;
+#if SB_API_VERSION >= SB_WINDOW_SIZE_CHANGED_API_VERSION
+  base::EventCallback window_size_change_event_callback_;
+#endif  // SB_API_VERSION >= SB_WINDOW_SIZE_CHANGED_API_VERSION
 
   // Thread checkers to ensure that callbacks for network and application events
   // always occur on the same thread.
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index b1d2a9e..bef9c6a 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -278,8 +278,9 @@
 #if defined(COBALT_CHECK_RENDER_TIMEOUT)
   timeout_polling_thread_.Start();
   timeout_polling_thread_.message_loop()->PostDelayedTask(
-      FROM_HERE, base::Bind(&BrowserModule::OnPollForRenderTimeout,
-                            base::Unretained(this), url),
+      FROM_HERE,
+      base::Bind(&BrowserModule::OnPollForRenderTimeout, base::Unretained(this),
+                 url),
       base::TimeDelta::FromSeconds(kRenderTimeOutPollingDelaySeconds));
 #endif
   TRACE_EVENT0("cobalt::browser", "BrowserModule::BrowserModule()");
@@ -548,13 +549,17 @@
 #if defined(ENABLE_SCREENSHOT)
 void BrowserModule::RequestScreenshotToFile(const FilePath& path,
                                             const base::Closure& done_cb) {
-  screen_shot_writer_->RequestScreenshot(path, done_cb);
+  if (screen_shot_writer_) {
+    screen_shot_writer_->RequestScreenshot(path, done_cb);
+  }
 }
 
 void BrowserModule::RequestScreenshotToBuffer(
     const ScreenShotWriter::PNGEncodeCompleteCallback&
         encode_complete_callback) {
-  screen_shot_writer_->RequestScreenshotToMemory(encode_complete_callback);
+  if (screen_shot_writer_) {
+    screen_shot_writer_->RequestScreenshotToMemory(encode_complete_callback);
+  }
 }
 #endif
 
@@ -624,8 +629,10 @@
   main_web_module_layer_->Submit(renderer_submission);
 
 #if defined(ENABLE_SCREENSHOT)
-  screen_shot_writer_->SetLastPipelineSubmission(renderer::Submission(
-      layout_results.render_tree, layout_results.layout_time));
+  if (screen_shot_writer_) {
+    screen_shot_writer_->SetLastPipelineSubmission(renderer::Submission(
+        layout_results.render_tree, layout_results.layout_time));
+  }
 #endif
   SubmitCurrentRenderTreeToRenderer();
 }
@@ -698,6 +705,25 @@
   SbSystemRequestSuspend();
 }
 
+#if SB_API_VERSION >= SB_WINDOW_SIZE_CHANGED_API_VERSION
+void BrowserModule::OnWindowSizeChanged(const SbWindowSize& size) {
+  math::Size math_size(size.width, size.height);
+  if (web_module_) {
+    web_module_->SetSize(math_size, size.video_pixel_ratio);
+  }
+#if defined(ENABLE_DEBUG_CONSOLE)
+  if (debug_console_) {
+    debug_console_->web_module().SetSize(math_size, size.video_pixel_ratio);
+  }
+#endif  // defined(ENABLE_DEBUG_CONSOLE)
+  if (splash_screen_) {
+    splash_screen_->web_module().SetSize(math_size, size.video_pixel_ratio);
+  }
+
+  return;
+}
+#endif  // SB_API_VERSION >= SB_WINDOW_SIZE_CHANGED_API_VERSION
+
 #if defined(ENABLE_DEBUG_CONSOLE)
 void BrowserModule::OnFuzzerToggle(const std::string& message) {
   if (MessageLoop::current() != self_message_loop_) {
@@ -1049,6 +1075,7 @@
 
 void BrowserModule::Start() {
   TRACE_EVENT0("cobalt::browser", "BrowserModule::Start()");
+  DCHECK_EQ(MessageLoop::current(), self_message_loop_);
   DCHECK(application_state_ == base::kApplicationStatePreloading);
 
   SuspendInternal(true /*is_start*/);
@@ -1061,6 +1088,7 @@
 
 void BrowserModule::Pause() {
   TRACE_EVENT0("cobalt::browser", "BrowserModule::Pause()");
+  DCHECK_EQ(MessageLoop::current(), self_message_loop_);
   DCHECK(application_state_ == base::kApplicationStateStarted);
   FOR_EACH_OBSERVER(LifecycleObserver, lifecycle_observers_, Pause());
   application_state_ = base::kApplicationStatePaused;
@@ -1068,6 +1096,7 @@
 
 void BrowserModule::Unpause() {
   TRACE_EVENT0("cobalt::browser", "BrowserModule::Unpause()");
+  DCHECK_EQ(MessageLoop::current(), self_message_loop_);
   DCHECK(application_state_ == base::kApplicationStatePaused);
   FOR_EACH_OBSERVER(LifecycleObserver, lifecycle_observers_, Unpause());
   application_state_ = base::kApplicationStateStarted;
@@ -1075,6 +1104,7 @@
 
 void BrowserModule::Suspend() {
   TRACE_EVENT0("cobalt::browser", "BrowserModule::Suspend()");
+  DCHECK_EQ(MessageLoop::current(), self_message_loop_);
   DCHECK(application_state_ == base::kApplicationStatePaused ||
          application_state_ == base::kApplicationStatePreloading);
 
@@ -1085,6 +1115,7 @@
 
 void BrowserModule::Resume() {
   TRACE_EVENT0("cobalt::browser", "BrowserModule::Resume()");
+  DCHECK_EQ(MessageLoop::current(), self_message_loop_);
   DCHECK(application_state_ == base::kApplicationStateSuspended);
 
   StartOrResumeInternalPreStateUpdate(false /*is_start*/);
@@ -1095,6 +1126,7 @@
 }
 
 void BrowserModule::ReduceMemory() {
+  DCHECK_EQ(MessageLoop::current(), self_message_loop_);
   if (splash_screen_) {
     splash_screen_->ReduceMemory();
   }
@@ -1113,6 +1145,7 @@
 void BrowserModule::CheckMemory(
     const int64_t& used_cpu_memory,
     const base::optional<int64_t>& used_gpu_memory) {
+  DCHECK_EQ(MessageLoop::current(), self_message_loop_);
   if (!auto_mem_) {
     return;
   }
@@ -1135,12 +1168,10 @@
 void BrowserModule::OnPollForRenderTimeout(const GURL& url) {
   SbTime last_render_timestamp = static_cast<SbTime>(SbAtomicAcquire_Load64(
       non_trivial_global_variables.Get().last_render_timestamp));
-  base::Time last_render =
-      base::Time::FromSbTime(last_render_timestamp);
-  bool timeout_expiration =
-      base::Time::Now() -
-          base::TimeDelta::FromSeconds(kLastRenderTimeoutSeconds) >
-      last_render;
+  base::Time last_render = base::Time::FromSbTime(last_render_timestamp);
+  bool timeout_expiration = base::Time::Now() - base::TimeDelta::FromSeconds(
+                                                    kLastRenderTimeoutSeconds) >
+                            last_render;
   bool timeout_response_trigger = false;
   if (timeout_expiration) {
     // The timeout only triggers if the timeout expiration has been detected
@@ -1171,8 +1202,9 @@
     }
   } else {
     timeout_polling_thread_.message_loop()->PostDelayedTask(
-        FROM_HERE, base::Bind(&BrowserModule::OnPollForRenderTimeout,
-                              base::Unretained(this), url),
+        FROM_HERE,
+        base::Bind(&BrowserModule::OnPollForRenderTimeout,
+                   base::Unretained(this), url),
         base::TimeDelta::FromSeconds(kRenderTimeOutPollingDelaySeconds));
   }
 }
@@ -1193,6 +1225,7 @@
 
 void BrowserModule::InitializeSystemWindow() {
   resource_provider_stub_ = base::nullopt;
+  DCHECK(!system_window_);
   system_window_.reset(new system_window::SystemWindow(
       event_dispatcher_, options_.requested_viewport_size));
 
@@ -1210,20 +1243,37 @@
                                          base::Unretained(this)),
                               system_window_.get())
                               .Pass();
-  renderer_module_.reset(new renderer::RendererModule(
-      system_window_.get(),
-      RendererModuleWithCameraOptions(options_.renderer_module_options,
-                                      input_device_manager_->camera_3d())));
-
-#if defined(ENABLE_SCREENSHOT)
-  screen_shot_writer_.reset(new ScreenShotWriter(renderer_module_->pipeline()));
-#endif  // defined(ENABLE_SCREENSHOT)
+  InstantiateRendererModule();
 
   media_module_ =
       media::MediaModule::Create(system_window_.get(), GetResourceProvider(),
                                  options_.media_module_options);
 }
 
+void BrowserModule::InstantiateRendererModule() {
+  DCHECK_EQ(MessageLoop::current(), self_message_loop_);
+  DCHECK(system_window_);
+  DCHECK(!renderer_module_);
+
+  renderer_module_.reset(new renderer::RendererModule(
+      system_window_.get(),
+      RendererModuleWithCameraOptions(options_.renderer_module_options,
+                                      input_device_manager_->camera_3d())));
+#if defined(ENABLE_SCREENSHOT)
+  screen_shot_writer_.reset(new ScreenShotWriter(renderer_module_->pipeline()));
+#endif  // defined(ENABLE_SCREENSHOT)
+}
+
+void BrowserModule::DestroyRendererModule() {
+  DCHECK_EQ(MessageLoop::current(), self_message_loop_);
+  DCHECK(renderer_module_);
+
+#if defined(ENABLE_SCREENSHOT)
+  screen_shot_writer_.reset();
+#endif  // defined(ENABLE_SCREENSHOT)
+  renderer_module_.reset();
+}
+
 void BrowserModule::UpdateFromSystemWindow() {
   math::Size size = GetViewportSize();
   float video_pixel_ratio = system_window_->GetVideoPixelRatio();
@@ -1278,7 +1328,7 @@
 
 #if defined(ENABLE_GPU_ARRAY_BUFFER_ALLOCATOR)
   // Note that the following function call will leak the GPU memory allocated.
-  // This is because after renderer_module_->Suspend() is called it is no longer
+  // This is because after the renderer_module_ is destroyed it is no longer
   // safe to release the GPU memory allocated.
   //
   // The following code can call reset() to release the allocated memory but the
@@ -1293,9 +1343,9 @@
   }
 
   if (renderer_module_) {
-    // Place the renderer module into a suspended state where it releases all
-    // its graphical resources.
-    renderer_module_->Suspend();
+    // Destroy the renderer module into so that it releases all its graphical
+    // resources.
+    DestroyRendererModule();
   }
 }
 
@@ -1303,20 +1353,12 @@
   TRACE_EVENT1("cobalt::browser",
                "BrowserModule::StartOrResumeInternalPreStateUpdate", "is_start",
                is_start ? "true" : "false");
-  render_tree::ResourceProvider* resource_provider = NULL;
-  if (!renderer_module_) {
+  if (!system_window_) {
     InitializeSystemWindow();
     UpdateFromSystemWindow();
-    resource_provider = GetResourceProvider();
   } else {
-    renderer_module_->Resume();
-
-    // Note that at this point, it is probable that this resource provider is
-    // different than the one that was managed in the associated call to
-    // Suspend().
-    resource_provider = GetResourceProvider();
-
-    media_module_->Resume(resource_provider);
+    InstantiateRendererModule();
+    media_module_->Resume(GetResourceProvider());
   }
 
 #if defined(ENABLE_GPU_ARRAY_BUFFER_ALLOCATOR)
@@ -1327,10 +1369,10 @@
 
   if (is_start) {
     FOR_EACH_OBSERVER(LifecycleObserver, lifecycle_observers_,
-                      Start(resource_provider));
+                      Start(GetResourceProvider()));
   } else {
     FOR_EACH_OBSERVER(LifecycleObserver, lifecycle_observers_,
-                      Resume(resource_provider));
+                      Resume(GetResourceProvider()));
   }
 }
 
@@ -1423,5 +1465,12 @@
   }
 }
 
+SbWindow BrowserModule::GetSbWindow() {
+  if (!system_window_) {
+    return NULL;
+  }
+  return system_window_->GetSbWindow();
+}
+
 }  // namespace browser
 }  // namespace cobalt
diff --git a/src/cobalt/browser/browser_module.h b/src/cobalt/browser/browser_module.h
index f3cf4a2..81a3bc3 100644
--- a/src/cobalt/browser/browser_module.h
+++ b/src/cobalt/browser/browser_module.h
@@ -161,6 +161,11 @@
   void CheckMemory(const int64_t& used_cpu_memory,
                    const base::optional<int64_t>& used_gpu_memory);
 
+#if SB_API_VERSION >= SB_WINDOW_SIZE_CHANGED_API_VERSION
+  // Called when a kSbEventTypeWindowSizeChange event is fired.
+  void OnWindowSizeChanged(const SbWindowSize& size);
+#endif  // SB_API_VERSION >= SB_WINDOW_SIZE_CHANGED_API_VERSION
+
  private:
 #if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
   static void CoreDumpHandler(void* browser_module_as_void);
@@ -296,6 +301,12 @@
   // Initializes the system window, and all components that require it.
   void InitializeSystemWindow();
 
+  // Instantiates a renderer module and dependent objects.
+  void InstantiateRendererModule();
+
+  // Destroys the renderer module and dependent objects.
+  void DestroyRendererModule();
+
   // Updates all components that have already been created with information
   // resulting from the creation of the system window.
   void UpdateFromSystemWindow();
@@ -325,6 +336,9 @@
   // module.
   void SubmitCurrentRenderTreeToRenderer();
 
+  // Get the SbWindow via |system_window_| or potentially NULL.
+  SbWindow GetSbWindow();
+
   // TODO:
   //     WeakPtr usage here can be avoided if BrowserModule has a thread to
   //     own where it can ensure that its tasks are all resolved when it is
diff --git a/src/cobalt/browser/splash_screen.h b/src/cobalt/browser/splash_screen.h
index bc21bbe..617338d 100644
--- a/src/cobalt/browser/splash_screen.h
+++ b/src/cobalt/browser/splash_screen.h
@@ -74,6 +74,8 @@
   // Returns whether Shutdown() has been called before or not.
   bool ShutdownSignaled() const { return shutdown_signaled_; }
 
+  WebModule& web_module() { return *web_module_; }
+
  private:
   // Run when window.close() is called by the WebModule.
   void OnWindowClosed();
diff --git a/src/cobalt/browser/testdata/pointer-events-demo/pointer-events-demo.html b/src/cobalt/browser/testdata/pointer-events-demo/pointer-events-demo.html
new file mode 100644
index 0000000..84fddce
--- /dev/null
+++ b/src/cobalt/browser/testdata/pointer-events-demo/pointer-events-demo.html
@@ -0,0 +1,177 @@
+<!DOCTYPE html>
+<!--
+ | Pointer events demo web page.
+ | Can be used to verify that pointer events are generated and passed into the
+ | correct DOM elements.
+ -->
+<html class="html">
+<head class="head">
+  <style>
+    body {
+      margin: 20px;
+      background-color: #0000FF;
+      font-size: 20px;
+      font-family: Roboto;
+    }
+    .padded {
+      padding: 20px;
+      transition: border-left-color 1s linear, border-top-color 1s linear, outline-width 1s linear, outline-color 1s linear;
+      background-clip: padding-box;
+    }
+    .top {
+      transform-no: rotate(-45deg);
+      display: inline-block;
+    }
+    .outer {
+      background-color: #484;
+      border: solid rgba(0, 0, 0, 0)  10px;
+      outline: solid #00C0F0 5px;
+    }
+    .outer:hover {
+      background-color: #848;
+      border: solid white 10px;
+      outline: solid #FF4000 20px;
+    }
+    .inner {
+      animation: blink 1.1s infinite;
+      background-color: #8F8;
+      outline: solid rgba(0, 0, 0, 1) 0px;
+    }
+    .inner:hover {
+      background-color: #F8F;
+      outline: solid rgba(160, 160, 255, 0) 40px;
+    }
+    .leftswap {
+      position:relative;
+      left:+185px;
+      top:0px;
+    }
+    .rightswap {
+      position:relative;
+      left:-185px;
+      top:0px;
+    }
+    .pointereventsnone {
+      pointer-events:none;
+    }
+    .pointereventsauto {
+      pointer-events:auto;
+    }
+
+  </style>
+  <script>
+    function phasename(phase) {
+      switch(phase) {
+        case 0: return ' [none]';
+        case 1: return ' [capturing]';
+        case 2: return ' [at target]';
+        case 3: return ' [bubbling]';
+      }
+      return ' [unknown: ' + phase + ']';
+    }
+
+    function sethandlers(event, classname, callback) {
+      var elements = document.getElementsByClassName(classname);
+      for (var i = 0; i < elements.length; ++i) {
+        elements[i].addEventListener(event, callback);
+      }
+    }
+
+    function setfocus(classname) {
+      var elements = document.getElementsByClassName(classname);
+      for (var i = 0; i < elements.length; ++i) {
+        console.log('Setting focus \"' + elements[i].className + '\"');
+        elements[i].focus();
+      }
+    }
+    function log_event(e) {
+      var pointertype = e.pointerType ? e.pointerType + ' ' : '';
+      var id = this.getAttribute('id') ?
+                   'id=\'' + this.getAttribute('id') + '\' ' : '';
+      console.log(e.type + ' ' + pointertype +
+                  id + '(' +
+                  this.getAttribute('class') + ') ' +
+                  phasename(e.eventPhase) +
+                  ' (' + e.screenX + ',' + e.screenY + ')');
+    }
+    function cancel(e) {
+      console.log('cancel');
+      e.preventDefault();
+    }
+    function stop(e) {
+      console.log('stop');
+      e.stopPropagation();
+    }
+    function capture(e) {
+      console.log('capture');
+      e.target.setPointerCapture(e.pointerId);
+    }
+    function setallhandlers(prefix, classname, callback) {
+      sethandlers(prefix + 'enter', classname, callback);
+      sethandlers(prefix + 'leave', classname, callback);
+      sethandlers(prefix + 'over', classname, callback);
+      sethandlers(prefix + 'out', classname, callback);
+      sethandlers(prefix + 'down', classname, callback);
+      sethandlers(prefix + 'up', classname, callback);
+      sethandlers(prefix + 'move', classname, callback);
+    }
+
+    window.onload = function() {
+      setallhandlers('mouse', 'log', log_event);
+      setallhandlers('pointer', 'log', log_event);
+      setallhandlers('mouse', 'cancel', cancel);
+      setallhandlers('pointer', 'cancel', cancel);
+      setallhandlers('mouse', 'stop', stop);
+      setallhandlers('pointer', 'stop', stop);
+      sethandlers('pointerdown', 'capture', capture);
+      setfocus('focus');
+
+      document.addEventListener("keydown", e => {
+          console.log('Do something on keydown');
+      });
+
+      var links = document.getElementsByClassName("clickhere");
+      for (var i = 0; i < links.length; i++) {
+        links[i].addEventListener("click", function(e) {
+          console.log('Click received at (' + e.screenX + ',' + e.screenY +
+                      ') on \"' + e.target.innerHTML + '\" during ' +
+                      phasename(e.eventPhase));
+        });
+      }
+    }
+  </script>
+</head>
+<body class="body">
+  <div class="topdiv">
+  <span id="top-left" class="top log pointereventsnone clickhere">
+    <div class="outer padded log pointereventsnone">
+      <div class="inner padded one log pointereventsauto clickhere">One</div>
+      <div class="inner padded two log capture stop cancel focus pointereventsauto clickhere" tabindex='0'>Two</div>
+      <div class="inner padded three log stop pointereventsauto clickhere" tabindex='1'>Three</div>
+      <div class="padded four log pointereventsauto clickhere">Four</div>
+      <div class="inner padded five offsetx log pointereventsauto">Five offset x</div>
+      <div class="inner padded six offsety log pointereventsauto">Six offset y</div>
+      <div class="padded seven log pointereventsauto">Seven</div>
+      <div class="inner padded eight log pointereventsauto">Eight</div>
+      <div class="padded nine log pointereventsauto">Nine</div>
+      <div class="inner padded leftswap log pointereventsauto">LeftSwap</div>
+    </div>
+  </span>
+  <span id="top-divider" class="top" style="width:50px;"></span>
+  <span id="top-right" class="top events">
+    <div class="outer padded events">
+      <div class="inner padded one events clickhere">One</div>
+      <div class="inner padded two events clickhere">Two</div>
+      <div class="inner padded three events clickhere">Three</div>
+      <div class="padded four events clickhere">Four</div>
+      <div class="inner padded five offsetx events">Five offset x</div>
+      <div class="inner padded six offsety events">Six offset y</div>
+      <div class="padded seven events">Seven</div>
+      <div class="inner padded eight events">Eight</div>
+      <div class="padded nine events">Nine</div>
+      <div class="inner padded rightswap events">RightSwap</div>
+    </div>
+  </span>
+  </div>
+</body>
+</html>
diff --git a/src/cobalt/build/_env.py b/src/cobalt/build/_env.py
new file mode 100644
index 0000000..6188bb7
--- /dev/null
+++ b/src/cobalt/build/_env.py
@@ -0,0 +1,26 @@
+#
+# 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.
+#
+"""Ask the parent directory to load the project environment."""
+
+from imp import load_source
+from os import path
+import sys
+
+_ENV = path.abspath(path.join(path.dirname(__file__), path.pardir, '_env.py'))
+if not path.exists(_ENV):
+  print '%s: Can\'t find repo root.\nMissing parent: %s' % (__file__, _ENV)
+  sys.exit(1)
+load_source('', _ENV)
diff --git a/src/cobalt/build/bootstrap_path.py b/src/cobalt/build/bootstrap_path.py
deleted file mode 100644
index 5b9a07a..0000000
--- a/src/cobalt/build/bootstrap_path.py
+++ /dev/null
@@ -1,36 +0,0 @@
-#
-# 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.
-#
-"""Utility to prepend the top-level source directory to sys.path.
-
-Since this may be used outside of gclient or git environment (such as part of a
-tarball), the path to the root must be hardcoded.
-"""
-
-import os
-import sys
-
-
-def _GetSrcRoot():
-  """Finds the first directory named 'src' that this module is in."""
-  current_path = os.path.normpath(__file__)
-  while not (os.path.basename(current_path) == 'src'):
-    next_path = os.path.dirname(current_path)
-    if next_path == current_path:
-      raise RuntimeError('Could not find src directory.')
-    current_path = next_path
-  return os.path.abspath(current_path)
-
-sys.path.insert(0, _GetSrcRoot())
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index d0d80b7..ed94c45 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-108984
\ No newline at end of file
+111497
\ No newline at end of file
diff --git a/src/cobalt/build/build_config.h b/src/cobalt/build/build_config.h
index 527f9bc..bfe3df5 100644
--- a/src/cobalt/build/build_config.h
+++ b/src/cobalt/build/build_config.h
@@ -50,11 +50,4 @@
 #endif  // COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K <
         //     COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P
 
-#if COBALT_ENCRYPTED_MEDIA_EXTENSION_ENABLE_KEY_STATUSES_UPDATE
-#if SB_API_VERSION < 6
-#error COBALT_ENCRYPTED_MEDIA_EXTENSION_ENABLE_KEY_STATUSES_UPDATE requires \
-           that SB_API_VERSION is greater than or equal to 6
-#endif  // SB_API_VERSION < 6
-#endif  // COBALT_ENCRYPTED_MEDIA_EXTENSION_ENABLE_KEY_STATUSES_UPDATE
-
 #endif  // COBALT_BUILD_BUILD_CONFIG_H_
diff --git a/src/cobalt/build/config/BUILD.gn b/src/cobalt/build/config/BUILD.gn
index 3851714..1fc0a0f 100644
--- a/src/cobalt/build/config/BUILD.gn
+++ b/src/cobalt/build/config/BUILD.gn
@@ -26,12 +26,6 @@
     allocate_on_demand = 0
   }
 
-  if (cobalt_encrypted_media_extension_enable_key_statuses_update) {
-    enable_key_statuses_update = 1
-  } else {
-    enable_key_statuses_update = 0
-  }
-
   defines = [
     "COBALT",
     "COBALT_MEDIA_BUFFER_POOL_ALLOCATE_ON_DEMAND=$allocate_on_demand",
@@ -43,7 +37,6 @@
     "COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET=$cobalt_media_buffer_non_video_budget",
     "COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P=$cobalt_media_buffer_video_budget_1080p",
     "COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K=$cobalt_media_buffer_video_budget_4k",
-    "COBALT_ENCRYPTED_MEDIA_EXTENSION_ENABLE_KEY_STATUSES_UPDATE=$enable_key_statuses_update",
 
     # From common.gypi
     "USE_OPENSSL=1",
diff --git a/src/cobalt/build/config/_env.py b/src/cobalt/build/config/_env.py
new file mode 100644
index 0000000..6188bb7
--- /dev/null
+++ b/src/cobalt/build/config/_env.py
@@ -0,0 +1,26 @@
+#
+# 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.
+#
+"""Ask the parent directory to load the project environment."""
+
+from imp import load_source
+from os import path
+import sys
+
+_ENV = path.abspath(path.join(path.dirname(__file__), path.pardir, '_env.py'))
+if not path.exists(_ENV):
+  print '%s: Can\'t find repo root.\nMissing parent: %s' % (__file__, _ENV)
+  sys.exit(1)
+load_source('', _ENV)
diff --git a/src/cobalt/build/config/base.gni b/src/cobalt/build/config/base.gni
index 834371c..bf91956 100644
--- a/src/cobalt/build/config/base.gni
+++ b/src/cobalt/build/config/base.gni
@@ -523,14 +523,6 @@
   cobalt_media_buffer_video_budget_4k = "(60 * 1024 * 1024)"
 }
 
-# Set to true to enable MediaKeySession::keyStatuses and
-# MediaKeySession::onkeystatuseschange support.  This requires that
-# SB_API_VERSION is greater than or equal to
-# SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION.
-if (!defined(cobalt_encrypted_media_extension_enable_key_statuses_update)) {
-  cobalt_encrypted_media_extension_enable_key_statuses_update = true
-}
-
 # Enables embedding Cobalt as a shared library within another app. This
 # requires a 'lib' starboard implementation for the corresponding platform.
 if (!defined(cobalt_enable_lib)) {
diff --git a/src/cobalt/build/config/base.gypi b/src/cobalt/build/config/base.gypi
index 056ae29..0614075 100644
--- a/src/cobalt/build/config/base.gypi
+++ b/src/cobalt/build/config/base.gypi
@@ -561,12 +561,6 @@
     # re-download video data.  Note that the JavaScript app may experience
     # significant difficulty if this value is too low.
     'cobalt_media_buffer_video_budget_4k%': 60 * 1024 * 1024,
-
-    # Set to 1 to enable MediaKeySession::keyStatuses and
-    # MediaKeySession::onkeystatuseschange support.  This requires that
-    # SB_API_VERSION is greater than or equal to
-    # SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION.
-    'cobalt_encrypted_media_extension_enable_key_statuses_update%': 1,
   },
 
   'target_defaults': {
@@ -641,11 +635,6 @@
           'JS_GC_ZEAL=1',
         ],
       }],
-      ['cobalt_encrypted_media_extension_enable_key_statuses_update == 1', {
-        'defines': [
-          'COBALT_ENCRYPTED_MEDIA_EXTENSION_ENABLE_KEY_STATUSES_UPDATE=1',
-        ],
-      }],
       ['final_executable_type=="shared_library"', {
         'target_conditions': [
           ['_toolset=="target"', {
diff --git a/src/cobalt/build/config/starboard.py b/src/cobalt/build/config/starboard.py
index fdef871..34c5760 100644
--- a/src/cobalt/build/config/starboard.py
+++ b/src/cobalt/build/config/starboard.py
@@ -91,6 +91,10 @@
     }
     return variables
 
+  def GetTestEnvVariables(self):
+    """Gets a dict of environment variables needed by unit test binaries."""
+    return {}
+
   def WebdriverBenchmarksEnabled(self):
     """Determines if webdriver benchmarks are enabled or not.
 
diff --git a/src/cobalt/build/gyp_cobalt b/src/cobalt/build/gyp_cobalt
index 129e0cf..4114b07 100755
--- a/src/cobalt/build/gyp_cobalt
+++ b/src/cobalt/build/gyp_cobalt
@@ -21,7 +21,7 @@
 import sys
 import textwrap
 
-import bootstrap_path
+import _env  # pylint: disable=unused-import
 import cobalt
 from cobalt.build import config
 from cobalt.build import gyp_utils
diff --git a/src/cobalt/build/gyp_utils.py b/src/cobalt/build/gyp_utils.py
index bf38b8a..e9b89b2 100644
--- a/src/cobalt/build/gyp_utils.py
+++ b/src/cobalt/build/gyp_utils.py
@@ -24,7 +24,7 @@
 import urllib
 import urllib2
 
-import bootstrap_path  # pylint: disable=unused-import
+import _env  # pylint: disable=unused-import
 from cobalt.tools import paths
 from starboard.tools import platform
 
diff --git a/src/cobalt/build/path_conversion_test.py b/src/cobalt/build/path_conversion_test.py
index e0e48b9..d2d751f 100644
--- a/src/cobalt/build/path_conversion_test.py
+++ b/src/cobalt/build/path_conversion_test.py
@@ -15,41 +15,41 @@
 
 import unittest
 
-import bootstrap_path  # pylint: disable=unused-import
+import _env  # pylint: disable=unused-import
 from cobalt.build.path_conversion import ConvertPath
 
 
 class PathConversionTest(unittest.TestCase):
 
   def testNoop(self):
-    self.assertEquals(ConvertPath('this/is/a/path.txt'), 'this/is/a/path.txt')
+    self.assertEqual(ConvertPath('this/is/a/path.txt'), 'this/is/a/path.txt')
 
   def testOutputDirectory(self):
-    self.assertEquals(
+    self.assertEqual(
         ConvertPath('this/is/a/path.txt', output_directory='another/location'),
         'another/location/path.txt')
 
   def testAddPrefix(self):
-    self.assertEquals(
+    self.assertEqual(
         ConvertPath('this/is/a/path.txt', output_prefix='banana_'),
         'this/is/a/banana_path.txt')
 
   def testChangeExtension(self):
-    self.assertEquals(
+    self.assertEqual(
         ConvertPath('this/is/a/path.txt', output_extension='clown'),
         'this/is/a/path.clown')
 
   def testSlashes(self):
-    self.assertEquals(
+    self.assertEqual(
         ConvertPath('this\\is\\a\\path.txt', forward_slashes=True),
         'this/is/a/path.txt')
 
-    self.assertEquals(
+    self.assertEqual(
         ConvertPath('this\\is\\a\\path.txt', forward_slashes=False),
         'this\\is\\a\\path.txt')
 
   def testBaseDirectory(self):
-    self.assertEquals(
+    self.assertEqual(
         ConvertPath(
             'if/this/is/a/path.txt',
             output_directory='then/that',
diff --git a/src/cobalt/dom/eme/media_key_session.cc b/src/cobalt/dom/eme/media_key_session.cc
index 11c2f98..fae4417 100644
--- a/src/cobalt/dom/eme/media_key_session.cc
+++ b/src/cobalt/dom/eme/media_key_session.cc
@@ -37,11 +37,11 @@
     : ALLOW_THIS_IN_INITIALIZER_LIST(event_queue_(this)),
       drm_system_(drm_system),
       drm_system_session_(drm_system->CreateSession(
-#if SB_API_VERSION >= 6
+#if SB_HAS(DRM_KEY_STATUSES)
           base::Bind(&MediaKeySession::OnSessionUpdateKeyStatuses,
                      base::AsWeakPtr(this))
-#endif  // SB_API_VERSION >= 6
-                     )),
+#endif  // SB_HAS(DRM_KEY_STATUSES)
+              )),  // NOLINT(whitespace/parens)
       script_value_factory_(script_value_factory),
       uninitialized_(true),
       callable_(false),
@@ -297,6 +297,13 @@
 
   // 8.2. Resolve promise.
   promise_reference->value().Resolve();
+
+#if !SB_HAS(DRM_KEY_STATUSES)
+  // When key statuses is not enabled, send a "keystatuseschange" anyway so the
+  // JS app knows that |keyStatuses| will no longer be updated.
+  LOG(INFO) << "Fired 'keystatuseschange' event on MediaKeySession.";
+  event_queue_.Enqueue(new Event(base::Tokens::keystatuseschange()));
+#endif  // !SB_HAS(DRM_KEY_STATUSES)
 }
 
 // See https://www.w3.org/TR/encrypted-media/#dom-mediakeysession-update.
@@ -307,6 +314,13 @@
   //
   // TODO: Introduce Starboard API that allows CDM to propagate error codes.
   promise_reference->value().Reject(new DOMException(DOMException::kNone));
+
+#if !SB_HAS(DRM_KEY_STATUSES)
+  // When key statuses is not enabled, send a "keystatuseschange" anyway so the
+  // JS app knows that |keyStatuses| will no longer be updated.
+  LOG(INFO) << "Fired 'keystatuseschange' event on MediaKeySession.";
+  event_queue_.Enqueue(new Event(base::Tokens::keystatuseschange()));
+#endif  // !SB_HAS(DRM_KEY_STATUSES)
 }
 
 // See https://www.w3.org/TR/encrypted-media/#update-key-statuses.
@@ -356,6 +370,7 @@
 
   // 5. Queue a task to fire a simple event named keystatuseschange at the
   //    session.
+  LOG(INFO) << "Fired 'keystatuseschange' event on MediaKeySession.";
   event_queue_.Enqueue(new Event(base::Tokens::keystatuseschange()));
 
   // 6. Queue a task to run the Attempt to Resume Playback If Necessary
diff --git a/src/cobalt/dom/eme/media_key_session.idl b/src/cobalt/dom/eme/media_key_session.idl
index 3813c47..8e6ac14 100644
--- a/src/cobalt/dom/eme/media_key_session.idl
+++ b/src/cobalt/dom/eme/media_key_session.idl
@@ -19,7 +19,6 @@
   // TODO: Implement |expiration|.
   // readonly attribute unrestricted double expiration;
   readonly attribute Promise<void> closed;
-  [Conditional=COBALT_ENCRYPTED_MEDIA_EXTENSION_ENABLE_KEY_STATUSES_UPDATE]
   readonly attribute MediaKeyStatusMap keyStatuses;
   attribute EventHandler onkeystatuseschange;
   attribute EventHandler onmessage;
diff --git a/src/cobalt/dom/eme/media_key_status_map.cc b/src/cobalt/dom/eme/media_key_status_map.cc
index 33e0bcc..50063c1 100644
--- a/src/cobalt/dom/eme/media_key_status_map.cc
+++ b/src/cobalt/dom/eme/media_key_status_map.cc
@@ -24,6 +24,16 @@
 
 namespace {
 
+std::string ConvertKeyIdToAscii(const std::string& key_id) {
+  const char kHexChars[] = "0123456789ABCDEF";
+  std::string key_id_in_ascii;
+  for (auto ch : key_id) {
+    key_id_in_ascii += kHexChars[static_cast<unsigned char>(ch) / 16];
+    key_id_in_ascii += kHexChars[static_cast<unsigned char>(ch) % 16];
+  }
+  return key_id_in_ascii;
+}
+
 std::string ConvertKeyStatusToString(MediaKeyStatus key_status) {
   switch (key_status) {
     case kMediaKeyStatusUsable:
@@ -57,6 +67,13 @@
 
 }  // namespace
 
+void MediaKeyStatusMap::Add(const std::string& key_id,
+                            MediaKeyStatus key_status) {
+  LOG(INFO) << "MediaKeyStatusMap::Add()  " << ConvertKeyIdToAscii(key_id)
+            << " => " << ConvertKeyStatusToString(key_status);
+  key_statuses_[key_id] = key_status;
+}
+
 void MediaKeyStatusMap::ForEach(const ForEachCallbackArg& callback) {
   ForEachCallbackArg::Reference reference(this, callback);
 
diff --git a/src/cobalt/dom/eme/media_key_status_map.h b/src/cobalt/dom/eme/media_key_status_map.h
index a2808c6..812ad90 100644
--- a/src/cobalt/dom/eme/media_key_status_map.h
+++ b/src/cobalt/dom/eme/media_key_status_map.h
@@ -46,9 +46,7 @@
 
   void Clear() { key_statuses_.clear(); }
 
-  void Add(const std::string& key_id, MediaKeyStatus key_status) {
-    key_statuses_[key_id] = key_status;
-  }
+  void Add(const std::string& key_id, MediaKeyStatus key_status);
 
   // Web IDL: MediaKeyStatusMap.
   //
diff --git a/src/cobalt/dom/eme/media_key_status_map.idl b/src/cobalt/dom/eme/media_key_status_map.idl
index 60b1279..58a963f 100644
--- a/src/cobalt/dom/eme/media_key_status_map.idl
+++ b/src/cobalt/dom/eme/media_key_status_map.idl
@@ -14,7 +14,6 @@
 
 // https://www.w3.org/TR/encrypted-media/#mediakeystatusmap-interface
 
-[Conditional=COBALT_ENCRYPTED_MEDIA_EXTENSION_ENABLE_KEY_STATUSES_UPDATE]
 interface MediaKeyStatusMap {
   iterable<BufferSource, MediaKeyStatus>;
   readonly attribute unsigned long size;
diff --git a/src/cobalt/dom/html_media_element.cc b/src/cobalt/dom/html_media_element.cc
index 9ee8083..83a3600 100644
--- a/src/cobalt/dom/html_media_element.cc
+++ b/src/cobalt/dom/html_media_element.cc
@@ -1490,7 +1490,7 @@
 
 void HTMLMediaElement::MediaEngineError(scoped_refptr<MediaError> error) {
   MLOG() << error->code();
-  DLOG(WARNING) << "HTMLMediaElement::MediaEngineError " << error->code();
+  LOG(WARNING) << "HTMLMediaElement::MediaEngineError " << error->code();
 
   // 1 - The user agent should cancel the fetching process.
   StopPeriodicTimers();
diff --git a/src/cobalt/layout/box.cc b/src/cobalt/layout/box.cc
index 1453801..d1dd5a5 100644
--- a/src/cobalt/layout/box.cc
+++ b/src/cobalt/layout/box.cc
@@ -839,8 +839,10 @@
 }
 
 void SetupOutlineNodeFromStyleWithOutset(
+    const math::RectF& rect,
     const scoped_refptr<const cssom::CSSComputedStyleData>& style,
     RectNode::Builder* rect_node_builder, float outset_width) {
+  rect_node_builder->rect = rect;
   rect_node_builder->rect.Outset(outset_width, outset_width);
   if (outset_width != 0) {
     rect_node_builder->border =
@@ -851,10 +853,11 @@
 }
 
 void SetupOutlineNodeFromStyle(
+    const math::RectF& rect,
     const scoped_refptr<const cssom::CSSComputedStyleData>& style,
     RectNode::Builder* rect_node_builder) {
   SetupOutlineNodeFromStyleWithOutset(
-      style, rect_node_builder,
+      rect, style, rect_node_builder,
       GetUsedNonNegativeLength(style->outline_width()).toFloat());
 }
 }  // namespace
@@ -1406,10 +1409,10 @@
   RectNode::Builder rect_node_builder(rect);
   bool has_animated_outline = HasAnimatedOutline(animations());
   if (has_animated_outline) {
-    SetupOutlineNodeFromStyleWithOutset(computed_style(), &rect_node_builder,
-                                        0);
+    SetupOutlineNodeFromStyleWithOutset(rect, computed_style(),
+                                        &rect_node_builder, 0);
   } else {
-    SetupOutlineNodeFromStyle(computed_style(), &rect_node_builder);
+    SetupOutlineNodeFromStyle(rect, computed_style(), &rect_node_builder);
   }
 
   scoped_refptr<RectNode> outline_node(new RectNode(rect_node_builder.Pass()));
@@ -1418,7 +1421,7 @@
 
   if (has_animated_outline) {
     AddAnimations<RectNode>(base::Bind(&PopulateBaseStyleForOutlineNode),
-                            base::Bind(&SetupOutlineNodeFromStyle),
+                            base::Bind(&SetupOutlineNodeFromStyle, rect),
                             *css_computed_style_declaration(), outline_node,
                             animate_node_builder);
   }
diff --git a/src/cobalt/layout_tests/layout_tests.cc b/src/cobalt/layout_tests/layout_tests.cc
index 55d0ea7..a93de29 100644
--- a/src/cobalt/layout_tests/layout_tests.cc
+++ b/src/cobalt/layout_tests/layout_tests.cc
@@ -89,17 +89,28 @@
 
   scoped_refptr<render_tree::animations::AnimateNode> animate_node =
       new render_tree::animations::AnimateNode(layout_results.render_tree);
-  scoped_refptr<render_tree::Node> animated_tree =
-      animate_node->Apply(layout_results.layout_time).animated->source();
+
+  scoped_refptr<render_tree::animations::AnimateNode> animated_node =
+      animate_node->Apply(layout_results.layout_time).animated;
+
+  // We reapply the animation application with the exact same layout time as
+  // before.  This is an extra sanity check to ensure that the animated results
+  // are deterministic.
+  scoped_refptr<render_tree::animations::AnimateNode> twice_animated_node =
+      animated_node->Apply(layout_results.layout_time).animated;
+  EXPECT_EQ(animated_node, twice_animated_node);
+
+  scoped_refptr<render_tree::Node> static_render_tree =
+      twice_animated_node->source();
 
   bool results =
-      pixel_tester.TestTree(animated_tree, GetParam().base_file_path);
+      pixel_tester.TestTree(static_render_tree, GetParam().base_file_path);
 
   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kRebaseline) ||
       (!results &&
        CommandLine::ForCurrentProcess()->HasSwitch(
            switches::kRebaselineFailedTests))) {
-    pixel_tester.Rebaseline(animated_tree, GetParam().base_file_path);
+    pixel_tester.Rebaseline(static_render_tree, GetParam().base_file_path);
   }
 
   EXPECT_TRUE(results);
diff --git a/src/cobalt/media/base/drm_system.cc b/src/cobalt/media/base/drm_system.cc
index 8b5516b..a5f2457 100644
--- a/src/cobalt/media/base/drm_system.cc
+++ b/src/cobalt/media/base/drm_system.cc
@@ -20,7 +20,7 @@
 namespace cobalt {
 namespace media {
 
-#if SB_API_VERSION >= 6
+#if SB_HAS(DRM_KEY_STATUSES)
 DrmSystem::Session::Session(
     DrmSystem* drm_system,
     SessionUpdateKeyStatusesCallback update_key_statuses_callback)
@@ -29,10 +29,10 @@
       closed_(false) {
   DCHECK(!update_key_statuses_callback_.is_null());
 }
-#else   // SB_API_VERSION >= 6
+#else   // SB_HAS(DRM_KEY_STATUSES)
 DrmSystem::Session::Session(DrmSystem* drm_system)
     : drm_system_(drm_system), closed_(false) {}
-#endif  // SB_API_VERSION >= 6
+#endif  // SB_HAS(DRM_KEY_STATUSES)
 
 DrmSystem::Session::~Session() {
   if (id_ && !closed_) {
@@ -77,11 +77,11 @@
     : wrapped_drm_system_(SbDrmCreateSystem(key_system, this,
                                             OnSessionUpdateRequestGeneratedFunc,
                                             OnSessionUpdatedFunc
-#if SB_API_VERSION >= 6
+#if SB_HAS(DRM_KEY_STATUSES)
                                             ,
                                             OnSessionKeyStatusesChangedFunc
-#endif  // SB_API_VERSION >= 6
-                                            )),
+#endif  // SB_HAS(DRM_KEY_STATUSES)
+                                            )),  // NOLINT(whitespace/parens)
       message_loop_(MessageLoop::current()),
       ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)),
       weak_this_(weak_ptr_factory_.GetWeakPtr()),
@@ -92,17 +92,17 @@
 
 DrmSystem::~DrmSystem() { SbDrmDestroySystem(wrapped_drm_system_); }
 
-#if SB_API_VERSION >= 6
+#if SB_HAS(DRM_KEY_STATUSES)
 scoped_ptr<DrmSystem::Session> DrmSystem::CreateSession(
     SessionUpdateKeyStatusesCallback session_update_key_statuses_callback) {
   return make_scoped_ptr(
       new Session(this, session_update_key_statuses_callback));
 }
-#else   // SB_API_VERSION >= 6
+#else   // SB_HAS(DRM_KEY_STATUSES)
 scoped_ptr<DrmSystem::Session> DrmSystem::CreateSession() {
   return make_scoped_ptr(new Session(this));
 }
-#endif  // SB_API_VERSION >= 6
+#endif  // SB_HAS(DRM_KEY_STATUSES)
 
 void DrmSystem::GenerateSessionUpdateRequest(
     Session* session, const std::string& type, const uint8_t* init_data,
@@ -226,7 +226,7 @@
   ticket_to_session_update_map_.erase(session_update_iterator);
 }
 
-#if SB_API_VERSION >= 6
+#if SB_HAS(DRM_KEY_STATUSES)
 void DrmSystem::OnSessionKeyStatusChanged(
     const std::string& session_id, const std::vector<std::string>& key_ids,
     const std::vector<SbDrmKeyStatus>& key_statuses) {
@@ -241,7 +241,7 @@
 
   session->update_key_statuses_callback().Run(key_ids, key_statuses);
 }
-#endif  // SB_API_VERSION >= 6
+#endif  // SB_HAS(DRM_KEY_STATUSES)
 
 // static
 void DrmSystem::OnSessionUpdateRequestGeneratedFunc(
@@ -283,7 +283,7 @@
                             drm_system->weak_this_, ticket, succeeded));
 }
 
-#if SB_API_VERSION >= 6
+#if SB_HAS(DRM_KEY_STATUSES)
 // static
 void DrmSystem::OnSessionKeyStatusesChangedFunc(
     SbDrmSystem wrapped_drm_system, void* context, const void* session_id,
@@ -315,7 +315,7 @@
       base::Bind(&DrmSystem::OnSessionKeyStatusChanged, drm_system->weak_this_,
                  session_id_copy, key_ids_copy, key_statuses_copy));
 }
-#endif  // SB_API_VERSION >= 6
+#endif  // SB_HAS(DRM_KEY_STATUSES)
 
 }  // namespace media
 }  // namespace cobalt
diff --git a/src/cobalt/media/base/drm_system.h b/src/cobalt/media/base/drm_system.h
index f60ad9d..6eea0af 100644
--- a/src/cobalt/media/base/drm_system.h
+++ b/src/cobalt/media/base/drm_system.h
@@ -40,11 +40,11 @@
   typedef base::Callback<void()> SessionUpdateRequestDidNotGenerateCallback;
   typedef base::Callback<void()> SessionUpdatedCallback;
   typedef base::Callback<void()> SessionDidNotUpdateCallback;
-#if SB_API_VERSION >= 6
+#if SB_HAS(DRM_KEY_STATUSES)
   typedef base::Callback<void(const std::vector<std::string>& key_ids,
                               const std::vector<SbDrmKeyStatus>& key_statuses)>
       SessionUpdateKeyStatusesCallback;
-#endif  // SB_API_VERSION >= 6
+#endif  // SB_HAS(DRM_KEY_STATUSES)
 
   // Flyweight that provides RAII semantics for sessions.
   // Most of logic is implemented by |DrmSystem| and thus sessions must be
@@ -89,31 +89,31 @@
    private:
     // Private API for |DrmSystem|.
     Session(DrmSystem* drm_system
-#if SB_API_VERSION >= 6
+#if SB_HAS(DRM_KEY_STATUSES)
             ,
             SessionUpdateKeyStatusesCallback update_key_statuses_callback
-#endif  // SB_API_VERSION >= 6
+#endif          // SB_HAS(DRM_KEY_STATUSES)
             );  // NOLINT(whitespace/parens)
     void set_id(const std::string& id) { id_ = id; }
     const SessionUpdateRequestGeneratedCallback&
     update_request_generated_callback() const {
       return update_request_generated_callback_;
     }
-#if SB_API_VERSION >= 6
+#if SB_HAS(DRM_KEY_STATUSES)
     const SessionUpdateKeyStatusesCallback& update_key_statuses_callback()
         const {
       return update_key_statuses_callback_;
     }
-#endif  // SB_API_VERSION >= 6
+#endif  // SB_HAS(DRM_KEY_STATUSES)
 
     DrmSystem* const drm_system_;
     bool closed_;
     base::optional<std::string> id_;
     // Supports spontaneous invocations of |SbDrmSessionUpdateRequestFunc|.
     SessionUpdateRequestGeneratedCallback update_request_generated_callback_;
-#if SB_API_VERSION >= 6
+#if SB_HAS(DRM_KEY_STATUSES)
     SessionUpdateKeyStatusesCallback update_key_statuses_callback_;
-#endif  // SB_API_VERSION >= 6
+#endif  // SB_HAS(DRM_KEY_STATUSES)
 
     friend class DrmSystem;
 
@@ -126,9 +126,9 @@
   SbDrmSystem wrapped_drm_system() { return wrapped_drm_system_; }
 
   scoped_ptr<Session> CreateSession(
-#if SB_API_VERSION >= 6
+#if SB_HAS(DRM_KEY_STATUSES)
       SessionUpdateKeyStatusesCallback session_update_key_statuses_callback
-#endif  // SB_API_VERSION >= 6
+#endif    // SB_HAS(DRM_KEY_STATUSES)
       );  // NOLINT(whitespace/parens)
 
  private:
@@ -169,11 +169,11 @@
       int ticket, const base::optional<std::string>& session_id,
       scoped_array<uint8> message, int message_size);
   void OnSessionUpdated(int ticket, bool succeeded);
-#if SB_API_VERSION >= 6
+#if SB_HAS(DRM_KEY_STATUSES)
   void OnSessionKeyStatusChanged(
       const std::string& session_id, const std::vector<std::string>& key_ids,
       const std::vector<SbDrmKeyStatus>& key_statuses);
-#endif  // SB_API_VERSION >= 6
+#endif  // SB_HAS(DRM_KEY_STATUSES)
 
   // Called on any thread, parameters need to be copied immediately.
   static void OnSessionUpdateRequestGeneratedFunc(
@@ -184,12 +184,12 @@
                                    void* context, int ticket,
                                    const void* session_id,
                                    int session_id_length, bool succeeded);
-#if SB_API_VERSION >= 6
+#if SB_HAS(DRM_KEY_STATUSES)
   static void OnSessionKeyStatusesChangedFunc(
       SbDrmSystem wrapped_drm_system, void* context, const void* session_id,
       int session_id_size, int number_of_keys, const SbDrmKeyId* key_ids,
       const SbDrmKeyStatus* key_statuses);
-#endif  // SB_API_VERSION >= 6
+#endif  // SB_HAS(DRM_KEY_STATUSES)
 
   const SbDrmSystem wrapped_drm_system_;
   MessageLoop* const message_loop_;
diff --git a/src/cobalt/renderer/pipeline.cc b/src/cobalt/renderer/pipeline.cc
index 4fb1ee3..0708fed 100644
--- a/src/cobalt/renderer/pipeline.cc
+++ b/src/cobalt/renderer/pipeline.cc
@@ -509,6 +509,13 @@
 void Pipeline::ShutdownSubmissionQueue() {
   TRACE_EVENT0("cobalt::renderer", "Pipeline::ShutdownSubmissionQueue()");
   DCHECK(rasterizer_thread_checker_.CalledOnValidThread());
+
+  // Clear out our time fence data, especially |post_fence_submission_| which
+  // may refer to a render tree.
+  time_fence_ = base::nullopt;
+  post_fence_submission_ = base::nullopt;
+  post_fence_receipt_time_ = base::nullopt;
+
   // Stop and shutdown the raterizer timer.  If we won't have a submission
   // queue anymore, we won't be able to rasterize anymore.
   rasterize_timer_ = base::nullopt;
@@ -530,6 +537,9 @@
   TRACE_EVENT0("cobalt::renderer", "Pipeline::ShutdownRasterizerThread()");
   DCHECK(rasterizer_thread_checker_.CalledOnValidThread());
 
+  // Shutdown the FPS overlay which may reference render trees.
+  fps_overlay_ = base::nullopt;
+
   // Submit a black fullscreen rect node to clear the display before shutting
   // down.  This can be helpful if we quit while playing a video via
   // punch-through, which may result in unexpected images/colors appearing for
diff --git a/src/cobalt/renderer/renderer_module.cc b/src/cobalt/renderer/renderer_module.cc
index fcbe6ae..7434939 100644
--- a/src/cobalt/renderer/renderer_module.cc
+++ b/src/cobalt/renderer/renderer_module.cc
@@ -57,22 +57,6 @@
                                const Options& options)
     : system_window_(system_window), options_(options) {
   TRACE_EVENT0("cobalt::renderer", "RendererModule::RendererModule()");
-  Resume();
-}
-
-RendererModule::~RendererModule() {}
-
-void RendererModule::Suspend() {
-  TRACE_EVENT0("cobalt::renderer", "RendererModule::Suspend()");
-  pipeline_.reset();
-  graphics_context_.reset();
-  display_.reset();
-  graphics_system_.reset();
-}
-
-void RendererModule::Resume() {
-  TRACE_EVENT0("cobalt::renderer", "RendererModule::Resume()");
-
   // Load up the platform's default graphics system.
   {
     TRACE_EVENT0("cobalt::renderer", "backend::CreateDefaultGraphicsSystem()");
@@ -112,5 +96,9 @@
   }
 }
 
+RendererModule::~RendererModule() {
+  TRACE_EVENT0("cobalt::renderer", "RendererModule::~RendererModule");
+}
+
 }  // namespace renderer
 }  // namespace cobalt
diff --git a/src/cobalt/renderer/renderer_module.h b/src/cobalt/renderer/renderer_module.h
index 39b8406..cfef2bb 100644
--- a/src/cobalt/renderer/renderer_module.h
+++ b/src/cobalt/renderer/renderer_module.h
@@ -109,9 +109,6 @@
                           const Options& options);
   ~RendererModule();
 
-  void Suspend();
-  void Resume();
-
   renderer::Pipeline* pipeline() { return pipeline_.get(); }
   const scoped_refptr<renderer::backend::RenderTarget> render_target() {
     return display_->GetRenderTarget();
diff --git a/src/cobalt/site/docs/reference/starboard/gyp-configuration.md b/src/cobalt/site/docs/reference/starboard/gyp-configuration.md
index 529fd31..c7b3683 100644
--- a/src/cobalt/site/docs/reference/starboard/gyp-configuration.md
+++ b/src/cobalt/site/docs/reference/starboard/gyp-configuration.md
@@ -11,7 +11,6 @@
 | **`cobalt_egl_swap_interval`**<br><br> Cobalt will call eglSwapInterval() and specify this value before calling eglSwapBuffers() each frame.<br><br>The default value is `1`. |
 | **`cobalt_enable_jit`**<br><br> Disable JIT and run in interpreter-only mode by default. It can be set to 1 to run in JIT mode.  For SpiderMonkey in particular, we have found that disabling JIT often results in faster JavaScript execution and lower memory usage. Setting this to 1 on a platform or engine for which there is no JIT implementation is a no-op. Setting this to 0 on an engine for which there is a JIT implementation is a platform configuration error.<br><br>The default value is `0`. |
 | **`cobalt_enable_lib`**<br><br> Enables embedding Cobalt as a shared library within another app. This requires a 'lib' starboard implementation for the corresponding platform.<br><br>The default value is `'<(sb_enable_lib)'`. |
-| **`cobalt_encrypted_media_extension_enable_key_statuses_update`**<br><br> Set to 1 to enable MediaKeySession::keyStatuses and MediaKeySession::onkeystatuseschange support.  This requires that SB_API_VERSION is greater than or equal to SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION.<br><br>The default value is `1`. |
 | **`cobalt_fastbuild`**<br><br> Contains the current build configuration.<br><br>The default value is `0`. |
 | **`cobalt_font_package`**<br><br> Contains the current font package selection.  This can be used to trade font quality, coverage, and latency for different font package sizes. The font package can be one of the following options:<ul><li><code>expanded</code> - The largest package. It includes everything in the 'standard' package, along with 'bold' weight CJK. It is recommended that 'local_font_cache_size_in_bytes' be increased to 24MB when using this package to account for the extra memory required by bold CJK. This package is ~48.7MB.<li><code>standard</code> - The default package. It includes all sans-serif, serif, and FCC fonts, non-CJK fallback fonts in both 'normal' and 'bold' weights, and 'normal' weight CJK ('bold' weight CJK is synthesized from it). This package is ~29.4MB.<li><code>limited_with_jp</code> - A significantly smaller package than 'standard'. This package removes all but 'normal' and 'bold' weighted sans-serif and serif, removes the FCC fonts (which must be provided by the system or downloaded from the web), removes the 'bold' weighted non-CJK fallback fonts (the 'normal' weight is still included and is used to synthesize bold), and replaces standard CJK with low quality CJK. However, higher quality Japanese is still included. Because low quality CJK cannot synthesize bold, bold glyphs are unavailable in Chinese and Korean. This package is ~10.9MB.<li><code>limited</code> - A smaller package than 'limited_with_jp'. The two packages are identical with the exception that 'limited' does not include the higher quality Japanese font; instead it relies on low quality CJK for all CJK characters. Because low quality CJK cannot synthesize bold, bold glyphs are unavailable in Chinese, Japanese, and Korean. This package is ~7.7MB.<li><code>minimal</code> - The smallest possible font package. It only includes Roboto's Basic Latin characters. Everything else must be provided by the system or downloaded from the web. This package is ~16.4KB.</li></ul> NOTE: When bold is needed, but unavailable, it is typically synthesized, resulting in lower quality glyphs than those generated directly from a bold font. However, this does not occur with low quality CJK, which is not high enough quality to synthesize. Its glyphs always have a 'normal' weight.<br><br>The default value is `'standard'`. |
 | **`cobalt_font_package_override_fallback_emoji`**<br><br> Font package overrides can be used to modify the files included within the selected package. The following values are available: -1 -- The package value for the specified category is not overridden. 0 -- The package value is overridden and no fonts for the specified category are included. 1 -- The package value is overridden and fonts from the specified category with a weight of 'normal' and a style of 'normal' are included. 2 -- The package value is overridden and fonts from the specified category with a weight of either 'normal' or bold' and a style of 'normal' are included. 3 -- The package value is overridden and fonts from the specified category with a weight of either 'normal' or 'bold' and a style of either 'normal' or 'italic' are included. 4 -- The package value is overridden and all available fonts from the specified category are included. This may include additional weights beyond 'normal' and 'bold'. See content/fonts/README.md for details on the specific values used by each of the packages use for the various font categories.<br><br>The default value is `-1`. |
diff --git a/src/cobalt/tools/_env.py b/src/cobalt/tools/_env.py
new file mode 100644
index 0000000..6188bb7
--- /dev/null
+++ b/src/cobalt/tools/_env.py
@@ -0,0 +1,26 @@
+#
+# 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.
+#
+"""Ask the parent directory to load the project environment."""
+
+from imp import load_source
+from os import path
+import sys
+
+_ENV = path.abspath(path.join(path.dirname(__file__), path.pardir, '_env.py'))
+if not path.exists(_ENV):
+  print '%s: Can\'t find repo root.\nMissing parent: %s' % (__file__, _ENV)
+  sys.exit(1)
+load_source('', _ENV)
diff --git a/src/cobalt/tools/env_test.py b/src/cobalt/tools/env_test.py
new file mode 100755
index 0000000..b920b76
--- /dev/null
+++ b/src/cobalt/tools/env_test.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python
+#
+# 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.
+#
+"""Tests out _env."""
+
+import sys
+import unittest
+
+
+class EnvironmentTest(unittest.TestCase):
+
+  def testSysPath(self):
+    import _env  # pylint: disable=unused-variable,g-import-not-at-top
+    import cobalt.tools.paths  # pylint: disable=g-import-not-at-top
+    self.assertTrue(cobalt.tools.paths.COBALT_ROOT)
+
+  def testNoDupes(self):
+    import _env  # pylint: disable=unused-variable,g-import-not-at-top
+    visited = set()
+    deduped = [x for x in sys.path if not (x in visited or visited.add(x))]
+    self.assertItemsEqual(sys.path, deduped)
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/src/cobalt/tools/paths.py b/src/cobalt/tools/paths.py
index 9d3736e..531c053 100644
--- a/src/cobalt/tools/paths.py
+++ b/src/cobalt/tools/paths.py
@@ -17,6 +17,7 @@
 
 import os
 
+import _env  # pylint: disable=unused-import
 import cobalt
 import starboard.tools.paths
 
diff --git a/src/cobalt/tools/variable_rewrites.dict b/src/cobalt/tools/variable_rewrites.dict
index e8165b3..a11e3fe 100644
--- a/src/cobalt/tools/variable_rewrites.dict
+++ b/src/cobalt/tools/variable_rewrites.dict
@@ -23,7 +23,6 @@
     "cobalt_copy_debug_console",
     "cobalt_copy_test_data",
     "cobalt_enable_jit",
-    "cobalt_encrypted_media_extension_enable_key_statuses_update",
     "cobalt_fastbuild",
     "cobalt_media_source_2016",
     "custom_media_session_client",
diff --git a/src/cobalt/trace_event/benchmark_runner.cc b/src/cobalt/trace_event/benchmark_runner.cc
index 01e8607..53bbc8a 100644
--- a/src/cobalt/trace_event/benchmark_runner.cc
+++ b/src/cobalt/trace_event/benchmark_runner.cc
@@ -11,6 +11,7 @@
 // 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 <cstdio>
 #include <map>
 
@@ -25,19 +26,18 @@
 using cobalt::trace_event::Benchmark;
 using cobalt::trace_event::BenchmarkResultsMap;
 
-PRINTF_FORMAT(1, 2) void Output(const char* fmt, ...) {
-  va_list ap;
-  va_start(ap, fmt);
-
-  std::vfprintf(stdout, fmt, ap);
-
-  va_end(ap);
-
+void Output(const char* message) {
+#if defined(COBALT_BUILD_TYPE_GOLD)
+  // Logging is compiled out in gold, so write directly to stdout instead.
+  // There is no need to synchronize, as nothing else will be logging.
+  std::fputs(message, stdout);
   std::fflush(stdout);
+#else
+  RAW_LOG(INFO, message);
+#endif
 }
 
 void JsonPrint(const BenchmarkResultsMap& benchmarks) {
-  Output("---Benchmark Results Start---\n");
   scoped_ptr<base::DictionaryValue> compilation(new base::DictionaryValue);
   for (BenchmarkResultsMap::const_iterator benchmark = benchmarks.begin();
        benchmark != benchmarks.end(); ++benchmark) {
@@ -75,16 +75,13 @@
   std::string print_string;
   base::JSONWriter::WriteWithOptions(
       compilation.get(), base::JSONWriter::OPTIONS_PRETTY_PRINT, &print_string);
-  Output("%s", print_string.c_str());
 
-  Output("---Benchmark Results End---\n");
+  std::string result = "---Benchmark Results Start---\n" + print_string +
+                       "---Benchmark Results End---\n";
+  Output(result.c_str());
 }
 
 int RunnerMain(int argc, char** argv) {
-#if defined(NDEBUG)
-  // Get rid of all log output so we only see benchmark results.
-  logging::SetMinLogLevel(logging::LOG_FATAL);
-#endif
   BenchmarkResultsMap benchmarks =
       cobalt::trace_event::BenchmarkRegistrar::GetInstance()
           ->ExecuteBenchmarks();
diff --git a/src/starboard/CHANGELOG.md b/src/starboard/CHANGELOG.md
index 174eff4..0ff2bcf 100644
--- a/src/starboard/CHANGELOG.md
+++ b/src/starboard/CHANGELOG.md
@@ -19,6 +19,11 @@
 expose codec information, use a custom SbPlayerOutputModeSupportedWithUrl() to
 query player output mode support.
 
+### Add kSbEventTypeWindowSizeChanged
+
+An event indicating that an SbWindow's size has changed. The event data is
+SbEventWindowSizeChangedData, containing a SbWindow and SbWindowSize.
+
 ## Version 7
 
 ### `SbDecodeTargetInfoPlane` can specify color plane information
diff --git a/src/starboard/_env.py b/src/starboard/_env.py
new file mode 100644
index 0000000..6188bb7
--- /dev/null
+++ b/src/starboard/_env.py
@@ -0,0 +1,26 @@
+#
+# 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.
+#
+"""Ask the parent directory to load the project environment."""
+
+from imp import load_source
+from os import path
+import sys
+
+_ENV = path.abspath(path.join(path.dirname(__file__), path.pardir, '_env.py'))
+if not path.exists(_ENV):
+  print '%s: Can\'t find repo root.\nMissing parent: %s' % (__file__, _ENV)
+  sys.exit(1)
+load_source('', _ENV)
diff --git a/src/starboard/configuration.h b/src/starboard/configuration.h
index 7962f1f..df4949d 100644
--- a/src/starboard/configuration.h
+++ b/src/starboard/configuration.h
@@ -69,6 +69,7 @@
 //   #define SB_MY_EXPERIMENTAL_FEATURE_VERSION SB_EXPERIMENTAL_API_VERSION
 
 #define SB_PLAYER_WITH_URL_API_VERSION SB_EXPERIMENTAL_API_VERSION
+#define SB_WINDOW_SIZE_CHANGED_API_VERSION SB_EXPERIMENTAL_API_VERSION
 
 // --- Release Candidate Feature Defines -------------------------------------
 
@@ -538,6 +539,16 @@
 #error "SB_MEDIA_GPU_BUFFER_BUDGET is deprecated."
 #endif  // defined(SB_MEDIA_GPU_BUFFER_BUDGET)
 
+#if SB_API_VERSION >= 6
+#if defined(SB_HAS_DRM_KEY_STATUSES)
+#if !SB_HAS(DRM_KEY_STATUSES)
+#error "SB_HAS_DRM_KEY_STATUSES is required for Starboard 6 or later."
+#endif  // !SB_HAS(DRM_KEY_STATUSES)
+#else   // defined(SB_HAS_DRM_KEY_STATUSES)
+#define SB_HAS_DRM_KEY_STATUSES 1
+#endif  // defined(SB_HAS_DRM_KEY_STATUSES)
+#endif  // SB_API_VERSION >= 6
+
 #if SB_API_VERSION >= 5
 #if !defined(SB_HAS_SPEECH_RECOGNIZER)
 #error "Your platform must define SB_HAS_SPEECH_RECOGNIZER."
diff --git a/src/starboard/drm.h b/src/starboard/drm.h
index 4ce3df8..fbc6ea1 100644
--- a/src/starboard/drm.h
+++ b/src/starboard/drm.h
@@ -52,14 +52,14 @@
   int32_t encrypted_byte_count;
 } SbDrmSubSampleMapping;
 
-#if SB_API_VERSION >= 6
+#if SB_HAS(DRM_KEY_STATUSES)
 typedef struct SbDrmKeyId {
   // The ID of the license (or key) required to decrypt this sample. For
   // PlayReady, this is the license GUID in packed little-endian binary form.
   uint8_t identifier[16];
   int identifier_size;
 } SbDrmKeyId;
-#endif  // SB_API_VERSION >= 6
+#endif  // SB_HAS(DRM_KEY_STATUSES)
 
 // All the optional information needed per sample for encrypted samples.
 typedef struct SbDrmSampleInfo {
@@ -121,7 +121,7 @@
 // A callback for notifications that the status of one or more keys in a session
 // has been changed.  All keys of the session and their new status will be
 // passed along.  Any keys not in the list is considered as deleted.
-#if SB_API_VERSION >= 6
+#if SB_HAS(DRM_KEY_STATUSES)
 typedef void (*SbDrmSessionKeyStatusesChangedFunc)(
     SbDrmSystem drm_system,
     void* context,
@@ -130,7 +130,7 @@
     int number_of_keys,
     const SbDrmKeyId* key_ids,
     const SbDrmKeyStatus* key_statuses);
-#endif  // SB_API_VERSION >= 6
+#endif  // SB_HAS(DRM_KEY_STATUSES)
 
 // --- Constants -------------------------------------------------------------
 
@@ -171,7 +171,7 @@
 // SbDrmGenerateSessionUpdateRequest() is called.
 // |session_updated_callback|: A function that is called every time after
 // SbDrmUpdateSession() is called.
-#if SB_API_VERSION >= 6
+#if SB_HAS(DRM_KEY_STATUSES)
 
 SB_EXPORT SbDrmSystem SbDrmCreateSystem(
     const char* key_system,
@@ -180,7 +180,7 @@
     SbDrmSessionUpdatedFunc session_updated_callback,
     SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback);
 
-#else  // SB_API_VERSION >= 6
+#else  // SB_HAS(DRM_KEY_STATUSES)
 
 SB_EXPORT SbDrmSystem
 SbDrmCreateSystem(const char* key_system,
@@ -188,7 +188,7 @@
                   SbDrmSessionUpdateRequestFunc update_request_callback,
                   SbDrmSessionUpdatedFunc session_updated_callback);
 
-#endif  // SB_API_VERSION >= SB_DRM_KEY_STATUS_UPDATE_SUPPORT_API_VERSION
+#endif  // SB_HAS(DRM_KEY_STATUSES)
 
 // Asynchronously generates a session update request payload for
 // |initialization_data|, of |initialization_data_size|, in case sensitive
diff --git a/src/starboard/event.h b/src/starboard/event.h
index cd01304..6ff7e17 100644
--- a/src/starboard/event.h
+++ b/src/starboard/event.h
@@ -86,6 +86,7 @@
 #include "starboard/export.h"
 #include "starboard/time.h"
 #include "starboard/types.h"
+#include "starboard/window.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -212,6 +213,12 @@
   // to respond to or handle this event, it is only advisory.
   kSbEventTypeLowMemory,
 #endif  // SB_API_VERSION >= 6
+
+#if SB_API_VERSION >= SB_WINDOW_SIZE_CHANGED_API_VERSION
+  // The size or position of a SbWindow has changed. The data is
+  // SbEventWindowSizeChangedData.
+  kSbEventTypeWindowSizeChanged,
+#endif  // SB_API_VERSION >= SB_WINDOW_SIZE_CHANGED_API_VERSION
 } SbEventType;
 
 // Structure representing a Starboard event and its data.
@@ -242,6 +249,14 @@
   const char* link;
 } SbEventStartData;
 
+#if SB_API_VERSION >= SB_WINDOW_SIZE_CHANGED_API_VERSION
+// Event data for kSbEventTypeWindowSizeChanged events.
+typedef struct SbEventWindowSizeChangedData {
+  SbWindow window;
+  SbWindowSize size;
+} SbEventWindowSizeChangedData;
+#endif  // SB_API_VERSION >= SB_WINDOW_SIZE_CHANGED_API_VERSION
+
 #define kSbEventIdInvalid (SbEventId)0
 
 // Returns whether the given event handle is valid.
diff --git a/src/starboard/linux/shared/gyp_configuration.py b/src/starboard/linux/shared/gyp_configuration.py
index af21937..51662ad 100644
--- a/src/starboard/linux/shared/gyp_configuration.py
+++ b/src/starboard/linux/shared/gyp_configuration.py
@@ -87,3 +87,10 @@
         test_filter.TestFilter(
             'starboard_platform_tests', test_filter.FILTER_ALL)
     ]
+
+  def GetTestEnvVariables(self):
+    return {
+        'base_unittests': {'ASAN_OPTIONS': 'detect_leaks=0'},
+        'crypto_unittests': {'ASAN_OPTIONS': 'detect_leaks=0'},
+        'net_unittests': {'ASAN_OPTIONS': 'detect_leaks=0'}
+    }
diff --git a/src/starboard/linux/shared/launcher.py b/src/starboard/linux/shared/launcher.py
index d1f3872..2a7808d 100644
--- a/src/starboard/linux/shared/launcher.py
+++ b/src/starboard/linux/shared/launcher.py
@@ -14,25 +14,14 @@
 # limitations under the License.
 """Linux implementation of Starboard launcher abstraction."""
 
-import importlib
 import os
-import sys
-
-if "environment" in sys.modules:
-  environment = sys.modules["environment"]
-else:
-  env_path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir,
-                                          os.pardir, "tools"))
-  if env_path not in sys.path:
-    sys.path.append(env_path)
-  environment = importlib.import_module("environment")
-
-
 import signal
 import socket
 import subprocess
+import sys
 
-import starboard.tools.abstract_launcher as abstract_launcher
+import _env  # pylint: disable=unused-import
+from starboard.tools import abstract_launcher
 
 
 class Launcher(abstract_launcher.AbstractLauncher):
@@ -48,13 +37,19 @@
         self.device_id = socket.gethostbyname("localhost")
 
     self.executable = self.GetTargetPath()
+
+    env = os.environ.copy()
+    env.update(self.env_variables)
+    self.full_env = env
+
     self.pid = None
 
   def Run(self):
     """Runs launcher's executable."""
 
     proc = subprocess.Popen([self.executable] + self.target_command_line_params,
-                            stdout=self.output_file, stderr=self.output_file)
+                            stdout=self.output_file, stderr=self.output_file,
+                            env=self.full_env)
     self.pid = proc.pid
     proc.wait()
     return proc.returncode
@@ -64,5 +59,5 @@
     if self.pid:
       try:
         os.kill(self.pid, signal.SIGTERM)
-      except OSError as e:
+      except OSError:
         sys.stderr.write("Cannot kill launcher.  Process already closed.\n")
diff --git a/src/starboard/linux/x64x11/mock/gyp_configuration.gypi b/src/starboard/linux/x64x11/mock/gyp_configuration.gypi
index ed8ff0f..7a4599d 100644
--- a/src/starboard/linux/x64x11/mock/gyp_configuration.gypi
+++ b/src/starboard/linux/x64x11/mock/gyp_configuration.gypi
@@ -23,7 +23,6 @@
     'gl_type': 'none',
 
     'cobalt_media_source_2016': 1,
-    'cobalt_encrypted_media_extension_enable_key_statuses_update': 0,
 
     'platform_libraries': [
       '-lpthread',
diff --git a/src/starboard/raspi/shared/gyp_configuration.py b/src/starboard/raspi/shared/gyp_configuration.py
index 5608043..685fc8a 100644
--- a/src/starboard/raspi/shared/gyp_configuration.py
+++ b/src/starboard/raspi/shared/gyp_configuration.py
@@ -109,3 +109,10 @@
         test_filter.TestFilter('web_platform_tests', test_filter.FILTER_ALL)
 
     ]
+
+  def GetTestEnvVariables(self):
+    return {
+        'base_unittests': {'ASAN_OPTIONS': 'detect_leaks=0'},
+        'crypto_unittests': {'ASAN_OPTIONS': 'detect_leaks=0'},
+        'net_unittests': {'ASAN_OPTIONS': 'detect_leaks=0'}
+    }
diff --git a/src/starboard/shared/starboard/application.cc b/src/starboard/shared/starboard/application.cc
index e935128..1ee5a85 100644
--- a/src/starboard/shared/starboard/application.cc
+++ b/src/starboard/shared/starboard/application.cc
@@ -141,6 +141,13 @@
 #endif  // SB_API_VERSION >= 6
 }
 
+#if SB_API_VERSION >= SB_WINDOW_SIZE_CHANGED_API_VERSION
+void Application::WindowSizeChanged(void* context,
+                                    EventHandledCallback callback) {
+  Inject(new Event(kSbEventTypeWindowSizeChanged, context, callback));
+}
+#endif  // SB_API_VERSION >= SB_WINDOW_SIZE_CHANGED_API_VERSION
+
 SbEventId Application::Schedule(SbEventCallback callback,
                                 void* context,
                                 SbTimeMonotonic delay) {
diff --git a/src/starboard/shared/starboard/application.h b/src/starboard/shared/starboard/application.h
index df00670..915f15a 100644
--- a/src/starboard/shared/starboard/application.h
+++ b/src/starboard/shared/starboard/application.h
@@ -48,7 +48,7 @@
   typedef SbEventDataDestructor EventHandledCallback;
 
   // Signature for a function that will be called at the beginning of Teardown.
-  typedef void(*TeardownCallback)(void);
+  typedef void (*TeardownCallback)(void);
 
   // Enumeration of states that the application can be in.
   enum State {
@@ -212,6 +212,15 @@
   // Injects an event of type kSbEventTypeLowMemory to the application.
   void InjectLowMemoryEvent();
 
+#if SB_API_VERSION >= SB_WINDOW_SIZE_CHANGED_API_VERSION
+  // Inject a window size change event.
+  //
+  // |context|: A context value to pass to |callback| on event completion. Must
+  // not be NULL if callback is not NULL.
+  // |callback|: A function to call on event completion, from the main thread.
+  void WindowSizeChanged(void* context, EventHandledCallback callback);
+#endif  // SB_API_VERSION >= SB_WINDOW_SIZE_CHANGED_API_VERSION
+
   // Schedules an event into the event queue.  May be called from an external
   // thread.
   SbEventId Schedule(SbEventCallback callback,
@@ -282,9 +291,7 @@
   // |DispatchAndDelete| to maintain consistency of the application state.
   // Returns whether to keep servicing the event queue, i.e. false means to
   // abort the event queue.
-  virtual bool DispatchNextEvent() {
-    return DispatchAndDelete(GetNextEvent());
-  }
+  virtual bool DispatchNextEvent() { return DispatchAndDelete(GetNextEvent()); }
 
   // Injects an event into the queue, such that it will be returned from
   // GetNextEvent(), giving ownership of the event. NULL is valid, and will just
diff --git a/src/starboard/shared/uwp/application_uwp.cc b/src/starboard/shared/uwp/application_uwp.cc
index d9bfee3..5e3808a 100644
--- a/src/starboard/shared/uwp/application_uwp.cc
+++ b/src/starboard/shared/uwp/application_uwp.cc
@@ -14,12 +14,12 @@
 
 #include "starboard/shared/uwp/application_uwp.h"
 
+#include <D3D11.h>
 #include <WinSock2.h>
 #include <mfapi.h>
 #include <ppltasks.h>
 #include <windows.h>
 #include <windows.system.display.h>
-#include <D3D11.h>
 
 #include <memory>
 #include <string>
@@ -66,9 +66,12 @@
 using Windows::Foundation::Collections::IVectorView;
 using Windows::Foundation::EventHandler;
 using Windows::Foundation::IAsyncOperation;
+using Windows::Foundation::Metadata::ApiInformation;
 using Windows::Foundation::TimeSpan;
 using Windows::Foundation::TypedEventHandler;
 using Windows::Foundation::Uri;
+using Windows::Graphics::Display::Core::HdmiDisplayInformation;
+using Windows::Graphics::Display::DisplayInformation;
 using Windows::Globalization::Calendar;
 using Windows::Media::Protection::HdcpProtection;
 using Windows::Media::Protection::HdcpSession;
@@ -91,6 +94,7 @@
 using Windows::UI::Popups::MessageDialog;
 using Windows::UI::Popups::UICommand;
 using Windows::UI::Popups::UICommandInvokedHandler;
+using Windows::UI::ViewManagement::ApplicationView;
 
 namespace {
 
@@ -637,7 +641,7 @@
   return nullptr;
 }
 
-SbWindow ApplicationUwp::CreateWindowForUWP(const SbWindowOptions* options) {
+SbWindow ApplicationUwp::CreateWindowForUWP(const SbWindowOptions*) {
   // TODO: Determine why SB_DCHECK(IsCurrentThread()) fails in nplb, fix it,
   // and add back this check.
 
@@ -645,7 +649,33 @@
     return kSbWindowInvalid;
   }
 
-  window_ = new SbWindowPrivate(options);
+  // Get the logical resolution in pixels. See section "Bounds" in
+  // https://docs.microsoft.com/en-us/uwp/api/windows.ui.core.corewindow.
+  Windows::Foundation::Rect bounds_in_dips =
+      CoreWindow::GetForCurrentThread()->Bounds;
+  float dpi = DisplayInformation::GetForCurrentView()->LogicalDpi;
+  int width = static_cast<int>(bounds_in_dips.Width * dpi / 96.0f);
+  int height = static_cast<int>(bounds_in_dips.Height * dpi / 96.0f);
+
+  // For UWP on XB1, the logical resolution is always 1080p, regardless of the
+  // actual output resolution -- section "Scale factor and adaptive layout" in
+  // "https://docs.microsoft.com/en-us/windows/uwp/input-and-devices/
+  // designing-for-tv". However, if the swap chain uses a special surface
+  // format (R10G10B10A2), it can be passed to the output without scaling.
+  bool supports_hdmi_api = ApiInformation::IsApiContractPresent(
+      "Windows.Foundation.UniversalApiContract", 4);
+  bool is_fullscreen = ApplicationView::GetForCurrentView()->IsFullScreenMode;
+  if (supports_hdmi_api && is_fullscreen) {
+    // This reports the actual output resolution.
+    auto display_info = HdmiDisplayInformation::GetForCurrentView();
+    auto current_mode = display_info->GetCurrentDisplayMode();
+    width = static_cast<int>(current_mode->ResolutionWidthInRawPixels);
+    height = static_cast<int>(current_mode->ResolutionHeightInRawPixels);
+  }
+
+  SB_LOG(INFO) << "Window resolution is " << width << " x " << height;
+
+  window_ = new SbWindowPrivate(width, height);
   return window_;
 }
 
@@ -774,6 +804,7 @@
     case kSbKeyGamepadRightStickLeft:
     case kSbKeyGamepadRightStickUp: {
       key_location = kSbKeyLocationRight;
+      break;
     }
     default: {
       SB_NOTREACHED();
diff --git a/src/starboard/shared/uwp/window_internal.cc b/src/starboard/shared/uwp/window_internal.cc
index fd79b8d..1123c59 100644
--- a/src/starboard/shared/uwp/window_internal.cc
+++ b/src/starboard/shared/uwp/window_internal.cc
@@ -17,16 +17,21 @@
 
 #include "starboard/shared/uwp/application_uwp.h"
 #include "starboard/shared/uwp/window_internal.h"
+#include "third_party/angle/include/angle_windowsstore.h"
 
 using Windows::UI::Core::CoreWindow;
 
-// TODO: Make sure the width and height here behave well given that we want
-// 1080 video, but perhaps 4k UI where applicable.
-SbWindowPrivate::SbWindowPrivate(const SbWindowOptions* options)
-    : width(options->size.width),
-      height(options->size.height) {
-  egl_native_window_ = reinterpret_cast<EGLNativeWindowType>(
+SbWindowPrivate::SbWindowPrivate(int width, int height)
+    : width(width),
+      height(height) {
+  angle_property_set = ref new Windows::Foundation::Collections::PropertySet();
+  angle_property_set->Insert(
+      ref new Platform::String(EGLNativeWindowTypeProperty),
       starboard::shared::uwp::ApplicationUwp::Get()->GetCoreWindow().Get());
+  angle_property_set->Insert(
+      ref new Platform::String(EGLRenderSurfaceSizeProperty),
+      Windows::Foundation::PropertyValue::CreateSize(
+          Windows::Foundation::Size(width, height)));
 }
 
 SbWindowPrivate::~SbWindowPrivate() {}
diff --git a/src/starboard/shared/uwp/window_internal.h b/src/starboard/shared/uwp/window_internal.h
index 583774b..19cbadb 100644
--- a/src/starboard/shared/uwp/window_internal.h
+++ b/src/starboard/shared/uwp/window_internal.h
@@ -22,10 +22,12 @@
 #include "starboard/window.h"
 
 struct SbWindowPrivate {
-  explicit SbWindowPrivate(const SbWindowOptions* options);
+  SbWindowPrivate(int width, int height);
   ~SbWindowPrivate();
 
-  EGLNativeWindowType egl_native_window() const { return egl_native_window_; }
+  EGLNativeWindowType egl_native_window() const {
+    return reinterpret_cast<EGLNativeWindowType>(angle_property_set);
+  }
 
   // The width of this window.
   int width;
@@ -34,7 +36,7 @@
   int height;
 
  private:
-  EGLNativeWindowType egl_native_window_;
+  Windows::Foundation::Collections::PropertySet^ angle_property_set;
 };
 
 #endif  // STARBOARD_SHARED_UWP_WINDOW_INTERNAL_H_
diff --git a/src/starboard/shared/win32/window_internal.cc b/src/starboard/shared/win32/window_internal.cc
index 94d63e1..cab001e 100644
--- a/src/starboard/shared/win32/window_internal.cc
+++ b/src/starboard/shared/win32/window_internal.cc
@@ -20,6 +20,13 @@
                                  HWND window_handle)
     : width(options->size.width),
       height(options->size.height),
-      window_handle_(window_handle) {}
+      window_handle_(window_handle) {
+  RECT window_client_rect;
+
+  if (GetClientRect(window_handle_, &window_client_rect)) {
+    width = window_client_rect.right - window_client_rect.left;
+    height = window_client_rect.bottom - window_client_rect.top;
+  }
+}
 
 SbWindowPrivate::~SbWindowPrivate() {}
diff --git a/src/starboard/shared/x11/application_x11.cc b/src/starboard/shared/x11/application_x11.cc
index d9d156b..54b6cc9 100644
--- a/src/starboard/shared/x11/application_x11.cc
+++ b/src/starboard/shared/x11/application_x11.cc
@@ -1238,8 +1238,27 @@
       return NULL;
     }
     case ConfigureNotify: {
-      // Ignore window size, position, border, and stacking order events.
+#if SB_API_VERSION >= SB_WINDOW_SIZE_CHANGED_API_VERSION
+      XConfigureEvent* x_configure_event =
+          reinterpret_cast<XConfigureEvent*>(x_event);
+      SbEventWindowSizeChangedData* data = new SbEventWindowSizeChangedData();
+      data->window = FindWindow(x_configure_event->window);
+      bool unhandled_resize = data->window->unhandled_resize;
+      data->window->BeginComposite();
+      unhandled_resize |= data->window->unhandled_resize;
+      if (!unhandled_resize) {
+        // Ignore move events.
+        return NULL;
+      }
+      // Get the current window size.
+      SbWindowSize window_size;
+      SbWindowGetSize(data->window, &window_size);
+      data->size = window_size;
+      data->window->unhandled_resize = false;
+      return new Event(kSbEventTypeWindowSizeChanged, data, NULL);
+#else  // SB_API_VERSION >= SB_WINDOW_SIZE_CHANGED_API_VERSION
       return NULL;
+#endif  // SB_API_VERSION >= SB_WINDOW_SIZE_CHANGED_API_VERSION
     }
     case SelectionNotify: {
       XSelectionEvent* x_selection_event =
diff --git a/src/starboard/shared/x11/window_internal.cc b/src/starboard/shared/x11/window_internal.cc
index 9f4436d..3199f35 100644
--- a/src/starboard/shared/x11/window_internal.cc
+++ b/src/starboard/shared/x11/window_internal.cc
@@ -54,7 +54,8 @@
       video_picture(None),
       gl_window(None),
       gl_picture(None),
-      display(display) {
+      display(display),
+      unhandled_resize(false) {
   // Request a 32-bit depth visual for our Window.
   XVisualInfo x_visual_info = {0};
   XMatchVisualInfo(display, DefaultScreen(display), 32, TrueColor,
@@ -148,6 +149,7 @@
       window_attributes.height != height) {
     width = window_attributes.width;
     height = window_attributes.height;
+    unhandled_resize = true;
     if (composition_pixmap != None) {
       XFreePixmap(display, composition_pixmap);
       composition_pixmap = None;
diff --git a/src/starboard/shared/x11/window_internal.h b/src/starboard/shared/x11/window_internal.h
index 7c97758..27b59c5 100644
--- a/src/starboard/shared/x11/window_internal.h
+++ b/src/starboard/shared/x11/window_internal.h
@@ -88,6 +88,10 @@
 
   // The height of this window.
   int height;
+
+  // If there has been a resize that has not been handled by the application
+  // yet.
+  bool unhandled_resize;
 };
 
 #endif  // STARBOARD_SHARED_X11_WINDOW_INTERNAL_H_
diff --git a/src/starboard/tools/_env.py b/src/starboard/tools/_env.py
new file mode 100644
index 0000000..6188bb7
--- /dev/null
+++ b/src/starboard/tools/_env.py
@@ -0,0 +1,26 @@
+#
+# 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.
+#
+"""Ask the parent directory to load the project environment."""
+
+from imp import load_source
+from os import path
+import sys
+
+_ENV = path.abspath(path.join(path.dirname(__file__), path.pardir, '_env.py'))
+if not path.exists(_ENV):
+  print '%s: Can\'t find repo root.\nMissing parent: %s' % (__file__, _ENV)
+  sys.exit(1)
+load_source('', _ENV)
diff --git a/src/starboard/tools/abstract_launcher.py b/src/starboard/tools/abstract_launcher.py
index a7e72df..6d83662 100644
--- a/src/starboard/tools/abstract_launcher.py
+++ b/src/starboard/tools/abstract_launcher.py
@@ -12,23 +12,15 @@
 # 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.
+#
 """Abstraction for running Cobalt development tools."""
 
+import abc
 import importlib
 import os
 import sys
 
-if "environment" in sys.modules:
-  environment = sys.modules["environment"]
-else:
-  env_path = os.path.abspath(os.path.dirname(__file__))
-  if env_path not in sys.path:
-    sys.path.append(env_path)
-  environment = importlib.import_module("environment")
-
-
-import abc
-
+import _env  # pylint: disable=unused-import
 import starboard.tools.platform as platform_module
 
 
@@ -91,7 +83,7 @@
 
 def LauncherFactory(platform, target_name, config, device_id=None,
                     target_params=None, output_file=None,
-                    out_directory=None):
+                    out_directory=None, env_variables=None):
   """Creates the proper launcher based upon command line args.
 
   Args:
@@ -104,6 +96,7 @@
       None, sys.stdout is used.
     out_directory: Directory containing the executable target. If None is
       provided, the path to the directory is dynamically generated.
+    env_variables:  Environment variables for the executable
 
   Returns:
     An instance of the concrete launcher class for the desired platform.
@@ -127,13 +120,15 @@
       bridge_module = importlib.import_module("app_launcher_bridge")
       return bridge_module.LauncherAdapter(
           platform, target_name, config, device_id, target_params=target_params,
-          output_file=output_file, out_directory=out_directory)
+          output_file=output_file, out_directory=out_directory,
+          env_variables=env_variables)
     else:
       raise RuntimeError("No launcher implemented for the given platform.")
   else:
     return launcher_module.Launcher(
         platform, target_name, config, device_id, target_params=target_params,
-        output_file=output_file, out_directory=out_directory)
+        output_file=output_file, out_directory=out_directory,
+        env_variables=env_variables)
 
 
 class AbstractLauncher(object):
@@ -157,10 +152,15 @@
     self.output_file = output_file
 
     target_command_line_params = kwargs.get("target_params", None)
-    if not target_command_line_params:
+    if target_command_line_params is None:
       target_command_line_params = []
     self.target_command_line_params = target_command_line_params
 
+    env_variables = kwargs.get("env_variables", None)
+    if env_variables is None:
+      env_variables = {}
+    self.env_variables = env_variables
+
     # Launchers that need different startup timeout times should reassign
     # this variable during initialization.
     self.startup_timeout_seconds = 2 * 60
@@ -214,4 +214,3 @@
       out_directory = DynamicallyBuildOutDirectory(self.platform, self.config)
 
     return os.path.abspath(os.path.join(out_directory, self.target_name))
-
diff --git a/src/starboard/tools/build.py b/src/starboard/tools/build.py
index 87fe843..cebbbea 100644
--- a/src/starboard/tools/build.py
+++ b/src/starboard/tools/build.py
@@ -15,12 +15,85 @@
 #
 """Build related constants and helper functions."""
 
+import logging
+import os
 
-class Config(object):
-  """Enumeration for valid configuration types."""
-  DEBUG = 'debug'
-  DEVEL = 'devel'
-  QA = 'qa'
-  GOLD = 'gold'
+import _env  # pylint: disable=unused-import
+import starboard.tools.config
+import starboard.tools.platform
 
-  VALID_CONFIGS = (DEBUG, DEVEL, QA, GOLD)
+
+# TODO: Rectify consistency of "Build Type" / "Build Config" naming.
+_BUILD_CONFIG_KEY = 'BUILD_TYPE'
+_BUILD_PLATFORM_KEY = 'BUILD_PLATFORM'
+_BUILD_CONFIGURATION_KEY = 'BUILD_CONFIGURATION'
+
+
+def _CheckConfig(key, raw_value, value):
+  if starboard.tools.config.IsValid(value):
+    return True
+
+  logging.warning("Environment variable '%s' is '%s', which is invalid.",
+                  key, raw_value)
+  logging.warning('Valid build configurations: %s',
+                  starboard.tools.config.GetAll())
+  return False
+
+
+def _CheckPlatform(key, raw_value, value):
+  if starboard.tools.platform.IsValid(value):
+    return True
+
+  logging.warning("Environment variable '%s' is '%s', which is invalid.",
+                  key, raw_value)
+  logging.warning('Valid platforms: %s', starboard.tools.platform.GetAllNames())
+  return False
+
+
+class Config(starboard.tools.config.Config):
+  pass
+
+
+def GetDefaultConfigAndPlatform():
+  """Returns a (config, platform) tuple based on the environment."""
+  default_config = None
+  default_platform = None
+  if _BUILD_CONFIG_KEY in os.environ:
+    raw_config = os.environ[_BUILD_CONFIG_KEY]
+    config = raw_config.lower()
+    if _CheckConfig(_BUILD_CONFIG_KEY, raw_config, config):
+      default_config = config
+
+  if _BUILD_PLATFORM_KEY in os.environ:
+    raw_platform = os.environ[_BUILD_PLATFORM_KEY]
+    platform = raw_platform.lower()
+    if _CheckPlatform(_BUILD_PLATFORM_KEY, raw_platform, platform):
+      default_platform = platform
+
+  if default_config and default_platform:
+    return default_config, default_platform
+
+  # Only check BUILD_CONFIGURATION if either platform or config is not
+  # provided individually, or at least one is invalid.
+  if _BUILD_CONFIGURATION_KEY not in os.environ:
+    return default_config, default_platform
+
+  raw_configuration = os.environ[_BUILD_CONFIGURATION_KEY]
+  build_configuration = raw_configuration.lower()
+  if '_' not in build_configuration:
+    logging.warning("Expected a '_' in '%s' and did not find one.  "
+                    "'%s' must be of the form <platform>_<config>.",
+                    _BUILD_CONFIGURATION_KEY, _BUILD_CONFIGURATION_KEY)
+    return default_config, default_platform
+
+  platform, config = build_configuration.split('_', 1)
+  if not default_config:
+    if _CheckConfig(_BUILD_CONFIGURATION_KEY, raw_configuration, config):
+      default_config = config
+
+  if not default_platform:
+    if _CheckPlatform(_BUILD_CONFIGURATION_KEY, raw_configuration,
+                      platform):
+      default_platform = platform
+
+  return default_config, default_platform
diff --git a/src/starboard/tools/command_line.py b/src/starboard/tools/command_line.py
index 0c86290..3a08850 100644
--- a/src/starboard/tools/command_line.py
+++ b/src/starboard/tools/command_line.py
@@ -21,37 +21,48 @@
 
 import argparse
 
+import _env  # pylint: disable=unused-import
+from starboard.tools import build
+import starboard.tools.config
+import starboard.tools.platform
+
 
 def CreateParser():
   """Returns an argparse.ArgumentParser object set up for Starboard tools."""
   arg_parser = argparse.ArgumentParser(
-      description="Runs application/tool executables.")
+      description='Runs application/tool executables.')
+  default_config, default_platform = build.GetDefaultConfigAndPlatform()
   arg_parser.add_argument(
-      "-p",
-      "--platform",
+      '-p',
+      '--platform',
+      choices=starboard.tools.platform.GetAllNames(),
+      default=default_platform,
+      required=not default_platform,
       help="Device platform, eg 'linux-x64x11'.")
   arg_parser.add_argument(
-      "-c",
-      "--config",
-      choices=["debug", "devel", "qa", "gold"],
+      '-c',
+      '--config',
+      choices=starboard.tools.config.GetAll(),
+      default=default_config,
+      required=not default_config,
       help="Build config (eg, 'qa' or 'devel')")
   arg_parser.add_argument(
-      "-d",
-      "--device_id",
-      help="Devkit or IP address for the target device.")
+      '-d',
+      '--device_id',
+      help='Devkit or IP address for the target device.')
   arg_parser.add_argument(
-      "-t",
-      "--target_name",
-      help="Name of executable target.")
+      '-t',
+      '--target_name',
+      help='Name of executable target.')
   arg_parser.add_argument(
-      "--target_params",
-      help="Command line arguments to pass to the executable."
-           " Because different executables could have differing command"
-           " line syntax, list all arguments exactly as you would to the"
-           " executable between a set of double quotation marks.")
+      '--target_params',
+      help='Command line arguments to pass to the executable.'
+           ' Because different executables could have differing command'
+           ' line syntax, list all arguments exactly as you would to the'
+           ' executable between a set of double quotation marks.')
   arg_parser.add_argument(
-      "-o",
-      "--out_directory",
-      help="Directory containing tool binaries or their components."
-           " Automatically derived if absent.")
+      '-o',
+      '--out_directory',
+      help='Directory containing tool binaries or their components.'
+           ' Automatically derived if absent.')
   return arg_parser
diff --git a/src/starboard/tools/command_line_test.py b/src/starboard/tools/command_line_test.py
new file mode 100755
index 0000000..b307ec4
--- /dev/null
+++ b/src/starboard/tools/command_line_test.py
@@ -0,0 +1,127 @@
+#!/usr/bin/env python
+#
+# 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.
+#
+"""Tests the command_line module."""
+
+import os
+import unittest
+
+import _env  # pylint: disable=unused-import
+from starboard.tools import command_line
+import starboard.tools.config
+import starboard.tools.platform
+
+
+_A_CONFIG = starboard.tools.config.GetAll()[0]
+_A_PLATFORM = starboard.tools.platform.GetAllNames()[0]
+
+
+def _RestoreMapping(target, source):
+  target.clear()
+  for key, value in source.iteritems():
+    target[key] = value
+
+
+def _ClearEnviron():
+  del os.environ['BUILD_CONFIGURATION']
+  del os.environ['BUILD_TYPE']
+  del os.environ['BUILD_PLATFORM']
+
+
+def _SetEnvironConfig(config):
+  os.environ['BUILD_TYPE'] = config
+
+
+def _SetEnvironPlatform(platform):
+  os.environ['BUILD_PLATFORM'] = platform
+
+
+def _SetEnvironBuildConfiguration(config, platform):
+  os.environ['BUILD_CONFIGURATION'] = '%s_%s' % (platform, config)
+
+
+def _SetEnviron(config, platform):
+  _SetEnvironConfig(config)
+  _SetEnvironPlatform(platform)
+  _SetEnvironBuildConfiguration(config, platform)
+
+
+class CommandLineTest(unittest.TestCase):
+
+  def setUp(self):
+    self.environ = os.environ.copy()
+    _ClearEnviron()
+
+  def tearDown(self):
+    _RestoreMapping(os.environ, self.environ)
+
+  def testNoEnvironmentRainyDayNoArgs(self):
+    parser = command_line.CreateParser()
+    with self.assertRaises(SystemExit):
+      parser.parse_args([])
+
+  def testNoEnvironmentRainyDayNoConfig(self):
+    parser = command_line.CreateParser()
+    with self.assertRaises(SystemExit):
+      parser.parse_args(['-p', _A_PLATFORM])
+
+  def testNoEnvironmentRainyDayNoPlatform(self):
+    parser = command_line.CreateParser()
+    with self.assertRaises(SystemExit):
+      parser.parse_args(['-c', _A_CONFIG])
+
+  def testNoEnvironmentSunnyDay(self):
+    parser = command_line.CreateParser()
+    args = parser.parse_args(['-c', _A_CONFIG, '-p', _A_PLATFORM])
+    self.assertEqual(_A_CONFIG, args.config)
+    self.assertEqual(_A_PLATFORM, args.platform)
+
+  def testDefaultsSunnyDay(self):
+    _SetEnviron(_A_CONFIG, _A_PLATFORM)
+    parser = command_line.CreateParser()
+    args = parser.parse_args([])
+    self.assertEqual(_A_CONFIG, args.config)
+    self.assertEqual(_A_PLATFORM, args.platform)
+
+  def testDefaultsRainyDayBadConfig(self):
+    _SetEnviron('badconfig', _A_PLATFORM)
+    parser = command_line.CreateParser()
+    with self.assertRaises(SystemExit):
+      parser.parse_args([])
+
+  def testDefaultsRainyDayBadPlatform(self):
+    _SetEnviron(_A_CONFIG, 'badplatform')
+    parser = command_line.CreateParser()
+    with self.assertRaises(SystemExit):
+      parser.parse_args([])
+
+  def testBadEnvironmentSunnyDay(self):
+    _SetEnviron('badconfig', 'badplatform')
+    parser = command_line.CreateParser()
+    args = parser.parse_args(['-c', _A_CONFIG, '-p', _A_PLATFORM])
+    self.assertEqual(_A_CONFIG, args.config)
+    self.assertEqual(_A_PLATFORM, args.platform)
+
+  def testInconsistentEnvironmentSunnyDay(self):
+    _SetEnvironBuildConfiguration(_A_CONFIG, _A_PLATFORM)
+    parser = command_line.CreateParser()
+    args = parser.parse_args([])
+    self.assertEqual(_A_CONFIG, args.config)
+    self.assertEqual(_A_PLATFORM, args.platform)
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/src/starboard/tools/config.py b/src/starboard/tools/config.py
new file mode 100644
index 0000000..3c8e454
--- /dev/null
+++ b/src/starboard/tools/config.py
@@ -0,0 +1,36 @@
+#
+# 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.
+#
+"""Canonical source of valid build config types."""
+
+
+class Config(object):
+  """Enumeration of valid build configs."""
+  DEBUG = 'debug'
+  DEVEL = 'devel'
+  GOLD = 'gold'
+  QA = 'qa'
+
+  VALID_CONFIGS = [DEBUG, DEVEL, QA, GOLD]
+
+
+def GetAll():
+  """Returns a list of all valid build configs."""
+  return Config.VALID_CONFIGS
+
+
+def IsValid(name):
+  """Returns whether |name| is a valid build config."""
+  return name in GetAll()
diff --git a/src/starboard/tools/config_test.py b/src/starboard/tools/config_test.py
new file mode 100755
index 0000000..4647714
--- /dev/null
+++ b/src/starboard/tools/config_test.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+#
+# 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.
+#
+"""Tests the config module."""
+
+import unittest
+
+import config as config_module
+
+
+class ConfigTest(unittest.TestCase):
+
+  def testGetAll(self):
+    configs = config_module.GetAll()
+    self.assertNotEqual(0, len(configs))
+
+  def testIsValid(self):
+    configs = config_module.GetAll()
+    for config in configs:
+      self.assertTrue(config_module.IsValid(config))
+      self.assertTrue(config_module.IsValid(config.lower()))
+      self.assertFalse(config_module.IsValid(config.upper()))
+    self.assertFalse(config_module.IsValid('invalidconfig'))
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/src/starboard/tools/example/_env.py b/src/starboard/tools/example/_env.py
new file mode 100644
index 0000000..6188bb7
--- /dev/null
+++ b/src/starboard/tools/example/_env.py
@@ -0,0 +1,26 @@
+#
+# 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.
+#
+"""Ask the parent directory to load the project environment."""
+
+from imp import load_source
+from os import path
+import sys
+
+_ENV = path.abspath(path.join(path.dirname(__file__), path.pardir, '_env.py'))
+if not path.exists(_ENV):
+  print '%s: Can\'t find repo root.\nMissing parent: %s' % (__file__, _ENV)
+  sys.exit(1)
+load_source('', _ENV)
diff --git a/src/starboard/tools/example/app_launcher_client.py b/src/starboard/tools/example/app_launcher_client.py
index 8df7546..fb7d3ba 100644
--- a/src/starboard/tools/example/app_launcher_client.py
+++ b/src/starboard/tools/example/app_launcher_client.py
@@ -15,19 +15,9 @@
 # limitations under the License.
 """Client to launch executables via the new launcher logic."""
 
-import importlib
-import os
 import sys
 
-if "environment" in sys.modules:
-  environment = sys.modules["environment"]
-else:
-  env_path = os.path.abspath(
-      os.path.join(os.path.dirname(__file__), os.pardir))
-  if env_path not in sys.path:
-    sys.path.append(env_path)
-  environment = importlib.import_module("environment")
-
+import _env  # pylint: disable=unused-import
 from starboard.tools import abstract_launcher
 from starboard.tools import command_line
 
diff --git a/src/starboard/tools/platform.py b/src/starboard/tools/platform.py
index 76e3438..ca1723d 100644
--- a/src/starboard/tools/platform.py
+++ b/src/starboard/tools/platform.py
@@ -16,21 +16,12 @@
 """Functionality to enumerate and represent starboard ports."""
 
 import importlib
-import os
-import sys
-
-if "environment" in sys.modules:
-  environment = sys.modules["environment"]
-else:
-  env_path = os.path.abspath(os.path.dirname(__file__))
-  if env_path not in sys.path:
-    sys.path.append(env_path)
-  environment = importlib.import_module("environment")
-
-
 import logging
+import os
 import re
 
+import _env  # pylint: disable=unused-import
+from starboard.tools import environment
 
 # The list of files that must be present in a directory to allow it to be
 # considered a valid port.
@@ -98,6 +89,19 @@
   return _GetAllPlatforms(port_root_paths)
 
 
+def GetAllNames():
+  """Gets a list of all valid Starboard platform names.
+
+  Returns:
+    List of valid platform names.
+  """
+  return sorted(GetAllPorts().keys())
+
+
+def IsValid(platform):
+  return platform in GetAllNames()
+
+
 class PlatformInfo(object):
   """Information about a specific starboard port."""
 
@@ -126,9 +130,9 @@
 
     Load the python module named |module_name| relative to |root_module|.
     Args:
-      module: Name of a python module to load. If None, load the platform
-          directory as a python module.
       root_module: An already-loaded module
+      module_name: Name of a python module to load. If None, load the platform
+          directory as a python module.
     Returns:
       A module loaded with importlib.import_module
     Throws:
diff --git a/src/starboard/tools/testing/README.md b/src/starboard/tools/testing/README.md
index a191e91..903d19b 100644
--- a/src/starboard/tools/testing/README.md
+++ b/src/starboard/tools/testing/README.md
@@ -79,4 +79,20 @@
 "test_filter.FILTER_ALL" as the test name.
 
 To disable unit testing for all targets and all configurations, return a list
-containing "test_filter.DISABLE_TESTING".
\ No newline at end of file
+containing "test_filter.DISABLE_TESTING".
+
+## Environment Variables
+
+If a platform requires extra environment variables in order to run tests
+properly, implement a method called "GetTestEnvVariables()" in the same
+PlatformConfig mentioned above.  There is an example of this method in
+"../../linux/x64x11/shared/gyp_configuration.py" as well.  The method should
+return a dictionary that maps test binary names to dictionaries of
+environment variables that they need.  Example:
+
+  def GetTestEnvVariables(self):
+    return {
+        'base_unittests': {'ASAN_OPTIONS': 'detect_leaks=0'},
+        'crypto_unittests': {'ASAN_OPTIONS': 'detect_leaks=0'},
+        'net_unittests': {'ASAN_OPTIONS': 'detect_leaks=0'}
+    }
\ No newline at end of file
diff --git a/src/starboard/tools/testing/_env.py b/src/starboard/tools/testing/_env.py
new file mode 100644
index 0000000..6188bb7
--- /dev/null
+++ b/src/starboard/tools/testing/_env.py
@@ -0,0 +1,26 @@
+#
+# 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.
+#
+"""Ask the parent directory to load the project environment."""
+
+from imp import load_source
+from os import path
+import sys
+
+_ENV = path.abspath(path.join(path.dirname(__file__), path.pardir, '_env.py'))
+if not path.exists(_ENV):
+  print '%s: Can\'t find repo root.\nMissing parent: %s' % (__file__, _ENV)
+  sys.exit(1)
+load_source('', _ENV)
diff --git a/src/starboard/tools/testing/test_runner.py b/src/starboard/tools/testing/test_runner.py
index c2a72c4..b931fae 100644
--- a/src/starboard/tools/testing/test_runner.py
+++ b/src/starboard/tools/testing/test_runner.py
@@ -13,31 +13,24 @@
 # 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.
+#
 """Cross-platform unit test runner."""
 
-import importlib
-import os
-import sys
-
-if "environment" in sys.modules:
-  environment = sys.modules["environment"]
-else:
-  env_path = os.path.abspath(os.path.join(os.path.dirname(__file__),
-                                          os.pardir))
-  if env_path not in sys.path:
-    sys.path.append(env_path)
-  environment = importlib.import_module("environment")
-
 import cStringIO
+import os
 import re
 import signal
 import subprocess
+import sys
 import threading
 import traceback
 
-import starboard.tools.abstract_launcher as abstract_launcher
-import starboard.tools.command_line as command_line
-import starboard.tools.testing.test_filter as test_filter
+import _env  # pylint: disable=unused-import
+from starboard.tools import abstract_launcher
+from starboard.tools import command_line
+from starboard.tools import environment
+from starboard.tools.testing import test_filter
+
 
 _TOTAL_TESTS_REGEX = (r"\[==========\] (.*) tests? from .*"
                       r"test cases? ran. \(.* ms total\)")
@@ -98,20 +91,21 @@
     self.device_id = device_id
     self.target_params = target_params
     self.out_directory = out_directory
+    self._platform_config = abstract_launcher.GetGypModuleForPlatform(
+        platform).CreatePlatformConfig()
 
     # If a particular test binary has been provided, configure only that one.
     if single_target:
-      self.test_targets = self._GetSingleTestTarget(platform, config,
-                                                    single_target)
+      self.test_targets = self._GetSingleTestTarget(single_target)
     else:
-      self.test_targets = self._GetTestTargets(platform, config)
+      self.test_targets = self._GetTestTargets()
 
-  def _GetSingleTestTarget(self, platform, config, single_target):
+    self.test_env_vars = self._GetAllTestEnvVariables()
+
+  def _GetSingleTestTarget(self, single_target):
     """Sets up a single test target for a given platform and configuration.
 
     Args:
-      platform: The platform on which the tests are run, ex. "linux-x64x11"
-      config:  The configuration of the binary, ex. "devel" or "qa".
       single_target:  The name of a test target to run.
 
     Returns:
@@ -122,8 +116,7 @@
       RuntimeError:  The specified test binary has been disabled for the given
         platform and configuration.
     """
-    gyp_module = abstract_launcher.GetGypModuleForPlatform(platform)
-    platform_filters = gyp_module.CreatePlatformConfig().GetTestFilters()
+    platform_filters = self._platform_config.GetTestFilters()
 
     final_targets = {}
     final_targets[single_target] = []
@@ -133,7 +126,7 @@
         return {}
       if platform_filter.target_name == single_target:
         # Only filter the tests specifying our config or all configs.
-        if platform_filter.config == config or not platform_filter.config:
+        if platform_filter.config == self.config or not platform_filter.config:
           if platform_filter.test_name == test_filter.FILTER_ALL:
             # If the provided target name has been filtered,
             # nothing will be run.
@@ -147,20 +140,15 @@
 
     return final_targets
 
-  def _GetTestTargets(self, platform, config):
+  def _GetTestTargets(self):
     """Collects all test targets for a given platform and configuration.
 
-    Args:
-      platform: The platform on which the tests are run, ex. "linux-x64x11".
-      config:  The configuration of the binary, ex. "devel" or "qa".
-
     Returns:
       A mapping from names of test binaries to lists of filters for
         each test binary.  If a test binary has no filters, its list is
         empty.
     """
-    gyp_module = abstract_launcher.GetGypModuleForPlatform(platform)
-    platform_filters = gyp_module.CreatePlatformConfig().GetTestFilters()
+    platform_filters = self._platform_config.GetTestFilters()
 
     final_targets = {}
 
@@ -172,7 +160,7 @@
       if platform_filter == test_filter.DISABLE_TESTING:
         return {}
       # Only filter the tests specifying our config or all configs.
-      if platform_filter.config == config or not platform_filter.config:
+      if platform_filter.config == self.config or not platform_filter.config:
         if platform_filter.test_name == test_filter.FILTER_ALL:
           # Filter the whole test binary
           del final_targets[platform_filter.target_name]
@@ -182,6 +170,10 @@
 
     return final_targets
 
+  def _GetAllTestEnvVariables(self):
+    """Gets all environment variables used for tests on the given platform."""
+    return self._platform_config.GetTestEnvVariables()
+
   def _BuildSystemInit(self):
     """Runs GYP on the target platform/config."""
     subprocess.check_call([os.path.abspath(os.path.join(
@@ -226,6 +218,9 @@
       A tuple containing tests results (See "_CollectTestResults()").
     """
 
+    # Get the environment variables for the test target
+    env = self.test_env_vars.get(target_name, {})
+
     # Set up a pipe for processing test output
     read_fd, write_fd = os.pipe()
     read_pipe = os.fdopen(read_fd, "r")
@@ -239,12 +234,14 @@
     launcher = abstract_launcher.LauncherFactory(
         self.platform, target_name, self.config,
         device_id=self.device_id, target_params=self.target_params,
-        output_file=write_pipe, out_directory=self.out_directory)
+        output_file=write_pipe, out_directory=self.out_directory,
+        env_variables=env)
 
     reader = TestLineReader(read_pipe, write_pipe)
     #  If we need to manually exit the test runner at any point,
     #  ensure that the launcher is killed properly first.
     def Abort(signum, frame):
+      del signum, frame  # Unused.
       launcher.Kill()
       reader.Kill()
       sys.stderr.write("TEST RUN STOPPED VIA MANUAL EXIT\n")
@@ -256,7 +253,7 @@
     return_code = 1
     try:
       return_code = launcher.Run()
-    except Exception as e:
+    except Exception:  # pylint: disable=broad-except
       sys.stderr.write("Error while running {}:\n".format(target_name))
       traceback.print_exc(file=sys.stderr)
     finally:
@@ -472,4 +469,3 @@
 
 if __name__ == "__main__":
   sys.exit(main())
-
diff --git a/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow.cpp b/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow.cpp
index a65923a..32d5676 100644
--- a/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow.cpp
+++ b/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow.cpp
@@ -163,6 +163,15 @@
     swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
     swapChainDesc.AlphaMode             = DXGI_ALPHA_MODE_UNSPECIFIED;
 
+#if defined(STARBOARD)
+    // Normally for UWP on XB1, the swap chain surface should always be 1080p,
+    // regardless of the actual output resolution. However, by using a special
+    // surface format (R10G10B10A2), it is possible to use other resolutions
+    // that will be passed to the output without scaling.
+    swapChainDesc.Format = DXGI_FORMAT_R10G10B10A2_UNORM;
+    swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
+#endif
+
     *swapChain = nullptr;
 
     ComPtr<IDXGISwapChain1> newSwapChain;