Import Cobalt 12.71685
diff --git a/src/base/logging.h b/src/base/logging.h
index 2fb626e..238db8c 100644
--- a/src/base/logging.h
+++ b/src/base/logging.h
@@ -308,18 +308,19 @@
 // by LOG() and LOG_IF, etc. Since these are used all over our code, it's
 // better to have compact code for these operations.
 #define COMPACT_GOOGLE_LOG_EX_INFO(ClassName, ...) \
-  logging::ClassName(__FILE__, __LINE__, logging::LOG_INFO , ##__VA_ARGS__)
-#define COMPACT_GOOGLE_LOG_EX_WARNING(ClassName, ...) \
-  logging::ClassName(__FILE__, __LINE__, logging::LOG_WARNING , ##__VA_ARGS__)
+  ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_INFO, ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_WARNING(ClassName, ...)              \
+  ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_WARNING, \
+                       ##__VA_ARGS__)
 #define COMPACT_GOOGLE_LOG_EX_ERROR(ClassName, ...) \
-  logging::ClassName(__FILE__, __LINE__, logging::LOG_ERROR , ##__VA_ARGS__)
-#define COMPACT_GOOGLE_LOG_EX_ERROR_REPORT(ClassName, ...) \
-  logging::ClassName(__FILE__, __LINE__, \
-                     logging::LOG_ERROR_REPORT , ##__VA_ARGS__)
+  ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_ERROR, ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_ERROR_REPORT(ClassName, ...)            \
+  ::logging::ClassName(__FILE__, __LINE__, logging::LOG_ERROR_REPORT, \
+                       ##__VA_ARGS__)
 #define COMPACT_GOOGLE_LOG_EX_FATAL(ClassName, ...) \
-  logging::ClassName(__FILE__, __LINE__, logging::LOG_FATAL , ##__VA_ARGS__)
+  ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_FATAL, ##__VA_ARGS__)
 #define COMPACT_GOOGLE_LOG_EX_DFATAL(ClassName, ...) \
-  logging::ClassName(__FILE__, __LINE__, logging::LOG_DFATAL , ##__VA_ARGS__)
+  ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_DFATAL, ##__VA_ARGS__)
 
 #define COMPACT_GOOGLE_LOG_INFO \
   COMPACT_GOOGLE_LOG_EX_INFO(LogMessage)
@@ -384,7 +385,7 @@
 
 // The VLOG macros log with negative verbosities.
 #define VLOG_STREAM(verbose_level) \
-  logging::LogMessage(__FILE__, __LINE__, -verbose_level).stream()
+  ::logging::LogMessage(__FILE__, __LINE__, -verbose_level).stream()
 
 #define VLOG(verbose_level) \
   LAZY_STREAM(VLOG_STREAM(verbose_level), VLOG_IS_ON(verbose_level))
@@ -394,17 +395,20 @@
       VLOG_IS_ON(verbose_level) && (condition))
 
 #if defined (OS_WIN)
-#define VPLOG_STREAM(verbose_level) \
-  logging::Win32ErrorLogMessage(__FILE__, __LINE__, -verbose_level, \
-    ::logging::GetLastSystemErrorCode()).stream()
+#define VPLOG_STREAM(verbose_level)                                    \
+  ::logging::Win32ErrorLogMessage(__FILE__, __LINE__, -verbose_level,  \
+                                  ::logging::GetLastSystemErrorCode()) \
+      .stream()
 #elif defined(OS_POSIX)
-#define VPLOG_STREAM(verbose_level) \
-  logging::ErrnoLogMessage(__FILE__, __LINE__, -verbose_level, \
-    ::logging::GetLastSystemErrorCode()).stream()
+#define VPLOG_STREAM(verbose_level)                               \
+  ::logging::ErrnoLogMessage(__FILE__, __LINE__, -verbose_level,  \
+                             ::logging::GetLastSystemErrorCode()) \
+      .stream()
 #elif defined(OS_STARBOARD)
-#define VPLOG_STREAM(verbose_level) \
-  logging::StarboardLogMessage(__FILE__, __LINE__, -verbose_level, \
-    ::logging::GetLastSystemErrorCode()).stream()
+#define VPLOG_STREAM(verbose_level)                                   \
+  ::logging::StarboardLogMessage(__FILE__, __LINE__, -verbose_level,  \
+                                 ::logging::GetLastSystemErrorCode()) \
+      .stream()
 #endif
 
 #define VPLOG(verbose_level) \
@@ -526,11 +530,10 @@
 //
 // TODO(akalin): Rewrite this so that constructs like if (...)
 // CHECK_EQ(...) else { ... } work properly.
-#define CHECK_OP(name, op, val1, val2)                          \
-  if (std::string* _result =                                    \
-      logging::Check##name##Impl((val1), (val2),                \
-                                 #val1 " " #op " " #val2))      \
-    logging::LogMessage(__FILE__, __LINE__, _result).stream()
+#define CHECK_OP(name, op, val1, val2)                     \
+  if (std::string* _result = ::logging::Check##name##Impl( \
+          (val1), (val2), #val1 " " #op " " #val2))        \
+  ::logging::LogMessage(__FILE__, __LINE__, _result).stream()
 
 #endif
 
@@ -779,14 +782,12 @@
 
 // Helper macro for binary operators.
 // Don't use this macro directly in your code, use DCHECK_EQ et al below.
-#define DCHECK_OP(name, op, val1, val2)                         \
-  if (DCHECK_IS_ON())                                           \
-    if (std::string* _result =                                  \
-        logging::Check##name##Impl((val1), (val2),              \
-                                   #val1 " " #op " " #val2))    \
-      logging::LogMessage(                                      \
-          __FILE__, __LINE__, ::logging::LOG_DCHECK,            \
-          _result).stream()
+#define DCHECK_OP(name, op, val1, val2)                                     \
+  if (DCHECK_IS_ON())                                                       \
+    if (std::string* _result = ::logging::Check##name##Impl(                \
+            (val1), (val2), #val1 " " #op " " #val2))                       \
+  ::logging::LogMessage(__FILE__, __LINE__, ::logging::LOG_DCHECK, _result) \
+      .stream()
 
 // Equality/Inequality checks - compare two values, and log a
 // LOG_DCHECK message including the two values when the result is not
@@ -1005,12 +1006,14 @@
 // Async signal safe logging mechanism.
 BASE_EXPORT void RawLog(int level, const char* message);
 
-#define RAW_LOG(level, message) logging::RawLog(logging::LOG_ ## level, message)
+#define RAW_LOG(level, message) \
+  ::logging::RawLog(::logging::LOG_##level, message)
 
-#define RAW_CHECK(condition)                                                   \
-  do {                                                                         \
-    if (!(condition))                                                          \
-      logging::RawLog(logging::LOG_FATAL, "Check failed: " #condition "\n");   \
+#define RAW_CHECK(condition)                               \
+  do {                                                     \
+    if (!(condition))                                      \
+      ::logging::RawLog(::logging::LOG_FATAL,              \
+                        "Check failed: " #condition "\n"); \
   } while (0)
 
 }  // namespace logging
diff --git a/src/base/message_loop.cc b/src/base/message_loop.cc
index 1d07390..9d209cd 100644
--- a/src/base/message_loop.cc
+++ b/src/base/message_loop.cc
@@ -19,6 +19,7 @@
 #include "base/metrics/histogram.h"
 #include "base/metrics/statistics_recorder.h"
 #include "base/run_loop.h"
+#include "base/synchronization/waitable_event.h"
 #include "base/third_party/dynamic_annotations/dynamic_annotations.h"
 #include "base/thread_task_runner_handle.h"
 #include "base/threading/thread_local.h"
@@ -328,6 +329,35 @@
   AddToIncomingQueue(&pending_task);
 }
 
+#if defined(COBALT)
+namespace {
+// Runs the given task, and then signals the given WaitableEvent.
+void RunAndSignal(const base::Closure& task, base::WaitableEvent* event) {
+  TRACE_EVENT0("task", "RunAndSignal");
+  task.Run();
+  event->Signal();
+}
+}  // namespace
+
+void MessageLoop::PostBlockingTask(
+    const tracked_objects::Location& from_here, const base::Closure& task) {
+  TRACE_EVENT0("task", "MessageLoop::PostBlockingTask");
+  DCHECK_NE(this, current())
+      << "PostBlockingTask can't be called from the MessageLoop's own thread. "
+      << from_here.ToString();
+  DCHECK(!task.is_null()) << from_here.ToString();
+
+  base::WaitableEvent task_finished(true /* manual reset */,
+                                    false /* initially unsignaled */);
+  PostTask(
+      from_here,
+      base::Bind(&RunAndSignal, task, base::Unretained(&task_finished)));
+
+  // Wait for the task to complete before proceeding.
+  task_finished.Wait();
+}
+#endif
+
 void MessageLoop::Run() {
   base::RunLoop run_loop;
   run_loop.Run();
diff --git a/src/base/message_loop.h b/src/base/message_loop.h
index 70a15b7..912898b 100644
--- a/src/base/message_loop.h
+++ b/src/base/message_loop.h
@@ -19,8 +19,8 @@
 #include "base/pending_task.h"
 #include "base/sequenced_task_runner_helpers.h"
 #include "base/synchronization/lock.h"
-#include "base/tracking_info.h"
 #include "base/time.h"
+#include "base/tracking_info.h"
 
 #if defined(OS_WIN)
 // We need this to declare base::MessagePumpWin::Dispatcher, which we should
@@ -202,6 +202,27 @@
       const base::Closure& task,
       base::TimeDelta delay);
 
+#if defined(COBALT)
+  // Posts an immediate task to this MessageLoop, and blocks until it has
+  // run. It is forbidden to call this method from the thread of the MessageLoop
+  // being posted to. One should exercise extreme caution when using this, as
+  // blocking between MessageLoops can cause deadlocks and is contraindicated in
+  // the Actor model of multiprogramming.
+  void PostBlockingTask(
+      const tracked_objects::Location& from_here,
+      const base::Closure& task);
+
+  // Adds a fence at the end of this MessageLoop's task queue, and then blocks
+  // until it has been reached. It is forbidden to call this method from the
+  // thread of the MessageLoop being posted to. One should exercise extreme
+  // caution when using this, as blocking between MessageLoops can cause
+  // deadlocks and is contraindicated in the Actor model of multiprogramming.
+  void WaitForFence() {
+    struct Fence { static void Task() {} };
+    PostBlockingTask(FROM_HERE, base::Bind(&Fence::Task));
+  }
+#endif
+
   // A variant on PostTask that deletes the given object.  This is useful
   // if the object needs to live until the next run of the MessageLoop (for
   // example, deleting a RenderProcessHost from within an IPC callback is not
diff --git a/src/base/message_loop_proxy_impl.cc b/src/base/message_loop_proxy_impl.cc
index c3efe26..c224f4a 100644
--- a/src/base/message_loop_proxy_impl.cc
+++ b/src/base/message_loop_proxy_impl.cc
@@ -5,6 +5,7 @@
 #include "base/message_loop_proxy_impl.h"
 
 #include "base/location.h"
+#include "base/synchronization/waitable_event.h"
 #include "base/threading/thread_restrictions.h"
 
 namespace base {
@@ -26,6 +27,37 @@
   return PostTaskHelper(from_here, task, delay, false);
 }
 
+#if defined(COBALT)
+namespace {
+// Runs the given task, and then signals the given WaitableEvent.
+void RunAndSignal(const Closure& task, WaitableEvent* event) {
+  task.Run();
+  event->Signal();
+}
+}  // namespace
+
+bool MessageLoopProxyImpl::PostBlockingTask(
+    const tracked_objects::Location& from_here,
+    const Closure& task) {
+  WaitableEvent task_finished(true /* manual reset */,
+                              false /* initially unsignaled */);
+  {
+    AutoLock lock(message_loop_lock_);
+    if (!target_message_loop_) {
+      return false;
+    }
+
+    target_message_loop_->PostTask(
+        from_here,
+        Bind(&RunAndSignal, task, Unretained(&task_finished)));
+  }
+
+  // Outside of the lock, wait for the task to complete before proceeding.
+  task_finished.Wait();
+  return true;
+}
+#endif
+
 bool MessageLoopProxyImpl::RunsTasksOnCurrentThread() const {
   return thread_id_ == PlatformThread::CurrentId();
 }
diff --git a/src/base/message_loop_proxy_impl.h b/src/base/message_loop_proxy_impl.h
index a01b415..4675a9e 100644
--- a/src/base/message_loop_proxy_impl.h
+++ b/src/base/message_loop_proxy_impl.h
@@ -27,6 +27,10 @@
       const base::Closure& task,
       base::TimeDelta delay) OVERRIDE;
   virtual bool RunsTasksOnCurrentThread() const OVERRIDE;
+#if defined(COBALT)
+  virtual bool PostBlockingTask(const tracked_objects::Location& from_here,
+                                const Closure& task) OVERRIDE;
+#endif
 
  protected:
   virtual ~MessageLoopProxyImpl();
diff --git a/src/base/message_loop_unittest.cc b/src/base/message_loop_unittest.cc
index b4bf2f3..2f49451 100644
--- a/src/base/message_loop_unittest.cc
+++ b/src/base/message_loop_unittest.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <limits.h>
+
 #include <vector>
 
 #include "base/bind.h"
@@ -332,6 +334,55 @@
   EXPECT_FALSE(run_time2.is_null());
 }
 
+#if defined(COBALT)
+void RunTest_PostBlockingTask(MessageLoop::Type message_loop_type) {
+  base::Thread thread("PostBlockingTsk");
+  thread.StartWithOptions(base::Thread::Options(message_loop_type, 0));
+
+  const TimeDelta kPause = TimeDelta::FromMilliseconds(50);
+
+  int num_tasks = INT_MAX;
+
+  TimeTicks time_before_post_1 = TimeTicks::Now();
+  thread.message_loop()->PostTask(
+      FROM_HERE, base::Bind(&SlowFunc, kPause, &num_tasks));
+  TimeTicks time_before_post_2 = TimeTicks::Now();
+  thread.message_loop()->PostBlockingTask(
+      FROM_HERE, base::Bind(&SlowFunc, kPause, &num_tasks));
+  TimeTicks time_after_post_2 = TimeTicks::Now();
+
+  // Not much time should have passed during the regular PostTask.
+  EXPECT_GT(kPause, time_before_post_2 - time_before_post_1);
+
+  // The PostBlockingTask should wait for both.
+  EXPECT_LE(kPause * 2, time_after_post_2 - time_before_post_1);
+}
+
+void RunTest_WaitForFence(MessageLoop::Type message_loop_type) {
+  base::Thread thread("WaitForFence");
+  thread.StartWithOptions(base::Thread::Options(message_loop_type, 0));
+
+  const TimeDelta kPause = TimeDelta::FromMilliseconds(50);
+
+  int num_tasks = INT_MAX;
+
+  TimeTicks time_before_post = TimeTicks::Now();
+  thread.message_loop()->PostTask(
+      FROM_HERE, base::Bind(&SlowFunc, kPause, &num_tasks));
+  thread.message_loop()->PostTask(
+      FROM_HERE, base::Bind(&SlowFunc, kPause, &num_tasks));
+  TimeTicks time_before_wait = TimeTicks::Now();
+  thread.message_loop()->WaitForFence();
+  TimeTicks time_after_wait = TimeTicks::Now();
+
+  // Not much time should have passed during the regular PostTask.
+  EXPECT_GT(kPause, time_before_wait - time_before_post);
+
+  // The WaitForFence should wait for the tasks to finish.
+  EXPECT_LE(kPause * 2, time_after_wait - time_before_post);
+}
+#endif
+
 #if defined(OS_WIN)
 
 void SubPumpFunc() {
@@ -1682,6 +1733,24 @@
   RunTest_PostDelayedTask_SharedTimer(MessageLoop::TYPE_IO);
 }
 
+#if defined(COBALT)
+TEST(MessageLoopTest, PostBlockingTask) {
+  RunTest_PostBlockingTask(MessageLoop::TYPE_DEFAULT);
+#if !defined(OS_STARBOARD)
+  RunTest_PostBlockingTask(MessageLoop::TYPE_UI);
+#endif
+  RunTest_PostBlockingTask(MessageLoop::TYPE_IO);
+}
+
+TEST(MessageLoopTest, WaitForFence) {
+  RunTest_WaitForFence(MessageLoop::TYPE_DEFAULT);
+#if !defined(OS_STARBOARD)
+  RunTest_WaitForFence(MessageLoop::TYPE_UI);
+#endif
+  RunTest_WaitForFence(MessageLoop::TYPE_IO);
+}
+#endif
+
 #if defined(OS_WIN)
 TEST(MessageLoopTest, PostDelayedTask_SharedTimer_SubPump) {
   RunTest_PostDelayedTask_SharedTimer_SubPump();
diff --git a/src/base/task_runner.h b/src/base/task_runner.h
index 4fbea4d..06aded1 100644
--- a/src/base/task_runner.h
+++ b/src/base/task_runner.h
@@ -7,7 +7,9 @@
 
 #include "base/base_export.h"
 #include "base/basictypes.h"
+#include "base/bind.h"
 #include "base/callback_forward.h"
+#include "base/location.h"
 #include "base/memory/ref_counted.h"
 #include "base/time.h"
 
@@ -75,6 +77,20 @@
                                const Closure& task,
                                base::TimeDelta delay) = 0;
 
+#if defined(COBALT)
+  // Like PostTask, but blocks until the posted task completes. Returns false
+  // and does not block if task was not posted.
+  virtual bool PostBlockingTask(const tracked_objects::Location& from_here,
+                                const Closure& task) = 0;
+
+  // Places a fence in the SequencedTaskRunner's queue and blocks until it is
+  // hit. Returns false if the fence was not set and no blocking was done.
+  bool WaitForFence() {
+    struct Fence { static void Task() {} };
+    return PostBlockingTask(FROM_HERE, base::Bind(&Fence::Task));
+  }
+#endif
+
   // Returns true if the current thread is a thread on which a task
   // may be run, and false if no task will be run on the current
   // thread.
diff --git a/src/base/threading/sequenced_worker_pool.cc b/src/base/threading/sequenced_worker_pool.cc
index 56f908b..59e684f 100644
--- a/src/base/threading/sequenced_worker_pool.cc
+++ b/src/base/threading/sequenced_worker_pool.cc
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "base/atomicops.h"
+#include "base/bind.h"
 #include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/critical_closure.h"
@@ -23,6 +24,7 @@
 #include "base/stringprintf.h"
 #include "base/synchronization/condition_variable.h"
 #include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
 #include "base/threading/platform_thread.h"
 #include "base/threading/simple_thread.h"
 #include "base/threading/thread_restrictions.h"
@@ -94,6 +96,10 @@
   virtual bool PostDelayedTask(const tracked_objects::Location& from_here,
                                const Closure& task,
                                TimeDelta delay) OVERRIDE;
+#if defined(COBALT)
+  virtual bool PostBlockingTask(const tracked_objects::Location& from_here,
+                                const Closure& task) OVERRIDE;
+#endif
   virtual bool RunsTasksOnCurrentThread() const OVERRIDE;
 
  private:
@@ -127,6 +133,14 @@
   return pool_->PostDelayedWorkerTask(from_here, task, delay);
 }
 
+#if defined(COBALT)
+bool SequencedWorkerPoolTaskRunner::PostBlockingTask(
+    const tracked_objects::Location& from_here,
+    const Closure& task) {
+  return pool_->PostBlockingTask(from_here, task);
+}
+#endif
+
 bool SequencedWorkerPoolTaskRunner::RunsTasksOnCurrentThread() const {
   return pool_->RunsTasksOnCurrentThread();
 }
@@ -147,6 +161,10 @@
   virtual bool PostDelayedTask(const tracked_objects::Location& from_here,
                                const Closure& task,
                                TimeDelta delay) OVERRIDE;
+#if defined(COBALT)
+  virtual bool PostBlockingTask(const tracked_objects::Location& from_here,
+                                const Closure& task) OVERRIDE;
+#endif
   virtual bool RunsTasksOnCurrentThread() const OVERRIDE;
 
   // SequencedTaskRunner implementation
@@ -191,6 +209,14 @@
   return pool_->PostDelayedSequencedWorkerTask(token_, from_here, task, delay);
 }
 
+#if defined(COBALT)
+bool SequencedWorkerPoolSequencedTaskRunner::PostBlockingTask(
+    const tracked_objects::Location& from_here,
+    const Closure& task) {
+  return pool_->PostBlockingTask(from_here, task);
+}
+#endif
+
 bool SequencedWorkerPoolSequencedTaskRunner::RunsTasksOnCurrentThread() const {
   return pool_->IsRunningSequenceOnCurrentThread(token_);
 }
@@ -270,6 +296,14 @@
                 const Closure& task,
                 TimeDelta delay);
 
+#if defined(COBALT)
+  bool PostBlockingTask(const std::string* optional_token_name,
+                        SequenceToken sequence_token,
+                        WorkerShutdown shutdown_behavior,
+                        const tracked_objects::Location& from_here,
+                        const Closure& task);
+#endif
+
   bool RunsTasksOnCurrentThread() const;
 
   bool IsRunningSequenceOnCurrentThread(SequenceToken sequence_token) const;
@@ -568,6 +602,43 @@
   return true;
 }
 
+#if defined(COBALT)
+namespace {
+// Runs the given task, and then signals the given WaitableEvent.
+void RunAndSignal(const base::Closure& task, base::WaitableEvent* event) {
+  task.Run();
+  event->Signal();
+}
+}  // namespace
+
+bool SequencedWorkerPool::Inner::PostBlockingTask(
+    const std::string* optional_token_name,
+    SequenceToken sequence_token,
+    WorkerShutdown shutdown_behavior,
+    const tracked_objects::Location& from_here,
+    const Closure& task) {
+  DCHECK(!IsRunningSequenceOnCurrentThread(sequence_token))
+      << "PostBlockingTask cannot be called from a SequenceWorkerPool's own "
+      << "thread." << from_here.ToString();
+  WaitableEvent task_finished(false /* automatic reset */,
+                              false /* initially unsignaled */);
+  bool posted = PostTask(
+      optional_token_name,
+      sequence_token,
+      shutdown_behavior,
+      from_here,
+      Bind(&RunAndSignal, task, Unretained(&task_finished)),
+      TimeDelta());
+  if (!posted) {
+    return false;
+  }
+
+  // Wait for the task to complete before proceeding.
+  task_finished.Wait();
+  return true;
+}
+#endif
+
 bool SequencedWorkerPool::Inner::RunsTasksOnCurrentThread() const {
   AutoLock lock(lock_);
   return ContainsKey(threads_, PlatformThread::CurrentId());
@@ -1045,6 +1116,15 @@
                           from_here, task, delay);
 }
 
+#if defined(COBALT)
+bool SequencedWorkerPool::PostBlockingWorkerTask(
+    const tracked_objects::Location& from_here,
+    const Closure& task) {
+  return inner_->PostBlockingTask(NULL, SequenceToken(), BLOCK_SHUTDOWN,
+                                  from_here, task);
+}
+#endif
+
 bool SequencedWorkerPool::PostWorkerTaskWithShutdownBehavior(
     const tracked_objects::Location& from_here,
     const Closure& task,
@@ -1097,6 +1177,14 @@
   return PostDelayedWorkerTask(from_here, task, delay);
 }
 
+#if defined(COBALT)
+bool SequencedWorkerPool::PostBlockingTask(
+    const tracked_objects::Location& from_here,
+    const Closure& task) {
+  return PostBlockingWorkerTask(from_here, task);
+}
+#endif
+
 bool SequencedWorkerPool::RunsTasksOnCurrentThread() const {
   return inner_->RunsTasksOnCurrentThread();
 }
diff --git a/src/base/threading/sequenced_worker_pool.h b/src/base/threading/sequenced_worker_pool.h
index 1a26a4a..cca19f9 100644
--- a/src/base/threading/sequenced_worker_pool.h
+++ b/src/base/threading/sequenced_worker_pool.h
@@ -231,6 +231,13 @@
                              const Closure& task,
                              TimeDelta delay);
 
+#if defined(COBALT)
+  // Like PostWorkerTask, but blocks until the posted task completes. Returns
+  // false and does not block if task was not posted.
+  bool PostBlockingWorkerTask(const tracked_objects::Location& from_here,
+                              const Closure& task);
+#endif
+
   // Same as PostWorkerTask but allows specification of the shutdown behavior.
   bool PostWorkerTaskWithShutdownBehavior(
       const tracked_objects::Location& from_here,
@@ -285,6 +292,10 @@
   virtual bool PostDelayedTask(const tracked_objects::Location& from_here,
                                const Closure& task,
                                TimeDelta delay) OVERRIDE;
+#if defined(COBALT)
+  virtual bool PostBlockingTask(const tracked_objects::Location& from_here,
+                                const Closure& task) OVERRIDE;
+#endif
   virtual bool RunsTasksOnCurrentThread() const OVERRIDE;
 
   // Returns true if the current thread is processing a task with the given
diff --git a/src/base/threading/worker_pool.cc b/src/base/threading/worker_pool.cc
index 16cc061..9aa7ba5 100644
--- a/src/base/threading/worker_pool.cc
+++ b/src/base/threading/worker_pool.cc
@@ -42,6 +42,10 @@
   virtual bool PostDelayedTask(const tracked_objects::Location& from_here,
                                const Closure& task,
                                TimeDelta delay) OVERRIDE;
+#if defined(COBALT)
+  virtual bool PostBlockingTask(const tracked_objects::Location& from_here,
+                                const Closure& task) OVERRIDE;
+#endif
   virtual bool RunsTasksOnCurrentThread() const OVERRIDE;
 
  private:
@@ -73,6 +77,14 @@
   return PostDelayedTaskAssertZeroDelay(from_here, task, delay);
 }
 
+#if defined(COBALT)
+bool WorkerPoolTaskRunner::PostBlockingTask(
+    const tracked_objects::Location& from_here,
+    const Closure& task) {
+  return WorkerPool::PostBlockingTask(from_here, task, tasks_are_slow_);
+}
+#endif
+
 bool WorkerPoolTaskRunner::RunsTasksOnCurrentThread() const {
   return WorkerPool::RunsTasksOnCurrentThread();
 }
diff --git a/src/base/threading/worker_pool.h b/src/base/threading/worker_pool.h
index 333b495..16e1d93 100644
--- a/src/base/threading/worker_pool.h
+++ b/src/base/threading/worker_pool.h
@@ -36,6 +36,13 @@
   static bool PostTask(const tracked_objects::Location& from_here,
                        const base::Closure& task, bool task_is_slow);
 
+#if defined(COBALT)
+  // Like PostTask, but blocks until the posted task completes. Returns false
+  // and does not block if task was not posted.
+  static bool PostBlockingTask(const tracked_objects::Location& from_here,
+                               const base::Closure& task, bool task_is_slow);
+#endif
+
   // Just like MessageLoopProxy::PostTaskAndReply, except the destination
   // for |task| is a worker thread and you can specify |task_is_slow| just
   // like you can for PostTask above.
diff --git a/src/base/threading/worker_pool_posix.cc b/src/base/threading/worker_pool_posix.cc
index 2ad3925..f15250f 100644
--- a/src/base/threading/worker_pool_posix.cc
+++ b/src/base/threading/worker_pool_posix.cc
@@ -11,6 +11,7 @@
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
 #include "base/stringprintf.h"
+#include "base/synchronization/waitable_event.h"
 #include "base/threading/platform_thread.h"
 #include "base/threading/thread_local.h"
 #include "base/threading/worker_pool.h"
@@ -42,6 +43,10 @@
 
   void PostTask(const tracked_objects::Location& from_here,
                 const base::Closure& task, bool task_is_slow);
+#if defined(COBALT)
+  void PostBlockingTask(const tracked_objects::Location& from_here,
+                        const base::Closure& task, bool task_is_slow);
+#endif
 
  private:
   scoped_refptr<base::PosixDynamicThreadPool> pool_;
@@ -61,6 +66,14 @@
   pool_->PostTask(from_here, task);
 }
 
+#if defined(COBALT)
+void WorkerPoolImpl::PostBlockingTask(
+    const tracked_objects::Location& from_here,
+    const base::Closure& task, bool task_is_slow) {
+  pool_->PostBlockingTask(from_here, task);
+}
+#endif
+
 base::LazyInstance<WorkerPoolImpl> g_lazy_worker_pool =
     LAZY_INSTANCE_INITIALIZER;
 
@@ -118,6 +131,16 @@
   return true;
 }
 
+#if defined(COBALT)
+// static
+bool WorkerPool::PostBlockingTask(
+    const tracked_objects::Location& from_here,
+    const base::Closure& task, bool task_is_slow) {
+  g_lazy_worker_pool.Pointer()->PostBlockingTask(from_here, task, task_is_slow);
+  return true;
+}
+#endif
+
 // static
 bool WorkerPool::RunsTasksOnCurrentThread() {
   return g_worker_pool_running_on_this_thread.Get().Get();
@@ -154,6 +177,29 @@
   AddTask(&pending_task);
 }
 
+#if defined(COBALT)
+namespace {
+// Runs the given task, and then signals the given WaitableEvent.
+void RunAndSignal(const Closure& task, WaitableEvent* event) {
+  task.Run();
+  event->Signal();
+}
+}  // namespace
+
+void PosixDynamicThreadPool::PostBlockingTask(
+    const tracked_objects::Location& from_here,
+    const base::Closure& task) {
+  base::WaitableEvent task_finished(false /* automatic reset */,
+                                    false /* initially unsignaled */);
+  PostTask(
+      from_here,
+      base::Bind(&RunAndSignal, task, base::Unretained(&task_finished)));
+
+  // Wait for the task to complete before proceeding.
+  task_finished.Wait();
+}
+#endif
+
 void PosixDynamicThreadPool::AddTask(PendingTask* pending_task) {
   AutoLock locked(lock_);
   DCHECK(!terminated_) <<
diff --git a/src/base/threading/worker_pool_posix.h b/src/base/threading/worker_pool_posix.h
index dd0ffb6..9b6e6ce 100644
--- a/src/base/threading/worker_pool_posix.h
+++ b/src/base/threading/worker_pool_posix.h
@@ -60,6 +60,12 @@
   void PostTask(const tracked_objects::Location& from_here,
                 const Closure& task);
 
+#if defined(COBALT)
+  // Like PostTask, but blocks until the posted task completes.
+  void PostBlockingTask(const tracked_objects::Location& from_here,
+                        const Closure& task);
+#endif
+
   // Worker thread method to wait for up to |idle_seconds_before_exit| for more
   // work from the thread pool.  Returns NULL if no work is available.
   PendingTask WaitForTask();
diff --git a/src/base/time_starboard.cc b/src/base/time_starboard.cc
index 0327166..9225951 100644
--- a/src/base/time_starboard.cc
+++ b/src/base/time_starboard.cc
@@ -98,7 +98,7 @@
 
 // static
 TimeTicks TimeTicks::ThreadNow() {
-#if SB_VERSION(3) && SB_HAS(TIME_THREAD_NOW)
+#if SB_API_VERSION >= 3 && SB_HAS(TIME_THREAD_NOW)
   return TimeTicks(SbTimeGetMonotonicThreadNow());
 #else
   return HighResNow();
@@ -107,7 +107,7 @@
 
 // static
 bool TimeTicks::HasThreadNow() {
-#if SB_VERSION(3) && SB_HAS(TIME_THREAD_NOW)
+#if SB_API_VERSION >= 3 && SB_HAS(TIME_THREAD_NOW)
   return true;
 #else
   return false;
diff --git a/src/build/common.gypi b/src/build/common.gypi
index 0164104..6f02d54 100644
--- a/src/build/common.gypi
+++ b/src/build/common.gypi
@@ -1978,29 +1978,6 @@
               '-Wno-unused-result',
             ],
           }],
-          [ 'OS=="win"', {
-            'defines': [
-              '_CRT_SECURE_NO_DEPRECATE',
-              '_CRT_NONSTDC_NO_WARNINGS',
-              '_CRT_NONSTDC_NO_DEPRECATE',
-              '_SCL_SECURE_NO_DEPRECATE',
-            ],
-            'msvs_disabled_warnings': [4800],
-            'msvs_settings': {
-              'VCCLCompilerTool': {
-                'WarningLevel': '3',
-                'WarnAsError': '<(win_third_party_warn_as_error)',
-                'Detect64BitPortabilityProblems': 'false',
-              },
-            },
-            'conditions': [
-              ['buildtype=="Official"', {
-                'msvs_settings': {
-                  'VCCLCompilerTool': { 'WarnAsError': 'false' },
-                }
-              }],
-            ],
-          }],
           # TODO(darin): Unfortunately, some third_party code depends on base.
           [ 'target_os=="win" and component=="shared_library"', {
             'msvs_disabled_warnings': [
@@ -2041,14 +2018,6 @@
           '__STDC_FORMAT_MACROS',
         ],
         'conditions': [
-          ['OS=="win"', {
-            # turn on warnings for signed/unsigned mismatch on chromium code.
-            'msvs_settings': {
-              'VCCLCompilerTool': {
-                'AdditionalOptions': ['/we4389'],
-              },
-            },
-          }],
           ['(OS == "win" or target_arch=="xb1") and component=="shared_library"', {
             'msvs_disabled_warnings': [
               4251,  # class 'std::xx' needs to have dll-interface.
diff --git a/src/cobalt/CHANGELOG.md b/src/cobalt/CHANGELOG.md
index 1c6e3c6..3deb938 100644
--- a/src/cobalt/CHANGELOG.md
+++ b/src/cobalt/CHANGELOG.md
@@ -2,11 +2,77 @@
 
 This document records all notable changes made to Cobalt since the last release.
 
-## Version 10
+## Version 11
 
-### AutoMem - Memory Configuration
-AutoMem has been added which assists developers in tuning the memory
-settings of the Cobalt app. On startup, memory settings are now printed
-for all builds except gold. Memory settings can be altered via the
-command line, build files and in some instances the Starboard API.
-*For more information, see [doc/memory_tuning.md](doc/memory_tuning.md)
+ - **Introduce C\+\+11**
+
+   C\+\+11 is now used within Cobalt code, and so Cobalt must be compiled using
+   C\+\+11 toolchains.
+
+ - **Update SpiderMonkey from version 24 to 45; support ECMAScript 6**
+
+   The Mozilla SpiderMonkey JavaScript engine is rebased up to version 45, and
+   thus ECMAScript 6 is now supported by Cobalt.  You will need to modify your
+   `gyp_configuration.gypi` file to set `'javascript_engine': 'mozjs-45'` to
+   enable this.
+
+ - **Fetch/Stream API**
+
+   Cobalt now supports the Fetch/Stream API.
+
+ - **On-device Speech-to-Text support**
+
+   Support for utilizing the new Starboard speech recognizer interface in order
+   to allow for on-device speech-to-text support is now added.  The Starboard
+   interface is defined in
+   [starboard/speech_recognizer.h](../starboard/speech_recognizer.h).
+
+ - **Mouse pointer support**
+
+   Cobalt now supports pointer events and will respond to Starboard
+   `kSbInputEventTypeMove` events.  These will be passed into the WebModule to
+   be processed by JavaScript.
+
+ - **Playback Rate**
+
+   Cobalt now supports adjusting the video playback rate.
+
+ - **AutoMem - Memory Configuration**
+
+   AutoMem has been added which assists developers in tuning the memory
+   settings of the Cobalt app. On startup, memory settings are now printed
+   for all builds except gold. Memory settings can be altered via the
+   command line, build files and in some instances the Starboard API.
+   For more information, see [cobalt/doc/memory_tuning.md](doc/memory_tuning.md).
+
+ - **Page Visibility API Support**
+
+   Cobalt now supports the page visibility API, and will signal visibility and
+   focus changes to the web app as the process transitions between Starboard
+   lifecycle states.
+
+ - **Opus Support**
+
+   Added support for providing Opus audio-specific config to Starboard. Requires
+   Starboard 6.
+
+ - **Linux build now supports 360 video**
+
+   The Linux build linux-x64x11 now supports 360 video, and can be used as a
+   reference implementation.
+
+ - **Stop the application if not retrying after a connection error**
+
+   A positive response from `kSbSystemPlatformErrorTypeConnectionError` now
+   indicates that Cobalt should retry the failed request. Any other response now
+   causes Cobalt to call `SbSystemRequestStop`.
+
+ - **Frame rate counter**
+
+   A frame rate counter is now made accessible.  It actually displays frame
+   times, the inverse of frame rate.  In this case, 16.6ms corresponds to 60fps.
+   It is accessible both as an overlay on the display by the command line
+   option, "--fps_overlay".  The data can also be printed to stdout with the
+   command line option "--fps_stdout".  The frame rate statistics will be
+   updated each time an animation ends, or after 60 frames have been processed.
+   Both command line flags are available in Gold builds.
diff --git a/src/cobalt/accessibility/internal/text_alternative_helper.cc b/src/cobalt/accessibility/internal/text_alternative_helper.cc
index 8c803da..56845a4 100644
--- a/src/cobalt/accessibility/internal/text_alternative_helper.cc
+++ b/src/cobalt/accessibility/internal/text_alternative_helper.cc
@@ -110,7 +110,7 @@
   // Rule 1
   // Skip hidden elements unless the author specifies to use them via an
   // aria-labelledby or aria-describedby being used in the current computation.
-  if (!in_labelled_by_ && IsAriaHidden(element)) {
+  if (!in_labelled_by_or_described_by_ && IsAriaHidden(element)) {
     return;
   }
 
@@ -118,8 +118,9 @@
   // The aria-labelledby attribute takes precedence as the element's text
   // alternative unless this computation is already occurring as the result of a
   // recursive aria-labelledby declaration.
-  if (!in_labelled_by_) {
-    if (TryAppendFromLabelledBy(element)) {
+  if (!in_labelled_by_or_described_by_) {
+    if (TryAppendFromLabelledByOrDescribedBy(element,
+                                             base::Tokens::aria_labelledby())) {
       return;
     }
   }
@@ -180,6 +181,16 @@
     scoped_refptr<dom::Node> child = children->Item(i);
     AppendTextAlternative(child);
   }
+
+  // 5.6.1.2. Description Compution
+  // If aria-describedby is present, user agents MUST compute the accessible
+  // description by concatenating the text alternatives for nodes referenced by
+  // an aria-describedby attribute on the current node. The text alternatives
+  // for the referenced nodes are computed using a number of methods.
+  if (!in_labelled_by_or_described_by_) {
+    TryAppendFromLabelledByOrDescribedBy(element,
+                                         base::Tokens::aria_describedby());
+  }
 }
 
 std::string TextAlternativeHelper::GetTextAlternative() {
@@ -199,36 +210,34 @@
   return IsAriaHidden(element->parent_element());
 }
 
-bool TextAlternativeHelper::TryAppendFromLabelledBy(
-    const scoped_refptr<dom::Element>& element) {
-  DCHECK(!in_labelled_by_);
-  base::optional<std::string> labelled_by_attribute =
-      element->GetAttribute(base::Tokens::aria_labelledby().c_str());
-  std::vector<std::string> labelled_by_ids;
+bool TextAlternativeHelper::TryAppendFromLabelledByOrDescribedBy(
+    const scoped_refptr<dom::Element>& element, const base::Token& token) {
+  DCHECK(!in_labelled_by_or_described_by_);
+  base::optional<std::string> attributes = element->GetAttribute(token.c_str());
+  std::vector<std::string> ids;
   // If aria-labelledby is empty or undefined, the aria-label attribute ... is
   // used (defined below).
-  base::SplitStringAlongWhitespace(labelled_by_attribute.value_or(""),
-                                   &labelled_by_ids);
+  base::SplitStringAlongWhitespace(attributes.value_or(""), &ids);
   const size_t current_num_alternatives = alternatives_.size();
-  if (!labelled_by_ids.empty()) {
-    in_labelled_by_ = true;
-    for (int i = 0; i < labelled_by_ids.size(); ++i) {
-      if (visited_element_ids_.find(base::Token(labelled_by_ids[i])) !=
+  if (!ids.empty()) {
+    in_labelled_by_or_described_by_ = true;
+    for (int i = 0; i < ids.size(); ++i) {
+      if (visited_element_ids_.find(base::Token(ids[i])) !=
           visited_element_ids_.end()) {
-        DLOG(WARNING) << "Skipping reference to ID: " << labelled_by_ids[i]
+        DLOG(WARNING) << "Skipping reference to ID: " << ids[i]
                       << " to prevent reference loop.";
         continue;
       }
-      scoped_refptr<dom::Element> labelled_by_element =
-          GetElementById(element, labelled_by_ids[i]);
-      if (!labelled_by_element) {
-        DLOG(WARNING) << "Could not find aria-labelledby target: "
-                      << labelled_by_ids[i];
+      scoped_refptr<dom::Element> target_element =
+          GetElementById(element, ids[i]);
+      if (!target_element) {
+        DLOG(WARNING) << "Could not find " << token.c_str()
+                      << " target: " << ids[i];
         continue;
       }
-      AppendTextAlternative(labelled_by_element);
+      AppendTextAlternative(target_element);
     }
-    in_labelled_by_ = false;
+    in_labelled_by_or_described_by_ = false;
   }
   // Check if any of these recursive calls to AppendTextAlternative actually
   // ended up appending something.
diff --git a/src/cobalt/accessibility/internal/text_alternative_helper.h b/src/cobalt/accessibility/internal/text_alternative_helper.h
index 721a589..51fbfb9 100644
--- a/src/cobalt/accessibility/internal/text_alternative_helper.h
+++ b/src/cobalt/accessibility/internal/text_alternative_helper.h
@@ -31,15 +31,16 @@
 // algorithm, made public for testing. This class should not be used directly.
 class TextAlternativeHelper {
  public:
-  TextAlternativeHelper() : in_labelled_by_(false) {}
+  TextAlternativeHelper() : in_labelled_by_or_described_by_(false) {}
   void AppendTextAlternative(const scoped_refptr<dom::Node>& node);
 
   // Return the accumulated alternatives joined by a single space character.
   std::string GetTextAlternative();
 
-  // Append the text alternative from a aria-labelledby property. Returns true
-  // if text alternative(s) were successfully appended.
-  bool TryAppendFromLabelledBy(const scoped_refptr<dom::Element>& element);
+  // Append the text alternative from a aria-labelledby or aria-describedby
+  // property. Returns true if text alternative(s) were successfully appended.
+  bool TryAppendFromLabelledByOrDescribedBy(
+      const scoped_refptr<dom::Element>& element, const base::Token& token);
 
   // Append the text alternative from a aria-label property. Returns true
   // if the aria-label property exists and has a non-empty value.
@@ -63,7 +64,7 @@
  private:
   typedef base::hash_set<base::Token> TokenSet;
 
-  bool in_labelled_by_;
+  bool in_labelled_by_or_described_by_;
   std::vector<std::string> alternatives_;
   TokenSet visited_element_ids_;
 };
diff --git a/src/cobalt/accessibility/internal/text_alternative_helper_test.cc b/src/cobalt/accessibility/internal/text_alternative_helper_test.cc
index 5ce980a..0c6c7db 100644
--- a/src/cobalt/accessibility/internal/text_alternative_helper_test.cc
+++ b/src/cobalt/accessibility/internal/text_alternative_helper_test.cc
@@ -139,8 +139,8 @@
                                    "target_element");
   document()->AppendChild(labelledby_element);
 
-  EXPECT_TRUE(
-      text_alternative_helper_.TryAppendFromLabelledBy(labelledby_element));
+  EXPECT_TRUE(text_alternative_helper_.TryAppendFromLabelledByOrDescribedBy(
+      labelledby_element, base::Tokens::aria_labelledby()));
   EXPECT_STREQ("labelledby target",
                text_alternative_helper_.GetTextAlternative().c_str());
 }
@@ -157,8 +157,8 @@
                                    "bad_reference");
   document()->AppendChild(labelledby_element);
 
-  EXPECT_FALSE(
-      text_alternative_helper_.TryAppendFromLabelledBy(labelledby_element));
+  EXPECT_FALSE(text_alternative_helper_.TryAppendFromLabelledByOrDescribedBy(
+      labelledby_element, base::Tokens::aria_labelledby()));
   EXPECT_TRUE(text_alternative_helper_.GetTextAlternative().empty());
 }
 
@@ -183,8 +183,8 @@
       "target_element1 target_element2 bad_reference target_element3");
   document()->AppendChild(labelledby_element);
 
-  EXPECT_TRUE(
-      text_alternative_helper_.TryAppendFromLabelledBy(labelledby_element));
+  EXPECT_TRUE(text_alternative_helper_.TryAppendFromLabelledByOrDescribedBy(
+      labelledby_element, base::Tokens::aria_labelledby()));
   EXPECT_STREQ("target1 target2 target3",
                text_alternative_helper_.GetTextAlternative().c_str());
 }
@@ -204,8 +204,8 @@
                                    "other_id self_id");
   document()->AppendChild(labelledby_element);
 
-  EXPECT_TRUE(
-      text_alternative_helper_.TryAppendFromLabelledBy(labelledby_element));
+  EXPECT_TRUE(text_alternative_helper_.TryAppendFromLabelledByOrDescribedBy(
+      labelledby_element, base::Tokens::aria_labelledby()));
   EXPECT_STREQ("other-text self-label",
                text_alternative_helper_.GetTextAlternative().c_str());
 }
@@ -289,6 +289,50 @@
                text_alternative_helper_.GetTextAlternative().c_str());
 }
 
+TEST_F(TextAlternativeHelperTest, GetTextFromAriaDescribedBy) {
+  scoped_refptr<dom::Element> target_element = document()->CreateElement("div");
+  target_element->AppendChild(document()->CreateTextNode("describedby target"));
+  target_element->set_id("target_element");
+  document()->AppendChild(target_element);
+
+  scoped_refptr<dom::Element> describedby_element =
+      document()->CreateElement("div");
+  describedby_element->SetAttribute(base::Tokens::aria_describedby().c_str(),
+                                    "target_element");
+  document()->AppendChild(describedby_element);
+
+  EXPECT_TRUE(text_alternative_helper_.TryAppendFromLabelledByOrDescribedBy(
+      describedby_element, base::Tokens::aria_describedby()));
+  EXPECT_STREQ("describedby target",
+               text_alternative_helper_.GetTextAlternative().c_str());
+}
+
+TEST_F(TextAlternativeHelperTest, HasBothLabelledByAndDescribedBy) {
+  // aria-describedby is ignored in this case.
+  scoped_refptr<dom::Element> element = document()->CreateElement("div");
+  document()->AppendChild(element);
+
+  element->SetAttribute(base::Tokens::aria_labelledby().c_str(), "calendar");
+  element->SetAttribute(base::Tokens::aria_describedby().c_str(), "info");
+
+  scoped_refptr<dom::Element> labelledby_element =
+      document()->CreateElement("div");
+  labelledby_element->set_id("calendar");
+  labelledby_element->AppendChild(document()->CreateTextNode("Calendar"));
+  element->AppendChild(labelledby_element);
+
+  scoped_refptr<dom::Element> describedby_element =
+      document()->CreateElement("div");
+  describedby_element->set_id("info");
+  describedby_element->AppendChild(
+      document()->CreateTextNode("Calendar content description."));
+  element->AppendChild(describedby_element);
+
+  text_alternative_helper_.AppendTextAlternative(element);
+  EXPECT_STREQ("Calendar",
+               text_alternative_helper_.GetTextAlternative().c_str());
+}
+
 }  // namespace internal
 }  // namespace accessibility
 }  // namespace cobalt
diff --git a/src/cobalt/accessibility/screen_reader_tests.cc b/src/cobalt/accessibility/screen_reader_tests.cc
index 64a0893..1319c0d 100644
--- a/src/cobalt/accessibility/screen_reader_tests.cc
+++ b/src/cobalt/accessibility/screen_reader_tests.cc
@@ -161,7 +161,8 @@
   // Create the webmodule and let it run.
   DLOG(INFO) << url.spec();
   browser::WebModule web_module(
-      url, base::Bind(&LiveRegionMutationTest::OnRenderTreeProducedStub),
+      url, base::kApplicationStateStarted,
+      base::Bind(&LiveRegionMutationTest::OnRenderTreeProducedStub),
       base::Bind(&LiveRegionMutationTest::OnError, base::Unretained(this)),
       base::Bind(&LiveRegionMutationTest::Quit, base::Unretained(this)),
       base::Closure(), /* window_minimize_callback */
diff --git a/src/cobalt/account/account.gyp b/src/cobalt/account/account.gyp
index 6a561d2..21fcce3 100644
--- a/src/cobalt/account/account.gyp
+++ b/src/cobalt/account/account.gyp
@@ -14,7 +14,7 @@
 
 {
   'variables': {
-    'cobalt_code': 1,
+    'sb_pedantic_warnings': 1,
   },
   'targets': [
     {
diff --git a/src/cobalt/audio/async_audio_decoder.cc b/src/cobalt/audio/async_audio_decoder.cc
index aac40c9..b0ad972 100644
--- a/src/cobalt/audio/async_audio_decoder.cc
+++ b/src/cobalt/audio/async_audio_decoder.cc
@@ -49,6 +49,8 @@
   thread_.Start();
 }
 
+void AsyncAudioDecoder::Stop() { thread_.Stop(); }
+
 void AsyncAudioDecoder::AsyncDecode(
     const uint8* audio_data, size_t size,
     const DecodeFinishCallback& decode_finish_callback) {
diff --git a/src/cobalt/audio/async_audio_decoder.h b/src/cobalt/audio/async_audio_decoder.h
index b82c0db..1e8a025 100644
--- a/src/cobalt/audio/async_audio_decoder.h
+++ b/src/cobalt/audio/async_audio_decoder.h
@@ -35,6 +35,7 @@
 
   void AsyncDecode(const uint8* audio_data, size_t size,
                    const DecodeFinishCallback& decode_finish_callback);
+  void Stop();
 
  private:
   // The decoder thread.
diff --git a/src/cobalt/audio/audio.gyp b/src/cobalt/audio/audio.gyp
index e55e00b..f7065ff 100644
--- a/src/cobalt/audio/audio.gyp
+++ b/src/cobalt/audio/audio.gyp
@@ -14,7 +14,7 @@
 
 {
   'variables': {
-    'cobalt_code': 1,
+    'sb_pedantic_warnings': 1,
   },
   'targets': [
     {
diff --git a/src/cobalt/audio/audio_context.cc b/src/cobalt/audio/audio_context.cc
index b0c2ddd..0a6075d 100644
--- a/src/cobalt/audio/audio_context.cc
+++ b/src/cobalt/audio/audio_context.cc
@@ -33,6 +33,21 @@
   DCHECK(main_message_loop_);
 }
 
+AudioContext::~AudioContext() {
+  DCHECK(main_message_loop_->BelongsToCurrentThread());
+
+  // Before releasing any |pending_decode_callbacks_|, stop audio decoder
+  // explicitly to ensure that all the decoding works are done.
+  audio_decoder_.Stop();
+
+  // It is possible that the callbacks in |pending_decode_callbacks_| have not
+  // been called when destroying AudioContext.
+  for (DecodeCallbacks::iterator it = pending_decode_callbacks_.begin();
+       it != pending_decode_callbacks_.end(); ++it) {
+    delete it->second;
+  }
+}
+
 scoped_refptr<AudioBufferSourceNode> AudioContext::CreateBufferSource() {
   DCHECK(main_message_loop_->BelongsToCurrentThread());
 
diff --git a/src/cobalt/audio/audio_context.h b/src/cobalt/audio/audio_context.h
index 2fb310d..2e37fed 100644
--- a/src/cobalt/audio/audio_context.h
+++ b/src/cobalt/audio/audio_context.h
@@ -88,6 +88,7 @@
   typedef DecodeErrorCallbackArg::Reference DecodeErrorCallbackReference;
 
   AudioContext();
+  ~AudioContext();
 
   // Web API: AudioContext
   //
diff --git a/src/cobalt/base/application_state.h b/src/cobalt/base/application_state.h
new file mode 100644
index 0000000..a7fbc19
--- /dev/null
+++ b/src/cobalt/base/application_state.h
@@ -0,0 +1,54 @@
+/*
+ * 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_APPLICATION_STATE_H_
+#define COBALT_BASE_APPLICATION_STATE_H_
+
+namespace base {
+
+// Application states that Cobalt can be in, as derived from Starboard lifecycle
+// states as described in starboard/events.h. These may deviate from the
+// Starboard lifecycle states in order to explicitly represent Cobalt-specific
+// substates that Starboard does not enforce.
+enum ApplicationState {
+  // The application is still visible, and therefore still requires graphics
+  // resources, but the web application may wish to take actions such as pause
+  // video. The application is expected to be able to move back into the Started
+  // state very quickly
+  kApplicationStatePaused,
+
+  // A possible initial state where the web application can be running, loading
+  // data, and so on, but is not visible to the user, and has not ever been
+  // given any graphics resources.
+  kApplicationStatePreloading,
+
+  // The state where the application is running in the foreground, fully
+  // visible, with all necessary graphics resources available. A possible
+  // initial state, where loading happens while in the foreground.
+  kApplicationStateStarted,
+
+  // Representation of a idle/terminal/shutdown state with no resources.
+  kApplicationStateStopped,
+
+  // The application was running at some point, but has been backgrounded to the
+  // point where graphics resources are invalid and execution should be halted
+  // until resumption.
+  kApplicationStateSuspended,
+};
+
+}  // namespace base
+
+#endif  // COBALT_BASE_APPLICATION_STATE_H_
diff --git a/src/cobalt/base/base.gyp b/src/cobalt/base/base.gyp
index 4753208..f4f9f34 100644
--- a/src/cobalt/base/base.gyp
+++ b/src/cobalt/base/base.gyp
@@ -58,6 +58,8 @@
         'polymorphic_equatable.h',
         'ref_counted_lock.h',
         'source_location.h',
+        'startup_timer.cc',
+        'startup_timer.h',
         'stop_watch.cc',
         'stop_watch.h',
         'token.cc',
diff --git a/src/cobalt/base/c_val.h b/src/cobalt/base/c_val.h
index 48227bf..a589822 100644
--- a/src/cobalt/base/c_val.h
+++ b/src/cobalt/base/c_val.h
@@ -21,6 +21,7 @@
 #include <set>
 #include <sstream>
 #include <string>
+#include <type_traits>
 #include <vector>
 
 #include "base/compiler_specific.h"
@@ -31,6 +32,7 @@
 #include "base/synchronization/lock.h"
 #include "base/time.h"
 #include "cobalt/base/ref_counted_lock.h"
+#include "cobalt/base/type_id.h"
 
 // The CVal system allows you to mark certain variables to be part of the
 // CVal system and therefore analyzable and trackable by other systems.  All
@@ -92,21 +94,6 @@
 
 }  // namespace CValDetail
 
-// An enumeration to allow CVals to track the type that they hold in a run-time
-// variable.
-enum CValType {
-  kSize,
-  kBool,
-  kU32,
-  kU64,
-  kS32,
-  kS64,
-  kFloat,
-  kDouble,
-  kString,
-  kTimeDelta,
-};
-
 // CVals are commonly used for values that are in units of bytes.  By making
 // a CVal of type SizeInBytes, this can be made explicit, and allows the CVal
 // system to use KB/MB suffixes instead of K/M.
@@ -140,64 +127,6 @@
 }  // namespace cval
 
 namespace CValDetail {
-// Introduce a Traits class so that we can convert from C++ type to
-// CValType.
-template <typename T>
-struct Traits {
-  // If you get a compiler error here, you must add a Traits class specific to
-  // the variable type you would like to support.
-  int UnsupportedCValType[0];
-};
-template <>
-struct Traits<cval::SizeInBytes> {
-  static const CValType kTypeVal = kSize;
-  static const bool kIsNumerical = true;
-};
-template <>
-struct Traits<bool> {
-  static const CValType kTypeVal = kBool;
-  static const bool kIsNumerical = false;
-};
-template <>
-struct Traits<uint32_t> {
-  static const CValType kTypeVal = kU32;
-  static const bool kIsNumerical = true;
-};
-template <>
-struct Traits<uint64_t> {
-  static const CValType kTypeVal = kU64;
-  static const bool kIsNumerical = true;
-};
-template <>
-struct Traits<int32_t> {
-  static const CValType kTypeVal = kS32;
-  static const bool kIsNumerical = true;
-};
-template <>
-struct Traits<int64_t> {
-  static const CValType kTypeVal = kS64;
-  static const bool kIsNumerical = true;
-};
-template <>
-struct Traits<float> {
-  static const CValType kTypeVal = kFloat;
-  static const bool kIsNumerical = true;
-};
-template <>
-struct Traits<double> {
-  static const CValType kTypeVal = kDouble;
-  static const bool kIsNumerical = true;
-};
-template <>
-struct Traits<std::string> {
-  static const CValType kTypeVal = kString;
-  static const bool kIsNumerical = false;
-};
-template <>
-struct Traits<base::TimeDelta> {
-  static const CValType kTypeVal = kTimeDelta;
-  static const bool kIsNumerical = true;
-};
 
 // Provide methods to convert from an arbitrary type to a string, useful for
 // systems that want to read the value of a CVal without caring about its type.
@@ -340,13 +269,21 @@
   static std::string Call(const T& value) { return ValToString(value); }
 };
 
+template <typename T>
+struct IsNumerical {
+  static const bool value =
+      (std::is_arithmetic<T>::value && !std::is_same<T, bool>::value) ||
+      std::is_same<T, base::TimeDelta>::value ||
+      std::is_same<T, cval::SizeInBytes>::value;
+};
+
 // As opposed to ValToString, here we take the opportunity to clean up the
 // number a bit to make it easier for humans to digest.  For example, if a
 // number is much larger than one million, we can divide it by one million
 // and postfix it with an 'M'.
 template <typename T>
 std::string ValToPrettyString(const T& value) {
-  return ValToPrettyStringHelper<T, Traits<T>::kIsNumerical>::Call(value);
+  return ValToPrettyStringHelper<T, IsNumerical<T>::value>::Call(value);
 }
 
 // Provides methods for converting the type to and from a double.
@@ -398,7 +335,7 @@
 // retrieve the string version of the value.
 class CValGenericValue {
  public:
-  CValType GetType() const { return type_; }
+  TypeId GetTypeId() const { return type_id_; }
 
   // Return the value casted to the specified type.  The requested type must
   // match the contained value's actual native type.
@@ -411,19 +348,19 @@
   // Returns true if the type of this value is exactly T.
   template <typename T>
   bool IsNativeType() const {
-    return type_ == CValDetail::Traits<T>::kTypeVal;
+    return type_id_ == base::GetTypeId<T>();
   }
 
   virtual std::string AsString() const = 0;
   virtual std::string AsPrettyString() const = 0;
 
  protected:
-  CValGenericValue(CValType type, void* value_mem)
-      : type_(type), generic_value_(value_mem) {}
+  CValGenericValue(TypeId type_id, void* value_mem)
+      : type_id_(type_id), generic_value_(value_mem) {}
   virtual ~CValGenericValue() {}
 
  private:
-  CValType type_;
+  TypeId type_id_;
   void* generic_value_;
 };
 
@@ -439,9 +376,9 @@
 class CValSpecificValue : public CValGenericValue {
  public:
   explicit CValSpecificValue(const T& value)
-      : CValGenericValue(Traits<T>::kTypeVal, &value_), value_(value) {}
+      : CValGenericValue(base::GetTypeId<T>(), &value_), value_(value) {}
   CValSpecificValue(const CValSpecificValue<T>& other)
-      : CValGenericValue(Traits<T>::kTypeVal, &value_), value_(other.value_) {}
+      : CValGenericValue(base::GetTypeId<T>(), &value_), value_(other.value_) {}
   virtual ~CValSpecificValue() {}
 
   std::string AsString() const { return ValToString(value_); }
@@ -549,13 +486,13 @@
 
 class CValBase {
  public:
-  CValBase(const std::string& name, CValType type,
+  CValBase(const std::string& name, TypeId type_id,
            const std::string& description)
-      : name_(name), description_(description), type_(type) {}
+      : name_(name), description_(description), type_id_(type_id) {}
 
   const std::string& GetName() const { return name_; }
   const std::string& GetDescription() const { return description_; }
-  CValType GetType() const { return type_; }
+  TypeId GetTypeId() const { return type_id_; }
 
   virtual std::string GetValueAsString() const = 0;
   virtual std::string GetValueAsPrettyString() const = 0;
@@ -565,7 +502,7 @@
 
   std::string name_;
   std::string description_;
-  CValType type_;
+  TypeId type_id_;
 
   friend CValManager;
 };
@@ -577,12 +514,12 @@
  public:
   CValImpl(const std::string& name, const T& initial_value,
            const std::string& description)
-      : CValBase(name, Traits<T>::kTypeVal, description),
+      : CValBase(name, base::GetTypeId<T>(), description),
         value_(initial_value) {
     CommonConstructor();
   }
   CValImpl(const std::string& name, const std::string& description)
-      : CValBase(name, Traits<T>::kTypeVal, description) {
+      : CValBase(name, base::GetTypeId<T>(), description) {
     CommonConstructor();
   }
   virtual ~CValImpl() {
diff --git a/src/cobalt/base/c_val_collection_entry_stats.h b/src/cobalt/base/c_val_collection_entry_stats.h
index 28cd938..faf7ad1 100644
--- a/src/cobalt/base/c_val_collection_entry_stats.h
+++ b/src/cobalt/base/c_val_collection_entry_stats.h
@@ -21,6 +21,7 @@
 #include <string>
 #include <vector>
 
+#include "base/callback.h"
 #include "base/logging.h"
 #include "base/stringprintf.h"
 #include "base/time.h"
@@ -50,9 +51,38 @@
  public:
   static const size_t kNoMaxSize = 0;
 
-  CValCollectionEntryStatsImpl(const std::string& name,
-                               size_t max_size = kNoMaxSize,
-                               bool enable_entry_list_c_val = false);
+  struct FlushResults {
+    FlushResults(size_t sample_count, EntryType average, EntryType minimum,
+                 EntryType maximum, EntryType standard_deviation,
+                 EntryType percentile_25th, EntryType percentile_50th,
+                 EntryType percentile_75th, EntryType percentile_95th)
+        : sample_count(sample_count),
+          average(average),
+          minimum(minimum),
+          maximum(maximum),
+          standard_deviation(standard_deviation),
+          percentile_25th(percentile_25th),
+          percentile_50th(percentile_50th),
+          percentile_75th(percentile_75th),
+          percentile_95th(percentile_95th) {}
+
+    size_t sample_count;
+    EntryType average;
+    EntryType minimum;
+    EntryType maximum;
+    EntryType standard_deviation;
+    EntryType percentile_25th;
+    EntryType percentile_50th;
+    EntryType percentile_75th;
+    EntryType percentile_95th;
+  };
+
+  typedef base::Callback<void(const FlushResults&)> OnFlushCallback;
+
+  CValCollectionEntryStatsImpl(
+      const std::string& name, size_t max_size = kNoMaxSize,
+      bool enable_entry_list_c_val = false,
+      const OnFlushCallback& on_flush = OnFlushCallback());
 
   // Add an entry to the collection. This may trigger a Flush() if adding the
   // entry causes the max size to be reached.
@@ -76,6 +106,10 @@
 
   // The maximum size of the collection before Flush() is automatically called.
   const size_t max_size_;
+
+  // Callback to call whenever the values are flushed.
+  const OnFlushCallback on_flush_;
+
   // The current collection of entries. These will be used to generate the cval
   // stats during the next call of Flush().
   CollectionType collection_;
@@ -96,10 +130,12 @@
 
 template <typename EntryType, typename Visibility>
 CValCollectionEntryStatsImpl<EntryType, Visibility>::
-    CValCollectionEntryStatsImpl(const std::string& name,
-                                 size_t max_size /*=kNoMaxSize*/,
-                                 bool enable_entry_list_c_val /*=false*/)
+    CValCollectionEntryStatsImpl(
+        const std::string& name, size_t max_size /*=kNoMaxSize*/,
+        bool enable_entry_list_c_val /*=false*/,
+        const OnFlushCallback& on_flush /*=OnFlushCallback()*/)
     : max_size_(max_size),
+      on_flush_(on_flush),
       count_(StringPrintf("%s.Cnt", name.c_str()), 0, "Total entries."),
       average_(StringPrintf("%s.Avg", name.c_str()), EntryType(),
                "Average value."),
@@ -152,18 +188,36 @@
   double mean = CValDetail::ToDouble<EntryType>(sum) / collection_.size();
 
   // Update the collection stat cvals.
-  count_ = collection_.size();
-  average_ = CValDetail::FromDouble<EntryType>(mean);
-  minimum_ = collection_.front();
-  maximum_ = collection_.back();
-  percentile_25th_ = CalculatePercentile(collection_, 25);
-  percentile_50th_ = CalculatePercentile(collection_, 50);
-  percentile_75th_ = CalculatePercentile(collection_, 75);
-  percentile_95th_ = CalculatePercentile(collection_, 95);
-  standard_deviation_ = CValDetail::FromDouble<EntryType>(
+  size_t count = collection_.size();
+  EntryType average = CValDetail::FromDouble<EntryType>(mean);
+  EntryType minimum = collection_.front();
+  EntryType maximum = collection_.back();
+  EntryType percentile_25th = CalculatePercentile(collection_, 25);
+  EntryType percentile_50th = CalculatePercentile(collection_, 50);
+  EntryType percentile_75th = CalculatePercentile(collection_, 75);
+  EntryType percentile_95th = CalculatePercentile(collection_, 95);
+  EntryType standard_deviation = CValDetail::FromDouble<EntryType>(
       CalculateStandardDeviation(collection_, mean));
 
+  // Flush the computed values out to the CVals.
+  count_ = count;
+  average_ = average;
+  minimum_ = minimum;
+  maximum_ = maximum;
+  percentile_25th_ = percentile_25th;
+  percentile_50th_ = percentile_50th;
+  percentile_75th_ = percentile_75th;
+  percentile_95th_ = percentile_95th;
+  standard_deviation_ = standard_deviation;
+
   collection_.clear();
+
+  // Callback to any listeners with the flush values.
+  if (!on_flush_.is_null()) {
+    on_flush_.Run(FlushResults(
+        count, average, minimum, maximum, standard_deviation, percentile_25th,
+        percentile_50th, percentile_75th, percentile_95th));
+  }
 }
 
 template <typename EntryType, typename Visibility>
@@ -219,7 +273,6 @@
 
   double variance =
       dif_squared_sum / static_cast<double>(collection.size() - 1);
-  variance = std::max(variance, 0.0);
 
   return std::sqrt(variance);
 }
@@ -290,11 +343,15 @@
 #endif  // ENABLE_DEBUG_C_VAL
 
  public:
+  typedef typename CValParent::OnFlushCallback OnFlushCallback;
+  typedef typename CValParent::FlushResults FlushResults;
+
   explicit CValCollectionEntryStats(const std::string& name)
       : CValParent(name) {}
   CValCollectionEntryStats(const std::string& name, size_t max_size,
-                           bool enable_entry_list_c_val)
-      : CValParent(name, max_size, enable_entry_list_c_val) {}
+                           bool enable_entry_list_c_val,
+                           const OnFlushCallback& on_flush = OnFlushCallback())
+      : CValParent(name, max_size, enable_entry_list_c_val, on_flush) {}
 };
 
 // CVals with visibility set to CValPublic are always tracked though the CVal
@@ -306,11 +363,15 @@
       CValParent;
 
  public:
+  typedef typename CValParent::OnFlushCallback OnFlushCallback;
+  typedef typename CValParent::FlushResults FlushResults;
+
   explicit CValCollectionEntryStats(const std::string& name)
       : CValParent(name) {}
   CValCollectionEntryStats(const std::string& name, size_t max_size,
-                           bool enable_entry_list_c_val)
-      : CValParent(name, max_size, enable_entry_list_c_val) {}
+                           bool enable_entry_list_c_val,
+                           const OnFlushCallback& on_flush = OnFlushCallback())
+      : CValParent(name, max_size, enable_entry_list_c_val, on_flush) {}
 };
 
 }  // namespace base
diff --git a/src/cobalt/base/c_val_collection_timer_stats.h b/src/cobalt/base/c_val_collection_timer_stats.h
index eda96b9..a40fb20 100644
--- a/src/cobalt/base/c_val_collection_timer_stats.h
+++ b/src/cobalt/base/c_val_collection_timer_stats.h
@@ -31,11 +31,17 @@
 template <typename Visibility = CValDebug>
 class CValCollectionTimerStats {
  public:
+  typedef typename base::CValCollectionEntryStats<
+      base::TimeDelta, Visibility>::OnFlushCallback OnFlushCallback;
+  typedef typename base::CValCollectionEntryStats<
+      base::TimeDelta, Visibility>::FlushResults FlushResults;
+
   explicit CValCollectionTimerStats(const std::string& name)
       : entry_stats_(name) {}
   CValCollectionTimerStats(const std::string& name, size_t max_size,
-                           bool enable_entry_list_c_val)
-      : entry_stats_(name, max_size, enable_entry_list_c_val) {}
+                           bool enable_entry_list_c_val,
+                           const OnFlushCallback& on_flush = OnFlushCallback())
+      : entry_stats_(name, max_size, enable_entry_list_c_val, on_flush) {}
 
   // Start the timer. If the timer is currently running, it is stopped and
   // re-started. If no time is provided, then |base::TimeTicks::Now()| is used.
diff --git a/src/cobalt/base/c_val_test.cc b/src/cobalt/base/c_val_test.cc
index 3dc416d..1087ad2 100644
--- a/src/cobalt/base/c_val_test.cc
+++ b/src/cobalt/base/c_val_test.cc
@@ -458,7 +458,7 @@
     void OnValueChanged(const std::string& name,
                         const base::CValGenericValue& value) OVERRIDE {
       EXPECT_EQ(name, "S32");
-      EXPECT_EQ(value.GetType(), base::kS32);
+      EXPECT_EQ(value.GetTypeId(), GetTypeId<int32_t>());
       EXPECT_EQ(value.IsNativeType<int32_t>(), true);
       EXPECT_EQ(value.AsNativeType<int32_t>(), 1);
       EXPECT_EQ(value.AsString(), "1");
diff --git a/src/cobalt/base/camera_transform.h b/src/cobalt/base/camera_transform.h
index ef0fafa..875e7f7 100644
--- a/src/cobalt/base/camera_transform.h
+++ b/src/cobalt/base/camera_transform.h
@@ -50,15 +50,6 @@
   }
 };
 
-// Structure to keep track of the current orientation state.
-struct CameraOrientation {
-  CameraOrientation() : roll(0.0f), pitch(0.0f), yaw(0.0f) {}
-
-  float roll;
-  float pitch;
-  float yaw;
-};
-
 }  // namespace base
 
 #endif  // COBALT_BASE_CAMERA_TRANSFORM_H_
diff --git a/src/cobalt/base/message_queue.h b/src/cobalt/base/message_queue.h
index 26d0619..4f6569c 100644
--- a/src/cobalt/base/message_queue.h
+++ b/src/cobalt/base/message_queue.h
@@ -17,6 +17,7 @@
 
 #include <queue>
 #include "base/callback.h"
+#include "base/debug/trace_event.h"
 #include "base/logging.h"
 #include "base/synchronization/lock.h"
 
@@ -35,6 +36,7 @@
 
   // Add a message to the end of the queue.
   void AddMessage(const base::Closure& message) {
+    TRACE_EVENT0("cobalt::base", "MessageQueue::AddMessage()");
     base::AutoLock lock(mutex_);
     DCHECK(!message.is_null());
     queue_.push(message);
@@ -43,11 +45,16 @@
   // Execute all messages in the MessageQueue until the queue is empty and then
   // return.
   void ProcessAll() {
-    base::AutoLock lock(mutex_);
+    TRACE_EVENT0("cobalt::base", "MessageQueue::ProcessAll()");
+    std::queue<base::Closure> work_queue;
+    {
+      base::AutoLock lock(mutex_);
+      work_queue.swap(queue_);
+    }
 
-    while (!queue_.empty()) {
-      queue_.front().Run();
-      queue_.pop();
+    while (!work_queue.empty()) {
+      work_queue.front().Run();
+      work_queue.pop();
     }
   }
 
diff --git a/src/cobalt/base/poller.h b/src/cobalt/base/poller.h
index b63ff9c..25e104e 100644
--- a/src/cobalt/base/poller.h
+++ b/src/cobalt/base/poller.h
@@ -17,7 +17,6 @@
 
 #include "base/callback.h"
 #include "base/message_loop.h"
-#include "base/synchronization/waitable_event.h"
 #include "base/threading/thread.h"
 #include "base/timer.h"
 
@@ -46,15 +45,9 @@
     if (!message_loop_) {
       StopTimer();
     } else {
-      message_loop_->PostTask(
-          FROM_HERE, base::Bind(&Poller::StopTimer, base::Unretained(this)));
-
       // Wait for the timer to actually be stopped.
-      base::WaitableEvent timer_stopped(true, false);
-      message_loop_->PostTask(FROM_HERE,
-                              base::Bind(&base::WaitableEvent::Signal,
-                                         base::Unretained(&timer_stopped)));
-      timer_stopped.Wait();
+      message_loop_->PostBlockingTask(
+          FROM_HERE, base::Bind(&Poller::StopTimer, base::Unretained(this)));
     }
   }
 
diff --git a/src/cobalt/base/startup_timer.cc b/src/cobalt/base/startup_timer.cc
new file mode 100644
index 0000000..ef01092
--- /dev/null
+++ b/src/cobalt/base/startup_timer.cc
@@ -0,0 +1,48 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/base/startup_timer.h"
+
+#include "starboard/once.h"
+
+namespace base {
+namespace StartupTimer {
+namespace {
+
+// StartupTimer is designed to measure time since the startup of the app.
+// It is loader initialized to have the most accurate start time possible.
+class Impl {
+ public:
+  static Impl* Instance();
+  base::TimeTicks StartTime() const { return start_time_; }
+  base::TimeDelta TimeElapsed() const {
+    return base::TimeTicks::Now() - start_time_;
+  }
+
+ private:
+  Impl() : start_time_(base::TimeTicks::Now()) {}
+
+  base::TimeTicks start_time_;
+};
+
+SB_ONCE_INITIALIZE_FUNCTION(Impl, Impl::Instance);
+Impl* s_on_startup_init_dont_use = Impl::Instance();
+
+}  // namespace
+
+TimeTicks StartTime() { return Impl::Instance()->StartTime(); }
+TimeDelta TimeElapsed() { return Impl::Instance()->TimeElapsed(); }
+
+}  // namespace StartupTimer
+}  // namespace base
diff --git a/src/cobalt/base/startup_timer.h b/src/cobalt/base/startup_timer.h
new file mode 100644
index 0000000..b9600e8
--- /dev/null
+++ b/src/cobalt/base/startup_timer.h
@@ -0,0 +1,32 @@
+// 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_STARTUP_TIMER_H_
+#define COBALT_BASE_STARTUP_TIMER_H_
+
+#include "base/time.h"
+
+namespace base {
+namespace StartupTimer {
+
+// Returns the time when the app started.
+TimeTicks StartTime();
+
+// Returns the time elapsed since the app started.
+TimeDelta TimeElapsed();
+
+}  // namespace StartupTimer
+}  // namespace base
+
+#endif  // COBALT_BASE_STARTUP_TIMER_H_
diff --git a/src/cobalt/base/tokens.h b/src/cobalt/base/tokens.h
index 8929dd1..079661b 100644
--- a/src/cobalt/base/tokens.h
+++ b/src/cobalt/base/tokens.h
@@ -50,7 +50,9 @@
     MacroOpWithNameOnly(change)                                      \
     MacroOpWithNameOnly(characterData)                               \
     MacroOpWithNameOnly(childList)                                   \
+    MacroOpWithNameOnly(click)                                       \
     MacroOpWithNameOnly(close)                                       \
+    MacroOpWithNameOnly(deviceorientation)                           \
     MacroOpWithNameOnly(durationchange)                              \
     MacroOpWithNameOnly(emptied)                                     \
     MacroOpWithNameOnly(end)                                         \
@@ -70,9 +72,23 @@
     MacroOpWithNameOnly(loadstart)                                   \
     MacroOpWithNameOnly(mark)                                        \
     MacroOpWithNameOnly(message)                                     \
+    MacroOpWithNameOnly(mousedown)                                   \
+    MacroOpWithNameOnly(mouseenter)                                  \
+    MacroOpWithNameOnly(mouseleave)                                  \
+    MacroOpWithNameOnly(mousemove)                                   \
+    MacroOpWithNameOnly(mouseout)                                    \
+    MacroOpWithNameOnly(mouseover)                                   \
+    MacroOpWithNameOnly(mouseup)                                     \
     MacroOpWithNameOnly(nomatch)                                     \
     MacroOpWithNameOnly(off)                                         \
     MacroOpWithNameOnly(open)                                        \
+    MacroOpWithNameOnly(pointerdown)                                 \
+    MacroOpWithNameOnly(pointerenter)                                \
+    MacroOpWithNameOnly(pointerleave)                                \
+    MacroOpWithNameOnly(pointermove)                                 \
+    MacroOpWithNameOnly(pointerout)                                  \
+    MacroOpWithNameOnly(pointerover)                                 \
+    MacroOpWithNameOnly(pointerup)                                   \
     MacroOpWithNameOnly(pause)                                       \
     MacroOpWithNameOnly(play)                                        \
     MacroOpWithNameOnly(playing)                                     \
@@ -105,15 +121,18 @@
     MacroOpWithNameOnly(update)                                      \
     MacroOpWithNameOnly(updateend)                                   \
     MacroOpWithNameOnly(updatestart)                                 \
+    MacroOpWithNameOnly(visibilitychange)                            \
     MacroOpWithNameOnly(voiceschanged)                               \
     MacroOpWithNameOnly(volumechange)                                \
-    MacroOpWithNameOnly(waiting)
+    MacroOpWithNameOnly(waiting)                                     \
+    MacroOpWithNameOnly(wheel)
 
 #define TOKENS_FOR_EACH_WITH_NAME_AND_VALUE(MacroOpWithNameAndValue)    \
     MacroOpWithNameAndValue(active_pseudo_class_selector, "active")     \
     MacroOpWithNameAndValue(after_pseudo_element_selector, "after")     \
     MacroOpWithNameAndValue(aria_atomic, "aria-atomic")                 \
     MacroOpWithNameAndValue(aria_busy, "aria-busy")                     \
+    MacroOpWithNameAndValue(aria_describedby, "aria-describedby")       \
     MacroOpWithNameAndValue(aria_hidden, "aria-hidden")                 \
     MacroOpWithNameAndValue(aria_label, "aria-label")                   \
     MacroOpWithNameAndValue(aria_labelledby, "aria-labelledby")         \
diff --git a/src/cobalt/base/type_id.h b/src/cobalt/base/type_id.h
index de6036a..5d4ae41 100644
--- a/src/cobalt/base/type_id.h
+++ b/src/cobalt/base/type_id.h
@@ -52,6 +52,9 @@
   bool operator==(const TypeId& other) const { return value_ == other.value_; }
   bool operator!=(const TypeId& other) const { return !(*this == other); }
   bool operator<(const TypeId& other) const { return value_ < other.value_; }
+  bool operator<=(const TypeId& other) const { return value_ <= other.value_; }
+  bool operator>(const TypeId& other) const { return value_ > other.value_; }
+  bool operator>=(const TypeId& other) const { return value_ >= other.value_; }
 
  private:
   explicit TypeId(intptr_t value) : value_(value) {}
diff --git a/src/cobalt/bindings/templates/dictionary.h.template b/src/cobalt/bindings/templates/dictionary.h.template
index 686ed73..20c58ff 100644
--- a/src/cobalt/bindings/templates/dictionary.h.template
+++ b/src/cobalt/bindings/templates/dictionary.h.template
@@ -43,6 +43,7 @@
 {% endif %}
 #include <string>
 
+#include "base/optional.h"
 #include "cobalt/script/sequence.h"
 {% for include in includes %}
 #include "{{include}}"
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc
index 5c3231b..02597f5 100644
--- a/src/cobalt/browser/application.cc
+++ b/src/cobalt/browser/application.cc
@@ -32,6 +32,7 @@
 #include "cobalt/base/init_cobalt.h"
 #include "cobalt/base/language.h"
 #include "cobalt/base/localized_strings.h"
+#include "cobalt/base/startup_timer.h"
 #include "cobalt/base/user_log.h"
 #include "cobalt/browser/memory_settings/auto_mem.h"
 #include "cobalt/browser/memory_settings/checker.h"
@@ -41,6 +42,7 @@
 #include "cobalt/loader/image/image_decoder.h"
 #include "cobalt/math/size.h"
 #include "cobalt/network/network_event.h"
+#include "cobalt/script/javascript_engine.h"
 #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
 #include "cobalt/storage/savegame_fake.h"
 #endif
@@ -53,21 +55,14 @@
 #endif  // defined(__LB_SHELL__FOR_RELEASE__)
 #include "lbshell/src/lb_memory_pages.h"
 #endif  // defined(__LB_SHELL__)
-#if defined(OS_STARBOARD)
 #include "starboard/configuration.h"
 #include "starboard/log.h"
-#endif  // defined(OS_STARBOARD)
 
 namespace cobalt {
 namespace browser {
 
 namespace {
 const int kStatUpdatePeriodMs = 1000;
-#if defined(COBALT_BUILD_TYPE_GOLD)
-const int kLiteStatUpdatePeriodMs = 1000;
-#else
-const int kLiteStatUpdatePeriodMs = 16;
-#endif
 
 const char kDefaultURL[] = "https://www.youtube.com/tv";
 
@@ -345,10 +340,12 @@
 
   // Right now the bytes_per_pixel is assumed in the engine. Any other value
   // is currently forbidden.
-  DCHECK_EQ(2, skia_glyph_atlas_texture_dimensions.bytes_per_pixel());
-  options->renderer_module_options.skia_glyph_texture_atlas_dimensions =
-      math::Size(skia_glyph_atlas_texture_dimensions.width(),
-                 skia_glyph_atlas_texture_dimensions.height());
+  if (skia_glyph_atlas_texture_dimensions.bytes_per_pixel() > 0) {
+    DCHECK_EQ(2, skia_glyph_atlas_texture_dimensions.bytes_per_pixel());
+    options->renderer_module_options.skia_glyph_texture_atlas_dimensions =
+        math::Size(skia_glyph_atlas_texture_dimensions.width(),
+                   skia_glyph_atlas_texture_dimensions.height());
+  }
 
   options->web_module_options.remote_typeface_cache_capacity =
       static_cast<int>(
@@ -365,9 +362,7 @@
 Application::Application(const base::Closure& quit_closure)
     : message_loop_(MessageLoop::current()),
       quit_closure_(quit_closure),
-      start_time_(base::TimeTicks::Now()),
-      stats_update_timer_(true, true),
-      lite_stats_update_timer_(true, true) {
+      stats_update_timer_(true, true) {
   // Check to see if a timed_trace has been set, indicating that we should
   // begin a timed trace upon startup.
   base::TimeDelta trace_duration = GetTimedTraceDuration();
@@ -392,10 +387,6 @@
   stats_update_timer_.Start(
       FROM_HERE, base::TimeDelta::FromMilliseconds(kStatUpdatePeriodMs),
       base::Bind(&Application::UpdatePeriodicStats, base::Unretained(this)));
-  lite_stats_update_timer_.Start(
-      FROM_HERE, base::TimeDelta::FromMilliseconds(kLiteStatUpdatePeriodMs),
-      base::Bind(&Application::UpdatePeriodicLiteStats,
-                 base::Unretained(this)));
 
   // Get the initial URL.
   GURL initial_url = GetInitialURL();
@@ -416,6 +407,13 @@
   options.initial_deep_link = GetInitialDeepLink();
   options.network_module_options.preferred_language = language;
 
+  if (command_line->HasSwitch(browser::switches::kFPSPrint)) {
+    options.renderer_module_options.enable_fps_stdout = true;
+  }
+  if (command_line->HasSwitch(browser::switches::kFPSOverlay)) {
+    options.renderer_module_options.enable_fps_overlay = true;
+  }
+
   ApplyCommandLineSettingsToRendererOptions(&options.renderer_module_options);
 
   if (command_line->HasSwitch(browser::switches::kDisableJavaScriptJit)) {
@@ -523,8 +521,9 @@
   }
 
   account_manager_.reset(new account::AccountManager());
-  browser_module_.reset(new BrowserModule(initial_url, system_window_.get(),
-                                          account_manager_.get(), options));
+  browser_module_.reset(
+      new BrowserModule(initial_url, base::kApplicationStateStarted,
+                        system_window_.get(), account_manager_.get(), options));
   UpdateAndMaybeRegisterUserAgent();
 
   app_status_ = kRunningAppStatus;
@@ -650,10 +649,12 @@
     DLOG(INFO) << "Got pause event.";
     app_status_ = kPausedAppStatus;
     ++app_pause_count_;
+    browser_module_->Pause();
   } else if (app_event->type() == system_window::ApplicationEvent::kUnpause) {
     DLOG(INFO) << "Got unpause event.";
     app_status_ = kRunningAppStatus;
     ++app_unpause_count_;
+    browser_module_->Unpause();
   } else if (app_event->type() == system_window::ApplicationEvent::kSuspend) {
     DLOG(INFO) << "Got suspend event.";
     app_status_ = kSuspendedAppStatus;
@@ -693,20 +694,21 @@
                       "Total free application CPU memory remaining."),
       used_cpu_memory("Memory.CPU.Used", 0,
                       "Total CPU memory allocated via the app's allocators."),
-#if !defined(__LB_SHELL__FOR_RELEASE__)
-      exe_memory("Memory.CPU.Exe", 0,
-                 "Total memory occupied by the size of the executable."),
-#endif
+      js_reserved_memory("Memory.JS", 0,
+                         "The total memory that is reserved by the engine, "
+                         "including the part that is actually occupied by "
+                         "JS objects, and the part that is not yet."),
+      app_start_time("Time.Cobalt.Start",
+                     base::StartupTimer::StartTime().ToInternalValue(),
+                     "Start time of the application in microseconds."),
       app_lifetime("Cobalt.Lifetime", base::TimeDelta(),
-                   "Application lifetime.") {
-#if defined(OS_STARBOARD)
+                   "Application lifetime in microseconds.") {
   if (SbSystemHasCapability(kSbSystemCapabilityCanQueryGPUMemoryStats)) {
     free_gpu_memory.emplace("Memory.GPU.Free", 0,
                             "Total free application GPU memory remaining.");
     used_gpu_memory.emplace("Memory.GPU.Used", 0,
                             "Total GPU memory allocated by the application.");
   }
-#endif  // defined(OS_STARBOARD)
 }
 
 void Application::RegisterUserLogs() {
@@ -757,10 +759,6 @@
   }
 }
 
-void Application::UpdatePeriodicLiteStats() {
-  c_val_stats_.app_lifetime = base::TimeTicks::Now() - start_time_;
-}
-
 math::Size Application::InitSystemWindow(CommandLine* command_line) {
   base::optional<math::Size> viewport_size;
   if (command_line->HasSwitch(browser::switches::kViewport)) {
@@ -814,6 +812,8 @@
 
 void Application::UpdatePeriodicStats() {
   TRACE_EVENT0("cobalt::browser", "Application::UpdatePeriodicStats()");
+  c_val_stats_.app_lifetime = base::StartupTimer::TimeElapsed();
+
   int64_t used_cpu_memory = SbSystemGetUsedCPUMemory();
   base::optional<int64_t> used_gpu_memory;
   if (SbSystemHasCapability(kSbSystemCapabilityCanQueryGPUMemoryStats)) {
@@ -831,6 +831,9 @@
     *c_val_stats_.used_gpu_memory = *used_gpu_memory;
   }
 
+  c_val_stats_.js_reserved_memory =
+      script::JavaScriptEngine::UpdateMemoryStatsAndReturnReserved();
+
   memory_settings_checker_.RunChecks(
       *auto_mem_, used_cpu_memory, used_gpu_memory);
 }
diff --git a/src/cobalt/browser/application.h b/src/cobalt/browser/application.h
index 47da7ec..6da8a33 100644
--- a/src/cobalt/browser/application.h
+++ b/src/cobalt/browser/application.h
@@ -96,9 +96,6 @@
   base::ThreadChecker network_event_thread_checker_;
   base::ThreadChecker application_event_thread_checker_;
 
-  // Time the application started
-  base::TimeTicks start_time_;
-
 #if defined(ENABLE_WEBDRIVER)
   // WebDriver implementation with embedded HTTP server.
   scoped_ptr<webdriver::WebDriverModule> web_driver_module_;
@@ -141,10 +138,11 @@
     base::optional<base::CVal<base::cval::SizeInBytes, base::CValPublic> >
         used_gpu_memory;
 
-#if !defined(__LB_SHELL__FOR_RELEASE__)
-    base::CVal<base::cval::SizeInBytes, base::CValPublic> exe_memory;
-#endif
+    // The total memory that is reserved by the engine, including the part that
+    // is actually occupied by JS objects, and the part that is not yet.
+    base::CVal<base::cval::SizeInBytes, base::CValPublic> js_reserved_memory;
 
+    base::CVal<int64> app_start_time;
     base::CVal<base::TimeDelta, base::CValPublic> app_lifetime;
   };
 
@@ -152,7 +150,6 @@
   void UpdateAndMaybeRegisterUserAgent();
 
   void UpdatePeriodicStats();
-  void UpdatePeriodicLiteStats();
 
   math::Size InitSystemWindow(CommandLine* command_line);
 
@@ -172,7 +169,6 @@
   CValStats c_val_stats_;
 
   base::Timer stats_update_timer_;
-  base::Timer lite_stats_update_timer_;
 
   scoped_ptr<memory_tracker::Tool> memory_tracker_tool_;
 
diff --git a/src/cobalt/browser/browser.gyp b/src/cobalt/browser/browser.gyp
index 85a49b2..0741ae3 100644
--- a/src/cobalt/browser/browser.gyp
+++ b/src/cobalt/browser/browser.gyp
@@ -14,7 +14,7 @@
 
 {
   'variables': {
-    'cobalt_code': 1,
+    'sb_pedantic_warnings': 1,
   },
   'targets': [
     {
@@ -29,6 +29,7 @@
         'debug_console.h',
         'h5vcc_url_handler.cc',
         'h5vcc_url_handler.h',
+        'lifecycle_observer.h',
         'memory_settings/auto_mem.cc',
         'memory_settings/auto_mem.h',
         'memory_settings/build_settings.cc',
@@ -84,6 +85,8 @@
         'splash_screen.h',
         'storage_upgrade_handler.cc',
         'storage_upgrade_handler.h',
+        'suspend_fuzzer.cc',
+        'suspend_fuzzer.h',
         'switches.cc',
         'switches.h',
         'trace_manager.cc',
@@ -126,6 +129,7 @@
         '<(DEPTH)/cobalt/math/math.gyp:math',
         '<(DEPTH)/cobalt/media_session/media_session.gyp:media_session',
         '<(DEPTH)/cobalt/network/network.gyp:network',
+        '<(DEPTH)/cobalt/page_visibility/page_visibility.gyp:page_visibility',
         '<(DEPTH)/cobalt/renderer/renderer.gyp:renderer',
         '<(DEPTH)/cobalt/script/engine.gyp:engine',
         '<(DEPTH)/cobalt/speech/speech.gyp:speech',
@@ -195,7 +199,7 @@
       'variables': {
         # This target includes non-Cobalt code that produces pendantic
         # warnings as errors.
-        'cobalt_code': 0,
+        'sb_pedantic_warnings': 0,
       },
       'conditions': [
         ['enable_screenshot==1', {
diff --git a/src/cobalt/browser/browser_bindings_gen.gyp b/src/cobalt/browser/browser_bindings_gen.gyp
index b954983..0ff0e7b 100644
--- a/src/cobalt/browser/browser_bindings_gen.gyp
+++ b/src/cobalt/browser/browser_bindings_gen.gyp
@@ -66,6 +66,7 @@
         '../dom/console.idl',
         '../dom/crypto.idl',
         '../dom/data_view.idl',
+        '../dom/device_orientation_event.idl',
         '../dom/document.idl',
         '../dom/document_timeline.idl',
         '../dom/document_type.idl',
@@ -113,6 +114,7 @@
         '../dom/memory_info.idl',
         '../dom/message_event.idl',
         '../dom/mime_type_array.idl',
+        '../dom/mouse_event.idl',
         '../dom/mutation_observer.idl',
         '../dom/mutation_record.idl',
         '../dom/named_node_map.idl',
@@ -122,6 +124,7 @@
         '../dom/performance.idl',
         '../dom/performance_timing.idl',
         '../dom/plugin_array.idl',
+        '../dom/pointer_event.idl',
         '../dom/progress_event.idl',
         '../dom/screen.idl',
         '../dom/security_policy_violation_event.idl',
@@ -144,6 +147,7 @@
         '../dom/video_playback_quality.idl',
         '../dom/video_track.idl',
         '../dom/video_track_list.idl',
+        '../dom/wheel_event.idl',
         '../dom/window.idl',
         '../dom/xml_document.idl',
         '../dom/xml_serializer.idl',
@@ -212,17 +216,26 @@
         '../audio/audio_node_channel_count_mode.idl',
         '../audio/audio_node_channel_interpretation.idl',
         '../dom/blob_property_bag.idl',
+        '../dom/device_orientation_event_init.idl',
         '../dom/dom_parser_supported_type.idl',
         '../dom/event_init.idl',
+        '../dom/event_modifier_init.idl',
+        '../dom/focus_event_init.idl',
+        '../dom/keyboard_event_init.idl',
+        '../dom/ui_event_init.idl',
         '../dom/media_source_end_of_stream_error.idl',
         '../dom/media_source_ready_state.idl',
+        '../dom/mouse_event_init.idl',
         '../dom/mutation_observer_init.idl',
+        '../dom/pointer_event_init.idl',
         '../dom/source_buffer_append_mode.idl',
         '../dom/track_default_type.idl',
+        '../dom/wheel_event_init.idl',
         '../media_session/media_image.idl',
         '../media_session/media_metadata_init.idl',
         '../media_session/media_session_action.idl',
         '../media_session/media_session_playback_state.idl',
+        '../page_visibility/visibility_state.idl',
         '../speech/speech_recognition_error_code.idl',
         '../speech/speech_synthesis_error_code.idl',
         '../web_animations/animation_fill_mode.idl',
@@ -266,6 +279,7 @@
         '../dom/window_session_storage.idl',
         '../dom/window_timers.idl',
         '../media_session/navigator_media_session.idl',
+        '../page_visibility/document.idl',
     ],
 
     'conditions': [
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index 50c5e80..d5c6719 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -209,6 +209,7 @@
 }  // namespace
 
 BrowserModule::BrowserModule(const GURL& url,
+                             base::ApplicationState initial_application_state,
                              system_window::SystemWindow* system_window,
                              account::AccountManager* account_manager,
                              const Options& options)
@@ -226,13 +227,17 @@
       input_device_manager_(input::InputDeviceManager::CreateFromWindow(
           base::Bind(&BrowserModule::OnKeyEventProduced,
                      base::Unretained(this)),
+          base::Bind(&BrowserModule::OnPointerEventProduced,
+                     base::Unretained(this)),
+          base::Bind(&BrowserModule::OnWheelEventProduced,
+                     base::Unretained(this)),
           system_window)),
       renderer_module_(system_window, RendererModuleWithCameraOptions(
                                           options.renderer_module_options,
                                           input_device_manager_->camera_3d())),
 #if defined(ENABLE_GPU_ARRAY_BUFFER_ALLOCATOR)
-      array_buffer_allocator_(new ResourceProviderArrayBufferAllocator(
-          renderer_module_.pipeline()->GetResourceProvider())),
+      array_buffer_allocator_(
+          new ResourceProviderArrayBufferAllocator(GetResourceProvider())),
       array_buffer_cache_(new dom::ArrayBuffer::Cache(3 * 1024 * 1024)),
 #endif  // defined(ENABLE_GPU_ARRAY_BUFFER_ALLOCATOR)
       network_module_(&storage_manager_, system_window->event_dispatcher(),
@@ -245,6 +250,10 @@
       web_module_loaded_(true /* manually_reset */,
                          false /* initially_signalled */),
       web_module_recreated_callback_(options.web_module_recreated_callback),
+      navigate_time_("Time.Browser.Navigate", 0,
+                     "The last time a navigation occurred."),
+      on_load_event_time_("Time.Browser.OnLoadEvent", 0,
+                          "The last time the window.OnLoad event fired."),
 #if defined(ENABLE_DEBUG_CONSOLE)
       ALLOW_THIS_IN_INITIALIZER_LIST(fuzzer_toggle_command_handler_(
           kFuzzerToggleCommand,
@@ -269,8 +278,8 @@
       render_timeout_count_(0),
 #endif
       will_quit_(false),
-      suspended_(false),
-      system_window_(system_window) {
+      system_window_(system_window),
+      application_state_(initial_application_state) {
 #if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
   SbCoreDumpRegisterHandler(BrowserModule::CoreDumpHandler, this);
   on_error_triggered_count_ = 0;
@@ -304,10 +313,9 @@
     GetVideoContainerSizeOverride(&output_size);
 #endif
 
-    media_module_ = (media::MediaModule::Create(
-        system_window, output_size,
-        renderer_module_.pipeline()->GetResourceProvider(),
-        options.media_module_options));
+    media_module_ = (media::MediaModule::Create(system_window, output_size,
+                                                GetResourceProvider(),
+                                                options.media_module_options));
   }
 
   // Setup our main web module to have the H5VCC API injected into it.
@@ -326,18 +334,24 @@
   if (command_line->HasSwitch(switches::kInputFuzzer)) {
     OnFuzzerToggle(std::string());
   }
+  if (command_line->HasSwitch(switches::kSuspendFuzzer)) {
+#if SB_API_VERSION >= 4
+    suspend_fuzzer_.emplace();
+#endif
+  }
 #endif  // ENABLE_DEBUG_CONSOLE && ENABLE_DEBUG_COMMAND_LINE_SWITCHES
 
 #if defined(ENABLE_DEBUG_CONSOLE)
   debug_console_.reset(new DebugConsole(
+      application_state_,
       base::Bind(&BrowserModule::QueueOnDebugConsoleRenderTreeProduced,
                  base::Unretained(this)),
       media_module_.get(), &network_module_,
-      renderer_module_.render_target()->GetSize(),
-      renderer_module_.pipeline()->GetResourceProvider(),
+      renderer_module_.render_target()->GetSize(), GetResourceProvider(),
       kLayoutMaxRefreshFrequencyInHz,
       base::Bind(&BrowserModule::GetDebugServer, base::Unretained(this)),
       web_module_options_.javascript_options));
+  lifecycle_observers_.AddObserver(debug_console_.get());
 #endif  // defined(ENABLE_DEBUG_CONSOLE)
 
   // Always render the debug console. It will draw nothing if disabled.
@@ -405,17 +419,25 @@
   // Destroy old WebModule first, so we don't get a memory high-watermark after
   // the second WebModule's constructor runs, but before scoped_ptr::reset() is
   // run.
+  if (web_module_) {
+    lifecycle_observers_.RemoveObserver(web_module_.get());
+  }
   web_module_.reset(NULL);
 
+  // Wait until after the old WebModule is destroyed before setting the navigate
+  // time so that it won't be included in the time taken to load the URL.
+  navigate_time_ = base::TimeTicks::Now().ToInternalValue();
+
   // Show a splash screen while we're waiting for the web page to load.
   const math::Size& viewport_size = renderer_module_.render_target()->GetSize();
   DestroySplashScreen();
   splash_screen_.reset(
-      new SplashScreen(base::Bind(&BrowserModule::QueueOnRenderTreeProduced,
+      new SplashScreen(application_state_,
+                       base::Bind(&BrowserModule::QueueOnRenderTreeProduced,
                                   base::Unretained(this)),
-                       &network_module_, viewport_size,
-                       renderer_module_.pipeline()->GetResourceProvider(),
+                       &network_module_, viewport_size, GetResourceProvider(),
                        kLayoutMaxRefreshFrequencyInHz));
+  lifecycle_observers_.AddObserver(splash_screen_.get());
 
   // Create new WebModule.
 #if !defined(COBALT_FORCE_CSP)
@@ -444,14 +466,16 @@
       COBALT_IMAGE_CACHE_CAPACITY_MULTIPLIER_WHEN_PLAYING_VIDEO;
   options.camera_3d = input_device_manager_->camera_3d();
   web_module_.reset(new WebModule(
-      url, base::Bind(&BrowserModule::QueueOnRenderTreeProduced,
-                      base::Unretained(this)),
+      url, application_state_,
+      base::Bind(&BrowserModule::QueueOnRenderTreeProduced,
+                 base::Unretained(this)),
       base::Bind(&BrowserModule::OnError, base::Unretained(this)),
       base::Bind(&BrowserModule::OnWindowClose, base::Unretained(this)),
       base::Bind(&BrowserModule::OnWindowMinimize, base::Unretained(this)),
       media_module_.get(), &network_module_, viewport_size,
-      renderer_module_.pipeline()->GetResourceProvider(), system_window_,
-      kLayoutMaxRefreshFrequencyInHz, options));
+      GetResourceProvider(), system_window_, kLayoutMaxRefreshFrequencyInHz,
+      options));
+  lifecycle_observers_.AddObserver(web_module_.get());
   if (!web_module_recreated_callback_.is_null()) {
     web_module_recreated_callback_.Run();
   }
@@ -473,6 +497,7 @@
   // changed unless the corresponding benchmark logic is changed as well.
   LOG(INFO) << "Loaded WebModule";
 
+  on_load_event_time_ = base::TimeTicks::Now().ToInternalValue();
   web_module_loaded_.Signal();
 }
 
@@ -632,40 +657,77 @@
 
 #endif  // defined(ENABLE_DEBUG_CONSOLE)
 
-void BrowserModule::OnKeyEventProduced(const dom::KeyboardEvent::Data& event) {
+void BrowserModule::OnKeyEventProduced(base::Token type,
+                                       const dom::KeyboardEventInit& event) {
   TRACE_EVENT0("cobalt::browser", "BrowserModule::OnKeyEventProduced()");
   if (MessageLoop::current() != self_message_loop_) {
     self_message_loop_->PostTask(
-        FROM_HERE,
-        base::Bind(&BrowserModule::OnKeyEventProduced, weak_this_, event));
+        FROM_HERE, base::Bind(&BrowserModule::OnKeyEventProduced, weak_this_,
+                              type, event));
     return;
   }
 
   // Filter the key event.
-  if (!FilterKeyEvent(event)) {
+  if (!FilterKeyEvent(type, event)) {
+    return;
+  }
+
+  InjectKeyEventToMainWebModule(type, event);
+}
+
+void BrowserModule::OnPointerEventProduced(base::Token type,
+                                           const dom::PointerEventInit& event) {
+  TRACE_EVENT0("cobalt::browser", "BrowserModule::OnPointerEventProduced()");
+  if (MessageLoop::current() != self_message_loop_) {
+    self_message_loop_->PostTask(
+        FROM_HERE, base::Bind(&BrowserModule::OnPointerEventProduced,
+                              weak_this_, type, event));
     return;
   }
 
 #if defined(ENABLE_DEBUG_CONSOLE)
-  trace_manager_.OnKeyEventProduced();
+  trace_manager_.OnInputEventProduced();
 #endif  // defined(ENABLE_DEBUG_CONSOLE)
 
-  InjectKeyEventToMainWebModule(event);
+  DCHECK(web_module_);
+  web_module_->InjectPointerEvent(type, event);
+}
+
+void BrowserModule::OnWheelEventProduced(base::Token type,
+                                         const dom::WheelEventInit& event) {
+  TRACE_EVENT0("cobalt::browser", "BrowserModule::OnWheelEventProduced()");
+  if (MessageLoop::current() != self_message_loop_) {
+    self_message_loop_->PostTask(
+        FROM_HERE, base::Bind(&BrowserModule::OnWheelEventProduced, weak_this_,
+                              type, event));
+    return;
+  }
+
+#if defined(ENABLE_DEBUG_CONSOLE)
+  trace_manager_.OnInputEventProduced();
+#endif  // defined(ENABLE_DEBUG_CONSOLE)
+
+  DCHECK(web_module_);
+  web_module_->InjectWheelEvent(type, event);
 }
 
 void BrowserModule::InjectKeyEventToMainWebModule(
-    const dom::KeyboardEvent::Data& event) {
+    base::Token type, const dom::KeyboardEventInit& event) {
   TRACE_EVENT0("cobalt::browser",
                "BrowserModule::InjectKeyEventToMainWebModule()");
   if (MessageLoop::current() != self_message_loop_) {
     self_message_loop_->PostTask(
         FROM_HERE, base::Bind(&BrowserModule::InjectKeyEventToMainWebModule,
-                              weak_this_, event));
+                              weak_this_, type, event));
     return;
   }
 
+#if defined(ENABLE_DEBUG_CONSOLE)
+  trace_manager_.OnInputEventProduced();
+#endif  // defined(ENABLE_DEBUG_CONSOLE)
+
   DCHECK(web_module_);
-  web_module_->InjectKeyboardEvent(event);
+  web_module_->InjectKeyboardEvent(type, event);
 }
 
 void BrowserModule::OnError(const GURL& url, const std::string& error) {
@@ -682,10 +744,11 @@
   Navigate(GURL(url_string));
 }
 
-bool BrowserModule::FilterKeyEvent(const dom::KeyboardEvent::Data& event) {
+bool BrowserModule::FilterKeyEvent(base::Token type,
+                                   const dom::KeyboardEventInit& event) {
   TRACE_EVENT0("cobalt::browser", "BrowserModule::FilterKeyEvent()");
   // Check for hotkeys first. If it is a hotkey, no more processing is needed.
-  if (!FilterKeyEventForHotkeys(event)) {
+  if (!FilterKeyEventForHotkeys(type, event)) {
     return false;
   }
 
@@ -693,7 +756,7 @@
   // If the debug console is fully visible, it gets the next chance to handle
   // key events.
   if (debug_console_->GetMode() >= debug::DebugHub::kDebugConsoleOn) {
-    if (!debug_console_->FilterKeyEvent(event)) {
+    if (!debug_console_->FilterKeyEvent(type, event)) {
       return false;
     }
   }
@@ -703,14 +766,14 @@
 }
 
 bool BrowserModule::FilterKeyEventForHotkeys(
-    const dom::KeyboardEvent::Data& event) {
+    base::Token type, const dom::KeyboardEventInit& event) {
 #if !defined(ENABLE_DEBUG_CONSOLE)
+  UNREFERENCED_PARAMETER(type);
   UNREFERENCED_PARAMETER(event);
 #else
-  if (event.key_code == dom::keycode::kF1 ||
-      (event.modifiers & dom::UIEventWithKeyState::kCtrlKey &&
-       event.key_code == dom::keycode::kO)) {
-    if (event.type == dom::KeyboardEvent::kTypeKeyDown) {
+  if (event.key_code() == dom::keycode::kF1 ||
+      (event.ctrl_key() && event.key_code() == dom::keycode::kO)) {
+    if (type == base::Tokens::keydown()) {
       // Ctrl+O toggles the debug console display.
       debug_console_->CycleMode();
     }
@@ -751,6 +814,9 @@
 
 void BrowserModule::DestroySplashScreen() {
   TRACE_EVENT0("cobalt::browser", "BrowserModule::DestroySplashScreen()");
+  if (splash_screen_) {
+    lifecycle_observers_.RemoveObserver(splash_screen_.get());
+  }
   splash_screen_.reset(NULL);
 }
 
@@ -767,17 +833,11 @@
     const webdriver::protocol::WindowId& window_id) {
   // Repost to our message loop to ensure synchronous access to |web_module_|.
   scoped_ptr<webdriver::WindowDriver> window_driver;
-  self_message_loop_->PostTask(
+  self_message_loop_->PostBlockingTask(
       FROM_HERE, base::Bind(&BrowserModule::CreateWindowDriverInternal,
                             base::Unretained(this), window_id,
                             base::Unretained(&window_driver)));
 
-  // Wait for the result and return it.
-  base::WaitableEvent got_window_driver(true, false);
-  self_message_loop_->PostTask(
-      FROM_HERE, base::Bind(&base::WaitableEvent::Signal,
-                            base::Unretained(&got_window_driver)));
-  got_window_driver.Wait();
   // This log is relied on by the webdriver benchmark tests, so it shouldn't be
   // changed unless the corresponding benchmark logic is changed as well.
   LOG(INFO) << "Created WindowDriver: ID=" << window_id.id();
@@ -798,17 +858,10 @@
 debug::DebugServer* BrowserModule::GetDebugServer() {
   // Repost to our message loop to ensure synchronous access to |web_module_|.
   debug::DebugServer* debug_server = NULL;
-  self_message_loop_->PostTask(
+  self_message_loop_->PostBlockingTask(
       FROM_HERE,
       base::Bind(&BrowserModule::GetDebugServerInternal, base::Unretained(this),
                  base::Unretained(&debug_server)));
-
-  // Wait for the result and return it.
-  base::WaitableEvent got_debug_server(true, false);
-  self_message_loop_->PostTask(FROM_HERE,
-                               base::Bind(&base::WaitableEvent::Signal,
-                                          base::Unretained(&got_debug_server)));
-  got_debug_server.Wait();
   DCHECK(debug_server);
   return debug_server;
 }
@@ -826,24 +879,36 @@
   network_module_.SetProxy(proxy_rules);
 }
 
+void BrowserModule::Start() {
+  TRACE_EVENT0("cobalt::browser", "BrowserModule::Start()");
+  DCHECK(application_state_ == base::kApplicationStatePreloading);
+  render_tree::ResourceProvider* resource_provider = GetResourceProvider();
+  FOR_EACH_OBSERVER(LifecycleObserver, lifecycle_observers_,
+                    Start(resource_provider));
+  application_state_ = base::kApplicationStateStarted;
+}
+
+void BrowserModule::Pause() {
+  TRACE_EVENT0("cobalt::browser", "BrowserModule::Pause()");
+  DCHECK(application_state_ == base::kApplicationStateStarted);
+  FOR_EACH_OBSERVER(LifecycleObserver, lifecycle_observers_, Pause());
+  application_state_ = base::kApplicationStatePaused;
+}
+
+void BrowserModule::Unpause() {
+  TRACE_EVENT0("cobalt::browser", "BrowserModule::Unpause()");
+  DCHECK(application_state_ == base::kApplicationStatePaused);
+  FOR_EACH_OBSERVER(LifecycleObserver, lifecycle_observers_, Unpause());
+  application_state_ = base::kApplicationStateStarted;
+}
+
 void BrowserModule::Suspend() {
   TRACE_EVENT0("cobalt::browser", "BrowserModule::Suspend()");
-  DCHECK_EQ(MessageLoop::current(), self_message_loop_);
-  DCHECK(!suspended_);
+  DCHECK(application_state_ == base::kApplicationStatePaused);
 
-// First suspend all our web modules which implies that they will release their
-// resource provider and all resources created through it.
-#if defined(ENABLE_DEBUG_CONSOLE)
-  if (debug_console_) {
-    debug_console_->Suspend();
-  }
-#endif
-  if (splash_screen_) {
-    splash_screen_->Suspend();
-  }
-  if (web_module_) {
-    web_module_->Suspend();
-  }
+  // First suspend all our web modules which implies that they will release
+  // their resource provider and all resources created through it.
+  FOR_EACH_OBSERVER(LifecycleObserver, lifecycle_observers_, Suspend());
 
   // Flush out any submitted render trees pushed since we started shutting down
   // the web modules above.
@@ -877,21 +942,19 @@
   // graphical resources.
   renderer_module_.Suspend();
 
-  suspended_ = true;
+  application_state_ = base::kApplicationStateSuspended;
 }
 
 void BrowserModule::Resume() {
   TRACE_EVENT0("cobalt::browser", "BrowserModule::Resume()");
-  DCHECK_EQ(MessageLoop::current(), self_message_loop_);
-  DCHECK(suspended_);
+  DCHECK(application_state_ == base::kApplicationStateSuspended);
 
   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().
-  render_tree::ResourceProvider* resource_provider =
-      renderer_module_.pipeline()->GetResourceProvider();
+  render_tree::ResourceProvider* resource_provider = GetResourceProvider();
 
   media_module_->Resume(resource_provider);
 
@@ -901,19 +964,10 @@
   NOTREACHED();
 #endif  // defined(ENABLE_GPU_ARRAY_BUFFER_ALLOCATOR)
 
-#if defined(ENABLE_DEBUG_CONSOLE)
-  if (debug_console_) {
-    debug_console_->Resume(resource_provider);
-  }
-#endif
-  if (splash_screen_) {
-    splash_screen_->Resume(resource_provider);
-  }
-  if (web_module_) {
-    web_module_->Resume(resource_provider);
-  }
+  FOR_EACH_OBSERVER(LifecycleObserver, lifecycle_observers_,
+                    Resume(resource_provider));
 
-  suspended_ = false;
+  application_state_ = base::kApplicationStatePaused;
 }
 
 #if defined(OS_STARBOARD)
@@ -975,5 +1029,9 @@
 }
 #endif
 
+render_tree::ResourceProvider* BrowserModule::GetResourceProvider() {
+  return renderer_module_.resource_provider();
+}
+
 }  // namespace browser
 }  // namespace cobalt
diff --git a/src/cobalt/browser/browser_module.h b/src/cobalt/browser/browser_module.h
index 8147240..2f12650 100644
--- a/src/cobalt/browser/browser_module.h
+++ b/src/cobalt/browser/browser_module.h
@@ -19,19 +19,25 @@
 #include <vector>
 
 #include "base/memory/scoped_ptr.h"
+#include "base/observer_list.h"
 #include "base/synchronization/lock.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/thread.h"
 #include "cobalt/account/account_manager.h"
+#include "cobalt/base/application_state.h"
 #include "cobalt/base/message_queue.h"
 #include "cobalt/browser/h5vcc_url_handler.h"
+#include "cobalt/browser/lifecycle_observer.h"
 #include "cobalt/browser/render_tree_combiner.h"
 #include "cobalt/browser/screen_shot_writer.h"
 #include "cobalt/browser/splash_screen.h"
+#include "cobalt/browser/suspend_fuzzer.h"
 #include "cobalt/browser/url_handler.h"
 #include "cobalt/browser/web_module.h"
 #include "cobalt/dom/array_buffer.h"
-#include "cobalt/dom/keyboard_event.h"
+#include "cobalt/dom/keyboard_event_init.h"
+#include "cobalt/dom/pointer_event_init.h"
+#include "cobalt/dom/wheel_event_init.h"
 #include "cobalt/input/input_device_manager.h"
 #include "cobalt/layout/layout_manager.h"
 #include "cobalt/network/network_module.h"
@@ -75,7 +81,9 @@
   // a URL before using it to initialize a new WebModule.
   typedef std::vector<URLHandler::URLHandlerCallback> URLHandlerCollection;
 
-  BrowserModule(const GURL& url, system_window::SystemWindow* system_window,
+  BrowserModule(const GURL& url,
+                base::ApplicationState initial_application_state,
+                system_window::SystemWindow* system_window,
                 account::AccountManager* account_manager,
                 const Options& options);
   ~BrowserModule();
@@ -115,11 +123,11 @@
   // Change the network proxy settings while the application is running.
   void SetProxy(const std::string& proxy_rules);
 
-  // Suspends the browser module from activity, and releases all graphical
-  // resources, placing the application into a low-memory state.
+  // LifecycleObserver-similar interface.
+  void Start();
+  void Pause();
+  void Unpause();
   void Suspend();
-
-  // Undoes the call to Suspend(), returning to normal functionality.
   void Resume();
 
  private:
@@ -154,15 +162,28 @@
   // persist the user's preference.
   void SaveDebugConsoleMode();
 
-  // Glue function to deal with the production of an input event from the
-  // input device, and manage handing it off to the web module for
+  // Glue function to deal with the production of a keyboard input event from a
+  // keyboard input device, and manage handing it off to the web module for
   // interpretation.
-  void OnKeyEventProduced(const dom::KeyboardEvent::Data& event);
+  void OnKeyEventProduced(base::Token type,
+                          const dom::KeyboardEventInit& event);
+
+  // Glue function to deal with the production of a pointer input event from a
+  // pointer input device, and manage handing it off to the web module for
+  // interpretation.
+  void OnPointerEventProduced(base::Token type,
+                              const dom::PointerEventInit& event);
+
+  // Glue function to deal with the production of a wheel input event from a
+  // wheel input device, and manage handing it off to the web module for
+  // interpretation.
+  void OnWheelEventProduced(base::Token type, const dom::WheelEventInit& event);
 
   // Injects a key event directly into the main web module, useful for setting
   // up an input fuzzer whose input should be sent directly to the main
   // web module and not filtered into the debug console.
-  void InjectKeyEventToMainWebModule(const dom::KeyboardEvent::Data& event);
+  void InjectKeyEventToMainWebModule(base::Token type,
+                                     const dom::KeyboardEventInit& event);
 
   // Error callback for any error that stops the program.
   void OnError(const GURL& url, const std::string& error);
@@ -170,12 +191,13 @@
   // Filters a key event.
   // Returns true if the event should be passed on to other handlers,
   // false if it was consumed within this function.
-  bool FilterKeyEvent(const dom::KeyboardEvent::Data& event);
+  bool FilterKeyEvent(base::Token type, const dom::KeyboardEventInit& event);
 
   // Filters a key event for hotkeys.
   // Returns true if the event should be passed on to other handlers,
   // false if it was consumed within this function.
-  bool FilterKeyEventForHotkeys(const dom::KeyboardEvent::Data& event);
+  bool FilterKeyEventForHotkeys(base::Token type,
+                                const dom::KeyboardEventInit& event);
 
   // Tries all registered URL handlers for a URL. Returns true if one of the
   // handlers handled the URL, false if otherwise.
@@ -229,6 +251,8 @@
   void OnPollForRenderTimeout(const GURL& url);
 #endif
 
+  render_tree::ResourceProvider* GetResourceProvider();
+
   // 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
@@ -310,6 +334,13 @@
   // which could occur on navigation.
   base::Closure web_module_recreated_callback_;
 
+  // The time when a URL navigation starts. This is recorded after the previous
+  // WebModule is destroyed.
+  base::CVal<int64> navigate_time_;
+
+  // The time when the WebModule's Window.onload event is fired.
+  base::CVal<int64> on_load_event_time_;
+
 #if defined(ENABLE_DEBUG_CONSOLE)
   // Possibly null, but if not, will contain a reference to an instance of
   // a debug fuzzer input device manager.
@@ -330,6 +361,8 @@
   // Command handler object for screenshot command from the debug console.
   base::ConsoleCommandManager::CommandHandler screenshot_command_handler_;
 #endif  // defined(ENABLE_SCREENSHOT)
+
+  base::optional<SuspendFuzzer> suspend_fuzzer_;
 #endif  // defined(ENABLE_DEBUG_CONSOLE)
 
   // Handler object for h5vcc URLs.
@@ -362,9 +395,13 @@
   // ensure synchronous access.
   base::Lock quit_lock_;
 
-  bool suspended_;
-
   system_window::SystemWindow* system_window_;
+
+  // The current application state.
+  base::ApplicationState application_state_;
+
+  // The list of LifecycleObserver that need to be managed.
+  ObserverList<LifecycleObserver> lifecycle_observers_;
 };
 
 }  // namespace browser
diff --git a/src/cobalt/browser/cobalt.gyp b/src/cobalt/browser/cobalt.gyp
index d858eac..d2d2f29 100644
--- a/src/cobalt/browser/cobalt.gyp
+++ b/src/cobalt/browser/cobalt.gyp
@@ -14,7 +14,7 @@
 
 {
   'variables': {
-    'cobalt_code': 1,
+    'sb_pedantic_warnings': 1,
   },
   'targets': [
     {
diff --git a/src/cobalt/browser/debug_console.cc b/src/cobalt/browser/debug_console.cc
index fea12f6..eaa2b2d 100644
--- a/src/cobalt/browser/debug_console.cc
+++ b/src/cobalt/browser/debug_console.cc
@@ -159,6 +159,7 @@
 }  // namespace
 
 DebugConsole::DebugConsole(
+    base::ApplicationState initial_application_state,
     const WebModule::OnRenderTreeProducedCallback&
         render_tree_produced_callback,
     media::MediaModule* media_module, network::NetworkModule* network_module,
@@ -187,7 +188,8 @@
                  base::Bind(&DebugConsole::GetMode, base::Unretained(this)),
                  get_debug_server_callback);
   web_module_.reset(new WebModule(
-      GURL(kInitialDebugConsoleUrl), render_tree_produced_callback,
+      GURL(kInitialDebugConsoleUrl), initial_application_state,
+      render_tree_produced_callback,
       base::Bind(&DebugConsole::OnError, base::Unretained(this)),
       base::Closure(), /* window_close_callback */
       base::Closure(), /* window_minimize_callback */
@@ -197,10 +199,11 @@
 
 DebugConsole::~DebugConsole() {}
 
-bool DebugConsole::FilterKeyEvent(const dom::KeyboardEvent::Data& event) {
+bool DebugConsole::FilterKeyEvent(base::Token type,
+                                  const dom::KeyboardEventInit& event) {
   // Assume here the full debug console is visible - pass all events to its
   // web module, and return false to indicate the event has been consumed.
-  web_module_->InjectKeyboardEvent(event);
+  web_module_->InjectKeyboardEvent(type, event);
   return false;
 }
 
@@ -229,12 +232,6 @@
   return mode_;
 }
 
-void DebugConsole::Suspend() { web_module_->Suspend(); }
-
-void DebugConsole::Resume(render_tree::ResourceProvider* resource_provider) {
-  web_module_->Resume(resource_provider);
-}
-
 }  // namespace browser
 }  // namespace cobalt
 
diff --git a/src/cobalt/browser/debug_console.h b/src/cobalt/browser/debug_console.h
index 7d87f00..0a55b35 100644
--- a/src/cobalt/browser/debug_console.h
+++ b/src/cobalt/browser/debug_console.h
@@ -21,9 +21,11 @@
 
 #include "base/callback.h"
 #include "base/logging.h"
+#include "cobalt/browser/lifecycle_observer.h"
+#include "cobalt/base/token.h"
 #include "cobalt/browser/web_module.h"
 #include "cobalt/debug/debug_hub.h"
-#include "cobalt/dom/keyboard_event.h"
+#include "cobalt/dom/keyboard_event_init.h"
 #include "googleurl/src/gurl.h"
 
 namespace cobalt {
@@ -31,9 +33,10 @@
 
 // DebugConsole wraps the web module and all components used to implement the
 // debug console.
-class DebugConsole {
+class DebugConsole : public LifecycleObserver {
  public:
   DebugConsole(
+      base::ApplicationState initial_application_state,
       const WebModule::OnRenderTreeProducedCallback&
           render_tree_produced_callback,
       media::MediaModule* media_module, network::NetworkModule* network_module,
@@ -47,7 +50,7 @@
   // Filters a key event.
   // Returns true if the event should be passed on to other handlers,
   // false if it was consumed within this function.
-  bool FilterKeyEvent(const dom::KeyboardEvent::Data& event);
+  bool FilterKeyEvent(base::Token type, const dom::KeyboardEventInit& event);
 
   const WebModule& web_module() const { return *web_module_; }
   WebModule& web_module() { return *web_module_; }
@@ -59,8 +62,16 @@
   // Returns the currently set debug console visibility mode.
   int GetMode();
 
-  void Suspend();
-  void Resume(render_tree::ResourceProvider* resource_provider);
+  // LifecycleObserver implementation.
+  void Start(render_tree::ResourceProvider* resource_provider) OVERRIDE {
+    web_module_->Start(resource_provider);
+  }
+  void Pause() OVERRIDE { web_module_->Pause(); }
+  void Unpause() OVERRIDE { web_module_->Unpause(); }
+  void Suspend() OVERRIDE { web_module_->Suspend(); }
+  void Resume(render_tree::ResourceProvider* resource_provider) OVERRIDE {
+    web_module_->Resume(resource_provider);
+  }
 
  private:
   void OnError(const GURL& /* url */, const std::string& error) {
diff --git a/src/cobalt/browser/h5vcc_url_handler.cc b/src/cobalt/browser/h5vcc_url_handler.cc
index 16825aa..94a8ed9 100644
--- a/src/cobalt/browser/h5vcc_url_handler.cc
+++ b/src/cobalt/browser/h5vcc_url_handler.cc
@@ -108,14 +108,19 @@
 
 void H5vccURLHandler::OnNetworkFailureDialogResponse(
     system_window::SystemWindow::DialogResponse response) {
-  UNREFERENCED_PARAMETER(response);
   const std::string retry_url = GetH5vccUrlQueryParam(url_, kRetryParam);
-  if (retry_url.length() > 0) {
+  // A positive response means we should retry.
+  if (response == system_window::SystemWindow::kDialogPositiveResponse &&
+      retry_url.length() > 0) {
     GURL url(retry_url);
     if (url.is_valid()) {
       browser_module()->Navigate(GURL(retry_url));
+      return;
     }
   }
+  // We were told not to retry, or don't have a retry URL, so leave the app.
+  LOG(ERROR) << "Stop after network error";
+  SbSystemRequestStop(0);
 }
 
 }  // namespace browser
diff --git a/src/cobalt/browser/lifecycle_observer.h b/src/cobalt/browser/lifecycle_observer.h
new file mode 100644
index 0000000..fb69e9e
--- /dev/null
+++ b/src/cobalt/browser/lifecycle_observer.h
@@ -0,0 +1,56 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BROWSER_LIFECYCLE_OBSERVER_H_
+#define COBALT_BROWSER_LIFECYCLE_OBSERVER_H_
+
+#include "cobalt/render_tree/resource_provider.h"
+#include "starboard/configuration.h"
+
+namespace cobalt {
+namespace browser {
+
+// A pure virtual interface for observers of the application lifecycle.
+class LifecycleObserver {
+ public:
+  // Start running visibly with the given graphics ResourceProvider, loading if
+  // necessary. This represents a transition from Preloading to Started, so it
+  // only makes sense if the object is in the Preloading state.
+  virtual void Start(render_tree::ResourceProvider* resource_provider) = 0;
+
+  // Pauses from Started, staying visible and retaining graphics resources.
+  virtual void Pause() = 0;
+
+  // Unpauses, going back to to the Started state, and continuing to
+  // use the same ResourceProvider and graphics resources.
+  virtual void Unpause() = 0;
+
+  // Suspends from Paused, and releases its reference to the ResourceProvider,
+  // additionally releasing all references to any resources created from
+  // it. This method must only be called if the object has previously been
+  // Paused.
+  virtual void Suspend() = 0;
+
+  // Resumes to Paused from Suspended, with a new ResourceProvider. This method
+  // must only be called if the object has previously been Suspended.
+  virtual void Resume(render_tree::ResourceProvider* resource_provider) = 0;
+
+ protected:
+  virtual ~LifecycleObserver() {}
+};
+
+}  // namespace browser
+}  // namespace cobalt
+
+#endif  // COBALT_BROWSER_LIFECYCLE_OBSERVER_H_
diff --git a/src/cobalt/browser/memory_settings/memory_settings.cc b/src/cobalt/browser/memory_settings/memory_settings.cc
index 5da12a6..caabaf3 100644
--- a/src/cobalt/browser/memory_settings/memory_settings.cc
+++ b/src/cobalt/browser/memory_settings/memory_settings.cc
@@ -17,6 +17,7 @@
 #include "cobalt/browser/memory_settings/memory_settings.h"
 
 #include <algorithm>
+#include <locale>
 #include <string>
 #include <vector>
 
@@ -46,11 +47,21 @@
   bool error_;  // true if there was a parse error.
 };
 
-// Parses a string like "1234x5678" to vector of parsed int values.
-std::vector<ParsedIntValue> ParseDimensions(const std::string& input) {
+char ToLowerCharTypesafe(int c) {
+  return static_cast<char>(::tolower(c));
+}
+
+std::string ToLower(const std::string& input) {
   std::string value_str = input;
   std::transform(value_str.begin(), value_str.end(),
-                 value_str.begin(), ::tolower);
+                 value_str.begin(), ToLowerCharTypesafe);
+
+  return value_str;
+}
+
+// Parses a string like "1234x5678" to vector of parsed int values.
+std::vector<ParsedIntValue> ParseDimensions(const std::string& input) {
+  std::string value_str = ToLower(input);
   std::vector<ParsedIntValue> output;
 
   std::vector<std::string> lengths;
@@ -86,10 +97,7 @@
   }
 
   // Lowercasing the string makes the units easier to detect.
-  std::string value_lower_case = value;
-  std::transform(value_lower_case.begin(), value_lower_case.end(),
-                 value_lower_case.begin(),
-                 ::tolower);
+  std::string value_lower_case = ToLower(value);
 
   if (StringEndsWith(value_lower_case, "kb")) {
     numerical_value *= 1024;  // convert kb -> bytes.
diff --git a/src/cobalt/browser/memory_tracker/tool/compressed_time_series_tool.cc b/src/cobalt/browser/memory_tracker/tool/compressed_time_series_tool.cc
index 2942749..c89f3aa 100644
--- a/src/cobalt/browser/memory_tracker/tool/compressed_time_series_tool.cc
+++ b/src/cobalt/browser/memory_tracker/tool/compressed_time_series_tool.cc
@@ -136,7 +136,7 @@
     for (MapIt it = samples.begin(); it != samples.end(); ++it) {
       const int64 alloc_bytes = it->second.allocated_bytes_[i];
       // Convert to float megabytes with decimals of precision.
-      double n = alloc_bytes / (1000 * 10);
+      double n = static_cast<double>(alloc_bytes / (1000 * 10));
       n = n / (100.);
       ss << n << kDelimiter;
     }
diff --git a/src/cobalt/browser/memory_tracker/tool/log_writer_tool.cc b/src/cobalt/browser/memory_tracker/tool/log_writer_tool.cc
index 65ad229..428c0af 100644
--- a/src/cobalt/browser/memory_tracker/tool/log_writer_tool.cc
+++ b/src/cobalt/browser/memory_tracker/tool/log_writer_tool.cc
@@ -75,7 +75,7 @@
   // this loop only iterates once.
   for (size_t i = kStartIndex; i < end_index; ++i) {
     void* p = addresses[i];
-    int bytes_written =
+    bytes_written =
         SbStringFormatF(buff + buff_pos, sizeof(buff) - buff_pos,
                         " %" PRIXPTR "", reinterpret_cast<uintptr_t>(p));
     DCHECK_GE(bytes_written, 0);
diff --git a/src/cobalt/browser/memory_tracker/tool/print_csv_tool.cc b/src/cobalt/browser/memory_tracker/tool/print_csv_tool.cc
index 51ef41f..64db442 100644
--- a/src/cobalt/browser/memory_tracker/tool/print_csv_tool.cc
+++ b/src/cobalt/browser/memory_tracker/tool/print_csv_tool.cc
@@ -87,14 +87,14 @@
       continue;
     }
 
-    const AllocationSamples& samples = it->second;
-    if (samples.allocated_bytes_.empty() ||
-        samples.number_allocations_.empty()) {
+    const AllocationSamples& samples_ref = it->second;
+    if (samples_ref.allocated_bytes_.empty() ||
+      samples_ref.number_allocations_.empty()) {
       SB_NOTREACHED() << "Should not be here";
       return "ERROR";
     }
-    const int64 n_allocs = samples.number_allocations_.back();
-    const int64 n_bytes = samples.allocated_bytes_.back();
+    const int64 n_allocs = samples_ref.number_allocations_.back();
+    const int64 n_bytes = samples_ref.allocated_bytes_.back();
     int64 bytes_per_alloc = 0;
     if (n_allocs > 0) {
       bytes_per_alloc = n_bytes / n_allocs;
@@ -135,14 +135,14 @@
       }
       const int64 alloc_bytes = it->second.allocated_bytes_[i];
       // Convert to float megabytes with decimals of precision.
-      double n = alloc_bytes / (1000 * 10);
+      double n = static_cast<double>(alloc_bytes / (1000 * 10));
       n = n / (100.);
       ss << n << kDelimiter;
     }
     if (total_cpu_memory_it != samples.end()) {
       const int64 alloc_bytes = total_cpu_memory_it->second.allocated_bytes_[i];
       // Convert to float megabytes with decimals of precision.
-      double n = alloc_bytes / (1000 * 10);
+      double n = static_cast<double>(alloc_bytes / (1000 * 10));
       n = n / (100.);
       ss << n << kDelimiter;
     }
diff --git a/src/cobalt/browser/memory_tracker/tool/tool_impl.cc b/src/cobalt/browser/memory_tracker/tool/tool_impl.cc
index 01634e0..f2a92bb 100644
--- a/src/cobalt/browser/memory_tracker/tool/tool_impl.cc
+++ b/src/cobalt/browser/memory_tracker/tool/tool_impl.cc
@@ -54,7 +54,7 @@
 
 size_t AllocationSizeBinner::GetBucketIndexForAllocationSize(size_t size) {
   for (int i = 0; i < 32; ++i) {
-    size_t val = 0x1 << i;
+    size_t val = static_cast<size_t>(0x1) << i;
     if (val > size) {
       return i;
     }
@@ -76,7 +76,7 @@
     *max_value = 0;
     return;
   }
-  *min_value = 0x1 << (idx - 1);
+  *min_value = static_cast<size_t>(0x1) << (idx - 1);
   *max_value = (*min_value << 1) - 1;
   return;
 }
@@ -86,7 +86,7 @@
   size_t largest_allocation_total_idx = 0;
 
   for (size_t i = 0; i < allocation_histogram_.size(); ++i) {
-    size_t alloc_size = 0x1 << i;
+    size_t alloc_size = static_cast<size_t>(0x1) << i;
     size_t count = allocation_histogram_[i];
     int64 allocation_total =
         static_cast<int64>(alloc_size) * static_cast<int64>(count);
diff --git a/src/cobalt/browser/resource_provider_array_buffer_allocator.cc b/src/cobalt/browser/resource_provider_array_buffer_allocator.cc
index 7f2ee02..ef9020e 100644
--- a/src/cobalt/browser/resource_provider_array_buffer_allocator.cc
+++ b/src/cobalt/browser/resource_provider_array_buffer_allocator.cc
@@ -27,8 +27,8 @@
   DCHECK(gpu_memory_buffer_space_->GetMemory());
 
   gpu_memory_pool_.set(starboard::make_scoped_ptr(
-      new nb::MemoryPool(gpu_memory_buffer_space_->GetMemory(),
-                         gpu_memory_buffer_space_->GetSizeInBytes())));
+      new nb::FirstFitMemoryPool(gpu_memory_buffer_space_->GetMemory(),
+                                 gpu_memory_buffer_space_->GetSizeInBytes())));
 }
 
 void* ResourceProviderArrayBufferAllocator::Allocate(size_t size) {
diff --git a/src/cobalt/browser/resource_provider_array_buffer_allocator.h b/src/cobalt/browser/resource_provider_array_buffer_allocator.h
index c9de006..98734cb 100644
--- a/src/cobalt/browser/resource_provider_array_buffer_allocator.h
+++ b/src/cobalt/browser/resource_provider_array_buffer_allocator.h
@@ -37,7 +37,7 @@
   void Free(void* p) OVERRIDE;
 
   scoped_ptr<render_tree::RawImageMemory> gpu_memory_buffer_space_;
-  starboard::LockedPtr<nb::MemoryPool> gpu_memory_pool_;
+  starboard::LockedPtr<nb::FirstFitMemoryPool> gpu_memory_pool_;
 };
 
 }  // namespace browser
diff --git a/src/cobalt/browser/splash_screen.cc b/src/cobalt/browser/splash_screen.cc
index ae60aa5..d2b8e12 100644
--- a/src/cobalt/browser/splash_screen.cc
+++ b/src/cobalt/browser/splash_screen.cc
@@ -24,7 +24,8 @@
 const char SplashScreen::Options::kDefaultSplashScreenURL[] =
     "h5vcc-embedded://splash_screen.html";
 
-SplashScreen::SplashScreen(const WebModule::OnRenderTreeProducedCallback&
+SplashScreen::SplashScreen(base::ApplicationState initial_application_state,
+                           const WebModule::OnRenderTreeProducedCallback&
                                render_tree_produced_callback,
                            network::NetworkModule* network_module,
                            const math::Size& window_dimensions,
@@ -44,7 +45,7 @@
       base::kThreadPriority_High;
 
   web_module_.reset(new WebModule(
-      options.url,
+      options.url, initial_application_state,
       base::Bind(&SplashScreen::OnRenderTreeProduced, base::Unretained(this)),
       base::Bind(&SplashScreen::OnError, base::Unretained(this)),
       base::Bind(&SplashScreen::OnWindowClosed, base::Unretained(this)),
@@ -60,11 +61,6 @@
   web_module_.reset();
 }
 
-void SplashScreen::Suspend() { web_module_->Suspend(); }
-void SplashScreen::Resume(render_tree::ResourceProvider* resource_provider) {
-  web_module_->Resume(resource_provider);
-}
-
 void SplashScreen::WaitUntilReady() {
   is_ready_.Wait();
 }
diff --git a/src/cobalt/browser/splash_screen.h b/src/cobalt/browser/splash_screen.h
index a0d65f7..8ca9ba5 100644
--- a/src/cobalt/browser/splash_screen.h
+++ b/src/cobalt/browser/splash_screen.h
@@ -20,6 +20,7 @@
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/synchronization/waitable_event.h"
+#include "cobalt/browser/lifecycle_observer.h"
 #include "cobalt/browser/web_module.h"
 #include "cobalt/media/media_module_stub.h"
 #include "googleurl/src/gurl.h"
@@ -29,7 +30,7 @@
 
 // SplashScreen uses a WebModule to present a splash screen.
 //
-class SplashScreen {
+class SplashScreen : public LifecycleObserver {
  public:
   struct Options {
     Options() : url(kDefaultSplashScreenURL) {}
@@ -37,7 +38,8 @@
     GURL url;
   };
 
-  SplashScreen(const WebModule::OnRenderTreeProducedCallback&
+  SplashScreen(base::ApplicationState initial_application_state,
+               const WebModule::OnRenderTreeProducedCallback&
                    render_tree_produced_callback,
                network::NetworkModule* network_module,
                const math::Size& window_dimensions,
@@ -45,8 +47,16 @@
                float layout_refresh_rate, const Options& options = Options());
   ~SplashScreen();
 
-  void Suspend();
-  void Resume(render_tree::ResourceProvider* resource_provider);
+  // LifecycleObserver implementation.
+  void Start(render_tree::ResourceProvider* resource_provider) OVERRIDE {
+    web_module_->Start(resource_provider);
+  }
+  void Pause() OVERRIDE { web_module_->Pause(); }
+  void Unpause() OVERRIDE { web_module_->Unpause(); }
+  void Suspend() OVERRIDE { web_module_->Suspend(); }
+  void Resume(render_tree::ResourceProvider* resource_provider) OVERRIDE {
+    web_module_->Resume(resource_provider);
+  }
 
   // Block the caller until the splash screen is ready to be rendered.
   void WaitUntilReady();
diff --git a/src/cobalt/browser/suspend_fuzzer.cc b/src/cobalt/browser/suspend_fuzzer.cc
new file mode 100644
index 0000000..b94158b
--- /dev/null
+++ b/src/cobalt/browser/suspend_fuzzer.cc
@@ -0,0 +1,70 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/browser/suspend_fuzzer.h"
+
+namespace cobalt {
+namespace browser {
+
+namespace {
+
+// How long to wait before starting the suspend fuzzer, if it is enabled.  It
+// is important that this value is large enough to let the hosted application
+// get into its initialized state, since otherwise, we would likely not be
+// fuzzing the main state of the application.
+const base::TimeDelta kBeginTimeout = base::TimeDelta::FromSeconds(30);
+
+// How long to wait in between suspend fuzzer suspends and resumes, if it is
+// enabled.
+const base::TimeDelta kInterval = base::TimeDelta::FromSeconds(10);
+
+}  // namespace
+
+SuspendFuzzer::SuspendFuzzer()
+    : thread_("suspend_fuzzer"), step_type_(kShouldRequestSuspend) {
+  thread_.Start();
+  thread_.message_loop()->PostDelayedTask(
+      FROM_HERE, base::Bind(&SuspendFuzzer::DoStep, base::Unretained(this)),
+      kBeginTimeout);
+}
+
+SuspendFuzzer::~SuspendFuzzer() { thread_.Stop(); }
+
+void SuspendFuzzer::DoStep() {
+  DCHECK(MessageLoop::current() == thread_.message_loop());
+#if SB_API_VERSION < 4
+  NOTREACHED() << "Cannot run suspend_fuzzer on SB_API_VERSION < 4.";
+#endif
+  if (step_type_ == kShouldRequestSuspend) {
+    SB_DLOG(INFO) << "suspend_fuzzer: Requesting suspend.";
+#if SB_API_VERSION >= 4
+    SbSystemRequestSuspend();
+#endif
+    step_type_ = kShouldRequestUnpause;
+  } else if (step_type_ == kShouldRequestUnpause) {
+    SB_DLOG(INFO) << "suspend_fuzzer: Requesting unpause.";
+#if SB_API_VERSION >= 4
+    SbSystemRequestUnpause();
+#endif
+    step_type_ = kShouldRequestSuspend;
+  } else {
+    NOTREACHED();
+  }
+  MessageLoop::current()->PostDelayedTask(
+      FROM_HERE, base::Bind(&SuspendFuzzer::DoStep, base::Unretained(this)),
+      kInterval);
+}
+
+}  // namespace browser
+}  // namespace cobalt
diff --git a/src/cobalt/browser/suspend_fuzzer.h b/src/cobalt/browser/suspend_fuzzer.h
new file mode 100644
index 0000000..4aa97cc
--- /dev/null
+++ b/src/cobalt/browser/suspend_fuzzer.h
@@ -0,0 +1,48 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BROWSER_SUSPEND_FUZZER_H_
+#define COBALT_BROWSER_SUSPEND_FUZZER_H_
+
+#include "base/bind.h"
+#include "base/threading/thread.h"
+
+namespace cobalt {
+namespace browser {
+
+// Repeatedly switch off between calling |SbSystemRequestSuspend| and
+// |SbSystemRequestUnpause|, or just no-op if on an SB_API_VERSION < 4.
+class SuspendFuzzer {
+ public:
+  SuspendFuzzer();
+  ~SuspendFuzzer();
+
+ private:
+  void DoStep();
+
+  // Suspending the application could possibly freeze our message loop or our
+  // web modules' message loops.  We thus create a separate thread to run the
+  // suspend fuzzer on.
+  base::Thread thread_;
+
+  enum StepType {
+    kShouldRequestSuspend,
+    kShouldRequestUnpause,
+  } step_type_;
+};
+
+}  // namespace browser
+}  // namespace cobalt
+
+#endif  // COBALT_BROWSER_SUSPEND_FUZZER_H_
diff --git a/src/cobalt/browser/switches.cc b/src/cobalt/browser/switches.cc
index 9e11ce3..0232dff 100644
--- a/src/cobalt/browser/switches.cc
+++ b/src/cobalt/browser/switches.cc
@@ -82,6 +82,11 @@
 // Decode all images using StubImageDecoder.
 const char kStubImageDecoder[] = "stub_image_decoder";
 
+// If this flag is set, alternating calls to |SbSystemRequestSuspend| and
+// |SbSystemRequestUnpause| will be made periodically. Requires
+// SB_API_VERSION >= 4, and will otherwise just no-op.
+const char kSuspendFuzzer[] = "suspend_fuzzer";
+
 // If this is set, then a trace (see base/debug/trace_eventh.h) is started on
 // Cobalt startup.  A value must also be specified for this switch, which is
 // the duration in seconds of how long the trace will be done for before ending
@@ -112,6 +117,15 @@
 
 #endif  // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
 
+// If toggled, framerate statistics will be printed to stdout after each
+// animation completes, or after a maximum number of frames has been collected.
+const char kFPSPrint[] = "fps_stdout";
+
+// If toggled, framerate statistics will be displayed in an on-screen overlay
+// and updated after each animation completes, or after a maximum number of
+// frames has been collected.
+const char kFPSOverlay[] = "fps_overlay";
+
 // Disables the hard-coded navigation whitelist without disabling any other
 // security checks. This is enabled in Gold builds.
 const char kDisableNavigationWhitelist[] = "disable_navigation_whitelist";
diff --git a/src/cobalt/browser/switches.h b/src/cobalt/browser/switches.h
index 7eba479..6f03782 100644
--- a/src/cobalt/browser/switches.h
+++ b/src/cobalt/browser/switches.h
@@ -39,15 +39,18 @@
 extern const char kRemoteDebuggingPort[];
 extern const char kShutdownAfter[];
 extern const char kStubImageDecoder[];
+extern const char kSuspendFuzzer[];
 extern const char kTimedTrace[];
 extern const char kUseTTS[];
 extern const char kVideoContainerSizeOverride[];
 extern const char kVideoDecoderStub[];
-extern const char kWebDriverPort[];
 extern const char kWebDriverListenIp[];
+extern const char kWebDriverPort[];
 #endif  // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
 
 extern const char kDisableNavigationWhitelist[];
+extern const char kFPSPrint[];
+extern const char kFPSOverlay[];
 extern const char kImageCacheSizeInBytes[];
 extern const char kInitialURL[];
 extern const char kRemoteTypefaceCacheSizeInBytes[];
diff --git a/src/cobalt/browser/testdata/deviceorientation-demo/deviceorientation-demo.html b/src/cobalt/browser/testdata/deviceorientation-demo/deviceorientation-demo.html
new file mode 100644
index 0000000..2867e52
--- /dev/null
+++ b/src/cobalt/browser/testdata/deviceorientation-demo/deviceorientation-demo.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+    body {
+      background-color: rgb(255, 255, 255);
+      font-size: 100px;
+    }
+</style>
+</head>
+<body>
+  <div id="alpha"></div>
+  <div id="beta"></div>
+  <div id="gamma"></div>
+  <div id="frequency"></div>
+  <div id="count"></div>
+
+  <script>
+    var lastShowAngles = NaN;
+    var lastShowHz = NaN;
+    var fireCount = 0;
+
+    window.addEventListener('deviceorientation', function(event) {
+      var now = Date.now();
+
+      if (isNaN(lastShowAngles) || now - lastShowAngles >= 200) {
+        ['alpha', 'beta', 'gamma'].forEach(function(axis) {
+          document.getElementById(axis).innerHTML =
+              axis + ' = ' + event[axis];
+        });
+        lastShowAngles = now;
+      }
+
+      var elapsedSinceShowHz = now - lastShowHz;
+      if (isNaN(lastShowHz) || elapsedSinceShowHz >= 1000) {
+        document.getElementById('frequency').innerHTML =
+          (fireCount * 1000 / elapsedSinceShowHz) + ' Hz';
+
+        fireCount = 0;
+        lastShowHz = now;
+      }
+
+      fireCount++;
+    }, true);
+  </script>
+</body>
+</html>
diff --git a/src/cobalt/browser/testdata/page-visibility-demo/page-visibility-demo.html b/src/cobalt/browser/testdata/page-visibility-demo/page-visibility-demo.html
new file mode 100644
index 0000000..88afa97
--- /dev/null
+++ b/src/cobalt/browser/testdata/page-visibility-demo/page-visibility-demo.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script type="text/javascript">
+    console.log("document.visibilityState: " + document.visibilityState);
+
+    window.onblur = function() {
+      console.log("window.onblur");
+    };
+
+    window.onfocus = function() {
+      console.log("window.onfocus");
+    };
+
+    document.onblur = function() {
+      console.log("document.onblur?!");
+    };
+
+    document.onfocus = function() {
+      console.log("document.onfocus?!");
+    };
+
+    document.onvisibilitychange = function() {
+      console.log("document.onvisibilitychange: " + document.visibilityState);
+    };
+  </script>
+</head>
+<body style="background-color:#ccc"></body>
+</html>
diff --git a/src/cobalt/browser/trace_manager.cc b/src/cobalt/browser/trace_manager.cc
index 6a31774..5a5769f 100644
--- a/src/cobalt/browser/trace_manager.cc
+++ b/src/cobalt/browser/trace_manager.cc
@@ -32,13 +32,14 @@
     "If a trace is currently running, stops it and saves the result; "
     "otherwise starts a new trace.";
 
-// Name of the command to start / stop tracing after key event.
-const char kKeyTraceCommand[] = "key_trace";
+// Name of the command to start / stop tracing after input event.
+const char kInputTraceCommand[] = "input_trace";
 
-// Help strings for the key trace command.
-const char kKeyTraceCommandShortHelp[] = "Starts/stops tracing after key event";
-const char kKeyTraceCommandLongHelp[] =
-    "Switches the flag of whether we start a new tracing after each key "
+// Help strings for the input trace command.
+const char kInputTraceCommandShortHelp[] =
+    "Starts/stops tracing after input event";
+const char kInputTraceCommandLongHelp[] =
+    "Switches the flag of whether we start a new tracing after each input "
     "event.";
 
 }  // namespace
@@ -53,17 +54,18 @@
           kTraceCommandChannel,
           base::Bind(&TraceManager::OnTraceMessage, base::Unretained(this)),
           kTraceCommandShortHelp, kTraceCommandLongHelp)),
-      ALLOW_THIS_IN_INITIALIZER_LIST(key_trace_command_handler_(
-          kKeyTraceCommand,
-          base::Bind(&TraceManager::OnKeyTraceMessage, base::Unretained(this)),
-          kKeyTraceCommandShortHelp, kKeyTraceCommandLongHelp)),
-      key_tracing_enabled_(false) {}
+      ALLOW_THIS_IN_INITIALIZER_LIST(input_trace_command_handler_(
+          kInputTraceCommand,
+          base::Bind(&TraceManager::OnInputTraceMessage,
+                     base::Unretained(this)),
+          kInputTraceCommandShortHelp, kInputTraceCommandLongHelp)),
+      input_tracing_enabled_(false) {}
 
-void TraceManager::OnKeyEventProduced() {
+void TraceManager::OnInputEventProduced() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (key_tracing_enabled_ && !IsTracing()) {
+  if (input_tracing_enabled_ && !IsTracing()) {
     static const int kTraceTimeInMilliSeconds = 500;
-    LOG(INFO) << "Key event produced, start tracing for "
+    LOG(INFO) << "Input event produced, start tracing for "
               << kTraceTimeInMilliSeconds << "ms...";
     start_time_to_event_map_.clear();
     trace_event::TraceWithEventParserForDuration(
@@ -102,19 +104,19 @@
   }
 }
 
-void TraceManager::OnKeyTraceMessage(const std::string& message) {
+void TraceManager::OnInputTraceMessage(const std::string& message) {
   if (MessageLoop::current() != self_message_loop_) {
     self_message_loop_->PostTask(FROM_HERE,
-                                 base::Bind(&TraceManager::OnKeyTraceMessage,
+                                 base::Bind(&TraceManager::OnInputTraceMessage,
                                             base::Unretained(this), message));
     return;
   }
 
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  key_tracing_enabled_ = !key_tracing_enabled_;
-  LOG(INFO) << "Key tracing is now "
-            << (key_tracing_enabled_ ? "enabled" : "disabled") << ".";
+  input_tracing_enabled_ = !input_tracing_enabled_;
+  LOG(INFO) << "Input tracing is now "
+            << (input_tracing_enabled_ ? "enabled" : "disabled") << ".";
 }
 
 void TraceManager::OnReceiveTraceEvent(
@@ -123,6 +125,8 @@
   // TODO: Generalize the following logic. Currently the criteria for
   // interesting events are hardcoded.
   if (event->name() == "WebModule::InjectKeyboardEvent()" ||
+      event->name() == "WebModule::InjectPointerEvent()" ||
+      event->name() == "WebModule::InjectWheelEvent()" ||
       event->name() == "Layout") {
     double event_duration = event->in_scope_duration()->InMillisecondsF();
 
@@ -148,7 +152,7 @@
               << event->in_scope_duration()->InMillisecondsF() << "ms";
   }
   start_time_to_event_map_.clear();
-  LOG(INFO) << "Key trace finished.";
+  LOG(INFO) << "Input trace finished.";
 }
 
 }  // namespace browser
diff --git a/src/cobalt/browser/trace_manager.h b/src/cobalt/browser/trace_manager.h
index ffa2c8f..180c2f5 100644
--- a/src/cobalt/browser/trace_manager.h
+++ b/src/cobalt/browser/trace_manager.h
@@ -34,14 +34,14 @@
 
   TraceManager();
 
-  // Called by browser module when a key event is produced.
-  void OnKeyEventProduced();
+  // Called by browser module when an input event is produced.
+  void OnInputEventProduced();
 
   // Message handler callback for trace message from debug console.
   void OnTraceMessage(const std::string& message);
 
-  // Message handler callback for key trace message from debug console.
-  void OnKeyTraceMessage(const std::string& message);
+  // Message handler callback for input trace message from debug console.
+  void OnInputTraceMessage(const std::string& message);
 
   // Called when receiving and finishing receiving parsed trace events.
   void OnReceiveTraceEvent(
@@ -58,10 +58,10 @@
 
   // Command handler object for trace command from the debug console.
   base::ConsoleCommandManager::CommandHandler trace_command_handler_;
-  // Command handler object for key trace command from the debug console.
-  base::ConsoleCommandManager::CommandHandler key_trace_command_handler_;
-  // Whether key tracing is enabled.
-  bool key_tracing_enabled_;
+  // Command handler object for input trace command from the debug console.
+  base::ConsoleCommandManager::CommandHandler input_trace_command_handler_;
+  // Whether input tracing is enabled.
+  bool input_tracing_enabled_;
 
   base::ThreadChecker thread_checker_;
   // This object can be set to start a trace if a hotkey (like F3) is pressed.
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc
index 380b091..059c85c 100644
--- a/src/cobalt/browser/web_module.cc
+++ b/src/cobalt/browser/web_module.cc
@@ -23,8 +23,7 @@
 #include "base/message_loop_proxy.h"
 #include "base/optional.h"
 #include "base/stringprintf.h"
-#include "cobalt/base/c_val.h"
-#include "cobalt/base/poller.h"
+#include "cobalt/base/startup_timer.h"
 #include "cobalt/base/tokens.h"
 #include "cobalt/browser/stack_size_constants.h"
 #include "cobalt/browser/switches.h"
@@ -34,32 +33,33 @@
 #include "cobalt/dom/blob.h"
 #include "cobalt/dom/csp_delegate_factory.h"
 #include "cobalt/dom/element.h"
+#include "cobalt/dom/event.h"
+#include "cobalt/dom/global_stats.h"
+#include "cobalt/dom/keyboard_event.h"
 #include "cobalt/dom/local_storage_database.h"
 #include "cobalt/dom/mutation_observer_task_manager.h"
+#include "cobalt/dom/pointer_event.h"
 #include "cobalt/dom/storage.h"
+#include "cobalt/dom/ui_event.h"
 #include "cobalt/dom/url.h"
+#include "cobalt/dom/wheel_event.h"
 #include "cobalt/dom_parser/parser.h"
 #include "cobalt/h5vcc/h5vcc.h"
+#include "cobalt/layout/topmost_event_target.h"
 #include "cobalt/loader/image/animated_image_tracker.h"
 #include "cobalt/media_session/media_session_client.h"
+#include "cobalt/page_visibility/visibility_state.h"
 #include "cobalt/script/javascript_engine.h"
 #include "cobalt/storage/storage_manager.h"
 #include "cobalt/system_window/system_window.h"
 #include "starboard/accessibility.h"
 #include "starboard/log.h"
-#include "starboard/once.h"
 
 namespace cobalt {
 namespace browser {
 
 namespace {
 
-#if defined(COBALT_BUILD_TYPE_GOLD)
-const int kPollerPeriodMs = 2000;
-#else   // #if defined(COBALT_BUILD_TYPE_GOLD)
-const int kPollerPeriodMs = 20;
-#endif  // #if defined(COBALT_BUILD_TYPE_GOLD)
-
 // The maximum number of element depth in the DOM tree. Elements at a level
 // deeper than this could be discarded, and will not be rendered.
 const int kDOMMaxElementDepth = 32;
@@ -85,45 +85,6 @@
     "To wipe the box tree and turn partial layout off.";
 #endif  // defined(ENABLE_PARTIAL_LAYOUT_CONTROL)
 
-class JSEngineStats {
- public:
-  JSEngineStats()
-      : js_reserved_memory_("Memory.JS", 0,
-                            "The total memory that is reserved by the engine, "
-                            "including the part that is actually occupied by "
-                            "JS objects, and the part that is not yet.") {}
-
-  static JSEngineStats* GetInstance() {
-    return Singleton<JSEngineStats,
-                     StaticMemorySingletonTraits<JSEngineStats> >::get();
-  }
-
-  void SetReservedMemory(size_t js_reserved_memory) {
-    js_reserved_memory_ = static_cast<uint64>(js_reserved_memory);
-  }
-
- private:
-  // The total memory that is reserved by the engine, including the part that is
-  // actually occupied by JS objects, and the part that is not yet.
-  base::CVal<base::cval::SizeInBytes, base::CValPublic> js_reserved_memory_;
-};
-
-// StartupTimer is designed to measure time since the startup of the app.
-// It is loader initialized to have the most accurate start time as possible.
-class StartupTimer {
- public:
-  static StartupTimer* Instance();
-  base::TimeDelta TimeSinceStartup() const {
-    return base::TimeTicks::Now() - start_time_;
-  }
-
- private:
-  StartupTimer() : start_time_(base::TimeTicks::Now()) {}
-  base::TimeTicks start_time_;
-};
-
-SB_ONCE_INITIALIZE_FUNCTION(StartupTimer, StartupTimer::Instance);
-StartupTimer* s_on_startup_init_dont_use = StartupTimer::Instance();
 }  // namespace
 
 // Private WebModule implementation. Each WebModule owns a single instance of
@@ -146,14 +107,29 @@
   // Otherwise, the currently focused element receives the event.
   // If element is specified, we must be on the WebModule's message loop
   void InjectKeyboardEvent(scoped_refptr<dom::Element> element,
-                           const dom::KeyboardEvent::Data& event);
+                           base::Token type,
+                           const dom::KeyboardEventInit& event);
+
+  // Called to inject a pointer event into the web module.
+  // Event is directed at a specific element if the element is non-null.
+  // Otherwise, the currently focused element receives the event.
+  // If element is specified, we must be on the WebModule's message loop
+  void InjectPointerEvent(scoped_refptr<dom::Element> element, base::Token type,
+                          const dom::PointerEventInit& event);
+
+  // Called to inject a wheel event into the web module.
+  // Event is directed at a specific element if the element is non-null.
+  // Otherwise, the currently focused element receives the event.
+  // If element is specified, we must be on the WebModule's message loop
+  void InjectWheelEvent(scoped_refptr<dom::Element> element, base::Token type,
+                        const dom::WheelEventInit& event);
 
   // Called to execute JavaScript in this WebModule. Sets the |result|
   // output parameter and signals |got_result|.
   void ExecuteJavascript(const std::string& script_utf8,
                          const base::SourceLocation& script_location,
                          base::WaitableEvent* got_result, std::string* result,
-                         bool *out_succeeded);
+                         bool* out_succeeded);
 
   // Clears disables timer related objects
   // so that the message loop can easily exit
@@ -175,11 +151,21 @@
   void CreateDebugServerIfNull();
 #endif  // ENABLE_DEBUG_CONSOLE
 
+  // Sets the application state, asserts preconditions to transition to that
+  // state, and dispatches any precipitate web events.
+  void SetApplicationState(base::ApplicationState state);
+
   // Suspension of the WebModule is a two-part process since a message loop
   // gap is needed in order to give a chance to handle loader callbacks
   // that were initiated from a loader thread.
   void SuspendLoaders();
   void FinishSuspend();
+
+  // See LifecycleObserver. These functions do not implement the interface, but
+  // have the same basic function.
+  void Start(render_tree::ResourceProvider* resource_provider);
+  void Pause();
+  void Unpause();
   void Resume(render_tree::ResourceProvider* resource_provider);
 
   void ReportScriptError(const base::SourceLocation& source_location,
@@ -218,12 +204,12 @@
     error_callback_.Run(window_->location()->url(), error);
   }
 
-  void UpdateJavaScriptEngineStats() {
-    if (javascript_engine_) {
-      JSEngineStats::GetInstance()->SetReservedMemory(
-          javascript_engine_->UpdateMemoryStatsAndReturnReserved());
-    }
-  }
+  // Inject the DOM event object into the window or the element.
+  void InjectInputEvent(scoped_refptr<dom::Element> element,
+                        const scoped_refptr<dom::Event>& event);
+
+  // Handle queued pointer events. Called by LayoutManager on_layout callback.
+  void HandlePointerEvents();
 
   // Thread checker ensures all calls to the WebModule are made from the same
   // thread that it is created in.
@@ -281,9 +267,6 @@
   // JavaScript engine for the browser.
   scoped_ptr<script::JavaScriptEngine> javascript_engine_;
 
-  // Poller that updates javascript engine stats.
-  scoped_ptr<base::PollerWithThread> javascript_engine_poller_;
-
   // JavaScript Global Object for the browser. There should be one per window,
   // but since there is only one window, we can have one per browser.
   scoped_refptr<script::GlobalEnvironment> global_environment_;
@@ -342,6 +325,8 @@
 #endif  // defined(ENABLE_PARTIAL_LAYOUT_CONTROL)
 
   scoped_ptr<media_session::MediaSessionClient> media_session_client_;
+
+  layout::TopmostEventTarget topmost_event_target_;
 };
 
 class WebModule::Impl::DocumentLoadedObserver : public dom::DocumentObserver {
@@ -364,9 +349,9 @@
 };
 
 WebModule::Impl::Impl(const ConstructionData& data)
-    : name_(data.options.name), is_running_(false) {
-  resource_provider_ = data.resource_provider;
-
+    : name_(data.options.name),
+      is_running_(false),
+      resource_provider_(data.resource_provider) {
   // Currently we rely on a platform to explicitly specify that it supports
   // the map-to-mesh filter via the ENABLE_MAP_TO_MESH define (and the
   // 'enable_map_to_mesh' gyp variable).  When we have better support for
@@ -454,11 +439,6 @@
   javascript_engine_->RegisterErrorHandler(error_handler);
 #endif
 
-  javascript_engine_poller_.reset(new base::PollerWithThread(
-      base::Bind(&WebModule::Impl::UpdateJavaScriptEngineStats,
-                 base::Unretained(this)),
-      base::TimeDelta::FromMilliseconds(kPollerPeriodMs)));
-
   global_environment_ = javascript_engine_->CreateGlobalEnvironment();
   DCHECK(global_environment_);
 
@@ -476,8 +456,9 @@
 
   window_ = new dom::Window(
       data.window_dimensions.width(), data.window_dimensions.height(),
-      css_parser_.get(), dom_parser_.get(), fetcher_factory_.get(),
-      &resource_provider_, animated_image_tracker_.get(), image_cache_.get(),
+      data.initial_application_state, css_parser_.get(), dom_parser_.get(),
+      fetcher_factory_.get(), &resource_provider_,
+      animated_image_tracker_.get(), image_cache_.get(),
       reduced_image_cache_capacity_manager_.get(), remote_typeface_cache_.get(),
       mesh_cache_.get(), local_storage_database_.get(), data.media_module,
       data.media_module, execution_state_.get(), script_runner_.get(),
@@ -519,15 +500,15 @@
   DCHECK(!error_callback_.is_null());
 
   layout_manager_.reset(new layout::LayoutManager(
-      name_, window_.get(), base::Bind(&WebModule::Impl::OnRenderTreeProduced,
-                                       base::Unretained(this)),
+      name_, window_.get(),
+      base::Bind(&WebModule::Impl::OnRenderTreeProduced,
+                 base::Unretained(this)),
+      base::Bind(&WebModule::Impl::HandlePointerEvents, base::Unretained(this)),
       data.options.layout_trigger, data.dom_max_element_depth,
       data.layout_refresh_rate, data.network_module->preferred_language(),
       web_module_stat_tracker_->layout_stat_tracker()));
   DCHECK(layout_manager_);
 
-  resource_provider_ = data.resource_provider;
-
 #if defined(ENABLE_DEBUG_CONSOLE)
   debug_overlay_.reset(
       new debug::RenderOverlay(data.render_tree_produced_callback));
@@ -585,7 +566,6 @@
   script_runner_.reset();
   execution_state_.reset();
   global_environment_ = NULL;
-  javascript_engine_poller_.reset();
   javascript_engine_.reset();
   web_module_stat_tracker_.reset();
   local_storage_database_.reset();
@@ -598,24 +578,18 @@
   css_parser_.reset();
 }
 
-void WebModule::Impl::InjectKeyboardEvent(
-    scoped_refptr<dom::Element> element,
-    const dom::KeyboardEvent::Data& event) {
+void WebModule::Impl::InjectInputEvent(scoped_refptr<dom::Element> element,
+                                       const scoped_refptr<dom::Event>& event) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(is_running_);
   DCHECK(window_);
 
-  // Construct the DOM object from the keyboard event builder and inject it
-  // into the window.
-  scoped_refptr<dom::KeyboardEvent> keyboard_event(
-      new dom::KeyboardEvent(event));
-
-  web_module_stat_tracker_->OnStartInjectEvent(keyboard_event);
+  web_module_stat_tracker_->OnStartInjectEvent(event);
 
   if (element) {
-    element->DispatchEvent(keyboard_event);
+    element->DispatchEvent(event);
   } else {
-    window_->InjectEvent(keyboard_event);
+    window_->InjectEvent(event);
   }
 
   web_module_stat_tracker_->OnEndInjectEvent(
@@ -623,14 +597,46 @@
       layout_manager_->IsNewRenderTreePending());
 }
 
+void WebModule::Impl::InjectKeyboardEvent(scoped_refptr<dom::Element> element,
+                                          base::Token type,
+                                          const dom::KeyboardEventInit& event) {
+  scoped_refptr<dom::KeyboardEvent> keyboard_event(
+      new dom::KeyboardEvent(type, window_, event));
+  InjectInputEvent(element, keyboard_event);
+}
+
+void WebModule::Impl::InjectPointerEvent(scoped_refptr<dom::Element> element,
+                                         base::Token type,
+                                         const dom::PointerEventInit& event) {
+  scoped_refptr<dom::PointerEvent> pointer_event(
+      new dom::PointerEvent(type, window_, event));
+  InjectInputEvent(element, pointer_event);
+}
+
+void WebModule::Impl::InjectWheelEvent(scoped_refptr<dom::Element> element,
+                                       base::Token type,
+                                       const dom::WheelEventInit& event) {
+  scoped_refptr<dom::WheelEvent> wheel_event(
+      new dom::WheelEvent(type, window_, event));
+  InjectInputEvent(element, wheel_event);
+}
+
 void WebModule::Impl::ExecuteJavascript(
     const std::string& script_utf8, const base::SourceLocation& script_location,
     base::WaitableEvent* got_result, std::string* result, bool* out_succeeded) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(is_running_);
   DCHECK(script_runner_);
+
+  // JavaScript is being run. Track it in the global stats.
+  dom::GlobalStats::GetInstance()->StartJavaScriptEvent();
+
   *result = script_runner_->Execute(script_utf8, script_location,
       out_succeeded);
+
+  // JavaScript is done running. Stop tracking it in the global stats.
+  dom::GlobalStats::GetInstance()->StopJavaScriptEvent();
+
   got_result->Signal();
 }
 
@@ -706,6 +712,8 @@
       window_id, window_weak_,
       base::Bind(&WebModule::Impl::global_environment, base::Unretained(this)),
       base::Bind(&WebModule::Impl::InjectKeyboardEvent, base::Unretained(this)),
+      base::Bind(&WebModule::Impl::InjectPointerEvent, base::Unretained(this)),
+      base::Bind(&WebModule::Impl::InjectWheelEvent, base::Unretained(this)),
       base::MessageLoopProxy::current()));
 }
 #endif  // defined(ENABLE_WEBDRIVER)
@@ -742,9 +750,32 @@
   }
 }
 
+void WebModule::Impl::SetApplicationState(base::ApplicationState state) {
+  window_->SetApplicationState(state);
+}
+
+void WebModule::Impl::Start(render_tree::ResourceProvider* resource_provider) {
+  TRACE_EVENT0("cobalt::browser", "WebModule::Impl::Start()");
+  SetApplicationState(base::kApplicationStateStarted);
+  // TODO: Initialize resource provider here rather than constructor.
+  DCHECK(resource_provider == resource_provider_);
+}
+
+void WebModule::Impl::Pause() {
+  TRACE_EVENT0("cobalt::browser", "WebModule::Impl::Pause()");
+  SetApplicationState(base::kApplicationStatePaused);
+}
+
+void WebModule::Impl::Unpause() {
+  TRACE_EVENT0("cobalt::browser", "WebModule::Impl::Unpause()");
+  SetApplicationState(base::kApplicationStateStarted);
+}
+
 void WebModule::Impl::SuspendLoaders() {
   TRACE_EVENT0("cobalt::browser", "WebModule::Impl::SuspendLoaders()");
 
+  SetApplicationState(base::kApplicationStateSuspended);
+
   // Purge the resource caches before running any suspend logic. This will force
   // any pending callbacks that the caches are batching to run.
   PurgeResourceCaches();
@@ -801,6 +832,8 @@
   // invalidated with the call to Suspend(), so the layout manager's first task
   // will be to perform a full re-layout.
   layout_manager_->Resume();
+
+  SetApplicationState(base::kApplicationStatePaused);
 }
 
 void WebModule::Impl::ReportScriptError(
@@ -810,7 +843,7 @@
       FilePath(source_location.file_path).BaseName().value();
 
   std::stringstream ss;
-  base::TimeDelta dt = StartupTimer::Instance()->TimeSinceStartup();
+  base::TimeDelta dt = base::StartupTimer::TimeElapsed();
 
   // Create the error output.
   // Example:
@@ -857,7 +890,7 @@
       video_playback_rate_multiplier(1.f) {}
 
 WebModule::WebModule(
-    const GURL& initial_url,
+    const GURL& initial_url, base::ApplicationState initial_application_state,
     const OnRenderTreeProducedCallback& render_tree_produced_callback,
     const OnErrorCallback& error_callback,
     const base::Closure& window_close_callback,
@@ -869,10 +902,10 @@
     const Options& options)
     : thread_(options.name.c_str()) {
   ConstructionData construction_data(
-      initial_url, render_tree_produced_callback, error_callback,
-      window_close_callback, window_minimize_callback, media_module,
-      network_module, window_dimensions, resource_provider, kDOMMaxElementDepth,
-      system_window, layout_refresh_rate, options);
+      initial_url, initial_application_state, render_tree_produced_callback,
+      error_callback, window_close_callback, window_minimize_callback,
+      media_module, network_module, window_dimensions, resource_provider,
+      kDOMMaxElementDepth, system_window, layout_refresh_rate, options);
 
   // Start the dedicated thread and create the internal implementation
   // object on that thread.
@@ -881,21 +914,15 @@
       options.thread_priority));
   DCHECK(message_loop());
 
-  message_loop()->PostTask(
-      FROM_HERE, base::Bind(&WebModule::Initialize, base::Unretained(this),
-                            construction_data));
-
   // Block this thread until the initialization is complete.
   // TODO: Figure out why this is necessary.
   // It would be preferable to return immediately and let the WebModule
   // continue in its own time, but without this wait there is a race condition
   // such that inline scripts may be executed before the document elements they
   // operate on are present.
-  base::WaitableEvent is_initialized(true, false);
-  message_loop()->PostTask(FROM_HERE,
-                           base::Bind(&base::WaitableEvent::Signal,
-                                      base::Unretained(&is_initialized)));
-  is_initialized.Wait();
+  message_loop()->PostBlockingTask(
+      FROM_HERE, base::Bind(&WebModule::Initialize, base::Unretained(this),
+                            construction_data));
 
 #if defined(ENABLE_PARTIAL_LAYOUT_CONTROL)
   CommandLine* command_line = CommandLine::ForCurrentProcess();
@@ -926,16 +953,11 @@
   // destroy the internal components before the message loop is set to NULL.
   // No posted tasks will be executed once the thread is stopped.
   DestructionObserver destruction_observer(this);
-  message_loop()->PostTask(FROM_HERE,
-                           base::Bind(&MessageLoop::AddDestructionObserver,
-                                      base::Unretained(message_loop()),
-                                      base::Unretained(&destruction_observer)));
-
-  base::WaitableEvent did_register_shutdown_observer(true, false);
-  message_loop()->PostTask(
-      FROM_HERE, base::Bind(&base::WaitableEvent::Signal,
-                            base::Unretained(&did_register_shutdown_observer)));
-  did_register_shutdown_observer.Wait();
+  message_loop()->PostBlockingTask(
+      FROM_HERE,
+      base::Bind(&MessageLoop::AddDestructionObserver,
+                 base::Unretained(message_loop()),
+                 base::Unretained(&destruction_observer)));
 
   // This will cancel the timers for tasks, which help the thread exit
   ClearAllIntervalsAndTimeouts();
@@ -949,15 +971,40 @@
   impl_.reset(new Impl(data));
 }
 
-void WebModule::InjectKeyboardEvent(const dom::KeyboardEvent::Data& event) {
+void WebModule::InjectKeyboardEvent(base::Token type,
+                                    const dom::KeyboardEventInit& event) {
   TRACE_EVENT1("cobalt::browser", "WebModule::InjectKeyboardEvent()", "type",
-               event.type);
+               type.c_str());
   DCHECK(message_loop());
   DCHECK(impl_);
-  message_loop()->PostTask(FROM_HERE,
-                           base::Bind(&WebModule::Impl::InjectKeyboardEvent,
-                                      base::Unretained(impl_.get()),
-                                      scoped_refptr<dom::Element>(), event));
+  message_loop()->PostTask(
+      FROM_HERE, base::Bind(&WebModule::Impl::InjectKeyboardEvent,
+                            base::Unretained(impl_.get()),
+                            scoped_refptr<dom::Element>(), type, event));
+}
+
+void WebModule::InjectPointerEvent(base::Token type,
+                                   const dom::PointerEventInit& event) {
+  TRACE_EVENT1("cobalt::browser", "WebModule::InjectPointerEvent()", "type",
+               type.c_str());
+  DCHECK(message_loop());
+  DCHECK(impl_);
+  message_loop()->PostTask(
+      FROM_HERE, base::Bind(&WebModule::Impl::InjectPointerEvent,
+                            base::Unretained(impl_.get()),
+                            scoped_refptr<dom::Element>(), type, event));
+}
+
+void WebModule::InjectWheelEvent(base::Token type,
+                                 const dom::WheelEventInit& event) {
+  TRACE_EVENT1("cobalt::browser", "WebModule::InjectWheelEvent()", "type",
+               type.c_str());
+  DCHECK(message_loop());
+  DCHECK(impl_);
+  message_loop()->PostTask(
+      FROM_HERE, base::Bind(&WebModule::Impl::InjectWheelEvent,
+                            base::Unretained(impl_.get()),
+                            scoped_refptr<dom::Element>(), type, event));
 }
 
 std::string WebModule::ExecuteJavascript(
@@ -1011,16 +1058,11 @@
   DCHECK(impl_);
 
   scoped_ptr<webdriver::WindowDriver> window_driver;
-  message_loop()->PostTask(FROM_HERE,
-                           base::Bind(&WebModule::Impl::CreateWindowDriver,
-                                      base::Unretained(impl_.get()), window_id,
-                                      base::Unretained(&window_driver)));
-
-  base::WaitableEvent window_driver_created(true, false);
-  message_loop()->PostTask(
-      FROM_HERE, base::Bind(&base::WaitableEvent::Signal,
-                            base::Unretained(&window_driver_created)));
-  window_driver_created.Wait();
+  message_loop()->PostBlockingTask(
+      FROM_HERE,
+      base::Bind(&WebModule::Impl::CreateWindowDriver,
+                 base::Unretained(impl_.get()), window_id,
+                 base::Unretained(&window_driver)));
 
   return window_driver.Pass();
 }
@@ -1032,65 +1074,93 @@
   DCHECK(message_loop());
   DCHECK(impl_);
 
-  message_loop()->PostTask(FROM_HERE,
-                           base::Bind(&WebModule::Impl::CreateDebugServerIfNull,
-                                      base::Unretained(impl_.get())));
+  message_loop()->PostBlockingTask(
+      FROM_HERE,
+      base::Bind(&WebModule::Impl::CreateDebugServerIfNull,
+                 base::Unretained(impl_.get())));
 
-  base::WaitableEvent debug_server_created(true, false);
-  message_loop()->PostTask(FROM_HERE,
-                           base::Bind(&base::WaitableEvent::Signal,
-                                      base::Unretained(&debug_server_created)));
-
-  debug_server_created.Wait();
   return impl_->debug_server();
 }
 #endif  // defined(ENABLE_DEBUG_CONSOLE)
 
-void WebModule::Suspend() {
-  TRACE_EVENT0("cobalt::browser", "WebModule::Suspend()");
-
-  // Suspend() must only be called by a thread external from the WebModule
-  // thread.
+void WebModule::Start(render_tree::ResourceProvider* resource_provider) {
+  // Must only be called by a thread external from the WebModule thread.
   DCHECK_NE(MessageLoop::current(), message_loop());
 
-  base::WaitableEvent task_finished(false /* automatic reset */,
-                                    false /* initially unsignaled */);
+  // We must block here so that the call doesn't return until the web
+  // application has had a chance to process the whole event.
+  message_loop()->PostTask(
+      FROM_HERE,
+      base::Bind(&WebModule::Impl::Start, base::Unretained(impl_.get()),
+                 base::Unretained(resource_provider)));
+}
 
-  // Suspension of the WebModule is orchestrated here in two phases.
-  // 1) Send a signal to suspend WebModule loader activity and cancel any
-  //    in-progress loads.  Since loading may occur from any thread, this may
-  //    result in cancel/completion callbacks being posted to message_loop().
-  message_loop()->PostTask(FROM_HERE,
-                           base::Bind(&WebModule::Impl::SuspendLoaders,
-                                      base::Unretained(impl_.get())));
+void WebModule::Pause() {
+  // Must only be called by a thread external from the WebModule thread.
+  DCHECK_NE(MessageLoop::current(), message_loop());
 
-  // Wait for the suspension task to complete before proceeding.
-  message_loop()->PostTask(FROM_HERE,
-                           base::Bind(&base::WaitableEvent::Signal,
-                                      base::Unretained(&task_finished)));
-  task_finished.Wait();
+  // We must block here so that the call doesn't return until the web
+  // application has had a chance to process the whole event.
+  message_loop()->PostBlockingTask(
+      FROM_HERE,
+      base::Bind(&WebModule::Impl::Pause, base::Unretained(impl_.get())));
+}
 
-  // 2) Now append to the task queue a task to complete the suspension process.
-  //    Between 1 and 2, tasks may have been registered to handle resource load
-  //    completion events, and so this FinishSuspend task will be executed after
-  //    the load completions are all resolved.
-  message_loop()->PostTask(FROM_HERE,
-                           base::Bind(&WebModule::Impl::FinishSuspend,
-                                      base::Unretained(impl_.get())));
+void WebModule::Unpause() {
+  // Must only be called by a thread external from the WebModule thread.
+  DCHECK_NE(MessageLoop::current(), message_loop());
 
-  // Wait for suspension to fully complete on the WebModule thread before
-  // continuing.
-  message_loop()->PostTask(FROM_HERE,
-                           base::Bind(&base::WaitableEvent::Signal,
-                                      base::Unretained(&task_finished)));
-  task_finished.Wait();
+  // We must block here so that the call doesn't return until the web
+  // application has had a chance to process the whole event.
+  message_loop()->PostTask(
+      FROM_HERE,
+      base::Bind(&WebModule::Impl::Unpause, base::Unretained(impl_.get())));
+}
+
+void WebModule::Suspend() {
+  // Must only be called by a thread external from the WebModule thread.
+  DCHECK_NE(MessageLoop::current(), message_loop());
+
+  // We must block here so that we don't queue the finish until after
+  // SuspendLoaders has run to completion, and therefore has already queued any
+  // precipitate tasks.
+  message_loop()->PostBlockingTask(FROM_HERE,
+                                   base::Bind(&WebModule::Impl::SuspendLoaders,
+                                              base::Unretained(impl_.get())));
+
+  // We must block here so that the call doesn't return until the web
+  // application has had a chance to process the whole event.
+  message_loop()->PostBlockingTask(FROM_HERE,
+                   base::Bind(&WebModule::Impl::FinishSuspend,
+                              base::Unretained(impl_.get())));
 }
 
 void WebModule::Resume(render_tree::ResourceProvider* resource_provider) {
+  // Must only be called by a thread external from the WebModule thread.
+  DCHECK_NE(MessageLoop::current(), message_loop());
+
+  // We must block here so that the call doesn't return until the web
+  // application has had a chance to process the whole event.
   message_loop()->PostTask(
       FROM_HERE, base::Bind(&WebModule::Impl::Resume,
                             base::Unretained(impl_.get()), resource_provider));
 }
 
+void WebModule::Impl::HandlePointerEvents() {
+  TRACE_EVENT0("cobalt::browser", "WebModule::Impl::HandlePointerEvents");
+  const scoped_refptr<dom::Document>& document = window_->document();
+  scoped_refptr<dom::Event> event;
+  do {
+    event = document->GetNextQueuedPointerEvent();
+    if (event) {
+      SB_DCHECK(
+          window_ ==
+          base::polymorphic_downcast<const dom::UIEvent* const>(event.get())
+              ->view());
+      topmost_event_target_.MaybeSendPointerEvents(event, window_);
+    }
+  } while (event && !layout_manager_->IsNewRenderTreePending());
+}
+
 }  // namespace browser
 }  // namespace cobalt
diff --git a/src/cobalt/browser/web_module.h b/src/cobalt/browser/web_module.h
index d590e3e..0cc79ce 100644
--- a/src/cobalt/browser/web_module.h
+++ b/src/cobalt/browser/web_module.h
@@ -29,6 +29,7 @@
 #include "cobalt/base/address_sanitizer.h"
 #include "cobalt/base/console_commands.h"
 #include "cobalt/base/source_location.h"
+#include "cobalt/browser/lifecycle_observer.h"
 #include "cobalt/css_parser/parser.h"
 #if defined(ENABLE_DEBUG_CONSOLE)
 #include "cobalt/debug/debug_server.h"
@@ -37,9 +38,11 @@
 #include "cobalt/dom/blob.h"
 #include "cobalt/dom/csp_delegate.h"
 #include "cobalt/dom/dom_settings.h"
-#include "cobalt/dom/keyboard_event.h"
+#include "cobalt/dom/keyboard_event_init.h"
 #include "cobalt/dom/local_storage_database.h"
 #include "cobalt/dom/media_source.h"
+#include "cobalt/dom/pointer_event_init.h"
+#include "cobalt/dom/wheel_event_init.h"
 #include "cobalt/dom/window.h"
 #include "cobalt/dom_parser/parser.h"
 #include "cobalt/layout/layout_manager.h"
@@ -74,7 +77,7 @@
 // This necessarily implies that details contained within WebModule, such as the
 // DOM, are intentionally kept private, since these structures expect to be
 // accessed from only one thread.
-class WebModule {
+class WebModule : public LifecycleObserver {
  public:
   struct Options {
     typedef base::Callback<scoped_refptr<script::Wrappable>(
@@ -181,6 +184,7 @@
   typedef base::Callback<void(const GURL&, const std::string&)> OnErrorCallback;
 
   WebModule(const GURL& initial_url,
+            base::ApplicationState initial_application_state,
             const OnRenderTreeProducedCallback& render_tree_produced_callback,
             const OnErrorCallback& error_callback,
             const base::Closure& window_close_callback,
@@ -193,8 +197,19 @@
             float layout_refresh_rate, const Options& options);
   ~WebModule();
 
-  // Call this to inject a keyboard event into the web module.
-  void InjectKeyboardEvent(const dom::KeyboardEvent::Data& event);
+  // Call this to inject a keyboard event into the web module. The value for
+  // type represents the event name, for example 'keydown' or 'keyup'.
+  void InjectKeyboardEvent(base::Token type,
+                           const dom::KeyboardEventInit& event);
+
+  // Call this to inject a pointer event into the web module. The value for type
+  // represents the event name, for example 'pointerdown', 'pointerup', or
+  // 'pointermove'.
+  void InjectPointerEvent(base::Token type, const dom::PointerEventInit& event);
+
+  // Call this to inject a wheel event into the web module. The value for type
+  // represents the event name, for example 'wheel'.
+  void InjectWheelEvent(base::Token type, const dom::WheelEventInit& event);
 
   // Call this to execute Javascript code in this web module.  The calling
   // thread will block until the JavaScript has executed and the output results
@@ -217,13 +232,12 @@
   debug::DebugServer* GetDebugServer();
 #endif  // ENABLE_DEBUG_CONSOLE
 
-  // Suspends the WebModule from creating new render trees, and releases this
-  // web module's reference to the resource provider, clearing it out and
-  // releasing all references to any resources created from it.
-  void Suspend();
-  // Resumes the WebModule, possibly with a new resource provider.  This method
-  // can only be called if we have previously suspended the WebModule.
-  void Resume(render_tree::ResourceProvider* resource_provider);
+  // LifecycleObserver implementation
+  void Start(render_tree::ResourceProvider* resource_provider) OVERRIDE;
+  void Pause() OVERRIDE;
+  void Unpause() OVERRIDE;
+  void Suspend() OVERRIDE;
+  void Resume(render_tree::ResourceProvider* resource_provider) OVERRIDE;
 
  private:
   // Data required to construct a WebModule, initialized in the constructor and
@@ -231,6 +245,7 @@
   struct ConstructionData {
     ConstructionData(
         const GURL& initial_url,
+        base::ApplicationState initial_application_state,
         const OnRenderTreeProducedCallback& render_tree_produced_callback,
         const OnErrorCallback& error_callback,
         const base::Closure& window_close_callback,
@@ -242,6 +257,7 @@
         int dom_max_element_depth, system_window::SystemWindow* system_window,
         float layout_refresh_rate, const Options& options)
         : initial_url(initial_url),
+          initial_application_state(initial_application_state),
           render_tree_produced_callback(render_tree_produced_callback),
           error_callback(error_callback),
           window_close_callback(window_close_callback),
@@ -256,6 +272,7 @@
           options(options) {}
 
     GURL initial_url;
+    base::ApplicationState initial_application_state;
     OnRenderTreeProducedCallback render_tree_produced_callback;
     OnErrorCallback error_callback;
     const base::Closure& window_close_callback;
diff --git a/src/cobalt/browser/web_module_stat_tracker.cc b/src/cobalt/browser/web_module_stat_tracker.cc
index bb5f370..4151969 100644
--- a/src/cobalt/browser/web_module_stat_tracker.cc
+++ b/src/cobalt/browser/web_module_stat_tracker.cc
@@ -80,6 +80,7 @@
   // If this is a valid event type, then start tracking it.
   if (current_event_type_ != kEventTypeInvalid) {
     event_is_processing_ = true;
+    event_start_time_ = base::TimeTicks::Now();
 
     dom_stat_tracker_->OnStartEvent();
     layout_stat_tracker_->OnStartEvent();
@@ -128,6 +129,12 @@
           StringPrintf("Event.Count.%s.DOM.HtmlElement.Destroyed",
                        name.c_str()),
           0, "Number of HTML elements destroyed."),
+      count_dom_html_elements_added(
+          StringPrintf("Event.Count.%s.DOM.HtmlElement.Added", name.c_str()), 0,
+          "Number of HTML elements added to document."),
+      count_dom_html_elements_removed(
+          StringPrintf("Event.Count.%s.DOM.HtmlElement.Removed", name.c_str()),
+          0, "Number of HTML elements removed from document."),
       count_dom_update_matching_rules(
           StringPrintf("Event.Count.%s.DOM.HtmlElement.UpdateMatchingRules",
                        name.c_str()),
@@ -225,6 +232,8 @@
 
 void WebModuleStatTracker::EndCurrentEvent(bool was_render_tree_produced) {
   if (current_event_type_ == kEventTypeInvalid) {
+    dom_stat_tracker_->OnEndEvent();
+    layout_stat_tracker_->OnEndEvent();
     return;
   }
 
@@ -240,6 +249,10 @@
       dom_stat_tracker_->html_elements_created_count();
   event_stats->count_dom_html_elements_destroyed =
       dom_stat_tracker_->html_elements_destroyed_count();
+  event_stats->count_dom_html_elements_added =
+      dom_stat_tracker_->html_elements_added_to_document_count();
+  event_stats->count_dom_html_elements_removed =
+      dom_stat_tracker_->html_elements_removed_from_document_count();
   event_stats->count_dom_update_matching_rules =
       dom_stat_tracker_->update_matching_rules_count();
   event_stats->count_dom_update_computed_style =
@@ -283,17 +296,31 @@
           layout::LayoutStatTracker::kStopWatchTypeRenderAndAnimate);
 
 #if defined(ENABLE_WEBDRIVER)
+  // Include the event's numbers in the total counts.
+  int html_elements_count = dom_stat_tracker_->html_elements_count() +
+                            dom_stat_tracker_->html_elements_created_count() -
+                            dom_stat_tracker_->html_elements_destroyed_count();
+  int document_html_elements_count =
+      dom_stat_tracker_->document_html_elements_count() +
+      dom_stat_tracker_->html_elements_added_to_document_count() -
+      dom_stat_tracker_->html_elements_removed_from_document_count();
+  int layout_boxes_count = layout_stat_tracker_->total_boxes() +
+                           layout_stat_tracker_->boxes_created_count() -
+                           layout_stat_tracker_->boxes_destroyed_count();
+
   // When the Webdriver is enabled, all of the event's values are stored within
   // a single string representing a dictionary of key-value pairs. This allows
   // the Webdriver to query a single CVal to retrieve all of the event's values.
   std::ostringstream oss;
   oss << "{"
+      << "\"StartTime\":" << event_start_time_.ToInternalValue() << ", "
       << "\"ProducedRenderTree\":" << was_render_tree_produced << ", "
       << "\"CntDomEventListeners\":"
       << dom::GlobalStats::GetInstance()->GetNumEventListeners() << ", "
       << "\"CntDomNodes\":" << dom::GlobalStats::GetInstance()->GetNumNodes()
       << ", "
-      << "\"CntDomHtmlElements\":" << dom_stat_tracker_->total_html_elements()
+      << "\"CntDomHtmlElements\":" << html_elements_count << ", "
+      << "\"CntDomDocumentHtmlElements\":" << document_html_elements_count
       << ", "
       << "\"CntDomHtmlElementsCreated\":"
       << dom_stat_tracker_->html_elements_created_count() << ", "
@@ -306,7 +333,7 @@
       << "\"CntDomGeneratePseudoComputedStyle\":"
       << dom_stat_tracker_->generate_pseudo_element_computed_style_count()
       << ", "
-      << "\"CntLayoutBoxes\":" << layout_stat_tracker_->total_boxes() << ", "
+      << "\"CntLayoutBoxes\":" << layout_boxes_count << ", "
       << "\"CntLayoutBoxesCreated\":"
       << layout_stat_tracker_->boxes_created_count() << ", "
       << "\"CntLayoutUpdateSize\":" << layout_stat_tracker_->update_size_count()
diff --git a/src/cobalt/browser/web_module_stat_tracker.h b/src/cobalt/browser/web_module_stat_tracker.h
index 3956a52..08ae658 100644
--- a/src/cobalt/browser/web_module_stat_tracker.h
+++ b/src/cobalt/browser/web_module_stat_tracker.h
@@ -19,6 +19,7 @@
 #include <vector>
 
 #include "base/memory/scoped_vector.h"
+#include "base/time.h"
 #include "cobalt/base/c_val.h"
 #include "cobalt/base/stop_watch.h"
 #include "cobalt/dom/dom_stat_tracker.h"
@@ -83,6 +84,8 @@
     // Count-related
     base::CVal<int, base::CValPublic> count_dom_html_elements_created;
     base::CVal<int, base::CValPublic> count_dom_html_elements_destroyed;
+    base::CVal<int, base::CValPublic> count_dom_html_elements_added;
+    base::CVal<int, base::CValPublic> count_dom_html_elements_removed;
     base::CVal<int, base::CValPublic> count_dom_update_matching_rules;
     base::CVal<int, base::CValPublic> count_dom_update_computed_style;
     base::CVal<int, base::CValPublic>
@@ -144,7 +147,8 @@
 
   std::string name_;
 
-  base::CVal<bool, base::CValPublic> event_is_processing_;
+  base::CVal<bool> event_is_processing_;
+  base::TimeTicks event_start_time_;
 };
 
 }  // namespace browser
diff --git a/src/cobalt/build/all.gyp b/src/cobalt/build/all.gyp
index 663e4ba..74e0b49 100644
--- a/src/cobalt/build/all.gyp
+++ b/src/cobalt/build/all.gyp
@@ -56,6 +56,7 @@
         '<(DEPTH)/cobalt/media_session/media_session.gyp:*',
         '<(DEPTH)/cobalt/media_session/media_session_test.gyp:*',
         '<(DEPTH)/cobalt/network/network.gyp:*',
+        '<(DEPTH)/cobalt/page_visibility/page_visibility.gyp:*',
         '<(DEPTH)/cobalt/render_tree/render_tree.gyp:*',
         '<(DEPTH)/cobalt/renderer/renderer.gyp:*',
         '<(DEPTH)/cobalt/renderer/sandbox/sandbox.gyp:*',
@@ -72,6 +73,7 @@
         '<(DEPTH)/cobalt/websocket/websocket.gyp:*',
         '<(DEPTH)/cobalt/xhr/xhr.gyp:*',
         '<(DEPTH)/crypto/crypto.gyp:crypto_unittests',
+        '<(DEPTH)/net/net.gyp:net_unittests',
         '<(DEPTH)/sql/sql.gyp:sql_unittests',
       ],
       'conditions': [
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index 8a3b29a..66e3496 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-62465
\ No newline at end of file
+71685
\ No newline at end of file
diff --git a/src/cobalt/build/config/base.gypi b/src/cobalt/build/config/base.gypi
index f3941f4..3133cd6 100644
--- a/src/cobalt/build/config/base.gypi
+++ b/src/cobalt/build/config/base.gypi
@@ -21,9 +21,9 @@
     # Whether Cobalt is being built.
     'cobalt': 1,
 
-    # Similarly to chromium_code, marks the projects that are created by the
-    # Cobalt team and thus are held to the highest standards of code health.
-    'cobalt_code%': 0,
+    # Enabling this variable enables pedantic levels of warnings for the current
+    # toolchain.
+    'sb_pedantic_warnings%': 0,
 
     # Contains the current build configuration.
     'cobalt_config%': 'gold',
@@ -206,14 +206,15 @@
     # Set to 1 to compile with SPDY support.
     'enable_spdy%': 0,
 
+    # Set to 1 to enable filtering of HTTP headers before sending.
+    'enable_xhr_header_filtering%': 0,
+
     # Halt execution on failure to allocate memory.
     'abort_on_allocation_failure%': 1,
 
     # Used by cobalt/media/media.gyp to pick a proper media platform.
     'sb_media_platform%': 'starboard',
 
-    'sb_has_deploy_step%': 0,
-
     # Needed for backwards compatibility with lbshell code.
     'lbshell_root%': '<(DEPTH)/lbshell',
 
@@ -407,10 +408,10 @@
     'platform_libraries%': [],
 
 
-    # The only currently-supported Javascript engine is 'mozjs'.
+    # The only currently-supported Javascript engine is 'mozjs-45'.
     # TODO: Figure out how to massage gyp the right way to make this work
     # as expected, rather than requiring it to be set for each platform.
-    #'javascript_engine%': 'mozjs',
+    #'javascript_engine%': 'mozjs-45',
 
     # Disable jit and run in interpreter-only mode by default. It can be set to
     # 1 to run in jit mode.  We have found that disabling jit often results in
@@ -489,16 +490,16 @@
 
   'target_defaults': {
     'variables': {
-      # The condition that operates on cobalt_code is in a target_conditions
-      # section, and will not have access to the default fallback value of
-      # cobalt_code at the top of this file, or to the cobalt_code
-      # variable placed at the root variables scope of .gyp files, because
-      # those variables are not set at target scope.  As a workaround,
-      # if cobalt_code is not set at target scope, define it in target scope
-      # to contain whatever value it has during early variable expansion.
-      # That's enough to make it available during target conditional
-      # processing.
-      'cobalt_code%': '<(cobalt_code)',
+      # The condition that operates on sb_pedantic_warnings is in a
+      # target_conditions section, and will not have access to the default
+      # fallback value of sb_pedantic_warnings at the top of this file,
+      # or to the sb_pedantic_warnings variable placed at the root
+      # variables scope of .gyp files, because those variables are not set at
+      # target scope.  As a workaround, if sb_pedantic_warnings is not
+      # set at target scope, define it in target scope to contain whatever
+      # value it has during early variable expansion. That's enough to make
+      # it available during target conditional processing.
+      'sb_pedantic_warnings%': '<(sb_pedantic_warnings)',
     },
     'defines': [
       'COBALT',
diff --git a/src/cobalt/build/config/base.py b/src/cobalt/build/config/base.py
index 622a61e..d7cd087 100644
--- a/src/cobalt/build/config/base.py
+++ b/src/cobalt/build/config/base.py
@@ -34,7 +34,7 @@
 VALID_BUILD_CONFIGS = [Configs.DEBUG, Configs.DEVEL, Configs.QA, Configs.GOLD]
 
 # Represents all supported platforms, uniquified and sorted.
-VALID_PLATFORMS = sorted(gyp_utils.GetThirdPartyPlatforms().keys())
+VALID_PLATFORMS = sorted(gyp_utils.GetAllPlatforms().keys())
 
 _CURRENT_PATH = os.path.abspath(os.path.dirname(__file__))
 
@@ -69,9 +69,11 @@
     Returns:
         A list containing paths to .gypi files.
     """
-    platforms = gyp_utils.GetThirdPartyPlatforms()
-    if self.platform in platforms:
-      return [os.path.join(platforms[self.platform], 'gyp_configuration.gypi')]
+    platforms = gyp_utils.GetAllPlatforms()
+    if self.platform in platforms.keys():
+      return [
+          os.path.join(platforms[self.platform].path, 'gyp_configuration.gypi')
+      ]
     return [os.path.join(self.config_path, self.platform + '.gypi')]
 
   def GetEnvironmentVariables(self):
@@ -111,9 +113,9 @@
   """
   try:
     logging.debug('Loading platform configuration for "%s".', platform)
-    platforms = gyp_utils.GetThirdPartyPlatforms()
-    if platform in platforms:
-      platform_path = platforms[platform]
+    platforms = gyp_utils.GetAllPlatforms()
+    if platform in platforms.keys():
+      platform_path = platforms[platform].path
       module_path = os.path.join(platform_path, 'gyp_configuration.py')
       platform_module = imp.load_source('platform_module', module_path)
     else:
diff --git a/src/cobalt/build/config/starboard.py b/src/cobalt/build/config/starboard.py
index 562c166..07ffc4b 100644
--- a/src/cobalt/build/config/starboard.py
+++ b/src/cobalt/build/config/starboard.py
@@ -30,6 +30,13 @@
   def IsStarboard(self):
     return True
 
+  def GetEnvironmentVariables(self):
+    raise NotImplementedError
+
+  def GetToolchain(self):
+    """Returns the instance of the toolchain implementation class."""
+    return None
+
   def GetVariables(self, config, use_clang=0):
     use_asan = 0
     use_tsan = 0
diff --git a/src/cobalt/build/gyp_cobalt b/src/cobalt/build/gyp_cobalt
index e83ef44..4444e7e 100755
--- a/src/cobalt/build/gyp_cobalt
+++ b/src/cobalt/build/gyp_cobalt
@@ -157,9 +157,11 @@
     # Make a copy of the common arguments.
     args = self.common_args[:]
 
-    # Add config specific variables.
     variables = {
+        # Add config specific variables.
         'cobalt_config': config_name,
+        # Default deploy script. Certain platforms may choose to change this.
+        'include_path_platform_deploy_gypi': 'starboard/build/deploy.gypi'
     }
     variables.update(self.platform_config.GetVariables(config_name))
     _AppendVariables(variables, args)
@@ -234,9 +236,9 @@
     if self.platform_config.IsStarboard():
       variables['OS'] = 'starboard'
       platform = self.platform_config.platform
-      platforms = gyp_utils.GetThirdPartyPlatforms()
-      if platform in platforms:
-        full_starboard_path = platforms[platform]
+      platforms = gyp_utils.GetAllPlatforms()
+      if platform in platforms.keys():
+        full_starboard_path = platforms[platform].path
         assert full_starboard_path[:len(source_tree_dir)] == source_tree_dir
         starboard_path = full_starboard_path[len(source_tree_dir) + 1:]
         starboard_path = starboard_path.replace(os.sep, '/')
diff --git a/src/cobalt/build/gyp_utils.py b/src/cobalt/build/gyp_utils.py
index 28248ef..bf38b8a 100644
--- a/src/cobalt/build/gyp_utils.py
+++ b/src/cobalt/build/gyp_utils.py
@@ -277,8 +277,8 @@
   return value
 
 
-def _FindThirdPartyPlatforms():
-  """Workhorse for GetThirdPartyPlatforms().
+def _FindAllPlatforms():
+  """Workhorse for GetAllPlatforms().
 
   Search through directories listed in _PORT_SEARCH_PATH to find valid
   ports, so that they can be added to the VALID_PLATFORMS list. This
@@ -286,7 +286,7 @@
   code in src/cobalt/.
 
   Returns:
-    A dictionary of name->absolute paths to the port directory.
+    A dictionary of name->PlatformInfo.
 
   """
 
@@ -306,19 +306,19 @@
         logging.error('Found duplicate port name "%s" at "%s" and "%s"',
                       platform_info.port_name, result[platform_info.port_name],
                       platform_info.path)
-      result[platform_info.port_name] = platform_info.path
+      result[platform_info.port_name] = platform_info
 
   return result
 
 
 # Global cache of TPP so the filesystem walk is only done once, and the values
 # are always consistent.
-_THIRD_PARTY_PLATFORMS = None
+_ALL_PLATFORMS = None
 
 
-def GetThirdPartyPlatforms():
+def GetAllPlatforms():
   """Return valid platform definitions found by scanning the source tree."""
-  global _THIRD_PARTY_PLATFORMS
-  if _THIRD_PARTY_PLATFORMS is None:
-    _THIRD_PARTY_PLATFORMS = _FindThirdPartyPlatforms()
-  return _THIRD_PARTY_PLATFORMS
+  global _ALL_PLATFORMS
+  if _ALL_PLATFORMS is None:
+    _ALL_PLATFORMS = _FindAllPlatforms()
+  return _ALL_PLATFORMS
diff --git a/src/cobalt/build/ninja/ninja-linux32.armv7l b/src/cobalt/build/ninja/ninja-linux32.armv7l
index e69de29..cb4c3ec 100644
--- a/src/cobalt/build/ninja/ninja-linux32.armv7l
+++ b/src/cobalt/build/ninja/ninja-linux32.armv7l
Binary files differ
diff --git a/src/cobalt/csp/csp.gyp b/src/cobalt/csp/csp.gyp
index a1e9144..e8e9ac7 100644
--- a/src/cobalt/csp/csp.gyp
+++ b/src/cobalt/csp/csp.gyp
@@ -14,7 +14,7 @@
 
 {
   'variables': {
-    'cobalt_code': 1,
+    'sb_pedantic_warnings': 1,
   },
   'targets': [
     {
diff --git a/src/cobalt/css_parser/css_parser.gyp b/src/cobalt/css_parser/css_parser.gyp
index 37ea756..24bf201 100644
--- a/src/cobalt/css_parser/css_parser.gyp
+++ b/src/cobalt/css_parser/css_parser.gyp
@@ -14,7 +14,7 @@
 
 {
   'variables': {
-    'cobalt_code': 1,
+    'sb_pedantic_warnings': 1,
 
     # Define the platform specific Bison binary.
     'conditions': [
diff --git a/src/cobalt/css_parser/parser.cc b/src/cobalt/css_parser/parser.cc
index 7be1525..b8b9047 100644
--- a/src/cobalt/css_parser/parser.cc
+++ b/src/cobalt/css_parser/parser.cc
@@ -400,6 +400,11 @@
         LogError("unrecoverable syntax error");
       }
       return false;
+    case 2:
+      LogError(
+          "the html put too many queries on bison stack without "
+          "closing/reducing");
+      return false;
     default:
       NOTREACHED();
       return false;
diff --git a/src/cobalt/cssom/cssom.gyp b/src/cobalt/cssom/cssom.gyp
index 08156e5..8adc37f 100644
--- a/src/cobalt/cssom/cssom.gyp
+++ b/src/cobalt/cssom/cssom.gyp
@@ -14,7 +14,7 @@
 
 {
   'variables': {
-    'cobalt_code': 1,
+    'sb_pedantic_warnings': 1,
   },
   'targets': [
     {
diff --git a/src/cobalt/cssom/cssom_test.gyp b/src/cobalt/cssom/cssom_test.gyp
index 8478def..318282a 100644
--- a/src/cobalt/cssom/cssom_test.gyp
+++ b/src/cobalt/cssom/cssom_test.gyp
@@ -14,7 +14,7 @@
 
 {
   'variables': {
-    'cobalt_code': 1,
+    'sb_pedantic_warnings': 1,
   },
   'targets': [
     {
diff --git a/src/cobalt/debug/content/cobalt-logo-180.png b/src/cobalt/debug/content/cobalt-logo-180.png
index 4488434..f50f70c 100644
--- a/src/cobalt/debug/content/cobalt-logo-180.png
+++ b/src/cobalt/debug/content/cobalt-logo-180.png
Binary files differ
diff --git a/src/cobalt/debug/content/favicon.ico b/src/cobalt/debug/content/favicon.ico
index eae7eed..2d01cfa 100644
--- a/src/cobalt/debug/content/favicon.ico
+++ b/src/cobalt/debug/content/favicon.ico
Binary files differ
diff --git a/src/cobalt/debug/debug.gyp b/src/cobalt/debug/debug.gyp
index 9fa32c3..3242c1d 100644
--- a/src/cobalt/debug/debug.gyp
+++ b/src/cobalt/debug/debug.gyp
@@ -14,7 +14,7 @@
 
 {
   'variables': {
-    'cobalt_code': 1,
+    'sb_pedantic_warnings': 1,
   },
   'targets': [
     {
diff --git a/src/cobalt/doc/performance_tuning.md b/src/cobalt/doc/performance_tuning.md
index 44fc05f..a142028 100644
--- a/src/cobalt/doc/performance_tuning.md
+++ b/src/cobalt/doc/performance_tuning.md
@@ -295,16 +295,12 @@
 wraps it with a custom allocator, in order to avoid fragmentation of main
 memory.  However, depending on your platform and your system allocator,
 overall memory usage may improve if media buffer allocations were made
-normally via the system allocator instead.  TODO: Unfortunately, there is
-currently no flip to switch to toggle one method or the other, however
-we should look into this because switching to using the system allocator
-has been found to reduce memory usage by ~5MB.  In the meantime, it can
-be hacked by modifying
-[`cobalt/media/shell_media_platform_starboard.cc`](../media/shell_media_platform_starboard.cc).
-Note also that if you choose to pre-allocate memory, for 1080p video it has been
-found that 24MB is a good media buffer size.  The pre-allocated media buffer
-capacity size can be adjusted by modifying the value of
-`SB_MEDIA_MAIN_BUFFER_BUDGET` in your platform's `configuration_public.h` file.
+normally via the system allocator instead.  This can be achieved by setting
+`cobalt_media_buffer_initial_capacity` and `cobalt_media_buffer_allocation_unit`
+to 0 in gyp_configuration.gypi.  Note also that if you choose to pre-allocate
+memory, for 1080p video it has been found that 24MB is a good media buffer size.
+The pre-allocated media buffer capacity size can be adjusted by modifying the
+value of `cobalt_media_buffer_initial_capacity` mentioned above.
 
 **Tags:** *configuration_public.h, cpu memory.*
 
diff --git a/src/cobalt/dom/animation_frame_request_callback_list.cc b/src/cobalt/dom/animation_frame_request_callback_list.cc
index 6108afc..e20138e 100644
--- a/src/cobalt/dom/animation_frame_request_callback_list.cc
+++ b/src/cobalt/dom/animation_frame_request_callback_list.cc
@@ -15,6 +15,7 @@
 #include "cobalt/dom/animation_frame_request_callback_list.h"
 
 #include "base/debug/trace_event.h"
+#include "cobalt/dom/global_stats.h"
 
 namespace cobalt {
 namespace dom {
@@ -44,12 +45,18 @@
   TRACE_EVENT1("cobalt::dom", "Window::RunAnimationFrameCallbacks()",
                "number_of_callbacks", frame_request_callbacks_.size());
 
+  // The callbacks are now being run. Track it in the global stats.
+  GlobalStats::GetInstance()->StartJavaScriptEvent();
+
   for (InternalList::const_iterator iter = frame_request_callbacks_.begin();
        iter != frame_request_callbacks_.end(); ++iter) {
     if (!(*iter)->cancelled) {
       (*iter)->callback.value().Run(animation_time);
     }
   }
+
+  // The callbacks are done running. Stop tracking it in the global stats.
+  GlobalStats::GetInstance()->StopJavaScriptEvent();
 }
 
 bool AnimationFrameRequestCallbackList::HasPendingCallbacks() const {
diff --git a/src/cobalt/dom/camera_3d.cc b/src/cobalt/dom/camera_3d.cc
index 297d1cd..9741f74 100644
--- a/src/cobalt/dom/camera_3d.cc
+++ b/src/cobalt/dom/camera_3d.cc
@@ -16,10 +16,19 @@
 
 #include "cobalt/dom/camera_3d.h"
 
+#include "third_party/glm/glm/gtx/euler_angles.hpp"
+
+#include "base/time.h"
+#include "cobalt/dom/device_orientation_event.h"
+
 namespace cobalt {
 namespace dom {
 
-Camera3D::Camera3D(const scoped_refptr<input::Camera3D>& impl) : impl_(impl) {}
+Camera3D::Camera3D(const scoped_refptr<input::Camera3D>& impl)
+    : impl_(impl),
+      last_event_alpha_(720.0),
+      last_event_beta_(720.0),
+      last_event_gamma_(720.0) {}
 
 void Camera3D::CreateKeyMapping(int keycode, uint32 camera_axis,
                                 float degrees_per_second) {
@@ -32,5 +41,88 @@
 
 void Camera3D::Reset() { impl_->Reset(); }
 
+void Camera3D::StartOrientationEvents(
+    const base::WeakPtr<EventTarget>& target) {
+  if (!impl()) {
+    return;
+  }
+  orientation_event_timer_.Start(
+      FROM_HERE,
+      base::TimeDelta::FromSecondsD(1.0 / ORIENTATION_POLL_FREQUENCY_HZ),
+      base::Bind(&Camera3D::FireOrientationEvent, base::Unretained(this),
+                 target));
+}
+
+void Camera3D::StopOrientationEvents() { orientation_event_timer_.Stop(); }
+
+namespace {
+
+// These produces angles within the ranges: alpha in [-180,180], beta in
+// [-90,90], gamma in [-180,180].
+static void QuaternionToIntrinsicZXY(const glm::dquat& q, double* intrinsic_z,
+                                     double* intrinsic_x, double* intrinsic_y) {
+  *intrinsic_z = atan2(2 * (q.x * q.y + q.w * q.z),
+                       q.w * q.w - q.x * q.x + q.y * q.y - q.z * q.z);
+  *intrinsic_x = asin(-2 * (q.y * q.z - q.w * q.x));
+  *intrinsic_y = atan2(2 * (q.x * q.z + q.w * q.y),
+                       q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z);
+}
+
+}  // namespace
+
+void Camera3D::FireOrientationEvent(base::WeakPtr<EventTarget> target) {
+  glm::dquat quaternion = glm::normalize(
+      glm::dquat(impl()->GetOrientation()) *
+      // The API assumes a different initial orientation (straight down instead
+      // of ahead), requiring a previous rotation of 90 degrees.
+      glm::angleAxis(M_PI / 2, glm::dvec3(1.0f, 0.0f, 0.0f)));
+
+  double intrinsic_z = 0.0f, intrinsic_x = 0.0f, intrinsic_y = 0.0f;
+  QuaternionToIntrinsicZXY(quaternion, &intrinsic_z, &intrinsic_x,
+                           &intrinsic_y);
+
+  // Adjust to the angle ranges specified by the Device Orientation API.
+  // They should be: alpha in [0,360], beta in [-180,180], gamma in [-90,90].
+  double alpha = input::Camera3D::RadiansToDegrees(intrinsic_z);
+  double beta = input::Camera3D::RadiansToDegrees(intrinsic_x);
+  double gamma = -input::Camera3D::RadiansToDegrees(intrinsic_y);
+
+  if (gamma > 90 || gamma < -90) {
+    gamma = copysign(180, gamma) - gamma;
+    // Represent excess gamma as rotations along other axes.
+    beta = 180 - beta;
+    alpha = 180 - alpha;
+  }
+
+  alpha = fmod(360 + alpha, 360);
+  beta = beta >= 180 ? beta - 360 : beta;
+
+  // Filter event based the time elapsed and the angle deltas since the last
+  // event.
+  base::TimeTicks now = base::TimeTicks::Now();
+  if (!last_event_time_ ||
+      (now - *last_event_time_).InSecondsF() >
+          1.0 / ORIENTATION_EVENT_MIN_FREQUENCY_HZ ||
+      fabs(alpha - last_event_alpha_) >
+          ORIENTATION_EVENT_DELTA_THRESHOLD_DEGREES ||
+      fabs(beta - last_event_beta_) >
+          ORIENTATION_EVENT_DELTA_THRESHOLD_DEGREES ||
+      fabs(gamma - last_event_gamma_) >
+          ORIENTATION_EVENT_DELTA_THRESHOLD_DEGREES) {
+    DeviceOrientationEventInit init;
+    init.set_absolute(false);
+    init.set_alpha(alpha);
+    init.set_beta(beta);
+    init.set_gamma(gamma);
+
+    target->DispatchEvent(
+        new DeviceOrientationEvent("deviceorientation", init));
+    last_event_time_ = now;
+    last_event_alpha_ = alpha;
+    last_event_beta_ = beta;
+    last_event_gamma_ = gamma;
+  }
+}
+
 }  // namespace dom
 }  // namespace cobalt
diff --git a/src/cobalt/dom/camera_3d.h b/src/cobalt/dom/camera_3d.h
index 056683b..792ea1f 100644
--- a/src/cobalt/dom/camera_3d.h
+++ b/src/cobalt/dom/camera_3d.h
@@ -17,6 +17,8 @@
 #ifndef COBALT_DOM_CAMERA_3D_H_
 #define COBALT_DOM_CAMERA_3D_H_
 
+#include "base/timer.h"
+#include "cobalt/dom/event_target.h"
 #include "cobalt/input/camera_3d.h"
 #include "cobalt/script/wrappable.h"
 
@@ -35,6 +37,18 @@
     kDomCameraYaw = input::Camera3D::kCameraYaw,
   };
 
+  // The frequency at which Camera3D will poll the hardware implementation.
+  // Effectively the maximum frequency at which orientation events will be
+  // dispatched.
+  static constexpr double ORIENTATION_POLL_FREQUENCY_HZ = 30.0;
+  // The minimum frequency at which Camera3D will dispatch events. Its inverse
+  // is the most that will elapse between dispatching consecutive events (might
+  // not be true in practice due to other processing done on the web thread).
+  static constexpr double ORIENTATION_EVENT_MIN_FREQUENCY_HZ = 0.5;
+  // The minimum change in any of the angles that will trigger an event. The
+  // above frequencies still have precedence to decide the timing.
+  static constexpr double ORIENTATION_EVENT_DELTA_THRESHOLD_DEGREES = 0.5;
+
   explicit Camera3D(const scoped_refptr<input::Camera3D>& impl);
 
   // Creates a mapping between the specified keyCode and the specified camera
@@ -54,9 +68,14 @@
   // Custom, not in any spec.
   scoped_refptr<input::Camera3D> impl() { return impl_; }
 
+  void StartOrientationEvents(const base::WeakPtr<EventTarget>& target);
+  void StopOrientationEvents();
+
   DEFINE_WRAPPABLE_TYPE(Camera3D);
 
  private:
+  void FireOrientationEvent(const base::WeakPtr<EventTarget> target);
+
   // We delegate all calls to the implementation of Camera3D so that all camera
   // state is stored within an object that is *not* a script::Wrappable. This
   // is important because input::Camera3D will typically be attached to a render
@@ -65,6 +84,13 @@
   // for just this.
   scoped_refptr<input::Camera3D> impl_;
 
+  // State to control the polling and event firing rate.
+  base::RepeatingTimer<Camera3D> orientation_event_timer_;
+  base::optional<base::TimeTicks> last_event_time_;
+  double last_event_alpha_;
+  double last_event_beta_;
+  double last_event_gamma_;
+
   DISALLOW_COPY_AND_ASSIGN(Camera3D);
 };
 
diff --git a/src/cobalt/dom/camera_3d_impl.cc b/src/cobalt/dom/camera_3d_impl.cc
deleted file mode 100644
index 90a502e..0000000
--- a/src/cobalt/dom/camera_3d_impl.cc
+++ /dev/null
@@ -1,152 +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.
- */
-
-#include "cobalt/dom/camera_3d_impl.h"
-
-#include <algorithm>
-#include <cmath>
-
-#include "third_party/glm/glm/gtc/matrix_transform.hpp"
-#include "third_party/glm/glm/gtx/transform.hpp"
-
-namespace cobalt {
-namespace dom {
-
-Camera3DImpl::Camera3DImpl(
-    const scoped_refptr<input::InputPoller>& input_poller)
-    : input_poller_(input_poller) {}
-
-void Camera3DImpl::CreateKeyMapping(int keycode, uint32 camera_axis,
-                                    float degrees_per_second) {
-  base::AutoLock lock(mutex_);
-  keycode_map_[keycode] = KeycodeMappingInfo(camera_axis, degrees_per_second);
-}
-
-void Camera3DImpl::ClearKeyMapping(int keycode) {
-  base::AutoLock lock(mutex_);
-  keycode_map_.erase(keycode);
-}
-
-void Camera3DImpl::ClearAllKeyMappings() {
-  base::AutoLock lock(mutex_);
-  keycode_map_.clear();
-}
-
-void Camera3DImpl::Reset() {
-  base::AutoLock lock(mutex_);
-  orientation_ = Orientation();
-}
-
-namespace {
-
-const float kPiF = static_cast<float>(M_PI);
-
-float DegreesToRadians(float degrees) { return (degrees / 360.0f) * 2 * kPiF; }
-
-}  // namespace
-
-glm::mat4 Camera3DImpl::QueryViewPerspectiveMatrix(
-    float width_over_height_aspect_ratio, float max_horizontal_fov_rad,
-    float max_vertical_fov_rad) {
-  base::AutoLock lock(mutex_);
-  AccumulateOrientation();
-
-  // Setup a temporary demo camera animation to show that this functionality
-  // works.  This should eventually be replaced by camera adjustments driven
-  // by input.  Note that we invert the rotation angles since this matrix is
-  // applied to the objects in our scene, and if the camera moves right, the
-  // objects, relatively, would move right.
-  glm::mat4 camera_rotations =
-      glm::rotate(-DegreesToRadians(orientation_.roll), glm::vec3(0, 0, 1)) *
-      glm::rotate(-DegreesToRadians(orientation_.pitch), glm::vec3(1, 0, 0)) *
-      glm::rotate(-DegreesToRadians(orientation_.yaw), glm::vec3(0, 1, 0));
-
-  // Setup a (right-handed) perspective projection matrix.
-  float vertical_fov_rad =
-      std::min(max_vertical_fov_rad,
-               2 * static_cast<float>(atan(tan(max_horizontal_fov_rad * 0.5f) /
-                                           width_over_height_aspect_ratio)));
-
-  const float kNearZ = 0.01f;
-  const float kFarZ = 1000.0f;
-  glm::mat4 projection = glm::perspectiveRH(
-      vertical_fov_rad, width_over_height_aspect_ratio, kNearZ, kFarZ);
-  return projection * camera_rotations;
-}
-
-void Camera3DImpl::AccumulateOrientation() {
-  if (!input_poller_) {
-    // Nothing to do if no input poller was provided.
-    return;
-  }
-
-  base::TimeTicks now = base::TimeTicks::Now();
-  if (last_update_) {
-    base::TimeDelta delta = now - *last_update_;
-    // Cap out the maximum time delta that we will accumulate changes over, to
-    // avoid a random extra long time delta that completely changes the camera
-    // orientation.
-    const base::TimeDelta kMaxTimeDelta = base::TimeDelta::FromMilliseconds(40);
-    if (delta > kMaxTimeDelta) {
-      delta = kMaxTimeDelta;
-    }
-
-    for (KeycodeMap::const_iterator iter = keycode_map_.begin();
-         iter != keycode_map_.end(); ++iter) {
-      // If the key does not have analog output, the AnalogInput() method will
-      // always return 0.0f, so check this first, and if it is indeed 0,
-      // fallback to a check to see if the button is digital and pressed.
-      float value = input_poller_->AnalogInput(static_cast<SbKey>(iter->first));
-      if (value == 0.0f) {
-        value = input_poller_->IsPressed(static_cast<SbKey>(iter->first))
-                    ? 1.0f
-                    : 0.0f;
-      }
-
-      // Get a pointer to the camera axis angle that this key is bound to.
-      float* target_angle;
-      switch (iter->second.axis) {
-        case kCameraRoll:
-          target_angle = &orientation_.roll;
-          break;
-        case kCameraPitch:
-          target_angle = &orientation_.pitch;
-          break;
-        case kCameraYaw:
-          target_angle = &orientation_.yaw;
-          break;
-      }
-
-      // Apply the angle adjustment from the key.
-      *target_angle += value * iter->second.degrees_per_second *
-                       static_cast<float>(delta.InSecondsF());
-
-      // Apply any clamping or wrapping to the resulting camera angles.
-      if (iter->second.axis == kCameraPitch) {
-        *target_angle = std::min(90.0f, std::max(-90.0f, *target_angle));
-      } else {
-        *target_angle = static_cast<float>(fmod(*target_angle, 360));
-        if (*target_angle < 0) {
-          *target_angle += 360;
-        }
-      }
-    }
-  }
-  last_update_ = now;
-}
-
-}  // namespace dom
-}  // namespace cobalt
diff --git a/src/cobalt/dom/camera_3d_impl.h b/src/cobalt/dom/camera_3d_impl.h
deleted file mode 100644
index 311d2ed..0000000
--- a/src/cobalt/dom/camera_3d_impl.h
+++ /dev/null
@@ -1,106 +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.
- */
-
-#ifndef COBALT_DOM_CAMERA_3D_IMPL_H_
-#define COBALT_DOM_CAMERA_3D_IMPL_H_
-
-#include <map>
-#include <utility>
-
-#include "base/optional.h"
-#include "base/synchronization/lock.h"
-#include "cobalt/input/input_poller.h"
-#include "third_party/glm/glm/mat4x4.hpp"
-
-namespace cobalt {
-namespace dom {
-
-// 3D camera is used for setting the key mapping.
-class Camera3DImpl : public base::RefCountedThreadSafe<Camera3DImpl> {
- public:
-  enum CameraAxes {
-    // Restricted to [0deg, 360deg]
-    kCameraRoll = 0x00,
-    // Restricted to [-90deg, 90deg]
-    kCameraPitch = 0x01,
-    // Restricted to [0deg, 360deg]
-    kCameraYaw = 0x02,
-  };
-
-  explicit Camera3DImpl(const scoped_refptr<input::InputPoller>& input_poller);
-
-  void CreateKeyMapping(int keycode, uint32 camera_axis,
-                        float degrees_per_second);
-  void ClearKeyMapping(int keycode);
-  void ClearAllKeyMappings();
-
-  void Reset();
-
-  // Returns the camera's view-perspective matrix, setup according to the passed
-  // in width/height aspect ratio.  It is likely that this function will be
-  // called from another thread, like a renderer thread.
-  glm::mat4 QueryViewPerspectiveMatrix(float width_over_height_aspect_ratio,
-                                       float max_horizontal_fov_rad,
-                                       float max_vertical_fov_rad);
-
- private:
-  struct KeycodeMappingInfo {
-    KeycodeMappingInfo() : axis(0), degrees_per_second(0.0f) {}
-    KeycodeMappingInfo(uint32 axis, float degrees_per_second)
-        : axis(axis), degrees_per_second(degrees_per_second) {}
-
-    uint32 axis;
-    float degrees_per_second;
-  };
-
-  typedef std::map<int, KeycodeMappingInfo> KeycodeMap;
-
-  // Structure to keep track of the current orientation state.
-  struct Orientation {
-    Orientation() : roll(0.0f), pitch(0.0f), yaw(0.0f) {}
-
-    float roll;
-    float pitch;
-    float yaw;
-  };
-
-  void AccumulateOrientation();
-
-  // The Camera3D's WebAPI will be accessed from the WebModule thread, but
-  // the internal camera orientation will be accessed on the renderer thread
-  // via QueryViewPerspectiveMatrix().  So, we do need a mutex to guard against
-  // these potentially parallel accesses.
-  base::Lock mutex_;
-
-  // A map of keys bound to camera movements.
-  KeycodeMap keycode_map_;
-
-  // The input poller from which we can check the state of a given key.
-  scoped_refptr<input::InputPoller> input_poller_;
-
-  // The current accumulated camera orientation state.
-  Orientation orientation_;
-
-  // The time that the last update to the camera's state has occurred.
-  base::optional<base::TimeTicks> last_update_;
-
-  DISALLOW_COPY_AND_ASSIGN(Camera3DImpl);
-};
-
-}  // namespace dom
-}  // namespace cobalt
-
-#endif  // COBALT_DOM_CAMERA_3D_IMPL_H_
diff --git a/src/cobalt/dom/cdata_section.h b/src/cobalt/dom/cdata_section.h
index bdf1605..137e46d 100644
--- a/src/cobalt/dom/cdata_section.h
+++ b/src/cobalt/dom/cdata_section.h
@@ -42,7 +42,7 @@
 
   // Custom, not in any spec: Node.
   //
-  scoped_refptr<CDATASection> AsCDATASection() OVERRIDE { return this; }
+  CDATASection* AsCDATASection() OVERRIDE { return this; }
 
   void Accept(NodeVisitor* visitor) OVERRIDE;
   void Accept(ConstNodeVisitor* visitor) const OVERRIDE;
diff --git a/src/cobalt/dom/comment.h b/src/cobalt/dom/comment.h
index 26d2310..014f1f9 100644
--- a/src/cobalt/dom/comment.h
+++ b/src/cobalt/dom/comment.h
@@ -41,7 +41,7 @@
 
   // Custom, not in any spec: Node.
   //
-  scoped_refptr<Comment> AsComment() OVERRIDE { return this; }
+  Comment* AsComment() OVERRIDE { return this; }
 
   void Accept(NodeVisitor* visitor) OVERRIDE;
   void Accept(ConstNodeVisitor* visitor) const OVERRIDE;
diff --git a/src/cobalt/dom/device_orientation_event.cc b/src/cobalt/dom/device_orientation_event.cc
new file mode 100644
index 0000000..284c5f9
--- /dev/null
+++ b/src/cobalt/dom/device_orientation_event.cc
@@ -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.
+ */
+
+#include "cobalt/dom/device_orientation_event.h"
+
+#include "cobalt/base/token.h"
+#include "cobalt/base/tokens.h"
+#include "cobalt/dom/storage.h"
+
+namespace cobalt {
+namespace dom {
+
+DeviceOrientationEvent::DeviceOrientationEvent()
+    : Event(base::Tokens::deviceorientation()) {}
+
+DeviceOrientationEvent::DeviceOrientationEvent(const std::string& type)
+    : Event(type) {}
+
+DeviceOrientationEvent::DeviceOrientationEvent(
+    const std::string& type, const DeviceOrientationEventInit& init)
+    : Event(type),
+      alpha_(init.alpha()),
+      beta_(init.beta()),
+      gamma_(init.gamma()),
+      absolute_(init.absolute()) {}
+
+}  // namespace dom
+}  // namespace cobalt
diff --git a/src/cobalt/dom/device_orientation_event.h b/src/cobalt/dom/device_orientation_event.h
new file mode 100644
index 0000000..5c9c8a7
--- /dev/null
+++ b/src/cobalt/dom/device_orientation_event.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_DOM_DEVICE_ORIENTATION_EVENT_H_
+#define COBALT_DOM_DEVICE_ORIENTATION_EVENT_H_
+
+#include <string>
+
+#include "base/optional.h"
+#include "cobalt/dom/device_orientation_event_init.h"
+#include "cobalt/dom/event.h"
+#include "cobalt/script/wrappable.h"
+
+namespace cobalt {
+namespace dom {
+
+// Represents a device orientation event, where the new orientation of the
+// device is represented as intrinsic rotations on the Z (by |alpha|
+// degrees), X' (by |beta| degrees), Y'' (by |gamma| degrees) axes; intrinsic
+// meaning each of the rotations is done on the axis of the rotated system
+// resulting of the previous rotation.
+//   https://www.w3.org/TR/2016/CR-orientation-event-20160818/
+class DeviceOrientationEvent : public Event {
+ public:
+  DeviceOrientationEvent();
+  explicit DeviceOrientationEvent(const std::string& type);
+
+  DeviceOrientationEvent(const std::string& type,
+                         const DeviceOrientationEventInit& init);
+
+  base::optional<double> alpha() const { return alpha_; }
+  base::optional<double> beta() const { return beta_; }
+  base::optional<double> gamma() const { return gamma_; }
+  bool absolute() const { return absolute_; }
+
+  DEFINE_WRAPPABLE_TYPE(DeviceOrientationEvent);
+
+ private:
+  base::optional<double> alpha_;
+  base::optional<double> beta_;
+  base::optional<double> gamma_;
+  bool absolute_;
+
+  DISALLOW_COPY_AND_ASSIGN(DeviceOrientationEvent);
+};
+
+}  // namespace dom
+}  // namespace cobalt
+
+#endif  // COBALT_DOM_DEVICE_ORIENTATION_EVENT_H_
diff --git a/src/cobalt/dom/device_orientation_event.idl b/src/cobalt/dom/device_orientation_event.idl
new file mode 100644
index 0000000..0ecd3e9
--- /dev/null
+++ b/src/cobalt/dom/device_orientation_event.idl
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+// https://www.w3.org/TR/2016/CR-orientation-event-20160818/
+
+[Constructor(DOMString type, optional DeviceOrientationEventInit eventInitDict)]
+interface DeviceOrientationEvent : Event {
+  readonly attribute double? alpha;
+  readonly attribute double? beta;
+  readonly attribute double? gamma;
+  readonly attribute boolean absolute;
+};
diff --git a/src/cobalt/dom/device_orientation_event_init.idl b/src/cobalt/dom/device_orientation_event_init.idl
new file mode 100644
index 0000000..42fda864
--- /dev/null
+++ b/src/cobalt/dom/device_orientation_event_init.idl
@@ -0,0 +1,21 @@
+// 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.
+
+// https://www.w3.org/TR/2016/CR-orientation-event-20160818/
+dictionary DeviceOrientationEventInit : EventInit {
+  double? alpha = null;
+  double? beta = null;
+  double? gamma = null;
+  boolean absolute = false;
+};
\ No newline at end of file
diff --git a/src/cobalt/dom/document.cc b/src/cobalt/dom/document.cc
index 85e8006..295eb3b 100644
--- a/src/cobalt/dom/document.cc
+++ b/src/cobalt/dom/document.cc
@@ -22,6 +22,7 @@
 #include "base/debug/trace_event.h"
 #include "base/message_loop.h"
 #include "base/string_util.h"
+#include "cobalt/base/token.h"
 #include "cobalt/base/tokens.h"
 #include "cobalt/cssom/css_media_rule.h"
 #include "cobalt/cssom/css_rule.h"
@@ -45,12 +46,17 @@
 #include "cobalt/dom/html_html_element.h"
 #include "cobalt/dom/html_script_element.h"
 #include "cobalt/dom/initial_computed_style.h"
+#include "cobalt/dom/keyboard_event.h"
 #include "cobalt/dom/keyframes_map_updater.h"
 #include "cobalt/dom/location.h"
+#include "cobalt/dom/message_event.h"
+#include "cobalt/dom/mouse_event.h"
 #include "cobalt/dom/named_node_map.h"
 #include "cobalt/dom/node_descendants_iterator.h"
+#include "cobalt/dom/pointer_event.h"
 #include "cobalt/dom/text.h"
 #include "cobalt/dom/ui_event.h"
+#include "cobalt/dom/wheel_event.h"
 #include "cobalt/dom/window.h"
 #include "cobalt/script/global_environment.h"
 #include "nb/memory_scope.h"
@@ -86,6 +92,7 @@
       dom_max_element_depth_(options.dom_max_element_depth) {
   DCHECK(html_element_context_);
   DCHECK(options.url.is_empty() || options.url.is_valid());
+  html_element_context_->page_visibility_state()->AddObserver(this);
 
   if (options.viewport_size) {
     SetViewport(*options.viewport_size);
@@ -215,11 +222,20 @@
     const std::string& interface_name,
     script::ExceptionState* exception_state) {
   TRACK_MEMORY_SCOPE("DOM");
-  // https://www.w3.org/TR/2015/WD-dom-20150428/#dom-document-createevent
+  // https://www.w3.org/TR/dom/#dom-document-createevent
   // The match of interface name is case-insensitive.
   if (base::strcasecmp(interface_name.c_str(), "event") == 0 ||
-      base::strcasecmp(interface_name.c_str(), "events") == 0) {
+      base::strcasecmp(interface_name.c_str(), "events") == 0 ||
+      base::strcasecmp(interface_name.c_str(), "htmlevents") == 0) {
     return new Event(Event::Uninitialized);
+  } else if (base::strcasecmp(interface_name.c_str(), "keyboardevent") == 0 ||
+             base::strcasecmp(interface_name.c_str(), "keyevents") == 0) {
+    return new KeyboardEvent(Event::Uninitialized);
+  } else if (base::strcasecmp(interface_name.c_str(), "messageevent") == 0) {
+    return new MessageEvent(Event::Uninitialized);
+  } else if (base::strcasecmp(interface_name.c_str(), "mouseevent") == 0 ||
+             base::strcasecmp(interface_name.c_str(), "mouseevents") == 0) {
+    return new MouseEvent(Event::Uninitialized);
   } else if (base::strcasecmp(interface_name.c_str(), "uievent") == 0 ||
              base::strcasecmp(interface_name.c_str(), "uievents") == 0) {
     return new UIEvent(Event::Uninitialized);
@@ -343,6 +359,11 @@
   }
 }
 
+// https://www.w3.org/TR/2016/REC-html51-20161101/matching-html-elements-using-selectors.html#selectordef-hover
+scoped_refptr<HTMLElement> Document::indicated_element() const {
+  return indicated_element_.get();
+}
+
 void Document::set_cookie(const std::string& cookie) {
 #if defined(COBALT_BUILD_TYPE_GOLD)
   UNREFERENCED_PARAMETER(cookie);
@@ -398,6 +419,23 @@
   }
 }
 
+void Document::SetIndicatedElement(HTMLElement* indicated_element) {
+  if (indicated_element != indicated_element_) {
+    is_selector_tree_dirty_ = true;
+    if (indicated_element_) {
+      indicated_element_->OnCSSMutation();
+    }
+    if (indicated_element) {
+      indicated_element_ = base::AsWeakPtr(indicated_element);
+      indicated_element_->OnCSSMutation();
+    } else {
+      indicated_element_.reset();
+    }
+  }
+}
+
+const scoped_refptr<Window> Document::window() { return window_; }
+
 void Document::IncreaseLoadingCounter() { ++loading_counter_; }
 
 void Document::DecreaseLoadingCounter() { --loading_counter_; }
@@ -707,6 +745,7 @@
 }
 
 Document::~Document() {
+  html_element_context_->page_visibility_state()->RemoveObserver(this);
   // Ensure that all outstanding weak ptrs become invalid.
   // Some objects that will be released while this destructor runs may
   // have weak ptrs to |this|.
@@ -770,6 +809,18 @@
       ->DisableJit();
 }
 
+void Document::OnWindowFocusChanged(bool has_focus) {
+  UNREFERENCED_PARAMETER(has_focus);
+  // Ignored by this class.
+}
+
+void Document::OnVisibilityStateChanged(
+    page_visibility::VisibilityState visibility_state) {
+  UNREFERENCED_PARAMETER(visibility_state);
+  DispatchEvent(new Event(base::Tokens::visibilitychange(), Event::kBubbles,
+                          Event::kNotCancelable));
+}
+
 void Document::TraceMembers(script::Tracer* tracer) {
   Node::TraceMembers(tracer);
 
@@ -789,6 +840,43 @@
   tracer->Trace(initial_computed_style_declaration_);
 }
 
+void Document::QueuePointerEvent(const scoped_refptr<Event>& event) {
+  // Only accept this for event types that are MouseEvents or known derivatives.
+  SB_DCHECK(event->GetWrappableType() == base::GetTypeId<PointerEvent>() ||
+            event->GetWrappableType() == base::GetTypeId<MouseEvent>() ||
+            event->GetWrappableType() == base::GetTypeId<WheelEvent>());
+
+  // Queue the event to be handled on the next layout.
+  pointer_events_.push(event);
+}
+
+scoped_refptr<Event> Document::GetNextQueuedPointerEvent() {
+  scoped_refptr<Event> event;
+  if (pointer_events_.empty()) {
+    return event;
+  }
+
+  // Ignore pointer move events when they are succeeded by additional pointer
+  // move events.
+  bool next_event_is_move_event =
+      pointer_events_.front()->type() == base::Tokens::pointermove() ||
+      pointer_events_.front()->type() == base::Tokens::mousemove();
+  bool current_event_is_move_event;
+  do {
+    current_event_is_move_event = next_event_is_move_event;
+    event = pointer_events_.front();
+    pointer_events_.pop();
+    if (!current_event_is_move_event) {
+      break;
+    }
+    next_event_is_move_event =
+        !pointer_events_.empty() &&
+        (pointer_events_.front()->type() == base::Tokens::pointermove() ||
+         pointer_events_.front()->type() == base::Tokens::mousemove());
+  } while (next_event_is_move_event);
+  return event;
+}
+
 void Document::DispatchOnLoadEvent() {
   TRACE_EVENT0("cobalt::dom", "Document::DispatchOnLoadEvent()");
 
diff --git a/src/cobalt/dom/document.h b/src/cobalt/dom/document.h
index 04ef5c5..da896bf 100644
--- a/src/cobalt/dom/document.h
+++ b/src/cobalt/dom/document.h
@@ -17,6 +17,7 @@
 
 #include <deque>
 #include <map>
+#include <queue>
 #include <string>
 
 #include "base/callback.h"
@@ -40,6 +41,8 @@
 #include "cobalt/math/size.h"
 #include "cobalt/network_bridge/cookie_jar.h"
 #include "cobalt/network_bridge/net_poster.h"
+#include "cobalt/page_visibility/page_visibility_state.h"
+#include "cobalt/page_visibility/visibility_state.h"
 #include "cobalt/script/exception_state.h"
 #include "googleurl/src/gurl.h"
 
@@ -81,7 +84,9 @@
 // (the DOM tree, including elements such as <head> and <body>) and provides
 // functionality which is global to the document.
 //   https://www.w3.org/TR/dom/#document
-class Document : public Node, public cssom::MutationObserver {
+class Document : public Node,
+                 public cssom::MutationObserver,
+                 public page_visibility::PageVisibilityState::Observer {
  public:
   struct Options {
     Options()
@@ -186,6 +191,7 @@
   scoped_refptr<HTMLHeadElement> head() const;
 
   scoped_refptr<Element> active_element() const;
+  scoped_refptr<HTMLElement> indicated_element() const;
 
   const EventListenerScriptValue* onreadystatechange() const {
     return GetAttributeEventListener(base::Tokens::readystatechange());
@@ -212,7 +218,7 @@
 
   // Custom, not in any spec: Node.
   //
-  scoped_refptr<Document> AsDocument() OVERRIDE { return this; }
+  Document* AsDocument() OVERRIDE { return this; }
 
   void Accept(NodeVisitor* visitor) OVERRIDE;
   void Accept(ConstNodeVisitor* visitor) const OVERRIDE;
@@ -252,10 +258,14 @@
   bool HasBrowsingContext() { return !!window_; }
 
   void set_window(Window* window) { window_ = window; }
+  const scoped_refptr<Window> window();
 
   // Sets the active element of the document.
   void SetActiveElement(Element* active_element);
 
+  // Sets the indicated element of the document.
+  void SetIndicatedElement(HTMLElement* indicated_element);
+
   // Count all ongoing loadings, including document itself and its dependent
   // resources, and dispatch OnLoad() if necessary.
   void IncreaseLoadingCounter();
@@ -347,13 +357,46 @@
   // Disable just-in-time compilation of JavaScript code.
   void DisableJit();
 
+  // Page Visibility fields.
+  bool hidden() const {
+    return visibility_state() == page_visibility::kVisibilityStateHidden;
+  }
+  page_visibility::VisibilityState visibility_state() const {
+    return page_visibility_state()->GetVisibilityState();
+  }
+  const EventListenerScriptValue* onvisibilitychange() const {
+    return GetAttributeEventListener(base::Tokens::visibilitychange());
+  }
+  void set_onvisibilitychange(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::visibilitychange(), event_listener);
+  }
+
+  // page_visibility::PageVisibilityState::Observer implementation.
+  void OnWindowFocusChanged(bool has_focus) OVERRIDE;
+  void OnVisibilityStateChanged(
+      page_visibility::VisibilityState visibility_state) OVERRIDE;
+
   void TraceMembers(script::Tracer* tracer) OVERRIDE;
 
+  // Queue up pointer related events.
+  void QueuePointerEvent(const scoped_refptr<Event>& event);
+
+  // Get the next queued pointer event.
+  scoped_refptr<Event> GetNextQueuedPointerEvent();
+
   DEFINE_WRAPPABLE_TYPE(Document);
 
  protected:
   ~Document() OVERRIDE;
 
+  page_visibility::PageVisibilityState* page_visibility_state() {
+    return html_element_context_->page_visibility_state();
+  }
+
+  const page_visibility::PageVisibilityState* page_visibility_state() const {
+    return html_element_context_->page_visibility_state();
+  }
+
  private:
   void DispatchOnLoadEvent();
 
@@ -405,6 +448,8 @@
 
   // Weak reference to the active element.
   base::WeakPtr<Element> active_element_;
+  // Weak reference to the indicated element.
+  base::WeakPtr<HTMLElement> indicated_element_;
   // List of document observers.
   ObserverList<DocumentObserver> observers_;
   // Selector Tree.
@@ -426,6 +471,8 @@
 
   // The max depth of elements that are guaranteed to be rendered.
   int dom_max_element_depth_;
+
+  std::queue<scoped_refptr<Event> > pointer_events_;
 };
 
 }  // namespace dom
diff --git a/src/cobalt/dom/document.idl b/src/cobalt/dom/document.idl
index 45d27e1..a2e81ee 100644
--- a/src/cobalt/dom/document.idl
+++ b/src/cobalt/dom/document.idl
@@ -20,6 +20,7 @@
   readonly attribute DOMString documentURI;
   readonly attribute Element? documentElement;
 
+  // user interaction
   // Non-standard return type, should be WindowProxy.
   // https://www.w3.org/TR/html5/single-page.html#dom-document-defaultview
   readonly attribute Window? defaultView;
diff --git a/src/cobalt/dom/document_test.cc b/src/cobalt/dom/document_test.cc
index 8544240..b9e82b2 100644
--- a/src/cobalt/dom/document_test.cc
+++ b/src/cobalt/dom/document_test.cc
@@ -29,11 +29,15 @@
 #include "cobalt/dom/html_head_element.h"
 #include "cobalt/dom/html_html_element.h"
 #include "cobalt/dom/html_style_element.h"
+#include "cobalt/dom/keyboard_event.h"
 #include "cobalt/dom/location.h"
+#include "cobalt/dom/message_event.h"
+#include "cobalt/dom/mouse_event.h"
 #include "cobalt/dom/node_list.h"
 #include "cobalt/dom/testing/gtest_workarounds.h"
 #include "cobalt/dom/testing/html_collection_testing.h"
 #include "cobalt/dom/text.h"
+#include "cobalt/dom/ui_event.h"
 #include "cobalt/script/testing/mock_exception_state.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -65,7 +69,8 @@
       dom_stat_tracker_(new DomStatTracker("DocumentTest")),
       html_element_context_(NULL, css_parser_.get(), NULL, NULL, NULL, NULL,
                             NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                            dom_stat_tracker_.get(), "") {
+                            dom_stat_tracker_.get(), "",
+                            base::kApplicationStateStarted) {
   EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
 }
 
@@ -144,19 +149,105 @@
   EXPECT_EQ(document, comment->node_document());
 }
 
-TEST_F(DocumentTest, CreateEvent) {
+TEST_F(DocumentTest, CreateEventEvent) {
   StrictMock<MockExceptionState> exception_state;
   scoped_refptr<script::ScriptException> exception;
   scoped_refptr<Document> document = new Document(&html_element_context_);
+
   // Create an Event, the name is case insensitive.
   scoped_refptr<Event> event = document->CreateEvent("EvEnT", &exception_state);
-
   EXPECT_TRUE(event);
   EXPECT_FALSE(event->initialized_flag());
 
+  event = document->CreateEvent("EvEnTs", &exception_state);
+  EXPECT_TRUE(event);
+  EXPECT_FALSE(event->initialized_flag());
+  EXPECT_FALSE(dynamic_cast<UIEvent*>(event.get()));
+  EXPECT_FALSE(dynamic_cast<KeyboardEvent*>(event.get()));
+  EXPECT_FALSE(dynamic_cast<MessageEvent*>(event.get()));
+  EXPECT_FALSE(dynamic_cast<MouseEvent*>(event.get()));
+
+  event = document->CreateEvent("HtMlEvEnTs", &exception_state);
+  EXPECT_TRUE(event);
+  EXPECT_FALSE(event->initialized_flag());
+}
+
+TEST_F(DocumentTest, CreateEventUIEvent) {
+  StrictMock<MockExceptionState> exception_state;
+  scoped_refptr<script::ScriptException> exception;
+  scoped_refptr<Document> document = new Document(&html_element_context_);
+
+  // Create an Event, the name is case insensitive.
+  scoped_refptr<Event> event =
+      document->CreateEvent("UiEvEnT", &exception_state);
+  EXPECT_TRUE(event);
+  EXPECT_FALSE(event->initialized_flag());
+  EXPECT_TRUE(base::polymorphic_downcast<UIEvent*>(event.get()));
+
+  event = document->CreateEvent("UiEvEnTs", &exception_state);
+  EXPECT_TRUE(event);
+  EXPECT_FALSE(event->initialized_flag());
+  EXPECT_TRUE(base::polymorphic_downcast<UIEvent*>(event.get()));
+}
+
+TEST_F(DocumentTest, CreateEventKeyboardEvent) {
+  StrictMock<MockExceptionState> exception_state;
+  scoped_refptr<script::ScriptException> exception;
+  scoped_refptr<Document> document = new Document(&html_element_context_);
+
+  // Create an Event, the name is case insensitive.
+  scoped_refptr<Event> event =
+      document->CreateEvent("KeYbOaRdEvEnT", &exception_state);
+  EXPECT_TRUE(event);
+  EXPECT_FALSE(event->initialized_flag());
+  EXPECT_TRUE(base::polymorphic_downcast<KeyboardEvent*>(event.get()));
+
+  event = document->CreateEvent("KeYeVeNtS", &exception_state);
+  EXPECT_TRUE(event);
+  EXPECT_FALSE(event->initialized_flag());
+  EXPECT_TRUE(base::polymorphic_downcast<KeyboardEvent*>(event.get()));
+}
+
+TEST_F(DocumentTest, CreateEventMessageEvent) {
+  StrictMock<MockExceptionState> exception_state;
+  scoped_refptr<script::ScriptException> exception;
+  scoped_refptr<Document> document = new Document(&html_element_context_);
+
+  // Create an Event, the name is case insensitive.
+  scoped_refptr<Event> event =
+      document->CreateEvent("MeSsAgEeVeNt", &exception_state);
+  EXPECT_TRUE(event);
+  EXPECT_FALSE(event->initialized_flag());
+  EXPECT_TRUE(base::polymorphic_downcast<MessageEvent*>(event.get()));
+}
+
+TEST_F(DocumentTest, CreateEventMouseEvent) {
+  StrictMock<MockExceptionState> exception_state;
+  scoped_refptr<script::ScriptException> exception;
+  scoped_refptr<Document> document = new Document(&html_element_context_);
+
+  // Create an Event, the name is case insensitive.
+  scoped_refptr<Event> event =
+      document->CreateEvent("MoUsEeVeNt", &exception_state);
+  EXPECT_TRUE(event);
+  EXPECT_FALSE(event->initialized_flag());
+  EXPECT_TRUE(base::polymorphic_downcast<MouseEvent*>(event.get()));
+
+  event = document->CreateEvent("MoUsEeVeNtS", &exception_state);
+  EXPECT_TRUE(event);
+  EXPECT_FALSE(event->initialized_flag());
+  EXPECT_TRUE(base::polymorphic_downcast<MouseEvent*>(event.get()));
+}
+
+TEST_F(DocumentTest, CreateEventEventNotSupported) {
+  StrictMock<MockExceptionState> exception_state;
+  scoped_refptr<script::ScriptException> exception;
+  scoped_refptr<Document> document = new Document(&html_element_context_);
+
   EXPECT_CALL(exception_state, SetException(_))
       .WillOnce(SaveArg<0>(&exception));
-  event = document->CreateEvent("Event Not Supported", &exception_state);
+  scoped_refptr<Event> event =
+      document->CreateEvent("Event Not Supported", &exception_state);
 
   EXPECT_FALSE(event);
   ASSERT_TRUE(exception);
diff --git a/src/cobalt/dom/document_type.h b/src/cobalt/dom/document_type.h
index a9eebd3..f5d857f 100644
--- a/src/cobalt/dom/document_type.h
+++ b/src/cobalt/dom/document_type.h
@@ -47,7 +47,7 @@
 
   // Custom, not in any spec: Node.
   //
-  scoped_refptr<DocumentType> AsDocumentType() OVERRIDE { return this; }
+  DocumentType* AsDocumentType() OVERRIDE { return this; }
 
   void Accept(NodeVisitor* visitor) OVERRIDE;
   void Accept(ConstNodeVisitor* visitor) const OVERRIDE;
diff --git a/src/cobalt/dom/dom.gyp b/src/cobalt/dom/dom.gyp
index a1d6fbb..e516263 100644
--- a/src/cobalt/dom/dom.gyp
+++ b/src/cobalt/dom/dom.gyp
@@ -14,7 +14,7 @@
 
 {
   'variables': {
-    'cobalt_code': 1,
+    'sb_pedantic_warnings': 1,
   },
   'targets': [
     {
@@ -40,8 +40,6 @@
         'buffer_source.h',
         'camera_3d.cc',
         'camera_3d.h',
-        'camera_3d_impl.cc',
-        'camera_3d_impl.h',
         'cdata_section.cc',
         'cdata_section.h',
         'character_data.cc',
@@ -65,6 +63,9 @@
         'css_transitions_adapter.h',
         'data_view.cc',
         'data_view.h',
+        'device_orientation_event.cc',
+        'device_orientation_event.h',
+        'device_orientation_event_init.h',
         'document.cc',
         'document.h',
         'document_timeline.cc',
@@ -178,6 +179,8 @@
         'message_event.h',
         'mime_type_array.cc',
         'mime_type_array.h',
+        'mouse_event.cc',
+        'mouse_event.h',
         'mutation_observer.cc',
         'mutation_observer.h',
         'mutation_observer_init.h',
@@ -206,6 +209,8 @@
         'performance_timing.h',
         'plugin_array.cc',
         'plugin_array.h',
+        'pointer_event.cc',
+        'pointer_event.h',
         'progress_event.cc',
         'progress_event.h',
         'pseudo_element.cc',
@@ -250,6 +255,8 @@
         'url_utils.h',
         'video_track.h',
         'video_track_list.h',
+        'wheel_event.cc',
+        'wheel_event.h',
         'window.cc',
         'window.h',
         'window_timers.cc',
@@ -268,6 +275,7 @@
         '<(DEPTH)/cobalt/media_session/media_session.gyp:media_session',
         # Interface layer to avoid directly depending on network.
         '<(DEPTH)/cobalt/network_bridge/network_bridge.gyp:network_bridge',
+        '<(DEPTH)/cobalt/page_visibility/page_visibility.gyp:page_visibility',
         '<(DEPTH)/cobalt/script/script.gyp:script',
         '<(DEPTH)/cobalt/storage/storage.gyp:storage',
         '<(DEPTH)/cobalt/system_window/system_window.gyp:system_window',
@@ -333,10 +341,10 @@
       'hard_dependency': 1,
       'export_dependent_settings': [
         # Additionally, ensure that the include directories for generated
-        # headers are put on the include directories for targets that depend
-        # on this one.
+        # headers are put on the include directories for targets that depend on
+        # this one.
         '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
-      ]
+      ],
     },
 
     {
diff --git a/src/cobalt/dom/dom_exception.gyp b/src/cobalt/dom/dom_exception.gyp
index ba6dcf6..cb2d09d 100644
--- a/src/cobalt/dom/dom_exception.gyp
+++ b/src/cobalt/dom/dom_exception.gyp
@@ -14,7 +14,7 @@
 
 {
   'variables': {
-    'cobalt_code': 1,
+    'sb_pedantic_warnings': 1,
   },
   'targets': [
     {
diff --git a/src/cobalt/dom/dom_parser_test.cc b/src/cobalt/dom/dom_parser_test.cc
index 21d7302..273fdd6 100644
--- a/src/cobalt/dom/dom_parser_test.cc
+++ b/src/cobalt/dom/dom_parser_test.cc
@@ -50,7 +50,8 @@
           NULL /* animated_image_tracker */, NULL /* image_cache */,
           NULL /* reduced_image_cache_capacity_manager */,
           NULL /* remote_typeface_cache */, NULL /* mesh_cache */,
-          NULL /* dom_stat_tracker */, "" /* language */),
+          NULL /* dom_stat_tracker */, "" /* language */,
+          base::kApplicationStateStarted),
       dom_parser_(new DOMParser(&html_element_context_)) {}
 
 TEST_F(DOMParserTest, ParsesXML) {
diff --git a/src/cobalt/dom/dom_stat_tracker.cc b/src/cobalt/dom/dom_stat_tracker.cc
index cadf09e..e2e1219 100644
--- a/src/cobalt/dom/dom_stat_tracker.cc
+++ b/src/cobalt/dom/dom_stat_tracker.cc
@@ -20,17 +20,28 @@
 namespace dom {
 
 DomStatTracker::DomStatTracker(const std::string& name)
-    : total_html_elements_(
-          StringPrintf("Count.%s.DOM.HtmlElement", name.c_str()), 0,
+    : html_elements_count_(
+          StringPrintf("Count.%s.DOM.HtmlElement.Total", name.c_str()), 0,
           "Total number of HTML elements."),
+      document_html_elements_count_(
+          StringPrintf("Count.%s.DOM.HtmlElement.Document", name.c_str()), 0,
+          "Number of HTML elements in the document."),
       is_event_active_(false),
       event_video_start_delay_stop_watch_(kStopWatchTypeEventVideoStartDelay,
                                           base::StopWatch::kAutoStartOff, this),
       event_video_start_delay_(
           StringPrintf("Event.Duration.%s.DOM.VideoStartDelay", name.c_str()),
           base::TimeDelta(), "Total delay between event and video starting."),
+      script_element_execute_count_(
+          StringPrintf("Count.%s.DOM.HtmlScriptElement.Execute", name.c_str()),
+          0, "Count of HTML script element execute calls."),
+      script_element_execute_time_(
+          StringPrintf("Time.%s.DOM.HtmlScriptElement.Execute", name.c_str()),
+          0, "Time of the last HTML script element execute."),
       html_elements_created_count_(0),
       html_elements_destroyed_count_(0),
+      html_elements_inserted_into_document_count_(0),
+      html_elements_removed_from_document_count_(0),
       update_matching_rules_count_(0),
       update_computed_style_count_(0),
       generate_html_element_computed_style_count_(0),
@@ -41,8 +52,10 @@
 DomStatTracker::~DomStatTracker() {
   FlushPeriodicTracking();
 
-  // Verify that all of the elements were destroyed.
-  DCHECK_EQ(total_html_elements_, 0);
+  // Verify that all of the elements were removed from the document and
+  // destroyed.
+  DCHECK_EQ(html_elements_count_, 0);
+  DCHECK_EQ(document_html_elements_count_, 0);
 
   event_video_start_delay_stop_watch_.Stop();
 }
@@ -84,12 +97,25 @@
   }
 }
 
+void DomStatTracker::OnHtmlScriptElementExecuted() {
+  ++script_element_execute_count_;
+  script_element_execute_time_ = base::TimeTicks::Now().ToInternalValue();
+}
+
 void DomStatTracker::OnHtmlElementCreated() { ++html_elements_created_count_; }
 
 void DomStatTracker::OnHtmlElementDestroyed() {
   ++html_elements_destroyed_count_;
 }
 
+void DomStatTracker::OnHtmlElementInsertedIntoDocument() {
+  ++html_elements_inserted_into_document_count_;
+}
+
+void DomStatTracker::OnHtmlElementRemovedFromDocument() {
+  ++html_elements_removed_from_document_count_;
+}
+
 void DomStatTracker::OnUpdateMatchingRules() { ++update_matching_rules_count_; }
 
 void DomStatTracker::OnUpdateComputedStyle() { ++update_computed_style_count_; }
@@ -117,12 +143,16 @@
 
 void DomStatTracker::FlushPeriodicTracking() {
   // Update the CVals before clearing the periodic values.
-  total_html_elements_ +=
+  html_elements_count_ +=
       html_elements_created_count_ - html_elements_destroyed_count_;
+  document_html_elements_count_ += html_elements_inserted_into_document_count_ -
+                                   html_elements_removed_from_document_count_;
 
   // Now clear the values.
   html_elements_created_count_ = 0;
   html_elements_destroyed_count_ = 0;
+  html_elements_inserted_into_document_count_ = 0;
+  html_elements_removed_from_document_count_ = 0;
   update_matching_rules_count_ = 0;
   update_computed_style_count_ = 0;
   generate_html_element_computed_style_count_ = 0;
diff --git a/src/cobalt/dom/dom_stat_tracker.h b/src/cobalt/dom/dom_stat_tracker.h
index 6e34ab2..35cedb7 100644
--- a/src/cobalt/dom/dom_stat_tracker.h
+++ b/src/cobalt/dom/dom_stat_tracker.h
@@ -42,16 +42,22 @@
   void OnEndEvent();
 
   void OnHtmlVideoElementPlaying();
+  void OnHtmlScriptElementExecuted();
 
   // Periodic count-related
   void OnHtmlElementCreated();
   void OnHtmlElementDestroyed();
+  void OnHtmlElementInsertedIntoDocument();
+  void OnHtmlElementRemovedFromDocument();
   void OnUpdateMatchingRules();
   void OnUpdateComputedStyle();
   void OnGenerateHtmlElementComputedStyle();
   void OnGeneratePseudoElementComputedStyle();
 
-  int total_html_elements() const { return total_html_elements_; }
+  int html_elements_count() const { return html_elements_count_; }
+  int document_html_elements_count() const {
+    return document_html_elements_count_;
+  }
 
   int html_elements_created_count() const {
     return html_elements_created_count_;
@@ -59,6 +65,12 @@
   int html_elements_destroyed_count() const {
     return html_elements_destroyed_count_;
   }
+  int html_elements_added_to_document_count() const {
+    return html_elements_inserted_into_document_count_;
+  }
+  int html_elements_removed_from_document_count() const {
+    return html_elements_removed_from_document_count_;
+  }
   int update_matching_rules_count() const {
     return update_matching_rules_count_;
   }
@@ -84,7 +96,8 @@
   void FlushPeriodicTracking();
 
   // Count cvals that are updated when the periodic tracking is flushed.
-  base::CVal<int, base::CValPublic> total_html_elements_;
+  base::CVal<int, base::CValPublic> html_elements_count_;
+  base::CVal<int, base::CValPublic> document_html_elements_count_;
 
   // Event-related
   bool is_event_active_;
@@ -93,10 +106,16 @@
   base::StopWatch event_video_start_delay_stop_watch_;
   base::CVal<base::TimeDelta> event_video_start_delay_;
 
+  // Count of HtmlScriptElement::Execute() calls and time of last call.
+  base::CVal<int> script_element_execute_count_;
+  base::CVal<int64> script_element_execute_time_;
+
   // Periodic counts. The counts are cleared after the CVals are updated in
   // |FlushPeriodicTracking|.
   int html_elements_created_count_;
   int html_elements_destroyed_count_;
+  int html_elements_inserted_into_document_count_;
+  int html_elements_removed_from_document_count_;
   int update_matching_rules_count_;
   int update_computed_style_count_;
   int generate_html_element_computed_style_count_;
diff --git a/src/cobalt/dom/dom_test.gyp b/src/cobalt/dom/dom_test.gyp
index 9954088..2c41b83 100644
--- a/src/cobalt/dom/dom_test.gyp
+++ b/src/cobalt/dom/dom_test.gyp
@@ -14,7 +14,7 @@
 
 {
   'variables': {
-    'cobalt_code': 1,
+    'sb_pedantic_warnings': 1,
   },
   'targets': [
     {
diff --git a/src/cobalt/dom/element.cc b/src/cobalt/dom/element.cc
index 521069e..d961ebc 100644
--- a/src/cobalt/dom/element.cc
+++ b/src/cobalt/dom/element.cc
@@ -512,6 +512,7 @@
 void Element::set_outer_html(const std::string& outer_html,
                              script::ExceptionState* exception_state) {
   TRACK_MEMORY_SCOPE("DOM");
+
   // 1. Let parent be the context object's parent.
   scoped_refptr<Node> parent = parent_node();
 
@@ -537,6 +538,9 @@
   // parent.
   // Remove this node from its parent.
   scoped_refptr<Node> reference = next_sibling();
+
+  // Make sure that this does not get cleaned up while it is being removed.
+  scoped_refptr<Node> keep_this_alive = this;
   parent->RemoveChild(this);
 
   // Use the DOM parser to parse the HTML input and generate children nodes.
diff --git a/src/cobalt/dom/element.h b/src/cobalt/dom/element.h
index 01901c9..337d467 100644
--- a/src/cobalt/dom/element.h
+++ b/src/cobalt/dom/element.h
@@ -117,7 +117,7 @@
 
   // Custom, not in any spec: Node.
   //
-  scoped_refptr<Element> AsElement() OVERRIDE { return this; }
+  Element* AsElement() OVERRIDE { return this; }
 
   void Accept(NodeVisitor* visitor) OVERRIDE;
   void Accept(ConstNodeVisitor* visitor) const OVERRIDE;
diff --git a/src/cobalt/dom/element_test.cc b/src/cobalt/dom/element_test.cc
index 5d1f52f..6f3941f 100644
--- a/src/cobalt/dom/element_test.cc
+++ b/src/cobalt/dom/element_test.cc
@@ -62,7 +62,8 @@
       dom_stat_tracker_(new DomStatTracker("ElementTest")),
       html_element_context_(NULL, css_parser_.get(), dom_parser_.get(), NULL,
                             NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                            NULL, NULL, dom_stat_tracker_.get(), "") {
+                            NULL, NULL, dom_stat_tracker_.get(), "",
+                            base::kApplicationStateStarted) {
   EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
   document_ = new Document(&html_element_context_);
   xml_document_ = new XMLDocument(&html_element_context_);
diff --git a/src/cobalt/dom/event.cc b/src/cobalt/dom/event.cc
index a3623e9..64c1d77 100644
--- a/src/cobalt/dom/event.cc
+++ b/src/cobalt/dom/event.cc
@@ -21,40 +21,31 @@
 namespace cobalt {
 namespace dom {
 
-Event::Event(UninitializedFlag uninitialized_flag)
+Event::Event(UninitializedFlag /* uninitialized_flag */)
     : event_phase_(kNone),
       time_stamp_(static_cast<uint64>(base::Time::Now().ToJsTime())) {
-  UNREFERENCED_PARAMETER(uninitialized_flag);
   InitEventInternal(base::Token(), false, false);
 }
 
-Event::Event(base::Token type)
-    : event_phase_(kNone),
-      time_stamp_(static_cast<uint64>(base::Time::Now().ToJsTime())) {
-  InitEventInternal(type, false, false);
-}
-
 Event::Event(const std::string& type)
     : event_phase_(kNone),
       time_stamp_(static_cast<uint64>(base::Time::Now().ToJsTime())) {
   InitEventInternal(base::Token(type), false, false);
 }
 
-Event::Event(base::Token type, const EventInit& eventInitDict)
+Event::Event(const std::string& type, const EventInit& init_dict)
     : event_phase_(kNone),
       time_stamp_(static_cast<uint64>(base::Time::Now().ToJsTime())) {
-  SB_DCHECK(eventInitDict.has_bubbles());
-  SB_DCHECK(eventInitDict.has_cancelable());
-  InitEventInternal(type, eventInitDict.bubbles(), eventInitDict.cancelable());
+  SB_DCHECK(init_dict.has_bubbles());
+  SB_DCHECK(init_dict.has_cancelable());
+  InitEventInternal(base::Token(type), init_dict.bubbles(),
+                    init_dict.cancelable());
 }
 
-Event::Event(const std::string& type, const EventInit& eventInitDict)
+Event::Event(base::Token type)
     : event_phase_(kNone),
       time_stamp_(static_cast<uint64>(base::Time::Now().ToJsTime())) {
-  SB_DCHECK(eventInitDict.has_bubbles());
-  SB_DCHECK(eventInitDict.has_cancelable());
-  InitEventInternal(base::Token(type), eventInitDict.bubbles(),
-                    eventInitDict.cancelable());
+  InitEventInternal(type, false, false);
 }
 
 Event::Event(base::Token type, Bubbles bubbles, Cancelable cancelable)
@@ -63,6 +54,14 @@
   InitEventInternal(type, bubbles == kBubbles, cancelable == kCancelable);
 }
 
+Event::Event(base::Token type, const EventInit& init_dict)
+    : event_phase_(kNone),
+      time_stamp_(static_cast<uint64>(base::Time::Now().ToJsTime())) {
+  SB_DCHECK(init_dict.has_bubbles());
+  SB_DCHECK(init_dict.has_cancelable());
+  InitEventInternal(type, init_dict.bubbles(), init_dict.cancelable());
+}
+
 Event::~Event() {
   // This needs to be in the .cc file because EventTarget is only forward
   // declared in the .h file, and the implicit destructor for target_ cannot
diff --git a/src/cobalt/dom/event.h b/src/cobalt/dom/event.h
index 090066d..62c00ac 100644
--- a/src/cobalt/dom/event.h
+++ b/src/cobalt/dom/event.h
@@ -54,11 +54,11 @@
   explicit Event(UninitializedFlag uninitialized_flag);
 
   // Creates an event that cannot be bubbled and cancelled.
-  explicit Event(base::Token type);
   explicit Event(const std::string& type);
-  Event(base::Token type, const EventInit& eventInitDict);
-  Event(const std::string& type, const EventInit& eventInitDict);
+  explicit Event(base::Token type);
+  Event(const std::string& type, const EventInit& init_dict);
   Event(base::Token type, Bubbles bubbles, Cancelable cancelable);
+  Event(base::Token type, const EventInit& init_dict);
 
   ~Event() OVERRIDE;
 
diff --git a/src/cobalt/dom/event.idl b/src/cobalt/dom/event.idl
index cd863e1..35e6829 100644
--- a/src/cobalt/dom/event.idl
+++ b/src/cobalt/dom/event.idl
@@ -36,7 +36,9 @@
 
   readonly attribute DOMTimeStamp timeStamp;
 
-  void initEvent(DOMString type, boolean bubbles, boolean cancelable);
+  void initEvent(DOMString type,
+                 optional boolean bubbles = false,
+                 optional boolean cancelable = false);
 };
 
 typedef unsigned long long DOMTimeStamp;
diff --git a/src/cobalt/dom/event_modifier_init.idl b/src/cobalt/dom/event_modifier_init.idl
new file mode 100644
index 0000000..08e0461
--- /dev/null
+++ b/src/cobalt/dom/event_modifier_init.idl
@@ -0,0 +1,34 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// https://www.w3.org/TR/2016/WD-uievents-20160804/#dictdef-eventmodifierinit
+// https://www.w3.org/TR/uievents/#event-modifier-initializers
+
+dictionary EventModifierInit : UIEventInit {
+  boolean ctrlKey = false;
+  boolean shiftKey = false;
+  boolean altKey = false;
+  boolean metaKey = false;
+
+  boolean modifierAltGraph = false;
+  boolean modifierCapsLock = false;
+  boolean modifierFn = false;
+  boolean modifierFnLock = false;
+  boolean modifierHyper = false;
+  boolean modifierNumLock = false;
+  boolean modifierScrollLock = false;
+  boolean modifierSuper = false;
+  boolean modifierSymbol = false;
+  boolean modifierSymbolLock = false;
+};
diff --git a/src/cobalt/dom/event_target.cc b/src/cobalt/dom/event_target.cc
index b923be1..ca6593c 100644
--- a/src/cobalt/dom/event_target.cc
+++ b/src/cobalt/dom/event_target.cc
@@ -83,10 +83,18 @@
     return false;
   }
 
+  // The event is now being dispatched. Track it in the global stats.
+  GlobalStats::GetInstance()->StartJavaScriptEvent();
+
   event->set_target(this);
   event->set_event_phase(Event::kAtTarget);
   FireEventOnListeners(event);
   event->set_event_phase(Event::kNone);
+
+  // The event has completed being dispatched. Stop tracking it in the global
+  // stats.
+  GlobalStats::GetInstance()->StopJavaScriptEvent();
+
   return !event->default_prevented();
 }
 
diff --git a/src/cobalt/dom/event_target.h b/src/cobalt/dom/event_target.h
index a31a414..9af3d72 100644
--- a/src/cobalt/dom/event_target.h
+++ b/src/cobalt/dom/event_target.h
@@ -90,6 +90,13 @@
     SetAttributeEventListener(base::Tokens::blur(), event_listener);
   }
 
+  const EventListenerScriptValue* onclick() {
+    return GetAttributeEventListener(base::Tokens::click());
+  }
+  void set_onclick(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::click(), event_listener);
+  }
+
   const EventListenerScriptValue* onerror() {
     return GetAttributeEventListener(base::Tokens::error());
   }
@@ -104,6 +111,27 @@
     SetAttributeEventListener(base::Tokens::focus(), event_listener);
   }
 
+  const EventListenerScriptValue* onkeydown() {
+    return GetAttributeEventListener(base::Tokens::keydown());
+  }
+  void set_onkeydown(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::keydown(), event_listener);
+  }
+
+  const EventListenerScriptValue* onkeypress() {
+    return GetAttributeEventListener(base::Tokens::keypress());
+  }
+  void set_onkeypress(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::keypress(), event_listener);
+  }
+
+  const EventListenerScriptValue* onkeyup() {
+    return GetAttributeEventListener(base::Tokens::keyup());
+  }
+  void set_onkeyup(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::keyup(), event_listener);
+  }
+
   const EventListenerScriptValue* onload() {
     return GetAttributeEventListener(base::Tokens::load());
   }
@@ -111,6 +139,181 @@
     SetAttributeEventListener(base::Tokens::load(), event_listener);
   }
 
+  const EventListenerScriptValue* onloadeddata() {
+    return GetAttributeEventListener(base::Tokens::loadeddata());
+  }
+  void set_onloadeddata(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::loadeddata(), event_listener);
+  }
+
+  const EventListenerScriptValue* onloadedmetadata() {
+    return GetAttributeEventListener(base::Tokens::loadedmetadata());
+  }
+  void set_onloadedmetadata(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::loadedmetadata(), event_listener);
+  }
+
+  const EventListenerScriptValue* onloadstart() {
+    return GetAttributeEventListener(base::Tokens::loadstart());
+  }
+  void set_onloadstart(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::loadstart(), event_listener);
+  }
+
+  const EventListenerScriptValue* onmousedown() {
+    return GetAttributeEventListener(base::Tokens::mousedown());
+  }
+  void set_onmousedown(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::mousedown(), event_listener);
+  }
+
+  const EventListenerScriptValue* onmouseenter() {
+    return GetAttributeEventListener(base::Tokens::mouseenter());
+  }
+  void set_onmouseenter(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::mouseenter(), event_listener);
+  }
+
+  const EventListenerScriptValue* onmouseleave() {
+    return GetAttributeEventListener(base::Tokens::mouseleave());
+  }
+  void set_onmouseleave(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::mouseleave(), event_listener);
+  }
+
+  const EventListenerScriptValue* onmousemove() {
+    return GetAttributeEventListener(base::Tokens::mousemove());
+  }
+  void set_onmousemove(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::mousemove(), event_listener);
+  }
+
+  const EventListenerScriptValue* onmouseout() {
+    return GetAttributeEventListener(base::Tokens::mouseout());
+  }
+  void set_onmouseout(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::mouseout(), event_listener);
+  }
+
+  const EventListenerScriptValue* onmouseover() {
+    return GetAttributeEventListener(base::Tokens::mouseover());
+  }
+  void set_onmouseover(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::mouseover(), event_listener);
+  }
+
+  const EventListenerScriptValue* onmouseup() {
+    return GetAttributeEventListener(base::Tokens::mouseup());
+  }
+  void set_onmouseup(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::mouseup(), event_listener);
+  }
+
+  const EventListenerScriptValue* onpause() {
+    return GetAttributeEventListener(base::Tokens::pause());
+  }
+  void set_onpause(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::pause(), event_listener);
+  }
+
+  const EventListenerScriptValue* onplay() {
+    return GetAttributeEventListener(base::Tokens::play());
+  }
+  void set_onplay(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::play(), event_listener);
+  }
+
+  const EventListenerScriptValue* onplaying() {
+    return GetAttributeEventListener(base::Tokens::playing());
+  }
+  void set_onplaying(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::playing(), event_listener);
+  }
+
+  const EventListenerScriptValue* onpointerdown() {
+    return GetAttributeEventListener(base::Tokens::pointerdown());
+  }
+  void set_onpointerdown(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::pointerdown(), event_listener);
+  }
+
+  const EventListenerScriptValue* onpointerenter() {
+    return GetAttributeEventListener(base::Tokens::pointerenter());
+  }
+  void set_onpointerenter(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::pointerenter(), event_listener);
+  }
+
+  const EventListenerScriptValue* onpointerleave() {
+    return GetAttributeEventListener(base::Tokens::pointerleave());
+  }
+  void set_onpointerleave(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::pointerleave(), event_listener);
+  }
+
+  const EventListenerScriptValue* onpointermove() {
+    return GetAttributeEventListener(base::Tokens::pointermove());
+  }
+  void set_onpointermove(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::pointermove(), event_listener);
+  }
+
+  const EventListenerScriptValue* onpointerout() {
+    return GetAttributeEventListener(base::Tokens::pointerout());
+  }
+  void set_onpointerout(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::pointerout(), event_listener);
+  }
+
+  const EventListenerScriptValue* onpointerover() {
+    return GetAttributeEventListener(base::Tokens::pointerover());
+  }
+  void set_onpointerover(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::pointerover(), event_listener);
+  }
+
+  const EventListenerScriptValue* onpointerup() {
+    return GetAttributeEventListener(base::Tokens::pointerup());
+  }
+  void set_onpointerup(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::pointerup(), event_listener);
+  }
+
+  const EventListenerScriptValue* onprogress() {
+    return GetAttributeEventListener(base::Tokens::progress());
+  }
+  void set_onprogress(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::progress(), event_listener);
+  }
+
+  const EventListenerScriptValue* onratechange() {
+    return GetAttributeEventListener(base::Tokens::ratechange());
+  }
+  void set_onratechange(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::ratechange(), event_listener);
+  }
+
+  const EventListenerScriptValue* onseeked() {
+    return GetAttributeEventListener(base::Tokens::seeked());
+  }
+  void set_onseeked(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::seeked(), event_listener);
+  }
+
+  const EventListenerScriptValue* onseeking() {
+    return GetAttributeEventListener(base::Tokens::seeking());
+  }
+  void set_onseeking(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::seeking(), event_listener);
+  }
+
+  const EventListenerScriptValue* ontimeupdate() {
+    return GetAttributeEventListener(base::Tokens::timeupdate());
+  }
+  void set_ontimeupdate(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::timeupdate(), event_listener);
+  }
+
   const EventListenerScriptValue* onunload() {
     return GetAttributeEventListener(base::Tokens::unload());
   }
@@ -118,6 +321,27 @@
     SetAttributeEventListener(base::Tokens::unload(), event_listener);
   }
 
+  const EventListenerScriptValue* onvolumechange() {
+    return GetAttributeEventListener(base::Tokens::volumechange());
+  }
+  void set_onvolumechange(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::volumechange(), event_listener);
+  }
+
+  const EventListenerScriptValue* onwaiting() {
+    return GetAttributeEventListener(base::Tokens::waiting());
+  }
+  void set_onwaiting(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::waiting(), event_listener);
+  }
+
+  const EventListenerScriptValue* onwheel() {
+    return GetAttributeEventListener(base::Tokens::wheel());
+  }
+  void set_onwheel(const EventListenerScriptValue& event_listener) {
+    SetAttributeEventListener(base::Tokens::wheel(), event_listener);
+  }
+
   // Set an event listener assigned as an attribute. Overwrite the existing one
   // if there is any.
   void SetAttributeEventListener(base::Token type,
@@ -137,14 +361,13 @@
 
   void TraceMembers(script::Tracer* tracer) OVERRIDE;
 
-  DEFINE_WRAPPABLE_TYPE(EventTarget);
-
- protected:
   // This function sends the event to the event listeners attached to the
   // current event target. It takes stop immediate propagation flag into
   // account. The caller should set the phase and target.
   void FireEventOnListeners(const scoped_refptr<Event>& event);
 
+  DEFINE_WRAPPABLE_TYPE(EventTarget);
+
  private:
   struct EventListenerInfo {
     EventListenerInfo(base::Token type, EventTarget* const event_target,
diff --git a/src/cobalt/dom/focus_event.cc b/src/cobalt/dom/focus_event.cc
index cacd086..b876182 100644
--- a/src/cobalt/dom/focus_event.cc
+++ b/src/cobalt/dom/focus_event.cc
@@ -19,14 +19,11 @@
 
 FocusEvent::FocusEvent(const std::string& type)
     : UIEvent(type), related_target_(NULL) {}
-
-FocusEvent::FocusEvent(base::Token type,
-                       const scoped_refptr<EventTarget>& related_target)
-    : UIEvent(type), related_target_(related_target) {}
-
 FocusEvent::FocusEvent(base::Token type, Bubbles bubbles, Cancelable cancelable,
+                       const scoped_refptr<Window>& view,
                        const scoped_refptr<EventTarget>& related_target)
-    : UIEvent(type, bubbles, cancelable), related_target_(related_target) {}
+    : UIEvent(type, bubbles, cancelable, view),
+      related_target_(related_target) {}
 
 }  // namespace dom
 }  // namespace cobalt
diff --git a/src/cobalt/dom/focus_event.h b/src/cobalt/dom/focus_event.h
index 57ba8a3..78cf3be 100644
--- a/src/cobalt/dom/focus_event.h
+++ b/src/cobalt/dom/focus_event.h
@@ -31,10 +31,8 @@
  public:
   explicit FocusEvent(const std::string& type);
 
-  FocusEvent(base::Token type,
-             const scoped_refptr<EventTarget>& related_target);
-
   FocusEvent(base::Token type, Bubbles bubbles, Cancelable cancelable,
+             const scoped_refptr<Window>& view,
              const scoped_refptr<EventTarget>& related_target);
 
   // Web API: FocusEvent
diff --git a/src/cobalt/dom/focus_event_init.idl b/src/cobalt/dom/focus_event_init.idl
new file mode 100644
index 0000000..e38c47f
--- /dev/null
+++ b/src/cobalt/dom/focus_event_init.idl
@@ -0,0 +1,19 @@
+// 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.
+
+// https://www.w3.org/TR/2016/WD-uievents-20160804/#dictdef-focuseventinit
+
+dictionary FocusEventInit : UIEventInit {
+  EventTarget? relatedTarget = null;
+};
diff --git a/src/cobalt/dom/global_event_handlers.idl b/src/cobalt/dom/global_event_handlers.idl
index 73f4b95..cdacb6b 100644
--- a/src/cobalt/dom/global_event_handlers.idl
+++ b/src/cobalt/dom/global_event_handlers.idl
@@ -17,9 +17,49 @@
 [NoInterfaceObject]
 interface GlobalEventHandlers {
   attribute EventHandler onblur;
+  attribute EventHandler onclick;
   attribute EventHandler onerror;
   attribute EventHandler onfocus;
+
+  attribute EventHandler onkeydown;
+  attribute EventHandler onkeypress;
+  attribute EventHandler onkeyup;
+
   attribute EventHandler onload;
+  attribute EventHandler onloadeddata;
+  attribute EventHandler onloadedmetadata;
+  attribute EventHandler onloadstart;
+
+  attribute EventHandler onmousedown;
+  attribute EventHandler onmouseenter;
+  attribute EventHandler onmouseleave;
+  attribute EventHandler onmousemove;
+  attribute EventHandler onmouseout;
+  attribute EventHandler onmouseover;
+  attribute EventHandler onmouseup;
+
+  attribute EventHandler onpause;
+  attribute EventHandler onplay;
+  attribute EventHandler onplaying;
+
+  // Extensions for the Pointer Events recommendation.
+  //  https://www.w3.org/TR/2015/REC-pointerevents-20150224/#extensions-to-the-globaleventhandlers-interface
+  attribute EventHandler onpointerdown;
+  attribute EventHandler onpointerenter;
+  attribute EventHandler onpointerleave;
+  attribute EventHandler onpointermove;
+  attribute EventHandler onpointerout;
+  attribute EventHandler onpointerover;
+  attribute EventHandler onpointerup;
+
+  attribute EventHandler onprogress;
+  attribute EventHandler onratechange;
+  attribute EventHandler onseeked;
+  attribute EventHandler onseeking;
+  attribute EventHandler ontimeupdate;
+  attribute EventHandler onvolumechange;
+  attribute EventHandler onwaiting;
+  attribute EventHandler onwheel;
 };
 
 // TODO: The IDL defines EventHandler as a nullable callback
diff --git a/src/cobalt/dom/global_stats.cc b/src/cobalt/dom/global_stats.cc
index 8dc8f94..b2de28e 100644
--- a/src/cobalt/dom/global_stats.cc
+++ b/src/cobalt/dom/global_stats.cc
@@ -42,9 +42,9 @@
                  "Total number of currently active nodes."),
       num_node_lists_("Count.DOM.NodeLists", 0,
                       "Total number of currently active node lists."),
-      num_active_dispatch_events_(
-          "Count.DOM.ActiveDispatchEvents", 0,
-          "Total number of currently active dispatch events."),
+      num_active_java_script_events_(
+          "Count.DOM.ActiveJavaScriptEvents", 0,
+          "Total number of currently active JavaScript events."),
       num_xhrs_("Count.XHR", 0, "Total number of currently active XHRs."),
       xhr_memory_("Memory.XHR", 0, "Memory allocated by XHRs in bytes.") {}
 
@@ -55,7 +55,8 @@
          num_dom_token_lists_ == 0 && num_event_listeners_ == 0 &&
          num_html_collections_ == 0 && num_named_node_maps_ == 0 &&
          num_nodes_ == 0 && num_node_lists_ == 0 &&
-         num_active_dispatch_events_ == 0 && num_xhrs_ == 0 && xhr_memory_ == 0;
+         num_active_java_script_events_ == 0 && num_xhrs_ == 0 &&
+         xhr_memory_ == 0;
 }
 
 void GlobalStats::Add(Attr* /*object*/) { ++num_attrs_; }
@@ -92,9 +93,9 @@
 
 void GlobalStats::RemoveEventListener() { --num_event_listeners_; }
 
-void GlobalStats::StartDispatchEvent() { ++num_active_dispatch_events_; }
+void GlobalStats::StartJavaScriptEvent() { ++num_active_java_script_events_; }
 
-void GlobalStats::StopDispatchEvent() { --num_active_dispatch_events_; }
+void GlobalStats::StopJavaScriptEvent() { --num_active_java_script_events_; }
 
 void GlobalStats::Add(xhr::XMLHttpRequest* /* object */) { ++num_xhrs_; }
 
diff --git a/src/cobalt/dom/global_stats.h b/src/cobalt/dom/global_stats.h
index 75d4feb..413fc90 100644
--- a/src/cobalt/dom/global_stats.h
+++ b/src/cobalt/dom/global_stats.h
@@ -62,8 +62,8 @@
   int GetNumEventListeners() const { return num_event_listeners_; }
   int GetNumNodes() const { return num_nodes_; }
 
-  void StartDispatchEvent();
-  void StopDispatchEvent();
+  void StartJavaScriptEvent();
+  void StopJavaScriptEvent();
 
   void Add(xhr::XMLHttpRequest* object);
   void Remove(xhr::XMLHttpRequest* object);
@@ -84,7 +84,7 @@
   base::CVal<int, base::CValPublic> num_nodes_;
   base::CVal<int> num_node_lists_;
 
-  base::CVal<int> num_active_dispatch_events_;
+  base::CVal<int> num_active_java_script_events_;
 
   // XHR-related tracking
   base::CVal<int> num_xhrs_;
diff --git a/src/cobalt/dom/html_element.cc b/src/cobalt/dom/html_element.cc
index 93a6f9f..215a8be 100644
--- a/src/cobalt/dom/html_element.cc
+++ b/src/cobalt/dom/html_element.cc
@@ -328,7 +328,7 @@
 
 // Algorithm for offsetParent:
 //   https://www.w3.org/TR/2013/WD-cssom-view-20131217/#dom-htmlelement-offsetparent
-scoped_refptr<Element> HTMLElement::offset_parent() {
+Element* HTMLElement::offset_parent() {
   DCHECK(node_document());
   node_document()->DoSynchronousLayout();
 
@@ -341,7 +341,7 @@
   if (!layout_boxes_ || IsRootElement() || AsHTMLBodyElement() ||
       !computed_style() ||
       computed_style()->position() == cssom::KeywordValue::GetFixed()) {
-    return scoped_refptr<Element>();
+    return NULL;
   }
 
   // 2. Return the nearest ancestor element of the element for which at least
@@ -349,14 +349,13 @@
   //    ancestor is found:
   //    . The computed value of the 'position' property is not 'static'.
   //    . It is the HTML body element.
-  for (scoped_refptr<Node> ancestor_node = parent_node(); ancestor_node;
+  for (Node* ancestor_node = parent_node(); ancestor_node;
        ancestor_node = ancestor_node->parent_node()) {
-    scoped_refptr<Element> ancestor_element = ancestor_node->AsElement();
+    Element* ancestor_element = ancestor_node->AsElement();
     if (!ancestor_element) {
       continue;
     }
-    scoped_refptr<HTMLElement> ancestor_html_element =
-        ancestor_element->AsHTMLElement();
+    HTMLElement* ancestor_html_element = ancestor_element->AsHTMLElement();
     if (!ancestor_html_element) {
       continue;
     }
@@ -369,7 +368,7 @@
   }
 
   // 3. Return null.
-  return scoped_refptr<Element>();
+  return NULL;
 }
 
 // Algorithm for offset_top:
@@ -600,7 +599,7 @@
   // are updated.
   old_matching_rules_.swap(matching_rules_);
 
-  matching_rules_->clear();
+  matching_rules_.clear();
   rule_matching_state_.matching_nodes.clear();
   rule_matching_state_.descendant_potential_nodes.clear();
   rule_matching_state_.following_sibling_potential_nodes.clear();
@@ -739,8 +738,6 @@
       ALLOW_THIS_IN_INITIALIZER_LIST(
           animations_adapter_(new DOMAnimatable(this))),
       css_animations_(&animations_adapter_),
-      old_matching_rules_(new cssom::RulesWithCascadePrecedence()),
-      matching_rules_(new cssom::RulesWithCascadePrecedence()),
       matching_rules_valid_(false) {
   css_computed_style_declaration_->set_animations(animations());
   style_->set_mutation_observer(this);
@@ -750,7 +747,11 @@
 
 HTMLElement::~HTMLElement() {
   --(non_trivial_static_fields.Get().html_element_count_log.count);
+  if (IsInDocument()) {
+    dom_stat_tracker_->OnHtmlElementRemovedFromDocument();
+  }
   dom_stat_tracker_->OnHtmlElementDestroyed();
+
   style_->set_mutation_observer(NULL);
 }
 
@@ -758,10 +759,14 @@
   directionality_ = other.directionality_;
 }
 
-void HTMLElement::OnMutation() { InvalidateMatchingRulesRecursively(); }
+void HTMLElement::OnInsertedIntoDocument() {
+  Node::OnInsertedIntoDocument();
+  dom_stat_tracker_->OnHtmlElementInsertedIntoDocument();
+}
 
 void HTMLElement::OnRemovedFromDocument() {
   Node::OnRemovedFromDocument();
+  dom_stat_tracker_->OnHtmlElementRemovedFromDocument();
 
   // When an element that is focused stops being a focusable element, or stops
   // being focused without another element being explicitly focused in its
@@ -779,6 +784,8 @@
   }
 }
 
+void HTMLElement::OnMutation() { InvalidateMatchingRulesRecursively(); }
+
 void HTMLElement::OnSetAttribute(const std::string& name,
                                  const std::string& value) {
   if (name == "class" || name == "id") {
@@ -856,7 +863,8 @@
   // before focus is shifted, and does bubble.
   //   https://www.w3.org/TR/2016/WD-uievents-20160804/#event-type-focusin
   DispatchEvent(new FocusEvent(base::Tokens::focusin(), Event::kBubbles,
-                               Event::kNotCancelable, this));
+                               Event::kNotCancelable, document->window(),
+                               this));
 
   // 3. Make the element the currently focused element in its top-level browsing
   // context.
@@ -871,7 +879,8 @@
   // focus is shifted, and does not bubble.
   //   https://www.w3.org/TR/2016/WD-uievents-20160804/#event-type-focus
   DispatchEvent(new FocusEvent(base::Tokens::focus(), Event::kNotBubbles,
-                               Event::kNotCancelable, this));
+                               Event::kNotCancelable, document->window(),
+                               this));
 
   // Custom, not in any sepc.
   InvalidateMatchingRulesRecursively();
@@ -888,11 +897,12 @@
   // focus. This event type is similar to blur, but is dispatched before focus
   // is shifted, and does bubble.
   //   https://www.w3.org/TR/2016/WD-uievents-20160804/#event-type-focusout
+  Document* document = node_document();
+  scoped_refptr<Window> window(document ? document->window() : NULL);
   DispatchEvent(new FocusEvent(base::Tokens::focusout(), Event::kBubbles,
-                               Event::kNotCancelable, this));
+                               Event::kNotCancelable, window, this));
 
   // 2. Unfocus the element.
-  Document* document = node_document();
   if (document && document->active_element() == this->AsElement()) {
     document->SetActiveElement(NULL);
   }
@@ -904,7 +914,8 @@
   // focus is shifted, and does not bubble.
   //   https://www.w3.org/TR/2016/WD-uievents-20160804/#event-type-blur
   DispatchEvent(new FocusEvent(base::Tokens::blur(), Event::kNotBubbles,
-                               Event::kNotCancelable, this));
+                               Event::kNotCancelable, document->window(),
+                               this));
 
   // Custom, not in any sepc.
   InvalidateMatchingRulesRecursively();
@@ -1113,7 +1124,7 @@
 
     // Check for whether the matching rules have changed. If they have, then a
     // new computed style must be generated from them.
-    if (!generate_computed_style && *old_matching_rules_ != *matching_rules_) {
+    if (!generate_computed_style && old_matching_rules_ != matching_rules_) {
       generate_computed_style = true;
     }
   }
@@ -1222,6 +1233,22 @@
   computed_style_valid_ = true;
 }
 
+bool HTMLElement::IsDesignated() {
+  Document* document = node_document();
+  if (document) {
+    scoped_refptr<Element> element = document->indicated_element();
+    while (element) {
+      if (element == this) {
+        return true;
+      }
+      // The parent of an element that is :hover is also in that state.
+      //  https://www.w3.org/TR/selectors4/#hover-pseudo
+      element = element->parent_element();
+    }
+  }
+  return false;
+}
+
 void HTMLElement::ClearActiveBackgroundImages() {
   if (html_element_context() &&
       html_element_context()->animated_image_tracker()) {
diff --git a/src/cobalt/dom/html_element.h b/src/cobalt/dom/html_element.h
index 42c0146..4698fed 100644
--- a/src/cobalt/dom/html_element.h
+++ b/src/cobalt/dom/html_element.h
@@ -131,7 +131,7 @@
   // Web API: CSSOM View Module: Extensions to the HTMLElement Interface
   // (partial interface)
   //   https://www.w3.org/TR/2013/WD-cssom-view-20131217/#extensions-to-the-htmlelement-interface
-  scoped_refptr<Element> offset_parent();
+  Element* offset_parent();
   float offset_top();
   float offset_left();
   float offset_width();
@@ -181,7 +181,7 @@
   //
   // Returns the cached matching rules of this element.
   cssom::RulesWithCascadePrecedence* matching_rules() {
-    return matching_rules_.get();
+    return &matching_rules_;
   }
   // Returns the rule matching state of this element.
   RuleMatchingState* rule_matching_state() { return &rule_matching_state_; }
@@ -263,12 +263,19 @@
 
   bool matching_rules_valid() const { return matching_rules_valid_; }
 
+  // Returns whether the element has been designated.
+  //   https://www.w3.org/TR/selectors4/#hover-pseudo
+  bool IsDesignated();
+
   DEFINE_WRAPPABLE_TYPE(HTMLElement);
 
  protected:
   HTMLElement(Document* document, base::Token local_name);
   ~HTMLElement() OVERRIDE;
 
+  void OnInsertedIntoDocument() OVERRIDE;
+  void OnRemovedFromDocument() OVERRIDE;
+
   void CopyDirectionality(const HTMLElement& other);
 
   // HTMLElement keeps a pointer to the dom stat tracker to ensure that it can
@@ -279,7 +286,6 @@
  private:
   // From Node.
   void OnMutation() OVERRIDE;
-  void OnRemovedFromDocument() OVERRIDE;
 
   // From Element.
   void OnSetAttribute(const std::string& name,
@@ -349,8 +355,8 @@
   cssom::AnimationSet css_animations_;
 
   // The following fields are used in rule matching.
-  scoped_ptr<cssom::RulesWithCascadePrecedence> old_matching_rules_;
-  scoped_ptr<cssom::RulesWithCascadePrecedence> matching_rules_;
+  cssom::RulesWithCascadePrecedence old_matching_rules_;
+  cssom::RulesWithCascadePrecedence matching_rules_;
   RuleMatchingState rule_matching_state_;
 
   // This contains information about the boxes generated from the element.
diff --git a/src/cobalt/dom/html_element_context.cc b/src/cobalt/dom/html_element_context.cc
index 064403a..d9c502d 100644
--- a/src/cobalt/dom/html_element_context.cc
+++ b/src/cobalt/dom/html_element_context.cc
@@ -55,7 +55,9 @@
         reduced_image_cache_capacity_manager,
     loader::font::RemoteTypefaceCache* remote_typeface_cache,
     loader::mesh::MeshCache* mesh_cache, DomStatTracker* dom_stat_tracker,
-    const std::string& language, float video_playback_rate_multiplier)
+    const std::string& language,
+    base::ApplicationState initial_application_state,
+    float video_playback_rate_multiplier)
     : fetcher_factory_(fetcher_factory),
       css_parser_(css_parser),
       dom_parser_(dom_parser),
@@ -73,6 +75,7 @@
       mesh_cache_(mesh_cache),
       dom_stat_tracker_(dom_stat_tracker),
       language_(language),
+      page_visibility_state_(initial_application_state),
       video_playback_rate_multiplier_(video_playback_rate_multiplier),
       sync_load_thread_("Synchronous Load"),
       html_element_factory_(new HTMLElementFactory()) {
diff --git a/src/cobalt/dom/html_element_context.h b/src/cobalt/dom/html_element_context.h
index 2eb952f..970bc32 100644
--- a/src/cobalt/dom/html_element_context.h
+++ b/src/cobalt/dom/html_element_context.h
@@ -19,6 +19,7 @@
 
 #include "base/memory/scoped_ptr.h"
 #include "base/threading/thread.h"
+#include "cobalt/base/application_state.h"
 #include "cobalt/cssom/css_parser.h"
 #include "cobalt/dom/dom_stat_tracker.h"
 #include "cobalt/dom/parser.h"
@@ -30,6 +31,7 @@
 #include "cobalt/loader/mesh/mesh_cache.h"
 #include "cobalt/media/can_play_type_handler.h"
 #include "cobalt/media/web_media_player_factory.h"
+#include "cobalt/page_visibility/page_visibility_state.h"
 #include "cobalt/script/script_runner.h"
 #include "cobalt/script/script_value_factory.h"
 
@@ -61,7 +63,9 @@
           reduced_image_cache_capacity_manager,
       loader::font::RemoteTypefaceCache* remote_typeface_cache,
       loader::mesh::MeshCache* mesh_cache, DomStatTracker* dom_stat_tracker,
-      const std::string& language, float video_playback_rate_multiplier = 1.0);
+      const std::string& language,
+      base::ApplicationState initial_application_state,
+      float video_playback_rate_multiplier = 1.0);
   ~HTMLElementContext();
 
   loader::FetcherFactory* fetcher_factory() { return fetcher_factory_; }
@@ -122,6 +126,10 @@
     return reduced_image_cache_capacity_manager_;
   }
 
+  page_visibility::PageVisibilityState* page_visibility_state() {
+    return &page_visibility_state_;
+  }
+
  private:
   loader::FetcherFactory* const fetcher_factory_;
   cssom::CSSParser* const css_parser_;
@@ -140,6 +148,7 @@
   loader::mesh::MeshCache* const mesh_cache_;
   DomStatTracker* const dom_stat_tracker_;
   const std::string language_;
+  page_visibility::PageVisibilityState page_visibility_state_;
   const float video_playback_rate_multiplier_;
 
   base::Thread sync_load_thread_;
diff --git a/src/cobalt/dom/html_element_factory_test.cc b/src/cobalt/dom/html_element_factory_test.cc
index 0b27f6f..c8047e8 100644
--- a/src/cobalt/dom/html_element_factory_test.cc
+++ b/src/cobalt/dom/html_element_factory_test.cc
@@ -60,7 +60,8 @@
             NULL /* image_cache */,
             NULL /* reduced_image_cache_capacity_manager */,
             NULL /* remote_typeface_cache */, NULL /* mesh_cache */,
-            dom_stat_tracker_.get(), "" /* language */),
+            dom_stat_tracker_.get(), "" /* language */,
+            base::kApplicationStateStarted),
         document_(new Document(&html_element_context_)) {}
   ~HTMLElementFactoryTest() OVERRIDE {}
 
diff --git a/src/cobalt/dom/html_element_test.cc b/src/cobalt/dom/html_element_test.cc
index 239d247..6bbf621 100644
--- a/src/cobalt/dom/html_element_test.cc
+++ b/src/cobalt/dom/html_element_test.cc
@@ -114,7 +114,8 @@
       : dom_stat_tracker_(new DomStatTracker("HTMLElementTest")),
         html_element_context_(NULL, &css_parser_, NULL, NULL, NULL, NULL, NULL,
                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                              dom_stat_tracker_.get(), ""),
+                              dom_stat_tracker_.get(), "",
+                              base::kApplicationStateStarted),
         document_(new Document(&html_element_context_)) {}
   ~HTMLElementTest() OVERRIDE {}
 
diff --git a/src/cobalt/dom/html_media_element.cc b/src/cobalt/dom/html_media_element.cc
index 7f7cc7e..dd2e52b 100644
--- a/src/cobalt/dom/html_media_element.cc
+++ b/src/cobalt/dom/html_media_element.cc
@@ -134,13 +134,13 @@
       pending_load_(false),
       sent_stalled_event_(false),
       sent_end_event_(false) {
-  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::HTMLMediaElement");
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::HTMLMediaElement()");
   MLOG();
   html_media_element_count_log.Get().count++;
 }
 
 HTMLMediaElement::~HTMLMediaElement() {
-  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::~HTMLMediaElement");
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::~HTMLMediaElement()");
   MLOG();
   ClearMediaSource();
   html_media_element_count_log.Get().count--;
@@ -157,7 +157,7 @@
 }
 
 void HTMLMediaElement::set_src(const std::string& src) {
-  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::set_src");
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::set_src()");
   MLOG() << src;
   SetAttribute("src", src);
   ClearMediaPlayer();
@@ -192,7 +192,7 @@
 }
 
 void HTMLMediaElement::Load() {
-  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::Load");
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::Load()");
   // LoadInternal may result in a 'beforeload' event, which can make arbitrary
   // DOM mutations.
   scoped_refptr<HTMLMediaElement> protect(this);
@@ -243,6 +243,8 @@
 // See https://www.w3.org/TR/encrypted-media/#dom-htmlmediaelement-setmediakeys.
 scoped_ptr<HTMLMediaElement::VoidPromiseValue> HTMLMediaElement::SetMediaKeys(
     const scoped_refptr<eme::MediaKeys>& media_keys) {
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::SetMediaKeys()");
+
   scoped_ptr<VoidPromiseValue> promise = node_document()
                                              ->html_element_context()
                                              ->script_value_factory()
@@ -575,7 +577,7 @@
 }
 
 void HTMLMediaElement::Play() {
-  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::Play");
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::Play()");
   MLOG();
   // 4.8.10.9. Playing the media resource
   if (!player_ || network_state_ == kNetworkEmpty) {
@@ -602,7 +604,7 @@
 }
 
 void HTMLMediaElement::Pause() {
-  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::Pause");
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::Pause()");
   MLOG();
   // 4.8.10.9. Playing the media resource
   if (!player_ || network_state_ == kNetworkEmpty) {
@@ -699,13 +701,13 @@
 #endif  // defined(COBALT_MEDIA_SOURCE_2016)
 
 void HTMLMediaElement::ScheduleEvent(const scoped_refptr<Event>& event) {
-  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::ScheduleEvent");
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::ScheduleEvent()");
   MLOG() << event->type();
   event_queue_.Enqueue(event);
 }
 
 void HTMLMediaElement::CreateMediaPlayer() {
-  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::CreateMediaPlayer");
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::CreateMediaPlayer()");
   MLOG();
   if (src().empty()) {
     reduced_image_cache_capacity_request_ = base::nullopt;
@@ -747,7 +749,7 @@
 }
 
 void HTMLMediaElement::ScheduleLoad() {
-  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::ScheduleLoad");
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::ScheduleLoad()");
   if (!pending_load_) {
     PrepareForLoad();
     pending_load_ = true;
@@ -760,7 +762,7 @@
 }
 
 void HTMLMediaElement::PrepareForLoad() {
-  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::PrepareForLoad");
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::PrepareForLoad()");
   // Perform the cleanup required for the resource load algorithm to run.
   StopPeriodicTimers();
   load_timer_.Stop();
@@ -825,7 +827,7 @@
 }
 
 void HTMLMediaElement::LoadInternal() {
-  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::LoadInternal");
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::LoadInternal()");
   DCHECK(node_document());
 
   // Select media resource.
@@ -956,7 +958,7 @@
 }
 
 void HTMLMediaElement::ClearMediaPlayer() {
-  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::ClearMediaPlayer");
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::ClearMediaPlayer()");
   MLOG();
 
   ClearMediaSource();
@@ -1023,7 +1025,7 @@
 // This is kept as a one shot timer to be in sync with the original code. It
 // should be replaced by PostTask if this is any future rewrite.
 void HTMLMediaElement::OnLoadTimer() {
-  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::OnLoadTimer");
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::OnLoadTimer()");
   scoped_refptr<HTMLMediaElement> protect(this);
 
   if (pending_load_) {
@@ -1125,6 +1127,8 @@
 }
 
 void HTMLMediaElement::SetReadyState(WebMediaPlayer::ReadyState state) {
+  TRACE_EVENT1("cobalt::dom", "HTMLMediaElement::SetReadyState()", "state",
+               state);
   // Set "was_potentially_playing" BEFORE updating ready_state_,
   // PotentiallyPlaying() uses it
   bool was_potentially_playing = PotentiallyPlaying();
@@ -1168,7 +1172,6 @@
 
   if (ready_state_ >= WebMediaPlayer::kReadyStateHaveMetadata &&
       old_state < WebMediaPlayer::kReadyStateHaveMetadata) {
-    PlayerOutputModeUpdated();
     duration_ = player_->GetDuration();
     ScheduleOwnEvent(base::Tokens::durationchange());
     ScheduleOwnEvent(base::Tokens::loadedmetadata());
@@ -1587,6 +1590,15 @@
   EndProcessingMediaPlayerCallback();
 }
 
+void HTMLMediaElement::OutputModeChanged() {
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::OutputModeChanged()");
+  // If the player mode is updated, trigger a re-layout so that we can setup
+  // the video render tree differently depending on whether we are in punch-out
+  // or decode-to-texture.
+  node_document()->OnDOMMutation();
+  InvalidateLayoutBoxesOfNodeAndAncestors();
+}
+
 void HTMLMediaElement::PlaybackStateChanged() {
   if (!player_) {
     return;
@@ -1607,7 +1619,7 @@
 
 #if defined(COBALT_MEDIA_SOURCE_2016)
 void HTMLMediaElement::SourceOpened(media::ChunkDemuxer* chunk_demuxer) {
-  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::SourceOpened");
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::SourceOpened()");
   BeginProcessingMediaPlayerCallback();
   DCHECK(media_source_);
   media_source_->SetChunkDemuxerAndOpen(chunk_demuxer);
@@ -1626,7 +1638,7 @@
 }
 
 bool HTMLMediaElement::PreferDecodeToTexture() {
-  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::PreferDecodeToTexture");
+  TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::PreferDecodeToTexture()");
 
 #if defined(ENABLE_MAP_TO_MESH)
   if (!node_document()->UpdateComputedStyleOnElementAndAncestor(this)) {
@@ -1788,13 +1800,5 @@
 }
 #endif  // !defined(COBALT_MEDIA_SOURCE_2016)
 
-void HTMLMediaElement::PlayerOutputModeUpdated() {
-  // If the player mode is updated, trigger a re-layout so that we can setup
-  // the video render tree differently depending on whether we are in punch-out
-  // or decode-to-texture.
-  node_document()->OnDOMMutation();
-  InvalidateLayoutBoxesOfNodeAndAncestors();
-}
-
 }  // namespace dom
 }  // namespace cobalt
diff --git a/src/cobalt/dom/html_media_element.h b/src/cobalt/dom/html_media_element.h
index a45eb4e..b3d1f0d 100644
--- a/src/cobalt/dom/html_media_element.h
+++ b/src/cobalt/dom/html_media_element.h
@@ -233,6 +233,7 @@
   void ReadyStateChanged() OVERRIDE;
   void TimeChanged() OVERRIDE;
   void DurationChanged() OVERRIDE;
+  void OutputModeChanged() OVERRIDE;
   void PlaybackStateChanged() OVERRIDE;
   void SawUnsupportedTracks() OVERRIDE;
   float Volume() const OVERRIDE;
@@ -264,10 +265,6 @@
   void SetSourceState(MediaSourceReadyState ready_state);
 #endif  // !defined(COBALT_MEDIA_SOURCE_2016)
 
-  // Called whenever the player's output mode (e.g. punch-out,
-  // decode-to-texture) is updated.
-  void PlayerOutputModeUpdated();
-
   scoped_ptr<WebMediaPlayer> player_;
 
   std::string current_src_;
diff --git a/src/cobalt/dom/html_meta_element.cc b/src/cobalt/dom/html_meta_element.cc
index 9e2ea63..f55b4e4 100644
--- a/src/cobalt/dom/html_meta_element.cc
+++ b/src/cobalt/dom/html_meta_element.cc
@@ -55,8 +55,7 @@
 }
 
 bool HTMLMetaElement::IsDescendantOfHeadElement() const {
-  for (scoped_refptr<Node> node = parent_node(); node;
-       node = node->parent_node()) {
+  for (Node* node = parent_node(); node; node = node->parent_node()) {
     if (node->AsElement() && node->AsElement()->AsHTMLElement() &&
         node->AsElement()->AsHTMLElement()->AsHTMLHeadElement())
       return true;
diff --git a/src/cobalt/dom/html_script_element.cc b/src/cobalt/dom/html_script_element.cc
index c7a3362..e140ed6 100644
--- a/src/cobalt/dom/html_script_element.cc
+++ b/src/cobalt/dom/html_script_element.cc
@@ -23,6 +23,7 @@
 #include "cobalt/base/tokens.h"
 #include "cobalt/dom/csp_delegate.h"
 #include "cobalt/dom/document.h"
+#include "cobalt/dom/global_stats.h"
 #include "cobalt/dom/html_element_context.h"
 #include "cobalt/loader/fetcher_factory.h"
 #include "cobalt/loader/sync_loader.h"
@@ -506,6 +507,9 @@
     return;
   }
 
+  // The script is now being run. Track it in the global stats.
+  GlobalStats::GetInstance()->StartJavaScriptEvent();
+
   TRACE_EVENT2("cobalt::dom", "HTMLScriptElement::Execute()", "file_path",
                script_location.file_path, "line_number",
                script_location.line_number);
@@ -537,6 +541,12 @@
     PreventGarbageCollectionAndPostToDispatchEvent(
         FROM_HERE, base::Tokens::readystatechange());
   }
+
+  // The script is done running. Stop tracking it in the global stats.
+  GlobalStats::GetInstance()->StopJavaScriptEvent();
+
+  // Notify the DomStatTracker of the execution.
+  dom_stat_tracker_->OnHtmlScriptElementExecuted();
 }
 
 void HTMLScriptElement::PreventGarbageCollectionAndPostToDispatchEvent(
diff --git a/src/cobalt/dom/keyboard_event.cc b/src/cobalt/dom/keyboard_event.cc
index 4af5b2b..8b3356e 100644
--- a/src/cobalt/dom/keyboard_event.cc
+++ b/src/cobalt/dom/keyboard_event.cc
@@ -24,65 +24,115 @@
 namespace cobalt {
 namespace dom {
 
-namespace {
-base::Token TypeEnumToToken(KeyboardEvent::Type type) {
-  switch (type) {
-    case KeyboardEvent::kTypeKeyDown:
-      return base::Tokens::keydown();
-    case KeyboardEvent::kTypeKeyUp:
-      return base::Tokens::keyup();
-    case KeyboardEvent::kTypeKeyPress:
-      return base::Tokens::keypress();
-    default:
-      NOTREACHED() << "Invalid KeyboardEvent::Type";
-      return base::Tokens::keydown();
+KeyboardEvent::KeyboardEvent(const std::string& type)
+    : UIEventWithKeyState(base::Token(type), kBubbles, kCancelable, NULL),
+      key_location_(kDomKeyLocationStandard),
+      key_code_(0),
+      char_code_(0),
+      repeat_(false) {}
+
+// TODO: Initialize from init_dict.key() or init_dict.code() when not empty.
+KeyboardEvent::KeyboardEvent(const std::string& type,
+                             const KeyboardEventInit& init_dict)
+    : UIEventWithKeyState(base::Token(type), kBubbles, kCancelable,
+                          init_dict.view(), init_dict),
+      key_location_(static_cast<KeyLocationCode>(init_dict.location())),
+      key_code_(init_dict.key_code()),
+      char_code_(init_dict.char_code()),
+      repeat_(init_dict.repeat()) {}
+
+KeyboardEvent::KeyboardEvent(base::Token type,
+                             const scoped_refptr<Window>& view,
+                             const KeyboardEventInit& init_dict)
+    : UIEventWithKeyState(type, kBubbles, kCancelable, view, init_dict),
+      key_location_(static_cast<KeyLocationCode>(init_dict.location())),
+      key_code_(init_dict.key_code()),
+      char_code_(init_dict.char_code()),
+      repeat_(init_dict.repeat()) {}
+
+KeyboardEvent::KeyboardEvent(UninitializedFlag uninitialized_flag)
+    : UIEventWithKeyState(uninitialized_flag),
+      key_code_(0),
+      char_code_(0),
+      repeat_(false) {}
+
+void KeyboardEvent::InitKeyboardEvent(const std::string& type, bool bubbles,
+                                      bool cancelable,
+                                      const scoped_refptr<Window>& view,
+                                      const std::string& key, uint32 location,
+                                      const std::string& modifierslist,
+                                      bool repeat) {
+  InitUIEventWithKeyState(type, bubbles, cancelable, view, 0, modifierslist);
+  key_location_ = static_cast<KeyLocationCode>(location);
+  repeat_ = repeat;
+
+  // TODO: Implement full keyCode determination according to 'Legacy key models'
+  // from: https://www.w3.org/TR/2016/WD-uievents-20160804/#legacy-key-models
+
+  // Parse keys from the "Fixed virtual key codes" table from:
+  // https://www.w3.org/TR/2016/WD-uievents-20160804/#fixed-virtual-key-codes
+  if (key == "Backspace") {
+    key_code_ = keycode::kBack;
+  } else if (key == "Tab") {
+    key_code_ = keycode::kTab;
+  } else if (key == "Enter") {
+    key_code_ = keycode::kReturn;
+  } else if (key == "Shift") {
+    key_code_ = keycode::kShift;
+  } else if (key == "Control") {
+    key_code_ = keycode::kControl;
+  } else if (key == "Alt") {
+    key_code_ = keycode::kMenu;
+  } else if (key == "CapsLock") {
+    key_code_ = keycode::kCapital;
+  } else if (key == "Escape") {
+    key_code_ = keycode::kEscape;
+  } else if (key == "Space") {
+    key_code_ = keycode::kSpace;
+  } else if (key == "PageUp") {
+    key_code_ = keycode::kPrior;
+  } else if (key == "PageDown") {
+    key_code_ = keycode::kNext;
+  } else if (key == "End") {
+    key_code_ = keycode::kEnd;
+  } else if (key == "Home") {
+    key_code_ = keycode::kHome;
+  } else if (key == "ArrowLeft") {
+    key_code_ = keycode::kLeft;
+  } else if (key == "ArrowUp") {
+    key_code_ = keycode::kUp;
+  } else if (key == "ArrowRight") {
+    key_code_ = keycode::kRight;
+  } else if (key == "ArrowDown") {
+    key_code_ = keycode::kDown;
+  } else if (key == "Delete") {
+    key_code_ = keycode::kDelete;
   }
 }
-}  // namespace
-
-KeyboardEvent::KeyboardEvent(const std::string& type)
-    : UIEventWithKeyState(base::Token(type), kBubbles, kCancelable, 0) {}
-
-KeyboardEvent::KeyboardEvent(const Data& data)
-    : UIEventWithKeyState(TypeEnumToToken(data.type), kBubbles, kCancelable,
-                          data.modifiers),
-      data_(data) {}
-
-KeyboardEvent::KeyboardEvent(Type type, KeyLocationCode location,
-                             unsigned int modifiers, int key_code,
-                             int char_code, bool is_repeat)
-    : UIEventWithKeyState(TypeEnumToToken(type), kBubbles, kCancelable,
-                          modifiers),
-      data_(type, location, modifiers, key_code, char_code, is_repeat) {}
 
 // How to determine keycode:
-//   https://www.w3.org/TR/DOM-Level-3-Events/#determine-keydown-keyup-keyCode
+//   https://www.w3.org/TR/2016/WD-uievents-20160804/#determine-keydown-keyup-keyCode
 // Virtual key code for keyup/keydown, 0 for keypress (split model)
-int KeyboardEvent::key_code() const {
+uint32 KeyboardEvent::key_code() const {
   if (type() == base::Tokens::keydown() || type() == base::Tokens::keyup()) {
-    return data_.key_code;
+    return key_code_;
   }
 
   return 0;
 }
 
-int KeyboardEvent::char_code() const {
-  return type() == base::Tokens::keypress() ? data_.char_code : 0;
+uint32 KeyboardEvent::char_code() const {
+  return type() == base::Tokens::keypress() ? char_code_ : 0;
 }
 
-std::string KeyboardEvent::key() const {
-  // First check if the event corresponds to a printable character.
-  // If so, just return a string containing that single character.
-  int char_code = ComputeCharCode(data_.key_code, modifiers());
-  if (char_code > 0 && char_code <= 127) {
-    return std::string(1, static_cast<char>(char_code));
-  }
+uint32 KeyboardEvent::which() const { return key_code(); }
 
+std::string KeyboardEvent::NonPrintableKey(int32_t key_code) const {
   // Otherwise, we have one of the non-printable characters.
   // Definitions taken from:
   //   https://www.w3.org/TR/DOM-Level-3-Events-key/
   //   https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key.
-  switch (data_.key_code) {
+  switch (key_code) {
     case keycode::kBack:
       return "Backspace";
     case keycode::kTab:
@@ -305,9 +355,111 @@
   }
 }
 
+std::string KeyboardEvent::key() const {
+  // First check if the event corresponds to a printable character.
+  // If so, just return a string containing that single character.
+  int char_code = ComputeCharCode(key_code_, shift_key());
+  if (char_code > 0 && char_code <= 127) {
+    return std::string(1, static_cast<char>(char_code));
+  }
+
+  return NonPrintableKey(key_code_);
+}
+
+std::string KeyboardEvent::code() const {
+  // First check if the event corresponds to a named key in the tables here:
+  // https://www.w3.org/TR/uievents-code/#code-value-tables
+
+  // First check if the event corresponds to a alphanumeric key.
+  if (key_code_ >= keycode::kA && key_code_ <= keycode::kZ) {
+    std::string key_code = "Key";
+    key_code.append(1, static_cast<char>(key_code_));
+    return key_code;
+  }
+
+  // Check if the event corresponds to a digit key.
+  if (key_code_ >= keycode::k0 && key_code_ <= keycode::k9) {
+    std::string key_code = "Digit";
+    key_code.append(1, static_cast<char>(key_code_));
+    return key_code;
+  }
+
+  // Check if the event corresponds to a numpad digit key.
+  //  https://www.w3.org/TR/uievents-code/#key-numpad-section
+  if (key_code_ >= keycode::kNumpad0 && key_code_ <= keycode::kDivide) {
+    std::string key_code = "Numpad";
+    char numpad_digit = '0' + static_cast<char>(key_code_ - keycode::kNumpad0);
+    key_code.append(1, numpad_digit);
+    return key_code;
+  }
+
+  // Check if the event corresponds to a remaining named key
+  switch (key_code_) {
+    case keycode::kSpace:
+      return "Space";
+    case keycode::kMultiply:
+      return "NumpadMultiply";
+    case keycode::kAdd:
+      return "NumpadAdd";
+    case keycode::kSeparator:
+      return "NumpadComma";
+    case keycode::kSubtract:
+      return "NumpadSubtract";
+    case keycode::kDecimal:
+      return "NumpadDecimal";
+    case keycode::kDivide:
+      return "NumpadDivide";
+    case keycode::kOem1:
+      return "Semicolon";
+    case keycode::kOemPlus:
+      return "Equal";
+    case keycode::kOemComma:
+      return "Comma";
+    case keycode::kOemMinus:
+      return "Minus";
+    case keycode::kOemPeriod:
+      return "Period";
+    case keycode::kOem2:
+      return "Slash";
+    case keycode::kOem3:
+      return "Backquote";
+    case keycode::kOem4:
+      return "BracketLeft";
+    case keycode::kOem5:
+      return "Backslash";
+    case keycode::kOem6:
+      return "BracketRight";
+    case keycode::kOem7:
+      return "Quote";
+    case keycode::kLwin:
+      return "MetaLeft";
+    case keycode::kRwin:
+      return "MetaRight";
+    case keycode::kLshift:
+      return "ShiftLeft";
+    case keycode::kRshift:
+      return "ShiftRight";
+    case keycode::kLcontrol:
+      return "ControlLeft";
+    case keycode::kRcontrol:
+      return "ControlRight";
+    case keycode::kLmenu:
+      return "AltLeft";
+    case keycode::kRmenu:
+      return "AltRight";
+    default:
+      // Nothing.
+      break;
+  }
+
+  // The event corresponds to a nonprintable key with a name that matches the
+  // named value for the key.
+  return NonPrintableKey(key_code_);
+}
+
 // Static.
-int32 KeyboardEvent::ComputeCharCode(int32 key_code, uint32 modifiers) {
-  if (modifiers & UIEventWithKeyState::kShiftKey) {
+int32 KeyboardEvent::ComputeCharCode(int32 key_code, bool shift_key) {
+  if (shift_key) {
     return KeyCodeToCharCodeWithShift(key_code);
   } else {
     return KeyCodeToCharCodeNoShift(key_code);
@@ -426,10 +578,12 @@
     case keycode::kLshift:
     case keycode::kLcontrol:
     case keycode::kLmenu:
+    case keycode::kLwin:
       return kDomKeyLocationLeft;
     case keycode::kRshift:
     case keycode::kRcontrol:
     case keycode::kRmenu:
+    case keycode::kRwin:
       return kDomKeyLocationRight;
     default:
       return kDomKeyLocationStandard;
diff --git a/src/cobalt/dom/keyboard_event.h b/src/cobalt/dom/keyboard_event.h
index 0b8b232..a0c5433 100644
--- a/src/cobalt/dom/keyboard_event.h
+++ b/src/cobalt/dom/keyboard_event.h
@@ -18,6 +18,7 @@
 #include <string>
 
 #include "base/memory/scoped_ptr.h"
+#include "cobalt/dom/keyboard_event_init.h"
 #include "cobalt/dom/ui_event_with_key_state.h"
 
 namespace cobalt {
@@ -26,16 +27,9 @@
 // The KeyboardEvent provides specific contextual information associated with
 // keyboard devices. Each keyboard event references a key using a value.
 // Keyboard events are commonly directed at the element that has the focus.
-//   https://www.w3.org/TR/DOM-Level-3-Events/#events-keyboardevents
+//   https://www.w3.org/TR/2016/WD-uievents-20160804/#events-keyboardevents
 class KeyboardEvent : public UIEventWithKeyState {
  public:
-  // Non-standard, used to make creating KeyboardEvents via C++ easier.
-  enum Type {
-    kTypeKeyDown,
-    kTypeKeyUp,
-    kTypeKeyPress,
-  };
-
   // Web API: KeyboardEvent
   //
   enum KeyLocationCode {
@@ -44,51 +38,40 @@
     kDomKeyLocationRight = 0x02,
     kDomKeyLocationNumpad = 0x03,
   };
-
-  struct Data {
-    Data()
-        : type(kTypeKeyDown),
-          key_location(kDomKeyLocationStandard),
-          modifiers(0),
-          key_code(0),
-          char_code(0),
-          repeat(false) {}
-
-    Data(Type type, KeyLocationCode key_location, uint32_t modifiers,
-         int32_t key_code, int32_t char_code, bool repeat)
-        : type(type),
-          key_location(key_location),
-          modifiers(modifiers),
-          key_code(key_code),
-          char_code(char_code),
-          repeat(repeat) {}
-
-    Type type;
-    KeyLocationCode key_location;
-    uint32_t modifiers;
-    int32_t key_code;
-    int32_t char_code;
-    bool repeat;
-  };
-
   explicit KeyboardEvent(const std::string& type);
-  explicit KeyboardEvent(const Data& data);
-  KeyboardEvent(Type type, KeyLocationCode location, unsigned int modifiers,
-                int key_code, int char_code, bool is_repeat);
+  KeyboardEvent(const std::string& type, const KeyboardEventInit& init_dict);
+  KeyboardEvent(base::Token type, const scoped_refptr<Window>& view,
+                const KeyboardEventInit& init_dict);
+
+  // Creates an event with its "initialized flag" unset.
+  explicit KeyboardEvent(UninitializedFlag uninitialized_flag);
+
+  void InitKeyboardEvent(const std::string& type, bool bubbles, bool cancelable,
+                         const scoped_refptr<Window>& view,
+                         const std::string& key, uint32 location,
+                         const std::string& modifierslist, bool repeat);
 
   // Returns a string describing the key event, as defined here:
   //   https://www.w3.org/TR/DOM-Level-3-Events-key/
   std::string key() const;
 
-  KeyLocationCode location() const { return data_.key_location; }
-  bool repeat() const { return data_.repeat; }
+  // Return a string describing the physical key pressed, not affected by
+  // current keyboard layout or modifier state, as defined here:
+  //   https://www.w3.org/TR/uievents-code/
+  std::string code() const;
+
+  KeyLocationCode location() const { return key_location_; }
+  bool repeat() const { return repeat_; }
+  bool is_composing() const { return false; }
 
   // Non-standard and deprecated.
   // key code for keydown and keyup, character for keypress
-  //   https://www.w3.org/TR/DOM-Level-3-Events/#legacy-key-models
-  int key_code() const;
-  int char_code() const;
-  KeyLocationCode key_location() const { return data_.key_location; }
+  //   https://www.w3.org/TR/2016/WD-uievents-20160804/#legacy-key-models
+  uint32 key_code() const;
+  uint32 char_code() const;
+  uint32 which() const;
+
+  KeyLocationCode key_location() const { return key_location_; }
 
   // keyIdentifier is deprecated and non-standard.
   // Here, we just map it to the standardized key() method, which matches some,
@@ -97,7 +80,7 @@
 
   // Custom, not in any spec.
   // Utility functions for keycode/charcode conversion.
-  static int32 ComputeCharCode(int32 key_code, uint32 modifiers);
+  static int32 ComputeCharCode(int32 key_code, bool shift_key);
   static int KeyCodeToCharCodeWithShift(int key_code);
   static int KeyCodeToCharCodeNoShift(int key_code);
   static KeyLocationCode KeyCodeToKeyLocation(int key_code);
@@ -106,8 +89,12 @@
 
  private:
   ~KeyboardEvent() OVERRIDE {}
+  std::string NonPrintableKey(int32_t key_code) const;
 
-  const Data data_;
+  KeyLocationCode key_location_;
+  uint32_t key_code_;
+  uint32_t char_code_;
+  bool repeat_;
 };
 
 }  // namespace dom
diff --git a/src/cobalt/dom/keyboard_event.idl b/src/cobalt/dom/keyboard_event.idl
index 73607f6..e0b6220 100644
--- a/src/cobalt/dom/keyboard_event.idl
+++ b/src/cobalt/dom/keyboard_event.idl
@@ -12,9 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// https://www.w3.org/TR/2015/WD-uievents-20151215/#interface-KeyboardEvent
+// https://www.w3.org/TR/2016/WD-uievents-20160804/#interface-keyboardevent
 
-[Constructor(DOMString type)]
+[Constructor(DOMString type, optional KeyboardEventInit eventInitDict)]
 interface KeyboardEvent : UIEvent {
   // enum KeyLocationCode
   const unsigned long DOM_KEY_LOCATION_STANDARD = 0x00;
@@ -23,19 +23,33 @@
   const unsigned long DOM_KEY_LOCATION_NUMPAD = 0x03;
 
   readonly attribute DOMString key;
+  readonly attribute DOMString code;
   readonly attribute unsigned long location;
+
   readonly attribute boolean ctrlKey;
   readonly attribute boolean shiftKey;
   readonly attribute boolean altKey;
   readonly attribute boolean metaKey;
+
   readonly attribute boolean repeat;
+  readonly attribute boolean isComposing;
 
   boolean getModifierState(DOMString text);
 
   // Deprecated. These features have been removed from the Web standards.
-  //   https://www.w3.org/TR/DOM-Level-3-Events/#legacy-key-models
-  readonly attribute long keyCode;
-  readonly attribute long charCode;
+  //   https://www.w3.org/TR/2016/WD-uievents-20160804/#legacy-key-models
+  readonly attribute unsigned long keyCode;
+  readonly attribute unsigned long charCode;
+  readonly attribute unsigned long which;
   readonly attribute unsigned long keyLocation;
   readonly attribute DOMString keyIdentifier;
+
+  void initKeyboardEvent(DOMString type,
+                         optional boolean bubbles = false,
+                         optional boolean cancelable = false,
+                         optional Window? view = null,
+                         optional DOMString key = "",
+                         optional unsigned long location = 0,
+                         optional DOMString modifiersList = "",
+                         optional boolean repeat = false);
 };
diff --git a/src/cobalt/dom/keyboard_event_init.idl b/src/cobalt/dom/keyboard_event_init.idl
new file mode 100644
index 0000000..1d6252f
--- /dev/null
+++ b/src/cobalt/dom/keyboard_event_init.idl
@@ -0,0 +1,28 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// https://www.w3.org/TR/uievents/#dictdef-keyboardeventinit
+// https://www.w3.org/TR/2016/WD-uievents-20160804/#legacy-dictionary-KeyboardEventInit
+
+dictionary KeyboardEventInit : EventModifierInit {
+  DOMString key = "";
+  DOMString code = "";
+  unsigned long location = 0;
+  boolean repeat = false;
+  boolean isComposing = false;
+
+  unsigned long charCode = 0;
+  unsigned long keyCode = 0;
+  unsigned long which = 0;
+};
diff --git a/src/cobalt/dom/keyboard_event_test.cc b/src/cobalt/dom/keyboard_event_test.cc
index a9794b8..cece5e1 100644
--- a/src/cobalt/dom/keyboard_event_test.cc
+++ b/src/cobalt/dom/keyboard_event_test.cc
@@ -15,6 +15,7 @@
 #include "cobalt/dom/keyboard_event.h"
 
 #include "cobalt/base/tokens.h"
+#include "cobalt/dom/keyboard_event_init.h"
 #include "cobalt/dom/keycode.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -22,132 +23,171 @@
 namespace dom {
 
 TEST(KeyboardEventTest, ShouldHaveBubblesAndCancelableSet) {
-  scoped_refptr<KeyboardEvent> keyboard_event = new KeyboardEvent(
-      KeyboardEvent::kTypeKeyDown, KeyboardEvent::kDomKeyLocationStandard,
-      KeyboardEvent::kNoModifier, 0, 0, false);
+  scoped_refptr<KeyboardEvent> keyboard_event = new KeyboardEvent("keydown");
   EXPECT_TRUE(keyboard_event->bubbles());
   EXPECT_TRUE(keyboard_event->cancelable());
 }
 
 TEST(KeyboardEventTest, CanGetKeyLocation) {
-  scoped_refptr<KeyboardEvent> keyboard_event = new KeyboardEvent(
-      KeyboardEvent::kTypeKeyDown, KeyboardEvent::kDomKeyLocationStandard,
-      KeyboardEvent::kNoModifier, 0, 0, false);
+  KeyboardEventInit init;
+  scoped_refptr<KeyboardEvent> keyboard_event =
+      new KeyboardEvent("keydown", init);
   EXPECT_EQ(keyboard_event->key_location(),
             KeyboardEvent::kDomKeyLocationStandard);
 
-  scoped_refptr<KeyboardEvent> keyboard_event_l = new KeyboardEvent(
-      KeyboardEvent::kTypeKeyDown, KeyboardEvent::kDomKeyLocationLeft,
-      KeyboardEvent::kNoModifier, 0, 0, false);
+  init.set_location(KeyboardEvent::kDomKeyLocationLeft);
+  scoped_refptr<KeyboardEvent> keyboard_event_l =
+      new KeyboardEvent("keydown", init);
   EXPECT_EQ(keyboard_event_l->key_location(),
             KeyboardEvent::kDomKeyLocationLeft);
 
-  scoped_refptr<KeyboardEvent> keyboard_event_r = new KeyboardEvent(
-      KeyboardEvent::kTypeKeyDown, KeyboardEvent::kDomKeyLocationRight,
-      KeyboardEvent::kNoModifier, 0, 0, false);
+  init.set_location(KeyboardEvent::kDomKeyLocationRight);
+  scoped_refptr<KeyboardEvent> keyboard_event_r =
+      new KeyboardEvent("keydown", init);
   EXPECT_EQ(keyboard_event_r->key_location(),
             KeyboardEvent::kDomKeyLocationRight);
 }
 
-TEST(KeyboardEventTest, CanGetKeyIdentifier) {
-  scoped_refptr<KeyboardEvent> keyboard_event_a = new KeyboardEvent(
-      KeyboardEvent::kTypeKeyDown, KeyboardEvent::kDomKeyLocationStandard,
-      KeyboardEvent::kNoModifier, keycode::kA, 0, false);
+TEST(KeyboardEventTest, CanGetKeyIdentifierAndKeyAndCode) {
+  KeyboardEventInit init;
+  init.set_key_code(keycode::kA);
+  scoped_refptr<KeyboardEvent> keyboard_event_a =
+      new KeyboardEvent("keydown", init);
   EXPECT_EQ(keyboard_event_a->key_identifier(), "a");
+  EXPECT_EQ(keyboard_event_a->key(), "a");
+  EXPECT_EQ(keyboard_event_a->code(), "KeyA");
 
-  scoped_refptr<KeyboardEvent> keyboard_event_ca = new KeyboardEvent(
-      KeyboardEvent::kTypeKeyDown, KeyboardEvent::kDomKeyLocationStandard,
-      KeyboardEvent::kCtrlKey, keycode::kA, 0, false);
+  init.set_key_code(keycode::kA);
+  init.set_ctrl_key(true);
+  scoped_refptr<KeyboardEvent> keyboard_event_ca =
+      new KeyboardEvent("keydown", init);
+  init.set_ctrl_key(false);
   EXPECT_EQ(keyboard_event_ca->key_identifier(), "a");
+  EXPECT_EQ(keyboard_event_ca->key(), "a");
+  EXPECT_EQ(keyboard_event_ca->code(), "KeyA");
 
-  scoped_refptr<KeyboardEvent> keyboard_event_sa = new KeyboardEvent(
-      KeyboardEvent::kTypeKeyDown, KeyboardEvent::kDomKeyLocationStandard,
-      KeyboardEvent::kShiftKey, keycode::kA, 0, false);
+  init.set_shift_key(true);
+  scoped_refptr<KeyboardEvent> keyboard_event_sa =
+      new KeyboardEvent("keydown", init);
+  init.set_shift_key(false);
   EXPECT_EQ(keyboard_event_sa->key_identifier(), "A");
+  EXPECT_EQ(keyboard_event_sa->key(), "A");
+  EXPECT_EQ(keyboard_event_sa->code(), "KeyA");
 
-  scoped_refptr<KeyboardEvent> keyboard_event_1 = new KeyboardEvent(
-      KeyboardEvent::kTypeKeyDown, KeyboardEvent::kDomKeyLocationStandard,
-      KeyboardEvent::kNoModifier, keycode::k1, 0, false);
+  init.set_key_code(keycode::k1);
+  scoped_refptr<KeyboardEvent> keyboard_event_1 =
+      new KeyboardEvent("keydown", init);
   EXPECT_EQ(keyboard_event_1->key_identifier(), "1");
+  EXPECT_EQ(keyboard_event_1->key(), "1");
+  EXPECT_EQ(keyboard_event_1->code(), "Digit1");
 
-  scoped_refptr<KeyboardEvent> keyboard_event_s1 = new KeyboardEvent(
-      KeyboardEvent::kTypeKeyDown, KeyboardEvent::kDomKeyLocationStandard,
-      KeyboardEvent::kShiftKey, keycode::k1, 0, false);
+  init.set_shift_key(true);
+  scoped_refptr<KeyboardEvent> keyboard_event_s1 =
+      new KeyboardEvent("keydown", init);
+  init.set_shift_key(false);
   EXPECT_EQ(keyboard_event_s1->key_identifier(), "!");
+  EXPECT_EQ(keyboard_event_s1->key(), "!");
+  EXPECT_EQ(keyboard_event_1->code(), "Digit1");
 
-  scoped_refptr<KeyboardEvent> keyboard_event_left = new KeyboardEvent(
-      KeyboardEvent::kTypeKeyDown, KeyboardEvent::kDomKeyLocationStandard,
-      KeyboardEvent::kNoModifier, keycode::kLeft, 0, false);
+  init.set_key_code(keycode::kLeft);
+  scoped_refptr<KeyboardEvent> keyboard_event_left =
+      new KeyboardEvent("keydown", init);
   EXPECT_EQ(keyboard_event_left->key_identifier(), "ArrowLeft");
+  EXPECT_EQ(keyboard_event_left->key(), "ArrowLeft");
+  EXPECT_EQ(keyboard_event_left->code(), "ArrowLeft");
 
-  scoped_refptr<KeyboardEvent> keyboard_event_sleft = new KeyboardEvent(
-      KeyboardEvent::kTypeKeyDown, KeyboardEvent::kDomKeyLocationStandard,
-      KeyboardEvent::kShiftKey, keycode::kLeft, 0, false);
+  init.set_shift_key(true);
+  scoped_refptr<KeyboardEvent> keyboard_event_sleft =
+      new KeyboardEvent("keydown", init);
+  init.set_shift_key(false);
   EXPECT_EQ(keyboard_event_sleft->key_identifier(), "ArrowLeft");
+  EXPECT_EQ(keyboard_event_sleft->key(), "ArrowLeft");
+  EXPECT_EQ(keyboard_event_sleft->code(), "ArrowLeft");
+
+  init.set_key_code(keycode::kNumpad5);
+  init.set_shift_key(true);
+  scoped_refptr<KeyboardEvent> keyboard_event_num5 =
+      new KeyboardEvent("keydown", init);
+  init.set_shift_key(false);
+  EXPECT_EQ(keyboard_event_num5->key_identifier(), "5");
+  EXPECT_EQ(keyboard_event_num5->key(), "5");
+  EXPECT_EQ(keyboard_event_num5->code(), "Numpad5");
+
+  init.set_key_code(keycode::kSpace);
+  scoped_refptr<KeyboardEvent> keyboard_event_space =
+      new KeyboardEvent("keydown", init);
+  EXPECT_EQ(keyboard_event_space->key_identifier(), " ");
+  EXPECT_EQ(keyboard_event_space->key(), " ");
+  EXPECT_EQ(keyboard_event_space->code(), "Space");
 }
 
 TEST(KeyboardEventTest, CanGetAltKey) {
-  scoped_refptr<KeyboardEvent> keyboard_event = new KeyboardEvent(
-      KeyboardEvent::kTypeKeyDown, KeyboardEvent::kDomKeyLocationStandard,
-      KeyboardEvent::kNoModifier, 0, 0, false);
+  KeyboardEventInit init;
+  scoped_refptr<KeyboardEvent> keyboard_event =
+      new KeyboardEvent("keydown", init);
   EXPECT_FALSE(keyboard_event->alt_key());
 
-  scoped_refptr<KeyboardEvent> keyboard_event_a = new KeyboardEvent(
-      KeyboardEvent::kTypeKeyDown, KeyboardEvent::kDomKeyLocationStandard,
-      KeyboardEvent::kAltKey, 0, 0, false);
+  init.set_alt_key(true);
+  scoped_refptr<KeyboardEvent> keyboard_event_a =
+      new KeyboardEvent("keydown", init);
+  init.set_alt_key(false);
   EXPECT_TRUE(keyboard_event_a->alt_key());
 }
 
 TEST(KeyboardEventTest, CanGetCtrlKey) {
-  scoped_refptr<KeyboardEvent> keyboard_event = new KeyboardEvent(
-      KeyboardEvent::kTypeKeyDown, KeyboardEvent::kDomKeyLocationStandard,
-      KeyboardEvent::kNoModifier, 0, 0, false);
+  KeyboardEventInit init;
+  scoped_refptr<KeyboardEvent> keyboard_event =
+      new KeyboardEvent("keydown", init);
   EXPECT_FALSE(keyboard_event->ctrl_key());
 
-  scoped_refptr<KeyboardEvent> keyboard_event_c = new KeyboardEvent(
-      KeyboardEvent::kTypeKeyDown, KeyboardEvent::kDomKeyLocationStandard,
-      KeyboardEvent::kCtrlKey, 0, 0, false);
+  init.set_ctrl_key(true);
+  scoped_refptr<KeyboardEvent> keyboard_event_c =
+      new KeyboardEvent("keydown", init);
+  init.set_ctrl_key(false);
   EXPECT_TRUE(keyboard_event_c->ctrl_key());
 }
 
 TEST(KeyboardEventTest, CanGetMetaKey) {
-  scoped_refptr<KeyboardEvent> keyboard_event = new KeyboardEvent(
-      KeyboardEvent::kTypeKeyDown, KeyboardEvent::kDomKeyLocationStandard,
-      KeyboardEvent::kNoModifier, 0, 0, false);
+  KeyboardEventInit init;
+  scoped_refptr<KeyboardEvent> keyboard_event =
+      new KeyboardEvent("keydown", init);
   EXPECT_FALSE(keyboard_event->meta_key());
 
-  scoped_refptr<KeyboardEvent> keyboard_event_m = new KeyboardEvent(
-      KeyboardEvent::kTypeKeyDown, KeyboardEvent::kDomKeyLocationStandard,
-      KeyboardEvent::kMetaKey, 0, 0, false);
+  init.set_meta_key(true);
+  scoped_refptr<KeyboardEvent> keyboard_event_m =
+      new KeyboardEvent("keydown", init);
+  init.set_meta_key(false);
   EXPECT_TRUE(keyboard_event_m->meta_key());
 }
 
 TEST(KeyboardEventTest, CanGetShiftKey) {
-  scoped_refptr<KeyboardEvent> keyboard_event = new KeyboardEvent(
-      KeyboardEvent::kTypeKeyDown, KeyboardEvent::kDomKeyLocationStandard,
-      KeyboardEvent::kNoModifier, 0, 0, false);
+  KeyboardEventInit init;
+  scoped_refptr<KeyboardEvent> keyboard_event =
+      new KeyboardEvent("keydown", init);
   EXPECT_FALSE(keyboard_event->shift_key());
 
-  scoped_refptr<KeyboardEvent> keyboard_event_s = new KeyboardEvent(
-      KeyboardEvent::kTypeKeyDown, KeyboardEvent::kDomKeyLocationStandard,
-      KeyboardEvent::kShiftKey, 0, 0, false);
+  init.set_shift_key(true);
+  scoped_refptr<KeyboardEvent> keyboard_event_s =
+      new KeyboardEvent("keydown", init);
+  init.set_shift_key(false);
   EXPECT_TRUE(keyboard_event_s->shift_key());
 }
 
 TEST(KeyboardEventTest, CanGetModifierState) {
-  scoped_refptr<KeyboardEvent> keyboard_event = new KeyboardEvent(
-      KeyboardEvent::kTypeKeyDown, KeyboardEvent::kDomKeyLocationStandard,
-      KeyboardEvent::kNoModifier, 0, 0, false);
+  KeyboardEventInit init;
+  scoped_refptr<KeyboardEvent> keyboard_event =
+      new KeyboardEvent("keydown", init);
   EXPECT_FALSE(keyboard_event->GetModifierState("Alt"));
   EXPECT_FALSE(keyboard_event->GetModifierState("Control"));
   EXPECT_FALSE(keyboard_event->GetModifierState("Meta"));
   EXPECT_FALSE(keyboard_event->GetModifierState("Shift"));
 
-  scoped_refptr<KeyboardEvent> keyboard_event_m = new KeyboardEvent(
-      KeyboardEvent::kTypeKeyDown, KeyboardEvent::kDomKeyLocationStandard,
-      KeyboardEvent::kAltKey | KeyboardEvent::kCtrlKey |
-          KeyboardEvent::kMetaKey | KeyboardEvent::kShiftKey,
-      0, 0, false);
+  init.set_alt_key(true);
+  init.set_ctrl_key(true);
+  init.set_meta_key(true);
+  init.set_shift_key(true);
+  scoped_refptr<KeyboardEvent> keyboard_event_m =
+      new KeyboardEvent("keydown", init);
   EXPECT_TRUE(keyboard_event_m->GetModifierState("Alt"));
   EXPECT_TRUE(keyboard_event_m->GetModifierState("Control"));
   EXPECT_TRUE(keyboard_event_m->GetModifierState("Meta"));
@@ -155,14 +195,14 @@
 }
 
 TEST(KeyboardEventTest, CanGetRepeat) {
-  scoped_refptr<KeyboardEvent> keyboard_event = new KeyboardEvent(
-      KeyboardEvent::kTypeKeyDown, KeyboardEvent::kDomKeyLocationStandard,
-      KeyboardEvent::kNoModifier, 0, 0, false);
+  KeyboardEventInit init;
+  scoped_refptr<KeyboardEvent> keyboard_event =
+      new KeyboardEvent("keydown", init);
   EXPECT_FALSE(keyboard_event->repeat());
 
-  scoped_refptr<KeyboardEvent> keyboard_event_r = new KeyboardEvent(
-      KeyboardEvent::kTypeKeyDown, KeyboardEvent::kDomKeyLocationStandard,
-      KeyboardEvent::kShiftKey, 0, 0, true);
+  init.set_repeat(true);
+  scoped_refptr<KeyboardEvent> keyboard_event_r =
+      new KeyboardEvent("keydown", init);
   EXPECT_TRUE(keyboard_event_r->repeat());
 }
 }  // namespace dom
diff --git a/src/cobalt/dom/memory_info.cc b/src/cobalt/dom/memory_info.cc
index 8758ab9..ceb427b 100644
--- a/src/cobalt/dom/memory_info.cc
+++ b/src/cobalt/dom/memory_info.cc
@@ -21,22 +21,14 @@
 namespace cobalt {
 namespace dom {
 
-uint32 MemoryInfo::total_js_heap_size(
-    script::EnvironmentSettings* settings) const {
-  DOMSettings* dom_settings =
-      base::polymorphic_downcast<dom::DOMSettings*>(settings);
-  size_t total_memory =
-      dom_settings->javascript_engine()->UpdateMemoryStatsAndReturnReserved();
-  return static_cast<uint32>(total_memory);
+uint32 MemoryInfo::total_js_heap_size() const {
+  return static_cast<uint32>(
+      script::JavaScriptEngine::UpdateMemoryStatsAndReturnReserved());
 }
 
-uint32 MemoryInfo::used_js_heap_size(
-    script::EnvironmentSettings* settings) const {
-  DOMSettings* dom_settings =
-      base::polymorphic_downcast<dom::DOMSettings*>(settings);
-  size_t total_memory =
-      dom_settings->javascript_engine()->UpdateMemoryStatsAndReturnReserved();
-  return static_cast<uint32>(total_memory);
+uint32 MemoryInfo::used_js_heap_size() const {
+  return static_cast<uint32>(
+      script::JavaScriptEngine::UpdateMemoryStatsAndReturnReserved());
 }
 
 }  // namespace dom
diff --git a/src/cobalt/dom/memory_info.h b/src/cobalt/dom/memory_info.h
index d3de6a6..7bd61d2 100644
--- a/src/cobalt/dom/memory_info.h
+++ b/src/cobalt/dom/memory_info.h
@@ -28,9 +28,9 @@
  public:
   MemoryInfo() {}
 
-  uint32 total_js_heap_size(script::EnvironmentSettings* settings) const;
+  uint32 total_js_heap_size() const;
 
-  uint32 used_js_heap_size(script::EnvironmentSettings* settings) const;
+  uint32 used_js_heap_size() const;
 
   DEFINE_WRAPPABLE_TYPE(MemoryInfo);
 
diff --git a/src/cobalt/dom/memory_info.idl b/src/cobalt/dom/memory_info.idl
index 0f8b713..6dbfd88 100644
--- a/src/cobalt/dom/memory_info.idl
+++ b/src/cobalt/dom/memory_info.idl
@@ -18,6 +18,6 @@
 [
   NoInterfaceObject,
 ] interface MemoryInfo {
-  [CallWith=EnvironmentSettings] readonly attribute unsigned long totalJSHeapSize;
-  [CallWith=EnvironmentSettings] readonly attribute unsigned long usedJSHeapSize;
+  readonly attribute unsigned long totalJSHeapSize;
+  readonly attribute unsigned long usedJSHeapSize;
 };
diff --git a/src/cobalt/dom/message_event.h b/src/cobalt/dom/message_event.h
index 6c23bad..075afb1 100644
--- a/src/cobalt/dom/message_event.h
+++ b/src/cobalt/dom/message_event.h
@@ -47,6 +47,10 @@
         response_type_(response_type),
         data_(data) {}
 
+  // Creates an event with its "initialized flag" unset.
+  explicit MessageEvent(UninitializedFlag uninitialized_flag)
+      : Event(uninitialized_flag) {}
+
   ResponseType data() const;
 
   // These helper functions are custom, and not in any spec.
diff --git a/src/cobalt/dom/mouse_event.cc b/src/cobalt/dom/mouse_event.cc
new file mode 100644
index 0000000..ea882f2
--- /dev/null
+++ b/src/cobalt/dom/mouse_event.cc
@@ -0,0 +1,108 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/dom/mouse_event.h"
+
+#include <string>
+
+#include "cobalt/base/token.h"
+
+namespace cobalt {
+namespace dom {
+
+MouseEvent::MouseEvent(const std::string& type)
+    : UIEventWithKeyState(base::Token(type), kBubbles, kCancelable, NULL),
+      screen_x_(0),
+      screen_y_(0),
+      client_x_(0),
+      client_y_(0),
+      button_(0),
+      buttons_(0) {}
+
+MouseEvent::MouseEvent(const std::string& type, const MouseEventInit& init_dict)
+    : UIEventWithKeyState(base::Token(type), kBubbles, kCancelable,
+                          init_dict.view(), init_dict),
+      screen_x_(init_dict.screen_x()),
+      screen_y_(init_dict.screen_y()),
+      client_x_(init_dict.client_x()),
+      client_y_(init_dict.client_y()),
+      button_(init_dict.button()),
+      buttons_(init_dict.buttons()) {}
+
+MouseEvent::MouseEvent(base::Token type, const scoped_refptr<Window>& view,
+                       const MouseEventInit& init_dict)
+    : UIEventWithKeyState(type, kBubbles, kCancelable, view, init_dict),
+      screen_x_(init_dict.screen_x()),
+      screen_y_(init_dict.screen_y()),
+      client_x_(init_dict.client_x()),
+      client_y_(init_dict.client_y()),
+      button_(init_dict.button()),
+      buttons_(init_dict.buttons()) {}
+
+MouseEvent::MouseEvent(base::Token type, Bubbles bubbles, Cancelable cancelable,
+                       const scoped_refptr<Window>& view,
+                       const MouseEventInit& init_dict)
+    : UIEventWithKeyState(type, bubbles, cancelable, view, init_dict),
+      screen_x_(init_dict.screen_x()),
+      screen_y_(init_dict.screen_y()),
+      client_x_(init_dict.client_x()),
+      client_y_(init_dict.client_y()),
+      button_(init_dict.button()),
+      buttons_(init_dict.buttons()) {}
+
+MouseEvent::MouseEvent(UninitializedFlag uninitialized_flag)
+    : UIEventWithKeyState(uninitialized_flag),
+      screen_x_(0),
+      screen_y_(0),
+      client_x_(0),
+      client_y_(0),
+      button_(0),
+      buttons_(0) {}
+
+void MouseEvent::InitMouseEvent(
+    const std::string& type, bool bubbles, bool cancelable,
+    const scoped_refptr<Window>& view, int32 detail, int32 screen_x,
+    int32 screen_y, int32 client_x, int32 client_y, bool ctrl_key, bool alt_key,
+    bool shift_key, bool meta_key, uint16 button,
+    const scoped_refptr<EventTarget>& related_target) {
+  InitUIEventWithKeyState(type, bubbles, cancelable, view, detail, ctrl_key,
+                          alt_key, shift_key, meta_key);
+  screen_x_ = screen_x;
+  screen_y_ = screen_y;
+  client_x_ = client_x;
+  client_y_ = client_y;
+  button_ = button;
+  buttons_ = 0;
+  related_target_ = related_target;
+}
+
+void MouseEvent::InitMouseEvent(
+    const std::string& type, bool bubbles, bool cancelable,
+    const scoped_refptr<Window>& view, int32 detail, int32 screen_x,
+    int32 screen_y, int32 client_x, int32 client_y,
+    const std::string& modifierslist, uint16 button,
+    const scoped_refptr<EventTarget>& related_target) {
+  InitUIEventWithKeyState(type, bubbles, cancelable, view, detail,
+                          modifierslist);
+  screen_x_ = screen_x;
+  screen_y_ = screen_y;
+  client_x_ = client_x;
+  client_y_ = client_y;
+  button_ = button;
+  buttons_ = 0;
+  related_target_ = related_target;
+}
+
+}  // namespace dom
+}  // namespace cobalt
diff --git a/src/cobalt/dom/mouse_event.h b/src/cobalt/dom/mouse_event.h
new file mode 100644
index 0000000..da09013
--- /dev/null
+++ b/src/cobalt/dom/mouse_event.h
@@ -0,0 +1,93 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_DOM_MOUSE_EVENT_H_
+#define COBALT_DOM_MOUSE_EVENT_H_
+
+#include <string>
+
+#include "cobalt/base/token.h"
+#include "cobalt/dom/event_target.h"
+#include "cobalt/dom/mouse_event_init.h"
+#include "cobalt/dom/ui_event_with_key_state.h"
+
+namespace cobalt {
+namespace dom {
+
+// The MouseEvent provides specific contextual information associated with
+// mouse devices.
+//   https://www.w3.org/TR/2016/WD-uievents-20160804/#events-mouseevents
+class MouseEvent : public UIEventWithKeyState {
+ public:
+  explicit MouseEvent(const std::string& type);
+  MouseEvent(const std::string& type, const MouseEventInit& init_dict);
+  MouseEvent(base::Token type, const scoped_refptr<Window>& view,
+             const MouseEventInit& init_dict);
+  MouseEvent(base::Token type, Bubbles bubbles, Cancelable cancelable,
+             const scoped_refptr<Window>& view,
+             const MouseEventInit& init_dict);
+
+  // Creates an event with its "initialized flag" unset.
+  explicit MouseEvent(UninitializedFlag uninitialized_flag);
+
+  void InitMouseEvent(const std::string& type, bool bubbles, bool cancelable,
+                      const scoped_refptr<Window>& view, int32 detail,
+                      int32 screen_x, int32 screen_y, int32 client_x,
+                      int32 client_y, bool ctrl_key, bool alt_key,
+                      bool shift_key, bool meta_key, uint16 button,
+                      const scoped_refptr<EventTarget>& related_target);
+
+  void InitMouseEvent(const std::string& type, bool bubbles, bool cancelable,
+                      const scoped_refptr<Window>& view, int32 detail,
+                      int32 screen_x, int32 screen_y, int32 client_x,
+                      int32 client_y, const std::string& modifierslist,
+                      uint16 button,
+                      const scoped_refptr<EventTarget>& related_target);
+
+  int32_t screen_x() const { return screen_x_; }
+  int32_t screen_y() const { return screen_y_; }
+  int32_t client_x() const { return client_x_; }
+  int32_t client_y() const { return client_y_; }
+
+  int16_t button() const { return button_; }
+  uint16_t buttons() const { return buttons_; }
+
+  void set_related_target(const scoped_refptr<EventTarget>& target) {
+    related_target_ = target;
+  }
+
+  const scoped_refptr<EventTarget>& related_target() const {
+    return related_target_;
+  }
+
+  DEFINE_WRAPPABLE_TYPE(MouseEvent);
+
+ protected:
+  ~MouseEvent() OVERRIDE {}
+
+ private:
+  int32_t screen_x_;
+  int32_t screen_y_;
+  int32_t client_x_;
+  int32_t client_y_;
+  int16_t button_;
+  uint16_t buttons_;
+
+  scoped_refptr<EventTarget> related_target_;
+};
+
+}  // namespace dom
+}  // namespace cobalt
+
+#endif  // COBALT_DOM_MOUSE_EVENT_H_
diff --git a/src/cobalt/dom/mouse_event.idl b/src/cobalt/dom/mouse_event.idl
new file mode 100644
index 0000000..759e615
--- /dev/null
+++ b/src/cobalt/dom/mouse_event.idl
@@ -0,0 +1,52 @@
+// 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.
+
+// https://www.w3.org/TR/2016/WD-uievents-20160804/#interface-mouseevent
+// https://www.w3.org/TR/2016/WD-uievents-20160804/#idl-interface-MouseEvent-initializers
+
+[Constructor(DOMString type, optional MouseEventInit eventInitDict)]
+interface MouseEvent : UIEvent {
+  readonly attribute long screenX;
+  readonly attribute long screenY;
+  readonly attribute long clientX;
+  readonly attribute long clientY;
+
+  readonly attribute boolean ctrlKey;
+  readonly attribute boolean shiftKey;
+  readonly attribute boolean altKey;
+  readonly attribute boolean metaKey;
+
+  readonly attribute short button;
+  readonly attribute unsigned short buttons;
+
+  readonly attribute EventTarget? relatedTarget;
+
+  boolean getModifierState(DOMString keyArg);
+
+  void initMouseEvent(DOMString type,
+                   optional boolean bubbles = false,
+                   optional boolean cancelable = false,
+                   optional Window? view = null,
+                   optional long detail = 0,
+                   optional long screenX = 0,
+                   optional long screenY = 0,
+                   optional long clientX = 0,
+                   optional long clientY = 0,
+                   optional boolean ctrlKey = false,
+                   optional boolean altKey = false,
+                   optional boolean shiftKey = false,
+                   optional boolean metaKey = false,
+                   optional unsigned short button = 0,
+                   optional EventTarget? relatedTarget = null);
+};
diff --git a/src/cobalt/dom/mouse_event_init.idl b/src/cobalt/dom/mouse_event_init.idl
new file mode 100644
index 0000000..9b752d8
--- /dev/null
+++ b/src/cobalt/dom/mouse_event_init.idl
@@ -0,0 +1,27 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// https://www.w3.org/TR/uievents/#ref-for-dictdef-eventmodifierinit-3
+// https://www.w3.org/TR/2013/WD-cssom-view-20131217/#extensions-to-the-mouseevent-interface
+
+dictionary MouseEventInit : EventModifierInit {
+  long screenX = 0;
+  long screenY = 0;
+  long clientX = 0;
+  long clientY = 0;
+
+  short button = 0;
+  unsigned short buttons = 0;
+  EventTarget? relatedTarget = null;
+};
diff --git a/src/cobalt/dom/node.cc b/src/cobalt/dom/node.cc
index b1f0569..4b46103 100644
--- a/src/cobalt/dom/node.cc
+++ b/src/cobalt/dom/node.cc
@@ -36,6 +36,7 @@
 #include "cobalt/dom/node_list_live.h"
 #include "cobalt/dom/rule_matching.h"
 #include "cobalt/dom/text.h"
+#include "cobalt/dom/window.h"
 #if defined(OS_STARBOARD)
 #include "starboard/configuration.h"
 #if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
@@ -85,8 +86,8 @@
 
 }  // namespace
 
-// Algorithm for DispatchEvent:
-//   https://www.w3.org/TR/dom/#dispatching-events
+// Diagram for DispatchEvent:
+//  https://www.w3.org/TR/DOM-Level-3-Events/#event-flow
 bool Node::DispatchEvent(const scoped_refptr<Event>& event) {
   DCHECK(event);
   DCHECK(!event->IsBeingDispatched());
@@ -100,38 +101,59 @@
   }
 
   // The event is now being dispatched. Track it in the global stats.
-  GlobalStats::GetInstance()->StartDispatchEvent();
+  GlobalStats::GetInstance()->StartJavaScriptEvent();
+
+  scoped_refptr<Window> window;
+  if (IsInDocument()) {
+    DCHECK(node_document());
+    window = node_document()->default_view();
+  }
 
   typedef std::vector<scoped_refptr<Node> > Ancestors;
   Ancestors ancestors;
-  for (scoped_refptr<Node> current = this->parent_node(); current != NULL;
+  for (Node* current = this->parent_node(); current != NULL;
        current = current->parent_node()) {
     ancestors.push_back(current);
   }
 
   event->set_target(this);
 
-  // The capture phase
-  if (!ancestors.empty()) {
-    event->set_event_phase(Event::kCapturingPhase);
+  // The capture phase: The event object propagates through the target's
+  // ancestors from the Window to the target's parent. This phase is also known
+  // as the capturing phase.
+  event->set_event_phase(Event::kCapturingPhase);
+  if (window) {
+    window->FireEventOnListeners(event);
+  }
+  if (!event->propagation_stopped() && !ancestors.empty()) {
     for (Ancestors::reverse_iterator iter = ancestors.rbegin();
          iter != ancestors.rend() && !event->propagation_stopped(); ++iter) {
       (*iter)->FireEventOnListeners(event);
     }
   }
 
-  // The at target phase
   if (!event->propagation_stopped()) {
+    // The target phase: The event object arrives at the event object's event
+    // target. This phase is also known as the at-target phase.
     event->set_event_phase(Event::kAtTarget);
     FireEventOnListeners(event);
   }
 
-  // The bubbling phase
-  if (!event->propagation_stopped() && event->bubbles() && !ancestors.empty()) {
-    event->set_event_phase(Event::kBubblingPhase);
-    for (Ancestors::iterator iter = ancestors.begin();
-         iter != ancestors.end() && !event->propagation_stopped(); ++iter) {
-      (*iter)->FireEventOnListeners(event);
+  // If the event type indicates that the event doesn't bubble, then the event
+  // object will halt after completion of this phase.
+  if (!event->propagation_stopped() && event->bubbles()) {
+    if (!ancestors.empty()) {
+      // The bubble phase: The event object propagates through the target's
+      // ancestors in reverse order, starting with the target's parent and
+      // ending with the Window. This phase is also known as the bubbling phase.
+      event->set_event_phase(Event::kBubblingPhase);
+      for (Ancestors::iterator iter = ancestors.begin();
+           iter != ancestors.end() && !event->propagation_stopped(); ++iter) {
+        (*iter)->FireEventOnListeners(event);
+      }
+      if (window) {
+        window->FireEventOnListeners(event);
+      }
     }
   }
 
@@ -139,14 +161,14 @@
 
   // The event has completed being dispatched. Stop tracking it in the global
   // stats.
-  GlobalStats::GetInstance()->StopDispatchEvent();
+  GlobalStats::GetInstance()->StopJavaScriptEvent();
 
   return !event->default_prevented();
 }
 
 // Algorithm for owner_document:
 //   https://www.w3.org/TR/2015/WD-dom-20150618/#dom-node-ownerdocument
-scoped_refptr<Document> Node::owner_document() const {
+Document* Node::owner_document() const {
   // 1. If the context object is a document, return null.
   if (IsDocument()) {
     return NULL;
@@ -155,7 +177,7 @@
   return node_document();
 }
 
-scoped_refptr<Element> Node::parent_element() const {
+Element* Node::parent_element() const {
   return parent_ ? parent_->AsElement() : NULL;
 }
 
@@ -322,7 +344,7 @@
   return HTMLCollection::CreateWithChildElements(this);
 }
 
-scoped_refptr<Element> Node::first_element_child() const {
+Element* Node::first_element_child() const {
   Node* child = first_child();
   while (child) {
     if (child->IsElement()) {
@@ -333,7 +355,7 @@
   return NULL;
 }
 
-scoped_refptr<Element> Node::last_element_child() const {
+Element* Node::last_element_child() const {
   Node* child = last_child();
   while (child) {
     if (child->IsElement()) {
@@ -366,7 +388,7 @@
       this, selectors, node_document_->html_element_context()->css_parser());
 }
 
-scoped_refptr<Element> Node::previous_element_sibling() const {
+Element* Node::previous_element_sibling() const {
   Node* sibling = previous_sibling();
   while (sibling) {
     if (sibling->IsElement()) {
@@ -377,7 +399,7 @@
   return NULL;
 }
 
-scoped_refptr<Element> Node::next_element_sibling() const {
+Element* Node::next_element_sibling() const {
   Node* sibling = next_sibling();
   while (sibling) {
     if (sibling->IsElement()) {
@@ -415,25 +437,25 @@
   // 4. Not needed by Cobalt.
 }
 
-scoped_refptr<Node> Node::GetRootNode() {
+Node* Node::GetRootNode() {
   Node* root = this;
   while (root->parent_node()) {
     root = root->parent_node();
   }
-  return make_scoped_refptr(root);
+  return root;
 }
 
-scoped_refptr<CDATASection> Node::AsCDATASection() { return NULL; }
+CDATASection* Node::AsCDATASection() { return NULL; }
 
-scoped_refptr<Comment> Node::AsComment() { return NULL; }
+Comment* Node::AsComment() { return NULL; }
 
-scoped_refptr<Document> Node::AsDocument() { return NULL; }
+Document* Node::AsDocument() { return NULL; }
 
-scoped_refptr<DocumentType> Node::AsDocumentType() { return NULL; }
+DocumentType* Node::AsDocumentType() { return NULL; }
 
-scoped_refptr<Element> Node::AsElement() { return NULL; }
+Element* Node::AsElement() { return NULL; }
 
-scoped_refptr<Text> Node::AsText() { return NULL; }
+Text* Node::AsText() { return NULL; }
 
 void Node::TraceMembers(script::Tracer* tracer) {
   EventTarget::TraceMembers(tracer);
diff --git a/src/cobalt/dom/node.h b/src/cobalt/dom/node.h
index e1fbecb..82cce91 100644
--- a/src/cobalt/dom/node.h
+++ b/src/cobalt/dom/node.h
@@ -119,15 +119,15 @@
   virtual NodeType node_type() const = 0;
   virtual base::Token node_name() const = 0;
 
-  scoped_refptr<Document> owner_document() const;
-  scoped_refptr<Node> parent_node() const { return parent_; }
-  scoped_refptr<Element> parent_element() const;
+  Document* owner_document() const;
+  Node* parent_node() const { return parent_; }
+  Element* parent_element() const;
   bool HasChildNodes() const;
   scoped_refptr<NodeList> child_nodes() const;
-  scoped_refptr<Node> first_child() const { return first_child_; }
-  scoped_refptr<Node> last_child() const { return last_child_; }
-  scoped_refptr<Node> next_sibling() const { return next_sibling_; }
-  scoped_refptr<Node> previous_sibling() const { return previous_sibling_; }
+  Node* first_child() const { return first_child_; }
+  Node* last_child() const { return last_child_; }
+  Node* next_sibling() const { return next_sibling_; }
+  Node* previous_sibling() const { return previous_sibling_; }
 
   virtual base::optional<std::string> node_value() const {
     return base::nullopt;
@@ -158,8 +158,8 @@
   //   https://www.w3.org/TR/dom/#parentnode
   //
   scoped_refptr<HTMLCollection> children() const;
-  scoped_refptr<Element> first_element_child() const;
-  scoped_refptr<Element> last_element_child() const;
+  Element* first_element_child() const;
+  Element* last_element_child() const;
   unsigned int child_element_count() const;
 
   scoped_refptr<Element> QuerySelector(const std::string& selectors);
@@ -169,8 +169,8 @@
   // The NonDocumentTypeChildNode interface contains methods that are particular
   // to Node objects that can have a parent.
   //   https://www.w3.org/TR/2014/WD-dom-20140710/#interface-nondocumenttypechildnode
-  scoped_refptr<Element> previous_element_sibling() const;
-  scoped_refptr<Element> next_element_sibling() const;
+  Element* previous_element_sibling() const;
+  Element* next_element_sibling() const;
 
   // From the spec: Node.
   //
@@ -190,7 +190,7 @@
 
   // Returns the root Node of the tree this node belongs to. If this node is the
   // root, it will return this Node.
-  scoped_refptr<Node> GetRootNode();
+  Node* GetRootNode();
 
   bool IsCDATASection() const { return node_type() == kCdataSectionNode; }
   bool IsComment() const { return node_type() == kCommentNode; }
@@ -201,12 +201,12 @@
 
   // Safe type conversion methods that will downcast to the required type if
   // possible or return NULL otherwise.
-  virtual scoped_refptr<CDATASection> AsCDATASection();
-  virtual scoped_refptr<Comment> AsComment();
-  virtual scoped_refptr<Document> AsDocument();
-  virtual scoped_refptr<DocumentType> AsDocumentType();
-  virtual scoped_refptr<Element> AsElement();
-  virtual scoped_refptr<Text> AsText();
+  virtual CDATASection* AsCDATASection();
+  virtual Comment* AsComment();
+  virtual Document* AsDocument();
+  virtual DocumentType* AsDocumentType();
+  virtual Element* AsElement();
+  virtual Text* AsText();
 
   // Node generation counter that will be modified for every content change
   // that affects the topology of the subtree defined by this node.
@@ -248,6 +248,8 @@
   // removed from to its owner document.
   virtual void OnRemovedFromDocument();
 
+  virtual bool IsInDocument() const { return inserted_into_document_; }
+
   virtual void PurgeCachedBackgroundImagesOfNodeAndDescendants();
   virtual void InvalidateComputedStylesOfNodeAndDescendants();
   virtual void InvalidateLayoutBoxesOfNodeAndAncestors();
diff --git a/src/cobalt/dom/node_list_live_test.cc b/src/cobalt/dom/node_list_live_test.cc
index a9762af..26d3b6f 100644
--- a/src/cobalt/dom/node_list_live_test.cc
+++ b/src/cobalt/dom/node_list_live_test.cc
@@ -29,7 +29,8 @@
       : dom_stat_tracker_("NodeListLiveTest"),
         html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                               NULL, NULL, NULL, NULL, NULL, NULL,
-                              &dom_stat_tracker_, ""),
+                              &dom_stat_tracker_, "",
+                              base::kApplicationStateStarted),
         document_(new Document(&html_element_context_)) {}
 
   ~NodeListLiveTest() OVERRIDE {}
diff --git a/src/cobalt/dom/node_list_test.cc b/src/cobalt/dom/node_list_test.cc
index df03cba..392963f 100644
--- a/src/cobalt/dom/node_list_test.cc
+++ b/src/cobalt/dom/node_list_test.cc
@@ -29,7 +29,8 @@
       : dom_stat_tracker_(new DomStatTracker("NodeListTest")),
         html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                               NULL, NULL, NULL, NULL, NULL, NULL,
-                              dom_stat_tracker_.get(), ""),
+                              dom_stat_tracker_.get(), "",
+                              base::kApplicationStateStarted),
         document_(new Document(&html_element_context_)) {}
 
   ~NodeListTest() OVERRIDE {}
diff --git a/src/cobalt/dom/pointer_event.cc b/src/cobalt/dom/pointer_event.cc
new file mode 100644
index 0000000..7cc2f38
--- /dev/null
+++ b/src/cobalt/dom/pointer_event.cc
@@ -0,0 +1,71 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/dom/pointer_event.h"
+
+#include <string>
+
+namespace cobalt {
+namespace dom {
+
+PointerEvent::PointerEvent(const std::string& type)
+    : MouseEvent(type),
+      pointer_id_(0),
+      width_(0),
+      height_(0),
+      pressure_(0),
+      tilt_x_(0),
+      tilt_y_(0),
+      is_primary_(false) {}
+
+PointerEvent::PointerEvent(const std::string& type,
+                           const PointerEventInit& init_dict)
+    : MouseEvent(type, init_dict),
+      pointer_id_(init_dict.pointer_id()),
+      width_(init_dict.width()),
+      height_(init_dict.height()),
+      pressure_(init_dict.pressure()),
+      tilt_x_(init_dict.tilt_x()),
+      tilt_y_(init_dict.tilt_y()),
+      pointer_type_(init_dict.pointer_type()),
+      is_primary_(init_dict.is_primary()) {}
+
+PointerEvent::PointerEvent(base::Token type, const scoped_refptr<Window>& view,
+                           const PointerEventInit& init_dict)
+    : MouseEvent(type, view, init_dict),
+      pointer_id_(init_dict.pointer_id()),
+      width_(init_dict.width()),
+      height_(init_dict.height()),
+      pressure_(init_dict.pressure()),
+      tilt_x_(init_dict.tilt_x()),
+      tilt_y_(init_dict.tilt_y()),
+      pointer_type_(init_dict.pointer_type()),
+      is_primary_(init_dict.is_primary()) {}
+
+PointerEvent::PointerEvent(base::Token type, Bubbles bubbles,
+                           Cancelable cancelable,
+                           const scoped_refptr<Window>& view,
+                           const PointerEventInit& init_dict)
+    : MouseEvent(type, bubbles, cancelable, view, init_dict),
+      pointer_id_(init_dict.pointer_id()),
+      width_(init_dict.width()),
+      height_(init_dict.height()),
+      pressure_(init_dict.pressure()),
+      tilt_x_(init_dict.tilt_x()),
+      tilt_y_(init_dict.tilt_y()),
+      pointer_type_(init_dict.pointer_type()),
+      is_primary_(init_dict.is_primary()) {}
+
+}  // namespace dom
+}  // namespace cobalt
diff --git a/src/cobalt/dom/pointer_event.h b/src/cobalt/dom/pointer_event.h
new file mode 100644
index 0000000..489126f
--- /dev/null
+++ b/src/cobalt/dom/pointer_event.h
@@ -0,0 +1,69 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_DOM_POINTER_EVENT_H_
+#define COBALT_DOM_POINTER_EVENT_H_
+
+#include <string>
+
+#include "cobalt/base/token.h"
+#include "cobalt/dom/mouse_event.h"
+#include "cobalt/dom/pointer_event_init.h"
+
+namespace cobalt {
+namespace dom {
+
+// The PointerEvent provides specific contextual information associated with
+// pointer devices. A pointer is a hardware agnostic representation of input
+// devices that can target a specific coordinate (or set of coordinates) on a
+// screen.
+//   https://www.w3.org/TR/2015/REC-pointerevents-20150224/
+class PointerEvent : public MouseEvent {
+ public:
+  explicit PointerEvent(const std::string& type);
+  PointerEvent(const std::string& type, const PointerEventInit& init_dict);
+  PointerEvent(base::Token type, const scoped_refptr<Window>& view,
+               const PointerEventInit& init_dict);
+  PointerEvent(base::Token type, Bubbles bubbles, Cancelable cancelable,
+               const scoped_refptr<Window>& view,
+               const PointerEventInit& init_dict);
+
+  int32_t pointer_id() const { return pointer_id_; }
+  double width() const { return width_; }
+  double height() const { return height_; }
+  float pressure() const { return pressure_; }
+  int32_t tilt_x() const { return tilt_x_; }
+  int32_t tilt_y() const { return tilt_y_; }
+  const std::string& pointer_type() const { return pointer_type_; }
+  int32_t is_primary() const { return is_primary_; }
+
+  DEFINE_WRAPPABLE_TYPE(PointerEvent);
+
+ private:
+  ~PointerEvent() OVERRIDE {}
+
+  int32_t pointer_id_;
+  double width_;
+  double height_;
+  float pressure_;
+  int32_t tilt_x_;
+  int32_t tilt_y_;
+  std::string pointer_type_;
+  bool is_primary_;
+};
+
+}  // namespace dom
+}  // namespace cobalt
+
+#endif  // COBALT_DOM_POINTER_EVENT_H_
diff --git a/src/cobalt/dom/pointer_event.idl b/src/cobalt/dom/pointer_event.idl
new file mode 100644
index 0000000..0fd4b20
--- /dev/null
+++ b/src/cobalt/dom/pointer_event.idl
@@ -0,0 +1,18 @@
+// 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.
+
+// https://www.w3.org/TR/2015/REC-pointerevents-20150224/
+
+[Constructor(DOMString type)]
+interface PointerEvent : MouseEvent {};
diff --git a/src/cobalt/dom/pointer_event_init.idl b/src/cobalt/dom/pointer_event_init.idl
new file mode 100644
index 0000000..83f279e
--- /dev/null
+++ b/src/cobalt/dom/pointer_event_init.idl
@@ -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.
+
+// https://www.w3.org/TR/pointerevents/#idl-def-PointerEventInit
+
+dictionary PointerEventInit : MouseEventInit {
+  long pointerId = 0;
+  double width = 0;
+  double height = 0;
+  float pressure = 0;
+  long tiltX = 0;
+  long tiltY = 0;
+  DOMString pointerType = "";
+  boolean isPrimary = false;
+};
diff --git a/src/cobalt/dom/progress_event.cc b/src/cobalt/dom/progress_event.cc
index ab00d02..5d20690 100644
--- a/src/cobalt/dom/progress_event.cc
+++ b/src/cobalt/dom/progress_event.cc
@@ -18,14 +18,20 @@
 namespace dom {
 
 ProgressEvent::ProgressEvent(const std::string& type)
-    : Event(type), loaded_(0), total_(0), length_computable_(false) {}
+    : Event(base::Token(type), kNotBubbles, kNotCancelable),
+      loaded_(0),
+      total_(0),
+      length_computable_(false) {}
 
 ProgressEvent::ProgressEvent(base::Token type)
-    : Event(type), loaded_(0), total_(0), length_computable_(false) {}
+    : Event(type, kNotBubbles, kNotCancelable),
+      loaded_(0),
+      total_(0),
+      length_computable_(false) {}
 
 ProgressEvent::ProgressEvent(base::Token type, uint64 loaded, uint64 total,
                              bool length_computable)
-    : Event(type),
+    : Event(type, kNotBubbles, kNotCancelable),
       loaded_(loaded),
       total_(total),
       length_computable_(length_computable) {}
diff --git a/src/cobalt/dom/progress_event.h b/src/cobalt/dom/progress_event.h
index 3847781..d8c0358 100644
--- a/src/cobalt/dom/progress_event.h
+++ b/src/cobalt/dom/progress_event.h
@@ -23,6 +23,8 @@
 namespace cobalt {
 namespace dom {
 
+// Events using the ProgressEvent interface indicate some kind of progression.
+//   https://www.w3.org/TR/progress-events/#interface-progressevent
 class ProgressEvent : public Event {
  public:
   explicit ProgressEvent(const std::string& type);
diff --git a/src/cobalt/dom/rule_matching.cc b/src/cobalt/dom/rule_matching.cc
index 2e1348a..1437827 100644
--- a/src/cobalt/dom/rule_matching.cc
+++ b/src/cobalt/dom/rule_matching.cc
@@ -260,8 +260,10 @@
   // device that does not detect hovering) are still conforming.
   //   https://www.w3.org/TR/selectors4/#hover-pseudo
   void VisitHoverPseudoClass(cssom::HoverPseudoClass*) OVERRIDE {
-    NOTIMPLEMENTED();
-    element_ = NULL;
+    if (!element_->AsHTMLElement() ||
+        !element_->AsHTMLElement()->IsDesignated()) {
+      element_ = NULL;
+    }
   }
 
   // The negation pseudo-class, :not(), is a functional pseudo-class taking a
@@ -523,6 +525,14 @@
                                     &candidate_nodes);
       }
 
+      // Hover pseudo class.
+      if (node->HasPseudoClass(cssom::kHoverPseudoClass, combinator_type) &&
+          element->IsDesignated()) {
+        GatherCandidateNodesFromSet(cssom::kHoverPseudoClass, combinator_type,
+                                    node->pseudo_class_nodes(),
+                                    &candidate_nodes);
+      }
+
       // Not pseudo class.
       if (node->HasPseudoClass(cssom::kNotPseudoClass, combinator_type)) {
         GatherCandidateNodesFromSet(cssom::kNotPseudoClass, combinator_type,
diff --git a/src/cobalt/dom/rule_matching_test.cc b/src/cobalt/dom/rule_matching_test.cc
index 5323f3d..eac8ded 100644
--- a/src/cobalt/dom/rule_matching_test.cc
+++ b/src/cobalt/dom/rule_matching_test.cc
@@ -45,7 +45,8 @@
         dom_stat_tracker_(new DomStatTracker("RuleMatchingTest")),
         html_element_context_(NULL, css_parser_.get(), dom_parser_.get(), NULL,
                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                              NULL, NULL, dom_stat_tracker_.get(), ""),
+                              NULL, NULL, dom_stat_tracker_.get(), "",
+                              base::kApplicationStateStarted),
         document_(new Document(&html_element_context_)),
         root_(document_->CreateElement("html")->AsHTMLElement()) {
     document_->AppendChild(root_);
diff --git a/src/cobalt/dom/serializer_test.cc b/src/cobalt/dom/serializer_test.cc
index 625e4cc..60cb8e0 100644
--- a/src/cobalt/dom/serializer_test.cc
+++ b/src/cobalt/dom/serializer_test.cc
@@ -46,7 +46,8 @@
       dom_stat_tracker_(new DomStatTracker("SerializerTest")),
       html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                             NULL, NULL, NULL, NULL, NULL, NULL,
-                            dom_stat_tracker_.get(), ""),
+                            dom_stat_tracker_.get(), "",
+                            base::kApplicationStateStarted),
       document_(new Document(&html_element_context_)),
       root_(new Element(document_, base::Token("root"))),
       source_location_(base::SourceLocation("[object SerializerTest]", 1, 1)) {}
diff --git a/src/cobalt/dom/testing/stub_window.h b/src/cobalt/dom/testing/stub_window.h
index ffe7834..39f3115 100644
--- a/src/cobalt/dom/testing/stub_window.h
+++ b/src/cobalt/dom/testing/stub_window.h
@@ -52,9 +52,9 @@
     engine_ = script::JavaScriptEngine::CreateEngine();
     global_environment_ = engine_->CreateGlobalEnvironment();
     window_ = new dom::Window(
-        1920, 1080, css_parser_.get(), dom_parser_.get(),
-        fetcher_factory_.get(), NULL, NULL, NULL, NULL, NULL, NULL,
-        &local_storage_database_, stub_media_module_.get(),
+        1920, 1080, base::kApplicationStateStarted, css_parser_.get(),
+        dom_parser_.get(), fetcher_factory_.get(), NULL, NULL, NULL, NULL, NULL,
+        NULL, &local_storage_database_, stub_media_module_.get(),
         stub_media_module_.get(), NULL, NULL, NULL, NULL,
         dom_stat_tracker_.get(), url_, "", "en-US",
         base::Callback<void(const GURL&)>(), base::Bind(&StubErrorCallback),
@@ -63,8 +63,7 @@
         base::Closure() /* csp_policy_changed */,
         base::Closure() /* ran_animation_frame_callbacks */,
         base::Closure() /* window_close */,
-        base::Closure() /* window_minimize */,
-        NULL, NULL, NULL);
+        base::Closure() /* window_minimize */, NULL, NULL, NULL);
     global_environment_->CreateGlobalObject(window_, &environment_settings_);
   }
 
diff --git a/src/cobalt/dom/text.h b/src/cobalt/dom/text.h
index 4e00491..37314f3 100644
--- a/src/cobalt/dom/text.h
+++ b/src/cobalt/dom/text.h
@@ -41,7 +41,7 @@
 
   // Custom, not in any spec: Node.
   //
-  scoped_refptr<Text> AsText() OVERRIDE { return this; }
+  Text* AsText() OVERRIDE { return this; }
 
   void Accept(NodeVisitor* visitor) OVERRIDE;
   void Accept(ConstNodeVisitor* visitor) const OVERRIDE;
diff --git a/src/cobalt/dom/ui_event.cc b/src/cobalt/dom/ui_event.cc
index 2b4d274..aa0a0e4 100644
--- a/src/cobalt/dom/ui_event.cc
+++ b/src/cobalt/dom/ui_event.cc
@@ -20,23 +20,36 @@
 namespace cobalt {
 namespace dom {
 
-UIEvent::UIEvent(const std::string& type) : Event(type) {}
+UIEvent::UIEvent(const std::string& type) : Event(type), detail_(0) {}
+UIEvent::UIEvent(const std::string& type, const UIEventInit& init_dict)
+    : Event(type, init_dict),
+      view_(init_dict.view()),
+      detail_(init_dict.detail()),
+      which_(init_dict.which()) {}
 
 UIEvent::UIEvent(UninitializedFlag uninitialized_flag)
-    : Event(uninitialized_flag) {}
+    : Event(uninitialized_flag), detail_(0), which_(0) {}
 
-UIEvent::UIEvent(base::Token type, Bubbles bubbles, Cancelable cancelable)
-    : Event(type, bubbles, cancelable) {}
+UIEvent::UIEvent(base::Token type, Bubbles bubbles, Cancelable cancelable,
+                 const scoped_refptr<Window>& view)
+    : Event(type, bubbles, cancelable), view_(view), detail_(0), which_(0) {}
 
 void UIEvent::InitUIEvent(const std::string& type, bool bubbles,
                           bool cancelable, const scoped_refptr<Window>& view,
                           int32 detail) {
-  UNREFERENCED_PARAMETER(detail);
   InitEvent(type, bubbles, cancelable);
   view_ = view;
+  detail_ = detail;
 }
 
-UIEvent::UIEvent(base::Token type) : Event(type) {}
+UIEvent::UIEvent(base::Token type) : Event(type), detail_(0), which_(0) {}
+UIEvent::UIEvent(base::Token type, Bubbles bubbles, Cancelable cancelable,
+                 const scoped_refptr<Window>& view,
+                 const UIEventInit& init_dict)
+    : Event(type, bubbles, cancelable),
+      view_(view),
+      detail_(init_dict.detail()),
+      which_(init_dict.which()) {}
 
 }  // namespace dom
 }  // namespace cobalt
diff --git a/src/cobalt/dom/ui_event.h b/src/cobalt/dom/ui_event.h
index 6659231..4bba136 100644
--- a/src/cobalt/dom/ui_event.h
+++ b/src/cobalt/dom/ui_event.h
@@ -20,6 +20,7 @@
 #include "base/string_piece.h"
 #include "cobalt/dom/document.h"
 #include "cobalt/dom/event.h"
+#include "cobalt/dom/ui_event_init.h"
 #include "cobalt/dom/window.h"
 #include "cobalt/script/wrappable.h"
 
@@ -28,15 +29,17 @@
 
 // The UIEvent provides specific contextual information associated with User
 // Interface events.
-//   https://www.w3.org/TR/DOM-Level-3-Events/#events-uievents
+//   https://www.w3.org/TR/2016/WD-uievents-20160804/#events-uievents
 class UIEvent : public Event {
  public:
   explicit UIEvent(const std::string& type);
+  UIEvent(const std::string& type, const UIEventInit& init_dict);
 
   // Creates an event with its "initialized flag" unset.
   explicit UIEvent(UninitializedFlag uninitialized_flag);
 
-  UIEvent(base::Token type, Bubbles bubbles, Cancelable cancelable);
+  UIEvent(base::Token type, Bubbles bubbles, Cancelable cancelable,
+          const scoped_refptr<Window>& view);
 
   // Web API: UIEvent
   //
@@ -44,20 +47,27 @@
                    const scoped_refptr<Window>& view, int32 detail);
 
   const scoped_refptr<Window>& view() const { return view_; }
+  int32 detail() const { return detail_; }
+
   // The following properties are defined inside UIEvent but are not valid for
   // all UIEvent subtypes.  Subtypes should override the getters when necessary.
   virtual int32 page_x() const { return 0; }
   virtual int32 page_y() const { return 0; }
-  virtual uint32 which() const { return 0; }
+  virtual uint32 which() const { return which_; }
 
   DEFINE_WRAPPABLE_TYPE(UIEvent);
 
  protected:
   explicit UIEvent(base::Token type);
+  explicit UIEvent(base::Token type, Bubbles bubbles, Cancelable cancelable,
+                   const scoped_refptr<Window>& view,
+                   const UIEventInit& init_dict);
 
   ~UIEvent() OVERRIDE {}
 
   scoped_refptr<Window> view_;
+  int32 detail_;
+  uint32 which_;
 };
 
 }  // namespace dom
diff --git a/src/cobalt/dom/ui_event.idl b/src/cobalt/dom/ui_event.idl
index aeb0ad6..4a8e0aa 100644
--- a/src/cobalt/dom/ui_event.idl
+++ b/src/cobalt/dom/ui_event.idl
@@ -14,11 +14,10 @@
 
 // https://www.w3.org/TR/2015/WD-uievents-20151215/#interface-UIEvent
 
-[Constructor(DOMString type)]
+[Constructor(DOMString type, optional UIEventInit eventInitDict)]
 interface UIEvent : Event {
-  void initUIEvent(DOMString type, boolean bubbles, boolean cancelable,
-                   Window? view, long detail);
   readonly attribute Window? view;
+  readonly attribute long detail;
 
   // Note that the following properties are not part of any specification but
   // are commonly implemented and required by Cobalt.
@@ -29,4 +28,10 @@
   // The numeric keyCode of the key pressed, or the character code (charCode)
   // for an alphanumeric key pressed.
   readonly attribute unsigned long which;
+
+  void initUIEvent(DOMString type,
+                   optional boolean bubbles = false,
+                   optional boolean cancelable = false,
+                   optional Window? view = null,
+                   optional long detail = 0);
 };
diff --git a/src/cobalt/dom/ui_event_init.idl b/src/cobalt/dom/ui_event_init.idl
new file mode 100644
index 0000000..9b6204d
--- /dev/null
+++ b/src/cobalt/dom/ui_event_init.idl
@@ -0,0 +1,22 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// https://www.w3.org/TR/uievents/#dictdef-uieventinit-uieventinit
+// https://w3c.github.io/uievents/#legacy-dictionary-UIEventInit
+
+dictionary UIEventInit : EventInit {
+  Window? view = null;
+  long detail = 0;
+  unsigned long which = 0;
+};
diff --git a/src/cobalt/dom/ui_event_with_key_state.cc b/src/cobalt/dom/ui_event_with_key_state.cc
index 16fc64c..7a5c9bf 100644
--- a/src/cobalt/dom/ui_event_with_key_state.cc
+++ b/src/cobalt/dom/ui_event_with_key_state.cc
@@ -14,20 +14,54 @@
 
 #include "cobalt/dom/ui_event_with_key_state.h"
 
+#include <vector>
+
+#include "base/string_split.h"
 #include "cobalt/system_window/input_event.h"
 
 namespace cobalt {
 namespace dom {
 
-// For simplicity, we make sure these match, to avoid doing a conversion.
-COMPILE_ASSERT(
-    UIEventWithKeyState::kNoModifier ==
-            system_window::InputEvent::kNoModifier &&
-        UIEventWithKeyState::kAltKey == system_window::InputEvent::kAltKey &&
-        UIEventWithKeyState::kCtrlKey == system_window::InputEvent::kCtrlKey &&
-        UIEventWithKeyState::kMetaKey == system_window::InputEvent::kMetaKey &&
-        UIEventWithKeyState::kShiftKey == system_window::InputEvent::kShiftKey,
-    Mismatched_modifier_enums);
+void UIEventWithKeyState::InitUIEventWithKeyState(
+    const std::string& type, bool bubbles, bool cancelable,
+    const scoped_refptr<Window>& view, int32 detail,
+    const std::string& modifierslist) {
+  InitUIEvent(type, bubbles, cancelable, view, detail);
+
+  ctrl_key_ = false;
+  shift_key_ = false;
+  alt_key_ = false;
+  meta_key_ = false;
+
+  std::vector<std::string> modifiers;
+  base::SplitStringAlongWhitespace(modifierslist, &modifiers);
+
+  for (std::vector<std::string>::const_iterator it = modifiers.begin();
+       it != modifiers.end(); ++it) {
+    const std::string& modifier = *it;
+    if (modifier == "Alt") {
+      alt_key_ = true;
+    } else if (modifier == "Control") {
+      ctrl_key_ = true;
+    } else if (modifier == "Meta") {
+      meta_key_ = true;
+    } else if (modifier == "Shift") {
+      shift_key_ = true;
+    }
+  }
+}
+
+void UIEventWithKeyState::InitUIEventWithKeyState(
+    const std::string& type, bool bubbles, bool cancelable,
+    const scoped_refptr<Window>& view, int32 detail, bool ctrl_key,
+    bool alt_key, bool shift_key, bool meta_key) {
+  InitUIEvent(type, bubbles, cancelable, view, detail);
+
+  ctrl_key_ = ctrl_key;
+  shift_key_ = shift_key;
+  alt_key_ = alt_key;
+  meta_key_ = meta_key;
+}
 
 bool UIEventWithKeyState::GetModifierState(const std::string& keyArg) const {
   // Standard names of modifier keys defined here:
@@ -41,7 +75,6 @@
   } else if (keyArg == "Shift") {
     return shift_key();
   }
-
   return false;
 }
 
diff --git a/src/cobalt/dom/ui_event_with_key_state.h b/src/cobalt/dom/ui_event_with_key_state.h
index c9055bc..1fbc87c 100644
--- a/src/cobalt/dom/ui_event_with_key_state.h
+++ b/src/cobalt/dom/ui_event_with_key_state.h
@@ -17,7 +17,9 @@
 
 #include <string>
 
+#include "cobalt/dom/event_modifier_init.h"
 #include "cobalt/dom/ui_event.h"
+#include "cobalt/dom/ui_event_init.h"
 
 namespace cobalt {
 namespace dom {
@@ -28,30 +30,55 @@
  public:
   // Web API: KeyboardEvent, MouseEvent
   //
-  static const uint32 kNoModifier = 0;
-  static const uint32 kAltKey = 1 << 0;
-  static const uint32 kCtrlKey = 1 << 1;
-  static const uint32 kMetaKey = 1 << 2;
-  static const uint32 kShiftKey = 1 << 3;
+  bool ctrl_key() const { return ctrl_key_; }
+  bool shift_key() const { return shift_key_; }
+  bool alt_key() const { return alt_key_; }
+  bool meta_key() const { return meta_key_; }
 
-  uint32 modifiers() const { return modifiers_; }
-  void set_modifiers(uint32 modifiers) { modifiers_ = modifiers; }
-
-  bool alt_key() const { return (modifiers_ & kAltKey) != 0; }
-  bool ctrl_key() const { return (modifiers_ & kCtrlKey) != 0; }
-  bool meta_key() const { return (modifiers_ & kMetaKey) != 0; }
-  bool shift_key() const { return (modifiers_ & kShiftKey) != 0; }
+  void set_ctrl_key(bool value) { ctrl_key_ = value; }
+  void set_shift_key(bool value) { shift_key_ = value; }
+  void set_alt_key(bool value) { alt_key_ = value; }
+  void set_meta_key(bool value) { meta_key_ = value; }
 
   bool GetModifierState(const std::string& keyArg) const;
 
  protected:
   UIEventWithKeyState(base::Token type, Bubbles bubbles, Cancelable cancelable,
-                      uint32 modifiers)
-      : UIEvent(type, bubbles, cancelable), modifiers_(modifiers) {}
+                      const scoped_refptr<Window>& view)
+      : UIEvent(type, bubbles, cancelable, view),
+        ctrl_key_(false),
+        shift_key_(false),
+        alt_key_(false),
+        meta_key_(false) {}
+  UIEventWithKeyState(base::Token type, Bubbles bubbles, Cancelable cancelable,
+                      const scoped_refptr<Window>& view,
+                      const EventModifierInit& init_dict)
+      : UIEvent(type, bubbles, cancelable, view, init_dict) {
+    ctrl_key_ = init_dict.ctrl_key();
+    shift_key_ = init_dict.shift_key();
+    alt_key_ = init_dict.alt_key();
+    meta_key_ = init_dict.meta_key();
+  }
 
+  // Creates an event with its "initialized flag" unset.
+  explicit UIEventWithKeyState(UninitializedFlag uninitialized_flag)
+      : UIEvent(uninitialized_flag) {}
+
+  void InitUIEventWithKeyState(const std::string& type, bool bubbles,
+                               bool cancelable,
+                               const scoped_refptr<Window>& view, int32 detail,
+                               const std::string& modifierslist);
+  void InitUIEventWithKeyState(const std::string& type, bool bubbles,
+                               bool cancelable,
+                               const scoped_refptr<Window>& view, int32 detail,
+                               bool ctrl_key, bool alt_key, bool shift_key,
+                               bool meta_key);
   ~UIEventWithKeyState() OVERRIDE {}
 
-  uint32 modifiers_;
+  bool ctrl_key_;
+  bool shift_key_;
+  bool alt_key_;
+  bool meta_key_;
 };
 
 }  // namespace dom
diff --git a/src/cobalt/dom/wheel_event.cc b/src/cobalt/dom/wheel_event.cc
new file mode 100644
index 0000000..7f991fe
--- /dev/null
+++ b/src/cobalt/dom/wheel_event.cc
@@ -0,0 +1,61 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/dom/wheel_event.h"
+
+#include <string>
+
+#include "cobalt/dom/ui_event_with_key_state.h"
+
+namespace cobalt {
+namespace dom {
+
+WheelEvent::WheelEvent(const std::string& type)
+    : MouseEvent(type),
+      delta_x_(0),
+      delta_y_(0),
+      delta_z_(0),
+      delta_mode_(kDomDeltaPixel) {}
+WheelEvent::WheelEvent(const std::string& type, const WheelEventInit& init_dict)
+    : MouseEvent(type, init_dict),
+      delta_x_(init_dict.delta_x()),
+      delta_y_(init_dict.delta_y()),
+      delta_z_(init_dict.delta_z()),
+      delta_mode_(init_dict.delta_mode()) {}
+
+WheelEvent::WheelEvent(base::Token type, const scoped_refptr<Window>& view,
+                       const WheelEventInit& init_dict)
+    : MouseEvent(type, kBubbles, kCancelable, view, init_dict),
+      delta_x_(init_dict.delta_x()),
+      delta_y_(init_dict.delta_y()),
+      delta_z_(init_dict.delta_z()),
+      delta_mode_(init_dict.delta_mode()) {}
+
+void WheelEvent::InitWheelEvent(
+    const std::string& type, bool bubbles, bool cancelable,
+    const scoped_refptr<Window>& view, int32 detail, int32 screen_x,
+    int32 screen_y, int32 client_x, int32 client_y, uint16 button,
+    const scoped_refptr<EventTarget>& related_target,
+    const std::string& modifierslist, double delta_x, double delta_y,
+    double delta_z, uint32 delta_mode) {
+  InitMouseEvent(type, bubbles, cancelable, view, detail, screen_x, screen_y,
+                 client_x, client_y, modifierslist, button, related_target);
+  delta_x_ = delta_x;
+  delta_y_ = delta_y;
+  delta_z_ = delta_z;
+  delta_mode_ = delta_mode;
+}
+
+}  // namespace dom
+}  // namespace cobalt
diff --git a/src/cobalt/dom/wheel_event.h b/src/cobalt/dom/wheel_event.h
new file mode 100644
index 0000000..a421c84
--- /dev/null
+++ b/src/cobalt/dom/wheel_event.h
@@ -0,0 +1,76 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_DOM_WHEEL_EVENT_H_
+#define COBALT_DOM_WHEEL_EVENT_H_
+
+#include <string>
+
+#include "cobalt/base/token.h"
+#include "cobalt/dom/mouse_event.h"
+#include "cobalt/dom/wheel_event_init.h"
+
+namespace cobalt {
+namespace dom {
+
+// The WheelEvent provides specific contextual information associated with
+// wheel devices. Wheels are devices that can be rotated in one or more spatial
+// dimensions, and which can be associated with a pointer device
+//   https://www.w3.org/TR/2016/WD-uievents-20160804/#events-wheelevents
+class WheelEvent : public MouseEvent {
+ public:
+  // Web API: WheelEvent
+  // DeltaMode values as defined by Web API Node.nodeType.
+  typedef uint32 DeltaMode;  // Work around lack of strongly-typed enums
+                             // in C++03.
+  enum DeltaModeInternal {   // A name is given only to pacify the compiler.
+                             // Use |DeltaMode| instead.
+    kDomDeltaPixel = 0x00,
+    kDomDeltaLine = 0x01,
+    kDomDeltaPage = 0x02,
+  };
+
+  explicit WheelEvent(const std::string& type);
+  WheelEvent(const std::string& type, const WheelEventInit& init_dict);
+  WheelEvent(base::Token type, const scoped_refptr<Window>& view,
+             const WheelEventInit& init_dict);
+
+  double delta_x() const { return delta_x_; }
+  double delta_y() const { return delta_y_; }
+  double delta_z() const { return delta_z_; }
+  DeltaMode delta_mode() const { return delta_mode_; }
+
+  void InitWheelEvent(const std::string& type, bool bubbles, bool cancelable,
+                      const scoped_refptr<Window>& view, int32 detail,
+                      int32 screen_x, int32 screen_y, int32 client_x,
+                      int32 client_y, uint16 button,
+                      const scoped_refptr<EventTarget>& related_target,
+                      const std::string& modifierslist, double delta_x,
+                      double delta_y, double delta_z, uint32 delta_mode);
+
+  DEFINE_WRAPPABLE_TYPE(WheelEvent);
+
+ private:
+  double delta_x_;
+  double delta_y_;
+  double delta_z_;
+  DeltaMode delta_mode_;
+
+  ~WheelEvent() OVERRIDE {}
+};
+
+}  // namespace dom
+}  // namespace cobalt
+
+#endif  // COBALT_DOM_WHEEL_EVENT_H_
diff --git a/src/cobalt/dom/wheel_event.idl b/src/cobalt/dom/wheel_event.idl
new file mode 100644
index 0000000..c24ead0
--- /dev/null
+++ b/src/cobalt/dom/wheel_event.idl
@@ -0,0 +1,46 @@
+// 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.
+
+// https://www.w3.org/TR/2016/WD-uievents-20160804/#interface-wheelevent
+// https://www.w3.org/TR/2016/WD-uievents-20160804/#idl-interface-WheelEvent-initializers
+
+[Constructor(DOMString type, optional WheelEventInit eventInitDict)]
+interface WheelEvent : MouseEvent {
+  // DeltaModeCode
+  const unsigned long DOM_DELTA_PIXEL = 0x00;
+  const unsigned long DOM_DELTA_LINE  = 0x01;
+  const unsigned long DOM_DELTA_PAGE  = 0x02;
+
+  readonly attribute double deltaX;
+  readonly attribute double deltaY;
+  readonly attribute double deltaZ;
+  readonly attribute unsigned long deltaMode;
+
+  void initWheelEvent(DOMString type,
+                   optional boolean bubbles = false,
+                   optional boolean cancelable = false,
+                   optional Window? view = null,
+                   optional long detail = 0,
+                   optional long screenX = 0,
+                   optional long screenY = 0,
+                   optional long clientX = 0,
+                   optional long clientY = 0,
+                   optional unsigned short button = 0,
+                   optional EventTarget? relatedTarget = null,
+                   optional DOMString modifiersList = "",
+                   optional double deltaX = 0,
+                   optional double deltaY = 0,
+                   optional double deltaZ = 0,
+                   optional unsigned long deltaMode = 0);
+};
\ No newline at end of file
diff --git a/src/cobalt/dom/wheel_event_init.idl b/src/cobalt/dom/wheel_event_init.idl
new file mode 100644
index 0000000..20451a5
--- /dev/null
+++ b/src/cobalt/dom/wheel_event_init.idl
@@ -0,0 +1,22 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// https://www.w3.org/TR/uievents/#dictdef-wheeleventinit
+
+dictionary WheelEventInit : MouseEventInit {
+  double deltaX = 0.0;
+  double deltaY = 0.0;
+  double deltaZ = 0.0;
+  unsigned long deltaMode = 0;
+};
diff --git a/src/cobalt/dom/window.cc b/src/cobalt/dom/window.cc
index 554ce06..7a089d4 100644
--- a/src/cobalt/dom/window.cc
+++ b/src/cobalt/dom/window.cc
@@ -24,6 +24,7 @@
 #include "cobalt/cssom/user_agent_style_sheet.h"
 #include "cobalt/dom/camera_3d.h"
 #include "cobalt/dom/console.h"
+#include "cobalt/dom/device_orientation_event.h"
 #include "cobalt/dom/document.h"
 #include "cobalt/dom/dom_settings.h"
 #include "cobalt/dom/element.h"
@@ -31,13 +32,17 @@
 #include "cobalt/dom/history.h"
 #include "cobalt/dom/html_element.h"
 #include "cobalt/dom/html_element_context.h"
+#include "cobalt/dom/keyboard_event.h"
 #include "cobalt/dom/location.h"
 #include "cobalt/dom/media_source.h"
+#include "cobalt/dom/mouse_event.h"
 #include "cobalt/dom/mutation_observer_task_manager.h"
 #include "cobalt/dom/navigator.h"
 #include "cobalt/dom/performance.h"
+#include "cobalt/dom/pointer_event.h"
 #include "cobalt/dom/screen.h"
 #include "cobalt/dom/storage.h"
+#include "cobalt/dom/wheel_event.h"
 #include "cobalt/dom/window_timers.h"
 #include "cobalt/media_session/media_session_client.h"
 #include "cobalt/script/javascript_engine.h"
@@ -66,8 +71,10 @@
   DISALLOW_COPY_AND_ASSIGN(RelayLoadEvent);
 };
 
-Window::Window(int width, int height, cssom::CSSParser* css_parser,
-               Parser* dom_parser, loader::FetcherFactory* fetcher_factory,
+Window::Window(int width, int height,
+               base::ApplicationState initial_application_state,
+               cssom::CSSParser* css_parser, Parser* dom_parser,
+               loader::FetcherFactory* fetcher_factory,
                render_tree::ResourceProvider** resource_provider,
                loader::image::AnimatedImageTracker* animated_image_tracker,
                loader::image::ImageCache* image_cache,
@@ -107,7 +114,7 @@
           media_source_registry, resource_provider, animated_image_tracker,
           image_cache, reduced_image_cache_capacity_manager,
           remote_typeface_cache, mesh_cache, dom_stat_tracker, language,
-          video_playback_rate_multiplier)),
+          initial_application_state, video_playback_rate_multiplier)),
       performance_(new Performance(new base::SystemMonotonicClock())),
       ALLOW_THIS_IN_INITIALIZER_LIST(document_(new Document(
           html_element_context_.get(),
@@ -147,6 +154,7 @@
   test_runner_ = new TestRunner();
 #endif  // ENABLE_TEST_RUNNER
   document_->AddObserver(relay_on_load_event_.get());
+  html_element_context_->page_visibility_state()->AddObserver(this);
 
   if (system_window_) {
     SbWindow sb_window = system_window_->GetSbWindow();
@@ -166,6 +174,7 @@
   MessageLoop::current()->PostTask(
       FROM_HERE, base::Bind(&Window::StartDocumentLoad, this, fetcher_factory,
                             url, dom_parser, error_callback));
+  camera_3d_->StartOrientationEvents(base::AsWeakPtr(this));
 }
 
 void Window::StartDocumentLoad(
@@ -399,29 +408,45 @@
 
 void Window::InjectEvent(const scoped_refptr<Event>& event) {
   // Forward the event on to the correct object in DOM.
-  if (event->type() == base::Tokens::keydown() ||
-      event->type() == base::Tokens::keypress() ||
-      event->type() == base::Tokens::keyup()) {
+  if (event->GetWrappableType() == base::GetTypeId<KeyboardEvent>()) {
     // Event.target:focused element processing the key event or if no element
     // focused, then the body element if available, otherwise the root element.
-    //   https://www.w3.org/TR/DOM-Level-3-Events/#event-type-keydown
-    //   https://www.w3.org/TR/DOM-Level-3-Events/#event-type-keypress
-    //   https://www.w3.org/TR/DOM-Level-3-Events/#event-type-keyup
+    //   https://www.w3.org/TR/2016/WD-uievents-20160804/#event-type-keydown
+    //   https://www.w3.org/TR/2016/WD-uievents-20160804/#event-type-keypress
+    //   https://www.w3.org/TR/2016/WD-uievents-20160804/#event-type-keyup
     if (document_->active_element()) {
       document_->active_element()->DispatchEvent(event);
     } else {
       document_->DispatchEvent(event);
     }
+  } else if (event->GetWrappableType() == base::GetTypeId<PointerEvent>() ||
+             event->GetWrappableType() == base::GetTypeId<MouseEvent>() ||
+             event->GetWrappableType() == base::GetTypeId<WheelEvent>()) {
+    document_->QueuePointerEvent(event);
   } else {
-    NOTREACHED();
+    SB_NOTREACHED();
   }
 }
 
+void Window::SetApplicationState(base::ApplicationState state) {
+  html_element_context_->page_visibility_state()->SetApplicationState(state);
+}
+
 void Window::SetSynchronousLayoutCallback(
     const base::Closure& synchronous_layout_callback) {
   document_->set_synchronous_layout_callback(synchronous_layout_callback);
 }
 
+void Window::OnWindowFocusChanged(bool has_focus) {
+  DispatchEvent(
+      new Event(has_focus ? base::Tokens::focus() : base::Tokens::blur()));
+}
+
+void Window::OnVisibilityStateChanged(
+    page_visibility::VisibilityState visibility_state) {
+  UNREFERENCED_PARAMETER(visibility_state);
+}
+
 void Window::TraceMembers(script::Tracer* tracer) {
   tracer->Trace(performance_);
   tracer->Trace(document_);
@@ -436,7 +461,9 @@
   tracer->Trace(screen_);
 }
 
-Window::~Window() {}
+Window::~Window() {
+  html_element_context_->page_visibility_state()->RemoveObserver(this);
+}
 
 void Window::FireHashChangeEvent() {
   PostToDispatchEvent(FROM_HERE, base::Tokens::hashchange());
diff --git a/src/cobalt/dom/window.h b/src/cobalt/dom/window.h
index 0fc144b..c405a0e 100644
--- a/src/cobalt/dom/window.h
+++ b/src/cobalt/dom/window.h
@@ -22,6 +22,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/timer.h"
+#include "cobalt/base/application_state.h"
 #include "cobalt/cssom/css_parser.h"
 #include "cobalt/cssom/css_style_declaration.h"
 #include "cobalt/dom/animation_frame_request_callback_list.h"
@@ -31,12 +32,10 @@
 #include "cobalt/dom/event_target.h"
 #include "cobalt/dom/media_query_list.h"
 #include "cobalt/dom/parser.h"
-#include "cobalt/dom/url_registry.h"
-#include "cobalt/network_bridge/cookie_jar.h"
-#include "cobalt/network_bridge/net_poster.h"
 #if defined(ENABLE_TEST_RUNNER)
 #include "cobalt/dom/test_runner.h"
 #endif  // ENABLE_TEST_RUNNER
+#include "cobalt/dom/url_registry.h"
 #include "cobalt/dom/window_timers.h"
 #include "cobalt/input/camera_3d.h"
 #include "cobalt/loader/decoder.h"
@@ -48,6 +47,9 @@
 #include "cobalt/loader/mesh/mesh_cache.h"
 #include "cobalt/media/can_play_type_handler.h"
 #include "cobalt/media/web_media_player_factory.h"
+#include "cobalt/network_bridge/cookie_jar.h"
+#include "cobalt/network_bridge/net_poster.h"
+#include "cobalt/page_visibility/page_visibility_state.h"
 #include "cobalt/script/callback_function.h"
 #include "cobalt/script/environment_settings.h"
 #include "cobalt/script/execution_state.h"
@@ -89,7 +91,8 @@
 //   https://www.w3.org/TR/html5/browsers.html#the-window-object
 //
 // TODO: Properly handle viewport resolution change event.
-class Window : public EventTarget {
+class Window : public EventTarget,
+               public page_visibility::PageVisibilityState::Observer {
  public:
   typedef AnimationFrameRequestCallbackList::FrameRequestCallback
       FrameRequestCallback;
@@ -102,7 +105,8 @@
   typedef UrlRegistry<MediaSource> MediaSourceRegistry;
 
   Window(
-      int width, int height, cssom::CSSParser* css_parser, Parser* dom_parser,
+      int width, int height, base::ApplicationState initial_application_state,
+      cssom::CSSParser* css_parser, Parser* dom_parser,
       loader::FetcherFactory* fetcher_factory,
       render_tree::ResourceProvider** resource_provider,
       loader::image::AnimatedImageTracker* animated_image_tracker,
@@ -279,6 +283,16 @@
   void SetSynchronousLayoutCallback(
       const base::Closure& synchronous_layout_callback);
 
+  // Sets the current application state, forwarding on to the
+  // PageVisibilityState associated with it and its document, causing
+  // precipitate events to be dispatched.
+  void SetApplicationState(base::ApplicationState state);
+
+  // page_visibility::PageVisibilityState::Observer implementation.
+  void OnWindowFocusChanged(bool has_focus) OVERRIDE;
+  void OnVisibilityStateChanged(
+      page_visibility::VisibilityState visibility_state) OVERRIDE;
+
   void TraceMembers(script::Tracer* tracer) OVERRIDE;
 
   DEFINE_WRAPPABLE_TYPE(Window);
diff --git a/src/cobalt/dom/window_test.cc b/src/cobalt/dom/window_test.cc
index f087c28..5fa0db3 100644
--- a/src/cobalt/dom/window_test.cc
+++ b/src/cobalt/dom/window_test.cc
@@ -51,9 +51,9 @@
         stub_media_module_(new media::MediaModuleStub()),
         url_("about:blank"),
         window_(new Window(
-            1920, 1080, css_parser_.get(), dom_parser_.get(),
-            fetcher_factory_.get(), NULL, NULL, NULL, NULL, NULL, NULL,
-            &local_storage_database_, stub_media_module_.get(),
+            1920, 1080, base::kApplicationStateStarted, css_parser_.get(),
+            dom_parser_.get(), fetcher_factory_.get(), NULL, NULL, NULL, NULL,
+            NULL, NULL, &local_storage_database_, stub_media_module_.get(),
             stub_media_module_.get(), NULL, NULL, NULL, NULL, NULL, url_, "",
             "en-US", base::Callback<void(const GURL &)>(),
             base::Bind(&MockErrorCallback::Run,
diff --git a/src/cobalt/dom/window_timers.cc b/src/cobalt/dom/window_timers.cc
index 792ae47..265d0db 100644
--- a/src/cobalt/dom/window_timers.cc
+++ b/src/cobalt/dom/window_timers.cc
@@ -18,6 +18,8 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/debug/trace_event.h"
+#include "cobalt/dom/global_stats.h"
 #include "nb/memory_scope.h"
 
 namespace cobalt {
@@ -85,8 +87,13 @@
 }
 
 void WindowTimers::RunTimerCallback(int handle) {
+  TRACE_EVENT0("cobalt::dom", "WindowTimers::RunTimerCallback");
   Timers::iterator timer = timers_.find(handle);
   DCHECK(timer != timers_.end());
+
+  // The callback is now being run. Track it in the global stats.
+  GlobalStats::GetInstance()->StartJavaScriptEvent();
+
   // Keep a |TimerInfo| reference, so it won't be released when running the
   // callback.
   scoped_refptr<TimerInfo> timer_info = timer->second;
@@ -99,6 +106,9 @@
   if (timer != timers_.end() && !timer->second->timer()->IsRunning()) {
     timers_.erase(timer);
   }
+
+  // The callback has finished running. Stop tracking it in the global stats.
+  GlobalStats::GetInstance()->StopJavaScriptEvent();
 }
 
 }  // namespace dom
diff --git a/src/cobalt/dom_parser/dom_parser.gyp b/src/cobalt/dom_parser/dom_parser.gyp
index f64e8d9..99775c7 100644
--- a/src/cobalt/dom_parser/dom_parser.gyp
+++ b/src/cobalt/dom_parser/dom_parser.gyp
@@ -14,7 +14,7 @@
 
 {
   'variables': {
-    'cobalt_code': 1,
+    'sb_pedantic_warnings': 1,
   },
   'targets': [
     {
diff --git a/src/cobalt/dom_parser/html_decoder_test.cc b/src/cobalt/dom_parser/html_decoder_test.cc
index 367607d..d4e89bb 100644
--- a/src/cobalt/dom_parser/html_decoder_test.cc
+++ b/src/cobalt/dom_parser/html_decoder_test.cc
@@ -68,9 +68,9 @@
           &fetcher_factory_, &stub_css_parser_, dom_parser_.get(),
           NULL /* can_play_type_handler */, NULL /* web_media_player_factory */,
           &stub_script_runner_, NULL /* script_value_factory */, NULL, NULL,
-          NULL, NULL, NULL, NULL, NULL, dom_stat_tracker_.get(), ""),
-      document_(
-          new dom::Document(&html_element_context_, dom::Document::Options())),
+          NULL, NULL, NULL, NULL, NULL, dom_stat_tracker_.get(), "",
+          base::kApplicationStateStarted),
+      document_(new dom::Document(&html_element_context_)),
       root_(new dom::Element(document_, base::Token("element"))),
       source_location_(base::SourceLocation("[object HTMLDecoderTest]", 1, 1)) {
 }
diff --git a/src/cobalt/dom_parser/libxml_parser_wrapper.cc b/src/cobalt/dom_parser/libxml_parser_wrapper.cc
index 5b7e6bc..7d2fbd9 100644
--- a/src/cobalt/dom_parser/libxml_parser_wrapper.cc
+++ b/src/cobalt/dom_parser/libxml_parser_wrapper.cc
@@ -215,6 +215,11 @@
   if (node_stack_.empty()) {
     LOG(WARNING) << "OnEndDocument is called without OnStartDocument.";
   } else {
+    while (parent_node_ != node_stack_.top()) {
+      LOG(WARNING) << "some elements did not get called on OnEndElement()";
+      node_stack_.pop();
+    }
+    DCHECK_GT(node_stack_.size(), static_cast<uint64_t>(0));
     DCHECK_EQ(parent_node_, node_stack_.top());
     node_stack_.pop();
   }
diff --git a/src/cobalt/dom_parser/parser.cc b/src/cobalt/dom_parser/parser.cc
index be0da9c..03150ea 100644
--- a/src/cobalt/dom_parser/parser.cc
+++ b/src/cobalt/dom_parser/parser.cc
@@ -28,7 +28,7 @@
     const base::SourceLocation& input_location) {
   DCHECK(html_element_context);
   scoped_refptr<dom::Document> document =
-      new dom::Document(html_element_context, dom::Document::Options());
+      new dom::Document(html_element_context);
   HTMLDecoder html_decoder(document, document, NULL, dom_max_element_depth_,
                            input_location, base::Closure(), error_callback_,
                            false);
diff --git a/src/cobalt/fetch/embedded_scripts/fetch.js b/src/cobalt/fetch/embedded_scripts/fetch.js
index 48db9cd..b602982 100644
--- a/src/cobalt/fetch/embedded_scripts/fetch.js
+++ b/src/cobalt/fetch/embedded_scripts/fetch.js
@@ -1,14 +1,20 @@
-'use strict';(function(e){function n(a){"string"!==typeof a&&(a=String(a));if(/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(a))throw new g("Invalid character in header field name");return a.toLowerCase()}function x(a){"string"!==typeof a&&(a=String(a));var b;var c=0;for(b=a.length;c<b;c++){var d=a.charCodeAt(c);if(9!==d&&10!==d&&13!==d&&32!==d)break}for(b=a.length-1;b>c&&(d=a.charCodeAt(b),9===d||10===d||13===d||32===d);b--);a=a.substring(c,b+1);c=0;for(b=a.length;c<b;c++)if(d=a.charCodeAt(c),256<=d||0===d||
-10===d||13===d)throw new g("Invalid character in header field value");return a}function f(a){this.map=new r;if(void 0!==a){if(null===a||"object"!==typeof a)throw new g("Constructing Headers with invalid parameters");a instanceof f?a.forEach(function(a,c){this.append(c,a)},this):m.isArray(a)?a.forEach(function(a){if(2!==a.length)throw new g("Constructing Headers with invalid parameters");this.append(a[0],a[1])},this):Object.getOwnPropertyNames(a).forEach(function(b){this.append(b,a[b])},this)}}function t(a){if(a.bodyUsed)return u(new g("Body already read"));
-if(null===a.body)return C(new k(0));if(D(a.body))return u(new g("ReadableStream already locked"));var b=a.body.getReader(),c=[],d=0;return b.read().then(function p(a){if(a.done){if(0===c.length)a=new k(0);else if(1===c.length)a=new k(c[0]);else{a=new k(d);for(var e=0,f=c.length,h=0;e<f;e++)a.set(c[e],h),h+=c[e].length}return a}return a.value instanceof k?(d+=a.value.length,c.push(a.value),b.read().then(p)):u(new g("Invalid stream read value type"))})}function E(a){a=unescape(encodeURIComponent(a));
-for(var b=new k(a.length),c=0,d=a.length;c<d;c++)b[c]=a.charCodeAt(c);return b}function y(){this._initBody=function(a){this._bodyUsed=!1;this.body=null===a||void 0===a?null:a instanceof v?a:new v({start:function(b){if(a)if("string"===typeof a)b.enqueue(E(a));else if(z.prototype.isPrototypeOf(a))b.enqueue(new k(a));else if(F(a))b.enqueue(new k(a.buffer));else throw new g("Unsupported BodyInit type");b.close()}});this.headers.get("content-type")||"string"===typeof a&&this.headers.set("content-type",
-"text/plain;charset=UTF-8")};Object.defineProperty(this,"bodyUsed",{get:function(){return this._bodyUsed?!0:this.body?!!G(this.body):!1}});this.arrayBuffer=function(){return t(this).then(function(a){return a.buffer})};this.text=function(){return t(this).then(function(a){return 0===a.length?"":decodeURIComponent(escape(String.fromCharCode.apply(null,a)))})};this.json=function(){return this.text().then(JSON.parse)};return this}function q(a,b){b=b||{};var c=b.body;if(a instanceof q){if(a.bodyUsed)throw new g("Request body already read");
-this.url=a.url;this.credentials=a.credentials;b.headers||(this.headers=new f(a.headers));this.method=a.method;this.mode=a.mode;c||null===a.body||(c=a.body,a._bodyUsed=!0)}else this.url=String(a);this.credentials=b.credentials||this.credentials||"omit";if(b.headers||!this.headers)this.headers=new f(b.headers);a=b.method||this.method||"GET";var d=a.toUpperCase();this.method=-1<H.indexOf(d)?d:a;this.mode=b.mode||this.mode||null;this.referrer=null;if(("GET"===this.method||"HEAD"===this.method)&&c)throw new g("Body not allowed for GET or HEAD requests");
-this._initBody(c)}function I(a){var b=new f;a.replace(/\r?\n[\t ]+/g," ").split(/\r?\n/).forEach(function(a){var c=a.split(":");if(a=c.shift().trim())c=c.join(":").trim(),b.append(a,c)});return b}function l(a,b){b||(b={});this.type="default";this.status="status"in b?b.status:200;if(200>this.status||599<this.status)throw new A("Invalid response status");this.ok=200<=this.status&&300>this.status;if("statusText"in b){var c=b.statusText;for(var d=0,e=c.length,p;d<e;d++)if(p=c.charCodeAt(d),9!==p&&(32>
-p||255<p||127===p))throw g("Invalid status text");}else c="OK";this.statusText=c;this.headers=new f(b.headers);this.url=b.url||"";if(a&&-1<J.indexOf(this.status))throw new g("Body not allowed with a null body status");this._initBody(a)}if(!e.fetch){var m=e.Array,z=e.ArrayBuffer,K=e.Symbol.iterator,r=e.Map,A=e.RangeError,g=e.TypeError,k=e.Uint8Array,w=e.Promise,u=w.reject,C=w.resolve,v=e.ReadableStream,B=e.ReadableStreamTee,G=e.IsReadableStreamDisturbed,D=e.IsReadableStreamLocked,L="[object Int8Array];[object Uint8Array];[object Uint8ClampedArray];[object Int16Array];[object Uint16Array];[object Int32Array];[object Uint32Array];[object Float32Array];[object Float64Array]".split(";"),
-F=z.isView||function(a){return a&&-1<L.indexOf(Object.prototype.toString.call(a))};f.prototype.append=function(a,b){if(2!==arguments.length)throw g("Invalid parameters to append");a=n(a);b=x(b);this.map.has(a)?this.map.set(a,this.map.get(a)+", "+b):this.map.set(a,b)};f.prototype["delete"]=function(a){if(1!==arguments.length)throw g("Invalid parameters to delete");this.map.delete(n(a))};f.prototype.get=function(a){if(1!==arguments.length)throw g("Invalid parameters to get");a=n(a);var b=this.map.get(a);
-return void 0!==b?b:null};f.prototype.has=function(a){if(1!==arguments.length)throw g("Invalid parameters to has");return this.map.has(n(a))};f.prototype.set=function(a,b){if(2!==arguments.length)throw g("Invalid parameters to set");this.map.set(n(a),x(b))};f.prototype.forEach=function(a,b){var c=this;m.from(this.map.entries()).sort().forEach(function(d){a.call(b,d[1],d[0],c)})};f.prototype.keys=function(){return(new r(m.from(this.map.entries()).sort())).keys()};f.prototype.values=function(){return(new r(m.from(this.map.entries()).sort())).values()};
-f.prototype.entries=function(){return(new r(m.from(this.map.entries()).sort())).entries()};f.prototype[K]=f.prototype.entries;var H="DELETE GET HEAD OPTIONS POST PUT".split(" ");q.prototype.clone=function(){var a=null;null!==this.body&&(a=B(this.body,!0),this.body=a[0],a=a[1]);return new q(this,{body:a})};y.call(q.prototype);var J=[101,204,205,304];y.call(l.prototype);l.prototype.clone=function(){var a=null;null!==this.body&&(a=B(this.body,!0),this.body=a[0],a=a[1]);return new l(a,{status:this.status,
-statusText:this.statusText,headers:new f(this.headers),url:this.url})};l.error=function(){var a=new l(null);a.type="error";a.status=0;a.statusText="";return a};var M=[301,302,303,307,308];l.redirect=function(a,b){if(!FetchInternal.IsUrlValid(a))throw new g("Invalid URL");void 0===b&&(b=302);if(-1===M.indexOf(b))throw new A("Invalid status code");return new l(null,{status:b,headers:{location:a}})};e.Headers=f;e.Request=q;e.Response=l;e.fetch=function(a,b){return new w(function(c,d){var e=!1,f=new q(a,
-b),h=new XMLHttpRequest,k=null,n=new v({start:function(a){k=a},cancel:function(a){e=!0;h.abort()}});h.onload=function(){k.close()};h.onreadystatechange=function(){if(h.readyState===h.HEADERS_RECEIVED){var a={status:h.status,statusText:h.statusText,headers:I(h.getAllResponseHeaders()||"")};a.url="responseURL"in h?h.responseURL:a.headers.get("X-Request-URL");c(new l(n,a))}};h.onerror=function(){k.error(new g("Network request failed"));d(new g("Network request failed"))};h.ontimeout=function(){k.error(new g("Network request failed"));
-d(new g("Network request failed"))};h.open(f.method,f.url,!0);"include"===f.credentials&&(h.withCredentials=!0);f.headers.forEach(function(a,b){h.setRequestHeader(b,a)});var m=function(a){e||k.enqueue(a)};null===f.body?h.fetch(m,null):t(f).then(function(a){h.fetch(m,a)})})};e.fetch.polyfill=!0}})(this);
\ No newline at end of file
+'use strict';(function(d){function B(a){"string"!==typeof a&&(a=String(a));if(/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(a))throw new e("Invalid character in header field name");return a.toLowerCase()}function P(a){"string"!==typeof a&&(a=String(a));var b;var c=0;for(b=a.length;c<b;c++){var g=a.charCodeAt(c);if(9!==g&&10!==g&&13!==g&&32!==g)break}for(b=a.length-1;b>c&&(g=a.charCodeAt(b),9===g||10===g||13===g||32===g);b--);a=a.substring(c,b+1);c=0;for(b=a.length;c<b;c++)if(g=a.charCodeAt(c),256<=g||0===g||
+10===g||13===g)throw new e("Invalid character in header field value");return a}function V(a,b){throw new e("Immutable header cannot be modified");}function W(a,b){return!1}function X(a,b){a=a.toLowerCase();return-1<Y.indexOf(a)||a.startsWith("proxy-")||a.startsWith("sec-")?!0:!1}function Z(a,b){a=a.toLowerCase();return-1<aa.indexOf(a)||"content-type"===a&&(b=b.split(";")[0].toLowerCase(),-1<ba.indexOf(b))?!1:!0}function I(a,b){return-1<ca.indexOf(a.toLowerCase())?!0:!1}function h(a){this[l]=new F;
+void 0===this[t]&&(this[t]=W);if(void 0!==a){if(null===a||"object"!==typeof a)throw new e("Constructing Headers with invalid parameters");a instanceof h?a.forEach(function(a,c){this.append(c,a)},this):C.isArray(a)?a.forEach(function(a){if(2!==a.length)throw new e("Constructing Headers with invalid parameters");this.append(a[0],a[1])},this):Object.getOwnPropertyNames(a).forEach(function(b){this.append(b,a[b])},this)}}function D(a,b){var c=da(h.prototype);c[t]=b;h.call(c,a);return c}function J(a){if(a.bodyUsed)return K(new e("Body was already read"));
+if(null===a.body)return ea(new u(0));if(fa(a.body))return K(new e("ReadableStream was already locked"));var b=a.body.getReader(),c=[],g=0;return b.read().then(function ga(a){if(a.done){if(0===c.length)a=new u(0);else if(1===c.length)a=new u(c[0]);else{a=new u(g);for(var d=0,k=c.length,f=0;d<k;d++)a.set(c[d],f),f+=c[d].length}return a}return a.value instanceof u?(g+=a.value.length,c.push(a.value),b.read().then(ga)):K(new e("Invalid stream data type"))})}function Q(){this._initBody=function(a){this[L]=
+!1;this[m]=null===a||void 0===a?null:a instanceof M?a:new M({start:function(b){if(a)if("string"===typeof a)b.enqueue(FetchInternal.encodeToUTF8(a));else if(R.prototype.isPrototypeOf(a))b.enqueue(new u(a));else if(ha(a))b.enqueue(new u(a.buffer));else throw new e("Unsupported BodyInit type");b.close()}});this[r].get("content-type")||"string"===typeof a&&this[r].set("content-type","text/plain;charset=UTF-8")};N(this,{body:{get:function(){return this[m]}},bodyUsed:{get:function(){return this[L]?!0:this[m]?
+!!ia(this[m]):!1}}});this.arrayBuffer=function(){return J(this).then(function(a){return a.buffer})};this.text=function(){return J(this).then(function(a){return FetchInternal.decodeFromUTF8(a)})};this.json=function(){return this.text().then(JSON.parse)};return this}function v(a,b){var c=void 0!==b&&null!==b&&void 0===b.cloneBody;b=b||{};var g=b.body||b.cloneBody,d=b.headers;if(a instanceof v){if(a.bodyUsed)throw new e("Request body was already read");this[w]=a.url;this[y]=a.cache;this[z]=a.credentials;
+void 0===d&&(d=a.headers);this[A]=a.integrity;this[x]=a.method;this[n]=a.mode;c&&"navigate"===this[n]&&(this[n]="same-origin");this[E]=a.redirect;g||null===a.body||(g=a.body,a[L]=!0)}else{this[w]=String(a);if(!FetchInternal.isUrlValid(this[w],!1))throw new e("Invalid request URL");this[n]="cors";this[z]="omit"}if(void 0!==b.window&&null!==b.window)throw new e("Invalid request window");this[y]=b.cache||this[y]||"default";if(-1===ja.indexOf(this[y]))throw new e("Invalid request cache mode");this[z]=
+b.credentials||this[z]||"omit";if(-1===ka.indexOf(this[z]))throw new e("Invalid request credentials");void 0!==b.integrity?this[A]=b.integrity:void 0===this[A]&&(this[A]="");a=(b.method||this[x]||"GET").toUpperCase();if(-1===la.indexOf(a))throw new e("Invalid request method");this[x]=a;if(b.mode&&-1===ma.indexOf(b.mode))throw new e("Invalid request mode");this[n]=b.mode||this[n]||"no-cors";if("no-cors"===this[n]){if(-1===na.indexOf(this[x]))throw new e("Invalid request method for no-cors");if(""!==
+this[A])throw new e("Request integrity data is not allowed with no-cors");}if("same-origin"!==this[n]&&"only-if-cached"===this[y])throw new e("Request mode must be same-origin for only-if-cached");this[E]=b.redirect||this[E]||"follow";if(-1===oa.indexOf(this[E]))throw new e("Invalid request redirect mode");this[r]="no-cors"===this[n]?D(d,Z):D(d,X);if(("GET"===this[x]||"HEAD"===this[x])&&g)throw new e("Request body is not allowed for GET or HEAD");this._initBody(g)}function pa(a,b){var c=D(void 0,
+b);a.replace(/\r?\n[\t ]+/g," ").split(/\r?\n/).forEach(function(a){var b=a.split(":");if(a=b.shift().trim())b=b.join(":").trim(),c.append(a,b)});return c}function p(a,b){b||(b={});this[G]="default";this[q]="status"in b?b.status:200;if(200>this[q]||599<this[q])throw new S("Invalid response status");this[T]=200<=this[q]&&300>this[q];if("statusText"in b){var c=b.statusText;for(var g=0,d=c.length,f;g<d;g++)if(f=c.charCodeAt(g),9!==f&&(32>f||255<f||127===f))throw e("Invalid response status text");}else c=
+"OK";this[H]=c;this[r]=D(b.headers,I);this[w]=b.url||"";if(a&&-1<qa.indexOf(this[q]))throw new e("Response body is not allowed with a null body status");this._initBody(a)}if(!d.fetch){var C=d.Array,R=d.ArrayBuffer,da=d.Object.create,N=d.Object.defineProperties,f=d.Symbol,ra=f.iterator,F=d.Map,S=d.RangeError,e=d.TypeError,u=d.Uint8Array,O=d.Promise,K=O.reject,ea=O.resolve,M=d.ReadableStream,U=d.ReadableStreamTee,ia=d.IsReadableStreamDisturbed,fa=d.IsReadableStreamLocked,m=f("body"),L=f("bodyUsed"),
+y=f("cache"),z=f("credentials"),t=f("guardCallback"),r=f("headers"),A=f("integrity"),l=f("map"),x=f("method"),n=f("mode"),T=f("ok"),E=f("redirect"),q=f("status"),H=f("statusText"),G=f("type"),w=f("url"),Y="accept-charset accept-encoding access-control-request-headers access-control-request-method connection content-length cookie cookie2 date dnt expect host keep-alive origin referer te trailer transfer-encoding upgrade via".split(" "),ca=["set-cookie","set-cookie2"],aa=["accept","accept-language",
+"content-language"],ba=["application/x-www-form-urlencoded","multipart/form-data","text/plain"],ja="default no-store reload no-cache force-cache only-if-cached".split(" "),ka=["omit","same-origin","include"],la="DELETE GET HEAD OPTIONS POST PUT".split(" "),na=["GET","HEAD","POST"],ma=["same-origin","no-cors","cors"],oa=["follow","error","manual"],qa=[101,204,205,304],sa=[301,302,303,307,308],ta="[object Int8Array];[object Uint8Array];[object Uint8ClampedArray];[object Int16Array];[object Uint16Array];[object Int32Array];[object Uint32Array];[object Float32Array];[object Float64Array]".split(";"),
+ha=R.isView||function(a){return a&&-1<ta.indexOf(Object.prototype.toString.call(a))};h.prototype.append=function(a,b){if(2!==arguments.length)throw e("Invalid parameters to append");a=B(a);b=P(b);this[t](a,b)||(this[l].has(a)?this[l].set(a,this[l].get(a)+", "+b):this[l].set(a,b))};h.prototype["delete"]=function(a){if(1!==arguments.length)throw e("Invalid parameters to delete");this[t](a,"invalid")||this[l].delete(B(a))};h.prototype.get=function(a){if(1!==arguments.length)throw e("Invalid parameters to get");
+a=B(a);var b=this[l].get(a);return void 0!==b?b:null};h.prototype.has=function(a){if(1!==arguments.length)throw e("Invalid parameters to has");return this[l].has(B(a))};h.prototype.set=function(a,b){if(2!==arguments.length)throw e("Invalid parameters to set");a=B(a);b=P(b);this[t](a,b)||this[l].set(a,b)};h.prototype.forEach=function(a,b){var c=this;C.from(this[l].entries()).sort().forEach(function(e){a.call(b,e[1],e[0],c)})};h.prototype.keys=function(){return(new F(C.from(this[l].entries()).sort())).keys()};
+h.prototype.values=function(){return(new F(C.from(this[l].entries()).sort())).values()};h.prototype.entries=function(){return(new F(C.from(this[l].entries()).sort())).entries()};h.prototype[ra]=h.prototype.entries;v.prototype.clone=function(){var a=null;null!==this[m]&&(a=U(this[m],!0),this[m]=a[0],a=a[1]);return new v(this,{cloneBody:a})};N(v.prototype,{cache:{get:function(){return this[y]}},credentials:{get:function(){return this[z]}},headers:{get:function(){return this[r]}},integrity:{get:function(){return this[A]}},
+method:{get:function(){return this[x]}},mode:{get:function(){return this[n]}},redirect:{get:function(){return this[E]}},url:{get:function(){return this[w]}}});Q.call(v.prototype);Q.call(p.prototype);p.prototype.clone=function(){var a=null;null!==this[m]&&(a=U(this[m],!0),this[m]=a[0],a=a[1]);return new p(a,{status:this[q],statusText:this[H],headers:D(this[r],I),url:this[w]})};N(p.prototype,{headers:{get:function(){return this[r]}},ok:{get:function(){return this[T]}},status:{get:function(){return this[q]}},
+statusText:{get:function(){return this[H]}},type:{get:function(){return this[G]}},url:{get:function(){return this[w]}}});p.error=function(){var a=new p(null);a[r][t]=V;a[G]="error";a[q]=0;a[H]="";return a};p.redirect=function(a,b){if(!FetchInternal.isUrlValid(a,!0))throw new e("Invalid URL for response redirect");void 0===b&&(b=302);if(-1===sa.indexOf(b))throw new S("Invalid status code for response redirect");return new p(null,{status:b,headers:{location:a}})};d.Headers=h;d.Request=v;d.Response=
+p;d.fetch=function(a,b){return new O(function(c,d){var g=!1,f=new v(a,b),k=new XMLHttpRequest,h=null,l=new M({start:function(a){h=a},cancel:function(a){g=!0;k.abort()}});k.onload=function(){h.close()};k.onreadystatechange=function(){if(k.readyState===k.HEADERS_RECEIVED){var a={status:k.status,statusText:k.statusText,headers:pa(k.getAllResponseHeaders()||"",I)};a.url="responseURL"in k?k.responseURL:a.headers.get("X-Request-URL");try{var b=new p(l,a);b[G]="basic";c(b)}catch(ua){d(ua)}}};k.onerror=function(){h.error(new e("Network request failed"));
+d(new e("Network request failed"))};k.ontimeout=function(){h.error(new e("Network request failed"));d(new e("Network request failed"))};k.open(f.method,f.url,!0);"include"===f.credentials&&(k.withCredentials=!0);f.headers.forEach(function(a,b){k.setRequestHeader(b,a)});var m=function(a){g||h.enqueue(a)};null===f.body?k.fetch(m,null):J(f).then(function(a){k.fetch(m,a)})})};d.fetch.polyfill=!0}})(this);
\ No newline at end of file
diff --git a/src/cobalt/fetch/fetch.js b/src/cobalt/fetch/fetch.js
index 6248427..b1971e3 100644
--- a/src/cobalt/fetch/fetch.js
+++ b/src/cobalt/fetch/fetch.js
@@ -37,8 +37,11 @@
 
   const Array = self.Array
   const ArrayBuffer = self.ArrayBuffer
+  const create = self.Object.create
+  const defineProperties = self.Object.defineProperties
   const Error = self.Error
-  const Symbol_iterator = self.Symbol.iterator
+  const Symbol = self.Symbol
+  const Symbol_iterator = Symbol.iterator
   const Map = self.Map
   const RangeError = self.RangeError
   const TypeError = self.TypeError
@@ -53,10 +56,105 @@
   const IsReadableStreamDisturbed = self.IsReadableStreamDisturbed
   const IsReadableStreamLocked = self.IsReadableStreamLocked
 
-  const err_InvalidHeadersInit = 'Constructing Headers with invalid parameters'
-  const err_NetworkRequestFailed = 'Network request failed'
+  const ERROR_INVALID_HEADERS_INIT =
+      'Constructing Headers with invalid parameters'
+  const ERROR_NETWORK_REQUEST_FAILED = 'Network request failed'
 
-  const viewClasses = [
+  // Internal slots for objects.
+  const BODY_SLOT = Symbol('body')
+  const BODY_USED_SLOT = Symbol('bodyUsed')
+  const CACHE_SLOT = Symbol('cache')
+  const CREDENTIALS_SLOT = Symbol('credentials')
+  const GUARD_CALLBACK_SLOT = Symbol('guardCallback')
+  const HEADERS_SLOT = Symbol('headers')
+  const INTEGRITY_SLOT = Symbol('integrity')
+  const MAP_SLOT = Symbol('map')
+  const METHOD_SLOT = Symbol('method')
+  const MODE_SLOT = Symbol('mode')
+  const OK_SLOT = Symbol('ok')
+  const REDIRECT_SLOT = Symbol('redirect')
+  const STATUS_SLOT = Symbol('status')
+  const STATUS_TEXT_SLOT = Symbol('statusText')
+  const TYPE_SLOT = Symbol('type')
+  const URL_SLOT = Symbol('url')
+
+  // Forbidden headers corresponding to various header guard types.
+  const INVALID_HEADERS_REQUEST = [
+    'accept-charset',
+    'accept-encoding',
+    'access-control-request-headers',
+    'access-control-request-method',
+    'connection',
+    'content-length',
+    'cookie',
+    'cookie2',
+    'date',
+    'dnt',
+    'expect',
+    'host',
+    'keep-alive',
+    'origin',
+    'referer',    // [sic]
+    'te',
+    'trailer',
+    'transfer-encoding',
+    'upgrade',
+    'via'
+  ]
+
+  const INVALID_HEADERS_RESPONSE = [
+    'set-cookie',
+    'set-cookie2'
+  ]
+
+  // The header guard for no-cors requests is special. Only these headers are
+  // allowed. And only certain values for content-type are allowed.
+  const VALID_HEADERS_NOCORS = [
+    'accept',
+    'accept-language',
+    'content-language'
+    // 'content-type' is treated specially.
+  ]
+
+  const VALID_HEADERS_NOCORS_CONTENT_TYPE = [
+    'application/x-www-form-urlencoded',
+    'multipart/form-data',
+    'text/plain'
+  ]
+
+  // Values that are allowed for Request cache mode.
+  const VALID_CACHE_MODES = [
+    'default',
+    'no-store',
+    'reload',
+    'no-cache',
+    'force-cache',
+    'only-if-cached'
+  ]
+
+  // Values that are allowed for Request credentials.
+  const VALID_CREDENTIALS = ['omit', 'same-origin', 'include']
+
+  // HTTP methods whose capitalization should be normalized.
+  const VALID_METHODS = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT']
+
+  // Methods that are allowed for Request mode: no-cors.
+  const VALID_METHODS_NOCORS = ['GET', 'HEAD', 'POST']
+
+  // Modes that are allowed for RequestInit. Although 'navigate' is a valid
+  // request mode, it is not allowed in the RequestInit parameter.
+  const VALID_MODES = ['same-origin', 'no-cors', 'cors']
+
+  // Values that are allowed for Request redirect mode.
+  const VALID_REDIRECT_MODES = ['follow', 'error', 'manual']
+
+  // Body is not allowed in responses with a null body status.
+  const NULL_BODY_STATUSES = [101, 204, 205, 304 ]
+
+  // Statuses that are allowed for Response.redirect.
+  const REDIRECT_STATUSES = [301, 302, 303, 307, 308]
+
+  const ARRAY_BUFFER_VIEW_CLASSES = [
     '[object Int8Array]',
     '[object Uint8Array]',
     '[object Uint8ClampedArray]',
@@ -69,7 +167,8 @@
   ]
 
   var isArrayBufferView = ArrayBuffer.isView || function(obj) {
-    return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1
+    return obj && ARRAY_BUFFER_VIEW_CLASSES.indexOf(
+        Object.prototype.toString.call(obj)) > -1
   }
 
   function normalizeName(name) {
@@ -120,44 +219,98 @@
     return value
   }
 
-  // https://fetch.spec.whatwg.org/#headers-class
-  function Headers(headers) {
-    this.map = new Map();
+  // Callbacks to determine whether a given header name & value are forbidden
+  // for various header guard types.
+  function guardImmutableCallback(name, value) {
+    throw new TypeError('Immutable header cannot be modified')
+  }
 
-    if (headers === undefined) {
+  function guardNoneCallback(name, value) {
+    return false
+  }
+
+  function guardRequestCallback(name, value) {
+    var nameLower = name.toLowerCase()
+    if (INVALID_HEADERS_REQUEST.indexOf(nameLower) > -1 ||
+        nameLower.startsWith('proxy-') || nameLower.startsWith('sec-')) {
+      return true
+    }
+    return false
+  }
+
+  function guardRequestNoCorsCallback(name, value) {
+    var nameLower = name.toLowerCase()
+    if (VALID_HEADERS_NOCORS.indexOf(nameLower) > -1) {
+      return false
+    }
+    if (nameLower === 'content-type') {
+      var mimeType = value.split(';')[0].toLowerCase()
+      if (VALID_HEADERS_NOCORS_CONTENT_TYPE.indexOf(mimeType) > -1) {
+        return false
+      }
+    }
+    return true
+  }
+
+  function guardResponseCallback(name, value) {
+    if (INVALID_HEADERS_RESPONSE.indexOf(name.toLowerCase()) > -1) {
+      return true
+    }
+    return false
+  }
+
+  // https://fetch.spec.whatwg.org/#headers-class
+  function Headers(init) {
+    this[MAP_SLOT] = new Map();
+    if (this[GUARD_CALLBACK_SLOT] === undefined) {
+      this[GUARD_CALLBACK_SLOT] = guardNoneCallback
+    }
+
+    if (init === undefined) {
       return
-    } else if (headers === null || typeof headers !== 'object') {
-      throw new TypeError(err_InvalidHeadersInit)
-    } else if (headers instanceof Headers) {
-      // Should use for..of in case |headers| has a custom Symbol.iterator.
-      // However, this results in the ClosureCompiler polyfilling Symbol.
-      headers.forEach(function(value, name) {
+    } else if (init === null || typeof init !== 'object') {
+      throw new TypeError(ERROR_INVALID_HEADERS_INIT)
+    } else if (init instanceof Headers) {
+      // TODO: Use for..of in case |init| has a custom Symbol.iterator.
+      // However, this results in the ClosureCompiler polyfilling Symbol, so
+      // use forEach until ClosureCompiler supports ES6 output.
+      init.forEach(function(value, name) {
         this.append(name, value)
       }, this)
-    } else if (Array.isArray(headers)) {
-      headers.forEach(function(header) {
+    } else if (Array.isArray(init)) {
+      init.forEach(function(header) {
         if (header.length !== 2) {
-          throw new TypeError(err_InvalidHeadersInit)
+          throw new TypeError(ERROR_INVALID_HEADERS_INIT)
         }
         this.append(header[0], header[1])
       }, this)
     } else {
-      Object.getOwnPropertyNames(headers).forEach(function(name) {
-        this.append(name, headers[name])
+      Object.getOwnPropertyNames(init).forEach(function(name) {
+        this.append(name, init[name])
       }, this)
     }
   }
 
+  function CreateHeadersWithGuard(headersInit, guardCallback) {
+    var headers = create(Headers.prototype)
+    headers[GUARD_CALLBACK_SLOT] = guardCallback
+    Headers.call(headers, headersInit)
+    return headers
+  }
+
   Headers.prototype.append = function(name, value) {
     if (arguments.length !== 2) {
       throw TypeError('Invalid parameters to append')
     }
     name = normalizeName(name)
     value = normalizeValue(value)
-    if (this.map.has(name)) {
-      this.map.set(name, this.map.get(name) + ', ' + value)
+    if (this[GUARD_CALLBACK_SLOT](name, value)) {
+      return
+    }
+    if (this[MAP_SLOT].has(name)) {
+      this[MAP_SLOT].set(name, this[MAP_SLOT].get(name) + ', ' + value)
     } else {
-      this.map.set(name, value)
+      this[MAP_SLOT].set(name, value)
     }
   }
 
@@ -165,7 +318,10 @@
     if (arguments.length !== 1) {
       throw TypeError('Invalid parameters to delete')
     }
-    this.map.delete(normalizeName(name))
+    if (this[GUARD_CALLBACK_SLOT](name, 'invalid')) {
+      return
+    }
+    this[MAP_SLOT].delete(normalizeName(name))
   }
 
   Headers.prototype.get = function(name) {
@@ -173,7 +329,7 @@
       throw TypeError('Invalid parameters to get')
     }
     name = normalizeName(name)
-    var value = this.map.get(name)
+    var value = this[MAP_SLOT].get(name)
     return value !== undefined ? value : null
   }
 
@@ -181,18 +337,23 @@
     if (arguments.length !== 1) {
       throw TypeError('Invalid parameters to has')
     }
-    return this.map.has(normalizeName(name))
+    return this[MAP_SLOT].has(normalizeName(name))
   }
 
   Headers.prototype.set = function(name, value) {
     if (arguments.length !== 2) {
       throw TypeError('Invalid parameters to set')
     }
-    this.map.set(normalizeName(name), normalizeValue(value))
+    name = normalizeName(name)
+    value = normalizeValue(value)
+    if (this[GUARD_CALLBACK_SLOT](name, value)) {
+      return
+    }
+    this[MAP_SLOT].set(name, value)
   }
 
   Headers.prototype.forEach = function(callback, thisArg) {
-    var sorted_array = Array.from(this.map.entries()).sort()
+    var sorted_array = Array.from(this[MAP_SLOT].entries()).sort()
     var that = this
     sorted_array.forEach(function(value) {
       callback.call(thisArg, value[1], value[0], that)
@@ -200,17 +361,17 @@
   }
 
   Headers.prototype.keys = function() {
-    var sorted_map = new Map(Array.from(this.map.entries()).sort())
+    var sorted_map = new Map(Array.from(this[MAP_SLOT].entries()).sort())
     return sorted_map.keys()
   }
 
   Headers.prototype.values = function() {
-    var sorted_map = new Map(Array.from(this.map.entries()).sort())
+    var sorted_map = new Map(Array.from(this[MAP_SLOT].entries()).sort())
     return sorted_map.values()
   }
 
   Headers.prototype.entries = function() {
-    var sorted_map = new Map(Array.from(this.map.entries()).sort())
+    var sorted_map = new Map(Array.from(this[MAP_SLOT].entries()).sort())
     return sorted_map.entries()
   }
 
@@ -218,7 +379,7 @@
 
   function consumeBodyAsUint8Array(body) {
     if (body.bodyUsed) {
-      return Promise_reject(new TypeError('Body already read'))
+      return Promise_reject(new TypeError('Body was already read'))
     }
 
     if (body.body === null) {
@@ -226,7 +387,7 @@
     }
 
     if (IsReadableStreamLocked(body.body)) {
-      return Promise_reject(new TypeError('ReadableStream already locked'))
+      return Promise_reject(new TypeError('ReadableStream was already locked'))
     }
 
     var reader = body.body.getReader()
@@ -252,31 +413,16 @@
         results.push(result.value)
         return reader.read().then(addResult)
       } else {
-        return Promise_reject(new TypeError('Invalid stream read value type'))
+        return Promise_reject(new TypeError('Invalid stream data type'))
       }
     })
   }
 
-  function encodeStringToUint8Array(str) {
-    // Encode string to UTF-8 then store it in an Uint8Array.
-    var utf8 = unescape(encodeURIComponent(str))
-    var uint8 = new Uint8Array(utf8.length)
-    for (var i = 0, len = utf8.length; i < len; i++) {
-      uint8[i] = utf8.charCodeAt(i)
-    }
-    return uint8
-  }
-
-  function decodeStringFromUint8Array(uint8) {
-    // Decode string from UTF-8 that is stored in the Uint8Array.
-    return decodeURIComponent(escape(String.fromCharCode.apply(null, uint8)))
-  }
-
   // https://fetch.spec.whatwg.org/#concept-bodyinit-extract
   function extractBody(controller, data, errorString) {
     if (!data) {
     } else if (typeof data === 'string') {
-      controller.enqueue(encodeStringToUint8Array(data))
+      controller.enqueue(FetchInternal.encodeToUTF8(data))
     } else if (ArrayBuffer.prototype.isPrototypeOf(data)) {
       controller.enqueue(new Uint8Array(data))
     } else if (isArrayBufferView(data)) {
@@ -291,13 +437,13 @@
   //   Blob types. So only support text(), arrayBuffer(), and json().
   function Body() {
     this._initBody = function(body) {
-      this._bodyUsed = false
+      this[BODY_USED_SLOT] = false
       if (body === null || body === undefined) {
-        this.body = null
+        this[BODY_SLOT] = null
       } else if (body instanceof ReadableStream) {
-        this.body = body
+        this[BODY_SLOT] = body
       } else {
-        this.body = new ReadableStream({
+        this[BODY_SLOT] = new ReadableStream({
           start(controller) {
             extractBody(controller, body, 'Unsupported BodyInit type')
             controller.close()
@@ -305,21 +451,24 @@
         })
       }
 
-      if (!this.headers.get('content-type')) {
+      if (!this[HEADERS_SLOT].get('content-type')) {
         if (typeof body === 'string') {
-          this.headers.set('content-type', 'text/plain;charset=UTF-8')
+          this[HEADERS_SLOT].set('content-type', 'text/plain;charset=UTF-8')
         }
       }
     }
 
-    Object.defineProperty(this, 'bodyUsed', {
-      get: function() {
-        if (this._bodyUsed) {
-          return true
-        } else if (this.body) {
-          return !!IsReadableStreamDisturbed(this.body)
-        } else {
-          return false
+    defineProperties(this, {
+      'body': { get: function() { return this[BODY_SLOT] } },
+      'bodyUsed': {
+        get: function() {
+          if (this[BODY_USED_SLOT]) {
+            return true
+          } else if (this[BODY_SLOT]) {
+            return !!IsReadableStreamDisturbed(this[BODY_SLOT])
+          } else {
+            return false
+          }
         }
       }
     })
@@ -332,11 +481,7 @@
 
     this.text = function() {
       return consumeBodyAsUint8Array(this).then(function(data) {
-        if (data.length === 0) {
-          return ''
-        } else {
-          return decodeStringFromUint8Array(data)
-        }
+        return FetchInternal.decodeFromUTF8(data)
       })
     }
 
@@ -347,66 +492,143 @@
     return this
   }
 
-  // HTTP methods whose capitalization should be normalized
-  const methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT']
-
   function normalizeMethod(method) {
     var upcased = method.toUpperCase()
-    return (methods.indexOf(upcased) > -1) ? upcased : method
+    if (VALID_METHODS.indexOf(upcased) === -1) {
+      throw new TypeError('Invalid request method')
+    }
+    return upcased
   }
 
   // https://fetch.spec.whatwg.org/#request-class
   function Request(input, init) {
+    // When cloning a request, |init| will have the non-standard member
+    // |cloneBody|. This signals that the "!hasInit" path should be used.
+    var hasInit = init !== undefined && init !== null &&
+                  init.cloneBody === undefined
     init = init || {}
-    var body = init.body
+    var body = init.body || init.cloneBody
+    var headersInit = init.headers
 
     if (input instanceof Request) {
       if (input.bodyUsed) {
-        throw new TypeError('Request body already read')
+        throw new TypeError('Request body was already read')
       }
-      this.url = input.url
-      this.credentials = input.credentials
-      if (!init.headers) {
-        this.headers = new Headers(input.headers)
+      this[URL_SLOT] = input.url
+      this[CACHE_SLOT] = input.cache
+      this[CREDENTIALS_SLOT] = input.credentials
+      if (headersInit === undefined) {
+        headersInit = input.headers
       }
-      this.method = input.method
-      this.mode = input.mode
+      this[INTEGRITY_SLOT] = input.integrity
+      this[METHOD_SLOT] = input.method
+      this[MODE_SLOT] = input.mode
+      if (hasInit && this[MODE_SLOT] === 'navigate') {
+        this[MODE_SLOT] = 'same-origin'
+      }
+      this[REDIRECT_SLOT] = input.redirect
       if (!body && input.body !== null) {
         // Take ownership of the stream and mark |input| as disturbed.
         body = input.body
-        input._bodyUsed = true
+        input[BODY_USED_SLOT] = true
       }
     } else {
-      this.url = String(input)
+      this[URL_SLOT] = String(input)
+      if (!FetchInternal.isUrlValid(this[URL_SLOT],
+                                    false /* allowCredentials */)) {
+        throw new TypeError('Invalid request URL')
+      }
+      this[MODE_SLOT] = 'cors'
+      this[CREDENTIALS_SLOT] = 'omit'
     }
 
-    this.credentials = init.credentials || this.credentials || 'omit'
-    if (init.headers || !this.headers) {
-      this.headers = new Headers(init.headers)
+    if (init.window !== undefined && init.window !== null) {
+      throw new TypeError('Invalid request window')
     }
-    this.method = normalizeMethod(init.method || this.method || 'GET')
-    this.mode = init.mode || this.mode || null
-    this.referrer = null
 
-    if ((this.method === 'GET' || this.method === 'HEAD') && body) {
-      throw new TypeError('Body not allowed for GET or HEAD requests')
+    this[CACHE_SLOT] = init.cache || this[CACHE_SLOT] || 'default'
+    if (VALID_CACHE_MODES.indexOf(this[CACHE_SLOT]) === -1) {
+      throw new TypeError('Invalid request cache mode')
+    }
+
+    this[CREDENTIALS_SLOT] = init.credentials || this[CREDENTIALS_SLOT] ||
+                             'omit'
+    if (VALID_CREDENTIALS.indexOf(this[CREDENTIALS_SLOT]) === -1) {
+      throw new TypeError('Invalid request credentials')
+    }
+
+    if (init.integrity !== undefined) {
+      this[INTEGRITY_SLOT] = init.integrity
+    } else if (this[INTEGRITY_SLOT] === undefined) {
+      this[INTEGRITY_SLOT] = ''
+    }
+    this[METHOD_SLOT] = normalizeMethod(init.method || this[METHOD_SLOT] ||
+                                        'GET')
+
+    if (init.mode && VALID_MODES.indexOf(init.mode) === -1) {
+      throw new TypeError('Invalid request mode')
+    }
+    this[MODE_SLOT] = init.mode || this[MODE_SLOT] || 'no-cors'
+    if (this[MODE_SLOT] === 'no-cors') {
+      if (VALID_METHODS_NOCORS.indexOf(this[METHOD_SLOT]) === -1) {
+        throw new TypeError('Invalid request method for no-cors')
+      }
+      if (this[INTEGRITY_SLOT] !== '') {
+        throw new TypeError(
+            'Request integrity data is not allowed with no-cors')
+      }
+    }
+    if (this[MODE_SLOT] !== 'same-origin' &&
+        this[CACHE_SLOT] === 'only-if-cached') {
+      throw new TypeError(
+          'Request mode must be same-origin for only-if-cached')
+    }
+
+    this[REDIRECT_SLOT] = init.redirect || this[REDIRECT_SLOT] || 'follow'
+    if (VALID_REDIRECT_MODES.indexOf(this[REDIRECT_SLOT]) === -1) {
+      throw new TypeError('Invalid request redirect mode')
+    }
+
+    if (this[MODE_SLOT] === 'no-cors') {
+      this[HEADERS_SLOT] =
+          CreateHeadersWithGuard(headersInit, guardRequestNoCorsCallback)
+    } else {
+      this[HEADERS_SLOT] =
+          CreateHeadersWithGuard(headersInit, guardRequestCallback)
+    }
+
+    if ((this[METHOD_SLOT] === 'GET' || this[METHOD_SLOT] === 'HEAD') && body) {
+      throw new TypeError('Request body is not allowed for GET or HEAD')
     }
     this._initBody(body)
   }
 
   Request.prototype.clone = function() {
     var cloneBody = null
-    if (this.body !== null) {
-      var streams = ReadableStreamTee(this.body, true /* cloneForBranch2 */)
-      this.body = streams[0]
+    if (this[BODY_SLOT] !== null) {
+      var streams = ReadableStreamTee(this[BODY_SLOT],
+                                      true /* cloneForBranch2 */)
+      this[BODY_SLOT] = streams[0]
       cloneBody = streams[1]
     }
-    return new Request(this, { body: cloneBody })
+    return new Request(this, { cloneBody: cloneBody })
   }
 
-  function parseHeaders(rawHeaders) {
-    var headers = new Headers()
-    // Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space
+  defineProperties(Request.prototype, {
+    'cache': { get: function() { return this[CACHE_SLOT] } },
+    'credentials': { get: function() { return this[CREDENTIALS_SLOT] } },
+    'headers': { get: function() { return this[HEADERS_SLOT] } },
+    'integrity': { get: function() { return this[INTEGRITY_SLOT] } },
+    'method': { get: function() { return this[METHOD_SLOT] } },
+    'mode': { get: function() { return this[MODE_SLOT] } },
+    'redirect': { get: function() { return this[REDIRECT_SLOT] } },
+    'url': { get: function() { return this[URL_SLOT] } }
+  })
+
+  function parseHeaders(rawHeaders, guardCallback) {
+    var headers = CreateHeadersWithGuard(undefined, guardCallback)
+    // Replace instances of \r\n and \n followed by at least one space or
+    // horizontal tab with a space.
     // https://tools.ietf.org/html/rfc7230#section-3.2
     var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' ')
     preProcessedHeaders.split(/\r?\n/).forEach(function(line) {
@@ -428,32 +650,32 @@
     for (var i = 0, len = text.length, c; i < len; i++) {
       c = text.charCodeAt(i)
       if (c !== 9 && (c < 32 || c > 255 || c === 127)) {
-        throw TypeError('Invalid status text')
+        throw TypeError('Invalid response status text')
       }
     }
     return text
   }
 
-  // Body is not allowed in responses with a null body status.
-  const nullBodyStatuses = [ 101, 204, 205, 304 ]
-
   // https://fetch.spec.whatwg.org/#response-class
   function Response(body, init) {
     if (!init) {
       init = {}
     }
 
-    this.type = 'default'
-    this.status = 'status' in init ? init.status : 200
-    if (this.status < 200 || this.status > 599) {
+    this[TYPE_SLOT] = 'default'
+    this[STATUS_SLOT] = 'status' in init ? init.status : 200
+    if (this[STATUS_SLOT] < 200 || this[STATUS_SLOT] > 599) {
       throw new RangeError('Invalid response status')
     }
-    this.ok = this.status >= 200 && this.status < 300
-    this.statusText = 'statusText' in init ? parseStatusText(init.statusText) : 'OK'
-    this.headers = new Headers(init.headers)
-    this.url = init.url || ''
-    if (body && nullBodyStatuses.indexOf(this.status) > -1) {
-      throw new TypeError('Body not allowed with a null body status')
+    this[OK_SLOT] = this[STATUS_SLOT] >= 200 && this[STATUS_SLOT] < 300
+    this[STATUS_TEXT_SLOT] = 'statusText' in init ?
+                             parseStatusText(init.statusText) : 'OK'
+    this[HEADERS_SLOT] =
+        CreateHeadersWithGuard(init.headers, guardResponseCallback)
+    this[URL_SLOT] = init.url || ''
+    if (body && NULL_BODY_STATUSES.indexOf(this[STATUS_SLOT]) > -1) {
+      throw new TypeError(
+          'Response body is not allowed with a null body status')
     }
     this._initBody(body)
   }
@@ -462,38 +684,48 @@
 
   Response.prototype.clone = function() {
     var cloneBody = null
-    if (this.body !== null) {
-      var streams = ReadableStreamTee(this.body, true /* cloneForBranch2 */)
-      this.body = streams[0]
+    if (this[BODY_SLOT] !== null) {
+      var streams = ReadableStreamTee(this[BODY_SLOT],
+                                      true /* cloneForBranch2 */)
+      this[BODY_SLOT] = streams[0]
       cloneBody = streams[1]
     }
     return new Response(cloneBody, {
-      status: this.status,
-      statusText: this.statusText,
-      headers: new Headers(this.headers),
-      url: this.url
+      status: this[STATUS_SLOT],
+      statusText: this[STATUS_TEXT_SLOT],
+      headers: CreateHeadersWithGuard(this[HEADERS_SLOT],
+                                      guardResponseCallback),
+      url: this[URL_SLOT]
     })
   }
 
+  defineProperties(Response.prototype, {
+    'headers': { get: function() { return this[HEADERS_SLOT] } },
+    'ok': { get: function() { return this[OK_SLOT] } },
+    'status': { get: function() { return this[STATUS_SLOT] } },
+    'statusText': { get: function() { return this[STATUS_TEXT_SLOT] } },
+    'type': { get: function() { return this[TYPE_SLOT] } },
+    'url': { get: function() { return this[URL_SLOT] } }
+  })
+
   Response.error = function() {
     var response = new Response(null)
-    response.type = 'error'
-    response.status = 0
-    response.statusText = ''
+    response[HEADERS_SLOT][GUARD_CALLBACK_SLOT] = guardImmutableCallback
+    response[TYPE_SLOT] = 'error'
+    response[STATUS_SLOT] = 0
+    response[STATUS_TEXT_SLOT] = ''
     return response
   }
 
-  var redirectStatuses = [301, 302, 303, 307, 308]
-
   Response.redirect = function(url, status) {
-    if (!FetchInternal.IsUrlValid(url)) {
-      throw new TypeError('Invalid URL')
+    if (!FetchInternal.isUrlValid(url, true /* allowCredentials */)) {
+      throw new TypeError('Invalid URL for response redirect')
     }
     if (status === undefined) {
       status = 302
     }
-    if (redirectStatuses.indexOf(status) === -1) {
-      throw new RangeError('Invalid status code')
+    if (REDIRECT_STATUSES.indexOf(status) === -1) {
+      throw new RangeError('Invalid status code for response redirect')
     }
 
     return new Response(null, {status: status, headers: {location: url}})
@@ -529,21 +761,31 @@
           var init = {
             status: xhr.status,
             statusText: xhr.statusText,
-            headers: parseHeaders(xhr.getAllResponseHeaders() || '')
+            headers: parseHeaders(xhr.getAllResponseHeaders() || '',
+                                  guardResponseCallback)
           }
-          init.url = 'responseURL' in xhr ? xhr.responseURL : init.headers.get('X-Request-URL')
-          resolve(new Response(responseStream, init))
+          init.url = 'responseURL' in xhr ?
+                     xhr.responseURL : init.headers.get('X-Request-URL')
+          try {
+            var response = new Response(responseStream, init)
+            response[TYPE_SLOT] = 'basic'
+            resolve(response)
+          } catch (err) {
+            reject(err)
+          }
         }
       }
 
       xhr.onerror = function() {
-        responseStreamController.error(new TypeError(err_NetworkRequestFailed))
-        reject(new TypeError(err_NetworkRequestFailed))
+        responseStreamController.error(
+            new TypeError(ERROR_NETWORK_REQUEST_FAILED))
+        reject(new TypeError(ERROR_NETWORK_REQUEST_FAILED))
       }
 
       xhr.ontimeout = function() {
-        responseStreamController.error(new TypeError(err_NetworkRequestFailed))
-        reject(new TypeError(err_NetworkRequestFailed))
+        responseStreamController.error(
+            new TypeError(ERROR_NETWORK_REQUEST_FAILED))
+        reject(new TypeError(ERROR_NETWORK_REQUEST_FAILED))
       }
 
       xhr.open(request.method, request.url, true)
diff --git a/src/cobalt/fetch/fetch_internal.cc b/src/cobalt/fetch/fetch_internal.cc
index ce79f8d..d6bd76e 100644
--- a/src/cobalt/fetch/fetch_internal.cc
+++ b/src/cobalt/fetch/fetch_internal.cc
@@ -14,15 +14,58 @@
 
 #include "cobalt/fetch/fetch_internal.h"
 
+#include "base/string_util.h"
+#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/dom/dom_settings.h"
 #include "googleurl/src/gurl.h"
 
 namespace cobalt {
 namespace fetch {
 
 // static
-bool FetchInternal::IsUrlValid(const std::string& url) {
-  GURL gurl(url);
-  return gurl.is_valid() || gurl.is_empty();
+bool FetchInternal::IsUrlValid(script::EnvironmentSettings* settings,
+                               const std::string& url, bool allow_credentials) {
+  dom::DOMSettings* dom_settings =
+      base::polymorphic_downcast<dom::DOMSettings*>(settings);
+  GURL gurl = dom_settings->base_url().Resolve(url);
+  return gurl.is_valid() && (allow_credentials ||
+                            (!gurl.has_username() && !gurl.has_password()));
+}
+
+// static
+scoped_refptr<dom::Uint8Array> FetchInternal::EncodeToUTF8(
+    script::EnvironmentSettings* settings, const std::string& text,
+    script::ExceptionState* exception_state) {
+  // The conversion helper already translated the JSValue into a UTF-8 encoded
+  // string. So just wrap the result in a Uint8Array.
+  return new dom::Uint8Array(settings,
+      reinterpret_cast<const uint8*>(text.c_str()),
+      static_cast<uint32>(text.size()),
+      exception_state);
+}
+
+// static
+std::string FetchInternal::DecodeFromUTF8(
+    const scoped_refptr<dom::Uint8Array>& data,
+    script::ExceptionState* exception_state) {
+  // The conversion helper expects the return value to be a UTF-8 encoded
+  // string.
+  base::StringPiece input(reinterpret_cast<const char*>(data->data()),
+                          data->length());
+
+  if (IsStringUTF8(input)) {
+    // Input is already UTF-8. Just strip the byte order mark if it's present.
+    const base::StringPiece kUtf8ByteOrderMarkStringPiece(kUtf8ByteOrderMark);
+    if (input.starts_with(kUtf8ByteOrderMarkStringPiece)) {
+      input = input.substr(kUtf8ByteOrderMarkStringPiece.length());
+    }
+    return input.as_string();
+  }
+
+  // Only UTF-8 is supported.
+  exception_state->SetSimpleException(script::kTypeError,
+                                      "Not a valid UTF-8 string");
+  return std::string();
 }
 
 }  // namespace fetch
diff --git a/src/cobalt/fetch/fetch_internal.h b/src/cobalt/fetch/fetch_internal.h
index f5b3add..946c7b6 100644
--- a/src/cobalt/fetch/fetch_internal.h
+++ b/src/cobalt/fetch/fetch_internal.h
@@ -17,6 +17,10 @@
 
 #include <string>
 
+#include "base/memory/ref_counted.h"
+#include "cobalt/dom/uint8_array.h"
+#include "cobalt/script/environment_settings.h"
+#include "cobalt/script/exception_state.h"
 #include "cobalt/script/wrappable.h"
 
 namespace cobalt {
@@ -29,7 +33,18 @@
 class FetchInternal : public script::Wrappable {
  public:
   // Return whether the given URL is valid.
-  static bool IsUrlValid(const std::string& url);
+  static bool IsUrlValid(script::EnvironmentSettings* settings,
+      const std::string& url, bool allow_credentials);
+
+  // Return a Uint8Array representing the given text as UTF-8 encoded data.
+  static scoped_refptr<dom::Uint8Array> EncodeToUTF8(
+      script::EnvironmentSettings* settings, const std::string& text,
+      script::ExceptionState* exception_state);
+
+  // Return a UTF-8 encoded string representing the given data.
+  static std::string DecodeFromUTF8(
+      const scoped_refptr<dom::Uint8Array>& data,
+      script::ExceptionState* exception_state);
 
   DEFINE_WRAPPABLE_TYPE(FetchInternal);
 };
diff --git a/src/cobalt/fetch/fetch_internal.idl b/src/cobalt/fetch/fetch_internal.idl
index bd1153b..32aa3b1 100644
--- a/src/cobalt/fetch/fetch_internal.idl
+++ b/src/cobalt/fetch/fetch_internal.idl
@@ -17,5 +17,9 @@
 // This is not meant to be public and should not be used outside of the fetch
 // implementation.
 interface FetchInternal {
-  static boolean IsUrlValid(DOMString url);
+  [CallWith=EnvironmentSettings] static boolean isUrlValid(
+      DOMString url, boolean allowCredentials);
+  [CallWith=EnvironmentSettings,RaisesException] static Uint8Array encodeToUTF8(
+      DOMString text);
+  [RaisesException] static DOMString decodeFromUTF8(Uint8Array data);
 };
diff --git a/src/cobalt/h5vcc/h5vcc.gyp b/src/cobalt/h5vcc/h5vcc.gyp
index 1e71529..9b96e06 100644
--- a/src/cobalt/h5vcc/h5vcc.gyp
+++ b/src/cobalt/h5vcc/h5vcc.gyp
@@ -14,7 +14,7 @@
 
 {
   'variables': {
-    'cobalt_code': 1,
+    'sb_pedantic_warnings': 1,
   },
   'targets': [
     {
diff --git a/src/cobalt/h5vcc/h5vcc_runtime.cc b/src/cobalt/h5vcc/h5vcc_runtime.cc
index feb236a..6f50618 100644
--- a/src/cobalt/h5vcc/h5vcc_runtime.cc
+++ b/src/cobalt/h5vcc/h5vcc_runtime.cc
@@ -67,10 +67,10 @@
   const system_window::ApplicationEvent* app_event =
       base::polymorphic_downcast<const system_window::ApplicationEvent*>(event);
   if (app_event->type() == system_window::ApplicationEvent::kPause) {
-    DLOG(INFO) << "Got pause event.";
+    // DLOG(INFO) << "Got pause event.";
     on_pause()->DispatchEvent();
   } else if (app_event->type() == system_window::ApplicationEvent::kUnpause) {
-    DLOG(INFO) << "Got unpause event.";
+    // DLOG(INFO) << "Got unpause event.";
     on_resume()->DispatchEvent();
   }
 }
diff --git a/src/cobalt/input/camera_3d.h b/src/cobalt/input/camera_3d.h
index 5138c80..37e25d2 100644
--- a/src/cobalt/input/camera_3d.h
+++ b/src/cobalt/input/camera_3d.h
@@ -19,6 +19,7 @@
 #include "cobalt/base/camera_transform.h"
 #include "cobalt/input/input_poller.h"
 #include "starboard/window.h"
+#include "third_party/glm/glm/gtc/quaternion.hpp"
 
 namespace cobalt {
 namespace input {
@@ -29,12 +30,14 @@
 // have empty default implementations.
 class Camera3D : public base::RefCountedThreadSafe<Camera3D> {
  public:
+  // The rotations around the axes are intrinsic, meaning they are applied in
+  // sequence, each on the rotated frame produced by the previous one.
   enum CameraAxes {
-    // Restricted to [0deg, 360deg]
+    // Applied around the Z axis. Restricted to [0deg, 360deg).
     kCameraRoll = 0x00,
-    // Restricted to [-90deg, 90deg]
+    // Applied around the X axis. Restricted to [-90deg, 90deg)
     kCameraPitch = 0x01,
-    // Restricted to [0deg, 360deg]
+    // Applied around the Y axis. Restricted to [0deg, 360deg)
     kCameraYaw = 0x02,
   };
 
@@ -49,7 +52,7 @@
   virtual void ClearAllKeyMappings() {}
 
   // Return the camera's current view direction.
-  virtual base::CameraOrientation GetOrientation() const = 0;
+  virtual glm::quat GetOrientation() const = 0;
 
   // The above methods are meant as an interface for the Camera3D's WebAPI on
   // the WebModule thread, but the methods below will be accessed on the
@@ -74,6 +77,16 @@
   virtual void Reset() {}
 
   virtual ~Camera3D() {}
+
+  template <typename FloatType>
+  static FloatType DegreesToRadians(FloatType degrees) {
+    return (degrees / 360) * 2 * static_cast<FloatType>(M_PI);
+  }
+
+  template <typename FloatType>
+  static FloatType RadiansToDegrees(FloatType radians) {
+    return radians * 180 / static_cast<FloatType>(M_PI);
+  }
 };
 
 }  // namespace input
diff --git a/src/cobalt/input/camera_3d_input_poller.cc b/src/cobalt/input/camera_3d_input_poller.cc
index 95447a6..8e6234a 100644
--- a/src/cobalt/input/camera_3d_input_poller.cc
+++ b/src/cobalt/input/camera_3d_input_poller.cc
@@ -21,6 +21,7 @@
 
 #include "cobalt/input/create_default_camera_3d.h"
 #include "third_party/glm/glm/gtc/matrix_transform.hpp"
+#include "third_party/glm/glm/gtc/quaternion.hpp"
 #include "third_party/glm/glm/gtx/transform.hpp"
 
 namespace cobalt {
@@ -28,7 +29,10 @@
 
 Camera3DInputPoller::Camera3DInputPoller(
     const scoped_refptr<input::InputPoller>& input_poller)
-    : input_poller_(input_poller),
+    : roll_in_radians_(0.0f),
+      pitch_in_radians_(0.0f),
+      yaw_in_radians_(0.0f),
+      input_poller_(input_poller),
       width_to_height_aspect_ratio_(16.0f / 9.0f),
       vertical_fov_(60.0f) {}
 
@@ -48,33 +52,24 @@
   keycode_map_.clear();
 }
 
-base::CameraOrientation Camera3DInputPoller::GetOrientation() const {
-  base::AutoLock lock(mutex_);
-  return orientation_;
+glm::quat Camera3DInputPoller::orientation() const {
+  return glm::angleAxis(-roll_in_radians_, glm::vec3(0, 0, 1)) *
+         glm::angleAxis(-pitch_in_radians_, glm::vec3(1, 0, 0)) *
+         glm::angleAxis(-yaw_in_radians_, glm::vec3(0, 1, 0));
 }
 
-namespace {
-
-const float kPiF = static_cast<float>(M_PI);
-
-float DegreesToRadians(float degrees) { return (degrees / 360.0f) * 2 * kPiF; }
-
-}  // namespace
+glm::quat Camera3DInputPoller::GetOrientation() const {
+  base::AutoLock lock(mutex_);
+  return orientation();
+}
 
 base::CameraTransform
 Camera3DInputPoller::GetCameraTransformAndUpdateOrientation() {
   base::AutoLock lock(mutex_);
   AccumulateOrientation();
 
-  // Note that we invert the rotation angles since this matrix is applied to
-  // the objects in our scene, and if the camera moves right, the objects,
-  // relatively, would move right.
-  glm::mat4 view_matrix =
-      glm::rotate(-DegreesToRadians(orientation_.roll), glm::vec3(0, 0, 1)) *
-      glm::rotate(-DegreesToRadians(orientation_.pitch), glm::vec3(1, 0, 0)) *
-      glm::rotate(-DegreesToRadians(orientation_.yaw), glm::vec3(0, 1, 0));
+  glm::mat4 view_matrix = glm::mat4_cast(orientation());
 
-  // Setup a (right-handed) perspective projection matrix.
   const float kNearZ = 0.01f;
   const float kFarZ = 1000.0f;
   glm::mat4 projection_matrix = glm::perspectiveRH(
@@ -91,7 +86,9 @@
 
 void Camera3DInputPoller::Reset() {
   base::AutoLock lock(mutex_);
-  orientation_ = base::CameraOrientation();
+  roll_in_radians_ = 0.0f;
+  pitch_in_radians_ = 0.0f;
+  yaw_in_radians_ = 0.0f;
 }
 
 void Camera3DInputPoller::AccumulateOrientation() {
@@ -111,6 +108,7 @@
       delta = kMaxTimeDelta;
     }
 
+    // Accumulate new rotation from all mapped inputs.
     for (KeycodeMap::const_iterator iter = keycode_map_.begin();
          iter != keycode_map_.end(); ++iter) {
       // If the key does not have analog output, the AnalogInput() method will
@@ -127,34 +125,46 @@
       float* target_angle;
       switch (iter->second.axis) {
         case kCameraRoll:
-          target_angle = &orientation_.roll;
+          target_angle = &roll_in_radians_;
           break;
         case kCameraPitch:
-          target_angle = &orientation_.pitch;
+          target_angle = &pitch_in_radians_;
           break;
         case kCameraYaw:
-          target_angle = &orientation_.yaw;
+          target_angle = &yaw_in_radians_;
           break;
       }
 
       // Apply the angle adjustment from the key.
-      *target_angle += value * iter->second.degrees_per_second *
+      *target_angle += value *
+                       DegreesToRadians(iter->second.degrees_per_second) *
                        static_cast<float>(delta.InSecondsF());
-
-      // Apply any clamping or wrapping to the resulting camera angles.
-      if (iter->second.axis == kCameraPitch) {
-        *target_angle = std::min(90.0f, std::max(-90.0f, *target_angle));
-      } else {
-        *target_angle = static_cast<float>(fmod(*target_angle, 360));
-        if (*target_angle < 0) {
-          *target_angle += 360;
-        }
-      }
     }
+
+    // Clamp the angles to ensure that they remain in the valid range.
+    ClampAngles();
   }
   last_update_ = now;
 }
 
+void Camera3DInputPoller::ClampAngles() {
+  float kTwoPi = static_cast<float>(M_PI * 2);
+  float kPiOverTwo = static_cast<float>(M_PI / 2);
+
+  pitch_in_radians_ =
+      std::max(-kPiOverTwo, std::min(kPiOverTwo, pitch_in_radians_));
+
+  roll_in_radians_ = fmod(roll_in_radians_, kTwoPi);
+  if (roll_in_radians_ < 0) {
+    roll_in_radians_ += kTwoPi;
+  }
+
+  yaw_in_radians_ = fmod(yaw_in_radians_, kTwoPi);
+  if (yaw_in_radians_ < 0) {
+    yaw_in_radians_ += kTwoPi;
+  }
+}
+
 scoped_refptr<Camera3D> CreatedDefaultCamera3D(
     SbWindow window, const scoped_refptr<InputPoller>& input_poller) {
   UNREFERENCED_PARAMETER(window);
diff --git a/src/cobalt/input/camera_3d_input_poller.h b/src/cobalt/input/camera_3d_input_poller.h
index bd89ad5..f31d227 100644
--- a/src/cobalt/input/camera_3d_input_poller.h
+++ b/src/cobalt/input/camera_3d_input_poller.h
@@ -40,7 +40,7 @@
                         float degrees_per_second) OVERRIDE;
   void ClearKeyMapping(int keycode) OVERRIDE;
   void ClearAllKeyMappings() OVERRIDE;
-  base::CameraOrientation GetOrientation() const OVERRIDE;
+  glm::quat GetOrientation() const OVERRIDE;
 
   // Returns the camera transforms based on hand-controlled inputs mapped
   // by the functions above
@@ -64,11 +64,16 @@
   typedef std::map<int, KeycodeMappingInfo> KeycodeMap;
 
   void AccumulateOrientation();
+  void ClampAngles();
+
+  glm::quat orientation() const;
 
   mutable base::Lock mutex_;
 
   // The current accumulated camera orientation state.
-  base::CameraOrientation orientation_;
+  float roll_in_radians_;
+  float pitch_in_radians_;
+  float yaw_in_radians_;
 
   // The time that the last update to the camera's state has occurred.
   base::optional<base::TimeTicks> last_update_;
diff --git a/src/cobalt/input/input_device_manager.h b/src/cobalt/input/input_device_manager.h
index 4dae106..1fb1f14 100644
--- a/src/cobalt/input/input_device_manager.h
+++ b/src/cobalt/input/input_device_manager.h
@@ -15,6 +15,8 @@
 #ifndef COBALT_INPUT_INPUT_DEVICE_MANAGER_H_
 #define COBALT_INPUT_INPUT_DEVICE_MANAGER_H_
 
+#include "cobalt/dom/pointer_event_init.h"
+#include "cobalt/dom/wheel_event_init.h"
 #include "cobalt/input/camera_3d.h"
 #include "cobalt/input/input_poller.h"
 #include "cobalt/input/key_event_handler.h"
@@ -29,6 +31,12 @@
 
 namespace input {
 
+typedef base::Callback<void(base::Token type, const dom::PointerEventInit&)>
+    PointerEventCallback;
+
+typedef base::Callback<void(base::Token type, const dom::WheelEventInit&)>
+    WheelEventCallback;
+
 // InputDeviceManager listens to events from platform-specific input devices
 // and maps them to platform-independent keyboard key events.
 class InputDeviceManager {
@@ -36,7 +44,9 @@
   // Creates an instance using a SystemWindow parameter.
   // This allows us to hook up keyboard events on desktop systems.
   static scoped_ptr<InputDeviceManager> CreateFromWindow(
-      const KeyboardEventCallback& callback,
+      const KeyboardEventCallback& keyboard_event_callback,
+      const PointerEventCallback& pointer_event_callback,
+      const WheelEventCallback& wheel_event_callback,
       system_window::SystemWindow* system_window);
 
   virtual ~InputDeviceManager() {}
diff --git a/src/cobalt/input/input_device_manager_desktop.cc b/src/cobalt/input/input_device_manager_desktop.cc
index 5925fe8..629b820 100644
--- a/src/cobalt/input/input_device_manager_desktop.cc
+++ b/src/cobalt/input/input_device_manager_desktop.cc
@@ -16,6 +16,14 @@
 
 #include <string>
 
+#include "cobalt/base/token.h"
+#include "cobalt/base/tokens.h"
+#include "cobalt/dom/keyboard_event.h"
+#include "cobalt/dom/keyboard_event_init.h"
+#include "cobalt/dom/pointer_event.h"
+#include "cobalt/dom/pointer_event_init.h"
+#include "cobalt/dom/wheel_event.h"
+#include "cobalt/dom/wheel_event_init.h"
 #include "cobalt/input/create_default_camera_3d.h"
 #include "cobalt/input/input_poller_impl.h"
 #include "cobalt/system_window/input_event.h"
@@ -24,13 +32,17 @@
 namespace input {
 
 InputDeviceManagerDesktop::InputDeviceManagerDesktop(
-    const KeyboardEventCallback& callback,
+    const KeyboardEventCallback& keyboard_event_callback,
+    const PointerEventCallback& pointer_event_callback,
+    const WheelEventCallback& wheel_event_callback,
     system_window::SystemWindow* system_window)
     : system_window_(system_window),
-      keyboard_event_callback_(
+      input_event_callback_(
           base::Bind(&InputDeviceManagerDesktop::HandleInputEvent,
                      base::Unretained(this))),
-      keypress_generator_filter_(callback) {
+      keypress_generator_filter_(keyboard_event_callback),
+      pointer_event_callback_(pointer_event_callback),
+      wheel_event_callback_(wheel_event_callback) {
   input_poller_ = new InputPollerImpl();
   DCHECK(system_window_);
   camera_3d_ =
@@ -39,7 +51,7 @@
   if (system_window_) {
     // Add this object's keyboard event callback to the system window.
     system_window_->event_dispatcher()->AddEventCallback(
-        system_window::InputEvent::TypeId(), keyboard_event_callback_);
+        system_window::InputEvent::TypeId(), input_event_callback_);
   }
 }
 
@@ -47,31 +59,199 @@
   // If we have an associated system window, remove our callback from it.
   if (system_window_) {
     system_window_->event_dispatcher()->RemoveEventCallback(
-        system_window::InputEvent::TypeId(), keyboard_event_callback_);
+        system_window::InputEvent::TypeId(), input_event_callback_);
   }
 }
 
+namespace {
+
+COMPILE_ASSERT(static_cast<uint32_t>(kSbKeyModifiersNone) ==
+                       system_window::InputEvent::kNoModifier &&
+                   static_cast<uint32_t>(kSbKeyModifiersAlt) ==
+                       system_window::InputEvent::kAltKey &&
+                   static_cast<uint32_t>(kSbKeyModifiersCtrl) ==
+                       system_window::InputEvent::kCtrlKey &&
+                   static_cast<uint32_t>(kSbKeyModifiersMeta) ==
+                       system_window::InputEvent::kMetaKey &&
+                   static_cast<uint32_t>(kSbKeyModifiersShift) ==
+                       system_window::InputEvent::kShiftKey,
+               Mismatched_modifier_enums);
+#if SB_API_VERSION >= SB_POINTER_INPUT_API_VERSION
+COMPILE_ASSERT(static_cast<uint32_t>(kSbKeyModifiersPointerButtonLeft) ==
+                       system_window::InputEvent::kLeftButton &&
+                   static_cast<uint32_t>(kSbKeyModifiersPointerButtonRight) ==
+                       system_window::InputEvent::kRightButton &&
+                   static_cast<uint32_t>(kSbKeyModifiersPointerButtonMiddle) ==
+                       system_window::InputEvent::kMiddleButton &&
+                   static_cast<uint32_t>(kSbKeyModifiersPointerButtonBack) ==
+                       system_window::InputEvent::kBackButton &&
+                   static_cast<uint32_t>(kSbKeyModifiersPointerButtonForward) ==
+                       system_window::InputEvent::kForwardButton,
+               Mismatched_modifier_enums);
+#endif
+
+void UpdateEventModifierInit(const system_window::InputEvent* input_event,
+                             EventModifierInit* event) {
+  const uint32 modifiers = input_event->modifiers();
+  event->set_ctrl_key(modifiers & system_window::InputEvent::kCtrlKey);
+  event->set_shift_key(modifiers & system_window::InputEvent::kShiftKey);
+  event->set_alt_key(modifiers & system_window::InputEvent::kAltKey);
+  event->set_meta_key(modifiers & system_window::InputEvent::kMetaKey);
+}
+
+void UpdateMouseEventInitButton(int key_code, MouseEventInit* event) {
+  // The value of the button attribute MUST be as follows:
+  //  https://www.w3.org/TR/uievents/#ref-for-dom-mouseevent-button-1
+  if (key_code == kSbKeyMouse1) {
+    // 0 MUST indicate the primary button of the device (in general, the left
+    // button or the only button on single-button devices, used to activate a
+    // user interface control or select text) or the un-initialized value.
+    event->set_button(0);
+  } else if (key_code == kSbKeyMouse2) {
+    // 1 MUST indicate the auxiliary button (in general, the middle button,
+    // often combined with a mouse wheel).
+    event->set_button(1);
+  } else if (key_code == kSbKeyMouse3) {
+    // 2 MUST indicate the secondary button (in general, the right button, often
+    // used to display a context menu).
+    event->set_button(2);
+  }
+}
+
+void UpdateMouseEventInitButtons(uint32 modifiers, MouseEventInit* event) {
+  // The value of the buttons attribute MUST be as follows:
+  //  https://www.w3.org/TR/uievents/#ref-for-dom-mouseevent-buttons-3
+
+  // 0 MUST indicate no button is currently active.
+  uint16 buttons = 0;
+  if (modifiers & system_window::InputEvent::kLeftButton) {
+    // 1 MUST indicate the primary button of the device (in general, the left
+    // button or the only button on single-button devices, used to activate a
+    // user interface control or select text).
+    buttons |= 1;
+  }
+  if (modifiers & system_window::InputEvent::kRightButton) {
+    // 2 MUST indicate the secondary button (in general, the right button, often
+    // used to display a context menu), if present.
+    buttons |= 2;
+  }
+  if (modifiers & system_window::InputEvent::kMiddleButton) {
+    // 4 MUST indicate the auxiliary button (in general, the middle button,
+    // often combined with a mouse wheel).
+    buttons |= 4;
+  }
+  event->set_buttons(buttons);
+}
+
+void UpdateMouseEventInit(const system_window::InputEvent* input_event,
+                          dom::MouseEventInit* mouse_event) {
+  UpdateEventModifierInit(input_event, mouse_event);
+  UpdateMouseEventInitButton(input_event->key_code(), mouse_event);
+  UpdateMouseEventInitButtons(input_event->modifiers(), mouse_event);
+
+  mouse_event->set_screen_x(input_event->position().x());
+  mouse_event->set_screen_y(input_event->position().y());
+  mouse_event->set_client_x(input_event->position().x());
+  mouse_event->set_client_y(input_event->position().y());
+}
+
+}  // namespace
+
+void InputDeviceManagerDesktop::HandleKeyboardEvent(
+    bool is_key_down, const system_window::InputEvent* input_event,
+    int key_code) {
+  base::Token type =
+      is_key_down ? base::Tokens::keydown() : base::Tokens::keyup();
+  dom::KeyboardEvent::KeyLocationCode location =
+      dom::KeyboardEvent::KeyCodeToKeyLocation(key_code);
+  dom::KeyboardEventInit keyboard_event;
+  UpdateEventModifierInit(input_event, &keyboard_event);
+  keyboard_event.set_location(location);
+  keyboard_event.set_repeat(input_event->is_repeat());
+  keyboard_event.set_char_code(key_code);
+  keyboard_event.set_key_code(key_code);
+  keypress_generator_filter_.HandleKeyboardEvent(type, keyboard_event);
+}
+
+void InputDeviceManagerDesktop::HandlePointerEvent(
+    base::Token type, const system_window::InputEvent* input_event) {
+  dom::PointerEventInit pointer_event;
+  UpdateMouseEventInit(input_event, &pointer_event);
+
+  pointer_event.set_pointer_id(input_event->device_id());
+#if SB_API_VERSION >= SB_POINTER_INPUT_API_VERSION
+  pointer_event.set_width(input_event->size().x());
+  pointer_event.set_height(input_event->size().y());
+  pointer_event.set_pressure(input_event->pressure());
+  pointer_event.set_tilt_x(input_event->tilt().x());
+  pointer_event.set_tilt_y(input_event->tilt().y());
+#endif
+  pointer_event.set_is_primary(true);
+  pointer_event_callback_.Run(type, pointer_event);
+}
+
+void InputDeviceManagerDesktop::HandleWheelEvent(
+    const system_window::InputEvent* input_event) {
+  base::Token type = base::Tokens::wheel();
+  dom::WheelEventInit wheel_event;
+  UpdateMouseEventInit(input_event, &wheel_event);
+
+  wheel_event.set_delta_x(input_event->delta().x());
+  wheel_event.set_delta_y(input_event->delta().y());
+  wheel_event.set_delta_z(0);
+  wheel_event.set_delta_mode(dom::WheelEvent::kDomDeltaLine);
+
+  wheel_event_callback_.Run(type, wheel_event);
+}
+
 void InputDeviceManagerDesktop::HandleInputEvent(const base::Event* event) {
   // The user has pressed a key on the keyboard.
   const system_window::InputEvent* input_event =
       base::polymorphic_downcast<const system_window::InputEvent*>(event);
-  const int key_code = input_event->key_code();
-  const uint32 modifiers = input_event->modifiers();
+  SB_DCHECK(input_event);
+  int key_code = input_event->key_code();
 
-  if (input_event->type() == system_window::InputEvent::kKeyDown ||
-      input_event->type() == system_window::InputEvent::kKeyUp) {
-    dom::KeyboardEvent::KeyLocationCode location =
-        dom::KeyboardEvent::KeyCodeToKeyLocation(key_code);
+  switch (input_event->type()) {
+    case system_window::InputEvent::kKeyDown:
+      HandleKeyboardEvent(true, input_event, key_code);
+      break;
+    case system_window::InputEvent::kKeyUp:
+      HandleKeyboardEvent(false, input_event, key_code);
+      break;
+    case system_window::InputEvent::kPointerDown:
+    case system_window::InputEvent::kPointerUp: {
+      if ((kSbKeyBrowserBack == key_code) ||
+          (kSbKeyBrowserForward == key_code) || (kSbKeyMouse2 == key_code)) {
+        // For consistency with behavior on current browsers, the 'Forward' and
+        // 'Back' mouse buttons are reported as keypress input for the 'Forward'
+        // and 'Back' navigation keys, not as Pointer Events for the X1 and X2
+        // buttons.
 
-    dom::KeyboardEvent::Type key_event_type =
-        input_event->type() == system_window::InputEvent::kKeyDown
-            ? dom::KeyboardEvent::kTypeKeyDown
-            : dom::KeyboardEvent::kTypeKeyUp;
-    dom::KeyboardEvent::Data keyboard_event(key_event_type, location, modifiers,
-                                            key_code, key_code,
-                                            input_event->is_repeat());
-
-    keypress_generator_filter_.HandleKeyboardEvent(keyboard_event);
+        if (kSbKeyMouse2 == key_code) {
+          // Temporarily Report middle button presses as the enter key.
+          key_code = kSbKeyReturn;
+        }
+        HandleKeyboardEvent(
+            input_event->type() == system_window::InputEvent::kPointerDown,
+            input_event, key_code);
+      } else {
+        base::Token type =
+            input_event->type() == system_window::InputEvent::kPointerDown
+                ? base::Tokens::pointerdown()
+                : base::Tokens::pointerup();
+        HandlePointerEvent(type, input_event);
+      }
+      break;
+    }
+    case system_window::InputEvent::kPointerMove: {
+      HandlePointerEvent(base::Tokens::pointermove(), input_event);
+      break;
+    }
+    case system_window::InputEvent::kWheel: {
+      HandleWheelEvent(input_event);
+    }
+    default:
+      break;
   }
 
   InputPollerImpl* input_poller_impl =
diff --git a/src/cobalt/input/input_device_manager_desktop.h b/src/cobalt/input/input_device_manager_desktop.h
index e2b7d57..13dfbf0 100644
--- a/src/cobalt/input/input_device_manager_desktop.h
+++ b/src/cobalt/input/input_device_manager_desktop.h
@@ -24,8 +24,11 @@
 
 class InputDeviceManagerDesktop : public InputDeviceManager {
  public:
-  InputDeviceManagerDesktop(const KeyboardEventCallback& callback,
-                            system_window::SystemWindow* system_window);
+  InputDeviceManagerDesktop(
+      const KeyboardEventCallback& keyboard_event_callback,
+      const PointerEventCallback& pointer_event_callback,
+      const WheelEventCallback& wheel_event_callback,
+      system_window::SystemWindow* system_window);
 
   ~InputDeviceManagerDesktop() OVERRIDE;
 
@@ -35,15 +38,29 @@
   void HandleInputEvent(const base::Event* event);
 
  private:
+  void HandleKeyboardEvent(bool is_key_down,
+                           const system_window::InputEvent* input_event,
+                           int key_code);
+  void HandlePointerEvent(base::Token type,
+                          const system_window::InputEvent* input_event);
+
+  void HandleWheelEvent(const system_window::InputEvent* input_event);
+
   // Reference to the system window that will provide keyboard events.
   system_window::SystemWindow* system_window_;
 
-  // Store a callback wrapping the object event handler, HandleKeyboardEvent.
+  // Store a callback wrapping the object event handler, HandleInputEvent.
   // This is so we can remove it again when this object is destroyed.
-  base::EventCallback keyboard_event_callback_;
+  base::EventCallback input_event_callback_;
 
   // Keyboard event filters to process the events generated.
   KeypressGeneratorFilter keypress_generator_filter_;
+
+  // Called to handle a pointer event.
+  PointerEventCallback pointer_event_callback_;
+
+  // Called to handle a wheel event.
+  WheelEventCallback wheel_event_callback_;
 };
 
 }  // namespace input
diff --git a/src/cobalt/input/input_device_manager_fuzzer.cc b/src/cobalt/input/input_device_manager_fuzzer.cc
index 45b54c1..1215128 100644
--- a/src/cobalt/input/input_device_manager_fuzzer.cc
+++ b/src/cobalt/input/input_device_manager_fuzzer.cc
@@ -16,17 +16,16 @@
 
 #include "base/basictypes.h"
 #include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
 #include "base/message_loop.h"
 #include "base/rand_util.h"
 #include "cobalt/base/tokens.h"
+#include "cobalt/dom/keyboard_event.h"
 #include "cobalt/dom/keycode.h"
 
 namespace cobalt {
 namespace input {
 
-using base::TimeDelta;
-using dom::KeyboardEvent;
-
 namespace {
 
 // Initialize the set of key events we are able to produce.
@@ -77,7 +76,7 @@
   return key_codes_[index];
 }
 
-TimeDelta InputDeviceManagerFuzzer::KeyInfo::GetRandomDelay() const {
+base::TimeDelta InputDeviceManagerFuzzer::KeyInfo::GetRandomDelay() const {
   if (minimum_delay_ == maximum_delay_) {
     return minimum_delay_;
   }
@@ -85,7 +84,8 @@
       (maximum_delay_ - minimum_delay_).InMicroseconds();
   diff_in_microseconds = static_cast<int64>(
       base::RandGenerator(static_cast<uint64>(diff_in_microseconds)));
-  return minimum_delay_ + TimeDelta::FromMicroseconds(diff_in_microseconds);
+  return minimum_delay_ +
+         base::TimeDelta::FromMicroseconds(diff_in_microseconds);
 }
 
 InputDeviceManagerFuzzer::InputDeviceManagerFuzzer(
@@ -94,13 +94,13 @@
       next_key_index_(0),
       thread_("InputDeviceManagerFuzzer") {
   key_infos_.push_back(KeyInfo(kKeyCodes, arraysize(kKeyCodes),
-                               TimeDelta::FromMilliseconds(400)));
+                               base::TimeDelta::FromMilliseconds(400)));
   // Modify the key_infos_ to use different input patterns.  For example, the
   // following pattern can be used to test play and stop of a video repeatedly.
   //   key_infos_.push_back(KeyInfo(keycode::kReturn,
-  //                        TimeDelta::FromSeconds(1)));
+  //                        base::TimeDelta::FromSeconds(1)));
   //   key_infos_.push_back(KeyInfo(keycode::kEscape,
-  //                        TimeDelta::FromSeconds(1)));
+  //                        base::TimeDelta::FromSeconds(1)));
 
   // Schedule task to send the first key event.  Add an explicit delay to avoid
   // possible conflicts with debug console.
@@ -115,17 +115,11 @@
   DCHECK_LT(next_key_index_, key_infos_.size());
   int key_code = key_infos_[next_key_index_].GetRandomKeyCode();
 
-  KeyboardEvent::Data key_down_event(
-      KeyboardEvent::kTypeKeyDown, KeyboardEvent::kDomKeyLocationStandard,
-      KeyboardEvent::kNoModifier, key_code, 0, false);
+  dom::KeyboardEventInit event_init;
+  event_init.set_key_code(key_code);
 
-  keyboard_event_callback_.Run(key_down_event);
-
-  KeyboardEvent::Data key_up_event(
-      KeyboardEvent::kTypeKeyUp, KeyboardEvent::kDomKeyLocationStandard,
-      KeyboardEvent::kNoModifier, key_code, 0, false);
-
-  keyboard_event_callback_.Run(key_up_event);
+  keyboard_event_callback_.Run(base::Tokens::keydown(), event_init);
+  keyboard_event_callback_.Run(base::Tokens::keyup(), event_init);
 
   MessageLoop::current()->PostDelayedTask(
       FROM_HERE, base::Bind(&InputDeviceManagerFuzzer::OnNextEvent,
diff --git a/src/cobalt/input/input_device_manager_starboard.cc b/src/cobalt/input/input_device_manager_starboard.cc
index 95508a0..9c21515 100644
--- a/src/cobalt/input/input_device_manager_starboard.cc
+++ b/src/cobalt/input/input_device_manager_starboard.cc
@@ -19,10 +19,13 @@
 
 // static
 scoped_ptr<InputDeviceManager> InputDeviceManager::CreateFromWindow(
-    const KeyboardEventCallback& callback,
+    const KeyboardEventCallback& keyboard_event_callback,
+    const PointerEventCallback& pointer_event_callback,
+    const WheelEventCallback& wheel_event_callback,
     system_window::SystemWindow* system_window) {
-  return scoped_ptr<InputDeviceManager>(
-      new InputDeviceManagerDesktop(callback, system_window));
+  return scoped_ptr<InputDeviceManager>(new InputDeviceManagerDesktop(
+      keyboard_event_callback, pointer_event_callback, wheel_event_callback,
+      system_window));
 }
 
 }  // namespace input
diff --git a/src/cobalt/input/input_poller_impl.cc b/src/cobalt/input/input_poller_impl.cc
index 526415c..008c249 100644
--- a/src/cobalt/input/input_poller_impl.cc
+++ b/src/cobalt/input/input_poller_impl.cc
@@ -93,6 +93,12 @@
           NOTREACHED();
       }
     } break;
+    case system_window::InputEvent::kPointerDown:
+    case system_window::InputEvent::kPointerUp:
+    case system_window::InputEvent::kPointerMove:
+    case system_window::InputEvent::kWheel:
+      // Pointer and Wheel events are ignored here.
+      break;
     default:
       NOTREACHED();
   }
diff --git a/src/cobalt/input/key_event_handler.cc b/src/cobalt/input/key_event_handler.cc
index 99b824f..9a1e8c2 100644
--- a/src/cobalt/input/key_event_handler.cc
+++ b/src/cobalt/input/key_event_handler.cc
@@ -18,8 +18,8 @@
 namespace input {
 
 void KeyEventHandler::HandleKeyboardEvent(
-    const dom::KeyboardEvent::Data& keyboard_event) {
-  DispatchKeyboardEvent(keyboard_event);
+    base::Token type, const dom::KeyboardEventInit& keyboard_event) {
+  DispatchKeyboardEvent(type, keyboard_event);
 }
 
 KeyEventHandler::KeyEventHandler(const KeyboardEventCallback& callback)
@@ -29,13 +29,13 @@
     : keyboard_event_filter_(filter) {}
 
 void KeyEventHandler::DispatchKeyboardEvent(
-    const dom::KeyboardEvent::Data& keyboard_event) const {
+    base::Token type, const dom::KeyboardEventInit& keyboard_event) const {
   // If we have a key filter attached to this object, let it filter the event,
   // otherwise call the stored callback function directly.
   if (keyboard_event_filter_) {
-    keyboard_event_filter_->HandleKeyboardEvent(keyboard_event);
+    keyboard_event_filter_->HandleKeyboardEvent(type, keyboard_event);
   } else {
-    keyboard_event_callback_.Run(keyboard_event);
+    keyboard_event_callback_.Run(type, keyboard_event);
   }
 }
 
diff --git a/src/cobalt/input/key_event_handler.h b/src/cobalt/input/key_event_handler.h
index a208139..e418dd5 100644
--- a/src/cobalt/input/key_event_handler.h
+++ b/src/cobalt/input/key_event_handler.h
@@ -16,12 +16,13 @@
 #define COBALT_INPUT_KEY_EVENT_HANDLER_H_
 
 #include "base/callback.h"
-#include "cobalt/dom/keyboard_event.h"
+#include "cobalt/base/token.h"
+#include "cobalt/dom/keyboard_event_init.h"
 
 namespace cobalt {
 namespace input {
 
-typedef base::Callback<void(const dom::KeyboardEvent::Data&)>
+typedef base::Callback<void(base::Token type, const dom::KeyboardEventInit&)>
     KeyboardEventCallback;
 
 // Base class for objects that can process keyboard events.
@@ -36,7 +37,8 @@
   // DispatchKeyboardEvent. Overridden versions of this function may modify the
   // incoming event and/or generate new events. Overriden versions should call
   // DispatchKeyboardEvent for each event filtered/produced.
-  virtual void HandleKeyboardEvent(const dom::KeyboardEvent::Data& event);
+  virtual void HandleKeyboardEvent(base::Token type,
+                                   const dom::KeyboardEventInit& event);
 
  protected:
   explicit KeyEventHandler(const KeyboardEventCallback& callback);
@@ -48,7 +50,8 @@
   // Called to dispatch a key event. The event will either be sent to the key
   // event filter attached to this object or passed directly to the stored
   // callback function if there is no attached filter.
-  void DispatchKeyboardEvent(const dom::KeyboardEvent::Data& event) const;
+  void DispatchKeyboardEvent(base::Token type,
+                             const dom::KeyboardEventInit& event) const;
 
  private:
   // The event callback should not be called directly by subclasses.
diff --git a/src/cobalt/input/key_repeat_filter.cc b/src/cobalt/input/key_repeat_filter.cc
index ccc0b37..20a0eb4 100644
--- a/src/cobalt/input/key_repeat_filter.cc
+++ b/src/cobalt/input/key_repeat_filter.cc
@@ -14,6 +14,7 @@
 
 #include "cobalt/input/key_repeat_filter.h"
 
+#include "cobalt/base/token.h"
 #include "cobalt/base/tokens.h"
 
 namespace cobalt {
@@ -36,22 +37,22 @@
     : KeyEventHandler(filter) {}
 
 void KeyRepeatFilter::HandleKeyboardEvent(
-    const dom::KeyboardEvent::Data& keyboard_event) {
-  if (keyboard_event.type == dom::KeyboardEvent::kTypeKeyDown) {
-    HandleKeyDown(keyboard_event);
+    base::Token type, const dom::KeyboardEventInit& keyboard_event) {
+  if (type == base::Tokens::keydown()) {
+    HandleKeyDown(type, keyboard_event);
   }
 
-  if (keyboard_event.type == dom::KeyboardEvent::kTypeKeyUp) {
-    HandleKeyUp(keyboard_event);
+  if (type == base::Tokens::keyup()) {
+    HandleKeyUp(type, keyboard_event);
   }
 }
 
 void KeyRepeatFilter::HandleKeyDown(
-    const dom::KeyboardEvent::Data& keyboard_event) {
+    base::Token type, const dom::KeyboardEventInit& keyboard_event) {
   // Record the information of the KeyboardEvent for firing repeat events.
   last_event_data_ = keyboard_event;
 
-  DispatchKeyboardEvent(keyboard_event);
+  DispatchKeyboardEvent(type, keyboard_event);
 
   // This key down event is triggered for the first time, so start the timer
   // with |kRepeatInitialDelay|.
@@ -60,21 +61,21 @@
 }
 
 void KeyRepeatFilter::HandleKeyUp(
-    const dom::KeyboardEvent::Data& keyboard_event) {
-  DispatchKeyboardEvent(keyboard_event);
+    base::Token type, const dom::KeyboardEventInit& keyboard_event) {
+  DispatchKeyboardEvent(type, keyboard_event);
 
   // If it is a key up event and it matches the previous one, stop the key
   // repeat timer.
-  if (last_event_data_->key_code == keyboard_event.key_code) {
+  if (last_event_data_->key_code() == keyboard_event.key_code()) {
     key_repeat_timer_.Stop();
   }
 }
 
 void KeyRepeatFilter::FireKeyRepeatEvent() {
-  dom::KeyboardEvent::Data repeat_event(*last_event_data_);
-  repeat_event.repeat = true;
+  dom::KeyboardEventInit repeat_event(*last_event_data_);
+  repeat_event.set_repeat(true);
 
-  DispatchKeyboardEvent(repeat_event);
+  DispatchKeyboardEvent(base::Tokens::keydown(), repeat_event);
 
   // If |FireKeyRepeatEvent| is triggered for the first time then reset the
   // timer to the repeat rate instead of the initial delay.
diff --git a/src/cobalt/input/key_repeat_filter.h b/src/cobalt/input/key_repeat_filter.h
index e1569b7..6b1dd0c 100644
--- a/src/cobalt/input/key_repeat_filter.h
+++ b/src/cobalt/input/key_repeat_filter.h
@@ -32,14 +32,16 @@
   explicit KeyRepeatFilter(KeyEventHandler* filter);
 
   void HandleKeyboardEvent(
-      const dom::KeyboardEvent::Data& keyboard_event) OVERRIDE;
+      base::Token type, const dom::KeyboardEventInit& keyboard_event) OVERRIDE;
 
  private:
-  void HandleKeyDown(const dom::KeyboardEvent::Data& keyboard_event);
-  void HandleKeyUp(const dom::KeyboardEvent::Data& keyboard_event);
+  void HandleKeyDown(base::Token type,
+                     const dom::KeyboardEventInit& keyboard_event);
+  void HandleKeyUp(base::Token type,
+                   const dom::KeyboardEventInit& keyboard_event);
   void FireKeyRepeatEvent();
 
-  base::optional<dom::KeyboardEvent::Data> last_event_data_;
+  base::optional<dom::KeyboardEventInit> last_event_data_;
 
   base::RepeatingTimer<KeyRepeatFilter> key_repeat_timer_;
 };
diff --git a/src/cobalt/input/keypress_generator_filter.cc b/src/cobalt/input/keypress_generator_filter.cc
index dab9a89..c34f05a 100644
--- a/src/cobalt/input/keypress_generator_filter.cc
+++ b/src/cobalt/input/keypress_generator_filter.cc
@@ -15,6 +15,7 @@
 #include "cobalt/input/keypress_generator_filter.h"
 
 #include "cobalt/base/tokens.h"
+#include "cobalt/dom/keyboard_event.h"
 #include "cobalt/dom/keycode.h"
 
 namespace cobalt {
@@ -28,41 +29,36 @@
     : KeyEventHandler(filter) {}
 
 void KeypressGeneratorFilter::HandleKeyboardEvent(
-    const dom::KeyboardEvent::Data& keyboard_event) {
+    base::Token type, const dom::KeyboardEventInit& keyboard_event) {
   // Handle the original event
-  DispatchKeyboardEvent(keyboard_event);
+  DispatchKeyboardEvent(type, keyboard_event);
 
   // If the event was a keydown, we may also generate a keypress event.
-  ConditionallyGenerateKeypressEvent(keyboard_event);
+  ConditionallyGenerateKeypressEvent(type, keyboard_event);
 }
 
 bool KeypressGeneratorFilter::ConditionallyGenerateKeypressEvent(
-    const dom::KeyboardEvent::Data& orig_event) {
+    base::Token type, const dom::KeyboardEventInit& orig_event) {
   // Ignore everything but keydown events.
-  if (orig_event.type != dom::KeyboardEvent::kTypeKeyDown) {
+  if (type != base::Tokens::keydown()) {
     return false;
   }
 
   // Don't generate a keypress event if one of the modifier keys other than
   // Shift is held down.
-  if (orig_event.modifiers & dom::UIEventWithKeyState::kAltKey ||
-      orig_event.modifiers & dom::UIEventWithKeyState::kCtrlKey ||
-      orig_event.modifiers & dom::UIEventWithKeyState::kMetaKey) {
+  if (orig_event.alt_key() || orig_event.ctrl_key() || orig_event.meta_key()) {
     return false;
   }
 
   // Get the char_code corresponding to the key_code of the event.
   // Only generate the keypress event if there is a valid char_code.
-  int key_code = orig_event.key_code;
-  int char_code =
-      dom::KeyboardEvent::ComputeCharCode(key_code, orig_event.modifiers);
+  int char_code = dom::KeyboardEvent::ComputeCharCode(orig_event.key_code(),
+                                                      orig_event.shift_key());
 
   if (char_code > 0) {
-    dom::KeyboardEvent::Data keypress_event(
-        dom::KeyboardEvent::kTypeKeyPress,
-        dom::KeyboardEvent::kDomKeyLocationStandard, orig_event.modifiers,
-        key_code, char_code, orig_event.repeat);
-    DispatchKeyboardEvent(keypress_event);
+    dom::KeyboardEventInit event(orig_event);
+    event.set_char_code(char_code);
+    DispatchKeyboardEvent(base::Tokens::keypress(), event);
     return true;
   }
 
diff --git a/src/cobalt/input/keypress_generator_filter.h b/src/cobalt/input/keypress_generator_filter.h
index 9ea280d..548b98d 100644
--- a/src/cobalt/input/keypress_generator_filter.h
+++ b/src/cobalt/input/keypress_generator_filter.h
@@ -15,7 +15,8 @@
 #ifndef COBALT_INPUT_KEYPRESS_GENERATOR_FILTER_H_
 #define COBALT_INPUT_KEYPRESS_GENERATOR_FILTER_H_
 
-#include "cobalt/dom/keyboard_event.h"
+#include "cobalt/base/token.h"
+#include "cobalt/dom/keyboard_event_init.h"
 #include "cobalt/input/key_event_handler.h"
 
 namespace cobalt {
@@ -31,14 +32,15 @@
 
   // Conditionally generates an additional keypress event.
   // Passes on the new and original events for further processing/handling.
-  void HandleKeyboardEvent(const dom::KeyboardEvent::Data& event) OVERRIDE;
+  void HandleKeyboardEvent(base::Token type,
+                           const dom::KeyboardEventInit& event) OVERRIDE;
 
  protected:
   // Generates a keypress event, if:
   // 1. The original event is a keydown.
   // 2. The keycode corresponds to a printable character, or BS/Enter.
   bool ConditionallyGenerateKeypressEvent(
-      const dom::KeyboardEvent::Data& orig_event);
+      base::Token type, const dom::KeyboardEventInit& orig_event);
 };
 
 }  // namespace input
diff --git a/src/cobalt/layout/box.cc b/src/cobalt/layout/box.cc
index 269f2d1..1f12a66 100644
--- a/src/cobalt/layout/box.cc
+++ b/src/cobalt/layout/box.cc
@@ -31,7 +31,10 @@
 #include "cobalt/layout/render_tree_animations.h"
 #include "cobalt/layout/size_layout_unit.h"
 #include "cobalt/layout/used_style.h"
+#include "cobalt/math/rect_f.h"
 #include "cobalt/math/transform_2d.h"
+#include "cobalt/math/vector2d.h"
+#include "cobalt/math/vector2d_f.h"
 #include "cobalt/render_tree/border.h"
 #include "cobalt/render_tree/brush.h"
 #include "cobalt/render_tree/color_rgba.h"
@@ -64,7 +67,8 @@
     : css_computed_style_declaration_(css_computed_style_declaration),
       used_style_provider_(used_style_provider),
       layout_stat_tracker_(layout_stat_tracker),
-      parent_(NULL) {
+      parent_(NULL),
+      render_sequence_(0) {
   DCHECK(animations());
   DCHECK(used_style_provider_);
 
@@ -410,6 +414,7 @@
     if (cached_render_tree_node_info_->node_) {
       parent_content_node_builder->AddChild(
           cached_render_tree_node_info_->node_);
+      render_sequence_ = parent_content_node_builder->children().size();
     }
     return;
   }
@@ -544,9 +549,43 @@
                   new AnimateNode(animate_node_builder, border_node));
 
     parent_content_node_builder->AddChild(cached_render_tree_node_info_->node_);
+    render_sequence_ = parent_content_node_builder->children().size();
   }
 }
 
+Box::RenderSequence Box::GetRenderSequence() {
+  std::vector<size_t> render_sequence;
+  Box* ancestor_box = this;
+  Box* box = NULL;
+  while (ancestor_box && (box != ancestor_box)) {
+    box = ancestor_box;
+    if (box->cached_render_tree_node_info_) {
+      render_sequence.push_back(box->render_sequence_);
+      if (box->IsPositioned() || box->IsTransformed()) {
+        ancestor_box = box->GetStackingContext();
+      } else {
+        ancestor_box = box->GetContainingBlock();
+      }
+    }
+  }
+  return render_sequence;
+}
+
+bool Box::IsRenderedLater(RenderSequence render_sequence,
+                          RenderSequence other_render_sequence) {
+  for (size_t step = 0; step < render_sequence.size(); ++step) {
+    if (other_render_sequence.size() < 1 + step) {
+      return true;
+    }
+    size_t idx = render_sequence.size() - 1 - step;
+    size_t other_idx = other_render_sequence.size() - 1 - step;
+    if (render_sequence[idx] != other_render_sequence[other_idx]) {
+      return render_sequence[idx] > other_render_sequence[other_idx];
+    }
+  }
+  return false;
+}
+
 AnonymousBlockBox* Box::AsAnonymousBlockBox() { return NULL; }
 ContainerBox* Box::AsContainerBox() { return NULL; }
 const ContainerBox* Box::AsContainerBox() const { return NULL; }
@@ -782,6 +821,14 @@
   }
 }
 
+bool Box::IsUnderCoordinate(const Vector2dLayoutUnit& coordinate) const {
+  RectLayoutUnit rect = GetBorderBox();
+  bool res =
+      coordinate.x() >= rect.x() && coordinate.x() <= rect.x() + rect.width() &&
+      coordinate.y() >= rect.y() && coordinate.y() <= rect.y() + rect.height();
+  return res;
+}
+
 void Box::UpdateCrossReferencesOfContainerBox(
     ContainerBox* source_box, bool is_nearest_containing_block,
     bool is_nearest_absolute_containing_block,
@@ -1299,5 +1346,30 @@
   }
 }
 
+void Box::UpdateCoordinateForTransform(math::Vector2dF* coordinate) const {
+  // If the element has a transform, it needs to be applied.
+  if (!computed_style()) {
+    return;
+  }
+  const scoped_refptr<cssom::PropertyValue>& transform =
+      computed_style()->transform();
+  if (transform != cssom::KeywordValue::GetNone()) {
+    math::Vector2dF border_box_offset(
+        left().toFloat() + margin_left().toFloat(),
+        top().toFloat() + margin_top().toFloat());
+    math::RectF rect = math::RectF(PointAtOffsetFromOrigin(border_box_offset),
+                                   GetBorderBoxSize());
+    math::Matrix3F matrix =
+        GetCSSTransform(transform, computed_style()->transform_origin(), rect);
+    if (!matrix.IsIdentity()) {
+      // transform the coordinate.
+      math::PointF transformed_point =
+          matrix.Inverse() * math::PointF(coordinate->x(), coordinate->y());
+      coordinate->set_x(transformed_point.x());
+      coordinate->set_y(transformed_point.y());
+    }
+  }
+}
+
 }  // namespace layout
 }  // namespace cobalt
diff --git a/src/cobalt/layout/box.h b/src/cobalt/layout/box.h
index 3484227..06e90ea 100644
--- a/src/cobalt/layout/box.h
+++ b/src/cobalt/layout/box.h
@@ -34,6 +34,8 @@
 #include "cobalt/layout/vector2d_layout_unit.h"
 #include "cobalt/math/point_f.h"
 #include "cobalt/math/rect_f.h"
+#include "cobalt/math/vector2d.h"
+#include "cobalt/math/vector2d_f.h"
 #include "cobalt/render_tree/animations/animate_node.h"
 #include "cobalt/render_tree/composition_node.h"
 #include "cobalt/web_animations/animation_set.h"
@@ -107,6 +109,14 @@
     kInlineLevel,
   };
 
+  // The RenderSequence of a box is used to compare the relative drawing order
+  // of boxes. It stores a value for the box's drawing order at each ancestor
+  // composition node up to the root of the render tree. As a result, starting
+  // from the root ancestor, the box for which the render sequence ends first,
+  // or for which the drawing order value at a composition node is lower is
+  // drawn before the other box.
+  typedef std::vector<size_t> RenderSequence;
+
   Box(const scoped_refptr<cssom::CSSComputedStyleDeclaration>&
           css_computed_style_declaration,
       UsedStyleProvider* used_style_provider,
@@ -140,7 +150,7 @@
   // Do not confuse with the formatting context that the element may establish.
   virtual Level GetLevel() const = 0;
 
-  // Returns true if the box is positioned (e.g. position is non-static or
+  // Returns trueif the box is positioned (e.g. position is non-static or
   // transform is not None).  Intuitively, this is true if the element does
   // not follow standard layout flow rules for determining its position.
   //   https://www.w3.org/TR/CSS21/visuren.html#positioned-element.
@@ -469,6 +479,22 @@
   // layout.
   void InvalidateParent() { parent_ = NULL; }
 
+  // Returns true if the box is positioned under the passed in coordinate.
+  bool IsUnderCoordinate(const Vector2dLayoutUnit& coordinate) const;
+
+  // Returns a data structure that can be used by Box::IsRenderedLater().
+  RenderSequence GetRenderSequence();
+
+  // Returns true if the box for the given render_sequence is rendered after
+  // the box for the other_render_sequence. The boxes must be from the same
+  // layout tree.
+  static bool IsRenderedLater(RenderSequence render_sequence,
+                              RenderSequence other_render_sequence);
+
+  // Updates the passed coordinate corresponding to the transform applied to
+  // this box.
+  void UpdateCoordinateForTransform(math::Vector2dF* coordinate) const;
+
  protected:
   UsedStyleProvider* used_style_provider() const {
     return used_style_provider_;
@@ -697,6 +723,11 @@
   // recalculated during each call to RenderAndAnimateContent.
   base::optional<CachedRenderTreeNodeInfo> cached_render_tree_node_info_;
 
+  // A value that indicates the drawing order relative to boxes with the same
+  // rendering ancestor box (which is either the stacking context or the
+  // containing block). Smaller values indicate boxes that are drawn earlier.
+  size_t render_sequence_;
+
   // For write access to parent/containing_block members.
   friend class ContainerBox;
   friend class LayoutBoxes;
diff --git a/src/cobalt/layout/box_generator.cc b/src/cobalt/layout/box_generator.cc
index e1f5288..5f9c536 100644
--- a/src/cobalt/layout/box_generator.cc
+++ b/src/cobalt/layout/box_generator.cc
@@ -918,8 +918,8 @@
   AppendPseudoElementToLine(html_element, dom::kBeforePseudoElementType);
 
   // Generate child boxes.
-  for (scoped_refptr<dom::Node> child_node = html_element->first_child();
-       child_node; child_node = child_node->next_sibling()) {
+  for (dom::Node* child_node = html_element->first_child(); child_node;
+       child_node = child_node->next_sibling()) {
     BoxGenerator child_box_generator(
         html_element->css_computed_style_declaration(),
         html_element->css_computed_style_declaration()->animations(),
diff --git a/src/cobalt/layout/layout.gyp b/src/cobalt/layout/layout.gyp
index e1b9469..fa7c03c 100644
--- a/src/cobalt/layout/layout.gyp
+++ b/src/cobalt/layout/layout.gyp
@@ -14,7 +14,7 @@
 
 {
   'variables': {
-    'cobalt_code': 1,
+    'sb_pedantic_warnings': 1,
   },
   'targets': [
     {
@@ -78,6 +78,8 @@
         'size_layout_unit.h',
         'text_box.cc',
         'text_box.h',
+        'topmost_event_target.cc',
+        'topmost_event_targer.h',
         'used_style.cc',
         'used_style.h',
         'vector2d_layout_unit.cc',
@@ -94,6 +96,11 @@
         '<(DEPTH)/cobalt/speech/speech.gyp:speech',
         '<(DEPTH)/third_party/icu/icu.gyp:icuuc',
       ],
+      # Exporting dom so that layout_test gets the transitive include paths to
+      # include generated headers.
+      'export_dependent_settings': [
+        '<(DEPTH)/cobalt/dom/dom.gyp:dom',
+      ],
     },
 
     {
diff --git a/src/cobalt/layout/layout_manager.cc b/src/cobalt/layout/layout_manager.cc
index c5be2b6..ba99c1f 100644
--- a/src/cobalt/layout/layout_manager.cc
+++ b/src/cobalt/layout/layout_manager.cc
@@ -42,9 +42,9 @@
  public:
   Impl(const std::string& name, const scoped_refptr<dom::Window>& window,
        const OnRenderTreeProducedCallback& on_render_tree_produced,
-       LayoutTrigger layout_trigger, int dom_max_element_depth,
-       float layout_refresh_rate, const std::string& language,
-       LayoutStatTracker* layout_stat_tracker);
+       const OnLayoutCallback& on_layout, LayoutTrigger layout_trigger,
+       int dom_max_element_depth, float layout_refresh_rate,
+       const std::string& language, LayoutStatTracker* layout_stat_tracker);
   ~Impl();
 
   // From dom::DocumentObserver.
@@ -72,6 +72,7 @@
   const icu::Locale locale_;
   const scoped_ptr<UsedStyleProvider> used_style_provider_;
   const OnRenderTreeProducedCallback on_render_tree_produced_callback_;
+  const OnLayoutCallback on_layout_callback_;
   const LayoutTrigger layout_trigger_;
 
   // This flag indicates whether or not we should do a re-layout.  The flag
@@ -150,15 +151,16 @@
 LayoutManager::Impl::Impl(
     const std::string& name, const scoped_refptr<dom::Window>& window,
     const OnRenderTreeProducedCallback& on_render_tree_produced,
-    LayoutTrigger layout_trigger, int dom_max_element_depth,
-    float layout_refresh_rate, const std::string& language,
-    LayoutStatTracker* layout_stat_tracker)
+    const OnLayoutCallback& on_layout, LayoutTrigger layout_trigger,
+    int dom_max_element_depth, float layout_refresh_rate,
+    const std::string& language, LayoutStatTracker* layout_stat_tracker)
     : window_(window),
       locale_(icu::Locale::createCanonical(language.c_str())),
       used_style_provider_(new UsedStyleProvider(
           window->html_element_context(), window->document()->font_cache(),
           base::Bind(&AttachCameraNodes, window))),
       on_render_tree_produced_callback_(on_render_tree_produced),
+      on_layout_callback_(on_layout),
       layout_trigger_(layout_trigger),
       layout_dirty_(StringPrintf("%s.Layout.IsDirty", name.c_str()), true,
                     "Non-zero when the layout is dirty and a new render tree "
@@ -361,17 +363,19 @@
 
     TRACE_EVENT_END0("cobalt::layout", kBenchmarkStatLayout);
   }
+
+  on_layout_callback_.Run();
 }
 
 LayoutManager::LayoutManager(
     const std::string& name, const scoped_refptr<dom::Window>& window,
     const OnRenderTreeProducedCallback& on_render_tree_produced,
-    LayoutTrigger layout_trigger, const int dom_max_element_depth,
-    const float layout_refresh_rate, const std::string& language,
-    LayoutStatTracker* layout_stat_tracker)
-    : impl_(new Impl(name, window, on_render_tree_produced, layout_trigger,
-                     dom_max_element_depth, layout_refresh_rate, language,
-                     layout_stat_tracker)) {}
+    const OnLayoutCallback& on_layout, LayoutTrigger layout_trigger,
+    const int dom_max_element_depth, const float layout_refresh_rate,
+    const std::string& language, LayoutStatTracker* layout_stat_tracker)
+    : impl_(new Impl(name, window, on_render_tree_produced, on_layout,
+                     layout_trigger, dom_max_element_depth, layout_refresh_rate,
+                     language, layout_stat_tracker)) {}
 
 LayoutManager::~LayoutManager() {}
 
diff --git a/src/cobalt/layout/layout_manager.h b/src/cobalt/layout/layout_manager.h
index e02cc0b..f2da289 100644
--- a/src/cobalt/layout/layout_manager.h
+++ b/src/cobalt/layout/layout_manager.h
@@ -49,6 +49,8 @@
   typedef base::Callback<void(const LayoutResults&)>
       OnRenderTreeProducedCallback;
 
+  typedef base::Callback<void()> OnLayoutCallback;
+
   // Specifies what event should trigger a layout, and hence what event
   // will result in a render tree being produced and passed in a call to
   // on_render_tree_produced_callback_.
@@ -62,7 +64,8 @@
   LayoutManager(const std::string& name,
                 const scoped_refptr<dom::Window>& window,
                 const OnRenderTreeProducedCallback& on_render_tree_produced,
-                LayoutTrigger layout_trigger, const int dom_max_element_depth,
+                const OnLayoutCallback& on_layout, LayoutTrigger layout_trigger,
+                const int dom_max_element_depth,
                 const float layout_refresh_rate, const std::string& language,
                 LayoutStatTracker* layout_stat_tracker);
   ~LayoutManager();
diff --git a/src/cobalt/layout/topmost_event_target.cc b/src/cobalt/layout/topmost_event_target.cc
new file mode 100644
index 0000000..547d103
--- /dev/null
+++ b/src/cobalt/layout/topmost_event_target.cc
@@ -0,0 +1,265 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/layout/topmost_event_target.h"
+
+#include <string>
+
+#include "cobalt/base/token.h"
+#include "cobalt/base/tokens.h"
+#include "cobalt/cssom/keyword_value.h"
+#include "cobalt/dom/document.h"
+#include "cobalt/dom/event.h"
+#include "cobalt/dom/html_element.h"
+#include "cobalt/dom/html_html_element.h"
+#include "cobalt/dom/mouse_event.h"
+#include "cobalt/dom/mouse_event_init.h"
+#include "cobalt/dom/pointer_event.h"
+#include "cobalt/dom/pointer_event_init.h"
+#include "cobalt/dom/ui_event.h"
+#include "cobalt/dom/wheel_event.h"
+#include "cobalt/layout/container_box.h"
+#include "cobalt/layout/used_style.h"
+#include "cobalt/math/vector2d.h"
+#include "cobalt/math/vector2d_f.h"
+
+namespace cobalt {
+namespace layout {
+
+void TopmostEventTarget::FindTopmostEventTarget(
+    const scoped_refptr<dom::Document>& document,
+    const math::Vector2dF& coordinate) {
+  const scoped_refptr<dom::HTMLElement>& html_element = document->html();
+  box_ = NULL;
+  html_element_ = html_element;
+  render_sequence_.clear();
+  if (html_element) {
+    dom::LayoutBoxes* boxes = html_element->layout_boxes();
+    if (boxes && boxes->type() == dom::LayoutBoxes::kLayoutLayoutBoxes) {
+      LayoutBoxes* layout_boxes = base::polymorphic_downcast<LayoutBoxes*>(
+          html_element->layout_boxes());
+      if (!layout_boxes->boxes().empty()) {
+        ConsiderElement(html_element, coordinate);
+      }
+    }
+  }
+}
+
+void TopmostEventTarget::ConsiderElement(
+    const scoped_refptr<dom::HTMLElement>& html_element,
+    const math::Vector2dF& coordinate) {
+  if (!html_element) return;
+  math::Vector2dF element_coordinate(coordinate);
+  dom::LayoutBoxes* boxes = html_element->layout_boxes();
+  if (boxes && boxes->type() == dom::LayoutBoxes::kLayoutLayoutBoxes) {
+    SB_DCHECK(html_element->computed_style());
+    LayoutBoxes* layout_boxes = base::polymorphic_downcast<LayoutBoxes*>(boxes);
+    const Boxes& boxes = layout_boxes->boxes();
+    if (!boxes.empty()) {
+      const Box* box = boxes.front();
+      box->UpdateCoordinateForTransform(&element_coordinate);
+
+      if (box->computed_style()->position() ==
+          cssom::KeywordValue::GetAbsolute()) {
+        // The containing block for position:absolute elements is formed by the
+        // padding box instead of the content box, as described in
+        // http://www.w3.org/TR/CSS21/visudet.html#containing-block-details.
+        element_coordinate +=
+            box->GetContainingBlock()->GetContentBoxOffsetFromPaddingBox();
+      }
+      ConsiderBoxes(html_element, layout_boxes, element_coordinate);
+    }
+  }
+
+  for (dom::Element* element = html_element->first_element_child(); element;
+       element = element->next_element_sibling()) {
+    dom::HTMLElement* child_html_element = element->AsHTMLElement();
+    if (child_html_element && child_html_element->computed_style()) {
+      ConsiderElement(child_html_element, element_coordinate);
+    }
+  }
+}
+
+void TopmostEventTarget::ConsiderBoxes(
+    const scoped_refptr<dom::HTMLElement>& html_element,
+    LayoutBoxes* layout_boxes, const math::Vector2dF& coordinate) {
+  const Boxes& boxes = layout_boxes->boxes();
+  Vector2dLayoutUnit layout_coordinate(LayoutUnit(coordinate.x()),
+                                       LayoutUnit(coordinate.y()));
+  for (Boxes::const_iterator box_iterator = boxes.begin();
+       box_iterator != boxes.end(); ++box_iterator) {
+    const scoped_refptr<Box>& box = *box_iterator;
+    if (box->IsUnderCoordinate(layout_coordinate)) {
+      Box::RenderSequence render_sequence = box->GetRenderSequence();
+      if (Box::IsRenderedLater(render_sequence, render_sequence_)) {
+        html_element_ = html_element;
+        box_ = box;
+        render_sequence_.swap(render_sequence);
+      }
+    }
+  }
+}
+
+void TopmostEventTarget::MaybeSendPointerEvents(
+    const scoped_refptr<dom::Event>& event,
+    const scoped_refptr<dom::Window>& window) {
+  const dom::MouseEvent* const mouse_event =
+      base::polymorphic_downcast<const dom::MouseEvent* const>(event.get());
+  SB_DCHECK(mouse_event);
+  const scoped_refptr<dom::Document>& document =
+      mouse_event->view()->document();
+
+  math::Vector2dF coordinate(mouse_event->client_x(), mouse_event->client_y());
+  FindTopmostEventTarget(document, coordinate);
+
+  if (html_element_) {
+    html_element_->DispatchEvent(event);
+  }
+  if (event->GetWrappableType() == base::GetTypeId<dom::PointerEvent>()) {
+    const dom::PointerEvent* const pointer_event =
+        base::polymorphic_downcast<const dom::PointerEvent* const>(event.get());
+
+    // Send compatibility mapping mouse events if needed.
+    //  https://www.w3.org/TR/2015/REC-pointerevents-20150224/#compatibility-mapping-with-mouse-events
+    if (html_element_) {
+      bool has_compatibility_mouse_event = false;
+      base::Token type;
+      if (pointer_event->type() == base::Tokens::pointerdown()) {
+        type = base::Tokens::mousedown();
+        has_compatibility_mouse_event = true;
+      } else if (pointer_event->type() == base::Tokens::pointerup()) {
+        type = base::Tokens::mouseup();
+        has_compatibility_mouse_event = true;
+      } else if (pointer_event->type() == base::Tokens::pointermove()) {
+        type = base::Tokens::mousemove();
+        has_compatibility_mouse_event = true;
+      }
+      if (has_compatibility_mouse_event) {
+        dom::MouseEventInit mouse_event_init;
+        mouse_event_init.set_screen_x(pointer_event->screen_x());
+        mouse_event_init.set_screen_y(pointer_event->screen_x());
+        mouse_event_init.set_client_x(pointer_event->screen_x());
+        mouse_event_init.set_client_y(pointer_event->screen_x());
+        mouse_event_init.set_button(pointer_event->button());
+        mouse_event_init.set_buttons(pointer_event->buttons());
+        html_element_->DispatchEvent(
+            new dom::MouseEvent(type, window, mouse_event_init));
+        if (pointer_event->type() == base::Tokens::pointerup()) {
+          type = base::Tokens::click();
+          html_element_->DispatchEvent(
+              new dom::MouseEvent(type, window, mouse_event_init));
+        }
+      }
+    }
+
+    // Send enter/leave/over/out (status change) events when needed.
+    if (previous_html_element_ != html_element_) {
+      // Store the data for the status change event(s).
+      dom::PointerEventInit event_init;
+      event_init.set_related_target(previous_html_element_);
+      const dom::MouseEvent* const pointer_event =
+          base::polymorphic_downcast<const dom::PointerEvent* const>(
+              event.get());
+      event_init.set_screen_x(pointer_event->screen_x());
+      event_init.set_screen_y(pointer_event->screen_x());
+      event_init.set_client_x(pointer_event->screen_x());
+      event_init.set_client_y(pointer_event->screen_x());
+      if (event->GetWrappableType() == base::GetTypeId<dom::PointerEvent>()) {
+        const dom::PointerEvent* const pointer_event =
+            base::polymorphic_downcast<const dom::PointerEvent* const>(
+                event.get());
+        event_init.set_pointer_id(pointer_event->pointer_id());
+        event_init.set_width(pointer_event->width());
+        event_init.set_height(pointer_event->height());
+        event_init.set_pressure(pointer_event->pressure());
+        event_init.set_tilt_x(pointer_event->tilt_x());
+        event_init.set_tilt_y(pointer_event->tilt_y());
+        event_init.set_pointer_type(pointer_event->pointer_type());
+        event_init.set_is_primary(pointer_event->is_primary());
+      }
+
+      // The enter/leave status change events apply to all ancestors up to the
+      // nearest common ancestor between the previous and current element.
+      scoped_refptr<dom::Element> nearest_common_ancestor;
+
+      if (previous_html_element_) {
+        previous_html_element_->DispatchEvent(new dom::PointerEvent(
+            base::Tokens::pointerout(), window, event_init));
+        previous_html_element_->DispatchEvent(
+            new dom::MouseEvent(base::Tokens::mouseout(), window, event_init));
+
+        // Find the nearest common ancestor, if there is any.
+        dom::Document* previous_document =
+            previous_html_element_->node_document();
+        if (previous_document) {
+          if (html_element_ &&
+              previous_document == html_element_->node_document()) {
+            // The nearest ancestor of the current element that is already
+            // designated is the nearest common ancestor of it and the previous
+            // element.
+            nearest_common_ancestor = html_element_;
+            while (nearest_common_ancestor &&
+                   nearest_common_ancestor->AsHTMLElement() &&
+                   !nearest_common_ancestor->AsHTMLElement()->IsDesignated()) {
+              nearest_common_ancestor =
+                  nearest_common_ancestor->parent_element();
+            }
+          }
+
+          for (scoped_refptr<dom::Element> element = previous_html_element_;
+               element != nearest_common_ancestor;
+               element = element->parent_element()) {
+            element->DispatchEvent(new dom::PointerEvent(
+                base::Tokens::pointerleave(), dom::Event::kNotBubbles,
+                dom::Event::kNotCancelable, window, event_init));
+            element->DispatchEvent(new dom::MouseEvent(
+                base::Tokens::mouseleave(), dom::Event::kNotBubbles,
+                dom::Event::kNotCancelable, window, event_init));
+          }
+
+          if (!html_element_ ||
+              previous_document != html_element_->node_document()) {
+            previous_document->SetIndicatedElement(NULL);
+          }
+        }
+      }
+      if (html_element_) {
+        html_element_->DispatchEvent(new dom::PointerEvent(
+            base::Tokens::pointerover(), window, event_init));
+        html_element_->DispatchEvent(
+            new dom::MouseEvent(base::Tokens::mouseover(), window, event_init));
+
+        for (scoped_refptr<dom::Element> element = html_element_;
+             element != nearest_common_ancestor;
+             element = element->parent_element()) {
+          element->DispatchEvent(new dom::PointerEvent(
+              base::Tokens::pointerenter(), dom::Event::kNotBubbles,
+              dom::Event::kNotCancelable, window, event_init));
+          element->DispatchEvent(new dom::MouseEvent(
+              base::Tokens::mouseenter(), dom::Event::kNotBubbles,
+              dom::Event::kNotCancelable, window, event_init));
+        }
+
+        dom::Document* document = html_element_->node_document();
+        if (document) {
+          document->SetIndicatedElement(html_element_);
+        }
+      }
+      previous_html_element_ = html_element_;
+    }
+  }
+}
+
+}  // namespace layout
+}  // namespace cobalt
diff --git a/src/cobalt/layout/topmost_event_target.h b/src/cobalt/layout/topmost_event_target.h
new file mode 100644
index 0000000..71e8166
--- /dev/null
+++ b/src/cobalt/layout/topmost_event_target.h
@@ -0,0 +1,55 @@
+// 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_LAYOUT_TOPMOST_EVENT_TARGET_H_
+#define COBALT_LAYOUT_TOPMOST_EVENT_TARGET_H_
+
+#include "cobalt/dom/document.h"
+#include "cobalt/dom/html_element.h"
+#include "cobalt/dom/window.h"
+#include "cobalt/layout/box.h"
+#include "cobalt/layout/layout_boxes.h"
+#include "cobalt/math/vector2d.h"
+#include "cobalt/math/vector2d_f.h"
+
+namespace cobalt {
+namespace layout {
+
+class TopmostEventTarget {
+ public:
+  explicit TopmostEventTarget() {}
+
+  void MaybeSendPointerEvents(const scoped_refptr<dom::Event>& event,
+                              const scoped_refptr<dom::Window>& window);
+
+  scoped_refptr<dom::HTMLElement> previous_html_element_;
+  scoped_refptr<dom::HTMLElement> html_element_;
+  scoped_refptr<Box> box_;
+  Box::RenderSequence render_sequence_;
+
+ private:
+  void FindTopmostEventTarget(const scoped_refptr<dom::Document>& document,
+                              const math::Vector2dF& coordinate);
+
+  void ConsiderElement(const scoped_refptr<dom::HTMLElement>& html_element,
+                       const math::Vector2dF& coordinate);
+  void ConsiderBoxes(const scoped_refptr<dom::HTMLElement>& html_element,
+                     LayoutBoxes* layout_boxes,
+                     const math::Vector2dF& coordinate);
+};
+
+}  // namespace layout
+}  // namespace cobalt
+
+#endif  // COBALT_LAYOUT_TOPMOST_EVENT_TARGET_H_
diff --git a/src/cobalt/layout/used_style.cc b/src/cobalt/layout/used_style.cc
index 91e64d4..5c9dab7 100644
--- a/src/cobalt/layout/used_style.cc
+++ b/src/cobalt/layout/used_style.cc
@@ -710,69 +710,11 @@
   }
 }
 
-// Returns the corner points that should be used to calculate the source and
-// destination gradient points.  This is determined by which quadrant the
-// gradient direction vector lies within.
-std::pair<math::PointF, math::PointF>
-GetSourceAndDestinationPointsFromGradientVector(
-    const math::Vector2dF& gradient_vector, const math::SizeF& frame_size) {
-  std::pair<math::PointF, math::PointF> ret;
-  if (gradient_vector.x() >= 0 && gradient_vector.y() >= 0) {
-    ret.first = math::PointF(0, 0);
-    ret.second = math::PointF(frame_size.width(), frame_size.height());
-  } else if (gradient_vector.x() < 0 && gradient_vector.y() >= 0) {
-    ret.first = math::PointF(frame_size.width(), 0);
-    ret.second = math::PointF(0, frame_size.height());
-  } else if (gradient_vector.x() < 0 && gradient_vector.y() < 0) {
-    ret.first = math::PointF(frame_size.width(), frame_size.height());
-    ret.second = math::PointF(0, 0);
-  } else if (gradient_vector.x() >= 0 && gradient_vector.y() < 0) {
-    ret.first = math::PointF(0, frame_size.height());
-    ret.second = math::PointF(frame_size.width(), 0);
-  } else {
-    NOTREACHED();
-  }
-
-  return ret;
-}
-
-math::PointF IntersectLines(math::PointF point_a, math::Vector2dF dir_a,
-                            math::PointF point_b, math::Vector2dF dir_b) {
-  DCHECK(dir_a.y() != 0 || dir_b.y() != 0);
-
-  if (dir_a.x() == 0) {
-    // Swap a and b so that we are guaranteed not to divide by 0.
-    std::swap(point_a, point_b);
-    std::swap(dir_a, dir_b);
-  }
-
-  float slope_a = dir_a.y() / dir_a.x();
-
-  // Calculate how far from |point_b| we should travel in units of |dir_b|
-  // in order to reach the point of intersection.
-  float distance_from_point_b =
-      (point_a.y() - point_b.y() + slope_a * (point_b.x() - point_a.x())) /
-      (dir_b.y() - slope_a * dir_b.x());
-
-  dir_b.Scale(distance_from_point_b);
-  return point_b + dir_b;
-}
-
 std::pair<math::PointF, math::PointF> LinearGradientPointsFromAngle(
     float angle_in_radians, const math::SizeF& frame_size) {
   // The method of defining the source and destination points for the linear
   // gradient are defined here:
   //   https://www.w3.org/TR/2012/CR-css3-images-20120417/#linear-gradients
-  //
-  // "Starting from the center of the gradient box, extend a line at the
-  //  specified angle in both directions. The ending point is the point on the
-  //  gradient line where a line drawn perpendicular to the gradient line would
-  //  intersect the corner of the gradient box in the specified direction. The
-  //  starting point is determined identically, but in the opposite direction."
-
-  // First determine the line parallel to the gradient angle.
-  math::PointF gradient_line_point(frame_size.width() / 2.0f,
-                                   frame_size.height() / 2.0f);
 
   // The angle specified by linear gradient has "up" as its origin direction
   // and rotates clockwise as the angle increases.  We must convert this to
@@ -780,29 +722,8 @@
   // we can pass it into the trigonometric functions cos() and sin().
   float ccw_angle_from_right = -angle_in_radians + static_cast<float>(M_PI / 2);
 
-  // Note that we flip the y value here since we move down in our screen space
-  // as y increases.
-  math::Vector2dF gradient_vector(
-      static_cast<float>(cos(ccw_angle_from_right)),
-      static_cast<float>(-sin(ccw_angle_from_right)));
-
-  // Determine the line direction that is perpendicular to the gradient line.
-  math::Vector2dF perpendicular_vector(-gradient_vector.y(),
-                                       gradient_vector.x());
-
-  // Determine the corner points that should be used to calculate the source
-  // and destination points, based on which quadrant the gradient direction
-  // vector lies within.
-  std::pair<math::PointF, math::PointF> corners =
-      GetSourceAndDestinationPointsFromGradientVector(gradient_vector,
-                                                      frame_size);
-
-  // Intersect the perpendicular line running through the source corner with
-  // the gradient line to get our source point.
-  return std::make_pair(IntersectLines(gradient_line_point, gradient_vector,
-                                       corners.first, perpendicular_vector),
-                        IntersectLines(gradient_line_point, gradient_vector,
-                                       corners.second, perpendicular_vector));
+  return render_tree::LinearGradientPointsFromAngle(
+      ccw_angle_from_right, frame_size);
 }
 
 // The specifications indicate that if positions are not specified for color
diff --git a/src/cobalt/layout_tests/layout_snapshot.cc b/src/cobalt/layout_tests/layout_snapshot.cc
index cb375a7..f57e2e5 100644
--- a/src/cobalt/layout_tests/layout_snapshot.cc
+++ b/src/cobalt/layout_tests/layout_snapshot.cc
@@ -82,14 +82,14 @@
 
   // Create the WebModule and wait for a layout to occur.
   browser::WebModule web_module(
-      url, base::Bind(&WebModuleOnRenderTreeProducedCallback, &results,
-                      &run_loop, MessageLoop::current()),
+      url, base::kApplicationStateStarted,
+      base::Bind(&WebModuleOnRenderTreeProducedCallback, &results, &run_loop,
+                 MessageLoop::current()),
       base::Bind(&WebModuleErrorCallback, &run_loop, MessageLoop::current()),
       base::Closure() /* window_close_callback */,
-      base::Closure() /* window_minimize_callback */,
-      stub_media_module.get(), &network_module, viewport_size,
-      resource_provider, stub_media_module->system_window(), 60.0f,
-      web_module_options);
+      base::Closure() /* window_minimize_callback */, stub_media_module.get(),
+      &network_module, viewport_size, resource_provider,
+      stub_media_module->system_window(), 60.0f, web_module_options);
 
   run_loop.Run();
 
diff --git a/src/cobalt/layout_tests/layout_tests.gyp b/src/cobalt/layout_tests/layout_tests.gyp
index 179732c..e0b1ce6 100644
--- a/src/cobalt/layout_tests/layout_tests.gyp
+++ b/src/cobalt/layout_tests/layout_tests.gyp
@@ -14,7 +14,7 @@
 
 {
   'variables': {
-    'cobalt_code': 1,
+    'sb_pedantic_warnings': 1,
   },
 
   # A library to support code that wishes to load a URL and take a snapshot
diff --git a/src/cobalt/layout_tests/testdata/web-platform-tests/fetch/web_platform_tests.txt b/src/cobalt/layout_tests/testdata/web-platform-tests/fetch/web_platform_tests.txt
index 7532609..3fd8ec9 100644
--- a/src/cobalt/layout_tests/testdata/web-platform-tests/fetch/web_platform_tests.txt
+++ b/src/cobalt/layout_tests/testdata/web-platform-tests/fetch/web_platform_tests.txt
@@ -5,12 +5,17 @@
 #   json(), and arrayBuffer(). Blob, URLSearchParams, and FormData are not
 #   well-supported by the javascript engine. Certain tests may be substitued
 #   with *.cobalt.html versions to accommodate this limitation.
+
+# Failure is not specific to fetch.
 api/basic/block-mime-as-script.html,DISABLE
+# Caching responses is not supported.
 api/basic/conditional-get.html,DISABLE
 api/basic/error-after-response.html,PASS
+# Integrity validation is not supported.
 api/basic/integrity.html,DISABLE
 api/basic/integrity-sharedworker.html,DISABLE
 api/basic/integrity-worker.html,DISABLE
+# Mode is not supported.
 api/basic/mode-no-cors.html,DISABLE
 api/basic/mode-no-cors-worker.html,DISABLE
 api/basic/request-referrer-redirected-worker.html,DISABLE
@@ -22,7 +27,9 @@
 api/basic/scheme-blob-worker.html,DISABLE
 api/basic/scheme-others.html,PASS
 api/basic/scheme-others-worker.html,DISABLE
+# Only UTF-8 is supported. UTF-16 is not supported.
 api/basic/text-utf8.html,DISABLE
+# CORS is not supported.
 api/cors/cors-expose-star.html,DISABLE
 api/cors/cors-expose-star-worker.html,DISABLE
 api/cors/cors-filtering.html,DISABLE
@@ -44,8 +51,12 @@
 api/headers/header-values.html,DISABLE
 # Invalid: this tests XHR
 api/headers/header-values-normalize.html,DISABLE
+# Fails because a SecurityError is thrown instead of the expected TypeError.
+# The fetch spec does not explicitly say a TypeError should be thrown, so the
+# test may be wrong.
 api/policies/csp-blocked.html,DISABLE
 api/policies/csp-blocked-worker.html,DISABLE
+# Referrer is intentionally not supported due to privacy concerns.
 api/policies/referrer-no-referrer.html,DISABLE
 api/policies/referrer-no-referrer-service-worker.https.html,DISABLE
 api/policies/referrer-no-referrer-worker.html,DISABLE
@@ -58,6 +69,7 @@
 api/policies/referrer-unsafe-url.html,DISABLE
 api/policies/referrer-unsafe-url-service-worker.https.html,DISABLE
 api/policies/referrer-unsafe-url-worker.html,DISABLE
+# Redirect is not fully supported.
 api/redirect/redirect-count.html,DISABLE
 api/redirect/redirect-count-worker.html,DISABLE
 api/redirect/redirect-location.html,DISABLE
@@ -73,8 +85,10 @@
 api/redirect/redirect-schemes.html,DISABLE
 api/redirect/redirect-to-dataurl.html,DISABLE
 api/redirect/redirect-to-dataurl-worker.html,DISABLE
+# Test fails to run because 'iframe' is not supported.
 api/request/multi-globals/url-parsing.html,DISABLE
 api/request/request-bad-port.html,PASS
+# Fails because Cobalt does not support caching responses.
 api/request/request-cache-default.html,DISABLE
 api/request/request-cache-default-conditional.html,DISABLE
 api/request/request-cache-force-cache.html,DISABLE
@@ -82,17 +96,25 @@
 api/request/request-cache-no-store.html,PASS
 api/request/request-cache-only-if-cached.html,DISABLE
 api/request/request-cache-reload.html,DISABLE
-api/request/request-clone.sub.html,DISABLE
+api/request/request-clone.sub.html,PASS
+# Fails because Body only supports text, json, and arrayBuffer. See the
+# corresponding tests for Response which have been customized to test only
+# those types.
 api/request/request-consume.html,DISABLE
+# Fails because blob and formData are not supported.
 api/request/request-consume-empty.html,DISABLE
-api/request/request-disturbed.html,DISABLE
-api/request/request-error.html,DISABLE
-api/request/request-headers.html,DISABLE
+api/request/request-disturbed.html,PASS
+api/request/request-error.html,PASS
+api/request/request-headers.html,PASS
+# Fails because implementation is a polyfill.
 api/request/request-idl.html,DISABLE
-api/request/request-init-001.sub.html,DISABLE
+api/request/request-init-001.sub.html,PASS
+# Fails because Blob, FormData, and URLSearchParams are not supported.
 api/request/request-init-002.html,DISABLE
-api/request/request-init-003.sub.html,DISABLE
+api/request/request-init-003.sub.html,PASS
 api/request/request-keepalive-quota.html,DISABLE
+# Fails because: 1) Body does not support blob nor formData, and 2) Request
+# does not support destination nor type.
 api/request/request-structure.html,DISABLE
 api/response/multi-globals/url-parsing.html,DISABLE
 # Fails because Blob is not fully supported.
@@ -116,5 +138,5 @@
 api/response/response-stream-disturbed-3.html,PASS
 api/response/response-stream-disturbed-4.html,PASS
 api/response/response-stream-disturbed-5.html,PASS
-# Not implemented.
+# Fails because trailer is not implemented.
 api/response/response-trailer.html,DISABLE
diff --git a/src/cobalt/layout_tests/web_platform_tests.cc b/src/cobalt/layout_tests/web_platform_tests.cc
index 12e70e2..f990066 100644
--- a/src/cobalt/layout_tests/web_platform_tests.cc
+++ b/src/cobalt/layout_tests/web_platform_tests.cc
@@ -172,14 +172,14 @@
 
   // Create the WebModule and wait for a layout to occur.
   browser::WebModule web_module(
-      url, base::Bind(&WebModuleOnRenderTreeProducedCallback, &results,
-                      &run_loop, MessageLoop::current()),
+      url, base::kApplicationStateStarted,
+      base::Bind(&WebModuleOnRenderTreeProducedCallback, &results, &run_loop,
+                 MessageLoop::current()),
       base::Bind(&WebModuleErrorCallback, &run_loop, MessageLoop::current()),
       base::Closure() /* window_close_callback */,
-      base::Closure() /* window_minimize_callback */,
-      media_module.get(), &network_module, kDefaultViewportSize,
-      &resource_provider, media_module->system_window(), 60.0f,
-      web_module_options);
+      base::Closure() /* window_minimize_callback */, media_module.get(),
+      &network_module, kDefaultViewportSize, &resource_provider,
+      media_module->system_window(), 60.0f, web_module_options);
   run_loop.Run();
   const std::string extract_results =
       "document.getElementById(\"__testharness__results__\").textContent;";
diff --git a/src/cobalt/loader/image/image_data_decoder.h b/src/cobalt/loader/image/image_data_decoder.h
index 1dd5a52..2d648b5 100644
--- a/src/cobalt/loader/image/image_data_decoder.h
+++ b/src/cobalt/loader/image/image_data_decoder.h
@@ -46,7 +46,7 @@
   }
 
 #if defined(STARBOARD)
-#if SB_VERSION(3) && SB_HAS(GRAPHICS)
+#if SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
   // Starboard version 3 adds support for hardware accelerated image decoding.
   // In order to make use of this feature, subclasses of ImageDataDecoder may
   // override this method in order to return an SbDecodeTarget, rather than a
@@ -59,7 +59,7 @@
   virtual SbDecodeTarget RetrieveSbDecodeTarget() {
     return kSbDecodeTargetInvalid;
   }
-#endif  // SB_VERSION(3) && SB_HAS(GRAPHICS)
+#endif  // SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
 #endif  // defined(STARBOARD)
 
   void DecodeChunk(const uint8* data, size_t size);
diff --git a/src/cobalt/loader/image/image_decoder.cc b/src/cobalt/loader/image/image_decoder.cc
index a44f373..739c523 100644
--- a/src/cobalt/loader/image/image_decoder.cc
+++ b/src/cobalt/loader/image/image_decoder.cc
@@ -162,7 +162,7 @@
       if (decoder_->FinishWithSuccess()) {
         if (!decoder_->has_animation()) {
 #if defined(STARBOARD)
-#if SB_VERSION(3) && SB_HAS(GRAPHICS)
+#if SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
           SbDecodeTarget target = decoder_->RetrieveSbDecodeTarget();
           if (SbDecodeTargetIsValid(target)) {
             success_callback_.Run(new StaticImage(
@@ -272,7 +272,7 @@
 
 namespace {
 #if defined(STARBOARD)
-#if SB_VERSION(3) && SB_HAS(GRAPHICS)
+#if SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
 const char* GetMimeTypeFromImageType(ImageDecoder::ImageType image_type) {
   switch (image_type) {
     case ImageDecoder::kImageTypeJPEG:
@@ -331,7 +331,7 @@
   }
   return scoped_ptr<ImageDataDecoder>();
 }
-#endif  // SB_VERSION(3) && SB_HAS(GRAPHICS)
+#endif  // SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
 #endif  // defined(STARBOARD)
 
 scoped_ptr<ImageDataDecoder> CreateImageDecoderFromImageType(
@@ -384,10 +384,10 @@
   }
 
 #if defined(STARBOARD)
-#if SB_VERSION(3) && SB_HAS(GRAPHICS)
+#if SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
   decoder_ =
       MaybeCreateStarboardDecoder(mime_type_, image_type_, resource_provider_);
-#endif  // SB_VERSION(3) && SB_HAS(GRAPHICS)
+#endif  // SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
 #endif  // defined(STARBOARD)
 
   if (!decoder_) {
diff --git a/src/cobalt/loader/image/image_decoder_starboard.h b/src/cobalt/loader/image/image_decoder_starboard.h
index 2cc98ef..39b6cbe 100644
--- a/src/cobalt/loader/image/image_decoder_starboard.h
+++ b/src/cobalt/loader/image/image_decoder_starboard.h
@@ -25,7 +25,7 @@
 #include "cobalt/loader/image/image_data_decoder.h"
 #include "starboard/decode_target.h"
 
-#if SB_VERSION(3) && SB_HAS(GRAPHICS)
+#if SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
 
 namespace cobalt {
 namespace loader {
@@ -63,7 +63,7 @@
 }  // namespace loader
 }  // namespace cobalt
 
-#endif  // SB_VERSION(3) && SB_HAS(GRAPHICS)
+#endif  // SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
 
 #endif  // defined(STARBOARD)
 
diff --git a/src/cobalt/loader/loader.gyp b/src/cobalt/loader/loader.gyp
index 0c9edff..b9bdc78 100644
--- a/src/cobalt/loader/loader.gyp
+++ b/src/cobalt/loader/loader.gyp
@@ -14,7 +14,7 @@
 
 {
   'variables': {
-    'cobalt_code': 1,
+    'sb_pedantic_warnings': 1,
   },
   'targets': [
     {
diff --git a/src/cobalt/loader/loader_factory.cc b/src/cobalt/loader/loader_factory.cc
index b2794a5..b2e68eb 100644
--- a/src/cobalt/loader/loader_factory.cc
+++ b/src/cobalt/loader/loader_factory.cc
@@ -14,7 +14,6 @@
 
 #include "cobalt/loader/loader_factory.h"
 
-#include "base/synchronization/waitable_event.h"
 #include "base/threading/platform_thread.h"
 #include "cobalt/loader/image/threaded_image_decoder_proxy.h"
 
@@ -117,11 +116,7 @@
   }
 
   // Wait for all loader thread messages to be flushed before returning.
-  base::WaitableEvent messages_flushed(true, false);
-  load_thread_.message_loop()->PostTask(
-      FROM_HERE, base::Bind(&base::WaitableEvent::Signal,
-                            base::Unretained(&messages_flushed)));
-  messages_flushed.Wait();
+  load_thread_.message_loop()->WaitForFence();
 }
 
 void LoaderFactory::Resume(render_tree::ResourceProvider* resource_provider) {
diff --git a/src/cobalt/loader/resource_cache.h b/src/cobalt/loader/resource_cache.h
index 2134423..336d137 100644
--- a/src/cobalt/loader/resource_cache.h
+++ b/src/cobalt/loader/resource_cache.h
@@ -512,6 +512,7 @@
 
   base::CVal<base::cval::SizeInBytes, base::CValPublic> size_in_bytes_;
   base::CVal<base::cval::SizeInBytes, base::CValPublic> capacity_in_bytes_;
+  base::CVal<int> count_requested_resources_;
   base::CVal<int> count_loading_resources_;
   base::CVal<int> count_pending_callbacks_;
 
@@ -539,6 +540,9 @@
           "The capacity, in bytes, of the resource cache.  "
           "Exceeding this results in *unused* resources being "
           "purged."),
+      count_requested_resources_(
+          base::StringPrintf("Count.%s.RequestedResources", name_.c_str()), 0,
+          "The total number of resources that have been requested."),
       count_loading_resources_(
           base::StringPrintf("Count.%s.LoadingResources", name_.c_str()), 0,
           "The number of loading resources that are still outstanding."),
@@ -576,6 +580,7 @@
   }
 
   // If we reach this point, then the resource doesn't exist yet.
+  ++count_requested_resources_;
 
   // Add the resource to a loading set. If no current resources have pending
   // callbacks, then this resource