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